github-issue-tower-defence-management 1.139.1 → 1.140.1
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/CHANGELOG.md +14 -0
- package/README.md +6 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +43 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/tokenExhaustionHandover.js +31 -0
- package/bin/adapter/entry-points/handlers/tokenExhaustionHandover.js.map +1 -0
- package/bin/adapter/repositories/FileHandoverStateRepository.js +99 -0
- package/bin/adapter/repositories/FileHandoverStateRepository.js.map +1 -0
- package/bin/adapter/repositories/FileSystemSessionOutputActivityRepository.js +6 -40
- package/bin/adapter/repositories/FileSystemSessionOutputActivityRepository.js.map +1 -1
- package/bin/adapter/repositories/NodeProcessSignalRepository.js +44 -0
- package/bin/adapter/repositories/NodeProcessSignalRepository.js.map +1 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js +67 -1
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -1
- package/bin/adapter/repositories/ProcClaudeHandoverSessionRepository.js +206 -0
- package/bin/adapter/repositories/ProcClaudeHandoverSessionRepository.js.map +1 -0
- package/bin/adapter/repositories/RateLimitSnapshotRepository.js +44 -0
- package/bin/adapter/repositories/RateLimitSnapshotRepository.js.map +1 -0
- package/bin/domain/entities/ClaudeHandoverSession.js +3 -0
- package/bin/domain/entities/ClaudeHandoverSession.js.map +1 -0
- package/bin/domain/entities/TokenExhaustionHandoverState.js +3 -0
- package/bin/domain/entities/TokenExhaustionHandoverState.js.map +1 -0
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +9 -46
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
- package/bin/domain/usecases/TokenExhaustionHandoverUseCase.js +255 -0
- package/bin/domain/usecases/TokenExhaustionHandoverUseCase.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/ProcessSignalRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/ProcessSignalRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +32 -0
- package/src/adapter/entry-points/handlers/tokenExhaustionHandover.ts +79 -0
- package/src/adapter/repositories/FileHandoverStateRepository.test.ts +114 -0
- package/src/adapter/repositories/FileHandoverStateRepository.ts +76 -0
- package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.test.ts +24 -11
- package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts +8 -50
- package/src/adapter/repositories/NodeProcessSignalRepository.test.ts +82 -0
- package/src/adapter/repositories/NodeProcessSignalRepository.ts +47 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +132 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.ts +100 -0
- package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.test.ts +194 -0
- package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.ts +208 -0
- package/src/adapter/repositories/RateLimitSnapshotRepository.ts +50 -0
- package/src/domain/entities/ClaudeHandoverSession.ts +11 -0
- package/src/domain/entities/LiveSessionActivitySnapshot.ts +0 -1
- package/src/domain/entities/LiveSessionOutputActivity.ts +0 -1
- package/src/domain/entities/TokenExhaustionHandoverState.ts +8 -0
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +7 -114
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +8 -53
- package/src/domain/usecases/TokenExhaustionHandoverUseCase.test.ts +696 -0
- package/src/domain/usecases/TokenExhaustionHandoverUseCase.ts +413 -0
- package/src/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.ts +5 -0
- package/src/domain/usecases/adapter-interfaces/ProcessSignalRepository.ts +5 -0
- package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +2 -0
- package/src/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.ts +6 -0
- package/src/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.ts +22 -0
- package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +2 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/tokenExhaustionHandover.d.ts +14 -0
- package/types/adapter/entry-points/handlers/tokenExhaustionHandover.d.ts.map +1 -0
- package/types/adapter/repositories/FileHandoverStateRepository.d.ts +11 -0
- package/types/adapter/repositories/FileHandoverStateRepository.d.ts.map +1 -0
- package/types/adapter/repositories/FileSystemSessionOutputActivityRepository.d.ts +1 -1
- package/types/adapter/repositories/FileSystemSessionOutputActivityRepository.d.ts.map +1 -1
- package/types/adapter/repositories/NodeProcessSignalRepository.d.ts +8 -0
- package/types/adapter/repositories/NodeProcessSignalRepository.d.ts.map +1 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +7 -1
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -1
- package/types/adapter/repositories/ProcClaudeHandoverSessionRepository.d.ts +13 -0
- package/types/adapter/repositories/ProcClaudeHandoverSessionRepository.d.ts.map +1 -0
- package/types/adapter/repositories/RateLimitSnapshotRepository.d.ts +9 -0
- package/types/adapter/repositories/RateLimitSnapshotRepository.d.ts.map +1 -0
- package/types/domain/entities/ClaudeHandoverSession.d.ts +10 -0
- package/types/domain/entities/ClaudeHandoverSession.d.ts.map +1 -0
- 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/entities/TokenExhaustionHandoverState.d.ts +8 -0
- package/types/domain/entities/TokenExhaustionHandoverState.d.ts.map +1 -0
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +0 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
- package/types/domain/usecases/TokenExhaustionHandoverUseCase.d.ts +50 -0
- package/types/domain/usecases/TokenExhaustionHandoverUseCase.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.d.ts +5 -0
- package/types/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/ProcessSignalRepository.d.ts +6 -0
- package/types/domain/usecases/adapter-interfaces/ProcessSignalRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts +2 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.d.ts +6 -0
- package/types/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.d.ts +21 -0
- package/types/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.d.ts.map +1 -0
|
@@ -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
|
);
|
|
@@ -345,7 +341,6 @@ 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
|
);
|
|
@@ -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
|
);
|
|
@@ -501,7 +494,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
501
494
|
sessionName: GITHUB_SESSION,
|
|
502
495
|
lastOutputEpochSeconds:
|
|
503
496
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS + 1,
|
|
504
|
-
hasInProgressToolCall: false,
|
|
505
497
|
},
|
|
506
498
|
],
|
|
507
499
|
);
|
|
@@ -529,7 +521,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
529
521
|
sessionName: GITHUB_SESSION,
|
|
530
522
|
lastOutputEpochSeconds:
|
|
531
523
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
532
|
-
hasInProgressToolCall: false,
|
|
533
524
|
},
|
|
534
525
|
],
|
|
535
526
|
);
|
|
@@ -550,7 +541,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
550
541
|
).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
|
|
551
542
|
});
|
|
552
543
|
|
|
553
|
-
it('
|
|
544
|
+
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
545
|
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
555
546
|
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
556
547
|
[
|
|
@@ -558,34 +549,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
558
549
|
sessionName: GITHUB_SESSION,
|
|
559
550
|
lastOutputEpochSeconds:
|
|
560
551
|
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
552
|
},
|
|
590
553
|
],
|
|
591
554
|
);
|
|
@@ -596,7 +559,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
596
559
|
await useCase.run(runParams());
|
|
597
560
|
|
|
598
561
|
expect(mockMessageComposer.composeMainStalledSection).toHaveBeenCalledWith(
|
|
599
|
-
|
|
562
|
+
DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
600
563
|
);
|
|
601
564
|
expect(
|
|
602
565
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
@@ -611,7 +574,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
611
574
|
sessionName: GITHUB_SESSION,
|
|
612
575
|
lastOutputEpochSeconds:
|
|
613
576
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS + 1,
|
|
614
|
-
hasInProgressToolCall: false,
|
|
615
577
|
},
|
|
616
578
|
],
|
|
617
579
|
);
|
|
@@ -738,7 +700,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
738
700
|
sessionName: GITHUB_SESSION,
|
|
739
701
|
lastOutputEpochSeconds:
|
|
740
702
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
741
|
-
hasInProgressToolCall: false,
|
|
742
703
|
},
|
|
743
704
|
],
|
|
744
705
|
);
|
|
@@ -763,14 +724,12 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
763
724
|
sessionName: GITHUB_SESSION,
|
|
764
725
|
lastOutputEpochSeconds:
|
|
765
726
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
766
|
-
hasInProgressToolCall: false,
|
|
767
727
|
},
|
|
768
728
|
];
|
|
769
729
|
const active = [
|
|
770
730
|
{
|
|
771
731
|
sessionName: GITHUB_SESSION,
|
|
772
732
|
lastOutputEpochSeconds: nowEpochSeconds,
|
|
773
|
-
hasInProgressToolCall: false,
|
|
774
733
|
},
|
|
775
734
|
];
|
|
776
735
|
|
|
@@ -808,7 +767,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
808
767
|
sessionName: GITHUB_SESSION,
|
|
809
768
|
lastOutputEpochSeconds:
|
|
810
769
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
811
|
-
hasInProgressToolCall: false,
|
|
812
770
|
},
|
|
813
771
|
],
|
|
814
772
|
);
|
|
@@ -898,7 +856,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
898
856
|
});
|
|
899
857
|
});
|
|
900
858
|
|
|
901
|
-
describe('fire-once latch
|
|
859
|
+
describe('fire-once latch', () => {
|
|
902
860
|
const setupSilentGithubSession = (): void => {
|
|
903
861
|
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
904
862
|
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
@@ -907,19 +865,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
907
865
|
sessionName: GITHUB_SESSION,
|
|
908
866
|
lastOutputEpochSeconds:
|
|
909
867
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
910
|
-
hasInProgressToolCall: false,
|
|
911
868
|
},
|
|
912
869
|
],
|
|
913
870
|
);
|
|
914
871
|
};
|
|
915
872
|
|
|
916
|
-
const idleSubAgent = (label: string): SubAgentActivity => ({
|
|
917
|
-
label,
|
|
918
|
-
silentSeconds: DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS,
|
|
919
|
-
runningSeconds: 60,
|
|
920
|
-
waitingOnExternalProcess: false,
|
|
921
|
-
});
|
|
922
|
-
|
|
923
873
|
it('loads the fire-once latch using the configured recency window', async () => {
|
|
924
874
|
setupSilentGithubSession();
|
|
925
875
|
|
|
@@ -973,69 +923,17 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
973
923
|
).toHaveBeenCalledWith({ sessionNames: [], now });
|
|
974
924
|
});
|
|
975
925
|
|
|
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
|
-
);
|
|
926
|
+
it('sends the reminder for a silent session even when its main REPL is mid-turn, because apparent busyness no longer defers delivery', async () => {
|
|
927
|
+
setupSilentGithubSession();
|
|
990
928
|
|
|
991
929
|
await useCase.run(runParams());
|
|
992
930
|
|
|
993
|
-
expect(mockMessageComposer.composeSubAgentSection).toHaveBeenCalled();
|
|
994
931
|
expect(
|
|
995
932
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
996
|
-
).
|
|
933
|
+
).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
|
|
997
934
|
expect(
|
|
998
935
|
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);
|
|
936
|
+
).toHaveBeenCalledWith({ sessionNames: [GITHUB_SESSION], now });
|
|
1039
937
|
});
|
|
1040
938
|
});
|
|
1041
939
|
|
|
@@ -1218,13 +1116,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
1218
1116
|
sessionName: GITHUB_SESSION_ALPHA,
|
|
1219
1117
|
lastOutputEpochSeconds:
|
|
1220
1118
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1221
|
-
hasInProgressToolCall: false,
|
|
1222
1119
|
},
|
|
1223
1120
|
{
|
|
1224
1121
|
sessionName: GITHUB_SESSION_BRAVO,
|
|
1225
1122
|
lastOutputEpochSeconds:
|
|
1226
1123
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1227
|
-
hasInProgressToolCall: false,
|
|
1228
1124
|
},
|
|
1229
1125
|
],
|
|
1230
1126
|
);
|
|
@@ -1260,7 +1156,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
1260
1156
|
sessionName: GITHUB_SESSION,
|
|
1261
1157
|
lastOutputEpochSeconds:
|
|
1262
1158
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1263
|
-
hasInProgressToolCall: false,
|
|
1264
1159
|
},
|
|
1265
1160
|
],
|
|
1266
1161
|
);
|
|
@@ -1802,13 +1697,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
1802
1697
|
sessionName: GITHUB_SESSION_ALPHA,
|
|
1803
1698
|
lastOutputEpochSeconds:
|
|
1804
1699
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1805
|
-
hasInProgressToolCall: false,
|
|
1806
1700
|
},
|
|
1807
1701
|
{
|
|
1808
1702
|
sessionName: GITHUB_SESSION_BRAVO,
|
|
1809
1703
|
lastOutputEpochSeconds:
|
|
1810
1704
|
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
1811
|
-
hasInProgressToolCall: false,
|
|
1812
1705
|
},
|
|
1813
1706
|
],
|
|
1814
1707
|
);
|
|
@@ -26,15 +26,6 @@ export const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS = 15 * 60;
|
|
|
26
26
|
export const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
|
|
27
27
|
export const DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS = 15 * 60;
|
|
28
28
|
export const DEFAULT_HUB_TASK_STATUS_CACHE_TTL_SECONDS = 5 * 60;
|
|
29
|
-
// Upper bound on how long an in-progress tool call suppresses the main-stall
|
|
30
|
-
// reminder. Set to 2 hours: comfortably above the ~1-hour maximum duration of
|
|
31
|
-
// the longest legitimate single tool call (the Monitor tool's own timeout
|
|
32
|
-
// ceiling), so every legitimate long-running tool call is still suppressed,
|
|
33
|
-
// while a session whose transcript tail has been an unanswered tool_use for
|
|
34
|
-
// longer than this — for example because the tool call itself hung and never
|
|
35
|
-
// returned — still receives the stall reminder instead of being suppressed
|
|
36
|
-
// forever.
|
|
37
|
-
export const IN_PROGRESS_TOOL_CALL_MAX_SUPPRESS_SECONDS = 2 * 60 * 60;
|
|
38
29
|
|
|
39
30
|
const GITHUB_ISSUE_OR_PULL_URL_PATTERN =
|
|
40
31
|
/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/(?:issues|pull)\/(\d+)$/;
|
|
@@ -71,7 +62,6 @@ type NotifyCandidate = {
|
|
|
71
62
|
sessionName: string;
|
|
72
63
|
message: string;
|
|
73
64
|
sectionLabels: string[];
|
|
74
|
-
mainInputBusy: boolean;
|
|
75
65
|
};
|
|
76
66
|
|
|
77
67
|
export class NotifySilentLiveSessionsUseCase {
|
|
@@ -212,20 +202,6 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
212
202
|
);
|
|
213
203
|
continue;
|
|
214
204
|
}
|
|
215
|
-
// Input-state gating: a session whose main REPL is mid-turn — currently
|
|
216
|
-
// running an in-progress tool call, which includes waiting on a
|
|
217
|
-
// long-running sub-agent — cannot accept an injected prompt cleanly, so
|
|
218
|
-
// the reminder is deferred rather than piled into a busy input box. The
|
|
219
|
-
// session stays a candidate, so it is reminded once it becomes
|
|
220
|
-
// input-ready. A tool call pending past
|
|
221
|
-
// IN_PROGRESS_TOOL_CALL_MAX_SUPPRESS_SECONDS is treated as hung and no
|
|
222
|
-
// longer counts as busy (see composeCandidate).
|
|
223
|
-
if (candidate.mainInputBusy) {
|
|
224
|
-
console.log(
|
|
225
|
-
`Skipping ${candidate.sessionName}: main REPL is not in an input-accepting state (in-progress tool call); deferring reminder until it is idle.`,
|
|
226
|
-
);
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
205
|
if (
|
|
230
206
|
!(await this.isHubTaskActive(
|
|
231
207
|
candidate.sessionName,
|
|
@@ -404,16 +380,11 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
404
380
|
transcriptPathBySessionName,
|
|
405
381
|
);
|
|
406
382
|
const lastOutputBySessionName = new Map<string, number>();
|
|
407
|
-
const inProgressToolCallBySessionName = new Map<string, boolean>();
|
|
408
383
|
for (const activity of activities) {
|
|
409
384
|
lastOutputBySessionName.set(
|
|
410
385
|
activity.sessionName,
|
|
411
386
|
activity.lastOutputEpochSeconds,
|
|
412
387
|
);
|
|
413
|
-
inProgressToolCallBySessionName.set(
|
|
414
|
-
activity.sessionName,
|
|
415
|
-
activity.hasInProgressToolCall,
|
|
416
|
-
);
|
|
417
388
|
}
|
|
418
389
|
|
|
419
390
|
const subAgentsBySessionName =
|
|
@@ -439,8 +410,6 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
439
410
|
return {
|
|
440
411
|
sessionName,
|
|
441
412
|
mainSilentSeconds,
|
|
442
|
-
mainHasInProgressToolCall:
|
|
443
|
-
inProgressToolCallBySessionName.get(sessionName) ?? false,
|
|
444
413
|
subAgents: subAgentsBySessionName.get(sessionName) ?? [],
|
|
445
414
|
unansweredOwnerCallAgeSeconds:
|
|
446
415
|
unansweredOwnerCallEpochSeconds === undefined
|
|
@@ -476,30 +445,17 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
476
445
|
// call signature and is intentionally ignored (treated as infinite).
|
|
477
446
|
const suppressedByUnansweredOwnerCall =
|
|
478
447
|
unansweredOwnerCallAgeSeconds !== null;
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
// the
|
|
485
|
-
//
|
|
486
|
-
// no pending tool call and is still flagged.
|
|
487
|
-
// The suppression is bounded by the pending tool call's own age
|
|
488
|
-
// (mainSilentSeconds, which for an in-progress tool call equals the age of
|
|
489
|
-
// the pending tool_use line) so it cannot last forever: a session stuck
|
|
490
|
-
// inside a tool call that never returns is reminded once that age exceeds
|
|
491
|
-
// IN_PROGRESS_TOOL_CALL_MAX_SUPPRESS_SECONDS, restoring the ~10-minute
|
|
492
|
-
// stall reminder for a truly hung session instead of suppressing it
|
|
493
|
-
// indefinitely.
|
|
494
|
-
const suppressedByInProgressToolCall =
|
|
495
|
-
snapshot.mainHasInProgressToolCall &&
|
|
496
|
-
mainSilentSeconds !== null &&
|
|
497
|
-
mainSilentSeconds < IN_PROGRESS_TOOL_CALL_MAX_SUPPRESS_SECONDS;
|
|
448
|
+
// The main-stall reminder is driven purely by silence: a session that has
|
|
449
|
+
// produced no assistant output for longer than the threshold is reminded
|
|
450
|
+
// regardless of whether its transcript tail is an in-progress tool_use. A
|
|
451
|
+
// session that merely looks busy (mid-tool-call) can in fact be stuck, so
|
|
452
|
+
// its apparent busyness MUST NOT suppress the reminder; the reminder queues
|
|
453
|
+
// cleanly into the session even when it is mid-turn. The only main-stall
|
|
454
|
+
// suppression is an unanswered owner call, which is a real wait on the owner.
|
|
498
455
|
const mainTriggered =
|
|
499
456
|
mainSilentSeconds !== null &&
|
|
500
457
|
mainSilentSeconds >= thresholds.mainSilentThresholdSeconds &&
|
|
501
|
-
!suppressedByUnansweredOwnerCall
|
|
502
|
-
!suppressedByInProgressToolCall;
|
|
458
|
+
!suppressedByUnansweredOwnerCall;
|
|
503
459
|
if (mainTriggered) {
|
|
504
460
|
sections.push(
|
|
505
461
|
this.messageComposer.composeMainStalledSection(mainSilentSeconds),
|
|
@@ -547,7 +503,6 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
547
503
|
sessionName: snapshot.sessionName,
|
|
548
504
|
message: sections.join('\n\n'),
|
|
549
505
|
sectionLabels,
|
|
550
|
-
mainInputBusy: suppressedByInProgressToolCall,
|
|
551
506
|
};
|
|
552
507
|
};
|
|
553
508
|
}
|