actoviq-agent-sdk 0.4.12 → 0.4.13
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/CHANGELOG.md +18 -0
- package/dist/src/config/agentDefinitionMigration.d.ts +125 -0
- package/dist/src/config/agentDefinitionMigration.d.ts.map +1 -0
- package/dist/src/config/agentDefinitionMigration.js +540 -0
- package/dist/src/config/agentDefinitionMigration.js.map +1 -0
- package/dist/src/config/agentProfiles.d.ts +19 -0
- package/dist/src/config/agentProfiles.d.ts.map +1 -1
- package/dist/src/config/agentProfiles.js +65 -13
- package/dist/src/config/agentProfiles.js.map +1 -1
- package/dist/src/config/modelConfigurationCatalog.d.ts +25 -0
- package/dist/src/config/modelConfigurationCatalog.d.ts.map +1 -0
- package/dist/src/config/modelConfigurationCatalog.js +50 -0
- package/dist/src/config/modelConfigurationCatalog.js.map +1 -0
- package/dist/src/gui/agentTargetPicker.d.ts +32 -0
- package/dist/src/gui/agentTargetPicker.d.ts.map +1 -0
- package/dist/src/gui/agentTargetPicker.js +99 -0
- package/dist/src/gui/agentTargetPicker.js.map +1 -0
- package/dist/src/gui/electronMain.js +1 -0
- package/dist/src/gui/electronMain.js.map +1 -1
- package/dist/src/gui/hadamardGui.d.ts +23 -0
- package/dist/src/gui/hadamardGui.d.ts.map +1 -1
- package/dist/src/gui/hadamardGui.js +4120 -931
- package/dist/src/gui/hadamardGui.js.map +1 -1
- package/dist/src/gui/projectWorkbench.d.ts +6 -2
- package/dist/src/gui/projectWorkbench.d.ts.map +1 -1
- package/dist/src/gui/projectWorkbench.js +76 -67
- package/dist/src/gui/projectWorkbench.js.map +1 -1
- package/dist/src/gui/promptTemplates.d.ts +0 -1
- package/dist/src/gui/promptTemplates.d.ts.map +1 -1
- package/dist/src/gui/promptTemplates.js +0 -3
- package/dist/src/gui/promptTemplates.js.map +1 -1
- package/dist/src/gui/transcript/clientBundle.d.ts +6 -1
- package/dist/src/gui/transcript/clientBundle.d.ts.map +1 -1
- package/dist/src/gui/transcript/clientBundle.js +6 -1
- package/dist/src/gui/transcript/clientBundle.js.map +1 -1
- package/dist/src/help/productCapabilities.d.ts +17 -0
- package/dist/src/help/productCapabilities.d.ts.map +1 -0
- package/dist/src/help/productCapabilities.js +146 -0
- package/dist/src/help/productCapabilities.js.map +1 -0
- package/dist/src/index.d.ts +15 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/issues/issueExecution.d.ts.map +1 -1
- package/dist/src/issues/issueExecution.js +29 -9
- package/dist/src/issues/issueExecution.js.map +1 -1
- package/dist/src/manager/assistantGlobalTools.d.ts +44 -0
- package/dist/src/manager/assistantGlobalTools.d.ts.map +1 -1
- package/dist/src/manager/assistantGlobalTools.js +565 -32
- package/dist/src/manager/assistantGlobalTools.js.map +1 -1
- package/dist/src/manager/assistantProposals.d.ts +87 -0
- package/dist/src/manager/assistantProposals.d.ts.map +1 -0
- package/dist/src/manager/assistantProposals.js +207 -0
- package/dist/src/manager/assistantProposals.js.map +1 -0
- package/dist/src/manager/referenceIndex.d.ts +82 -0
- package/dist/src/manager/referenceIndex.d.ts.map +1 -0
- package/dist/src/manager/referenceIndex.js +167 -0
- package/dist/src/manager/referenceIndex.js.map +1 -0
- package/dist/src/manager/referenceOperations.d.ts +108 -0
- package/dist/src/manager/referenceOperations.d.ts.map +1 -0
- package/dist/src/manager/referenceOperations.js +821 -0
- package/dist/src/manager/referenceOperations.js.map +1 -0
- package/dist/src/manager/resolveTargetRef.d.ts +67 -0
- package/dist/src/manager/resolveTargetRef.d.ts.map +1 -0
- package/dist/src/manager/resolveTargetRef.js +81 -0
- package/dist/src/manager/resolveTargetRef.js.map +1 -0
- package/dist/src/router/modelRouter.d.ts +22 -2
- package/dist/src/router/modelRouter.d.ts.map +1 -1
- package/dist/src/router/modelRouter.js +76 -9
- package/dist/src/router/modelRouter.js.map +1 -1
- package/dist/src/runtime/agentClient.d.ts +18 -0
- package/dist/src/runtime/agentClient.d.ts.map +1 -1
- package/dist/src/runtime/agentClient.js +209 -21
- package/dist/src/runtime/agentClient.js.map +1 -1
- package/dist/src/runtime/agentSession.d.ts +7 -0
- package/dist/src/runtime/agentSession.d.ts.map +1 -1
- package/dist/src/runtime/agentSession.js +10 -0
- package/dist/src/runtime/agentSession.js.map +1 -1
- package/dist/src/runtime/agentTemplates.d.ts +28 -0
- package/dist/src/runtime/agentTemplates.d.ts.map +1 -0
- package/dist/src/runtime/agentTemplates.js +73 -0
- package/dist/src/runtime/agentTemplates.js.map +1 -0
- package/dist/src/runtime/conversationEngine.d.ts +2 -0
- package/dist/src/runtime/conversationEngine.d.ts.map +1 -1
- package/dist/src/runtime/conversationEngine.js +10 -1
- package/dist/src/runtime/conversationEngine.js.map +1 -1
- package/dist/src/runtime/defaultHadamardAgents.d.ts.map +1 -1
- package/dist/src/runtime/defaultHadamardAgents.js +9 -47
- package/dist/src/runtime/defaultHadamardAgents.js.map +1 -1
- package/dist/src/runtime/effectiveAgentRunOptions.d.ts +49 -0
- package/dist/src/runtime/effectiveAgentRunOptions.d.ts.map +1 -0
- package/dist/src/runtime/effectiveAgentRunOptions.js +50 -0
- package/dist/src/runtime/effectiveAgentRunOptions.js.map +1 -0
- package/dist/src/runtime/externalAgentRunner.d.ts +25 -0
- package/dist/src/runtime/externalAgentRunner.d.ts.map +1 -0
- package/dist/src/runtime/externalAgentRunner.js +57 -0
- package/dist/src/runtime/externalAgentRunner.js.map +1 -0
- package/dist/src/runtime/hadamardAgentDefinitions.d.ts +15 -0
- package/dist/src/runtime/hadamardAgentDefinitions.d.ts.map +1 -1
- package/dist/src/runtime/hadamardAgentDefinitions.js +42 -2
- package/dist/src/runtime/hadamardAgentDefinitions.js.map +1 -1
- package/dist/src/runtime/hadamardAgents.d.ts +6 -0
- package/dist/src/runtime/hadamardAgents.d.ts.map +1 -1
- package/dist/src/runtime/hadamardAgents.js +52 -1
- package/dist/src/runtime/hadamardAgents.js.map +1 -1
- package/dist/src/runtime/hadamardPermissions.d.ts +1 -0
- package/dist/src/runtime/hadamardPermissions.d.ts.map +1 -1
- package/dist/src/runtime/hadamardPermissions.js +6 -0
- package/dist/src/runtime/hadamardPermissions.js.map +1 -1
- package/dist/src/runtime/tools.d.ts.map +1 -1
- package/dist/src/runtime/tools.js +2 -0
- package/dist/src/runtime/tools.js.map +1 -1
- package/dist/src/team/executorTarget.d.ts +26 -0
- package/dist/src/team/executorTarget.d.ts.map +1 -0
- package/dist/src/team/executorTarget.js +15 -0
- package/dist/src/team/executorTarget.js.map +1 -0
- package/dist/src/team/modelTeam.d.ts +5 -1
- package/dist/src/team/modelTeam.d.ts.map +1 -1
- package/dist/src/team/modelTeam.js +157 -41
- package/dist/src/team/modelTeam.js.map +1 -1
- package/dist/src/team/teamComposition.d.ts +20 -0
- package/dist/src/team/teamComposition.d.ts.map +1 -0
- package/dist/src/team/teamComposition.js +78 -0
- package/dist/src/team/teamComposition.js.map +1 -0
- package/dist/src/team/teamDefinitions.d.ts +10 -1
- package/dist/src/team/teamDefinitions.d.ts.map +1 -1
- package/dist/src/team/teamDefinitions.js +123 -16
- package/dist/src/team/teamDefinitions.js.map +1 -1
- package/dist/src/team/teamGraph.d.ts +1 -1
- package/dist/src/team/teamGraph.d.ts.map +1 -1
- package/dist/src/team/teamGraph.js +1 -1
- package/dist/src/team/teamGraph.js.map +1 -1
- package/dist/src/team/teamPrompts.d.ts.map +1 -1
- package/dist/src/team/teamPrompts.js +0 -4
- package/dist/src/team/teamPrompts.js.map +1 -1
- package/dist/src/team/teamProposalService.d.ts +9 -1
- package/dist/src/team/teamProposalService.d.ts.map +1 -1
- package/dist/src/team/teamProposalService.js +66 -6
- package/dist/src/team/teamProposalService.js.map +1 -1
- package/dist/src/team/teamPropose.d.ts +3 -1
- package/dist/src/team/teamPropose.d.ts.map +1 -1
- package/dist/src/team/teamPropose.js +16 -10
- package/dist/src/team/teamPropose.js.map +1 -1
- package/dist/src/team/teamRuntime.d.ts +5 -0
- package/dist/src/team/teamRuntime.d.ts.map +1 -1
- package/dist/src/team/teamRuntime.js +23 -5
- package/dist/src/team/teamRuntime.js.map +1 -1
- package/dist/src/team/workflowSquad.d.ts +10 -2
- package/dist/src/team/workflowSquad.d.ts.map +1 -1
- package/dist/src/team/workflowSquad.js +250 -12
- package/dist/src/team/workflowSquad.js.map +1 -1
- package/dist/src/tools/enterWorktree.d.ts.map +1 -1
- package/dist/src/tools/enterWorktree.js +3 -0
- package/dist/src/tools/enterWorktree.js.map +1 -1
- package/dist/src/tools/exaSearch.d.ts +2 -2
- package/dist/src/tools/exitWorktree.d.ts.map +1 -1
- package/dist/src/tools/exitWorktree.js +3 -1
- package/dist/src/tools/exitWorktree.js.map +1 -1
- package/dist/src/tools/hadamardCoreTools.d.ts +5 -14
- package/dist/src/tools/hadamardCoreTools.d.ts.map +1 -1
- package/dist/src/tools/hadamardCoreTools.js +31 -0
- package/dist/src/tools/hadamardCoreTools.js.map +1 -1
- package/dist/src/tools/hadamardFileTools.d.ts.map +1 -1
- package/dist/src/tools/hadamardFileTools.js +116 -38
- package/dist/src/tools/hadamardFileTools.js.map +1 -1
- package/dist/src/tools/planMode/PlanModeTools.js +2 -2
- package/dist/src/tools/planMode/PlanModeTools.js.map +1 -1
- package/dist/src/tui/hadamardTui.d.ts.map +1 -1
- package/dist/src/tui/hadamardTui.js +358 -69
- package/dist/src/tui/hadamardTui.js.map +1 -1
- package/dist/src/tui/pendingInput.d.ts +17 -0
- package/dist/src/tui/pendingInput.d.ts.map +1 -0
- package/dist/src/tui/pendingInput.js +23 -0
- package/dist/src/tui/pendingInput.js.map +1 -0
- package/dist/src/tui/reasoningDisplay.d.ts +13 -0
- package/dist/src/tui/reasoningDisplay.d.ts.map +1 -0
- package/dist/src/tui/reasoningDisplay.js +45 -0
- package/dist/src/tui/reasoningDisplay.js.map +1 -0
- package/dist/src/tui/screen.d.ts +11 -1
- package/dist/src/tui/screen.d.ts.map +1 -1
- package/dist/src/tui/screen.js +64 -34
- package/dist/src/tui/screen.js.map +1 -1
- package/dist/src/types.d.ts +171 -4
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/ui/commandSurface.d.ts +8 -0
- package/dist/src/ui/commandSurface.d.ts.map +1 -1
- package/dist/src/ui/commandSurface.js +71 -7
- package/dist/src/ui/commandSurface.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/gui/projectSettings.d.ts +0 -2
- package/dist/src/gui/projectSettings.d.ts.map +0 -1
- package/dist/src/gui/projectSettings.js +0 -2
- package/dist/src/gui/projectSettings.js.map +0 -1
|
@@ -10,7 +10,7 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
10
10
|
import fs from 'node:fs';
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import * as readline from 'node:readline';
|
|
13
|
-
import { createHadamardCoreTools, createAgentSdk, createHadamardBridgeSdk, detectBridgeProviders, loadDefaultHadamardSettings, loadJsonConfigFile, listWorkflows, loadWorkflow, listTeamDefinitions, loadTeamDefinition, cloneTeamDefinition, instantiateTeamDefinition, listTeamAgentLabels, countTeamAgents,
|
|
13
|
+
import { createHadamardCoreTools, createAgentSdk, createHadamardBridgeSdk, detectBridgeProviders, loadDefaultHadamardSettings, loadJsonConfigFile, listWorkflows, loadWorkflow, saveWorkflow, deleteWorkflow, listTeamDefinitions, loadTeamDefinition, cloneTeamDefinition, deleteTeamDefinition, getBuiltInTeamDefinition, instantiateTeamDefinition, listTeamAgentLabels, countTeamAgents, askTeamDefinition, createTeamTool, readTeamPreferences, createManagerTools, createAssistantTeamTools, buildAssistantTeamSystemPrompt, TeamProposalStore, SessionCatalog, createAssistantGlobalTools, buildAssistantGlobalSystemPrompt, readAssistantConfig, writeAssistantConfig, buildManagerSystemPrompt, buildUpdateProgressPrompt, formatManagerUpdatePreview, resolveGitHubDigestForUpdate, readManagerConfig, writeManagerConfig, readProjectPlanFile, readProgressFile, managerProgressPath, createProjectIssue, executeProjectIssue, isIssueStatus, isIssueStorageMode, listProjectIssues, listScheduledAutomationTasks, upsertScheduledAutomationTask, resolveHadamardHome, externalSkillPreferencesToRuntimeOptions, readHadamardExternalSkillPreferences, createManagedPluginRuntime, patchManagedPluginSettings, readManagedPluginCatalog, listRouterProfiles, loadRouterProfile, resolveRoutedRun, transitionProjectIssue, WorktreeService, } from '../index.js';
|
|
14
14
|
import { adaptBridgeRun } from '../parity/bridgeEventAdapter.js';
|
|
15
15
|
import { ExternalCliRuntimeManager } from '../parity/externalCliRuntimeManager.js';
|
|
16
16
|
import { externalCliSessionMatchesConfig, listExternalCliSessions, readExternalCliSession, } from '../parity/externalCliSessions.js';
|
|
@@ -22,6 +22,7 @@ import { createPreToolUseHookClassifier, readPreToolUseHooks, readPostToolUseHoo
|
|
|
22
22
|
import { parseTypedHooks } from '../hooks/hookConfig.js';
|
|
23
23
|
import { isRecord } from '../runtime/helpers.js';
|
|
24
24
|
import { getLoadedJsonConfig } from '../config/loadJsonConfigFile.js';
|
|
25
|
+
import { buildModelConfigurationCatalog, findModelConfiguration, resolveHadamardConfigurationModel, } from '../config/modelConfigurationCatalog.js';
|
|
25
26
|
import { findBridgeConfig, maskApiKey, readBridgeConfigs, addBridgeConfig, removeBridgeConfig, isManagedExternalCliRuntime, } from '../parity/bridgeConfigs.js';
|
|
26
27
|
import { buildRouteModelApi } from '../router/modelRouter.js';
|
|
27
28
|
import { addMcpServer, readMcpServerConfig, removeMcpServer } from '../mcp/mcpServerConfig.js';
|
|
@@ -32,14 +33,16 @@ import { planFilePath, readPlanFile } from '../tools/planMode/PlanModeTools.js';
|
|
|
32
33
|
import { loadProjectContext } from '../memory/projectContext.js';
|
|
33
34
|
import { LegacySurfaceEventPipeline, } from '../surfaces/index.js';
|
|
34
35
|
import { pathToFileURL } from 'node:url';
|
|
35
|
-
import { HADAMARD_INTERACTIVE_COMMANDS, SUBCOMMAND_DESCRIPTIONS, filterInteractiveCommands, interactiveCommandUsage, selectInteractiveCommand, } from '../ui/commandSurface.js';
|
|
36
|
+
import { HADAMARD_INTERACTIVE_COMMANDS, SUBCOMMAND_DESCRIPTIONS, canRunInteractiveCommand, filterInteractiveCommands, interactiveCommandRunPolicy, interactiveCommandUsage, parseTeamAskArguments, selectInteractiveCommand, } from '../ui/commandSurface.js';
|
|
36
37
|
import { createAgentExecutionProjectView, createAgentExecutionRootView, formatAgentExecutionTreeLines, } from '../ui/agentExecutionView.js';
|
|
37
38
|
import { A, stringWidth, truncateToWidth, wrapToWidth } from './ansi.js';
|
|
38
39
|
import { InputEditor } from './editor.js';
|
|
39
40
|
import { discoverHadamardPlugins } from './pluginCatalog.js';
|
|
40
41
|
import { TuiScreen } from './screen.js';
|
|
42
|
+
import { ReasoningDisplayState } from './reasoningDisplay.js';
|
|
43
|
+
import { recallLatestFollowUp, restoreAbandonedFollowUp, submitActiveInput, } from './pendingInput.js';
|
|
41
44
|
import { filterTuiSelectionItems, moveTuiSelection, } from './selection.js';
|
|
42
|
-
import { StreamFlusher, formatBanner, formatCompactNotice, formatDivider, formatEditCall, formatErrorLine, formatInfoLine, formatQueuedPrompt,
|
|
45
|
+
import { StreamFlusher, formatBanner, formatCompactNotice, formatDivider, formatEditCall, formatErrorLine, formatInfoLine, formatQueuedPrompt, formatToolCall, formatToolResult, formatUserPrompt, } from './transcript.js';
|
|
43
46
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
44
47
|
const CTRL_C_EXIT_WINDOW_MS = 600;
|
|
45
48
|
const DYNAMIC_FRAME_MS = 33;
|
|
@@ -355,6 +358,8 @@ export async function runHadamardTui(options = {}) {
|
|
|
355
358
|
let running = false;
|
|
356
359
|
let commandBusy = false;
|
|
357
360
|
let shuttingDown = false;
|
|
361
|
+
/** Follow-up pulled out of the queue into the editor; restored if abandoned. */
|
|
362
|
+
let recalledFollowUp = null;
|
|
358
363
|
// Active team tool the main agent may call (toggled via /team). null = no team.
|
|
359
364
|
// The tool is only injected into runs when preferences.team.autoInvoke is on;
|
|
360
365
|
// otherwise attach is a selection and /team ask stays the manual path.
|
|
@@ -684,7 +689,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
684
689
|
let ctrlCCount = 0;
|
|
685
690
|
let ctrlCTimer = null;
|
|
686
691
|
let streamedTextSeen = false;
|
|
687
|
-
|
|
692
|
+
const reasoningDisplay = new ReasoningDisplayState();
|
|
688
693
|
// Track tool names by callId for PostToolUse hook context.
|
|
689
694
|
const toolCallNames = new Map();
|
|
690
695
|
// Live todo list (captured from TodoWrite tool calls). Rendered as a
|
|
@@ -884,6 +889,16 @@ export async function runHadamardTui(options = {}) {
|
|
|
884
889
|
lines.push(promptDivider());
|
|
885
890
|
return lines;
|
|
886
891
|
}
|
|
892
|
+
function promptCursorPosition(promptStartLine) {
|
|
893
|
+
if (editor.isEmpty()) {
|
|
894
|
+
return { line: promptStartLine + 1, column: 2 };
|
|
895
|
+
}
|
|
896
|
+
const visual = editor.visualLines(Math.max(screen.width - 5, 7));
|
|
897
|
+
return {
|
|
898
|
+
line: promptStartLine + 1 + visual.cursorRow,
|
|
899
|
+
column: 2 + visual.cursorCol,
|
|
900
|
+
};
|
|
901
|
+
}
|
|
887
902
|
// ── @-mention file completion ──────────────────────────────────────
|
|
888
903
|
// Prefer git's view (tracked + untracked, honoring .gitignore) so the list
|
|
889
904
|
// matches what the agent actually sees; fall back to a bounded fs walk for
|
|
@@ -1034,7 +1049,10 @@ export async function runHadamardTui(options = {}) {
|
|
|
1034
1049
|
const selected = index === menuSelected;
|
|
1035
1050
|
const command = `/${name}`.padEnd(commandWidth);
|
|
1036
1051
|
const label = selected ? `${A.inverse}${command}${A.reset}` : command;
|
|
1037
|
-
const
|
|
1052
|
+
const commandName = name.split(/\s/u, 1)[0] ?? name;
|
|
1053
|
+
const unavailable = running && interactiveCommandRunPolicy(commandName) === 'idle-only';
|
|
1054
|
+
const summary = TUI_SLASH_COMMANDS[name] ?? SUBCOMMAND_DESCRIPTIONS[name] ?? '';
|
|
1055
|
+
const description = truncateToWidth(unavailable ? `${summary} · after current run` : summary, descriptionWidth);
|
|
1038
1056
|
return `${label} ${A.dim}${description}${A.reset}`;
|
|
1039
1057
|
});
|
|
1040
1058
|
const hiddenAbove = windowStart;
|
|
@@ -1047,6 +1065,14 @@ export async function runHadamardTui(options = {}) {
|
|
|
1047
1065
|
parts.push(`↓${hiddenBelow}`);
|
|
1048
1066
|
lines.push(`${A.dim} ${parts.join(' ')} more · ${menuSelected + 1}/${matches.length} (↑/↓ to scroll)${A.reset}`);
|
|
1049
1067
|
}
|
|
1068
|
+
const selected = matches[Math.min(menuSelected, matches.length - 1)];
|
|
1069
|
+
if (selected) {
|
|
1070
|
+
const commandName = selected.split(/\s/u, 1)[0] ?? selected;
|
|
1071
|
+
const availability = interactiveCommandRunPolicy(commandName) === 'during-run'
|
|
1072
|
+
? 'available while working'
|
|
1073
|
+
: 'idle session';
|
|
1074
|
+
lines.push(`${A.dim} ${interactiveCommandUsage(commandName)} · ${availability}${A.reset}`);
|
|
1075
|
+
}
|
|
1050
1076
|
return lines;
|
|
1051
1077
|
}
|
|
1052
1078
|
function buildDialog() {
|
|
@@ -1142,7 +1168,9 @@ export async function runHadamardTui(options = {}) {
|
|
|
1142
1168
|
const ctxColor = pct >= 90 ? A.red : pct >= 70 ? A.yellow : A.dim;
|
|
1143
1169
|
const modelLabel = activeRouter
|
|
1144
1170
|
? `router:${activeRouter.name}${routedModelLabel ? ` → ${routedModelLabel}` : ''}`
|
|
1145
|
-
:
|
|
1171
|
+
: !bridgeMode && activeBridgeConfig?.runtime === 'hadamard' && bridgeModelLabel
|
|
1172
|
+
? `${activeBridgeConfig.name}:${bridgeModelLabel}`
|
|
1173
|
+
: session.model;
|
|
1146
1174
|
const bridgeTag = bridgeMode && activeBridgeConfig
|
|
1147
1175
|
? ` · bridge:${activeBridgeConfig.name}${bridgeModelLabel ? ` · ${bridgeModelLabel}` : ''}`
|
|
1148
1176
|
: '';
|
|
@@ -1167,10 +1195,39 @@ export async function runHadamardTui(options = {}) {
|
|
|
1167
1195
|
}
|
|
1168
1196
|
function buildHintLine() {
|
|
1169
1197
|
if (running) {
|
|
1170
|
-
return [`${A.dim} enter
|
|
1198
|
+
return [`${A.dim} enter queue follow-up · shift+enter steer now · ↑ recall queued · esc interrupt${A.reset}`];
|
|
1171
1199
|
}
|
|
1172
1200
|
return [`${A.dim} ? shortcuts · / commands · @ files · \\↵ newline · ↑↓ history · ctrl+c clear/exit${A.reset}`];
|
|
1173
1201
|
}
|
|
1202
|
+
function buildPendingInputPanel() {
|
|
1203
|
+
const pending = session.pendingInputs;
|
|
1204
|
+
const visible = pending.followUps.slice(-3);
|
|
1205
|
+
const hidden = pending.followUps.length - visible.length;
|
|
1206
|
+
const lines = visible.map((text, index) => (`${A.dim} queued ${hidden + index + 1}: ${truncateToWidth(text.replace(/\s+/g, ' '), Math.max(screen.width - 15, 12))}${A.reset}`));
|
|
1207
|
+
if (pending.steering.length > 0) {
|
|
1208
|
+
lines.push(`${A.dim} ${pending.steering.length} immediate steering message${pending.steering.length === 1 ? '' : 's'} pending${A.reset}`);
|
|
1209
|
+
}
|
|
1210
|
+
return lines;
|
|
1211
|
+
}
|
|
1212
|
+
function selectionDialogCursorPosition(startLine) {
|
|
1213
|
+
if (!selectionDialog?.searchable)
|
|
1214
|
+
return undefined;
|
|
1215
|
+
return {
|
|
1216
|
+
line: startLine + 2 + (selectionDialog.subtitle ? 1 : 0),
|
|
1217
|
+
column: 4 + stringWidth(selectionDialog.query),
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
function textInputDialogCursorPosition(startLine) {
|
|
1221
|
+
if (!textInputDialog)
|
|
1222
|
+
return undefined;
|
|
1223
|
+
const valueBeforeCursor = textInputDialog.secret
|
|
1224
|
+
? '•'.repeat(textInputDialog.editor.cursor)
|
|
1225
|
+
: textInputDialog.editor.text.slice(0, textInputDialog.editor.cursor);
|
|
1226
|
+
return {
|
|
1227
|
+
line: startLine + 2 + (textInputDialog.description ? 1 : 0),
|
|
1228
|
+
column: 2 + stringWidth(`${textInputDialog.label}: `) + stringWidth(valueBeforeCursor),
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1174
1231
|
function buildTodoPanel() {
|
|
1175
1232
|
if (currentTodos.length === 0)
|
|
1176
1233
|
return [];
|
|
@@ -1206,7 +1263,14 @@ export async function runHadamardTui(options = {}) {
|
|
|
1206
1263
|
}
|
|
1207
1264
|
function renderDynamic() {
|
|
1208
1265
|
const lines = [];
|
|
1266
|
+
let promptCursor;
|
|
1209
1267
|
lines.push(...buildStatusLine());
|
|
1268
|
+
if (running && session.pendingInputCount > 0) {
|
|
1269
|
+
lines.push(...buildPendingInputPanel());
|
|
1270
|
+
}
|
|
1271
|
+
if (reasoningDisplay.hasActive) {
|
|
1272
|
+
lines.push(...reasoningDisplay.liveLines(screen.width));
|
|
1273
|
+
}
|
|
1210
1274
|
const tail = flusher.tail();
|
|
1211
1275
|
if (running && tail) {
|
|
1212
1276
|
lines.push(tail);
|
|
@@ -1220,13 +1284,19 @@ export async function runHadamardTui(options = {}) {
|
|
|
1220
1284
|
lines.push(...buildDialog());
|
|
1221
1285
|
}
|
|
1222
1286
|
else if (selectionDialog) {
|
|
1287
|
+
const dialogStartLine = lines.length;
|
|
1223
1288
|
lines.push(...buildSelectionDialog());
|
|
1289
|
+
promptCursor = selectionDialogCursorPosition(dialogStartLine);
|
|
1224
1290
|
}
|
|
1225
1291
|
else if (textInputDialog) {
|
|
1292
|
+
const dialogStartLine = lines.length;
|
|
1226
1293
|
lines.push(...buildTextInputDialog());
|
|
1294
|
+
promptCursor = textInputDialogCursorPosition(dialogStartLine);
|
|
1227
1295
|
}
|
|
1228
1296
|
else {
|
|
1297
|
+
const promptStartLine = lines.length;
|
|
1229
1298
|
lines.push(...buildPromptBar());
|
|
1299
|
+
promptCursor = promptCursorPosition(promptStartLine);
|
|
1230
1300
|
const atMenu = buildAtMenu();
|
|
1231
1301
|
if (atMenu.length > 0) {
|
|
1232
1302
|
lines.push(...atMenu);
|
|
@@ -1236,7 +1306,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
1236
1306
|
lines.push(...(menu.length > 0 ? menu : buildHintLine()));
|
|
1237
1307
|
}
|
|
1238
1308
|
}
|
|
1239
|
-
screen.setDynamic(lines);
|
|
1309
|
+
screen.setDynamic(lines, promptCursor);
|
|
1240
1310
|
}
|
|
1241
1311
|
function scheduleDynamicRender() {
|
|
1242
1312
|
if (dynamicRenderTimer)
|
|
@@ -1255,6 +1325,11 @@ export async function runHadamardTui(options = {}) {
|
|
|
1255
1325
|
function appendStatic(lines) {
|
|
1256
1326
|
screen.appendStatic(lines);
|
|
1257
1327
|
}
|
|
1328
|
+
function collapseReasoning() {
|
|
1329
|
+
const lines = reasoningDisplay.complete();
|
|
1330
|
+
if (lines.length > 0)
|
|
1331
|
+
appendStatic(lines);
|
|
1332
|
+
}
|
|
1258
1333
|
// Inline CLI spinner for long async operations (probe, reload).
|
|
1259
1334
|
// Writes directly to stdout with \r — independent of the redrawable region.
|
|
1260
1335
|
const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
@@ -1415,7 +1490,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
1415
1490
|
if (activeRouter && !bridgeMode) {
|
|
1416
1491
|
try {
|
|
1417
1492
|
const decision = await resolveRoutedRun(activeRouter, text, abortCtrl.signal);
|
|
1418
|
-
routed = { model: decision.model, modelApi: decision.modelApi };
|
|
1493
|
+
routed = { model: decision.model, modelApi: decision.modelApi, effort: decision.effort };
|
|
1419
1494
|
routedModelLabel = `${decision.label} (${decision.model})`;
|
|
1420
1495
|
appendStatic(formatInfoLine(`router → ${routedModelLabel}`));
|
|
1421
1496
|
}
|
|
@@ -1466,14 +1541,18 @@ export async function runHadamardTui(options = {}) {
|
|
|
1466
1541
|
resultPromise = stream.result;
|
|
1467
1542
|
}
|
|
1468
1543
|
else {
|
|
1544
|
+
const selectedConfigModel = resolveHadamardConfigurationModel(activeBridgeConfig, bridgeMode, bridgeModelLabel);
|
|
1469
1545
|
const stream = session.stream(expandImageRefs(text), {
|
|
1470
1546
|
systemPrompt: systemPrompt + buildAgentContext(),
|
|
1471
1547
|
signal: abortCtrl.signal,
|
|
1472
1548
|
permissionMode: currentPermissionMode(),
|
|
1473
|
-
effort
|
|
1549
|
+
// A matched route's effort overrides the session effort for this turn.
|
|
1550
|
+
effort: routed?.effort ?? currentEffort(),
|
|
1474
1551
|
approver,
|
|
1475
1552
|
classifier: preToolUseHookClassifier,
|
|
1476
|
-
...(routed
|
|
1553
|
+
...(routed
|
|
1554
|
+
? { model: routed.model, modelApi: routed.modelApi }
|
|
1555
|
+
: selectedConfigModel ? { model: selectedConfigModel } : {}),
|
|
1477
1556
|
...(activeTeamTool && teamPrefs.autoInvoke ? { tools: [...tools, activeTeamTool] } : {}),
|
|
1478
1557
|
...(canUseTool ? { canUseTool } : {}),
|
|
1479
1558
|
});
|
|
@@ -1487,6 +1566,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
1487
1566
|
}
|
|
1488
1567
|
}
|
|
1489
1568
|
const result = await resultPromise;
|
|
1569
|
+
collapseReasoning();
|
|
1490
1570
|
// Accumulate token + USD usage for /cost and /usage. The model is the
|
|
1491
1571
|
// routed model (if a router is active) or the session model. Bridge runs
|
|
1492
1572
|
recordUsage(routed?.model ?? activeBridgeModelApi?.model ?? bridgeModelLabel ?? session.model, result.usage);
|
|
@@ -1517,6 +1597,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
1517
1597
|
appendStatic(['']);
|
|
1518
1598
|
}
|
|
1519
1599
|
catch (error) {
|
|
1600
|
+
collapseReasoning();
|
|
1520
1601
|
const rest = flusher.drain();
|
|
1521
1602
|
if (rest.length > 0)
|
|
1522
1603
|
appendStatic(rest);
|
|
@@ -1548,7 +1629,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
1548
1629
|
switch (event.type) {
|
|
1549
1630
|
case 'run.started':
|
|
1550
1631
|
streamedTextSeen = false;
|
|
1551
|
-
|
|
1632
|
+
reasoningDisplay.reset();
|
|
1552
1633
|
return;
|
|
1553
1634
|
case 'request.started':
|
|
1554
1635
|
lastTokenEstimate = typeof data.requestTokenEstimate === 'number'
|
|
@@ -1560,6 +1641,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
1560
1641
|
const delta = typeof data.delta === 'string' ? data.delta : '';
|
|
1561
1642
|
if (!delta)
|
|
1562
1643
|
return;
|
|
1644
|
+
collapseReasoning();
|
|
1563
1645
|
streamedTextSeen = true;
|
|
1564
1646
|
const flushed = flusher.push(delta);
|
|
1565
1647
|
if (flushed.length > 0)
|
|
@@ -1571,10 +1653,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
1571
1653
|
const delta = typeof data.delta === 'string' ? data.delta : '';
|
|
1572
1654
|
if (!delta)
|
|
1573
1655
|
return;
|
|
1574
|
-
|
|
1575
|
-
if (lines.length > 0)
|
|
1576
|
-
appendStatic(lines);
|
|
1577
|
-
streamedThinkingSeen = true;
|
|
1656
|
+
reasoningDisplay.append(delta);
|
|
1578
1657
|
scheduleDynamicRender();
|
|
1579
1658
|
return;
|
|
1580
1659
|
}
|
|
@@ -1590,15 +1669,15 @@ export async function runHadamardTui(options = {}) {
|
|
|
1590
1669
|
}
|
|
1591
1670
|
case 'model.content': {
|
|
1592
1671
|
const content = isRecord(data.content) ? data.content : undefined;
|
|
1593
|
-
if (data.kind === 'content' && content?.type === 'thinking' && !
|
|
1672
|
+
if (data.kind === 'content' && content?.type === 'thinking' && !reasoningDisplay.hasStreamedContent) {
|
|
1594
1673
|
const thinking = typeof content.thinking === 'string' ? content.thinking : '';
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
appendStatic(lines);
|
|
1674
|
+
reasoningDisplay.setCompleteContent(thinking);
|
|
1675
|
+
scheduleDynamicRender();
|
|
1598
1676
|
}
|
|
1599
1677
|
return;
|
|
1600
1678
|
}
|
|
1601
1679
|
case 'tool.started': {
|
|
1680
|
+
collapseReasoning();
|
|
1602
1681
|
const callId = typeof data.callId === 'string' ? data.callId : '';
|
|
1603
1682
|
const publicName = typeof data.publicName === 'string'
|
|
1604
1683
|
? data.publicName
|
|
@@ -1735,35 +1814,37 @@ export async function runHadamardTui(options = {}) {
|
|
|
1735
1814
|
await resumeSession(selected);
|
|
1736
1815
|
}
|
|
1737
1816
|
async function chooseModel() {
|
|
1817
|
+
const catalog = buildModelConfigurationCatalog({
|
|
1818
|
+
model: sdk.config.model,
|
|
1819
|
+
provider: sdk.config.provider,
|
|
1820
|
+
baseURL: sdk.config.baseURL,
|
|
1821
|
+
}, readBridgeConfigs().configs);
|
|
1738
1822
|
const items = [
|
|
1739
|
-
{
|
|
1740
|
-
id:
|
|
1741
|
-
label: '
|
|
1742
|
-
description:
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
.
|
|
1747
|
-
id: `tier:${tier}`,
|
|
1748
|
-
label: tier,
|
|
1749
|
-
description: sdk.config.modelTiers[tier],
|
|
1823
|
+
...catalog.map(config => ({
|
|
1824
|
+
id: config.id,
|
|
1825
|
+
label: config.source === 'default' ? 'default' : config.name,
|
|
1826
|
+
description: [
|
|
1827
|
+
config.execution === 'cli' ? `${config.runtime} CLI` : config.runtime,
|
|
1828
|
+
config.model || 'runtime default',
|
|
1829
|
+
config.models.length > 1 ? `${config.models.length} models` : '',
|
|
1830
|
+
].filter(Boolean).join(' · '),
|
|
1750
1831
|
})),
|
|
1751
1832
|
{
|
|
1752
1833
|
id: 'custom',
|
|
1753
1834
|
label: 'Enter a model ID',
|
|
1754
|
-
description: '
|
|
1835
|
+
description: 'Use the default provider for this session',
|
|
1755
1836
|
},
|
|
1756
1837
|
{
|
|
1757
1838
|
id: 'configure',
|
|
1758
|
-
label: '
|
|
1759
|
-
description: '
|
|
1839
|
+
label: 'Manage configurations',
|
|
1840
|
+
description: 'Default plus named provider configurations',
|
|
1760
1841
|
},
|
|
1761
1842
|
];
|
|
1762
1843
|
const selected = await selectItem({
|
|
1763
1844
|
title: 'Select model',
|
|
1764
1845
|
subtitle: `Current: ${session.model}`,
|
|
1765
1846
|
items,
|
|
1766
|
-
searchable:
|
|
1847
|
+
searchable: true,
|
|
1767
1848
|
});
|
|
1768
1849
|
if (!selected)
|
|
1769
1850
|
return;
|
|
@@ -1779,15 +1860,38 @@ export async function runHadamardTui(options = {}) {
|
|
|
1779
1860
|
});
|
|
1780
1861
|
if (!model?.trim())
|
|
1781
1862
|
return;
|
|
1863
|
+
await disableBridge();
|
|
1782
1864
|
await session.setModel(model.trim());
|
|
1783
1865
|
}
|
|
1784
|
-
else if (selected === 'default') {
|
|
1785
|
-
await session.setModel(sdk.config.model);
|
|
1786
|
-
}
|
|
1787
1866
|
else {
|
|
1788
|
-
|
|
1867
|
+
const config = findModelConfiguration(catalog, selected);
|
|
1868
|
+
if (!config)
|
|
1869
|
+
return;
|
|
1870
|
+
if (config.source === 'default') {
|
|
1871
|
+
await disableBridge();
|
|
1872
|
+
await session.setModel(sdk.config.model);
|
|
1873
|
+
}
|
|
1874
|
+
else if (config.config) {
|
|
1875
|
+
let selectedConfig = { ...config.config, model: config.model };
|
|
1876
|
+
if (config.models.length > 1) {
|
|
1877
|
+
const model = await selectItem({
|
|
1878
|
+
title: config.name,
|
|
1879
|
+
subtitle: 'Select a model from this configuration',
|
|
1880
|
+
searchable: true,
|
|
1881
|
+
items: config.models.map(item => ({
|
|
1882
|
+
id: item.name,
|
|
1883
|
+
label: item.name,
|
|
1884
|
+
description: item.modality ?? 'text',
|
|
1885
|
+
})),
|
|
1886
|
+
});
|
|
1887
|
+
if (!model)
|
|
1888
|
+
return;
|
|
1889
|
+
selectedConfig = { ...selectedConfig, model };
|
|
1890
|
+
}
|
|
1891
|
+
await activateBridgeConfig(selectedConfig);
|
|
1892
|
+
}
|
|
1789
1893
|
}
|
|
1790
|
-
appendStatic([...formatInfoLine(`model
|
|
1894
|
+
appendStatic([...formatInfoLine(`model configuration: ${activeBridgeConfig?.name ?? 'default'} · ${bridgeModelLabel ?? session.model}`), '']);
|
|
1791
1895
|
}
|
|
1792
1896
|
async function configureModelSettings() {
|
|
1793
1897
|
const store = await resolveHadamardSettingsStore({ configPath: options.configPath });
|
|
@@ -1826,11 +1930,16 @@ export async function runHadamardTui(options = {}) {
|
|
|
1826
1930
|
label: 'Base URL',
|
|
1827
1931
|
description: env.HADAMARD_BASE_URL || 'provider default',
|
|
1828
1932
|
},
|
|
1829
|
-
|
|
1830
|
-
id:
|
|
1831
|
-
label:
|
|
1832
|
-
description: env
|
|
1833
|
-
}
|
|
1933
|
+
{
|
|
1934
|
+
id: 'model',
|
|
1935
|
+
label: 'Default model',
|
|
1936
|
+
description: env.HADAMARD_MODEL || sdk.config.model || 'not configured',
|
|
1937
|
+
},
|
|
1938
|
+
{
|
|
1939
|
+
id: 'named',
|
|
1940
|
+
label: 'Named configurations',
|
|
1941
|
+
description: `${readBridgeConfigs().configs.length} configured`,
|
|
1942
|
+
},
|
|
1834
1943
|
{
|
|
1835
1944
|
id: 'save',
|
|
1836
1945
|
label: 'Save and apply',
|
|
@@ -1900,21 +2009,23 @@ export async function runHadamardTui(options = {}) {
|
|
|
1900
2009
|
}
|
|
1901
2010
|
continue;
|
|
1902
2011
|
}
|
|
1903
|
-
if (selected
|
|
1904
|
-
const tier = selected.slice('tier:'.length).toUpperCase();
|
|
1905
|
-
const key = `HADAMARD_DEFAULT_${tier}_MODEL`;
|
|
2012
|
+
if (selected === 'model') {
|
|
1906
2013
|
const model = await promptText({
|
|
1907
|
-
title:
|
|
2014
|
+
title: 'Configure default model',
|
|
1908
2015
|
label: 'Model ID',
|
|
1909
|
-
initial: env
|
|
2016
|
+
initial: env.HADAMARD_MODEL ?? sdk.config.model,
|
|
1910
2017
|
});
|
|
1911
2018
|
if (model !== undefined) {
|
|
1912
2019
|
if (model.trim())
|
|
1913
|
-
env
|
|
2020
|
+
env.HADAMARD_MODEL = model.trim();
|
|
1914
2021
|
else
|
|
1915
|
-
delete env
|
|
2022
|
+
delete env.HADAMARD_MODEL;
|
|
1916
2023
|
dirty = true;
|
|
1917
2024
|
}
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
if (selected === 'named') {
|
|
2028
|
+
await manageBridgeConfigs();
|
|
1918
2029
|
}
|
|
1919
2030
|
}
|
|
1920
2031
|
}
|
|
@@ -3406,6 +3517,10 @@ export async function runHadamardTui(options = {}) {
|
|
|
3406
3517
|
const spaceIndex = raw.indexOf(' ');
|
|
3407
3518
|
const name = (spaceIndex === -1 ? raw.slice(1) : raw.slice(1, spaceIndex)).toLowerCase();
|
|
3408
3519
|
const args = spaceIndex === -1 ? '' : raw.slice(spaceIndex + 1).trim();
|
|
3520
|
+
if (!canRunInteractiveCommand(name, running)) {
|
|
3521
|
+
appendStatic(formatInfoLine(`/${name} requires an idle session; it will be available after this run`));
|
|
3522
|
+
return;
|
|
3523
|
+
}
|
|
3409
3524
|
appendStatic(formatUserPrompt(raw));
|
|
3410
3525
|
commandBusy = true;
|
|
3411
3526
|
renderDynamic();
|
|
@@ -3432,6 +3547,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
3432
3547
|
}
|
|
3433
3548
|
case 'clear':
|
|
3434
3549
|
process.stdout.write('\x1b[2J\x1b[H');
|
|
3550
|
+
screen.setDynamic([]);
|
|
3435
3551
|
renderDynamic();
|
|
3436
3552
|
return;
|
|
3437
3553
|
case 'init': {
|
|
@@ -3458,8 +3574,71 @@ export async function runHadamardTui(options = {}) {
|
|
|
3458
3574
|
await chooseRouter(args.slice('router'.length).trim());
|
|
3459
3575
|
return;
|
|
3460
3576
|
}
|
|
3461
|
-
|
|
3462
|
-
|
|
3577
|
+
if (args === 'custom' || args.startsWith('custom ')) {
|
|
3578
|
+
const customModel = args.slice('custom'.length).trim();
|
|
3579
|
+
if (!customModel) {
|
|
3580
|
+
appendStatic([...formatErrorLine('usage: /model custom <model-id>'), '']);
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
await disableBridge();
|
|
3584
|
+
await session.setModel(customModel);
|
|
3585
|
+
appendStatic([...formatInfoLine(`custom model: ${session.model}`), '']);
|
|
3586
|
+
return;
|
|
3587
|
+
}
|
|
3588
|
+
const catalog = buildModelConfigurationCatalog({ model: sdk.config.model, provider: sdk.config.provider, baseURL: sdk.config.baseURL }, readBridgeConfigs().configs);
|
|
3589
|
+
// `/model <config>` or `/model <config> <model-id>` — match the longest
|
|
3590
|
+
// known configuration name so model ids with spaces still work.
|
|
3591
|
+
let config = findModelConfiguration(catalog, args);
|
|
3592
|
+
let explicitModel;
|
|
3593
|
+
if (!config) {
|
|
3594
|
+
const tokens = args.split(/\s+/u).filter(Boolean);
|
|
3595
|
+
for (let i = tokens.length - 1; i >= 1; i -= 1) {
|
|
3596
|
+
const candidate = tokens.slice(0, i).join(' ');
|
|
3597
|
+
const match = findModelConfiguration(catalog, candidate);
|
|
3598
|
+
if (match) {
|
|
3599
|
+
config = match;
|
|
3600
|
+
explicitModel = tokens.slice(i).join(' ');
|
|
3601
|
+
break;
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
if (!config) {
|
|
3606
|
+
appendStatic([
|
|
3607
|
+
...formatErrorLine(`unknown model configuration: ${args}`),
|
|
3608
|
+
...formatInfoLine('Use /model to choose one, or /model custom <model-id>.'),
|
|
3609
|
+
'',
|
|
3610
|
+
]);
|
|
3611
|
+
return;
|
|
3612
|
+
}
|
|
3613
|
+
if (config.source === 'default') {
|
|
3614
|
+
await disableBridge();
|
|
3615
|
+
await session.setModel(explicitModel || sdk.config.model);
|
|
3616
|
+
appendStatic([...formatInfoLine(`model configuration: default · ${session.model}`), '']);
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
if (!config.config)
|
|
3620
|
+
return;
|
|
3621
|
+
let selectedConfig = { ...config.config, model: config.model };
|
|
3622
|
+
if (explicitModel) {
|
|
3623
|
+
selectedConfig = { ...selectedConfig, model: explicitModel };
|
|
3624
|
+
}
|
|
3625
|
+
else if (config.models.length > 1) {
|
|
3626
|
+
const model = await selectItem({
|
|
3627
|
+
title: config.name,
|
|
3628
|
+
subtitle: 'Select a model from this configuration',
|
|
3629
|
+
searchable: true,
|
|
3630
|
+
items: config.models.map(item => ({
|
|
3631
|
+
id: item.name,
|
|
3632
|
+
label: item.name,
|
|
3633
|
+
description: item.modality ?? 'text',
|
|
3634
|
+
})),
|
|
3635
|
+
});
|
|
3636
|
+
if (!model)
|
|
3637
|
+
return;
|
|
3638
|
+
selectedConfig = { ...selectedConfig, model };
|
|
3639
|
+
}
|
|
3640
|
+
await activateBridgeConfig(selectedConfig);
|
|
3641
|
+
appendStatic([...formatInfoLine(`model configuration: ${activeBridgeConfig?.name ?? config.name} · ${bridgeModelLabel ?? session.model}`), '']);
|
|
3463
3642
|
return;
|
|
3464
3643
|
}
|
|
3465
3644
|
case 'effort':
|
|
@@ -4234,8 +4413,40 @@ export async function runHadamardTui(options = {}) {
|
|
|
4234
4413
|
await runSavedWorkflow(runSpace === -1 ? runRest : runRest.slice(0, runSpace), runSpace === -1 ? undefined : runRest.slice(runSpace + 1).trim());
|
|
4235
4414
|
return;
|
|
4236
4415
|
}
|
|
4416
|
+
if (args.startsWith('delete ')) {
|
|
4417
|
+
const name = args.slice(7).trim();
|
|
4418
|
+
if (!name) {
|
|
4419
|
+
appendStatic([...formatErrorLine('usage: /workflows delete <name>'), '']);
|
|
4420
|
+
return;
|
|
4421
|
+
}
|
|
4422
|
+
const removed = await deleteWorkflow(name, sdk.config.workDir);
|
|
4423
|
+
appendStatic([...formatInfoLine(removed ? `deleted workflow: ${name}` : `workflow not found: ${name}`), '']);
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
4426
|
+
if (args.startsWith('save ')) {
|
|
4427
|
+
const rest = args.slice(5).trim();
|
|
4428
|
+
const split = rest.indexOf(' ');
|
|
4429
|
+
if (split === -1) {
|
|
4430
|
+
appendStatic([...formatErrorLine('usage: /workflows save <name> <script-path> [--overwrite]'), '']);
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
const name = rest.slice(0, split).trim();
|
|
4434
|
+
const pathParts = rest.slice(split + 1).trim().split(/\s+/);
|
|
4435
|
+
const overwrite = pathParts.includes('--overwrite');
|
|
4436
|
+
const scriptPath = pathParts.filter((part) => part !== '--overwrite').join(' ');
|
|
4437
|
+
try {
|
|
4438
|
+
const resolved = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(sdk.config.workDir, scriptPath);
|
|
4439
|
+
const script = fs.readFileSync(resolved, 'utf8');
|
|
4440
|
+
const filePath = await saveWorkflow(name, script, { projectDir: sdk.config.workDir, overwrite });
|
|
4441
|
+
appendStatic([...formatInfoLine(`saved workflow: ${name} -> ${filePath}`), '']);
|
|
4442
|
+
}
|
|
4443
|
+
catch (error) {
|
|
4444
|
+
appendStatic([...formatErrorLine(error.message || String(error)), '']);
|
|
4445
|
+
}
|
|
4446
|
+
return;
|
|
4447
|
+
}
|
|
4237
4448
|
if (args && args !== 'list') {
|
|
4238
|
-
appendStatic([...formatErrorLine('usage: /workflows [list|run <name> [task]]'), '']);
|
|
4449
|
+
appendStatic([...formatErrorLine('usage: /workflows [list|run <name> [task]|save <name> <script-path>|delete <name>]'), '']);
|
|
4239
4450
|
return;
|
|
4240
4451
|
}
|
|
4241
4452
|
// No sub-command → selection picker.
|
|
@@ -4455,15 +4666,36 @@ export async function runHadamardTui(options = {}) {
|
|
|
4455
4666
|
}
|
|
4456
4667
|
return;
|
|
4457
4668
|
}
|
|
4669
|
+
if (args.startsWith('delete ')) {
|
|
4670
|
+
const teamName = args.slice(7).trim();
|
|
4671
|
+
if (!teamName) {
|
|
4672
|
+
appendStatic([...formatErrorLine('usage: /team delete <name>'), '']);
|
|
4673
|
+
return;
|
|
4674
|
+
}
|
|
4675
|
+
if (getBuiltInTeamDefinition(teamName)) {
|
|
4676
|
+
appendStatic([...formatErrorLine(`cannot delete built-in team: ${teamName}`), '']);
|
|
4677
|
+
return;
|
|
4678
|
+
}
|
|
4679
|
+
const removed = await deleteTeamDefinition(teamName, sdk.config.workDir);
|
|
4680
|
+
if (!removed) {
|
|
4681
|
+
appendStatic([...formatErrorLine(`team not found: ${teamName}`), '']);
|
|
4682
|
+
return;
|
|
4683
|
+
}
|
|
4684
|
+
if (activeTeamName === teamName) {
|
|
4685
|
+
activeTeamTool = null;
|
|
4686
|
+
activeTeamName = null;
|
|
4687
|
+
}
|
|
4688
|
+
appendStatic([...formatInfoLine(`deleted team: ${teamName}`), '']);
|
|
4689
|
+
return;
|
|
4690
|
+
}
|
|
4458
4691
|
if (args.startsWith('ask ')) {
|
|
4459
4692
|
const rest = args.slice(4).trim();
|
|
4460
|
-
const
|
|
4461
|
-
if (
|
|
4693
|
+
const parsed = parseTeamAskArguments(rest);
|
|
4694
|
+
if (!parsed) {
|
|
4462
4695
|
appendStatic([...formatErrorLine('usage: /team ask <name> <prompt>'), '']);
|
|
4463
4696
|
return;
|
|
4464
4697
|
}
|
|
4465
|
-
const teamName =
|
|
4466
|
-
const prompt = rest.slice(spaceIdx + 1).trim();
|
|
4698
|
+
const { name: teamName, prompt } = parsed;
|
|
4467
4699
|
const loaded = loadTeamDefinition(teamName, sdk.config.workDir);
|
|
4468
4700
|
if (!loaded) {
|
|
4469
4701
|
appendStatic([...formatErrorLine(`team not found: ${teamName}`), '']);
|
|
@@ -4489,7 +4721,6 @@ export async function runHadamardTui(options = {}) {
|
|
|
4489
4721
|
'',
|
|
4490
4722
|
]);
|
|
4491
4723
|
try {
|
|
4492
|
-
const team = createModelTeam(definition);
|
|
4493
4724
|
const teamRunView = createTeamRunViewState(definition.name);
|
|
4494
4725
|
const printTeamRunTree = () => {
|
|
4495
4726
|
const lines = formatTeamRunTreeLines(teamRunView);
|
|
@@ -4497,7 +4728,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
4497
4728
|
return;
|
|
4498
4729
|
appendStatic([...lines.map((line) => `${A.dim}${line}${A.reset}`), '']);
|
|
4499
4730
|
};
|
|
4500
|
-
const result = await
|
|
4731
|
+
const result = await askTeamDefinition(definition, prompt, undefined, {
|
|
4501
4732
|
workDir: sdk.config.workDir,
|
|
4502
4733
|
onEvent: (e) => {
|
|
4503
4734
|
applyTeamRunEvent(teamRunView, e);
|
|
@@ -5165,17 +5396,31 @@ export async function runHadamardTui(options = {}) {
|
|
|
5165
5396
|
}
|
|
5166
5397
|
}
|
|
5167
5398
|
// ── Submit / key handling ──────────────────────────────────────────
|
|
5168
|
-
|
|
5169
|
-
|
|
5399
|
+
/** If the editor was emptied after ↑-recall, put the follow-up back in queue. */
|
|
5400
|
+
function restoreRecalledFollowUpIfAbandoned() {
|
|
5401
|
+
if (!recalledFollowUp || !editor.isEmpty())
|
|
5402
|
+
return;
|
|
5403
|
+
restoreAbandonedFollowUp(session, recalledFollowUp);
|
|
5404
|
+
recalledFollowUp = null;
|
|
5405
|
+
}
|
|
5406
|
+
async function submit(mode = 'follow-up') {
|
|
5407
|
+
if (applyAtCompletion()) {
|
|
5170
5408
|
renderDynamic();
|
|
5171
5409
|
return;
|
|
5172
5410
|
}
|
|
5173
|
-
const selectedCommand =
|
|
5174
|
-
? selectInteractiveCommand(editor.text, menuSelected)
|
|
5175
|
-
: undefined;
|
|
5411
|
+
const selectedCommand = selectInteractiveCommand(editor.text, menuSelected);
|
|
5176
5412
|
if (selectedCommand) {
|
|
5413
|
+
const selectedName = selectedCommand.slice(1).split(/\s/u, 1)[0] ?? '';
|
|
5414
|
+
// Slash completions are not a resubmit of the recalled follow-up.
|
|
5415
|
+
restoreAbandonedFollowUp(session, recalledFollowUp);
|
|
5416
|
+
recalledFollowUp = null;
|
|
5177
5417
|
editor.clear();
|
|
5178
5418
|
menuSelected = 0;
|
|
5419
|
+
if (running && !canRunInteractiveCommand(selectedName, true)) {
|
|
5420
|
+
appendStatic(formatInfoLine(`/${selectedName} requires an idle session; it will be available after this run`));
|
|
5421
|
+
renderDynamic();
|
|
5422
|
+
return;
|
|
5423
|
+
}
|
|
5179
5424
|
// A selected completion already contains its subcommand. Keeping the
|
|
5180
5425
|
// partially typed second word would turn `/agents runs` into
|
|
5181
5426
|
// `/agents runs runs` on Enter.
|
|
@@ -5190,17 +5435,31 @@ export async function runHadamardTui(options = {}) {
|
|
|
5190
5435
|
const text = value.trim();
|
|
5191
5436
|
menuSelected = 0;
|
|
5192
5437
|
if (!text) {
|
|
5438
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5193
5439
|
renderDynamic();
|
|
5194
5440
|
return;
|
|
5195
5441
|
}
|
|
5196
5442
|
if (running) {
|
|
5197
5443
|
if (text.startsWith('/')) {
|
|
5198
|
-
|
|
5444
|
+
// Slash commands are not a resubmit of the recalled follow-up.
|
|
5445
|
+
restoreAbandonedFollowUp(session, recalledFollowUp);
|
|
5446
|
+
recalledFollowUp = null;
|
|
5447
|
+
const command = text.slice(1).trim().split(/\s/u, 1)[0]?.toLowerCase() ?? '';
|
|
5448
|
+
if (!canRunInteractiveCommand(command, true)) {
|
|
5449
|
+
appendStatic(formatInfoLine(`/${command} requires an idle session; it will be available after this run`));
|
|
5450
|
+
renderDynamic();
|
|
5451
|
+
return;
|
|
5452
|
+
}
|
|
5453
|
+
await runSlashCommand(text);
|
|
5199
5454
|
renderDynamic();
|
|
5200
5455
|
return;
|
|
5201
5456
|
}
|
|
5202
|
-
|
|
5203
|
-
|
|
5457
|
+
// Resubmit consumes the recalled draft (re-queued below).
|
|
5458
|
+
recalledFollowUp = null;
|
|
5459
|
+
submitActiveInput(session, text, mode);
|
|
5460
|
+
appendStatic(mode === 'steer'
|
|
5461
|
+
? formatInfoLine(`steering now: ${text.replace(/\s+/g, ' ')}`)
|
|
5462
|
+
: formatQueuedPrompt(text));
|
|
5204
5463
|
renderDynamic();
|
|
5205
5464
|
return;
|
|
5206
5465
|
}
|
|
@@ -5209,9 +5468,12 @@ export async function runHadamardTui(options = {}) {
|
|
|
5209
5468
|
return;
|
|
5210
5469
|
}
|
|
5211
5470
|
if (text.startsWith('/')) {
|
|
5471
|
+
restoreAbandonedFollowUp(session, recalledFollowUp);
|
|
5472
|
+
recalledFollowUp = null;
|
|
5212
5473
|
await runSlashCommand(text);
|
|
5213
5474
|
return;
|
|
5214
5475
|
}
|
|
5476
|
+
recalledFollowUp = null;
|
|
5215
5477
|
void startRun(text);
|
|
5216
5478
|
}
|
|
5217
5479
|
function handleDialogKey(key) {
|
|
@@ -5392,6 +5654,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
5392
5654
|
else if (!editor.isEmpty()) {
|
|
5393
5655
|
editor.clear();
|
|
5394
5656
|
menuSelected = 0;
|
|
5657
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5395
5658
|
}
|
|
5396
5659
|
renderDynamic();
|
|
5397
5660
|
return;
|
|
@@ -5402,6 +5665,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
5402
5665
|
return;
|
|
5403
5666
|
}
|
|
5404
5667
|
editor.deleteForward();
|
|
5668
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5405
5669
|
break;
|
|
5406
5670
|
case 'a':
|
|
5407
5671
|
editor.moveHome();
|
|
@@ -5411,12 +5675,15 @@ export async function runHadamardTui(options = {}) {
|
|
|
5411
5675
|
break;
|
|
5412
5676
|
case 'k':
|
|
5413
5677
|
editor.killToEnd();
|
|
5678
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5414
5679
|
break;
|
|
5415
5680
|
case 'u':
|
|
5416
5681
|
editor.killToStart();
|
|
5682
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5417
5683
|
break;
|
|
5418
5684
|
case 'w':
|
|
5419
5685
|
editor.deleteWordLeft();
|
|
5686
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5420
5687
|
break;
|
|
5421
5688
|
case 'left':
|
|
5422
5689
|
editor.moveWordLeft();
|
|
@@ -5438,6 +5705,13 @@ export async function runHadamardTui(options = {}) {
|
|
|
5438
5705
|
}
|
|
5439
5706
|
switch (name) {
|
|
5440
5707
|
case 'return': {
|
|
5708
|
+
if (key.shift) {
|
|
5709
|
+
if (running)
|
|
5710
|
+
void submit('steer');
|
|
5711
|
+
else
|
|
5712
|
+
editor.insert('\n');
|
|
5713
|
+
return;
|
|
5714
|
+
}
|
|
5441
5715
|
if (key.meta) {
|
|
5442
5716
|
editor.insert('\n');
|
|
5443
5717
|
break;
|
|
@@ -5446,6 +5720,10 @@ export async function runHadamardTui(options = {}) {
|
|
|
5446
5720
|
return;
|
|
5447
5721
|
}
|
|
5448
5722
|
case 'enter':
|
|
5723
|
+
if (key.shift && running) {
|
|
5724
|
+
void submit('steer');
|
|
5725
|
+
return;
|
|
5726
|
+
}
|
|
5449
5727
|
editor.insert('\n');
|
|
5450
5728
|
break;
|
|
5451
5729
|
case 'escape': {
|
|
@@ -5455,6 +5733,7 @@ export async function runHadamardTui(options = {}) {
|
|
|
5455
5733
|
else if (!editor.isEmpty()) {
|
|
5456
5734
|
editor.clear();
|
|
5457
5735
|
menuSelected = 0;
|
|
5736
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5458
5737
|
}
|
|
5459
5738
|
break;
|
|
5460
5739
|
}
|
|
@@ -5462,9 +5741,11 @@ export async function runHadamardTui(options = {}) {
|
|
|
5462
5741
|
editor.backspace();
|
|
5463
5742
|
menuSelected = 0;
|
|
5464
5743
|
atSelected = 0;
|
|
5744
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5465
5745
|
break;
|
|
5466
5746
|
case 'delete':
|
|
5467
5747
|
editor.deleteForward();
|
|
5748
|
+
restoreRecalledFollowUpIfAbandoned();
|
|
5468
5749
|
break;
|
|
5469
5750
|
case 'left':
|
|
5470
5751
|
if (key.meta)
|
|
@@ -5485,6 +5766,14 @@ export async function runHadamardTui(options = {}) {
|
|
|
5485
5766
|
editor.moveEnd();
|
|
5486
5767
|
break;
|
|
5487
5768
|
case 'up': {
|
|
5769
|
+
if (running && editor.isEmpty()) {
|
|
5770
|
+
const recalled = recallLatestFollowUp(session);
|
|
5771
|
+
if (recalled) {
|
|
5772
|
+
recalledFollowUp = recalled;
|
|
5773
|
+
editor.setText(recalled);
|
|
5774
|
+
break;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5488
5777
|
const atToken = activeAtToken(editor.text, editor.cursor);
|
|
5489
5778
|
const atCount = atToken ? atCompletions(atToken.token).length : 0;
|
|
5490
5779
|
const menu = filterSlashCommands(editor.text);
|