Files
aryx/tests/shared/session.test.ts
T
David KayaandCopilot 805e369b67 refactor: remove legacy patterns system, unify on workflows
Remove the entire patterns domain model, IPC channels, sidecar services,
renderer components, and tests. Sessions now bind exclusively to workflows
via workflowId. Builtin workflows replace builtin patterns.

Backend:
- Make AgentNodeConfig standalone (no longer extends PatternAgentDefinition)
- Add WorkflowOrchestrationMode and WorkflowExecutionDefinition
- Create builtin workflows (single-agent, sequential, concurrent, handoff, group-chat)
- Rewrite session model config helpers for workflow-only
- Remove pattern IPC channels, handlers, and preload bindings
- Merge createSession/createWorkflowSession into single method
- Remove sidecar PatternGraphResolver, PatternValidator, pattern DTOs
- Add workspace migration for legacy sessions (patternId -> workflowId)

Frontend:
- Delete PatternEditor, pattern-graph components, patternGraph lib
- Delete NewSessionModal (session creation uses workflows directly)
- Remove PatternsSection from SettingsPanel
- Update App.tsx, ChatPane, ActivityPanel, Sidebar, RunTimeline,
  AgentConfigFields, InlinePills, sessionActivity to use workflow types
- Delete pattern.ts domain module

78 files changed across backend and frontend.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-06 22:37:00 +02:00

226 lines
6.6 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import type { WorkflowDefinition } from '@shared/domain/workflow';
import {
applySessionApprovalSettings,
applySessionModelConfig,
createSessionModelConfig,
resolveSessionApprovalSettings,
resolveSessionToolingSelection,
resolveSessionTitle,
resolveSessionModelConfig,
type SessionRecord,
} from '@shared/domain/session';
function createWorkflow(): WorkflowDefinition {
return {
id: 'workflow-single',
name: '1-on-1 Copilot Chat',
description: 'Single agent chat',
graph: {
nodes: [
{ id: 'start', kind: 'start', label: 'Start', position: { x: 0, y: 0 }, config: { kind: 'start' } },
{
id: 'agent-primary-node',
kind: 'agent',
label: 'Primary Agent',
position: { x: 200, y: 0 },
order: 0,
config: {
kind: 'agent',
id: 'agent-primary',
name: 'Primary Agent',
description: 'Helpful assistant',
instructions: 'Help the user.',
model: 'gpt-5.4',
reasoningEffort: 'high',
},
},
{
id: 'agent-secondary-node',
kind: 'agent',
label: 'Secondary Agent',
position: { x: 400, y: 0 },
order: 1,
config: {
kind: 'agent',
id: 'agent-secondary',
name: 'Secondary Agent',
description: 'Unused here',
instructions: 'Review.',
model: 'claude-sonnet-4.5',
reasoningEffort: 'medium',
},
},
{ id: 'end', kind: 'end', label: 'End', position: { x: 600, y: 0 }, config: { kind: 'end' } },
],
edges: [
{ id: 'edge-start-primary', source: 'start', target: 'agent-primary-node', kind: 'direct' },
{ id: 'edge-primary-secondary', source: 'agent-primary-node', target: 'agent-secondary-node', kind: 'direct' },
{ id: 'edge-secondary-end', source: 'agent-secondary-node', target: 'end', kind: 'direct' },
],
},
settings: {
checkpointing: { enabled: false },
executionMode: 'off-thread',
orchestrationMode: 'single',
maxIterations: 1,
},
createdAt: '2026-03-23T00:00:00.000Z',
updatedAt: '2026-03-23T00:00:00.000Z',
};
}
function createSession(overrides?: Partial<SessionRecord>): SessionRecord {
return {
id: 'session-1',
projectId: 'project-scratchpad',
workflowId: 'workflow-single',
title: 'Scratchpad',
createdAt: '2026-03-23T00:00:00.000Z',
updatedAt: '2026-03-23T00:00:00.000Z',
status: 'idle',
messages: [],
runs: [],
...overrides,
};
}
describe('session model config helpers', () => {
test('captures the initial model settings from the primary agent', () => {
expect(createSessionModelConfig(createWorkflow())).toEqual({
model: 'gpt-5.4',
reasoningEffort: 'high',
});
});
test('resolves persisted session overrides over the workflow defaults', () => {
const config = resolveSessionModelConfig(
createSession({
sessionModelConfig: {
model: 'claude-opus-4.5',
reasoningEffort: 'medium',
},
}),
createWorkflow(),
);
expect(config).toEqual({
model: 'claude-opus-4.5',
reasoningEffort: 'medium',
});
});
test('applies session model settings only to the primary agent', () => {
const workflow = createWorkflow();
const updated = applySessionModelConfig(
workflow,
createSession({
sessionModelConfig: {
model: 'gpt-5.4-mini',
reasoningEffort: 'low',
},
}),
);
const primaryAgent = updated.graph.nodes[1];
const secondaryAgent = updated.graph.nodes[2];
const originalSecondaryAgent = workflow.graph.nodes[2];
expect(primaryAgent?.config.kind === 'agent' ? primaryAgent.config.model : undefined).toBe('gpt-5.4-mini');
expect(primaryAgent?.config.kind === 'agent' ? primaryAgent.config.reasoningEffort : undefined).toBe('low');
expect(secondaryAgent).toEqual(originalSecondaryAgent);
});
});
describe('session title helpers', () => {
test('keeps a manual title instead of recomputing it from the first user message', () => {
const workflow = createWorkflow();
const session = createSession({
title: 'Release readiness review',
titleSource: 'manual',
});
expect(
resolveSessionTitle(session, workflow, [
{
id: 'msg-1',
role: 'user',
authorName: 'You',
content: 'Investigate why the version badge keeps saying unknown after refresh.',
createdAt: '2026-03-23T00:00:00.000Z',
},
]),
).toBe('Release readiness review');
});
test('builds auto titles from markdown-heavy first messages', () => {
const workflow = createWorkflow();
const session = createSession();
expect(
resolveSessionTitle(session, workflow, [
{
id: 'msg-1',
role: 'user',
authorName: 'You',
content: '```ts\nconst answer = 42;\n```',
createdAt: '2026-03-23T00:00:00.000Z',
},
]),
).toBe('const answer = 42;');
});
});
describe('session tooling helpers', () => {
test('normalizes missing or duplicated tooling selections into stable arrays', () => {
expect(resolveSessionToolingSelection(createSession())).toEqual({
enabledMcpServerIds: [],
enabledLspProfileIds: [],
});
expect(
resolveSessionToolingSelection(
createSession({
tooling: {
enabledMcpServerIds: ['mcp-git', ' mcp-git ', ''],
enabledLspProfileIds: ['ts', ' ts ', ''],
},
}),
),
).toEqual({
enabledMcpServerIds: ['mcp-git'],
enabledLspProfileIds: ['ts'],
});
});
});
describe('session approval helpers', () => {
test('normalizes session approval overrides and applies them over workflow defaults', () => {
const workflow = {
...createWorkflow(),
settings: {
...createWorkflow().settings,
approvalPolicy: {
rules: [{ kind: 'tool-call' as const }],
autoApprovedToolNames: ['git.status'],
},
},
};
expect(resolveSessionApprovalSettings(createSession())).toBeUndefined();
expect(
applySessionApprovalSettings(
workflow,
createSession({
approvalSettings: {
autoApprovedToolNames: ['git.diff', ' git.diff '],
},
}),
).settings.approvalPolicy,
).toEqual({
rules: [{ kind: 'tool-call' }],
autoApprovedToolNames: ['git.diff'],
});
});
});