tlc-claude-code 1.4.1 → 1.4.2
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/dashboard/dist/App.js +229 -35
- package/dashboard/dist/components/AgentRegistryPane.d.ts +35 -0
- package/dashboard/dist/components/AgentRegistryPane.js +89 -0
- package/dashboard/dist/components/AgentRegistryPane.test.d.ts +1 -0
- package/dashboard/dist/components/AgentRegistryPane.test.js +200 -0
- package/dashboard/dist/components/RouterPane.d.ts +5 -0
- package/dashboard/dist/components/RouterPane.js +65 -0
- package/dashboard/dist/components/RouterPane.test.d.ts +1 -0
- package/dashboard/dist/components/RouterPane.test.js +176 -0
- package/package.json +5 -2
- package/server/index.js +178 -0
- package/server/lib/agent-cleanup.js +177 -0
- package/server/lib/agent-cleanup.test.js +359 -0
- package/server/lib/agent-hooks.js +126 -0
- package/server/lib/agent-hooks.test.js +303 -0
- package/server/lib/agent-metadata.js +179 -0
- package/server/lib/agent-metadata.test.js +383 -0
- package/server/lib/agent-persistence.js +191 -0
- package/server/lib/agent-persistence.test.js +475 -0
- package/server/lib/agent-registry-command.js +340 -0
- package/server/lib/agent-registry-command.test.js +334 -0
- package/server/lib/agent-registry.js +155 -0
- package/server/lib/agent-registry.test.js +239 -0
- package/server/lib/agent-state.js +236 -0
- package/server/lib/agent-state.test.js +375 -0
- package/server/lib/api-provider.js +186 -0
- package/server/lib/api-provider.test.js +336 -0
- package/server/lib/cli-detector.js +166 -0
- package/server/lib/cli-detector.test.js +269 -0
- package/server/lib/cli-provider.js +212 -0
- package/server/lib/cli-provider.test.js +349 -0
- package/server/lib/debug.test.js +62 -0
- package/server/lib/devserver-router-api.js +249 -0
- package/server/lib/devserver-router-api.test.js +426 -0
- package/server/lib/model-router.js +245 -0
- package/server/lib/model-router.test.js +313 -0
- package/server/lib/output-schemas.js +269 -0
- package/server/lib/output-schemas.test.js +307 -0
- package/server/lib/provider-interface.js +153 -0
- package/server/lib/provider-interface.test.js +394 -0
- package/server/lib/provider-queue.js +158 -0
- package/server/lib/provider-queue.test.js +315 -0
- package/server/lib/router-config.js +221 -0
- package/server/lib/router-config.test.js +237 -0
- package/server/lib/router-setup-command.js +419 -0
- package/server/lib/router-setup-command.test.js +375 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
AgentHooks,
|
|
4
|
+
getAgentHooks,
|
|
5
|
+
resetHooks,
|
|
6
|
+
HOOK_TYPES,
|
|
7
|
+
} from './agent-hooks.js';
|
|
8
|
+
|
|
9
|
+
describe('AgentHooks', () => {
|
|
10
|
+
let hooks;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
resetHooks();
|
|
14
|
+
hooks = new AgentHooks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('registerHook', () => {
|
|
18
|
+
it('adds handler to hook type', () => {
|
|
19
|
+
const handler = vi.fn();
|
|
20
|
+
|
|
21
|
+
hooks.registerHook('onStart', handler);
|
|
22
|
+
|
|
23
|
+
const handlers = hooks.getHandlers('onStart');
|
|
24
|
+
expect(handlers).toHaveLength(1);
|
|
25
|
+
expect(handlers[0]).toBe(handler);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('validates hook type', () => {
|
|
29
|
+
const handler = vi.fn();
|
|
30
|
+
|
|
31
|
+
expect(() => hooks.registerHook('invalidHook', handler)).toThrow();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('returns unregister function', () => {
|
|
35
|
+
const handler = vi.fn();
|
|
36
|
+
|
|
37
|
+
const unregister = hooks.registerHook('onStart', handler);
|
|
38
|
+
|
|
39
|
+
expect(typeof unregister).toBe('function');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('allows multiple handlers per hook', () => {
|
|
43
|
+
const handler1 = vi.fn();
|
|
44
|
+
const handler2 = vi.fn();
|
|
45
|
+
const handler3 = vi.fn();
|
|
46
|
+
|
|
47
|
+
hooks.registerHook('onComplete', handler1);
|
|
48
|
+
hooks.registerHook('onComplete', handler2);
|
|
49
|
+
hooks.registerHook('onComplete', handler3);
|
|
50
|
+
|
|
51
|
+
const handlers = hooks.getHandlers('onComplete');
|
|
52
|
+
expect(handlers).toHaveLength(3);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('triggerHook', () => {
|
|
57
|
+
it('calls all handlers for hook type', async () => {
|
|
58
|
+
const handler1 = vi.fn();
|
|
59
|
+
const handler2 = vi.fn();
|
|
60
|
+
|
|
61
|
+
hooks.registerHook('onStart', handler1);
|
|
62
|
+
hooks.registerHook('onStart', handler2);
|
|
63
|
+
|
|
64
|
+
await hooks.triggerHook('onStart', {});
|
|
65
|
+
|
|
66
|
+
expect(handler1).toHaveBeenCalledTimes(1);
|
|
67
|
+
expect(handler2).toHaveBeenCalledTimes(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('passes agent context to handlers', async () => {
|
|
71
|
+
const handler = vi.fn();
|
|
72
|
+
const context = {
|
|
73
|
+
agentId: 'agent-123',
|
|
74
|
+
taskId: 'task-456',
|
|
75
|
+
phase: 'build',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
hooks.registerHook('onStart', handler);
|
|
79
|
+
|
|
80
|
+
await hooks.triggerHook('onStart', context);
|
|
81
|
+
|
|
82
|
+
expect(handler).toHaveBeenCalledWith(context);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('awaits async handlers', async () => {
|
|
86
|
+
const order = [];
|
|
87
|
+
const asyncHandler = vi.fn(async () => {
|
|
88
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
89
|
+
order.push('async');
|
|
90
|
+
});
|
|
91
|
+
const syncHandler = vi.fn(() => {
|
|
92
|
+
order.push('sync');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
hooks.registerHook('onComplete', asyncHandler);
|
|
96
|
+
hooks.registerHook('onComplete', syncHandler);
|
|
97
|
+
|
|
98
|
+
await hooks.triggerHook('onComplete', {});
|
|
99
|
+
|
|
100
|
+
expect(asyncHandler).toHaveBeenCalled();
|
|
101
|
+
expect(syncHandler).toHaveBeenCalled();
|
|
102
|
+
// Both should complete before triggerHook returns
|
|
103
|
+
expect(order).toContain('async');
|
|
104
|
+
expect(order).toContain('sync');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('continues on handler error', async () => {
|
|
108
|
+
const errorHandler = vi.fn(() => {
|
|
109
|
+
throw new Error('Handler failed');
|
|
110
|
+
});
|
|
111
|
+
const successHandler = vi.fn();
|
|
112
|
+
|
|
113
|
+
hooks.registerHook('onError', errorHandler);
|
|
114
|
+
hooks.registerHook('onError', successHandler);
|
|
115
|
+
|
|
116
|
+
// Should not throw
|
|
117
|
+
await expect(hooks.triggerHook('onError', {})).resolves.not.toThrow();
|
|
118
|
+
|
|
119
|
+
// Both should have been called
|
|
120
|
+
expect(errorHandler).toHaveBeenCalled();
|
|
121
|
+
expect(successHandler).toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('returns results from all handlers', async () => {
|
|
125
|
+
const handler1 = vi.fn(() => 'result1');
|
|
126
|
+
const handler2 = vi.fn(() => 'result2');
|
|
127
|
+
|
|
128
|
+
hooks.registerHook('onComplete', handler1);
|
|
129
|
+
hooks.registerHook('onComplete', handler2);
|
|
130
|
+
|
|
131
|
+
const results = await hooks.triggerHook('onComplete', {});
|
|
132
|
+
|
|
133
|
+
expect(results).toHaveLength(2);
|
|
134
|
+
expect(results).toContain('result1');
|
|
135
|
+
expect(results).toContain('result2');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('returns empty array for no handlers', async () => {
|
|
139
|
+
const results = await hooks.triggerHook('onStart', {});
|
|
140
|
+
|
|
141
|
+
expect(results).toEqual([]);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('removeHook', () => {
|
|
146
|
+
it('removes specific handler', () => {
|
|
147
|
+
const handler1 = vi.fn();
|
|
148
|
+
const handler2 = vi.fn();
|
|
149
|
+
|
|
150
|
+
hooks.registerHook('onStart', handler1);
|
|
151
|
+
const unregister = hooks.registerHook('onStart', handler2);
|
|
152
|
+
|
|
153
|
+
unregister();
|
|
154
|
+
|
|
155
|
+
const handlers = hooks.getHandlers('onStart');
|
|
156
|
+
expect(handlers).toHaveLength(1);
|
|
157
|
+
expect(handlers[0]).toBe(handler1);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('does not affect other handlers', async () => {
|
|
161
|
+
const handler1 = vi.fn();
|
|
162
|
+
const handler2 = vi.fn();
|
|
163
|
+
const handler3 = vi.fn();
|
|
164
|
+
|
|
165
|
+
hooks.registerHook('onComplete', handler1);
|
|
166
|
+
const unregister = hooks.registerHook('onComplete', handler2);
|
|
167
|
+
hooks.registerHook('onComplete', handler3);
|
|
168
|
+
|
|
169
|
+
unregister();
|
|
170
|
+
|
|
171
|
+
await hooks.triggerHook('onComplete', {});
|
|
172
|
+
|
|
173
|
+
expect(handler1).toHaveBeenCalled();
|
|
174
|
+
expect(handler2).not.toHaveBeenCalled();
|
|
175
|
+
expect(handler3).toHaveBeenCalled();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('clearHooks', () => {
|
|
180
|
+
it('removes all handlers for a hook type', () => {
|
|
181
|
+
hooks.registerHook('onStart', vi.fn());
|
|
182
|
+
hooks.registerHook('onStart', vi.fn());
|
|
183
|
+
hooks.registerHook('onStart', vi.fn());
|
|
184
|
+
|
|
185
|
+
hooks.clearHooks('onStart');
|
|
186
|
+
|
|
187
|
+
expect(hooks.getHandlers('onStart')).toHaveLength(0);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('removes all handlers when no type specified', () => {
|
|
191
|
+
hooks.registerHook('onStart', vi.fn());
|
|
192
|
+
hooks.registerHook('onComplete', vi.fn());
|
|
193
|
+
hooks.registerHook('onError', vi.fn());
|
|
194
|
+
hooks.registerHook('onCancel', vi.fn());
|
|
195
|
+
|
|
196
|
+
hooks.clearHooks();
|
|
197
|
+
|
|
198
|
+
expect(hooks.getHandlers('onStart')).toHaveLength(0);
|
|
199
|
+
expect(hooks.getHandlers('onComplete')).toHaveLength(0);
|
|
200
|
+
expect(hooks.getHandlers('onError')).toHaveLength(0);
|
|
201
|
+
expect(hooks.getHandlers('onCancel')).toHaveLength(0);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('hooks execute in registration order', () => {
|
|
206
|
+
it('calls handlers in order registered', async () => {
|
|
207
|
+
const order = [];
|
|
208
|
+
|
|
209
|
+
hooks.registerHook('onStart', () => order.push(1));
|
|
210
|
+
hooks.registerHook('onStart', () => order.push(2));
|
|
211
|
+
hooks.registerHook('onStart', () => order.push(3));
|
|
212
|
+
|
|
213
|
+
await hooks.triggerHook('onStart', {});
|
|
214
|
+
|
|
215
|
+
expect(order).toEqual([1, 2, 3]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('maintains order with async handlers', async () => {
|
|
219
|
+
const order = [];
|
|
220
|
+
|
|
221
|
+
hooks.registerHook('onComplete', async () => {
|
|
222
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
223
|
+
order.push(1);
|
|
224
|
+
});
|
|
225
|
+
hooks.registerHook('onComplete', () => order.push(2));
|
|
226
|
+
hooks.registerHook('onComplete', async () => {
|
|
227
|
+
await new Promise(resolve => setTimeout(resolve, 1));
|
|
228
|
+
order.push(3);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
await hooks.triggerHook('onComplete', {});
|
|
232
|
+
|
|
233
|
+
// Each handler completes in sequence
|
|
234
|
+
expect(order).toEqual([1, 2, 3]);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('onStart receives task config', () => {
|
|
239
|
+
it('passes full task config to onStart handlers', async () => {
|
|
240
|
+
const handler = vi.fn();
|
|
241
|
+
const taskConfig = {
|
|
242
|
+
agentId: 'agent-001',
|
|
243
|
+
taskId: 'task-001',
|
|
244
|
+
phase: 'build',
|
|
245
|
+
plan: '01-setup',
|
|
246
|
+
taskNumber: 1,
|
|
247
|
+
taskName: 'Initialize project',
|
|
248
|
+
workingDir: '/project',
|
|
249
|
+
timeout: 30000,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
hooks.registerHook('onStart', handler);
|
|
253
|
+
|
|
254
|
+
await hooks.triggerHook('onStart', taskConfig);
|
|
255
|
+
|
|
256
|
+
expect(handler).toHaveBeenCalledWith(taskConfig);
|
|
257
|
+
const passedConfig = handler.mock.calls[0][0];
|
|
258
|
+
expect(passedConfig.agentId).toBe('agent-001');
|
|
259
|
+
expect(passedConfig.taskName).toBe('Initialize project');
|
|
260
|
+
expect(passedConfig.timeout).toBe(30000);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
describe('HOOK_TYPES constant', () => {
|
|
265
|
+
it('exports valid hook types', () => {
|
|
266
|
+
expect(HOOK_TYPES).toContain('onStart');
|
|
267
|
+
expect(HOOK_TYPES).toContain('onComplete');
|
|
268
|
+
expect(HOOK_TYPES).toContain('onError');
|
|
269
|
+
expect(HOOK_TYPES).toContain('onCancel');
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('singleton pattern', () => {
|
|
274
|
+
it('returns same instance across calls', () => {
|
|
275
|
+
const instance1 = getAgentHooks();
|
|
276
|
+
const instance2 = getAgentHooks();
|
|
277
|
+
|
|
278
|
+
expect(instance1).toBe(instance2);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('shares state across imports', () => {
|
|
282
|
+
const instance1 = getAgentHooks();
|
|
283
|
+
const handler = vi.fn();
|
|
284
|
+
instance1.registerHook('onStart', handler);
|
|
285
|
+
|
|
286
|
+
const instance2 = getAgentHooks();
|
|
287
|
+
const handlers = instance2.getHandlers('onStart');
|
|
288
|
+
|
|
289
|
+
expect(handlers).toHaveLength(1);
|
|
290
|
+
expect(handlers[0]).toBe(handler);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('resetHooks clears singleton', () => {
|
|
294
|
+
const instance1 = getAgentHooks();
|
|
295
|
+
instance1.registerHook('onStart', vi.fn());
|
|
296
|
+
|
|
297
|
+
resetHooks();
|
|
298
|
+
|
|
299
|
+
const instance2 = getAgentHooks();
|
|
300
|
+
expect(instance2.getHandlers('onStart')).toHaveLength(0);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Metadata - Tracks execution metadata for agent tasks
|
|
3
|
+
*
|
|
4
|
+
* Provides immutable tracking of:
|
|
5
|
+
* - Model used
|
|
6
|
+
* - Token counts (input/output)
|
|
7
|
+
* - Cost calculation
|
|
8
|
+
* - Duration
|
|
9
|
+
* - Task type and parameters
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Model pricing per million tokens (USD)
|
|
14
|
+
* Updated as of early 2026
|
|
15
|
+
*/
|
|
16
|
+
const MODEL_PRICING = {
|
|
17
|
+
'claude-3-opus': { input: 15, output: 75 },
|
|
18
|
+
'claude-3-sonnet': { input: 3, output: 15 },
|
|
19
|
+
'claude-3-haiku': { input: 0.25, output: 1.25 },
|
|
20
|
+
'claude-3.5-sonnet': { input: 3, output: 15 },
|
|
21
|
+
'claude-3.5-haiku': { input: 0.25, output: 1.25 },
|
|
22
|
+
'gpt-4': { input: 30, output: 60 },
|
|
23
|
+
'gpt-4-turbo': { input: 10, output: 30 },
|
|
24
|
+
'gpt-3.5-turbo': { input: 0.5, output: 1.5 },
|
|
25
|
+
'default': { input: 1, output: 3 },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* AgentMetadata class - tracks metadata for a single agent task execution
|
|
30
|
+
*/
|
|
31
|
+
class AgentMetadata {
|
|
32
|
+
/**
|
|
33
|
+
* Create a new AgentMetadata instance
|
|
34
|
+
* @param {Object} config - Configuration
|
|
35
|
+
* @param {string} config.model - Model identifier
|
|
36
|
+
* @param {string} config.taskType - Type of task being executed
|
|
37
|
+
* @param {Object} [config.parameters] - Task parameters
|
|
38
|
+
*/
|
|
39
|
+
constructor(config) {
|
|
40
|
+
if (!config.model) {
|
|
41
|
+
throw new Error('model is required');
|
|
42
|
+
}
|
|
43
|
+
if (!config.taskType) {
|
|
44
|
+
throw new Error('taskType is required');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.model = config.model;
|
|
48
|
+
this.taskType = config.taskType;
|
|
49
|
+
this.parameters = config.parameters || {};
|
|
50
|
+
this.inputTokens = config.inputTokens || 0;
|
|
51
|
+
this.outputTokens = config.outputTokens || 0;
|
|
52
|
+
this.totalTokens = config.totalTokens || 0;
|
|
53
|
+
this.cost = config.cost || 0;
|
|
54
|
+
this.duration = config.duration || null;
|
|
55
|
+
this.startedAt = config.startedAt || Date.now();
|
|
56
|
+
this.completedAt = config.completedAt || null;
|
|
57
|
+
this.frozen = config.frozen || false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Update token counts
|
|
62
|
+
* @param {Object} tokens - Token counts to add
|
|
63
|
+
* @param {number} [tokens.input=0] - Input tokens to add
|
|
64
|
+
* @param {number} [tokens.output=0] - Output tokens to add
|
|
65
|
+
* @returns {AgentMetadata} This instance for chaining
|
|
66
|
+
* @throws {Error} If metadata is frozen
|
|
67
|
+
*/
|
|
68
|
+
updateTokens({ input = 0, output = 0 }) {
|
|
69
|
+
if (this.frozen) {
|
|
70
|
+
throw new Error('Cannot update frozen metadata');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.inputTokens += input;
|
|
74
|
+
this.outputTokens += output;
|
|
75
|
+
this.totalTokens = this.inputTokens + this.outputTokens;
|
|
76
|
+
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Calculate cost based on model pricing
|
|
82
|
+
* @returns {number} Total cost in USD
|
|
83
|
+
*/
|
|
84
|
+
calculateCost() {
|
|
85
|
+
const pricing = MODEL_PRICING[this.model] || MODEL_PRICING['default'];
|
|
86
|
+
|
|
87
|
+
const inputCost = (this.inputTokens * pricing.input) / 1_000_000;
|
|
88
|
+
const outputCost = (this.outputTokens * pricing.output) / 1_000_000;
|
|
89
|
+
|
|
90
|
+
this.cost = inputCost + outputCost;
|
|
91
|
+
return this.cost;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Set duration based on elapsed time since start
|
|
96
|
+
* @returns {AgentMetadata} This instance for chaining
|
|
97
|
+
* @throws {Error} If metadata is frozen
|
|
98
|
+
*/
|
|
99
|
+
setDuration() {
|
|
100
|
+
if (this.frozen) {
|
|
101
|
+
throw new Error('Cannot update frozen metadata');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.completedAt = Date.now();
|
|
105
|
+
this.duration = this.completedAt - this.startedAt;
|
|
106
|
+
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Freeze the metadata, preventing further updates
|
|
112
|
+
* Automatically calculates final cost
|
|
113
|
+
* @returns {AgentMetadata} This instance for chaining
|
|
114
|
+
*/
|
|
115
|
+
freeze() {
|
|
116
|
+
this.calculateCost();
|
|
117
|
+
this.frozen = true;
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Serialize to plain JSON object
|
|
123
|
+
* @returns {Object} Plain object representation
|
|
124
|
+
*/
|
|
125
|
+
toJSON() {
|
|
126
|
+
return {
|
|
127
|
+
model: this.model,
|
|
128
|
+
taskType: this.taskType,
|
|
129
|
+
parameters: this.parameters,
|
|
130
|
+
inputTokens: this.inputTokens,
|
|
131
|
+
outputTokens: this.outputTokens,
|
|
132
|
+
totalTokens: this.totalTokens,
|
|
133
|
+
cost: this.cost,
|
|
134
|
+
duration: this.duration,
|
|
135
|
+
startedAt: this.startedAt,
|
|
136
|
+
completedAt: this.completedAt,
|
|
137
|
+
frozen: this.frozen,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Restore from JSON
|
|
143
|
+
* @param {Object} json - Serialized metadata
|
|
144
|
+
* @returns {AgentMetadata} Restored instance
|
|
145
|
+
*/
|
|
146
|
+
static fromJSON(json) {
|
|
147
|
+
return new AgentMetadata({
|
|
148
|
+
model: json.model,
|
|
149
|
+
taskType: json.taskType,
|
|
150
|
+
parameters: json.parameters,
|
|
151
|
+
inputTokens: json.inputTokens,
|
|
152
|
+
outputTokens: json.outputTokens,
|
|
153
|
+
totalTokens: json.totalTokens,
|
|
154
|
+
cost: json.cost,
|
|
155
|
+
duration: json.duration,
|
|
156
|
+
startedAt: json.startedAt,
|
|
157
|
+
completedAt: json.completedAt,
|
|
158
|
+
frozen: json.frozen,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create a new metadata instance
|
|
165
|
+
* @param {Object} config - Configuration
|
|
166
|
+
* @param {string} config.model - Model identifier
|
|
167
|
+
* @param {string} config.taskType - Type of task being executed
|
|
168
|
+
* @param {Object} [config.parameters] - Task parameters
|
|
169
|
+
* @returns {AgentMetadata} New metadata instance
|
|
170
|
+
*/
|
|
171
|
+
function createMetadata(config) {
|
|
172
|
+
return new AgentMetadata(config);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
AgentMetadata,
|
|
177
|
+
createMetadata,
|
|
178
|
+
MODEL_PRICING,
|
|
179
|
+
};
|