@sebastianandreasson/pi-autonomous-agents 0.2.0 → 0.4.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/src/pi-report.mjs CHANGED
@@ -35,6 +35,17 @@ async function main() {
35
35
  console.log(`- ${kind}: ${count}`)
36
36
  }
37
37
 
38
+ const iterationSummaries = recent.filter((event) => event.kind === 'iteration_summary')
39
+ const warningsByIteration = iterationSummaries
40
+ .filter((event) => String(event.riskWarnings ?? '').trim() !== '')
41
+
42
+ if (warningsByIteration.length > 0) {
43
+ console.log('\nLarge file warnings:')
44
+ for (const event of warningsByIteration.slice(-5)) {
45
+ console.log(`- iteration ${event.iteration}: ${event.riskWarnings}`)
46
+ }
47
+ }
48
+
38
49
  const last = recent.at(-1)
39
50
  if (!last) {
40
51
  return
@@ -54,6 +54,44 @@ function extractToolTarget(toolName, args) {
54
54
  return ''
55
55
  }
56
56
 
57
+ function extractShellCommand(args) {
58
+ if (!args || typeof args !== 'object') {
59
+ return ''
60
+ }
61
+
62
+ if (typeof args.command === 'string') {
63
+ return args.command
64
+ }
65
+
66
+ if (typeof args.cmd === 'string') {
67
+ return args.cmd
68
+ }
69
+
70
+ return ''
71
+ }
72
+
73
+ function isLargeShellRead(command) {
74
+ const text = String(command ?? '').trim()
75
+ if (text === '') {
76
+ return false
77
+ }
78
+
79
+ if (/^\s*cat\s+\S+/.test(text)) {
80
+ return true
81
+ }
82
+
83
+ const sedMatch = text.match(/sed\s+-n\s+['"]?(\d+)\s*,\s*(\d+)p['"]?/)
84
+ if (sedMatch) {
85
+ const start = Number.parseInt(sedMatch[1], 10)
86
+ const end = Number.parseInt(sedMatch[2], 10)
87
+ if (Number.isFinite(start) && Number.isFinite(end) && end >= start) {
88
+ return (end - start) >= 120
89
+ }
90
+ }
91
+
92
+ return false
93
+ }
94
+
57
95
  function extractAssistantText(message) {
58
96
  if (!message || message.role !== 'assistant' || !Array.isArray(message.content)) {
59
97
  return ''
@@ -121,6 +159,7 @@ async function run() {
121
159
  const pending = new Map()
122
160
  let requestCounter = 0
123
161
  const streamTerminal = request.streamTerminal === true
162
+ const requestedModel = typeof request.model === 'string' ? request.model : ''
124
163
  const loopRepeatThreshold = Number.isFinite(Number(request.loopRepeatThreshold))
125
164
  ? Number(request.loopRepeatThreshold)
126
165
  : 12
@@ -294,6 +333,7 @@ async function run() {
294
333
  activeToolName = String(data.toolName ?? '')
295
334
  activeToolStartedAt = Date.now()
296
335
  const target = extractToolTarget(data.toolName, data.args)
336
+ const shellCommand = data.toolName === 'bash' ? extractShellCommand(data.args) : ''
297
337
  if (signature === lastToolSignature) {
298
338
  repeatedToolCount += 1
299
339
  } else {
@@ -324,6 +364,9 @@ async function run() {
324
364
  }
325
365
 
326
366
  writeLive(`[PI tool:start] ${data.toolName}${suffix}\n`)
367
+ if (data.toolName === 'bash' && isLargeShellRead(shellCommand)) {
368
+ writeLive('[PI warning] large bash file read detected; prefer read or a smaller exact window to avoid truncated context.\n')
369
+ }
327
370
  }
328
371
 
329
372
  if (data.type === 'tool_execution_end') {
@@ -416,6 +459,9 @@ async function run() {
416
459
  await waitForAgentEnd()
417
460
 
418
461
  if (heartbeatTimedOut) {
462
+ const toolCalls = events.filter((event) => event.type === 'tool_execution_start').length
463
+ const toolErrors = events.filter((event) => event.type === 'tool_execution_end' && event.isError).length
464
+ const messageUpdates = events.filter((event) => event.type === 'message_update').length
419
465
  console.log(JSON.stringify({
420
466
  sessionId: request.sessionId ?? '',
421
467
  sessionFile: request.sessionFile ?? '',
@@ -438,6 +484,15 @@ async function run() {
438
484
  continueAccepted ? 'continue_accepted=true' : '',
439
485
  continueRejected ? 'continue_rejected=true' : '',
440
486
  ].join(' '),
487
+ role: '',
488
+ model: requestedModel,
489
+ toolCalls,
490
+ toolErrors,
491
+ messageUpdates,
492
+ stopReason: '',
493
+ loopDetected: false,
494
+ loopSignature: '',
495
+ terminalReason: 'heartbeat_timeout',
441
496
  }))
442
497
  return
443
498
  }
@@ -464,6 +519,15 @@ async function run() {
464
519
  : assistantError !== '' || (assistantText === '' && toolCalls === 0 && messageUpdates === 0)
465
520
  ? 'failed'
466
521
  : 'success'
522
+ const terminalReason = loopDetected
523
+ ? 'loop_detected'
524
+ : assistantError !== ''
525
+ ? 'assistant_error'
526
+ : assistantStopReason === 'length'
527
+ ? 'assistant_stop_length'
528
+ : status === 'failed'
529
+ ? 'empty_agent_turn'
530
+ : 'agent_completed'
467
531
  const notes = [
468
532
  `PI session ${state.data.sessionId}`,
469
533
  `pi_pid=${child.pid ?? 'unknown'}`,
@@ -494,6 +558,15 @@ async function run() {
494
558
  status,
495
559
  output,
496
560
  notes,
561
+ role: '',
562
+ model: requestedModel,
563
+ toolCalls,
564
+ toolErrors,
565
+ messageUpdates,
566
+ stopReason: assistantStopReason,
567
+ loopDetected,
568
+ loopSignature,
569
+ terminalReason,
497
570
  }))
498
571
  } finally {
499
572
  if (heartbeatInterval) {