@wrongstack/cli 0.283.0 → 0.283.1
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/index.js +1295 -166
- package/dist/index.js.map +1 -1
- package/package.json +16 -15
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { CODEX_MODELS, color, ToolCapabilities, PHASE_EVENT_NAMES, readBundledInstructionText, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, ConfigError, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, createHqPersistence, HqCommandAuditLog, HqAlertEngine, toAlertMessage, validateHqCommand, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, FetchError, removeColumn, addColumn, getTask, removeTask, getBoard, assignTask, updateTaskAssignment, listBoards, updateGoalMetricOnTask, addGoalMetricToTask, releaseTaskClaim, claimReadyTask, getTaskChain, setTaskChain, copyTaskToBoard, transferTaskToBoard, moveTask, updateTask, mergeTasks, splitTask, addTask, syncBoardFromTaskGraph, deserializeTaskGraph, exportBoardToTaskGraph, serializeTaskGraph, getKanbanOrchestrationSnapshot, listReadyTasks, generateBoardFromDescription, createBoard, parseLinesIntoTasks, removeBoard, duplicateBoard, updateBoard, wstackGlobalRoot, DefaultSecretScrubber, resolveWstackPaths, watchProviderConfig, TOKENS, PromptUsageStore, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, shellHooksEqual, countShellHooks, normalizeTokenSavingTier, allServers as allServers$1, SlashCommandRegistry, attachDepWatcherBridge, createFallbackModelExtension, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, SddRunRegistry, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DefaultLogger, DefaultModelsRegistry, isStdinTTY, FsError, atomicWrite, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, sessionScopedPath as sessionScopedPath$1, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, makeFleetStatusTool, applyToolDescriptionModes, applyToolResultRenderModes, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, createHqPublisherFromEnv, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, applyModelRuntime, installDesignStudioMiddleware, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, WorktreeManager, Director, makeFleetEmitTool, AdaptiveConcurrencyController, resolveSubagentModelTarget, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, mergeModelRuntime, ToolValidationError, WIDE_SUBAGENT_CAPABILITIES, resolveProjectDir, FleetSupervisor, AgentError, PhaseStore, PhaseOrchestrator, assignNickname, AutoPhasePlanner, PhaseGraphBuilder, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, ProviderRegistry, makePreferSideConflictResolver, makeAgentSubagentRunner, NULL_FLEET_BUS, ERROR_CODES, buildChildEnv, exportBoardAsMarkdown, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, loadTasks, formatTaskList, formatTaskProgress, mutateTasks, emptyPlan, addPlanItem, savePlan, formatPlan, recordCompletedWorkEvidence, saveCompletedWorkCheckpoint, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, getDesignKitLoader, getDesignState, clearActiveKit, clearPersistedActiveKit, recordOverrides, setDesignOverrides, loadActiveKit, isDesignStack, applyTokenOverrides, materializeTokens, setActiveKit, readHqRuntimeFileSync, resolveHqConfig, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, resolveModelMatrix, resolveModelTargetFromEntry, fallbackProfileChain, parseModelRef, normalizeModelRef, onResize, hasOpenTodos, InputBuilder, detectContinueIntent, resolveContinuation, DefaultSecretVault as DefaultSecretVault$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, DEFAULT_CONTEXT_WINDOW_MODE_ID, HQ_AUTH_FILE_VERSION, truncate, createBoardFromTaskGraph, addDependency, addNoteToTask, addCheckToTask, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, normalizeToolResultRenderMode, getToolResultRenderMode, setToolResultRenderMode, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, areDependenciesMet, findBlockedTasks, renderInstructionTemplate, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, mutatePlan, setPlanItemStatus, getPlanTemplate, resolveProviderModelList, ParseError, startSessionTelemetryBridge, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge } from '@wrongstack/core';
|
|
2
|
+
import { CODEX_MODELS, color, ToolCapabilities, PHASE_EVENT_NAMES, readBundledInstructionText, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, ConfigError, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, createHqPersistence, HqCommandAuditLog, HqAlertEngine, toAlertMessage, validateHqCommand, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, FetchError, removeColumn, addColumn, getTask, removeTask, getBoard, assignTask, updateTaskAssignment, listBoards, updateGoalMetricOnTask, addGoalMetricToTask, releaseTaskClaim, claimReadyTask, getTaskChain, setTaskChain, copyTaskToBoard, transferTaskToBoard, moveTask, updateTask, mergeTasks, splitTask, addTask, syncBoardFromTaskGraph, deserializeTaskGraph, exportBoardToTaskGraph, serializeTaskGraph, getKanbanOrchestrationSnapshot, listReadyTasks, generateBoardFromDescription, createBoard, parseLinesIntoTasks, removeBoard, duplicateBoard, updateBoard, wstackGlobalRoot, DefaultSecretScrubber, resolveWstackPaths, watchProviderConfig, TOKENS, PromptUsageStore, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, shellHooksEqual, countShellHooks, normalizeTokenSavingTier, allServers as allServers$1, SlashCommandRegistry, attachDepWatcherBridge, createFallbackModelExtension, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, SddRunRegistry, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DefaultLogger, DefaultModelsRegistry, isStdinTTY, FsError, atomicWrite, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, sessionScopedPath as sessionScopedPath$1, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, makeFleetStatusTool, applyToolDescriptionModes, applyToolResultRenderModes, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, createHqPublisherFromEnv, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, applyModelRuntime, installDesignStudioMiddleware, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, WorktreeManager, Director, makeFleetEmitTool, AdaptiveConcurrencyController, resolveSubagentModelTarget, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, mergeModelRuntime, ToolValidationError, WIDE_SUBAGENT_CAPABILITIES, resolveProjectDir, FleetSupervisor, AgentError, PhaseStore, PhaseOrchestrator, assignNickname, AutoPhasePlanner, PhaseGraphBuilder, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, ProviderRegistry, makePreferSideConflictResolver, makeAgentSubagentRunner, NULL_FLEET_BUS, ERROR_CODES, buildChildEnv, exportBoardAsMarkdown, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, loadTasks, formatTaskList, formatTaskProgress, mutateTasks, emptyPlan, addPlanItem, savePlan, formatPlan, recordCompletedWorkEvidence, saveCompletedWorkCheckpoint, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, getDesignKitLoader, getDesignState, clearActiveKit, clearPersistedActiveKit, recordOverrides, setDesignOverrides, loadActiveKit, isDesignStack, applyTokenOverrides, materializeTokens, setActiveKit, readHqRuntimeFileSync, resolveHqConfig, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, resolveModelMatrix, resolveModelTargetFromEntry, fallbackProfileChain, parseModelRef, normalizeModelRef, onResize, hasOpenTodos, InputBuilder, detectContinueIntent, resolveContinuation, DefaultSecretVault as DefaultSecretVault$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, DEFAULT_CONTEXT_WINDOW_MODE_ID, HQ_AUTH_FILE_VERSION, stripAnsi, truncate, createBoardFromTaskGraph, addDependency, addNoteToTask, addCheckToTask, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, normalizeToolResultRenderMode, getToolResultRenderMode, setToolResultRenderMode, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, areDependenciesMet, findBlockedTasks, renderInstructionTemplate, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, mutatePlan, setPlanItemStatus, getPlanTemplate, resolveProviderModelList, ParseError, startSessionTelemetryBridge, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge } from '@wrongstack/core';
|
|
3
3
|
import * as fs2 from 'fs/promises';
|
|
4
4
|
import { access, readFile } from 'fs/promises';
|
|
5
5
|
import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
|
|
@@ -4219,7 +4219,66 @@ var init_hq_dashboard_html = __esm({
|
|
|
4219
4219
|
.subbadge { font-size: 9px; text-transform: uppercase; letter-spacing: 0.5px; padding: 1px 6px; border-radius: 999px; background: rgba(163,113,247,0.18); color: var(--purple); font-weight: 700; }
|
|
4220
4220
|
.chat-meta { margin-left: auto; font-size: 11px; color: var(--dim); }
|
|
4221
4221
|
.chat-body { flex: 1; overflow-y: auto; padding: 16px 22px; }
|
|
4222
|
-
.
|
|
4222
|
+
.cmd-dock { border-top: 1px solid var(--border); background: var(--bg2); padding: 10px 14px; display: grid; gap: 8px; }
|
|
4223
|
+
.cmd-row { display: flex; gap: 8px; align-items: center; }
|
|
4224
|
+
.cmd-target { min-width: 220px; max-width: 44%; flex: 0 1 360px; background: var(--inset); border: 1px solid var(--border); color: var(--text); border-radius: 7px; padding: 7px 9px; font-size: 12px; }
|
|
4225
|
+
.cmd-type { flex: 0 0 auto; min-width: 84px; background: var(--inset); border: 1px solid var(--border); color: var(--text); border-radius: 7px; padding: 7px 9px; font-size: 12px; font-weight: 700; }
|
|
4226
|
+
.cmd-type:disabled { opacity: 0.55; cursor: not-allowed; }
|
|
4227
|
+
.cmd-text { flex: 1; min-height: 38px; max-height: 120px; resize: vertical; background: var(--inset); border: 1px solid var(--border); color: var(--text); border-radius: 7px; padding: 8px 10px; font: 12.5px/1.4 ui-sans-serif, system-ui, sans-serif; }
|
|
4228
|
+
.cmd-target:focus, .cmd-type:focus, .cmd-text:focus { outline: none; border-color: var(--accent); }
|
|
4229
|
+
.cmd-send { flex: 0 0 auto; background: var(--accent); color: #04121f; border: 1px solid var(--accent); border-radius: 7px; padding: 8px 12px; font-size: 12px; font-weight: 800; cursor: pointer; }
|
|
4230
|
+
.cmd-send:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
4231
|
+
.cmd-status { font-size: 11px; color: var(--dim); min-height: 14px; }
|
|
4232
|
+
.cmd-status.ok { color: var(--green); }
|
|
4233
|
+
.cmd-status.err { color: var(--red); }
|
|
4234
|
+
.cmd-history { border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); background: var(--panel); padding: 10px 18px; }
|
|
4235
|
+
.cmd-hhead { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
|
4236
|
+
.cmd-htitle { font-size: 11px; font-weight: 800; color: var(--bright); text-transform: uppercase; letter-spacing: 0.6px; }
|
|
4237
|
+
.cmd-hnote { font-size: 10.5px; color: var(--dim); }
|
|
4238
|
+
.cmd-hrefresh { margin-left: auto; background: var(--inset); border: 1px solid var(--border); border-radius: 7px; color: var(--muted); cursor: pointer; font-size: 11px; padding: 5px 9px; }
|
|
4239
|
+
.cmd-hrefresh:hover { border-color: var(--accent); color: var(--text); }
|
|
4240
|
+
.cmd-hlist { display: grid; gap: 5px; }
|
|
4241
|
+
.cmd-hrow { display: grid; grid-template-columns: 92px 82px minmax(0, 1fr) 88px; gap: 8px; align-items: center; font-size: 11.5px; color: var(--muted); background: var(--inset); border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px; }
|
|
4242
|
+
.cmd-htype { color: var(--bright); font-weight: 700; }
|
|
4243
|
+
.cmd-hstatus { text-transform: uppercase; font-size: 9.5px; font-weight: 800; letter-spacing: 0.5px; color: var(--amber); }
|
|
4244
|
+
.cmd-hstatus.acked { color: var(--green); }
|
|
4245
|
+
.cmd-hstatus.delivered { color: var(--cyan); }
|
|
4246
|
+
.cmd-hstatus.failed, .cmd-hstatus.rejected { color: var(--red); }
|
|
4247
|
+
.cmd-hclient, .cmd-hmsg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4248
|
+
.cmd-history .side-empty { padding: 12px; font-size: 11.5px; }
|
|
4249
|
+
.timeline-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--bg2); }
|
|
4250
|
+
.timeline-head { padding: 12px 18px; border-bottom: 1px solid var(--border); display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
|
|
4251
|
+
.timeline-title { font-size: 13px; font-weight: 800; color: var(--bright); }
|
|
4252
|
+
.timeline-note { font-size: 11px; color: var(--dim); }
|
|
4253
|
+
.timeline-filters { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
4254
|
+
.timeline-input, .timeline-select { background: var(--inset); border: 1px solid var(--border); color: var(--text); border-radius: 7px; padding: 6px 9px; font-size: 12px; }
|
|
4255
|
+
.timeline-input { width: 260px; }
|
|
4256
|
+
.timeline-input:focus, .timeline-select:focus { outline: none; border-color: var(--accent); }
|
|
4257
|
+
.timeline-btn { background: var(--inset); border: 1px solid var(--border); color: var(--muted); border-radius: 7px; padding: 6px 10px; font-size: 12px; cursor: pointer; }
|
|
4258
|
+
.timeline-btn:hover { color: var(--text); border-color: var(--accent); }
|
|
4259
|
+
.timeline-list { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 18px 18px; display: grid; gap: 8px; align-content: start; }
|
|
4260
|
+
.timeline-row { display: grid; grid-template-columns: 34px 92px minmax(170px, 260px) minmax(0, 1fr); gap: 10px; border: 1px solid var(--border); border-radius: 10px; padding: 9px 10px; cursor: pointer; background: var(--panel); align-items: start; }
|
|
4261
|
+
.timeline-row:hover { background: var(--panel2); border-color: var(--accent); }
|
|
4262
|
+
.timeline-row.tool { border-color: rgba(57,208,216,0.22); background: rgba(57,208,216,0.045); }
|
|
4263
|
+
.timeline-row.assistant { border-color: rgba(163,113,247,0.20); }
|
|
4264
|
+
.timeline-row.error { border-color: rgba(248,81,73,0.32); background: rgba(248,81,73,0.06); }
|
|
4265
|
+
.timeline-mark { width: 28px; height: 28px; border-radius: 9px; display: grid; place-items: center; background: var(--inset); border: 1px solid var(--border); color: var(--muted); font-size: 14px; }
|
|
4266
|
+
.timeline-row.assistant .timeline-mark { color: var(--purple); border-color: rgba(163,113,247,0.35); background: rgba(163,113,247,0.12); }
|
|
4267
|
+
.timeline-row.tool .timeline-mark { color: var(--cyan); border-color: rgba(57,208,216,0.35); background: rgba(57,208,216,0.11); }
|
|
4268
|
+
.timeline-row.user .timeline-mark { color: var(--accent); border-color: rgba(88,166,255,0.35); background: rgba(88,166,255,0.11); }
|
|
4269
|
+
.timeline-row.error .timeline-mark { color: var(--red); border-color: rgba(248,81,73,0.38); background: rgba(248,81,73,0.12); }
|
|
4270
|
+
.timeline-time { color: var(--dim); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
4271
|
+
.timeline-who { min-width: 0; }
|
|
4272
|
+
.timeline-agent { color: var(--bright); font-weight: 700; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4273
|
+
.timeline-project { color: var(--muted); font-size: 10.5px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4274
|
+
.timeline-msg { min-width: 0; color: var(--text); font-size: 12.5px; line-height: 1.45; white-space: pre-wrap; word-break: break-word; max-height: 86px; overflow: hidden; }
|
|
4275
|
+
.timeline-role { display: inline-block; margin-right: 6px; color: var(--dim); text-transform: uppercase; font-size: 9.5px; letter-spacing: 0.5px; font-weight: 800; }
|
|
4276
|
+
.timeline-role.user { color: var(--accent); }
|
|
4277
|
+
.timeline-role.assistant { color: var(--purple); }
|
|
4278
|
+
.timeline-role.tool { color: var(--cyan); }
|
|
4279
|
+
.timeline-role.error { color: var(--red); }
|
|
4280
|
+
.timeline-toolchip { display: inline-block; margin-right: 6px; border: 1px solid rgba(57,208,216,0.32); background: rgba(57,208,216,0.08); color: var(--cyan); border-radius: 999px; padding: 1px 7px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 10px; }
|
|
4281
|
+
.bub.live .bub-card { border-color: rgba(63,185,80,0.5); box-shadow: 0 0 0 1px rgba(63,185,80,0.15); }
|
|
4223
4282
|
.bub.live .live-dot { margin-left: auto; color: var(--green); font-size: 9px; animation: livepulse 1.4s ease-in-out infinite; }
|
|
4224
4283
|
@keyframes livepulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }
|
|
4225
4284
|
.caret { display: inline-block; width: 7px; height: 13px; margin-left: 2px; background: var(--green); vertical-align: text-bottom; animation: blink 1s steps(2, start) infinite; }
|
|
@@ -4243,7 +4302,9 @@ var init_hq_dashboard_html = __esm({
|
|
|
4243
4302
|
.fnode.terminal.k-repl { border-left-color: var(--amber); }
|
|
4244
4303
|
.fnode.terminal.k-webui { border-left-color: var(--accent); }
|
|
4245
4304
|
.fnode.terminal.k-cli { border-left-color: var(--cyan); }
|
|
4246
|
-
.fnode.agent { width:
|
|
4305
|
+
.fnode.agent { width: 196px; padding: 9px 10px; }
|
|
4306
|
+
.fnode.agent .fnode-sub { max-height: 30px; white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
|
4307
|
+
.fnode.agent .fnode-chips { max-height: 34px; overflow: hidden; }
|
|
4247
4308
|
.fnode.agent.s-running, .fnode.agent.s-streaming { border-color: var(--green); box-shadow: 0 0 0 1px rgba(63,185,80,0.25), 0 10px 22px rgba(0,0,0,0.4); animation: pulse 1.8s ease-in-out infinite; }
|
|
4248
4309
|
.fnode.agent.s-waiting_user { border-color: var(--amber); }
|
|
4249
4310
|
.fnode.agent.s-error { border-color: var(--red); }
|
|
@@ -4265,30 +4326,82 @@ var init_hq_dashboard_html = __esm({
|
|
|
4265
4326
|
.side-close:hover { background: var(--panel); color: var(--text); }
|
|
4266
4327
|
.side-body { flex: 1; overflow-y: auto; padding: 14px 16px; }
|
|
4267
4328
|
.side-empty { margin: auto; color: var(--dim); font-style: italic; text-align: center; padding: 40px 24px; }
|
|
4268
|
-
.bub { margin-bottom:
|
|
4269
|
-
.bub
|
|
4270
|
-
.bub
|
|
4271
|
-
.bub.
|
|
4272
|
-
.bub.user .bub-
|
|
4273
|
-
.bub.
|
|
4329
|
+
.bub { margin-bottom: 14px; display: flex; gap: 10px; align-items: flex-start; font-size: 13px; line-height: 1.55; }
|
|
4330
|
+
.bub.user { flex-direction: row-reverse; }
|
|
4331
|
+
.bub-avatar { flex: 0 0 32px; width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center; font-size: 15px; border: 1px solid var(--border2); background: var(--panel); color: var(--muted); box-shadow: 0 0 0 3px rgba(13,17,23,0.65); }
|
|
4332
|
+
.bub.assistant .bub-avatar { background: rgba(163,113,247,0.16); color: var(--purple); border-color: rgba(163,113,247,0.35); }
|
|
4333
|
+
.bub.user .bub-avatar { background: var(--accent); color: #04121f; border-color: rgba(88,166,255,0.7); }
|
|
4334
|
+
.bub.tool .bub-avatar { background: rgba(57,208,216,0.13); color: var(--cyan); border-color: rgba(57,208,216,0.35); }
|
|
4335
|
+
.bub.error .bub-avatar { background: rgba(248,81,73,0.14); color: var(--red); border-color: rgba(248,81,73,0.4); }
|
|
4336
|
+
.bub.system .bub-avatar { background: var(--inset); color: var(--dim); }
|
|
4337
|
+
.bub-content { min-width: 0; flex: 1; max-width: 860px; display: flex; flex-direction: column; gap: 5px; }
|
|
4338
|
+
.bub.user .bub-content { align-items: flex-end; }
|
|
4339
|
+
.bub-head { display: flex; align-items: center; gap: 8px; padding: 0 2px; color: var(--dim); font-size: 10.5px; }
|
|
4340
|
+
.bub-role { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px; color: var(--muted); }
|
|
4274
4341
|
.bub.assistant .bub-role { color: var(--purple); }
|
|
4275
|
-
.bub.
|
|
4342
|
+
.bub.user .bub-role { color: var(--accent); }
|
|
4276
4343
|
.bub.tool .bub-role { color: var(--cyan); }
|
|
4277
|
-
.bub.error { background: rgba(248,81,73,0.08); border-color: rgba(248,81,73,0.3); }
|
|
4278
4344
|
.bub.error .bub-role { color: var(--red); }
|
|
4279
|
-
.bub
|
|
4280
|
-
.bub
|
|
4345
|
+
.bub-card { width: fit-content; max-width: 100%; border: 1px solid var(--border); border-radius: 14px; padding: 10px 12px; background: var(--panel); color: var(--text); box-shadow: 0 8px 20px rgba(0,0,0,0.18); }
|
|
4346
|
+
.bub.assistant .bub-card { background: var(--panel); border-color: var(--border2); border-bottom-left-radius: 5px; }
|
|
4347
|
+
.bub.user .bub-card { background: rgba(88,166,255,0.16); border-color: rgba(88,166,255,0.35); border-bottom-right-radius: 5px; }
|
|
4348
|
+
.bub.tool .bub-card { background: rgba(57,208,216,0.06); border-color: rgba(57,208,216,0.22); border-bottom-left-radius: 5px; }
|
|
4349
|
+
.bub.error .bub-card { background: rgba(248,81,73,0.08); border-color: rgba(248,81,73,0.32); color: var(--text); }
|
|
4350
|
+
.bub.system .bub-card { background: transparent; border-style: dashed; color: var(--muted); box-shadow: none; }
|
|
4351
|
+
.bub pre { margin: 0; white-space: pre-wrap; word-break: break-word; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11.5px; max-height: 360px; overflow: auto; }
|
|
4281
4352
|
.bub .txt { white-space: pre-wrap; word-break: break-word; }
|
|
4353
|
+
.assistant-body, .txt { word-break: break-word; }
|
|
4354
|
+
.msg-md { display: grid; gap: 8px; }
|
|
4355
|
+
.msg-md p { margin: 0; white-space: pre-wrap; }
|
|
4356
|
+
.md-heading { font-weight: 800; color: var(--bright); margin-top: 2px; }
|
|
4357
|
+
.md-bullet { display: grid; grid-template-columns: 14px minmax(0, 1fr); gap: 6px; align-items: start; }
|
|
4358
|
+
.md-bullet-dot { color: var(--accent); }
|
|
4359
|
+
.md-inline { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 0.92em; padding: 1px 4px; border-radius: 5px; background: var(--inset); border: 1px solid var(--border); color: var(--cyan); }
|
|
4360
|
+
.md-codewrap { border: 1px solid var(--border); border-radius: 9px; overflow: hidden; background: var(--bg2); }
|
|
4361
|
+
.md-codelabel { display: flex; align-items: center; gap: 6px; padding: 5px 9px; border-bottom: 1px solid var(--border); color: var(--dim); font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
4362
|
+
.md-code { margin: 0; padding: 10px 11px; max-height: 360px; overflow: auto; white-space: pre; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11.5px; line-height: 1.45; color: var(--text); }
|
|
4363
|
+
.tool-card { display: grid; gap: 8px; min-width: min(520px, 100%); }
|
|
4364
|
+
.tool-head { display: flex; align-items: center; gap: 8px; color: var(--text); }
|
|
4365
|
+
.tool-name { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; font-weight: 800; color: var(--cyan); }
|
|
4366
|
+
.tool-status { margin-left: auto; font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--green); font-weight: 800; }
|
|
4367
|
+
.tool-status.error { color: var(--red); }
|
|
4368
|
+
.tool-duration { font-size: 10.5px; color: var(--dim); font-variant-numeric: tabular-nums; }
|
|
4369
|
+
.tool-group { margin: 0 0 14px 42px; max-width: 860px; border: 1px solid var(--border); border-radius: 12px; background: rgba(57,208,216,0.045); overflow: hidden; }
|
|
4370
|
+
.tool-group > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px; padding: 9px 11px; color: var(--text); user-select: none; }
|
|
4371
|
+
.tool-group > summary::-webkit-details-marker { display: none; }
|
|
4372
|
+
.tool-group > summary::before { content: '\u25B8'; color: var(--dim); font-size: 10px; }
|
|
4373
|
+
.tool-group[open] > summary::before { content: '\u25BE'; }
|
|
4374
|
+
.tool-group-title { font-weight: 800; color: var(--cyan); font-size: 12px; }
|
|
4375
|
+
.tool-group-meta { color: var(--muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4376
|
+
.tool-group-status { margin-left: auto; font-size: 10px; text-transform: uppercase; font-weight: 800; letter-spacing: 0.5px; color: var(--green); }
|
|
4377
|
+
.tool-group-status.error { color: var(--red); }
|
|
4378
|
+
.tool-group-body { padding: 10px 12px 2px; border-top: 1px solid var(--border); background: rgba(13,17,23,0.32); }
|
|
4379
|
+
.tool-group-body .bub { margin-bottom: 10px; }
|
|
4380
|
+
.tool-group-body .bub-content { max-width: 100%; }
|
|
4381
|
+
.tool-group-body .bub-avatar { width: 26px; height: 26px; flex-basis: 26px; border-radius: 8px; font-size: 12px; }
|
|
4282
4382
|
.bub-sublabel { font-size: 9px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--dim); margin: 4px 0 2px; }
|
|
4283
|
-
.bub-fold { margin-top:
|
|
4284
|
-
.bub-fold > summary { cursor: pointer; list-style: none; font-size: 11px; color: var(--cyan); padding:
|
|
4383
|
+
.bub-fold { margin-top: 0; }
|
|
4384
|
+
.bub-fold > summary { cursor: pointer; list-style: none; font-size: 11px; color: var(--cyan); padding: 6px 9px; background: var(--inset); border: 1px solid var(--border); border-radius: 8px; user-select: none; display: flex; align-items: center; gap: 6px; font-variant-numeric: tabular-nums; }
|
|
4285
4385
|
.bub-fold > summary::-webkit-details-marker { display: none; }
|
|
4286
4386
|
.bub-fold > summary::before { content: '\u25B8'; color: var(--dim); font-size: 9px; }
|
|
4287
4387
|
.bub-fold[open] > summary::before { content: '\u25BE'; }
|
|
4288
4388
|
.bub-fold > summary:hover { border-color: var(--accent); color: var(--text); }
|
|
4289
4389
|
.bub-fold[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
|
|
4290
|
-
.bub-fold > pre { margin: 0; border: 1px solid var(--border); border-top: none; border-radius: 0 0
|
|
4390
|
+
.bub-fold > pre { margin: 0; border: 1px solid var(--border); border-top: none; border-radius: 0 0 8px 8px; padding: 9px 10px; background: var(--bg2); }
|
|
4291
4391
|
.bub-argpre { color: var(--cyan); }
|
|
4392
|
+
.tool-output { border: 1px solid var(--border); border-top: none; border-radius: 0 0 8px 8px; background: var(--bg2); overflow: hidden; }
|
|
4393
|
+
.tool-output-head { display: flex; align-items: center; gap: 8px; padding: 6px 9px; border-bottom: 1px solid var(--border); color: var(--dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
4394
|
+
.tool-output-kind { color: var(--cyan); font-family: ui-monospace, "SF Mono", Menlo, monospace; font-weight: 800; }
|
|
4395
|
+
.tool-output-meta { margin-left: auto; font-variant-numeric: tabular-nums; }
|
|
4396
|
+
.tool-output pre { margin: 0; padding: 9px 10px; max-height: 380px; overflow: auto; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11.5px; line-height: 1.45; }
|
|
4397
|
+
.tool-output.error pre { color: var(--red); }
|
|
4398
|
+
.tool-output.bash pre { white-space: pre-wrap; word-break: break-word; }
|
|
4399
|
+
.tool-output.json pre, .tool-output.numbered pre { white-space: pre; }
|
|
4400
|
+
.tool-output-footer { display: flex; gap: 12px; border-top: 1px solid var(--border); padding: 5px 9px; background: rgba(255,255,255,0.02); color: var(--dim); font-size: 10.5px; font-variant-numeric: tabular-nums; }
|
|
4401
|
+
.tool-output-footer.bad { color: var(--red); }
|
|
4402
|
+
.line-view { display: flex; max-height: 380px; overflow: auto; }
|
|
4403
|
+
.line-gutter { flex: 0 0 auto; min-width: 38px; padding: 9px 8px; border-right: 1px solid var(--border); background: rgba(255,255,255,0.025); color: var(--dim); text-align: right; user-select: none; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11.5px; line-height: 1.45; white-space: pre; }
|
|
4404
|
+
.line-view pre { flex: 1; max-height: none; min-width: 0; }
|
|
4292
4405
|
.loading { color: var(--muted); font-style: italic; padding: 20px 0; }
|
|
4293
4406
|
|
|
4294
4407
|
/* Mailbox tab (demoted) */
|
|
@@ -4314,7 +4427,8 @@ var init_hq_dashboard_html = __esm({
|
|
|
4314
4427
|
/* shared data store (framework-agnostic) */
|
|
4315
4428
|
var Store = {
|
|
4316
4429
|
snapshot: null, connected: false, tab: 'console', theme: 'dark',
|
|
4317
|
-
selected: null, transcripts: {}, agentMsgs: {},
|
|
4430
|
+
selected: null, transcripts: {}, agentMsgs: {}, timeline: [], timelineSeen: {}, timelineLoaded: false,
|
|
4431
|
+
commands: [], commandsLoaded: false, commandsLoading: false, listeners: new Set(),
|
|
4318
4432
|
emit: function(){ this.listeners.forEach(function(l){ try { l(); } catch(e){} }); },
|
|
4319
4433
|
subscribe: function(l){ this.listeners.add(l); var s=this; return function(){ s.listeners.delete(l); }; },
|
|
4320
4434
|
set: function(p){ Object.assign(this, p); this.emit(); }
|
|
@@ -4328,6 +4442,106 @@ function fmtAgo(iso){ if(!iso) return ''; var d=new Date(iso).getTime(); if(isNa
|
|
|
4328
4442
|
function fmtElapsed(iso){ if(!iso) return ''; var d=new Date(iso).getTime(); if(isNaN(d)) return ''; var s=Math.max(0,Math.floor((Date.now()-d)/1000)); if(s<60) return s+'s'; var m=Math.floor(s/60); if(m<60) return m+'m '+(s%60)+'s'; var h=Math.floor(m/60); if(h<24) return h+'h '+(m%60)+'m'; return Math.floor(h/24)+'d '+(h%24)+'h'; }
|
|
4329
4443
|
function fmtNum(n){ n=Number(n)||0; if(n>=1e6) return (n/1e6).toFixed(1)+'M'; if(n>=1e3) return (n/1e3).toFixed(1)+'k'; return String(n); }
|
|
4330
4444
|
function esc(s){ if(s==null) return ''; return String(s); }
|
|
4445
|
+
function cacheKey(agentId, projectId, clientId){ return (projectId||'') + '||' + (clientId||'') + '||' + agentId; }
|
|
4446
|
+
function findSession(snap, sessionId){ return ((snap && snap.liveSessions) || []).filter(function(s){ return s.sessionId === sessionId; })[0] || null; }
|
|
4447
|
+
function findAgent(session, agentId){ return session ? ((session.agents||[]).filter(function(a){ return a.id === agentId; })[0] || null) : null; }
|
|
4448
|
+
function clientIdForSession(snap, session){
|
|
4449
|
+
if(!session) return '';
|
|
4450
|
+
if(session.clientId) return session.clientId;
|
|
4451
|
+
var clients = (snap && snap.clients) || [];
|
|
4452
|
+
for(var i=0;i<clients.length;i++){
|
|
4453
|
+
var c = clients[i];
|
|
4454
|
+
if(c.projectId === session.projectId && c.machineId === session.machineId && c.kind === session.clientKind && (!session.pid || !c.pid || c.pid === session.pid)) return c.clientId || '';
|
|
4455
|
+
}
|
|
4456
|
+
return '';
|
|
4457
|
+
}
|
|
4458
|
+
|
|
4459
|
+
function timelineKey(e){
|
|
4460
|
+
return [e.source||'', e.projectId||'', e.clientId||'', e.sessionId||'', e.agentId||'', e.ts||'', e.role||'', e.tool||'', String(e.text||'').slice(0,80)].join('|');
|
|
4461
|
+
}
|
|
4462
|
+
function addTimelineEntries(entries){
|
|
4463
|
+
if(!entries || !entries.length) return;
|
|
4464
|
+
var changed = false;
|
|
4465
|
+
for(var i=0;i<entries.length;i++){
|
|
4466
|
+
var e = entries[i];
|
|
4467
|
+
var key = timelineKey(e);
|
|
4468
|
+
if(Store.timelineSeen[key]) continue;
|
|
4469
|
+
Store.timelineSeen[key] = 1;
|
|
4470
|
+
Store.timeline.push(e);
|
|
4471
|
+
changed = true;
|
|
4472
|
+
}
|
|
4473
|
+
if(!changed) return;
|
|
4474
|
+
Store.timeline.sort(function(a,b){ return (Date.parse(b.ts||'')||0) - (Date.parse(a.ts||'')||0); });
|
|
4475
|
+
if(Store.timeline.length > 1500) Store.timeline = Store.timeline.slice(0, 1500);
|
|
4476
|
+
Store.emit();
|
|
4477
|
+
}
|
|
4478
|
+
function timelineEntriesFromEvent(ev){
|
|
4479
|
+
var out = [];
|
|
4480
|
+
if(!ev || !ev.payload) return out;
|
|
4481
|
+
if(ev.type === 'session.transcript' && ev.payload.sessionId && Array.isArray(ev.payload.entries)){
|
|
4482
|
+
ev.payload.entries.forEach(function(entry){
|
|
4483
|
+
out.push({
|
|
4484
|
+
source: 'session', projectId: ev.projectId, clientId: ev.clientId, sessionId: ev.payload.sessionId,
|
|
4485
|
+
agentId: entry.agentId || 'leader', agentName: entry.agentId || 'leader',
|
|
4486
|
+
ts: entry.ts || ev.timestamp, role: entry.role || 'assistant', tool: entry.tool, text: entry.text || ''
|
|
4487
|
+
});
|
|
4488
|
+
});
|
|
4489
|
+
} else if(ev.type === 'agent.message' && ev.payload.subagentId){
|
|
4490
|
+
out.push({
|
|
4491
|
+
source: 'agent', projectId: ev.projectId, clientId: ev.clientId, sessionId: ev.sessionId || null,
|
|
4492
|
+
agentId: ev.payload.subagentId, agentName: ev.payload.agentName || ev.payload.subagentId,
|
|
4493
|
+
ts: ev.payload.ts || ev.timestamp, role: agentMsgRole(ev.payload.kind), tool: ev.payload.toolName, text: ev.payload.content || ''
|
|
4494
|
+
});
|
|
4495
|
+
} else if(ev.type === 'agent.status' && ev.payload.subagentId){
|
|
4496
|
+
out.push({
|
|
4497
|
+
source: 'agent', projectId: ev.projectId, clientId: ev.clientId, sessionId: ev.sessionId || null,
|
|
4498
|
+
agentId: ev.payload.subagentId, agentName: ev.payload.agentName || ev.payload.subagentId,
|
|
4499
|
+
ts: ev.payload.ts || ev.timestamp, role: 'system', text: (ev.payload.status||'status') + (ev.payload.summary ? (': '+ev.payload.summary) : (ev.payload.task ? (': '+ev.payload.task) : ''))
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
return out;
|
|
4503
|
+
}
|
|
4504
|
+
function timelineAgentValue(projectId, clientId, agentId){ return [projectId||'', clientId||'', agentId||''].join('||'); }
|
|
4505
|
+
function parseTimelineAgentValue(value){
|
|
4506
|
+
if(!value || value === 'all') return null;
|
|
4507
|
+
var parts = String(value).split('||');
|
|
4508
|
+
return { projectId: parts[0] || '', clientId: parts[1] || '', agentId: parts[2] || '' };
|
|
4509
|
+
}
|
|
4510
|
+
function timelineFilterQuery(filters){
|
|
4511
|
+
var f = filters || {};
|
|
4512
|
+
var q = [];
|
|
4513
|
+
function add(k,v){ if(v && v !== 'all') q.push(encodeURIComponent(k) + '=' + encodeURIComponent(v)); }
|
|
4514
|
+
add('projectId', f.projectId);
|
|
4515
|
+
add('clientId', f.clientId);
|
|
4516
|
+
add('agentId', f.agentId);
|
|
4517
|
+
add('source', f.source);
|
|
4518
|
+
add('role', f.role);
|
|
4519
|
+
add('q', f.q);
|
|
4520
|
+
return q.length ? ('&' + q.join('&')) : '';
|
|
4521
|
+
}
|
|
4522
|
+
function loadRecentTimeline(filters, force){
|
|
4523
|
+
var f = typeof filters === 'string' ? { projectId: filters } : (filters || {});
|
|
4524
|
+
if(Store.timelineLoaded && !force) return;
|
|
4525
|
+
Store.timelineLoaded = true;
|
|
4526
|
+
var path = '/api/timeline?limit=800' + timelineFilterQuery(f);
|
|
4527
|
+
fetch(withTok(path))
|
|
4528
|
+
.then(function(r){ return r.ok ? r.json() : null; })
|
|
4529
|
+
.then(function(d){
|
|
4530
|
+
if(d && Array.isArray(d.entries)){
|
|
4531
|
+
addTimelineEntries(d.entries);
|
|
4532
|
+
return;
|
|
4533
|
+
}
|
|
4534
|
+
return fetch(withTok('/api/events?limit=800'))
|
|
4535
|
+
.then(function(r){ return r.ok ? r.json() : null; })
|
|
4536
|
+
.then(function(fallback){
|
|
4537
|
+
var events = fallback && Array.isArray(fallback.events) ? fallback.events : [];
|
|
4538
|
+
var batch = [];
|
|
4539
|
+
events.forEach(function(ev){ batch = batch.concat(timelineEntriesFromEvent(ev)); });
|
|
4540
|
+
addTimelineEntries(batch);
|
|
4541
|
+
});
|
|
4542
|
+
})
|
|
4543
|
+
.catch(function(){});
|
|
4544
|
+
}
|
|
4331
4545
|
|
|
4332
4546
|
function loadTranscript(sessionId){
|
|
4333
4547
|
if(!sessionId) return;
|
|
@@ -4349,17 +4563,24 @@ function applyThemeClass(t){ try { document.body.className = (t==='light'?'light
|
|
|
4349
4563
|
function initTheme(){ var t='dark'; try { t = localStorage.getItem('hq.theme') || 'dark'; } catch(e){} Store.theme = t; applyThemeClass(t); }
|
|
4350
4564
|
function toggleTheme(){ var t = (Store.theme==='light'?'dark':'light'); Store.theme = t; applyThemeClass(t); try { localStorage.setItem('hq.theme', t); } catch(e){} Store.emit(); }
|
|
4351
4565
|
|
|
4352
|
-
function loadAgentMessages(agentId){
|
|
4566
|
+
function loadAgentMessages(agentId, session){
|
|
4353
4567
|
if(!agentId) return;
|
|
4354
|
-
|
|
4568
|
+
var projectId = session && session.projectId ? session.projectId : '';
|
|
4569
|
+
var clientId = clientIdForSession(Store.snapshot, session);
|
|
4570
|
+
var key = cacheKey(agentId, projectId, clientId);
|
|
4571
|
+
var url = '/api/agents/'+encodeURIComponent(agentId)+'/messages?full=1';
|
|
4572
|
+
if(projectId) url += '&projectId=' + encodeURIComponent(projectId);
|
|
4573
|
+
if(clientId) url += '&clientId=' + encodeURIComponent(clientId);
|
|
4574
|
+
fetch(withTok(url))
|
|
4355
4575
|
.then(function(r){ return r.ok ? r.json() : null; })
|
|
4356
|
-
.then(function(d){ if(d && Array.isArray(d.entries)){ Store.agentMsgs[
|
|
4576
|
+
.then(function(d){ if(d && Array.isArray(d.entries)){ Store.agentMsgs[key] = d.entries; Store.emit(); } })
|
|
4357
4577
|
.catch(function(){});
|
|
4358
4578
|
}
|
|
4359
4579
|
|
|
4360
4580
|
function selectSession(sessionId, agentId){
|
|
4361
4581
|
Store.selected = { sessionId: sessionId, agentId: agentId || null };
|
|
4362
|
-
|
|
4582
|
+
var session = findSession(Store.snapshot, sessionId);
|
|
4583
|
+
if(agentId && agentId !== 'leader'){ loadAgentMessages(agentId, session); }
|
|
4363
4584
|
else { loadTranscript(sessionId); }
|
|
4364
4585
|
Store.emit();
|
|
4365
4586
|
}
|
|
@@ -4378,7 +4599,7 @@ function connectWs(){
|
|
|
4378
4599
|
if(reconnect){
|
|
4379
4600
|
var sel = Store.selected;
|
|
4380
4601
|
if(sel && sel.sessionId){
|
|
4381
|
-
if(sel.agentId && sel.agentId !== 'leader') loadAgentMessages(sel.agentId);
|
|
4602
|
+
if(sel.agentId && sel.agentId !== 'leader') loadAgentMessages(sel.agentId, findSession(Store.snapshot, sel.sessionId));
|
|
4382
4603
|
else loadTranscript(sel.sessionId);
|
|
4383
4604
|
}
|
|
4384
4605
|
}
|
|
@@ -4392,7 +4613,7 @@ function connectWs(){
|
|
|
4392
4613
|
ws.onerror = function(){ try { ws.close(); } catch(e){} };
|
|
4393
4614
|
}
|
|
4394
4615
|
|
|
4395
|
-
function agentMsgRole(kind){ return kind==='tool_use'?'tool' : kind==='error'?'error' : kind==='status'?'system' : 'assistant'; }
|
|
4616
|
+
function agentMsgRole(kind){ return (kind==='tool_use'||kind==='tool_result')?'tool' : kind==='error'?'error' : kind==='status'?'system' : 'assistant'; }
|
|
4396
4617
|
|
|
4397
4618
|
// Append streamed entries into a transcript cache, merging a tool RESULT
|
|
4398
4619
|
// (toolUseId, no args) into the matching args entry so a tool's call + result
|
|
@@ -4420,6 +4641,7 @@ function appendEntries(cache, news){
|
|
|
4420
4641
|
|
|
4421
4642
|
function handleEvent(ev){
|
|
4422
4643
|
if(!ev) return;
|
|
4644
|
+
addTimelineEntries(timelineEntriesFromEvent(ev));
|
|
4423
4645
|
if(ev.type === 'session.transcript' && ev.payload && ev.payload.sessionId){
|
|
4424
4646
|
var sid = ev.payload.sessionId;
|
|
4425
4647
|
var c = Store.transcripts[sid];
|
|
@@ -4433,15 +4655,17 @@ function handleEvent(ev){
|
|
|
4433
4655
|
// agent card id. Lets clicking a subagent show ITS own live history.
|
|
4434
4656
|
if(ev.type === 'agent.message' && ev.payload && ev.payload.subagentId){
|
|
4435
4657
|
var p = ev.payload;
|
|
4436
|
-
var
|
|
4658
|
+
var k = cacheKey(p.subagentId, ev.projectId, ev.clientId);
|
|
4659
|
+
var arr = Store.agentMsgs[k] || (Store.agentMsgs[k] = []);
|
|
4437
4660
|
arr.push({ ts: p.ts, role: agentMsgRole(p.kind), text: p.content || '', tool: p.toolName });
|
|
4438
|
-
if(arr.length > 4000) Store.agentMsgs[
|
|
4661
|
+
if(arr.length > 4000) Store.agentMsgs[k] = arr.slice(-4000);
|
|
4439
4662
|
Store.emit();
|
|
4440
4663
|
return;
|
|
4441
4664
|
}
|
|
4442
4665
|
if(ev.type === 'agent.status' && ev.payload && ev.payload.subagentId){
|
|
4443
4666
|
var sp = ev.payload;
|
|
4444
|
-
var
|
|
4667
|
+
var k2 = cacheKey(sp.subagentId, ev.projectId, ev.clientId);
|
|
4668
|
+
var a2 = Store.agentMsgs[k2] || (Store.agentMsgs[k2] = []);
|
|
4445
4669
|
a2.push({ ts: sp.ts, role: 'system', text: '\u2014 ' + (sp.status||'') + (sp.summary ? (': '+sp.summary) : (sp.task ? (': '+sp.task) : '')) });
|
|
4446
4670
|
Store.emit();
|
|
4447
4671
|
return;
|
|
@@ -4456,6 +4680,101 @@ function machineKey(hostname, machineId){
|
|
|
4456
4680
|
return hn ? ('host:' + hn.toLowerCase()) : ('mid:' + (machineId || 'local'));
|
|
4457
4681
|
}
|
|
4458
4682
|
|
|
4683
|
+
function postHqCommand(clientId, type, payload){
|
|
4684
|
+
return fetch(withTok('/api/command'), {
|
|
4685
|
+
method: 'POST',
|
|
4686
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4687
|
+
body: JSON.stringify({ clientId: clientId, type: type, payload: payload })
|
|
4688
|
+
}).then(function(r){
|
|
4689
|
+
return r.json().catch(function(){ return {}; }).then(function(body){
|
|
4690
|
+
if(!r.ok){ throw new Error(body && body.error ? body.error : ('HTTP ' + r.status)); }
|
|
4691
|
+
return body;
|
|
4692
|
+
});
|
|
4693
|
+
});
|
|
4694
|
+
}
|
|
4695
|
+
/* Direct mailbox write \u2014 used when the target has no live command-capable
|
|
4696
|
+
client. The prompt lands in the project mailbox regardless of whether any
|
|
4697
|
+
agent loop is running; the server resolves the projectRoot from sessionId/
|
|
4698
|
+
projectId, so no filesystem path leaves the browser. */
|
|
4699
|
+
function postHqMailboxSend(target, type, opts){
|
|
4700
|
+
return fetch(withTok('/api/mailbox-send'), {
|
|
4701
|
+
method: 'POST',
|
|
4702
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4703
|
+
body: JSON.stringify({
|
|
4704
|
+
sessionId: target.sessionId, projectId: target.projectId,
|
|
4705
|
+
type: type, to: target.to, subject: opts.subject, body: opts.body, priority: opts.priority
|
|
4706
|
+
})
|
|
4707
|
+
}).then(function(r){
|
|
4708
|
+
return r.json().catch(function(){ return {}; }).then(function(body){
|
|
4709
|
+
if(!r.ok){ throw new Error(body && body.error ? body.error : ('HTTP ' + r.status)); }
|
|
4710
|
+
return body;
|
|
4711
|
+
});
|
|
4712
|
+
});
|
|
4713
|
+
}
|
|
4714
|
+
function loadCommands(force){
|
|
4715
|
+
if(Store.commandsLoading) return;
|
|
4716
|
+
if(Store.commandsLoaded && !force) return;
|
|
4717
|
+
Store.commandsLoading = true;
|
|
4718
|
+
fetch(withTok('/api/commands?limit=80'))
|
|
4719
|
+
.then(function(r){ return r.ok ? r.json() : null; })
|
|
4720
|
+
.then(function(d){
|
|
4721
|
+
var commands = d && Array.isArray(d.commands) ? d.commands.slice() : [];
|
|
4722
|
+
commands.sort(function(a,b){ return (Date.parse(b.enqueuedAt||'')||0) - (Date.parse(a.enqueuedAt||'')||0); });
|
|
4723
|
+
Store.commands = commands;
|
|
4724
|
+
Store.commandsLoaded = true;
|
|
4725
|
+
Store.commandsLoading = false;
|
|
4726
|
+
Store.emit();
|
|
4727
|
+
})
|
|
4728
|
+
.catch(function(){ Store.commandsLoading = false; Store.commandsLoaded = true; Store.emit(); });
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
function buildCommandTargets(snap){
|
|
4732
|
+
var targets = [];
|
|
4733
|
+
var seenProjects = {};
|
|
4734
|
+
((snap && snap.liveSessions) || []).forEach(function(s){
|
|
4735
|
+
// A live client means the fast control-plane path (/api/command) is
|
|
4736
|
+
// available. Without one, targets still work via the direct mailbox
|
|
4737
|
+
// route (/api/mailbox-send) \u2014 the send simply lands in the project
|
|
4738
|
+
// mailbox and is picked up whenever an agent next runs. direct=true
|
|
4739
|
+
// marks those; the offline suffix hints at the fallback path.
|
|
4740
|
+
var clientId = clientIdForSession(snap, s);
|
|
4741
|
+
var direct = !clientId;
|
|
4742
|
+
var projName = s.projectName || s.projectId;
|
|
4743
|
+
var offlineSuffix = direct ? ' \xB7 (offline \u2192 mailbox)' : '';
|
|
4744
|
+
var projectKey = s.projectId + '|' + (clientId || 'nocli');
|
|
4745
|
+
if(!seenProjects[projectKey]){
|
|
4746
|
+
seenProjects[projectKey] = 1;
|
|
4747
|
+
targets.push({
|
|
4748
|
+
value: 'project|' + projectKey, kind: 'broadcast', clientId: clientId || null, direct: direct,
|
|
4749
|
+
to: 'all', sessionId: s.sessionId, projectId: s.projectId,
|
|
4750
|
+
label: 'Broadcast project \xB7 ' + projName + offlineSuffix
|
|
4751
|
+
});
|
|
4752
|
+
}
|
|
4753
|
+
targets.push({
|
|
4754
|
+
value: s.sessionId + '|leader', kind: 'agent', clientId: clientId || null, direct: direct,
|
|
4755
|
+
to: 'leader', sessionId: s.sessionId, projectId: s.projectId, agentId: 'leader',
|
|
4756
|
+
label: projName + ' \xB7 ' + (s.clientKind||'cli').toUpperCase() + ' \xB7 leader' + offlineSuffix
|
|
4757
|
+
});
|
|
4758
|
+
(s.agents||[]).forEach(function(a){
|
|
4759
|
+
if(a.id === 'leader') return;
|
|
4760
|
+
targets.push({
|
|
4761
|
+
value: s.sessionId + '|' + a.id, kind: 'agent', clientId: clientId || null, direct: direct,
|
|
4762
|
+
to: a.id, sessionId: s.sessionId, projectId: s.projectId, agentId: a.id,
|
|
4763
|
+
label: projName + ' \xB7 ' + (a.name || a.id) + offlineSuffix
|
|
4764
|
+
});
|
|
4765
|
+
});
|
|
4766
|
+
});
|
|
4767
|
+
return targets;
|
|
4768
|
+
}
|
|
4769
|
+
|
|
4770
|
+
function selectedTargetValue(snap){
|
|
4771
|
+
var sel = Store.selected;
|
|
4772
|
+
if(!sel || !sel.sessionId) return '';
|
|
4773
|
+
var session = findSession(snap, sel.sessionId);
|
|
4774
|
+
if(!session) return '';
|
|
4775
|
+
return sel.agentId ? (sel.sessionId + '|' + sel.agentId) : (sel.sessionId + '|leader');
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4459
4778
|
/* fleet tree (shared) */
|
|
4460
4779
|
function buildTree(snap){
|
|
4461
4780
|
var sessions = (snap && snap.liveSessions) || [];
|
|
@@ -4583,11 +4902,13 @@ function projNode(id, p, machine){
|
|
|
4583
4902
|
}
|
|
4584
4903
|
function termNode(id, t, sel){
|
|
4585
4904
|
var synthetic = !!t.synthetic;
|
|
4905
|
+
var agentCount = t.agentCount || (t.agents ? t.agents.length : 0);
|
|
4586
4906
|
return fleetNode(id, 'terminal', {
|
|
4587
4907
|
termKind: t.clientKind, status: t.status, icon: kindIcon(t.clientKind),
|
|
4588
4908
|
label: (t.clientKind||'cli').toUpperCase() + ' \xB7 ' + shortId(t.sessionId),
|
|
4589
4909
|
sub: t.status + (t.pid ? (' \xB7 pid '+t.pid) : '') + (synthetic ? ' \xB7 waiting for session telemetry' : ''),
|
|
4590
|
-
|
|
4910
|
+
agentCount: agentCount,
|
|
4911
|
+
chips: synthetic ? ['connected', '0 agents'] : [ agentCount+' agents' ],
|
|
4591
4912
|
clickable: !synthetic, selected: !synthetic && !!(sel && sel.sessionId === t.sessionId && !sel.agentId),
|
|
4592
4913
|
onClick: synthetic ? undefined : function(){ selectSession(t.sessionId); }
|
|
4593
4914
|
});
|
|
@@ -4603,6 +4924,7 @@ function agentNode(id, a, t, sel){
|
|
|
4603
4924
|
chips.push((a.iterations||0)+' it');
|
|
4604
4925
|
if(typeof a.costUsd === 'number' && a.costUsd > 0) chips.push('$'+a.costUsd.toFixed(2));
|
|
4605
4926
|
return fleetNode(id, 'agent', {
|
|
4927
|
+
parentTerminalId: 'terminal:' + t.sessionId,
|
|
4606
4928
|
status: a.status, label: a.name || a.id,
|
|
4607
4929
|
sub: a.status + (a.partialText ? (' \xB7 '+String(a.partialText).slice(0,44)) : ''),
|
|
4608
4930
|
chips: chips, clickable: true,
|
|
@@ -4613,21 +4935,94 @@ function agentNode(id, a, t, sel){
|
|
|
4613
4935
|
function kindIcon(k){ return k==='tui'?'\u{1F5B3}':k==='webui'?'\u{1F310}':k==='repl'?'\u2328\uFE0F':'\u25B7'; }
|
|
4614
4936
|
function miniColor(n){ var k=n.data&&n.data.kind; return k==='machine'?'#a371f7':k==='project'?'#58a6ff':k==='agent'?'#3fb950':'#39d0d8'; }
|
|
4615
4937
|
|
|
4616
|
-
// Node footprints (width \xD7 height) for
|
|
4617
|
-
|
|
4618
|
-
//
|
|
4938
|
+
// Node footprints (width \xD7 height) for graph sizing. Agents are compact because
|
|
4939
|
+
// subagent fleets can be large; they are clustered around their terminal rather
|
|
4940
|
+
// than laid out as a long tree rank.
|
|
4941
|
+
var NODE_SIZE = { machine: [220, 78], project: [220, 78], terminal: [220, 70], agent: [196, 78] };
|
|
4942
|
+
function agentGrid(count, dir){
|
|
4943
|
+
if(!count) return { cols: 0, rows: 0, width: 0, height: 0 };
|
|
4944
|
+
var cols = dir === 'TB'
|
|
4945
|
+
? Math.min(4, Math.max(1, Math.ceil(Math.sqrt(count))))
|
|
4946
|
+
: (count > 10 ? 4 : (count > 3 ? 2 : 1));
|
|
4947
|
+
var rows = Math.ceil(count / cols);
|
|
4948
|
+
var s = NODE_SIZE.agent;
|
|
4949
|
+
return { cols: cols, rows: rows, width: cols*s[0] + Math.max(0, cols-1)*22, height: rows*s[1] + Math.max(0, rows-1)*18 };
|
|
4950
|
+
}
|
|
4951
|
+
function actualNodeSize(n){ return NODE_SIZE[n.data.kind] || [210, 64]; }
|
|
4952
|
+
function graphNodeSize(n, dir){
|
|
4953
|
+
var base = actualNodeSize(n);
|
|
4954
|
+
if(n.data.kind !== 'terminal') return base;
|
|
4955
|
+
var count = n.data.agentCount || 0;
|
|
4956
|
+
if(!count) return base;
|
|
4957
|
+
var grid = agentGrid(count, dir);
|
|
4958
|
+
if(dir === 'TB') return [Math.max(base[0], grid.width), base[1] + 58 + grid.height];
|
|
4959
|
+
return [base[0] + 70 + grid.width, Math.max(base[1], grid.height)];
|
|
4960
|
+
}
|
|
4961
|
+
function terminalActualPosition(center, n, graphSize, actualSize, dir){
|
|
4962
|
+
if(n.data.kind !== 'terminal' || !(n.data.agentCount > 0)){
|
|
4963
|
+
return { x: center.x - actualSize[0]/2, y: center.y - actualSize[1]/2 };
|
|
4964
|
+
}
|
|
4965
|
+
if(dir === 'TB'){
|
|
4966
|
+
return { x: center.x - actualSize[0]/2, y: center.y - graphSize[1]/2 };
|
|
4967
|
+
}
|
|
4968
|
+
return { x: center.x - graphSize[0]/2, y: center.y - actualSize[1]/2 };
|
|
4969
|
+
}
|
|
4970
|
+
function positionAgentsAroundTerminals(nodes, positioned, dir){
|
|
4971
|
+
var byTerm = {};
|
|
4972
|
+
nodes.forEach(function(n){
|
|
4973
|
+
if(n.data.kind === 'agent' && n.data.parentTerminalId){
|
|
4974
|
+
(byTerm[n.data.parentTerminalId] || (byTerm[n.data.parentTerminalId] = [])).push(n);
|
|
4975
|
+
}
|
|
4976
|
+
});
|
|
4977
|
+
Object.keys(byTerm).forEach(function(termId){
|
|
4978
|
+
var term = positioned[termId]; if(!term) return;
|
|
4979
|
+
var agents = byTerm[termId];
|
|
4980
|
+
var grid = agentGrid(agents.length, dir);
|
|
4981
|
+
var termSize = actualNodeSize(term);
|
|
4982
|
+
var agentSize = NODE_SIZE.agent;
|
|
4983
|
+
var termCenter = { x: term.position.x + termSize[0]/2, y: term.position.y + termSize[1]/2 };
|
|
4984
|
+
var startX, startY;
|
|
4985
|
+
if(dir === 'TB'){
|
|
4986
|
+
startX = termCenter.x - grid.width/2;
|
|
4987
|
+
startY = term.position.y + termSize[1] + 58;
|
|
4988
|
+
} else {
|
|
4989
|
+
startX = term.position.x + termSize[0] + 70;
|
|
4990
|
+
startY = termCenter.y - grid.height/2;
|
|
4991
|
+
}
|
|
4992
|
+
agents.forEach(function(agent, index){
|
|
4993
|
+
var col = index % grid.cols;
|
|
4994
|
+
var row = Math.floor(index / grid.cols);
|
|
4995
|
+
positioned[agent.id] = Object.assign({}, agent, {
|
|
4996
|
+
position: {
|
|
4997
|
+
x: startX + col * (agentSize[0] + 22),
|
|
4998
|
+
y: startY + row * (agentSize[1] + 18)
|
|
4999
|
+
}
|
|
5000
|
+
});
|
|
5001
|
+
});
|
|
5002
|
+
});
|
|
5003
|
+
}
|
|
5004
|
+
// Auto-layout the logical nodes into a clean spine, then cluster agents around
|
|
5005
|
+
// the terminal they belong to so large fleets stay readable.
|
|
4619
5006
|
function layoutTree(nodes, edges, dir, dagre){
|
|
4620
5007
|
if(!dagre || !nodes.length) return nodes;
|
|
4621
5008
|
var g = new dagre.graphlib.Graph();
|
|
4622
5009
|
g.setGraph({ rankdir: dir || 'LR', nodesep: 26, ranksep: 90, marginx: 30, marginy: 30, ranker: 'tight-tree' });
|
|
4623
5010
|
g.setDefaultEdgeLabel(function(){ return {}; });
|
|
4624
|
-
nodes.
|
|
4625
|
-
|
|
5011
|
+
var spine = nodes.filter(function(n){ return n.data.kind !== 'agent'; });
|
|
5012
|
+
spine.forEach(function(n){ var s = graphNodeSize(n, dir); g.setNode(n.id, { width: s[0], height: s[1] }); });
|
|
5013
|
+
edges.forEach(function(e){
|
|
5014
|
+
if(g.hasNode(e.source) && g.hasNode(e.target)) g.setEdge(e.source, e.target);
|
|
5015
|
+
});
|
|
4626
5016
|
dagre.layout(g);
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
5017
|
+
var positioned = {};
|
|
5018
|
+
spine.forEach(function(n){
|
|
5019
|
+
var p = g.node(n.id); if(!p){ positioned[n.id] = n; return; }
|
|
5020
|
+
var actual = actualNodeSize(n);
|
|
5021
|
+
var graph = graphNodeSize(n, dir);
|
|
5022
|
+
positioned[n.id] = Object.assign({}, n, { position: terminalActualPosition(p, n, graph, actual, dir) });
|
|
4630
5023
|
});
|
|
5024
|
+
positionAgentsAroundTerminals(nodes, positioned, dir);
|
|
5025
|
+
return nodes.map(function(n){ return positioned[n.id] || n; });
|
|
4631
5026
|
}
|
|
4632
5027
|
|
|
4633
5028
|
/* React app (preferred) */
|
|
@@ -4715,24 +5110,505 @@ async function boot(){
|
|
|
4715
5110
|
h('pre', { className: preClass || null }, content)
|
|
4716
5111
|
);
|
|
4717
5112
|
}
|
|
5113
|
+
function foldContent(key, summary, child){
|
|
5114
|
+
return h('details', { key: key, className: 'bub-fold' },
|
|
5115
|
+
h('summary', null, summary),
|
|
5116
|
+
child
|
|
5117
|
+
);
|
|
5118
|
+
}
|
|
5119
|
+
function roleIcon(role){
|
|
5120
|
+
if(role === 'user') return '\u{1F464}';
|
|
5121
|
+
if(role === 'tool') return '\u2318';
|
|
5122
|
+
if(role === 'error') return '!';
|
|
5123
|
+
if(role === 'system') return 'i';
|
|
5124
|
+
return '\u{1F916}';
|
|
5125
|
+
}
|
|
5126
|
+
function roleLabel(role){
|
|
5127
|
+
if(role === 'user') return 'You';
|
|
5128
|
+
if(role === 'tool') return 'Tool';
|
|
5129
|
+
if(role === 'error') return 'Error';
|
|
5130
|
+
if(role === 'system') return 'System';
|
|
5131
|
+
return 'Assistant';
|
|
5132
|
+
}
|
|
5133
|
+
function countLines(text){
|
|
5134
|
+
if(!text) return 0;
|
|
5135
|
+
return String(text).split('\\n').length;
|
|
5136
|
+
}
|
|
5137
|
+
function toolSummary(e){
|
|
5138
|
+
var parts = [];
|
|
5139
|
+
if(e.toolInput) parts.push('input ' + e.toolInput.length + ' chars');
|
|
5140
|
+
if(e.text) parts.push(countLines(e.text) + ' lines');
|
|
5141
|
+
if(e.durationMs!=null) parts.push(e.durationMs + 'ms');
|
|
5142
|
+
return parts.join(' \xB7 ');
|
|
5143
|
+
}
|
|
5144
|
+
function detectToolShape(tool, result){
|
|
5145
|
+
var text = String(result || '');
|
|
5146
|
+
var trimmed = text.trim();
|
|
5147
|
+
function numberedLine(line){
|
|
5148
|
+
var t = String(line || '').trim();
|
|
5149
|
+
var arrow = t.indexOf('\u2192');
|
|
5150
|
+
if(arrow <= 0) return false;
|
|
5151
|
+
var n = t.slice(0, arrow).trim();
|
|
5152
|
+
if(!n) return false;
|
|
5153
|
+
for(var i=0;i<n.length;i++){ var c = n.charCodeAt(i); if(c < 48 || c > 57) return false; }
|
|
5154
|
+
return true;
|
|
5155
|
+
}
|
|
5156
|
+
if(text.split('\\n').slice(0, 8).some(numberedLine)) return { kind: 'numbered', text: text };
|
|
5157
|
+
if((trimmed.indexOf('{') === 0 && trimmed.lastIndexOf('}') === trimmed.length - 1) || (trimmed.indexOf('[') === 0 && trimmed.lastIndexOf(']') === trimmed.length - 1)){
|
|
5158
|
+
try { return { kind: 'json', text: JSON.stringify(JSON.parse(trimmed), null, 2) }; } catch(_e){}
|
|
5159
|
+
}
|
|
5160
|
+
var toolLower = String(tool || '').toLowerCase();
|
|
5161
|
+
var isBash = ['bash','shell','exec','run','tsc','pnpm','npm','yarn'].some(function(prefix){ return toolLower.indexOf(prefix) === 0; });
|
|
5162
|
+
var lines = text.split('\\n');
|
|
5163
|
+
var last = lines.length ? String(lines[lines.length - 1] || '').trim().toLowerCase() : '';
|
|
5164
|
+
var exitCode = null;
|
|
5165
|
+
if(last.indexOf('exit') === 0 || last.indexOf('[exit') === 0){
|
|
5166
|
+
var digits = '';
|
|
5167
|
+
for(var j=0;j<last.length;j++){ var ch = last.charCodeAt(j); if(ch >= 48 && ch <= 57) digits += last[j]; }
|
|
5168
|
+
if(digits) exitCode = Number(digits);
|
|
5169
|
+
}
|
|
5170
|
+
if(isBash || exitCode !== null){
|
|
5171
|
+
return { kind: 'bash', text: exitCode !== null ? lines.slice(0, -1).join('\\n').trimEnd() : text, exitCode: exitCode };
|
|
5172
|
+
}
|
|
5173
|
+
return { kind: 'plain', text: text };
|
|
5174
|
+
}
|
|
5175
|
+
function renderLineView(text, numbered){
|
|
5176
|
+
var lines = String(text || '').split('\\n');
|
|
5177
|
+
if(!numbered) return h('pre', null, text || '');
|
|
5178
|
+
return h('div', { className: 'line-view' },
|
|
5179
|
+
h('pre', { className: 'line-gutter', 'aria-hidden': true }, lines.map(function(_l, i){ return String(i + 1); }).join('\\n')),
|
|
5180
|
+
h('pre', null, text || '')
|
|
5181
|
+
);
|
|
5182
|
+
}
|
|
5183
|
+
function renderToolOutput(e){
|
|
5184
|
+
var shape = detectToolShape(e.tool, e.text);
|
|
5185
|
+
var lineCount = countLines(shape.text);
|
|
5186
|
+
var cls = 'tool-output ' + shape.kind + (e.isError || e.role === 'error' ? ' error' : '');
|
|
5187
|
+
var footer = null;
|
|
5188
|
+
if(shape.kind === 'bash' && (shape.exitCode !== null || e.durationMs != null)){
|
|
5189
|
+
footer = h('div', { className: 'tool-output-footer ' + (shape.exitCode && shape.exitCode !== 0 ? 'bad' : '') },
|
|
5190
|
+
shape.exitCode !== null ? h('span', null, 'exit code ', h('strong', null, String(shape.exitCode))) : null,
|
|
5191
|
+
e.durationMs != null ? h('span', null, e.durationMs + 'ms') : null
|
|
5192
|
+
);
|
|
5193
|
+
}
|
|
5194
|
+
return h('div', { className: cls },
|
|
5195
|
+
h('div', { className: 'tool-output-head' },
|
|
5196
|
+
h('span', { className: 'tool-output-kind' }, shape.kind === 'plain' ? 'output' : shape.kind),
|
|
5197
|
+
h('span', { className: 'tool-output-meta' }, lineCount + ' lines')
|
|
5198
|
+
),
|
|
5199
|
+
renderLineView(shape.text, shape.kind === 'numbered' || shape.kind === 'bash'),
|
|
5200
|
+
footer
|
|
5201
|
+
);
|
|
5202
|
+
}
|
|
5203
|
+
function renderInlineText(text, keyBase){
|
|
5204
|
+
var s = String(text || '');
|
|
5205
|
+
var bt = String.fromCharCode(96);
|
|
5206
|
+
var out = [];
|
|
5207
|
+
var pos = 0, n = 0;
|
|
5208
|
+
while(pos < s.length){
|
|
5209
|
+
var start = s.indexOf(bt, pos);
|
|
5210
|
+
if(start < 0){ out.push(s.slice(pos)); break; }
|
|
5211
|
+
var end = s.indexOf(bt, start + 1);
|
|
5212
|
+
if(end < 0){ out.push(s.slice(pos)); break; }
|
|
5213
|
+
if(start > pos) out.push(s.slice(pos, start));
|
|
5214
|
+
out.push(h('code', { key: keyBase + '-ic-' + n++, className: 'md-inline' }, s.slice(start + 1, end)));
|
|
5215
|
+
pos = end + 1;
|
|
5216
|
+
}
|
|
5217
|
+
return out.length ? out : s;
|
|
5218
|
+
}
|
|
5219
|
+
function renderMessageText(text, role){
|
|
5220
|
+
var raw = String(text || '');
|
|
5221
|
+
if(!raw) return h('div', { className: 'msg-md empty' }, '');
|
|
5222
|
+
var fence = String.fromCharCode(96) + String.fromCharCode(96) + String.fromCharCode(96);
|
|
5223
|
+
var lines = raw.split('\\n');
|
|
5224
|
+
var blocks = [];
|
|
5225
|
+
var para = [];
|
|
5226
|
+
var code = [];
|
|
5227
|
+
var lang = '';
|
|
5228
|
+
var inCode = false;
|
|
5229
|
+
function flushPara(){
|
|
5230
|
+
if(!para.length) return;
|
|
5231
|
+
var content = para.join(' ');
|
|
5232
|
+
blocks.push(h('p', { key: 'p-' + blocks.length }, renderInlineText(content, 'p-' + blocks.length)));
|
|
5233
|
+
para = [];
|
|
5234
|
+
}
|
|
5235
|
+
function flushCode(){
|
|
5236
|
+
blocks.push(h('div', { key: 'c-' + blocks.length, className: 'md-codewrap' },
|
|
5237
|
+
h('div', { className: 'md-codelabel' }, lang || 'code'),
|
|
5238
|
+
h('pre', { className: 'md-code' }, code.join('\\n'))
|
|
5239
|
+
));
|
|
5240
|
+
code = [];
|
|
5241
|
+
lang = '';
|
|
5242
|
+
}
|
|
5243
|
+
for(var i=0;i<lines.length;i++){
|
|
5244
|
+
var line = lines[i];
|
|
5245
|
+
if(line.slice(0,3) === fence){
|
|
5246
|
+
if(inCode){ flushCode(); inCode = false; }
|
|
5247
|
+
else { flushPara(); inCode = true; lang = line.slice(3).trim(); code = []; }
|
|
5248
|
+
continue;
|
|
5249
|
+
}
|
|
5250
|
+
if(inCode){ code.push(line); continue; }
|
|
5251
|
+
if(!line.trim()){ flushPara(); continue; }
|
|
5252
|
+
if(line.indexOf('- ') === 0 || line.indexOf('* ') === 0){
|
|
5253
|
+
flushPara();
|
|
5254
|
+
blocks.push(h('div', { key: 'b-' + blocks.length, className: 'md-bullet' },
|
|
5255
|
+
h('span', { className: 'md-bullet-dot' }, '\u2022'),
|
|
5256
|
+
h('span', null, renderInlineText(line.slice(2).trim(), 'b-' + blocks.length))
|
|
5257
|
+
));
|
|
5258
|
+
continue;
|
|
5259
|
+
}
|
|
5260
|
+
if(line.indexOf('# ') === 0 || line.indexOf('## ') === 0 || line.indexOf('### ') === 0){
|
|
5261
|
+
flushPara();
|
|
5262
|
+
blocks.push(h('div', { key: 'h-' + blocks.length, className: 'md-heading' }, line.replace(/^#+\\s*/, '')));
|
|
5263
|
+
continue;
|
|
5264
|
+
}
|
|
5265
|
+
para.push(line);
|
|
5266
|
+
}
|
|
5267
|
+
if(inCode) flushCode();
|
|
5268
|
+
flushPara();
|
|
5269
|
+
return h('div', { className: (role === 'assistant' ? 'assistant-body ' : '') + 'msg-md' }, blocks);
|
|
5270
|
+
}
|
|
4718
5271
|
function Bubble(p){
|
|
4719
5272
|
var e = p.e;
|
|
4720
5273
|
var isToolish = e.role === 'tool' || e.role === 'error';
|
|
4721
5274
|
var bodyEls = [];
|
|
4722
5275
|
if(isToolish){
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
5276
|
+
bodyEls.push(h('div', { key: 'th', className: 'tool-head' },
|
|
5277
|
+
h('span', { className: 'tool-name' }, e.tool || 'tool'),
|
|
5278
|
+
h('span', { className: 'tool-duration' }, toolSummary(e)),
|
|
5279
|
+
h('span', { className: 'tool-status ' + (e.isError || e.role === 'error' ? 'error' : '') }, e.isError || e.role === 'error' ? 'failed' : 'done')
|
|
5280
|
+
));
|
|
5281
|
+
if(e.toolInput){ bodyEls.push(fold('a', 'Input \xB7 ' + e.toolInput.length + ' chars', e.toolInput, 'bub-argpre')); }
|
|
5282
|
+
if(e.text){ bodyEls.push(foldContent('o', (e.isError||e.role==='error'?'Error output':'Output') + ' \xB7 ' + countLines(e.text) + ' lines', renderToolOutput(e))); }
|
|
5283
|
+
if(!e.toolInput && !e.text){ bodyEls.push(h('div', { key:'n', className:'bub-sublabel' }, 'no output')); }
|
|
4727
5284
|
} else {
|
|
4728
|
-
bodyEls.push(h('div', { key:'t', className: 'txt' }, e.text || ''));
|
|
5285
|
+
bodyEls.push(h('div', { key:'t', className: 'txt' }, renderMessageText(e.text || '', e.role)));
|
|
4729
5286
|
}
|
|
4730
5287
|
return h('div', { className: 'bub ' + e.role },
|
|
4731
|
-
h('div', { className: 'bub-
|
|
4732
|
-
|
|
4733
|
-
h('
|
|
5288
|
+
h('div', { className: 'bub-avatar' }, roleIcon(e.role)),
|
|
5289
|
+
h('div', { className: 'bub-content' },
|
|
5290
|
+
h('div', { className: 'bub-head' },
|
|
5291
|
+
h('span', { className: 'bub-role' }, isToolish && e.tool ? e.tool : roleLabel(e.role)),
|
|
5292
|
+
h('span', null, fmtTime(e.ts))
|
|
5293
|
+
),
|
|
5294
|
+
h('div', { className: 'bub-card' },
|
|
5295
|
+
isToolish ? h('div', { className: 'tool-card' }, bodyEls) : bodyEls
|
|
5296
|
+
)
|
|
4734
5297
|
),
|
|
4735
|
-
|
|
5298
|
+
);
|
|
5299
|
+
}
|
|
5300
|
+
function isToolEntry(e){ return e && (e.role === 'tool' || e.role === 'error'); }
|
|
5301
|
+
function ToolGroup(p){
|
|
5302
|
+
var tools = p.tools || [];
|
|
5303
|
+
var names = [];
|
|
5304
|
+
var seen = {};
|
|
5305
|
+
var errored = false;
|
|
5306
|
+
var totalMs = 0;
|
|
5307
|
+
tools.forEach(function(t){
|
|
5308
|
+
if(t.tool && !seen[t.tool]){ seen[t.tool] = 1; names.push(t.tool); }
|
|
5309
|
+
if(t.isError || t.role === 'error') errored = true;
|
|
5310
|
+
if(typeof t.durationMs === 'number') totalMs += t.durationMs;
|
|
5311
|
+
});
|
|
5312
|
+
var preview = names.slice(0, 4).join(', ') + (names.length > 4 ? ' +' + (names.length - 4) : '');
|
|
5313
|
+
var meta = preview || tools.map(function(t){ return t.role; }).join(', ');
|
|
5314
|
+
if(totalMs > 0) meta += ' \xB7 ' + totalMs + 'ms';
|
|
5315
|
+
return h('details', { className: 'tool-group', open: p.defaultOpen ? true : undefined },
|
|
5316
|
+
h('summary', null,
|
|
5317
|
+
h('span', { className: 'tool-group-title' }, tools.length + ' tool calls'),
|
|
5318
|
+
h('span', { className: 'tool-group-meta' }, meta),
|
|
5319
|
+
h('span', { className: 'tool-group-status ' + (errored ? 'error' : '') }, errored ? 'failed' : 'done')
|
|
5320
|
+
),
|
|
5321
|
+
h('div', { className: 'tool-group-body' },
|
|
5322
|
+
tools.map(function(t, i){ return h(Bubble, { key: i, e: t }); })
|
|
5323
|
+
)
|
|
5324
|
+
);
|
|
5325
|
+
}
|
|
5326
|
+
|
|
5327
|
+
function PromptDock(p){
|
|
5328
|
+
var targets = buildCommandTargets(p.snap);
|
|
5329
|
+
var preferred = selectedTargetValue(p.snap) || (targets[0] && targets[0].value) || '';
|
|
5330
|
+
var tvState = React.useState(preferred); var targetValue = tvState[0], setTargetValue = tvState[1];
|
|
5331
|
+
var textState = React.useState(''); var text = textState[0], setText = textState[1];
|
|
5332
|
+
var sendState = React.useState(false); var sending = sendState[0], setSending = sendState[1];
|
|
5333
|
+
var stState = React.useState({ text: '', cls: '' }); var status = stState[0], setStatus = stState[1];
|
|
5334
|
+
var typeState = React.useState('steer'); var sendType = typeState[0], setSendType = typeState[1];
|
|
5335
|
+
React.useEffect(function(){ if(preferred) setTargetValue(preferred); }, [preferred]);
|
|
5336
|
+
var target = targets.filter(function(t){ return t.value === targetValue; })[0] || targets[0] || null;
|
|
5337
|
+
// Broadcast targets always fan out project-wide; agent targets use the
|
|
5338
|
+
// selected send-type (steer / btw / queue). All types are written to the
|
|
5339
|
+
// project mailbox by the client dispatcher, so they land even when no
|
|
5340
|
+
// agent loop is actively running. HQ prompts are raw and bypass prompt
|
|
5341
|
+
// refinement \u2014 a steer/btw/queue is already a directive, not user input.
|
|
5342
|
+
var effectiveType = target && target.kind === 'broadcast' ? 'broadcast' : sendType;
|
|
5343
|
+
// Derive the subject from the send-type so the two never drift (a steer
|
|
5344
|
+
// must not carry a "queue"-flavored subject). Single source of truth,
|
|
5345
|
+
// computed alongside effectiveType.
|
|
5346
|
+
var effectiveSubject =
|
|
5347
|
+
effectiveType === 'steer' ? 'HQ steer' :
|
|
5348
|
+
effectiveType === 'btw' ? 'HQ note' :
|
|
5349
|
+
effectiveType === 'broadcast' ? 'HQ broadcast' :
|
|
5350
|
+
'HQ prompt'; // queue (and any future fallthrough)
|
|
5351
|
+
function submit(){
|
|
5352
|
+
if(!target || sending) return;
|
|
5353
|
+
var body = text.trim();
|
|
5354
|
+
if(!body){ setStatus({ text: 'Write a prompt or note first.', cls: 'err' }); return; }
|
|
5355
|
+
setSending(true);
|
|
5356
|
+
// When the target has a live command-capable client, go through the
|
|
5357
|
+
// control plane (/api/command). Otherwise fall back to a direct mailbox
|
|
5358
|
+
// write (/api/mailbox-send) so the prompt still goes out immediately \u2014
|
|
5359
|
+
// it lands in the project mailbox and the next agent to run picks it up.
|
|
5360
|
+
var useDirect = target.direct || !target.clientId;
|
|
5361
|
+
setStatus({ text: useDirect ? 'Delivering to mailbox\u2026' : 'Queueing command\u2026', cls: '' });
|
|
5362
|
+
var sendPromise;
|
|
5363
|
+
if(useDirect){
|
|
5364
|
+
sendPromise = postHqMailboxSend(target, effectiveType, { subject: effectiveSubject, body: body, priority: 'high' });
|
|
5365
|
+
} else {
|
|
5366
|
+
var payload = target.kind === 'broadcast'
|
|
5367
|
+
? { subject: effectiveSubject, body: body, priority: 'high' }
|
|
5368
|
+
: { to: target.to, subject: effectiveSubject, body: body, priority: 'high' };
|
|
5369
|
+
sendPromise = postHqCommand(target.clientId, effectiveType, payload);
|
|
5370
|
+
}
|
|
5371
|
+
sendPromise
|
|
5372
|
+
.then(function(res){
|
|
5373
|
+
setText('');
|
|
5374
|
+
var okMsg = useDirect
|
|
5375
|
+
? 'Delivered to mailbox (' + (res.messageId ? shortId(res.messageId) : 'ok') + ') for ' + target.label
|
|
5376
|
+
: 'Queued ' + (res.commandId ? shortId(res.commandId) : 'command') + ' for ' + target.label;
|
|
5377
|
+
setStatus({ text: okMsg, cls: 'ok' });
|
|
5378
|
+
loadCommands(true);
|
|
5379
|
+
setTimeout(function(){ loadCommands(true); }, 2500);
|
|
5380
|
+
})
|
|
5381
|
+
.catch(function(err){ setStatus({ text: err && err.message ? err.message : String(err), cls: 'err' }); })
|
|
5382
|
+
.finally(function(){ setSending(false); });
|
|
5383
|
+
}
|
|
5384
|
+
var isBroadcast = target && target.kind === 'broadcast';
|
|
5385
|
+
var typeOptions = [
|
|
5386
|
+
{ value: 'steer', label: 'Steer' },
|
|
5387
|
+
{ value: 'btw', label: 'BTW' },
|
|
5388
|
+
{ value: 'queue', label: 'Queue' }
|
|
5389
|
+
];
|
|
5390
|
+
var sendLabel = isBroadcast ? 'Broadcast'
|
|
5391
|
+
: effectiveType === 'btw' ? 'Send BTW'
|
|
5392
|
+
: effectiveType === 'queue' ? 'Queue'
|
|
5393
|
+
: 'Send steer';
|
|
5394
|
+
return h('div', { className: 'cmd-dock' },
|
|
5395
|
+
h('div', { className: 'cmd-row' },
|
|
5396
|
+
h('select', { className: 'cmd-target', value: target ? target.value : '', disabled: !targets.length || sending, onChange: function(e){ setTargetValue(e.target.value); } },
|
|
5397
|
+
targets.length ? targets.map(function(t){ return h('option', { key: t.value, value: t.value }, t.label); }) : h('option', { value: '' }, 'No command-capable client')
|
|
5398
|
+
),
|
|
5399
|
+
h('select', {
|
|
5400
|
+
className: 'cmd-type',
|
|
5401
|
+
value: isBroadcast ? 'broadcast' : sendType,
|
|
5402
|
+
disabled: !target || sending || isBroadcast,
|
|
5403
|
+
title: isBroadcast ? 'Broadcast fans out to the whole project' : 'How the prompt reaches the agent',
|
|
5404
|
+
onChange: function(e){ setSendType(e.target.value); }
|
|
5405
|
+
},
|
|
5406
|
+
isBroadcast
|
|
5407
|
+
? h('option', { value: 'broadcast' }, 'Broadcast')
|
|
5408
|
+
: typeOptions.map(function(o){ return h('option', { key: o.value, value: o.value }, o.label); })
|
|
5409
|
+
),
|
|
5410
|
+
h('textarea', {
|
|
5411
|
+
className: 'cmd-text',
|
|
5412
|
+
value: text,
|
|
5413
|
+
disabled: !target || sending,
|
|
5414
|
+
placeholder: isBroadcast ? 'Broadcast a prompt to this project\u2026' : 'Send a prompt to the selected agent\u2026',
|
|
5415
|
+
onChange: function(e){ setText(e.target.value); },
|
|
5416
|
+
onKeyDown: function(e){ if((e.ctrlKey || e.metaKey) && e.key === 'Enter'){ e.preventDefault(); submit(); } }
|
|
5417
|
+
}),
|
|
5418
|
+
h('button', { className: 'cmd-send', disabled: !target || sending || !text.trim(), onClick: submit }, sending ? 'Sending\u2026' : sendLabel)
|
|
5419
|
+
),
|
|
5420
|
+
h('div', { className: 'cmd-status ' + (status.cls||'') }, status.text || 'Ctrl+Enter sends raw (no refine) via the project mailbox. Steer = change course now \xB7 BTW = FYI context \xB7 Queue = waits its turn.')
|
|
5421
|
+
);
|
|
5422
|
+
}
|
|
5423
|
+
|
|
5424
|
+
function clientLabelForCommand(snap, clientId){
|
|
5425
|
+
var clients = (snap && snap.clients) || [];
|
|
5426
|
+
for(var i=0;i<clients.length;i++){
|
|
5427
|
+
var c = clients[i];
|
|
5428
|
+
if(c.clientId === clientId){
|
|
5429
|
+
var project = projectNameFor(snap, c.projectId);
|
|
5430
|
+
return project + ' \xB7 ' + String(c.kind || 'cli').toUpperCase();
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
return shortId(clientId);
|
|
5434
|
+
}
|
|
5435
|
+
function commandStatus(c){
|
|
5436
|
+
return c.ackStatus || c.status || 'queued';
|
|
5437
|
+
}
|
|
5438
|
+
function CommandHistory(p){
|
|
5439
|
+
React.useEffect(function(){
|
|
5440
|
+
loadCommands(false);
|
|
5441
|
+
var id = setInterval(function(){ loadCommands(true); }, 4000);
|
|
5442
|
+
return function(){ clearInterval(id); };
|
|
5443
|
+
}, []);
|
|
5444
|
+
var rows = (Store.commands || []).slice(0, 8);
|
|
5445
|
+
return h('div', { className: 'cmd-history' },
|
|
5446
|
+
h('div', { className: 'cmd-hhead' },
|
|
5447
|
+
h('div', { className: 'cmd-htitle' }, 'Control Queue'),
|
|
5448
|
+
h('div', { className: 'cmd-hnote' }, rows.length ? 'recent HQ commands and acknowledgements' : (Store.commandsLoading ? 'loading command audit' : 'no commands queued yet')),
|
|
5449
|
+
h('button', { className: 'cmd-hrefresh', onClick: function(){ loadCommands(true); } }, 'Refresh')
|
|
5450
|
+
),
|
|
5451
|
+
rows.length ? h('div', { className: 'cmd-hlist' },
|
|
5452
|
+
rows.map(function(c){
|
|
5453
|
+
var st = commandStatus(c);
|
|
5454
|
+
return h('div', { key: c.commandId, className: 'cmd-hrow' },
|
|
5455
|
+
h('div', { className: 'cmd-htype' }, c.type || 'command'),
|
|
5456
|
+
h('div', { className: 'cmd-hstatus ' + st }, st),
|
|
5457
|
+
h('div', { className: 'cmd-hclient' }, clientLabelForCommand(p.snap, c.clientId)),
|
|
5458
|
+
h('div', { className: 'cmd-hmsg', title: c.ackMessage || c.commandId }, c.ackMessage || fmtAgo(c.ackedAt || c.enqueuedAt))
|
|
5459
|
+
);
|
|
5460
|
+
})
|
|
5461
|
+
) : h('div', { className: 'side-empty' }, Store.commandsLoading ? 'Loading command history\u2026' : 'No HQ control commands have been queued in this server process.')
|
|
5462
|
+
);
|
|
5463
|
+
}
|
|
5464
|
+
|
|
5465
|
+
function projectNameFor(snap, projectId){
|
|
5466
|
+
var p = ((snap && snap.projects) || []).filter(function(x){ return x.projectId === projectId; })[0];
|
|
5467
|
+
if(p) return p.projectName || p.projectId;
|
|
5468
|
+
var s = ((snap && snap.liveSessions) || []).filter(function(x){ return x.projectId === projectId; })[0];
|
|
5469
|
+
return s ? (s.projectName || s.projectId) : (projectId || 'unknown');
|
|
5470
|
+
}
|
|
5471
|
+
function sessionForTimelineEntry(snap, e){
|
|
5472
|
+
if(e.sessionId){
|
|
5473
|
+
var exact = findSession(snap, e.sessionId);
|
|
5474
|
+
if(exact) return exact;
|
|
5475
|
+
}
|
|
5476
|
+
var sessions = (snap && snap.liveSessions) || [];
|
|
5477
|
+
for(var i=0;i<sessions.length;i++){
|
|
5478
|
+
var s = sessions[i];
|
|
5479
|
+
if(s.projectId === e.projectId && clientIdForSession(snap, s) === e.clientId){
|
|
5480
|
+
if(!e.agentId || (s.agents||[]).some(function(a){ return a.id === e.agentId; })) return s;
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
return null;
|
|
5484
|
+
}
|
|
5485
|
+
function agentNameForEntry(snap, e){
|
|
5486
|
+
var session = sessionForTimelineEntry(snap, e);
|
|
5487
|
+
var ag = session && e.agentId ? findAgent(session, e.agentId) : null;
|
|
5488
|
+
return (ag && (ag.name || ag.id)) || e.agentName || e.agentId || 'leader';
|
|
5489
|
+
}
|
|
5490
|
+
function timelinePreviewText(e){
|
|
5491
|
+
var text = String(e.text || '');
|
|
5492
|
+
if(e.role === 'tool'){
|
|
5493
|
+
var lines = text.split('\\n');
|
|
5494
|
+
return (lines[0] || 'tool output') + (lines.length > 1 ? (' +' + (lines.length - 1) + ' lines') : '');
|
|
5495
|
+
}
|
|
5496
|
+
return text;
|
|
5497
|
+
}
|
|
5498
|
+
function timelineAgentOptions(snap, project){
|
|
5499
|
+
var opts = [];
|
|
5500
|
+
var seen = {};
|
|
5501
|
+
function add(projectId, clientId, agentId, label){
|
|
5502
|
+
if(!agentId) return;
|
|
5503
|
+
if(project !== 'all' && projectId !== project) return;
|
|
5504
|
+
var value = timelineAgentValue(projectId, clientId, agentId);
|
|
5505
|
+
if(seen[value]) return;
|
|
5506
|
+
seen[value] = 1;
|
|
5507
|
+
opts.push({ value: value, projectId: projectId, clientId: clientId, agentId: agentId, label: label });
|
|
5508
|
+
}
|
|
5509
|
+
((snap && snap.liveSessions) || []).forEach(function(s){
|
|
5510
|
+
var cid = clientIdForSession(snap, s);
|
|
5511
|
+
add(s.projectId, cid, 'leader', (s.projectName || s.projectId) + ' \xB7 leader');
|
|
5512
|
+
(s.agents||[]).forEach(function(a){
|
|
5513
|
+
add(s.projectId, cid, a.id, (s.projectName || s.projectId) + ' \xB7 ' + (a.name || a.id));
|
|
5514
|
+
});
|
|
5515
|
+
});
|
|
5516
|
+
Store.timeline.forEach(function(e){
|
|
5517
|
+
add(e.projectId, e.clientId, e.agentId, projectNameFor(snap, e.projectId) + ' \xB7 ' + (e.agentName || e.agentId));
|
|
5518
|
+
});
|
|
5519
|
+
opts.sort(function(a,b){ return a.label.toLowerCase().localeCompare(b.label.toLowerCase()); });
|
|
5520
|
+
return opts;
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5523
|
+
function GlobalTimelineView(p){
|
|
5524
|
+
var qState = React.useState(''); var q = qState[0], setQ = qState[1];
|
|
5525
|
+
var prState = React.useState('all'); var project = prState[0], setProject = prState[1];
|
|
5526
|
+
var srcState = React.useState('all'); var source = srcState[0], setSource = srcState[1];
|
|
5527
|
+
var roleState = React.useState('all'); var role = roleState[0], setRole = roleState[1];
|
|
5528
|
+
var agState = React.useState('all'); var agent = agState[0], setAgent = agState[1];
|
|
5529
|
+
var agentFilter = parseTimelineAgentValue(agent);
|
|
5530
|
+
var requestFilters = {
|
|
5531
|
+
projectId: project === 'all' ? '' : project,
|
|
5532
|
+
source: source === 'all' ? '' : source,
|
|
5533
|
+
role: role === 'all' ? '' : role,
|
|
5534
|
+
clientId: agentFilter ? agentFilter.clientId : '',
|
|
5535
|
+
agentId: agentFilter ? agentFilter.agentId : ''
|
|
5536
|
+
};
|
|
5537
|
+
React.useEffect(function(){ loadRecentTimeline(requestFilters, true); }, [project, source, role, agent]);
|
|
5538
|
+
var ql = q.trim().toLowerCase();
|
|
5539
|
+
var projects = (p.snap && p.snap.projects) || [];
|
|
5540
|
+
var agents = timelineAgentOptions(p.snap, project);
|
|
5541
|
+
var entries = Store.timeline.filter(function(e){
|
|
5542
|
+
if(project !== 'all' && e.projectId !== project) return false;
|
|
5543
|
+
if(source !== 'all' && e.source !== source) return false;
|
|
5544
|
+
if(role !== 'all' && e.role !== role) return false;
|
|
5545
|
+
if(agentFilter && (e.projectId !== agentFilter.projectId || e.clientId !== agentFilter.clientId || e.agentId !== agentFilter.agentId)) return false;
|
|
5546
|
+
if(!ql) return true;
|
|
5547
|
+
var hay = [e.text, e.role, e.tool, e.agentName, e.agentId, projectNameFor(p.snap, e.projectId)].join(' ').toLowerCase();
|
|
5548
|
+
return hay.indexOf(ql) >= 0;
|
|
5549
|
+
}).slice(0, 500);
|
|
5550
|
+
function openEntry(e){
|
|
5551
|
+
var session = sessionForTimelineEntry(p.snap, e);
|
|
5552
|
+
if(!session) return;
|
|
5553
|
+
selectSession(session.sessionId, e.agentId && e.agentId !== 'leader' ? e.agentId : null);
|
|
5554
|
+
Store.set({ tab: 'console' });
|
|
5555
|
+
}
|
|
5556
|
+
return h('div', { className: 'timeline-wrap' },
|
|
5557
|
+
h('div', { className: 'timeline-head' },
|
|
5558
|
+
h('div', null,
|
|
5559
|
+
h('div', { className: 'timeline-title' }, 'All Agent Timelines'),
|
|
5560
|
+
h('div', { className: 'timeline-note' }, entries.length + ' visible events from recent HQ telemetry across projects')
|
|
5561
|
+
),
|
|
5562
|
+
h('div', { className: 'timeline-filters' },
|
|
5563
|
+
h('select', { className: 'timeline-select', value: project, onChange: function(e){ setProject(e.target.value); setAgent('all'); } },
|
|
5564
|
+
h('option', { value: 'all' }, 'All projects'),
|
|
5565
|
+
projects.map(function(pr){ return h('option', { key: pr.projectId, value: pr.projectId }, pr.projectName || pr.projectId); })
|
|
5566
|
+
),
|
|
5567
|
+
h('select', { className: 'timeline-select', value: agent, onChange: function(e){ setAgent(e.target.value); } },
|
|
5568
|
+
h('option', { value: 'all' }, 'All agents'),
|
|
5569
|
+
agents.map(function(a){ return h('option', { key: a.value, value: a.value }, a.label); })
|
|
5570
|
+
),
|
|
5571
|
+
h('select', { className: 'timeline-select', value: source, onChange: function(e){ setSource(e.target.value); } },
|
|
5572
|
+
h('option', { value: 'all' }, 'All sources'),
|
|
5573
|
+
h('option', { value: 'session' }, 'Leader/session'),
|
|
5574
|
+
h('option', { value: 'agent' }, 'Subagent')
|
|
5575
|
+
),
|
|
5576
|
+
h('select', { className: 'timeline-select', value: role, onChange: function(e){ setRole(e.target.value); } },
|
|
5577
|
+
h('option', { value: 'all' }, 'All roles'),
|
|
5578
|
+
h('option', { value: 'user' }, 'User'),
|
|
5579
|
+
h('option', { value: 'assistant' }, 'Assistant'),
|
|
5580
|
+
h('option', { value: 'tool' }, 'Tool'),
|
|
5581
|
+
h('option', { value: 'system' }, 'System'),
|
|
5582
|
+
h('option', { value: 'error' }, 'Error')
|
|
5583
|
+
),
|
|
5584
|
+
h('input', { className: 'timeline-input', placeholder: 'Filter text / agent / tool\u2026', value: q, onChange: function(e){ setQ(e.target.value); } }),
|
|
5585
|
+
h('button', { className: 'timeline-btn', onClick: function(){
|
|
5586
|
+
var rf = Object.assign({}, requestFilters, { q: q.trim() });
|
|
5587
|
+
loadRecentTimeline(rf, true);
|
|
5588
|
+
} }, 'Refresh')
|
|
5589
|
+
)
|
|
5590
|
+
),
|
|
5591
|
+
h(PromptDock, { snap: p.snap }),
|
|
5592
|
+
h(CommandHistory, { snap: p.snap }),
|
|
5593
|
+
h('div', { className: 'timeline-list' },
|
|
5594
|
+
entries.length ? entries.map(function(e, i){
|
|
5595
|
+
var pn = projectNameFor(p.snap, e.projectId);
|
|
5596
|
+
var an = agentNameForEntry(p.snap, e);
|
|
5597
|
+
return h('div', { key: i, className: 'timeline-row ' + (e.role || '') + ' ' + (e.source || ''), onClick: function(){ openEntry(e); } },
|
|
5598
|
+
h('div', { className: 'timeline-mark' }, roleIcon(e.role)),
|
|
5599
|
+
h('div', { className: 'timeline-time' }, fmtTime(e.ts), h('div', null, fmtAgo(e.ts))),
|
|
5600
|
+
h('div', { className: 'timeline-who' },
|
|
5601
|
+
h('div', { className: 'timeline-agent' }, an),
|
|
5602
|
+
h('div', { className: 'timeline-project' }, pn + (e.source ? (' \xB7 ' + e.source) : ''))
|
|
5603
|
+
),
|
|
5604
|
+
h('div', { className: 'timeline-msg' },
|
|
5605
|
+
h('span', { className: 'timeline-role ' + (e.role||'') }, e.role || 'event'),
|
|
5606
|
+
e.tool ? h('span', { className: 'timeline-toolchip' }, e.tool) : null,
|
|
5607
|
+
timelinePreviewText(e)
|
|
5608
|
+
)
|
|
5609
|
+
);
|
|
5610
|
+
}) : h('div', { className: 'side-empty' }, Store.timelineLoaded ? 'No timeline events yet.' : 'Loading recent HQ timeline\u2026')
|
|
5611
|
+
)
|
|
4736
5612
|
);
|
|
4737
5613
|
}
|
|
4738
5614
|
|
|
@@ -4819,15 +5695,15 @@ async function boot(){
|
|
|
4819
5695
|
|
|
4820
5696
|
var toolbar = h('div', { className: 'gtoolbar' },
|
|
4821
5697
|
h('div', { className: 'tgroup' },
|
|
4822
|
-
h(ToolBtn, { label: '\u2B0C LR', title: 'Left \u2192 right
|
|
4823
|
-
h(ToolBtn, { label: '\u2B0D TB', title: 'Top \u2192 bottom
|
|
5698
|
+
h(ToolBtn, { label: '\u2B0C LR', title: 'Left \u2192 right clustered layout', active: dir==='LR', onClick: function(){ setDir('LR'); } }),
|
|
5699
|
+
h(ToolBtn, { label: '\u2B0D TB', title: 'Top \u2192 bottom clustered layout', active: dir==='TB', onClick: function(){ setDir('TB'); } })
|
|
4824
5700
|
),
|
|
4825
5701
|
h('div', { className: 'tgroup' },
|
|
4826
5702
|
h(ToolBtn, { label: '\u{1F5A5}\uFE0F Machine', title: 'Group under the machine', active: groupBy==='machine', onClick: function(){ setGroupBy('machine'); } }),
|
|
4827
5703
|
h(ToolBtn, { label: '\u{1F4C1} Project', title: 'Group by project', active: groupBy==='project', onClick: function(){ setGroupBy('project'); } })
|
|
4828
5704
|
),
|
|
4829
5705
|
h('div', { className: 'tgroup' },
|
|
4830
|
-
h(ToolBtn, { label: '\u2728 Auto-arrange', title: 'Re-arrange
|
|
5706
|
+
h(ToolBtn, { label: '\u2728 Auto-arrange', title: 'Re-arrange with terminal-centered agent clusters', onClick: function(){ applyLayout(nodes, edges); } }),
|
|
4831
5707
|
h(ToolBtn, { label: '\u22A1 Fit', title: 'Fit to screen', onClick: function(){ if(rfRef.current) rfRef.current.fitView({ padding: 0.18, duration: 400 }); } })
|
|
4832
5708
|
),
|
|
4833
5709
|
h('div', { className: 'glegend' },
|
|
@@ -5017,11 +5893,16 @@ async function boot(){
|
|
|
5017
5893
|
|
|
5018
5894
|
function LiveBubble(p){
|
|
5019
5895
|
return h('div', { className: 'bub assistant live', key: 'live' },
|
|
5020
|
-
h('div', { className: 'bub-
|
|
5021
|
-
|
|
5022
|
-
h('
|
|
5023
|
-
|
|
5024
|
-
|
|
5896
|
+
h('div', { className: 'bub-avatar' }, p.tool ? '\u2318' : '\u{1F916}'),
|
|
5897
|
+
h('div', { className: 'bub-content' },
|
|
5898
|
+
h('div', { className: 'bub-head' },
|
|
5899
|
+
h('span', { className: 'bub-role' }, p.tool ? p.tool : 'Assistant'),
|
|
5900
|
+
h('span', { className: 'live-dot' }, '\u25CF streaming')
|
|
5901
|
+
),
|
|
5902
|
+
h('div', { className: 'bub-card' },
|
|
5903
|
+
h('div', { className: 'assistant-body' }, renderMessageText(p.text || '', 'assistant'), h('span', { className: 'caret' }))
|
|
5904
|
+
)
|
|
5905
|
+
)
|
|
5025
5906
|
);
|
|
5026
5907
|
}
|
|
5027
5908
|
|
|
@@ -5029,14 +5910,14 @@ async function boot(){
|
|
|
5029
5910
|
var sel = Store.selected;
|
|
5030
5911
|
var bodyRef = React.useRef(null);
|
|
5031
5912
|
var stickRef = React.useRef(true);
|
|
5032
|
-
// A non-leader agent is a subagent (shadow) \u2014 show ITS own buffered stream
|
|
5033
|
-
//
|
|
5034
|
-
|
|
5913
|
+
// A non-leader agent is a subagent (shadow) \u2014 show ITS own buffered stream.
|
|
5914
|
+
// The leader / a bare terminal show the session's full on-disk transcript.
|
|
5915
|
+
var session = (p.snap && p.snap.liveSessions || []).filter(function(s){ return s.sessionId===sel.sessionId; })[0];
|
|
5035
5916
|
var isSub = !!(sel && sel.agentId && sel.agentId !== 'leader');
|
|
5036
|
-
var
|
|
5917
|
+
var subKey = isSub ? cacheKey(sel.agentId, session && session.projectId, clientIdForSession(p.snap, session)) : '';
|
|
5918
|
+
var subMsgs = isSub ? (Store.agentMsgs[subKey] || Store.agentMsgs[sel.agentId] || []) : null;
|
|
5037
5919
|
var tc = (sel && !isSub) ? Store.transcripts[sel.sessionId] : null;
|
|
5038
5920
|
var entries = isSub ? subMsgs : (tc ? tc.entries : []);
|
|
5039
|
-
var session = (p.snap && p.snap.liveSessions || []).filter(function(s){ return s.sessionId===sel.sessionId; })[0];
|
|
5040
5921
|
var agentsList = session ? (session.agents||[]) : [];
|
|
5041
5922
|
var ag = sel.agentId ? agentsList.filter(function(a){ return a.id===sel.agentId; })[0]
|
|
5042
5923
|
: (agentsList.filter(function(a){ return a.id==='leader'; })[0] || agentsList[0]);
|
|
@@ -5062,7 +5943,17 @@ async function boot(){
|
|
|
5062
5943
|
if(!isSub && tc && tc.loading && !entries.length && !liveText){ bodyEls.push(h('div', { key:'l', className: 'loading' }, 'Loading full chat history\u2026')); }
|
|
5063
5944
|
else if(!entries.length && !liveText){ bodyEls.push(h('div', { key:'e', className: 'side-empty' }, isSub ? ('No messages from ' + (ag?ag.name:'this subagent') + ' yet \u2014 its conversation streams here live as it works.') : ((tc&&tc.error)?'Could not load history.':'No transcript yet for this terminal.'))); }
|
|
5064
5945
|
else {
|
|
5065
|
-
for(var i=0;i<entries.length;i++){
|
|
5946
|
+
for(var i=0;i<entries.length;i++){
|
|
5947
|
+
if(isToolEntry(entries[i])){
|
|
5948
|
+
var group = [], start = i;
|
|
5949
|
+
while(i < entries.length && isToolEntry(entries[i])){ group.push(entries[i]); i++; }
|
|
5950
|
+
if(group.length > 1) bodyEls.push(h(ToolGroup, { key: 'tg-' + start, tools: group, defaultOpen: i >= entries.length }));
|
|
5951
|
+
else bodyEls.push(h(Bubble, { key: start, e: group[0] }));
|
|
5952
|
+
i--;
|
|
5953
|
+
} else {
|
|
5954
|
+
bodyEls.push(h(Bubble, { key: i, e: entries[i] }));
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5066
5957
|
if(liveText || (streaming && !isSub && liveTool)){ bodyEls.push(h(LiveBubble, { text: liveText, tool: liveTool })); }
|
|
5067
5958
|
}
|
|
5068
5959
|
return h('div', { className: 'chatview' },
|
|
@@ -5072,7 +5963,8 @@ async function boot(){
|
|
|
5072
5963
|
ag ? h('span', { className: 'chat-agent' }, h('span', { className: 'dot '+(ag.status||'idle') }), ag.name || ag.id, isSub ? h('span', { className: 'subbadge' }, 'subagent') : null) : null,
|
|
5073
5964
|
h('span', { className: 'chat-meta' }, metaText)
|
|
5074
5965
|
),
|
|
5075
|
-
h('div', { className: 'chat-body', ref: bodyRef, onScroll: onScroll }, bodyEls)
|
|
5966
|
+
h('div', { className: 'chat-body', ref: bodyRef, onScroll: onScroll }, bodyEls),
|
|
5967
|
+
h(PromptDock, { snap: p.snap })
|
|
5076
5968
|
);
|
|
5077
5969
|
}
|
|
5078
5970
|
|
|
@@ -5106,11 +5998,13 @@ async function boot(){
|
|
|
5106
5998
|
h(TopBar, { snap: snap, connected: s.connected }),
|
|
5107
5999
|
h('div', { className: 'hq-tabs' },
|
|
5108
6000
|
tabBtn('console', '\u{1F6F0}\uFE0F Console'),
|
|
6001
|
+
tabBtn('timeline', '\u{1F9FE} Timeline'),
|
|
5109
6002
|
tabBtn('map', '\u{1F9ED} Map'),
|
|
5110
6003
|
h('div', { className: 'hq-tab' + (tab==='mailbox'?' active':''), onClick: function(){ Store.set({ tab:'mailbox' }); } }, '\u{1F4EC} Mailbox', unread? h('span', { className:'badge' }, unread):null)
|
|
5111
6004
|
),
|
|
5112
6005
|
h('div', { className: 'hq-body' },
|
|
5113
6006
|
tab==='map' ? h(FleetView, { snap: snap }) :
|
|
6007
|
+
tab==='timeline' ? h(GlobalTimelineView, { snap: snap }) :
|
|
5114
6008
|
tab==='mailbox' ? h(MailboxView, { snap: snap }) :
|
|
5115
6009
|
h(ConsoleView, { snap: snap })
|
|
5116
6010
|
)
|
|
@@ -5124,6 +6018,8 @@ async function boot(){
|
|
|
5124
6018
|
});
|
|
5125
6019
|
connectWs();
|
|
5126
6020
|
primeSnapshot();
|
|
6021
|
+
loadRecentTimeline();
|
|
6022
|
+
loadCommands();
|
|
5127
6023
|
}
|
|
5128
6024
|
|
|
5129
6025
|
function primeSnapshot(){
|
|
@@ -5177,14 +6073,15 @@ function renderFallback(){
|
|
|
5177
6073
|
if(tc.loading && !tc.entries.length){ html += '<div class="loading">Loading full chat history\u2026</div>'; }
|
|
5178
6074
|
else if(!tc.entries.length){ html += '<div class="side-empty">No transcript yet.</div>'; }
|
|
5179
6075
|
else { tc.entries.forEach(function(e){
|
|
5180
|
-
var head = '<div class="bub '+escAttr(e.role)+'"><div class="bub-
|
|
6076
|
+
var head = '<div class="bub '+escAttr(e.role)+'"><div class="bub-avatar">'+escAttr(roleIcon(e.role))+'</div><div class="bub-content"><div class="bub-head"><span class="bub-role">'+escAttr((e.role==='tool'||e.role==='error')&&e.tool?e.tool:roleLabel(e.role))+'</span><span>'+fmtTime(e.ts)+'</span></div><div class="bub-card">';
|
|
5181
6077
|
var body;
|
|
5182
6078
|
if(e.role==='tool' || e.role==='error'){
|
|
5183
|
-
body = '';
|
|
5184
|
-
if(e.toolInput) body += '<details class="bub-fold"><summary
|
|
5185
|
-
if(e.text) body += '<details class="bub-fold"><summary>'+(e.isError?'
|
|
5186
|
-
|
|
5187
|
-
|
|
6079
|
+
body = '<div class="tool-card"><div class="tool-head"><span class="tool-name">'+escAttr(e.tool||'tool')+'</span><span class="tool-duration">'+escAttr(toolSummary(e))+'</span><span class="tool-status '+escAttr(e.isError||e.role==='error'?'error':'')+'">'+escAttr(e.isError||e.role==='error'?'failed':'done')+'</span></div>';
|
|
6080
|
+
if(e.toolInput) body += '<details class="bub-fold"><summary>Input \xB7 '+e.toolInput.length+' chars</summary><pre class="bub-argpre">'+escAttr(e.toolInput)+'</pre></details>';
|
|
6081
|
+
if(e.text) body += '<details class="bub-fold"><summary>'+(e.isError?'Error output':'Output')+' \xB7 '+countLines(e.text)+' lines</summary><pre>'+escAttr(e.text)+'</pre></details>';
|
|
6082
|
+
body += '</div>';
|
|
6083
|
+
} else { body = '<div class="'+escAttr(e.role==='assistant'?'assistant-body':'txt')+'">'+escAttr(e.text||'')+'</div>'; }
|
|
6084
|
+
html += head + body + '</div></div></div>';
|
|
5188
6085
|
}); }
|
|
5189
6086
|
html += '</div></div>';
|
|
5190
6087
|
}
|
|
@@ -5561,6 +6458,7 @@ function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth) {
|
|
|
5561
6458
|
const agentMessages = /* @__PURE__ */ new Map();
|
|
5562
6459
|
const persistence = createHqPersistence(dataDir);
|
|
5563
6460
|
const auditLog = new HqCommandAuditLog();
|
|
6461
|
+
const hqSessionTag = randomUUID().slice(0, 8);
|
|
5564
6462
|
const alertEngine = new HqAlertEngine({
|
|
5565
6463
|
onAlert: (alert) => {
|
|
5566
6464
|
const msg = toAlertMessage(alert);
|
|
@@ -5750,6 +6648,98 @@ function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth) {
|
|
|
5750
6648
|
res.end(JSON.stringify({ commandId, queued: true, clientId: target.clientId }));
|
|
5751
6649
|
return;
|
|
5752
6650
|
}
|
|
6651
|
+
if (url.pathname === "/api/mailbox-send" && req2.method === "POST") {
|
|
6652
|
+
const suppliedToken = extractBrowserToken(req2, url);
|
|
6653
|
+
const inBrowserTokenMode = mutableAuth.browserTokens.size > 0;
|
|
6654
|
+
if (inBrowserTokenMode && (!suppliedToken || !mutableAuth.browserTokens.has(suppliedToken))) {
|
|
6655
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
6656
|
+
res.end(JSON.stringify({ error: "unauthorized" }));
|
|
6657
|
+
return;
|
|
6658
|
+
}
|
|
6659
|
+
const tokenObj = suppliedToken !== void 0 ? mutableAuth.browserTokenObjs.get(suppliedToken) : void 0;
|
|
6660
|
+
const canEnqueue = !inBrowserTokenMode || tokenObj?.capabilities === void 0 || tokenObj.capabilities.includes("control.enqueue");
|
|
6661
|
+
if (!canEnqueue) {
|
|
6662
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
6663
|
+
res.end(JSON.stringify({ error: "forbidden: token lacks control.enqueue capability" }));
|
|
6664
|
+
return;
|
|
6665
|
+
}
|
|
6666
|
+
let mbody;
|
|
6667
|
+
try {
|
|
6668
|
+
mbody = JSON.parse(await readRequestBody(req2));
|
|
6669
|
+
} catch {
|
|
6670
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
6671
|
+
res.end(JSON.stringify({ error: "invalid json body" }));
|
|
6672
|
+
return;
|
|
6673
|
+
}
|
|
6674
|
+
if (typeof mbody.type !== "string" || typeof mbody.body !== "string") {
|
|
6675
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
6676
|
+
res.end(JSON.stringify({ error: "missing type or body" }));
|
|
6677
|
+
return;
|
|
6678
|
+
}
|
|
6679
|
+
if (typeof mbody.sessionId !== "string" && typeof mbody.projectId !== "string") {
|
|
6680
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
6681
|
+
res.end(JSON.stringify({ error: "missing sessionId or projectId" }));
|
|
6682
|
+
return;
|
|
6683
|
+
}
|
|
6684
|
+
const { SessionRegistry } = await import('@wrongstack/core');
|
|
6685
|
+
const mbGlobalRoot = path5.dirname(dataDir);
|
|
6686
|
+
let projectRoot;
|
|
6687
|
+
try {
|
|
6688
|
+
const registry = new SessionRegistry(mbGlobalRoot);
|
|
6689
|
+
if (typeof mbody.sessionId === "string") {
|
|
6690
|
+
const entry = await registry.get(mbody.sessionId).catch(() => null);
|
|
6691
|
+
projectRoot = entry?.projectRoot;
|
|
6692
|
+
}
|
|
6693
|
+
if (projectRoot === void 0 && typeof mbody.projectId === "string") {
|
|
6694
|
+
const all = await registry.list().catch(() => []);
|
|
6695
|
+
const match = all.find((e) => e.projectSlug === mbody.projectId);
|
|
6696
|
+
projectRoot = match?.projectRoot;
|
|
6697
|
+
}
|
|
6698
|
+
} catch {
|
|
6699
|
+
projectRoot = void 0;
|
|
6700
|
+
}
|
|
6701
|
+
if (typeof projectRoot !== "string" || projectRoot.length === 0) {
|
|
6702
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
6703
|
+
res.end(JSON.stringify({ error: "could not resolve target project mailbox" }));
|
|
6704
|
+
return;
|
|
6705
|
+
}
|
|
6706
|
+
const to = typeof mbody.to === "string" ? mbody.to : "leader";
|
|
6707
|
+
const subject = typeof mbody.subject === "string" ? mbody.subject : "HQ prompt";
|
|
6708
|
+
const priority = mbody.priority === "high" ? "high" : mbody.priority === "low" ? "low" : "normal";
|
|
6709
|
+
const validated = validateHqCommand({
|
|
6710
|
+
commandId: randomUUID(),
|
|
6711
|
+
type: mbody.type,
|
|
6712
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6713
|
+
payload: { to, subject, body: mbody.body, priority },
|
|
6714
|
+
requiresAck: false
|
|
6715
|
+
});
|
|
6716
|
+
const mailboxType = validated?.type === "steer" || validated?.type === "btw" ? validated.type : validated?.type === "queue" ? "note" : validated?.type === "broadcast" ? "broadcast" : void 0;
|
|
6717
|
+
if (mailboxType === void 0) {
|
|
6718
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
6719
|
+
res.end(JSON.stringify({ error: "unrecognized or malformed mailbox message", type: mbody.type }));
|
|
6720
|
+
return;
|
|
6721
|
+
}
|
|
6722
|
+
try {
|
|
6723
|
+
const { GlobalMailbox: GlobalMailbox9, resolveProjectDir: resolveProjectDir10 } = await import('@wrongstack/core');
|
|
6724
|
+
const projectDir = resolveProjectDir10(projectRoot, mbGlobalRoot);
|
|
6725
|
+
const mailbox = new GlobalMailbox9(projectDir);
|
|
6726
|
+
const from = `hq@${hqSessionTag}`;
|
|
6727
|
+
const sent = await mailbox.send({
|
|
6728
|
+
from,
|
|
6729
|
+
to: mailboxType === "broadcast" ? "all" : to,
|
|
6730
|
+
type: mailboxType,
|
|
6731
|
+
subject,
|
|
6732
|
+
body: mbody.body,
|
|
6733
|
+
priority
|
|
6734
|
+
});
|
|
6735
|
+
res.writeHead(202, { "Content-Type": "application/json" });
|
|
6736
|
+
res.end(JSON.stringify({ delivered: true, messageId: sent?.id, to, type: mailboxType }));
|
|
6737
|
+
} catch (err) {
|
|
6738
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6739
|
+
res.end(JSON.stringify({ error: "mailbox write failed", detail: String(err) }));
|
|
6740
|
+
}
|
|
6741
|
+
return;
|
|
6742
|
+
}
|
|
5753
6743
|
if (url.pathname === "/api/commands" && req2.method === "GET") {
|
|
5754
6744
|
const rawLimit = Number.parseInt(url.searchParams.get("limit") ?? "200", 10);
|
|
5755
6745
|
const limit = Math.min(1e3, Math.max(1, Number.isFinite(rawLimit) ? rawLimit : 200));
|
|
@@ -12915,6 +13905,38 @@ async function runOAuthLoginMenu(deps) {
|
|
|
12915
13905
|
const pick = await deps.reader.readLine(` ${color.amber("?")} Pick ${color.dim("(or b to go back)")}: `);
|
|
12916
13906
|
await runOAuthLoginChoice(deps, pick);
|
|
12917
13907
|
}
|
|
13908
|
+
|
|
13909
|
+
// src/auth-menu/local-presets.ts
|
|
13910
|
+
var LOCAL_LLM_PRESETS = [
|
|
13911
|
+
{
|
|
13912
|
+
id: "omniroute",
|
|
13913
|
+
label: "OmniRoute",
|
|
13914
|
+
defaultBaseUrl: "http://localhost:20128/v1",
|
|
13915
|
+
noAuth: true,
|
|
13916
|
+
hint: "WrongStack local gateway \u2014 port 20128, no auth, auto-discovers models"
|
|
13917
|
+
},
|
|
13918
|
+
{
|
|
13919
|
+
id: "ollama",
|
|
13920
|
+
label: "Ollama",
|
|
13921
|
+
defaultBaseUrl: "http://localhost:11434/v1",
|
|
13922
|
+
noAuth: true,
|
|
13923
|
+
hint: "https://ollama.com \u2014 port 11434, no auth"
|
|
13924
|
+
},
|
|
13925
|
+
{
|
|
13926
|
+
id: "vllm",
|
|
13927
|
+
label: "vLLM",
|
|
13928
|
+
defaultBaseUrl: "http://localhost:8000/v1",
|
|
13929
|
+
noAuth: false,
|
|
13930
|
+
hint: "https://docs.vllm.ai \u2014 port 8000, optional Bearer"
|
|
13931
|
+
},
|
|
13932
|
+
{
|
|
13933
|
+
id: "lmstudio",
|
|
13934
|
+
label: "LM Studio",
|
|
13935
|
+
defaultBaseUrl: "http://localhost:1234/v1",
|
|
13936
|
+
noAuth: false,
|
|
13937
|
+
hint: "https://lmstudio.ai \u2014 port 1234, optional Bearer"
|
|
13938
|
+
}
|
|
13939
|
+
];
|
|
12918
13940
|
var defaultUidFn = () => os9__default.userInfo().uid;
|
|
12919
13941
|
async function getFileUid(filePath) {
|
|
12920
13942
|
try {
|
|
@@ -13231,38 +14253,6 @@ async function restoreLast(homeFn = defaultHomeDir) {
|
|
|
13231
14253
|
return { ok: true };
|
|
13232
14254
|
}
|
|
13233
14255
|
|
|
13234
|
-
// src/auth-menu/local-presets.ts
|
|
13235
|
-
var LOCAL_LLM_PRESETS = [
|
|
13236
|
-
{
|
|
13237
|
-
id: "omniroute",
|
|
13238
|
-
label: "OmniRoute",
|
|
13239
|
-
defaultBaseUrl: "http://localhost:20128/v1",
|
|
13240
|
-
noAuth: true,
|
|
13241
|
-
hint: "WrongStack local gateway \u2014 port 20128, no auth, auto-discovers models"
|
|
13242
|
-
},
|
|
13243
|
-
{
|
|
13244
|
-
id: "ollama",
|
|
13245
|
-
label: "Ollama",
|
|
13246
|
-
defaultBaseUrl: "http://localhost:11434/v1",
|
|
13247
|
-
noAuth: true,
|
|
13248
|
-
hint: "https://ollama.com \u2014 port 11434, no auth"
|
|
13249
|
-
},
|
|
13250
|
-
{
|
|
13251
|
-
id: "vllm",
|
|
13252
|
-
label: "vLLM",
|
|
13253
|
-
defaultBaseUrl: "http://localhost:8000/v1",
|
|
13254
|
-
noAuth: false,
|
|
13255
|
-
hint: "https://docs.vllm.ai \u2014 port 8000, optional Bearer"
|
|
13256
|
-
},
|
|
13257
|
-
{
|
|
13258
|
-
id: "lmstudio",
|
|
13259
|
-
label: "LM Studio",
|
|
13260
|
-
defaultBaseUrl: "http://localhost:1234/v1",
|
|
13261
|
-
noAuth: false,
|
|
13262
|
-
hint: "https://lmstudio.ai \u2014 port 1234, optional Bearer"
|
|
13263
|
-
}
|
|
13264
|
-
];
|
|
13265
|
-
|
|
13266
14256
|
// src/provider-helpers.ts
|
|
13267
14257
|
function visibleModelIds(providerId, config, catalogModelIds, cfg) {
|
|
13268
14258
|
const entry = cfg ?? config.providers?.[providerId];
|
|
@@ -13334,7 +14324,62 @@ async function buildPickableProviders(modelsRegistry, config) {
|
|
|
13334
14324
|
}
|
|
13335
14325
|
|
|
13336
14326
|
// src/picker.ts
|
|
13337
|
-
var theme = {
|
|
14327
|
+
var theme = {
|
|
14328
|
+
accent: color.amber,
|
|
14329
|
+
chrome: color.gray,
|
|
14330
|
+
cursor: (s) => color.amber(color.bold(s)),
|
|
14331
|
+
ctx: color.cyan,
|
|
14332
|
+
cost: color.green,
|
|
14333
|
+
caps: color.magenta
|
|
14334
|
+
};
|
|
14335
|
+
var BOX = {
|
|
14336
|
+
tl: "\u256D",
|
|
14337
|
+
tr: "\u256E",
|
|
14338
|
+
bl: "\u2570",
|
|
14339
|
+
br: "\u256F",
|
|
14340
|
+
h: "\u2500",
|
|
14341
|
+
v: "\u2502"
|
|
14342
|
+
};
|
|
14343
|
+
var PICKER_MIN_WIDTH = 74;
|
|
14344
|
+
var PICKER_MAX_WIDTH = 100;
|
|
14345
|
+
function pickerWidth() {
|
|
14346
|
+
const cols = process.stdout.columns;
|
|
14347
|
+
if (typeof cols !== "number" || !Number.isFinite(cols) || cols <= 0) {
|
|
14348
|
+
return PICKER_MIN_WIDTH;
|
|
14349
|
+
}
|
|
14350
|
+
const inner = cols - 2;
|
|
14351
|
+
return Math.max(PICKER_MIN_WIDTH, Math.min(PICKER_MAX_WIDTH, inner));
|
|
14352
|
+
}
|
|
14353
|
+
function visibleWidth(s) {
|
|
14354
|
+
return stripAnsi(s).length;
|
|
14355
|
+
}
|
|
14356
|
+
function padVisible(s, width) {
|
|
14357
|
+
const w = visibleWidth(s);
|
|
14358
|
+
if (w === width) return s;
|
|
14359
|
+
if (w < width) return s + " ".repeat(width - w);
|
|
14360
|
+
const plain = stripAnsi(s);
|
|
14361
|
+
return `${plain.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
14362
|
+
}
|
|
14363
|
+
function boxTop(title) {
|
|
14364
|
+
const width = pickerWidth();
|
|
14365
|
+
const label = ` ${title} `;
|
|
14366
|
+
const pad = Math.max(0, width - visibleWidth(label) - 1);
|
|
14367
|
+
return theme.chrome(
|
|
14368
|
+
`${BOX.tl}${BOX.h}${theme.accent(color.bold(label))}${theme.chrome(BOX.h.repeat(pad))}${BOX.tr}`
|
|
14369
|
+
);
|
|
14370
|
+
}
|
|
14371
|
+
function boxBottom() {
|
|
14372
|
+
return theme.chrome(`${BOX.bl}${BOX.h.repeat(pickerWidth())}${BOX.br}`);
|
|
14373
|
+
}
|
|
14374
|
+
function boxRow(content) {
|
|
14375
|
+
return `${theme.chrome(BOX.v)} ${padVisible(content, pickerWidth() - 2)} ${theme.chrome(BOX.v)}`;
|
|
14376
|
+
}
|
|
14377
|
+
function boxDivider() {
|
|
14378
|
+
return theme.chrome(`${BOX.v}${BOX.h.repeat(pickerWidth())}${BOX.v}`);
|
|
14379
|
+
}
|
|
14380
|
+
function keyHints(pairs) {
|
|
14381
|
+
return pairs.map(([k, label]) => `${theme.accent(k)} ${color.dim(label)}`).join(color.dim(" \xB7 "));
|
|
14382
|
+
}
|
|
13338
14383
|
var CODEX_PROVIDER_ID2 = "openai-codex";
|
|
13339
14384
|
var CODEX_PICKER_HEADER = "Select Model and Effort";
|
|
13340
14385
|
var CODEX_LEGACY_NOTE = "Access legacy models by running `wstack -m <model_name>` or in your `config.json`.";
|
|
@@ -13428,38 +14473,55 @@ function renderLiveProviderList(query, filtered, selectedIdx, savedSet) {
|
|
|
13428
14473
|
const ordered = orderProvidersForDisplay(filtered);
|
|
13429
14474
|
const scrollOffset = Math.max(0, selectedIdx - LIVE_PICKER_MAX_VISIBLE + 1);
|
|
13430
14475
|
const visible = ordered.slice(scrollOffset, scrollOffset + LIVE_PICKER_MAX_VISIBLE);
|
|
13431
|
-
|
|
13432
|
-
|
|
14476
|
+
const lines = [];
|
|
14477
|
+
lines.push(boxTop("Select a provider"));
|
|
14478
|
+
const caret = query.length > 0 ? color.dim("\u258F") : theme.cursor("\u258F");
|
|
14479
|
+
lines.push(boxRow(`${theme.accent("\u276F")} ${color.dim("Select provider:")} ${query}${caret}`));
|
|
14480
|
+
lines.push(boxDivider());
|
|
13433
14481
|
if (scrollOffset > 0) {
|
|
13434
|
-
|
|
13435
|
-
`;
|
|
14482
|
+
lines.push(boxRow(color.dim(` ${scrollOffset} more above \u2191`)));
|
|
13436
14483
|
}
|
|
13437
14484
|
let flat = 0;
|
|
13438
14485
|
let lastFamily = "";
|
|
13439
14486
|
for (const p of visible) {
|
|
13440
14487
|
if (p.family !== lastFamily) {
|
|
13441
|
-
|
|
13442
|
-
`;
|
|
14488
|
+
lines.push(boxRow(`${theme.accent("\xB7")} ${color.dim(p.family.toUpperCase())}`));
|
|
13443
14489
|
lastFamily = p.family;
|
|
13444
14490
|
}
|
|
13445
|
-
const
|
|
14491
|
+
const selected = flat === selectedIdx - scrollOffset;
|
|
14492
|
+
const sel = selected ? theme.cursor("\u25B6") : " ";
|
|
13446
14493
|
let marker;
|
|
13447
14494
|
if (savedSet) {
|
|
13448
14495
|
const mark = savedSet.has(p.id) ? color.cyan("\u25C9") : color.dim("\u25CB");
|
|
13449
|
-
marker = `${sel} ${mark}
|
|
14496
|
+
marker = `${sel} ${mark}`;
|
|
13450
14497
|
} else {
|
|
13451
|
-
marker = `${sel}
|
|
14498
|
+
marker = `${sel}`;
|
|
13452
14499
|
}
|
|
13453
|
-
|
|
13454
|
-
|
|
14500
|
+
const id = selected ? theme.accent(color.bold(p.id)) : p.id;
|
|
14501
|
+
const name = selected ? p.name : color.dim(p.name);
|
|
14502
|
+
lines.push(boxRow(`${marker} ${padVisible(id, 24)} ${name}`));
|
|
13455
14503
|
flat++;
|
|
13456
14504
|
}
|
|
13457
14505
|
if (scrollOffset + LIVE_PICKER_MAX_VISIBLE < ordered.length) {
|
|
13458
|
-
|
|
13459
|
-
|
|
14506
|
+
lines.push(
|
|
14507
|
+
boxRow(
|
|
14508
|
+
color.dim(` ${ordered.length - scrollOffset - LIVE_PICKER_MAX_VISIBLE} more below \u2193`)
|
|
14509
|
+
)
|
|
14510
|
+
);
|
|
13460
14511
|
}
|
|
13461
|
-
|
|
13462
|
-
|
|
14512
|
+
lines.push(boxDivider());
|
|
14513
|
+
lines.push(
|
|
14514
|
+
boxRow(
|
|
14515
|
+
keyHints([
|
|
14516
|
+
["\u2191\u2193", "scroll"],
|
|
14517
|
+
["Enter", "select"],
|
|
14518
|
+
["Esc", "clear"],
|
|
14519
|
+
["Ctrl+C", "quit"]
|
|
14520
|
+
])
|
|
14521
|
+
)
|
|
14522
|
+
);
|
|
14523
|
+
lines.push(boxBottom());
|
|
14524
|
+
return lines.join("\n");
|
|
13463
14525
|
}
|
|
13464
14526
|
function filterModels(query, models) {
|
|
13465
14527
|
const q = query.trim().toLowerCase();
|
|
@@ -13471,28 +14533,48 @@ function renderLiveModelList(query, filtered, selectedIdx, header) {
|
|
|
13471
14533
|
(a, b) => (b.release_date ?? "").localeCompare(a.release_date ?? "")
|
|
13472
14534
|
);
|
|
13473
14535
|
const visible = ordered.slice(0, LIVE_PICKER_MAX_VISIBLE);
|
|
13474
|
-
|
|
13475
|
-
|
|
13476
|
-
|
|
14536
|
+
const lines = [];
|
|
14537
|
+
lines.push(boxTop("Select a model"));
|
|
14538
|
+
lines.push(boxRow(color.dim(header)));
|
|
14539
|
+
const caret = query.length > 0 ? color.dim("\u258F") : theme.cursor("\u258F");
|
|
14540
|
+
lines.push(boxRow(`${theme.accent("\u276F")} ${color.dim("Select model:")} ${query}${caret}`));
|
|
14541
|
+
lines.push(boxDivider());
|
|
13477
14542
|
let flat = 0;
|
|
13478
14543
|
for (const m of visible) {
|
|
13479
|
-
const
|
|
13480
|
-
const
|
|
13481
|
-
const
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
const
|
|
13486
|
-
|
|
13487
|
-
|
|
14544
|
+
const selected = flat === selectedIdx;
|
|
14545
|
+
const marker = selected ? theme.cursor("\u25B6") : " ";
|
|
14546
|
+
const ctxRaw = m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k` : "?";
|
|
14547
|
+
const ctx = theme.ctx(ctxRaw.padStart(6));
|
|
14548
|
+
const costRaw = m.cost?.input !== void 0 ? `$${m.cost.input}/$${m.cost.output ?? "?"}` : "";
|
|
14549
|
+
const cost = costRaw ? theme.cost(costRaw) : "";
|
|
14550
|
+
const capTags = [];
|
|
14551
|
+
if (m.tool_call) capTags.push("tools");
|
|
14552
|
+
if (m.reasoning) capTags.push("reason");
|
|
14553
|
+
if (m.modalities?.input?.includes("image")) capTags.push("vision");
|
|
14554
|
+
const caps = capTags.map((c) => theme.caps(c)).join(color.dim(" "));
|
|
14555
|
+
const id = selected ? theme.accent(color.bold(m.id)) : m.id;
|
|
14556
|
+
const row = `${padVisible(id, 34)} ${ctx} ${padVisible(cost, 12)} ${caps}`;
|
|
14557
|
+
lines.push(boxRow(`${marker} ${row}`));
|
|
13488
14558
|
flat++;
|
|
13489
14559
|
}
|
|
13490
14560
|
if (ordered.length > LIVE_PICKER_MAX_VISIBLE) {
|
|
13491
|
-
|
|
13492
|
-
|
|
14561
|
+
lines.push(
|
|
14562
|
+
boxRow(color.dim(` \u2026 ${ordered.length - LIVE_PICKER_MAX_VISIBLE} more \u2014 type to filter`))
|
|
14563
|
+
);
|
|
13493
14564
|
}
|
|
13494
|
-
|
|
13495
|
-
|
|
14565
|
+
lines.push(boxDivider());
|
|
14566
|
+
lines.push(
|
|
14567
|
+
boxRow(
|
|
14568
|
+
keyHints([
|
|
14569
|
+
["\u2191\u2193", "move"],
|
|
14570
|
+
["Enter", "select"],
|
|
14571
|
+
["Esc", "clear"],
|
|
14572
|
+
["Ctrl+C", "quit"]
|
|
14573
|
+
])
|
|
14574
|
+
)
|
|
14575
|
+
);
|
|
14576
|
+
lines.push(boxBottom());
|
|
14577
|
+
return lines.join("\n");
|
|
13496
14578
|
}
|
|
13497
14579
|
function codexPickerPreamble(provider) {
|
|
13498
14580
|
if (provider.id !== CODEX_PROVIDER_ID2) return "";
|
|
@@ -13549,11 +14631,19 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
13549
14631
|
return false;
|
|
13550
14632
|
}
|
|
13551
14633
|
}
|
|
13552
|
-
var
|
|
14634
|
+
var QUERY_MARKER = "Select provider:";
|
|
13553
14635
|
function cursorToQuery(frame, query) {
|
|
13554
|
-
const
|
|
13555
|
-
const
|
|
13556
|
-
|
|
14636
|
+
const rows = frame.split("\n");
|
|
14637
|
+
const lastIdx = rows.length - 1;
|
|
14638
|
+
const queryIdx = rows.findIndex((r) => r.includes(QUERY_MARKER));
|
|
14639
|
+
if (queryIdx < 0) return;
|
|
14640
|
+
const up = lastIdx - queryIdx;
|
|
14641
|
+
const queryLine = rows[queryIdx] ?? "";
|
|
14642
|
+
const plain = stripAnsi(queryLine);
|
|
14643
|
+
const markerAt = plain.indexOf(QUERY_MARKER);
|
|
14644
|
+
const col = markerAt + QUERY_MARKER.length + 1 + query.length + 1;
|
|
14645
|
+
if (up > 0) writeOut(`\x1B[${up}A`);
|
|
14646
|
+
writeOut(`\x1B[${col}G`);
|
|
13557
14647
|
}
|
|
13558
14648
|
async function runLiveProviderPicker(displayList, savedSet) {
|
|
13559
14649
|
const stdin = process.stdin;
|
|
@@ -13625,10 +14715,11 @@ async function runPicker(deps) {
|
|
|
13625
14715
|
const { modelsRegistry, renderer, reader, config, defaultProvider, defaultModel } = deps;
|
|
13626
14716
|
renderer.write(
|
|
13627
14717
|
`
|
|
13628
|
-
${color.bold(
|
|
14718
|
+
${theme.accent(color.bold("\u258C WrongStack"))} ${color.dim("Provider & Model Selection")}
|
|
13629
14719
|
`
|
|
13630
14720
|
);
|
|
13631
|
-
renderer.write(color.dim("Loading provider catalog\u2026
|
|
14721
|
+
renderer.write(color.dim(`${theme.chrome("\xB7")} Loading provider catalog\u2026
|
|
14722
|
+
`));
|
|
13632
14723
|
let providers;
|
|
13633
14724
|
try {
|
|
13634
14725
|
providers = await modelsRegistry.listProviders();
|
|
@@ -13687,9 +14778,7 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
|
|
|
13687
14778
|
npm: inherited?.npm
|
|
13688
14779
|
});
|
|
13689
14780
|
}
|
|
13690
|
-
const keyed = merged.filter(
|
|
13691
|
-
(p) => hasApiKey(p, config) || isKeylessLocalProvider(p)
|
|
13692
|
-
);
|
|
14781
|
+
const keyed = merged.filter((p) => hasApiKey(p, config) || isKeylessLocalProvider(p));
|
|
13693
14782
|
let displayList = keyed;
|
|
13694
14783
|
let showingFallback = false;
|
|
13695
14784
|
if (keyed.length === 0) {
|
|
@@ -13732,12 +14821,14 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
|
|
|
13732
14821
|
let idx = 1;
|
|
13733
14822
|
let defaultIdx;
|
|
13734
14823
|
renderer.write("\n");
|
|
14824
|
+
renderer.write(`${boxTop("Select a provider")}
|
|
14825
|
+
`);
|
|
13735
14826
|
for (const fam of familyOrder) {
|
|
13736
14827
|
const list = [...families.get(fam) ?? []].sort(
|
|
13737
14828
|
(a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase())
|
|
13738
14829
|
);
|
|
13739
14830
|
if (!list || list.length === 0) continue;
|
|
13740
|
-
renderer.write(
|
|
14831
|
+
renderer.write(`${boxRow(`${theme.accent("\xB7")} ${color.dim(fam.toUpperCase())}`)}
|
|
13741
14832
|
`);
|
|
13742
14833
|
for (const p of list) {
|
|
13743
14834
|
const envFound = p.envVars.some((v) => !!process.env[v]);
|
|
@@ -13746,27 +14837,29 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
|
|
|
13746
14837
|
const marker = envFound ? color.green("\u25CF") : configKey ? color.cyan("\u25C9") : color.dim("\u25CB");
|
|
13747
14838
|
const isDefault = p.id === defaultProvider;
|
|
13748
14839
|
if (isDefault) defaultIdx = idx;
|
|
13749
|
-
const idLabel = isDefault ? color.bold(p.id) : p.id;
|
|
14840
|
+
const idLabel = isDefault ? theme.accent(color.bold(p.id)) : p.id;
|
|
13750
14841
|
const suffix = isDefault ? color.dim(" (default)") : "";
|
|
13751
14842
|
renderer.write(
|
|
13752
|
-
|
|
14843
|
+
`${boxRow(`${color.dim(`${idx}.`.padStart(4))} ${marker} ${padVisible(idLabel, 22)} ${color.dim(p.name)}${suffix}`)}
|
|
13753
14844
|
`
|
|
13754
14845
|
);
|
|
13755
14846
|
ordered.push({ provider: p, index: idx });
|
|
13756
14847
|
idx++;
|
|
13757
14848
|
}
|
|
13758
14849
|
}
|
|
14850
|
+
renderer.write(`${boxDivider()}
|
|
14851
|
+
`);
|
|
13759
14852
|
if (showingFallback) {
|
|
13760
14853
|
renderer.write(
|
|
13761
|
-
`
|
|
13762
|
-
${color.yellow("\u26A0 No API keys detected.")} ${color.dim("Pick a provider, then run `wstack auth <provider>` to add one.")}
|
|
14854
|
+
`${boxRow(`${color.yellow("\u26A0 No API keys detected.")} ${color.dim("Run `wstack auth <provider>` after picking.")}`)}
|
|
13763
14855
|
`
|
|
13764
14856
|
);
|
|
13765
14857
|
} else {
|
|
13766
|
-
renderer.write(
|
|
13767
|
-
${color.dim("\u25CF = env key \u25C9 = stored in config \u25CB = no key")}
|
|
14858
|
+
renderer.write(`${boxRow(color.dim("\u25CF env key \u25C9 stored in config \u25CB no key"))}
|
|
13768
14859
|
`);
|
|
13769
14860
|
}
|
|
14861
|
+
renderer.write(`${boxBottom()}
|
|
14862
|
+
`);
|
|
13770
14863
|
const defaultHint = defaultIdx !== void 0 && defaultProvider ? ` ${color.dim(`[Enter = ${defaultProvider}]`)}` : "";
|
|
13771
14864
|
const providerAnswer = (await reader.readLine(
|
|
13772
14865
|
`
|
|
@@ -13872,8 +14965,13 @@ async function runLiveModelPicker(provider, defaultModel) {
|
|
|
13872
14965
|
}
|
|
13873
14966
|
async function pickModel(provider, registry, renderer, reader, defaultModel) {
|
|
13874
14967
|
renderer.write(`
|
|
13875
|
-
|
|
13876
|
-
|
|
14968
|
+
${boxTop("Select a model")}
|
|
14969
|
+
`);
|
|
14970
|
+
renderer.write(
|
|
14971
|
+
`${boxRow(`${color.bold(provider.name)} ${color.dim(`(${provider.id})`)} models:`)}
|
|
14972
|
+
`
|
|
14973
|
+
);
|
|
14974
|
+
renderer.write(`${boxBottom()}
|
|
13877
14975
|
`);
|
|
13878
14976
|
const codexBlock = codexPickerPreamble(provider);
|
|
13879
14977
|
if (codexBlock) renderer.write(codexBlock);
|
|
@@ -13886,20 +14984,16 @@ async function pickModel(provider, registry, renderer, reader, defaultModel) {
|
|
|
13886
14984
|
" No models listed yet for this provider \u2014 type a model id to use it (local servers auto-discover models at launch).\n"
|
|
13887
14985
|
)
|
|
13888
14986
|
);
|
|
13889
|
-
const typed = (await reader.readLine(
|
|
13890
|
-
|
|
13891
|
-
${color.amber("?")} Model id ${color.dim("(or Enter to cancel)")}: `
|
|
13892
|
-
)).trim();
|
|
14987
|
+
const typed = (await reader.readLine(`
|
|
14988
|
+
${color.amber("?")} Model id ${color.dim("(or Enter to cancel)")}: `)).trim();
|
|
13893
14989
|
if (!typed) {
|
|
13894
14990
|
renderer.write(color.dim("Cancelled.\n"));
|
|
13895
14991
|
return void 0;
|
|
13896
14992
|
}
|
|
13897
|
-
renderer.write(
|
|
13898
|
-
`
|
|
14993
|
+
renderer.write(`
|
|
13899
14994
|
${color.green("\u2713")} ${color.bold(provider.id)} / ${color.bold(typed)}
|
|
13900
14995
|
|
|
13901
|
-
`
|
|
13902
|
-
);
|
|
14996
|
+
`);
|
|
13903
14997
|
return { provider: provider.id, model: typed };
|
|
13904
14998
|
}
|
|
13905
14999
|
if (process.stdin.isTTY) {
|
|
@@ -13921,24 +15015,33 @@ ${color.amber("?")} Model id ${color.dim("(or Enter to cancel)")}: `
|
|
|
13921
15015
|
let offset = 0;
|
|
13922
15016
|
while (offset < models.length) {
|
|
13923
15017
|
const page = models.slice(offset, offset + pageSize);
|
|
15018
|
+
const pageFrom = offset + 1;
|
|
15019
|
+
const pageTo = Math.min(offset + page.length, models.length);
|
|
15020
|
+
renderer.write(`${boxTop(`Models ${pageFrom}\u2013${pageTo} of ${models.length}`)}
|
|
15021
|
+
`);
|
|
13924
15022
|
for (let i = 0; i < page.length; i++) {
|
|
13925
15023
|
const m = expectDefined(page[i]);
|
|
13926
15024
|
const num = offset + i + 1;
|
|
13927
|
-
const
|
|
15025
|
+
const ctxRaw = m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k` : "?";
|
|
15026
|
+
const ctx = theme.ctx(ctxRaw.padStart(6));
|
|
13928
15027
|
const cost = m.cost?.input !== void 0 ? `$${m.cost.input}/$${m.cost.output ?? "?"}` : "";
|
|
13929
|
-
const
|
|
13930
|
-
|
|
13931
|
-
if (m.
|
|
13932
|
-
if (m.
|
|
13933
|
-
|
|
15028
|
+
const costCol = cost ? theme.cost(cost) : "";
|
|
15029
|
+
const capTags = [];
|
|
15030
|
+
if (m.tool_call) capTags.push("tools");
|
|
15031
|
+
if (m.reasoning) capTags.push("reason");
|
|
15032
|
+
if (m.modalities?.input?.includes("image")) capTags.push("vision");
|
|
15033
|
+
const capStr = capTags.map((c) => theme.caps(c)).join(color.dim(" "));
|
|
13934
15034
|
const isDefault = m.id === defaultModel;
|
|
13935
|
-
const idLabel = isDefault ? color.bold(m.id) : m.id;
|
|
15035
|
+
const idLabel = isDefault ? theme.accent(color.bold(m.id)) : m.id;
|
|
13936
15036
|
const suffix = isDefault ? color.dim(" (default)") : "";
|
|
15037
|
+
const num5 = color.dim(`${num}.`.padStart(5));
|
|
13937
15038
|
renderer.write(
|
|
13938
|
-
|
|
15039
|
+
`${boxRow(`${num5} ${padVisible(idLabel, 34)} ${ctx} ${padVisible(costCol, 11)} ${capStr}${suffix}`)}
|
|
13939
15040
|
`
|
|
13940
15041
|
);
|
|
13941
15042
|
}
|
|
15043
|
+
renderer.write(`${boxBottom()}
|
|
15044
|
+
`);
|
|
13942
15045
|
offset += pageSize;
|
|
13943
15046
|
if (offset < models.length) {
|
|
13944
15047
|
const more = (await reader.readLine(
|
|
@@ -14710,14 +15813,14 @@ async function promptOptionalKey(deps, preset) {
|
|
|
14710
15813
|
|
|
14711
15814
|
// src/auth-menu/panel-service.ts
|
|
14712
15815
|
var ANSI_RE = /\u001b\[[0-9;]*m/g;
|
|
14713
|
-
function
|
|
15816
|
+
function stripAnsi2(text) {
|
|
14714
15817
|
return text.replace(ANSI_RE, "");
|
|
14715
15818
|
}
|
|
14716
15819
|
function cleanPrompt(prompt) {
|
|
14717
|
-
return
|
|
15820
|
+
return stripAnsi2(prompt).replace(/[\r\n]+/g, " ").replace(/^\s*\?\s*/, "").replace(/[:\s]+$/, "").trim();
|
|
14718
15821
|
}
|
|
14719
15822
|
function emitLines(io, raw, prefix = "") {
|
|
14720
|
-
for (const line of
|
|
15823
|
+
for (const line of stripAnsi2(raw).split(/\r?\n/)) {
|
|
14721
15824
|
const text = line.trim();
|
|
14722
15825
|
if (text.length > 0) io.onLog(prefix + text);
|
|
14723
15826
|
}
|
|
@@ -40334,6 +41437,32 @@ async function dispatch(controller, commandId, type, payload) {
|
|
|
40334
41437
|
await mailbox.send({ from, to, type: "steer", subject, body, priority });
|
|
40335
41438
|
return { commandId, status: "accepted", message: `steered ${to}` };
|
|
40336
41439
|
}
|
|
41440
|
+
case "btw": {
|
|
41441
|
+
const mailbox = controller.steerMailbox;
|
|
41442
|
+
if (mailbox === void 0) {
|
|
41443
|
+
return { commandId, status: "rejected", message: "no mailbox available" };
|
|
41444
|
+
}
|
|
41445
|
+
const to = typeof payload["to"] === "string" ? payload["to"] : "leader";
|
|
41446
|
+
const subject = typeof payload["subject"] === "string" ? payload["subject"] : "HQ note";
|
|
41447
|
+
const body = typeof payload["body"] === "string" ? payload["body"] : "";
|
|
41448
|
+
const priority = payload["priority"] === "high" ? "high" : payload["priority"] === "low" ? "low" : "normal";
|
|
41449
|
+
const from = `hq@${controller.sessionTag()}`;
|
|
41450
|
+
await mailbox.send({ from, to, type: "btw", subject, body, priority });
|
|
41451
|
+
return { commandId, status: "accepted", message: `noted ${to}` };
|
|
41452
|
+
}
|
|
41453
|
+
case "queue": {
|
|
41454
|
+
const mailbox = controller.steerMailbox;
|
|
41455
|
+
if (mailbox === void 0) {
|
|
41456
|
+
return { commandId, status: "rejected", message: "no mailbox available" };
|
|
41457
|
+
}
|
|
41458
|
+
const to = typeof payload["to"] === "string" ? payload["to"] : "leader";
|
|
41459
|
+
const subject = typeof payload["subject"] === "string" ? payload["subject"] : "HQ prompt";
|
|
41460
|
+
const body = typeof payload["body"] === "string" ? payload["body"] : "";
|
|
41461
|
+
const priority = payload["priority"] === "high" ? "high" : payload["priority"] === "low" ? "low" : "normal";
|
|
41462
|
+
const from = `hq@${controller.sessionTag()}`;
|
|
41463
|
+
await mailbox.send({ from, to, type: "note", subject, body, priority });
|
|
41464
|
+
return { commandId, status: "accepted", message: `queued for ${to}` };
|
|
41465
|
+
}
|
|
40337
41466
|
case "broadcast": {
|
|
40338
41467
|
const mailbox = controller.steerMailbox;
|
|
40339
41468
|
if (mailbox === void 0) {
|