github-issue-tower-defence-management 1.111.3 → 1.112.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +9 -6
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-BPH6W1yn.css → index-BaVsPpWs.css} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-Bu5bQZyP.js → index-ITb3VBcs.js} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +19 -3
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js +6 -0
- package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js.map +1 -1
- package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js +9 -0
- package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js.map +1 -1
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +13 -3
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +72 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +140 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +8 -0
- package/src/adapter/entry-points/console/ui/src/index.css +88 -4
- package/src/adapter/entry-points/console/ui-dist/assets/{index-BPH6W1yn.css → index-BaVsPpWs.css} +1 -1
- package/src/adapter/entry-points/console/ui-dist/assets/{index-Bu5bQZyP.js → index-ITb3VBcs.js} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +20 -1
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +105 -2
- package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +44 -0
- package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +8 -0
- package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +17 -1
- package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +11 -0
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +86 -1
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +20 -7
- package/src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts +1 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.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/domain/usecases/DefaultSilentSessionMessageComposer.d.ts +1 -0
- package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts.map +1 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.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
|
@@ -21,6 +21,7 @@ const CLAUDE_PID = 201;
|
|
|
21
21
|
|
|
22
22
|
const EMPTY_TEMPLATES: SilentSessionMessageTemplates = {
|
|
23
23
|
mainStalledMessage: null,
|
|
24
|
+
ownerReNotificationMessage: null,
|
|
24
25
|
subAgentMessageHeader: null,
|
|
25
26
|
subAgentMessageFooter: null,
|
|
26
27
|
};
|
|
@@ -83,6 +84,11 @@ describe('notifySilentTmuxSessions', () => {
|
|
|
83
84
|
cacheDirectory,
|
|
84
85
|
);
|
|
85
86
|
|
|
87
|
+
const makeCacheRepositoryAt = (
|
|
88
|
+
basePath: string,
|
|
89
|
+
): LocalStorageCacheRepository =>
|
|
90
|
+
new LocalStorageCacheRepository(new LocalStorageRepository(), basePath);
|
|
91
|
+
|
|
86
92
|
const makeEnvironReader = (): ProcessEnvironReader => ({
|
|
87
93
|
readEnviron: (pid: number) =>
|
|
88
94
|
pid === CLAUDE_PID
|
|
@@ -163,6 +169,7 @@ describe('notifySilentTmuxSessions', () => {
|
|
|
163
169
|
...baseParams(runner),
|
|
164
170
|
messageTemplates: {
|
|
165
171
|
mainStalledMessage: 'CUSTOM_MAIN_TEMPLATE',
|
|
172
|
+
ownerReNotificationMessage: null,
|
|
166
173
|
subAgentMessageHeader: null,
|
|
167
174
|
subAgentMessageFooter: null,
|
|
168
175
|
},
|
|
@@ -176,7 +183,10 @@ describe('notifySilentTmuxSessions', () => {
|
|
|
176
183
|
);
|
|
177
184
|
});
|
|
178
185
|
|
|
179
|
-
it('
|
|
186
|
+
it('sends no notification while an owner call is unanswered and the wait is within the threshold', async () => {
|
|
187
|
+
const recentOwnerCall = new Date(
|
|
188
|
+
(NOW_EPOCH_SECONDS - 5 * 60) * 1000,
|
|
189
|
+
).toISOString();
|
|
180
190
|
writeTranscript([
|
|
181
191
|
{
|
|
182
192
|
type: 'user',
|
|
@@ -185,7 +195,7 @@ describe('notifySilentTmuxSessions', () => {
|
|
|
185
195
|
},
|
|
186
196
|
{
|
|
187
197
|
type: 'assistant',
|
|
188
|
-
timestamp:
|
|
198
|
+
timestamp: recentOwnerCall,
|
|
189
199
|
message: {
|
|
190
200
|
role: 'assistant',
|
|
191
201
|
stop_reason: 'end_turn',
|
|
@@ -208,6 +218,44 @@ describe('notifySilentTmuxSessions', () => {
|
|
|
208
218
|
expect(sendCall).toBeUndefined();
|
|
209
219
|
});
|
|
210
220
|
|
|
221
|
+
it('re-notifies the owner when an owner call has been unanswered past the threshold', async () => {
|
|
222
|
+
const stalePendingOwnerCall = new Date(
|
|
223
|
+
(NOW_EPOCH_SECONDS - 11 * 60) * 1000,
|
|
224
|
+
).toISOString();
|
|
225
|
+
writeTranscript([
|
|
226
|
+
{
|
|
227
|
+
type: 'user',
|
|
228
|
+
timestamp: '2026-06-25T23:00:00.000Z',
|
|
229
|
+
message: { role: 'user', content: 'go ahead' },
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
type: 'assistant',
|
|
233
|
+
timestamp: stalePendingOwnerCall,
|
|
234
|
+
message: {
|
|
235
|
+
role: 'assistant',
|
|
236
|
+
stop_reason: 'end_turn',
|
|
237
|
+
content: [
|
|
238
|
+
{ type: 'text', text: 'waiting <<OWNER_CALL>> please decide' },
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
]);
|
|
243
|
+
const runner = liveSessionRunner();
|
|
244
|
+
|
|
245
|
+
await notifySilentTmuxSessions({
|
|
246
|
+
...baseParams(runner),
|
|
247
|
+
ownerCallMarker: '<<OWNER_CALL>>',
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const sendCall = runner.runCommand.mock.calls.find(
|
|
251
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'send-keys',
|
|
252
|
+
);
|
|
253
|
+
expect(sendCall?.[1][2]).toBe(SESSION_NAME);
|
|
254
|
+
expect(sendCall?.[1][4]).toContain(SILENT_SESSION_REMINDER_SENTINEL);
|
|
255
|
+
expect(sendCall?.[1][4]).toContain('Re-raise your pending call-to-user');
|
|
256
|
+
expect(sendCall?.[1][4]).not.toContain('You have produced no output for');
|
|
257
|
+
});
|
|
258
|
+
|
|
211
259
|
it('does not re-notify the same silent session on the next cycle within cooldown', async () => {
|
|
212
260
|
silentAssistantTranscript();
|
|
213
261
|
const cacheRepository = makeCacheRepository();
|
|
@@ -230,4 +278,59 @@ describe('notifySilentTmuxSessions', () => {
|
|
|
230
278
|
);
|
|
231
279
|
expect(secondSendCall).toBeUndefined();
|
|
232
280
|
});
|
|
281
|
+
|
|
282
|
+
it('shares the cooldown across project passes when a single project-independent cache scope is used', async () => {
|
|
283
|
+
silentAssistantTranscript();
|
|
284
|
+
// The handler runs once per project over the same global set of tmux
|
|
285
|
+
// sessions. When the silent-session cooldown lives in one shared cache
|
|
286
|
+
// scope, a second per-project pass for the same session within the cooldown
|
|
287
|
+
// window must NOT re-send, even though it is a different project pass.
|
|
288
|
+
const sharedCacheBasePath = path.join(cacheDirectory, 'shared');
|
|
289
|
+
const firstRunner = liveSessionRunner();
|
|
290
|
+
|
|
291
|
+
await notifySilentTmuxSessions({
|
|
292
|
+
...baseParams(firstRunner),
|
|
293
|
+
cacheRepository: makeCacheRepositoryAt(sharedCacheBasePath),
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const secondRunner = liveSessionRunner();
|
|
297
|
+
await notifySilentTmuxSessions({
|
|
298
|
+
...baseParams(secondRunner),
|
|
299
|
+
cacheRepository: makeCacheRepositoryAt(sharedCacheBasePath),
|
|
300
|
+
now: new Date(NOW.getTime() + 60 * 1000),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const secondSendCall = secondRunner.runCommand.mock.calls.find(
|
|
304
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'send-keys',
|
|
305
|
+
);
|
|
306
|
+
expect(secondSendCall).toBeUndefined();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('re-sends within the cooldown window when each project pass uses its own cache scope', async () => {
|
|
310
|
+
silentAssistantTranscript();
|
|
311
|
+
// This reproduces the per-project cooldown defect: when each project pass
|
|
312
|
+
// stores its cooldown under its own project-scoped cache path, the same
|
|
313
|
+
// session is notified once per project within the cooldown window.
|
|
314
|
+
const firstRunner = liveSessionRunner();
|
|
315
|
+
await notifySilentTmuxSessions({
|
|
316
|
+
...baseParams(firstRunner),
|
|
317
|
+
cacheRepository: makeCacheRepositoryAt(
|
|
318
|
+
path.join(cacheDirectory, 'umino'),
|
|
319
|
+
),
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const secondRunner = liveSessionRunner();
|
|
323
|
+
await notifySilentTmuxSessions({
|
|
324
|
+
...baseParams(secondRunner),
|
|
325
|
+
cacheRepository: makeCacheRepositoryAt(
|
|
326
|
+
path.join(cacheDirectory, 'xmile'),
|
|
327
|
+
),
|
|
328
|
+
now: new Date(NOW.getTime() + 60 * 1000),
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const secondSendCall = secondRunner.runCommand.mock.calls.find(
|
|
332
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'send-keys',
|
|
333
|
+
);
|
|
334
|
+
expect(secondSendCall?.[1][2]).toBe(SESSION_NAME);
|
|
335
|
+
});
|
|
233
336
|
});
|
|
@@ -6,6 +6,9 @@ type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
|
6
6
|
|
|
7
7
|
const createFallback = (): Mocked<SilentSessionMessageComposer> => ({
|
|
8
8
|
composeMainStalledSection: jest.fn().mockReturnValue('FALLBACK_MAIN'),
|
|
9
|
+
composeOwnerReNotificationSection: jest
|
|
10
|
+
.fn()
|
|
11
|
+
.mockReturnValue('FALLBACK_OWNER'),
|
|
9
12
|
composeSubAgentSection: jest.fn().mockReturnValue('FALLBACK_SUB'),
|
|
10
13
|
});
|
|
11
14
|
|
|
@@ -15,6 +18,7 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
|
|
|
15
18
|
const composer = new ConfigurableSilentSessionMessageComposer(
|
|
16
19
|
{
|
|
17
20
|
mainStalledMessage: null,
|
|
21
|
+
ownerReNotificationMessage: null,
|
|
18
22
|
subAgentMessageHeader: null,
|
|
19
23
|
subAgentMessageFooter: null,
|
|
20
24
|
},
|
|
@@ -29,6 +33,7 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
|
|
|
29
33
|
const composer = new ConfigurableSilentSessionMessageComposer(
|
|
30
34
|
{
|
|
31
35
|
mainStalledMessage: 'CUSTOM_MAIN',
|
|
36
|
+
ownerReNotificationMessage: null,
|
|
32
37
|
subAgentMessageHeader: null,
|
|
33
38
|
subAgentMessageFooter: null,
|
|
34
39
|
},
|
|
@@ -45,6 +50,7 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
|
|
|
45
50
|
const composer = new ConfigurableSilentSessionMessageComposer(
|
|
46
51
|
{
|
|
47
52
|
mainStalledMessage: null,
|
|
53
|
+
ownerReNotificationMessage: null,
|
|
48
54
|
subAgentMessageHeader: null,
|
|
49
55
|
subAgentMessageFooter: null,
|
|
50
56
|
},
|
|
@@ -62,6 +68,7 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
|
|
|
62
68
|
const composer = new ConfigurableSilentSessionMessageComposer(
|
|
63
69
|
{
|
|
64
70
|
mainStalledMessage: null,
|
|
71
|
+
ownerReNotificationMessage: null,
|
|
65
72
|
subAgentMessageHeader: 'HEADER',
|
|
66
73
|
subAgentMessageFooter: 'FOOTER',
|
|
67
74
|
},
|
|
@@ -79,11 +86,48 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
|
|
|
79
86
|
expect(fallback.composeSubAgentSection).not.toHaveBeenCalled();
|
|
80
87
|
});
|
|
81
88
|
|
|
89
|
+
it('uses the fallback owner-re-notification section when no template is configured', () => {
|
|
90
|
+
const fallback = createFallback();
|
|
91
|
+
const composer = new ConfigurableSilentSessionMessageComposer(
|
|
92
|
+
{
|
|
93
|
+
mainStalledMessage: null,
|
|
94
|
+
ownerReNotificationMessage: null,
|
|
95
|
+
subAgentMessageHeader: null,
|
|
96
|
+
subAgentMessageFooter: null,
|
|
97
|
+
},
|
|
98
|
+
fallback,
|
|
99
|
+
);
|
|
100
|
+
expect(composer.composeOwnerReNotificationSection(600)).toBe(
|
|
101
|
+
'FALLBACK_OWNER',
|
|
102
|
+
);
|
|
103
|
+
expect(fallback.composeOwnerReNotificationSection).toHaveBeenCalledWith(
|
|
104
|
+
600,
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('uses the configured owner-re-notification template when provided', () => {
|
|
109
|
+
const fallback = createFallback();
|
|
110
|
+
const composer = new ConfigurableSilentSessionMessageComposer(
|
|
111
|
+
{
|
|
112
|
+
mainStalledMessage: null,
|
|
113
|
+
ownerReNotificationMessage: 'CUSTOM_OWNER',
|
|
114
|
+
subAgentMessageHeader: null,
|
|
115
|
+
subAgentMessageFooter: null,
|
|
116
|
+
},
|
|
117
|
+
fallback,
|
|
118
|
+
);
|
|
119
|
+
const section = composer.composeOwnerReNotificationSection(600);
|
|
120
|
+
expect(section).toContain('CUSTOM_OWNER');
|
|
121
|
+
expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
|
|
122
|
+
expect(fallback.composeOwnerReNotificationSection).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
|
|
82
125
|
it('does not double-prepend the sentinel when the template already carries it', () => {
|
|
83
126
|
const fallback = createFallback();
|
|
84
127
|
const composer = new ConfigurableSilentSessionMessageComposer(
|
|
85
128
|
{
|
|
86
129
|
mainStalledMessage: `${SILENT_SESSION_REMINDER_SENTINEL} CUSTOM_MAIN`,
|
|
130
|
+
ownerReNotificationMessage: null,
|
|
87
131
|
subAgentMessageHeader: null,
|
|
88
132
|
subAgentMessageFooter: null,
|
|
89
133
|
},
|
|
@@ -9,6 +9,7 @@ const withReminderSentinel = (message: string): string =>
|
|
|
9
9
|
|
|
10
10
|
export type SilentSessionMessageTemplates = {
|
|
11
11
|
mainStalledMessage: string | null;
|
|
12
|
+
ownerReNotificationMessage: string | null;
|
|
12
13
|
subAgentMessageHeader: string | null;
|
|
13
14
|
subAgentMessageFooter: string | null;
|
|
14
15
|
};
|
|
@@ -31,6 +32,13 @@ export class ConfigurableSilentSessionMessageComposer implements SilentSessionMe
|
|
|
31
32
|
return withReminderSentinel(this.templates.mainStalledMessage);
|
|
32
33
|
};
|
|
33
34
|
|
|
35
|
+
composeOwnerReNotificationSection = (waitingSeconds: number): string => {
|
|
36
|
+
if (this.templates.ownerReNotificationMessage === null) {
|
|
37
|
+
return this.fallback.composeOwnerReNotificationSection(waitingSeconds);
|
|
38
|
+
}
|
|
39
|
+
return withReminderSentinel(this.templates.ownerReNotificationMessage);
|
|
40
|
+
};
|
|
41
|
+
|
|
34
42
|
composeSubAgentSection = (subAgents: SubAgentActivity[]): string => {
|
|
35
43
|
if (
|
|
36
44
|
this.templates.subAgentMessageHeader === null &&
|
|
@@ -9,6 +9,20 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
9
9
|
expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
it('embeds the reminder sentinel in the owner-re-notification section', () => {
|
|
13
|
+
const section = composer.composeOwnerReNotificationSection(1800);
|
|
14
|
+
expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('instructs the agent to re-raise its pending call-to-user without treating the reminder as an owner reply', () => {
|
|
18
|
+
const section = composer.composeOwnerReNotificationSection(1800);
|
|
19
|
+
expect(section).toContain('waiting on the owner for 30 minutes');
|
|
20
|
+
expect(section).toContain('NOT the owner replying');
|
|
21
|
+
expect(section).toContain('Re-raise your pending call-to-user');
|
|
22
|
+
expect(section).toContain('you are not stalled or idle');
|
|
23
|
+
expect(section.toLowerCase()).not.toContain('no output for');
|
|
24
|
+
});
|
|
25
|
+
|
|
12
26
|
it('embeds the reminder sentinel in the sub-agent section', () => {
|
|
13
27
|
const section = composer.composeSubAgentSection([
|
|
14
28
|
{ label: 'sub-process-1', silentSeconds: 360, runningSeconds: 1200 },
|
|
@@ -53,10 +67,12 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
53
67
|
|
|
54
68
|
it('does not contain any host-specific or internal identifiers', () => {
|
|
55
69
|
const mainSection = composer.composeMainStalledSection(600);
|
|
70
|
+
const ownerSection = composer.composeOwnerReNotificationSection(1800);
|
|
56
71
|
const subSection = composer.composeSubAgentSection([
|
|
57
72
|
{ label: 'sub-process-1', silentSeconds: 360, runningSeconds: 1200 },
|
|
58
73
|
]);
|
|
59
|
-
const combined =
|
|
74
|
+
const combined =
|
|
75
|
+
`${mainSection}\n${ownerSection}\n${subSection}`.toLowerCase();
|
|
60
76
|
expect(combined).not.toContain('claude');
|
|
61
77
|
expect(combined).not.toContain('take ownership');
|
|
62
78
|
expect(combined).not.toContain('/home/');
|
|
@@ -19,11 +19,22 @@ const composeMainStalledMessage = (mainSilentSeconds: number): string => {
|
|
|
19
19
|
].join('\n');
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
const composeOwnerReNotificationMessage = (waitingSeconds: number): string => {
|
|
23
|
+
const minutes = Math.floor(waitingSeconds / 60);
|
|
24
|
+
return [
|
|
25
|
+
`${SILENT_SESSION_REMINDER_SENTINEL} This is an automated monitor reminder, NOT the owner replying. Do NOT treat this message as an owner answer to your outstanding call-to-user; your pending question is still unanswered. You have been waiting on the owner for ${minutes} minutes and the owner has not yet replied. The owner is only notified when a fresh call-to-user is raised, so a single past call-to-user that has gone quiet will never reach them. Re-raise your pending call-to-user to the owner now: re-emit it so a new call-to-user marker fires and the owner is notified again. Keep your question and proposed options unchanged unless you have new information. You are correctly waiting on the owner — you are not stalled or idle — so do not abandon the wait and do not invent an owner decision yourself; simply re-surface the same request so the owner sees it.`,
|
|
26
|
+
].join('\n');
|
|
27
|
+
};
|
|
28
|
+
|
|
22
29
|
export class DefaultSilentSessionMessageComposer implements SilentSessionMessageComposer {
|
|
23
30
|
composeMainStalledSection = (mainSilentSeconds: number): string => {
|
|
24
31
|
return composeMainStalledMessage(mainSilentSeconds);
|
|
25
32
|
};
|
|
26
33
|
|
|
34
|
+
composeOwnerReNotificationSection = (waitingSeconds: number): string => {
|
|
35
|
+
return composeOwnerReNotificationMessage(waitingSeconds);
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
composeSubAgentSection = (subAgents: SubAgentActivity[]): string => {
|
|
28
39
|
const lines = subAgents.map(
|
|
29
40
|
(subAgent) =>
|
|
@@ -21,6 +21,7 @@ import { InteractiveLiveSession } from '../entities/InteractiveLiveSession';
|
|
|
21
21
|
type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
22
22
|
|
|
23
23
|
const MAIN_STALLED_SECTION = 'MAIN_STALLED_SECTION';
|
|
24
|
+
const OWNER_RENOTIFICATION_SECTION = 'OWNER_RENOTIFICATION_SECTION';
|
|
24
25
|
const SUBAGENT_SECTION = 'SUBAGENT_SECTION';
|
|
25
26
|
|
|
26
27
|
describe('NotifySilentLiveSessionsUseCase', () => {
|
|
@@ -124,6 +125,9 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
124
125
|
composeMainStalledSection: jest
|
|
125
126
|
.fn()
|
|
126
127
|
.mockReturnValue(MAIN_STALLED_SECTION),
|
|
128
|
+
composeOwnerReNotificationSection: jest
|
|
129
|
+
.fn()
|
|
130
|
+
.mockReturnValue(OWNER_RENOTIFICATION_SECTION),
|
|
127
131
|
composeSubAgentSection: jest.fn().mockReturnValue(SUBAGENT_SECTION),
|
|
128
132
|
};
|
|
129
133
|
mockSleeper = {
|
|
@@ -197,7 +201,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
197
201
|
).toHaveBeenCalledWith(['workbench']);
|
|
198
202
|
});
|
|
199
203
|
|
|
200
|
-
it('
|
|
204
|
+
it('sends the owner-re-notification section instead of the stalled section when an owner call is pending past the threshold', async () => {
|
|
201
205
|
setupLiveInteractiveSession('workbench');
|
|
202
206
|
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
203
207
|
[
|
|
@@ -217,11 +221,92 @@ describe('NotifySilentLiveSessionsUseCase', () => {
|
|
|
217
221
|
expect(
|
|
218
222
|
mockMessageComposer.composeMainStalledSection,
|
|
219
223
|
).not.toHaveBeenCalled();
|
|
224
|
+
expect(
|
|
225
|
+
mockMessageComposer.composeOwnerReNotificationSection,
|
|
226
|
+
).toHaveBeenCalledWith(DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS);
|
|
227
|
+
expect(
|
|
228
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
229
|
+
).toHaveBeenCalledWith('workbench', OWNER_RENOTIFICATION_SECTION);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('does not send the owner-re-notification section when the owner-call wait is within the threshold', async () => {
|
|
233
|
+
setupLiveInteractiveSession('workbench');
|
|
234
|
+
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
235
|
+
[
|
|
236
|
+
{
|
|
237
|
+
sessionName: 'workbench',
|
|
238
|
+
lastOutputEpochSeconds:
|
|
239
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS + 1,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
);
|
|
243
|
+
mockOwnerCallStatusProvider.listSessionNamesWithUnansweredOwnerCall.mockResolvedValue(
|
|
244
|
+
new Set(['workbench']),
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
await useCase.run(runParams());
|
|
248
|
+
|
|
249
|
+
expect(
|
|
250
|
+
mockMessageComposer.composeOwnerReNotificationSection,
|
|
251
|
+
).not.toHaveBeenCalled();
|
|
220
252
|
expect(
|
|
221
253
|
mockNotificationRepository.sendSelfCheckNotification,
|
|
222
254
|
).not.toHaveBeenCalled();
|
|
223
255
|
});
|
|
224
256
|
|
|
257
|
+
it('does not re-notify the owner within the cooldown window while still waiting on the owner', async () => {
|
|
258
|
+
setupLiveInteractiveSession('workbench');
|
|
259
|
+
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
260
|
+
[
|
|
261
|
+
{
|
|
262
|
+
sessionName: 'workbench',
|
|
263
|
+
lastOutputEpochSeconds:
|
|
264
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
);
|
|
268
|
+
mockOwnerCallStatusProvider.listSessionNamesWithUnansweredOwnerCall.mockResolvedValue(
|
|
269
|
+
new Set(['workbench']),
|
|
270
|
+
);
|
|
271
|
+
mockNotificationRepository.getLastNotifiedEpochSeconds.mockResolvedValue(
|
|
272
|
+
nowEpochSeconds - DEFAULT_NOTIFICATION_COOLDOWN_SECONDS + 1,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
await useCase.run(runParams());
|
|
276
|
+
|
|
277
|
+
expect(
|
|
278
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
279
|
+
).not.toHaveBeenCalled();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('stops re-notifying the owner once a genuine owner reply clears the unanswered call', async () => {
|
|
283
|
+
setupLiveInteractiveSession('workbench');
|
|
284
|
+
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
285
|
+
[
|
|
286
|
+
{
|
|
287
|
+
sessionName: 'workbench',
|
|
288
|
+
lastOutputEpochSeconds:
|
|
289
|
+
nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
);
|
|
293
|
+
mockOwnerCallStatusProvider.listSessionNamesWithUnansweredOwnerCall.mockResolvedValue(
|
|
294
|
+
new Set<string>(),
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
await useCase.run(runParams());
|
|
298
|
+
|
|
299
|
+
expect(
|
|
300
|
+
mockMessageComposer.composeOwnerReNotificationSection,
|
|
301
|
+
).not.toHaveBeenCalled();
|
|
302
|
+
expect(mockMessageComposer.composeMainStalledSection).toHaveBeenCalledWith(
|
|
303
|
+
DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
|
|
304
|
+
);
|
|
305
|
+
expect(
|
|
306
|
+
mockNotificationRepository.sendSelfCheckNotification,
|
|
307
|
+
).toHaveBeenCalledWith('workbench', MAIN_STALLED_SECTION);
|
|
308
|
+
});
|
|
309
|
+
|
|
225
310
|
it('does not send the main stalled section when output is within the threshold', async () => {
|
|
226
311
|
setupLiveInteractiveSession('workbench');
|
|
227
312
|
mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
|
|
@@ -172,14 +172,27 @@ export class NotifySilentLiveSessionsUseCase {
|
|
|
172
172
|
|
|
173
173
|
if (
|
|
174
174
|
snapshot.mainSilentSeconds !== null &&
|
|
175
|
-
snapshot.mainSilentSeconds >= thresholds.mainSilentThresholdSeconds
|
|
176
|
-
!snapshot.hasUnansweredOwnerCall
|
|
175
|
+
snapshot.mainSilentSeconds >= thresholds.mainSilentThresholdSeconds
|
|
177
176
|
) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
if (snapshot.hasUnansweredOwnerCall) {
|
|
178
|
+
// The session is correctly waiting on the owner, but it has been silent
|
|
179
|
+
// long enough that the owner has effectively not been re-notified. Rather
|
|
180
|
+
// than suppress, instruct the agent to re-raise its pending call-to-user
|
|
181
|
+
// so the owner's marker re-fires. The per-session cooldown applied in
|
|
182
|
+
// run() limits this to roughly once per cooldown window, and it stops
|
|
183
|
+
// automatically once the owner replies (hasUnansweredOwnerCall clears).
|
|
184
|
+
sections.push(
|
|
185
|
+
this.messageComposer.composeOwnerReNotificationSection(
|
|
186
|
+
snapshot.mainSilentSeconds,
|
|
187
|
+
),
|
|
188
|
+
);
|
|
189
|
+
} else {
|
|
190
|
+
sections.push(
|
|
191
|
+
this.messageComposer.composeMainStalledSection(
|
|
192
|
+
snapshot.mainSilentSeconds,
|
|
193
|
+
),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
183
196
|
}
|
|
184
197
|
|
|
185
198
|
const stalledSubAgents = snapshot.subAgents.filter(
|
|
@@ -2,5 +2,6 @@ import { SubAgentActivity } from '../../entities/LiveSessionActivitySnapshot';
|
|
|
2
2
|
|
|
3
3
|
export interface SilentSessionMessageComposer {
|
|
4
4
|
composeMainStalledSection: (mainSilentSeconds: number) => string;
|
|
5
|
+
composeOwnerReNotificationSection: (waitingSeconds: number) => string;
|
|
5
6
|
composeSubAgentSection: (subAgents: SubAgentActivity[]) => string;
|
|
6
7
|
}
|
|
@@ -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;
|
|
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,CA8iBP;CACH"}
|
|
@@ -2,6 +2,7 @@ import { SubAgentActivity } from '../../domain/entities/LiveSessionActivitySnaps
|
|
|
2
2
|
import { SilentSessionMessageComposer } from '../../domain/usecases/adapter-interfaces/SilentSessionMessageComposer';
|
|
3
3
|
export type SilentSessionMessageTemplates = {
|
|
4
4
|
mainStalledMessage: string | null;
|
|
5
|
+
ownerReNotificationMessage: string | null;
|
|
5
6
|
subAgentMessageHeader: string | null;
|
|
6
7
|
subAgentMessageFooter: string | null;
|
|
7
8
|
};
|
|
@@ -10,6 +11,7 @@ export declare class ConfigurableSilentSessionMessageComposer implements SilentS
|
|
|
10
11
|
private readonly fallback;
|
|
11
12
|
constructor(templates: SilentSessionMessageTemplates, fallback: SilentSessionMessageComposer);
|
|
12
13
|
composeMainStalledSection: (mainSilentSeconds: number) => string;
|
|
14
|
+
composeOwnerReNotificationSection: (waitingSeconds: number) => string;
|
|
13
15
|
composeSubAgentSection: (subAgents: SubAgentActivity[]) => string;
|
|
14
16
|
}
|
|
15
17
|
//# sourceMappingURL=ConfigurableSilentSessionMessageComposer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigurableSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AACrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uEAAuE,CAAC;AAQrH,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC,CAAC;AAOF,qBAAa,wCAAyC,YAAW,4BAA4B;IAEzF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,SAAS,EAAE,6BAA6B,EACxC,QAAQ,EAAE,4BAA4B;IAGzD,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAK7D;IAEF,sBAAsB,GAAI,WAAW,gBAAgB,EAAE,KAAG,MAAM,CAsB9D;CACH"}
|
|
1
|
+
{"version":3,"file":"ConfigurableSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AACrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uEAAuE,CAAC;AAQrH,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC,CAAC;AAOF,qBAAa,wCAAyC,YAAW,4BAA4B;IAEzF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,SAAS,EAAE,6BAA6B,EACxC,QAAQ,EAAE,4BAA4B;IAGzD,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAK7D;IAEF,iCAAiC,GAAI,gBAAgB,MAAM,KAAG,MAAM,CAKlE;IAEF,sBAAsB,GAAI,WAAW,gBAAgB,EAAE,KAAG,MAAM,CAsB9D;CACH"}
|
|
@@ -2,6 +2,7 @@ import { SubAgentActivity } from '../entities/LiveSessionActivitySnapshot';
|
|
|
2
2
|
import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
|
|
3
3
|
export declare class DefaultSilentSessionMessageComposer implements SilentSessionMessageComposer {
|
|
4
4
|
composeMainStalledSection: (mainSilentSeconds: number) => string;
|
|
5
|
+
composeOwnerReNotificationSection: (waitingSeconds: number) => string;
|
|
5
6
|
composeSubAgentSection: (subAgents: SubAgentActivity[]) => string;
|
|
6
7
|
}
|
|
7
8
|
//# sourceMappingURL=DefaultSilentSessionMessageComposer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,mDAAmD,CAAC;
|
|
1
|
+
{"version":3,"file":"DefaultSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,mDAAmD,CAAC;AA2BjG,qBAAa,mCAAoC,YAAW,4BAA4B;IACtF,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAE7D;IAEF,iCAAiC,GAAI,gBAAgB,MAAM,KAAG,MAAM,CAElE;IAEF,sBAAsB,GAAI,WAAW,gBAAgB,EAAE,KAAG,MAAM,CAY9D;CACH"}
|
|
@@ -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,
|
|
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,CAkDpB;CACH"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SubAgentActivity } from '../../entities/LiveSessionActivitySnapshot';
|
|
2
2
|
export interface SilentSessionMessageComposer {
|
|
3
3
|
composeMainStalledSection: (mainSilentSeconds: number) => string;
|
|
4
|
+
composeOwnerReNotificationSection: (waitingSeconds: number) => string;
|
|
4
5
|
composeSubAgentSection: (subAgents: SubAgentActivity[]) => string;
|
|
5
6
|
}
|
|
6
7
|
//# sourceMappingURL=SilentSessionMessageComposer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAE9E,MAAM,WAAW,4BAA4B;IAC3C,yBAAyB,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,MAAM,CAAC;IACjE,sBAAsB,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,MAAM,CAAC;CACnE"}
|
|
1
|
+
{"version":3,"file":"SilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAE9E,MAAM,WAAW,4BAA4B;IAC3C,yBAAyB,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,MAAM,CAAC;IACjE,iCAAiC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,MAAM,CAAC;IACtE,sBAAsB,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,MAAM,CAAC;CACnE"}
|