github-issue-tower-defence-management 1.118.0 → 1.118.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 +7 -0
- package/README.md +5 -3
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +14 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js +7 -2
- package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
- package/bin/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.js +128 -0
- package/bin/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.js.map +1 -0
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +61 -16
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
- package/bin/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +11 -0
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +6 -0
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +13 -0
- package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.test.ts +251 -0
- package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.ts +121 -0
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +218 -9
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +105 -19
- package/src/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.ts +20 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts +3 -0
- package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts.map +1 -1
- package/types/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.d.ts +20 -0
- package/types/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.d.ts.map +1 -0
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +7 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.d.ts +19 -0
- package/types/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.d.ts.map +1 -0
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS,
|
|
9
9
|
DEFAULT_NOTIFICATION_STAGGER_SECONDS,
|
|
10
10
|
DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS,
|
|
11
|
+
DEFAULT_HUB_TASK_STATUS_CACHE_TTL_SECONDS,
|
|
11
12
|
} from './NotifySilentLiveSessionsUseCase';
|
|
12
13
|
import { Issue } from '../entities/Issue';
|
|
13
14
|
import { LiveSessionProcessSnapshotProvider } from './adapter-interfaces/LiveSessionProcessSnapshotProvider';
|
|
@@ -17,6 +18,7 @@ import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionS
|
|
|
17
18
|
import { OwnerCallStatusProvider } from './adapter-interfaces/OwnerCallStatusProvider';
|
|
18
19
|
import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
|
|
19
20
|
import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
|
|
21
|
+
import { SilentSessionHubTaskStatusCacheRepository } from './adapter-interfaces/SilentSessionHubTaskStatusCacheRepository';
|
|
20
22
|
import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
|
|
21
23
|
import { Sleeper } from './adapter-interfaces/Sleeper';
|
|
22
24
|
import { SubAgentActivity } from '../entities/LiveSessionActivitySnapshot';
|
|
@@ -46,6 +48,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
46
48
|
let mockMessageComposer: Mocked<SilentSessionMessageComposer>;
|
|
47
49
|
let mockSleeper: Mocked<Sleeper>;
|
|
48
50
|
let mockHubTaskStatusResolver: Mocked<HubTaskStatusResolver>;
|
|
51
|
+
let mockHubTaskStatusCacheRepository: Mocked<SilentSessionHubTaskStatusCacheRepository>;
|
|
49
52
|
const now = new Date('2026-06-26T00:00:00Z');
|
|
50
53
|
const nowEpochSeconds = Math.floor(now.getTime() / 1000);
|
|
51
54
|
const GITHUB_SESSION = 'https_//github_com/HiromiShikata/repo/issues/42';
|
|
@@ -58,6 +61,8 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
58
61
|
overrides?: Partial<{
|
|
59
62
|
activeHubTaskStatus: string | null;
|
|
60
63
|
candidateDebounceRecencyWindowSeconds: number;
|
|
64
|
+
hubTaskStatusCacheTtlSeconds: number;
|
|
65
|
+
now: Date;
|
|
61
66
|
}>,
|
|
62
67
|
): {
|
|
63
68
|
mainSilentThresholdSeconds: number;
|
|
@@ -66,6 +71,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
66
71
|
staggerSeconds: number;
|
|
67
72
|
candidateDebounceRecencyWindowSeconds: number;
|
|
68
73
|
activeHubTaskStatus: string | null;
|
|
74
|
+
hubTaskStatusCacheTtlSeconds: number;
|
|
69
75
|
now: Date;
|
|
70
76
|
} => ({
|
|
71
77
|
mainSilentThresholdSeconds: DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
@@ -75,6 +81,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
75
81
|
candidateDebounceRecencyWindowSeconds:
|
|
76
82
|
DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS,
|
|
77
83
|
activeHubTaskStatus: null,
|
|
84
|
+
hubTaskStatusCacheTtlSeconds: DEFAULT_HUB_TASK_STATUS_CACHE_TTL_SECONDS,
|
|
78
85
|
now,
|
|
79
86
|
...overrides,
|
|
80
87
|
});
|
|
@@ -165,6 +172,10 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
165
172
|
mockHubTaskStatusResolver = {
|
|
166
173
|
getIssueByUrl: jest.fn().mockResolvedValue(null),
|
|
167
174
|
};
|
|
175
|
+
mockHubTaskStatusCacheRepository = {
|
|
176
|
+
loadHubTaskStatus: jest.fn().mockResolvedValue(null),
|
|
177
|
+
saveHubTaskStatus: jest.fn().mockResolvedValue(undefined),
|
|
178
|
+
};
|
|
168
179
|
useCase = new NotifySilentLiveSessionsUseCase(
|
|
169
180
|
mockSnapshotProvider,
|
|
170
181
|
mockTranscriptResolver,
|
|
@@ -176,6 +187,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
176
187
|
mockMessageComposer,
|
|
177
188
|
mockSleeper,
|
|
178
189
|
mockHubTaskStatusResolver,
|
|
190
|
+
mockHubTaskStatusCacheRepository,
|
|
179
191
|
);
|
|
180
192
|
});
|
|
181
193
|
|
|
@@ -804,14 +816,11 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
804
816
|
warnSpy.mockRestore();
|
|
805
817
|
});
|
|
806
818
|
|
|
807
|
-
it('fails open
|
|
819
|
+
it('fails open with a distinct no-cache warning when the hub task is not a resolvable tracked task and no cached status exists', async () => {
|
|
808
820
|
setupSilentMainSession(HUB_TASK_SESSION);
|
|
809
821
|
const warnSpy = jest
|
|
810
822
|
.spyOn(console, 'warn')
|
|
811
823
|
.mockImplementation(() => undefined);
|
|
812
|
-
const logSpy = jest
|
|
813
|
-
.spyOn(console, 'log')
|
|
814
|
-
.mockImplementation(() => undefined);
|
|
815
824
|
mockHubTaskStatusResolver.getIssueByUrl.mockResolvedValue(null);
|
|
816
825
|
|
|
817
826
|
await useCase.run(runParams({ activeHubTaskStatus: ACTIVE_STATUS }));
|
|
@@ -819,9 +828,12 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
819
828
|
expect(
|
|
820
829
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
821
830
|
).toHaveBeenCalledWith(HUB_TASK_SESSION, MAIN_STALLED_SECTION);
|
|
822
|
-
expect(warnSpy).
|
|
823
|
-
|
|
824
|
-
|
|
831
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
832
|
+
expect.stringContaining('no cached status'),
|
|
833
|
+
);
|
|
834
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
835
|
+
expect.stringContaining('fail-open'),
|
|
836
|
+
);
|
|
825
837
|
warnSpy.mockRestore();
|
|
826
838
|
});
|
|
827
839
|
|
|
@@ -873,7 +885,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
873
885
|
).not.toHaveBeenCalled();
|
|
874
886
|
});
|
|
875
887
|
|
|
876
|
-
it('fails open without throwing
|
|
888
|
+
it('fails open without throwing for a real tmux session-name form whose repository is unresolvable and has no cached status', async () => {
|
|
877
889
|
const REAL_TMUX_SESSION =
|
|
878
890
|
'https_//github_com/example-org/example-repo/issues/2350';
|
|
879
891
|
const CANONICAL_ISSUE_URL =
|
|
@@ -894,7 +906,204 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
894
906
|
expect(
|
|
895
907
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
896
908
|
).toHaveBeenCalledWith(REAL_TMUX_SESSION, MAIN_STALLED_SECTION);
|
|
897
|
-
expect(warnSpy).
|
|
909
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
910
|
+
expect.stringContaining('no cached status'),
|
|
911
|
+
);
|
|
912
|
+
warnSpy.mockRestore();
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
it('suppresses a resolved CLOSED hub task and writes the resolved status to the cache', async () => {
|
|
916
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
917
|
+
mockHubTaskStatusResolver.getIssueByUrl.mockResolvedValue(
|
|
918
|
+
issueFor({
|
|
919
|
+
url: HUB_TASK_SESSION,
|
|
920
|
+
state: 'CLOSED',
|
|
921
|
+
status: ACTIVE_STATUS,
|
|
922
|
+
isClosed: true,
|
|
923
|
+
}),
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
await useCase.run(runParams({ activeHubTaskStatus: ACTIVE_STATUS }));
|
|
927
|
+
|
|
928
|
+
expect(
|
|
929
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
930
|
+
).not.toHaveBeenCalled();
|
|
931
|
+
expect(
|
|
932
|
+
mockHubTaskStatusCacheRepository.saveHubTaskStatus,
|
|
933
|
+
).toHaveBeenCalledWith({
|
|
934
|
+
url: HUB_TASK_SESSION,
|
|
935
|
+
state: 'CLOSED',
|
|
936
|
+
status: ACTIVE_STATUS,
|
|
937
|
+
now,
|
|
938
|
+
});
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
it('suppresses a resolved Done (non-active project status) hub task', async () => {
|
|
942
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
943
|
+
mockHubTaskStatusResolver.getIssueByUrl.mockResolvedValue(
|
|
944
|
+
issueFor({ url: HUB_TASK_SESSION, state: 'OPEN', status: 'Done' }),
|
|
945
|
+
);
|
|
946
|
+
|
|
947
|
+
await useCase.run(runParams({ activeHubTaskStatus: ACTIVE_STATUS }));
|
|
948
|
+
|
|
949
|
+
expect(
|
|
950
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
951
|
+
).not.toHaveBeenCalled();
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it('sends a resolved active hub task and writes its status to the cache', async () => {
|
|
955
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
956
|
+
mockHubTaskStatusResolver.getIssueByUrl.mockResolvedValue(
|
|
957
|
+
issueFor({
|
|
958
|
+
url: HUB_TASK_SESSION,
|
|
959
|
+
state: 'OPEN',
|
|
960
|
+
status: ACTIVE_STATUS,
|
|
961
|
+
}),
|
|
962
|
+
);
|
|
963
|
+
|
|
964
|
+
await useCase.run(runParams({ activeHubTaskStatus: ACTIVE_STATUS }));
|
|
965
|
+
|
|
966
|
+
expect(
|
|
967
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
968
|
+
).toHaveBeenCalledWith(HUB_TASK_SESSION, MAIN_STALLED_SECTION);
|
|
969
|
+
expect(
|
|
970
|
+
mockHubTaskStatusCacheRepository.saveHubTaskStatus,
|
|
971
|
+
).toHaveBeenCalledWith({
|
|
972
|
+
url: HUB_TASK_SESSION,
|
|
973
|
+
state: 'OPEN',
|
|
974
|
+
status: ACTIVE_STATUS,
|
|
975
|
+
now,
|
|
976
|
+
});
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
it('decides from a fresh cached entry without consulting the resolver', async () => {
|
|
980
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
981
|
+
mockHubTaskStatusCacheRepository.loadHubTaskStatus.mockResolvedValue({
|
|
982
|
+
url: HUB_TASK_SESSION,
|
|
983
|
+
state: 'OPEN',
|
|
984
|
+
status: ACTIVE_STATUS,
|
|
985
|
+
recordedEpochSeconds: nowEpochSeconds - 60,
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
await useCase.run(
|
|
989
|
+
runParams({
|
|
990
|
+
activeHubTaskStatus: ACTIVE_STATUS,
|
|
991
|
+
hubTaskStatusCacheTtlSeconds: 300,
|
|
992
|
+
}),
|
|
993
|
+
);
|
|
994
|
+
|
|
995
|
+
expect(mockHubTaskStatusResolver.getIssueByUrl).not.toHaveBeenCalled();
|
|
996
|
+
expect(
|
|
997
|
+
mockHubTaskStatusCacheRepository.saveHubTaskStatus,
|
|
998
|
+
).not.toHaveBeenCalled();
|
|
999
|
+
expect(
|
|
1000
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
1001
|
+
).toHaveBeenCalledWith(HUB_TASK_SESSION, MAIN_STALLED_SECTION);
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
it('suppresses from a fresh cached closed entry without consulting the resolver', async () => {
|
|
1005
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
1006
|
+
mockHubTaskStatusCacheRepository.loadHubTaskStatus.mockResolvedValue({
|
|
1007
|
+
url: HUB_TASK_SESSION,
|
|
1008
|
+
state: 'CLOSED',
|
|
1009
|
+
status: ACTIVE_STATUS,
|
|
1010
|
+
recordedEpochSeconds: nowEpochSeconds - 60,
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
await useCase.run(
|
|
1014
|
+
runParams({
|
|
1015
|
+
activeHubTaskStatus: ACTIVE_STATUS,
|
|
1016
|
+
hubTaskStatusCacheTtlSeconds: 300,
|
|
1017
|
+
}),
|
|
1018
|
+
);
|
|
1019
|
+
|
|
1020
|
+
expect(mockHubTaskStatusResolver.getIssueByUrl).not.toHaveBeenCalled();
|
|
1021
|
+
expect(
|
|
1022
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
1023
|
+
).not.toHaveBeenCalled();
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
it('re-resolves when the cached entry is older than the configured time-to-live', async () => {
|
|
1027
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
1028
|
+
mockHubTaskStatusCacheRepository.loadHubTaskStatus.mockResolvedValue({
|
|
1029
|
+
url: HUB_TASK_SESSION,
|
|
1030
|
+
state: 'OPEN',
|
|
1031
|
+
status: ACTIVE_STATUS,
|
|
1032
|
+
recordedEpochSeconds: nowEpochSeconds - 600,
|
|
1033
|
+
});
|
|
1034
|
+
mockHubTaskStatusResolver.getIssueByUrl.mockResolvedValue(
|
|
1035
|
+
issueFor({
|
|
1036
|
+
url: HUB_TASK_SESSION,
|
|
1037
|
+
state: 'OPEN',
|
|
1038
|
+
status: ACTIVE_STATUS,
|
|
1039
|
+
}),
|
|
1040
|
+
);
|
|
1041
|
+
|
|
1042
|
+
await useCase.run(
|
|
1043
|
+
runParams({
|
|
1044
|
+
activeHubTaskStatus: ACTIVE_STATUS,
|
|
1045
|
+
hubTaskStatusCacheTtlSeconds: 300,
|
|
1046
|
+
}),
|
|
1047
|
+
);
|
|
1048
|
+
|
|
1049
|
+
expect(mockHubTaskStatusResolver.getIssueByUrl).toHaveBeenCalledWith(
|
|
1050
|
+
HUB_TASK_SESSION,
|
|
1051
|
+
);
|
|
1052
|
+
expect(
|
|
1053
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
1054
|
+
).toHaveBeenCalledWith(HUB_TASK_SESSION, MAIN_STALLED_SECTION);
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
it('falls back to an expired cached active entry and still sends when live resolution returns null', async () => {
|
|
1058
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
1059
|
+
mockHubTaskStatusCacheRepository.loadHubTaskStatus.mockResolvedValue({
|
|
1060
|
+
url: HUB_TASK_SESSION,
|
|
1061
|
+
state: 'OPEN',
|
|
1062
|
+
status: ACTIVE_STATUS,
|
|
1063
|
+
recordedEpochSeconds: nowEpochSeconds - 600,
|
|
1064
|
+
});
|
|
1065
|
+
mockHubTaskStatusResolver.getIssueByUrl.mockResolvedValue(null);
|
|
1066
|
+
|
|
1067
|
+
await useCase.run(
|
|
1068
|
+
runParams({
|
|
1069
|
+
activeHubTaskStatus: ACTIVE_STATUS,
|
|
1070
|
+
hubTaskStatusCacheTtlSeconds: 300,
|
|
1071
|
+
}),
|
|
1072
|
+
);
|
|
1073
|
+
|
|
1074
|
+
expect(
|
|
1075
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
1076
|
+
).toHaveBeenCalledWith(HUB_TASK_SESSION, MAIN_STALLED_SECTION);
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
it('falls back to an expired cached closed entry and suppresses when live resolution throws', async () => {
|
|
1080
|
+
setupSilentMainSession(HUB_TASK_SESSION);
|
|
1081
|
+
const warnSpy = jest
|
|
1082
|
+
.spyOn(console, 'warn')
|
|
1083
|
+
.mockImplementation(() => undefined);
|
|
1084
|
+
mockHubTaskStatusCacheRepository.loadHubTaskStatus.mockResolvedValue({
|
|
1085
|
+
url: HUB_TASK_SESSION,
|
|
1086
|
+
state: 'CLOSED',
|
|
1087
|
+
status: ACTIVE_STATUS,
|
|
1088
|
+
recordedEpochSeconds: nowEpochSeconds - 600,
|
|
1089
|
+
});
|
|
1090
|
+
mockHubTaskStatusResolver.getIssueByUrl.mockRejectedValue(
|
|
1091
|
+
new Error('GraphQL rate limit'),
|
|
1092
|
+
);
|
|
1093
|
+
|
|
1094
|
+
await useCase.run(
|
|
1095
|
+
runParams({
|
|
1096
|
+
activeHubTaskStatus: ACTIVE_STATUS,
|
|
1097
|
+
hubTaskStatusCacheTtlSeconds: 300,
|
|
1098
|
+
}),
|
|
1099
|
+
);
|
|
1100
|
+
|
|
1101
|
+
expect(
|
|
1102
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
1103
|
+
).not.toHaveBeenCalled();
|
|
1104
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
1105
|
+
expect.stringContaining('expired cached status'),
|
|
1106
|
+
);
|
|
898
1107
|
warnSpy.mockRestore();
|
|
899
1108
|
});
|
|
900
1109
|
|
|
@@ -8,6 +8,7 @@ import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionS
|
|
|
8
8
|
import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
|
|
9
9
|
import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
|
|
10
10
|
import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
|
|
11
|
+
import { SilentSessionHubTaskStatusCacheRepository } from './adapter-interfaces/SilentSessionHubTaskStatusCacheRepository';
|
|
11
12
|
import { Sleeper } from './adapter-interfaces/Sleeper';
|
|
12
13
|
import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
13
14
|
import { ResolveInteractiveLiveSessionsUseCase } from './ResolveInteractiveLiveSessionsUseCase';
|
|
@@ -17,6 +18,7 @@ export const DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS = 5 * 60;
|
|
|
17
18
|
export const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS = 15 * 60;
|
|
18
19
|
export const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
|
|
19
20
|
export const DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS = 15 * 60;
|
|
21
|
+
export const DEFAULT_HUB_TASK_STATUS_CACHE_TTL_SECONDS = 5 * 60;
|
|
20
22
|
|
|
21
23
|
const GITHUB_ISSUE_OR_PULL_URL_PATTERN =
|
|
22
24
|
/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/(?:issues|pull)\/(\d+)$/;
|
|
@@ -69,6 +71,7 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
69
71
|
private readonly messageComposer: SilentSessionMessageComposer,
|
|
70
72
|
private readonly sleeper: Sleeper,
|
|
71
73
|
private readonly hubTaskStatusResolver: HubTaskStatusResolver | null = null,
|
|
74
|
+
private readonly hubTaskStatusCacheRepository: SilentSessionHubTaskStatusCacheRepository | null = null,
|
|
72
75
|
) {}
|
|
73
76
|
|
|
74
77
|
run = async (params: {
|
|
@@ -78,6 +81,7 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
78
81
|
staggerSeconds: number;
|
|
79
82
|
candidateDebounceRecencyWindowSeconds: number;
|
|
80
83
|
activeHubTaskStatus: string | null;
|
|
84
|
+
hubTaskStatusCacheTtlSeconds: number;
|
|
81
85
|
now: Date;
|
|
82
86
|
}): Promise<void> => {
|
|
83
87
|
const snapshot =
|
|
@@ -146,6 +150,8 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
146
150
|
!(await this.isHubTaskActive(
|
|
147
151
|
candidate.sessionName,
|
|
148
152
|
params.activeHubTaskStatus,
|
|
153
|
+
params.hubTaskStatusCacheTtlSeconds,
|
|
154
|
+
params.now,
|
|
149
155
|
))
|
|
150
156
|
) {
|
|
151
157
|
continue;
|
|
@@ -165,6 +171,8 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
165
171
|
private isHubTaskActive = async (
|
|
166
172
|
sessionName: string,
|
|
167
173
|
activeHubTaskStatus: string | null,
|
|
174
|
+
hubTaskStatusCacheTtlSeconds: number,
|
|
175
|
+
now: Date,
|
|
168
176
|
): Promise<boolean> => {
|
|
169
177
|
if (activeHubTaskStatus === null || this.hubTaskStatusResolver === null) {
|
|
170
178
|
return true;
|
|
@@ -173,32 +181,110 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
173
181
|
if (hubTaskIssueUrl === null) {
|
|
174
182
|
return true;
|
|
175
183
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
|
|
185
|
+
const cachedEntry =
|
|
186
|
+
this.hubTaskStatusCacheRepository === null
|
|
187
|
+
? null
|
|
188
|
+
: await this.hubTaskStatusCacheRepository.loadHubTaskStatus({
|
|
189
|
+
url: hubTaskIssueUrl,
|
|
190
|
+
});
|
|
191
|
+
const nowEpochSeconds = Math.floor(now.getTime() / 1000);
|
|
192
|
+
if (cachedEntry !== null) {
|
|
193
|
+
const cacheAgeSeconds =
|
|
194
|
+
nowEpochSeconds - cachedEntry.recordedEpochSeconds;
|
|
195
|
+
if (cacheAgeSeconds <= hubTaskStatusCacheTtlSeconds) {
|
|
196
|
+
const active = this.isResolvedStatusActive(
|
|
197
|
+
cachedEntry.state,
|
|
198
|
+
cachedEntry.status,
|
|
199
|
+
activeHubTaskStatus,
|
|
188
200
|
);
|
|
189
|
-
|
|
201
|
+
if (!active) {
|
|
202
|
+
console.log(
|
|
203
|
+
`Skipping ${sessionName}: hub task ${hubTaskIssueUrl} is no longer active per cached status (state "${cachedEntry.state}", status "${cachedEntry.status ?? 'null'}", active status "${activeHubTaskStatus}").`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return active;
|
|
190
207
|
}
|
|
191
|
-
|
|
192
|
-
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const resolution = await this.tryResolveAndCacheHubTask(
|
|
211
|
+
hubTaskIssueUrl,
|
|
212
|
+
activeHubTaskStatus,
|
|
213
|
+
sessionName,
|
|
214
|
+
now,
|
|
215
|
+
);
|
|
216
|
+
if (resolution.resolved) {
|
|
217
|
+
return resolution.active;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (cachedEntry !== null) {
|
|
221
|
+
const active = this.isResolvedStatusActive(
|
|
222
|
+
cachedEntry.state,
|
|
223
|
+
cachedEntry.status,
|
|
224
|
+
activeHubTaskStatus,
|
|
225
|
+
);
|
|
193
226
|
console.warn(
|
|
194
|
-
`
|
|
195
|
-
error instanceof Error ? error.message : String(error)
|
|
196
|
-
}`,
|
|
227
|
+
`Hub task ${hubTaskIssueUrl} for session ${sessionName} could not be resolved (${resolution.reason}); falling back to expired cached status (state "${cachedEntry.state}", status "${cachedEntry.status ?? 'null'}"), so the notification is ${active ? 'sent' : 'suppressed'}.`,
|
|
197
228
|
);
|
|
198
|
-
return
|
|
229
|
+
return active;
|
|
199
230
|
}
|
|
231
|
+
|
|
232
|
+
console.warn(
|
|
233
|
+
`Hub task ${hubTaskIssueUrl} for session ${sessionName} is not resolvable and has no cached status (${resolution.reason}); sending notification (fail-open).`,
|
|
234
|
+
);
|
|
235
|
+
return true;
|
|
200
236
|
};
|
|
201
237
|
|
|
238
|
+
private tryResolveAndCacheHubTask = async (
|
|
239
|
+
hubTaskIssueUrl: string,
|
|
240
|
+
activeHubTaskStatus: string,
|
|
241
|
+
sessionName: string,
|
|
242
|
+
now: Date,
|
|
243
|
+
): Promise<
|
|
244
|
+
{ resolved: true; active: boolean } | { resolved: false; reason: string }
|
|
245
|
+
> => {
|
|
246
|
+
if (this.hubTaskStatusResolver === null) {
|
|
247
|
+
return { resolved: false, reason: 'resolver is not configured' };
|
|
248
|
+
}
|
|
249
|
+
let issue: Awaited<ReturnType<HubTaskStatusResolver['getIssueByUrl']>>;
|
|
250
|
+
try {
|
|
251
|
+
issue = await this.hubTaskStatusResolver.getIssueByUrl(hubTaskIssueUrl);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return {
|
|
254
|
+
resolved: false,
|
|
255
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (issue === null) {
|
|
259
|
+
return { resolved: false, reason: 'resolver returned no tracked task' };
|
|
260
|
+
}
|
|
261
|
+
if (this.hubTaskStatusCacheRepository !== null) {
|
|
262
|
+
await this.hubTaskStatusCacheRepository.saveHubTaskStatus({
|
|
263
|
+
url: hubTaskIssueUrl,
|
|
264
|
+
state: issue.state,
|
|
265
|
+
status: issue.status,
|
|
266
|
+
now,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const active = this.isResolvedStatusActive(
|
|
270
|
+
issue.state,
|
|
271
|
+
issue.status,
|
|
272
|
+
activeHubTaskStatus,
|
|
273
|
+
);
|
|
274
|
+
if (!active) {
|
|
275
|
+
console.log(
|
|
276
|
+
`Skipping ${sessionName}: hub task ${hubTaskIssueUrl} is no longer active (state "${issue.state}", status "${issue.status ?? 'null'}", active status "${activeHubTaskStatus}").`,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
return { resolved: true, active };
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
private isResolvedStatusActive = (
|
|
283
|
+
state: 'OPEN' | 'CLOSED' | 'MERGED',
|
|
284
|
+
status: string | null,
|
|
285
|
+
activeHubTaskStatus: string,
|
|
286
|
+
): boolean => state === 'OPEN' && status === activeHubTaskStatus;
|
|
287
|
+
|
|
202
288
|
private collectSnapshots = async (
|
|
203
289
|
interactiveSessions: InteractiveLiveSession[],
|
|
204
290
|
transcriptPathBySessionName: Map<string, string>,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
|
|
3
|
+
export type SilentSessionHubTaskStatusCacheEntry = {
|
|
4
|
+
url: string;
|
|
5
|
+
state: Issue['state'];
|
|
6
|
+
status: string | null;
|
|
7
|
+
recordedEpochSeconds: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface SilentSessionHubTaskStatusCacheRepository {
|
|
11
|
+
loadHubTaskStatus: (params: {
|
|
12
|
+
url: string;
|
|
13
|
+
}) => Promise<SilentSessionHubTaskStatusCacheEntry | null>;
|
|
14
|
+
saveHubTaskStatus: (params: {
|
|
15
|
+
url: string;
|
|
16
|
+
state: Issue['state'];
|
|
17
|
+
status: string | null;
|
|
18
|
+
now: Date;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAiD3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAiD3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAmkBP;CACH"}
|
|
@@ -18,6 +18,8 @@ export type NotifySilentTmuxSessionsParams = {
|
|
|
18
18
|
candidateDebounceStateFilePath: string | null;
|
|
19
19
|
activeHubTaskStatus: string | null;
|
|
20
20
|
hubTaskStatusResolver: HubTaskStatusResolver | null;
|
|
21
|
+
hubTaskStatusCacheStateFilePath: string | null;
|
|
22
|
+
hubTaskStatusCacheTtlSeconds: number;
|
|
21
23
|
messageTemplates: SilentSessionMessageTemplates;
|
|
22
24
|
now: Date;
|
|
23
25
|
};
|
|
@@ -28,5 +30,6 @@ export declare const DEFAULT_NOTIFY_SILENT_TMUX_SESSIONS_PARAMS: {
|
|
|
28
30
|
readonly subAgentRunningThresholdSeconds: number;
|
|
29
31
|
readonly staggerSeconds: 25;
|
|
30
32
|
readonly candidateDebounceRecencyWindowSeconds: number;
|
|
33
|
+
readonly hubTaskStatusCacheTtlSeconds: number;
|
|
31
34
|
};
|
|
32
35
|
//# sourceMappingURL=notifySilentTmuxSessions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,
|
|
1
|
+
{"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,EAOtB,MAAM,0DAA0D,CAAC;AAclE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAKrE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,0BAA0B,EAAE,MAAM,CAAC;IACnC,8BAA8B,EAAE,MAAM,CAAC;IACvC,+BAA+B,EAAE,MAAM,CAAC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC,EAAE,MAAM,CAAC;IAC9C,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACpD,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,CAAC;IACrC,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAoCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CAwEd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;;CAQ7C,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Issue } from '../../domain/entities/Issue';
|
|
2
|
+
import { SilentSessionHubTaskStatusCacheEntry, SilentSessionHubTaskStatusCacheRepository } from '../../domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository';
|
|
3
|
+
export declare const DEFAULT_HUB_TASK_STATUS_RETENTION_WINDOW_SECONDS: number;
|
|
4
|
+
export declare class FileSystemSilentSessionHubTaskStatusCacheRepository implements SilentSessionHubTaskStatusCacheRepository {
|
|
5
|
+
private readonly stateFilePath;
|
|
6
|
+
private readonly retentionWindowSeconds;
|
|
7
|
+
constructor(stateFilePath?: string, retentionWindowSeconds?: number);
|
|
8
|
+
loadHubTaskStatus: (params: {
|
|
9
|
+
url: string;
|
|
10
|
+
}) => Promise<SilentSessionHubTaskStatusCacheEntry | null>;
|
|
11
|
+
saveHubTaskStatus: (params: {
|
|
12
|
+
url: string;
|
|
13
|
+
state: Issue["state"];
|
|
14
|
+
status: string | null;
|
|
15
|
+
now: Date;
|
|
16
|
+
}) => Promise<void>;
|
|
17
|
+
private readEntries;
|
|
18
|
+
private writeEntries;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=FileSystemSilentSessionHubTaskStatusCacheRepository.d.ts.map
|
package/types/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSystemSilentSessionHubTaskStatusCacheRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,OAAO,EACL,oCAAoC,EACpC,yCAAyC,EAC1C,MAAM,oFAAoF,CAAC;AAQ5F,eAAO,MAAM,gDAAgD,QAAU,CAAC;AAOxE,qBAAa,mDAAoD,YAAW,yCAAyC;IAEjH,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBADtB,aAAa,GAAE,MAA+B,EAC9C,sBAAsB,GAAE,MAAyD;IAGpG,iBAAiB,GAAU,QAAQ;QACjC,GAAG,EAAE,MAAM,CAAC;KACb,KAAG,OAAO,CAAC,oCAAoC,GAAG,IAAI,CAAC,CAOtD;IAEF,iBAAiB,GAAU,QAAQ;QACjC,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CAoBf;IAEF,OAAO,CAAC,WAAW,CAwCjB;IAEF,OAAO,CAAC,YAAY,CAWlB;CACH"}
|
|
@@ -6,6 +6,7 @@ import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionS
|
|
|
6
6
|
import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
|
|
7
7
|
import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
|
|
8
8
|
import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
|
|
9
|
+
import { SilentSessionHubTaskStatusCacheRepository } from './adapter-interfaces/SilentSessionHubTaskStatusCacheRepository';
|
|
9
10
|
import { Sleeper } from './adapter-interfaces/Sleeper';
|
|
10
11
|
import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
11
12
|
export declare const DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS: number;
|
|
@@ -13,6 +14,7 @@ export declare const DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS: number;
|
|
|
13
14
|
export declare const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS: number;
|
|
14
15
|
export declare const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
|
|
15
16
|
export declare const DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS: number;
|
|
17
|
+
export declare const DEFAULT_HUB_TASK_STATUS_CACHE_TTL_SECONDS: number;
|
|
16
18
|
export declare const parseHubTaskIssueUrlFromSessionName: (sessionName: string) => string | null;
|
|
17
19
|
export declare const isGitHubIssueOrPullRequestSessionName: (sessionName: string) => boolean;
|
|
18
20
|
export type HubTaskStatusResolver = Pick<IssueRepository, 'getIssueByUrl'>;
|
|
@@ -27,8 +29,9 @@ export declare class NotifySilentLiveSessionsUseCase {
|
|
|
27
29
|
private readonly messageComposer;
|
|
28
30
|
private readonly sleeper;
|
|
29
31
|
private readonly hubTaskStatusResolver;
|
|
32
|
+
private readonly hubTaskStatusCacheRepository;
|
|
30
33
|
private readonly resolveInteractiveLiveSessions;
|
|
31
|
-
constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, candidateStateRepository: SilentSessionCandidateStateRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper, hubTaskStatusResolver?: HubTaskStatusResolver | null);
|
|
34
|
+
constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, candidateStateRepository: SilentSessionCandidateStateRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper, hubTaskStatusResolver?: HubTaskStatusResolver | null, hubTaskStatusCacheRepository?: SilentSessionHubTaskStatusCacheRepository | null);
|
|
32
35
|
run: (params: {
|
|
33
36
|
mainSilentThresholdSeconds: number;
|
|
34
37
|
subAgentSilentThresholdSeconds: number;
|
|
@@ -36,9 +39,12 @@ export declare class NotifySilentLiveSessionsUseCase {
|
|
|
36
39
|
staggerSeconds: number;
|
|
37
40
|
candidateDebounceRecencyWindowSeconds: number;
|
|
38
41
|
activeHubTaskStatus: string | null;
|
|
42
|
+
hubTaskStatusCacheTtlSeconds: number;
|
|
39
43
|
now: Date;
|
|
40
44
|
}) => Promise<void>;
|
|
41
45
|
private isHubTaskActive;
|
|
46
|
+
private tryResolveAndCacheHubTask;
|
|
47
|
+
private isResolvedStatusActive;
|
|
42
48
|
private collectSnapshots;
|
|
43
49
|
private composeMessage;
|
|
44
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotifySilentLiveSessionsUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifySilentLiveSessionsUseCase.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kCAAkC,EAAE,MAAM,yDAAyD,CAAC;AAC7G,OAAO,EAAE,wCAAwC,EAAE,MAAM,+DAA+D,CAAC;AACzH,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,sDAAsD,CAAC;AACvG,OAAO,EAAE,iCAAiC,EAAE,MAAM,wDAAwD,CAAC;AAC3G,OAAO,EAAE,4BAA4B,EAAE,MAAM,mDAAmD,CAAC;AACjG,OAAO,EAAE,mCAAmC,EAAE,MAAM,0DAA0D,CAAC;AAC/G,OAAO,EAAE,qCAAqC,EAAE,MAAM,4DAA4D,CAAC;AACnH,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAGvE,eAAO,MAAM,qCAAqC,QAAU,CAAC;AAC7D,eAAO,MAAM,yCAAyC,QAAS,CAAC;AAChE,eAAO,MAAM,0CAA0C,QAAU,CAAC;AAClE,eAAO,MAAM,oCAAoC,KAAK,CAAC;AACvD,eAAO,MAAM,iDAAiD,QAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"NotifySilentLiveSessionsUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifySilentLiveSessionsUseCase.ts"],"names":[],"mappings":"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"}
|
package/types/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
export type SilentSessionHubTaskStatusCacheEntry = {
|
|
3
|
+
url: string;
|
|
4
|
+
state: Issue['state'];
|
|
5
|
+
status: string | null;
|
|
6
|
+
recordedEpochSeconds: number;
|
|
7
|
+
};
|
|
8
|
+
export interface SilentSessionHubTaskStatusCacheRepository {
|
|
9
|
+
loadHubTaskStatus: (params: {
|
|
10
|
+
url: string;
|
|
11
|
+
}) => Promise<SilentSessionHubTaskStatusCacheEntry | null>;
|
|
12
|
+
saveHubTaskStatus: (params: {
|
|
13
|
+
url: string;
|
|
14
|
+
state: Issue['state'];
|
|
15
|
+
status: string | null;
|
|
16
|
+
now: Date;
|
|
17
|
+
}) => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=SilentSessionHubTaskStatusCacheRepository.d.ts.map
|
package/types/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SilentSessionHubTaskStatusCacheRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE7C,MAAM,MAAM,oCAAoC,GAAG;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,WAAW,yCAAyC;IACxD,iBAAiB,EAAE,CAAC,MAAM,EAAE;QAC1B,GAAG,EAAE,MAAM,CAAC;KACb,KAAK,OAAO,CAAC,oCAAoC,GAAG,IAAI,CAAC,CAAC;IAC3D,iBAAiB,EAAE,CAAC,MAAM,EAAE;QAC1B,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,GAAG,EAAE,IAAI,CAAC;KACX,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB"}
|