github-issue-tower-defence-management 1.114.1 → 1.115.0

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 (32) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +5 -5
  3. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +10 -2
  4. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  5. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js +3 -2
  6. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
  7. package/bin/adapter/repositories/FileSystemSessionOutputActivityRepository.js +14 -14
  8. package/bin/adapter/repositories/FileSystemSessionOutputActivityRepository.js.map +1 -1
  9. package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +36 -2
  10. package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
  11. package/bin/domain/usecases/ResolveInteractiveLiveSessionsUseCase.js +34 -6
  12. package/bin/domain/usecases/ResolveInteractiveLiveSessionsUseCase.js.map +1 -1
  13. package/package.json +1 -1
  14. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +6 -0
  15. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +120 -0
  16. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +7 -0
  17. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +46 -0
  18. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.test.ts +57 -5
  19. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts +14 -14
  20. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +188 -1
  21. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +59 -0
  22. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.test.ts +76 -0
  23. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts +37 -6
  24. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  25. package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts +3 -0
  26. package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts.map +1 -1
  27. package/types/adapter/repositories/FileSystemSessionOutputActivityRepository.d.ts +8 -5
  28. package/types/adapter/repositories/FileSystemSessionOutputActivityRepository.d.ts.map +1 -1
  29. package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +7 -1
  30. package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
  31. package/types/domain/usecases/ResolveInteractiveLiveSessionsUseCase.d.ts +12 -0
  32. package/types/domain/usecases/ResolveInteractiveLiveSessionsUseCase.d.ts.map +1 -1
@@ -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 { Sleeper } from './adapter-interfaces/Sleeper';
11
+ import { IssueRepository } from './adapter-interfaces/IssueRepository';
11
12
  import { ResolveInteractiveLiveSessionsUseCase } from './ResolveInteractiveLiveSessionsUseCase';
12
13
 
13
14
  export const DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS = 10 * 60;
@@ -16,6 +17,17 @@ export const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS = 15 * 60;
16
17
  export const DEFAULT_NOTIFICATION_COOLDOWN_SECONDS = 30 * 60;
17
18
  export const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
18
19
 
20
+ const GITHUB_ISSUE_URL_PATTERN =
21
+ /^https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/\d+$/;
22
+
23
+ export const parseHubTaskIssueUrlFromSessionName = (
24
+ sessionName: string,
25
+ ): string | null => {
26
+ return GITHUB_ISSUE_URL_PATTERN.test(sessionName) ? sessionName : null;
27
+ };
28
+
29
+ export type HubTaskStatusResolver = Pick<IssueRepository, 'getIssueByUrl'>;
30
+
19
31
  type NotifyCandidate = {
20
32
  sessionName: string;
21
33
  message: string;
@@ -34,6 +46,7 @@ export class NotifySilentLiveSessionsUseCase {
34
46
  private readonly notificationRepository: SilentSessionNotificationRepository,
35
47
  private readonly messageComposer: SilentSessionMessageComposer,
36
48
  private readonly sleeper: Sleeper,
49
+ private readonly hubTaskStatusResolver: HubTaskStatusResolver | null = null,
37
50
  ) {}
38
51
 
39
52
  run = async (params: {
@@ -42,6 +55,7 @@ export class NotifySilentLiveSessionsUseCase {
42
55
  subAgentRunningThresholdSeconds: number;
43
56
  cooldownSeconds: number;
44
57
  staggerSeconds: number;
58
+ activeHubTaskStatus: string | null;
45
59
  now: Date;
46
60
  }): Promise<void> => {
47
61
  const snapshot =
@@ -96,6 +110,14 @@ export class NotifySilentLiveSessionsUseCase {
96
110
  );
97
111
  continue;
98
112
  }
113
+ if (
114
+ !(await this.isHubTaskActive(
115
+ candidate.sessionName,
116
+ params.activeHubTaskStatus,
117
+ ))
118
+ ) {
119
+ continue;
120
+ }
99
121
  if (sentCount > 0) {
100
122
  await this.sleeper.sleep(params.staggerSeconds * 1000);
101
123
  }
@@ -112,6 +134,43 @@ export class NotifySilentLiveSessionsUseCase {
112
134
  }
113
135
  };
114
136
 
137
+ private isHubTaskActive = async (
138
+ sessionName: string,
139
+ activeHubTaskStatus: string | null,
140
+ ): Promise<boolean> => {
141
+ if (activeHubTaskStatus === null || this.hubTaskStatusResolver === null) {
142
+ return true;
143
+ }
144
+ const hubTaskIssueUrl = parseHubTaskIssueUrlFromSessionName(sessionName);
145
+ if (hubTaskIssueUrl === null) {
146
+ return true;
147
+ }
148
+ try {
149
+ const issue =
150
+ await this.hubTaskStatusResolver.getIssueByUrl(hubTaskIssueUrl);
151
+ if (issue === null) {
152
+ console.warn(
153
+ `Hub task ${hubTaskIssueUrl} for session ${sessionName} could not be resolved; sending notification (fail-open).`,
154
+ );
155
+ return true;
156
+ }
157
+ if (issue.state !== 'OPEN' || issue.status !== activeHubTaskStatus) {
158
+ console.log(
159
+ `Skipping ${sessionName}: hub task ${hubTaskIssueUrl} is no longer active (state "${issue.state}", status "${issue.status ?? 'null'}", active status "${activeHubTaskStatus}").`,
160
+ );
161
+ return false;
162
+ }
163
+ return true;
164
+ } catch (error) {
165
+ console.warn(
166
+ `Failed to resolve hub task status for session ${sessionName} (${hubTaskIssueUrl}); sending notification (fail-open): ${
167
+ error instanceof Error ? error.message : String(error)
168
+ }`,
169
+ );
170
+ return true;
171
+ }
172
+ };
173
+
115
174
  private collectSnapshots = async (
116
175
  interactiveSessions: InteractiveLiveSession[],
117
176
  transcriptPathBySessionName: Map<string, string>,
@@ -219,6 +219,82 @@ describe('ResolveInteractiveLiveSessionsUseCase', () => {
219
219
  ]);
220
220
  });
221
221
 
222
+ it('collects the descendant-propagated session id after the own launch id for a non-resume session', () => {
223
+ const snapshot: LiveSessionProcessSnapshot = {
224
+ sessions: [{ sessionName: 'non-resume', panePids: [900] }],
225
+ processes: [
226
+ processInfo({ pid: 900, ppid: 1, commandLine: 'shell' }),
227
+ processInfo({
228
+ pid: 901,
229
+ ppid: 900,
230
+ commandLine: 'claude --model opus',
231
+ sessionId: 'launch-id',
232
+ configDir: '/config/non-resume',
233
+ }),
234
+ processInfo({
235
+ pid: 902,
236
+ ppid: 901,
237
+ commandLine: 'node tool worker',
238
+ sessionId: 'harness-id',
239
+ configDir: '/config/non-resume',
240
+ }),
241
+ processInfo({
242
+ pid: 903,
243
+ ppid: 902,
244
+ commandLine: 'node nested worker',
245
+ sessionId: 'harness-id',
246
+ configDir: '/config/non-resume',
247
+ }),
248
+ ],
249
+ };
250
+
251
+ const result = useCase.resolve(snapshot);
252
+
253
+ expect(result).toEqual([
254
+ {
255
+ sessionName: 'non-resume',
256
+ sessionId: 'launch-id',
257
+ candidateSessionIds: ['launch-id', 'harness-id'],
258
+ configDir: '/config/non-resume',
259
+ },
260
+ ]);
261
+ });
262
+
263
+ it('orders the rotated current id, then the launch id, then the descendant id', () => {
264
+ const snapshot: LiveSessionProcessSnapshot = {
265
+ sessions: [{ sessionName: 'non-resume', panePids: [910] }],
266
+ processes: [
267
+ processInfo({ pid: 910, ppid: 1, commandLine: 'shell' }),
268
+ processInfo({
269
+ pid: 911,
270
+ ppid: 910,
271
+ commandLine: 'claude --model opus',
272
+ sessionId: 'launch-id',
273
+ currentSessionId: 'rotated-id',
274
+ configDir: '/config/non-resume',
275
+ }),
276
+ processInfo({
277
+ pid: 912,
278
+ ppid: 911,
279
+ commandLine: 'node tool worker',
280
+ sessionId: 'harness-id',
281
+ configDir: '/config/non-resume',
282
+ }),
283
+ ],
284
+ };
285
+
286
+ const result = useCase.resolve(snapshot);
287
+
288
+ expect(result).toEqual([
289
+ {
290
+ sessionName: 'non-resume',
291
+ sessionId: 'launch-id',
292
+ candidateSessionIds: ['rotated-id', 'launch-id', 'harness-id'],
293
+ configDir: '/config/non-resume',
294
+ },
295
+ ]);
296
+ });
297
+
222
298
  it('returns an empty list when there are no sessions', () => {
223
299
  const result = useCase.resolve({ sessions: [], processes: [] });
224
300
 
@@ -66,30 +66,61 @@ export class ResolveInteractiveLiveSessionsUseCase {
66
66
  ) {
67
67
  continue;
68
68
  }
69
+ const candidateSessionIds = this.collectCandidateSessionIds(
70
+ interactiveProcess,
71
+ childrenByPpid,
72
+ );
69
73
  sessions.push({
70
74
  sessionName: session.sessionName,
71
75
  sessionId: interactiveProcess.sessionId,
72
- candidateSessionIds:
73
- this.collectCandidateSessionIds(interactiveProcess),
76
+ candidateSessionIds,
74
77
  configDir: interactiveProcess.configDir,
75
78
  });
76
79
  }
77
80
  return sessions;
78
81
  };
79
82
 
83
+ /**
84
+ * Collects the distinct session ids that may name the actively-written
85
+ * transcript on disk, in priority order. The rotated current session id
86
+ * recorded for the interactive process is first, because for a resumed or
87
+ * compacted session the id rotates and the live transcript is named by the
88
+ * current id. The interactive process's own launch id follows, then the
89
+ * distinct ids propagated to its descendant processes. For a `--resume`
90
+ * session these ids coincide, yielding a single id; for a non-resume session
91
+ * the own (launch) id names no transcript and the live transcript is named by
92
+ * the descendant-propagated id, which is included here so the resolver can
93
+ * find the actively-written file.
94
+ */
80
95
  private collectCandidateSessionIds = (
81
96
  interactiveProcess: LiveSessionProcessInfo,
97
+ childrenByPpid: Map<number, LiveSessionProcessInfo[]>,
82
98
  ): string[] => {
83
99
  const candidateSessionIds: string[] = [];
84
100
  const seenSessionIds = new Set<string>();
85
- for (const sessionId of [
86
- interactiveProcess.currentSessionId,
87
- interactiveProcess.sessionId,
88
- ]) {
101
+ const pushSessionId = (sessionId: string | null): void => {
89
102
  if (sessionId !== null && !seenSessionIds.has(sessionId)) {
90
103
  seenSessionIds.add(sessionId);
91
104
  candidateSessionIds.push(sessionId);
92
105
  }
106
+ };
107
+ pushSessionId(interactiveProcess.currentSessionId);
108
+ const visitedPids = new Set<number>();
109
+ const queue: LiveSessionProcessInfo[] = [interactiveProcess];
110
+ let head = 0;
111
+ while (head < queue.length) {
112
+ const process = queue[head];
113
+ head += 1;
114
+ if (visitedPids.has(process.pid)) {
115
+ continue;
116
+ }
117
+ visitedPids.add(process.pid);
118
+ pushSessionId(process.sessionId);
119
+ for (const child of childrenByPpid.get(process.pid) ?? []) {
120
+ if (!visitedPids.has(child.pid)) {
121
+ queue.push(child);
122
+ }
123
+ }
93
124
  }
94
125
  return candidateSessionIds;
95
126
  };
@@ -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;AA0D3D,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,CAyiBP;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;AA0D3D,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,CA+iBP;CACH"}
@@ -1,5 +1,6 @@
1
1
  import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
2
2
  import { ProcessEnvironReader } from '../../../domain/usecases/adapter-interfaces/ProcessEnvironReader';
3
+ import { HubTaskStatusResolver } from '../../../domain/usecases/NotifySilentLiveSessionsUseCase';
3
4
  import { LocalStorageCacheRepository } from '../../repositories/LocalStorageCacheRepository';
4
5
  import { SilentSessionMessageTemplates } from '../../repositories/ConfigurableSilentSessionMessageComposer';
5
6
  export type NotifySilentTmuxSessionsParams = {
@@ -16,6 +17,8 @@ export type NotifySilentTmuxSessionsParams = {
16
17
  subAgentRunningThresholdSeconds: number;
17
18
  cooldownSeconds: number;
18
19
  staggerSeconds: number;
20
+ activeHubTaskStatus: string | null;
21
+ hubTaskStatusResolver: HubTaskStatusResolver | null;
19
22
  messageTemplates: SilentSessionMessageTemplates;
20
23
  now: Date;
21
24
  };
@@ -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;AAexG,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAQ7F,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,IAAI,CAAC,2BAA2B,EAAE,WAAW,GAAG,KAAK,CAAC,CAAC;IACxE,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,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAoCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CA0Dd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;CAM7C,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;AAOlE,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAQ7F,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,IAAI,CAAC,2BAA2B,EAAE,WAAW,GAAG,KAAK,CAAC,CAAC;IACxE,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,eAAe,EAAE,MAAM,CAAC;IACxB,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,CA8Dd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;CAM7C,CAAC"}
@@ -1,14 +1,17 @@
1
1
  import { LiveSessionOutputActivity } from '../../domain/entities/LiveSessionOutputActivity';
2
2
  import { SessionOutputActivityRepository } from '../../domain/usecases/adapter-interfaces/SessionOutputActivityRepository';
3
3
  /**
4
- * Reads the last main-session output time for each live session from its
4
+ * Reads the last main-session activity time for each live session from its
5
5
  * already-resolved transcript path. Idle time is computed from the timestamp of
6
- * the latest `assistant` entry rather than from the transcript file modification
7
- * time, so a transcript touched only by tool results or owner replies still
8
- * counts as silent.
6
+ * the latest entry of any kind (assistant text, owner replies, tool results, or
7
+ * any other entry type) rather than from the transcript file modification time.
8
+ * Because a session that is actively running tool calls keeps appending entries
9
+ * such as `user` and `tool_result` even while it emits no assistant text, every
10
+ * entry with a parseable timestamp counts as activity, so a working session is
11
+ * not mistaken for a silent one.
9
12
  */
10
13
  export declare class FileSystemSessionOutputActivityRepository implements SessionOutputActivityRepository {
11
14
  listSessionOutputActivities: (transcriptPathBySessionName: Map<string, string>) => Promise<LiveSessionOutputActivity[]>;
12
- private readLastAssistantOutputEpochSeconds;
15
+ private readLastActivityEpochSeconds;
13
16
  }
14
17
  //# sourceMappingURL=FileSystemSessionOutputActivityRepository.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileSystemSessionOutputActivityRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAC5F,OAAO,EAAE,+BAA+B,EAAE,MAAM,0EAA0E,CAAC;AAqB3H;;;;;;GAMG;AACH,qBAAa,yCAA0C,YAAW,+BAA+B;IAC/F,2BAA2B,GACzB,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAC/C,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAUrC;IAEF,OAAO,CAAC,mCAAmC,CAuCzC;CACH"}
1
+ {"version":3,"file":"FileSystemSessionOutputActivityRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAC5F,OAAO,EAAE,+BAA+B,EAAE,MAAM,0EAA0E,CAAC;AAqB3H;;;;;;;;;GASG;AACH,qBAAa,yCAA0C,YAAW,+BAA+B;IAC/F,2BAA2B,GACzB,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAC/C,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAUrC;IAEF,OAAO,CAAC,4BAA4B,CAoClC;CACH"}
@@ -6,11 +6,14 @@ 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 { Sleeper } from './adapter-interfaces/Sleeper';
9
+ import { IssueRepository } from './adapter-interfaces/IssueRepository';
9
10
  export declare const DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS: number;
10
11
  export declare const DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS: number;
11
12
  export declare const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS: number;
12
13
  export declare const DEFAULT_NOTIFICATION_COOLDOWN_SECONDS: number;
13
14
  export declare const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
15
+ export declare const parseHubTaskIssueUrlFromSessionName: (sessionName: string) => string | null;
16
+ export type HubTaskStatusResolver = Pick<IssueRepository, 'getIssueByUrl'>;
14
17
  export declare class NotifySilentLiveSessionsUseCase {
15
18
  private readonly liveSessionProcessSnapshotProvider;
16
19
  private readonly interactiveLiveSessionTranscriptResolver;
@@ -20,16 +23,19 @@ export declare class NotifySilentLiveSessionsUseCase {
20
23
  private readonly notificationRepository;
21
24
  private readonly messageComposer;
22
25
  private readonly sleeper;
26
+ private readonly hubTaskStatusResolver;
23
27
  private readonly resolveInteractiveLiveSessions;
24
- constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper);
28
+ constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper, hubTaskStatusResolver?: HubTaskStatusResolver | null);
25
29
  run: (params: {
26
30
  mainSilentThresholdSeconds: number;
27
31
  subAgentSilentThresholdSeconds: number;
28
32
  subAgentRunningThresholdSeconds: number;
29
33
  cooldownSeconds: number;
30
34
  staggerSeconds: number;
35
+ activeHubTaskStatus: string | null;
31
36
  now: Date;
32
37
  }) => Promise<void>;
38
+ private isHubTaskActive;
33
39
  private collectSnapshots;
34
40
  private composeMessage;
35
41
  }
@@ -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;AAGvD,eAAO,MAAM,qCAAqC,QAAU,CAAC;AAC7D,eAAO,MAAM,yCAAyC,QAAS,CAAC;AAChE,eAAO,MAAM,0CAA0C,QAAU,CAAC;AAClE,eAAO,MAAM,qCAAqC,QAAU,CAAC;AAC7D,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAOvD,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;IAX1B,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;IAGnC,GAAG,GAAU,QAAQ;QACnB,0BAA0B,EAAE,MAAM,CAAC;QACnC,8BAA8B,EAAE,MAAM,CAAC;QACvC,+BAA+B,EAAE,MAAM,CAAC;QACxC,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CAmEf;IAEF,OAAO,CAAC,gBAAgB,CA8CtB;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,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,qCAAqC,QAAU,CAAC;AAC7D,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAKvD,eAAO,MAAM,mCAAmC,GAC9C,aAAa,MAAM,KAClB,MAAM,GAAG,IAEX,CAAC;AAEF,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,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CA2Ef;IAEF,OAAO,CAAC,eAAe,CAmCrB;IAEF,OAAO,CAAC,gBAAgB,CA8CtB;IAEF,OAAO,CAAC,cAAc,CAqCpB;CACH"}
@@ -18,6 +18,18 @@ export declare const OWNER_HANDOVER_COMMAND_MARKER = "Take ownership of";
18
18
  */
19
19
  export declare class ResolveInteractiveLiveSessionsUseCase {
20
20
  resolve: (snapshot: LiveSessionProcessSnapshot) => InteractiveLiveSession[];
21
+ /**
22
+ * Collects the distinct session ids that may name the actively-written
23
+ * transcript on disk, in priority order. The rotated current session id
24
+ * recorded for the interactive process is first, because for a resumed or
25
+ * compacted session the id rotates and the live transcript is named by the
26
+ * current id. The interactive process's own launch id follows, then the
27
+ * distinct ids propagated to its descendant processes. For a `--resume`
28
+ * session these ids coincide, yielding a single id; for a non-resume session
29
+ * the own (launch) id names no transcript and the live transcript is named by
30
+ * the descendant-propagated id, which is included here so the resolver can
31
+ * find the actively-written file.
32
+ */
21
33
  private collectCandidateSessionIds;
22
34
  private findInteractiveProcess;
23
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ResolveInteractiveLiveSessionsUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAEL,0BAA0B,EAC3B,MAAM,wCAAwC,CAAC;AAOhD,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAejE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qCAAqC;IAChD,OAAO,GACL,UAAU,0BAA0B,KACnC,sBAAsB,EAAE,CAiCzB;IAEF,OAAO,CAAC,0BAA0B,CAehC;IAEF,OAAO,CAAC,sBAAsB,CA0B5B;CACH"}
1
+ {"version":3,"file":"ResolveInteractiveLiveSessionsUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAEL,0BAA0B,EAC3B,MAAM,wCAAwC,CAAC;AAOhD,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAejE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qCAAqC;IAChD,OAAO,GACL,UAAU,0BAA0B,KACnC,sBAAsB,EAAE,CAoCzB;IAEF;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,0BAA0B,CA+BhC;IAEF,OAAO,CAAC,sBAAsB,CA0B5B;CACH"}