actoviq-agent-sdk 0.4.11 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/src/computer/hadamardComputerUse.d.ts +15 -0
- package/dist/src/computer/hadamardComputerUse.d.ts.map +1 -1
- package/dist/src/computer/hadamardComputerUse.js +120 -4
- package/dist/src/computer/hadamardComputerUse.js.map +1 -1
- package/dist/src/config/agentDefinitionMigration.d.ts +125 -0
- package/dist/src/config/agentDefinitionMigration.d.ts.map +1 -0
- package/dist/src/config/agentDefinitionMigration.js +540 -0
- package/dist/src/config/agentDefinitionMigration.js.map +1 -0
- package/dist/src/config/agentProfiles.d.ts +23 -0
- package/dist/src/config/agentProfiles.d.ts.map +1 -1
- package/dist/src/config/agentProfiles.js +69 -13
- package/dist/src/config/agentProfiles.js.map +1 -1
- package/dist/src/config/hadamardHome.js +0 -1
- package/dist/src/config/hadamardHome.js.map +1 -1
- package/dist/src/config/modelConfigurationCatalog.d.ts +25 -0
- package/dist/src/config/modelConfigurationCatalog.d.ts.map +1 -0
- package/dist/src/config/modelConfigurationCatalog.js +50 -0
- package/dist/src/config/modelConfigurationCatalog.js.map +1 -0
- package/dist/src/config/projectSettings.d.ts +62 -0
- package/dist/src/config/projectSettings.d.ts.map +1 -0
- package/dist/src/config/projectSettings.js +242 -0
- package/dist/src/config/projectSettings.js.map +1 -0
- package/dist/src/config/resolveRuntimeConfig.d.ts.map +1 -1
- package/dist/src/config/resolveRuntimeConfig.js +97 -4
- package/dist/src/config/resolveRuntimeConfig.js.map +1 -1
- package/dist/src/goal/goalCommandService.d.ts +11 -0
- package/dist/src/goal/goalCommandService.d.ts.map +1 -0
- package/dist/src/goal/goalCommandService.js +138 -0
- package/dist/src/goal/goalCommandService.js.map +1 -0
- package/dist/src/goal/goalContinuation.d.ts +50 -0
- package/dist/src/goal/goalContinuation.d.ts.map +1 -0
- package/dist/src/goal/goalContinuation.js +131 -0
- package/dist/src/goal/goalContinuation.js.map +1 -0
- package/dist/src/goal/goalController.d.ts +42 -0
- package/dist/src/goal/goalController.d.ts.map +1 -0
- package/dist/src/goal/goalController.js +281 -0
- package/dist/src/goal/goalController.js.map +1 -0
- package/dist/src/goal/goalPrompt.d.ts +3 -0
- package/dist/src/goal/goalPrompt.d.ts.map +1 -1
- package/dist/src/goal/goalPrompt.js +28 -2
- package/dist/src/goal/goalPrompt.js.map +1 -1
- package/dist/src/goal/goalService.d.ts +68 -2
- package/dist/src/goal/goalService.d.ts.map +1 -1
- package/dist/src/goal/goalService.js +500 -8
- package/dist/src/goal/goalService.js.map +1 -1
- package/dist/src/goal/goalStore.d.ts.map +1 -1
- package/dist/src/goal/goalStore.js +172 -5
- package/dist/src/goal/goalStore.js.map +1 -1
- package/dist/src/goal/goalTools.d.ts +2 -1
- package/dist/src/goal/goalTools.d.ts.map +1 -1
- package/dist/src/goal/goalTools.js +70 -5
- package/dist/src/goal/goalTools.js.map +1 -1
- package/dist/src/goal/goalWorkCoordinator.d.ts +43 -0
- package/dist/src/goal/goalWorkCoordinator.d.ts.map +1 -0
- package/dist/src/goal/goalWorkCoordinator.js +72 -0
- package/dist/src/goal/goalWorkCoordinator.js.map +1 -0
- package/dist/src/goal/index.d.ts +10 -4
- package/dist/src/goal/index.d.ts.map +1 -1
- package/dist/src/goal/index.js +7 -1
- package/dist/src/goal/index.js.map +1 -1
- package/dist/src/goal/projectGoalApi.d.ts +82 -0
- package/dist/src/goal/projectGoalApi.d.ts.map +1 -0
- package/dist/src/goal/projectGoalApi.js +287 -0
- package/dist/src/goal/projectGoalApi.js.map +1 -0
- package/dist/src/goal/projectGoalStore.d.ts +131 -0
- package/dist/src/goal/projectGoalStore.d.ts.map +1 -0
- package/dist/src/goal/projectGoalStore.js +808 -0
- package/dist/src/goal/projectGoalStore.js.map +1 -0
- package/dist/src/goal/types.d.ts +127 -3
- package/dist/src/goal/types.d.ts.map +1 -1
- package/dist/src/goal/types.js +2 -2
- package/dist/src/goal/types.js.map +1 -1
- package/dist/src/gui/agentTargetPicker.d.ts +32 -0
- package/dist/src/gui/agentTargetPicker.d.ts.map +1 -0
- package/dist/src/gui/agentTargetPicker.js +99 -0
- package/dist/src/gui/agentTargetPicker.js.map +1 -0
- package/dist/src/gui/electronMain.js +10 -3
- package/dist/src/gui/electronMain.js.map +1 -1
- package/dist/src/gui/hadamardGui.d.ts +24 -0
- package/dist/src/gui/hadamardGui.d.ts.map +1 -1
- package/dist/src/gui/hadamardGui.js +6321 -1327
- package/dist/src/gui/hadamardGui.js.map +1 -1
- package/dist/src/gui/projectWorkbench.d.ts +13 -2
- package/dist/src/gui/projectWorkbench.d.ts.map +1 -1
- package/dist/src/gui/projectWorkbench.js +109 -67
- package/dist/src/gui/projectWorkbench.js.map +1 -1
- package/dist/src/gui/promptTemplates.d.ts +18 -0
- package/dist/src/gui/promptTemplates.d.ts.map +1 -0
- package/dist/src/gui/promptTemplates.js +178 -0
- package/dist/src/gui/promptTemplates.js.map +1 -0
- package/dist/src/gui/terminalManager.d.ts +14 -0
- package/dist/src/gui/terminalManager.d.ts.map +1 -1
- package/dist/src/gui/terminalManager.js +25 -5
- package/dist/src/gui/terminalManager.js.map +1 -1
- package/dist/src/gui/transcript/clientBundle.d.ts +6 -1
- package/dist/src/gui/transcript/clientBundle.d.ts.map +1 -1
- package/dist/src/gui/transcript/clientBundle.js +6 -1
- package/dist/src/gui/transcript/clientBundle.js.map +1 -1
- package/dist/src/help/productCapabilities.d.ts +17 -0
- package/dist/src/help/productCapabilities.d.ts.map +1 -0
- package/dist/src/help/productCapabilities.js +146 -0
- package/dist/src/help/productCapabilities.js.map +1 -0
- package/dist/src/index.d.ts +21 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +15 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/issues/issueExecution.d.ts.map +1 -1
- package/dist/src/issues/issueExecution.js +29 -9
- package/dist/src/issues/issueExecution.js.map +1 -1
- package/dist/src/manager/assistantGlobalTools.d.ts +44 -0
- package/dist/src/manager/assistantGlobalTools.d.ts.map +1 -1
- package/dist/src/manager/assistantGlobalTools.js +565 -34
- package/dist/src/manager/assistantGlobalTools.js.map +1 -1
- package/dist/src/manager/assistantProposals.d.ts +87 -0
- package/dist/src/manager/assistantProposals.d.ts.map +1 -0
- package/dist/src/manager/assistantProposals.js +207 -0
- package/dist/src/manager/assistantProposals.js.map +1 -0
- package/dist/src/manager/referenceIndex.d.ts +82 -0
- package/dist/src/manager/referenceIndex.d.ts.map +1 -0
- package/dist/src/manager/referenceIndex.js +167 -0
- package/dist/src/manager/referenceIndex.js.map +1 -0
- package/dist/src/manager/referenceOperations.d.ts +108 -0
- package/dist/src/manager/referenceOperations.d.ts.map +1 -0
- package/dist/src/manager/referenceOperations.js +821 -0
- package/dist/src/manager/referenceOperations.js.map +1 -0
- package/dist/src/manager/resolveTargetRef.d.ts +67 -0
- package/dist/src/manager/resolveTargetRef.d.ts.map +1 -0
- package/dist/src/manager/resolveTargetRef.js +81 -0
- package/dist/src/manager/resolveTargetRef.js.map +1 -0
- package/dist/src/memory/durableMemoryPipeline.d.ts +51 -0
- package/dist/src/memory/durableMemoryPipeline.d.ts.map +1 -0
- package/dist/src/memory/durableMemoryPipeline.js +266 -0
- package/dist/src/memory/durableMemoryPipeline.js.map +1 -0
- package/dist/src/memory/durableMemoryStore.d.ts +59 -0
- package/dist/src/memory/durableMemoryStore.d.ts.map +1 -0
- package/dist/src/memory/durableMemoryStore.js +194 -0
- package/dist/src/memory/durableMemoryStore.js.map +1 -0
- package/dist/src/memory/hadamardDream.d.ts +18 -0
- package/dist/src/memory/hadamardDream.d.ts.map +1 -1
- package/dist/src/memory/hadamardDream.js +119 -39
- package/dist/src/memory/hadamardDream.js.map +1 -1
- package/dist/src/memory/hadamardMemory.d.ts +20 -39
- package/dist/src/memory/hadamardMemory.d.ts.map +1 -1
- package/dist/src/memory/hadamardMemory.js +112 -450
- package/dist/src/memory/hadamardMemory.js.map +1 -1
- package/dist/src/memory/hadamardSessionMemoryState.d.ts +2 -5
- package/dist/src/memory/hadamardSessionMemoryState.d.ts.map +1 -1
- package/dist/src/memory/hadamardSessionMemoryState.js +5 -61
- package/dist/src/memory/hadamardSessionMemoryState.js.map +1 -1
- package/dist/src/memory/memoryCommandService.d.ts +25 -0
- package/dist/src/memory/memoryCommandService.d.ts.map +1 -0
- package/dist/src/memory/memoryCommandService.js +93 -0
- package/dist/src/memory/memoryCommandService.js.map +1 -0
- package/dist/src/memory/memoryProposalCommandService.d.ts.map +1 -1
- package/dist/src/memory/memoryProposalCommandService.js +2 -1
- package/dist/src/memory/memoryProposalCommandService.js.map +1 -1
- package/dist/src/memory/memoryProposalTools.d.ts +1 -0
- package/dist/src/memory/memoryProposalTools.d.ts.map +1 -1
- package/dist/src/memory/memoryProposalTools.js +16 -11
- package/dist/src/memory/memoryProposalTools.js.map +1 -1
- package/dist/src/parity/bridgeConfigs.d.ts +4 -0
- package/dist/src/parity/bridgeConfigs.d.ts.map +1 -1
- package/dist/src/parity/bridgeConfigs.js +30 -1
- package/dist/src/parity/bridgeConfigs.js.map +1 -1
- package/dist/src/router/modelRouter.d.ts +22 -2
- package/dist/src/router/modelRouter.d.ts.map +1 -1
- package/dist/src/router/modelRouter.js +76 -9
- package/dist/src/router/modelRouter.js.map +1 -1
- package/dist/src/runtime/agentClient.d.ts +24 -4
- package/dist/src/runtime/agentClient.d.ts.map +1 -1
- package/dist/src/runtime/agentClient.js +581 -262
- package/dist/src/runtime/agentClient.js.map +1 -1
- package/dist/src/runtime/agentSession.d.ts +8 -3
- package/dist/src/runtime/agentSession.d.ts.map +1 -1
- package/dist/src/runtime/agentSession.js +10 -3
- package/dist/src/runtime/agentSession.js.map +1 -1
- package/dist/src/runtime/agentTemplates.d.ts +28 -0
- package/dist/src/runtime/agentTemplates.d.ts.map +1 -0
- package/dist/src/runtime/agentTemplates.js +73 -0
- package/dist/src/runtime/agentTemplates.js.map +1 -0
- package/dist/src/runtime/conversationEngine.d.ts +4 -0
- package/dist/src/runtime/conversationEngine.d.ts.map +1 -1
- package/dist/src/runtime/conversationEngine.js +52 -3
- package/dist/src/runtime/conversationEngine.js.map +1 -1
- package/dist/src/runtime/defaultHadamardAgents.d.ts.map +1 -1
- package/dist/src/runtime/defaultHadamardAgents.js +9 -47
- package/dist/src/runtime/defaultHadamardAgents.js.map +1 -1
- package/dist/src/runtime/effectiveAgentRunOptions.d.ts +49 -0
- package/dist/src/runtime/effectiveAgentRunOptions.d.ts.map +1 -0
- package/dist/src/runtime/effectiveAgentRunOptions.js +50 -0
- package/dist/src/runtime/effectiveAgentRunOptions.js.map +1 -0
- package/dist/src/runtime/externalAgentRunner.d.ts +25 -0
- package/dist/src/runtime/externalAgentRunner.d.ts.map +1 -0
- package/dist/src/runtime/externalAgentRunner.js +57 -0
- package/dist/src/runtime/externalAgentRunner.js.map +1 -0
- package/dist/src/runtime/hadamardAgentDefinitions.d.ts +15 -0
- package/dist/src/runtime/hadamardAgentDefinitions.d.ts.map +1 -1
- package/dist/src/runtime/hadamardAgentDefinitions.js +42 -2
- package/dist/src/runtime/hadamardAgentDefinitions.js.map +1 -1
- package/dist/src/runtime/hadamardAgents.d.ts +6 -0
- package/dist/src/runtime/hadamardAgents.d.ts.map +1 -1
- package/dist/src/runtime/hadamardAgents.js +52 -1
- package/dist/src/runtime/hadamardAgents.js.map +1 -1
- package/dist/src/runtime/hadamardCompact.d.ts +12 -1
- package/dist/src/runtime/hadamardCompact.d.ts.map +1 -1
- package/dist/src/runtime/hadamardCompact.js +91 -35
- package/dist/src/runtime/hadamardCompact.js.map +1 -1
- package/dist/src/runtime/hadamardPermissions.d.ts +1 -0
- package/dist/src/runtime/hadamardPermissions.d.ts.map +1 -1
- package/dist/src/runtime/hadamardPermissions.js +6 -0
- package/dist/src/runtime/hadamardPermissions.js.map +1 -1
- package/dist/src/runtime/hadamardSlashCommands.d.ts +7 -1
- package/dist/src/runtime/hadamardSlashCommands.d.ts.map +1 -1
- package/dist/src/runtime/hadamardSlashCommands.js +47 -5
- package/dist/src/runtime/hadamardSlashCommands.js.map +1 -1
- package/dist/src/runtime/tools.d.ts.map +1 -1
- package/dist/src/runtime/tools.js +2 -0
- package/dist/src/runtime/tools.js.map +1 -1
- package/dist/src/team/executorTarget.d.ts +26 -0
- package/dist/src/team/executorTarget.d.ts.map +1 -0
- package/dist/src/team/executorTarget.js +15 -0
- package/dist/src/team/executorTarget.js.map +1 -0
- package/dist/src/team/modelTeam.d.ts +5 -1
- package/dist/src/team/modelTeam.d.ts.map +1 -1
- package/dist/src/team/modelTeam.js +157 -41
- package/dist/src/team/modelTeam.js.map +1 -1
- package/dist/src/team/teamComposition.d.ts +20 -0
- package/dist/src/team/teamComposition.d.ts.map +1 -0
- package/dist/src/team/teamComposition.js +78 -0
- package/dist/src/team/teamComposition.js.map +1 -0
- package/dist/src/team/teamDefinitions.d.ts +10 -1
- package/dist/src/team/teamDefinitions.d.ts.map +1 -1
- package/dist/src/team/teamDefinitions.js +123 -16
- package/dist/src/team/teamDefinitions.js.map +1 -1
- package/dist/src/team/teamGraph.d.ts +1 -1
- package/dist/src/team/teamGraph.d.ts.map +1 -1
- package/dist/src/team/teamGraph.js +1 -1
- package/dist/src/team/teamGraph.js.map +1 -1
- package/dist/src/team/teamPrompts.d.ts.map +1 -1
- package/dist/src/team/teamPrompts.js +0 -4
- package/dist/src/team/teamPrompts.js.map +1 -1
- package/dist/src/team/teamProposalService.d.ts +9 -1
- package/dist/src/team/teamProposalService.d.ts.map +1 -1
- package/dist/src/team/teamProposalService.js +66 -6
- package/dist/src/team/teamProposalService.js.map +1 -1
- package/dist/src/team/teamPropose.d.ts +3 -1
- package/dist/src/team/teamPropose.d.ts.map +1 -1
- package/dist/src/team/teamPropose.js +16 -10
- package/dist/src/team/teamPropose.js.map +1 -1
- package/dist/src/team/teamRuntime.d.ts +5 -0
- package/dist/src/team/teamRuntime.d.ts.map +1 -1
- package/dist/src/team/teamRuntime.js +23 -5
- package/dist/src/team/teamRuntime.js.map +1 -1
- package/dist/src/team/workflowSquad.d.ts +10 -2
- package/dist/src/team/workflowSquad.d.ts.map +1 -1
- package/dist/src/team/workflowSquad.js +250 -12
- package/dist/src/team/workflowSquad.js.map +1 -1
- package/dist/src/tools/enterWorktree.d.ts.map +1 -1
- package/dist/src/tools/enterWorktree.js +3 -0
- package/dist/src/tools/enterWorktree.js.map +1 -1
- package/dist/src/tools/exaSearch.d.ts +2 -2
- package/dist/src/tools/exitWorktree.d.ts.map +1 -1
- package/dist/src/tools/exitWorktree.js +3 -1
- package/dist/src/tools/exitWorktree.js.map +1 -1
- package/dist/src/tools/hadamardCoreTools.d.ts +5 -14
- package/dist/src/tools/hadamardCoreTools.d.ts.map +1 -1
- package/dist/src/tools/hadamardCoreTools.js +31 -0
- package/dist/src/tools/hadamardCoreTools.js.map +1 -1
- package/dist/src/tools/hadamardFileTools.d.ts.map +1 -1
- package/dist/src/tools/hadamardFileTools.js +116 -38
- package/dist/src/tools/hadamardFileTools.js.map +1 -1
- package/dist/src/tools/planMode/PlanModeTools.d.ts +22 -0
- package/dist/src/tools/planMode/PlanModeTools.d.ts.map +1 -1
- package/dist/src/tools/planMode/PlanModeTools.js +102 -5
- package/dist/src/tools/planMode/PlanModeTools.js.map +1 -1
- package/dist/src/tools/todo/TodoWriteTool.d.ts +1 -1
- package/dist/src/tui/hadamardTui.d.ts.map +1 -1
- package/dist/src/tui/hadamardTui.js +387 -168
- package/dist/src/tui/hadamardTui.js.map +1 -1
- package/dist/src/tui/pendingInput.d.ts +17 -0
- package/dist/src/tui/pendingInput.d.ts.map +1 -0
- package/dist/src/tui/pendingInput.js +23 -0
- package/dist/src/tui/pendingInput.js.map +1 -0
- package/dist/src/tui/reasoningDisplay.d.ts +13 -0
- package/dist/src/tui/reasoningDisplay.d.ts.map +1 -0
- package/dist/src/tui/reasoningDisplay.js +45 -0
- package/dist/src/tui/reasoningDisplay.js.map +1 -0
- package/dist/src/tui/screen.d.ts +11 -1
- package/dist/src/tui/screen.d.ts.map +1 -1
- package/dist/src/tui/screen.js +64 -34
- package/dist/src/tui/screen.js.map +1 -1
- package/dist/src/types.d.ts +210 -77
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/ui/commandSurface.d.ts +8 -0
- package/dist/src/ui/commandSurface.d.ts.map +1 -1
- package/dist/src/ui/commandSurface.js +78 -13
- package/dist/src/ui/commandSurface.js.map +1 -1
- package/dist/src/update/appUpdateService.d.ts +34 -1
- package/dist/src/update/appUpdateService.d.ts.map +1 -1
- package/dist/src/update/appUpdateService.js +114 -9
- package/dist/src/update/appUpdateService.js.map +1 -1
- package/package.json +3 -2
- package/dist/src/prompts/outputStyles.d.ts +0 -20
- package/dist/src/prompts/outputStyles.d.ts.map +0 -1
- package/dist/src/prompts/outputStyles.js +0 -35
- package/dist/src/prompts/outputStyles.js.map +0 -1
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference operations (Agents panel redesign, P1): transactional rename and
|
|
3
|
+
* delete-with-fallback over the unified reference model.
|
|
4
|
+
*
|
|
5
|
+
* `renameDefinitionAndReferences` renames a config / agent / router / team and
|
|
6
|
+
* atomically rewrites every referencing field known to the reference index
|
|
7
|
+
* (profile.bridgeConfig, manager.json, router route/fallback targets incl.
|
|
8
|
+
* legacy role-matched routes, team teamRef/targetRef nodes, automation tasks,
|
|
9
|
+
* teamPreferences.defaultAttached). Referencers are written first, the
|
|
10
|
+
* definition itself last; any failure restores the original file bytes.
|
|
11
|
+
*
|
|
12
|
+
* `applyDeleteFallback` rewrites referencers ahead of a force-delete:
|
|
13
|
+
* config → re-point to another config; agent → re-point to another agent or
|
|
14
|
+
* degrade to a raw model ref keeping the model name; team → remove referencing
|
|
15
|
+
* graph nodes or leave them broken (the P0 ⚠ badge then marks them).
|
|
16
|
+
*
|
|
17
|
+
* Router/team JSON files are rewritten at the raw-file level so `$ENV_VAR`
|
|
18
|
+
* apiKey references survive verbatim (the loaded views resolve them).
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
21
|
+
import { readFile, rm, writeFile } from 'node:fs/promises';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { getBridgeConfigsPath, readBridgeConfigs, writeBridgeConfigs, } from '../parity/bridgeConfigs.js';
|
|
24
|
+
import { getAgentProfilesPath, readAgentProfiles, writeAgentProfiles, } from '../config/agentProfiles.js';
|
|
25
|
+
import { resolveHadamardHome } from '../config/hadamardHome.js';
|
|
26
|
+
import { BUILT_IN_TEAM_DEFINITIONS, deleteTeamDefinition } from '../team/teamDefinitions.js';
|
|
27
|
+
import { readManagerConfig, writeManagerConfig, managerConfigPath } from './projectManager.js';
|
|
28
|
+
import { listScheduledAutomationTasks, scheduledAutomationFilePath, upsertScheduledAutomationTask, } from '../scheduling/taskPersistence.js';
|
|
29
|
+
import { findAgentDefinitionFile, listAgentDefinitionNames, readAgentDefinitionMarkdown, } from '../config/agentDefinitionMigration.js';
|
|
30
|
+
import { getDefaultHadamardAgents } from '../runtime/defaultHadamardAgents.js';
|
|
31
|
+
const VALID_DEFINITION_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
32
|
+
function isRecord(value) {
|
|
33
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
// ── Raw-file reference rewrites (pure, exported for tests) ──────────
|
|
36
|
+
/** Rewrite one typed target ref; returns the (possibly new) ref and whether it changed. */
|
|
37
|
+
function rewriteTargetRef(ref, kind, oldName, newName) {
|
|
38
|
+
if (!ref)
|
|
39
|
+
return { ref, changed: false };
|
|
40
|
+
if (kind === 'agent' && ref.kind === 'agent' && ref.name === oldName) {
|
|
41
|
+
return { ref: { ...ref, name: newName }, changed: true };
|
|
42
|
+
}
|
|
43
|
+
if (kind === 'team' && ref.kind === 'team' && ref.name === oldName) {
|
|
44
|
+
return { ref: { ...ref, name: newName }, changed: true };
|
|
45
|
+
}
|
|
46
|
+
if (kind === 'config' && ref.kind === 'model' && ref.config === oldName) {
|
|
47
|
+
return { ref: { ...ref, config: newName }, changed: true };
|
|
48
|
+
}
|
|
49
|
+
return { ref, changed: false };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Rewrite references inside a raw router profile JSON. `agentNames` are the
|
|
53
|
+
* pre-rename saved profile names, used to recognize legacy role/name-matched
|
|
54
|
+
* routes (their implicit agent reference follows the rename).
|
|
55
|
+
* Returns true when anything changed.
|
|
56
|
+
*/
|
|
57
|
+
export function rewriteRouterFileRefs(raw, kind, oldName, newName, agentNames) {
|
|
58
|
+
let changed = false;
|
|
59
|
+
const leader = rewriteTargetRef(raw.routerModelTarget, kind, oldName, newName);
|
|
60
|
+
if (leader.changed) {
|
|
61
|
+
raw.routerModelTarget = leader.ref;
|
|
62
|
+
changed = true;
|
|
63
|
+
}
|
|
64
|
+
raw.routes = (raw.routes ?? []).map((route) => {
|
|
65
|
+
const next = { ...route };
|
|
66
|
+
const rewritten = rewriteTargetRef(next.target, kind, oldName, newName);
|
|
67
|
+
if (rewritten.changed) {
|
|
68
|
+
next.target = rewritten.ref;
|
|
69
|
+
changed = true;
|
|
70
|
+
}
|
|
71
|
+
if (!route.target && kind === 'agent' && agentNames.has(oldName)) {
|
|
72
|
+
// Legacy route without a typed target: a role/name equal to the renamed
|
|
73
|
+
// agent is an implicit reference (lazy migration would infer it).
|
|
74
|
+
if (next.role === oldName) {
|
|
75
|
+
next.role = newName;
|
|
76
|
+
changed = true;
|
|
77
|
+
}
|
|
78
|
+
else if (next.name === oldName) {
|
|
79
|
+
next.name = newName;
|
|
80
|
+
changed = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return next;
|
|
84
|
+
});
|
|
85
|
+
const fallback = rewriteTargetRef(raw.fallbackTarget, kind, oldName, newName);
|
|
86
|
+
if (fallback.changed) {
|
|
87
|
+
raw.fallbackTarget = fallback.ref;
|
|
88
|
+
changed = true;
|
|
89
|
+
}
|
|
90
|
+
return changed;
|
|
91
|
+
}
|
|
92
|
+
function rewriteWorkflowTreeRefs(node, kind, oldName, newName) {
|
|
93
|
+
if (!node)
|
|
94
|
+
return false;
|
|
95
|
+
let changed = false;
|
|
96
|
+
const rewritten = rewriteTargetRef(node.targetRef, kind, oldName, newName);
|
|
97
|
+
if (rewritten.changed) {
|
|
98
|
+
node.targetRef = rewritten.ref;
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
for (const child of node.children ?? []) {
|
|
102
|
+
if (rewriteWorkflowTreeRefs(child, kind, oldName, newName))
|
|
103
|
+
changed = true;
|
|
104
|
+
}
|
|
105
|
+
return changed;
|
|
106
|
+
}
|
|
107
|
+
/** Rewrite references inside a raw team definition JSON. Returns true when changed. */
|
|
108
|
+
export function rewriteTeamFileRefs(raw, kind, oldName, newName) {
|
|
109
|
+
let changed = false;
|
|
110
|
+
raw.nodes = (raw.nodes ?? []).map((node) => {
|
|
111
|
+
const next = { ...node };
|
|
112
|
+
const rewritten = rewriteTargetRef(next.targetRef, kind, oldName, newName);
|
|
113
|
+
if (rewritten.changed) {
|
|
114
|
+
next.targetRef = rewritten.ref;
|
|
115
|
+
changed = true;
|
|
116
|
+
}
|
|
117
|
+
if (kind === 'team' && next.type === 'team' && next.teamRef === oldName) {
|
|
118
|
+
next.teamRef = newName;
|
|
119
|
+
changed = true;
|
|
120
|
+
}
|
|
121
|
+
return next;
|
|
122
|
+
});
|
|
123
|
+
if (rewriteWorkflowTreeRefs(raw.workflowTree, kind, oldName, newName))
|
|
124
|
+
changed = true;
|
|
125
|
+
return changed;
|
|
126
|
+
}
|
|
127
|
+
/** Degrade agent references in a raw router profile to raw model refs. Returns changed flag. */
|
|
128
|
+
export function degradeAgentRefsInRouter(raw, agentName, model) {
|
|
129
|
+
let changed = false;
|
|
130
|
+
const degrade = (ref) => {
|
|
131
|
+
if (ref?.kind === 'agent' && ref.name === agentName) {
|
|
132
|
+
changed = true;
|
|
133
|
+
return { kind: 'model', config: '', model };
|
|
134
|
+
}
|
|
135
|
+
return ref;
|
|
136
|
+
};
|
|
137
|
+
raw.routes = (raw.routes ?? []).map((route) => ({ ...route, target: degrade(route.target) }));
|
|
138
|
+
raw.fallbackTarget = degrade(raw.fallbackTarget);
|
|
139
|
+
return changed;
|
|
140
|
+
}
|
|
141
|
+
/** Strip agent targetRefs from a raw team definition (legacy model fields remain). */
|
|
142
|
+
export function removeAgentRefsInTeam(raw, agentName) {
|
|
143
|
+
let changed = false;
|
|
144
|
+
const strip = (ref) => {
|
|
145
|
+
if (ref?.kind === 'agent' && ref.name === agentName) {
|
|
146
|
+
changed = true;
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
return ref;
|
|
150
|
+
};
|
|
151
|
+
raw.nodes = (raw.nodes ?? []).map((node) => ({ ...node, targetRef: strip(node.targetRef) }));
|
|
152
|
+
const visit = (node) => {
|
|
153
|
+
if (!node)
|
|
154
|
+
return;
|
|
155
|
+
node.targetRef = strip(node.targetRef);
|
|
156
|
+
for (const child of node.children ?? [])
|
|
157
|
+
visit(child);
|
|
158
|
+
};
|
|
159
|
+
visit(raw.workflowTree);
|
|
160
|
+
return changed;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Remove graph nodes that invoke the given team (legacy `teamRef` or typed
|
|
164
|
+
* `targetRef`) plus their incident edges. Workflow-tree nodes keep the node
|
|
165
|
+
* and lose only the targetRef (tree surgery would change semantics).
|
|
166
|
+
*/
|
|
167
|
+
export function removeTeamRefNodes(raw, teamName) {
|
|
168
|
+
const references = (node) => (node.type === 'team' && node.teamRef === teamName)
|
|
169
|
+
|| (node.targetRef?.kind === 'team' && node.targetRef.name === teamName);
|
|
170
|
+
const removed = new Set((raw.nodes ?? []).filter(references).map((node) => node.id ?? node.name ?? node.role ?? ''));
|
|
171
|
+
if (removed.size === 0 && !raw.workflowTree)
|
|
172
|
+
return false;
|
|
173
|
+
let changed = removed.size > 0;
|
|
174
|
+
if (removed.size > 0) {
|
|
175
|
+
raw.nodes = (raw.nodes ?? []).filter((node) => !references(node));
|
|
176
|
+
raw.edges = (raw.edges ?? []).filter((edge) => !removed.has(edge.from) && !removed.has(edge.to));
|
|
177
|
+
}
|
|
178
|
+
const visit = (node) => {
|
|
179
|
+
if (!node)
|
|
180
|
+
return;
|
|
181
|
+
if (node.targetRef?.kind === 'team' && node.targetRef.name === teamName) {
|
|
182
|
+
node.targetRef = undefined;
|
|
183
|
+
changed = true;
|
|
184
|
+
}
|
|
185
|
+
for (const child of node.children ?? [])
|
|
186
|
+
visit(child);
|
|
187
|
+
};
|
|
188
|
+
visit(raw.workflowTree);
|
|
189
|
+
return changed;
|
|
190
|
+
}
|
|
191
|
+
// ── File-level IO helpers ───────────────────────────────────────────
|
|
192
|
+
function routerDirs(projectDir, homeDir) {
|
|
193
|
+
const home = resolveHadamardHome(homeDir);
|
|
194
|
+
const dirs = [];
|
|
195
|
+
if (projectDir)
|
|
196
|
+
dirs.push(path.join(projectDir, '.hadamard', 'routers'));
|
|
197
|
+
dirs.push(path.join(home, 'routers'));
|
|
198
|
+
return dirs;
|
|
199
|
+
}
|
|
200
|
+
function teamDirs(projectDir, homeDir) {
|
|
201
|
+
const home = resolveHadamardHome(homeDir);
|
|
202
|
+
const dirs = [];
|
|
203
|
+
if (projectDir)
|
|
204
|
+
dirs.push(path.join(projectDir, '.hadamard', 'teams'));
|
|
205
|
+
dirs.push(path.join(home, 'teams'));
|
|
206
|
+
return dirs;
|
|
207
|
+
}
|
|
208
|
+
function listJsonFiles(dirs) {
|
|
209
|
+
const files = [];
|
|
210
|
+
for (const dir of dirs) {
|
|
211
|
+
if (!existsSync(dir))
|
|
212
|
+
continue;
|
|
213
|
+
try {
|
|
214
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
215
|
+
if (entry.isFile() && entry.name.endsWith('.json'))
|
|
216
|
+
files.push(path.join(dir, entry.name));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch { /* skip inaccessible */ }
|
|
220
|
+
}
|
|
221
|
+
return files;
|
|
222
|
+
}
|
|
223
|
+
/** Snapshot file bytes, run `fn`, restore every file when it throws. */
|
|
224
|
+
async function withFileRollback(paths, fn) {
|
|
225
|
+
const snapshots = new Map();
|
|
226
|
+
for (const filePath of paths) {
|
|
227
|
+
if (snapshots.has(filePath))
|
|
228
|
+
continue;
|
|
229
|
+
try {
|
|
230
|
+
snapshots.set(filePath, await readFile(filePath, 'utf-8'));
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
snapshots.set(filePath, null);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
await fn();
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
for (const [filePath, content] of snapshots) {
|
|
241
|
+
try {
|
|
242
|
+
if (content === null)
|
|
243
|
+
await rm(filePath, { force: true });
|
|
244
|
+
else
|
|
245
|
+
await writeFile(filePath, content, 'utf-8');
|
|
246
|
+
}
|
|
247
|
+
catch { /* best-effort rollback */ }
|
|
248
|
+
}
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function rewriteJsonFile(filePath, mutate) {
|
|
253
|
+
let raw;
|
|
254
|
+
try {
|
|
255
|
+
raw = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
if (!mutate(raw))
|
|
261
|
+
return false;
|
|
262
|
+
writeFileSync(filePath, `${JSON.stringify(raw, null, 2)}\n`, 'utf-8');
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
// ── Rename transaction ──────────────────────────────────────────────
|
|
266
|
+
/**
|
|
267
|
+
* Rename a config / agent / router / team and rewrite every reference the
|
|
268
|
+
* index knows about. Referencers are written first, the definition itself
|
|
269
|
+
* last; any failure restores the original bytes of every touched file.
|
|
270
|
+
*/
|
|
271
|
+
export async function renameDefinitionAndReferences(kind, oldName, newName, ctx = {}) {
|
|
272
|
+
const from = oldName.trim();
|
|
273
|
+
const to = newName.trim();
|
|
274
|
+
if (!from || !to)
|
|
275
|
+
throw new Error('Rename requires non-empty old and new names.');
|
|
276
|
+
if (from === to)
|
|
277
|
+
throw new Error('New name is the same as the current name.');
|
|
278
|
+
if (kind !== 'config' && !VALID_DEFINITION_NAME.test(to)) {
|
|
279
|
+
throw new Error('Invalid name (use letters, digits, . _ -)');
|
|
280
|
+
}
|
|
281
|
+
const homeDir = ctx.homeDir;
|
|
282
|
+
const projectDir = ctx.projectDir;
|
|
283
|
+
const rewritten = [];
|
|
284
|
+
// ── Load phase (any read/validation error aborts before writes) ──
|
|
285
|
+
const bridgeStore = readBridgeConfigs(homeDir);
|
|
286
|
+
const profileStore = readAgentProfiles(homeDir);
|
|
287
|
+
const agentNames = new Set(profileStore.profiles.map((profile) => profile.name));
|
|
288
|
+
const managerPath = ctx.managerProjectPath ?? projectDir;
|
|
289
|
+
const manager = managerPath ? await readManagerConfig(managerPath, resolveHadamardHome(homeDir)) : undefined;
|
|
290
|
+
const tasks = projectDir ? await listScheduledAutomationTasks(projectDir) : [];
|
|
291
|
+
const issues = kind === 'agent' && ctx.issues ? await ctx.issues.read() : [];
|
|
292
|
+
const assistantConfig = kind === 'config' && ctx.assistantConfig
|
|
293
|
+
? await ctx.assistantConfig.read()
|
|
294
|
+
: undefined;
|
|
295
|
+
if (kind === 'config' && !bridgeStore.configs.some((config) => config.name === from)) {
|
|
296
|
+
throw new Error(`Bridge config not found: ${from}`);
|
|
297
|
+
}
|
|
298
|
+
if (kind === 'config' && bridgeStore.configs.some((config) => config.name === to)) {
|
|
299
|
+
throw new Error(`A config named "${to}" already exists.`);
|
|
300
|
+
}
|
|
301
|
+
// S3 unified store: an agent may live in the legacy profile store or as a
|
|
302
|
+
// .md definition in either agents dir (project scope shadows personal).
|
|
303
|
+
const agentMdNames = kind === 'agent' ? new Set(listAgentDefinitionNames(homeDir, projectDir)) : new Set();
|
|
304
|
+
const builtInAgentNames = kind === 'agent' ? new Set(getDefaultHadamardAgents().map(agent => agent.name)) : new Set();
|
|
305
|
+
if (kind === 'agent') {
|
|
306
|
+
if (!agentNames.has(from) && !agentMdNames.has(from)) {
|
|
307
|
+
throw new Error(`Agent not found: ${from}`);
|
|
308
|
+
}
|
|
309
|
+
if (agentNames.has(to) || agentMdNames.has(to) || builtInAgentNames.has(to)) {
|
|
310
|
+
throw new Error(`An agent named "${to}" already exists.`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (kind === 'team' && BUILT_IN_TEAM_DEFINITIONS[from]) {
|
|
314
|
+
throw new Error(`"${from}" is a built-in preset and cannot be renamed. Clone it instead.`);
|
|
315
|
+
}
|
|
316
|
+
if (kind === 'team' && BUILT_IN_TEAM_DEFINITIONS[to]) {
|
|
317
|
+
throw new Error(`"${to}" is a built-in preset name — pick another name.`);
|
|
318
|
+
}
|
|
319
|
+
const routerFiles = listJsonFiles(routerDirs(projectDir, homeDir));
|
|
320
|
+
const teamFiles = listJsonFiles(teamDirs(projectDir, homeDir));
|
|
321
|
+
const agentMd = kind === 'agent' ? findAgentDefinitionFile(from, homeDir, projectDir) : null;
|
|
322
|
+
const renamedAgentMdFile = agentMd ? path.join(agentMd.directory, `${to}.md`) : undefined;
|
|
323
|
+
if (renamedAgentMdFile && existsSync(renamedAgentMdFile)) {
|
|
324
|
+
throw new Error(`An agent named "${to}" already exists.`);
|
|
325
|
+
}
|
|
326
|
+
const definitionFile = (kind === 'router' ? routerFiles : kind === 'team' ? teamFiles : [])
|
|
327
|
+
.find((filePath) => path.basename(filePath, '.json') === from);
|
|
328
|
+
if ((kind === 'router' || kind === 'team') && !definitionFile) {
|
|
329
|
+
throw new Error(`${kind === 'router' ? 'Router profile' : 'Team'} not found: ${from}`);
|
|
330
|
+
}
|
|
331
|
+
const renamedDefinitionFile = definitionFile
|
|
332
|
+
? path.join(path.dirname(definitionFile), `${to}.json`)
|
|
333
|
+
: undefined;
|
|
334
|
+
if (renamedDefinitionFile && existsSync(renamedDefinitionFile)) {
|
|
335
|
+
throw new Error(`A ${kind} named "${to}" already exists.`);
|
|
336
|
+
}
|
|
337
|
+
// ── Plan phase: preview (read-only) pass collects the affected files ──
|
|
338
|
+
const changedRouterFiles = routerFiles.filter((filePath) => {
|
|
339
|
+
let raw;
|
|
340
|
+
try {
|
|
341
|
+
raw = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
if (!isRecord(raw))
|
|
347
|
+
return false;
|
|
348
|
+
const clone = JSON.parse(JSON.stringify(raw));
|
|
349
|
+
return rewriteRouterFileRefs(clone, kind, from, to, agentNames);
|
|
350
|
+
});
|
|
351
|
+
const changedTeamFiles = teamFiles.filter((filePath) => {
|
|
352
|
+
let raw;
|
|
353
|
+
try {
|
|
354
|
+
raw = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
if (!isRecord(raw))
|
|
360
|
+
return false;
|
|
361
|
+
const clone = JSON.parse(JSON.stringify(raw));
|
|
362
|
+
return rewriteTeamFileRefs(clone, kind, from, to);
|
|
363
|
+
});
|
|
364
|
+
const plannedFiles = new Set();
|
|
365
|
+
const profilesChanged = kind === 'config'
|
|
366
|
+
&& profileStore.profiles.some((profile) => profile.bridgeConfig === from);
|
|
367
|
+
const managerChanged = kind === 'config' && manager?.bridgeConfig === from;
|
|
368
|
+
const tasksChanged = kind === 'team'
|
|
369
|
+
&& tasks.some((task) => task.kind === 'workflow' && task.workflowSource === 'agent' && task.workflowName === from);
|
|
370
|
+
const prefs = ctx.teamPreferences?.read();
|
|
371
|
+
const prefsChanged = kind === 'team' && prefs?.defaultAttached === from;
|
|
372
|
+
const changedIssues = kind === 'agent'
|
|
373
|
+
? issues.filter(issue => issue.agentConfig === from)
|
|
374
|
+
: [];
|
|
375
|
+
const assistantChanged = kind === 'config' && assistantConfig?.bridgeConfig === from;
|
|
376
|
+
if (kind === 'config')
|
|
377
|
+
plannedFiles.add(getBridgeConfigsPath(homeDir));
|
|
378
|
+
if (kind === 'agent' || profilesChanged)
|
|
379
|
+
plannedFiles.add(getAgentProfilesPath(homeDir));
|
|
380
|
+
if (managerChanged && managerPath)
|
|
381
|
+
plannedFiles.add(managerConfigPath(managerPath, resolveHadamardHome(homeDir)));
|
|
382
|
+
if (tasksChanged && projectDir)
|
|
383
|
+
plannedFiles.add(scheduledAutomationFilePath(projectDir));
|
|
384
|
+
for (const filePath of changedRouterFiles)
|
|
385
|
+
plannedFiles.add(filePath);
|
|
386
|
+
for (const filePath of changedTeamFiles)
|
|
387
|
+
plannedFiles.add(filePath);
|
|
388
|
+
if (definitionFile)
|
|
389
|
+
plannedFiles.add(definitionFile);
|
|
390
|
+
if (renamedDefinitionFile)
|
|
391
|
+
plannedFiles.add(renamedDefinitionFile);
|
|
392
|
+
// S3: snapshot the .md definition files too (S1a gap — rollback covers them now).
|
|
393
|
+
if (agentMd)
|
|
394
|
+
plannedFiles.add(agentMd.filePath);
|
|
395
|
+
if (renamedAgentMdFile)
|
|
396
|
+
plannedFiles.add(renamedAgentMdFile);
|
|
397
|
+
// ── Write phase: referencers first, definition last ──
|
|
398
|
+
try {
|
|
399
|
+
await withFileRollback(plannedFiles, async () => {
|
|
400
|
+
for (const filePath of changedRouterFiles) {
|
|
401
|
+
rewriteJsonFile(filePath, (raw) => {
|
|
402
|
+
if (!isRecord(raw))
|
|
403
|
+
return false;
|
|
404
|
+
return rewriteRouterFileRefs(raw, kind, from, to, agentNames);
|
|
405
|
+
});
|
|
406
|
+
rewritten.push(`router "${path.basename(filePath, '.json')}"`);
|
|
407
|
+
}
|
|
408
|
+
for (const filePath of changedTeamFiles) {
|
|
409
|
+
if (definitionFile && filePath === definitionFile)
|
|
410
|
+
continue; // renamed below
|
|
411
|
+
rewriteJsonFile(filePath, (raw) => {
|
|
412
|
+
if (!isRecord(raw))
|
|
413
|
+
return false;
|
|
414
|
+
return rewriteTeamFileRefs(raw, kind, from, to);
|
|
415
|
+
});
|
|
416
|
+
rewritten.push(`team "${path.basename(filePath, '.json')}"`);
|
|
417
|
+
}
|
|
418
|
+
if (profilesChanged) {
|
|
419
|
+
writeAgentProfiles({
|
|
420
|
+
version: 1,
|
|
421
|
+
profiles: profileStore.profiles.map((profile) => profile.bridgeConfig === from ? { ...profile, bridgeConfig: to } : profile),
|
|
422
|
+
}, homeDir);
|
|
423
|
+
rewritten.push('agent-configs.json (bridgeConfig)');
|
|
424
|
+
}
|
|
425
|
+
if (managerChanged && managerPath && manager) {
|
|
426
|
+
await writeManagerConfig(managerPath, resolveHadamardHome(homeDir), { ...manager, bridgeConfig: to });
|
|
427
|
+
rewritten.push('manager.json (bridgeConfig)');
|
|
428
|
+
}
|
|
429
|
+
if (tasksChanged && projectDir) {
|
|
430
|
+
for (const task of tasks) {
|
|
431
|
+
if (task.kind === 'workflow' && task.workflowSource === 'agent' && task.workflowName === from) {
|
|
432
|
+
await upsertScheduledAutomationTask(projectDir, { ...task, workflowName: to });
|
|
433
|
+
rewritten.push(`automation "${task.name || task.id}" (workflowName)`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (prefsChanged && prefs && ctx.teamPreferences) {
|
|
438
|
+
await ctx.teamPreferences.write({ ...prefs, defaultAttached: to });
|
|
439
|
+
rewritten.push('teamPreferences.defaultAttached');
|
|
440
|
+
}
|
|
441
|
+
if (changedIssues.length && ctx.issues) {
|
|
442
|
+
for (const issue of changedIssues) {
|
|
443
|
+
await ctx.issues.writeAgentConfig(issue.id, to);
|
|
444
|
+
rewritten.push(`issue "${issue.number ?? issue.id}" (agentConfig)`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (assistantChanged && assistantConfig && ctx.assistantConfig) {
|
|
448
|
+
await ctx.assistantConfig.write({ ...assistantConfig, bridgeConfig: to });
|
|
449
|
+
rewritten.push('assistant.json (bridgeConfig)');
|
|
450
|
+
}
|
|
451
|
+
// Definition itself.
|
|
452
|
+
if (kind === 'config') {
|
|
453
|
+
writeBridgeConfigs({
|
|
454
|
+
configs: bridgeStore.configs.map((config) => config.name === from ? { ...config, name: to } : config),
|
|
455
|
+
}, homeDir);
|
|
456
|
+
}
|
|
457
|
+
else if (kind === 'agent') {
|
|
458
|
+
// Legacy json store write only while the json still exists; migrated
|
|
459
|
+
// stores are covered by the .md rename below.
|
|
460
|
+
if (existsSync(getAgentProfilesPath(homeDir))) {
|
|
461
|
+
writeAgentProfiles({
|
|
462
|
+
version: 1,
|
|
463
|
+
profiles: profileStore.profiles.map((profile) => profile.name === from ? { ...profile, name: to } : profile),
|
|
464
|
+
}, homeDir);
|
|
465
|
+
rewritten.push('agent-configs.json (name)');
|
|
466
|
+
}
|
|
467
|
+
if (agentMd && renamedAgentMdFile) {
|
|
468
|
+
// Unified store: rewrite the frontmatter name (string-level, so the
|
|
469
|
+
// rest of the file stays byte-identical) and rename the file.
|
|
470
|
+
const content = readFileSync(agentMd.filePath, 'utf-8');
|
|
471
|
+
const updated = /^name:.*$/m.test(content)
|
|
472
|
+
? content.replace(/^name:.*$/m, `name: ${to}`)
|
|
473
|
+
: content;
|
|
474
|
+
await writeFile(renamedAgentMdFile, updated, 'utf-8');
|
|
475
|
+
await rm(agentMd.filePath, { force: true });
|
|
476
|
+
rewritten.push(`agent "${to}.md"`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
else if (definitionFile && renamedDefinitionFile) {
|
|
480
|
+
const raw = JSON.parse(readFileSync(definitionFile, 'utf-8'));
|
|
481
|
+
raw.name = to;
|
|
482
|
+
await writeFile(renamedDefinitionFile, `${JSON.stringify(raw, null, 2)}\n`, 'utf-8');
|
|
483
|
+
await rm(definitionFile, { force: true });
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
const rollbackErrors = [];
|
|
489
|
+
if (prefsChanged && prefs && ctx.teamPreferences) {
|
|
490
|
+
try {
|
|
491
|
+
await ctx.teamPreferences.write(prefs);
|
|
492
|
+
}
|
|
493
|
+
catch (rollbackError) {
|
|
494
|
+
rollbackErrors.push(rollbackError);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (changedIssues.length && ctx.issues) {
|
|
498
|
+
for (const issue of changedIssues) {
|
|
499
|
+
try {
|
|
500
|
+
await ctx.issues.writeAgentConfig(issue.id, issue.agentConfig ?? null);
|
|
501
|
+
}
|
|
502
|
+
catch (rollbackError) {
|
|
503
|
+
rollbackErrors.push(rollbackError);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (assistantChanged && assistantConfig && ctx.assistantConfig) {
|
|
508
|
+
try {
|
|
509
|
+
await ctx.assistantConfig.write(assistantConfig);
|
|
510
|
+
}
|
|
511
|
+
catch (rollbackError) {
|
|
512
|
+
rollbackErrors.push(rollbackError);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (rollbackErrors.length > 0) {
|
|
516
|
+
throw new AggregateError([error, ...rollbackErrors], 'Reference rename failed and one or more external references could not be restored.');
|
|
517
|
+
}
|
|
518
|
+
throw error;
|
|
519
|
+
}
|
|
520
|
+
return { rewritten };
|
|
521
|
+
}
|
|
522
|
+
// ── Force-delete fallbacks ──────────────────────────────────────────
|
|
523
|
+
/**
|
|
524
|
+
* Rewrite referencers ahead of a force-delete. The definition deletion itself
|
|
525
|
+
* stays with the calling endpoint. `leave` is a no-op by design — the broken
|
|
526
|
+
* references stay visible via the reference index and ⚠ badges.
|
|
527
|
+
*/
|
|
528
|
+
export async function applyDeleteFallback(kind, name, strategy, ctx = {}) {
|
|
529
|
+
const rewritten = [];
|
|
530
|
+
if (strategy.type === 'leave')
|
|
531
|
+
return { rewritten };
|
|
532
|
+
const homeDir = ctx.homeDir;
|
|
533
|
+
const projectDir = ctx.projectDir;
|
|
534
|
+
const routerFiles = listJsonFiles(routerDirs(projectDir, homeDir));
|
|
535
|
+
const teamFiles = listJsonFiles(teamDirs(projectDir, homeDir));
|
|
536
|
+
const profileStore = readAgentProfiles(homeDir);
|
|
537
|
+
const managerPath = ctx.managerProjectPath ?? projectDir;
|
|
538
|
+
const manager = managerPath ? await readManagerConfig(managerPath, resolveHadamardHome(homeDir)) : undefined;
|
|
539
|
+
const issues = kind === 'agent' && ctx.issues ? await ctx.issues.read() : [];
|
|
540
|
+
const assistantConfig = kind === 'config' && ctx.assistantConfig
|
|
541
|
+
? await ctx.assistantConfig.read()
|
|
542
|
+
: undefined;
|
|
543
|
+
const prefs = ctx.teamPreferences?.read();
|
|
544
|
+
const changedIssues = kind === 'agent'
|
|
545
|
+
? issues.filter(issue => issue.agentConfig === name)
|
|
546
|
+
: [];
|
|
547
|
+
const assistantChanged = kind === 'config'
|
|
548
|
+
&& strategy.type === 'repoint'
|
|
549
|
+
&& assistantConfig?.bridgeConfig === name;
|
|
550
|
+
const prefsChanged = kind === 'team' && prefs?.defaultAttached === name;
|
|
551
|
+
if (strategy.type === 'repoint' && !strategy.target.trim()) {
|
|
552
|
+
throw new Error('Re-point strategy requires a target name.');
|
|
553
|
+
}
|
|
554
|
+
// The agent's model is needed to degrade agent references to raw model refs.
|
|
555
|
+
// S3: pure .md agents (and project-scoped ones) are not in the profile
|
|
556
|
+
// store — read the model from the .md definition as a fallback.
|
|
557
|
+
const degradedModel = kind === 'agent'
|
|
558
|
+
? (profileStore.profiles.find((profile) => profile.name === name)?.model
|
|
559
|
+
?? readAgentDefinitionMarkdown(name, homeDir, projectDir)?.frontmatter.model?.trim()
|
|
560
|
+
?? '')
|
|
561
|
+
: '';
|
|
562
|
+
const mutateRouter = (raw) => {
|
|
563
|
+
if (strategy.type === 'repoint') {
|
|
564
|
+
return rewriteRouterFileRefs(raw, kind, name, strategy.target, new Set());
|
|
565
|
+
}
|
|
566
|
+
if (strategy.type === 'degrade-model' && kind === 'agent') {
|
|
567
|
+
return degradeAgentRefsInRouter(raw, name, degradedModel);
|
|
568
|
+
}
|
|
569
|
+
return false;
|
|
570
|
+
};
|
|
571
|
+
const mutateTeam = (raw) => {
|
|
572
|
+
if (strategy.type === 'repoint')
|
|
573
|
+
return rewriteTeamFileRefs(raw, kind, name, strategy.target);
|
|
574
|
+
if (strategy.type === 'degrade-model' && kind === 'agent')
|
|
575
|
+
return removeAgentRefsInTeam(raw, name);
|
|
576
|
+
if (strategy.type === 'remove-nodes' && kind === 'team')
|
|
577
|
+
return removeTeamRefNodes(raw, name);
|
|
578
|
+
return false;
|
|
579
|
+
};
|
|
580
|
+
const plannedFiles = new Set();
|
|
581
|
+
if (kind === 'config' && strategy.type === 'repoint') {
|
|
582
|
+
plannedFiles.add(getAgentProfilesPath(homeDir));
|
|
583
|
+
if (manager?.bridgeConfig === name && managerPath) {
|
|
584
|
+
plannedFiles.add(managerConfigPath(managerPath, resolveHadamardHome(homeDir)));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
for (const filePath of [...routerFiles, ...teamFiles])
|
|
588
|
+
plannedFiles.add(filePath);
|
|
589
|
+
try {
|
|
590
|
+
await withFileRollback(plannedFiles, async () => {
|
|
591
|
+
for (const filePath of routerFiles) {
|
|
592
|
+
if (rewriteJsonFile(filePath, (raw) => isRecord(raw) && mutateRouter(raw))) {
|
|
593
|
+
rewritten.push(`router "${path.basename(filePath, '.json')}"`);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
for (const filePath of teamFiles) {
|
|
597
|
+
if (rewriteJsonFile(filePath, (raw) => isRecord(raw) && mutateTeam(raw))) {
|
|
598
|
+
rewritten.push(`team "${path.basename(filePath, '.json')}"`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (kind === 'config' && strategy.type === 'repoint') {
|
|
602
|
+
if (profileStore.profiles.some((profile) => profile.bridgeConfig === name)) {
|
|
603
|
+
writeAgentProfiles({
|
|
604
|
+
version: 1,
|
|
605
|
+
profiles: profileStore.profiles.map((profile) => profile.bridgeConfig === name ? { ...profile, bridgeConfig: strategy.target } : profile),
|
|
606
|
+
}, homeDir);
|
|
607
|
+
rewritten.push('agent-configs.json (bridgeConfig)');
|
|
608
|
+
}
|
|
609
|
+
if (manager?.bridgeConfig === name && managerPath) {
|
|
610
|
+
await writeManagerConfig(managerPath, resolveHadamardHome(homeDir), { ...manager, bridgeConfig: strategy.target });
|
|
611
|
+
rewritten.push('manager.json (bridgeConfig)');
|
|
612
|
+
}
|
|
613
|
+
if (assistantChanged && assistantConfig && ctx.assistantConfig) {
|
|
614
|
+
await ctx.assistantConfig.write({ ...assistantConfig, bridgeConfig: strategy.target });
|
|
615
|
+
rewritten.push('assistant.json (bridgeConfig)');
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (kind === 'agent' && ctx.issues) {
|
|
619
|
+
for (const issue of changedIssues) {
|
|
620
|
+
const replacement = strategy.type === 'repoint' ? strategy.target : null;
|
|
621
|
+
await ctx.issues.writeAgentConfig(issue.id, replacement);
|
|
622
|
+
rewritten.push(`issue "${issue.number ?? issue.id}" (agentConfig)`);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (prefsChanged && prefs && ctx.teamPreferences) {
|
|
626
|
+
const replacement = strategy.type === 'repoint' ? strategy.target : null;
|
|
627
|
+
await ctx.teamPreferences.write({ ...prefs, defaultAttached: replacement });
|
|
628
|
+
rewritten.push('teamPreferences.defaultAttached');
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
catch (error) {
|
|
633
|
+
const rollbackErrors = [];
|
|
634
|
+
if (changedIssues.length && ctx.issues) {
|
|
635
|
+
for (const issue of changedIssues) {
|
|
636
|
+
try {
|
|
637
|
+
await ctx.issues.writeAgentConfig(issue.id, issue.agentConfig ?? null);
|
|
638
|
+
}
|
|
639
|
+
catch (rollbackError) {
|
|
640
|
+
rollbackErrors.push(rollbackError);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (assistantChanged && assistantConfig && ctx.assistantConfig) {
|
|
645
|
+
try {
|
|
646
|
+
await ctx.assistantConfig.write(assistantConfig);
|
|
647
|
+
}
|
|
648
|
+
catch (rollbackError) {
|
|
649
|
+
rollbackErrors.push(rollbackError);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (prefsChanged && prefs && ctx.teamPreferences) {
|
|
653
|
+
try {
|
|
654
|
+
await ctx.teamPreferences.write(prefs);
|
|
655
|
+
}
|
|
656
|
+
catch (rollbackError) {
|
|
657
|
+
rollbackErrors.push(rollbackError);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (rollbackErrors.length > 0) {
|
|
661
|
+
throw new AggregateError([error, ...rollbackErrors], 'Reference fallback failed and one or more external references could not be restored.');
|
|
662
|
+
}
|
|
663
|
+
throw error;
|
|
664
|
+
}
|
|
665
|
+
return { rewritten };
|
|
666
|
+
}
|
|
667
|
+
// ── Squad → unified agent conversion (09 Aug 2026) ──────────────────
|
|
668
|
+
/**
|
|
669
|
+
* Convert-on-save for legacy single-agent squads (squadType 'agent'). The
|
|
670
|
+
* caller has already written the unified .md agent; this removes the squad
|
|
671
|
+
* json (built-in presets are immutable — skipped, the .md shadows them) and
|
|
672
|
+
* rewires references P1-style:
|
|
673
|
+
* - team graph/workflow nodes invoking the squad (legacy teamRef or typed
|
|
674
|
+
* targetRef kind 'team') become { kind: 'agent' } targetRefs;
|
|
675
|
+
* - teamPreferences.defaultAttached pointing at the squad is cleared (an
|
|
676
|
+
* agent cannot be attached as a team);
|
|
677
|
+
* - automation tasks keep their workflowName — the reference goes broken
|
|
678
|
+
* on purpose (visible via the index ⚠ badges), matching 'leave'.
|
|
679
|
+
*/
|
|
680
|
+
export async function convertAgentSquadToAgentDefinition(squadName, agentName, ctx = {}) {
|
|
681
|
+
const rewritten = [];
|
|
682
|
+
const teamFiles = listJsonFiles(teamDirs(ctx.projectDir, ctx.homeDir));
|
|
683
|
+
const convertNode = (node) => {
|
|
684
|
+
const legacy = node.type === 'team' && node.teamRef === squadName;
|
|
685
|
+
const typed = node.targetRef?.kind === 'team' && node.targetRef.name === squadName;
|
|
686
|
+
if (!legacy && !typed)
|
|
687
|
+
return false;
|
|
688
|
+
delete node.type;
|
|
689
|
+
delete node.teamRef;
|
|
690
|
+
node.targetRef = { kind: 'agent', name: agentName };
|
|
691
|
+
return true;
|
|
692
|
+
};
|
|
693
|
+
const mutateTeam = (raw) => {
|
|
694
|
+
let changed = false;
|
|
695
|
+
raw.nodes = (raw.nodes ?? []).map((node) => {
|
|
696
|
+
const next = { ...node };
|
|
697
|
+
if (convertNode(next))
|
|
698
|
+
changed = true;
|
|
699
|
+
return next;
|
|
700
|
+
});
|
|
701
|
+
const visit = (node) => {
|
|
702
|
+
if (!node)
|
|
703
|
+
return;
|
|
704
|
+
if (convertNode(node))
|
|
705
|
+
changed = true;
|
|
706
|
+
for (const child of node.children ?? [])
|
|
707
|
+
visit(child);
|
|
708
|
+
};
|
|
709
|
+
visit(raw.workflowTree);
|
|
710
|
+
return changed;
|
|
711
|
+
};
|
|
712
|
+
await withFileRollback(new Set(teamFiles), async () => {
|
|
713
|
+
for (const filePath of teamFiles) {
|
|
714
|
+
if (rewriteJsonFile(filePath, (raw) => isRecord(raw) && mutateTeam(raw))) {
|
|
715
|
+
rewritten.push(`team "${path.basename(filePath, '.json')}"`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
if (!BUILT_IN_TEAM_DEFINITIONS[squadName]) {
|
|
720
|
+
const removed = await deleteTeamDefinition(squadName, ctx.projectDir, ctx.homeDir);
|
|
721
|
+
if (removed)
|
|
722
|
+
rewritten.push(`squad "${squadName}.json" deleted`);
|
|
723
|
+
}
|
|
724
|
+
const prefs = ctx.teamPreferences?.read();
|
|
725
|
+
if (prefs?.defaultAttached === squadName && ctx.teamPreferences) {
|
|
726
|
+
await ctx.teamPreferences.write({ ...prefs, defaultAttached: null });
|
|
727
|
+
rewritten.push('teamPreferences.defaultAttached (cleared)');
|
|
728
|
+
}
|
|
729
|
+
return { rewritten };
|
|
730
|
+
}
|
|
731
|
+
// ── Config model re-point ───────────────────────────────────────────
|
|
732
|
+
/**
|
|
733
|
+
* Re-point every typed reference to `configName`'s `fromModel` onto
|
|
734
|
+
* `toModel` (same config). Used when a model is removed from a config:
|
|
735
|
+
* agent profiles (bridgeConfig+model), router route/fallback targets, and
|
|
736
|
+
* team node targetRefs.
|
|
737
|
+
*/
|
|
738
|
+
export async function repointConfigModel(configName, fromModel, toModel, ctx = {}) {
|
|
739
|
+
const rewritten = [];
|
|
740
|
+
const homeDir = ctx.homeDir;
|
|
741
|
+
const projectDir = ctx.projectDir;
|
|
742
|
+
const profileStore = readAgentProfiles(homeDir);
|
|
743
|
+
const rewriteModelRef = (ref) => {
|
|
744
|
+
if (ref?.kind === 'model' && ref.config === configName && ref.model === fromModel) {
|
|
745
|
+
return { ref: { ...ref, model: toModel }, changed: true };
|
|
746
|
+
}
|
|
747
|
+
return { ref, changed: false };
|
|
748
|
+
};
|
|
749
|
+
const mutateRouter = (raw) => {
|
|
750
|
+
let changed = false;
|
|
751
|
+
const leader = rewriteModelRef(raw.routerModelTarget);
|
|
752
|
+
if (leader.changed) {
|
|
753
|
+
raw.routerModelTarget = leader.ref;
|
|
754
|
+
raw.routerModel.model = toModel;
|
|
755
|
+
changed = true;
|
|
756
|
+
}
|
|
757
|
+
raw.routes = (raw.routes ?? []).map((route) => {
|
|
758
|
+
const next = rewriteModelRef(route.target);
|
|
759
|
+
if (next.changed)
|
|
760
|
+
changed = true;
|
|
761
|
+
return { ...route, ...(next.changed ? { model: toModel } : {}), target: next.ref };
|
|
762
|
+
});
|
|
763
|
+
const fallback = rewriteModelRef(raw.fallbackTarget);
|
|
764
|
+
if (fallback.changed) {
|
|
765
|
+
raw.fallbackTarget = fallback.ref;
|
|
766
|
+
if (raw.fallback)
|
|
767
|
+
raw.fallback.model = toModel;
|
|
768
|
+
changed = true;
|
|
769
|
+
}
|
|
770
|
+
return changed;
|
|
771
|
+
};
|
|
772
|
+
const mutateTeam = (raw) => {
|
|
773
|
+
let changed = false;
|
|
774
|
+
raw.nodes = (raw.nodes ?? []).map((node) => {
|
|
775
|
+
const next = rewriteModelRef(node.targetRef);
|
|
776
|
+
if (next.changed)
|
|
777
|
+
changed = true;
|
|
778
|
+
return { ...node, targetRef: next.ref };
|
|
779
|
+
});
|
|
780
|
+
const visit = (node) => {
|
|
781
|
+
if (!node)
|
|
782
|
+
return;
|
|
783
|
+
const next = rewriteModelRef(node.targetRef);
|
|
784
|
+
if (next.changed) {
|
|
785
|
+
node.targetRef = next.ref;
|
|
786
|
+
changed = true;
|
|
787
|
+
}
|
|
788
|
+
for (const child of node.children ?? [])
|
|
789
|
+
visit(child);
|
|
790
|
+
};
|
|
791
|
+
visit(raw.workflowTree);
|
|
792
|
+
return changed;
|
|
793
|
+
};
|
|
794
|
+
const files = [
|
|
795
|
+
...listJsonFiles(routerDirs(projectDir, homeDir)),
|
|
796
|
+
...listJsonFiles(teamDirs(projectDir, homeDir)),
|
|
797
|
+
];
|
|
798
|
+
const planned = new Set([getAgentProfilesPath(homeDir), ...files]);
|
|
799
|
+
await withFileRollback(planned, async () => {
|
|
800
|
+
if (profileStore.profiles.some((p) => p.bridgeConfig === configName && p.model === fromModel)) {
|
|
801
|
+
writeAgentProfiles({
|
|
802
|
+
version: 1,
|
|
803
|
+
profiles: profileStore.profiles.map((profile) => profile.bridgeConfig === configName && profile.model === fromModel
|
|
804
|
+
? { ...profile, model: toModel }
|
|
805
|
+
: profile),
|
|
806
|
+
}, homeDir);
|
|
807
|
+
rewritten.push('agent-configs.json (model)');
|
|
808
|
+
}
|
|
809
|
+
for (const filePath of files) {
|
|
810
|
+
const isRouter = filePath.includes(`${path.sep}routers${path.sep}`);
|
|
811
|
+
if (rewriteJsonFile(filePath, (raw) => isRecord(raw)
|
|
812
|
+
&& (isRouter
|
|
813
|
+
? mutateRouter(raw)
|
|
814
|
+
: mutateTeam(raw)))) {
|
|
815
|
+
rewritten.push(path.basename(filePath, '.json'));
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
return { rewritten };
|
|
820
|
+
}
|
|
821
|
+
//# sourceMappingURL=referenceOperations.js.map
|