mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-26 12:53:59 +02:00
fix: preserve toolArguments through normalization and activity state
- Add toolArguments to normalizeRunTimelineEvent output so persisted sessions retain tool argument data across app restarts - Add toolArguments field to AgentActivityState and propagate it in applySessionEventActivity for real-time activity labels - Add tests for normalization round-trip with and without toolArguments - Add test for activity state including toolArguments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -7,6 +7,7 @@ export interface AgentActivityState {
|
|||||||
agentName: string;
|
agentName: string;
|
||||||
activityType?: SessionEventRecord['activityType'];
|
activityType?: SessionEventRecord['activityType'];
|
||||||
toolName?: string;
|
toolName?: string;
|
||||||
|
toolArguments?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SessionUsageState {
|
export interface SessionUsageState {
|
||||||
@@ -45,6 +46,7 @@ export function applySessionEventActivity(
|
|||||||
agentName: event.agentName?.trim() || event.agentId?.trim() || agentKey,
|
agentName: event.agentName?.trim() || event.agentId?.trim() || agentKey,
|
||||||
activityType: event.activityType,
|
activityType: event.activityType,
|
||||||
toolName: event.toolName,
|
toolName: event.toolName,
|
||||||
|
toolArguments: event.toolArguments,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -494,6 +494,7 @@ function normalizeRunTimelineEvent(
|
|||||||
targetAgentName: normalizeOptionalString(event.targetAgentName),
|
targetAgentName: normalizeOptionalString(event.targetAgentName),
|
||||||
toolName: normalizeOptionalString(event.toolName),
|
toolName: normalizeOptionalString(event.toolName),
|
||||||
toolCallId: normalizeOptionalString(event.toolCallId),
|
toolCallId: normalizeOptionalString(event.toolCallId),
|
||||||
|
toolArguments: event.toolArguments,
|
||||||
fileChanges: normalizeToolCallFileChanges(event.fileChanges),
|
fileChanges: normalizeToolCallFileChanges(event.fileChanges),
|
||||||
approvalId: normalizeOptionalString(event.approvalId),
|
approvalId: normalizeOptionalString(event.approvalId),
|
||||||
approvalKind: event.approvalKind,
|
approvalKind: event.approvalKind,
|
||||||
|
|||||||
@@ -99,6 +99,32 @@ describe('session activity helpers', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('includes toolArguments in activity state', () => {
|
||||||
|
const event: SessionEventRecord = {
|
||||||
|
sessionId: 'session-1',
|
||||||
|
kind: 'agent-activity',
|
||||||
|
occurredAt: '2026-03-23T00:00:00.000Z',
|
||||||
|
activityType: 'tool-calling',
|
||||||
|
agentId: 'architect',
|
||||||
|
agentName: 'Architect',
|
||||||
|
toolName: 'view',
|
||||||
|
toolArguments: { path: 'src/main.ts', view_range: [1, 50] },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = applySessionEventActivity({}, event);
|
||||||
|
expect(result).toEqual({
|
||||||
|
'session-1': {
|
||||||
|
architect: {
|
||||||
|
agentId: 'architect',
|
||||||
|
agentName: 'Architect',
|
||||||
|
activityType: 'tool-calling',
|
||||||
|
toolName: 'view',
|
||||||
|
toolArguments: { path: 'src/main.ts', view_range: [1, 50] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('warns when an agent-activity event is missing identifiers', () => {
|
test('warns when an agent-activity event is missing identifiers', () => {
|
||||||
const originalWarn = console.warn;
|
const originalWarn = console.warn;
|
||||||
const warnings: unknown[][] = [];
|
const warnings: unknown[][] = [];
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
upsertRunApprovalEvent,
|
upsertRunApprovalEvent,
|
||||||
upsertRunMessageEvent,
|
upsertRunMessageEvent,
|
||||||
} from '@shared/domain/runTimeline';
|
} from '@shared/domain/runTimeline';
|
||||||
|
import type { SessionRunRecord } from '@shared/domain/runTimeline';
|
||||||
import type { ProjectRecord } from '@shared/domain/project';
|
import type { ProjectRecord } from '@shared/domain/project';
|
||||||
import type { PendingApprovalRecord } from '@shared/domain/approval';
|
import type { PendingApprovalRecord } from '@shared/domain/approval';
|
||||||
|
|
||||||
@@ -265,6 +266,71 @@ describe('run timeline helpers', () => {
|
|||||||
expect(normalizeSessionRunRecords(undefined)).toEqual([]);
|
expect(normalizeSessionRunRecords(undefined)).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('preserves toolArguments through normalization round-trip', () => {
|
||||||
|
const baseRun = createSessionRunRecord({
|
||||||
|
requestId: 'turn-1',
|
||||||
|
project: createProject(),
|
||||||
|
workspaceKind: 'project',
|
||||||
|
workflow: createWorkflow(),
|
||||||
|
triggerMessageId: 'msg-user-1',
|
||||||
|
startedAt: '2026-03-23T00:00:01.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
const run = appendRunActivityEvent(baseRun, {
|
||||||
|
activityType: 'tool-calling',
|
||||||
|
occurredAt: '2026-03-23T00:00:02.000Z',
|
||||||
|
agentId: 'agent-writer',
|
||||||
|
toolName: 'view',
|
||||||
|
toolCallId: 'tool-call-view-1',
|
||||||
|
toolArguments: { path: 'src/main.ts', view_range: [1, 50] },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simulate persisting and reloading via normalizeSessionRunRecords
|
||||||
|
const roundTripped = normalizeSessionRunRecords(
|
||||||
|
JSON.parse(JSON.stringify([run])) as SessionRunRecord[],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(roundTripped).toHaveLength(1);
|
||||||
|
const toolCallEvent = roundTripped[0].events.find(
|
||||||
|
(event) => event.kind === 'tool-call',
|
||||||
|
);
|
||||||
|
expect(toolCallEvent).toBeDefined();
|
||||||
|
expect(toolCallEvent!.toolArguments).toEqual({
|
||||||
|
path: 'src/main.ts',
|
||||||
|
view_range: [1, 50],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles missing toolArguments gracefully during normalization', () => {
|
||||||
|
const baseRun = createSessionRunRecord({
|
||||||
|
requestId: 'turn-1',
|
||||||
|
project: createProject(),
|
||||||
|
workspaceKind: 'project',
|
||||||
|
workflow: createWorkflow(),
|
||||||
|
triggerMessageId: 'msg-user-1',
|
||||||
|
startedAt: '2026-03-23T00:00:01.000Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
const run = appendRunActivityEvent(baseRun, {
|
||||||
|
activityType: 'tool-calling',
|
||||||
|
occurredAt: '2026-03-23T00:00:02.000Z',
|
||||||
|
agentId: 'agent-writer',
|
||||||
|
toolName: 'rg',
|
||||||
|
toolCallId: 'tool-call-rg-1',
|
||||||
|
// No toolArguments provided
|
||||||
|
});
|
||||||
|
|
||||||
|
const roundTripped = normalizeSessionRunRecords(
|
||||||
|
JSON.parse(JSON.stringify([run])) as SessionRunRecord[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const toolCallEvent = roundTripped[0].events.find(
|
||||||
|
(event) => event.kind === 'tool-call',
|
||||||
|
);
|
||||||
|
expect(toolCallEvent).toBeDefined();
|
||||||
|
expect(toolCallEvent!.toolArguments).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
test('tracks approval checkpoints as a single timeline event that can be resolved later', () => {
|
test('tracks approval checkpoints as a single timeline event that can be resolved later', () => {
|
||||||
const baseRun = createSessionRunRecord({
|
const baseRun = createSessionRunRecord({
|
||||||
requestId: 'turn-1',
|
requestId: 'turn-1',
|
||||||
|
|||||||
Reference in New Issue
Block a user