mirror of
https://github.com/davidkaya/aryx.git
synced 2026-11-15 14:52:03 +01:00
feat: add workspace agents for reusable agent definitions across patterns
Introduce a workspace-level agent library that allows users to define agents once and reference them from multiple orchestration patterns. Domain model: - Add WorkspaceAgentDefinition type with name, model, instructions, etc. - Extend PatternAgentDefinition with optional workspaceAgentId and overrides - Add resolution helpers that merge workspace agent base with per-pattern overrides - Extend WorkspaceSettings with agents array and normalize on load IPC & main process: - Add saveWorkspaceAgent/deleteWorkspaceAgent IPC channels and handlers - Resolve workspace agent references in buildEffectivePattern before sending to sidecar (no C# changes needed) Settings UI: - Add 'Agents' tab under Orchestration in the Settings panel - Create WorkspaceAgentEditor component using ToolingEditorShell - Show usage count (which patterns reference each agent) Pattern editor integration: - Add agent picker dropdown: 'New inline agent' or 'From library' - Show linked badge (chain icon) on referenced agent graph nodes - Show linked workspace agent banner in the inspector - Add 'Save to Agent Library' action to promote inline agents - Add 'Unlink' action to convert referenced agents back to inline Tests: - Add unit tests for resolution helpers (resolvePatternAgent, resolvePatternAgents, findWorkspaceAgentUsages, normalize) - Update existing tooling test for new agents field Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
10316a2871
commit
e39fffaf3b
17 files changed
+808
-17
No files matched your search
@@ -26,6 +26,8 @@ export const ipcChannels = {
|
||||
deleteMcpServer: 'tooling:mcp:delete',
|
||||
saveLspProfile: 'tooling:lsp:save',
|
||||
deleteLspProfile: 'tooling:lsp:delete',
|
||||
saveWorkspaceAgent: 'workspace-agents:save',
|
||||
deleteWorkspaceAgent: 'workspace-agents:delete',
|
||||
describeTerminal: 'terminal:describe',
|
||||
createTerminal: 'terminal:create',
|
||||
restartTerminal: 'terminal:restart',
|
||||
|
||||
@@ -21,6 +21,7 @@ import type {
|
||||
import type { WorkspaceState } from '@shared/domain/workspace';
|
||||
import type { ChatMessageAttachment } from '@shared/domain/attachment';
|
||||
import type { ProjectPromptInvocation } from '@shared/domain/projectCustomization';
|
||||
import type { WorkspaceAgentDefinition } from '@shared/domain/workspaceAgent';
|
||||
|
||||
export interface CreateSessionInput {
|
||||
projectId: string;
|
||||
@@ -118,6 +119,10 @@ export interface SaveLspProfileInput {
|
||||
profile: LspProfileDefinition;
|
||||
}
|
||||
|
||||
export interface SaveWorkspaceAgentInput {
|
||||
agent: WorkspaceAgentDefinition;
|
||||
}
|
||||
|
||||
export type DiscoveredToolingResolution = 'accept' | 'dismiss';
|
||||
|
||||
export interface RescanProjectConfigsInput {
|
||||
@@ -273,6 +278,8 @@ export interface ElectronApi {
|
||||
deleteMcpServer(serverId: string): Promise<WorkspaceState>;
|
||||
saveLspProfile(input: SaveLspProfileInput): Promise<WorkspaceState>;
|
||||
deleteLspProfile(profileId: string): Promise<WorkspaceState>;
|
||||
saveWorkspaceAgent(input: SaveWorkspaceAgentInput): Promise<WorkspaceState>;
|
||||
deleteWorkspaceAgent(agentId: string): Promise<WorkspaceState>;
|
||||
updateSessionTooling(input: UpdateSessionToolingInput): Promise<WorkspaceState>;
|
||||
updateSessionApprovalSettings(input: UpdateSessionApprovalSettingsInput): Promise<WorkspaceState>;
|
||||
createSession(input: CreateSessionInput): Promise<WorkspaceState>;
|
||||
|
||||
@@ -33,6 +33,7 @@ export const reasoningEffortOptions: ReadonlyArray<{ value: ReasoningEffort; lab
|
||||
];
|
||||
|
||||
import type { PatternAgentCopilotConfig } from '@shared/contracts/sidecar';
|
||||
import type { PatternAgentOverrides } from '@shared/domain/workspaceAgent';
|
||||
|
||||
export interface PatternAgentDefinition {
|
||||
id: string;
|
||||
@@ -42,6 +43,10 @@ export interface PatternAgentDefinition {
|
||||
model: string;
|
||||
reasoningEffort?: ReasoningEffort;
|
||||
copilot?: PatternAgentCopilotConfig;
|
||||
/** When set, this agent references a workspace agent instead of being fully inline. */
|
||||
workspaceAgentId?: string;
|
||||
/** Per-pattern overrides applied on top of the workspace agent base. */
|
||||
overrides?: PatternAgentOverrides;
|
||||
}
|
||||
|
||||
export interface PatternGraphPosition {
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
type ProjectDiscoveredTooling,
|
||||
} from '@shared/domain/discoveredTooling';
|
||||
import { nowIso } from '@shared/utils/ids';
|
||||
import type { WorkspaceAgentDefinition } from '@shared/domain/workspaceAgent';
|
||||
import { normalizeWorkspaceAgentDefinition } from '@shared/domain/workspaceAgent';
|
||||
|
||||
export type McpServerTransport = 'local' | 'http' | 'sse';
|
||||
|
||||
@@ -63,6 +65,7 @@ export interface WorkspaceSettings {
|
||||
theme: AppearanceTheme;
|
||||
tooling: WorkspaceToolingSettings;
|
||||
discoveredUserTooling: DiscoveredToolingState;
|
||||
agents?: WorkspaceAgentDefinition[];
|
||||
terminalHeight?: number;
|
||||
notificationsEnabled?: boolean;
|
||||
minimizeToTray?: boolean;
|
||||
@@ -207,6 +210,7 @@ export function normalizeWorkspaceSettings(settings?: Partial<WorkspaceSettings>
|
||||
lspProfiles: (settings?.tooling?.lspProfiles ?? []).map(normalizeLspProfileDefinition),
|
||||
},
|
||||
discoveredUserTooling: normalizeDiscoveredToolingState(settings?.discoveredUserTooling),
|
||||
agents: (settings?.agents ?? []).map(normalizeWorkspaceAgentDefinition),
|
||||
...(terminalHeight !== undefined ? { terminalHeight } : {}),
|
||||
...(settings?.notificationsEnabled !== undefined ? { notificationsEnabled: settings.notificationsEnabled } : {}),
|
||||
...(settings?.minimizeToTray !== undefined ? { minimizeToTray: settings.minimizeToTray } : {}),
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import type { PatternAgentCopilotConfig } from '@shared/contracts/sidecar';
|
||||
import type { PatternAgentDefinition, PatternDefinition, ReasoningEffort } from '@shared/domain/pattern';
|
||||
|
||||
export interface WorkspaceAgentDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
instructions: string;
|
||||
model: string;
|
||||
reasoningEffort?: ReasoningEffort;
|
||||
copilot?: PatternAgentCopilotConfig;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface PatternAgentOverrides {
|
||||
name?: string;
|
||||
description?: string;
|
||||
instructions?: string;
|
||||
model?: string;
|
||||
reasoningEffort?: ReasoningEffort;
|
||||
}
|
||||
|
||||
export interface WorkspaceAgentUsage {
|
||||
patternId: string;
|
||||
patternName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a single pattern agent by merging its workspace agent base with
|
||||
* per-pattern overrides. Returns the agent unchanged if it is inline.
|
||||
*/
|
||||
export function resolvePatternAgent(
|
||||
agent: PatternAgentDefinition,
|
||||
workspaceAgents: ReadonlyArray<WorkspaceAgentDefinition>,
|
||||
): PatternAgentDefinition {
|
||||
if (!agent.workspaceAgentId) {
|
||||
return agent;
|
||||
}
|
||||
|
||||
const base = workspaceAgents.find((wa) => wa.id === agent.workspaceAgentId);
|
||||
if (!base) {
|
||||
return agent;
|
||||
}
|
||||
|
||||
const overrides = agent.overrides ?? {};
|
||||
return {
|
||||
id: agent.id,
|
||||
name: overrides.name ?? base.name,
|
||||
description: overrides.description ?? base.description,
|
||||
instructions: overrides.instructions ?? base.instructions,
|
||||
model: overrides.model ?? base.model,
|
||||
reasoningEffort: overrides.reasoningEffort ?? base.reasoningEffort,
|
||||
copilot: base.copilot,
|
||||
workspaceAgentId: agent.workspaceAgentId,
|
||||
overrides: agent.overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all agents in a pattern, producing a new pattern whose agents
|
||||
* have workspace-agent references merged with their base definitions.
|
||||
*/
|
||||
export function resolvePatternAgents(
|
||||
pattern: PatternDefinition,
|
||||
workspaceAgents: ReadonlyArray<WorkspaceAgentDefinition>,
|
||||
): PatternDefinition {
|
||||
return {
|
||||
...pattern,
|
||||
agents: pattern.agents.map((agent) => resolvePatternAgent(agent, workspaceAgents)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns every pattern that references the given workspace agent.
|
||||
*/
|
||||
export function findWorkspaceAgentUsages(
|
||||
agentId: string,
|
||||
patterns: ReadonlyArray<PatternDefinition>,
|
||||
): WorkspaceAgentUsage[] {
|
||||
const usages: WorkspaceAgentUsage[] = [];
|
||||
for (const pattern of patterns) {
|
||||
if (pattern.agents.some((a) => a.workspaceAgentId === agentId)) {
|
||||
usages.push({ patternId: pattern.id, patternName: pattern.name });
|
||||
}
|
||||
}
|
||||
return usages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a workspace agent definition, trimming string fields and
|
||||
* ensuring consistent shape.
|
||||
*/
|
||||
export function normalizeWorkspaceAgentDefinition(
|
||||
agent: WorkspaceAgentDefinition,
|
||||
): WorkspaceAgentDefinition {
|
||||
return {
|
||||
...agent,
|
||||
name: agent.name.trim(),
|
||||
description: agent.description.trim(),
|
||||
instructions: agent.instructions.trim(),
|
||||
model: agent.model.trim(),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user