github-issue-tower-defence-management 1.111.0 → 1.111.2

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 (29) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +3 -3
  3. package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js +6 -2
  4. package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js.map +1 -1
  5. package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js +15 -2
  6. package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js.map +1 -1
  7. package/bin/adapter/repositories/TranscriptSessionSubAgentActivityRepository.js +2 -1
  8. package/bin/adapter/repositories/TranscriptSessionSubAgentActivityRepository.js.map +1 -1
  9. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js +3 -2
  10. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js.map +1 -1
  11. package/bin/domain/usecases/silentSessionReminderSentinel.js +12 -0
  12. package/bin/domain/usecases/silentSessionReminderSentinel.js.map +1 -0
  13. package/package.json +1 -1
  14. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +4 -1
  15. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +19 -1
  16. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +8 -2
  17. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +67 -0
  18. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +17 -2
  19. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.test.ts +29 -0
  20. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts +4 -1
  21. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +13 -0
  22. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +3 -2
  23. package/src/domain/usecases/silentSessionReminderSentinel.ts +9 -0
  24. package/types/adapter/repositories/ConfigurableSilentSessionMessageComposer.d.ts.map +1 -1
  25. package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts.map +1 -1
  26. package/types/adapter/repositories/TranscriptSessionSubAgentActivityRepository.d.ts.map +1 -1
  27. package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts.map +1 -1
  28. package/types/domain/usecases/silentSessionReminderSentinel.d.ts +2 -0
  29. package/types/domain/usecases/silentSessionReminderSentinel.d.ts.map +1 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.111.2](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.111.1...v1.111.2) (2026-06-28)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **schedule:** do not count monitor-injected reminders as owner replies ([#1036](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1036)) ([b1e667c](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/b1e667c4e8c6a798f86bff3765dfba03246bce1c)), closes [#1035](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1035)
7
+
8
+ ## [1.111.1](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.111.0...v1.111.1) (2026-06-28)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **schedule:** treat stop_sequence as a completed sub-agent in stall detection ([#1034](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1034)) ([0318011](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/0318011691871b1d8141eed735de911a15866e92)), closes [#1033](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1033)
14
+
1
15
  # [1.111.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.110.1...v1.111.0) (2026-06-27)
2
16
 
3
17
 
package/README.md CHANGED
@@ -288,7 +288,7 @@ silentNotificationEnabled?: boolean # Optional: Master switch for the silent liv
288
288
  subAgentOutputRootDirectory?: string # Optional: Root directory holding one output file per sub-process (file name derived from the sub-process label). The modification time of each file is read to compute how long the sub-process output has been idle. When unset, sub-process idle time is reported as 0 and only the running-time threshold can trigger a sub-process notification
289
289
  subAgentProcessMatchPattern?: string # Optional: Regular expression matched against each process command line to discover the sub-processes that belong to a monitored session. The expression must define a named capture group `session` whose value equals the monitored session name, and may define a named capture group `label` used as the display name. When unset, the sub-process check is skipped
290
290
  ownerCallMarker?: string # Optional: Marker substring that identifies an assistant message asking the owner for a decision or confirmation. When set, the main-session stalled section is suppressed while a session's latest marker-bearing assistant message is newer (by full timestamp) than its latest genuine owner reply. When unset, no session is treated as waiting on the owner
291
- subAgentTranscriptRootDirectory?: string # Optional: Root directory under which each session has a `<sessionName>/subagents/` directory holding one `agent-<id>.jsonl` transcript per sub-agent. A sub-agent whose latest transcript entry reports a `stop_reason` of `end_turn` is treated as finished and skipped; for any other sub-agent the idle time is computed from the transcript file modification time (following symlinks) and the running time from its first entry timestamp. When set, this transcript-based discovery is used instead of `subAgentProcessMatchPattern`. When unset, the process-match discovery is used
291
+ subAgentTranscriptRootDirectory?: string # Optional: Root directory under which each session has a `<sessionName>/subagents/` directory holding one `agent-<id>.jsonl` transcript per sub-agent. A sub-agent whose latest transcript entry reports a `stop_reason` of `end_turn` or `stop_sequence` is treated as finished and skipped; for any other sub-agent the idle time is computed from the transcript file modification time (following symlinks) and the running time from its first entry timestamp. When set, this transcript-based discovery is used instead of `subAgentProcessMatchPattern`. When unset, the process-match discovery is used
292
292
  mainSilentThresholdSeconds?: number # Optional: Seconds of main-session output silence after which the main-session self-check section is sent. Default 600
293
293
  subAgentSilentThresholdSeconds?: number # Optional: Seconds of sub-process output silence after which the sub-process section is sent. Default 300
294
294
  subAgentRunningThresholdSeconds?: number # Optional: Seconds a sub-process may run before the sub-process section is sent regardless of its output silence. Default 900
@@ -558,9 +558,9 @@ Each schedule cycle, when `silentNotificationEnabled` is true, also inspects eve
558
558
 
559
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.
560
560
 
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.
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 and get re-notified.
562
562
 
563
- The sub-process section is sent, regardless of main output, when at least one sub-process of the session has been output-idle for `subAgentSilentThresholdSeconds` or has been running for `subAgentRunningThresholdSeconds`; the message lists each matched sub-process with its idle and running minutes. Sub-processes are discovered in one of two ways: when `subAgentTranscriptRootDirectory` is configured, each session's `subagents/agent-<id>.jsonl` transcripts are scanned, finished sub-agents (latest transcript entry `stop_reason` equal to `end_turn`) are skipped, and idle time is taken from the transcript file modification time (following symlinks) while running time is taken from its first entry timestamp; otherwise sub-processes are discovered by matching `subAgentProcessMatchPattern` against process command lines.
563
+ The sub-process section is sent, regardless of main output, when at least one sub-process of the session has been output-idle for `subAgentSilentThresholdSeconds` or has been running for `subAgentRunningThresholdSeconds`; the message lists each matched sub-process with its idle and running minutes. Sub-processes are discovered in one of two ways: when `subAgentTranscriptRootDirectory` is configured, each session's `subagents/agent-<id>.jsonl` transcripts are scanned, finished sub-agents (latest transcript entry `stop_reason` equal to `end_turn` or `stop_sequence`) are skipped, and idle time is taken from the transcript file modification time (following symlinks) while running time is taken from its first entry timestamp; otherwise sub-processes are discovered by matching `subAgentProcessMatchPattern` against process command lines.
564
564
 
565
565
  A per-session cooldown (`silentNotificationCooldownSeconds`) prevents repeated notifications, persisted via the existing cache. When more than one session is notified in the same cycle, the sends are spaced out sequentially with `silentNotificationStaggerSeconds` between consecutive sends (no wait before the first or after the last) so the targets are not all triggered at once. Process-snapshot collection, transcript resolution, owner-call detection, the wait between sends, and the message wording are all performed through injectable ports, so the logic is unit-testable without touching the host, and the host-specific process and environment access, sub-process pattern, and message wording are supplied through adapters or configuration. When `silentNotificationEnabled` is not true the step is a no-op, and any error during the step is logged and swallowed so the schedule cycle is never affected.
566
566
 
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConfigurableSilentSessionMessageComposer = void 0;
4
+ const silentSessionReminderSentinel_1 = require("../../domain/usecases/silentSessionReminderSentinel");
5
+ const withReminderSentinel = (message) => message.includes(silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL)
6
+ ? message
7
+ : `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} ${message}`;
4
8
  const formatMinutes = (seconds) => {
5
9
  const minutes = Math.floor(seconds / 60);
6
10
  return `${minutes}m`;
@@ -13,7 +17,7 @@ class ConfigurableSilentSessionMessageComposer {
13
17
  if (this.templates.mainStalledMessage === null) {
14
18
  return this.fallback.composeMainStalledSection(mainSilentSeconds);
15
19
  }
16
- return this.templates.mainStalledMessage;
20
+ return withReminderSentinel(this.templates.mainStalledMessage);
17
21
  };
18
22
  this.composeSubAgentSection = (subAgents) => {
19
23
  if (this.templates.subAgentMessageHeader === null &&
@@ -29,7 +33,7 @@ class ConfigurableSilentSessionMessageComposer {
29
33
  if (this.templates.subAgentMessageFooter !== null) {
30
34
  sections.push(this.templates.subAgentMessageFooter);
31
35
  }
32
- return sections.join('\n');
36
+ return withReminderSentinel(sections.join('\n'));
33
37
  };
34
38
  }
35
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigurableSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AASA,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAa,wCAAwC;IACnD,YACmB,SAAwC,EACxC,QAAsC;QADtC,cAAS,GAAT,SAAS,CAA+B;QACxC,aAAQ,GAAR,QAAQ,CAA8B;QAGzD,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;gBAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;YACpE,CAAC;YACD,OAAO,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;QAC3C,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,IACE,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI;gBAC7C,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAC7C,CAAC;gBACD,OAAO,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;YACxB,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC;IA/BC,CAAC;CAgCL;AApCD,4FAoCC"}
1
+ {"version":3,"file":"ConfigurableSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AAEA,uGAAuG;AAEvG,MAAM,oBAAoB,GAAG,CAAC,OAAe,EAAU,EAAE,CACvD,OAAO,CAAC,QAAQ,CAAC,gEAAgC,CAAC;IAChD,CAAC,CAAC,OAAO;IACT,CAAC,CAAC,GAAG,gEAAgC,IAAI,OAAO,EAAE,CAAC;AAQvD,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAa,wCAAwC;IACnD,YACmB,SAAwC,EACxC,QAAsC;QADtC,cAAS,GAAT,SAAS,CAA+B;QACxC,aAAQ,GAAR,QAAQ,CAA8B;QAGzD,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;gBAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;YACpE,CAAC;YACD,OAAO,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACjE,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,IACE,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI;gBAC7C,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAC7C,CAAC;gBACD,OAAO,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;YACxB,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,OAAO,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC;IA/BC,CAAC;CAgCL;AApCD,4FAoCC"}
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.TranscriptOwnerCallStatusProvider = void 0;
37
37
  const fs = __importStar(require("fs"));
38
+ const silentSessionReminderSentinel_1 = require("../../domain/usecases/silentSessionReminderSentinel");
38
39
  const isRecord = (value) => typeof value === 'object' && value !== null;
39
40
  const readString = (value, key) => {
40
41
  const candidate = value[key];
@@ -67,12 +68,24 @@ const extractText = (content) => {
67
68
  };
68
69
  const hasOwnerTextReply = (content) => {
69
70
  if (typeof content === 'string') {
70
- return content.length > 0;
71
+ if (content.length === 0) {
72
+ return false;
73
+ }
74
+ // A monitor-injected self-check reminder lands in the target session's
75
+ // transcript as a user text entry. It carries the reminder sentinel, so it
76
+ // is the monitor talking to the session, not the owner replying. It MUST NOT
77
+ // advance the last-owner-reply time, otherwise an outstanding call-to-user
78
+ // is wrongly treated as answered and the session stops being suppressed.
79
+ return !content.includes(silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL);
71
80
  }
72
81
  if (!Array.isArray(content)) {
73
82
  return false;
74
83
  }
75
- return content.some((block) => isRecord(block) && block.type === 'text');
84
+ const hasTextBlock = content.some((block) => isRecord(block) && block.type === 'text');
85
+ if (!hasTextBlock) {
86
+ return false;
87
+ }
88
+ return !extractText(content).includes(silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL);
76
89
  };
77
90
  class TranscriptOwnerCallStatusProvider {
78
91
  constructor(ownerCallMarker) {
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptOwnerCallStatusProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TranscriptOwnerCallStatusProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"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"}
@@ -133,7 +133,8 @@ class TranscriptSessionSubAgentActivityRepository {
133
133
  return null;
134
134
  }
135
135
  const transcript = parseTranscript(content);
136
- if (transcript.lastStopReason === 'end_turn') {
136
+ if (transcript.lastStopReason === 'end_turn' ||
137
+ transcript.lastStopReason === 'stop_sequence') {
137
138
  return null;
138
139
  }
139
140
  const silentSeconds = clampToZero(nowEpochSeconds - Math.floor(stats.mtimeMs / 1000));
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptSessionSubAgentActivityRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TranscriptSessionSubAgentActivityRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DefaultSilentSessionMessageComposer = void 0;
4
+ const silentSessionReminderSentinel_1 = require("./silentSessionReminderSentinel");
4
5
  const formatMinutes = (seconds) => {
5
6
  const minutes = Math.floor(seconds / 60);
6
7
  return `${minutes}m`;
@@ -8,7 +9,7 @@ const formatMinutes = (seconds) => {
8
9
  const composeMainStalledMessage = (mainSilentSeconds) => {
9
10
  const minutes = Math.floor(mainSilentSeconds / 60);
10
11
  return [
11
- `You have produced no output for ${minutes} minutes. Idle waiting wastes this live session and is unacceptable. Always work proactively and stay ahead of the work: anticipate the next steps and, at every point, choose the fastest path so the whole task finishes as early as possible; never wait passively. Finish every task in the shortest possible time — but "fastest" means correct and incident-free, not merely quick: a fast but wrong result is worthless and causes incidents, so be fast without breaking things. Use parallel execution and your whole team of sub-agents to minimize total wall-clock time. Your goal is to drive every task to completion and have the owner confirm that all tasks are done. Do NOT close this session on your own — it is closed only after the owner has verified completion and tells you to close it. Self-check now:`,
12
+ `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} You have produced no output for ${minutes} minutes. Idle waiting wastes this live session and is unacceptable. Always work proactively and stay ahead of the work: anticipate the next steps and, at every point, choose the fastest path so the whole task finishes as early as possible; never wait passively. Finish every task in the shortest possible time — but "fastest" means correct and incident-free, not merely quick: a fast but wrong result is worthless and causes incidents, so be fast without breaking things. Use parallel execution and your whole team of sub-agents to minimize total wall-clock time. Your goal is to drive every task to completion and have the owner confirm that all tasks are done. Do NOT close this session on your own — it is closed only after the owner has verified completion and tells you to close it. Self-check now:`,
12
13
  `1. Every request from the owner is registered as a session task and your task list is kept current (mark tasks completed when done); verify nothing is missing or stale.`,
13
14
  `2. Your plan is the fastest correct path: parallelize independent work across sub-agents, delegate, and remove needless serialization. Choose the fastest safe method, not the easiest.`,
14
15
  `3. A monitor is in place that detects when any sub-agent produces no output for 5 minutes.`,
@@ -24,7 +25,7 @@ class DefaultSilentSessionMessageComposer {
24
25
  this.composeSubAgentSection = (subAgents) => {
25
26
  const lines = subAgents.map((subAgent) => `- ${subAgent.label}: silent for ${formatMinutes(subAgent.silentSeconds)}, running for ${formatMinutes(subAgent.runningSeconds)}`);
26
27
  return [
27
- 'The following sub-processes have been silent or running for a long time:',
28
+ `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} The following sub-processes have been silent or running for a long time:`,
28
29
  ...lines,
29
30
  'If a sub-process is stalled, take action (restart, hand off, or replace it). If it is legitimately waiting on an external dependency (continuous integration, an external API, or another process), let it continue.',
30
31
  ].join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AAGA,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;IACnD,OAAO;QACL,mCAAmC,OAAO,syBAAsyB;QACh1B,0KAA0K;QAC1K,yLAAyL;QACzL,4FAA4F;QAC5F,0YAA0Y;QAC1Y,qHAAqH;KACtH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC,CAAC;AAEF,MAAa,mCAAmC;IAAhD;QACE,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,OAAO,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;QACtD,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,OAAO;gBACL,0EAA0E;gBAC1E,GAAG,KAAK;gBACR,sNAAsN;aACvN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC;IACJ,CAAC;CAAA;AAlBD,kFAkBC"}
1
+ {"version":3,"file":"DefaultSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AAEA,mFAAmF;AAEnF,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;IACnD,OAAO;QACL,GAAG,gEAAgC,oCAAoC,OAAO,syBAAsyB;QACp3B,0KAA0K;QAC1K,yLAAyL;QACzL,4FAA4F;QAC5F,0YAA0Y;QAC1Y,qHAAqH;KACtH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC,CAAC;AAEF,MAAa,mCAAmC;IAAhD;QACE,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,OAAO,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;QACtD,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,OAAO;gBACL,GAAG,gEAAgC,2EAA2E;gBAC9G,GAAG,KAAK;gBACR,sNAAsN;aACvN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC;IACJ,CAAC;CAAA;AAlBD,kFAkBC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SILENT_SESSION_REMINDER_SENTINEL = void 0;
4
+ // Stable, recognizable marker embedded in every monitor-injected silent-session
5
+ // self-check reminder. It lets owner-reply detection distinguish a reminder that
6
+ // the monitor injected into a session's transcript (which arrives as a user text
7
+ // entry via `tmux send-keys`) from a genuine owner reply, so an injected reminder
8
+ // is never miscounted as the owner answering an outstanding call-to-user. The tag
9
+ // is bracketed and namespaced to make an accidental collision with genuine human
10
+ // owner text effectively impossible.
11
+ exports.SILENT_SESSION_REMINDER_SENTINEL = '[TDPM_SILENT_SESSION_SELF_CHECK_REMINDER]';
12
+ //# sourceMappingURL=silentSessionReminderSentinel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silentSessionReminderSentinel.js","sourceRoot":"","sources":["../../../src/domain/usecases/silentSessionReminderSentinel.ts"],"names":[],"mappings":";;;AAAA,gFAAgF;AAChF,iFAAiF;AACjF,iFAAiF;AACjF,kFAAkF;AAClF,kFAAkF;AAClF,iFAAiF;AACjF,qCAAqC;AACxB,QAAA,gCAAgC,GAC3C,2CAA2C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.111.0",
3
+ "version": "1.111.2",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -6,6 +6,7 @@ import { ProcessEnvironReader } from '../../../domain/usecases/adapter-interface
6
6
  import { LocalStorageCacheRepository } from '../../repositories/LocalStorageCacheRepository';
7
7
  import { LocalStorageRepository } from '../../repositories/LocalStorageRepository';
8
8
  import { SilentSessionMessageTemplates } from '../../repositories/ConfigurableSilentSessionMessageComposer';
9
+ import { SILENT_SESSION_REMINDER_SENTINEL } from '../../../domain/usecases/silentSessionReminderSentinel';
9
10
  import {
10
11
  notifySilentTmuxSessions,
11
12
  DEFAULT_NOTIFY_SILENT_TMUX_SESSIONS_PARAMS,
@@ -170,7 +171,9 @@ describe('notifySilentTmuxSessions', () => {
170
171
  const sendCall = runner.runCommand.mock.calls.find(
171
172
  (call) => call[0] === 'tmux' && call[1][0] === 'send-keys',
172
173
  );
173
- expect(sendCall?.[1][4]).toBe('CUSTOM_MAIN_TEMPLATE');
174
+ expect(sendCall?.[1][4]).toBe(
175
+ `${SILENT_SESSION_REMINDER_SENTINEL} CUSTOM_MAIN_TEMPLATE`,
176
+ );
174
177
  });
175
178
 
176
179
  it('suppresses the main stalled notification when the transcript shows an unanswered owner call', async () => {
@@ -1,5 +1,6 @@
1
1
  import { ConfigurableSilentSessionMessageComposer } from './ConfigurableSilentSessionMessageComposer';
2
2
  import { SilentSessionMessageComposer } from '../../domain/usecases/adapter-interfaces/SilentSessionMessageComposer';
3
+ import { SILENT_SESSION_REMINDER_SENTINEL } from '../../domain/usecases/silentSessionReminderSentinel';
3
4
 
4
5
  type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
5
6
 
@@ -33,7 +34,9 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
33
34
  },
34
35
  fallback,
35
36
  );
36
- expect(composer.composeMainStalledSection(600)).toBe('CUSTOM_MAIN');
37
+ const section = composer.composeMainStalledSection(600);
38
+ expect(section).toContain('CUSTOM_MAIN');
39
+ expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
37
40
  expect(fallback.composeMainStalledSection).not.toHaveBeenCalled();
38
41
  });
39
42
 
@@ -72,6 +75,21 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
72
75
  expect(section).toContain('silent for 6m');
73
76
  expect(section).toContain('running for 20m');
74
77
  expect(section).toContain('FOOTER');
78
+ expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
75
79
  expect(fallback.composeSubAgentSection).not.toHaveBeenCalled();
76
80
  });
81
+
82
+ it('does not double-prepend the sentinel when the template already carries it', () => {
83
+ const fallback = createFallback();
84
+ const composer = new ConfigurableSilentSessionMessageComposer(
85
+ {
86
+ mainStalledMessage: `${SILENT_SESSION_REMINDER_SENTINEL} CUSTOM_MAIN`,
87
+ subAgentMessageHeader: null,
88
+ subAgentMessageFooter: null,
89
+ },
90
+ fallback,
91
+ );
92
+ const section = composer.composeMainStalledSection(600);
93
+ expect(section).toBe(`${SILENT_SESSION_REMINDER_SENTINEL} CUSTOM_MAIN`);
94
+ });
77
95
  });
@@ -1,5 +1,11 @@
1
1
  import { SubAgentActivity } from '../../domain/entities/LiveSessionActivitySnapshot';
2
2
  import { SilentSessionMessageComposer } from '../../domain/usecases/adapter-interfaces/SilentSessionMessageComposer';
3
+ import { SILENT_SESSION_REMINDER_SENTINEL } from '../../domain/usecases/silentSessionReminderSentinel';
4
+
5
+ const withReminderSentinel = (message: string): string =>
6
+ message.includes(SILENT_SESSION_REMINDER_SENTINEL)
7
+ ? message
8
+ : `${SILENT_SESSION_REMINDER_SENTINEL} ${message}`;
3
9
 
4
10
  export type SilentSessionMessageTemplates = {
5
11
  mainStalledMessage: string | null;
@@ -22,7 +28,7 @@ export class ConfigurableSilentSessionMessageComposer implements SilentSessionMe
22
28
  if (this.templates.mainStalledMessage === null) {
23
29
  return this.fallback.composeMainStalledSection(mainSilentSeconds);
24
30
  }
25
- return this.templates.mainStalledMessage;
31
+ return withReminderSentinel(this.templates.mainStalledMessage);
26
32
  };
27
33
 
28
34
  composeSubAgentSection = (subAgents: SubAgentActivity[]): string => {
@@ -46,6 +52,6 @@ export class ConfigurableSilentSessionMessageComposer implements SilentSessionMe
46
52
  if (this.templates.subAgentMessageFooter !== null) {
47
53
  sections.push(this.templates.subAgentMessageFooter);
48
54
  }
49
- return sections.join('\n');
55
+ return withReminderSentinel(sections.join('\n'));
50
56
  };
51
57
  }
@@ -2,6 +2,7 @@ import * as fs from 'fs';
2
2
  import * as os from 'os';
3
3
  import * as path from 'path';
4
4
  import { TranscriptOwnerCallStatusProvider } from './TranscriptOwnerCallStatusProvider';
5
+ import { SILENT_SESSION_REMINDER_SENTINEL } from '../../domain/usecases/silentSessionReminderSentinel';
5
6
 
6
7
  describe('TranscriptOwnerCallStatusProvider', () => {
7
8
  let rootDirectory: string;
@@ -63,6 +64,29 @@ describe('TranscriptOwnerCallStatusProvider', () => {
63
64
  },
64
65
  });
65
66
 
67
+ const injectedReminderStringContent = (timestamp: string): object => ({
68
+ type: 'user',
69
+ timestamp,
70
+ message: {
71
+ role: 'user',
72
+ content: `${SILENT_SESSION_REMINDER_SENTINEL} You have produced no output for 10 minutes. Self-check now:`,
73
+ },
74
+ });
75
+
76
+ const injectedReminderBlockContent = (timestamp: string): object => ({
77
+ type: 'user',
78
+ timestamp,
79
+ message: {
80
+ role: 'user',
81
+ content: [
82
+ {
83
+ type: 'text',
84
+ text: `${SILENT_SESSION_REMINDER_SENTINEL} The following sub-processes have been silent or running for a long time:`,
85
+ },
86
+ ],
87
+ },
88
+ });
89
+
66
90
  const sessionName = 'workbench';
67
91
 
68
92
  it('reports a session as waiting when the last owner call is newer than the last owner reply', async () => {
@@ -121,6 +145,49 @@ describe('TranscriptOwnerCallStatusProvider', () => {
121
145
  expect(result.has(sessionName)).toBe(true);
122
146
  });
123
147
 
148
+ it('does not count a monitor-injected reminder with string content as an owner reply', async () => {
149
+ const transcriptPath = writeTranscript('workbench.jsonl', [
150
+ assistantWithMarker('2026-06-27T10:00:00.000Z'),
151
+ injectedReminderStringContent('2026-06-27T10:10:00.000Z'),
152
+ ]);
153
+ const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
154
+
155
+ const result = await provider.listSessionNamesWithUnansweredOwnerCall(
156
+ new Map([[sessionName, transcriptPath]]),
157
+ );
158
+
159
+ expect(result.has(sessionName)).toBe(true);
160
+ });
161
+
162
+ it('does not count a monitor-injected reminder with text-block content as an owner reply', async () => {
163
+ const transcriptPath = writeTranscript('workbench.jsonl', [
164
+ assistantWithMarker('2026-06-27T10:00:00.000Z'),
165
+ injectedReminderBlockContent('2026-06-27T10:10:00.000Z'),
166
+ ]);
167
+ const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
168
+
169
+ const result = await provider.listSessionNamesWithUnansweredOwnerCall(
170
+ new Map([[sessionName, transcriptPath]]),
171
+ );
172
+
173
+ expect(result.has(sessionName)).toBe(true);
174
+ });
175
+
176
+ it('still counts a genuine owner reply that arrives after a monitor-injected reminder', async () => {
177
+ const transcriptPath = writeTranscript('workbench.jsonl', [
178
+ assistantWithMarker('2026-06-27T10:00:00.000Z'),
179
+ injectedReminderStringContent('2026-06-27T10:10:00.000Z'),
180
+ ownerReply('2026-06-27T10:20:00.000Z'),
181
+ ]);
182
+ const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
183
+
184
+ const result = await provider.listSessionNamesWithUnansweredOwnerCall(
185
+ new Map([[sessionName, transcriptPath]]),
186
+ );
187
+
188
+ expect(result.has(sessionName)).toBe(false);
189
+ });
190
+
124
191
  it('does not report a session that never raised an owner call', async () => {
125
192
  const transcriptPath = writeTranscript('workbench.jsonl', [
126
193
  assistantPlain('2026-06-27T10:00:00.000Z'),
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import { OwnerCallStatusProvider } from '../../domain/usecases/adapter-interfaces/OwnerCallStatusProvider';
3
+ import { SILENT_SESSION_REMINDER_SENTINEL } from '../../domain/usecases/silentSessionReminderSentinel';
3
4
 
4
5
  const isRecord = (value: unknown): value is Record<string, unknown> =>
5
6
  typeof value === 'object' && value !== null;
@@ -41,12 +42,26 @@ const extractText = (content: unknown): string => {
41
42
 
42
43
  const hasOwnerTextReply = (content: unknown): boolean => {
43
44
  if (typeof content === 'string') {
44
- return content.length > 0;
45
+ if (content.length === 0) {
46
+ return false;
47
+ }
48
+ // A monitor-injected self-check reminder lands in the target session's
49
+ // transcript as a user text entry. It carries the reminder sentinel, so it
50
+ // is the monitor talking to the session, not the owner replying. It MUST NOT
51
+ // advance the last-owner-reply time, otherwise an outstanding call-to-user
52
+ // is wrongly treated as answered and the session stops being suppressed.
53
+ return !content.includes(SILENT_SESSION_REMINDER_SENTINEL);
45
54
  }
46
55
  if (!Array.isArray(content)) {
47
56
  return false;
48
57
  }
49
- return content.some((block) => isRecord(block) && block.type === 'text');
58
+ const hasTextBlock = content.some(
59
+ (block) => isRecord(block) && block.type === 'text',
60
+ );
61
+ if (!hasTextBlock) {
62
+ return false;
63
+ }
64
+ return !extractText(content).includes(SILENT_SESSION_REMINDER_SENTINEL);
50
65
  };
51
66
 
52
67
  export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvider {
@@ -67,6 +67,15 @@ describe('TranscriptSessionSubAgentActivityRepository', () => {
67
67
  },
68
68
  ];
69
69
 
70
+ const stopSequenceFinishedEntries = (startTimestamp: string): object[] => [
71
+ { type: 'user', timestamp: startTimestamp, message: { role: 'user' } },
72
+ {
73
+ type: 'assistant',
74
+ timestamp: startTimestamp,
75
+ message: { role: 'assistant', stop_reason: 'stop_sequence' },
76
+ },
77
+ ];
78
+
70
79
  it('reports a running sub-agent with silent seconds from the file mtime and running seconds from the first entry', async () => {
71
80
  const sessionName = 'https_//github_com/owner/repo/issues/9';
72
81
  const startTimestamp = '2026-06-27T11:45:00.000Z';
@@ -110,6 +119,26 @@ describe('TranscriptSessionSubAgentActivityRepository', () => {
110
119
  expect(result.size).toBe(0);
111
120
  });
112
121
 
122
+ it('excludes a finished sub-agent whose last entry stop_reason is stop_sequence', async () => {
123
+ const sessionName = 'https_//github_com/owner/repo/issues/9';
124
+ writeAgentTranscript(
125
+ sessionName,
126
+ 'done2',
127
+ stopSequenceFinishedEntries('2026-06-27T11:00:00.000Z'),
128
+ nowEpochSeconds - 30,
129
+ );
130
+ const repository = new TranscriptSessionSubAgentActivityRepository(
131
+ createResolver(),
132
+ now,
133
+ );
134
+
135
+ const result = await repository.listSubAgentActivitiesBySessionName([
136
+ sessionName,
137
+ ]);
138
+
139
+ expect(result.size).toBe(0);
140
+ });
141
+
113
142
  it('follows a symlinked transcript when computing silent seconds', async () => {
114
143
  const sessionName = 'https_//github_com/owner/repo/issues/9';
115
144
  const targetDir = fs.mkdtempSync(
@@ -126,7 +126,10 @@ export class TranscriptSessionSubAgentActivityRepository implements SessionSubAg
126
126
  return null;
127
127
  }
128
128
  const transcript = parseTranscript(content);
129
- if (transcript.lastStopReason === 'end_turn') {
129
+ if (
130
+ transcript.lastStopReason === 'end_turn' ||
131
+ transcript.lastStopReason === 'stop_sequence'
132
+ ) {
130
133
  return null;
131
134
  }
132
135
  const silentSeconds = clampToZero(
@@ -1,8 +1,21 @@
1
1
  import { DefaultSilentSessionMessageComposer } from './DefaultSilentSessionMessageComposer';
2
+ import { SILENT_SESSION_REMINDER_SENTINEL } from './silentSessionReminderSentinel';
2
3
 
3
4
  describe('DefaultSilentSessionMessageComposer', () => {
4
5
  const composer = new DefaultSilentSessionMessageComposer();
5
6
 
7
+ it('embeds the reminder sentinel in the main-stalled section', () => {
8
+ const section = composer.composeMainStalledSection(600);
9
+ expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
10
+ });
11
+
12
+ it('embeds the reminder sentinel in the sub-agent section', () => {
13
+ const section = composer.composeSubAgentSection([
14
+ { label: 'sub-process-1', silentSeconds: 360, runningSeconds: 1200 },
15
+ ]);
16
+ expect(section).toContain(SILENT_SESSION_REMINDER_SENTINEL);
17
+ });
18
+
6
19
  it('renders the configured main-stalled message with the silent minutes substituted', () => {
7
20
  const section = composer.composeMainStalledSection(600);
8
21
  expect(section).toContain('You have produced no output for 10 minutes.');
@@ -1,5 +1,6 @@
1
1
  import { SubAgentActivity } from '../entities/LiveSessionActivitySnapshot';
2
2
  import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
3
+ import { SILENT_SESSION_REMINDER_SENTINEL } from './silentSessionReminderSentinel';
3
4
 
4
5
  const formatMinutes = (seconds: number): string => {
5
6
  const minutes = Math.floor(seconds / 60);
@@ -9,7 +10,7 @@ const formatMinutes = (seconds: number): string => {
9
10
  const composeMainStalledMessage = (mainSilentSeconds: number): string => {
10
11
  const minutes = Math.floor(mainSilentSeconds / 60);
11
12
  return [
12
- `You have produced no output for ${minutes} minutes. Idle waiting wastes this live session and is unacceptable. Always work proactively and stay ahead of the work: anticipate the next steps and, at every point, choose the fastest path so the whole task finishes as early as possible; never wait passively. Finish every task in the shortest possible time — but "fastest" means correct and incident-free, not merely quick: a fast but wrong result is worthless and causes incidents, so be fast without breaking things. Use parallel execution and your whole team of sub-agents to minimize total wall-clock time. Your goal is to drive every task to completion and have the owner confirm that all tasks are done. Do NOT close this session on your own — it is closed only after the owner has verified completion and tells you to close it. Self-check now:`,
13
+ `${SILENT_SESSION_REMINDER_SENTINEL} You have produced no output for ${minutes} minutes. Idle waiting wastes this live session and is unacceptable. Always work proactively and stay ahead of the work: anticipate the next steps and, at every point, choose the fastest path so the whole task finishes as early as possible; never wait passively. Finish every task in the shortest possible time — but "fastest" means correct and incident-free, not merely quick: a fast but wrong result is worthless and causes incidents, so be fast without breaking things. Use parallel execution and your whole team of sub-agents to minimize total wall-clock time. Your goal is to drive every task to completion and have the owner confirm that all tasks are done. Do NOT close this session on your own — it is closed only after the owner has verified completion and tells you to close it. Self-check now:`,
13
14
  `1. Every request from the owner is registered as a session task and your task list is kept current (mark tasks completed when done); verify nothing is missing or stale.`,
14
15
  `2. Your plan is the fastest correct path: parallelize independent work across sub-agents, delegate, and remove needless serialization. Choose the fastest safe method, not the easiest.`,
15
16
  `3. A monitor is in place that detects when any sub-agent produces no output for 5 minutes.`,
@@ -31,7 +32,7 @@ export class DefaultSilentSessionMessageComposer implements SilentSessionMessage
31
32
  )}, running for ${formatMinutes(subAgent.runningSeconds)}`,
32
33
  );
33
34
  return [
34
- 'The following sub-processes have been silent or running for a long time:',
35
+ `${SILENT_SESSION_REMINDER_SENTINEL} The following sub-processes have been silent or running for a long time:`,
35
36
  ...lines,
36
37
  'If a sub-process is stalled, take action (restart, hand off, or replace it). If it is legitimately waiting on an external dependency (continuous integration, an external API, or another process), let it continue.',
37
38
  ].join('\n');
@@ -0,0 +1,9 @@
1
+ // Stable, recognizable marker embedded in every monitor-injected silent-session
2
+ // self-check reminder. It lets owner-reply detection distinguish a reminder that
3
+ // the monitor injected into a session's transcript (which arrives as a user text
4
+ // entry via `tmux send-keys`) from a genuine owner reply, so an injected reminder
5
+ // is never miscounted as the owner answering an outstanding call-to-user. The tag
6
+ // is bracketed and namespaced to make an accidental collision with genuine human
7
+ // owner text effectively impossible.
8
+ export const SILENT_SESSION_REMINDER_SENTINEL =
9
+ '[TDPM_SILENT_SESSION_SELF_CHECK_REMINDER]';
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigurableSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AACrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uEAAuE,CAAC;AAErH,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC,CAAC;AAOF,qBAAa,wCAAyC,YAAW,4BAA4B;IAEzF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,SAAS,EAAE,6BAA6B,EACxC,QAAQ,EAAE,4BAA4B;IAGzD,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAK7D;IAEF,sBAAsB,GAAI,WAAW,gBAAgB,EAAE,KAAG,MAAM,CAsB9D;CACH"}
1
+ {"version":3,"file":"ConfigurableSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AACrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uEAAuE,CAAC;AAQrH,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC,CAAC;AAOF,qBAAa,wCAAyC,YAAW,4BAA4B;IAEzF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,SAAS,EAAE,6BAA6B,EACxC,QAAQ,EAAE,4BAA4B;IAGzD,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAK7D;IAEF,sBAAsB,GAAI,WAAW,gBAAgB,EAAE,KAAG,MAAM,CAsB9D;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAkD3G,qBAAa,iCAAkC,YAAW,uBAAuB;IACnE,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,MAAM,GAAG,IAAI;IAE3D,uCAAuC,GACrC,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAYrB;IAEF,OAAO,CAAC,sBAAsB,CAkD5B;CACH"}
1
+ {"version":3,"file":"TranscriptOwnerCallStatusProvider.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAiE3G,qBAAa,iCAAkC,YAAW,uBAAuB;IACnE,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,MAAM,GAAG,IAAI;IAE3D,uCAAuC,GACrC,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAYrB;IAEF,OAAO,CAAC,sBAAsB,CAkD5B;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptSessionSubAgentActivityRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AACrF,OAAO,EAAE,iCAAiC,EAAE,MAAM,4EAA4E,CAAC;AAC/H,OAAO,EAAE,mCAAmC,EAAE,MAAM,8EAA8E,CAAC;AA4DnI,qBAAa,2CAA4C,YAAW,iCAAiC;IAEjG,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,iBAAiB,EAAE,mCAAmC,EACtD,GAAG,EAAE,IAAI;IAG5B,mCAAmC,GACjC,cAAc,MAAM,EAAE,KACrB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAezC;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;IAEF,OAAO,CAAC,UAAU,CA6BhB;CACH"}
1
+ {"version":3,"file":"TranscriptSessionSubAgentActivityRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AACrF,OAAO,EAAE,iCAAiC,EAAE,MAAM,4EAA4E,CAAC;AAC/H,OAAO,EAAE,mCAAmC,EAAE,MAAM,8EAA8E,CAAC;AA4DnI,qBAAa,2CAA4C,YAAW,iCAAiC;IAEjG,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,iBAAiB,EAAE,mCAAmC,EACtD,GAAG,EAAE,IAAI;IAG5B,mCAAmC,GACjC,cAAc,MAAM,EAAE,KACrB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAezC;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;IAEF,OAAO,CAAC,UAAU,CAgChB;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,mDAAmD,CAAC;AAmBjG,qBAAa,mCAAoC,YAAW,4BAA4B;IACtF,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAE7D;IAEF,sBAAsB,GAAI,WAAW,gBAAgB,EAAE,KAAG,MAAM,CAY9D;CACH"}
1
+ {"version":3,"file":"DefaultSilentSessionMessageComposer.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,mDAAmD,CAAC;AAoBjG,qBAAa,mCAAoC,YAAW,4BAA4B;IACtF,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAE7D;IAEF,sBAAsB,GAAI,WAAW,gBAAgB,EAAE,KAAG,MAAM,CAY9D;CACH"}
@@ -0,0 +1,2 @@
1
+ export declare const SILENT_SESSION_REMINDER_SENTINEL = "[TDPM_SILENT_SESSION_SELF_CHECK_REMINDER]";
2
+ //# sourceMappingURL=silentSessionReminderSentinel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silentSessionReminderSentinel.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/silentSessionReminderSentinel.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,gCAAgC,8CACA,CAAC"}