@windsland52/maa-log-tools 1.2.1 → 1.2.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/README.md CHANGED
@@ -71,7 +71,7 @@ process-start-bounded interval contains the timestamp; otherwise it returns `nul
71
71
 
72
72
  ## Runtime inspection
73
73
 
74
- `buildRuntimeInspection(kernelOutput, frameworkExtraction, sourceSegments?)` emits
74
+ `buildRuntimeInspection(kernelOutput, frameworkExtraction, sourceSegments)` emits
75
75
  `mla-runtime-inspection/v1`. It nests task executions under their runtime session and keeps three
76
76
  different semantics separate:
77
77
 
@@ -61,6 +61,15 @@ export interface RecognitionOccurrenceSample {
61
61
  end: RuntimeEvidencePosition;
62
62
  };
63
63
  }
64
+ export interface RepeatedNodeOccurrenceSample {
65
+ pattern: string[];
66
+ firstSeenAt: string;
67
+ lastSeenAt: string;
68
+ repeatCount: number;
69
+ durationMs: number;
70
+ termination: 'left_pattern' | 'task_ended' | 'still_repeating_at_log_end';
71
+ evidence: RuntimeEvidencePosition;
72
+ }
64
73
  export interface RecognitionActivitySignal extends RuntimeScope {
65
74
  signalId: string;
66
75
  kind: 'recognition_activity';
@@ -116,24 +125,9 @@ export interface RepeatedNodeSequenceSignal extends RuntimeScope {
116
125
  stillRepeatingAtLogEnd: number;
117
126
  };
118
127
  representatives: {
119
- first: {
120
- firstSeenAt: string;
121
- lastSeenAt: string;
122
- repeatCount: number;
123
- evidence: RuntimeEvidencePosition;
124
- };
125
- longest: {
126
- firstSeenAt: string;
127
- lastSeenAt: string;
128
- repeatCount: number;
129
- evidence: RuntimeEvidencePosition;
130
- };
131
- last: {
132
- firstSeenAt: string;
133
- lastSeenAt: string;
134
- repeatCount: number;
135
- evidence: RuntimeEvidencePosition;
136
- };
128
+ first: RepeatedNodeOccurrenceSample;
129
+ longest: RepeatedNodeOccurrenceSample;
130
+ last: RepeatedNodeOccurrenceSample;
137
131
  };
138
132
  detector: {
139
133
  name: 'repeated-completed-node-sequence';
@@ -395,21 +395,30 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
395
395
  const last = completed[lastIndex];
396
396
  if (!first || !last)
397
397
  continue;
398
- const reachesEnd = lastIndex === completed.length - 1;
399
398
  const rawPattern = completed.slice(repeated.start, repeated.start + repeated.length).map(node => node.name);
400
399
  const pattern = repeated.length === 1 ? rawPattern : canonicalCycle(rawPattern);
401
400
  const kind = repeated.length === 1 ? 'repeated_node' : 'repeated_node_cycle';
402
401
  const key = JSON.stringify([kind, pattern]);
403
402
  const group = repetitionGroups.get(key) ?? [];
403
+ const timelineLastIndex = timeline.findIndex(item => item.nodeInfo === last);
404
+ const trailing = timelineLastIndex < 0 ? [] : timeline.slice(timelineLastIndex + 1);
405
+ const reachesCompletedEnd = lastIndex === completed.length - 1;
406
+ const continuesAtLogEnd = reachesCompletedEnd
407
+ && task.status === 'running'
408
+ && trailing.every((item, offset) => (item.nodeInfo.status === 'running'
409
+ && item.nodeInfo.name === rawPattern[offset % rawPattern.length]));
410
+ const taskEndedAtPattern = reachesCompletedEnd
411
+ && timelineLastIndex === timeline.length - 1
412
+ && task.status !== 'running';
404
413
  group.push({
405
414
  pattern,
406
415
  repeatCount: repeated.count,
407
416
  durationMs: elapsed(first.ts, last.end_ts ?? last.ts) ?? 0,
408
417
  firstSeenAt: first.ts,
409
418
  lastSeenAt: last.end_ts ?? last.ts,
410
- termination: reachesEnd
411
- ? task.status === 'running' ? 'still_repeating_at_log_end' : 'task_ended'
412
- : 'left_pattern',
419
+ termination: continuesAtLogEnd
420
+ ? 'still_repeating_at_log_end'
421
+ : taskEndedAtPattern ? 'task_ended' : 'left_pattern',
413
422
  evidence: evidenceAt(evidenceIndex, first.ts),
414
423
  });
415
424
  repetitionGroups.set(key, group);
@@ -429,8 +438,6 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
429
438
  const totalRepeatCount = group.reduce((sum, item) => sum + item.repeatCount, 0);
430
439
  const maximumRepeatCount = Math.max(...group.map(item => item.repeatCount));
431
440
  const repetitionReasons = [];
432
- if (terminations.leftPattern > 0)
433
- repetitionReasons.push('incomplete_repetition');
434
441
  if (terminations.stillRepeatingAtLogEnd > 0)
435
442
  repetitionReasons.push('still_repeating_at_log_end');
436
443
  if (maximumRepeatCount >= 10 || totalRepeatCount >= 20)
@@ -483,8 +490,9 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
483
490
  const visionImages = imageSets.flatMap(set => set.vision);
484
491
  const ownFailures = failures.filter(failure => directFailureIds.includes(failure.failureId));
485
492
  const ownSignals = signals.filter(signal => signalIds.includes(signal.signalId));
486
- const recognitionActivity = ownSignals
487
- .filter((signal) => (signal.kind === 'recognition_activity'))
493
+ const recognitionSignals = ownSignals
494
+ .filter((signal) => (signal.kind === 'recognition_activity'));
495
+ const recognitionActivity = [...recognitionSignals]
488
496
  .sort((left, right) => (right.unsuccessfulAttempts.maximum - left.unsuccessfulAttempts.maximum
489
497
  || right.occurrenceCount - left.occurrenceCount))
490
498
  .slice(0, 5)
@@ -519,7 +527,7 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
519
527
  const statuses = new Set(attempts.map(attempt => attempt.status));
520
528
  return statuses.has('failed') && statuses.has('success');
521
529
  }).length,
522
- recognitionActivityGroups: recognitionActivity.length,
530
+ recognitionActivityGroups: recognitionSignals.length,
523
531
  maximumRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map(attempts => attempts.length)),
524
532
  maximumUnsuccessfulRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map(attempts => attempts.filter(attempt => attempt.status === 'failed').length)),
525
533
  actionAttempts: timeline.filter(item => item.nodeInfo.action_details != null).length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windsland52/maa-log-tools",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "bin": {
@@ -43,9 +43,9 @@
43
43
  "dependencies": {
44
44
  "fflate": "^0.8.2",
45
45
  "@windsland52/maa-log-adapter": "1.0.1",
46
+ "@windsland52/maa-log-runtime": "1.0.1",
46
47
  "@windsland52/maa-log-kernel": "1.0.1",
47
- "@windsland52/maa-log-parser": "1.0.1",
48
- "@windsland52/maa-log-runtime": "1.0.1"
48
+ "@windsland52/maa-log-parser": "1.0.1"
49
49
  },
50
50
  "engines": {
51
51
  "node": ">=24.0.0"