github-issue-tower-defence-management 1.140.2 → 1.141.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +7 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +43 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.js +32 -0
- package/bin/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.js.map +1 -0
- package/bin/adapter/repositories/FileSystemSessionAssistantTurnsRepository.js +135 -0
- package/bin/adapter/repositories/FileSystemSessionAssistantTurnsRepository.js.map +1 -0
- package/bin/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.js +120 -0
- package/bin/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.js.map +1 -0
- package/bin/domain/entities/OutputDegeneration.js +3 -0
- package/bin/domain/entities/OutputDegeneration.js.map +1 -0
- package/bin/domain/usecases/OutputDegenerationDetector.js +150 -0
- package/bin/domain/usecases/OutputDegenerationDetector.js.map +1 -0
- package/bin/domain/usecases/SessionOutputDegenerationRecoveryUseCase.js +84 -0
- package/bin/domain/usecases/SessionOutputDegenerationRecoveryUseCase.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +44 -0
- package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.test.ts +69 -0
- package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts +71 -0
- package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.test.ts +138 -0
- package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.ts +113 -0
- package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.test.ts +85 -0
- package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.ts +105 -0
- package/src/domain/entities/OutputDegeneration.ts +10 -0
- package/src/domain/usecases/OutputDegenerationDetector.test.ts +167 -0
- package/src/domain/usecases/OutputDegenerationDetector.ts +169 -0
- package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.test.ts +224 -0
- package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.ts +142 -0
- package/src/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.ts +6 -0
- package/src/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.ts +4 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.d.ts +19 -0
- package/types/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.d.ts.map +1 -0
- package/types/adapter/repositories/FileSystemSessionAssistantTurnsRepository.d.ts +10 -0
- package/types/adapter/repositories/FileSystemSessionAssistantTurnsRepository.d.ts.map +1 -0
- package/types/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.d.ts +15 -0
- package/types/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.d.ts.map +1 -0
- package/types/domain/entities/OutputDegeneration.d.ts +10 -0
- package/types/domain/entities/OutputDegeneration.d.ts.map +1 -0
- package/types/domain/usecases/OutputDegenerationDetector.d.ts +32 -0
- package/types/domain/usecases/OutputDegenerationDetector.d.ts.map +1 -0
- package/types/domain/usecases/SessionOutputDegenerationRecoveryUseCase.d.ts +31 -0
- package/types/domain/usecases/SessionOutputDegenerationRecoveryUseCase.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.d.ts +4 -0
- package/types/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.d.ts +8 -0
- package/types/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.d.ts.map +1 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { SessionOutputDegenerationRecoveryUseCase } from './SessionOutputDegenerationRecoveryUseCase';
|
|
2
|
+
import { LiveSessionProcessSnapshotProvider } from './adapter-interfaces/LiveSessionProcessSnapshotProvider';
|
|
3
|
+
import { InteractiveLiveSessionTranscriptResolver } from './adapter-interfaces/InteractiveLiveSessionTranscriptResolver';
|
|
4
|
+
import { SessionAssistantTurnsRepository } from './adapter-interfaces/SessionAssistantTurnsRepository';
|
|
5
|
+
import { SessionDegenerationCooldownStateRepository } from './adapter-interfaces/SessionDegenerationCooldownStateRepository';
|
|
6
|
+
import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
|
|
7
|
+
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
8
|
+
import { Sleeper } from './adapter-interfaces/Sleeper';
|
|
9
|
+
import { LiveSessionProcessSnapshot } from '../entities/LiveSessionProcessSnapshot';
|
|
10
|
+
|
|
11
|
+
const SESSION_NAME = 'https://github.com/HiromiShikata/secretary/issues/2824';
|
|
12
|
+
|
|
13
|
+
const buildSnapshot = (sessionName: string): LiveSessionProcessSnapshot => ({
|
|
14
|
+
sessions: [{ sessionName, panePids: [100] }],
|
|
15
|
+
processes: [
|
|
16
|
+
{
|
|
17
|
+
pid: 100,
|
|
18
|
+
ppid: 1,
|
|
19
|
+
commandLine: 'claude --resume',
|
|
20
|
+
sessionId: 'session-1',
|
|
21
|
+
currentSessionId: 'session-1',
|
|
22
|
+
configDir: '/home/user/.claude',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const repeat = (token: string, count: number): string =>
|
|
28
|
+
Array.from({ length: count }, () => token).join(' ');
|
|
29
|
+
|
|
30
|
+
type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
31
|
+
|
|
32
|
+
describe('SessionOutputDegenerationRecoveryUseCase', () => {
|
|
33
|
+
let snapshotProvider: Mocked<LiveSessionProcessSnapshotProvider>;
|
|
34
|
+
let transcriptResolver: Mocked<InteractiveLiveSessionTranscriptResolver>;
|
|
35
|
+
let assistantTurnsRepository: Mocked<SessionAssistantTurnsRepository>;
|
|
36
|
+
let notificationRepository: Mocked<SilentSessionNotificationRepository>;
|
|
37
|
+
let tmuxSessionRepository: Mocked<Pick<TmuxSessionRepository, 'killSession'>>;
|
|
38
|
+
let cooldownStateRepository: Mocked<SessionDegenerationCooldownStateRepository>;
|
|
39
|
+
let sleeper: Mocked<Sleeper>;
|
|
40
|
+
let useCase: SessionOutputDegenerationRecoveryUseCase;
|
|
41
|
+
|
|
42
|
+
const now = new Date('2026-07-26T00:00:00Z');
|
|
43
|
+
const nowEpochSeconds = Math.floor(now.getTime() / 1000);
|
|
44
|
+
|
|
45
|
+
const runWith = (params: {
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
turns: string[];
|
|
48
|
+
lastResetEpochSeconds?: number;
|
|
49
|
+
}): Promise<void> => {
|
|
50
|
+
snapshotProvider.getSnapshot.mockResolvedValue(buildSnapshot(SESSION_NAME));
|
|
51
|
+
transcriptResolver.resolveTranscriptPaths.mockReturnValue(
|
|
52
|
+
new Map([[SESSION_NAME, '/fake/transcript.jsonl']]),
|
|
53
|
+
);
|
|
54
|
+
assistantTurnsRepository.listRecentAssistantTurnsBySessionName.mockResolvedValue(
|
|
55
|
+
new Map([[SESSION_NAME, params.turns]]),
|
|
56
|
+
);
|
|
57
|
+
cooldownStateRepository.loadLastResetEpochSecondsBySessionName.mockResolvedValue(
|
|
58
|
+
params.lastResetEpochSeconds === undefined
|
|
59
|
+
? new Map()
|
|
60
|
+
: new Map([[SESSION_NAME, params.lastResetEpochSeconds]]),
|
|
61
|
+
);
|
|
62
|
+
return useCase.run({
|
|
63
|
+
enabled: params.enabled,
|
|
64
|
+
warningMessage: 'WARNING',
|
|
65
|
+
graceSeconds: 5,
|
|
66
|
+
cooldownSeconds: 300,
|
|
67
|
+
now,
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
snapshotProvider = {
|
|
73
|
+
getSnapshot: jest.fn(),
|
|
74
|
+
};
|
|
75
|
+
transcriptResolver = {
|
|
76
|
+
resolveTranscriptPaths: jest.fn(),
|
|
77
|
+
};
|
|
78
|
+
assistantTurnsRepository = {
|
|
79
|
+
listRecentAssistantTurnsBySessionName: jest.fn(),
|
|
80
|
+
};
|
|
81
|
+
notificationRepository = {
|
|
82
|
+
sendSelfCheckNotification: jest.fn().mockResolvedValue(undefined),
|
|
83
|
+
};
|
|
84
|
+
tmuxSessionRepository = {
|
|
85
|
+
killSession: jest.fn().mockResolvedValue(undefined),
|
|
86
|
+
};
|
|
87
|
+
cooldownStateRepository = {
|
|
88
|
+
loadLastResetEpochSecondsBySessionName: jest.fn(),
|
|
89
|
+
recordReset: jest.fn().mockResolvedValue(undefined),
|
|
90
|
+
};
|
|
91
|
+
sleeper = {
|
|
92
|
+
sleep: jest.fn().mockResolvedValue(undefined),
|
|
93
|
+
};
|
|
94
|
+
useCase = new SessionOutputDegenerationRecoveryUseCase(
|
|
95
|
+
snapshotProvider,
|
|
96
|
+
transcriptResolver,
|
|
97
|
+
assistantTurnsRepository,
|
|
98
|
+
notificationRepository,
|
|
99
|
+
tmuxSessionRepository,
|
|
100
|
+
cooldownStateRepository,
|
|
101
|
+
sleeper,
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('warns, waits the grace period, kills, and records the reset when enabled and degenerated (intra-turn)', async () => {
|
|
106
|
+
await runWith({ enabled: true, turns: [repeat('court', 40)] });
|
|
107
|
+
|
|
108
|
+
expect(
|
|
109
|
+
notificationRepository.sendSelfCheckNotification,
|
|
110
|
+
).toHaveBeenCalledWith(SESSION_NAME, 'WARNING');
|
|
111
|
+
expect(sleeper.sleep).toHaveBeenCalledWith(5000);
|
|
112
|
+
expect(tmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
113
|
+
SESSION_NAME,
|
|
114
|
+
);
|
|
115
|
+
expect(cooldownStateRepository.recordReset).toHaveBeenCalledWith({
|
|
116
|
+
sessionName: SESSION_NAME,
|
|
117
|
+
now,
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('does not warn or kill in dry-run but still records the reset for cooldown', async () => {
|
|
122
|
+
await runWith({ enabled: false, turns: [repeat('court', 40)] });
|
|
123
|
+
|
|
124
|
+
expect(
|
|
125
|
+
notificationRepository.sendSelfCheckNotification,
|
|
126
|
+
).not.toHaveBeenCalled();
|
|
127
|
+
expect(sleeper.sleep).not.toHaveBeenCalled();
|
|
128
|
+
expect(tmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
129
|
+
expect(cooldownStateRepository.recordReset).toHaveBeenCalledWith({
|
|
130
|
+
sessionName: SESSION_NAME,
|
|
131
|
+
now,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('skips a session that was reset within the cooldown window', async () => {
|
|
136
|
+
await runWith({
|
|
137
|
+
enabled: true,
|
|
138
|
+
turns: [repeat('court', 40)],
|
|
139
|
+
lastResetEpochSeconds: nowEpochSeconds - 100,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(
|
|
143
|
+
notificationRepository.sendSelfCheckNotification,
|
|
144
|
+
).not.toHaveBeenCalled();
|
|
145
|
+
expect(tmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
146
|
+
expect(cooldownStateRepository.recordReset).not.toHaveBeenCalled();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('acts again once the cooldown window has elapsed', async () => {
|
|
150
|
+
await runWith({
|
|
151
|
+
enabled: true,
|
|
152
|
+
turns: [repeat('court', 40)],
|
|
153
|
+
lastResetEpochSeconds: nowEpochSeconds - 301,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(tmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
157
|
+
SESSION_NAME,
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('does not act on a healthy session', async () => {
|
|
162
|
+
await runWith({
|
|
163
|
+
enabled: true,
|
|
164
|
+
turns: [
|
|
165
|
+
'I finished the migration, verified the seed data, and uploaded the report.',
|
|
166
|
+
],
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(
|
|
170
|
+
notificationRepository.sendSelfCheckNotification,
|
|
171
|
+
).not.toHaveBeenCalled();
|
|
172
|
+
expect(tmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
173
|
+
expect(cooldownStateRepository.recordReset).not.toHaveBeenCalled();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('resets a session detected only by the cross-turn signature', async () => {
|
|
177
|
+
const clean = (index: number): string =>
|
|
178
|
+
`Turn ${index}: completed and reported.`;
|
|
179
|
+
const trailing = (index: number): string =>
|
|
180
|
+
`Turn ${index}: real message.\n\ncourt`;
|
|
181
|
+
const turns = [
|
|
182
|
+
trailing(1),
|
|
183
|
+
clean(2),
|
|
184
|
+
trailing(3),
|
|
185
|
+
clean(4),
|
|
186
|
+
trailing(5),
|
|
187
|
+
clean(6),
|
|
188
|
+
trailing(7),
|
|
189
|
+
clean(8),
|
|
190
|
+
clean(9),
|
|
191
|
+
clean(10),
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
await runWith({ enabled: true, turns });
|
|
195
|
+
|
|
196
|
+
expect(tmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
197
|
+
SESSION_NAME,
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('does not act on a non-github-named interactive session', async () => {
|
|
202
|
+
snapshotProvider.getSnapshot.mockResolvedValue(buildSnapshot('app'));
|
|
203
|
+
transcriptResolver.resolveTranscriptPaths.mockReturnValue(new Map());
|
|
204
|
+
assistantTurnsRepository.listRecentAssistantTurnsBySessionName.mockResolvedValue(
|
|
205
|
+
new Map(),
|
|
206
|
+
);
|
|
207
|
+
cooldownStateRepository.loadLastResetEpochSecondsBySessionName.mockResolvedValue(
|
|
208
|
+
new Map(),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
await useCase.run({
|
|
212
|
+
enabled: true,
|
|
213
|
+
warningMessage: 'WARNING',
|
|
214
|
+
graceSeconds: 5,
|
|
215
|
+
cooldownSeconds: 300,
|
|
216
|
+
now,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
expect(
|
|
220
|
+
assistantTurnsRepository.listRecentAssistantTurnsBySessionName,
|
|
221
|
+
).toHaveBeenCalledWith(new Map(), 10);
|
|
222
|
+
expect(tmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { LiveSessionProcessSnapshotProvider } from './adapter-interfaces/LiveSessionProcessSnapshotProvider';
|
|
2
|
+
import { InteractiveLiveSessionTranscriptResolver } from './adapter-interfaces/InteractiveLiveSessionTranscriptResolver';
|
|
3
|
+
import { SessionAssistantTurnsRepository } from './adapter-interfaces/SessionAssistantTurnsRepository';
|
|
4
|
+
import { SessionDegenerationCooldownStateRepository } from './adapter-interfaces/SessionDegenerationCooldownStateRepository';
|
|
5
|
+
import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
|
|
6
|
+
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
7
|
+
import { Sleeper } from './adapter-interfaces/Sleeper';
|
|
8
|
+
import {
|
|
9
|
+
OutputDegenerationDetector,
|
|
10
|
+
OUTPUT_DEGENERATION_CROSS_TURN_WINDOW,
|
|
11
|
+
} from './OutputDegenerationDetector';
|
|
12
|
+
import { ResolveInteractiveLiveSessionsUseCase } from './ResolveInteractiveLiveSessionsUseCase';
|
|
13
|
+
import { isGitHubIssueOrPullRequestSessionName } from './NotifySilentLiveSessionsUseCase';
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_OUTPUT_DEGENERATION_GRACE_SECONDS = 5;
|
|
16
|
+
export const DEFAULT_OUTPUT_DEGENERATION_COOLDOWN_SECONDS = 300;
|
|
17
|
+
export const DEFAULT_OUTPUT_DEGENERATION_WARNING_MESSAGE =
|
|
18
|
+
'OUTPUT DEGENERATION DETECTED: your session is about to be reset. Immediately write a checkpoint to your assigned task issue (single concrete next action to resume, working directory and branch, in-flight sub-agent branch/PR URLs).';
|
|
19
|
+
|
|
20
|
+
export class SessionOutputDegenerationRecoveryUseCase {
|
|
21
|
+
private readonly resolveInteractiveLiveSessions =
|
|
22
|
+
new ResolveInteractiveLiveSessionsUseCase();
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
private readonly liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider,
|
|
26
|
+
private readonly interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver,
|
|
27
|
+
private readonly assistantTurnsRepository: SessionAssistantTurnsRepository,
|
|
28
|
+
private readonly notificationRepository: SilentSessionNotificationRepository,
|
|
29
|
+
private readonly tmuxSessionRepository: Pick<
|
|
30
|
+
TmuxSessionRepository,
|
|
31
|
+
'killSession'
|
|
32
|
+
>,
|
|
33
|
+
private readonly cooldownStateRepository: SessionDegenerationCooldownStateRepository,
|
|
34
|
+
private readonly sleeper: Sleeper,
|
|
35
|
+
private readonly detector: OutputDegenerationDetector = new OutputDegenerationDetector(),
|
|
36
|
+
) {}
|
|
37
|
+
|
|
38
|
+
run = async (params: {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
warningMessage: string;
|
|
41
|
+
graceSeconds: number;
|
|
42
|
+
cooldownSeconds: number;
|
|
43
|
+
now: Date;
|
|
44
|
+
}): Promise<void> => {
|
|
45
|
+
const snapshot =
|
|
46
|
+
await this.liveSessionProcessSnapshotProvider.getSnapshot();
|
|
47
|
+
const allInteractiveSessions =
|
|
48
|
+
this.resolveInteractiveLiveSessions.resolve(snapshot);
|
|
49
|
+
const interactiveSessions = allInteractiveSessions.filter((session) =>
|
|
50
|
+
isGitHubIssueOrPullRequestSessionName(session.sessionName),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const transcriptPathBySessionName =
|
|
54
|
+
this.interactiveLiveSessionTranscriptResolver.resolveTranscriptPaths(
|
|
55
|
+
interactiveSessions,
|
|
56
|
+
);
|
|
57
|
+
const turnsBySessionName =
|
|
58
|
+
await this.assistantTurnsRepository.listRecentAssistantTurnsBySessionName(
|
|
59
|
+
transcriptPathBySessionName,
|
|
60
|
+
OUTPUT_DEGENERATION_CROSS_TURN_WINDOW,
|
|
61
|
+
);
|
|
62
|
+
const lastResetBySessionName =
|
|
63
|
+
await this.cooldownStateRepository.loadLastResetEpochSecondsBySessionName();
|
|
64
|
+
|
|
65
|
+
const nowEpochSeconds = Math.floor(params.now.getTime() / 1000);
|
|
66
|
+
let detectedCount = 0;
|
|
67
|
+
let resetCount = 0;
|
|
68
|
+
let dryRunCount = 0;
|
|
69
|
+
let cooldownSkippedCount = 0;
|
|
70
|
+
|
|
71
|
+
for (const session of interactiveSessions) {
|
|
72
|
+
const sessionName = session.sessionName;
|
|
73
|
+
const lastResetEpochSeconds = lastResetBySessionName.get(sessionName);
|
|
74
|
+
if (
|
|
75
|
+
lastResetEpochSeconds !== undefined &&
|
|
76
|
+
nowEpochSeconds - lastResetEpochSeconds < params.cooldownSeconds
|
|
77
|
+
) {
|
|
78
|
+
cooldownSkippedCount += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const turns = turnsBySessionName.get(sessionName) ?? [];
|
|
83
|
+
const newestTurn = turns.length > 0 ? turns[0] : null;
|
|
84
|
+
const intraTurn =
|
|
85
|
+
newestTurn !== null &&
|
|
86
|
+
this.detector.isIntraTurnDegeneration(newestTurn);
|
|
87
|
+
const crossTurn = this.detector.detectCrossTurnDegeneration(turns);
|
|
88
|
+
if (!intraTurn && crossTurn === null) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
detectedCount += 1;
|
|
92
|
+
|
|
93
|
+
if (intraTurn && newestTurn !== null) {
|
|
94
|
+
const { token, run, total } =
|
|
95
|
+
this.detector.maxConsecutiveShortTokenRun(newestTurn);
|
|
96
|
+
console.log(
|
|
97
|
+
`Output degeneration detected (intra-turn) session=${sessionName} token=${JSON.stringify(token)} run=${run} turnTokens=${total} enabled=${params.enabled}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (crossTurn !== null) {
|
|
101
|
+
console.log(
|
|
102
|
+
`Output degeneration detected (cross-turn) session=${sessionName} token=${JSON.stringify(crossTurn.token)} turns=${crossTurn.turnCount}/${Math.min(turns.length, OUTPUT_DEGENERATION_CROSS_TURN_WINDOW)} enabled=${params.enabled}`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (params.enabled) {
|
|
107
|
+
try {
|
|
108
|
+
await this.notificationRepository.sendSelfCheckNotification(
|
|
109
|
+
sessionName,
|
|
110
|
+
params.warningMessage,
|
|
111
|
+
);
|
|
112
|
+
await this.sleeper.sleep(params.graceSeconds * 1000);
|
|
113
|
+
await this.tmuxSessionRepository.killSession(sessionName);
|
|
114
|
+
resetCount += 1;
|
|
115
|
+
console.log(
|
|
116
|
+
`Output degeneration reset session=${sessionName} graceSeconds=${params.graceSeconds}`,
|
|
117
|
+
);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error(
|
|
120
|
+
`Failed to reset degenerated session ${sessionName}: ${
|
|
121
|
+
error instanceof Error ? error.message : String(error)
|
|
122
|
+
}`,
|
|
123
|
+
);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
dryRunCount += 1;
|
|
128
|
+
console.log(
|
|
129
|
+
`Output degeneration dry-run: would warn and reset session=${sessionName}`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
await this.cooldownStateRepository.recordReset({
|
|
133
|
+
sessionName,
|
|
134
|
+
now: params.now,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(
|
|
139
|
+
`Output degeneration recovery: detected ${detectedCount} degenerated session(s) of ${interactiveSessions.length} interactive session(s); reset ${resetCount}, dry-run would-reset ${dryRunCount}, cooldown-skipped ${cooldownSkippedCount}.`,
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAyCA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAkD3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,EACjB,6BAA4B,MAAM,EAAE,GAAG,IAAW,KACjD,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,CA8qBP;CACH"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
2
|
+
import { ProcessEnvironReader } from '../../../domain/usecases/adapter-interfaces/ProcessEnvironReader';
|
|
3
|
+
export type ResetDegeneratedTmuxSessionsParams = {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
localCommandRunner: LocalCommandRunner;
|
|
6
|
+
processEnvironReader?: ProcessEnvironReader;
|
|
7
|
+
warningMessage: string;
|
|
8
|
+
graceSeconds: number;
|
|
9
|
+
cooldownSeconds: number;
|
|
10
|
+
cooldownStateFilePath: string | null;
|
|
11
|
+
now: Date;
|
|
12
|
+
};
|
|
13
|
+
export declare const resetDegeneratedTmuxSessions: (params: ResetDegeneratedTmuxSessionsParams) => Promise<void>;
|
|
14
|
+
export declare const DEFAULT_RESET_DEGENERATED_TMUX_SESSIONS_PARAMS: {
|
|
15
|
+
readonly warningMessage: "OUTPUT DEGENERATION DETECTED: your session is about to be reset. Immediately write a checkpoint to your assigned task issue (single concrete next action to resume, working directory and branch, in-flight sub-agent branch/PR URLs).";
|
|
16
|
+
readonly graceSeconds: 5;
|
|
17
|
+
readonly cooldownSeconds: 300;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=resetDegeneratedTmuxSessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resetDegeneratedTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AACpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AAgBxG,MAAM,MAAM,kCAAkC,GAAG;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,QAAQ,kCAAkC,KACzC,OAAO,CAAC,IAAI,CAkCd,CAAC;AAEF,eAAO,MAAM,8CAA8C;;;;CAIjD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SessionAssistantTurnsRepository } from '../../domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository';
|
|
2
|
+
export declare const DEFAULT_TRANSCRIPT_TAIL_BYTES = 3000000;
|
|
3
|
+
export declare class FileSystemSessionAssistantTurnsRepository implements SessionAssistantTurnsRepository {
|
|
4
|
+
private readonly tailBytes;
|
|
5
|
+
constructor(tailBytes?: number);
|
|
6
|
+
listRecentAssistantTurnsBySessionName: (transcriptPathBySessionName: Map<string, string>, maxTurnsPerSession: number) => Promise<Map<string, string[]>>;
|
|
7
|
+
private readRecentAssistantTurns;
|
|
8
|
+
private readTailLines;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=FileSystemSessionAssistantTurnsRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSystemSessionAssistantTurnsRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,0EAA0E,CAAC;AAE3H,eAAO,MAAM,6BAA6B,UAAY,CAAC;AA0BvD,qBAAa,yCAA0C,YAAW,+BAA+B;IAE7F,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,GAAE,MAAsC;IAGpE,qCAAqC,GACnC,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChD,oBAAoB,MAAM,KACzB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAY/B;IAEF,OAAO,CAAC,wBAAwB,CAkC9B;IAEF,OAAO,CAAC,aAAa,CAwBnB;CACH"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SessionDegenerationCooldownStateRepository } from '../../domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository';
|
|
2
|
+
export declare const DEFAULT_RESET_RETENTION_WINDOW_SECONDS: number;
|
|
3
|
+
export declare class FileSystemSessionDegenerationCooldownStateRepository implements SessionDegenerationCooldownStateRepository {
|
|
4
|
+
private readonly stateFilePath;
|
|
5
|
+
private readonly retentionWindowSeconds;
|
|
6
|
+
constructor(stateFilePath?: string, retentionWindowSeconds?: number);
|
|
7
|
+
loadLastResetEpochSecondsBySessionName: () => Promise<Map<string, number>>;
|
|
8
|
+
recordReset: (params: {
|
|
9
|
+
sessionName: string;
|
|
10
|
+
now: Date;
|
|
11
|
+
}) => Promise<void>;
|
|
12
|
+
private readResetEntries;
|
|
13
|
+
private writeState;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=FileSystemSessionDegenerationCooldownStateRepository.d.ts.map
|
package/types/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSystemSessionDegenerationCooldownStateRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,0CAA0C,EAAE,MAAM,qFAAqF,CAAC;AAUjJ,eAAO,MAAM,sCAAsC,QAAU,CAAC;AAO9D,qBAAa,oDAAqD,YAAW,0CAA0C;IAEnH,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBADtB,aAAa,GAAE,MAA+B,EAC9C,sBAAsB,GAAE,MAA+C;IAG1F,sCAAsC,QAAa,OAAO,CACxD,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CACpB,CAMC;IAEF,WAAW,GAAU,QAAQ;QAC3B,WAAW,EAAE,MAAM,CAAC;QACpB,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CAkBf;IAEF,OAAO,CAAC,gBAAgB,CAoCtB;IAEF,OAAO,CAAC,UAAU,CAMhB;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OutputDegeneration.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/OutputDegeneration.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CrossTurnDegeneration } from '../entities/OutputDegeneration';
|
|
2
|
+
export declare const OUTPUT_DEGENERATION_REPEAT_THRESHOLD = 5;
|
|
3
|
+
export declare const OUTPUT_DEGENERATION_DOMINATION_FRACTION = 0.8;
|
|
4
|
+
export declare const OUTPUT_DEGENERATION_MAX_TOKEN_LENGTH = 32;
|
|
5
|
+
export declare const OUTPUT_DEGENERATION_ABSOLUTE_RUN_THRESHOLD = 15;
|
|
6
|
+
export declare const OUTPUT_DEGENERATION_REP4_THRESHOLD = 0.3;
|
|
7
|
+
export declare const OUTPUT_DEGENERATION_REP4_MIN_TOKENS = 40;
|
|
8
|
+
export declare const OUTPUT_DEGENERATION_CROSS_TURN_WINDOW = 10;
|
|
9
|
+
export declare const OUTPUT_DEGENERATION_CROSS_TURN_MIN_TURNS = 4;
|
|
10
|
+
export type ShortTokenRun = {
|
|
11
|
+
token: string | null;
|
|
12
|
+
run: number;
|
|
13
|
+
total: number;
|
|
14
|
+
};
|
|
15
|
+
export declare class OutputDegenerationDetector {
|
|
16
|
+
private readonly repeatThreshold;
|
|
17
|
+
private readonly dominationFraction;
|
|
18
|
+
private readonly maxTokenLength;
|
|
19
|
+
private readonly absoluteRunThreshold;
|
|
20
|
+
private readonly rep4Threshold;
|
|
21
|
+
private readonly rep4MinTokens;
|
|
22
|
+
private readonly crossTurnWindow;
|
|
23
|
+
private readonly crossTurnMinTurns;
|
|
24
|
+
constructor(repeatThreshold?: number, dominationFraction?: number, maxTokenLength?: number, absoluteRunThreshold?: number, rep4Threshold?: number, rep4MinTokens?: number, crossTurnWindow?: number, crossTurnMinTurns?: number);
|
|
25
|
+
maxConsecutiveShortTokenRun: (text: string) => ShortTokenRun;
|
|
26
|
+
isIntraTurnDegeneration: (text: string) => boolean;
|
|
27
|
+
detectCrossTurnDegeneration: (texts: string[]) => CrossTurnDegeneration | null;
|
|
28
|
+
private stripRepetitionExemptSpans;
|
|
29
|
+
private duplicateNgramFraction;
|
|
30
|
+
private trailingSpuriousToken;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=OutputDegenerationDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OutputDegenerationDetector.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/OutputDegenerationDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,eAAO,MAAM,oCAAoC,IAAI,CAAC;AACtD,eAAO,MAAM,uCAAuC,MAAM,CAAC;AAC3D,eAAO,MAAM,oCAAoC,KAAK,CAAC;AACvD,eAAO,MAAM,0CAA0C,KAAK,CAAC;AAC7D,eAAO,MAAM,kCAAkC,MAAM,CAAC;AACtD,eAAO,MAAM,mCAAmC,KAAK,CAAC;AACtD,eAAO,MAAM,qCAAqC,KAAK,CAAC;AACxD,eAAO,MAAM,wCAAwC,IAAI,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAUF,qBAAa,0BAA0B;IAEnC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAPjB,eAAe,GAAE,MAA6C,EAC9D,kBAAkB,GAAE,MAAgD,EACpE,cAAc,GAAE,MAA6C,EAC7D,oBAAoB,GAAE,MAAmD,EACzE,aAAa,GAAE,MAA2C,EAC1D,aAAa,GAAE,MAA4C,EAC3D,eAAe,GAAE,MAA8C,EAC/D,iBAAiB,GAAE,MAAiD;IAGvF,2BAA2B,GAAI,MAAM,MAAM,KAAG,aAAa,CAyBzD;IAEF,uBAAuB,GAAI,MAAM,MAAM,KAAG,OAAO,CAoB/C;IAEF,2BAA2B,GACzB,OAAO,MAAM,EAAE,KACd,qBAAqB,GAAG,IAAI,CAqB7B;IAEF,OAAO,CAAC,0BAA0B,CAqBhC;IAEF,OAAO,CAAC,sBAAsB,CAiB5B;IAEF,OAAO,CAAC,qBAAqB,CAc3B;CACH"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LiveSessionProcessSnapshotProvider } from './adapter-interfaces/LiveSessionProcessSnapshotProvider';
|
|
2
|
+
import { InteractiveLiveSessionTranscriptResolver } from './adapter-interfaces/InteractiveLiveSessionTranscriptResolver';
|
|
3
|
+
import { SessionAssistantTurnsRepository } from './adapter-interfaces/SessionAssistantTurnsRepository';
|
|
4
|
+
import { SessionDegenerationCooldownStateRepository } from './adapter-interfaces/SessionDegenerationCooldownStateRepository';
|
|
5
|
+
import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
|
|
6
|
+
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
7
|
+
import { Sleeper } from './adapter-interfaces/Sleeper';
|
|
8
|
+
import { OutputDegenerationDetector } from './OutputDegenerationDetector';
|
|
9
|
+
export declare const DEFAULT_OUTPUT_DEGENERATION_GRACE_SECONDS = 5;
|
|
10
|
+
export declare const DEFAULT_OUTPUT_DEGENERATION_COOLDOWN_SECONDS = 300;
|
|
11
|
+
export declare const DEFAULT_OUTPUT_DEGENERATION_WARNING_MESSAGE = "OUTPUT DEGENERATION DETECTED: your session is about to be reset. Immediately write a checkpoint to your assigned task issue (single concrete next action to resume, working directory and branch, in-flight sub-agent branch/PR URLs).";
|
|
12
|
+
export declare class SessionOutputDegenerationRecoveryUseCase {
|
|
13
|
+
private readonly liveSessionProcessSnapshotProvider;
|
|
14
|
+
private readonly interactiveLiveSessionTranscriptResolver;
|
|
15
|
+
private readonly assistantTurnsRepository;
|
|
16
|
+
private readonly notificationRepository;
|
|
17
|
+
private readonly tmuxSessionRepository;
|
|
18
|
+
private readonly cooldownStateRepository;
|
|
19
|
+
private readonly sleeper;
|
|
20
|
+
private readonly detector;
|
|
21
|
+
private readonly resolveInteractiveLiveSessions;
|
|
22
|
+
constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, assistantTurnsRepository: SessionAssistantTurnsRepository, notificationRepository: SilentSessionNotificationRepository, tmuxSessionRepository: Pick<TmuxSessionRepository, 'killSession'>, cooldownStateRepository: SessionDegenerationCooldownStateRepository, sleeper: Sleeper, detector?: OutputDegenerationDetector);
|
|
23
|
+
run: (params: {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
warningMessage: string;
|
|
26
|
+
graceSeconds: number;
|
|
27
|
+
cooldownSeconds: number;
|
|
28
|
+
now: Date;
|
|
29
|
+
}) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=SessionOutputDegenerationRecoveryUseCase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionOutputDegenerationRecoveryUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kCAAkC,EAAE,MAAM,yDAAyD,CAAC;AAC7G,OAAO,EAAE,wCAAwC,EAAE,MAAM,+DAA+D,CAAC;AACzH,OAAO,EAAE,+BAA+B,EAAE,MAAM,sDAAsD,CAAC;AACvG,OAAO,EAAE,0CAA0C,EAAE,MAAM,iEAAiE,CAAC;AAC7H,OAAO,EAAE,mCAAmC,EAAE,MAAM,0DAA0D,CAAC;AAC/G,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EACL,0BAA0B,EAE3B,MAAM,8BAA8B,CAAC;AAItC,eAAO,MAAM,yCAAyC,IAAI,CAAC;AAC3D,eAAO,MAAM,4CAA4C,MAAM,CAAC;AAChE,eAAO,MAAM,2CAA2C,2OACkL,CAAC;AAE3O,qBAAa,wCAAwC;IAKjD,OAAO,CAAC,QAAQ,CAAC,kCAAkC;IACnD,OAAO,CAAC,QAAQ,CAAC,wCAAwC;IACzD,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAItC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAd3B,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CACD;gBAG3B,kCAAkC,EAAE,kCAAkC,EACtE,wCAAwC,EAAE,wCAAwC,EAClF,wBAAwB,EAAE,+BAA+B,EACzD,sBAAsB,EAAE,mCAAmC,EAC3D,qBAAqB,EAAE,IAAI,CAC1C,qBAAqB,EACrB,aAAa,CACd,EACgB,uBAAuB,EAAE,0CAA0C,EACnE,OAAO,EAAE,OAAO,EAChB,QAAQ,GAAE,0BAA6D;IAG1F,GAAG,GAAU,QAAQ;QACnB,OAAO,EAAE,OAAO,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CAiGf;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionAssistantTurnsRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,+BAA+B;IAC9C,qCAAqC,EAAE,CACrC,2BAA2B,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChD,kBAAkB,EAAE,MAAM,KACvB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;CACrC"}
|
package/types/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SessionDegenerationCooldownStateRepository {
|
|
2
|
+
loadLastResetEpochSecondsBySessionName: () => Promise<Map<string, number>>;
|
|
3
|
+
recordReset: (params: {
|
|
4
|
+
sessionName: string;
|
|
5
|
+
now: Date;
|
|
6
|
+
}) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=SessionDegenerationCooldownStateRepository.d.ts.map
|
package/types/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionDegenerationCooldownStateRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0CAA0C;IACzD,sCAAsC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3E,WAAW,EAAE,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E"}
|