github-issue-tower-defence-management 1.122.4 → 1.122.5
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 -1
- 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 +3 -1
- package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
- package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js +6 -0
- package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js.map +1 -1
- package/bin/adapter/repositories/NoUnansweredOwnerCallStatusProvider.js +2 -2
- package/bin/adapter/repositories/NoUnansweredOwnerCallStatusProvider.js.map +1 -1
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js +14 -11
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js.map +1 -1
- package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js +13 -0
- package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js.map +1 -1
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +15 -5
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +11 -0
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +46 -7
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +5 -0
- package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +42 -0
- package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +16 -0
- package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +1 -1
- package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.test.ts +7 -6
- package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts +3 -3
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +99 -49
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +24 -14
- package/src/domain/entities/LiveSessionActivitySnapshot.ts +1 -1
- package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +75 -0
- package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +26 -0
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +106 -10
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +24 -6
- package/src/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.ts +2 -2
- package/src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts +4 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts +2 -0
- package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts.map +1 -1
- package/types/adapter/repositories/ConfigurableSilentSessionMessageComposer.d.ts +2 -0
- package/types/adapter/repositories/ConfigurableSilentSessionMessageComposer.d.ts.map +1 -1
- package/types/adapter/repositories/NoUnansweredOwnerCallStatusProvider.d.ts +1 -1
- package/types/adapter/repositories/NoUnansweredOwnerCallStatusProvider.d.ts.map +1 -1
- package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts +2 -2
- package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts.map +1 -1
- package/types/domain/entities/LiveSessionActivitySnapshot.d.ts +1 -1
- package/types/domain/entities/LiveSessionActivitySnapshot.d.ts.map +1 -1
- package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts +1 -0
- package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts.map +1 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +2 -0
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.d.ts +1 -1
- package/types/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.d.ts +1 -0
- package/types/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.d.ts.map +1 -1
|
@@ -91,31 +91,41 @@ const hasOwnerTextReply = (content: unknown): boolean => {
|
|
|
91
91
|
export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvider {
|
|
92
92
|
constructor(private readonly ownerCallMarker: string | null) {}
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
listUnansweredOwnerCallEpochSecondsBySessionName = async (
|
|
95
95
|
transcriptPathBySessionName: Map<string, string>,
|
|
96
|
-
): Promise<
|
|
97
|
-
const
|
|
96
|
+
): Promise<Map<string, number>> => {
|
|
97
|
+
const unansweredOwnerCallEpochSecondsBySessionName = new Map<
|
|
98
|
+
string,
|
|
99
|
+
number
|
|
100
|
+
>();
|
|
98
101
|
if (this.ownerCallMarker === null || this.ownerCallMarker.length === 0) {
|
|
99
|
-
return
|
|
102
|
+
return unansweredOwnerCallEpochSecondsBySessionName;
|
|
100
103
|
}
|
|
101
104
|
const marker = this.ownerCallMarker;
|
|
102
105
|
for (const [sessionName, transcriptPath] of transcriptPathBySessionName) {
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
const unansweredOwnerCallEpochMs = this.findUnansweredOwnerCallEpochMs(
|
|
107
|
+
transcriptPath,
|
|
108
|
+
marker,
|
|
109
|
+
);
|
|
110
|
+
if (unansweredOwnerCallEpochMs !== null) {
|
|
111
|
+
unansweredOwnerCallEpochSecondsBySessionName.set(
|
|
112
|
+
sessionName,
|
|
113
|
+
Math.floor(unansweredOwnerCallEpochMs / 1000),
|
|
114
|
+
);
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
|
-
return
|
|
117
|
+
return unansweredOwnerCallEpochSecondsBySessionName;
|
|
108
118
|
};
|
|
109
119
|
|
|
110
|
-
private
|
|
120
|
+
private findUnansweredOwnerCallEpochMs = (
|
|
111
121
|
transcriptPath: string,
|
|
112
122
|
marker: string,
|
|
113
|
-
):
|
|
123
|
+
): number | null => {
|
|
114
124
|
let content: string;
|
|
115
125
|
try {
|
|
116
126
|
content = fs.readFileSync(transcriptPath, 'utf8');
|
|
117
127
|
} catch {
|
|
118
|
-
return
|
|
128
|
+
return null;
|
|
119
129
|
}
|
|
120
130
|
let lastOwnerCallEpochMs: number | null = null;
|
|
121
131
|
let lastOwnerReplyEpochMs: number | null = null;
|
|
@@ -155,11 +165,11 @@ export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvide
|
|
|
155
165
|
}
|
|
156
166
|
}
|
|
157
167
|
if (lastOwnerCallEpochMs === null) {
|
|
158
|
-
return
|
|
168
|
+
return null;
|
|
159
169
|
}
|
|
160
|
-
return
|
|
161
|
-
lastOwnerReplyEpochMs === null ||
|
|
170
|
+
return lastOwnerReplyEpochMs === null ||
|
|
162
171
|
lastOwnerCallEpochMs > lastOwnerReplyEpochMs
|
|
163
|
-
|
|
172
|
+
? lastOwnerCallEpochMs
|
|
173
|
+
: null;
|
|
164
174
|
};
|
|
165
175
|
}
|
|
@@ -116,6 +116,81 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
116
116
|
expect(section).not.toContain('""');
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
+
it('embeds the reminder sentinel in the stale-owner-call main-stalled section', () => {
|
|
120
|
+
const section = composer.composeMainStalledWithStaleOwnerCallSection(
|
|
121
|
+
600,
|
|
122
|
+
3600,
|
|
123
|
+
);
|
|
124
|
+
expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('renders the stale-owner-call section with the silent and owner-call-age minutes substituted', () => {
|
|
128
|
+
const section = composer.composeMainStalledWithStaleOwnerCallSection(
|
|
129
|
+
659,
|
|
130
|
+
3659,
|
|
131
|
+
);
|
|
132
|
+
expect(section).toContain(
|
|
133
|
+
'You have produced no output for 10 minutes, and the owner call you raised 60 minutes ago is still unanswered.',
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('directs the agent to re-raise its pending ask as a fresh fully self-contained owner call', () => {
|
|
138
|
+
const section = composer.composeMainStalledWithStaleOwnerCallSection(
|
|
139
|
+
600,
|
|
140
|
+
3600,
|
|
141
|
+
);
|
|
142
|
+
expect(section).toContain(
|
|
143
|
+
'If you are still blocked on the owner, re-raise your pending ask NOW as a fresh owner call.',
|
|
144
|
+
);
|
|
145
|
+
expect(section).toContain(
|
|
146
|
+
'Make it fully self-contained: restate the whole situation — what happened, what you are asking, and any decision needed — inside the new owner call itself',
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('directs the agent to continue autonomously when it is no longer blocked', () => {
|
|
151
|
+
const section = composer.composeMainStalledWithStaleOwnerCallSection(
|
|
152
|
+
600,
|
|
153
|
+
3600,
|
|
154
|
+
);
|
|
155
|
+
expect(section).toContain(
|
|
156
|
+
'If you are no longer blocked — the answer became unnecessary, you can proceed safely, or the information arrived another way — resume immediately and drive all remaining tasks to completion.',
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('states that the stale-owner-call reminder itself does not notify the owner', () => {
|
|
161
|
+
const section = composer.composeMainStalledWithStaleOwnerCallSection(
|
|
162
|
+
600,
|
|
163
|
+
3600,
|
|
164
|
+
);
|
|
165
|
+
expect(section).toContain(
|
|
166
|
+
'This reminder does not notify the owner; only a fresh owner call from you surfaces this session to the owner.',
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('interpolates the configured owner-call marker into the stale-owner-call format guidance', () => {
|
|
171
|
+
const markedComposer = new DefaultSilentSessionMessageComposer(
|
|
172
|
+
'<<OWNER_CALL>>',
|
|
173
|
+
);
|
|
174
|
+
const section = markedComposer.composeMainStalledWithStaleOwnerCallSection(
|
|
175
|
+
600,
|
|
176
|
+
3600,
|
|
177
|
+
);
|
|
178
|
+
expect(section).toContain(
|
|
179
|
+
'the configured owner-call marker tag "<<OWNER_CALL>>" as a complete matching pair',
|
|
180
|
+
);
|
|
181
|
+
expect(section).toContain('🔴');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('composes the stale-owner-call section distinctly from the plain main-stalled section', () => {
|
|
185
|
+
const plainSection = composer.composeMainStalledSection(600);
|
|
186
|
+
const staleSection = composer.composeMainStalledWithStaleOwnerCallSection(
|
|
187
|
+
600,
|
|
188
|
+
3600,
|
|
189
|
+
);
|
|
190
|
+
expect(staleSection).not.toBe(plainSection);
|
|
191
|
+
expect(plainSection).not.toContain('is still unanswered');
|
|
192
|
+
});
|
|
193
|
+
|
|
119
194
|
it('emits a distinct idle message for a sub-agent that is only output-idle', () => {
|
|
120
195
|
const section = composer.composeSubAgentSection({
|
|
121
196
|
idleSubAgents: [
|
|
@@ -67,6 +67,21 @@ const composeMainStalledMessage = (
|
|
|
67
67
|
].join('\n');
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
const composeMainStalledWithStaleOwnerCallMessage = (
|
|
71
|
+
mainSilentSeconds: number,
|
|
72
|
+
unansweredOwnerCallAgeSeconds: number,
|
|
73
|
+
ownerCallMarker: string | null,
|
|
74
|
+
): string => {
|
|
75
|
+
const silentMinutes = Math.floor(mainSilentSeconds / 60);
|
|
76
|
+
const ownerCallAgeMinutes = Math.floor(unansweredOwnerCallAgeSeconds / 60);
|
|
77
|
+
return [
|
|
78
|
+
`${SILENT_SESSION_REMINDER_SENTINEL} You have produced no output for ${silentMinutes} minutes, and the owner call you raised ${ownerCallAgeMinutes} minutes ago is still unanswered. An owner call outstanding this long may have been missed, may have scrolled out of the owner's view, or may no longer reflect your current situation, so do not keep waiting on it passively. Act now, choosing one of the following:`,
|
|
79
|
+
`1. If you are still blocked on the owner, re-raise your pending ask NOW as a fresh owner call. Make it fully self-contained: restate the whole situation — what happened, what you are asking, and any decision needed — inside the new owner call itself, so the owner understands everything from that single latest message without scrolling back. ${composeOwnerCallFormatGuidance(ownerCallMarker)}`,
|
|
80
|
+
`2. If you are no longer blocked — the answer became unnecessary, you can proceed safely, or the information arrived another way — resume immediately and drive all remaining tasks to completion.`,
|
|
81
|
+
`This reminder does not notify the owner; only a fresh owner call from you surfaces this session to the owner. Also, in your next output, report an estimate of how many minutes you expect to need to finish all remaining tasks.`,
|
|
82
|
+
].join('\n');
|
|
83
|
+
};
|
|
84
|
+
|
|
70
85
|
export class DefaultSilentSessionMessageComposer implements SilentSessionMessageComposer {
|
|
71
86
|
constructor(private readonly ownerCallMarker: string | null = null) {}
|
|
72
87
|
|
|
@@ -74,6 +89,17 @@ export class DefaultSilentSessionMessageComposer implements SilentSessionMessage
|
|
|
74
89
|
return composeMainStalledMessage(mainSilentSeconds, this.ownerCallMarker);
|
|
75
90
|
};
|
|
76
91
|
|
|
92
|
+
composeMainStalledWithStaleOwnerCallSection = (
|
|
93
|
+
mainSilentSeconds: number,
|
|
94
|
+
unansweredOwnerCallAgeSeconds: number,
|
|
95
|
+
): string => {
|
|
96
|
+
return composeMainStalledWithStaleOwnerCallMessage(
|
|
97
|
+
mainSilentSeconds,
|
|
98
|
+
unansweredOwnerCallAgeSeconds,
|
|
99
|
+
this.ownerCallMarker,
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
77
103
|
composeSubAgentSection = (stallSections: SubAgentStallSections): string => {
|
|
78
104
|
const sections: string[] = [];
|
|
79
105
|
if (stallSections.idleSubAgents.length > 0) {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
parseHubTaskIssueUrlFromSessionName,
|
|
5
5
|
isGitHubIssueOrPullRequestSessionName,
|
|
6
6
|
DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
7
|
+
DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS,
|
|
7
8
|
DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS,
|
|
8
9
|
DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS,
|
|
9
10
|
DEFAULT_NOTIFICATION_STAGGER_SECONDS,
|
|
@@ -28,6 +29,8 @@ import { InteractiveLiveSession } from '../entities/InteractiveLiveSession';
|
|
|
28
29
|
type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
29
30
|
|
|
30
31
|
const MAIN_STALLED_SECTION = 'MAIN_STALLED_SECTION';
|
|
32
|
+
const MAIN_STALLED_STALE_OWNER_CALL_SECTION =
|
|
33
|
+
'MAIN_STALLED_STALE_OWNER_CALL_SECTION';
|
|
31
34
|
const SUBAGENT_SECTION = 'SUBAGENT_SECTION';
|
|
32
35
|
|
|
33
36
|
class EveryNameRecentSet extends Set<string> {
|
|
@@ -66,6 +69,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
66
69
|
}>,
|
|
67
70
|
): {
|
|
68
71
|
mainSilentThresholdSeconds: number;
|
|
72
|
+
unansweredOwnerCallGraceSeconds: number;
|
|
69
73
|
subAgentSilentThresholdSeconds: number;
|
|
70
74
|
subAgentRunningThresholdSeconds: number;
|
|
71
75
|
staggerSeconds: number;
|
|
@@ -75,6 +79,8 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
75
79
|
now: Date;
|
|
76
80
|
} => ({
|
|
77
81
|
mainSilentThresholdSeconds: DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
82
|
+
unansweredOwnerCallGraceSeconds:
|
|
83
|
+
DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS,
|
|
78
84
|
subAgentSilentThresholdSeconds: DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS,
|
|
79
85
|
subAgentRunningThresholdSeconds: DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS,
|
|
80
86
|
staggerSeconds: DEFAULT_NOTIFICATION_STAGGER_SECONDS,
|
|
@@ -147,9 +153,9 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
147
153
|
.mockResolvedValue(new Map<string, SubAgentActivity[]>()),
|
|
148
154
|
};
|
|
149
155
|
mockOwnerCallStatusProvider = {
|
|
150
|
-
|
|
156
|
+
listUnansweredOwnerCallEpochSecondsBySessionName: jest
|
|
151
157
|
.fn()
|
|
152
|
-
.mockResolvedValue(new
|
|
158
|
+
.mockResolvedValue(new Map<string, number>()),
|
|
153
159
|
};
|
|
154
160
|
mockNotificationRepository = {
|
|
155
161
|
sendSelfCheckNotification: jest.fn().mockResolvedValue(undefined),
|
|
@@ -170,6 +176,9 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
170
176
|
composeMainStalledSection: jest
|
|
171
177
|
.fn()
|
|
172
178
|
.mockReturnValue(MAIN_STALLED_SECTION),
|
|
179
|
+
composeMainStalledWithStaleOwnerCallSection: jest
|
|
180
|
+
.fn()
|
|
181
|
+
.mockReturnValue(MAIN_STALLED_STALE_OWNER_CALL_SECTION),
|
|
173
182
|
composeSubAgentSection: jest.fn().mockReturnValue(SUBAGENT_SECTION),
|
|
174
183
|
};
|
|
175
184
|
mockSleeper = {
|
|
@@ -249,6 +258,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
249
258
|
|
|
250
259
|
it('exposes the default thresholds as named constants', () => {
|
|
251
260
|
expect(DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS).toBe(600);
|
|
261
|
+
expect(DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS).toBe(3600);
|
|
252
262
|
expect(DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS).toBe(300);
|
|
253
263
|
expect(DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS).toBe(900);
|
|
254
264
|
expect(DEFAULT_NOTIFICATION_STAGGER_SECONDS).toBe(25);
|
|
@@ -371,36 +381,119 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
371
381
|
mockSessionOutputActivityRepository.listSessionOutputActivities,
|
|
372
382
|
).toHaveBeenCalledWith(expectedMap);
|
|
373
383
|
expect(
|
|
374
|
-
mockOwnerCallStatusProvider.
|
|
384
|
+
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName,
|
|
375
385
|
).toHaveBeenCalledWith(expectedMap);
|
|
376
386
|
expect(
|
|
377
387
|
mockSubAgentActivityRepository.listSubAgentActivitiesBySessionName,
|
|
378
388
|
).toHaveBeenCalledWith([GITHUB_SESSION], expectedMap);
|
|
379
389
|
});
|
|
380
390
|
|
|
381
|
-
it('suppresses the stalled section and sends nothing
|
|
391
|
+
it('suppresses the stalled section and sends nothing while the unanswered owner call is younger than the grace period', async () => {
|
|
392
|
+
setupSilentMainSession(GITHUB_SESSION);
|
|
393
|
+
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName.mockResolvedValue(
|
|
394
|
+
new Map([
|
|
395
|
+
[
|
|
396
|
+
GITHUB_SESSION,
|
|
397
|
+
nowEpochSeconds - DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS + 1,
|
|
398
|
+
],
|
|
399
|
+
]),
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
await useCase.run(runParams());
|
|
403
|
+
|
|
404
|
+
expect(
|
|
405
|
+
mockMessageComposer.composeMainStalledSection,
|
|
406
|
+
).not.toHaveBeenCalled();
|
|
407
|
+
expect(
|
|
408
|
+
mockMessageComposer.composeMainStalledWithStaleOwnerCallSection,
|
|
409
|
+
).not.toHaveBeenCalled();
|
|
410
|
+
expect(
|
|
411
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
412
|
+
).not.toHaveBeenCalled();
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('sends the stale-owner-call stalled section once the unanswered owner call reaches the grace period', async () => {
|
|
416
|
+
setupSilentMainSession(GITHUB_SESSION);
|
|
417
|
+
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName.mockResolvedValue(
|
|
418
|
+
new Map([
|
|
419
|
+
[
|
|
420
|
+
GITHUB_SESSION,
|
|
421
|
+
nowEpochSeconds - DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS,
|
|
422
|
+
],
|
|
423
|
+
]),
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
await useCase.run(runParams());
|
|
427
|
+
|
|
428
|
+
expect(
|
|
429
|
+
mockMessageComposer.composeMainStalledWithStaleOwnerCallSection,
|
|
430
|
+
).toHaveBeenCalledWith(
|
|
431
|
+
DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
432
|
+
DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS,
|
|
433
|
+
);
|
|
434
|
+
expect(
|
|
435
|
+
mockMessageComposer.composeMainStalledSection,
|
|
436
|
+
).not.toHaveBeenCalled();
|
|
437
|
+
expect(
|
|
438
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
439
|
+
).toHaveBeenCalledWith(
|
|
440
|
+
GITHUB_SESSION,
|
|
441
|
+
MAIN_STALLED_STALE_OWNER_CALL_SECTION,
|
|
442
|
+
);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('does not send the stale-owner-call section when the owner call is past the grace period but the main output is not silent past the threshold', async () => {
|
|
382
446
|
setupLiveInteractiveSession(GITHUB_SESSION);
|
|
383
447
|
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
384
448
|
[
|
|
385
449
|
{
|
|
386
450
|
sessionName: GITHUB_SESSION,
|
|
387
451
|
lastOutputEpochSeconds:
|
|
388
|
-
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
452
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS + 1,
|
|
389
453
|
},
|
|
390
454
|
],
|
|
391
455
|
);
|
|
392
|
-
mockOwnerCallStatusProvider.
|
|
393
|
-
new
|
|
456
|
+
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName.mockResolvedValue(
|
|
457
|
+
new Map([
|
|
458
|
+
[
|
|
459
|
+
GITHUB_SESSION,
|
|
460
|
+
nowEpochSeconds - DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS,
|
|
461
|
+
],
|
|
462
|
+
]),
|
|
394
463
|
);
|
|
395
464
|
|
|
396
465
|
await useCase.run(runParams());
|
|
397
466
|
|
|
398
467
|
expect(
|
|
399
|
-
|
|
468
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
400
469
|
).not.toHaveBeenCalled();
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('defers a first-cycle stale-owner-call candidate until it persists into the next cycle', async () => {
|
|
473
|
+
setupSilentMainSession(GITHUB_SESSION);
|
|
474
|
+
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName.mockResolvedValue(
|
|
475
|
+
new Map([
|
|
476
|
+
[
|
|
477
|
+
GITHUB_SESSION,
|
|
478
|
+
nowEpochSeconds - DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS,
|
|
479
|
+
],
|
|
480
|
+
]),
|
|
481
|
+
);
|
|
482
|
+
mockCandidateStateRepository.loadRecentCandidateSessionNames.mockResolvedValue(
|
|
483
|
+
new Set<string>(),
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
await useCase.run(runParams());
|
|
487
|
+
|
|
401
488
|
expect(
|
|
402
489
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
403
490
|
).not.toHaveBeenCalled();
|
|
491
|
+
expect(
|
|
492
|
+
mockCandidateStateRepository.saveCandidateSessionNames,
|
|
493
|
+
).toHaveBeenCalledWith({
|
|
494
|
+
sessionNames: [GITHUB_SESSION],
|
|
495
|
+
now,
|
|
496
|
+
});
|
|
404
497
|
});
|
|
405
498
|
|
|
406
499
|
it('sends the main stalled section when the session is silent past the threshold and not waiting on the owner', async () => {
|
|
@@ -414,8 +507,8 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
414
507
|
},
|
|
415
508
|
],
|
|
416
509
|
);
|
|
417
|
-
mockOwnerCallStatusProvider.
|
|
418
|
-
new
|
|
510
|
+
mockOwnerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName.mockResolvedValue(
|
|
511
|
+
new Map<string, number>(),
|
|
419
512
|
);
|
|
420
513
|
|
|
421
514
|
await useCase.run(runParams());
|
|
@@ -423,6 +516,9 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
423
516
|
expect(mockMessageComposer.composeMainStalledSection).toHaveBeenCalledWith(
|
|
424
517
|
DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
425
518
|
);
|
|
519
|
+
expect(
|
|
520
|
+
mockMessageComposer.composeMainStalledWithStaleOwnerCallSection,
|
|
521
|
+
).not.toHaveBeenCalled();
|
|
426
522
|
expect(
|
|
427
523
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
428
524
|
).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
|
|
@@ -14,6 +14,7 @@ import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
|
14
14
|
import { ResolveInteractiveLiveSessionsUseCase } from './ResolveInteractiveLiveSessionsUseCase';
|
|
15
15
|
|
|
16
16
|
export const DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS = 10 * 60;
|
|
17
|
+
export const DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS = 60 * 60;
|
|
17
18
|
export const DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS = 5 * 60;
|
|
18
19
|
export const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS = 15 * 60;
|
|
19
20
|
export const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
|
|
@@ -78,6 +79,7 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
78
79
|
|
|
79
80
|
run = async (params: {
|
|
80
81
|
mainSilentThresholdSeconds: number;
|
|
82
|
+
unansweredOwnerCallGraceSeconds: number;
|
|
81
83
|
subAgentSilentThresholdSeconds: number;
|
|
82
84
|
subAgentRunningThresholdSeconds: number;
|
|
83
85
|
staggerSeconds: number;
|
|
@@ -324,8 +326,8 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
324
326
|
transcriptPathBySessionName,
|
|
325
327
|
);
|
|
326
328
|
|
|
327
|
-
const
|
|
328
|
-
await this.ownerCallStatusProvider.
|
|
329
|
+
const unansweredOwnerCallEpochSecondsBySessionName =
|
|
330
|
+
await this.ownerCallStatusProvider.listUnansweredOwnerCallEpochSecondsBySessionName(
|
|
329
331
|
transcriptPathBySessionName,
|
|
330
332
|
);
|
|
331
333
|
|
|
@@ -336,12 +338,16 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
336
338
|
lastOutputEpochSeconds === undefined
|
|
337
339
|
? null
|
|
338
340
|
: nowEpochSeconds - lastOutputEpochSeconds;
|
|
341
|
+
const unansweredOwnerCallEpochSeconds =
|
|
342
|
+
unansweredOwnerCallEpochSecondsBySessionName.get(sessionName);
|
|
339
343
|
return {
|
|
340
344
|
sessionName,
|
|
341
345
|
mainSilentSeconds,
|
|
342
346
|
subAgents: subAgentsBySessionName.get(sessionName) ?? [],
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
unansweredOwnerCallAgeSeconds:
|
|
348
|
+
unansweredOwnerCallEpochSeconds === undefined
|
|
349
|
+
? null
|
|
350
|
+
: nowEpochSeconds - unansweredOwnerCallEpochSeconds,
|
|
345
351
|
};
|
|
346
352
|
});
|
|
347
353
|
};
|
|
@@ -350,6 +356,7 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
350
356
|
snapshot: LiveSessionActivitySnapshot,
|
|
351
357
|
thresholds: {
|
|
352
358
|
mainSilentThresholdSeconds: number;
|
|
359
|
+
unansweredOwnerCallGraceSeconds: number;
|
|
353
360
|
subAgentSilentThresholdSeconds: number;
|
|
354
361
|
subAgentRunningThresholdSeconds: number;
|
|
355
362
|
now: Date;
|
|
@@ -358,13 +365,24 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
358
365
|
const sections: string[] = [];
|
|
359
366
|
|
|
360
367
|
const mainSilentSeconds = snapshot.mainSilentSeconds;
|
|
368
|
+
const unansweredOwnerCallAgeSeconds =
|
|
369
|
+
snapshot.unansweredOwnerCallAgeSeconds;
|
|
370
|
+
const suppressedByRecentOwnerCall =
|
|
371
|
+
unansweredOwnerCallAgeSeconds !== null &&
|
|
372
|
+
unansweredOwnerCallAgeSeconds <
|
|
373
|
+
thresholds.unansweredOwnerCallGraceSeconds;
|
|
361
374
|
const mainTriggered =
|
|
362
375
|
mainSilentSeconds !== null &&
|
|
363
376
|
mainSilentSeconds >= thresholds.mainSilentThresholdSeconds &&
|
|
364
|
-
!
|
|
377
|
+
!suppressedByRecentOwnerCall;
|
|
365
378
|
if (mainTriggered) {
|
|
366
379
|
sections.push(
|
|
367
|
-
|
|
380
|
+
unansweredOwnerCallAgeSeconds !== null
|
|
381
|
+
? this.messageComposer.composeMainStalledWithStaleOwnerCallSection(
|
|
382
|
+
mainSilentSeconds,
|
|
383
|
+
unansweredOwnerCallAgeSeconds,
|
|
384
|
+
)
|
|
385
|
+
: this.messageComposer.composeMainStalledSection(mainSilentSeconds),
|
|
368
386
|
);
|
|
369
387
|
}
|
|
370
388
|
|
|
@@ -7,5 +7,9 @@ export type SubAgentStallSections = {
|
|
|
7
7
|
|
|
8
8
|
export interface SilentSessionMessageComposer {
|
|
9
9
|
composeMainStalledSection: (mainSilentSeconds: number) => string;
|
|
10
|
+
composeMainStalledWithStaleOwnerCallSection: (
|
|
11
|
+
mainSilentSeconds: number,
|
|
12
|
+
unansweredOwnerCallAgeSeconds: number,
|
|
13
|
+
) => string;
|
|
10
14
|
composeSubAgentSection: (sections: SubAgentStallSections) => string;
|
|
11
15
|
}
|
|
@@ -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,CA8kBP;CACH"}
|
|
@@ -11,6 +11,7 @@ export type NotifySilentTmuxSessionsParams = {
|
|
|
11
11
|
subAgentProcessMatchPattern: string | null;
|
|
12
12
|
subAgentTranscriptRootDirectory: string | null;
|
|
13
13
|
mainSilentThresholdSeconds: number;
|
|
14
|
+
unansweredOwnerCallGraceSeconds: number;
|
|
14
15
|
subAgentSilentThresholdSeconds: number;
|
|
15
16
|
subAgentRunningThresholdSeconds: number;
|
|
16
17
|
staggerSeconds: number;
|
|
@@ -26,6 +27,7 @@ export type NotifySilentTmuxSessionsParams = {
|
|
|
26
27
|
export declare const notifySilentTmuxSessions: (params: NotifySilentTmuxSessionsParams) => Promise<void>;
|
|
27
28
|
export declare const DEFAULT_NOTIFY_SILENT_TMUX_SESSIONS_PARAMS: {
|
|
28
29
|
readonly mainSilentThresholdSeconds: number;
|
|
30
|
+
readonly unansweredOwnerCallGraceSeconds: number;
|
|
29
31
|
readonly subAgentSilentThresholdSeconds: number;
|
|
30
32
|
readonly subAgentRunningThresholdSeconds: number;
|
|
31
33
|
readonly staggerSeconds: 25;
|
|
@@ -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,EAQtB,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,+BAA+B,EAAE,MAAM,CAAC;IACxC,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;AAqCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CA2Ed,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;;;CAS7C,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SilentSessionMessageComposer, SubAgentStallSections } from '../../domain/usecases/adapter-interfaces/SilentSessionMessageComposer';
|
|
2
2
|
export type SilentSessionMessageTemplates = {
|
|
3
3
|
mainStalledMessage: string | null;
|
|
4
|
+
mainStalledStaleOwnerCallMessage: string | null;
|
|
4
5
|
subAgentIdleMessageHeader: string | null;
|
|
5
6
|
subAgentIdleMessageFooter: string | null;
|
|
6
7
|
subAgentLongRunningMessageHeader: string | null;
|
|
@@ -12,6 +13,7 @@ export declare class ConfigurableSilentSessionMessageComposer implements SilentS
|
|
|
12
13
|
private readonly ownerCallMarker;
|
|
13
14
|
constructor(templates: SilentSessionMessageTemplates, fallback: SilentSessionMessageComposer, ownerCallMarker?: string | null);
|
|
14
15
|
composeMainStalledSection: (mainSilentSeconds: number) => string;
|
|
16
|
+
composeMainStalledWithStaleOwnerCallSection: (mainSilentSeconds: number, unansweredOwnerCallAgeSeconds: number) => string;
|
|
15
17
|
composeSubAgentSection: (stallSections: SubAgentStallSections) => string;
|
|
16
18
|
private composeIdleSection;
|
|
17
19
|
private composeLongRunningSection;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigurableSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,uEAAuE,CAAC;AAQ/E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD,CAAC;AAOF,qBAAa,wCAAyC,YAAW,4BAA4B;IAEzF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAFf,SAAS,EAAE,6BAA6B,EACxC,QAAQ,EAAE,4BAA4B,EACtC,eAAe,GAAE,MAAM,GAAG,IAAW;IAGxD,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAO7D;IAEF,sBAAsB,GAAI,eAAe,qBAAqB,KAAG,MAAM,CA4CrE;IAEF,OAAO,CAAC,kBAAkB,CAkBxB;IAEF,OAAO,CAAC,yBAAyB,CAkB/B;CACH"}
|
|
1
|
+
{"version":3,"file":"ConfigurableSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,uEAAuE,CAAC;AAQ/E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD,CAAC;AAOF,qBAAa,wCAAyC,YAAW,4BAA4B;IAEzF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAFf,SAAS,EAAE,6BAA6B,EACxC,QAAQ,EAAE,4BAA4B,EACtC,eAAe,GAAE,MAAM,GAAG,IAAW;IAGxD,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAO7D;IAEF,2CAA2C,GACzC,mBAAmB,MAAM,EACzB,+BAA+B,MAAM,KACpC,MAAM,CAUP;IAEF,sBAAsB,GAAI,eAAe,qBAAqB,KAAG,MAAM,CA4CrE;IAEF,OAAO,CAAC,kBAAkB,CAkBxB;IAEF,OAAO,CAAC,yBAAyB,CAkB/B;CACH"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OwnerCallStatusProvider } from '../../domain/usecases/adapter-interfaces/OwnerCallStatusProvider';
|
|
2
2
|
export declare class NoUnansweredOwnerCallStatusProvider implements OwnerCallStatusProvider {
|
|
3
|
-
|
|
3
|
+
listUnansweredOwnerCallEpochSecondsBySessionName: (_transcriptPathBySessionName: Map<string, string>) => Promise<Map<string, number>>;
|
|
4
4
|
}
|
|
5
5
|
//# sourceMappingURL=NoUnansweredOwnerCallStatusProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoUnansweredOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAE3G,qBAAa,mCAAoC,YAAW,uBAAuB;IACjF,
|
|
1
|
+
{"version":3,"file":"NoUnansweredOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAE3G,qBAAa,mCAAoC,YAAW,uBAAuB;IACjF,gDAAgD,GAC9C,8BAA8B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAChD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAE7B;CACH"}
|
|
@@ -2,7 +2,7 @@ import { OwnerCallStatusProvider } from '../../domain/usecases/adapter-interface
|
|
|
2
2
|
export declare class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvider {
|
|
3
3
|
private readonly ownerCallMarker;
|
|
4
4
|
constructor(ownerCallMarker: string | null);
|
|
5
|
-
|
|
6
|
-
private
|
|
5
|
+
listUnansweredOwnerCallEpochSecondsBySessionName: (transcriptPathBySessionName: Map<string, string>) => Promise<Map<string, number>>;
|
|
6
|
+
private findUnansweredOwnerCallEpochMs;
|
|
7
7
|
}
|
|
8
8
|
//# sourceMappingURL=TranscriptOwnerCallStatusProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranscriptOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAyF3G,qBAAa,iCAAkC,YAAW,uBAAuB;IACnE,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,MAAM,GAAG,IAAI;IAE3D,
|
|
1
|
+
{"version":3,"file":"TranscriptOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAyF3G,qBAAa,iCAAkC,YAAW,uBAAuB;IACnE,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,MAAM,GAAG,IAAI;IAE3D,gDAAgD,GAC9C,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAsB7B;IAEF,OAAO,CAAC,8BAA8B,CAsDpC;CACH"}
|
|
@@ -8,6 +8,6 @@ export type LiveSessionActivitySnapshot = {
|
|
|
8
8
|
sessionName: string;
|
|
9
9
|
mainSilentSeconds: number | null;
|
|
10
10
|
subAgents: SubAgentActivity[];
|
|
11
|
-
|
|
11
|
+
unansweredOwnerCallAgeSeconds: number | null;
|
|
12
12
|
};
|
|
13
13
|
//# sourceMappingURL=LiveSessionActivitySnapshot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveSessionActivitySnapshot.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/LiveSessionActivitySnapshot.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,
|
|
1
|
+
{"version":3,"file":"LiveSessionActivitySnapshot.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/LiveSessionActivitySnapshot.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,6BAA6B,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9C,CAAC"}
|
|
@@ -4,6 +4,7 @@ export declare class DefaultSilentSessionMessageComposer implements SilentSessio
|
|
|
4
4
|
private readonly ownerCallMarker;
|
|
5
5
|
constructor(ownerCallMarker?: string | null);
|
|
6
6
|
composeMainStalledSection: (mainSilentSeconds: number) => string;
|
|
7
|
+
composeMainStalledWithStaleOwnerCallSection: (mainSilentSeconds: number, unansweredOwnerCallAgeSeconds: number) => string;
|
|
7
8
|
composeSubAgentSection: (stallSections: SubAgentStallSections) => string;
|
|
8
9
|
}
|
|
9
10
|
//# sourceMappingURL=DefaultSilentSessionMessageComposer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,mDAAmD,CAAC;AAwC3D,eAAO,MAAM,8BAA8B,GACzC,iBAAiB,MAAM,GAAG,IAAI,KAC7B,MAMF,CAAC;
|
|
1
|
+
{"version":3,"file":"DefaultSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,mDAAmD,CAAC;AAwC3D,eAAO,MAAM,8BAA8B,GACzC,iBAAiB,MAAM,GAAG,IAAI,KAC7B,MAMF,CAAC;AAgCF,qBAAa,mCAAoC,YAAW,4BAA4B;IAC1E,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,GAAE,MAAM,GAAG,IAAW;IAElE,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAE7D;IAEF,2CAA2C,GACzC,mBAAmB,MAAM,EACzB,+BAA+B,MAAM,KACpC,MAAM,CAMP;IAEF,sBAAsB,GAAI,eAAe,qBAAqB,KAAG,MAAM,CAWrE;CACH"}
|
|
@@ -10,6 +10,7 @@ import { SilentSessionHubTaskStatusCacheRepository } from './adapter-interfaces/
|
|
|
10
10
|
import { Sleeper } from './adapter-interfaces/Sleeper';
|
|
11
11
|
import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
12
12
|
export declare const DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS: number;
|
|
13
|
+
export declare const DEFAULT_UNANSWERED_OWNER_CALL_GRACE_SECONDS: number;
|
|
13
14
|
export declare const DEFAULT_SUBAGENT_SILENT_THRESHOLD_SECONDS: number;
|
|
14
15
|
export declare const DEFAULT_SUBAGENT_RUNNING_THRESHOLD_SECONDS: number;
|
|
15
16
|
export declare const DEFAULT_NOTIFICATION_STAGGER_SECONDS = 25;
|
|
@@ -34,6 +35,7 @@ export declare class NotifySilentLiveSessionsUseCase {
|
|
|
34
35
|
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);
|
|
35
36
|
run: (params: {
|
|
36
37
|
mainSilentThresholdSeconds: number;
|
|
38
|
+
unansweredOwnerCallGraceSeconds: number;
|
|
37
39
|
subAgentSilentThresholdSeconds: number;
|
|
38
40
|
subAgentRunningThresholdSeconds: number;
|
|
39
41
|
staggerSeconds: number;
|