github-issue-tower-defence-management 1.122.3 → 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 +15 -0
- package/README.md +8 -4
- package/bin/adapter/entry-points/console/consoleDoneStore.js +19 -5
- package/bin/adapter/entry-points/console/consoleDoneStore.js.map +1 -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/consoleListsWriter.js +2 -0
- package/bin/adapter/entry-points/handlers/consoleListsWriter.js.map +1 -1
- package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js +4 -2
- package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
- package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js +12 -7
- package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js.map +1 -1
- package/bin/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.js +60 -11
- package/bin/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.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/ProcessListSessionSubAgentActivityRepository.js +1 -0
- package/bin/adapter/repositories/ProcessListSessionSubAgentActivityRepository.js.map +1 -1
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js +14 -11
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js.map +1 -1
- package/bin/adapter/repositories/TranscriptSessionSubAgentActivityRepository.js +64 -7
- package/bin/adapter/repositories/TranscriptSessionSubAgentActivityRepository.js.map +1 -1
- package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js +18 -7
- package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js.map +1 -1
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +64 -19
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/consoleDoneStore.test.ts +38 -0
- package/src/adapter/entry-points/console/consoleDoneStore.ts +29 -4
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +11 -0
- package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +72 -0
- package/src/adapter/entry-points/handlers/consoleListsWriter.ts +3 -0
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +46 -7
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +6 -0
- package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +102 -37
- package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +22 -17
- package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +1 -1
- package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.test.ts +223 -0
- package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.ts +94 -10
- package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.test.ts +7 -6
- package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts +3 -3
- package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.test.ts +12 -2
- package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.ts +1 -0
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +99 -49
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +24 -14
- package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.test.ts +196 -4
- package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts +91 -7
- package/src/domain/entities/LiveSessionActivitySnapshot.ts +2 -1
- package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +187 -51
- package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +32 -15
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +356 -28
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +94 -26
- package/src/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.ts +2 -2
- package/src/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.ts +8 -0
- package/src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts +7 -6
- package/types/adapter/entry-points/console/consoleDoneStore.d.ts +2 -0
- package/types/adapter/entry-points/console/consoleDoneStore.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/consoleListsWriter.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 +4 -3
- package/types/adapter/repositories/ConfigurableSilentSessionMessageComposer.d.ts.map +1 -1
- package/types/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.d.ts +13 -2
- package/types/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.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/ProcessListSessionSubAgentActivityRepository.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/adapter/repositories/TranscriptSessionSubAgentActivityRepository.d.ts +5 -1
- package/types/adapter/repositories/TranscriptSessionSubAgentActivityRepository.d.ts.map +1 -1
- package/types/domain/entities/LiveSessionActivitySnapshot.d.ts +2 -1
- package/types/domain/entities/LiveSessionActivitySnapshot.d.ts.map +1 -1
- package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts +3 -3
- package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts.map +1 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +4 -1
- 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/SilentSessionCandidateStateRepository.d.ts +8 -0
- package/types/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.d.ts +5 -4
- package/types/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.d.ts.map +1 -1
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { DefaultSilentSessionMessageComposer } from './DefaultSilentSessionMessageComposer';
|
|
2
2
|
import { SILENT_SESSION_REMINDER_SENTINEL } from './silentSessionReminderSentinel';
|
|
3
3
|
|
|
4
|
-
const THRESHOLDS = {
|
|
5
|
-
subAgentSilentThresholdSeconds: 300,
|
|
6
|
-
subAgentRunningThresholdSeconds: 900,
|
|
7
|
-
};
|
|
8
|
-
|
|
9
4
|
describe('DefaultSilentSessionMessageComposer', () => {
|
|
10
5
|
const composer = new DefaultSilentSessionMessageComposer();
|
|
11
6
|
|
|
@@ -15,10 +10,16 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
15
10
|
});
|
|
16
11
|
|
|
17
12
|
it('embeds the reminder sentinel in the sub-agent section', () => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
const subAgent = {
|
|
14
|
+
label: 'sub-process-1',
|
|
15
|
+
silentSeconds: 360,
|
|
16
|
+
runningSeconds: 1200,
|
|
17
|
+
waitingOnExternalProcess: false,
|
|
18
|
+
};
|
|
19
|
+
const section = composer.composeSubAgentSection({
|
|
20
|
+
idleSubAgents: [subAgent],
|
|
21
|
+
longRunningSubAgents: [subAgent],
|
|
22
|
+
});
|
|
22
23
|
expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
|
|
23
24
|
});
|
|
24
25
|
|
|
@@ -115,11 +116,93 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
115
116
|
expect(section).not.toContain('""');
|
|
116
117
|
});
|
|
117
118
|
|
|
118
|
-
it('
|
|
119
|
-
const section = composer.
|
|
120
|
-
|
|
121
|
-
|
|
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',
|
|
122
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
|
+
|
|
194
|
+
it('emits a distinct idle message for a sub-agent that is only output-idle', () => {
|
|
195
|
+
const section = composer.composeSubAgentSection({
|
|
196
|
+
idleSubAgents: [
|
|
197
|
+
{
|
|
198
|
+
label: 'sub-process-idle',
|
|
199
|
+
silentSeconds: 360,
|
|
200
|
+
runningSeconds: 60,
|
|
201
|
+
waitingOnExternalProcess: false,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
longRunningSubAgents: [],
|
|
205
|
+
});
|
|
123
206
|
expect(section).toContain('sub-process-idle');
|
|
124
207
|
expect(section).toContain('no output for 6m');
|
|
125
208
|
expect(section).toContain('restart, hand off, or replace it');
|
|
@@ -128,10 +211,17 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
128
211
|
});
|
|
129
212
|
|
|
130
213
|
it('presents the system-detected idle duration as the authoritative signal in the idle message', () => {
|
|
131
|
-
const section = composer.composeSubAgentSection(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
214
|
+
const section = composer.composeSubAgentSection({
|
|
215
|
+
idleSubAgents: [
|
|
216
|
+
{
|
|
217
|
+
label: 'sub-process-idle',
|
|
218
|
+
silentSeconds: 360,
|
|
219
|
+
runningSeconds: 60,
|
|
220
|
+
waitingOnExternalProcess: false,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
longRunningSubAgents: [],
|
|
224
|
+
});
|
|
135
225
|
expect(section).toContain(
|
|
136
226
|
"The system has already detected, from each sub-process's last tool activity, that it has produced no output for about the minutes shown below.",
|
|
137
227
|
);
|
|
@@ -142,10 +232,17 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
142
232
|
});
|
|
143
233
|
|
|
144
234
|
it('forbids speculation and dismissal without evidence in the idle message', () => {
|
|
145
|
-
const section = composer.composeSubAgentSection(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
235
|
+
const section = composer.composeSubAgentSection({
|
|
236
|
+
idleSubAgents: [
|
|
237
|
+
{
|
|
238
|
+
label: 'sub-process-idle',
|
|
239
|
+
silentSeconds: 360,
|
|
240
|
+
runningSeconds: 60,
|
|
241
|
+
waitingOnExternalProcess: false,
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
longRunningSubAgents: [],
|
|
245
|
+
});
|
|
149
246
|
expect(section).toContain(
|
|
150
247
|
'Around five minutes of silence is a real warning of a possible hang',
|
|
151
248
|
);
|
|
@@ -155,10 +252,17 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
155
252
|
});
|
|
156
253
|
|
|
157
254
|
it('requires a concrete cause-check covering recent activity and external-dependency waiting in the idle message', () => {
|
|
158
|
-
const section = composer.composeSubAgentSection(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
255
|
+
const section = composer.composeSubAgentSection({
|
|
256
|
+
idleSubAgents: [
|
|
257
|
+
{
|
|
258
|
+
label: 'sub-process-idle',
|
|
259
|
+
silentSeconds: 360,
|
|
260
|
+
runningSeconds: 60,
|
|
261
|
+
waitingOnExternalProcess: false,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
longRunningSubAgents: [],
|
|
265
|
+
});
|
|
162
266
|
expect(section).toContain('determine the CAUSE by a concrete check');
|
|
163
267
|
expect(section).toContain(
|
|
164
268
|
'a very recent push or commit, or output from any nested sub-processes this sub-process itself started',
|
|
@@ -175,26 +279,34 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
175
279
|
});
|
|
176
280
|
|
|
177
281
|
it('requires logging the investigation result even though owner notification is not required', () => {
|
|
178
|
-
const section = composer.composeSubAgentSection(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
282
|
+
const section = composer.composeSubAgentSection({
|
|
283
|
+
idleSubAgents: [
|
|
284
|
+
{
|
|
285
|
+
label: 'sub-process-idle',
|
|
286
|
+
silentSeconds: 360,
|
|
287
|
+
runningSeconds: 60,
|
|
288
|
+
waitingOnExternalProcess: false,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
longRunningSubAgents: [],
|
|
292
|
+
});
|
|
182
293
|
expect(section).toContain(
|
|
183
294
|
'Owner notification is not required, but you MUST output your investigation result in this session so it remains as a log.',
|
|
184
295
|
);
|
|
185
296
|
});
|
|
186
297
|
|
|
187
298
|
it('emits a distinct long-running message for a sub-agent that has only run too long', () => {
|
|
188
|
-
const section = composer.composeSubAgentSection(
|
|
189
|
-
[
|
|
299
|
+
const section = composer.composeSubAgentSection({
|
|
300
|
+
idleSubAgents: [],
|
|
301
|
+
longRunningSubAgents: [
|
|
190
302
|
{
|
|
191
303
|
label: 'sub-process-long',
|
|
192
304
|
silentSeconds: 30,
|
|
193
305
|
runningSeconds: 1200,
|
|
306
|
+
waitingOnExternalProcess: false,
|
|
194
307
|
},
|
|
195
308
|
],
|
|
196
|
-
|
|
197
|
-
);
|
|
309
|
+
});
|
|
198
310
|
expect(section).toContain('sub-process-long');
|
|
199
311
|
expect(section).toContain('running for 20m');
|
|
200
312
|
expect(section).toContain('infinite loop');
|
|
@@ -209,25 +321,32 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
209
321
|
});
|
|
210
322
|
|
|
211
323
|
it('does not foreground the short idle time in the long-running message', () => {
|
|
212
|
-
const section = composer.composeSubAgentSection(
|
|
213
|
-
[
|
|
324
|
+
const section = composer.composeSubAgentSection({
|
|
325
|
+
idleSubAgents: [],
|
|
326
|
+
longRunningSubAgents: [
|
|
214
327
|
{
|
|
215
328
|
label: 'sub-process-long',
|
|
216
329
|
silentSeconds: 30,
|
|
217
330
|
runningSeconds: 1200,
|
|
331
|
+
waitingOnExternalProcess: false,
|
|
218
332
|
},
|
|
219
333
|
],
|
|
220
|
-
|
|
221
|
-
);
|
|
334
|
+
});
|
|
222
335
|
expect(section).not.toContain('no output for');
|
|
223
336
|
expect(section).not.toContain('silent for');
|
|
224
337
|
});
|
|
225
338
|
|
|
226
339
|
it('emits both distinct messages for a sub-agent matching both conditions, kept separate', () => {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
340
|
+
const subAgent = {
|
|
341
|
+
label: 'sub-process-both',
|
|
342
|
+
silentSeconds: 360,
|
|
343
|
+
runningSeconds: 1200,
|
|
344
|
+
waitingOnExternalProcess: false,
|
|
345
|
+
};
|
|
346
|
+
const section = composer.composeSubAgentSection({
|
|
347
|
+
idleSubAgents: [subAgent],
|
|
348
|
+
longRunningSubAgents: [subAgent],
|
|
349
|
+
});
|
|
231
350
|
expect(section).toContain('no output for 6m');
|
|
232
351
|
expect(section).toContain('determine the CAUSE by a concrete check');
|
|
233
352
|
expect(section).toContain('running for 20m');
|
|
@@ -238,13 +357,24 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
238
357
|
});
|
|
239
358
|
|
|
240
359
|
it('groups each sub-agent under the condition it matched', () => {
|
|
241
|
-
const section = composer.composeSubAgentSection(
|
|
242
|
-
[
|
|
243
|
-
{
|
|
244
|
-
|
|
360
|
+
const section = composer.composeSubAgentSection({
|
|
361
|
+
idleSubAgents: [
|
|
362
|
+
{
|
|
363
|
+
label: 'idle-only',
|
|
364
|
+
silentSeconds: 360,
|
|
365
|
+
runningSeconds: 60,
|
|
366
|
+
waitingOnExternalProcess: false,
|
|
367
|
+
},
|
|
245
368
|
],
|
|
246
|
-
|
|
247
|
-
|
|
369
|
+
longRunningSubAgents: [
|
|
370
|
+
{
|
|
371
|
+
label: 'long-only',
|
|
372
|
+
silentSeconds: 10,
|
|
373
|
+
runningSeconds: 960,
|
|
374
|
+
waitingOnExternalProcess: false,
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
});
|
|
248
378
|
const [idleSection, longRunningSection] = section.split('\n\n');
|
|
249
379
|
expect(idleSection).toContain('idle-only');
|
|
250
380
|
expect(idleSection).not.toContain('long-only');
|
|
@@ -254,10 +384,16 @@ describe('DefaultSilentSessionMessageComposer', () => {
|
|
|
254
384
|
|
|
255
385
|
it('does not contain any host-specific or internal identifiers', () => {
|
|
256
386
|
const mainSection = composer.composeMainStalledSection(600);
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
387
|
+
const subAgent = {
|
|
388
|
+
label: 'sub-process-1',
|
|
389
|
+
silentSeconds: 360,
|
|
390
|
+
runningSeconds: 1200,
|
|
391
|
+
waitingOnExternalProcess: false,
|
|
392
|
+
};
|
|
393
|
+
const subSection = composer.composeSubAgentSection({
|
|
394
|
+
idleSubAgents: [subAgent],
|
|
395
|
+
longRunningSubAgents: [subAgent],
|
|
396
|
+
});
|
|
261
397
|
const combined = `${mainSection}\n${subSection}`.toLowerCase();
|
|
262
398
|
expect(combined).not.toContain('claude');
|
|
263
399
|
expect(combined).not.toContain('take ownership');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SubAgentActivity } from '../entities/LiveSessionActivitySnapshot';
|
|
2
2
|
import {
|
|
3
3
|
SilentSessionMessageComposer,
|
|
4
|
-
|
|
4
|
+
SubAgentStallSections,
|
|
5
5
|
} from './adapter-interfaces/SilentSessionMessageComposer';
|
|
6
6
|
import { SILENT_SESSION_REMINDER_SENTINEL } from './silentSessionReminderSentinel';
|
|
7
7
|
|
|
@@ -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,24 +89,26 @@ export class DefaultSilentSessionMessageComposer implements SilentSessionMessage
|
|
|
74
89
|
return composeMainStalledMessage(mainSilentSeconds, this.ownerCallMarker);
|
|
75
90
|
};
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
composeMainStalledWithStaleOwnerCallSection = (
|
|
93
|
+
mainSilentSeconds: number,
|
|
94
|
+
unansweredOwnerCallAgeSeconds: number,
|
|
80
95
|
): string => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const longRunningSubAgents = subAgents.filter(
|
|
86
|
-
(subAgent) =>
|
|
87
|
-
subAgent.runningSeconds >= thresholds.subAgentRunningThresholdSeconds,
|
|
96
|
+
return composeMainStalledWithStaleOwnerCallMessage(
|
|
97
|
+
mainSilentSeconds,
|
|
98
|
+
unansweredOwnerCallAgeSeconds,
|
|
99
|
+
this.ownerCallMarker,
|
|
88
100
|
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
composeSubAgentSection = (stallSections: SubAgentStallSections): string => {
|
|
89
104
|
const sections: string[] = [];
|
|
90
|
-
if (idleSubAgents.length > 0) {
|
|
91
|
-
sections.push(composeIdleSubAgentSection(idleSubAgents));
|
|
105
|
+
if (stallSections.idleSubAgents.length > 0) {
|
|
106
|
+
sections.push(composeIdleSubAgentSection(stallSections.idleSubAgents));
|
|
92
107
|
}
|
|
93
|
-
if (longRunningSubAgents.length > 0) {
|
|
94
|
-
sections.push(
|
|
108
|
+
if (stallSections.longRunningSubAgents.length > 0) {
|
|
109
|
+
sections.push(
|
|
110
|
+
composeLongRunningSubAgentSection(stallSections.longRunningSubAgents),
|
|
111
|
+
);
|
|
95
112
|
}
|
|
96
113
|
return sections.join('\n\n');
|
|
97
114
|
};
|