@posthog/agent 1.20.0 → 1.22.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.
- package/dist/claude-cli/cli.js +2251 -1888
- package/dist/src/adapters/claude/claude-adapter.d.ts +1 -1
- package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
- package/dist/src/adapters/claude/claude-adapter.js +141 -133
- package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
- package/dist/src/adapters/types.d.ts +3 -3
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/agent.d.ts +1 -1
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +9 -8
- package/dist/src/agent.js.map +1 -1
- package/dist/src/file-manager.d.ts +10 -0
- package/dist/src/file-manager.d.ts.map +1 -1
- package/dist/src/file-manager.js +49 -10
- package/dist/src/file-manager.js.map +1 -1
- package/dist/src/git-manager.d.ts +1 -0
- package/dist/src/git-manager.d.ts.map +1 -1
- package/dist/src/git-manager.js +10 -3
- package/dist/src/git-manager.js.map +1 -1
- package/dist/src/posthog-api.d.ts +2 -1
- package/dist/src/posthog-api.d.ts.map +1 -1
- package/dist/src/posthog-api.js +11 -0
- package/dist/src/posthog-api.js.map +1 -1
- package/dist/src/task-progress-reporter.d.ts +12 -4
- package/dist/src/task-progress-reporter.d.ts.map +1 -1
- package/dist/src/task-progress-reporter.js +282 -117
- package/dist/src/task-progress-reporter.js.map +1 -1
- package/dist/src/types.d.ts +17 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/workflow/config.d.ts.map +1 -1
- package/dist/src/workflow/config.js +11 -0
- package/dist/src/workflow/config.js.map +1 -1
- package/dist/src/workflow/steps/build.js +3 -3
- package/dist/src/workflow/steps/build.js.map +1 -1
- package/dist/src/workflow/steps/finalize.d.ts +3 -0
- package/dist/src/workflow/steps/finalize.d.ts.map +1 -0
- package/dist/src/workflow/steps/finalize.js +182 -0
- package/dist/src/workflow/steps/finalize.js.map +1 -0
- package/dist/src/workflow/steps/plan.js +3 -3
- package/dist/src/workflow/steps/plan.js.map +1 -1
- package/dist/src/workflow/steps/research.js +3 -3
- package/dist/src/workflow/steps/research.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/claude/claude-adapter.ts +56 -46
- package/src/adapters/types.ts +3 -3
- package/src/agent.ts +17 -8
- package/src/file-manager.ts +59 -6
- package/src/git-manager.ts +11 -3
- package/src/posthog-api.ts +33 -1
- package/src/task-progress-reporter.ts +310 -138
- package/src/types.ts +20 -1
- package/src/workflow/config.ts +11 -0
- package/src/workflow/steps/build.ts +3 -3
- package/src/workflow/steps/finalize.ts +216 -0
- package/src/workflow/steps/plan.ts +3 -3
- package/src/workflow/steps/research.ts +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Logger } from './utils/logger.js';
|
|
2
2
|
import type { PostHogAPIClient, TaskRunUpdate } from './posthog-api.js';
|
|
3
|
-
import type { AgentEvent, TaskRun } from './types.js';
|
|
3
|
+
import type { AgentEvent, TaskRun, LogEntry } from './types.js';
|
|
4
4
|
|
|
5
5
|
interface ProgressMetadata {
|
|
6
6
|
totalSteps?: number;
|
|
@@ -20,6 +20,14 @@ export class TaskProgressReporter {
|
|
|
20
20
|
private outputLog: string[] = [];
|
|
21
21
|
private totalSteps?: number;
|
|
22
22
|
private lastLogEntry?: string;
|
|
23
|
+
private tokenBuffer: string = '';
|
|
24
|
+
private tokenCount: number = 0;
|
|
25
|
+
private tokenFlushTimer?: NodeJS.Timeout;
|
|
26
|
+
private readonly TOKEN_BATCH_SIZE = 100;
|
|
27
|
+
private readonly TOKEN_FLUSH_INTERVAL_MS = 1000;
|
|
28
|
+
private logWriteQueue: Promise<void> = Promise.resolve();
|
|
29
|
+
private readonly LOG_APPEND_MAX_RETRIES = 3;
|
|
30
|
+
private readonly LOG_APPEND_RETRY_BASE_DELAY_MS = 200;
|
|
23
31
|
|
|
24
32
|
constructor(posthogAPI: PostHogAPIClient | undefined, logger: Logger) {
|
|
25
33
|
this.posthogAPI = posthogAPI;
|
|
@@ -51,10 +59,27 @@ export class TaskProgressReporter {
|
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
async complete(): Promise<void> {
|
|
62
|
+
await this.flushTokens(); // Flush any remaining tokens before completion
|
|
63
|
+
try {
|
|
64
|
+
await this.logWriteQueue;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
this.logger.debug('Pending logs failed to write during completion', { error });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (this.tokenFlushTimer) {
|
|
70
|
+
clearTimeout(this.tokenFlushTimer);
|
|
71
|
+
this.tokenFlushTimer = undefined;
|
|
72
|
+
}
|
|
54
73
|
await this.update({ status: 'completed' }, 'Task execution completed');
|
|
55
74
|
}
|
|
56
75
|
|
|
57
76
|
async fail(error: Error | string): Promise<void> {
|
|
77
|
+
try {
|
|
78
|
+
await this.logWriteQueue;
|
|
79
|
+
} catch (logError) {
|
|
80
|
+
this.logger.debug('Pending logs failed to write during fail', { error: logError });
|
|
81
|
+
}
|
|
82
|
+
|
|
58
83
|
const message = typeof error === 'string' ? error : error.message;
|
|
59
84
|
await this.update({ status: 'failed', error_message: message }, `Task execution failed: ${message}`);
|
|
60
85
|
}
|
|
@@ -63,71 +88,318 @@ export class TaskProgressReporter {
|
|
|
63
88
|
await this.update({}, line);
|
|
64
89
|
}
|
|
65
90
|
|
|
91
|
+
private async flushTokens(): Promise<void> {
|
|
92
|
+
if (!this.tokenBuffer || this.tokenCount === 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const buffer = this.tokenBuffer;
|
|
97
|
+
this.tokenBuffer = '';
|
|
98
|
+
this.tokenCount = 0;
|
|
99
|
+
|
|
100
|
+
await this.appendLogEntry({
|
|
101
|
+
type: 'token',
|
|
102
|
+
message: buffer,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private scheduleTokenFlush(): void {
|
|
107
|
+
if (this.tokenFlushTimer) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.tokenFlushTimer = setTimeout(() => {
|
|
112
|
+
this.tokenFlushTimer = undefined;
|
|
113
|
+
this.flushTokens().catch((err) => {
|
|
114
|
+
this.logger.warn('Failed to flush tokens', { error: err });
|
|
115
|
+
});
|
|
116
|
+
}, this.TOKEN_FLUSH_INTERVAL_MS);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private appendLogEntry(entry: LogEntry): Promise<void> {
|
|
120
|
+
if (!this.posthogAPI || !this.runId || !this.taskId) {
|
|
121
|
+
return Promise.resolve();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const taskId = this.taskId;
|
|
125
|
+
const runId = this.runId;
|
|
126
|
+
|
|
127
|
+
this.logWriteQueue = this.logWriteQueue
|
|
128
|
+
.catch((error) => {
|
|
129
|
+
this.logger.debug('Previous log append failed', {
|
|
130
|
+
taskId,
|
|
131
|
+
runId,
|
|
132
|
+
error: error instanceof Error ? error.message : String(error),
|
|
133
|
+
});
|
|
134
|
+
})
|
|
135
|
+
.then(() => this.writeLogEntry(taskId, runId, entry));
|
|
136
|
+
|
|
137
|
+
return this.logWriteQueue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async writeLogEntry(
|
|
141
|
+
taskId: string,
|
|
142
|
+
runId: string,
|
|
143
|
+
entry: LogEntry,
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
if (!this.posthogAPI) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (let attempt = 1; attempt <= this.LOG_APPEND_MAX_RETRIES; attempt++) {
|
|
150
|
+
try {
|
|
151
|
+
await this.posthogAPI.appendTaskRunLog(taskId, runId, [entry]);
|
|
152
|
+
return;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.logger.warn('Failed to append log entry', {
|
|
155
|
+
taskId,
|
|
156
|
+
runId,
|
|
157
|
+
attempt,
|
|
158
|
+
maxAttempts: this.LOG_APPEND_MAX_RETRIES,
|
|
159
|
+
error: (error as Error).message,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (attempt === this.LOG_APPEND_MAX_RETRIES) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const delayMs =
|
|
167
|
+
this.LOG_APPEND_RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1);
|
|
168
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
66
173
|
async recordEvent(event: AgentEvent): Promise<void> {
|
|
67
174
|
if (!this.posthogAPI || !this.runId || !this.taskId) {
|
|
68
175
|
return;
|
|
69
176
|
}
|
|
70
177
|
|
|
71
178
|
switch (event.type) {
|
|
72
|
-
case 'token':
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
179
|
+
case 'token': {
|
|
180
|
+
// Batch tokens for efficiency
|
|
181
|
+
this.tokenBuffer += event.content;
|
|
182
|
+
this.tokenCount++;
|
|
183
|
+
|
|
184
|
+
if (this.tokenCount >= this.TOKEN_BATCH_SIZE) {
|
|
185
|
+
await this.flushTokens();
|
|
186
|
+
if (this.tokenFlushTimer) {
|
|
187
|
+
clearTimeout(this.tokenFlushTimer);
|
|
188
|
+
this.tokenFlushTimer = undefined;
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
this.scheduleTokenFlush();
|
|
192
|
+
}
|
|
83
193
|
return;
|
|
194
|
+
}
|
|
84
195
|
|
|
85
|
-
case '
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
196
|
+
case 'content_block_start': {
|
|
197
|
+
await this.appendLogEntry({
|
|
198
|
+
type: 'content_block_start',
|
|
199
|
+
message: JSON.stringify({
|
|
200
|
+
index: event.index,
|
|
201
|
+
contentType: event.contentType,
|
|
202
|
+
toolName: event.toolName,
|
|
203
|
+
toolId: event.toolId,
|
|
204
|
+
ts: event.ts,
|
|
205
|
+
}),
|
|
206
|
+
});
|
|
90
207
|
return;
|
|
91
208
|
}
|
|
92
209
|
|
|
93
|
-
case '
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
210
|
+
case 'content_block_stop': {
|
|
211
|
+
await this.appendLogEntry({
|
|
212
|
+
type: 'content_block_stop',
|
|
213
|
+
message: JSON.stringify({
|
|
214
|
+
index: event.index,
|
|
215
|
+
ts: event.ts,
|
|
216
|
+
}),
|
|
217
|
+
});
|
|
98
218
|
return;
|
|
99
219
|
}
|
|
100
220
|
|
|
101
|
-
case '
|
|
102
|
-
|
|
221
|
+
case 'message_start': {
|
|
222
|
+
await this.appendLogEntry({
|
|
223
|
+
type: 'message_start',
|
|
224
|
+
message: JSON.stringify({
|
|
225
|
+
messageId: event.messageId,
|
|
226
|
+
model: event.model,
|
|
227
|
+
ts: event.ts,
|
|
228
|
+
}),
|
|
229
|
+
});
|
|
103
230
|
return;
|
|
231
|
+
}
|
|
104
232
|
|
|
105
|
-
case '
|
|
106
|
-
await this.
|
|
233
|
+
case 'message_delta': {
|
|
234
|
+
await this.appendLogEntry({
|
|
235
|
+
type: 'message_delta',
|
|
236
|
+
message: JSON.stringify({
|
|
237
|
+
stopReason: event.stopReason,
|
|
238
|
+
stopSequence: event.stopSequence,
|
|
239
|
+
usage: event.usage,
|
|
240
|
+
ts: event.ts,
|
|
241
|
+
}),
|
|
242
|
+
});
|
|
107
243
|
return;
|
|
244
|
+
}
|
|
108
245
|
|
|
109
|
-
case '
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
);
|
|
246
|
+
case 'message_stop': {
|
|
247
|
+
await this.appendLogEntry({
|
|
248
|
+
type: 'message_stop',
|
|
249
|
+
message: JSON.stringify({ ts: event.ts }),
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
case 'status': {
|
|
255
|
+
await this.appendLogEntry({
|
|
256
|
+
type: 'status',
|
|
257
|
+
message: JSON.stringify({
|
|
258
|
+
phase: event.phase,
|
|
259
|
+
kind: event.kind,
|
|
260
|
+
branch: event.branch,
|
|
261
|
+
prUrl: event.prUrl,
|
|
262
|
+
taskId: event.taskId,
|
|
263
|
+
messageId: event.messageId,
|
|
264
|
+
model: event.model,
|
|
265
|
+
ts: event.ts,
|
|
266
|
+
}),
|
|
267
|
+
});
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
case 'artifact': {
|
|
272
|
+
await this.appendLogEntry({
|
|
273
|
+
type: 'artifact',
|
|
274
|
+
message: JSON.stringify({
|
|
275
|
+
kind: event.kind,
|
|
276
|
+
content: event.content,
|
|
277
|
+
ts: event.ts,
|
|
278
|
+
}),
|
|
279
|
+
});
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
case 'init': {
|
|
284
|
+
await this.appendLogEntry({
|
|
285
|
+
type: 'init',
|
|
286
|
+
message: JSON.stringify({
|
|
287
|
+
model: event.model,
|
|
288
|
+
tools: event.tools,
|
|
289
|
+
permissionMode: event.permissionMode,
|
|
290
|
+
cwd: event.cwd,
|
|
291
|
+
apiKeySource: event.apiKeySource,
|
|
292
|
+
ts: event.ts,
|
|
293
|
+
}),
|
|
294
|
+
});
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
case 'metric': {
|
|
299
|
+
await this.appendLogEntry({
|
|
300
|
+
type: 'metric',
|
|
301
|
+
message: JSON.stringify({
|
|
302
|
+
key: event.key,
|
|
303
|
+
value: event.value,
|
|
304
|
+
unit: event.unit,
|
|
305
|
+
ts: event.ts,
|
|
306
|
+
}),
|
|
307
|
+
});
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
case 'compact_boundary': {
|
|
312
|
+
await this.appendLogEntry({
|
|
313
|
+
type: 'compact_boundary',
|
|
314
|
+
message: JSON.stringify({
|
|
315
|
+
trigger: event.trigger,
|
|
316
|
+
preTokens: event.preTokens,
|
|
317
|
+
ts: event.ts,
|
|
318
|
+
}),
|
|
319
|
+
});
|
|
114
320
|
return;
|
|
115
321
|
}
|
|
116
322
|
|
|
117
|
-
case '
|
|
118
|
-
|
|
323
|
+
case 'tool_call': {
|
|
324
|
+
await this.appendLogEntry({
|
|
325
|
+
type: 'tool_call',
|
|
326
|
+
message: JSON.stringify({
|
|
327
|
+
toolName: event.toolName,
|
|
328
|
+
callId: event.callId,
|
|
329
|
+
args: event.args,
|
|
330
|
+
parentToolUseId: event.parentToolUseId,
|
|
331
|
+
ts: event.ts,
|
|
332
|
+
}),
|
|
333
|
+
});
|
|
119
334
|
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
case 'tool_result': {
|
|
338
|
+
await this.appendLogEntry({
|
|
339
|
+
type: 'tool_result',
|
|
340
|
+
message: JSON.stringify({
|
|
341
|
+
toolName: event.toolName,
|
|
342
|
+
callId: event.callId,
|
|
343
|
+
result: event.result,
|
|
344
|
+
isError: event.isError,
|
|
345
|
+
parentToolUseId: event.parentToolUseId,
|
|
346
|
+
ts: event.ts,
|
|
347
|
+
}),
|
|
348
|
+
});
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
case 'error': {
|
|
353
|
+
await this.appendLogEntry({
|
|
354
|
+
type: 'error',
|
|
355
|
+
message: JSON.stringify({
|
|
356
|
+
message: event.message,
|
|
357
|
+
errorType: event.errorType,
|
|
358
|
+
context: event.context,
|
|
359
|
+
ts: event.ts,
|
|
360
|
+
}),
|
|
361
|
+
});
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
case 'done': {
|
|
366
|
+
await this.appendLogEntry({
|
|
367
|
+
type: 'done',
|
|
368
|
+
message: JSON.stringify({
|
|
369
|
+
result: event.result,
|
|
370
|
+
durationMs: event.durationMs,
|
|
371
|
+
durationApiMs: event.durationApiMs,
|
|
372
|
+
numTurns: event.numTurns,
|
|
373
|
+
totalCostUsd: event.totalCostUsd,
|
|
374
|
+
usage: event.usage,
|
|
375
|
+
modelUsage: event.modelUsage,
|
|
376
|
+
permissionDenials: event.permissionDenials,
|
|
377
|
+
ts: event.ts,
|
|
378
|
+
}),
|
|
379
|
+
});
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
120
382
|
|
|
121
383
|
case 'user_message': {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
384
|
+
await this.appendLogEntry({
|
|
385
|
+
type: 'user_message',
|
|
386
|
+
message: JSON.stringify({
|
|
387
|
+
content: event.content,
|
|
388
|
+
isSynthetic: event.isSynthetic,
|
|
389
|
+
ts: event.ts,
|
|
390
|
+
}),
|
|
391
|
+
});
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
case 'raw_sdk_event': {
|
|
396
|
+
// Skip raw SDK events - too verbose for persistence
|
|
126
397
|
return;
|
|
127
398
|
}
|
|
128
399
|
|
|
129
400
|
default:
|
|
130
|
-
// For any unfamiliar event types,
|
|
401
|
+
// For any unfamiliar event types, log them as-is
|
|
402
|
+
this.logger.debug('Unknown event type', { type: (event as any).type });
|
|
131
403
|
return;
|
|
132
404
|
}
|
|
133
405
|
}
|
|
@@ -168,104 +440,4 @@ export class TaskProgressReporter {
|
|
|
168
440
|
}
|
|
169
441
|
}
|
|
170
442
|
|
|
171
|
-
private summarizeUserMessage(content?: string): string | null {
|
|
172
|
-
if (!content) {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
const trimmed = content.trim();
|
|
176
|
-
if (!trimmed) {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const fileUpdateMatch = trimmed.match(/The file\s+([^\s]+)\s+has been updated/i);
|
|
181
|
-
if (fileUpdateMatch) {
|
|
182
|
-
return `[user] file updated: ${fileUpdateMatch[1]}`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (/Todos have been modified/i.test(trimmed)) {
|
|
186
|
-
return '[todo] list updated';
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const diffMatch = trimmed.match(/diff --git a\/([^\s]+) b\/([^\s]+)/);
|
|
190
|
-
if (diffMatch) {
|
|
191
|
-
return `[diff] ${diffMatch[2] ?? diffMatch[1]}`;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const gitStatusMatch = trimmed.match(/^On branch ([^\n]+)/);
|
|
195
|
-
if (gitStatusMatch) {
|
|
196
|
-
return `[git] status ${gitStatusMatch[1]}`;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (/This Bash command contains multiple operations/i.test(trimmed)) {
|
|
200
|
-
return '[approval] multi-step command pending';
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (/This command requires approval/i.test(trimmed)) {
|
|
204
|
-
return '[approval] command awaiting approval';
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (/^Exit plan mode\?/i.test(trimmed)) {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (trimmed.includes('node_modules')) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (trimmed.includes('total ') && trimmed.includes('drwx')) {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (trimmed.includes('→')) {
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (trimmed.split('\n').length > 2) {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const normalized = trimmed.replace(/\s+/g, ' ');
|
|
228
|
-
const maxLen = 120;
|
|
229
|
-
if (!normalized) {
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
const preview = normalized.length > maxLen ? `${normalized.slice(0, maxLen)}…` : normalized;
|
|
233
|
-
return `[user] ${preview}`;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
private truncateMultiline(text: string, max = 160): string {
|
|
238
|
-
if (!text) {
|
|
239
|
-
return '';
|
|
240
|
-
}
|
|
241
|
-
const compact = text.replace(/\s+/g, ' ').trim();
|
|
242
|
-
return compact.length > max ? `${compact.slice(0, max)}…` : compact;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
private formatToolCallEvent(event: Extract<AgentEvent, { type: 'tool_call' }>): string | null {
|
|
246
|
-
// File operations to track
|
|
247
|
-
const fileOps = ['read_file', 'write', 'search_replace', 'delete_file', 'glob_file_search', 'file_search', 'list_dir', 'edit_notebook'];
|
|
248
|
-
// Terminal commands to track
|
|
249
|
-
const terminalOps = ['run_terminal_cmd', 'bash', 'shell'];
|
|
250
|
-
|
|
251
|
-
if (fileOps.includes(event.toolName)) {
|
|
252
|
-
// Extract file path from args
|
|
253
|
-
const path = event.args?.target_file || event.args?.file_path || event.args?.target_notebook || event.args?.target_directory || '';
|
|
254
|
-
return `[tool] ${event.toolName}${path ? `: ${path}` : ''}`;
|
|
255
|
-
} else if (terminalOps.includes(event.toolName)) {
|
|
256
|
-
// Extract command from args
|
|
257
|
-
const cmd = event.args?.command || '';
|
|
258
|
-
const truncated = cmd.length > 80 ? `${cmd.slice(0, 80)}…` : cmd;
|
|
259
|
-
return `[cmd] ${truncated}`;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Skip other tools from persistence
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
private formatToolResultEvent(event: Extract<AgentEvent, { type: 'tool_result' }>): string | null {
|
|
267
|
-
// We don't need to log tool results separately - tool calls are sufficient
|
|
268
|
-
// This keeps the log concise
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
443
|
}
|
package/src/types.ts
CHANGED
|
@@ -32,6 +32,17 @@ export interface LogEntry {
|
|
|
32
32
|
[key: string]: unknown; // Allow additional fields
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
export type ArtifactType = 'plan' | 'context' | 'reference' | 'output' | 'artifact';
|
|
36
|
+
|
|
37
|
+
export interface TaskRunArtifact {
|
|
38
|
+
name: string;
|
|
39
|
+
type: ArtifactType;
|
|
40
|
+
size?: number;
|
|
41
|
+
content_type?: string;
|
|
42
|
+
storage_path?: string;
|
|
43
|
+
uploaded_at?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
// TaskRun model - represents individual execution runs of tasks
|
|
36
47
|
export interface TaskRun {
|
|
37
48
|
id: string;
|
|
@@ -43,6 +54,7 @@ export interface TaskRun {
|
|
|
43
54
|
error_message: string | null;
|
|
44
55
|
output: Record<string, unknown> | null; // Structured output (PR URL, commit SHA, etc.)
|
|
45
56
|
state: Record<string, unknown>; // Intermediate run state (defaults to {}, never null)
|
|
57
|
+
artifacts?: TaskRunArtifact[];
|
|
46
58
|
created_at: string;
|
|
47
59
|
updated_at: string;
|
|
48
60
|
completed_at: string | null;
|
|
@@ -51,10 +63,17 @@ export interface TaskRun {
|
|
|
51
63
|
export interface SupportingFile {
|
|
52
64
|
name: string;
|
|
53
65
|
content: string;
|
|
54
|
-
type:
|
|
66
|
+
type: ArtifactType;
|
|
55
67
|
created_at: string;
|
|
56
68
|
}
|
|
57
69
|
|
|
70
|
+
export interface TaskArtifactUploadPayload {
|
|
71
|
+
name: string;
|
|
72
|
+
type: ArtifactType;
|
|
73
|
+
content: string;
|
|
74
|
+
content_type?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
58
77
|
export enum PermissionMode {
|
|
59
78
|
PLAN = "plan",
|
|
60
79
|
DEFAULT = "default",
|
package/src/workflow/config.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { WorkflowDefinition } from './types.js';
|
|
|
2
2
|
import { researchStep } from './steps/research.js';
|
|
3
3
|
import { planStep } from './steps/plan.js';
|
|
4
4
|
import { buildStep } from './steps/build.js';
|
|
5
|
+
import { finalizeStep } from './steps/finalize.js';
|
|
5
6
|
|
|
6
7
|
const MODELS = {
|
|
7
8
|
SONNET: "claude-sonnet-4-5",
|
|
@@ -39,4 +40,14 @@ export const TASK_WORKFLOW: WorkflowDefinition = [
|
|
|
39
40
|
push: true,
|
|
40
41
|
run: buildStep,
|
|
41
42
|
},
|
|
43
|
+
{
|
|
44
|
+
id: 'finalize',
|
|
45
|
+
name: 'Finalize',
|
|
46
|
+
agent: 'system', // not used
|
|
47
|
+
model: MODELS.HAIKU, // not used
|
|
48
|
+
permissionMode: 'plan', // not used
|
|
49
|
+
commit: true,
|
|
50
|
+
push: true,
|
|
51
|
+
run: finalizeStep,
|
|
52
|
+
},
|
|
42
53
|
];
|
|
@@ -88,9 +88,9 @@ export const buildStep: WorkflowStepRunner = async ({ step, context }) => {
|
|
|
88
88
|
|
|
89
89
|
for await (const message of response) {
|
|
90
90
|
emitEvent(adapter.createRawSDKEvent(message));
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
emitEvent(
|
|
91
|
+
const transformedEvents = adapter.transform(message);
|
|
92
|
+
for (const event of transformedEvents) {
|
|
93
|
+
emitEvent(event);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const todoList = await todoManager.checkAndPersistFromMessage(message, task.id);
|