@wrongstack/cli 0.272.2 → 0.273.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1149 -399
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, DefaultSecretScrubber, projectHash, wstackGlobalRoot, TOKENS, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, normalizeTokenSavingTier, SlashCommandRegistry, attachDepWatcherBridge, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DEFAULT_CONTEXT_WINDOW_MODE_ID, resolveProjectDir, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, resolveProviderModelList, DefaultLogger, DefaultModelsRegistry, isStdinTTY, atomicWrite, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, createHqPublisherFromEnv, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, createDefaultPipelines, applyModelRuntime, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool,
|
|
2
|
+
import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, DefaultSecretScrubber, resolveWstackPaths, projectHash, wstackGlobalRoot, TOKENS, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, normalizeTokenSavingTier, SlashCommandRegistry, attachDepWatcherBridge, createFallbackModelExtension, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, SddRunRegistry, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DEFAULT_CONTEXT_WINDOW_MODE_ID, resolveProjectDir, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, resolveProviderModelList, DefaultLogger, DefaultModelsRegistry, isStdinTTY, atomicWrite, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, applyToolDescriptionModes, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, createHqPublisherFromEnv, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, createDefaultPipelines, applyModelRuntime, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, AdaptiveConcurrencyController, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, WIDE_SUBAGENT_CAPABILITIES, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, assignNickname, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, DefaultSessionRewinder, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, setToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, readHqRuntimeFileSync, resolveHqConfig, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, onResize, InputBuilder, DefaultSecretVault, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, HQ_AUTH_FILE_VERSION, FsError, ERROR_CODES, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, ConfigError, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, DefaultTokenCounter, isSecretField as isSecretField$1, startSessionTelemetryBridge } from '@wrongstack/core';
|
|
3
3
|
import * as fs4 from 'fs/promises';
|
|
4
4
|
import { decryptConfigSecrets, encryptConfigSecrets, DefaultSecretVault as DefaultSecretVault$1, isSecretField } from '@wrongstack/core/security';
|
|
5
5
|
import * as path4 from 'path';
|
|
@@ -8,11 +8,11 @@ import * as crypto2 from 'crypto';
|
|
|
8
8
|
import { createHash, randomUUID, randomBytes } from 'crypto';
|
|
9
9
|
import * as http from 'http';
|
|
10
10
|
import { createServer } from 'http';
|
|
11
|
-
import * as
|
|
12
|
-
import
|
|
11
|
+
import * as os7 from 'os';
|
|
12
|
+
import os7__default from 'os';
|
|
13
13
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
|
-
import { findFreePort, AutoPhaseWebSocketHandler, WorktreeWebSocketHandler, generateAuthToken, verifyClient, handleGitInfo, handleShellOpen, handleGitDiff, handleGitChanges, handleSkillsExport, handleSkillsEdit, handleSkillsCreate, handleSkillsUpdate, handleSkillsUninstall, handleSkillsInstall, handleSkillsContent, handleMcpRestart, handleMcpDisable, handleMcpEnable, handleMcpDiscover, handleMcpSleep, handleMcpWake, handleMcpUpdate, handleMcpRemove, handleMcpAdd, handleMcpList, handleMemoryForget, handleMemoryRemember, handleMemoryList, handleCompletionRequest, createToolLspCompletionSource, handleFilesWrite, handleFilesRead, handleFilesTree, handleFilesList, createEternalSubscription, createHttpServer, registerInstance, openBrowser as openBrowser$1, unregisterInstance, estimateTokens as estimateTokens$1, stringifyContent, messagePreview, messageTokens, createCustomModeStore } from '@wrongstack/webui/server';
|
|
15
|
+
import { findFreePort, AutoPhaseWebSocketHandler, WorktreeWebSocketHandler, SpecsWebSocketHandler, SddBoardWebSocketHandler, SddWizardWebSocketHandler, buildSddWizardDeps, generateAuthToken, verifyClient, handleGitInfo, handleShellOpen, handleGitDiff, handleGitChanges, handleSkillsExport, handleSkillsEdit, handleSkillsCreate, handleSkillsUpdate, handleSkillsUninstall, handleSkillsInstall, handleSkillsContent, handleMcpRestart, handleMcpDisable, handleMcpEnable, handleMcpDiscover, handleMcpSleep, handleMcpWake, handleMcpUpdate, handleMcpRemove, handleMcpAdd, handleMcpList, handleMemoryForget, handleMemoryRemember, handleMemoryList, handleCompletionRequest, createToolLspCompletionSource, handleFilesWrite, handleFilesRead, handleFilesTree, handleFilesList, createEternalSubscription, createHttpServer, registerInstance, openBrowser as openBrowser$1, unregisterInstance, estimateTokens as estimateTokens$1, stringifyContent, messagePreview, messageTokens, createCustomModeStore } from '@wrongstack/webui/server';
|
|
16
16
|
import { toErrorMessage, readJsonObjectFile, updateJsonObjectFile, setJsonPath, jsonObjectFileExists, removeJsonPath } from '@wrongstack/core/utils';
|
|
17
17
|
import { setOAuthTokenPersister, makeProviderFromConfig, capabilitiesFor, buildProviderFactoriesFromRegistry, refreshCopilotToken, copilotBaseUrlFromToken } from '@wrongstack/providers';
|
|
18
18
|
import { getProcessRegistry, builtinToolsPack, rememberTool, forgetTool, searchMemoryTool, relatedMemoryTool, runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes, shutdownCodebaseIndexHost, TIER2_TOOLS, TIER3_TOOLS, TIER1_TOOLS, resetIndexCircuitBreaker } from '@wrongstack/tools';
|
|
@@ -657,26 +657,26 @@ function fmtDuration(ms) {
|
|
|
657
657
|
const remMin = m - h * 60;
|
|
658
658
|
return `${h}h${remMin}m`;
|
|
659
659
|
}
|
|
660
|
-
function fmtTaskResultLine(r,
|
|
660
|
+
function fmtTaskResultLine(r, color86) {
|
|
661
661
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
662
662
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
663
663
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
664
664
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
665
|
-
const errKindChip = errKind ?
|
|
666
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
665
|
+
const errKindChip = errKind ? color86.dim(` [${errKind}]`) : "";
|
|
666
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color86.dim(errTail)}` : "";
|
|
667
667
|
switch (r.status) {
|
|
668
668
|
case "success":
|
|
669
|
-
return { mark:
|
|
669
|
+
return { mark: color86.green("\u2713"), stats, tail: "" };
|
|
670
670
|
case "timeout":
|
|
671
671
|
return {
|
|
672
|
-
mark:
|
|
673
|
-
stats: `${
|
|
672
|
+
mark: color86.yellow("\u23F1"),
|
|
673
|
+
stats: `${color86.yellow("timeout")} ${stats}`,
|
|
674
674
|
tail: errSnip
|
|
675
675
|
};
|
|
676
676
|
case "stopped":
|
|
677
|
-
return { mark:
|
|
677
|
+
return { mark: color86.dim("\u2298"), stats: `${color86.dim("stopped")} ${stats}`, tail: errSnip };
|
|
678
678
|
case "failed":
|
|
679
|
-
return { mark:
|
|
679
|
+
return { mark: color86.red("\u2717"), stats: `${color86.red("failed")} ${stats}`, tail: errSnip };
|
|
680
680
|
}
|
|
681
681
|
}
|
|
682
682
|
var init_utils = __esm({
|
|
@@ -1335,6 +1335,19 @@ function sddHelp() {
|
|
|
1335
1335
|
" \u2502 /sdd cancel Cancel session \u2502",
|
|
1336
1336
|
" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
|
|
1337
1337
|
"",
|
|
1338
|
+
" \u250C\u2500 \u26A1 Parallel Run \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
1339
|
+
" \u2502 /sdd parallel [N] Run tasks in parallel (N slots) \u2502",
|
|
1340
|
+
" \u2502 /sdd stop Stop the active parallel run \u2502",
|
|
1341
|
+
" \u2502 /sdd retry-failed Requeue all failed tasks \u2502",
|
|
1342
|
+
" \u2502 /sdd split <id> <A ; B> Split a task into sub-tasks \u2502",
|
|
1343
|
+
" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
|
|
1344
|
+
"",
|
|
1345
|
+
" \u250C\u2500 \u{1F9F9} Lifecycle (after /sdd stop) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
1346
|
+
" \u2502 /sdd clean Remove run worktrees + branches \u2502",
|
|
1347
|
+
" \u2502 /sdd rollback Revert the run's merged commits \u2502",
|
|
1348
|
+
" \u2502 /sdd destroy Delete the whole SDD project \u2502",
|
|
1349
|
+
" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
|
|
1350
|
+
"",
|
|
1338
1351
|
" \u250C\u2500 \u{1F4C1} Spec History \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
1339
1352
|
" \u2502 /sdd list List saved specs \u2502",
|
|
1340
1353
|
" \u2502 /sdd show <id> Show spec details \u2502",
|
|
@@ -1626,9 +1639,92 @@ Start executing the tasks one by one.`
|
|
|
1626
1639
|
);
|
|
1627
1640
|
return { message };
|
|
1628
1641
|
}
|
|
1629
|
-
case "stop":
|
|
1642
|
+
case "stop":
|
|
1643
|
+
case "abort": {
|
|
1630
1644
|
opts.onSddParallelStop?.();
|
|
1631
|
-
return { message: "SDD parallel run stopped." };
|
|
1645
|
+
return { message: "SDD parallel run stopped. Use /sdd clean to remove worktrees, /sdd rollback to undo commits, or /sdd destroy to delete the project." };
|
|
1646
|
+
}
|
|
1647
|
+
case "clean":
|
|
1648
|
+
case "cleanup":
|
|
1649
|
+
case "worktrees": {
|
|
1650
|
+
if (!opts.onSddCleanWorktrees) {
|
|
1651
|
+
return { message: "Worktree cleanup is not available in this session." };
|
|
1652
|
+
}
|
|
1653
|
+
const removed = await opts.onSddCleanWorktrees();
|
|
1654
|
+
return {
|
|
1655
|
+
message: removed > 0 ? `Cleaned ${removed} SDD worktree${removed === 1 ? "" : "s"} (and their wstack/ap branches).` : "No SDD worktrees to clean."
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
case "rollback":
|
|
1659
|
+
case "revert": {
|
|
1660
|
+
if (!opts.onSddRollback) {
|
|
1661
|
+
return { message: "Rollback is not available in this session." };
|
|
1662
|
+
}
|
|
1663
|
+
const res = await opts.onSddRollback();
|
|
1664
|
+
if (res.ok) {
|
|
1665
|
+
return {
|
|
1666
|
+
message: res.reverted > 0 ? `Rolled back ${res.reverted} run commit${res.reverted === 1 ? "" : "s"} (revert commits added \u2014 history preserved).` : "Nothing to roll back."
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
return {
|
|
1670
|
+
message: `Rollback failed${res.reverted ? ` after ${res.reverted} revert(s)` : ""}: ${res.reason ?? "unknown error"}`
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
case "destroy":
|
|
1674
|
+
case "nuke": {
|
|
1675
|
+
if (!opts.onSddDestroy) {
|
|
1676
|
+
return { message: "Destroy is not available in this session." };
|
|
1677
|
+
}
|
|
1678
|
+
const res = await opts.onSddDestroy();
|
|
1679
|
+
const builder = sddState.getBuilder();
|
|
1680
|
+
if (builder) {
|
|
1681
|
+
await builder.deleteSession().catch(() => {
|
|
1682
|
+
});
|
|
1683
|
+
sddState.setBuilder(null);
|
|
1684
|
+
}
|
|
1685
|
+
sddState.clearTaskState();
|
|
1686
|
+
const parts = [
|
|
1687
|
+
`SDD project destroyed.`,
|
|
1688
|
+
res.worktreesRemoved > 0 ? ` Removed ${res.worktreesRemoved} worktree${res.worktreesRemoved === 1 ? "" : "s"}.` : "",
|
|
1689
|
+
res.deleted.length ? ` Deleted: ${res.deleted.join(", ")}.` : "",
|
|
1690
|
+
` (Merged commits left intact \u2014 use /sdd rollback to undo them.)`
|
|
1691
|
+
].filter(Boolean);
|
|
1692
|
+
return { message: parts.join("\n") };
|
|
1693
|
+
}
|
|
1694
|
+
case "retry-failed":
|
|
1695
|
+
case "retry-all": {
|
|
1696
|
+
if (!opts.onSddRetryAllFailed) {
|
|
1697
|
+
return { message: "No active SDD parallel run to retry." };
|
|
1698
|
+
}
|
|
1699
|
+
const n = opts.onSddRetryAllFailed();
|
|
1700
|
+
return {
|
|
1701
|
+
message: n > 0 ? `Requeued ${n} failed task${n === 1 ? "" : "s"} to pending.` : "No failed tasks to retry."
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
case "split": {
|
|
1705
|
+
if (!opts.onSddSplitTask) {
|
|
1706
|
+
return { message: "No active SDD parallel run to split a task in." };
|
|
1707
|
+
}
|
|
1708
|
+
const taskId = restArgs[0];
|
|
1709
|
+
if (!taskId) {
|
|
1710
|
+
return { message: "Usage: /sdd split <task-id> <subtask ; subtask ; \u2026>" };
|
|
1711
|
+
}
|
|
1712
|
+
const subtasks = restArgs.slice(1).join(" ").split(";").map((s) => s.trim()).filter(Boolean).map((piece) => {
|
|
1713
|
+
const [title, ...rest] = piece.split("::");
|
|
1714
|
+
const t = (title ?? "").trim();
|
|
1715
|
+
const d = rest.join("::").trim();
|
|
1716
|
+
return { title: t, description: d || t };
|
|
1717
|
+
}).filter((s) => s.title);
|
|
1718
|
+
if (subtasks.length < 2) {
|
|
1719
|
+
return { message: "Provide at least two sub-tasks: /sdd split <task-id> <A ; B>" };
|
|
1720
|
+
}
|
|
1721
|
+
const ids = opts.onSddSplitTask(taskId, subtasks);
|
|
1722
|
+
if (ids === null) {
|
|
1723
|
+
return { message: `No active run, or task "${taskId}" is unknown / running (can't split).` };
|
|
1724
|
+
}
|
|
1725
|
+
return {
|
|
1726
|
+
message: `Split ${taskId} into ${ids.length} sub-task${ids.length === 1 ? "" : "s"}: ${ids.join(", ")}`
|
|
1727
|
+
};
|
|
1632
1728
|
}
|
|
1633
1729
|
case "plan":
|
|
1634
1730
|
case "impl": {
|
|
@@ -2117,20 +2213,25 @@ Start executing the tasks one by one.`
|
|
|
2117
2213
|
return { message: lines.join("\n") };
|
|
2118
2214
|
}
|
|
2119
2215
|
case "cancel": {
|
|
2120
|
-
const sessionPath = opts.paths.projectSddSession;
|
|
2121
2216
|
let deletedFromDisk = false;
|
|
2122
|
-
|
|
2123
|
-
await
|
|
2124
|
-
deletedFromDisk =
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2217
|
+
if (opts.onSddDestroy) {
|
|
2218
|
+
const res = await opts.onSddDestroy();
|
|
2219
|
+
deletedFromDisk = res.deleted.length > 0 || res.worktreesRemoved > 0;
|
|
2220
|
+
} else {
|
|
2221
|
+
const sessionPath = opts.paths.projectSddSession;
|
|
2222
|
+
try {
|
|
2223
|
+
await fs4.unlink(sessionPath);
|
|
2224
|
+
deletedFromDisk = true;
|
|
2225
|
+
} catch {
|
|
2226
|
+
}
|
|
2227
|
+
try {
|
|
2228
|
+
await fs4.rm(opts.paths.projectSpecs, { recursive: true, force: true });
|
|
2229
|
+
} catch {
|
|
2230
|
+
}
|
|
2231
|
+
try {
|
|
2232
|
+
await fs4.rm(opts.paths.projectTaskGraphs, { recursive: true, force: true });
|
|
2233
|
+
} catch {
|
|
2234
|
+
}
|
|
2134
2235
|
}
|
|
2135
2236
|
const cancelBuilder = sddState.getBuilder();
|
|
2136
2237
|
if (cancelBuilder) {
|
|
@@ -4108,7 +4209,7 @@ async function clearHqRuntimeMarker(dataDir, url) {
|
|
|
4108
4209
|
function lanIPv4Addresses() {
|
|
4109
4210
|
const out = [];
|
|
4110
4211
|
try {
|
|
4111
|
-
const ifaces =
|
|
4212
|
+
const ifaces = os7.networkInterfaces();
|
|
4112
4213
|
for (const name of Object.keys(ifaces)) {
|
|
4113
4214
|
for (const ni of ifaces[name] ?? []) {
|
|
4114
4215
|
if (ni.family === "IPv4" && !ni.internal) out.push(ni.address);
|
|
@@ -4210,7 +4311,7 @@ async function handleApiSessions(res) {
|
|
|
4210
4311
|
}
|
|
4211
4312
|
}
|
|
4212
4313
|
async function handleApiSessionEvents(res, sessionId, limit, full, transcripts) {
|
|
4213
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
4314
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths6, DefaultSessionStore: DefaultSessionStore5 } = await import('@wrongstack/core');
|
|
4214
4315
|
const globalRoot = path4.dirname(resolveHqDataDir());
|
|
4215
4316
|
try {
|
|
4216
4317
|
const registry = new SessionRegistry(globalRoot);
|
|
@@ -4221,7 +4322,7 @@ async function handleApiSessionEvents(res, sessionId, limit, full, transcripts)
|
|
|
4221
4322
|
let clientType;
|
|
4222
4323
|
let projectName;
|
|
4223
4324
|
if (entry) {
|
|
4224
|
-
const paths =
|
|
4325
|
+
const paths = resolveWstackPaths6({ projectRoot: entry.projectRoot, globalRoot });
|
|
4225
4326
|
const store = new DefaultSessionStore5({ dir: paths.projectSessions });
|
|
4226
4327
|
const data = await store.load(sessionId).catch(() => null);
|
|
4227
4328
|
if (data) {
|
|
@@ -5117,7 +5218,7 @@ async function getUpdateNotification(signal, homeFn) {
|
|
|
5117
5218
|
var defaultHomeDir2, CACHE_TTL_MS;
|
|
5118
5219
|
var init_update_check = __esm({
|
|
5119
5220
|
"src/update-check.ts"() {
|
|
5120
|
-
defaultHomeDir2 = () =>
|
|
5221
|
+
defaultHomeDir2 = () => os7.homedir();
|
|
5121
5222
|
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5122
5223
|
}
|
|
5123
5224
|
});
|
|
@@ -5223,6 +5324,8 @@ async function seedConfigToMeta(opts) {
|
|
|
5223
5324
|
meta["enhanceDelayMs"] = autonomyCfg["enhanceDelayMs"] ?? 6e4;
|
|
5224
5325
|
meta["enhanceLanguage"] = autonomyCfg["enhanceLanguage"] ?? "original";
|
|
5225
5326
|
meta["nextPrediction"] = cfg.nextPrediction ?? false;
|
|
5327
|
+
meta["fallbackModels"] = cfg.fallbackModels ?? [];
|
|
5328
|
+
meta["fallbackAuto"] = cfg.fallbackAuto !== false;
|
|
5226
5329
|
meta["featureMcp"] = features["mcp"] !== false;
|
|
5227
5330
|
meta["featurePlugins"] = features["plugins"] !== false;
|
|
5228
5331
|
meta["featureMemory"] = features["memory"] !== false;
|
|
@@ -5269,8 +5372,7 @@ function createPrefsSeeding(opts) {
|
|
|
5269
5372
|
const decrypted = decryptConfigSecrets$1(parsed, vault);
|
|
5270
5373
|
const autonomy = decrypted.autonomy ?? {};
|
|
5271
5374
|
if ("autonomy" in payload) autonomy["defaultMode"] = payload["autonomy"];
|
|
5272
|
-
if ("autonomyDelayMs" in payload)
|
|
5273
|
-
autonomy["autoProceedDelayMs"] = payload["autonomyDelayMs"];
|
|
5375
|
+
if ("autonomyDelayMs" in payload) autonomy["autoProceedDelayMs"] = payload["autonomyDelayMs"];
|
|
5274
5376
|
if ("autoProceedMaxIterations" in payload)
|
|
5275
5377
|
autonomy["autoProceedMaxIterations"] = payload["autoProceedMaxIterations"];
|
|
5276
5378
|
if ("yolo" in payload) {
|
|
@@ -5282,9 +5384,10 @@ function createPrefsSeeding(opts) {
|
|
|
5282
5384
|
if ("streamFleet" in payload) autonomy["streamFleet"] = payload["streamFleet"];
|
|
5283
5385
|
if ("enhanceEnabled" in payload) autonomy["enhance"] = payload["enhanceEnabled"];
|
|
5284
5386
|
if ("enhanceDelayMs" in payload) autonomy["enhanceDelayMs"] = payload["enhanceDelayMs"];
|
|
5285
|
-
if ("enhanceLanguage" in payload)
|
|
5286
|
-
autonomy["enhanceLanguage"] = payload["enhanceLanguage"];
|
|
5387
|
+
if ("enhanceLanguage" in payload) autonomy["enhanceLanguage"] = payload["enhanceLanguage"];
|
|
5287
5388
|
if ("nextPrediction" in payload) decrypted["nextPrediction"] = payload["nextPrediction"];
|
|
5389
|
+
if ("fallbackModels" in payload) decrypted["fallbackModels"] = payload["fallbackModels"];
|
|
5390
|
+
if ("fallbackAuto" in payload) decrypted["fallbackAuto"] = payload["fallbackAuto"];
|
|
5288
5391
|
decrypted["autonomy"] = autonomy;
|
|
5289
5392
|
if ("featureMcp" in payload || "featurePlugins" in payload || "featureMemory" in payload || "featureSkills" in payload || "featureModelsRegistry" in payload) {
|
|
5290
5393
|
const features = decrypted.features ?? {};
|
|
@@ -5380,6 +5483,8 @@ var init_prefs_seeding = __esm({
|
|
|
5380
5483
|
"confirmExit",
|
|
5381
5484
|
"streamFleet",
|
|
5382
5485
|
"nextPrediction",
|
|
5486
|
+
"fallbackModels",
|
|
5487
|
+
"fallbackAuto",
|
|
5383
5488
|
"enhanceEnabled",
|
|
5384
5489
|
"enhanceDelayMs",
|
|
5385
5490
|
"enhanceLanguage",
|
|
@@ -6413,22 +6518,29 @@ var init_projects = __esm({
|
|
|
6413
6518
|
function sendResult7(ctx, ws, success, message) {
|
|
6414
6519
|
ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
6415
6520
|
}
|
|
6521
|
+
function projectSavedProviders(providers) {
|
|
6522
|
+
return Object.entries(providers).map(([id, cfg]) => {
|
|
6523
|
+
const models = cfg.models;
|
|
6524
|
+
const pickedModelId = models && models.length > 0 ? models[0] : void 0;
|
|
6525
|
+
return {
|
|
6526
|
+
id,
|
|
6527
|
+
family: cfg.family ?? id,
|
|
6528
|
+
baseUrl: cfg.baseUrl,
|
|
6529
|
+
models,
|
|
6530
|
+
...pickedModelId !== void 0 ? { pickedModelId } : {},
|
|
6531
|
+
apiKeys: normalizeKeys(cfg).map((k) => ({
|
|
6532
|
+
label: k.label,
|
|
6533
|
+
maskedKey: maskedKey(k.apiKey),
|
|
6534
|
+
isActive: k.label === cfg.activeKey,
|
|
6535
|
+
createdAt: k.createdAt
|
|
6536
|
+
}))
|
|
6537
|
+
};
|
|
6538
|
+
});
|
|
6539
|
+
}
|
|
6416
6540
|
function broadcastSaved(ctx, providers) {
|
|
6417
6541
|
ctx.broadcast({
|
|
6418
6542
|
type: "providers.saved",
|
|
6419
|
-
payload: {
|
|
6420
|
-
providers: Object.entries(providers).map(([id, cfg]) => ({
|
|
6421
|
-
id,
|
|
6422
|
-
family: cfg.family,
|
|
6423
|
-
baseUrl: cfg.baseUrl,
|
|
6424
|
-
apiKeys: normalizeKeys(cfg).map((k) => ({
|
|
6425
|
-
label: k.label,
|
|
6426
|
-
maskedKey: maskedKey(k.apiKey),
|
|
6427
|
-
isActive: k.label === cfg.activeKey,
|
|
6428
|
-
createdAt: k.createdAt
|
|
6429
|
-
}))
|
|
6430
|
-
}))
|
|
6431
|
-
}
|
|
6543
|
+
payload: { providers: projectSavedProviders(providers) }
|
|
6432
6544
|
});
|
|
6433
6545
|
}
|
|
6434
6546
|
async function handleProvidersList(ctx, ws) {
|
|
@@ -6484,19 +6596,7 @@ async function handleProvidersSaved(ctx, ws) {
|
|
|
6484
6596
|
const providers = await ctx.providerStore.load();
|
|
6485
6597
|
ctx.send(ws, {
|
|
6486
6598
|
type: "providers.saved",
|
|
6487
|
-
payload: {
|
|
6488
|
-
providers: Object.entries(providers).map(([id, cfg]) => ({
|
|
6489
|
-
id,
|
|
6490
|
-
family: cfg.family,
|
|
6491
|
-
baseUrl: cfg.baseUrl,
|
|
6492
|
-
apiKeys: normalizeKeys(cfg).map((k) => ({
|
|
6493
|
-
label: k.label,
|
|
6494
|
-
maskedKey: maskedKey(k.apiKey),
|
|
6495
|
-
isActive: k.label === cfg.activeKey,
|
|
6496
|
-
createdAt: k.createdAt
|
|
6497
|
-
}))
|
|
6498
|
-
}))
|
|
6499
|
-
}
|
|
6599
|
+
payload: { providers: projectSavedProviders(providers) }
|
|
6500
6600
|
});
|
|
6501
6601
|
} catch (err) {
|
|
6502
6602
|
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
@@ -6518,6 +6618,7 @@ async function handleKeyUpsert(ctx, ws, providerId, label, apiKey) {
|
|
|
6518
6618
|
providers[providerId] = existing;
|
|
6519
6619
|
await ctx.providerStore.save(providers);
|
|
6520
6620
|
sendResult7(ctx, ws, true, `Key "${label}" saved for ${providerId}`);
|
|
6621
|
+
broadcastSaved(ctx, providers);
|
|
6521
6622
|
} catch (err) {
|
|
6522
6623
|
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
6523
6624
|
}
|
|
@@ -6542,6 +6643,7 @@ async function handleKeyDelete(ctx, ws, providerId, label) {
|
|
|
6542
6643
|
}
|
|
6543
6644
|
await ctx.providerStore.save(providers);
|
|
6544
6645
|
sendResult7(ctx, ws, true, `Key "${label}" deleted from ${providerId}`);
|
|
6646
|
+
broadcastSaved(ctx, providers);
|
|
6545
6647
|
} catch (err) {
|
|
6546
6648
|
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
6547
6649
|
}
|
|
@@ -6559,6 +6661,7 @@ async function handleKeySetActive(ctx, ws, providerId, label) {
|
|
|
6559
6661
|
providers[providerId] = existing;
|
|
6560
6662
|
await ctx.providerStore.save(providers);
|
|
6561
6663
|
sendResult7(ctx, ws, true, `Active key for ${providerId} set to "${label}"`);
|
|
6664
|
+
broadcastSaved(ctx, providers);
|
|
6562
6665
|
} catch (err) {
|
|
6563
6666
|
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
6564
6667
|
}
|
|
@@ -6588,22 +6691,7 @@ async function handleProviderAdd(ctx, ws, payload) {
|
|
|
6588
6691
|
await ctx.providerStore.save(providers);
|
|
6589
6692
|
sendResult7(ctx, ws, true, `Provider "${payload.id}" added`);
|
|
6590
6693
|
ctx.log(`[WebUI] Provider "${payload.id}" added via provider.add`);
|
|
6591
|
-
ctx
|
|
6592
|
-
type: "providers.saved",
|
|
6593
|
-
payload: {
|
|
6594
|
-
providers: Object.entries(providers).map(([id, cfg]) => ({
|
|
6595
|
-
id,
|
|
6596
|
-
family: cfg.family,
|
|
6597
|
-
baseUrl: cfg.baseUrl,
|
|
6598
|
-
apiKeys: normalizeKeys(cfg).map((k) => ({
|
|
6599
|
-
label: k.label,
|
|
6600
|
-
maskedKey: maskedKey(k.apiKey),
|
|
6601
|
-
isActive: k.label === cfg.activeKey,
|
|
6602
|
-
createdAt: k.createdAt
|
|
6603
|
-
}))
|
|
6604
|
-
}))
|
|
6605
|
-
}
|
|
6606
|
-
});
|
|
6694
|
+
broadcastSaved(ctx, providers);
|
|
6607
6695
|
} catch (err) {
|
|
6608
6696
|
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
6609
6697
|
}
|
|
@@ -6618,6 +6706,7 @@ async function handleProviderRemove(ctx, ws, providerId) {
|
|
|
6618
6706
|
delete providers[providerId];
|
|
6619
6707
|
await ctx.providerStore.save(providers);
|
|
6620
6708
|
sendResult7(ctx, ws, true, `Provider "${providerId}" removed`);
|
|
6709
|
+
broadcastSaved(ctx, providers);
|
|
6621
6710
|
} catch (err) {
|
|
6622
6711
|
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
6623
6712
|
}
|
|
@@ -7235,7 +7324,7 @@ async function runWebUI(opts) {
|
|
|
7235
7324
|
})();
|
|
7236
7325
|
return customModeStoreP;
|
|
7237
7326
|
};
|
|
7238
|
-
const autoPhaseStoreDir = opts.projectRoot ? path4.join(opts.projectRoot, ".wrongstack", "autophase") : path4.join(
|
|
7327
|
+
const autoPhaseStoreDir = opts.projectRoot ? path4.join(opts.projectRoot, ".wrongstack", "autophase") : path4.join(os7.tmpdir(), ".wrongstack", "autophase");
|
|
7239
7328
|
const autoPhaseHandler = new AutoPhaseWebSocketHandler(
|
|
7240
7329
|
opts.agent,
|
|
7241
7330
|
opts.agent.ctx,
|
|
@@ -7245,6 +7334,29 @@ async function runWebUI(opts) {
|
|
|
7245
7334
|
opts.projectRoot
|
|
7246
7335
|
);
|
|
7247
7336
|
const worktreeHandler = new WorktreeWebSocketHandler(opts.events, consoleLogger);
|
|
7337
|
+
const specsPaths = opts.projectRoot ? resolveWstackPaths({ projectRoot: opts.projectRoot }) : null;
|
|
7338
|
+
const specsHandler = new SpecsWebSocketHandler(
|
|
7339
|
+
specsPaths?.projectSpecs ?? path4.join(os7.tmpdir(), ".wrongstack", "specs"),
|
|
7340
|
+
specsPaths?.projectTaskGraphs ?? path4.join(os7.tmpdir(), ".wrongstack", "task-graphs")
|
|
7341
|
+
);
|
|
7342
|
+
const sddBoardHandler = new SddBoardWebSocketHandler(
|
|
7343
|
+
specsPaths?.projectSddBoards ?? path4.join(os7.tmpdir(), ".wrongstack", "sdd-boards"),
|
|
7344
|
+
opts.events
|
|
7345
|
+
);
|
|
7346
|
+
const sddWizardHandler = opts.sddSubagentFactory && specsPaths ? new SddWizardWebSocketHandler(
|
|
7347
|
+
buildSddWizardDeps({
|
|
7348
|
+
agent: opts.agent,
|
|
7349
|
+
events: opts.events,
|
|
7350
|
+
projectRoot: opts.projectRoot ?? process.cwd(),
|
|
7351
|
+
subagentFactory: opts.sddSubagentFactory,
|
|
7352
|
+
paths: {
|
|
7353
|
+
projectSpecs: specsPaths.projectSpecs,
|
|
7354
|
+
projectTaskGraphs: specsPaths.projectTaskGraphs,
|
|
7355
|
+
projectSddBoards: specsPaths.projectSddBoards,
|
|
7356
|
+
projectDir: specsPaths.projectDir
|
|
7357
|
+
}
|
|
7358
|
+
})
|
|
7359
|
+
) : null;
|
|
7248
7360
|
await seedConfigToMeta(opts);
|
|
7249
7361
|
const { prefSnapshot, persistPrefs } = createPrefsSeeding(opts);
|
|
7250
7362
|
const sessionStartedAt = Date.now();
|
|
@@ -8241,6 +8353,9 @@ ${text}` : text;
|
|
|
8241
8353
|
const client = { ws, sessionId: opts.session.id };
|
|
8242
8354
|
clients.set(ws, client);
|
|
8243
8355
|
autoPhaseHandler.addClient(ws);
|
|
8356
|
+
specsHandler.addClient(ws);
|
|
8357
|
+
sddBoardHandler.addClient(ws);
|
|
8358
|
+
sddWizardHandler?.addClient(ws);
|
|
8244
8359
|
worktreeHandler.addClient(ws);
|
|
8245
8360
|
let msgCount = 0;
|
|
8246
8361
|
let windowResetAt = Date.now() + 6e4;
|
|
@@ -8845,7 +8960,11 @@ ${text}` : text;
|
|
|
8845
8960
|
break;
|
|
8846
8961
|
// ── Mailbox operations — project-level inter-agent messaging (PR 8 of #30) ────
|
|
8847
8962
|
case "mailbox.messages":
|
|
8848
|
-
await handleMailboxMessages(
|
|
8963
|
+
await handleMailboxMessages(
|
|
8964
|
+
mailboxCtx,
|
|
8965
|
+
msg,
|
|
8966
|
+
ws
|
|
8967
|
+
);
|
|
8849
8968
|
break;
|
|
8850
8969
|
case "mailbox.agents":
|
|
8851
8970
|
await handleMailboxAgents(mailboxCtx, msg, ws);
|
|
@@ -8867,6 +8986,18 @@ ${text}` : text;
|
|
|
8867
8986
|
await autoPhaseHandler.handleMessage(
|
|
8868
8987
|
msg
|
|
8869
8988
|
);
|
|
8989
|
+
} else if (msgType.startsWith("specs.")) {
|
|
8990
|
+
await specsHandler.handleMessage(
|
|
8991
|
+
msg
|
|
8992
|
+
);
|
|
8993
|
+
} else if (msgType.startsWith("sdd.board.")) {
|
|
8994
|
+
await sddBoardHandler.handleMessage(
|
|
8995
|
+
msg
|
|
8996
|
+
);
|
|
8997
|
+
} else if (msgType.startsWith("sdd.spec.") || msgType.startsWith("sdd.run.")) {
|
|
8998
|
+
await sddWizardHandler?.handleMessage(
|
|
8999
|
+
msg
|
|
9000
|
+
);
|
|
8870
9001
|
} else {
|
|
8871
9002
|
console.debug(`[WebUI] Unhandled message type: ${msgType}`);
|
|
8872
9003
|
}
|
|
@@ -8918,6 +9049,12 @@ var init_webui_server = __esm({
|
|
|
8918
9049
|
|
|
8919
9050
|
// src/cli-main.ts
|
|
8920
9051
|
init_provider_config_utils();
|
|
9052
|
+
var DEFAULT_TASK_CONCURRENCY = 2;
|
|
9053
|
+
function resolveTaskConcurrency() {
|
|
9054
|
+
const raw = Number.parseInt(process.env["WRONGSTACK_AUTOPHASE_TASK_CONCURRENCY"] ?? "", 10);
|
|
9055
|
+
if (!Number.isFinite(raw)) return DEFAULT_TASK_CONCURRENCY;
|
|
9056
|
+
return Math.min(8, Math.max(1, raw));
|
|
9057
|
+
}
|
|
8921
9058
|
var WORKTREE_PHASE_CONCURRENCY = 4;
|
|
8922
9059
|
var MAX_CMD_OUTPUT = 2e5;
|
|
8923
9060
|
function gitText(args, cwd) {
|
|
@@ -9131,6 +9268,7 @@ ${res.out}` };
|
|
|
9131
9268
|
};
|
|
9132
9269
|
}
|
|
9133
9270
|
const abort = new AbortController();
|
|
9271
|
+
const usedNicknames = /* @__PURE__ */ new Set();
|
|
9134
9272
|
log(`\u{1F9E0} Planning phases for: ${goal}`);
|
|
9135
9273
|
let phases;
|
|
9136
9274
|
try {
|
|
@@ -9180,9 +9318,16 @@ ${res.out}` };
|
|
|
9180
9318
|
executeTask: async (task, phaseId, env) => {
|
|
9181
9319
|
const phase = graph.phases.get(phaseId);
|
|
9182
9320
|
const phaseName = phase?.name ?? phaseId;
|
|
9321
|
+
let agentName = task.assignee;
|
|
9322
|
+
if (!agentName) {
|
|
9323
|
+
const nick = assignNickname("executor", usedNicknames);
|
|
9324
|
+
usedNicknames.add(nick.key);
|
|
9325
|
+
agentName = nick.display.replace(/\s*\([^)]*\)\s*$/, "");
|
|
9326
|
+
active?.orchestrator.setTaskAssignee(task.id, void 0, agentName);
|
|
9327
|
+
}
|
|
9183
9328
|
return runOnce(
|
|
9184
9329
|
buildTaskPrompt(task, phaseName, goal),
|
|
9185
|
-
`autophase-${
|
|
9330
|
+
`autophase-${agentName}`.slice(0, 48),
|
|
9186
9331
|
abort.signal,
|
|
9187
9332
|
env?.cwd
|
|
9188
9333
|
);
|
|
@@ -9227,9 +9372,11 @@ ${res.out}` };
|
|
|
9227
9372
|
// With isolation, parallelizable phases run concurrently; without it,
|
|
9228
9373
|
// stay strictly sequential to protect the shared working tree.
|
|
9229
9374
|
maxConcurrentPhases: worktrees ? deps.maxConcurrentPhases ?? WORKTREE_PHASE_CONCURRENCY : 1,
|
|
9230
|
-
//
|
|
9231
|
-
//
|
|
9232
|
-
|
|
9375
|
+
// Within a phase, todos share the phase worktree. Default to a small
|
|
9376
|
+
// amount of parallelism so multiple agents genuinely pick up different
|
|
9377
|
+
// tasks at once (visible on the board); raise/lower via
|
|
9378
|
+
// WRONGSTACK_AUTOPHASE_TASK_CONCURRENCY (1 = strictly sequential).
|
|
9379
|
+
maxConcurrentTasks: resolveTaskConcurrency()
|
|
9233
9380
|
});
|
|
9234
9381
|
const onUntyped = deps.events.on;
|
|
9235
9382
|
const offUntyped = deps.events.off;
|
|
@@ -9284,6 +9431,30 @@ ${res.out}` };
|
|
|
9284
9431
|
isRunning: () => a.orchestrator.isRunning()
|
|
9285
9432
|
};
|
|
9286
9433
|
},
|
|
9434
|
+
onAutoPhaseMoveTask(taskId, toPhaseId) {
|
|
9435
|
+
if (!active) return false;
|
|
9436
|
+
const ok = active.orchestrator.moveTask(taskId, toPhaseId);
|
|
9437
|
+
if (ok) void persist(active.graph);
|
|
9438
|
+
return ok;
|
|
9439
|
+
},
|
|
9440
|
+
onAutoPhaseAssignTask(taskId, agentId, agentName) {
|
|
9441
|
+
if (!active) return false;
|
|
9442
|
+
const ok = active.orchestrator.setTaskAssignee(taskId, agentId, agentName);
|
|
9443
|
+
if (ok) void persist(active.graph);
|
|
9444
|
+
return ok;
|
|
9445
|
+
},
|
|
9446
|
+
onAutoPhaseAddTask(phaseId, spec) {
|
|
9447
|
+
if (!active) return null;
|
|
9448
|
+
const id = active.orchestrator.addTask(phaseId, spec);
|
|
9449
|
+
if (id) void persist(active.graph);
|
|
9450
|
+
return id;
|
|
9451
|
+
},
|
|
9452
|
+
onAutoPhaseRetryTask(taskId) {
|
|
9453
|
+
if (!active) return false;
|
|
9454
|
+
const ok = active.orchestrator.requeueTask(taskId);
|
|
9455
|
+
if (ok) void persist(active.graph);
|
|
9456
|
+
return ok;
|
|
9457
|
+
},
|
|
9287
9458
|
async onWorktree(action, target) {
|
|
9288
9459
|
const root = deps.projectRoot;
|
|
9289
9460
|
if (!await isGitRepo(root)) return "\u26A0 Not a git repository \u2014 worktrees unavailable.";
|
|
@@ -9855,7 +10026,7 @@ function printLaunchHints(renderer, flags, opts = {}) {
|
|
|
9855
10026
|
renderer.write(`${lines.join("\n")}
|
|
9856
10027
|
`);
|
|
9857
10028
|
}
|
|
9858
|
-
var defaultUidFn = () =>
|
|
10029
|
+
var defaultUidFn = () => os7__default.userInfo().uid;
|
|
9859
10030
|
async function getFileUid(filePath) {
|
|
9860
10031
|
try {
|
|
9861
10032
|
const stat8 = await fs4.stat(filePath);
|
|
@@ -9865,7 +10036,7 @@ async function getFileUid(filePath) {
|
|
|
9865
10036
|
}
|
|
9866
10037
|
}
|
|
9867
10038
|
async function checkConfigOwnership(homeFn, uidFn = defaultUidFn) {
|
|
9868
|
-
if (
|
|
10039
|
+
if (os7__default.platform() === "win32") return true;
|
|
9869
10040
|
const cfg = configPath(homeFn);
|
|
9870
10041
|
const fileUid = await getFileUid(cfg);
|
|
9871
10042
|
if (fileUid === void 0) return true;
|
|
@@ -9956,7 +10127,7 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
9956
10127
|
}
|
|
9957
10128
|
return changes.length > 0 ? changes.slice(0, 5).join(", ") : "no changes";
|
|
9958
10129
|
}
|
|
9959
|
-
var defaultHomeDir = () =>
|
|
10130
|
+
var defaultHomeDir = () => os7__default.homedir();
|
|
9960
10131
|
function historyDir(homeFn = defaultHomeDir) {
|
|
9961
10132
|
return path4.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
9962
10133
|
}
|
|
@@ -10172,6 +10343,10 @@ async function restoreLast(homeFn = defaultHomeDir) {
|
|
|
10172
10343
|
}
|
|
10173
10344
|
|
|
10174
10345
|
// src/provider-helpers.ts
|
|
10346
|
+
function visibleModelIds(providerId, config, catalogModelIds, cfg) {
|
|
10347
|
+
const entry = cfg ?? config.providers?.[providerId];
|
|
10348
|
+
return entry?.models !== void 0 ? [...entry.models] : [...catalogModelIds];
|
|
10349
|
+
}
|
|
10175
10350
|
function hasApiKey(provider, config) {
|
|
10176
10351
|
if (provider.envVars.some((v) => !!process.env[v])) return true;
|
|
10177
10352
|
const entry = config?.providers?.[provider.id];
|
|
@@ -10206,7 +10381,7 @@ async function buildPickableProviders(modelsRegistry, config) {
|
|
|
10206
10381
|
const inherited = catalogById.get(catalogType);
|
|
10207
10382
|
const family = cfg.family ?? inherited?.family ?? "unsupported";
|
|
10208
10383
|
if (family === "unsupported") continue;
|
|
10209
|
-
const models =
|
|
10384
|
+
const models = visibleModelIds(id, config, (inherited?.models ?? []).map((m) => m.id), cfg);
|
|
10210
10385
|
out.push({ id, family, models });
|
|
10211
10386
|
}
|
|
10212
10387
|
for (const p of catalog) {
|
|
@@ -10218,7 +10393,7 @@ async function buildPickableProviders(modelsRegistry, config) {
|
|
|
10218
10393
|
return out;
|
|
10219
10394
|
}
|
|
10220
10395
|
var theme = { primary: color.amber };
|
|
10221
|
-
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ??
|
|
10396
|
+
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os7__default.homedir()) {
|
|
10222
10397
|
try {
|
|
10223
10398
|
const { atomicWrite: atomicWrite15 } = await import('@wrongstack/core');
|
|
10224
10399
|
const fs40 = await import('fs/promises');
|
|
@@ -10301,7 +10476,7 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
|
|
|
10301
10476
|
// know which models their endpoint actually serves (e.g. LM
|
|
10302
10477
|
// Studio, vLLM, or a proxy with custom model ids). Otherwise the
|
|
10303
10478
|
// catalog list keeps providing suggestions.
|
|
10304
|
-
models:
|
|
10479
|
+
models: visibleModelIds(p.id, config ?? { providers: {} }, p.models.map((m) => m.id), cfg).map((m) => p.models.find((pm) => pm.id === m) ?? { id: m, name: m })
|
|
10305
10480
|
});
|
|
10306
10481
|
} else {
|
|
10307
10482
|
merged.push(p);
|
|
@@ -10318,7 +10493,7 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
|
|
|
10318
10493
|
family: cfg.family,
|
|
10319
10494
|
apiBase: cfg.baseUrl ?? inherited?.apiBase,
|
|
10320
10495
|
envVars: cfg.envVars ?? inherited?.envVars ?? [],
|
|
10321
|
-
models:
|
|
10496
|
+
models: visibleModelIds(id, config ?? { providers: {} }, (inherited?.models ?? []).map((m) => m.id), cfg).map((m) => inherited?.models.find((pm) => pm.id === m) ?? { id: m, name: m }),
|
|
10322
10497
|
npm: inherited?.npm
|
|
10323
10498
|
});
|
|
10324
10499
|
}
|
|
@@ -12885,133 +13060,6 @@ function stripSurroundingQuotes(s) {
|
|
|
12885
13060
|
}
|
|
12886
13061
|
return s;
|
|
12887
13062
|
}
|
|
12888
|
-
function parseModelRef(ref) {
|
|
12889
|
-
const trimmed = ref.trim();
|
|
12890
|
-
const slash = trimmed.indexOf("/");
|
|
12891
|
-
if (slash !== -1) {
|
|
12892
|
-
return {
|
|
12893
|
-
provider: trimmed.slice(0, slash) || void 0,
|
|
12894
|
-
model: trimmed.slice(slash + 1).trim()
|
|
12895
|
-
};
|
|
12896
|
-
}
|
|
12897
|
-
const parts = trimmed.split(/\s+/);
|
|
12898
|
-
if (parts.length >= 2) {
|
|
12899
|
-
return { provider: parts[0], model: parts.slice(1).join(" ") };
|
|
12900
|
-
}
|
|
12901
|
-
return { model: trimmed };
|
|
12902
|
-
}
|
|
12903
|
-
function shouldFallback(err) {
|
|
12904
|
-
if (err instanceof StreamHangError) {
|
|
12905
|
-
return 599;
|
|
12906
|
-
}
|
|
12907
|
-
if (!(err instanceof ProviderError)) return null;
|
|
12908
|
-
const s = err.status;
|
|
12909
|
-
if (s === 0) return s;
|
|
12910
|
-
if (s === 429 || s === 529 || s >= 500) return s;
|
|
12911
|
-
return null;
|
|
12912
|
-
}
|
|
12913
|
-
function providerHasKey(entry) {
|
|
12914
|
-
if (!entry) return false;
|
|
12915
|
-
if (typeof entry.apiKey === "string" && entry.apiKey.length > 0) return true;
|
|
12916
|
-
if (Array.isArray(entry.apiKeys) && entry.apiKeys.some((k) => k?.apiKey)) return true;
|
|
12917
|
-
if (Array.isArray(entry.envVars) && entry.envVars.some((v) => !!process.env[v])) return true;
|
|
12918
|
-
return false;
|
|
12919
|
-
}
|
|
12920
|
-
var SMART_DEFAULT_MAX = 4;
|
|
12921
|
-
function smartDefaultFallbackChain(config) {
|
|
12922
|
-
const leaderProvider = config.provider;
|
|
12923
|
-
const leaderModel = config.model;
|
|
12924
|
-
const providers = config.providers ?? {};
|
|
12925
|
-
const seen = /* @__PURE__ */ new Set();
|
|
12926
|
-
const sameProvider = [];
|
|
12927
|
-
const crossProvider = [];
|
|
12928
|
-
const ids = Object.keys(providers).sort(
|
|
12929
|
-
(a, b) => a === leaderProvider ? -1 : b === leaderProvider ? 1 : a.localeCompare(b)
|
|
12930
|
-
);
|
|
12931
|
-
for (const id of ids) {
|
|
12932
|
-
const entry = providers[id];
|
|
12933
|
-
if (!providerHasKey(entry)) continue;
|
|
12934
|
-
const models = entry?.models ?? [];
|
|
12935
|
-
for (const model of models) {
|
|
12936
|
-
if (id === leaderProvider && model === leaderModel) continue;
|
|
12937
|
-
const ref = `${id}/${model}`;
|
|
12938
|
-
if (seen.has(ref)) continue;
|
|
12939
|
-
seen.add(ref);
|
|
12940
|
-
(id === leaderProvider ? sameProvider : crossProvider).push(ref);
|
|
12941
|
-
}
|
|
12942
|
-
}
|
|
12943
|
-
return [...sameProvider, ...crossProvider].slice(0, SMART_DEFAULT_MAX);
|
|
12944
|
-
}
|
|
12945
|
-
function effectiveFallbackChain(config) {
|
|
12946
|
-
const explicit = config.fallbackModels ?? [];
|
|
12947
|
-
if (explicit.length > 0) return explicit;
|
|
12948
|
-
if (config.fallbackAuto === false) return [];
|
|
12949
|
-
return smartDefaultFallbackChain(config);
|
|
12950
|
-
}
|
|
12951
|
-
function createFallbackModelExtension(deps) {
|
|
12952
|
-
let dirty = false;
|
|
12953
|
-
return {
|
|
12954
|
-
name: "fallback-model",
|
|
12955
|
-
beforeRun: async (ctx) => {
|
|
12956
|
-
if (!dirty) return;
|
|
12957
|
-
const cfg = deps.getConfig();
|
|
12958
|
-
try {
|
|
12959
|
-
ctx.provider = await deps.buildProvider(cfg.provider);
|
|
12960
|
-
ctx.model = cfg.model;
|
|
12961
|
-
await deps.onModelSwitch?.(cfg.provider, cfg.model);
|
|
12962
|
-
} catch (err) {
|
|
12963
|
-
deps.logger?.warn(
|
|
12964
|
-
`fallback-model: could not restore primary "${cfg.provider}/${cfg.model}": ${err instanceof Error ? err.message : String(err)}`
|
|
12965
|
-
);
|
|
12966
|
-
}
|
|
12967
|
-
dirty = false;
|
|
12968
|
-
},
|
|
12969
|
-
wrapProviderRunner: async (ctx, request, inner) => {
|
|
12970
|
-
try {
|
|
12971
|
-
return await inner(ctx, request);
|
|
12972
|
-
} catch (firstErr) {
|
|
12973
|
-
let lastErr = firstErr;
|
|
12974
|
-
const cfg = deps.getConfig();
|
|
12975
|
-
const chain = effectiveFallbackChain(cfg);
|
|
12976
|
-
for (const ref of chain) {
|
|
12977
|
-
const status = shouldFallback(lastErr);
|
|
12978
|
-
if (status === null) break;
|
|
12979
|
-
const parsed = parseModelRef(ref);
|
|
12980
|
-
if (!parsed.model) continue;
|
|
12981
|
-
const targetProviderId = parsed.provider ?? cfg.provider;
|
|
12982
|
-
const from = { providerId: ctx.provider.id, model: ctx.model };
|
|
12983
|
-
let nextProvider;
|
|
12984
|
-
try {
|
|
12985
|
-
nextProvider = await deps.buildProvider(targetProviderId);
|
|
12986
|
-
} catch (err) {
|
|
12987
|
-
deps.logger?.warn(
|
|
12988
|
-
`fallback-model: skipping "${ref}" \u2014 cannot build provider "${targetProviderId}": ${err instanceof Error ? err.message : String(err)}`
|
|
12989
|
-
);
|
|
12990
|
-
continue;
|
|
12991
|
-
}
|
|
12992
|
-
const providerSwitched = nextProvider.id !== from.providerId;
|
|
12993
|
-
ctx.provider = nextProvider;
|
|
12994
|
-
ctx.model = parsed.model;
|
|
12995
|
-
request.model = parsed.model;
|
|
12996
|
-
dirty = true;
|
|
12997
|
-
await deps.onModelSwitch?.(targetProviderId, parsed.model);
|
|
12998
|
-
deps.events.emit("provider.fallback", {
|
|
12999
|
-
from,
|
|
13000
|
-
to: { providerId: nextProvider.id, model: parsed.model },
|
|
13001
|
-
status,
|
|
13002
|
-
providerSwitched
|
|
13003
|
-
});
|
|
13004
|
-
try {
|
|
13005
|
-
return await inner(ctx, request);
|
|
13006
|
-
} catch (err) {
|
|
13007
|
-
lastErr = err;
|
|
13008
|
-
}
|
|
13009
|
-
}
|
|
13010
|
-
throw lastErr;
|
|
13011
|
-
}
|
|
13012
|
-
}
|
|
13013
|
-
};
|
|
13014
|
-
}
|
|
13015
13063
|
|
|
13016
13064
|
// src/slash-commands/fallback.ts
|
|
13017
13065
|
async function patchGlobalConfig(globalConfigPath, mutate) {
|
|
@@ -14186,7 +14234,7 @@ function buildFleetCommand(opts) {
|
|
|
14186
14234
|
// ── /fleet terminate <subagentId> ────────────────────────────────────
|
|
14187
14235
|
case "terminate":
|
|
14188
14236
|
case "stop":
|
|
14189
|
-
return handleTerminate(opts, subargs);
|
|
14237
|
+
return await handleTerminate(opts, subargs);
|
|
14190
14238
|
// ── /fleet spawn <role> [count] ──────────────────────────────────────
|
|
14191
14239
|
case "spawn":
|
|
14192
14240
|
case "add":
|
|
@@ -14357,7 +14405,7 @@ async function handleKill(opts, subargs) {
|
|
|
14357
14405
|
return { message: msg2 };
|
|
14358
14406
|
}
|
|
14359
14407
|
if (opts.onFleetKill) {
|
|
14360
|
-
const killed = opts.onFleetKill();
|
|
14408
|
+
const killed = await opts.onFleetKill();
|
|
14361
14409
|
const msg2 = `${color.red("\u2717 Killed")} ${killed} subagent(s).`;
|
|
14362
14410
|
opts.renderer.write(msg2);
|
|
14363
14411
|
return { message: msg2 };
|
|
@@ -14366,7 +14414,7 @@ async function handleKill(opts, subargs) {
|
|
|
14366
14414
|
opts.renderer.writeWarning(msg);
|
|
14367
14415
|
return { message: msg };
|
|
14368
14416
|
}
|
|
14369
|
-
function handleTerminate(opts, subargs) {
|
|
14417
|
+
async function handleTerminate(opts, subargs) {
|
|
14370
14418
|
const targetId = subargs[0];
|
|
14371
14419
|
if (!targetId) {
|
|
14372
14420
|
const msg2 = `${color.amber("\u26A0 /fleet terminate requires a subagentId.")} Use /fleet to see active ids.`;
|
|
@@ -14378,7 +14426,7 @@ function handleTerminate(opts, subargs) {
|
|
|
14378
14426
|
opts.renderer.writeWarning(msg2);
|
|
14379
14427
|
return { message: msg2 };
|
|
14380
14428
|
}
|
|
14381
|
-
const ok = opts.onFleetTerminate(targetId);
|
|
14429
|
+
const ok = await opts.onFleetTerminate(targetId);
|
|
14382
14430
|
if (ok) {
|
|
14383
14431
|
const msg2 = `${color.green("\u2713 Terminated")} subagent ${color.bold(targetId)}.`;
|
|
14384
14432
|
opts.renderer.write(msg2);
|
|
@@ -15044,7 +15092,7 @@ function buildInterruptCommand(opts) {
|
|
|
15044
15092
|
].join("\n"),
|
|
15045
15093
|
async run() {
|
|
15046
15094
|
const aborted = opts.interruptController?.abortLeader() ?? false;
|
|
15047
|
-
const killed = opts.onFleetKill
|
|
15095
|
+
const killed = opts.onFleetKill ? await opts.onFleetKill() : 0;
|
|
15048
15096
|
if (!aborted && killed === 0) {
|
|
15049
15097
|
return { message: color.dim(" Nothing to interrupt \u2014 no run in progress.") };
|
|
15050
15098
|
}
|
|
@@ -17262,7 +17310,7 @@ async function killSession(sessionId) {
|
|
|
17262
17310
|
};
|
|
17263
17311
|
}
|
|
17264
17312
|
}
|
|
17265
|
-
function
|
|
17313
|
+
function providerHasKey(entry) {
|
|
17266
17314
|
if (!entry) return false;
|
|
17267
17315
|
if (typeof entry.apiKey === "string" && entry.apiKey.length > 0) return true;
|
|
17268
17316
|
if (Array.isArray(entry.apiKeys) && entry.apiKeys.some((k) => k?.apiKey)) return true;
|
|
@@ -17273,7 +17321,7 @@ function keyedProviderIds(config) {
|
|
|
17273
17321
|
const ids = /* @__PURE__ */ new Set();
|
|
17274
17322
|
if (config.provider) ids.add(config.provider);
|
|
17275
17323
|
for (const [id, entry] of Object.entries(config.providers ?? {})) {
|
|
17276
|
-
if (
|
|
17324
|
+
if (providerHasKey(entry)) ids.add(id);
|
|
17277
17325
|
}
|
|
17278
17326
|
return [...ids].sort();
|
|
17279
17327
|
}
|
|
@@ -17500,7 +17548,7 @@ function buildSetModelCommand(opts) {
|
|
|
17500
17548
|
issues.push(
|
|
17501
17549
|
`${color.red("\u2717")} ${color.amber(key)}: provider "${entry.provider}" is not configured`
|
|
17502
17550
|
);
|
|
17503
|
-
} else if (!
|
|
17551
|
+
} else if (!providerHasKey(provCfg2)) {
|
|
17504
17552
|
warnings.push(
|
|
17505
17553
|
`${color.amber("\u26A0")} ${color.amber(key)}: provider "${entry.provider}" has no API key`
|
|
17506
17554
|
);
|
|
@@ -18311,7 +18359,7 @@ ${parts.join("\n")}
|
|
|
18311
18359
|
return false;
|
|
18312
18360
|
}
|
|
18313
18361
|
if (fleetRunning > 0) {
|
|
18314
|
-
const killed = opts.onFleetKill
|
|
18362
|
+
const killed = opts.onFleetKill ? await opts.onFleetKill() : 0;
|
|
18315
18363
|
if (killed > 0) {
|
|
18316
18364
|
opts.renderer.write(color.dim(` Stopped ${killed} subagent(s).
|
|
18317
18365
|
`));
|
|
@@ -20495,6 +20543,150 @@ function buildTodosCommand(opts) {
|
|
|
20495
20543
|
}
|
|
20496
20544
|
};
|
|
20497
20545
|
}
|
|
20546
|
+
function fit(text, width) {
|
|
20547
|
+
if (text.length <= width) return text.padEnd(width);
|
|
20548
|
+
return `${text.slice(0, Math.max(0, width - 3))}...`;
|
|
20549
|
+
}
|
|
20550
|
+
function formatDescriptionMode(mode) {
|
|
20551
|
+
const raw = `desc:${mode}`;
|
|
20552
|
+
return mode === "simple" ? color.amber(raw) : color.cyan(raw);
|
|
20553
|
+
}
|
|
20554
|
+
function buildToolCommand(opts) {
|
|
20555
|
+
const help = [
|
|
20556
|
+
"Usage:",
|
|
20557
|
+
" /tool Show tool description-mode overrides",
|
|
20558
|
+
" /tool list List every tool and its description mode",
|
|
20559
|
+
" /tool <name> Show one tool mode",
|
|
20560
|
+
" /tool <name> simple Use a 1-2 line tool description",
|
|
20561
|
+
" /tool <name> extend Use the full/extended tool description",
|
|
20562
|
+
"",
|
|
20563
|
+
"Modes:",
|
|
20564
|
+
" simple Short top-level description and usage hint",
|
|
20565
|
+
" extend Full description (default)",
|
|
20566
|
+
"",
|
|
20567
|
+
"Examples:",
|
|
20568
|
+
" /tool read simple",
|
|
20569
|
+
" /tool bash extend"
|
|
20570
|
+
].join("\n");
|
|
20571
|
+
function getCurrentTools() {
|
|
20572
|
+
return opts.configStore.get().tools;
|
|
20573
|
+
}
|
|
20574
|
+
function nextToolsConfig(name, mode) {
|
|
20575
|
+
const current = getCurrentTools();
|
|
20576
|
+
const descriptionMode = { ...current.descriptionMode ?? {} };
|
|
20577
|
+
if (mode === "simple") {
|
|
20578
|
+
descriptionMode[name] = "simple";
|
|
20579
|
+
} else {
|
|
20580
|
+
delete descriptionMode[name];
|
|
20581
|
+
}
|
|
20582
|
+
return { ...current, descriptionMode };
|
|
20583
|
+
}
|
|
20584
|
+
async function persistMode(name, mode) {
|
|
20585
|
+
const nextTools = nextToolsConfig(name, mode);
|
|
20586
|
+
if (!opts.paths) {
|
|
20587
|
+
opts.configStore.update({ tools: nextTools });
|
|
20588
|
+
return false;
|
|
20589
|
+
}
|
|
20590
|
+
await persistConfigSetting(
|
|
20591
|
+
{
|
|
20592
|
+
configStore: opts.configStore,
|
|
20593
|
+
globalConfigPath: opts.paths.globalConfig,
|
|
20594
|
+
inProjectConfigPath: opts.paths.inProjectConfig,
|
|
20595
|
+
vault: noOpVault
|
|
20596
|
+
},
|
|
20597
|
+
(cfg) => {
|
|
20598
|
+
cfg.tools = nextTools;
|
|
20599
|
+
}
|
|
20600
|
+
);
|
|
20601
|
+
return true;
|
|
20602
|
+
}
|
|
20603
|
+
function formatOverrides() {
|
|
20604
|
+
const configured = opts.configStore.get().tools.descriptionMode ?? {};
|
|
20605
|
+
const simple = Object.entries(configured).filter(([, mode]) => normalizeToolDescriptionMode(mode) === "simple").map(([name]) => name).sort();
|
|
20606
|
+
return [
|
|
20607
|
+
`${color.bold("Tool descriptions")} ${color.dim(`(default: desc:extend)`)}`,
|
|
20608
|
+
"",
|
|
20609
|
+
simple.length > 0 ? ` ${formatDescriptionMode("simple")}: ${simple.map((name) => color.cyan(name)).join(", ")}` : ` ${formatDescriptionMode("simple")}: ${color.dim("none")}`,
|
|
20610
|
+
"",
|
|
20611
|
+
color.dim(" /tool <name> simple \xB7 /tool <name> extend \xB7 /tool list")
|
|
20612
|
+
].join("\n");
|
|
20613
|
+
}
|
|
20614
|
+
function formatList() {
|
|
20615
|
+
const header = ` ${color.dim(fit("tool", 28))} ${color.dim(fit("owner", 28))} ` + color.dim("description");
|
|
20616
|
+
const rows = opts.toolRegistry.listWithOwner().map(({ tool }) => {
|
|
20617
|
+
const mode = getToolDescriptionMode(opts.toolRegistry, tool.name);
|
|
20618
|
+
const owner = opts.toolRegistry.ownerOf(tool.name) ?? "core";
|
|
20619
|
+
return ` ${fit(tool.name, 28)} ${color.dim(fit(`[${owner}]`, 28))} ` + formatDescriptionMode(mode);
|
|
20620
|
+
});
|
|
20621
|
+
return [
|
|
20622
|
+
`${color.bold("Tool description modes")} ${color.dim("(default: desc:extend)")}`,
|
|
20623
|
+
"",
|
|
20624
|
+
header,
|
|
20625
|
+
...rows
|
|
20626
|
+
].join("\n");
|
|
20627
|
+
}
|
|
20628
|
+
function formatOne(name) {
|
|
20629
|
+
const tool = opts.toolRegistry.get(name);
|
|
20630
|
+
if (!tool) {
|
|
20631
|
+
return `${color.red("Unknown tool")}: ${name}. Use ${color.dim("/tools")} to list registered tools.`;
|
|
20632
|
+
}
|
|
20633
|
+
const mode = getToolDescriptionMode(opts.toolRegistry, name);
|
|
20634
|
+
return [
|
|
20635
|
+
`${color.bold(name)} description mode: ${formatDescriptionMode(mode)}`,
|
|
20636
|
+
"",
|
|
20637
|
+
color.dim(tool.description)
|
|
20638
|
+
].join("\n");
|
|
20639
|
+
}
|
|
20640
|
+
return {
|
|
20641
|
+
name: "tool",
|
|
20642
|
+
category: "Config",
|
|
20643
|
+
description: "Set per-tool description detail: simple or extend.",
|
|
20644
|
+
argsHint: "[<name> simple|extend]",
|
|
20645
|
+
help,
|
|
20646
|
+
async run(args) {
|
|
20647
|
+
if (!opts.configStore) {
|
|
20648
|
+
return { message: `${color.red("Error")} config store not available.` };
|
|
20649
|
+
}
|
|
20650
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
20651
|
+
const sub = (parts[0] ?? "").toLowerCase();
|
|
20652
|
+
if (!sub) return { message: formatOverrides() };
|
|
20653
|
+
if (sub === "help" || sub === "--help") return { message: this.help ?? "" };
|
|
20654
|
+
if (sub === "list") return { message: formatList() };
|
|
20655
|
+
const name = parts[0] ?? "";
|
|
20656
|
+
const tool = opts.toolRegistry.get(name);
|
|
20657
|
+
if (!tool) {
|
|
20658
|
+
return {
|
|
20659
|
+
message: `${color.red("Unknown tool")}: ${name}. Use ${color.dim("/tools")} to list registered tools.`
|
|
20660
|
+
};
|
|
20661
|
+
}
|
|
20662
|
+
if (parts.length === 1) return { message: formatOne(name) };
|
|
20663
|
+
const mode = normalizeToolDescriptionMode(parts[1]);
|
|
20664
|
+
if (!mode) {
|
|
20665
|
+
return { message: `${color.amber("Usage:")} /tool ${name} simple|extend` };
|
|
20666
|
+
}
|
|
20667
|
+
try {
|
|
20668
|
+
const persisted = await persistMode(name, mode);
|
|
20669
|
+
setToolDescriptionMode(opts.toolRegistry, name, mode);
|
|
20670
|
+
const persistence = persisted ? color.dim("saved") : color.dim("runtime only; config paths unavailable");
|
|
20671
|
+
return {
|
|
20672
|
+
message: `${color.green("\u2713")} ${color.cyan(name)} description mode -> ${formatDescriptionMode(mode)} ${persistence}`
|
|
20673
|
+
};
|
|
20674
|
+
} catch (err) {
|
|
20675
|
+
return {
|
|
20676
|
+
message: `${color.red("Could not save tool setting")}: ${toErrorMessage(err)}`
|
|
20677
|
+
};
|
|
20678
|
+
}
|
|
20679
|
+
}
|
|
20680
|
+
};
|
|
20681
|
+
}
|
|
20682
|
+
function fit2(text, width) {
|
|
20683
|
+
if (text.length <= width) return text.padEnd(width);
|
|
20684
|
+
return `${text.slice(0, Math.max(0, width - 3))}...`;
|
|
20685
|
+
}
|
|
20686
|
+
function formatDescriptionMode2(mode) {
|
|
20687
|
+
const raw = `desc:${mode}`;
|
|
20688
|
+
return mode === "simple" ? color.amber(raw) : color.dim(raw);
|
|
20689
|
+
}
|
|
20498
20690
|
function buildToolsCommand(opts) {
|
|
20499
20691
|
return {
|
|
20500
20692
|
name: "tools",
|
|
@@ -20502,10 +20694,14 @@ function buildToolsCommand(opts) {
|
|
|
20502
20694
|
description: "List registered tools.",
|
|
20503
20695
|
async run() {
|
|
20504
20696
|
const all = opts.toolRegistry.listWithOwner();
|
|
20505
|
-
const
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
|
|
20697
|
+
const header = ` ${color.dim(fit2("tool", 28))} ${color.dim(fit2("owner", 28))} ${color.dim(fit2("rw", 4))} ${color.dim(fit2("perm", 8))} ` + color.dim("description");
|
|
20698
|
+
const lines = all.map(({ tool, owner }) => {
|
|
20699
|
+
const mode = getToolDescriptionMode(opts.toolRegistry, tool.name);
|
|
20700
|
+
const rw = tool.mutating ? color.yellow(fit2("mut", 4)) : color.cyan(fit2("ro", 4));
|
|
20701
|
+
return ` ${fit2(tool.name, 28)} ${color.dim(fit2(`[${owner}]`, 28))} ${rw} ${color.dim(fit2(tool.permission, 8))} ` + formatDescriptionMode2(mode);
|
|
20702
|
+
});
|
|
20703
|
+
const msg = `${color.bold("Tools")} (${all.length}) ${color.dim("description detail via /tool <name> simple|extend")}:
|
|
20704
|
+
${header}
|
|
20509
20705
|
${lines.join("\n")}
|
|
20510
20706
|
`;
|
|
20511
20707
|
opts.renderer.write(msg);
|
|
@@ -20688,6 +20884,9 @@ function buildYoloCommand(opts) {
|
|
|
20688
20884
|
}
|
|
20689
20885
|
};
|
|
20690
20886
|
}
|
|
20887
|
+
var DEFAULT_SHADOW_PROVIDER = "anthropic";
|
|
20888
|
+
var DEFAULT_SHADOW_INTERVAL_MS = 3e4;
|
|
20889
|
+
var MIN_SHADOW_INTERVAL_MS = 5e3;
|
|
20691
20890
|
function buildShadowCommand(opts) {
|
|
20692
20891
|
return {
|
|
20693
20892
|
name: "shadow",
|
|
@@ -20697,8 +20896,8 @@ function buildShadowCommand(opts) {
|
|
|
20697
20896
|
argsHint: "<start|stop|status|hoop|model|interval> [--flag=value]",
|
|
20698
20897
|
help: [
|
|
20699
20898
|
"Usage:",
|
|
20700
|
-
" /shadow start [--
|
|
20701
|
-
"
|
|
20899
|
+
" /shadow start [--model=<provider/model>]",
|
|
20900
|
+
" Run one quiet Shadow Agent fleet check",
|
|
20702
20901
|
" /shadow stop",
|
|
20703
20902
|
" Stop the Shadow Agent",
|
|
20704
20903
|
" /shadow status",
|
|
@@ -20708,19 +20907,21 @@ function buildShadowCommand(opts) {
|
|
|
20708
20907
|
" /shadow model <provider/model>",
|
|
20709
20908
|
" Change Shadow Agent analysis model",
|
|
20710
20909
|
" /shadow interval <ms>",
|
|
20711
|
-
" Change
|
|
20910
|
+
" Change the legacy interval default kept for compatibility",
|
|
20712
20911
|
"",
|
|
20713
20912
|
"The Shadow Agent monitors:",
|
|
20714
20913
|
" \u2022 All fleet agents and their current tasks",
|
|
20715
20914
|
" \u2022 Mailbox state and message flow",
|
|
20716
20915
|
" \u2022 Spike tasks (starting/stopping instantly)",
|
|
20717
20916
|
" \u2022 Fleet-wide activity across all terminals",
|
|
20718
|
-
" \u2022 Uses deterministic rules first
|
|
20917
|
+
" \u2022 Uses deterministic host rules first; LLM only for manual/problem cases",
|
|
20918
|
+
" \u2022 Does not post routine healthy reports",
|
|
20719
20919
|
"",
|
|
20720
20920
|
"Model must be specified as provider/model, e.g. anthropic/claude-3-5-sonnet",
|
|
20921
|
+
"When omitted, Shadow uses the current leader provider/model.",
|
|
20721
20922
|
"",
|
|
20722
20923
|
"Examples:",
|
|
20723
|
-
" /shadow start
|
|
20924
|
+
" /shadow start",
|
|
20724
20925
|
" /shadow start --model=anthropic/claude-3-5-sonnet",
|
|
20725
20926
|
" /shadow status Show all agent activity",
|
|
20726
20927
|
" /shadow hoop subagent-abc123 Stop agent and notify",
|
|
@@ -20733,14 +20934,24 @@ function buildShadowCommand(opts) {
|
|
|
20733
20934
|
if (!opts.onSpawn) {
|
|
20734
20935
|
return { message: "/shadow requires a running director with subagent support." };
|
|
20735
20936
|
}
|
|
20736
|
-
const flags =
|
|
20737
|
-
|
|
20738
|
-
|
|
20739
|
-
|
|
20740
|
-
|
|
20741
|
-
|
|
20937
|
+
const flags = parseFlags2(rest);
|
|
20938
|
+
const shadowDefaults = opts.shadowController?.getDefaults?.();
|
|
20939
|
+
let intervalMs;
|
|
20940
|
+
try {
|
|
20941
|
+
intervalMs = parseInterval(
|
|
20942
|
+
flags["interval"] ?? String(shadowDefaults?.intervalMs ?? DEFAULT_SHADOW_INTERVAL_MS)
|
|
20943
|
+
);
|
|
20944
|
+
} catch (e) {
|
|
20945
|
+
return { message: `/shadow start: ${e.message}` };
|
|
20946
|
+
}
|
|
20947
|
+
const defaultModelRef = getDefaultModelRef(opts);
|
|
20948
|
+
let modelRef;
|
|
20949
|
+
const modelRaw = flags["model"];
|
|
20950
|
+
if (modelRaw === true) {
|
|
20951
|
+
return { message: "/shadow start: --model requires a provider/model value" };
|
|
20952
|
+
}
|
|
20742
20953
|
try {
|
|
20743
|
-
|
|
20954
|
+
modelRef = parseProviderModelRef(modelRaw ?? "default", defaultModelRef);
|
|
20744
20955
|
} catch (e) {
|
|
20745
20956
|
return { message: `/shadow start: ${e.message}` };
|
|
20746
20957
|
}
|
|
@@ -20755,10 +20966,10 @@ function buildShadowCommand(opts) {
|
|
|
20755
20966
|
};
|
|
20756
20967
|
}
|
|
20757
20968
|
const spawnId = await opts.onSpawn(
|
|
20758
|
-
`Shadow Agent \u2014
|
|
20969
|
+
`Shadow Agent \u2014 one-shot quiet fleet check`,
|
|
20759
20970
|
{
|
|
20760
|
-
provider:
|
|
20761
|
-
model,
|
|
20971
|
+
provider: modelRef.provider,
|
|
20972
|
+
model: modelRef.model,
|
|
20762
20973
|
tools: [
|
|
20763
20974
|
"fleet_status",
|
|
20764
20975
|
"fleet_health",
|
|
@@ -20766,30 +20977,33 @@ function buildShadowCommand(opts) {
|
|
|
20766
20977
|
"mailbox",
|
|
20767
20978
|
"mail_inbox",
|
|
20768
20979
|
"mail_send",
|
|
20769
|
-
"cron_schedule",
|
|
20770
|
-
"cron_list",
|
|
20771
|
-
"cron_cancel",
|
|
20772
|
-
"spawn_subagent",
|
|
20773
|
-
"assign_task",
|
|
20774
20980
|
"terminate_subagent"
|
|
20775
20981
|
],
|
|
20776
20982
|
name: "shadow",
|
|
20777
|
-
|
|
20983
|
+
shadowIntervalMs: intervalMs
|
|
20778
20984
|
}
|
|
20779
20985
|
);
|
|
20780
|
-
return { message: `${color.green("\u2713")} Shadow Agent
|
|
20781
|
-
${color.dim("
|
|
20782
|
-
${color.dim("Model:")} ${
|
|
20986
|
+
return { message: `${color.green("\u2713")} Shadow Agent queued: ${spawnId}
|
|
20987
|
+
${color.dim("Mode:")} one-shot quiet check
|
|
20988
|
+
${color.dim("Model:")} ${modelRef.label}` };
|
|
20783
20989
|
}
|
|
20784
20990
|
case "stop": {
|
|
20991
|
+
const activeId = opts.shadowController?.activeId;
|
|
20992
|
+
if (!activeId) {
|
|
20993
|
+
return { message: `${color.yellow("\u26A0")} No active Shadow Agent is registered for this session.` };
|
|
20994
|
+
}
|
|
20995
|
+
if (!opts.onFleetTerminate) {
|
|
20996
|
+
return { message: "/shadow stop requires fleet termination support in this session." };
|
|
20997
|
+
}
|
|
20998
|
+
const ok = await opts.onFleetTerminate(activeId);
|
|
20999
|
+
if (ok) {
|
|
21000
|
+
opts.shadowController?.clear();
|
|
21001
|
+
return { message: `${color.green("\u2713")} Shadow Agent stopped: ${activeId}` };
|
|
21002
|
+
}
|
|
20785
21003
|
return {
|
|
20786
21004
|
message: [
|
|
20787
|
-
`${color.
|
|
20788
|
-
""
|
|
20789
|
-
`It auto-starts on first LLM request and runs until session end.`,
|
|
20790
|
-
"",
|
|
20791
|
-
`To stop a specific agent: ${color.bold("/hoop <agent-id>")}`,
|
|
20792
|
-
`To stop all agents: ${color.bold("/hoop all")}`
|
|
21005
|
+
`${color.red("\u2717")} Failed to stop Shadow Agent ${color.bold(activeId)}.`,
|
|
21006
|
+
`It may already be stopped. Use ${color.bold("/shadow status")} to inspect active agents.`
|
|
20793
21007
|
].join("\n")
|
|
20794
21008
|
};
|
|
20795
21009
|
}
|
|
@@ -20807,44 +21021,71 @@ ${color.dim("Model:")} ${model}` };
|
|
|
20807
21021
|
}
|
|
20808
21022
|
const reasonFlag = extraArgs.find((f) => f.startsWith("--reason="));
|
|
20809
21023
|
const reason = reasonFlag ? reasonFlag.slice("--reason=".length) : "Agent flagged by Shadow Agent";
|
|
20810
|
-
|
|
20811
|
-
|
|
21024
|
+
if (targetId.toLowerCase() === "all") {
|
|
21025
|
+
if (!opts.onFleetKill) {
|
|
21026
|
+
return { message: "/shadow hoop all requires fleet kill support in this session." };
|
|
21027
|
+
}
|
|
21028
|
+
const killed = await opts.onFleetKill();
|
|
21029
|
+
opts.shadowController?.clear();
|
|
21030
|
+
return {
|
|
21031
|
+
message: [
|
|
21032
|
+
`${color.red("\u26A0")} HOOP: Stopped ${killed} running agent(s)`,
|
|
21033
|
+
"",
|
|
21034
|
+
`Target: ${color.bold("all")}`,
|
|
21035
|
+
`Reason: ${color.yellow(reason)}`
|
|
21036
|
+
].join("\n")
|
|
21037
|
+
};
|
|
21038
|
+
}
|
|
21039
|
+
if (!opts.onFleetTerminate) {
|
|
21040
|
+
return { message: "/shadow hoop requires fleet termination support in this session." };
|
|
21041
|
+
}
|
|
21042
|
+
const agentInfo = opts.onAgents?.(targetId) ?? "";
|
|
21043
|
+
const ok = await opts.onFleetTerminate(targetId);
|
|
21044
|
+
if (ok && opts.shadowController?.activeId === targetId) {
|
|
21045
|
+
opts.shadowController.clear();
|
|
21046
|
+
}
|
|
20812
21047
|
return {
|
|
20813
21048
|
message: [
|
|
20814
|
-
`${color.red("\u26A0")} HOOP:
|
|
21049
|
+
ok ? `${color.red("\u26A0")} HOOP: Stopped agent` : `${color.red("\u2717")} HOOP: Failed to stop agent`,
|
|
20815
21050
|
"",
|
|
20816
21051
|
`Target: ${color.bold(targetId)}`,
|
|
20817
21052
|
`Reason: ${color.yellow(reason)}`,
|
|
20818
|
-
|
|
20819
|
-
Agent info:
|
|
20820
|
-
|
|
20821
|
-
`${color.dim("Sending termination signal...")}`,
|
|
20822
|
-
`${color.dim("Notification will be sent to project mailbox.")}`
|
|
21053
|
+
agentInfo ? `
|
|
21054
|
+
Agent info:
|
|
21055
|
+
${agentInfo}` : ""
|
|
20823
21056
|
].join("\n")
|
|
20824
21057
|
};
|
|
20825
21058
|
}
|
|
20826
21059
|
case "model": {
|
|
20827
21060
|
const [modelId] = rest;
|
|
21061
|
+
const defaultModelRef = getDefaultModelRef(opts);
|
|
20828
21062
|
if (!modelId) {
|
|
20829
|
-
return { message:
|
|
21063
|
+
return { message: `/shadow model <provider/model> \u2014 change Shadow Agent analysis model.
|
|
21064
|
+
Current default: ${defaultModelRef.label}` };
|
|
20830
21065
|
}
|
|
21066
|
+
let parsed;
|
|
20831
21067
|
try {
|
|
20832
|
-
|
|
21068
|
+
parsed = parseProviderModelRef(modelId, defaultModelRef);
|
|
20833
21069
|
} catch (e) {
|
|
20834
21070
|
return { message: `/shadow model: ${e.message}` };
|
|
20835
21071
|
}
|
|
20836
|
-
|
|
21072
|
+
opts.shadowController?.setDefaults?.({ provider: parsed.provider, model: parsed.model });
|
|
21073
|
+
return { message: `/shadow model ${parsed.label}
|
|
20837
21074
|
${color.dim("Model will be applied on next /shadow start")}` };
|
|
20838
21075
|
}
|
|
20839
21076
|
case "interval": {
|
|
20840
21077
|
const [msStr] = rest;
|
|
20841
21078
|
if (!msStr) {
|
|
20842
|
-
return { message:
|
|
21079
|
+
return { message: `/shadow interval <ms> \u2014 update the legacy Shadow interval default.
|
|
21080
|
+
Current default: ${DEFAULT_SHADOW_INTERVAL_MS}ms (30 seconds)` };
|
|
20843
21081
|
}
|
|
20844
|
-
|
|
20845
|
-
|
|
20846
|
-
|
|
21082
|
+
let ms;
|
|
21083
|
+
try {
|
|
21084
|
+
ms = parseInterval(msStr);
|
|
21085
|
+
} catch (e) {
|
|
21086
|
+
return { message: `/shadow interval: ${e.message}` };
|
|
20847
21087
|
}
|
|
21088
|
+
opts.shadowController?.setDefaults?.({ intervalMs: ms });
|
|
20848
21089
|
return { message: `/shadow interval ${ms}ms
|
|
20849
21090
|
${color.dim("Interval will be applied on next /shadow start")}` };
|
|
20850
21091
|
}
|
|
@@ -20855,12 +21096,49 @@ ${color.dim("Interval will be applied on next /shadow start")}` };
|
|
|
20855
21096
|
}
|
|
20856
21097
|
};
|
|
20857
21098
|
}
|
|
20858
|
-
function
|
|
20859
|
-
|
|
20860
|
-
|
|
21099
|
+
function getDefaultModelRef(opts) {
|
|
21100
|
+
const shadowDefaults = opts.shadowController?.getDefaults?.();
|
|
21101
|
+
const liveConfig = opts.configStore?.get?.();
|
|
21102
|
+
const provider = shadowDefaults?.provider?.trim() || liveConfig?.provider?.trim() || opts.llmProvider?.id?.trim() || DEFAULT_SHADOW_PROVIDER;
|
|
21103
|
+
const model = shadowDefaults?.model?.trim() || liveConfig?.model?.trim() || opts.llmModel?.trim() || "";
|
|
21104
|
+
return {
|
|
21105
|
+
provider,
|
|
21106
|
+
model,
|
|
21107
|
+
label: model ? `${provider}/${model}` : provider
|
|
21108
|
+
};
|
|
21109
|
+
}
|
|
21110
|
+
function parseProviderModelRef(model, defaultRef) {
|
|
21111
|
+
if (model === "default") {
|
|
21112
|
+
return defaultRef;
|
|
21113
|
+
}
|
|
21114
|
+
const slash = model.indexOf("/");
|
|
21115
|
+
if (slash <= 0 || slash === model.length - 1) {
|
|
20861
21116
|
throw new Error(`Model must be in provider/model format (e.g. anthropic/claude-3-5-sonnet), got: "${model}"`);
|
|
20862
21117
|
}
|
|
20863
|
-
return
|
|
21118
|
+
return {
|
|
21119
|
+
provider: model.slice(0, slash),
|
|
21120
|
+
model: model.slice(slash + 1),
|
|
21121
|
+
label: model
|
|
21122
|
+
};
|
|
21123
|
+
}
|
|
21124
|
+
function parseInterval(value) {
|
|
21125
|
+
if (value === true || !/^\d+$/.test(value)) {
|
|
21126
|
+
throw new Error(`interval must be an integer >= ${MIN_SHADOW_INTERVAL_MS}ms`);
|
|
21127
|
+
}
|
|
21128
|
+
const ms = Number.parseInt(value, 10);
|
|
21129
|
+
if (!Number.isFinite(ms) || ms < MIN_SHADOW_INTERVAL_MS) {
|
|
21130
|
+
throw new Error(`interval must be an integer >= ${MIN_SHADOW_INTERVAL_MS}ms`);
|
|
21131
|
+
}
|
|
21132
|
+
return ms;
|
|
21133
|
+
}
|
|
21134
|
+
function parseFlags2(args) {
|
|
21135
|
+
return Object.fromEntries(
|
|
21136
|
+
args.filter((f) => f.startsWith("--")).map((f) => {
|
|
21137
|
+
const raw = f.slice(2);
|
|
21138
|
+
const eq = raw.indexOf("=");
|
|
21139
|
+
return eq >= 0 ? [raw.slice(0, eq), raw.slice(eq + 1)] : [raw, true];
|
|
21140
|
+
})
|
|
21141
|
+
);
|
|
20864
21142
|
}
|
|
20865
21143
|
|
|
20866
21144
|
// src/slash-commands/index.ts
|
|
@@ -20877,6 +21155,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
20877
21155
|
buildDoctorCommand(opts),
|
|
20878
21156
|
buildCodebaseReindexCommand(opts),
|
|
20879
21157
|
buildTechStackCommand(opts),
|
|
21158
|
+
buildToolCommand(opts),
|
|
20880
21159
|
buildToolsCommand(opts),
|
|
20881
21160
|
buildPluginCommand(opts),
|
|
20882
21161
|
buildPruneCommand(opts),
|
|
@@ -24200,7 +24479,7 @@ var diagCmd = async (_args, deps) => {
|
|
|
24200
24479
|
` modelsCache: ${deps.paths.modelsCache}`,
|
|
24201
24480
|
` cacheAge: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never"}`,
|
|
24202
24481
|
` node: ${process.version}`,
|
|
24203
|
-
` os: ${
|
|
24482
|
+
` os: ${os7.platform()} ${os7.release()}`,
|
|
24204
24483
|
` provider: ${cfg.provider ?? "<unset>"}`,
|
|
24205
24484
|
` model: ${cfg.model ?? "<unset>"}`,
|
|
24206
24485
|
` tools: ${deps.toolRegistry?.list().length ?? 0}`,
|
|
@@ -26052,6 +26331,9 @@ var projectsCmd = async (_args, deps) => {
|
|
|
26052
26331
|
return 0;
|
|
26053
26332
|
}
|
|
26054
26333
|
};
|
|
26334
|
+
|
|
26335
|
+
// src/subcommands/handlers/providers-models.ts
|
|
26336
|
+
init_provider_config_utils();
|
|
26055
26337
|
var providersCmd = async (args, deps) => {
|
|
26056
26338
|
const showAll = args.includes("--all");
|
|
26057
26339
|
const showUnsupported = args.includes("--unsupported");
|
|
@@ -26099,7 +26381,7 @@ ${color.dim(`Current: ${deps.config.provider ?? "<unset>"} / ${deps.config.model
|
|
|
26099
26381
|
return 1;
|
|
26100
26382
|
}
|
|
26101
26383
|
};
|
|
26102
|
-
function
|
|
26384
|
+
function parseFlags3(args) {
|
|
26103
26385
|
const flags = {};
|
|
26104
26386
|
for (let i = 0; i < args.length; i++) {
|
|
26105
26387
|
const a = expectDefined(args[i]);
|
|
@@ -26144,6 +26426,10 @@ function fmtPrice3(usdPer1M) {
|
|
|
26144
26426
|
}
|
|
26145
26427
|
var modelsCmd = async (args, deps) => {
|
|
26146
26428
|
const sub = args[0];
|
|
26429
|
+
if (sub === "hide") return modelsHide(args.slice(1), deps);
|
|
26430
|
+
if (sub === "show") return modelsShow(args.slice(1), deps);
|
|
26431
|
+
if (sub === "hidden") return modelsHidden(args.slice(1), deps);
|
|
26432
|
+
if (sub === "reset") return modelsReset(args.slice(1), deps);
|
|
26147
26433
|
if (sub === "add") return modelsAdd(args.slice(1), deps);
|
|
26148
26434
|
if (sub === "remove") return modelsRemove(args.slice(1), deps);
|
|
26149
26435
|
if (sub === "list") return modelsList(args.slice(1), deps);
|
|
@@ -26161,7 +26447,7 @@ var modelsCmd = async (args, deps) => {
|
|
|
26161
26447
|
return 1;
|
|
26162
26448
|
}
|
|
26163
26449
|
}
|
|
26164
|
-
const flags =
|
|
26450
|
+
const flags = parseFlags3(args);
|
|
26165
26451
|
const search = typeof flags["search"] === "string" ? flags["search"].toLowerCase() : "";
|
|
26166
26452
|
const perPage = Number(flags["per-page"]) > 0 ? Number(flags["per-page"]) : DEFAULT_PER_PAGE;
|
|
26167
26453
|
const page = Math.max(1, Number(flags["page"]) || 1);
|
|
@@ -26193,12 +26479,17 @@ var modelsCmd = async (args, deps) => {
|
|
|
26193
26479
|
`));
|
|
26194
26480
|
const userModels = deps.config.providers?.[providerId]?.models;
|
|
26195
26481
|
const catalogById = new Map(provider.models.map((m) => [m.id, m]));
|
|
26196
|
-
const
|
|
26482
|
+
const allKnownSorted = [...provider.models].sort(
|
|
26197
26483
|
(a, b) => (b.release_date ?? "").localeCompare(a.release_date ?? "")
|
|
26198
26484
|
);
|
|
26199
|
-
|
|
26200
|
-
|
|
26201
|
-
|
|
26485
|
+
const allSorted = userModels !== void 0 ? userModels.map((id) => catalogById.get(id) ?? { id, name: id }) : allKnownSorted;
|
|
26486
|
+
if (userModels !== void 0) {
|
|
26487
|
+
const hiddenCount = Math.max(0, allKnownSorted.length - userModels.length);
|
|
26488
|
+
deps.renderer.write(
|
|
26489
|
+
color.dim(`(${userModels.length} visible model(s) from your saved config${hiddenCount > 0 ? `, ${hiddenCount} hidden` : ""})
|
|
26490
|
+
`)
|
|
26491
|
+
);
|
|
26492
|
+
}
|
|
26202
26493
|
const filtered = search ? allSorted.filter((m) => m.id.toLowerCase().includes(search)) : allSorted;
|
|
26203
26494
|
const total = filtered.length;
|
|
26204
26495
|
const totalPages = Math.max(1, Math.ceil(total / perPage));
|
|
@@ -26250,6 +26541,164 @@ ${navLines.join(" \xB7 ")}
|
|
|
26250
26541
|
);
|
|
26251
26542
|
return 0;
|
|
26252
26543
|
};
|
|
26544
|
+
function aliasProviderId(configProviderId, cfg) {
|
|
26545
|
+
return cfg?.type && cfg.type !== configProviderId ? cfg.type : configProviderId;
|
|
26546
|
+
}
|
|
26547
|
+
async function getCatalogProviderForConfigProvider(providerId, deps) {
|
|
26548
|
+
const cfg = deps.config.providers?.[providerId];
|
|
26549
|
+
const lookupId = aliasProviderId(providerId, cfg);
|
|
26550
|
+
const provider = await deps.modelsRegistry.getProvider(lookupId);
|
|
26551
|
+
return { providerId: lookupId, provider };
|
|
26552
|
+
}
|
|
26553
|
+
function uniqueStrings(values) {
|
|
26554
|
+
return [...new Set(values.map((v) => v.trim()).filter(Boolean))];
|
|
26555
|
+
}
|
|
26556
|
+
async function modelsHide(args, deps) {
|
|
26557
|
+
const providerId = args[0];
|
|
26558
|
+
const modelId = args[1];
|
|
26559
|
+
if (!providerId || !modelId) {
|
|
26560
|
+
deps.renderer.writeError("Usage: wstack models hide <provider> <model>");
|
|
26561
|
+
return 1;
|
|
26562
|
+
}
|
|
26563
|
+
const saved = deps.config.providers?.[providerId];
|
|
26564
|
+
if (!saved) {
|
|
26565
|
+
deps.renderer.writeError(`Provider "${providerId}" is not configured.`);
|
|
26566
|
+
return 1;
|
|
26567
|
+
}
|
|
26568
|
+
const { providerId: lookupId, provider } = await getCatalogProviderForConfigProvider(providerId, deps);
|
|
26569
|
+
if (!provider) {
|
|
26570
|
+
deps.renderer.writeError(
|
|
26571
|
+
lookupId !== providerId ? `Alias "${providerId}" points at catalog id "${lookupId}" which is not in the cache.` : `Provider "${providerId}" not in catalog.`
|
|
26572
|
+
);
|
|
26573
|
+
return 1;
|
|
26574
|
+
}
|
|
26575
|
+
const knownIds = provider.models.map((m) => m.id);
|
|
26576
|
+
const visible = saved.models !== void 0 ? [...saved.models] : [...knownIds];
|
|
26577
|
+
if (!visible.includes(modelId)) {
|
|
26578
|
+
deps.renderer.writeInfo(`${providerId}/${modelId} is already hidden.`);
|
|
26579
|
+
return 0;
|
|
26580
|
+
}
|
|
26581
|
+
const nextVisible = visible.filter((id) => id !== modelId);
|
|
26582
|
+
await mutateConfigProviders(deps.paths.globalConfig, deps.vault, (providers) => {
|
|
26583
|
+
const p = providers[providerId];
|
|
26584
|
+
if (!p) return;
|
|
26585
|
+
p.models = nextVisible;
|
|
26586
|
+
});
|
|
26587
|
+
deps.config.providers = {
|
|
26588
|
+
...deps.config.providers ?? {},
|
|
26589
|
+
[providerId]: { ...saved, models: nextVisible }
|
|
26590
|
+
};
|
|
26591
|
+
deps.renderer.writeInfo(
|
|
26592
|
+
`Hidden ${providerId}/${modelId}. Visible: ${nextVisible.length}, hidden: ${Math.max(0, knownIds.length - nextVisible.length)}.`
|
|
26593
|
+
);
|
|
26594
|
+
if (nextVisible.length === 0) {
|
|
26595
|
+
deps.renderer.write(color.dim("This provider now has no visible models. Use `wstack models show` or `wstack models reset` to restore.\n"));
|
|
26596
|
+
}
|
|
26597
|
+
return 0;
|
|
26598
|
+
}
|
|
26599
|
+
async function modelsShow(args, deps) {
|
|
26600
|
+
const providerId = args[0];
|
|
26601
|
+
const modelId = args[1];
|
|
26602
|
+
if (!providerId || !modelId) {
|
|
26603
|
+
deps.renderer.writeError("Usage: wstack models show <provider> <model>");
|
|
26604
|
+
return 1;
|
|
26605
|
+
}
|
|
26606
|
+
const saved = deps.config.providers?.[providerId];
|
|
26607
|
+
if (!saved) {
|
|
26608
|
+
deps.renderer.writeError(`Provider "${providerId}" is not configured.`);
|
|
26609
|
+
return 1;
|
|
26610
|
+
}
|
|
26611
|
+
const { providerId: lookupId, provider } = await getCatalogProviderForConfigProvider(providerId, deps);
|
|
26612
|
+
if (!provider) {
|
|
26613
|
+
deps.renderer.writeError(
|
|
26614
|
+
lookupId !== providerId ? `Alias "${providerId}" points at catalog id "${lookupId}" which is not in the cache.` : `Provider "${providerId}" not in catalog.`
|
|
26615
|
+
);
|
|
26616
|
+
return 1;
|
|
26617
|
+
}
|
|
26618
|
+
const knownIds = provider.models.map((m) => m.id);
|
|
26619
|
+
if (saved.models === void 0) {
|
|
26620
|
+
deps.renderer.writeInfo(`${providerId}/${modelId} is already visible.`);
|
|
26621
|
+
return 0;
|
|
26622
|
+
}
|
|
26623
|
+
const nextVisible = uniqueStrings([...saved.models, modelId]);
|
|
26624
|
+
await mutateConfigProviders(deps.paths.globalConfig, deps.vault, (providers) => {
|
|
26625
|
+
const p = providers[providerId];
|
|
26626
|
+
if (!p) return;
|
|
26627
|
+
p.models = nextVisible;
|
|
26628
|
+
});
|
|
26629
|
+
deps.config.providers = {
|
|
26630
|
+
...deps.config.providers ?? {},
|
|
26631
|
+
[providerId]: { ...saved, models: nextVisible }
|
|
26632
|
+
};
|
|
26633
|
+
const isKnown = knownIds.includes(modelId);
|
|
26634
|
+
deps.renderer.writeInfo(
|
|
26635
|
+
`Visible ${providerId}/${modelId}${isKnown ? "" : " (not in current catalog)"}. Visible: ${nextVisible.length}.`
|
|
26636
|
+
);
|
|
26637
|
+
return 0;
|
|
26638
|
+
}
|
|
26639
|
+
async function modelsHidden(args, deps) {
|
|
26640
|
+
const providerId = args[0] ?? deps.config.provider;
|
|
26641
|
+
if (!providerId) {
|
|
26642
|
+
deps.renderer.writeError("Usage: wstack models hidden <provider>");
|
|
26643
|
+
return 1;
|
|
26644
|
+
}
|
|
26645
|
+
const saved = deps.config.providers?.[providerId];
|
|
26646
|
+
if (!saved) {
|
|
26647
|
+
deps.renderer.writeError(`Provider "${providerId}" is not configured.`);
|
|
26648
|
+
return 1;
|
|
26649
|
+
}
|
|
26650
|
+
const { providerId: lookupId, provider } = await getCatalogProviderForConfigProvider(providerId, deps);
|
|
26651
|
+
if (!provider) {
|
|
26652
|
+
deps.renderer.writeError(
|
|
26653
|
+
lookupId !== providerId ? `Alias "${providerId}" points at catalog id "${lookupId}" which is not in the cache.` : `Provider "${providerId}" not in catalog.`
|
|
26654
|
+
);
|
|
26655
|
+
return 1;
|
|
26656
|
+
}
|
|
26657
|
+
const knownIds = provider.models.map((m) => m.id);
|
|
26658
|
+
const visible = saved.models;
|
|
26659
|
+
const hidden = visible === void 0 ? [] : knownIds.filter((id) => !visible.includes(id));
|
|
26660
|
+
deps.renderer.write(`${color.bold("Hidden models")} ${color.dim(`(${providerId})`)}
|
|
26661
|
+
`);
|
|
26662
|
+
if (hidden.length === 0) {
|
|
26663
|
+
deps.renderer.write(color.dim("(none hidden)\n"));
|
|
26664
|
+
return 0;
|
|
26665
|
+
}
|
|
26666
|
+
for (const id of hidden) deps.renderer.write(` ${id}
|
|
26667
|
+
`);
|
|
26668
|
+
deps.renderer.write(color.dim(`
|
|
26669
|
+
Restore: wstack models show ${providerId} <model> \xB7 reset ${providerId}
|
|
26670
|
+
`));
|
|
26671
|
+
return 0;
|
|
26672
|
+
}
|
|
26673
|
+
async function modelsReset(args, deps) {
|
|
26674
|
+
const providerId = args[0];
|
|
26675
|
+
if (!providerId) {
|
|
26676
|
+
deps.renderer.writeError("Usage: wstack models reset <provider>");
|
|
26677
|
+
return 1;
|
|
26678
|
+
}
|
|
26679
|
+
const saved = deps.config.providers?.[providerId];
|
|
26680
|
+
if (!saved) {
|
|
26681
|
+
deps.renderer.writeError(`Provider "${providerId}" is not configured.`);
|
|
26682
|
+
return 1;
|
|
26683
|
+
}
|
|
26684
|
+
if (saved.models === void 0) {
|
|
26685
|
+
deps.renderer.writeInfo(`${providerId} already shows the full catalog model list.`);
|
|
26686
|
+
return 0;
|
|
26687
|
+
}
|
|
26688
|
+
await mutateConfigProviders(deps.paths.globalConfig, deps.vault, (providers) => {
|
|
26689
|
+
const p = providers[providerId];
|
|
26690
|
+
if (!p) return;
|
|
26691
|
+
delete p.models;
|
|
26692
|
+
});
|
|
26693
|
+
const nextProvider = { ...saved };
|
|
26694
|
+
delete nextProvider.models;
|
|
26695
|
+
deps.config.providers = {
|
|
26696
|
+
...deps.config.providers ?? {},
|
|
26697
|
+
[providerId]: nextProvider
|
|
26698
|
+
};
|
|
26699
|
+
deps.renderer.writeInfo(`Reset visible model list for ${providerId} to the full catalog default.`);
|
|
26700
|
+
return 0;
|
|
26701
|
+
}
|
|
26253
26702
|
async function modelsCaps(args, deps) {
|
|
26254
26703
|
const providerId = args[0] ?? deps.config.provider;
|
|
26255
26704
|
const modelId = args[1] ?? deps.config.model;
|
|
@@ -26343,7 +26792,7 @@ function parseBoolFlag(flags, key) {
|
|
|
26343
26792
|
return void 0;
|
|
26344
26793
|
}
|
|
26345
26794
|
async function modelsAdd(args, deps) {
|
|
26346
|
-
const flags =
|
|
26795
|
+
const flags = parseFlags3(args);
|
|
26347
26796
|
const pos = positionals2(args);
|
|
26348
26797
|
const modelId = pos[0];
|
|
26349
26798
|
if (!modelId) {
|
|
@@ -27196,7 +27645,7 @@ function installWarningSummary(output) {
|
|
|
27196
27645
|
}
|
|
27197
27646
|
var versionCmd = async (_args, deps) => {
|
|
27198
27647
|
deps.renderer.write(
|
|
27199
|
-
`WrongStack ${CLI_VERSION} (apiVersion ${API_VERSION}, node ${process.version}, ${
|
|
27648
|
+
`WrongStack ${CLI_VERSION} (apiVersion ${API_VERSION}, node ${process.version}, ${os7.platform()})
|
|
27200
27649
|
`
|
|
27201
27650
|
);
|
|
27202
27651
|
return 0;
|
|
@@ -27738,6 +28187,7 @@ function registerBuiltinTools(deps) {
|
|
|
27738
28187
|
deps.toolRegistry.register(
|
|
27739
28188
|
makeMailInboxTool({ projectDir: deps.wpaths.projectDir, events: deps.events })
|
|
27740
28189
|
);
|
|
28190
|
+
applyToolDescriptionModes(deps.toolRegistry, deps.config.tools?.descriptionMode);
|
|
27741
28191
|
}
|
|
27742
28192
|
|
|
27743
28193
|
// src/cli-eternal-flag.ts
|
|
@@ -28330,7 +28780,8 @@ async function runWebUIDispatch(ctx) {
|
|
|
28330
28780
|
renderer,
|
|
28331
28781
|
onAutonomy,
|
|
28332
28782
|
onModelContextResolved,
|
|
28333
|
-
activeRecoveryLock
|
|
28783
|
+
activeRecoveryLock,
|
|
28784
|
+
sddSubagentFactory
|
|
28334
28785
|
} = ctx;
|
|
28335
28786
|
agent.disableInteractiveConfirmation();
|
|
28336
28787
|
renderer.setSilent(true);
|
|
@@ -28361,6 +28812,7 @@ async function runWebUIDispatch(ctx) {
|
|
|
28361
28812
|
modeStore,
|
|
28362
28813
|
modeId,
|
|
28363
28814
|
needsSetup,
|
|
28815
|
+
sddSubagentFactory,
|
|
28364
28816
|
// Print the "open this" banner only once the server is actually
|
|
28365
28817
|
// listening, using the RESOLVED ports. The requested port
|
|
28366
28818
|
// (flags.port) auto-advances past busy ports inside runWebUI, so a
|
|
@@ -28417,9 +28869,13 @@ var AUTO_PHASE_EVENTS = [
|
|
|
28417
28869
|
"phase.completed",
|
|
28418
28870
|
"phase.failed",
|
|
28419
28871
|
"phase.statusChange",
|
|
28872
|
+
"phase.taskStarted",
|
|
28420
28873
|
"phase.taskCompleted",
|
|
28421
28874
|
"phase.taskFailed",
|
|
28422
28875
|
"phase.taskRetrying",
|
|
28876
|
+
"phase.taskAssigned",
|
|
28877
|
+
"phase.taskAdded",
|
|
28878
|
+
"phase.taskMoved",
|
|
28423
28879
|
"phase.verifying",
|
|
28424
28880
|
"phase.verifyFailed",
|
|
28425
28881
|
"phase.repairing",
|
|
@@ -28430,6 +28886,8 @@ var AUTO_PHASE_EVENTS = [
|
|
|
28430
28886
|
"graph.failed",
|
|
28431
28887
|
"agent.assigned",
|
|
28432
28888
|
"agent.released",
|
|
28889
|
+
// Live multi-agent SDD board snapshot → TUI board overlay (Ctrl+B).
|
|
28890
|
+
"sdd.board.snapshot",
|
|
28433
28891
|
// Git-worktree isolation lifecycle → TUI worktree panel/monitor.
|
|
28434
28892
|
"worktree.allocated",
|
|
28435
28893
|
"worktree.committed",
|
|
@@ -29552,6 +30010,13 @@ async function runRepl(opts) {
|
|
|
29552
30010
|
interrupts = 0;
|
|
29553
30011
|
return;
|
|
29554
30012
|
}
|
|
30013
|
+
const sddRun = opts.getSddRun?.();
|
|
30014
|
+
if (sddRun?.isRunning()) {
|
|
30015
|
+
sddRun.stop();
|
|
30016
|
+
opts.renderer.writeWarning("SDD run stop requested. Press Ctrl+C again to exit.");
|
|
30017
|
+
interrupts = 0;
|
|
30018
|
+
return;
|
|
30019
|
+
}
|
|
29555
30020
|
if (activeCtrl) {
|
|
29556
30021
|
activeCtrl.abort();
|
|
29557
30022
|
const killed = opts.onInterruptFleet?.() ?? 0;
|
|
@@ -30400,6 +30865,7 @@ async function execute(deps) {
|
|
|
30400
30865
|
tokenCounter,
|
|
30401
30866
|
config,
|
|
30402
30867
|
configStore,
|
|
30868
|
+
sddSubagentFactory,
|
|
30403
30869
|
renderer,
|
|
30404
30870
|
reader,
|
|
30405
30871
|
session,
|
|
@@ -30451,6 +30917,7 @@ async function execute(deps) {
|
|
|
30451
30917
|
onValidateAutoProceed,
|
|
30452
30918
|
getEternalEngine,
|
|
30453
30919
|
getParallelEngine,
|
|
30920
|
+
getSddRun,
|
|
30454
30921
|
subscribeEternalIteration,
|
|
30455
30922
|
subscribeEternalStage,
|
|
30456
30923
|
skillLoader,
|
|
@@ -30729,6 +31196,7 @@ async function execute(deps) {
|
|
|
30729
31196
|
// can access them (entry.tsx parses from rendered messages).
|
|
30730
31197
|
setSuggestions,
|
|
30731
31198
|
getEternalEngine,
|
|
31199
|
+
getSddRun,
|
|
30732
31200
|
subscribeEternalIteration,
|
|
30733
31201
|
subscribeEternalStage,
|
|
30734
31202
|
subscribeAutoPhase,
|
|
@@ -30966,7 +31434,8 @@ async function execute(deps) {
|
|
|
30966
31434
|
renderer,
|
|
30967
31435
|
onAutonomy,
|
|
30968
31436
|
activeRecoveryLock,
|
|
30969
|
-
onModelContextResolved
|
|
31437
|
+
onModelContextResolved,
|
|
31438
|
+
sddSubagentFactory
|
|
30970
31439
|
});
|
|
30971
31440
|
} else {
|
|
30972
31441
|
code = await runRepl({
|
|
@@ -30996,6 +31465,7 @@ async function execute(deps) {
|
|
|
30996
31465
|
autoProceedMaxIterations,
|
|
30997
31466
|
getEternalEngine,
|
|
30998
31467
|
getParallelEngine,
|
|
31468
|
+
getSddRun,
|
|
30999
31469
|
skillLoader,
|
|
31000
31470
|
agentsMonitorController,
|
|
31001
31471
|
fleetStreamController,
|
|
@@ -31109,6 +31579,19 @@ function buildRoutingRunner(config, host) {
|
|
|
31109
31579
|
}
|
|
31110
31580
|
|
|
31111
31581
|
// src/fleet/host.ts
|
|
31582
|
+
function buildShadowAgentTaskDescription(reason) {
|
|
31583
|
+
return `Shadow Agent one-shot fleet monitor. Reason: ${reason}.
|
|
31584
|
+
|
|
31585
|
+
Run exactly one quiet pass:
|
|
31586
|
+
- Call fleet_status and fleet_health.
|
|
31587
|
+
- Inspect mail_inbox only for explicit hoop/shadow control messages.
|
|
31588
|
+
- Do not broadcast startup, heartbeat, shutdown, or healthy status messages.
|
|
31589
|
+
- Use mail_send only for high/critical anomalies or explicit control replies.
|
|
31590
|
+
- Terminate agents only when explicitly commanded.
|
|
31591
|
+
- If the fleet is healthy and no command needs a reply, keep the final answer to "shadow: quiet".
|
|
31592
|
+
|
|
31593
|
+
The host will stop this Shadow Agent after this pass. Do not schedule follow-up work.`;
|
|
31594
|
+
}
|
|
31112
31595
|
var MultiAgentHost = class _MultiAgentHost {
|
|
31113
31596
|
constructor(deps, opts = {}) {
|
|
31114
31597
|
this.deps = deps;
|
|
@@ -31124,10 +31607,9 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
31124
31607
|
* critic.evaluation) onto the fleet bus without needing the tool registered
|
|
31125
31608
|
* in the host's ToolRegistry. */
|
|
31126
31609
|
fleetEmitTool;
|
|
31127
|
-
/**
|
|
31128
|
-
*
|
|
31129
|
-
|
|
31130
|
-
fleetStatusTool;
|
|
31610
|
+
/** Director-owned tools available to scoped subagents even when the leader
|
|
31611
|
+
* ToolRegistry was not populated because director mode was promoted lazily. */
|
|
31612
|
+
directorToolsByName = /* @__PURE__ */ new Map();
|
|
31131
31613
|
/** Lazily built alongside the director — produces per-subagent JSONL
|
|
31132
31614
|
* writers under `<sessionsRoot>/<runId>/`. Null without sessionsRoot. */
|
|
31133
31615
|
sessionFactory;
|
|
@@ -31155,6 +31637,23 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
31155
31637
|
* adaptiveConcurrency.enabled = true. Monitors FleetBus for 429 errors and
|
|
31156
31638
|
* automatically adjusts maxConcurrent to prevent rate limiting. */
|
|
31157
31639
|
adaptiveConcurrencyController;
|
|
31640
|
+
/** Active Shadow Agent spawned by the host or /shadow start. */
|
|
31641
|
+
shadowAgentId = null;
|
|
31642
|
+
/** Assigned monitoring task for the active Shadow Agent. */
|
|
31643
|
+
shadowTaskId = null;
|
|
31644
|
+
/** All internal Shadow Agent startup/heartbeat task ids, excluded from fleet summaries. */
|
|
31645
|
+
shadowTaskIds = /* @__PURE__ */ new Set();
|
|
31646
|
+
/** Shadow task ids assigned but not yet completed. Prevents heartbeat backlog. */
|
|
31647
|
+
shadowOutstandingTaskIds = /* @__PURE__ */ new Set();
|
|
31648
|
+
/** Shadow task ids whose subagent should stop immediately after the one-shot pass. */
|
|
31649
|
+
shadowStopAfterTaskIds = /* @__PURE__ */ new Set();
|
|
31650
|
+
shadowHeartbeatIntervalMs = 3e4;
|
|
31651
|
+
/** Suppresses buildDirector() auto-start while /shadow start is explicitly spawning one. */
|
|
31652
|
+
shadowAutoStartSuppressions = 0;
|
|
31653
|
+
shadowObservedWorkDepth = 0;
|
|
31654
|
+
shadowPassInFlight = false;
|
|
31655
|
+
shadowQueuedProblem = null;
|
|
31656
|
+
shadowActivityOffHandles = [];
|
|
31158
31657
|
/**
|
|
31159
31658
|
* Force the lazy build path to run *now* and return the live Director,
|
|
31160
31659
|
* or null when director mode is off. Used by the CLI to register the
|
|
@@ -31237,6 +31736,14 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
31237
31736
|
});
|
|
31238
31737
|
this.director.on("task.completed", ({ task, result }) => {
|
|
31239
31738
|
this.fleetManager?.removePendingTask(task.id);
|
|
31739
|
+
const isShadowTask = this.shadowTaskIds.has(task.id);
|
|
31740
|
+
if (isShadowTask) {
|
|
31741
|
+
this.shadowOutstandingTaskIds.delete(task.id);
|
|
31742
|
+
if (this.shadowStopAfterTaskIds.delete(task.id)) {
|
|
31743
|
+
void this.stopShadowAfterTask(result.subagentId);
|
|
31744
|
+
}
|
|
31745
|
+
return;
|
|
31746
|
+
}
|
|
31240
31747
|
this.emitLifecycleCompleted(task.id, result);
|
|
31241
31748
|
const monitor = this.opts.agentMonitor;
|
|
31242
31749
|
if (monitor) {
|
|
@@ -31305,17 +31812,33 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
31305
31812
|
task,
|
|
31306
31813
|
subagentId
|
|
31307
31814
|
}) => {
|
|
31815
|
+
if (this.shadowTaskIds.has(task.id)) return;
|
|
31308
31816
|
this.deps.events.emit("subagent.task_started", {
|
|
31309
31817
|
subagentId,
|
|
31310
31818
|
taskId: task.id,
|
|
31311
31819
|
description: task.description
|
|
31312
31820
|
});
|
|
31313
31821
|
};
|
|
31822
|
+
const coordinatorSubagentStoppedHandler = ({ subagentId }) => {
|
|
31823
|
+
this.clearShadowAgent(subagentId);
|
|
31824
|
+
};
|
|
31825
|
+
this.directorOffHandles.push(
|
|
31826
|
+
this.director.fleet.filter("subagent.removed", (e) => {
|
|
31827
|
+
const payload = e.payload;
|
|
31828
|
+
this.clearShadowAgent(payload.subagentId ?? e.subagentId);
|
|
31829
|
+
})
|
|
31830
|
+
);
|
|
31314
31831
|
const coordinator = this.getCoordinator();
|
|
31315
31832
|
coordinator.on("task.assigned", coordinatorTaskAssignedHandler);
|
|
31316
|
-
|
|
31833
|
+
coordinator.on("subagent.stopped", coordinatorSubagentStoppedHandler);
|
|
31834
|
+
this.coordinatorOffHandle = () => {
|
|
31835
|
+
coordinator.off("task.assigned", coordinatorTaskAssignedHandler);
|
|
31836
|
+
coordinator.off("subagent.stopped", coordinatorSubagentStoppedHandler);
|
|
31837
|
+
};
|
|
31317
31838
|
this.fleetEmitTool = makeFleetEmitTool(this.director);
|
|
31318
|
-
this.
|
|
31839
|
+
this.directorToolsByName = new Map(
|
|
31840
|
+
this.director.tools(FLEET_ROSTER).map((tool) => [tool.name, tool])
|
|
31841
|
+
);
|
|
31319
31842
|
const adaptiveConfig = this.deps.configStore.get().adaptiveConcurrency;
|
|
31320
31843
|
if (adaptiveConfig?.enabled) {
|
|
31321
31844
|
this.adaptiveConcurrencyController = new AdaptiveConcurrencyController(
|
|
@@ -31329,55 +31852,135 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
31329
31852
|
this.getCoordinator().setRunner(runner);
|
|
31330
31853
|
this.directorRunnerSet = true;
|
|
31331
31854
|
}
|
|
31332
|
-
|
|
31855
|
+
this.armShadowAgentIfNeeded();
|
|
31333
31856
|
}
|
|
31334
31857
|
/**
|
|
31335
|
-
*
|
|
31336
|
-
*
|
|
31858
|
+
* Arm host-owned Shadow observation. Healthy work windows stay fully
|
|
31859
|
+
* deterministic; a one-shot LLM Shadow pass runs only after problematic work
|
|
31860
|
+
* finishes, or when the user explicitly invokes /shadow start.
|
|
31337
31861
|
*/
|
|
31338
|
-
|
|
31339
|
-
|
|
31862
|
+
armShadowAgentIfNeeded() {
|
|
31863
|
+
if (this.shadowActivityOffHandles.length > 0) return;
|
|
31864
|
+
this.shadowActivityOffHandles.push(
|
|
31865
|
+
this.deps.events.on("agent.run.started", () => this.noteShadowWorkStarted()),
|
|
31866
|
+
this.deps.events.on("agent.run.completed", (e) => {
|
|
31867
|
+
const problem = e.status === "failed" || e.status === "max_iterations" ? `leader run ended with ${e.status}` : void 0;
|
|
31868
|
+
this.noteShadowWorkCompleted(problem);
|
|
31869
|
+
}),
|
|
31870
|
+
this.deps.events.on("subagent.task_started", () => this.noteShadowWorkStarted()),
|
|
31871
|
+
this.deps.events.on("subagent.task_completed", (e) => {
|
|
31872
|
+
const problem = e.status === "failed" || e.status === "timeout" ? `subagent ${e.subagentId} task ${e.taskId} ended with ${e.status}${e.error?.message ? `: ${e.error.message}` : ""}` : void 0;
|
|
31873
|
+
this.noteShadowWorkCompleted(problem);
|
|
31874
|
+
})
|
|
31875
|
+
);
|
|
31876
|
+
}
|
|
31877
|
+
recordShadowAgent(subagentId, taskId, intervalMs = this.shadowHeartbeatIntervalMs) {
|
|
31878
|
+
this.shadowAgentId = subagentId;
|
|
31879
|
+
this.shadowTaskId = taskId;
|
|
31880
|
+
this.shadowHeartbeatIntervalMs = intervalMs;
|
|
31881
|
+
this.markShadowTask(taskId);
|
|
31882
|
+
this.opts.onShadowAgentStarted?.(subagentId);
|
|
31883
|
+
}
|
|
31884
|
+
clearShadowAgent(subagentId) {
|
|
31885
|
+
if (subagentId && this.shadowAgentId !== subagentId) return;
|
|
31886
|
+
const stoppedId = this.shadowAgentId;
|
|
31887
|
+
this.shadowAgentId = null;
|
|
31888
|
+
this.shadowTaskId = null;
|
|
31889
|
+
this.shadowOutstandingTaskIds.clear();
|
|
31890
|
+
this.shadowStopAfterTaskIds.clear();
|
|
31891
|
+
if (stoppedId) this.opts.onShadowAgentStopped?.(stoppedId);
|
|
31892
|
+
}
|
|
31893
|
+
markShadowTask(taskId) {
|
|
31894
|
+
this.shadowTaskIds.add(taskId);
|
|
31895
|
+
this.shadowOutstandingTaskIds.add(taskId);
|
|
31896
|
+
}
|
|
31897
|
+
isActiveSubagent(subagentId) {
|
|
31898
|
+
if (!this.director) return false;
|
|
31899
|
+
const status = this.getCoordinator().getStatus().subagents.find((a) => a.id === subagentId)?.status;
|
|
31900
|
+
return status === "running" || status === "idle";
|
|
31901
|
+
}
|
|
31902
|
+
noteShadowWorkStarted() {
|
|
31903
|
+
if (this.shadowAutoStartSuppressions > 0) return;
|
|
31904
|
+
this.shadowObservedWorkDepth++;
|
|
31905
|
+
}
|
|
31906
|
+
noteShadowWorkCompleted(problem) {
|
|
31907
|
+
if (problem) {
|
|
31908
|
+
this.shadowQueuedProblem = this.shadowQueuedProblem ? `${this.shadowQueuedProblem}; ${problem}` : problem;
|
|
31909
|
+
}
|
|
31910
|
+
if (this.shadowObservedWorkDepth > 0) {
|
|
31911
|
+
this.shadowObservedWorkDepth--;
|
|
31912
|
+
}
|
|
31913
|
+
if (this.shadowObservedWorkDepth === 0 && this.shadowQueuedProblem) {
|
|
31914
|
+
const queued = this.shadowQueuedProblem;
|
|
31915
|
+
this.shadowQueuedProblem = null;
|
|
31916
|
+
this.requestShadowPass(queued);
|
|
31917
|
+
}
|
|
31918
|
+
}
|
|
31919
|
+
requestShadowPass(reason) {
|
|
31920
|
+
if (!this.director) return;
|
|
31921
|
+
if (this.shadowObservedWorkDepth > 0) {
|
|
31922
|
+
this.shadowQueuedProblem = this.shadowQueuedProblem ? `${this.shadowQueuedProblem}; ${reason}` : reason;
|
|
31923
|
+
return;
|
|
31924
|
+
}
|
|
31925
|
+
if (this.shadowPassInFlight || this.shadowAgentId && this.isActiveSubagent(this.shadowAgentId)) {
|
|
31926
|
+
this.shadowQueuedProblem = this.shadowQueuedProblem ? `${this.shadowQueuedProblem}; ${reason}` : reason;
|
|
31927
|
+
return;
|
|
31928
|
+
}
|
|
31929
|
+
this.shadowPassInFlight = true;
|
|
31930
|
+
queueMicrotask(() => {
|
|
31931
|
+
void this.runShadowPass(reason);
|
|
31932
|
+
});
|
|
31933
|
+
}
|
|
31934
|
+
async runShadowPass(reason) {
|
|
31340
31935
|
try {
|
|
31341
|
-
|
|
31342
|
-
|
|
31343
|
-
|
|
31344
|
-
|
|
31345
|
-
|
|
31346
|
-
|
|
31347
|
-
|
|
31348
|
-
|
|
31349
|
-
|
|
31350
|
-
|
|
31351
|
-
|
|
31352
|
-
|
|
31353
|
-
|
|
31354
|
-
|
|
31355
|
-
|
|
31356
|
-
|
|
31357
|
-
|
|
31358
|
-
|
|
31359
|
-
|
|
31360
|
-
|
|
31361
|
-
|
|
31362
|
-
|
|
31363
|
-
|
|
31364
|
-
|
|
31365
|
-
|
|
31366
|
-
|
|
31367
|
-
|
|
31368
|
-
|
|
31369
|
-
|
|
31370
|
-
|
|
31371
|
-
|
|
31372
|
-
|
|
31373
|
-
|
|
31374
|
-
|
|
31375
|
-
|
|
31376
|
-
|
|
31377
|
-
|
|
31378
|
-
|
|
31379
|
-
|
|
31380
|
-
|
|
31936
|
+
if (!this.director) return;
|
|
31937
|
+
if (this.shadowObservedWorkDepth > 0) {
|
|
31938
|
+
this.shadowQueuedProblem = this.shadowQueuedProblem ? `${this.shadowQueuedProblem}; ${reason}` : reason;
|
|
31939
|
+
return;
|
|
31940
|
+
}
|
|
31941
|
+
const liveConfig = this.deps.configStore.get();
|
|
31942
|
+
await this._spawnAndAssign(
|
|
31943
|
+
{
|
|
31944
|
+
name: "shadow",
|
|
31945
|
+
role: "shadow-agent",
|
|
31946
|
+
provider: liveConfig.provider,
|
|
31947
|
+
model: liveConfig.model,
|
|
31948
|
+
tools: [
|
|
31949
|
+
"fleet_status",
|
|
31950
|
+
"fleet_health",
|
|
31951
|
+
"fleet_usage",
|
|
31952
|
+
"mailbox",
|
|
31953
|
+
"mail_inbox",
|
|
31954
|
+
"mail_send",
|
|
31955
|
+
"terminate_subagent"
|
|
31956
|
+
]
|
|
31957
|
+
},
|
|
31958
|
+
buildShadowAgentTaskDescription(reason),
|
|
31959
|
+
{
|
|
31960
|
+
internalTask: true,
|
|
31961
|
+
stopShadowAfterTask: true,
|
|
31962
|
+
shadowIntervalMs: this.shadowHeartbeatIntervalMs
|
|
31963
|
+
}
|
|
31964
|
+
);
|
|
31965
|
+
} finally {
|
|
31966
|
+
this.shadowPassInFlight = false;
|
|
31967
|
+
if (this.shadowObservedWorkDepth === 0 && this.shadowQueuedProblem) {
|
|
31968
|
+
const queued = this.shadowQueuedProblem;
|
|
31969
|
+
this.shadowQueuedProblem = null;
|
|
31970
|
+
this.requestShadowPass(queued);
|
|
31971
|
+
}
|
|
31972
|
+
}
|
|
31973
|
+
}
|
|
31974
|
+
async stopShadowAfterTask(subagentId) {
|
|
31975
|
+
try {
|
|
31976
|
+
await this.getCoordinator().stop(subagentId);
|
|
31977
|
+
} finally {
|
|
31978
|
+
this.clearShadowAgent(subagentId);
|
|
31979
|
+
if (this.shadowObservedWorkDepth === 0 && this.shadowQueuedProblem) {
|
|
31980
|
+
const queued = this.shadowQueuedProblem;
|
|
31981
|
+
this.shadowQueuedProblem = null;
|
|
31982
|
+
this.requestShadowPass(queued);
|
|
31983
|
+
}
|
|
31381
31984
|
}
|
|
31382
31985
|
}
|
|
31383
31986
|
/**
|
|
@@ -31466,20 +32069,6 @@ Start your heartbeat loop now.`,
|
|
|
31466
32069
|
};
|
|
31467
32070
|
}
|
|
31468
32071
|
const tools = subCfg.tools ? [...subCfg.tools] : void 0;
|
|
31469
|
-
let injectedFleetEmit;
|
|
31470
|
-
let injectedFleetStatus;
|
|
31471
|
-
if (tools) {
|
|
31472
|
-
const emitIdx = tools.indexOf("fleet_emit");
|
|
31473
|
-
if (emitIdx >= 0 && this.fleetEmitTool) {
|
|
31474
|
-
tools.splice(emitIdx, 1);
|
|
31475
|
-
injectedFleetEmit = this.fleetEmitTool;
|
|
31476
|
-
}
|
|
31477
|
-
const statusIdx = tools.indexOf("fleet_status");
|
|
31478
|
-
if (statusIdx >= 0 && this.fleetStatusTool) {
|
|
31479
|
-
tools.splice(statusIdx, 1);
|
|
31480
|
-
injectedFleetStatus = this.fleetStatusTool;
|
|
31481
|
-
}
|
|
31482
|
-
}
|
|
31483
32072
|
const ctx = new Context({
|
|
31484
32073
|
systemPrompt: baseSystem,
|
|
31485
32074
|
provider,
|
|
@@ -31500,8 +32089,6 @@ Start your heartbeat loop now.`,
|
|
|
31500
32089
|
});
|
|
31501
32090
|
if (subCfg.role) ctx.meta["agentRole"] = subCfg.role;
|
|
31502
32091
|
const baseRegistry = this.subagentToolRegistry(tools);
|
|
31503
|
-
if (injectedFleetEmit) baseRegistry.register(injectedFleetEmit);
|
|
31504
|
-
if (injectedFleetStatus) baseRegistry.register(injectedFleetStatus);
|
|
31505
32092
|
const subAllowedCaps = this.resolveSubagentCapabilities(subCfg);
|
|
31506
32093
|
const toolExecutor = new ToolExecutor(baseRegistry, {
|
|
31507
32094
|
permissionPolicy: new AutoApprovePermissionPolicy(subAllowedCaps),
|
|
@@ -31529,7 +32116,13 @@ Start your heartbeat loop now.`,
|
|
|
31529
32116
|
});
|
|
31530
32117
|
agent.extensions.register(
|
|
31531
32118
|
createFallbackModelExtension({
|
|
31532
|
-
|
|
32119
|
+
// A per-task `fallbackModels` (set from the SDD board) overrides the
|
|
32120
|
+
// leader's chain for this subagent; otherwise it inherits the config's
|
|
32121
|
+
// explicit list or smart default. Mirrors the runtime light factory.
|
|
32122
|
+
getConfig: () => {
|
|
32123
|
+
const live = this.deps.configStore.get();
|
|
32124
|
+
return subCfg.fallbackModels?.length ? { ...live, fallbackModels: subCfg.fallbackModels } : live;
|
|
32125
|
+
},
|
|
31533
32126
|
buildProvider: (id) => this.buildSubagentProvider(config, id, effModel),
|
|
31534
32127
|
events
|
|
31535
32128
|
})
|
|
@@ -31672,7 +32265,15 @@ Start your heartbeat loop now.`,
|
|
|
31672
32265
|
const all = this.deps.toolRegistry.list();
|
|
31673
32266
|
if (!allow || allow.length === 0) return all;
|
|
31674
32267
|
const allowSet = new Set(allow);
|
|
31675
|
-
|
|
32268
|
+
const result = /* @__PURE__ */ new Map();
|
|
32269
|
+
for (const tool of all) {
|
|
32270
|
+
if (allowSet.has(tool.name)) result.set(tool.name, tool);
|
|
32271
|
+
}
|
|
32272
|
+
for (const name of allowSet) {
|
|
32273
|
+
const directorTool = this.directorToolsByName.get(name);
|
|
32274
|
+
if (directorTool && !result.has(name)) result.set(name, directorTool);
|
|
32275
|
+
}
|
|
32276
|
+
return Array.from(result.values());
|
|
31676
32277
|
}
|
|
31677
32278
|
/**
|
|
31678
32279
|
* Resolve the capability allowlist for a subagent's auto-approve policy.
|
|
@@ -31720,17 +32321,36 @@ Start your heartbeat loop now.`,
|
|
|
31720
32321
|
* the full tool registry.
|
|
31721
32322
|
*/
|
|
31722
32323
|
async spawn(description, opts) {
|
|
31723
|
-
|
|
32324
|
+
const isShadowSpawn = opts?.name === "shadow";
|
|
32325
|
+
if (isShadowSpawn) this.shadowAutoStartSuppressions++;
|
|
32326
|
+
try {
|
|
32327
|
+
await this.buildDirector();
|
|
32328
|
+
} finally {
|
|
32329
|
+
if (isShadowSpawn) this.shadowAutoStartSuppressions--;
|
|
32330
|
+
}
|
|
32331
|
+
if (isShadowSpawn && this.shadowAgentId && this.isActiveSubagent(this.shadowAgentId)) {
|
|
32332
|
+
return { subagentId: this.shadowAgentId, taskId: this.shadowTaskId ?? "shadow-active" };
|
|
32333
|
+
}
|
|
31724
32334
|
const subagentConfig = {
|
|
31725
32335
|
name: opts?.name ?? "adhoc",
|
|
31726
|
-
role: "general",
|
|
32336
|
+
role: isShadowSpawn ? "shadow-agent" : "general",
|
|
31727
32337
|
provider: opts?.provider,
|
|
31728
32338
|
model: opts?.model,
|
|
31729
32339
|
tools: opts?.tools,
|
|
31730
32340
|
allowedCapabilities: opts?.allowedCapabilities
|
|
31731
32341
|
};
|
|
31732
|
-
const { subagentId, taskId } = await this._spawnAndAssign(
|
|
31733
|
-
|
|
32342
|
+
const { subagentId, taskId } = await this._spawnAndAssign(
|
|
32343
|
+
subagentConfig,
|
|
32344
|
+
description,
|
|
32345
|
+
{
|
|
32346
|
+
internalTask: isShadowSpawn,
|
|
32347
|
+
stopShadowAfterTask: isShadowSpawn,
|
|
32348
|
+
shadowIntervalMs: opts?.shadowIntervalMs
|
|
32349
|
+
}
|
|
32350
|
+
);
|
|
32351
|
+
if (!isShadowSpawn) {
|
|
32352
|
+
this.fleetManager?.addPendingTask(taskId, subagentId, description);
|
|
32353
|
+
}
|
|
31734
32354
|
return { subagentId, taskId };
|
|
31735
32355
|
}
|
|
31736
32356
|
/**
|
|
@@ -31763,11 +32383,32 @@ Start your heartbeat loop now.`,
|
|
|
31763
32383
|
* Returns `{ subagentId, taskId }`. Caller holds `pending` tracking
|
|
31764
32384
|
* and event emission — the helper only talks to the coordinator.
|
|
31765
32385
|
*/
|
|
31766
|
-
async _spawnAndAssign(subagentConfig, description = "") {
|
|
32386
|
+
async _spawnAndAssign(subagentConfig, description = "", opts) {
|
|
31767
32387
|
const taskId = randomUUID();
|
|
31768
32388
|
if (!this.director) throw new Error("Director is not initialized");
|
|
31769
32389
|
const subagentId = await this.director.spawn(subagentConfig);
|
|
31770
|
-
|
|
32390
|
+
const task = { id: taskId, description, subagentId };
|
|
32391
|
+
if (opts?.internalTask) {
|
|
32392
|
+
this.markShadowTask(taskId);
|
|
32393
|
+
if (opts.stopShadowAfterTask) this.shadowStopAfterTaskIds.add(taskId);
|
|
32394
|
+
if (subagentConfig.name === "shadow" || subagentConfig.role === "shadow-agent") {
|
|
32395
|
+
this.recordShadowAgent(subagentId, taskId, opts.shadowIntervalMs);
|
|
32396
|
+
}
|
|
32397
|
+
try {
|
|
32398
|
+
await this.director.assignInternal(task);
|
|
32399
|
+
} catch (err) {
|
|
32400
|
+
this.shadowTaskIds.delete(taskId);
|
|
32401
|
+
this.shadowOutstandingTaskIds.delete(taskId);
|
|
32402
|
+
this.shadowStopAfterTaskIds.delete(taskId);
|
|
32403
|
+
if (subagentConfig.name === "shadow" || subagentConfig.role === "shadow-agent") {
|
|
32404
|
+
this.clearShadowAgent(subagentId);
|
|
32405
|
+
await this.director.remove(subagentId).catch(() => void 0);
|
|
32406
|
+
}
|
|
32407
|
+
throw err;
|
|
32408
|
+
}
|
|
32409
|
+
} else {
|
|
32410
|
+
await this.director.assign(task);
|
|
32411
|
+
}
|
|
31771
32412
|
return { subagentId, taskId };
|
|
31772
32413
|
}
|
|
31773
32414
|
/**
|
|
@@ -31804,7 +32445,7 @@ Start your heartbeat loop now.`,
|
|
|
31804
32445
|
}
|
|
31805
32446
|
const fleetStatus = this.fleetManager?.getFleetStatus() ?? { pending: []};
|
|
31806
32447
|
const pending = fleetStatus.pending.filter((p) => activeSubagentIds.has(p.subagentId));
|
|
31807
|
-
const completed = this.director ? this.director.completedResults() : [];
|
|
32448
|
+
const completed = this.director ? this.director.completedResults().filter((r) => !this.shadowTaskIds.has(r.taskId)) : [];
|
|
31808
32449
|
const completedCount = completed.length;
|
|
31809
32450
|
const liveCount = live.filter((s) => s.status === "running" || s.status === "idle").length;
|
|
31810
32451
|
const summary = !this.director ? "No subagents have been spawned." : liveCount > 0 ? `${pending.length} pending, ${liveCount} active, ${completedCount} completed.` : `${pending.length} pending, ${completedCount} completed.`;
|
|
@@ -31821,7 +32462,7 @@ Start your heartbeat loop now.`,
|
|
|
31821
32462
|
* the table renders the most interesting subagent at the top.
|
|
31822
32463
|
*/
|
|
31823
32464
|
usage() {
|
|
31824
|
-
const completed = this.director ? this.director.completedResults() : [];
|
|
32465
|
+
const completed = this.director ? this.director.completedResults().filter((r) => !this.shadowTaskIds.has(r.taskId)) : [];
|
|
31825
32466
|
const bySubagent = /* @__PURE__ */ new Map();
|
|
31826
32467
|
for (const r of completed) {
|
|
31827
32468
|
const cur = bySubagent.get(r.subagentId) ?? {
|
|
@@ -31925,9 +32566,11 @@ Start your heartbeat loop now.`,
|
|
|
31925
32566
|
async kill(subagentId) {
|
|
31926
32567
|
if (!this.director) return false;
|
|
31927
32568
|
await this.getCoordinator().stop(subagentId);
|
|
32569
|
+
if (this.shadowAgentId === subagentId) this.clearShadowAgent(subagentId);
|
|
31928
32570
|
return true;
|
|
31929
32571
|
}
|
|
31930
32572
|
async stopAll() {
|
|
32573
|
+
this.clearShadowAgent();
|
|
31931
32574
|
if (this.director) {
|
|
31932
32575
|
await this.getCoordinator().stopAll();
|
|
31933
32576
|
}
|
|
@@ -31971,6 +32614,11 @@ Start your heartbeat loop now.`,
|
|
|
31971
32614
|
* Safe to call multiple times — subsequent calls are no-ops.
|
|
31972
32615
|
*/
|
|
31973
32616
|
async dispose() {
|
|
32617
|
+
this.clearShadowAgent();
|
|
32618
|
+
for (const off of this.shadowActivityOffHandles) {
|
|
32619
|
+
off();
|
|
32620
|
+
}
|
|
32621
|
+
this.shadowActivityOffHandles.length = 0;
|
|
31974
32622
|
for (const off of this.directorOffHandles) {
|
|
31975
32623
|
off();
|
|
31976
32624
|
}
|
|
@@ -33703,6 +34351,22 @@ async function main(argv) {
|
|
|
33703
34351
|
}
|
|
33704
34352
|
});
|
|
33705
34353
|
brainMonitor.start();
|
|
34354
|
+
let shadowDefaults = {};
|
|
34355
|
+
const shadowController = {
|
|
34356
|
+
activeId: null,
|
|
34357
|
+
register(id) {
|
|
34358
|
+
this.activeId = id;
|
|
34359
|
+
},
|
|
34360
|
+
clear() {
|
|
34361
|
+
this.activeId = null;
|
|
34362
|
+
},
|
|
34363
|
+
getDefaults() {
|
|
34364
|
+
return { ...shadowDefaults };
|
|
34365
|
+
},
|
|
34366
|
+
setDefaults(defaults) {
|
|
34367
|
+
shadowDefaults = { ...shadowDefaults, ...defaults };
|
|
34368
|
+
}
|
|
34369
|
+
};
|
|
33706
34370
|
const multiAgentHost = new MultiAgentHost(
|
|
33707
34371
|
{
|
|
33708
34372
|
container,
|
|
@@ -33731,7 +34395,11 @@ async function main(argv) {
|
|
|
33731
34395
|
getLeaderMaxContext: () => effectiveMaxContext,
|
|
33732
34396
|
brain,
|
|
33733
34397
|
agentMonitor,
|
|
33734
|
-
traceId: sessResult.traceId
|
|
34398
|
+
traceId: sessResult.traceId,
|
|
34399
|
+
onShadowAgentStarted: (subagentId) => shadowController.register(subagentId),
|
|
34400
|
+
onShadowAgentStopped: (subagentId) => {
|
|
34401
|
+
if (shadowController.activeId === subagentId) shadowController.clear();
|
|
34402
|
+
}
|
|
33735
34403
|
}
|
|
33736
34404
|
);
|
|
33737
34405
|
toolRegistry.register(
|
|
@@ -33916,15 +34584,7 @@ async function main(argv) {
|
|
|
33916
34584
|
`)
|
|
33917
34585
|
});
|
|
33918
34586
|
const coordinatorController = {};
|
|
33919
|
-
const
|
|
33920
|
-
activeId: null,
|
|
33921
|
-
register(id) {
|
|
33922
|
-
this.activeId = id;
|
|
33923
|
-
},
|
|
33924
|
-
clear() {
|
|
33925
|
-
this.activeId = null;
|
|
33926
|
-
}
|
|
33927
|
-
};
|
|
34587
|
+
const sddRunRegistry = new SddRunRegistry();
|
|
33928
34588
|
const slashCmds = buildBuiltinSlashCommands({
|
|
33929
34589
|
registry: slashRegistry,
|
|
33930
34590
|
toolRegistry,
|
|
@@ -33984,9 +34644,6 @@ async function main(argv) {
|
|
|
33984
34644
|
},
|
|
33985
34645
|
onSpawn: async (description, spawnOpts) => {
|
|
33986
34646
|
const { subagentId, taskId } = await multiAgentHost.spawn(description, spawnOpts);
|
|
33987
|
-
if (shadowController && spawnOpts?.name === "shadow") {
|
|
33988
|
-
shadowController.register(subagentId);
|
|
33989
|
-
}
|
|
33990
34647
|
const tags = [];
|
|
33991
34648
|
if (spawnOpts?.provider) tags.push(spawnOpts.provider);
|
|
33992
34649
|
if (spawnOpts?.model) tags.push(spawnOpts.model);
|
|
@@ -34169,14 +34826,14 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
34169
34826
|
if (!director) return null;
|
|
34170
34827
|
return director.snapshot();
|
|
34171
34828
|
},
|
|
34172
|
-
onFleetKill: () => {
|
|
34829
|
+
onFleetKill: async () => {
|
|
34173
34830
|
if (!director) return 0;
|
|
34174
34831
|
const s = director.status();
|
|
34175
34832
|
let killed = 0;
|
|
34176
34833
|
for (const sa of s.subagents) {
|
|
34177
34834
|
if (sa.status === "running" || sa.status === "idle") {
|
|
34178
34835
|
try {
|
|
34179
|
-
director.remove(sa.id);
|
|
34836
|
+
await director.remove(sa.id);
|
|
34180
34837
|
killed++;
|
|
34181
34838
|
} catch {
|
|
34182
34839
|
}
|
|
@@ -34184,10 +34841,10 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
34184
34841
|
}
|
|
34185
34842
|
return killed;
|
|
34186
34843
|
},
|
|
34187
|
-
onFleetTerminate: (subagentId) => {
|
|
34844
|
+
onFleetTerminate: async (subagentId) => {
|
|
34188
34845
|
if (!director) return false;
|
|
34189
34846
|
try {
|
|
34190
|
-
director.terminate(subagentId);
|
|
34847
|
+
await director.terminate(subagentId);
|
|
34191
34848
|
return true;
|
|
34192
34849
|
} catch {
|
|
34193
34850
|
return false;
|
|
@@ -34604,7 +35261,6 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
34604
35261
|
context.model
|
|
34605
35262
|
),
|
|
34606
35263
|
onSddParallelRun: async (opts) => {
|
|
34607
|
-
const { SddParallelRun } = await import('@wrongstack/core');
|
|
34608
35264
|
const sdd = await Promise.resolve().then(() => (init_sdd(), sdd_exports));
|
|
34609
35265
|
const tracker2 = sdd.getTaskTracker();
|
|
34610
35266
|
const builder = sdd.getActiveBuilder();
|
|
@@ -34623,14 +35279,58 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
34623
35279
|
if (!graph) {
|
|
34624
35280
|
return "No task graph found for the current SDD session.";
|
|
34625
35281
|
}
|
|
34626
|
-
const
|
|
34627
|
-
|
|
35282
|
+
const core = await import('@wrongstack/core');
|
|
35283
|
+
let worktrees;
|
|
35284
|
+
if (process.env["WRONGSTACK_SDD_WORKTREES"] !== "0") {
|
|
35285
|
+
const { spawnSync } = await import('child_process');
|
|
35286
|
+
const inGit = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
35287
|
+
cwd: projectRoot,
|
|
35288
|
+
encoding: "utf8",
|
|
35289
|
+
windowsHide: true
|
|
35290
|
+
}).stdout?.trim() === "true";
|
|
35291
|
+
if (inGit) worktrees = new core.WorktreeManager({ projectRoot, events });
|
|
35292
|
+
}
|
|
35293
|
+
const boardStore = new core.SddBoardStore({ baseDir: wpaths.projectSddBoards });
|
|
35294
|
+
const verifyTask = core.makeCommandVerifier();
|
|
35295
|
+
const sddSupervisor = new core.SddSupervisor({
|
|
35296
|
+
brain,
|
|
35297
|
+
reassignModels: core.effectiveFallbackChain(config)
|
|
35298
|
+
});
|
|
35299
|
+
const sddSubagentFactory = multiAgentHost.makeSubagentFactory(config);
|
|
35300
|
+
const conflictMode = process.env["WRONGSTACK_SDD_CONFLICT_RESOLVER"];
|
|
35301
|
+
const conflictResolver = conflictMode === "prefer-incoming" ? core.makePreferSideConflictResolver("incoming") : conflictMode === "prefer-base" ? core.makePreferSideConflictResolver("base") : conflictMode === "llm" ? core.makeLlmConflictResolver({
|
|
35302
|
+
run: async (prompt) => {
|
|
35303
|
+
const r = await sddSubagentFactory({
|
|
35304
|
+
id: `sdd-conflict-${Date.now()}`,
|
|
35305
|
+
role: "executor",
|
|
35306
|
+
name: "Conflict Resolver",
|
|
35307
|
+
disabledTools: ["delegate"],
|
|
35308
|
+
// Only returns the resolved text; the core helper writes the
|
|
35309
|
+
// file. Keep it on the read-only capability floor.
|
|
35310
|
+
allowedCapabilities: ["fs.read", "net.outbound"]
|
|
35311
|
+
});
|
|
35312
|
+
try {
|
|
35313
|
+
const res = await r.agent.run([{ type: "text", text: prompt }]);
|
|
35314
|
+
return res.finalText ?? "";
|
|
35315
|
+
} finally {
|
|
35316
|
+
await r.dispose?.();
|
|
35317
|
+
}
|
|
35318
|
+
}
|
|
35319
|
+
}) : void 0;
|
|
35320
|
+
const handle = core.startSddRun({
|
|
34628
35321
|
tracker: tracker2,
|
|
34629
35322
|
graph,
|
|
34630
35323
|
agent,
|
|
34631
35324
|
projectRoot,
|
|
35325
|
+
events,
|
|
34632
35326
|
parallelSlots: opts?.parallelSlots,
|
|
34633
|
-
subagentFactory,
|
|
35327
|
+
subagentFactory: sddSubagentFactory,
|
|
35328
|
+
worktrees,
|
|
35329
|
+
boardStore,
|
|
35330
|
+
registry: sddRunRegistry,
|
|
35331
|
+
verifyTask,
|
|
35332
|
+
conflictResolver,
|
|
35333
|
+
superviseFailure: sddSupervisor.superviseFailure,
|
|
34634
35334
|
onProgress: (p) => {
|
|
34635
35335
|
renderer.write(
|
|
34636
35336
|
` \u2591 wave ${p.wave + 1} \xB7 ${p.completed}/${p.total} tasks \xB7 ${p.percent}% done
|
|
@@ -34638,27 +35338,70 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
34638
35338
|
);
|
|
34639
35339
|
}
|
|
34640
35340
|
});
|
|
34641
|
-
globalThis.__sddParallelRun = run;
|
|
34642
|
-
|
|
34643
|
-
|
|
34644
|
-
|
|
34645
|
-
|
|
34646
|
-
|
|
34647
|
-
|
|
34648
|
-
|
|
34649
|
-
|
|
34650
|
-
|
|
34651
|
-
|
|
35341
|
+
globalThis.__sddParallelRun = handle.run;
|
|
35342
|
+
try {
|
|
35343
|
+
const result = await handle.completion;
|
|
35344
|
+
const lines = [
|
|
35345
|
+
`SDD parallel run complete:`,
|
|
35346
|
+
` ${result.totalWaves} waves \xB7 ${result.totalCompleted} done \xB7 ${result.totalFailed} failed`,
|
|
35347
|
+
` ${(result.totalDurationMs / 1e3).toFixed(1)}s total`
|
|
35348
|
+
];
|
|
35349
|
+
if (result.deadlocked) lines.push(color.red(" \u26A0 deadlock \u2014 tasks blocked by failed tasks."));
|
|
35350
|
+
if (result.stopRequested) lines.push(color.yellow(" \u26A1 stopped by user."));
|
|
35351
|
+
return lines.join("\n");
|
|
35352
|
+
} finally {
|
|
35353
|
+
globalThis.__sddParallelRun = void 0;
|
|
35354
|
+
}
|
|
34652
35355
|
},
|
|
34653
35356
|
onSddParallelStop: () => {
|
|
34654
35357
|
const run = globalThis.__sddParallelRun;
|
|
34655
35358
|
run?.stop();
|
|
34656
35359
|
},
|
|
35360
|
+
onSddRetryAllFailed: () => sddRunRegistry.getActive()?.retryAllFailed() ?? 0,
|
|
35361
|
+
onSddSplitTask: (taskId, subtasks) => {
|
|
35362
|
+
const active = sddRunRegistry.getActive();
|
|
35363
|
+
if (!active) return null;
|
|
35364
|
+
const snap = active.snapshot();
|
|
35365
|
+
const match = snap.tasks.find((t) => t.id === taskId || t.shortId === taskId);
|
|
35366
|
+
const ids = active.splitTask(match?.id ?? taskId, subtasks);
|
|
35367
|
+
return ids.length ? ids : null;
|
|
35368
|
+
},
|
|
35369
|
+
// ── SDD lifecycle: clean worktrees · rollback commits · destroy project ──
|
|
35370
|
+
onSddCleanWorktrees: async () => {
|
|
35371
|
+
const active = sddRunRegistry.getActive();
|
|
35372
|
+
if (active) return active.cleanupWorktrees();
|
|
35373
|
+
const core = await import('@wrongstack/core');
|
|
35374
|
+
const { removed } = await core.cleanupSddWorktrees(projectRoot);
|
|
35375
|
+
return removed;
|
|
35376
|
+
},
|
|
35377
|
+
onSddRollback: async () => {
|
|
35378
|
+
const active = sddRunRegistry.getActive();
|
|
35379
|
+
if (active) return active.rollback();
|
|
35380
|
+
const core = await import('@wrongstack/core');
|
|
35381
|
+
return core.rollbackSddRunFromDisk({ projectRoot, boardsDir: wpaths.projectSddBoards });
|
|
35382
|
+
},
|
|
35383
|
+
onSddDestroy: async () => {
|
|
35384
|
+
sddRunRegistry.getActive()?.stop();
|
|
35385
|
+
const core = await import('@wrongstack/core');
|
|
35386
|
+
return core.destroySddProject({
|
|
35387
|
+
projectRoot,
|
|
35388
|
+
paths: {
|
|
35389
|
+
projectSpecs: wpaths.projectSpecs,
|
|
35390
|
+
projectTaskGraphs: wpaths.projectTaskGraphs,
|
|
35391
|
+
projectSddSession: wpaths.projectSddSession,
|
|
35392
|
+
projectSddBoards: wpaths.projectSddBoards
|
|
35393
|
+
}
|
|
35394
|
+
});
|
|
35395
|
+
},
|
|
34657
35396
|
onAutoPhaseStart: autoPhaseHost.onAutoPhaseStart,
|
|
34658
35397
|
onAutoPhasePause: autoPhaseHost.onAutoPhasePause,
|
|
34659
35398
|
onAutoPhaseResume: autoPhaseHost.onAutoPhaseResume,
|
|
34660
35399
|
onAutoPhaseStop: autoPhaseHost.onAutoPhaseStop,
|
|
34661
35400
|
getAutoPhaseRunner: autoPhaseHost.getAutoPhaseRunner,
|
|
35401
|
+
onAutoPhaseMoveTask: autoPhaseHost.onAutoPhaseMoveTask,
|
|
35402
|
+
onAutoPhaseAssignTask: autoPhaseHost.onAutoPhaseAssignTask,
|
|
35403
|
+
onAutoPhaseAddTask: autoPhaseHost.onAutoPhaseAddTask,
|
|
35404
|
+
onAutoPhaseRetryTask: autoPhaseHost.onAutoPhaseRetryTask,
|
|
34662
35405
|
onWorktree: autoPhaseHost.onWorktree
|
|
34663
35406
|
});
|
|
34664
35407
|
for (const cmd of slashCmds) slashRegistry.register(cmd);
|
|
@@ -34696,6 +35439,9 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
34696
35439
|
tokenCounter,
|
|
34697
35440
|
config,
|
|
34698
35441
|
configStore,
|
|
35442
|
+
// Real director-backed per-task agent factory — threaded to the CLI-hosted
|
|
35443
|
+
// WebUI so its "New SDD Project" wizard runs the same multi-agent fleet.
|
|
35444
|
+
sddSubagentFactory: multiAgentHost.makeSubagentFactory(config),
|
|
34699
35445
|
// Project-scoped mailbox — the AutonomousCoordinator in execution.ts
|
|
34700
35446
|
// subscribes to it so goals/tasks/knowledge are shared with other
|
|
34701
35447
|
// terminals working on the same project.
|
|
@@ -34847,6 +35593,10 @@ Reply YES to auto-proceed, NO to wait for human input.`
|
|
|
34847
35593
|
},
|
|
34848
35594
|
getEternalEngine: () => eternalEngine,
|
|
34849
35595
|
getParallelEngine: () => parallelEngine,
|
|
35596
|
+
// Active SDD parallel run (if any). Lets the REPL/TUI SIGINT handler stop a
|
|
35597
|
+
// running `/sdd parallel` mid-flight — without this the run is unreachable
|
|
35598
|
+
// from Ctrl+C (it has its own coordinator, not the autonomy engines).
|
|
35599
|
+
getSddRun: () => sddRunRegistry.getActive(),
|
|
34850
35600
|
subscribeEternalIteration: (fn) => {
|
|
34851
35601
|
eternalListeners.add(fn);
|
|
34852
35602
|
return () => eternalListeners.delete(fn);
|