github-issue-tower-defence-management 1.114.0 → 1.114.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +1 -1
  3. package/bin/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.js +36 -27
  4. package/bin/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.js.map +1 -1
  5. package/bin/adapter/repositories/FileSystemSessionRecordReader.js +69 -0
  6. package/bin/adapter/repositories/FileSystemSessionRecordReader.js.map +1 -0
  7. package/bin/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.js +9 -2
  8. package/bin/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.js.map +1 -1
  9. package/bin/domain/usecases/ResolveInteractiveLiveSessionsUseCase.js +15 -0
  10. package/bin/domain/usecases/ResolveInteractiveLiveSessionsUseCase.js.map +1 -1
  11. package/bin/domain/usecases/adapter-interfaces/SessionRecordReader.js +3 -0
  12. package/bin/domain/usecases/adapter-interfaces/SessionRecordReader.js.map +1 -0
  13. package/package.json +1 -1
  14. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +107 -26
  15. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts +49 -29
  16. package/src/adapter/repositories/FileSystemSessionRecordReader.test.ts +59 -0
  17. package/src/adapter/repositories/FileSystemSessionRecordReader.ts +31 -0
  18. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.test.ts +51 -0
  19. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts +10 -1
  20. package/src/domain/entities/InteractiveLiveSession.ts +1 -0
  21. package/src/domain/entities/LiveSessionProcessSnapshot.ts +1 -0
  22. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +4 -0
  23. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.test.ts +66 -59
  24. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts +19 -0
  25. package/src/domain/usecases/adapter-interfaces/SessionRecordReader.ts +3 -0
  26. package/types/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.d.ts +4 -11
  27. package/types/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.d.ts.map +1 -1
  28. package/types/adapter/repositories/FileSystemSessionRecordReader.d.ts +5 -0
  29. package/types/adapter/repositories/FileSystemSessionRecordReader.d.ts.map +1 -0
  30. package/types/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.d.ts +3 -1
  31. package/types/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.d.ts.map +1 -1
  32. package/types/domain/entities/InteractiveLiveSession.d.ts +1 -0
  33. package/types/domain/entities/InteractiveLiveSession.d.ts.map +1 -1
  34. package/types/domain/entities/LiveSessionProcessSnapshot.d.ts +1 -0
  35. package/types/domain/entities/LiveSessionProcessSnapshot.d.ts.map +1 -1
  36. package/types/domain/usecases/ResolveInteractiveLiveSessionsUseCase.d.ts +1 -0
  37. package/types/domain/usecases/ResolveInteractiveLiveSessionsUseCase.d.ts.map +1 -1
  38. package/types/domain/usecases/adapter-interfaces/SessionRecordReader.d.ts +4 -0
  39. package/types/domain/usecases/adapter-interfaces/SessionRecordReader.d.ts.map +1 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [1.114.1](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.114.0...v1.114.1) (2026-06-28)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **silent-notification:** resolve live transcripts via rotated id and shared projects dir ([#1065](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1065)) ([95e491a](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/95e491ab813e577c795c5a11d59fb1fab1b035e4)), closes [#1064](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1064)
7
+
1
8
  # [1.114.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.113.0...v1.114.0) (2026-06-28)
2
9
 
3
10
 
package/README.md CHANGED
@@ -556,7 +556,7 @@ For each `In Tmux by human` issue with no live session, the reconciler launches
556
556
 
557
557
  Each schedule cycle, when `silentNotificationEnabled` is true, also inspects every live interactive Claude Code session and sends a short self-check reminder into a session via `tmux send-keys` when either of two independent conditions holds. The reminder is composed of only the sections whose condition is met, so a session receives one combined message containing one or both sections. When `silentNotificationEnabled` is not set, the entire step is a no-op even if the daemon picks up its configuration automatically.
558
558
 
559
- Sessions are selected purely from the live process tree, independently of session naming or any GitHub issue linkage: every live tmux session is taken, the descendants of each session's pane processes are walked, and the first descendant that is an interactive Claude Code process (its command line contains `claude` and does not contain the owner-handover marker `Take ownership of`, and its environment exposes both `CLAUDE_CODE_SESSION_ID` and `CLAUDE_CONFIG_DIR`) is taken as that session's process. Both issue-url-named sessions and plain-named sessions (for example `workbench`) are selected equally. Because the walk is anchored on tmux panes, background owner-handover spawns, the preparation daemon, and monitor processes — none of which run inside an interactive tmux pane — are excluded; the owner-handover marker check is an additional guard. Each selected session's transcript is then resolved from its process environment as `<CLAUDE_CONFIG_DIR>/projects/<cwd-slug>/<CLAUDE_CODE_SESSION_ID>.jsonl` (the most recently modified match is used when several exist), so a session that has no issue URL resolves just as reliably as one that does.
559
+ Sessions are selected purely from the live process tree, independently of session naming or any GitHub issue linkage: every live tmux session is taken, the descendants of each session's pane processes are walked, and the first descendant that is an interactive Claude Code process (its command line contains `claude` and does not contain the owner-handover marker `Take ownership of`, and its environment exposes both `CLAUDE_CODE_SESSION_ID` and `CLAUDE_CONFIG_DIR`) is taken as that session's process. Both issue-url-named sessions and plain-named sessions (for example `workbench`) are selected equally. Because the walk is anchored on tmux panes, background owner-handover spawns, the preparation daemon, and monitor processes — none of which run inside an interactive tmux pane — are excluded; the owner-handover marker check is an additional guard. Each selected session's transcript is then resolved by scanning the `projects/<cwd-slug>/` subdirectories of two roots — the per-session `<CLAUDE_CONFIG_DIR>/projects/` and the shared `~/.claude/projects/` — for a `<sessionId>.jsonl` file named by any of the session's candidate session ids. The candidate ids are, in priority order, the current session id recorded in `<CLAUDE_CONFIG_DIR>/sessions/<pid>.json` (the session id rotates on resume or compaction, and this record holds the current value) followed by the launch id from the `CLAUDE_CODE_SESSION_ID` environment variable. Searching the shared root and the rotated current id is required because, for a resumed or compacted session, the actively-written transcript is named by the current id and lives under the shared root rather than under the per-session config directory; the most recently modified match is used when several exist. A session that has no issue URL resolves just as reliably as one that does.
560
560
 
561
561
  The main-session stalled section is sent when the session's main output has been idle for at least `mainSilentThresholdSeconds`, unless the session is currently waiting on the owner (in that case the silence is expected and the section is suppressed). Idle time is computed from the timestamp of the latest `assistant` entry in the session's resolved transcript rather than from the transcript file modification time, so a transcript that is only updated by tool results or owner replies still counts as silent. Whether a session is waiting on the owner is determined through an injectable port; the built-in default reports no session as waiting, so the suppression is only applied when a host provides an implementation. When `ownerCallMarker` is configured, a transcript-based implementation is used: it reads each session's resolved transcript and treats the session as waiting on the owner when the latest entry containing `ownerCallMarker` is newer (by full timestamp) than the latest genuine owner reply. A self-check reminder that the monitor injects into a session via `tmux send-keys` itself lands in that session's transcript as a `user` text entry; every injected reminder carries a fixed sentinel tag (`[TDPM_SILENT_SESSION_SELF_CHECK_REMINDER]`), and a `user` entry whose text contains that sentinel is not counted as a genuine owner reply. This prevents the monitor's own reminder from being mistaken for the owner answering, which would otherwise make a genuinely waiting session stop being suppressed.
562
562
 
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FileSystemInteractiveLiveSessionTranscriptResolver = void 0;
37
37
  const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
38
39
  const path = __importStar(require("path"));
39
40
  const modifiedEpochMs = (filePath) => {
40
41
  try {
@@ -45,19 +46,10 @@ const modifiedEpochMs = (filePath) => {
45
46
  return null;
46
47
  }
47
48
  };
48
- /**
49
- * Resolves the real transcript path of an interactive Claude Code session from
50
- * the session id and config directory taken from the session's process
51
- * environment. The transcript lives at
52
- * `<configDir>/projects/<cwd-slug>/<sessionId>.jsonl`; this resolver scans the
53
- * `projects` subdirectories for a file named `<sessionId>.jsonl` and returns the
54
- * most recently modified match. Because resolution is keyed on the process
55
- * session id rather than on a session name or issue URL, a plain-named session
56
- * (for example one named `workbench`) resolves just as well as an issue-url
57
- * named one.
58
- */
49
+ const defaultSharedProjectsDirectory = () => path.join(os.homedir(), '.claude', 'projects');
59
50
  class FileSystemInteractiveLiveSessionTranscriptResolver {
60
- constructor() {
51
+ constructor(sharedProjectsDirectory = defaultSharedProjectsDirectory()) {
52
+ this.sharedProjectsDirectory = sharedProjectsDirectory;
61
53
  this.resolveTranscriptPaths = (sessions) => {
62
54
  const resolved = new Map();
63
55
  for (const session of sessions) {
@@ -69,7 +61,34 @@ class FileSystemInteractiveLiveSessionTranscriptResolver {
69
61
  return resolved;
70
62
  };
71
63
  this.resolveTranscriptPath = (session) => {
72
- const projectsDirectory = path.join(session.configDir, 'projects');
64
+ const projectsDirectories = this.listProjectsDirectories(session.configDir);
65
+ let latestPath = null;
66
+ let latestEpochMs = -Infinity;
67
+ for (const candidateSessionId of session.candidateSessionIds) {
68
+ const fileName = `${candidateSessionId}.jsonl`;
69
+ for (const projectsDirectory of projectsDirectories) {
70
+ for (const candidate of this.listCandidatePaths(projectsDirectory, fileName)) {
71
+ const epochMs = modifiedEpochMs(candidate);
72
+ if (epochMs === null) {
73
+ continue;
74
+ }
75
+ if (epochMs > latestEpochMs) {
76
+ latestEpochMs = epochMs;
77
+ latestPath = candidate;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return latestPath;
83
+ };
84
+ this.listProjectsDirectories = (configDir) => {
85
+ const perSessionProjectsDirectory = path.join(configDir, 'projects');
86
+ if (perSessionProjectsDirectory === this.sharedProjectsDirectory) {
87
+ return [perSessionProjectsDirectory];
88
+ }
89
+ return [perSessionProjectsDirectory, this.sharedProjectsDirectory];
90
+ };
91
+ this.listCandidatePaths = (projectsDirectory, fileName) => {
73
92
  let projectEntries;
74
93
  try {
75
94
  projectEntries = fs.readdirSync(projectsDirectory, {
@@ -77,26 +96,16 @@ class FileSystemInteractiveLiveSessionTranscriptResolver {
77
96
  });
78
97
  }
79
98
  catch {
80
- return null;
99
+ return [];
81
100
  }
82
- const fileName = `${session.sessionId}.jsonl`;
83
- let latestPath = null;
84
- let latestEpochMs = -Infinity;
101
+ const candidatePaths = [];
85
102
  for (const projectEntry of projectEntries) {
86
103
  if (!projectEntry.isDirectory()) {
87
104
  continue;
88
105
  }
89
- const candidate = path.join(projectsDirectory, projectEntry.name, fileName);
90
- const epochMs = modifiedEpochMs(candidate);
91
- if (epochMs === null) {
92
- continue;
93
- }
94
- if (epochMs > latestEpochMs) {
95
- latestEpochMs = epochMs;
96
- latestPath = candidate;
97
- }
106
+ candidatePaths.push(path.join(projectsDirectory, projectEntry.name, fileName));
98
107
  }
99
- return latestPath;
108
+ return candidatePaths;
100
109
  };
101
110
  }
102
111
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FileSystemInteractiveLiveSessionTranscriptResolver.js","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAI7B,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAiB,EAAE;IAC1D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAa,kDAAkD;IAA/D;QACE,2BAAsB,GAAG,CACvB,QAAkC,EACb,EAAE;YACvB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;YAC3C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,MAAM,cAAc,GAAG,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;gBAC3D,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;oBAC5B,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC,CAAC;QAEM,0BAAqB,GAAG,CAC9B,OAA+B,EAChB,EAAE;YACjB,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YACnE,IAAI,cAA2B,CAAC;YAChC,IAAI,CAAC;gBACH,cAAc,GAAG,EAAE,CAAC,WAAW,CAAC,iBAAiB,EAAE;oBACjD,aAAa,EAAE,IAAI;iBACpB,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,SAAS,QAAQ,CAAC;YAC9C,IAAI,UAAU,GAAkB,IAAI,CAAC;YACrC,IAAI,aAAa,GAAG,CAAC,QAAQ,CAAC;YAC9B,KAAK,MAAM,YAAY,IAAI,cAAc,EAAE,CAAC;gBAC1C,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,CAAC;oBAChC,SAAS;gBACX,CAAC;gBACD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CACzB,iBAAiB,EACjB,YAAY,CAAC,IAAI,EACjB,QAAQ,CACT,CAAC;gBACF,MAAM,OAAO,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;gBAC3C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,SAAS;gBACX,CAAC;gBACD,IAAI,OAAO,GAAG,aAAa,EAAE,CAAC;oBAC5B,aAAa,GAAG,OAAO,CAAC;oBACxB,UAAU,GAAG,SAAS,CAAC;gBACzB,CAAC;YACH,CAAC;YACD,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC;IACJ,CAAC;CAAA;AAjDD,gHAiDC"}
1
+ {"version":3,"file":"FileSystemInteractiveLiveSessionTranscriptResolver.js","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileSystemSessionRecordReader = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const isRecord = (value) => typeof value === 'object' && value !== null;
40
+ class FileSystemSessionRecordReader {
41
+ constructor() {
42
+ this.readCurrentSessionId = (configDir, pid) => {
43
+ const recordPath = path.join(configDir, 'sessions', `${pid}.json`);
44
+ let content;
45
+ try {
46
+ content = fs.readFileSync(recordPath, 'utf8');
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(content);
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ if (!isRecord(parsed)) {
59
+ return null;
60
+ }
61
+ const sessionId = parsed.sessionId;
62
+ return typeof sessionId === 'string' && sessionId.length > 0
63
+ ? sessionId
64
+ : null;
65
+ };
66
+ }
67
+ }
68
+ exports.FileSystemSessionRecordReader = FileSystemSessionRecordReader;
69
+ //# sourceMappingURL=FileSystemSessionRecordReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileSystemSessionRecordReader.js","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSessionRecordReader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAG7B,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AAE9C,MAAa,6BAA6B;IAA1C;QACE,yBAAoB,GAAG,CAAC,SAAiB,EAAE,GAAW,EAAiB,EAAE;YACvE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;YACnE,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,MAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;YACnC,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;gBAC1D,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI,CAAC;QACX,CAAC,CAAC;IACJ,CAAC;CAAA;AAvBD,sEAuBC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalProcessLiveSessionProcessSnapshotProvider = void 0;
4
+ const FileSystemSessionRecordReader_1 = require("./FileSystemSessionRecordReader");
4
5
  const SESSION_ID_ENV_KEY = 'CLAUDE_CODE_SESSION_ID';
5
6
  const CONFIG_DIR_ENV_KEY = 'CLAUDE_CONFIG_DIR';
6
7
  /**
@@ -11,9 +12,10 @@ const CONFIG_DIR_ENV_KEY = 'CLAUDE_CONFIG_DIR';
11
12
  * pure resolver, so all host access is contained in this adapter.
12
13
  */
13
14
  class LocalProcessLiveSessionProcessSnapshotProvider {
14
- constructor(localCommandRunner, environReader) {
15
+ constructor(localCommandRunner, environReader, sessionRecordReader = new FileSystemSessionRecordReader_1.FileSystemSessionRecordReader()) {
15
16
  this.localCommandRunner = localCommandRunner;
16
17
  this.environReader = environReader;
18
+ this.sessionRecordReader = sessionRecordReader;
17
19
  this.getSnapshot = async () => {
18
20
  const sessionNames = await this.listSessionNames();
19
21
  const sessions = [];
@@ -59,12 +61,17 @@ class LocalProcessLiveSessionProcessSnapshotProvider {
59
61
  const ppid = Number(match[2]);
60
62
  const commandLine = match[3].trim();
61
63
  const environ = this.environReader.readEnviron(pid);
64
+ const configDir = environ?.[CONFIG_DIR_ENV_KEY] ?? null;
65
+ const currentSessionId = configDir === null
66
+ ? null
67
+ : this.sessionRecordReader.readCurrentSessionId(configDir, pid);
62
68
  processes.push({
63
69
  pid,
64
70
  ppid,
65
71
  commandLine,
66
72
  sessionId: environ?.[SESSION_ID_ENV_KEY] ?? null,
67
- configDir: environ?.[CONFIG_DIR_ENV_KEY] ?? null,
73
+ currentSessionId,
74
+ configDir,
68
75
  });
69
76
  }
70
77
  return processes;
@@ -1 +1 @@
1
- {"version":3,"file":"LocalProcessLiveSessionProcessSnapshotProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LocalProcessLiveSessionProcessSnapshotProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts"],"names":[],"mappings":"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"}
@@ -55,11 +55,26 @@ class ResolveInteractiveLiveSessionsUseCase {
55
55
  sessions.push({
56
56
  sessionName: session.sessionName,
57
57
  sessionId: interactiveProcess.sessionId,
58
+ candidateSessionIds: this.collectCandidateSessionIds(interactiveProcess),
58
59
  configDir: interactiveProcess.configDir,
59
60
  });
60
61
  }
61
62
  return sessions;
62
63
  };
64
+ this.collectCandidateSessionIds = (interactiveProcess) => {
65
+ const candidateSessionIds = [];
66
+ const seenSessionIds = new Set();
67
+ for (const sessionId of [
68
+ interactiveProcess.currentSessionId,
69
+ interactiveProcess.sessionId,
70
+ ]) {
71
+ if (sessionId !== null && !seenSessionIds.has(sessionId)) {
72
+ seenSessionIds.add(sessionId);
73
+ candidateSessionIds.push(sessionId);
74
+ }
75
+ }
76
+ return candidateSessionIds;
77
+ };
63
78
  this.findInteractiveProcess = (panePids, processByPid, childrenByPpid) => {
64
79
  const visited = new Set();
65
80
  const queue = [...panePids];
@@ -1 +1 @@
1
- {"version":3,"file":"ResolveInteractiveLiveSessionsUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ResolveInteractiveLiveSessionsUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=SessionRecordReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionRecordReader.js","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SessionRecordReader.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.114.0",
3
+ "version": "1.114.1",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -5,11 +5,13 @@ import { FileSystemInteractiveLiveSessionTranscriptResolver } from './FileSystem
5
5
 
6
6
  describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
7
7
  let configRoot: string;
8
+ let sharedProjectsDirectory: string;
8
9
 
9
10
  beforeEach(() => {
10
11
  configRoot = fs.mkdtempSync(
11
12
  path.join(os.tmpdir(), 'interactive-transcript-'),
12
13
  );
14
+ sharedProjectsDirectory = path.join(configRoot, 'shared', 'projects');
13
15
  });
14
16
 
15
17
  afterEach(() => {
@@ -17,14 +19,13 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
17
19
  });
18
20
 
19
21
  const writeTranscript = (params: {
20
- configDir: string;
22
+ projectsDirectory: string;
21
23
  cwdSlug: string;
22
24
  sessionId: string;
23
25
  mtimeEpochSeconds?: number;
24
26
  }): string => {
25
27
  const projectDirectory = path.join(
26
- params.configDir,
27
- 'projects',
28
+ params.projectsDirectory,
28
29
  params.cwdSlug,
29
30
  );
30
31
  fs.mkdirSync(projectDirectory, { recursive: true });
@@ -40,40 +41,100 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
40
41
  return filePath;
41
42
  };
42
43
 
43
- it('resolves a transcript by config dir and session id for a plain-named session', () => {
44
+ it('resolves a resume-case transcript by config dir and session id', () => {
44
45
  const configDir = path.join(configRoot, 'workbench');
45
46
  const filePath = writeTranscript({
46
- configDir,
47
+ projectsDirectory: path.join(configDir, 'projects'),
47
48
  cwdSlug: '-home-user',
48
49
  sessionId: 'wb-uuid',
49
50
  });
50
- const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver();
51
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
52
+ sharedProjectsDirectory,
53
+ );
51
54
 
52
55
  const result = resolver.resolveTranscriptPaths([
53
- { sessionName: 'workbench', sessionId: 'wb-uuid', configDir },
56
+ {
57
+ sessionName: 'workbench',
58
+ sessionId: 'wb-uuid',
59
+ candidateSessionIds: ['wb-uuid'],
60
+ configDir,
61
+ },
54
62
  ]);
55
63
 
56
64
  expect(result.get('workbench')).toBe(filePath);
57
65
  });
58
66
 
59
- it('chooses the most recently modified match across project directories', () => {
67
+ it('resolves a rotated-id transcript via a later candidate session id', () => {
68
+ const configDir = path.join(configRoot, 'workbench');
69
+ const rotatedPath = writeTranscript({
70
+ projectsDirectory: path.join(configDir, 'projects'),
71
+ cwdSlug: '-home-user',
72
+ sessionId: 'rotated-uuid',
73
+ });
74
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
75
+ sharedProjectsDirectory,
76
+ );
77
+
78
+ const result = resolver.resolveTranscriptPaths([
79
+ {
80
+ sessionName: 'workbench',
81
+ sessionId: 'launch-uuid',
82
+ candidateSessionIds: ['rotated-uuid', 'launch-uuid'],
83
+ configDir,
84
+ },
85
+ ]);
86
+
87
+ expect(result.get('workbench')).toBe(rotatedPath);
88
+ });
89
+
90
+ it('resolves a transcript that lives under the shared projects directory', () => {
91
+ const configDir = path.join(configRoot, 'workbench');
92
+ const sharedPath = writeTranscript({
93
+ projectsDirectory: sharedProjectsDirectory,
94
+ cwdSlug: '-home-user',
95
+ sessionId: 'rotated-uuid',
96
+ });
97
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
98
+ sharedProjectsDirectory,
99
+ );
100
+
101
+ const result = resolver.resolveTranscriptPaths([
102
+ {
103
+ sessionName: 'workbench',
104
+ sessionId: 'launch-uuid',
105
+ candidateSessionIds: ['rotated-uuid', 'launch-uuid'],
106
+ configDir,
107
+ },
108
+ ]);
109
+
110
+ expect(result.get('workbench')).toBe(sharedPath);
111
+ });
112
+
113
+ it('prefers the most recently modified match across both projects roots', () => {
60
114
  const configDir = path.join(configRoot, 'workbench');
61
115
  writeTranscript({
62
- configDir,
63
- cwdSlug: '-home-user-old',
64
- sessionId: 'wb-uuid',
116
+ projectsDirectory: path.join(configDir, 'projects'),
117
+ cwdSlug: '-home-user',
118
+ sessionId: 'rotated-uuid',
65
119
  mtimeEpochSeconds: 1700000000,
66
120
  });
67
121
  const newerPath = writeTranscript({
68
- configDir,
69
- cwdSlug: '-home-user-new',
70
- sessionId: 'wb-uuid',
122
+ projectsDirectory: sharedProjectsDirectory,
123
+ cwdSlug: '-home-user',
124
+ sessionId: 'rotated-uuid',
71
125
  mtimeEpochSeconds: 1700000500,
72
126
  });
73
- const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver();
127
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
128
+ sharedProjectsDirectory,
129
+ );
74
130
 
75
131
  const result = resolver.resolveTranscriptPaths([
76
- { sessionName: 'workbench', sessionId: 'wb-uuid', configDir },
132
+ {
133
+ sessionName: 'workbench',
134
+ sessionId: 'launch-uuid',
135
+ candidateSessionIds: ['rotated-uuid', 'launch-uuid'],
136
+ configDir,
137
+ },
77
138
  ]);
78
139
 
79
140
  expect(result.get('workbench')).toBe(newerPath);
@@ -83,26 +144,30 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
83
144
  const workbenchConfig = path.join(configRoot, 'workbench');
84
145
  const controlRoomConfig = path.join(configRoot, 'control-room');
85
146
  const workbenchPath = writeTranscript({
86
- configDir: workbenchConfig,
147
+ projectsDirectory: path.join(workbenchConfig, 'projects'),
87
148
  cwdSlug: '-home-user',
88
149
  sessionId: 'wb-uuid',
89
150
  });
90
151
  const controlRoomPath = writeTranscript({
91
- configDir: controlRoomConfig,
152
+ projectsDirectory: path.join(controlRoomConfig, 'projects'),
92
153
  cwdSlug: '-home-user',
93
154
  sessionId: 'cr-uuid',
94
155
  });
95
- const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver();
156
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
157
+ sharedProjectsDirectory,
158
+ );
96
159
 
97
160
  const result = resolver.resolveTranscriptPaths([
98
161
  {
99
162
  sessionName: 'workbench',
100
163
  sessionId: 'wb-uuid',
164
+ candidateSessionIds: ['wb-uuid'],
101
165
  configDir: workbenchConfig,
102
166
  },
103
167
  {
104
168
  sessionName: 'control-room',
105
169
  sessionId: 'cr-uuid',
170
+ candidateSessionIds: ['cr-uuid'],
106
171
  configDir: controlRoomConfig,
107
172
  },
108
173
  ]);
@@ -111,34 +176,50 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
111
176
  expect(result.get('control-room')).toBe(controlRoomPath);
112
177
  });
113
178
 
114
- it('omits a session whose transcript file is absent', () => {
179
+ it('omits a session whose transcript file is absent in both roots', () => {
115
180
  const configDir = path.join(configRoot, 'workbench');
116
181
  fs.mkdirSync(path.join(configDir, 'projects', '-home-user'), {
117
182
  recursive: true,
118
183
  });
119
- const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver();
184
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
185
+ sharedProjectsDirectory,
186
+ );
120
187
 
121
188
  const result = resolver.resolveTranscriptPaths([
122
- { sessionName: 'workbench', sessionId: 'missing-uuid', configDir },
189
+ {
190
+ sessionName: 'workbench',
191
+ sessionId: 'missing-uuid',
192
+ candidateSessionIds: ['missing-uuid'],
193
+ configDir,
194
+ },
123
195
  ]);
124
196
 
125
197
  expect(result.has('workbench')).toBe(false);
126
198
  });
127
199
 
128
- it('omits a session whose config dir has no projects directory', () => {
200
+ it('omits a session when neither projects directory exists', () => {
129
201
  const configDir = path.join(configRoot, 'workbench');
130
202
  fs.mkdirSync(configDir, { recursive: true });
131
- const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver();
203
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
204
+ sharedProjectsDirectory,
205
+ );
132
206
 
133
207
  const result = resolver.resolveTranscriptPaths([
134
- { sessionName: 'workbench', sessionId: 'wb-uuid', configDir },
208
+ {
209
+ sessionName: 'workbench',
210
+ sessionId: 'wb-uuid',
211
+ candidateSessionIds: ['wb-uuid'],
212
+ configDir,
213
+ },
135
214
  ]);
136
215
 
137
216
  expect(result.has('workbench')).toBe(false);
138
217
  });
139
218
 
140
219
  it('returns an empty map for an empty session list', () => {
141
- const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver();
220
+ const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
221
+ sharedProjectsDirectory,
222
+ );
142
223
 
143
224
  const result = resolver.resolveTranscriptPaths([]);
144
225