github-issue-tower-defence-management 1.151.7 → 1.152.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/.github/workflows/create-pr.yml +1 -1
- package/CHANGELOG.md +7 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-CGqYXHZV.css → index-lemrqYKZ.css} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +0 -19
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/inTmuxByHumanDataWriter.js +1 -13
- package/bin/adapter/entry-points/handlers/inTmuxByHumanDataWriter.js.map +1 -1
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js +0 -41
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js.map +1 -1
- package/bin/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.js +2 -21
- package/bin/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/ui/src/index.css +0 -7
- package/src/adapter/entry-points/console/ui-dist/assets/{index-CGqYXHZV.css → index-lemrqYKZ.css} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -33
- package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +3 -104
- package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts +0 -19
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.singleScan.test.ts +10 -12
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +0 -178
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +0 -61
- package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +0 -99
- package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts +1 -47
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/inTmuxByHumanDataWriter.d.ts +0 -2
- package/types/adapter/entry-points/handlers/inTmuxByHumanDataWriter.d.ts.map +1 -1
- package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts +0 -3
- package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts.map +1 -1
- package/types/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.d.ts +0 -19
- package/types/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.d.ts.map +1 -1
- package/src/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.test.ts +0 -111
- package/src/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.ts +0 -33
- package/src/domain/entities/UnansweredOwnerCall.ts +0 -4
- package/src/domain/usecases/intmux/UnansweredOwnerCallSessionKeyContract.test.ts +0 -141
- /package/bin/adapter/entry-points/console/ui-dist/assets/{index-BjFRJoM1.js → index-DR_LIP9n.js} +0 -0
- /package/src/adapter/entry-points/console/ui-dist/assets/{index-BjFRJoM1.js → index-DR_LIP9n.js} +0 -0
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { OwnerCallStatusProvider } from '../../domain/usecases/adapter-interfaces/OwnerCallStatusProvider';
|
|
4
|
-
import { UnansweredOwnerCall } from '../../domain/entities/UnansweredOwnerCall';
|
|
5
4
|
import { SILENT_SESSION_REMINDER_SENTINEL } from '../../domain/usecases/silentSessionReminderSentinel';
|
|
6
5
|
|
|
7
6
|
type TranscriptOwnerCall = {
|
|
8
7
|
epochMs: number;
|
|
9
|
-
body: string;
|
|
10
8
|
candidateOnly: boolean;
|
|
11
9
|
};
|
|
12
10
|
|
|
@@ -196,44 +194,6 @@ export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvide
|
|
|
196
194
|
return unansweredOwnerCallEpochSecondsBySessionName;
|
|
197
195
|
};
|
|
198
196
|
|
|
199
|
-
listUnansweredOwnerCallsBySessionName = async (
|
|
200
|
-
transcriptPathBySessionName: Map<string, string>,
|
|
201
|
-
): Promise<Map<string, UnansweredOwnerCall[]>> => {
|
|
202
|
-
const unansweredOwnerCallsBySessionName = new Map<
|
|
203
|
-
string,
|
|
204
|
-
UnansweredOwnerCall[]
|
|
205
|
-
>();
|
|
206
|
-
if (this.ownerCallMarkerFamily.length === 0) {
|
|
207
|
-
return unansweredOwnerCallsBySessionName;
|
|
208
|
-
}
|
|
209
|
-
for (const [sessionName, transcriptPath] of transcriptPathBySessionName) {
|
|
210
|
-
const transcript = this.scanTranscript(transcriptPath);
|
|
211
|
-
if (transcript === null) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
const answeredBeforeEpochMs = this.resolveReplyEpochMs(
|
|
215
|
-
transcriptPath,
|
|
216
|
-
transcript.lastOwnerReplyEpochMs,
|
|
217
|
-
);
|
|
218
|
-
const unansweredCalls = transcript.ownerCalls
|
|
219
|
-
.filter(
|
|
220
|
-
(ownerCall) =>
|
|
221
|
-
(answeredBeforeEpochMs === null ||
|
|
222
|
-
ownerCall.epochMs > answeredBeforeEpochMs) &&
|
|
223
|
-
this.isCallDeliveredToOwner(transcriptPath, ownerCall),
|
|
224
|
-
)
|
|
225
|
-
.sort((oneCall, otherCall) => oneCall.epochMs - otherCall.epochMs)
|
|
226
|
-
.map((ownerCall) => ({
|
|
227
|
-
calledAt: new Date(ownerCall.epochMs).toISOString(),
|
|
228
|
-
body: ownerCall.body,
|
|
229
|
-
}));
|
|
230
|
-
if (unansweredCalls.length > 0) {
|
|
231
|
-
unansweredOwnerCallsBySessionName.set(sessionName, unansweredCalls);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return unansweredOwnerCallsBySessionName;
|
|
235
|
-
};
|
|
236
|
-
|
|
237
197
|
private scanTranscript = (
|
|
238
198
|
transcriptPath: string,
|
|
239
199
|
): TranscriptOwnerCallScan | null => {
|
|
@@ -287,7 +247,6 @@ export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvide
|
|
|
287
247
|
if (matchedMarkers.length > 0) {
|
|
288
248
|
ownerCalls.push({
|
|
289
249
|
epochMs,
|
|
290
|
-
body: assistantText,
|
|
291
250
|
candidateOnly: matchedMarkers.every(isCandidateOwnerCallMarker),
|
|
292
251
|
});
|
|
293
252
|
}
|
|
@@ -365,26 +324,6 @@ export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvide
|
|
|
365
324
|
): number | null =>
|
|
366
325
|
this.readMarkerEpochMs(transcriptPath, OWNER_REPLY_MARKER_FILE_EXTENSION);
|
|
367
326
|
|
|
368
|
-
// The format gate the owner's sessions run under can hold an owner call instead of delivering
|
|
369
|
-
// it, and it records that decision beside the reply marker: the held call's timestamp in the
|
|
370
|
-
// suppression marker, and the timestamp of any call it later approved in the approval marker. A
|
|
371
|
-
// held call the approval marker does not name never reached the owner, so treating it as a wait
|
|
372
|
-
// on the owner would silence the session's stall reminder for good — the session would keep its
|
|
373
|
-
// task and never be woken again. Such a call is therefore not an outstanding owner call here. A
|
|
374
|
-
// delivered call, and a newer call the suppression marker does not name, are untouched.
|
|
375
|
-
private isCallDeliveredToOwner = (
|
|
376
|
-
transcriptPath: string,
|
|
377
|
-
ownerCall: TranscriptOwnerCall,
|
|
378
|
-
): boolean => {
|
|
379
|
-
if (this.isCallSuppressedUndelivered(transcriptPath, ownerCall.epochMs)) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
return (
|
|
383
|
-
!ownerCall.candidateOnly ||
|
|
384
|
-
this.isCandidateCallDelivered(transcriptPath, ownerCall.epochMs)
|
|
385
|
-
);
|
|
386
|
-
};
|
|
387
|
-
|
|
388
327
|
private isCallSuppressedUndelivered = (
|
|
389
328
|
transcriptPath: string,
|
|
390
329
|
ownerCallEpochMs: number,
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Issue } from '../../entities/Issue';
|
|
2
2
|
import { FieldOption, Project } from '../../entities/Project';
|
|
3
3
|
import { GenerateInTmuxByHumanDataUseCase } from './GenerateInTmuxByHumanDataUseCase';
|
|
4
|
-
import { UnansweredOwnerCall } from '../../entities/UnansweredOwnerCall';
|
|
5
|
-
import { toTmuxSessionName } from './InTmuxByHumanSessionReconcileUseCase';
|
|
6
4
|
|
|
7
5
|
const ASSIGNEE = 'owner-login';
|
|
8
6
|
const CONSOLE_BASE_URL = 'https://console.example.test';
|
|
@@ -100,7 +98,6 @@ describe('GenerateInTmuxByHumanDataUseCase', () => {
|
|
|
100
98
|
consoleBaseUrl?: string | null;
|
|
101
99
|
consoleToken?: string | null;
|
|
102
100
|
newIssueRepo?: string;
|
|
103
|
-
unansweredCallsByTmuxSessionName?: Map<string, UnansweredOwnerCall[]>;
|
|
104
101
|
} = {},
|
|
105
102
|
) =>
|
|
106
103
|
usecase.run({
|
|
@@ -119,9 +116,6 @@ describe('GenerateInTmuxByHumanDataUseCase', () => {
|
|
|
119
116
|
overrides.consoleToken === undefined
|
|
120
117
|
? CONSOLE_TOKEN
|
|
121
118
|
: overrides.consoleToken,
|
|
122
|
-
unansweredCallsByTmuxSessionName:
|
|
123
|
-
overrides.unansweredCallsByTmuxSessionName ??
|
|
124
|
-
new Map<string, UnansweredOwnerCall[]>(),
|
|
125
119
|
now: NOW,
|
|
126
120
|
});
|
|
127
121
|
|
|
@@ -339,97 +333,4 @@ describe('GenerateInTmuxByHumanDataUseCase', () => {
|
|
|
339
333
|
expect(result.v3).not.toBeNull();
|
|
340
334
|
});
|
|
341
335
|
});
|
|
342
|
-
|
|
343
|
-
describe('v5 document', () => {
|
|
344
|
-
const callsFor = (
|
|
345
|
-
issueUrl: string,
|
|
346
|
-
calls: UnansweredOwnerCall[],
|
|
347
|
-
): Map<string, UnansweredOwnerCall[]> =>
|
|
348
|
-
new Map([[toTmuxSessionName(issueUrl), calls]]);
|
|
349
|
-
|
|
350
|
-
it('builds version 5 with key order version, overviewUrl, tdpmConsoleUrl, newIssueUrl, groups', () => {
|
|
351
|
-
const result = run([makeIssue({ story: 'Story Alpha' })]);
|
|
352
|
-
expect(result.v5).not.toBeNull();
|
|
353
|
-
expect(Object.keys(result.v5 ?? {})).toEqual([
|
|
354
|
-
'version',
|
|
355
|
-
'overviewUrl',
|
|
356
|
-
'tdpmConsoleUrl',
|
|
357
|
-
'newIssueUrl',
|
|
358
|
-
'groups',
|
|
359
|
-
]);
|
|
360
|
-
expect(result.v5?.version).toBe(5);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('gives a session with no unanswered call an empty unansweredCalls array', () => {
|
|
364
|
-
const result = run([makeIssue({ story: 'Story Alpha' })]);
|
|
365
|
-
expect(result.v5?.groups).toEqual([
|
|
366
|
-
{
|
|
367
|
-
story: 'Story Alpha',
|
|
368
|
-
sessions: [
|
|
369
|
-
{
|
|
370
|
-
name: 'https://github.com/demo/repo/issues/1',
|
|
371
|
-
description: 'Issue 1',
|
|
372
|
-
unansweredCalls: [],
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
},
|
|
376
|
-
]);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('carries the unanswered calls of a session looked up by the tmux session name the reconciler derives from the issue url', () => {
|
|
380
|
-
const issue = makeIssue({ story: 'Story Alpha' });
|
|
381
|
-
const call: UnansweredOwnerCall = {
|
|
382
|
-
calledAt: '2026-08-13T10:14:00.000Z',
|
|
383
|
-
body: 'Please decide whether to merge the release branch',
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
const result = run([issue], {
|
|
387
|
-
unansweredCallsByTmuxSessionName: callsFor(issue.url, [call]),
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
expect(result.v5?.groups[0].sessions[0].unansweredCalls).toEqual([call]);
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it('carries every unanswered call of one session in call order with its own time and body', () => {
|
|
394
|
-
const issue = makeIssue({ story: 'Story Alpha' });
|
|
395
|
-
const calls: UnansweredOwnerCall[] = [
|
|
396
|
-
{ calledAt: '2026-08-13T10:14:00.000Z', body: 'first call body' },
|
|
397
|
-
{ calledAt: '2026-08-13T10:41:30.000Z', body: 'second call body' },
|
|
398
|
-
];
|
|
399
|
-
|
|
400
|
-
const result = run([issue], {
|
|
401
|
-
unansweredCallsByTmuxSessionName: callsFor(issue.url, calls),
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
expect(result.v5?.groups[0].sessions[0].unansweredCalls).toEqual(calls);
|
|
405
|
-
expect(
|
|
406
|
-
result.v5?.groups[0].sessions[0].unansweredCalls.map(
|
|
407
|
-
(unansweredCall) => unansweredCall.calledAt,
|
|
408
|
-
),
|
|
409
|
-
).toEqual(['2026-08-13T10:14:00.000Z', '2026-08-13T10:41:30.000Z']);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
it('leaves the version 4 document free of unanswered call data', () => {
|
|
413
|
-
const issue = makeIssue({ story: 'Story Alpha' });
|
|
414
|
-
|
|
415
|
-
const result = run([issue], {
|
|
416
|
-
unansweredCallsByTmuxSessionName: callsFor(issue.url, [
|
|
417
|
-
{ calledAt: '2026-08-13T10:14:00.000Z', body: 'first call body' },
|
|
418
|
-
]),
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
expect(result.v4?.groups[0].sessions[0]).toEqual({
|
|
422
|
-
name: 'https://github.com/demo/repo/issues/1',
|
|
423
|
-
description: 'Issue 1',
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
it('is null when the console token is unset while v3 is still produced', () => {
|
|
428
|
-
const result = run([makeIssue({ story: 'Story Alpha' })], {
|
|
429
|
-
consoleToken: null,
|
|
430
|
-
});
|
|
431
|
-
expect(result.v5).toBeNull();
|
|
432
|
-
expect(result.v3).not.toBeNull();
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
336
|
});
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Issue } from '../../entities/Issue';
|
|
2
2
|
import { FieldOption, Project } from '../../entities/Project';
|
|
3
|
-
import { UnansweredOwnerCall } from '../../entities/UnansweredOwnerCall';
|
|
4
|
-
import { toTmuxSessionName } from './InTmuxByHumanSessionReconcileUseCase';
|
|
5
3
|
|
|
6
4
|
export type InTmuxByHumanUrlEntry = {
|
|
7
5
|
url: string;
|
|
@@ -28,17 +26,6 @@ export type InTmuxByHumanGroupV4 = {
|
|
|
28
26
|
sessions: InTmuxByHumanSession[];
|
|
29
27
|
};
|
|
30
28
|
|
|
31
|
-
export type InTmuxByHumanSessionV5 = {
|
|
32
|
-
name: string;
|
|
33
|
-
description: string;
|
|
34
|
-
unansweredCalls: UnansweredOwnerCall[];
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type InTmuxByHumanGroupV5 = {
|
|
38
|
-
story: string;
|
|
39
|
-
sessions: InTmuxByHumanSessionV5[];
|
|
40
|
-
};
|
|
41
|
-
|
|
42
29
|
export type InTmuxByHumanV3 = {
|
|
43
30
|
version: 3;
|
|
44
31
|
overviewUrl: string;
|
|
@@ -54,20 +41,11 @@ export type InTmuxByHumanV4 = {
|
|
|
54
41
|
groups: InTmuxByHumanGroupV4[];
|
|
55
42
|
};
|
|
56
43
|
|
|
57
|
-
export type InTmuxByHumanV5 = {
|
|
58
|
-
version: 5;
|
|
59
|
-
overviewUrl: string;
|
|
60
|
-
tdpmConsoleUrl: string;
|
|
61
|
-
newIssueUrl: string;
|
|
62
|
-
groups: InTmuxByHumanGroupV5[];
|
|
63
|
-
};
|
|
64
|
-
|
|
65
44
|
export type InTmuxByHumanData = {
|
|
66
45
|
v1: InTmuxByHumanGroupV1[];
|
|
67
46
|
v2: InTmuxByHumanGroupV2[];
|
|
68
47
|
v3: InTmuxByHumanV3 | null;
|
|
69
48
|
v4: InTmuxByHumanV4 | null;
|
|
70
|
-
v5: InTmuxByHumanV5 | null;
|
|
71
49
|
};
|
|
72
50
|
|
|
73
51
|
export type GenerateInTmuxByHumanDataInput = {
|
|
@@ -80,7 +58,6 @@ export type GenerateInTmuxByHumanDataInput = {
|
|
|
80
58
|
newIssueRepo?: string;
|
|
81
59
|
consoleBaseUrl: string | null;
|
|
82
60
|
consoleToken: string | null;
|
|
83
|
-
unansweredCallsByTmuxSessionName: Map<string, UnansweredOwnerCall[]>;
|
|
84
61
|
now: Date;
|
|
85
62
|
};
|
|
86
63
|
|
|
@@ -104,7 +81,6 @@ export class GenerateInTmuxByHumanDataUseCase {
|
|
|
104
81
|
newIssueRepo,
|
|
105
82
|
consoleBaseUrl,
|
|
106
83
|
consoleToken,
|
|
107
|
-
unansweredCallsByTmuxSessionName,
|
|
108
84
|
} = input;
|
|
109
85
|
|
|
110
86
|
const storyOrder = project.story
|
|
@@ -138,17 +114,6 @@ export class GenerateInTmuxByHumanDataUseCase {
|
|
|
138
114
|
})),
|
|
139
115
|
}));
|
|
140
116
|
|
|
141
|
-
const v5Groups: InTmuxByHumanGroupV5[] = groups.map((group) => ({
|
|
142
|
-
story: group.story,
|
|
143
|
-
sessions: group.issues.map((issue) => ({
|
|
144
|
-
name: issue.url,
|
|
145
|
-
description: issue.title,
|
|
146
|
-
unansweredCalls:
|
|
147
|
-
unansweredCallsByTmuxSessionName.get(toTmuxSessionName(issue.url)) ??
|
|
148
|
-
[],
|
|
149
|
-
})),
|
|
150
|
-
}));
|
|
151
|
-
|
|
152
117
|
const overviewUrl = project.url;
|
|
153
118
|
const tdpmConsoleUrl = consoleBaseUrl
|
|
154
119
|
? `${consoleBaseUrl}/projects/${pjcode}`
|
|
@@ -174,18 +139,7 @@ export class GenerateInTmuxByHumanDataUseCase {
|
|
|
174
139
|
}
|
|
175
140
|
: null;
|
|
176
141
|
|
|
177
|
-
|
|
178
|
-
tdpmConsoleUrl && consoleToken
|
|
179
|
-
? {
|
|
180
|
-
version: 5,
|
|
181
|
-
overviewUrl,
|
|
182
|
-
tdpmConsoleUrl: `${tdpmConsoleUrl}?k=${consoleToken}`,
|
|
183
|
-
newIssueUrl: `https://github.com/${org}/${newIssueRepo ?? repo}/issues/new?assignees=${assigneeLogin}`,
|
|
184
|
-
groups: v5Groups,
|
|
185
|
-
}
|
|
186
|
-
: null;
|
|
187
|
-
|
|
188
|
-
return { v1, v2, v3, v4, v5 };
|
|
142
|
+
return { v1, v2, v3, v4 };
|
|
189
143
|
};
|
|
190
144
|
|
|
191
145
|
private isInTmuxByHuman = (issue: Issue, assigneeLogin: string): boolean =>
|
|
@@ -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":"AA+CA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAqD3D,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,CA6wBP;CACH"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Issue } from '../../../domain/entities/Issue';
|
|
2
2
|
import type { Project } from '../../../domain/entities/Project';
|
|
3
|
-
import type { UnansweredOwnerCall } from '../../../domain/entities/UnansweredOwnerCall';
|
|
4
3
|
export type InTmuxByHumanDataWriterParams = {
|
|
5
4
|
inTmuxDataOutputDir: string | null | undefined;
|
|
6
5
|
inTmuxConsoleBaseUrl: string | null | undefined;
|
|
@@ -13,7 +12,6 @@ export type InTmuxByHumanDataWriterParams = {
|
|
|
13
12
|
newIssueRepo?: string | null | undefined;
|
|
14
13
|
project: Project;
|
|
15
14
|
issues: Issue[];
|
|
16
|
-
unansweredCallsByTmuxSessionName?: Map<string, UnansweredOwnerCall[]>;
|
|
17
15
|
now: Date;
|
|
18
16
|
};
|
|
19
17
|
export declare const writeInTmuxByHumanData: (params: InTmuxByHumanDataWriterParams) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inTmuxByHumanDataWriter.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"inTmuxByHumanDataWriter.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAMhE,MAAM,MAAM,6BAA6B,GAAG;IAC1C,mBAAmB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChD,kBAAkB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,kBAAkB,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAUF,eAAO,MAAM,sBAAsB,GACjC,QAAQ,6BAA6B,KACpC,IA0EF,CAAC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { OwnerCallStatusProvider } from '../../domain/usecases/adapter-interfaces/OwnerCallStatusProvider';
|
|
2
|
-
import { UnansweredOwnerCall } from '../../domain/entities/UnansweredOwnerCall';
|
|
3
2
|
export declare const ownerCallMarkerFamilyResolve: (marker: string) => string[];
|
|
4
3
|
export declare class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvider {
|
|
5
4
|
private readonly ownerReplyMarkerDirectory;
|
|
@@ -7,13 +6,11 @@ export declare class TranscriptOwnerCallStatusProvider implements OwnerCallStatu
|
|
|
7
6
|
private readonly transcriptScanByPath;
|
|
8
7
|
constructor(ownerCallMarker: string | null, ownerReplyMarkerDirectory?: string | null);
|
|
9
8
|
listUnansweredOwnerCallEpochSecondsBySessionName: (transcriptPathBySessionName: Map<string, string>) => Promise<Map<string, number>>;
|
|
10
|
-
listUnansweredOwnerCallsBySessionName: (transcriptPathBySessionName: Map<string, string>) => Promise<Map<string, UnansweredOwnerCall[]>>;
|
|
11
9
|
private scanTranscript;
|
|
12
10
|
private readTranscriptScan;
|
|
13
11
|
private resolveReplyEpochMs;
|
|
14
12
|
private findUnansweredOwnerCallEpochMs;
|
|
15
13
|
private readOwnerReplyMarkerEpochMs;
|
|
16
|
-
private isCallDeliveredToOwner;
|
|
17
14
|
private isCallSuppressedUndelivered;
|
|
18
15
|
private isCandidateCallDelivered;
|
|
19
16
|
private readMarkerEpochMs;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranscriptOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;
|
|
1
|
+
{"version":3,"file":"TranscriptOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAuG3G,eAAO,MAAM,4BAA4B,GAAI,QAAQ,MAAM,KAAG,MAAM,EAMtD,CAAC;AA4Cf,qBAAa,iCAAkC,YAAW,uBAAuB;IAU7E,OAAO,CAAC,QAAQ,CAAC,yBAAyB;IAT5C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAW;IAEjD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAGjC;gBAGF,eAAe,EAAE,MAAM,GAAG,IAAI,EACb,yBAAyB,GAAE,MAAM,GAAG,IAAW;IAQlE,gDAAgD,GAC9C,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAmB7B;IAEF,OAAO,CAAC,cAAc,CAUpB;IAEF,OAAO,CAAC,kBAAkB,CA4DxB;IAEF,OAAO,CAAC,mBAAmB,CAUzB;IAEF,OAAO,CAAC,8BAA8B,CAgCpC;IAOF,OAAO,CAAC,2BAA2B,CAGyC;IAE5E,OAAO,CAAC,2BAA2B,CAgBjC;IAEF,OAAO,CAAC,wBAAwB,CAoB9B;IAEF,OAAO,CAAC,iBAAiB,CA4BvB;CACH"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Issue } from '../../entities/Issue';
|
|
2
2
|
import { Project } from '../../entities/Project';
|
|
3
|
-
import { UnansweredOwnerCall } from '../../entities/UnansweredOwnerCall';
|
|
4
3
|
export type InTmuxByHumanUrlEntry = {
|
|
5
4
|
url: string;
|
|
6
5
|
title: string;
|
|
@@ -21,15 +20,6 @@ export type InTmuxByHumanGroupV4 = {
|
|
|
21
20
|
story: string;
|
|
22
21
|
sessions: InTmuxByHumanSession[];
|
|
23
22
|
};
|
|
24
|
-
export type InTmuxByHumanSessionV5 = {
|
|
25
|
-
name: string;
|
|
26
|
-
description: string;
|
|
27
|
-
unansweredCalls: UnansweredOwnerCall[];
|
|
28
|
-
};
|
|
29
|
-
export type InTmuxByHumanGroupV5 = {
|
|
30
|
-
story: string;
|
|
31
|
-
sessions: InTmuxByHumanSessionV5[];
|
|
32
|
-
};
|
|
33
23
|
export type InTmuxByHumanV3 = {
|
|
34
24
|
version: 3;
|
|
35
25
|
overviewUrl: string;
|
|
@@ -43,19 +33,11 @@ export type InTmuxByHumanV4 = {
|
|
|
43
33
|
newIssueUrl: string;
|
|
44
34
|
groups: InTmuxByHumanGroupV4[];
|
|
45
35
|
};
|
|
46
|
-
export type InTmuxByHumanV5 = {
|
|
47
|
-
version: 5;
|
|
48
|
-
overviewUrl: string;
|
|
49
|
-
tdpmConsoleUrl: string;
|
|
50
|
-
newIssueUrl: string;
|
|
51
|
-
groups: InTmuxByHumanGroupV5[];
|
|
52
|
-
};
|
|
53
36
|
export type InTmuxByHumanData = {
|
|
54
37
|
v1: InTmuxByHumanGroupV1[];
|
|
55
38
|
v2: InTmuxByHumanGroupV2[];
|
|
56
39
|
v3: InTmuxByHumanV3 | null;
|
|
57
40
|
v4: InTmuxByHumanV4 | null;
|
|
58
|
-
v5: InTmuxByHumanV5 | null;
|
|
59
41
|
};
|
|
60
42
|
export type GenerateInTmuxByHumanDataInput = {
|
|
61
43
|
project: Project;
|
|
@@ -67,7 +49,6 @@ export type GenerateInTmuxByHumanDataInput = {
|
|
|
67
49
|
newIssueRepo?: string;
|
|
68
50
|
consoleBaseUrl: string | null;
|
|
69
51
|
consoleToken: string | null;
|
|
70
|
-
unansweredCallsByTmuxSessionName: Map<string, UnansweredOwnerCall[]>;
|
|
71
52
|
now: Date;
|
|
72
53
|
};
|
|
73
54
|
export declare class GenerateInTmuxByHumanDataUseCase {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GenerateInTmuxByHumanDataUseCase.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAe,OAAO,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"GenerateInTmuxByHumanDataUseCase.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAe,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,qBAAqB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,oBAAoB,EAAE,CAAC;IAC3B,EAAE,EAAE,oBAAoB,EAAE,CAAC;IAC3B,EAAE,EAAE,eAAe,GAAG,IAAI,CAAC;IAC3B,EAAE,EAAE,eAAe,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAUF,qBAAa,gCAAgC;IAC3C,GAAG,GAAI,OAAO,8BAA8B,KAAG,iBAAiB,CAsE9D;IAEF,OAAO,CAAC,eAAe,CAMS;IAEhC,OAAO,CAAC,iBAAiB,CAkCvB;CACH"}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { resolveUnansweredOwnerCallsByTmuxSessionName } from './resolveUnansweredOwnerCalls';
|
|
2
|
-
import { UnansweredOwnerCall } from '../../../domain/entities/UnansweredOwnerCall';
|
|
3
|
-
import { InteractiveLiveSession } from '../../../domain/entities/InteractiveLiveSession';
|
|
4
|
-
import { LiveSessionProcessSnapshot } from '../../../domain/entities/LiveSessionProcessSnapshot';
|
|
5
|
-
import { toTmuxSessionName } from '../../../domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase';
|
|
6
|
-
|
|
7
|
-
const ISSUE_URL = 'https://github.com/demo/repo/issues/1';
|
|
8
|
-
const TMUX_SESSION_NAME = toTmuxSessionName(ISSUE_URL);
|
|
9
|
-
const CONFIG_DIR = '/home/agent/.claude';
|
|
10
|
-
const SESSION_ID = 'session-id-1';
|
|
11
|
-
|
|
12
|
-
const snapshotWithSession = (): LiveSessionProcessSnapshot => ({
|
|
13
|
-
sessions: [{ sessionName: TMUX_SESSION_NAME, panePids: [100] }],
|
|
14
|
-
processes: [
|
|
15
|
-
{
|
|
16
|
-
pid: 100,
|
|
17
|
-
ppid: 1,
|
|
18
|
-
commandLine: `claude ${ISSUE_URL}`,
|
|
19
|
-
sessionId: SESSION_ID,
|
|
20
|
-
currentSessionId: SESSION_ID,
|
|
21
|
-
configDir: CONFIG_DIR,
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('resolveUnansweredOwnerCallsByTmuxSessionName', () => {
|
|
27
|
-
it('asks the call provider for the transcripts of the live interactive sessions and returns what it reports', async () => {
|
|
28
|
-
const call: UnansweredOwnerCall = {
|
|
29
|
-
calledAt: '2026-08-13T10:14:00.000Z',
|
|
30
|
-
body: 'Please decide whether to merge the release branch',
|
|
31
|
-
};
|
|
32
|
-
const requestedTranscriptPaths: Map<string, string>[] = [];
|
|
33
|
-
const resolvedSessions: InteractiveLiveSession[][] = [];
|
|
34
|
-
|
|
35
|
-
const result = await resolveUnansweredOwnerCallsByTmuxSessionName({
|
|
36
|
-
liveSessionProcessSnapshotProvider: {
|
|
37
|
-
getSnapshot: async () => snapshotWithSession(),
|
|
38
|
-
},
|
|
39
|
-
interactiveLiveSessionTranscriptResolver: {
|
|
40
|
-
resolveTranscriptPaths: (sessions) => {
|
|
41
|
-
resolvedSessions.push(sessions);
|
|
42
|
-
return new Map(
|
|
43
|
-
sessions.map((session) => [
|
|
44
|
-
session.sessionName,
|
|
45
|
-
`/transcripts/${session.sessionId}.jsonl`,
|
|
46
|
-
]),
|
|
47
|
-
);
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
unansweredOwnerCallListProvider: {
|
|
51
|
-
listUnansweredOwnerCallsBySessionName: async (
|
|
52
|
-
transcriptPathBySessionName,
|
|
53
|
-
) => {
|
|
54
|
-
requestedTranscriptPaths.push(transcriptPathBySessionName);
|
|
55
|
-
return new Map([[TMUX_SESSION_NAME, [call]]]);
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
expect(resolvedSessions[0]?.map((session) => session.sessionName)).toEqual([
|
|
61
|
-
TMUX_SESSION_NAME,
|
|
62
|
-
]);
|
|
63
|
-
expect(requestedTranscriptPaths[0]?.get(TMUX_SESSION_NAME)).toBe(
|
|
64
|
-
`/transcripts/${SESSION_ID}.jsonl`,
|
|
65
|
-
);
|
|
66
|
-
expect(result.get(TMUX_SESSION_NAME)).toEqual([call]);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('keys the returned calls by the tmux session name the reconciler derives from the issue url', async () => {
|
|
70
|
-
const result = await resolveUnansweredOwnerCallsByTmuxSessionName({
|
|
71
|
-
liveSessionProcessSnapshotProvider: {
|
|
72
|
-
getSnapshot: async () => snapshotWithSession(),
|
|
73
|
-
},
|
|
74
|
-
interactiveLiveSessionTranscriptResolver: {
|
|
75
|
-
resolveTranscriptPaths: (sessions) =>
|
|
76
|
-
new Map(
|
|
77
|
-
sessions.map((session) => [session.sessionName, '/transcript']),
|
|
78
|
-
),
|
|
79
|
-
},
|
|
80
|
-
unansweredOwnerCallListProvider: {
|
|
81
|
-
listUnansweredOwnerCallsBySessionName: async (
|
|
82
|
-
transcriptPathBySessionName,
|
|
83
|
-
) =>
|
|
84
|
-
new Map(
|
|
85
|
-
[...transcriptPathBySessionName.keys()].map((sessionName) => [
|
|
86
|
-
sessionName,
|
|
87
|
-
[{ calledAt: '2026-08-13T10:14:00.000Z', body: 'body' }],
|
|
88
|
-
]),
|
|
89
|
-
),
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
expect([...result.keys()]).toEqual([toTmuxSessionName(ISSUE_URL)]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('returns nothing when no live session exposes a transcript', async () => {
|
|
97
|
-
const result = await resolveUnansweredOwnerCallsByTmuxSessionName({
|
|
98
|
-
liveSessionProcessSnapshotProvider: {
|
|
99
|
-
getSnapshot: async () => ({ sessions: [], processes: [] }),
|
|
100
|
-
},
|
|
101
|
-
interactiveLiveSessionTranscriptResolver: {
|
|
102
|
-
resolveTranscriptPaths: () => new Map(),
|
|
103
|
-
},
|
|
104
|
-
unansweredOwnerCallListProvider: {
|
|
105
|
-
listUnansweredOwnerCallsBySessionName: async () => new Map(),
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
expect(result.size).toBe(0);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { UnansweredOwnerCall } from '../../../domain/entities/UnansweredOwnerCall';
|
|
2
|
-
import { InteractiveLiveSession } from '../../../domain/entities/InteractiveLiveSession';
|
|
3
|
-
import { ResolveInteractiveLiveSessionsUseCase } from '../../../domain/usecases/ResolveInteractiveLiveSessionsUseCase';
|
|
4
|
-
import { LiveSessionProcessSnapshotProvider } from '../../../domain/usecases/adapter-interfaces/LiveSessionProcessSnapshotProvider';
|
|
5
|
-
import { InteractiveLiveSessionTranscriptResolver } from '../../../domain/usecases/adapter-interfaces/InteractiveLiveSessionTranscriptResolver';
|
|
6
|
-
|
|
7
|
-
export type UnansweredOwnerCallListProvider = {
|
|
8
|
-
listUnansweredOwnerCallsBySessionName: (
|
|
9
|
-
transcriptPathBySessionName: Map<string, string>,
|
|
10
|
-
) => Promise<Map<string, UnansweredOwnerCall[]>>;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export type ResolveUnansweredOwnerCallsParams = {
|
|
14
|
-
liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider;
|
|
15
|
-
interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver;
|
|
16
|
-
unansweredOwnerCallListProvider: UnansweredOwnerCallListProvider;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const resolveUnansweredOwnerCallsByTmuxSessionName = async (
|
|
20
|
-
params: ResolveUnansweredOwnerCallsParams,
|
|
21
|
-
): Promise<Map<string, UnansweredOwnerCall[]>> => {
|
|
22
|
-
const snapshot =
|
|
23
|
-
await params.liveSessionProcessSnapshotProvider.getSnapshot();
|
|
24
|
-
const interactiveSessions: InteractiveLiveSession[] =
|
|
25
|
-
new ResolveInteractiveLiveSessionsUseCase().resolve(snapshot);
|
|
26
|
-
const transcriptPathBySessionName =
|
|
27
|
-
params.interactiveLiveSessionTranscriptResolver.resolveTranscriptPaths(
|
|
28
|
-
interactiveSessions,
|
|
29
|
-
);
|
|
30
|
-
return params.unansweredOwnerCallListProvider.listUnansweredOwnerCallsBySessionName(
|
|
31
|
-
transcriptPathBySessionName,
|
|
32
|
-
);
|
|
33
|
-
};
|