github-issue-tower-defence-management 1.140.0 → 1.140.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/.github/workflows/umino-project.yml +2 -1
- package/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/bin/adapter/repositories/FileSystemSessionOutputActivityRepository.js +6 -40
- package/bin/adapter/repositories/FileSystemSessionOutputActivityRepository.js.map +1 -1
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +29 -51
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +33 -3
- package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.test.ts +24 -11
- package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts +8 -50
- package/src/domain/entities/LiveSessionActivitySnapshot.ts +0 -1
- package/src/domain/entities/LiveSessionOutputActivity.ts +0 -1
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +127 -122
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +33 -62
- package/types/adapter/repositories/FileSystemSessionOutputActivityRepository.d.ts +1 -1
- package/types/adapter/repositories/FileSystemSessionOutputActivityRepository.d.ts.map +1 -1
- package/types/domain/entities/LiveSessionActivitySnapshot.d.ts +0 -1
- package/types/domain/entities/LiveSessionActivitySnapshot.d.ts.map +1 -1
- package/types/domain/entities/LiveSessionOutputActivity.d.ts +0 -1
- package/types/domain/entities/LiveSessionOutputActivity.d.ts.map +1 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +0 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
|
@@ -21,54 +21,34 @@ const parseEpochMilliseconds = (timestamp: string | null): number | null => {
|
|
|
21
21
|
return Number.isNaN(parsed) ? null : parsed;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const readContentBlocks = (
|
|
25
|
-
message: Record<string, unknown>,
|
|
26
|
-
): Record<string, unknown>[] => {
|
|
27
|
-
const content = message.content;
|
|
28
|
-
if (!Array.isArray(content)) {
|
|
29
|
-
return [];
|
|
30
|
-
}
|
|
31
|
-
return content.filter(isRecord);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
type TranscriptActivity = {
|
|
35
|
-
lastAssistantOutputEpochSeconds: number | null;
|
|
36
|
-
hasInProgressToolCall: boolean;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
24
|
export class FileSystemSessionOutputActivityRepository implements SessionOutputActivityRepository {
|
|
40
25
|
listSessionOutputActivities = async (
|
|
41
26
|
transcriptPathBySessionName: Map<string, string>,
|
|
42
27
|
): Promise<LiveSessionOutputActivity[]> => {
|
|
43
28
|
const activities: LiveSessionOutputActivity[] = [];
|
|
44
29
|
for (const [sessionName, transcriptPath] of transcriptPathBySessionName) {
|
|
45
|
-
const
|
|
46
|
-
this.
|
|
30
|
+
const lastAssistantOutputEpochSeconds =
|
|
31
|
+
this.readLastAssistantOutputEpochSeconds(transcriptPath);
|
|
47
32
|
if (lastAssistantOutputEpochSeconds !== null) {
|
|
48
33
|
activities.push({
|
|
49
34
|
sessionName,
|
|
50
35
|
lastOutputEpochSeconds: lastAssistantOutputEpochSeconds,
|
|
51
|
-
hasInProgressToolCall,
|
|
52
36
|
});
|
|
53
37
|
}
|
|
54
38
|
}
|
|
55
39
|
return activities;
|
|
56
40
|
};
|
|
57
41
|
|
|
58
|
-
private
|
|
42
|
+
private readLastAssistantOutputEpochSeconds = (
|
|
59
43
|
transcriptPath: string,
|
|
60
|
-
):
|
|
44
|
+
): number | null => {
|
|
61
45
|
let content: string;
|
|
62
46
|
try {
|
|
63
47
|
content = fs.readFileSync(transcriptPath, 'utf8');
|
|
64
48
|
} catch {
|
|
65
|
-
return
|
|
66
|
-
lastAssistantOutputEpochSeconds: null,
|
|
67
|
-
hasInProgressToolCall: false,
|
|
68
|
-
};
|
|
49
|
+
return null;
|
|
69
50
|
}
|
|
70
51
|
let lastAssistantOutputEpochMs: number | null = null;
|
|
71
|
-
const pendingToolUseIds = new Set<string>();
|
|
72
52
|
for (const line of content.split('\n')) {
|
|
73
53
|
const trimmed = line.trim();
|
|
74
54
|
if (trimmed.length === 0) {
|
|
@@ -83,37 +63,15 @@ export class FileSystemSessionOutputActivityRepository implements SessionOutputA
|
|
|
83
63
|
if (!isRecord(parsed)) {
|
|
84
64
|
continue;
|
|
85
65
|
}
|
|
86
|
-
const message = parsed.message;
|
|
87
66
|
if (readString(parsed, 'type') === 'assistant') {
|
|
88
67
|
const epochMs = parseEpochMilliseconds(readString(parsed, 'timestamp'));
|
|
89
68
|
if (epochMs !== null) {
|
|
90
69
|
lastAssistantOutputEpochMs = epochMs;
|
|
91
70
|
}
|
|
92
71
|
}
|
|
93
|
-
if (!isRecord(message)) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
for (const block of readContentBlocks(message)) {
|
|
97
|
-
const blockType = readString(block, 'type');
|
|
98
|
-
if (blockType === 'tool_use') {
|
|
99
|
-
const toolUseId = readString(block, 'id');
|
|
100
|
-
if (toolUseId !== null) {
|
|
101
|
-
pendingToolUseIds.add(toolUseId);
|
|
102
|
-
}
|
|
103
|
-
} else if (blockType === 'tool_result') {
|
|
104
|
-
const toolUseId = readString(block, 'tool_use_id');
|
|
105
|
-
if (toolUseId !== null) {
|
|
106
|
-
pendingToolUseIds.delete(toolUseId);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
72
|
}
|
|
111
|
-
return
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
? null
|
|
115
|
-
: Math.floor(lastAssistantOutputEpochMs / 1000),
|
|
116
|
-
hasInProgressToolCall: pendingToolUseIds.size > 0,
|
|
117
|
-
};
|
|
73
|
+
return lastAssistantOutputEpochMs === null
|
|
74
|
+
? null
|
|
75
|
+
: Math.floor(lastAssistantOutputEpochMs / 1000);
|
|
118
76
|
};
|
|
119
77
|
}
|
|
@@ -8,7 +8,6 @@ export type SubAgentActivity = {
|
|
|
8
8
|
export type LiveSessionActivitySnapshot = {
|
|
9
9
|
sessionName: string;
|
|
10
10
|
mainSilentSeconds: number | null;
|
|
11
|
-
mainHasInProgressToolCall: boolean;
|
|
12
11
|
subAgents: SubAgentActivity[];
|
|
13
12
|
unansweredOwnerCallAgeSeconds: number | null;
|
|
14
13
|
};
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
DEFAULT_NOTIFICATION_STAGGER_SECONDS,
|
|
11
11
|
DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS,
|
|
12
12
|
DEFAULT_HUB_TASK_STATUS_CACHE_TTL_SECONDS,
|
|
13
|
-
IN_PROGRESS_TOOL_CALL_MAX_SUPPRESS_SECONDS,
|
|
14
13
|
} from './NotifySilentLiveSessionsUseCase';
|
|
15
14
|
import { Issue } from '../entities/Issue';
|
|
16
15
|
import { LiveSessionProcessSnapshotProvider } from './adapter-interfaces/LiveSessionProcessSnapshotProvider';
|
|
@@ -268,7 +267,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
268
267
|
sessionName,
|
|
269
268
|
lastOutputEpochSeconds:
|
|
270
269
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
271
|
-
hasInProgressToolCall: false,
|
|
272
270
|
},
|
|
273
271
|
],
|
|
274
272
|
);
|
|
@@ -299,7 +297,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
299
297
|
sessionName: GITHUB_SESSION,
|
|
300
298
|
lastOutputEpochSeconds:
|
|
301
299
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
302
|
-
hasInProgressToolCall: false,
|
|
303
300
|
},
|
|
304
301
|
],
|
|
305
302
|
);
|
|
@@ -323,7 +320,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
323
320
|
sessionName: pullRequestSession,
|
|
324
321
|
lastOutputEpochSeconds:
|
|
325
322
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
326
|
-
hasInProgressToolCall: false,
|
|
327
323
|
},
|
|
328
324
|
],
|
|
329
325
|
);
|
|
@@ -335,7 +331,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
335
331
|
).toHaveBeenCalledWith(pullRequestSession, MAIN_STALLED_SECTION);
|
|
336
332
|
});
|
|
337
333
|
|
|
338
|
-
it('excludes a non-github-named session
|
|
334
|
+
it('excludes a non-github-named session that has no resolvable transcript so it is never notified', async () => {
|
|
339
335
|
mockSnapshotProvider.getSnapshot.mockResolvedValue(
|
|
340
336
|
snapshotWithSessions(['workbench']),
|
|
341
337
|
);
|
|
@@ -345,16 +341,15 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
345
341
|
sessionName: 'workbench',
|
|
346
342
|
lastOutputEpochSeconds:
|
|
347
343
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
348
|
-
hasInProgressToolCall: false,
|
|
349
344
|
},
|
|
350
345
|
],
|
|
351
346
|
);
|
|
352
347
|
|
|
353
348
|
await useCase.run(runParams());
|
|
354
349
|
|
|
355
|
-
expect(mockTranscriptResolver.resolveTranscriptPaths).toHaveBeenCalledWith(
|
|
356
|
-
|
|
357
|
-
);
|
|
350
|
+
expect(mockTranscriptResolver.resolveTranscriptPaths).toHaveBeenCalledWith([
|
|
351
|
+
sessionFor('workbench'),
|
|
352
|
+
]);
|
|
358
353
|
expect(
|
|
359
354
|
mockSubAgentActivityRepository.listSubAgentActivitiesBySessionName,
|
|
360
355
|
).toHaveBeenCalledWith([], new Map());
|
|
@@ -366,7 +361,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
366
361
|
).not.toHaveBeenCalled();
|
|
367
362
|
});
|
|
368
363
|
|
|
369
|
-
it('monitors
|
|
364
|
+
it('monitors the github-named session and excludes a non-github-named session that has no resolvable transcript', async () => {
|
|
370
365
|
mockSnapshotProvider.getSnapshot.mockResolvedValue(
|
|
371
366
|
snapshotWithSessions([GITHUB_SESSION, 'orchestrator']),
|
|
372
367
|
);
|
|
@@ -379,13 +374,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
379
374
|
sessionName: GITHUB_SESSION,
|
|
380
375
|
lastOutputEpochSeconds:
|
|
381
376
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
382
|
-
hasInProgressToolCall: false,
|
|
383
377
|
},
|
|
384
378
|
{
|
|
385
379
|
sessionName: 'orchestrator',
|
|
386
380
|
lastOutputEpochSeconds:
|
|
387
381
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
388
|
-
hasInProgressToolCall: false,
|
|
389
382
|
},
|
|
390
383
|
],
|
|
391
384
|
);
|
|
@@ -394,6 +387,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
394
387
|
|
|
395
388
|
expect(mockTranscriptResolver.resolveTranscriptPaths).toHaveBeenCalledWith([
|
|
396
389
|
sessionFor(GITHUB_SESSION),
|
|
390
|
+
sessionFor('orchestrator'),
|
|
397
391
|
]);
|
|
398
392
|
expect(
|
|
399
393
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
@@ -501,7 +495,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
501
495
|
sessionName: GITHUB_SESSION,
|
|
502
496
|
lastOutputEpochSeconds:
|
|
503
497
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS + 1,
|
|
504
|
-
hasInProgressToolCall: false,
|
|
505
498
|
},
|
|
506
499
|
],
|
|
507
500
|
);
|
|
@@ -529,7 +522,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
529
522
|
sessionName: GITHUB_SESSION,
|
|
530
523
|
lastOutputEpochSeconds:
|
|
531
524
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
532
|
-
hasInProgressToolCall: false,
|
|
533
525
|
},
|
|
534
526
|
],
|
|
535
527
|
);
|
|
@@ -550,7 +542,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
550
542
|
).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
|
|
551
543
|
});
|
|
552
544
|
|
|
553
|
-
it('
|
|
545
|
+
it('sends the main stalled section for a session silent past the threshold regardless of any in-progress tool call, because apparent busyness never suppresses the reminder', async () => {
|
|
554
546
|
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
555
547
|
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
556
548
|
[
|
|
@@ -558,34 +550,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
558
550
|
sessionName: GITHUB_SESSION,
|
|
559
551
|
lastOutputEpochSeconds:
|
|
560
552
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
561
|
-
hasInProgressToolCall: true,
|
|
562
|
-
},
|
|
563
|
-
],
|
|
564
|
-
);
|
|
565
|
-
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName.mockResolvedValue(
|
|
566
|
-
new Map<string, number>(),
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
await useCase.run(runParams());
|
|
570
|
-
|
|
571
|
-
expect(
|
|
572
|
-
mockMessageComposer.composeMainStalledSection,
|
|
573
|
-
).not.toHaveBeenCalled();
|
|
574
|
-
expect(
|
|
575
|
-
mockNotificationRepository.sendSelfCheckNotification,
|
|
576
|
-
).not.toHaveBeenCalled();
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
it('sends the main stalled section when the in-progress tool call has been pending longer than the suppression bound', async () => {
|
|
580
|
-
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
581
|
-
const pendingToolCallAgeSeconds =
|
|
582
|
-
IN_PROGRESS_TOOL_CALL_MAX_SUPPRESS_SECONDS + 60 * 60;
|
|
583
|
-
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
584
|
-
[
|
|
585
|
-
{
|
|
586
|
-
sessionName: GITHUB_SESSION,
|
|
587
|
-
lastOutputEpochSeconds: nowEpochSeconds - pendingToolCallAgeSeconds,
|
|
588
|
-
hasInProgressToolCall: true,
|
|
589
553
|
},
|
|
590
554
|
],
|
|
591
555
|
);
|
|
@@ -596,7 +560,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
596
560
|
await useCase.run(runParams());
|
|
597
561
|
|
|
598
562
|
expect(mockMessageComposer.composeMainStalledSection).toHaveBeenCalledWith(
|
|
599
|
-
|
|
563
|
+
DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
600
564
|
);
|
|
601
565
|
expect(
|
|
602
566
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
@@ -611,7 +575,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
611
575
|
sessionName: GITHUB_SESSION,
|
|
612
576
|
lastOutputEpochSeconds:
|
|
613
577
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS + 1,
|
|
614
|
-
hasInProgressToolCall: false,
|
|
615
578
|
},
|
|
616
579
|
],
|
|
617
580
|
);
|
|
@@ -729,6 +692,117 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
729
692
|
).not.toHaveBeenCalled();
|
|
730
693
|
});
|
|
731
694
|
|
|
695
|
+
describe('role-named resident leader and PM agent sessions', () => {
|
|
696
|
+
it('reminds a role-named leader agent session that has a resolvable transcript and is silent past the threshold', async () => {
|
|
697
|
+
setupLiveInteractiveSession('secretary');
|
|
698
|
+
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
699
|
+
[
|
|
700
|
+
{
|
|
701
|
+
sessionName: 'secretary',
|
|
702
|
+
lastOutputEpochSeconds:
|
|
703
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
704
|
+
},
|
|
705
|
+
],
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
await useCase.run(runParams());
|
|
709
|
+
|
|
710
|
+
expect(
|
|
711
|
+
mockMessageComposer.composeMainStalledSection,
|
|
712
|
+
).toHaveBeenCalledWith(DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS);
|
|
713
|
+
expect(
|
|
714
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
715
|
+
).toHaveBeenCalledWith('secretary', MAIN_STALLED_SECTION);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('excludes a non-agent interactive session with no resolvable transcript (for example a viewer named sso_login)', async () => {
|
|
719
|
+
mockSnapshotProvider.getSnapshot.mockResolvedValue(
|
|
720
|
+
snapshotWithSessions(['sso_login']),
|
|
721
|
+
);
|
|
722
|
+
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
723
|
+
[
|
|
724
|
+
{
|
|
725
|
+
sessionName: 'sso_login',
|
|
726
|
+
lastOutputEpochSeconds:
|
|
727
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
728
|
+
},
|
|
729
|
+
],
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
await useCase.run(runParams());
|
|
733
|
+
|
|
734
|
+
expect(
|
|
735
|
+
mockTranscriptResolver.resolveTranscriptPaths,
|
|
736
|
+
).toHaveBeenCalledWith([sessionFor('sso_login')]);
|
|
737
|
+
expect(
|
|
738
|
+
mockMessageComposer.composeMainStalledSection,
|
|
739
|
+
).not.toHaveBeenCalled();
|
|
740
|
+
expect(
|
|
741
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
742
|
+
).not.toHaveBeenCalled();
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it('reminds both a github-named session and a role-named leader session in the same cycle', async () => {
|
|
746
|
+
mockSnapshotProvider.getSnapshot.mockResolvedValue(
|
|
747
|
+
snapshotWithSessions([GITHUB_SESSION, 'app']),
|
|
748
|
+
);
|
|
749
|
+
mockTranscriptResolver.resolveTranscriptPaths.mockReturnValue(
|
|
750
|
+
transcriptMapFor([GITHUB_SESSION, 'app']),
|
|
751
|
+
);
|
|
752
|
+
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
753
|
+
[
|
|
754
|
+
{
|
|
755
|
+
sessionName: GITHUB_SESSION,
|
|
756
|
+
lastOutputEpochSeconds:
|
|
757
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
sessionName: 'app',
|
|
761
|
+
lastOutputEpochSeconds:
|
|
762
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
763
|
+
},
|
|
764
|
+
],
|
|
765
|
+
);
|
|
766
|
+
|
|
767
|
+
await useCase.run(runParams());
|
|
768
|
+
|
|
769
|
+
expect(
|
|
770
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
771
|
+
).toHaveBeenCalledTimes(2);
|
|
772
|
+
expect(
|
|
773
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
774
|
+
).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
|
|
775
|
+
expect(
|
|
776
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
777
|
+
).toHaveBeenCalledWith('app', MAIN_STALLED_SECTION);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('parks a role-named leader session while its latest owner call is unanswered', async () => {
|
|
781
|
+
setupLiveInteractiveSession('uminopm');
|
|
782
|
+
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
783
|
+
[
|
|
784
|
+
{
|
|
785
|
+
sessionName: 'uminopm',
|
|
786
|
+
lastOutputEpochSeconds:
|
|
787
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
788
|
+
},
|
|
789
|
+
],
|
|
790
|
+
);
|
|
791
|
+
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName.mockResolvedValue(
|
|
792
|
+
new Map([['uminopm', nowEpochSeconds - 60]]),
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
await useCase.run(runParams());
|
|
796
|
+
|
|
797
|
+
expect(
|
|
798
|
+
mockMessageComposer.composeMainStalledSection,
|
|
799
|
+
).not.toHaveBeenCalled();
|
|
800
|
+
expect(
|
|
801
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
802
|
+
).not.toHaveBeenCalled();
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
|
|
732
806
|
it('notifies a persistent stall only once per silent episode instead of every cycle', async () => {
|
|
733
807
|
wireStatefulNotifiedLatch();
|
|
734
808
|
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
@@ -738,7 +812,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
738
812
|
sessionName: GITHUB_SESSION,
|
|
739
813
|
lastOutputEpochSeconds:
|
|
740
814
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
741
|
-
hasInProgressToolCall: false,
|
|
742
815
|
},
|
|
743
816
|
],
|
|
744
817
|
);
|
|
@@ -763,14 +836,12 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
763
836
|
sessionName: GITHUB_SESSION,
|
|
764
837
|
lastOutputEpochSeconds:
|
|
765
838
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
766
|
-
hasInProgressToolCall: false,
|
|
767
839
|
},
|
|
768
840
|
];
|
|
769
841
|
const active = [
|
|
770
842
|
{
|
|
771
843
|
sessionName: GITHUB_SESSION,
|
|
772
844
|
lastOutputEpochSeconds: nowEpochSeconds,
|
|
773
|
-
hasInProgressToolCall: false,
|
|
774
845
|
},
|
|
775
846
|
];
|
|
776
847
|
|
|
@@ -808,7 +879,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
808
879
|
sessionName: GITHUB_SESSION,
|
|
809
880
|
lastOutputEpochSeconds:
|
|
810
881
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
811
|
-
hasInProgressToolCall: false,
|
|
812
882
|
},
|
|
813
883
|
],
|
|
814
884
|
);
|
|
@@ -898,7 +968,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
898
968
|
});
|
|
899
969
|
});
|
|
900
970
|
|
|
901
|
-
describe('fire-once latch
|
|
971
|
+
describe('fire-once latch', () => {
|
|
902
972
|
const setupSilentGithubSession = (): void => {
|
|
903
973
|
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
904
974
|
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
@@ -907,19 +977,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
907
977
|
sessionName: GITHUB_SESSION,
|
|
908
978
|
lastOutputEpochSeconds:
|
|
909
979
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
910
|
-
hasInProgressToolCall: false,
|
|
911
980
|
},
|
|
912
981
|
],
|
|
913
982
|
);
|
|
914
983
|
};
|
|
915
984
|
|
|
916
|
-
const idleSubAgent = (label: string): SubAgentActivity => ({
|
|
917
|
-
label,
|
|
918
|
-
silentSeconds: DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS,
|
|
919
|
-
runningSeconds: 60,
|
|
920
|
-
waitingOnExternalProcess: false,
|
|
921
|
-
});
|
|
922
|
-
|
|
923
985
|
it('loads the fire-once latch using the configured recency window', async () => {
|
|
924
986
|
setupSilentGithubSession();
|
|
925
987
|
|
|
@@ -973,69 +1035,17 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
973
1035
|
).toHaveBeenCalledWith({ sessionNames: [], now });
|
|
974
1036
|
});
|
|
975
1037
|
|
|
976
|
-
it('
|
|
977
|
-
|
|
978
|
-
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
979
|
-
[
|
|
980
|
-
{
|
|
981
|
-
sessionName: GITHUB_SESSION,
|
|
982
|
-
lastOutputEpochSeconds: nowEpochSeconds,
|
|
983
|
-
hasInProgressToolCall: true,
|
|
984
|
-
},
|
|
985
|
-
],
|
|
986
|
-
);
|
|
987
|
-
mockSubAgentActivityRepository.listSubAgentActivitiesBySessionName.mockResolvedValue(
|
|
988
|
-
new Map([[GITHUB_SESSION, [idleSubAgent('sub-process-1')]]]),
|
|
989
|
-
);
|
|
1038
|
+
it('sends the reminder for a silent session even when its main REPL is mid-turn, because apparent busyness no longer defers delivery', async () => {
|
|
1039
|
+
setupSilentGithubSession();
|
|
990
1040
|
|
|
991
1041
|
await useCase.run(runParams());
|
|
992
1042
|
|
|
993
|
-
expect(mockMessageComposer.composeSubAgentSection).toHaveBeenCalled();
|
|
994
1043
|
expect(
|
|
995
1044
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
996
|
-
).
|
|
1045
|
+
).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
|
|
997
1046
|
expect(
|
|
998
1047
|
mockNotifiedStateRepository.saveNotifiedSessionNames,
|
|
999
|
-
).toHaveBeenCalledWith({ sessionNames: [], now });
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
it('delivers the deferred reminder once the main REPL becomes input-ready', async () => {
|
|
1003
|
-
wireStatefulNotifiedLatch();
|
|
1004
|
-
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
1005
|
-
mockSubAgentActivityRepository.listSubAgentActivitiesBySessionName.mockResolvedValue(
|
|
1006
|
-
new Map([[GITHUB_SESSION, [idleSubAgent('sub-process-1')]]]),
|
|
1007
|
-
);
|
|
1008
|
-
|
|
1009
|
-
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
1010
|
-
[
|
|
1011
|
-
{
|
|
1012
|
-
sessionName: GITHUB_SESSION,
|
|
1013
|
-
lastOutputEpochSeconds: nowEpochSeconds,
|
|
1014
|
-
hasInProgressToolCall: true,
|
|
1015
|
-
},
|
|
1016
|
-
],
|
|
1017
|
-
);
|
|
1018
|
-
await useCase.run(runParams());
|
|
1019
|
-
expect(
|
|
1020
|
-
mockNotificationRepository.sendSelfCheckNotification,
|
|
1021
|
-
).not.toHaveBeenCalled();
|
|
1022
|
-
|
|
1023
|
-
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
1024
|
-
[
|
|
1025
|
-
{
|
|
1026
|
-
sessionName: GITHUB_SESSION,
|
|
1027
|
-
lastOutputEpochSeconds: nowEpochSeconds,
|
|
1028
|
-
hasInProgressToolCall: false,
|
|
1029
|
-
},
|
|
1030
|
-
],
|
|
1031
|
-
);
|
|
1032
|
-
await useCase.run(runParams());
|
|
1033
|
-
expect(
|
|
1034
|
-
mockNotificationRepository.sendSelfCheckNotification,
|
|
1035
|
-
).toHaveBeenCalledTimes(1);
|
|
1036
|
-
expect(
|
|
1037
|
-
mockNotificationRepository.sendSelfCheckNotification,
|
|
1038
|
-
).toHaveBeenCalledWith(GITHUB_SESSION, SUBAGENT_SECTION);
|
|
1048
|
+
).toHaveBeenCalledWith({ sessionNames: [GITHUB_SESSION], now });
|
|
1039
1049
|
});
|
|
1040
1050
|
});
|
|
1041
1051
|
|
|
@@ -1218,13 +1228,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
1218
1228
|
sessionName: GITHUB_SESSION_ALPHA,
|
|
1219
1229
|
lastOutputEpochSeconds:
|
|
1220
1230
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1221
|
-
hasInProgressToolCall: false,
|
|
1222
1231
|
},
|
|
1223
1232
|
{
|
|
1224
1233
|
sessionName: GITHUB_SESSION_BRAVO,
|
|
1225
1234
|
lastOutputEpochSeconds:
|
|
1226
1235
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1227
|
-
hasInProgressToolCall: false,
|
|
1228
1236
|
},
|
|
1229
1237
|
],
|
|
1230
1238
|
);
|
|
@@ -1260,7 +1268,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
1260
1268
|
sessionName: GITHUB_SESSION,
|
|
1261
1269
|
lastOutputEpochSeconds:
|
|
1262
1270
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1263
|
-
hasInProgressToolCall: false,
|
|
1264
1271
|
},
|
|
1265
1272
|
],
|
|
1266
1273
|
);
|
|
@@ -1326,15 +1333,15 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
1326
1333
|
).not.toHaveBeenCalled();
|
|
1327
1334
|
});
|
|
1328
1335
|
|
|
1329
|
-
it('
|
|
1330
|
-
setupSilentMainSession('
|
|
1336
|
+
it('does not consult the hub-task resolver for a role-named leader session that has no hub task URL, and still reminds it', async () => {
|
|
1337
|
+
setupSilentMainSession('tdpm-cli');
|
|
1331
1338
|
|
|
1332
1339
|
await useCase.run(runParams({ activeHubTaskStatus: ACTIVE_STATUS }));
|
|
1333
1340
|
|
|
1334
1341
|
expect(mockHubTaskStatusResolver.getIssueByUrl).not.toHaveBeenCalled();
|
|
1335
1342
|
expect(
|
|
1336
1343
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
1337
|
-
).
|
|
1344
|
+
).toHaveBeenCalledWith('tdpm-cli', MAIN_STALLED_SECTION);
|
|
1338
1345
|
});
|
|
1339
1346
|
|
|
1340
1347
|
it('does not call the resolver at all when the active status is unconfigured', async () => {
|
|
@@ -1802,13 +1809,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
1802
1809
|
sessionName: GITHUB_SESSION_ALPHA,
|
|
1803
1810
|
lastOutputEpochSeconds:
|
|
1804
1811
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1805
|
-
hasInProgressToolCall: false,
|
|
1806
1812
|
},
|
|
1807
1813
|
{
|
|
1808
1814
|
sessionName: GITHUB_SESSION_BRAVO,
|
|
1809
1815
|
lastOutputEpochSeconds:
|
|
1810
1816
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1811
|
-
hasInProgressToolCall: false,
|
|
1812
1817
|
},
|
|
1813
1818
|
],
|
|
1814
1819
|
);
|