@modelprofile.com/flexharness-agent 6.0.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.
Files changed (43) hide show
  1. package/dist_ts_agent/index.d.ts +17 -0
  2. package/dist_ts_agent/index.js +14 -0
  3. package/dist_ts_agent/plugins.d.ts +16 -0
  4. package/dist_ts_agent/plugins.js +18 -0
  5. package/dist_ts_agent/smartagent.classes.agent.d.ts +2 -0
  6. package/dist_ts_agent/smartagent.classes.agent.js +95 -0
  7. package/dist_ts_agent/smartagent.classes.session.d.ts +90 -0
  8. package/dist_ts_agent/smartagent.classes.session.js +2139 -0
  9. package/dist_ts_agent/smartagent.classes.toolregistry.d.ts +12 -0
  10. package/dist_ts_agent/smartagent.classes.toolregistry.js +23 -0
  11. package/dist_ts_agent/smartagent.context.d.ts +3 -0
  12. package/dist_ts_agent/smartagent.context.js +247 -0
  13. package/dist_ts_agent/smartagent.events.d.ts +160 -0
  14. package/dist_ts_agent/smartagent.events.js +136 -0
  15. package/dist_ts_agent/smartagent.interfaces.d.ts +275 -0
  16. package/dist_ts_agent/smartagent.interfaces.js +24 -0
  17. package/dist_ts_agent/smartagent.persistence.d.ts +88 -0
  18. package/dist_ts_agent/smartagent.persistence.js +677 -0
  19. package/dist_ts_agent/smartagent.transactions.d.ts +15 -0
  20. package/dist_ts_agent/smartagent.transactions.js +141 -0
  21. package/dist_ts_agent/tool.adapter.d.ts +29 -0
  22. package/dist_ts_agent/tool.adapter.js +60 -0
  23. package/dist_ts_agent/tool.contracts.d.ts +126 -0
  24. package/dist_ts_agent/tool.contracts.js +2 -0
  25. package/dist_ts_agent/tool.persistence.d.ts +23 -0
  26. package/dist_ts_agent/tool.persistence.js +35 -0
  27. package/license.md +21 -0
  28. package/package.json +31 -0
  29. package/readme.md +37 -0
  30. package/ts_agent/index.ts +119 -0
  31. package/ts_agent/plugins.ts +62 -0
  32. package/ts_agent/readme.md +37 -0
  33. package/ts_agent/smartagent.classes.agent.ts +104 -0
  34. package/ts_agent/smartagent.classes.session.ts +2588 -0
  35. package/ts_agent/smartagent.classes.toolregistry.ts +26 -0
  36. package/ts_agent/smartagent.context.ts +280 -0
  37. package/ts_agent/smartagent.events.ts +347 -0
  38. package/ts_agent/smartagent.interfaces.ts +365 -0
  39. package/ts_agent/smartagent.persistence.ts +827 -0
  40. package/ts_agent/smartagent.transactions.ts +193 -0
  41. package/ts_agent/tool.adapter.ts +85 -0
  42. package/ts_agent/tool.contracts.ts +149 -0
  43. package/ts_agent/tool.persistence.ts +64 -0
@@ -0,0 +1,26 @@
1
+ import type { ToolSet } from './plugins.js';
2
+
3
+ export class ToolRegistry {
4
+ private tools: ToolSet = {};
5
+
6
+ /**
7
+ * Register a tool.
8
+ * @param name Tool name (must be unique, snake_case recommended)
9
+ * @param def Tool definition created with ai-sdk's tool() helper
10
+ */
11
+ public register(name: string, def: ToolSet[string]): this {
12
+ if (name === 'invalid') {
13
+ throw new Error('Tool name "invalid" is reserved for SmartAgent tool-call repair.');
14
+ }
15
+ if (Object.prototype.hasOwnProperty.call(this.tools, name)) {
16
+ throw new Error(`Tool "${name}" is already registered.`);
17
+ }
18
+ this.tools[name] = def;
19
+ return this;
20
+ }
21
+
22
+ /** Get the full ToolSet for passing to runAgent */
23
+ public getTools(): ToolSet {
24
+ return { ...this.tools };
25
+ }
26
+ }
@@ -0,0 +1,280 @@
1
+ import type * as plugins from './plugins.js';
2
+ import type {
3
+ IAgentRuntimeEventPayload,
4
+ IAssistantMessageEvent,
5
+ IRuntimeEvent,
6
+ IToolCallEvent,
7
+ IToolResultEvent,
8
+ TAgentAssistantMessage,
9
+ TAgentEvent,
10
+ TAgentToolCallPart,
11
+ TAgentToolResultOutput,
12
+ } from './smartagent.events.js';
13
+ import { filterModelVisibleAgentEvents } from './smartagent.transactions.js';
14
+
15
+ const eventPriority = (event: TAgentEvent): number => {
16
+ if (event.type === 'assistant-message') return 0;
17
+ if (event.type === 'tool-call') return 1;
18
+ if (event.type === 'tool-result') return 2;
19
+ return 3;
20
+ };
21
+
22
+ const orderEvents = (events: readonly TAgentEvent[]): TAgentEvent[] => {
23
+ const inferenceIds = new Set<string>();
24
+ for (const event of events) {
25
+ if (event.type === 'runtime-event' && event.event.type === 'inference-started') {
26
+ inferenceIds.add(event.id);
27
+ }
28
+ }
29
+ const causedEvents = new Map<string, Array<{ event: TAgentEvent; index: number }>>();
30
+ for (const [index, event] of events.entries()) {
31
+ if (!event.causationId || !inferenceIds.has(event.causationId)) continue;
32
+ const group = causedEvents.get(event.causationId) ?? [];
33
+ group.push({ event, index });
34
+ causedEvents.set(event.causationId, group);
35
+ }
36
+ const ordered: TAgentEvent[] = [];
37
+ for (const event of events) {
38
+ if (event.causationId && inferenceIds.has(event.causationId)) continue;
39
+ ordered.push(event);
40
+ if (
41
+ event.type === 'runtime-event'
42
+ && event.event.type === 'inference-started'
43
+ ) {
44
+ const group = causedEvents.get(event.id) ?? [];
45
+ for (let priority = 0; priority <= 3; priority++) {
46
+ ordered.push(...group
47
+ .filter(({ event: causedEvent }) => eventPriority(causedEvent) === priority)
48
+ .map(({ event: causedEvent }) => causedEvent));
49
+ }
50
+ }
51
+ }
52
+ return ordered;
53
+ };
54
+
55
+ const applyLatestCompaction = (events: readonly TAgentEvent[]): TAgentEvent[] => {
56
+ const latestCompaction = [...events].reverse().find(
57
+ (event) => event.type === 'context-compaction',
58
+ );
59
+ if (!latestCompaction || latestCompaction.type !== 'context-compaction') {
60
+ return [...events];
61
+ }
62
+ const coveredIds = new Set(latestCompaction.coveredEventIds);
63
+ return [
64
+ ...latestCompaction.replacementEvents,
65
+ ...events.filter((event) => !coveredIds.has(event.id) && event.id !== latestCompaction.id),
66
+ ];
67
+ };
68
+
69
+ const assistantToolCallIds = (event: IAssistantMessageEvent): string[] => {
70
+ if (!Array.isArray(event.content)) return [];
71
+ return event.content
72
+ .filter((part): part is TAgentToolCallPart => part.type === 'tool-call')
73
+ .map((part) => part.toolCallId);
74
+ };
75
+
76
+ const assistantToolResultIds = (event: IAssistantMessageEvent): string[] => {
77
+ if (!Array.isArray(event.content)) return [];
78
+ return event.content
79
+ .filter((part) => part.type === 'tool-result')
80
+ .map((part) => part.toolCallId);
81
+ };
82
+
83
+ const assistantOccurrenceKey = (event: IAssistantMessageEvent, toolCallId: string): string =>
84
+ `${event.inferenceId ?? event.id}:${toolCallId}`;
85
+
86
+ const toolOccurrenceKey = (
87
+ event: IToolCallEvent | IToolResultEvent,
88
+ assistantEventIds: ReadonlySet<string>,
89
+ ): string => {
90
+ const parentAssistantId = event.parentIds?.find((parentId) => assistantEventIds.has(parentId));
91
+ return `${parentAssistantId ?? event.inferenceId ?? event.id}:${event.toolCallId}`;
92
+ };
93
+
94
+ const jsonValue = (value: unknown): unknown => {
95
+ if (value === undefined) return null;
96
+ try {
97
+ return JSON.parse(JSON.stringify(value));
98
+ } catch {
99
+ return String(value);
100
+ }
101
+ };
102
+
103
+ const toModelOutput = (event: IToolResultEvent): TAgentToolResultOutput => {
104
+ if (event.modelOutput) return event.modelOutput;
105
+ if (event.error !== undefined) return { type: 'error-text', value: event.error };
106
+ if (typeof event.output === 'string') return { type: 'text', value: event.output };
107
+ return { type: 'json', value: jsonValue(event.output) } as TAgentToolResultOutput;
108
+ };
109
+
110
+ const pushStandaloneToolCall = (
111
+ messages: plugins.ModelMessage[],
112
+ event: IToolCallEvent,
113
+ ): void => {
114
+ const part: TAgentToolCallPart = {
115
+ type: 'tool-call',
116
+ toolCallId: event.toolCallId,
117
+ toolName: event.toolName,
118
+ input: event.input,
119
+ providerExecuted: event.providerExecuted,
120
+ providerOptions: event.providerOptions,
121
+ };
122
+ const previous = messages[messages.length - 1];
123
+ if (
124
+ previous?.role === 'assistant'
125
+ && Array.isArray(previous.content)
126
+ && previous.content.every((contentPart) => contentPart.type === 'tool-call')
127
+ ) {
128
+ previous.content.push(part);
129
+ return;
130
+ }
131
+ messages.push({ role: 'assistant', content: [part] });
132
+ };
133
+
134
+ const pushToolResult = (
135
+ messages: plugins.ModelMessage[],
136
+ event: IToolResultEvent,
137
+ ): void => {
138
+ messages.push({
139
+ role: 'tool',
140
+ content: [{
141
+ type: 'tool-result',
142
+ toolCallId: event.toolCallId,
143
+ toolName: event.toolName,
144
+ output: toModelOutput(event),
145
+ providerOptions: event.providerOptions,
146
+ }],
147
+ providerOptions: event.messageProviderOptions,
148
+ });
149
+ };
150
+
151
+ const formatRuntimeValue = (value: unknown): string => {
152
+ let formatted: string;
153
+ try {
154
+ formatted = JSON.stringify(value, undefined, 2);
155
+ } catch {
156
+ formatted = String(value);
157
+ }
158
+ const maxLength = 20_000;
159
+ return formatted.length <= maxLength
160
+ ? formatted
161
+ : `${formatted.slice(0, maxLength)}\n[Runtime value truncated]`;
162
+ };
163
+
164
+ const renderJobRuntimeEvents = (events: readonly IRuntimeEvent[]): string | undefined => {
165
+ const executionStates = new Map<string, IAgentRuntimeEventPayload>();
166
+ for (const runtimeEvent of events) {
167
+ if (runtimeEvent.modelVisible === false || runtimeEvent.event.type === 'inference-started') {
168
+ continue;
169
+ }
170
+ const executionId = runtimeEvent.event.executionId;
171
+ if (typeof executionId === 'string') {
172
+ executionStates.set(executionId, {
173
+ ...executionStates.get(executionId),
174
+ ...runtimeEvent.event,
175
+ });
176
+ }
177
+ }
178
+ if (executionStates.size === 0) return undefined;
179
+
180
+ const completed: string[] = [];
181
+ const running: string[] = [];
182
+ for (const [executionId, state] of executionStates) {
183
+ if (state.state === 'running') {
184
+ running.push(`- ${executionId}`);
185
+ } else {
186
+ completed.push(`- ${executionId}:\n${formatRuntimeValue(state)}`);
187
+ }
188
+ }
189
+ const sections = ['Runtime update:'];
190
+ if (completed.length > 0) sections.push(`Completed:\n${completed.join('\n')}`);
191
+ if (running.length > 0) sections.push(`Still running:\n${running.join('\n')}`);
192
+ return sections.join('\n\n');
193
+ };
194
+
195
+ const renderExternalRuntimeEvent = (event: IAgentRuntimeEventPayload): string =>
196
+ `Runtime update:\n\n${formatRuntimeValue(event)}`;
197
+
198
+ export function buildModelMessages(events: readonly TAgentEvent[]): plugins.ModelMessage[] {
199
+ const orderedEvents = orderEvents(applyLatestCompaction(filterModelVisibleAgentEvents(events)));
200
+ const assistantEventIds = new Set(
201
+ orderedEvents
202
+ .filter((event): event is IAssistantMessageEvent => event.type === 'assistant-message')
203
+ .map((event) => event.id),
204
+ );
205
+ const representedToolCalls = new Set<string>();
206
+ const representedToolResults = new Set<string>();
207
+ for (const event of orderedEvents) {
208
+ if (event.type !== 'assistant-message') continue;
209
+ for (const toolCallId of assistantToolCallIds(event)) {
210
+ representedToolCalls.add(assistantOccurrenceKey(event, toolCallId));
211
+ }
212
+ for (const toolResultId of assistantToolResultIds(event)) {
213
+ representedToolResults.add(assistantOccurrenceKey(event, toolResultId));
214
+ }
215
+ }
216
+
217
+ const messages: plugins.ModelMessage[] = [];
218
+ const jobRuntimeEvents = orderedEvents.filter(
219
+ (event): event is IRuntimeEvent =>
220
+ event.type === 'runtime-event'
221
+ && typeof event.event.executionId === 'string'
222
+ && event.modelVisible !== false,
223
+ );
224
+ let latestJobRuntimeIndex = -1;
225
+ for (const [index, event] of orderedEvents.entries()) {
226
+ if (
227
+ event.type === 'runtime-event'
228
+ && typeof event.event.executionId === 'string'
229
+ && event.modelVisible !== false
230
+ ) {
231
+ latestJobRuntimeIndex = index;
232
+ }
233
+ }
234
+
235
+ for (const [index, event] of orderedEvents.entries()) {
236
+ switch (event.type) {
237
+ case 'system-message':
238
+ messages.push({ role: 'system', content: event.content, providerOptions: event.providerOptions });
239
+ break;
240
+ case 'user-message':
241
+ messages.push({ role: 'user', content: event.content, providerOptions: event.providerOptions });
242
+ break;
243
+ case 'assistant-message':
244
+ messages.push({
245
+ role: 'assistant',
246
+ content: event.content as TAgentAssistantMessage['content'],
247
+ providerOptions: event.providerOptions,
248
+ });
249
+ break;
250
+ case 'tool-call':
251
+ if (!representedToolCalls.has(toolOccurrenceKey(event, assistantEventIds))) {
252
+ pushStandaloneToolCall(messages, event);
253
+ }
254
+ break;
255
+ case 'tool-result':
256
+ if (!representedToolResults.has(toolOccurrenceKey(event, assistantEventIds))) {
257
+ pushToolResult(messages, event);
258
+ }
259
+ break;
260
+ case 'model-message':
261
+ messages.push(event.message);
262
+ break;
263
+ case 'runtime-event':
264
+ if (event.modelVisible === false || event.event.type === 'inference-started') break;
265
+ if (typeof event.event.executionId === 'string') {
266
+ if (index === latestJobRuntimeIndex) {
267
+ const runtimeUpdate = renderJobRuntimeEvents(jobRuntimeEvents);
268
+ if (runtimeUpdate) messages.push({ role: 'user', content: runtimeUpdate });
269
+ }
270
+ } else {
271
+ messages.push({ role: 'user', content: renderExternalRuntimeEvent(event.event) });
272
+ }
273
+ break;
274
+ case 'context-compaction':
275
+ break;
276
+ }
277
+ }
278
+
279
+ return messages;
280
+ }
@@ -0,0 +1,347 @@
1
+ import * as plugins from './plugins.js';
2
+
3
+ export type TAgentSystemMessage = Extract<plugins.ModelMessage, { role: 'system' }>;
4
+ export type TAgentUserMessage = Extract<plugins.ModelMessage, { role: 'user' }>;
5
+ export type TAgentAssistantMessage = Extract<plugins.ModelMessage, { role: 'assistant' }>;
6
+ export type TAgentToolMessage = Extract<plugins.ModelMessage, { role: 'tool' }>;
7
+ export type TAgentToolCallPart = Extract<
8
+ Exclude<TAgentAssistantMessage['content'], string>[number],
9
+ { type: 'tool-call' }
10
+ >;
11
+ export type TAgentToolResultPart = Extract<
12
+ TAgentToolMessage['content'][number],
13
+ { type: 'tool-result' }
14
+ >;
15
+ export type TAgentToolResultOutput = TAgentToolResultPart['output'];
16
+
17
+ export interface IAgentEventBase {
18
+ id: string;
19
+ timestamp: number;
20
+ /** Monotonic commit order assigned by AgentSession. */
21
+ sequence?: number;
22
+ parentIds?: string[];
23
+ causationId?: string;
24
+ generationId?: string;
25
+ inferenceId?: string;
26
+ }
27
+
28
+ export interface ISystemMessageEvent extends IAgentEventBase {
29
+ type: 'system-message';
30
+ content: TAgentSystemMessage['content'];
31
+ providerOptions?: TAgentSystemMessage['providerOptions'];
32
+ }
33
+
34
+ export interface IUserMessageEvent extends IAgentEventBase {
35
+ type: 'user-message';
36
+ content: TAgentUserMessage['content'];
37
+ providerOptions?: TAgentUserMessage['providerOptions'];
38
+ }
39
+
40
+ export interface IAssistantMessageEvent extends IAgentEventBase {
41
+ type: 'assistant-message';
42
+ content: TAgentAssistantMessage['content'];
43
+ providerOptions?: TAgentAssistantMessage['providerOptions'];
44
+ }
45
+
46
+ export interface IToolCallEvent extends IAgentEventBase {
47
+ type: 'tool-call';
48
+ toolCallId: string;
49
+ toolName: string;
50
+ input: unknown;
51
+ providerExecuted?: boolean;
52
+ providerOptions?: TAgentToolCallPart['providerOptions'];
53
+ }
54
+
55
+ export interface IToolResultEvent extends IAgentEventBase {
56
+ type: 'tool-result';
57
+ toolCallId: string;
58
+ toolName: string;
59
+ executionIntentId?: string;
60
+ output?: unknown;
61
+ error?: string;
62
+ modelOutput?: TAgentToolResultOutput;
63
+ /** True when modelOutput came from an AI SDK response message rather than a provisional callback. */
64
+ responseMessageCommitted?: boolean;
65
+ providerOptions?: TAgentToolResultPart['providerOptions'];
66
+ messageProviderOptions?: TAgentToolMessage['providerOptions'];
67
+ }
68
+
69
+ export interface IAgentRuntimeEventPayload {
70
+ type: string;
71
+ [key: string]: unknown;
72
+ }
73
+
74
+ export interface IRuntimeEvent extends IAgentEventBase {
75
+ type: 'runtime-event';
76
+ event: IAgentRuntimeEventPayload;
77
+ /** Internal events coordinate projection but are not shown to the model. */
78
+ modelVisible?: boolean;
79
+ }
80
+
81
+ export interface IModelMessageEvent extends IAgentEventBase {
82
+ type: 'model-message';
83
+ message: plugins.ModelMessage;
84
+ }
85
+
86
+ export type TAgentGenerationOutcome = 'accepted' | 'rejected' | 'interrupted';
87
+
88
+ export interface IGenerationBegunEvent extends IAgentEventBase {
89
+ type: 'generation-begun';
90
+ generationId: string;
91
+ claimTokenDigest: string;
92
+ }
93
+
94
+ export interface IGenerationExecutionStartedEvent extends IAgentEventBase {
95
+ type: 'generation-execution-started';
96
+ generationId: string;
97
+ }
98
+
99
+ export interface IGenerationExecutionCompletedEvent extends IAgentEventBase {
100
+ type: 'generation-execution-completed';
101
+ generationId: string;
102
+ }
103
+
104
+ export interface IGenerationOutcomeEvent extends IAgentEventBase {
105
+ type: 'generation-outcome';
106
+ generationId: string;
107
+ outcome: TAgentGenerationOutcome;
108
+ reason?: string;
109
+ }
110
+
111
+ export interface IToolExecutionIntentEvent extends IAgentEventBase {
112
+ type: 'tool-execution-intent';
113
+ generationId: string;
114
+ toolCallId: string;
115
+ toolName: string;
116
+ input: unknown;
117
+ }
118
+
119
+ export type TToolExecutionReconciliation =
120
+ | 'executed'
121
+ | 'not-executed'
122
+ | 'abandoned-unknown';
123
+
124
+ export interface IToolExecutionReconciliationEvent extends IAgentEventBase {
125
+ type: 'tool-execution-reconciliation';
126
+ generationId: string;
127
+ intentId: string;
128
+ toolCallId: string;
129
+ toolName: string;
130
+ resolution: TToolExecutionReconciliation;
131
+ output?: unknown;
132
+ modelOutput?: TAgentToolResultOutput;
133
+ error?: string;
134
+ }
135
+
136
+ export type TAgentConversationEvent =
137
+ | ISystemMessageEvent
138
+ | IUserMessageEvent
139
+ | IAssistantMessageEvent
140
+ | IToolCallEvent
141
+ | IToolResultEvent
142
+ | IModelMessageEvent;
143
+
144
+ export interface IContextCompactionEvent extends IAgentEventBase {
145
+ type: 'context-compaction';
146
+ coveredEventIds: string[];
147
+ replacementEvents: TAgentConversationEvent[];
148
+ archivedTransactions?: IArchivedAgentGenerationTransaction[];
149
+ }
150
+
151
+ export interface IArchivedAgentGenerationTransaction {
152
+ generationId: string;
153
+ claimTokenDigest: string;
154
+ outcome: TAgentGenerationOutcome;
155
+ }
156
+
157
+ export type TAgentControlEvent =
158
+ | IGenerationBegunEvent
159
+ | IGenerationExecutionStartedEvent
160
+ | IGenerationExecutionCompletedEvent
161
+ | IGenerationOutcomeEvent
162
+ | IToolExecutionIntentEvent
163
+ | IToolExecutionReconciliationEvent;
164
+
165
+ export type TAgentEvent =
166
+ | TAgentConversationEvent
167
+ | IRuntimeEvent
168
+ | IContextCompactionEvent
169
+ | TAgentControlEvent;
170
+
171
+ type TEventIdentity = 'id' | 'timestamp' | 'sequence';
172
+ export type TAgentEventInput<TEvent extends TAgentEvent = TAgentEvent> =
173
+ TEvent extends TAgentEvent
174
+ ? Omit<TEvent, TEventIdentity> & Partial<Pick<TEvent, TEventIdentity>>
175
+ : never;
176
+
177
+ export interface IModelMessagesToEventsOptions {
178
+ parentIds?: string[];
179
+ causationId?: string;
180
+ generationId?: string;
181
+ inferenceId?: string;
182
+ identityFactory?: TModelMessageEventIdentityFactory;
183
+ }
184
+
185
+ export type TModelMessageEmittedEventKind = TAgentConversationEvent['type'];
186
+
187
+ export interface IModelMessageEventIdentityCoordinates {
188
+ messageIndex: number;
189
+ partIndex?: number;
190
+ eventKind: TModelMessageEmittedEventKind;
191
+ message: plugins.ModelMessage;
192
+ part?: unknown;
193
+ parent?: {
194
+ messageIndex: number;
195
+ eventKind: TModelMessageEmittedEventKind;
196
+ eventId: string;
197
+ };
198
+ }
199
+
200
+ export type TModelMessageEventIdentityFactory = (
201
+ coordinates: IModelMessageEventIdentityCoordinates,
202
+ ) => string;
203
+
204
+ export const createAgentEventId = (prefix = 'event'): string =>
205
+ `${prefix}_${plugins.crypto.randomUUID()}`;
206
+
207
+ export function createAgentEvent<TEvent extends TAgentEvent>(
208
+ event: TAgentEventInput<TEvent>,
209
+ ): TEvent {
210
+ return {
211
+ ...event,
212
+ id: event.id ?? createAgentEventId(event.type),
213
+ timestamp: event.timestamp ?? Date.now(),
214
+ } as unknown as TEvent;
215
+ }
216
+
217
+ const modelOutputValue = (output: TAgentToolResultOutput): unknown => {
218
+ if ('value' in output) return output.value;
219
+ if (output.type === 'execution-denied') return output.reason;
220
+ return output;
221
+ };
222
+
223
+ export function modelMessagesToAgentEvents(
224
+ messages: readonly plugins.ModelMessage[],
225
+ options: IModelMessagesToEventsOptions = {},
226
+ ): TAgentConversationEvent[] {
227
+ const events: TAgentConversationEvent[] = [];
228
+ const causalFields = {
229
+ parentIds: options.parentIds,
230
+ causationId: options.causationId,
231
+ generationId: options.generationId,
232
+ inferenceId: options.inferenceId,
233
+ };
234
+ const identity = (coordinates: IModelMessageEventIdentityCoordinates): { id?: string } => {
235
+ if (!options.identityFactory) return {};
236
+ const id = options.identityFactory(coordinates);
237
+ if (typeof id !== 'string' || !id.trim()) {
238
+ throw new Error('modelMessagesToAgentEvents identityFactory must return a non-empty string.');
239
+ }
240
+ return { id };
241
+ };
242
+
243
+ for (const [messageIndex, message] of messages.entries()) {
244
+ if (message.role === 'system') {
245
+ events.push(createAgentEvent<ISystemMessageEvent>({
246
+ type: 'system-message',
247
+ content: message.content,
248
+ providerOptions: message.providerOptions,
249
+ ...causalFields,
250
+ ...identity({ messageIndex, eventKind: 'system-message', message }),
251
+ }));
252
+ continue;
253
+ }
254
+ if (message.role === 'user') {
255
+ events.push(createAgentEvent<IUserMessageEvent>({
256
+ type: 'user-message',
257
+ content: message.content,
258
+ providerOptions: message.providerOptions,
259
+ ...causalFields,
260
+ ...identity({ messageIndex, eventKind: 'user-message', message }),
261
+ }));
262
+ continue;
263
+ }
264
+ if (message.role === 'assistant') {
265
+ const assistantEvent = createAgentEvent<IAssistantMessageEvent>({
266
+ type: 'assistant-message',
267
+ content: message.content,
268
+ providerOptions: message.providerOptions,
269
+ ...causalFields,
270
+ ...identity({ messageIndex, eventKind: 'assistant-message', message }),
271
+ });
272
+ events.push(assistantEvent);
273
+ if (Array.isArray(message.content)) {
274
+ for (const [partIndex, part] of message.content.entries()) {
275
+ if (part.type === 'tool-call') {
276
+ events.push(createAgentEvent<IToolCallEvent>({
277
+ type: 'tool-call',
278
+ toolCallId: part.toolCallId,
279
+ toolName: part.toolName,
280
+ input: part.input,
281
+ providerExecuted: part.providerExecuted,
282
+ providerOptions: part.providerOptions,
283
+ parentIds: [assistantEvent.id],
284
+ causationId: options.causationId,
285
+ generationId: options.generationId,
286
+ inferenceId: options.inferenceId,
287
+ ...identity({
288
+ messageIndex,
289
+ partIndex,
290
+ eventKind: 'tool-call',
291
+ message,
292
+ part,
293
+ parent: {
294
+ messageIndex,
295
+ eventKind: 'assistant-message',
296
+ eventId: assistantEvent.id,
297
+ },
298
+ }),
299
+ }));
300
+ }
301
+ }
302
+ }
303
+ continue;
304
+ }
305
+
306
+ const toolResultParts = message.content.filter(
307
+ (part): part is TAgentToolResultPart => part.type === 'tool-result',
308
+ );
309
+ if (toolResultParts.length !== message.content.length) {
310
+ events.push(createAgentEvent<IModelMessageEvent>({
311
+ type: 'model-message',
312
+ message,
313
+ ...causalFields,
314
+ ...identity({ messageIndex, eventKind: 'model-message', message }),
315
+ }));
316
+ continue;
317
+ }
318
+ for (const [partIndex, part] of message.content.entries()) {
319
+ if (part.type !== 'tool-result') continue;
320
+ events.push(createAgentEvent<IToolResultEvent>({
321
+ type: 'tool-result',
322
+ toolCallId: part.toolCallId,
323
+ toolName: part.toolName,
324
+ output: modelOutputValue(part.output),
325
+ error: part.output.type === 'error-text'
326
+ ? part.output.value
327
+ : part.output.type === 'error-json'
328
+ ? JSON.stringify(part.output.value)
329
+ : undefined,
330
+ modelOutput: part.output,
331
+ responseMessageCommitted: true,
332
+ providerOptions: part.providerOptions,
333
+ messageProviderOptions: message.providerOptions,
334
+ ...causalFields,
335
+ ...identity({
336
+ messageIndex,
337
+ partIndex,
338
+ eventKind: 'tool-result',
339
+ message,
340
+ part,
341
+ }),
342
+ }));
343
+ }
344
+ }
345
+
346
+ return events;
347
+ }