github-issue-tower-defence-management 1.117.8 → 1.117.9

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +4 -2
  3. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +14 -2
  4. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  5. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js +7 -2
  6. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
  7. package/bin/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.js +128 -0
  8. package/bin/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.js.map +1 -0
  9. package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +16 -4
  10. package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
  11. package/bin/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.js +3 -0
  12. package/bin/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.js.map +1 -0
  13. package/package.json +1 -1
  14. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +11 -0
  15. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +37 -3
  16. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +14 -0
  17. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.test.ts +201 -0
  18. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.ts +115 -0
  19. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +117 -22
  20. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +22 -2
  21. package/src/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.ts +10 -0
  22. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  23. package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts +3 -0
  24. package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts.map +1 -1
  25. package/types/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.d.ts +18 -0
  26. package/types/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.d.ts.map +1 -0
  27. package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +5 -1
  28. package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
  29. package/types/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.d.ts +11 -0
  30. package/types/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.d.ts.map +1 -0
@@ -7,6 +7,7 @@ import {
7
7
  DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS,
8
8
  DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS,
9
9
  DEFAULT_NOTIFICATION_STAGGER_SECONDS,
10
+ DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS,
10
11
  } from './NotifySilentLiveSessionsUseCase';
11
12
  import { Issue } from '../entities/Issue';
12
13
  import { LiveSessionProcessSnapshotProvider } from './adapter-interfaces/LiveSessionProcessSnapshotProvider';
@@ -15,6 +16,7 @@ import { SessionOutputActivityRepository } from './adapter-interfaces/SessionOut
15
16
  import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionSubAgentActivityRepository';
16
17
  import { OwnerCallStatusProvider } from './adapter-interfaces/OwnerCallStatusProvider';
17
18
  import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
19
+ import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
18
20
  import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
19
21
  import { Sleeper } from './adapter-interfaces/Sleeper';
20
22
  import { SubAgentActivity } from '../entities/LiveSessionActivitySnapshot';
@@ -26,6 +28,12 @@ type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
26
28
  const MAIN_STALLED_SECTION = 'MAIN_STALLED_SECTION';
27
29
  const SUBAGENT_SECTION = 'SUBAGENT_SECTION';
28
30
 
31
+ class EveryNameRecentSet extends Set<string> {
32
+ override has = (): boolean => true;
33
+ }
34
+
35
+ const everyNameRecentSet = (): Set<string> => new EveryNameRecentSet();
36
+
29
37
  describe('NotifySilentLiveSessionsUseCase', () => {
30
38
  let useCase: NotifySilentLiveSessionsUseCase;
31
39
  let mockSnapshotProvider: Mocked<LiveSessionProcessSnapshotProvider>;
@@ -34,6 +42,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
34
42
  let mockSubAgentActivityRepository: Mocked<SessionSubAgentActivityRepository>;
35
43
  let mockOwnerCallStatusProvider: Mocked<OwnerCallStatusProvider>;
36
44
  let mockNotificationRepository: Mocked<SilentSessionNotificationRepository>;
45
+ let mockCandidateStateRepository: Mocked<SilentSessionCandidateStateRepository>;
37
46
  let mockMessageComposer: Mocked<SilentSessionMessageComposer>;
38
47
  let mockSleeper: Mocked<Sleeper>;
39
48
  let mockHubTaskStatusResolver: Mocked<HubTaskStatusResolver>;
@@ -46,12 +55,16 @@ describe('NotifySilentLiveSessionsUseCase', () => {
46
55
  'https_//github_com/HiromiShikata/repo/issues/200';
47
56
 
48
57
  const runParams = (
49
- overrides?: Partial<{ activeHubTaskStatus: string | null }>,
58
+ overrides?: Partial<{
59
+ activeHubTaskStatus: string | null;
60
+ candidateDebounceRecencyWindowSeconds: number;
61
+ }>,
50
62
  ): {
51
63
  mainSilentThresholdSeconds: number;
52
64
  subAgentSilentThresholdSeconds: number;
53
65
  subAgentRunningThresholdSeconds: number;
54
66
  staggerSeconds: number;
67
+ candidateDebounceRecencyWindowSeconds: number;
55
68
  activeHubTaskStatus: string | null;
56
69
  now: Date;
57
70
  } => ({
@@ -59,6 +72,8 @@ describe('NotifySilentLiveSessionsUseCase', () => {
59
72
  subAgentSilentThresholdSeconds: DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS,
60
73
  subAgentRunningThresholdSeconds: DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS,
61
74
  staggerSeconds: DEFAULT_NOTIFICATION_STAGGER_SECONDS,
75
+ candidateDebounceRecencyWindowSeconds:
76
+ DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS,
62
77
  activeHubTaskStatus: null,
63
78
  now,
64
79
  ...overrides,
@@ -132,6 +147,12 @@ describe('NotifySilentLiveSessionsUseCase', () => {
132
147
  mockNotificationRepository = {
133
148
  sendSelfCheckNotification: jest.fn().mockResolvedValue(undefined),
134
149
  };
150
+ mockCandidateStateRepository = {
151
+ loadRecentCandidateSessionNames: jest
152
+ .fn()
153
+ .mockResolvedValue(everyNameRecentSet()),
154
+ saveCandidateSessionNames: jest.fn().mockResolvedValue(undefined),
155
+ };
135
156
  mockMessageComposer = {
136
157
  composeMainStalledSection: jest
137
158
  .fn()
@@ -151,6 +172,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
151
172
  mockSubAgentActivityRepository,
152
173
  mockOwnerCallStatusProvider,
153
174
  mockNotificationRepository,
175
+ mockCandidateStateRepository,
154
176
  mockMessageComposer,
155
177
  mockSleeper,
156
178
  mockHubTaskStatusResolver,
@@ -464,7 +486,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
464
486
  ).not.toHaveBeenCalled();
465
487
  });
466
488
 
467
- it('re-notifies a session on every consecutive cycle while it remains a valid silent target', async () => {
489
+ it('re-notifies a persistent stall on every cycle once it has been a candidate in the previous cycle', async () => {
468
490
  setupLiveInteractiveSession(GITHUB_SESSION);
469
491
  mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
470
492
  [
@@ -490,29 +512,102 @@ describe('NotifySilentLiveSessionsUseCase', () => {
490
512
  ).toHaveBeenNthCalledWith(2, GITHUB_SESSION, MAIN_STALLED_SECTION);
491
513
  });
492
514
 
493
- it('notifies on every repeated cycle at the same instant without reading any cooldown state', async () => {
494
- setupLiveInteractiveSession(GITHUB_SESSION);
495
- mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
496
- [
497
- {
498
- sessionName: GITHUB_SESSION,
499
- lastOutputEpochSeconds:
500
- nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
515
+ describe('two-consecutive-cycle debounce', () => {
516
+ const setupSilentGithubSession = (): void => {
517
+ setupLiveInteractiveSession(GITHUB_SESSION);
518
+ mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
519
+ [
520
+ {
521
+ sessionName: GITHUB_SESSION,
522
+ lastOutputEpochSeconds:
523
+ nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
524
+ },
525
+ ],
526
+ );
527
+ };
528
+
529
+ it('does not notify a session that is a candidate in only one cycle', async () => {
530
+ setupSilentGithubSession();
531
+ mockCandidateStateRepository.loadRecentCandidateSessionNames.mockResolvedValue(
532
+ new Set<string>(),
533
+ );
534
+
535
+ await useCase.run(runParams());
536
+
537
+ expect(
538
+ mockNotificationRepository.sendSelfCheckNotification,
539
+ ).not.toHaveBeenCalled();
540
+ });
541
+
542
+ it('records the current candidate set so a first-cycle candidate is remembered for the next cycle', async () => {
543
+ setupSilentGithubSession();
544
+ mockCandidateStateRepository.loadRecentCandidateSessionNames.mockResolvedValue(
545
+ new Set<string>(),
546
+ );
547
+
548
+ await useCase.run(runParams());
549
+
550
+ expect(
551
+ mockCandidateStateRepository.saveCandidateSessionNames,
552
+ ).toHaveBeenCalledWith({ sessionNames: [GITHUB_SESSION], now });
553
+ });
554
+
555
+ it('notifies a session that is a candidate in two consecutive cycles on the second cycle', async () => {
556
+ setupSilentGithubSession();
557
+ const previousCandidates = new Set<string>();
558
+ mockCandidateStateRepository.loadRecentCandidateSessionNames.mockImplementation(
559
+ async () => new Set(previousCandidates),
560
+ );
561
+ mockCandidateStateRepository.saveCandidateSessionNames.mockImplementation(
562
+ async ({ sessionNames }) => {
563
+ previousCandidates.clear();
564
+ for (const sessionName of sessionNames) {
565
+ previousCandidates.add(sessionName);
566
+ }
501
567
  },
502
- ],
503
- );
568
+ );
504
569
 
505
- await useCase.run(runParams());
506
- await useCase.run(runParams());
507
- await useCase.run(runParams());
570
+ await useCase.run(runParams());
571
+ expect(
572
+ mockNotificationRepository.sendSelfCheckNotification,
573
+ ).not.toHaveBeenCalled();
508
574
 
509
- expect(
510
- mockNotificationRepository.sendSelfCheckNotification,
511
- ).toHaveBeenCalledTimes(3);
512
- expect(Object.keys(mockNotificationRepository)).toEqual([
513
- 'sendSelfCheckNotification',
514
- ]);
515
- expect(runParams()).not.toHaveProperty('cooldownSeconds');
575
+ await useCase.run(runParams());
576
+ expect(
577
+ mockNotificationRepository.sendSelfCheckNotification,
578
+ ).toHaveBeenCalledTimes(1);
579
+ expect(
580
+ mockNotificationRepository.sendSelfCheckNotification,
581
+ ).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
582
+ });
583
+
584
+ it('suppresses the owner-reply-race candidate that was not a candidate in the previous cycle', async () => {
585
+ setupSilentGithubSession();
586
+ mockCandidateStateRepository.loadRecentCandidateSessionNames.mockResolvedValue(
587
+ new Set<string>(),
588
+ );
589
+
590
+ await useCase.run(runParams());
591
+
592
+ expect(
593
+ mockNotificationRepository.sendSelfCheckNotification,
594
+ ).not.toHaveBeenCalled();
595
+ expect(
596
+ mockMessageComposer.composeMainStalledSection,
597
+ ).toHaveBeenCalledWith(DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS);
598
+ });
599
+
600
+ it('loads the previous candidate set using the configured recency window', async () => {
601
+ setupSilentGithubSession();
602
+
603
+ await useCase.run(
604
+ runParams({ candidateDebounceRecencyWindowSeconds: 900 }),
605
+ );
606
+
607
+ expect(
608
+ mockCandidateStateRepository.loadRecentCandidateSessionNames,
609
+ ).toHaveBeenCalledWith({ now, recencyWindowSeconds: 900 });
610
+ });
516
611
  });
517
612
 
518
613
  it('sends to multiple sessions sequentially with a stagger delay between sends', async () => {
@@ -7,6 +7,7 @@ import { SessionOutputActivityRepository } from './adapter-interfaces/SessionOut
7
7
  import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionSubAgentActivityRepository';
8
8
  import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
9
9
  import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
10
+ import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
10
11
  import { Sleeper } from './adapter-interfaces/Sleeper';
11
12
  import { IssueRepository } from './adapter-interfaces/IssueRepository';
12
13
  import { ResolveInteractiveLiveSessionsUseCase } from './ResolveInteractiveLiveSessionsUseCase';
@@ -15,6 +16,7 @@ export const DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS = 10 * 60;
15
16
  export const DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS = 5 * 60;
16
17
  export const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS = 15 * 60;
17
18
  export const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
19
+ export const DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS = 15 * 60;
18
20
 
19
21
  const GITHUB_ISSUE_OR_PULL_URL_PATTERN =
20
22
  /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/(?:issues|pull)\/(\d+)$/;
@@ -63,6 +65,7 @@ export class NotifySilentLiveSessionsUseCase {
63
65
  private readonly subAgentActivityRepository: SessionSubAgentActivityRepository,
64
66
  private readonly ownerCallStatusProvider: OwnerCallStatusProvider,
65
67
  private readonly notificationRepository: SilentSessionNotificationRepository,
68
+ private readonly candidateStateRepository: SilentSessionCandidateStateRepository,
66
69
  private readonly messageComposer: SilentSessionMessageComposer,
67
70
  private readonly sleeper: Sleeper,
68
71
  private readonly hubTaskStatusResolver: HubTaskStatusResolver | null = null,
@@ -73,6 +76,7 @@ export class NotifySilentLiveSessionsUseCase {
73
76
  subAgentSilentThresholdSeconds: number;
74
77
  subAgentRunningThresholdSeconds: number;
75
78
  staggerSeconds: number;
79
+ candidateDebounceRecencyWindowSeconds: number;
76
80
  activeHubTaskStatus: string | null;
77
81
  now: Date;
78
82
  }): Promise<void> => {
@@ -116,12 +120,28 @@ export class NotifySilentLiveSessionsUseCase {
116
120
  : 0,
117
121
  );
118
122
 
123
+ const previousCandidateSessionNames =
124
+ await this.candidateStateRepository.loadRecentCandidateSessionNames({
125
+ now: params.now,
126
+ recencyWindowSeconds: params.candidateDebounceRecencyWindowSeconds,
127
+ });
128
+ await this.candidateStateRepository.saveCandidateSessionNames({
129
+ sessionNames: candidates.map((candidate) => candidate.sessionName),
130
+ now: params.now,
131
+ });
132
+
133
+ const debouncedCandidates = candidates.filter((candidate) =>
134
+ previousCandidateSessionNames.has(candidate.sessionName),
135
+ );
136
+ const suppressedFirstCycleCount =
137
+ candidates.length - debouncedCandidates.length;
138
+
119
139
  console.log(
120
- `Silent live session notification: ${candidates.length} candidate(s) of ${interactiveSessions.length} interactive session(s).`,
140
+ `Silent live session notification: ${debouncedCandidates.length} debounced candidate(s) of ${candidates.length} current candidate(s) across ${interactiveSessions.length} interactive session(s); ${suppressedFirstCycleCount} first-cycle candidate(s) deferred until they persist into the next cycle.`,
121
141
  );
122
142
 
123
143
  let sentCount = 0;
124
- for (const candidate of candidates) {
144
+ for (const candidate of debouncedCandidates) {
125
145
  if (
126
146
  !(await this.isHubTaskActive(
127
147
  candidate.sessionName,
@@ -0,0 +1,10 @@
1
+ export interface SilentSessionCandidateStateRepository {
2
+ loadRecentCandidateSessionNames: (params: {
3
+ now: Date;
4
+ recencyWindowSeconds: number;
5
+ }) => Promise<Set<string>>;
6
+ saveCandidateSessionNames: (params: {
7
+ sessionNames: string[];
8
+ now: Date;
9
+ }) => Promise<void>;
10
+ }
@@ -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,CAmiBP;CACH"}
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,CA8iBP;CACH"}
@@ -14,6 +14,8 @@ export type NotifySilentTmuxSessionsParams = {
14
14
  subAgentSilentThresholdSeconds: number;
15
15
  subAgentRunningThresholdSeconds: number;
16
16
  staggerSeconds: number;
17
+ candidateDebounceRecencyWindowSeconds: number;
18
+ candidateDebounceStateFilePath: string | null;
17
19
  activeHubTaskStatus: string | null;
18
20
  hubTaskStatusResolver: HubTaskStatusResolver | null;
19
21
  messageTemplates: SilentSessionMessageTemplates;
@@ -25,5 +27,6 @@ export declare const DEFAULT_NOTIFY_SILENT_TMUX_SESSIONS_PARAMS: {
25
27
  readonly subAgentSilentThresholdSeconds: number;
26
28
  readonly subAgentRunningThresholdSeconds: number;
27
29
  readonly staggerSeconds: 25;
30
+ readonly candidateDebounceRecencyWindowSeconds: number;
28
31
  };
29
32
  //# 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,EAKtB,MAAM,0DAA0D,CAAC;AAclE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAGrE,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,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACpD,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAoCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CAwDd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;CAK7C,CAAC"}
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,EAMtB,MAAM,0DAA0D,CAAC;AAclE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAIrE,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,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAoCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CAgEd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;CAO7C,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { SilentSessionCandidateStateRepository } from '../../domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository';
2
+ export declare const DEFAULT_STATE_RETENTION_WINDOW_SECONDS: number;
3
+ export declare class FileSystemSilentSessionCandidateStateRepository implements SilentSessionCandidateStateRepository {
4
+ private readonly stateFilePath;
5
+ private readonly retentionWindowSeconds;
6
+ constructor(stateFilePath?: string, retentionWindowSeconds?: number);
7
+ loadRecentCandidateSessionNames: (params: {
8
+ now: Date;
9
+ recencyWindowSeconds: number;
10
+ }) => Promise<Set<string>>;
11
+ saveCandidateSessionNames: (params: {
12
+ sessionNames: string[];
13
+ now: Date;
14
+ }) => Promise<void>;
15
+ private readEntries;
16
+ private writeEntries;
17
+ }
18
+ //# sourceMappingURL=FileSystemSilentSessionCandidateStateRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileSystemSilentSessionCandidateStateRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,qCAAqC,EAAE,MAAM,gFAAgF,CAAC;AAUvI,eAAO,MAAM,sCAAsC,QAAU,CAAC;AAO9D,qBAAa,+CAAgD,YAAW,qCAAqC;IAEzG,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBADtB,aAAa,GAAE,MAA+B,EAC9C,sBAAsB,GAAE,MAA+C;IAG1F,+BAA+B,GAAU,QAAQ;QAC/C,GAAG,EAAE,IAAI,CAAC;QACV,oBAAoB,EAAE,MAAM,CAAC;KAC9B,KAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAYtB;IAEF,yBAAyB,GAAU,QAAQ;QACzC,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CAqBf;IAEF,OAAO,CAAC,WAAW,CAoCjB;IAEF,OAAO,CAAC,YAAY,CAMlB;CACH"}
@@ -5,12 +5,14 @@ import { SessionOutputActivityRepository } from './adapter-interfaces/SessionOut
5
5
  import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionSubAgentActivityRepository';
6
6
  import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
7
7
  import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
8
+ import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
8
9
  import { Sleeper } from './adapter-interfaces/Sleeper';
9
10
  import { IssueRepository } from './adapter-interfaces/IssueRepository';
10
11
  export declare const DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS: number;
11
12
  export declare const DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS: number;
12
13
  export declare const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS: number;
13
14
  export declare const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
15
+ export declare const DEFAULT_CANDIDATE_DEBOUNCE_RECENCY_WINDOW_SECONDS: number;
14
16
  export declare const parseHubTaskIssueUrlFromSessionName: (sessionName: string) => string | null;
15
17
  export declare const isGitHubIssueOrPullRequestSessionName: (sessionName: string) => boolean;
16
18
  export type HubTaskStatusResolver = Pick<IssueRepository, 'getIssueByUrl'>;
@@ -21,16 +23,18 @@ export declare class NotifySilentLiveSessionsUseCase {
21
23
  private readonly subAgentActivityRepository;
22
24
  private readonly ownerCallStatusProvider;
23
25
  private readonly notificationRepository;
26
+ private readonly candidateStateRepository;
24
27
  private readonly messageComposer;
25
28
  private readonly sleeper;
26
29
  private readonly hubTaskStatusResolver;
27
30
  private readonly resolveInteractiveLiveSessions;
28
- constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper, hubTaskStatusResolver?: HubTaskStatusResolver | null);
31
+ constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, candidateStateRepository: SilentSessionCandidateStateRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper, hubTaskStatusResolver?: HubTaskStatusResolver | null);
29
32
  run: (params: {
30
33
  mainSilentThresholdSeconds: number;
31
34
  subAgentSilentThresholdSeconds: number;
32
35
  subAgentRunningThresholdSeconds: number;
33
36
  staggerSeconds: number;
37
+ candidateDebounceRecencyWindowSeconds: number;
34
38
  activeHubTaskStatus: string | null;
35
39
  now: Date;
36
40
  }) => Promise<void>;
@@ -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,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;AAQvD,eAAO,MAAM,mCAAmC,GAC9C,aAAa,MAAM,KAClB,MAAM,GAAG,IAWX,CAAC;AAKF,eAAO,MAAM,qCAAqC,GAChD,aAAa,MAAM,KAClB,OACkE,CAAC;AAEtE,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;AAO3E,qBAAa,+BAA+B;IAKxC,OAAO,CAAC,QAAQ,CAAC,kCAAkC;IACnD,OAAO,CAAC,QAAQ,CAAC,wCAAwC;IACzD,OAAO,CAAC,QAAQ,CAAC,+BAA+B;IAChD,OAAO,CAAC,QAAQ,CAAC,0BAA0B;IAC3C,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAZxC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CACD;gBAG3B,kCAAkC,EAAE,kCAAkC,EACtE,wCAAwC,EAAE,wCAAwC,EAClF,+BAA+B,EAAE,+BAA+B,EAChE,0BAA0B,EAAE,iCAAiC,EAC7D,uBAAuB,EAAE,uBAAuB,EAChD,sBAAsB,EAAE,mCAAmC,EAC3D,eAAe,EAAE,4BAA4B,EAC7C,OAAO,EAAE,OAAO,EAChB,qBAAqB,GAAE,qBAAqB,GAAG,IAAW;IAG7E,GAAG,GAAU,QAAQ;QACnB,0BAA0B,EAAE,MAAM,CAAC;QACnC,8BAA8B,EAAE,MAAM,CAAC;QACvC,+BAA+B,EAAE,MAAM,CAAC;QACxC,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CAiEf;IAEF,OAAO,CAAC,eAAe,CAmCrB;IAEF,OAAO,CAAC,gBAAgB,CA+CtB;IAEF,OAAO,CAAC,cAAc,CAqCpB;CACH"}
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;AAQzE,eAAO,MAAM,mCAAmC,GAC9C,aAAa,MAAM,KAClB,MAAM,GAAG,IAWX,CAAC;AAKF,eAAO,MAAM,qCAAqC,GAChD,aAAa,MAAM,KAClB,OACkE,CAAC;AAEtE,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;AAO3E,qBAAa,+BAA+B;IAKxC,OAAO,CAAC,QAAQ,CAAC,kCAAkC;IACnD,OAAO,CAAC,QAAQ,CAAC,wCAAwC;IACzD,OAAO,CAAC,QAAQ,CAAC,+BAA+B;IAChD,OAAO,CAAC,QAAQ,CAAC,0BAA0B;IAC3C,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAbxC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CACD;gBAG3B,kCAAkC,EAAE,kCAAkC,EACtE,wCAAwC,EAAE,wCAAwC,EAClF,+BAA+B,EAAE,+BAA+B,EAChE,0BAA0B,EAAE,iCAAiC,EAC7D,uBAAuB,EAAE,uBAAuB,EAChD,sBAAsB,EAAE,mCAAmC,EAC3D,wBAAwB,EAAE,qCAAqC,EAC/D,eAAe,EAAE,4BAA4B,EAC7C,OAAO,EAAE,OAAO,EAChB,qBAAqB,GAAE,qBAAqB,GAAG,IAAW;IAG7E,GAAG,GAAU,QAAQ;QACnB,0BAA0B,EAAE,MAAM,CAAC;QACnC,8BAA8B,EAAE,MAAM,CAAC;QACvC,+BAA+B,EAAE,MAAM,CAAC;QACxC,cAAc,EAAE,MAAM,CAAC;QACvB,qCAAqC,EAAE,MAAM,CAAC;QAC9C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CAiFf;IAEF,OAAO,CAAC,eAAe,CAmCrB;IAEF,OAAO,CAAC,gBAAgB,CA+CtB;IAEF,OAAO,CAAC,cAAc,CAqCpB;CACH"}
@@ -0,0 +1,11 @@
1
+ export interface SilentSessionCandidateStateRepository {
2
+ loadRecentCandidateSessionNames: (params: {
3
+ now: Date;
4
+ recencyWindowSeconds: number;
5
+ }) => Promise<Set<string>>;
6
+ saveCandidateSessionNames: (params: {
7
+ sessionNames: string[];
8
+ now: Date;
9
+ }) => Promise<void>;
10
+ }
11
+ //# sourceMappingURL=SilentSessionCandidateStateRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SilentSessionCandidateStateRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qCAAqC;IACpD,+BAA+B,EAAE,CAAC,MAAM,EAAE;QACxC,GAAG,EAAE,IAAI,CAAC;QACV,oBAAoB,EAAE,MAAM,CAAC;KAC9B,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3B,yBAAyB,EAAE,CAAC,MAAM,EAAE;QAClC,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,GAAG,EAAE,IAAI,CAAC;KACX,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB"}