github-issue-tower-defence-management 1.119.1 → 1.119.3

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 (21) hide show
  1. package/.gitleaks.toml +13 -0
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +1 -1
  4. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js +1 -1
  5. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
  6. package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js +8 -3
  7. package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js.map +1 -1
  8. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js +6 -5
  9. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +4 -1
  12. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +1 -0
  13. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +55 -1
  14. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +12 -2
  15. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +68 -2
  16. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +4 -4
  17. package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts.map +1 -1
  18. package/types/adapter/repositories/ConfigurableSilentSessionMessageComposer.d.ts +2 -1
  19. package/types/adapter/repositories/ConfigurableSilentSessionMessageComposer.d.ts.map +1 -1
  20. package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts +1 -0
  21. package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts.map +1 -1
package/.gitleaks.toml CHANGED
@@ -7,3 +7,16 @@ title = "gitleaks config"
7
7
 
8
8
  [extend]
9
9
  useDefault = true
10
+
11
+ # Intentional credential-vault skill files.
12
+ # Some private repositories intentionally store login credentials as plaintext
13
+ # inside dedicated credential-vault skill files (the *-login-accounts/SKILL.md
14
+ # files under .claude/skills/). Those files are an intentional secret store, so
15
+ # credentials committed there are expected and must not fail the scan. Secret
16
+ # scanning stays active for every other file so accidental secret commits
17
+ # elsewhere are still caught.
18
+ [[allowlists]]
19
+ description = "Allow intentional credentials in login-accounts credential-vault skill files"
20
+ paths = [
21
+ '''(^|/)\.claude/skills/[^/]*login-accounts/SKILL\.md$''',
22
+ ]
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.119.3](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.119.2...v1.119.3) (2026-07-02)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **silent-monitor:** owner-call guidance requires a self-contained message and forbids scroll-back ([#1138](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1138)) ([782f958](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/782f958936a099deb828ed738f7d670a050c318a))
7
+
8
+ ## [1.119.2](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.119.1...v1.119.2) (2026-07-02)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **silent-monitor:** idle stall reminder mandates authoritative-signal cause-check and logged result ([#1134](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1134)) ([bf659a9](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/bf659a96256c4d80ea7aa2502c8dab9433e15f56))
14
+
1
15
  ## [1.119.1](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.119.0...v1.119.1) (2026-07-01)
2
16
 
3
17
 
package/README.md CHANGED
@@ -565,7 +565,7 @@ Candidate sessions are first selected from the live process tree: every live tmu
565
565
 
566
566
  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 entry of any kind in the session's resolved transcript rather than from the transcript file modification time, so a session that keeps appending tool results, owner replies, or any other entry type while emitting no assistant text still counts as active and is not mistaken for a silent one. 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. The stalled section's owner-call self-check point also instructs the agent on the required owner-call notification format: the owner-call must be emitted as the configured owner-call marker tag (interpolated from `ownerCallMarker` when set) as a complete matching pair — opening marker, content, then closing marker — on a single line with no newline inside the tag, and the content between the markers must begin with the 🔴 emoji immediately, because the owner's app only surfaces the notification together with its content when the exact, well-formed tag with the leading 🔴 is present and a malformed tag results in only a red indicator with no readable content. The same self-check point also tells the agent when to fire the owner-call: if the agent has completed or answered a request from the owner in this session, it must fire the owner-call to report the result to the owner, because completing or answering an owner's requested action is itself a reason to fire the owner-call. Completing or answering an owner request without firing the owner-call means the owner is never notified — the owner's app only surfaces the session when the owner-call fires — so the task silently stalls; the point states the causal link explicitly, noting that if the self-check reminder keeps arriving it is likely because an owner request was completed or answered without firing the owner-call, so the agent should fire the owner-call to report the result to the owner.
567
567
 
568
- 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`. It is composed of up to two distinct messages, one per condition: an idle message that lists each sub-process that produced no output for at least `subAgentSilentThresholdSeconds` with its no-output minutes and asks the agent to check whether it is stuck or legitimately waiting on an external dependency, and a long-running message that lists each sub-process that has been running for at least `subAgentRunningThresholdSeconds` with its running minutes and asks the agent to verify it is genuinely advancing rather than caught in an infinite loop, a task that is too large, or a stuck approach. The long-running message foregrounds the running duration and does not surface the short idle time, so a sub-process flagged only for running long is not dismissed because its silent time looks small; a sub-process that matches both conditions appears in both messages. The long-running threshold is overridable per project via `subAgentRunningThresholdSeconds` (default 900 seconds) for repositories whose continuous integration legitimately runs longer. Sub-processes are discovered in one of two ways: when `subAgentTranscriptRootDirectory` is configured, each session's `subagents/agent-<id>.jsonl` transcripts are scanned. The sub-agent directory is the `subagents/` directory that sits next to the session's already-resolved main-session transcript file under `projects/<cwd-slug>/<sessionId>/subagents/`, derived by stripping the `.jsonl` suffix from the main-session transcript path and appending `subagents`; this on-disk path is keyed by the session's working-directory slug and session id, not by the tmux session name. Finished sub-agents are skipped, determined from the last meaningful transcript entry rather than from the last non-null `stop_reason` seen anywhere in the file. A sub-agent is treated as finished when its last entry is a completion or termination marker: an assistant message whose `stop_reason` is `end_turn` or `stop_sequence`, an assistant message whose final content block is `text` (a final answer returned to the parent, which Claude Code often writes with a `null` `stop_reason`), a user message whose final content block is `text` (a terminal user entry such as `[Request interrupted by user]`), a user message whose final content block is a `tool_result`, or a user message with no content blocks. The `tool_result` and empty-user terminal shapes are the ones a sub-agent leaves when it ends right after its last tool call completes — for example the `tool_result` of the final `StructuredOutput` call a sub-agent uses to return its result, the launch acknowledgement of a background command, or any other tool whose result is written but no assistant turn follows because the sub-agent's process is gone (killed, crashed, or token-exhausted). Recognizing these shapes as terminal is what lets a marker-less dead transcript be excluded by the completion signal itself rather than by an age cap. Only a sub-agent whose last entry is an assistant message with a pending `tool_use` block and no following tool result is still considered active and subject to the idle and running thresholds; this is the genuinely-incomplete shape — the sub-agent requested a tool that never returned and is hung mid-work. Idle time is taken from the transcript file modification time (following symlinks) while running time is taken from its first entry timestamp. Because sub-agents run in-process inside the parent session rather than as separate operating-system processes, no process-liveness signal can be matched back to a transcript; the completion-or-termination marker at the end of the transcript is the sole signal used to exclude finished sub-agents. There is no age cap: a sub-agent that is past the idle or running threshold and does not end in a completion or termination marker is flagged regardless of how long it has been silent, so a genuinely-stuck sub-agent that has been hung on a pending tool call for hours or days is still reported instead of being silently dropped. Otherwise sub-processes are discovered by matching `subAgentProcessMatchPattern` against process command lines.
568
+ 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`. It is composed of up to two distinct messages, one per condition: an idle message that lists each sub-process that produced no output for at least `subAgentSilentThresholdSeconds` with its no-output minutes, tells the agent to treat that system-detected idle duration as the authoritative signal rather than re-deriving whether the sub-process is alive, forbids dismissing the signal from speculation, and requires the agent to determine the cause by a concrete check (whether there is genuinely no recent activity anywhere, including a very recent push or commit or output from nested sub-processes it started, versus being legitimately blocked on an external dependency such as a continuous-integration run, an external API, or another process), to act if stuck or state the waiting conclusion with concrete evidence if waiting, and to output that investigation result into the session as a log even though owner notification is not required, and a long-running message that lists each sub-process that has been running for at least `subAgentRunningThresholdSeconds` with its running minutes and asks the agent to verify it is genuinely advancing rather than caught in an infinite loop, a task that is too large, or a stuck approach. The long-running message foregrounds the running duration and does not surface the short idle time, so a sub-process flagged only for running long is not dismissed because its silent time looks small; a sub-process that matches both conditions appears in both messages. The long-running threshold is overridable per project via `subAgentRunningThresholdSeconds` (default 900 seconds) for repositories whose continuous integration legitimately runs longer. Sub-processes are discovered in one of two ways: when `subAgentTranscriptRootDirectory` is configured, each session's `subagents/agent-<id>.jsonl` transcripts are scanned. The sub-agent directory is the `subagents/` directory that sits next to the session's already-resolved main-session transcript file under `projects/<cwd-slug>/<sessionId>/subagents/`, derived by stripping the `.jsonl` suffix from the main-session transcript path and appending `subagents`; this on-disk path is keyed by the session's working-directory slug and session id, not by the tmux session name. Finished sub-agents are skipped, determined from the last meaningful transcript entry rather than from the last non-null `stop_reason` seen anywhere in the file. A sub-agent is treated as finished when its last entry is a completion or termination marker: an assistant message whose `stop_reason` is `end_turn` or `stop_sequence`, an assistant message whose final content block is `text` (a final answer returned to the parent, which Claude Code often writes with a `null` `stop_reason`), a user message whose final content block is `text` (a terminal user entry such as `[Request interrupted by user]`), a user message whose final content block is a `tool_result`, or a user message with no content blocks. The `tool_result` and empty-user terminal shapes are the ones a sub-agent leaves when it ends right after its last tool call completes — for example the `tool_result` of the final `StructuredOutput` call a sub-agent uses to return its result, the launch acknowledgement of a background command, or any other tool whose result is written but no assistant turn follows because the sub-agent's process is gone (killed, crashed, or token-exhausted). Recognizing these shapes as terminal is what lets a marker-less dead transcript be excluded by the completion signal itself rather than by an age cap. Only a sub-agent whose last entry is an assistant message with a pending `tool_use` block and no following tool result is still considered active and subject to the idle and running thresholds; this is the genuinely-incomplete shape — the sub-agent requested a tool that never returned and is hung mid-work. Idle time is taken from the transcript file modification time (following symlinks) while running time is taken from its first entry timestamp. Because sub-agents run in-process inside the parent session rather than as separate operating-system processes, no process-liveness signal can be matched back to a transcript; the completion-or-termination marker at the end of the transcript is the sole signal used to exclude finished sub-agents. There is no age cap: a sub-agent that is past the idle or running threshold and does not end in a completion or termination marker is flagged regardless of how long it has been silent, so a genuinely-stuck sub-agent that has been hung on a pending tool call for hours or days is still reported instead of being silently dropped. Otherwise sub-processes are discovered by matching `subAgentProcessMatchPattern` against process command lines.
569
569
 
570
570
  A two-consecutive-cycle debounce guards against transient false positives: a session is notified only when it meets the silent-target condition (output silent at or beyond the threshold and not waiting on the owner) in the current cycle and it was also a notification candidate in the immediately previous cycle. The set of candidate session names is persisted across cycles, keyed by the globally-unique session name, in a state file written atomically (the default path is `${XDG_CACHE_HOME:-~/.cache}/tdpm/silent-session-candidates.json`, overridable via `candidateDebounceStateFilePath`). The monitor runs as a fresh process each schedule cycle, so this on-disk state is what carries the previous cycle's candidate set forward. Each cycle loads the candidate set recorded within `candidateDebounceRecencyWindowSeconds` of now (default 900, a small multiple of the schedule interval to tolerate interval jitter), then records the current candidate set with the current timestamp; entries that age past the retention window are dropped on the next save, and a save merges with other recently-recorded session names so concurrent per-project runs do not clobber each other. A candidate that appears for only one isolated cycle — for example because the owner's reply has not yet been flushed to the transcript when the monitor reads it, or because a transient hub-task status resolution error fails open for a single cycle — is therefore deferred and never notified, while a genuine stall that persists across cycles is still caught, one cycle later. Because silence is measured from the latest transcript entry of any type — including the monitor's own injected reminder and the owner's reply — a notified session's measured silence resets as soon as it produces any activity, so a session that has resumed is not re-flagged. 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. When `activeHubTaskStatus` is configured, each notification is gated on the latest GitHub state and Project Status of the session's hub task: a session whose name identifies a github.com issue or pull request — accepted both as a clean `https://github.com/{owner}/{repo}/issues/{N}` URL and as the real tmux session-name form `https_//github_com/{owner}/{repo}/issues/{N}` produced by replacing `.` and `:` with `_` (the `pull` path is accepted in both forms too) — is notified only while that hub task is open and in the configured active Status, so a session whose hub task has been closed, merged, or moved to another Status (for example Done) is suppressed; sessions whose name does not identify a github.com issue or pull request are never gated. To cut GitHub query load and remove most fail-opens, each resolved status is cached by issue URL in a state file written atomically (the default path is `${XDG_CACHE_HOME:-~/.cache}/tdpm/silent-session-hub-task-status.json`, overridable via `hubTaskStatusCacheStateFilePath`). On each cycle the gate reads the cache first: a cached entry recorded within `hubTaskStatusCacheTtlSeconds` of now (default 300) decides the gate without any GitHub query, and otherwise the hub task is re-resolved and the result written back to the cache. When live resolution fails — the resolver returns no tracked task (a closed or Done issue filtered out of the active project), or a transient API error is thrown — the gate falls back to the cached status even after it has expired: a cached active entry keeps the notification (still notified if genuinely stalled) and a cached closed or non-active entry suppresses it, each logged with a distinct warning. Only when there is no cached status at all and live resolution fails does the gate fail open once (a closed issue and a transient error are indistinguishable in that case without an extra network call), logging a distinct warning, so the next cycle can populate the cache. When `activeHubTaskStatus` is unset, this hub-task check is a no-op. 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.
571
571
 
@@ -37,7 +37,7 @@ const notifySilentTmuxSessions = async (params) => {
37
37
  console.log('Silent live session notification skipped: not enabled (set silentNotificationEnabled or TDPM_SILENT_NOTIFICATION_ENABLED=true to enable).');
38
38
  return;
39
39
  }
40
- const messageComposer = new ConfigurableSilentSessionMessageComposer_1.ConfigurableSilentSessionMessageComposer(messageTemplates, new DefaultSilentSessionMessageComposer_1.DefaultSilentSessionMessageComposer(ownerCallMarker));
40
+ const messageComposer = new ConfigurableSilentSessionMessageComposer_1.ConfigurableSilentSessionMessageComposer(messageTemplates, new DefaultSilentSessionMessageComposer_1.DefaultSilentSessionMessageComposer(ownerCallMarker), ownerCallMarker);
41
41
  const useCase = new NotifySilentLiveSessionsUseCase_1.NotifySilentLiveSessionsUseCase(new LocalProcessLiveSessionProcessSnapshotProvider_1.LocalProcessLiveSessionProcessSnapshotProvider(localCommandRunner, processEnvironReader ?? new ProcFsProcessEnvironReader_1.ProcFsProcessEnvironReader()), new FileSystemInteractiveLiveSessionTranscriptResolver_1.FileSystemInteractiveLiveSessionTranscriptResolver(), new FileSystemSessionOutputActivityRepository_1.FileSystemSessionOutputActivityRepository(), createSubAgentActivityRepository(subAgentTranscriptRootDirectory, subAgentProcessMatchPattern, subAgentOutputRootDirectory, localCommandRunner, now), createOwnerCallStatusProvider(ownerCallMarker), new TmuxSilentSessionNotificationRepository_1.TmuxSilentSessionNotificationRepository(localCommandRunner), candidateDebounceStateFilePath !== null
42
42
  ? new FileSystemSilentSessionCandidateStateRepository_1.FileSystemSilentSessionCandidateStateRepository(candidateDebounceStateFilePath)
43
43
  : new FileSystemSilentSessionCandidateStateRepository_1.FileSystemSilentSessionCandidateStateRepository(), messageComposer, new RealSleeper_1.RealSleeper(), hubTaskStatusResolver, hubTaskStatusCacheStateFilePath !== null
@@ -1 +1 @@
1
- {"version":3,"file":"notifySilentTmuxSessions.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notifySilentTmuxSessions.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.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.ConfigurableSilentSessionMessageComposer = void 0;
4
+ const DefaultSilentSessionMessageComposer_1 = require("../../domain/usecases/DefaultSilentSessionMessageComposer");
4
5
  const silentSessionReminderSentinel_1 = require("../../domain/usecases/silentSessionReminderSentinel");
5
6
  const withReminderSentinel = (message) => message.includes(silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL)
6
7
  ? message
@@ -10,14 +11,15 @@ const formatMinutes = (seconds) => {
10
11
  return `${minutes}m`;
11
12
  };
12
13
  class ConfigurableSilentSessionMessageComposer {
13
- constructor(templates, fallback) {
14
+ constructor(templates, fallback, ownerCallMarker = null) {
14
15
  this.templates = templates;
15
16
  this.fallback = fallback;
17
+ this.ownerCallMarker = ownerCallMarker;
16
18
  this.composeMainStalledSection = (mainSilentSeconds) => {
17
19
  if (this.templates.mainStalledMessage === null) {
18
20
  return this.fallback.composeMainStalledSection(mainSilentSeconds);
19
21
  }
20
- return withReminderSentinel(this.templates.mainStalledMessage);
22
+ return withReminderSentinel(`${this.templates.mainStalledMessage} ${(0, DefaultSilentSessionMessageComposer_1.composeOwnerCallFormatGuidance)(this.ownerCallMarker)}`);
21
23
  };
22
24
  this.composeSubAgentSection = (subAgents, thresholds) => {
23
25
  const hasIdleTemplate = this.templates.subAgentIdleMessageHeader !== null ||
@@ -34,7 +36,10 @@ class ConfigurableSilentSessionMessageComposer {
34
36
  sections.push(this.composeIdleSection(idleSubAgents, this.templates.subAgentIdleMessageHeader, this.templates.subAgentIdleMessageFooter));
35
37
  }
36
38
  else if (idleSubAgents.length > 0) {
37
- sections.push(this.composeIdleSection(idleSubAgents, null, null));
39
+ sections.push(this.fallback.composeSubAgentSection(idleSubAgents, {
40
+ subAgentSilentThresholdSeconds: thresholds.subAgentSilentThresholdSeconds,
41
+ subAgentRunningThresholdSeconds: Number.POSITIVE_INFINITY,
42
+ }));
38
43
  }
39
44
  if (longRunningSubAgents.length > 0 && hasLongRunningTemplate) {
40
45
  sections.push(this.composeLongRunningSection(longRunningSubAgents, this.templates.subAgentLongRunningMessageHeader, this.templates.subAgentLongRunningMessageFooter));
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigurableSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ConfigurableSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DefaultSilentSessionMessageComposer = void 0;
3
+ exports.DefaultSilentSessionMessageComposer = exports.composeOwnerCallFormatGuidance = void 0;
4
4
  const silentSessionReminderSentinel_1 = require("./silentSessionReminderSentinel");
5
5
  const formatMinutes = (seconds) => {
6
6
  const minutes = Math.floor(seconds / 60);
@@ -9,9 +9,9 @@ const formatMinutes = (seconds) => {
9
9
  const composeIdleSubAgentSection = (idleSubAgents) => {
10
10
  const lines = idleSubAgents.map((subAgent) => `- ${subAgent.label}: no output for ${formatMinutes(subAgent.silentSeconds)}`);
11
11
  return [
12
- `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} The following sub-process(es) have produced no output for several minutes and may be stalled:`,
12
+ `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} The system has already detected, from each sub-process's last tool activity, that it has produced no output for about the minutes shown below. Treat that figure as the authoritative system signal; do NOT spend effort re-deriving whether the sub-process is alive. Around five minutes of silence is a real warning of a possible hang: do NOT speak from speculation ("probably still working") and do NOT dismiss it without evidence.`,
13
13
  ...lines,
14
- 'Check each one. If it is stuck, 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.',
14
+ 'For each one, determine the CAUSE by a concrete check and report the result. Verify specifically: (a) whether there is genuinely no recent activity anywhere — including a very recent push or commit, or output from any nested sub-processes this sub-process itself started; and (b) whether it is legitimately blocked waiting on an external dependency (a continuous-integration run, an external API, or another process). Waiting on continuous integration is plausible, but confirm it by investigation before concluding — do not assume. Based on the check: if it is genuinely stuck, take action (restart, hand off, or replace it); if it is legitimately waiting, state that conclusion together with the concrete evidence you found. Owner notification is not required, but you MUST output your investigation result in this session so it remains as a log.',
15
15
  ].join('\n');
16
16
  };
17
17
  const composeLongRunningSubAgentSection = (longRunningSubAgents) => {
@@ -26,8 +26,9 @@ const composeOwnerCallFormatGuidance = (ownerCallMarker) => {
26
26
  const markerInstruction = ownerCallMarker !== null && ownerCallMarker.length > 0
27
27
  ? `Emit the owner-call as the configured owner-call marker tag "${ownerCallMarker}" as a complete matching pair — opening marker, content, then closing marker — on a single line with no newline inside the tag.`
28
28
  : `Emit the owner-call as the configured owner-call marker tag as a complete matching pair — opening marker, content, then closing marker — on a single line with no newline inside the tag.`;
29
- return `${markerInstruction} The content between the markers MUST begin with the 🔴 emoji immediately, with no space after it. The owner's app only surfaces the notification together with its content when the exact, well-formed tag with the leading 🔴 is present; a malformed tag (a broken or missing closing marker, or a missing leading 🔴) results in only a red indicator with no readable content.`;
29
+ return `${markerInstruction} The content between the markers MUST begin with the 🔴 emoji immediately, with no space after it. The owner's app only surfaces the notification together with its content when the exact, well-formed tag with the leading 🔴 is present; a malformed tag (a broken or missing closing marker, or a missing leading 🔴) results in only a red indicator with no readable content. Make the owner-call message fully self-contained: the owner MUST understand the whole situation — what happened, what you are asking, and any decision needed — from this single latest owner-call message alone, without reading or scrolling back to earlier messages. NEVER tell the owner to scroll up, go back, or read previous or above messages; if context is needed, restate it inside the owner-call message itself.`;
30
30
  };
31
+ exports.composeOwnerCallFormatGuidance = composeOwnerCallFormatGuidance;
31
32
  const composeMainStalledMessage = (mainSilentSeconds, ownerCallMarker) => {
32
33
  const minutes = Math.floor(mainSilentSeconds / 60);
33
34
  return [
@@ -35,7 +36,7 @@ const composeMainStalledMessage = (mainSilentSeconds, ownerCallMarker) => {
35
36
  `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.`,
36
37
  `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.`,
37
38
  `3. A monitor is in place that detects when any sub-agent produces no output for 5 minutes.`,
38
- `4. If you are blocked on an owner decision — or once you believe all tasks are complete — do not wait passively: the owner is not notified of passive waiting and will not reply unless you raise a new call-to-user, so raise one now (to get the decision, or to ask the owner to verify completion). If you have COMPLETED or ANSWERED a request from the owner in this session, you MUST fire the owner-call to report the RESULT to the owner: completing or answering an owner's requested action is itself a reason to fire the owner-call. Completing or answering an owner request WITHOUT firing the owner-call means the owner is NEVER notified — the owner's app only surfaces this session when the owner-call fires — so the task silently stalls. If this self-check reminder keeps arriving, it is likely because an owner request was completed or answered without firing the owner-call; fire the owner-call now to report the result to the owner. ${composeOwnerCallFormatGuidance(ownerCallMarker)} If no owner input is needed yet, resume immediately and drive all remaining tasks to completion.`,
39
+ `4. If you are blocked on an owner decision — or once you believe all tasks are complete — do not wait passively: the owner is not notified of passive waiting and will not reply unless you raise a new call-to-user, so raise one now (to get the decision, or to ask the owner to verify completion). If you have COMPLETED or ANSWERED a request from the owner in this session, you MUST fire the owner-call to report the RESULT to the owner: completing or answering an owner's requested action is itself a reason to fire the owner-call. Completing or answering an owner request WITHOUT firing the owner-call means the owner is NEVER notified — the owner's app only surfaces this session when the owner-call fires — so the task silently stalls. If this self-check reminder keeps arriving, it is likely because an owner request was completed or answered without firing the owner-call; fire the owner-call now to report the result to the owner. ${(0, exports.composeOwnerCallFormatGuidance)(ownerCallMarker)} If no owner input is needed yet, resume immediately and drive all remaining tasks to completion.`,
39
40
  `Also, in your next output, report an estimate of how many minutes you expect to need to finish all remaining tasks.`,
40
41
  ].join('\n');
41
42
  };
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DefaultSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.119.1",
3
+ "version": "1.119.3",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -232,9 +232,12 @@ describe('notifySilentTmuxSessions', () => {
232
232
  const sendCall = runner.runCommand.mock.calls.find(
233
233
  (call) => call[0] === 'tmux' && call[1][0] === 'send-keys',
234
234
  );
235
- expect(sendCall?.[1][4]).toBe(
235
+ expect(sendCall?.[1][4]).toContain(
236
236
  `${SILENT_SESSION_REMINDER_SENTINEL} CUSTOM_MAIN_TEMPLATE`,
237
237
  );
238
+ expect(sendCall?.[1][4]).toContain(
239
+ 'NEVER tell the owner to scroll up, go back, or read previous or above messages',
240
+ );
238
241
  });
239
242
 
240
243
  it('suppresses the notification whenever an owner call is unanswered, regardless of how long the session has been silent', async () => {
@@ -122,6 +122,7 @@ export const notifySilentTmuxSessions = async (
122
122
  const messageComposer = new ConfigurableSilentSessionMessageComposer(
123
123
  messageTemplates,
124
124
  new DefaultSilentSessionMessageComposer(ownerCallMarker),
125
+ ownerCallMarker,
125
126
  );
126
127
  const useCase = new NotifySilentLiveSessionsUseCase(
127
128
  new LocalProcessLiveSessionProcessSnapshotProvider(
@@ -48,6 +48,30 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
48
48
  expect(fallback.composeMainStalledSection).not.toHaveBeenCalled();
49
49
  });
50
50
 
51
+ it('appends the owner-call format guidance, including the self-contained and no-scroll-back requirements, to the configured main template', () => {
52
+ const fallback = createFallback();
53
+ const composer = new ConfigurableSilentSessionMessageComposer(
54
+ {
55
+ ...noTemplates,
56
+ mainStalledMessage: 'CUSTOM_MAIN',
57
+ },
58
+ fallback,
59
+ '<<OWNER_CALL>>',
60
+ );
61
+ const section = composer.composeMainStalledSection(600);
62
+ expect(section).toContain('CUSTOM_MAIN');
63
+ expect(section).toContain(
64
+ 'the configured owner-call marker tag "<<OWNER_CALL>>" as a complete matching pair',
65
+ );
66
+ expect(section).toContain('🔴');
67
+ expect(section).toContain(
68
+ 'Make the owner-call message fully self-contained: the owner MUST understand the whole situation — what happened, what you are asking, and any decision needed — from this single latest owner-call message alone, without reading or scrolling back to earlier messages.',
69
+ );
70
+ expect(section).toContain(
71
+ 'NEVER tell the owner to scroll up, go back, or read previous or above messages; if context is needed, restate it inside the owner-call message itself.',
72
+ );
73
+ });
74
+
51
75
  it('uses the fallback sub-agent section when no sub-agent template is configured', () => {
52
76
  const fallback = createFallback();
53
77
  const composer = new ConfigurableSilentSessionMessageComposer(
@@ -133,6 +157,31 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
133
157
  expect(longRunningSection).toContain('running for 20m');
134
158
  });
135
159
 
160
+ it('renders the built-in idle message via the fallback when only a long-running template is configured', () => {
161
+ const fallback = createFallback();
162
+ fallback.composeSubAgentSection.mockReturnValue('BUILTIN_IDLE');
163
+ const composer = new ConfigurableSilentSessionMessageComposer(
164
+ {
165
+ ...noTemplates,
166
+ subAgentLongRunningMessageHeader: 'LONG_HEADER',
167
+ },
168
+ fallback,
169
+ );
170
+ const section = composer.composeSubAgentSection(
171
+ [{ label: 'task-idle', silentSeconds: 360, runningSeconds: 60 }],
172
+ THRESHOLDS,
173
+ );
174
+ expect(section).toContain('BUILTIN_IDLE');
175
+ expect(fallback.composeSubAgentSection).toHaveBeenCalledWith(
176
+ [{ label: 'task-idle', silentSeconds: 360, runningSeconds: 60 }],
177
+ {
178
+ subAgentSilentThresholdSeconds:
179
+ THRESHOLDS.subAgentSilentThresholdSeconds,
180
+ subAgentRunningThresholdSeconds: Number.POSITIVE_INFINITY,
181
+ },
182
+ );
183
+ });
184
+
136
185
  it('does not double-prepend the sentinel when the template already carries it', () => {
137
186
  const fallback = createFallback();
138
187
  const composer = new ConfigurableSilentSessionMessageComposer(
@@ -143,6 +192,11 @@ describe('ConfigurableSilentSessionMessageComposer', () => {
143
192
  fallback,
144
193
  );
145
194
  const section = composer.composeMainStalledSection(600);
146
- expect(section).toBe(`${SILENT_SESSION_REMINDER_SENTINEL} CUSTOM_MAIN`);
195
+ const sentinelOccurrences =
196
+ section.split(SILENT_SESSION_REMINDER_SENTINEL).length - 1;
197
+ expect(sentinelOccurrences).toBe(1);
198
+ expect(
199
+ section.startsWith(`${SILENT_SESSION_REMINDER_SENTINEL} CUSTOM_MAIN`),
200
+ ).toBe(true);
147
201
  });
148
202
  });
@@ -1,4 +1,5 @@
1
1
  import { SubAgentActivity } from '../../domain/entities/LiveSessionActivitySnapshot';
2
+ import { composeOwnerCallFormatGuidance } from '../../domain/usecases/DefaultSilentSessionMessageComposer';
2
3
  import {
3
4
  SilentSessionMessageComposer,
4
5
  SubAgentStallThresholds,
@@ -27,13 +28,16 @@ export class ConfigurableSilentSessionMessageComposer implements SilentSessionMe
27
28
  constructor(
28
29
  private readonly templates: SilentSessionMessageTemplates,
29
30
  private readonly fallback: SilentSessionMessageComposer,
31
+ private readonly ownerCallMarker: string | null = null,
30
32
  ) {}
31
33
 
32
34
  composeMainStalledSection = (mainSilentSeconds: number): string => {
33
35
  if (this.templates.mainStalledMessage === null) {
34
36
  return this.fallback.composeMainStalledSection(mainSilentSeconds);
35
37
  }
36
- return withReminderSentinel(this.templates.mainStalledMessage);
38
+ return withReminderSentinel(
39
+ `${this.templates.mainStalledMessage} ${composeOwnerCallFormatGuidance(this.ownerCallMarker)}`,
40
+ );
37
41
  };
38
42
 
39
43
  composeSubAgentSection = (
@@ -69,7 +73,13 @@ export class ConfigurableSilentSessionMessageComposer implements SilentSessionMe
69
73
  ),
70
74
  );
71
75
  } else if (idleSubAgents.length > 0) {
72
- sections.push(this.composeIdleSection(idleSubAgents, null, null));
76
+ sections.push(
77
+ this.fallback.composeSubAgentSection(idleSubAgents, {
78
+ subAgentSilentThresholdSeconds:
79
+ thresholds.subAgentSilentThresholdSeconds,
80
+ subAgentRunningThresholdSeconds: Number.POSITIVE_INFINITY,
81
+ }),
82
+ );
73
83
  }
74
84
  if (longRunningSubAgents.length > 0 && hasLongRunningTemplate) {
75
85
  sections.push(
@@ -59,6 +59,16 @@ describe('DefaultSilentSessionMessageComposer', () => {
59
59
  );
60
60
  });
61
61
 
62
+ it('requires the owner-call message to be fully self-contained and forbids telling the owner to scroll back', () => {
63
+ const section = composer.composeMainStalledSection(600);
64
+ expect(section).toContain(
65
+ 'Make the owner-call message fully self-contained: the owner MUST understand the whole situation — what happened, what you are asking, and any decision needed — from this single latest owner-call message alone, without reading or scrolling back to earlier messages.',
66
+ );
67
+ expect(section).toContain(
68
+ 'NEVER tell the owner to scroll up, go back, or read previous or above messages; if context is needed, restate it inside the owner-call message itself.',
69
+ );
70
+ });
71
+
62
72
  it('instructs the agent to fire the owner-call when an owner request has been completed or answered', () => {
63
73
  const section = composer.composeMainStalledSection(600);
64
74
  expect(section).toContain(
@@ -112,12 +122,68 @@ describe('DefaultSilentSessionMessageComposer', () => {
112
122
  );
113
123
  expect(section).toContain('sub-process-idle');
114
124
  expect(section).toContain('no output for 6m');
115
- expect(section).toContain('may be stalled');
116
125
  expect(section).toContain('restart, hand off, or replace it');
117
126
  expect(section).toContain('waiting on an external dependency');
118
127
  expect(section).not.toContain('infinite loop');
119
128
  });
120
129
 
130
+ it('presents the system-detected idle duration as the authoritative signal in the idle message', () => {
131
+ const section = composer.composeSubAgentSection(
132
+ [{ label: 'sub-process-idle', silentSeconds: 360, runningSeconds: 60 }],
133
+ THRESHOLDS,
134
+ );
135
+ expect(section).toContain(
136
+ "The system has already detected, from each sub-process's last tool activity, that it has produced no output for about the minutes shown below.",
137
+ );
138
+ expect(section).toContain(
139
+ 'Treat that figure as the authoritative system signal; do NOT spend effort re-deriving whether the sub-process is alive.',
140
+ );
141
+ expect(section).toContain('no output for 6m');
142
+ });
143
+
144
+ it('forbids speculation and dismissal without evidence in the idle message', () => {
145
+ const section = composer.composeSubAgentSection(
146
+ [{ label: 'sub-process-idle', silentSeconds: 360, runningSeconds: 60 }],
147
+ THRESHOLDS,
148
+ );
149
+ expect(section).toContain(
150
+ 'Around five minutes of silence is a real warning of a possible hang',
151
+ );
152
+ expect(section).toContain(
153
+ 'do NOT speak from speculation ("probably still working") and do NOT dismiss it without evidence.',
154
+ );
155
+ });
156
+
157
+ it('requires a concrete cause-check covering recent activity and external-dependency waiting in the idle message', () => {
158
+ const section = composer.composeSubAgentSection(
159
+ [{ label: 'sub-process-idle', silentSeconds: 360, runningSeconds: 60 }],
160
+ THRESHOLDS,
161
+ );
162
+ expect(section).toContain('determine the CAUSE by a concrete check');
163
+ expect(section).toContain(
164
+ 'a very recent push or commit, or output from any nested sub-processes this sub-process itself started',
165
+ );
166
+ expect(section).toContain(
167
+ 'legitimately blocked waiting on an external dependency',
168
+ );
169
+ expect(section).toContain(
170
+ 'confirm it by investigation before concluding — do not assume',
171
+ );
172
+ expect(section).toContain(
173
+ 'if it is legitimately waiting, state that conclusion together with the concrete evidence you found',
174
+ );
175
+ });
176
+
177
+ it('requires logging the investigation result even though owner notification is not required', () => {
178
+ const section = composer.composeSubAgentSection(
179
+ [{ label: 'sub-process-idle', silentSeconds: 360, runningSeconds: 60 }],
180
+ THRESHOLDS,
181
+ );
182
+ expect(section).toContain(
183
+ 'Owner notification is not required, but you MUST output your investigation result in this session so it remains as a log.',
184
+ );
185
+ });
186
+
121
187
  it('emits a distinct long-running message for a sub-agent that has only run too long', () => {
122
188
  const section = composer.composeSubAgentSection(
123
189
  [
@@ -163,7 +229,7 @@ describe('DefaultSilentSessionMessageComposer', () => {
163
229
  THRESHOLDS,
164
230
  );
165
231
  expect(section).toContain('no output for 6m');
166
- expect(section).toContain('may be stalled');
232
+ expect(section).toContain('determine the CAUSE by a concrete check');
167
233
  expect(section).toContain('running for 20m');
168
234
  expect(section).toContain('infinite loop');
169
235
  const sentinelOccurrences =
@@ -20,9 +20,9 @@ const composeIdleSubAgentSection = (
20
20
  )}`,
21
21
  );
22
22
  return [
23
- `${SILENT_SESSION_REMINDER_SENTINEL} The following sub-process(es) have produced no output for several minutes and may be stalled:`,
23
+ `${SILENT_SESSION_REMINDER_SENTINEL} The system has already detected, from each sub-process's last tool activity, that it has produced no output for about the minutes shown below. Treat that figure as the authoritative system signal; do NOT spend effort re-deriving whether the sub-process is alive. Around five minutes of silence is a real warning of a possible hang: do NOT speak from speculation ("probably still working") and do NOT dismiss it without evidence.`,
24
24
  ...lines,
25
- 'Check each one. If it is stuck, 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.',
25
+ 'For each one, determine the CAUSE by a concrete check and report the result. Verify specifically: (a) whether there is genuinely no recent activity anywhere — including a very recent push or commit, or output from any nested sub-processes this sub-process itself started; and (b) whether it is legitimately blocked waiting on an external dependency (a continuous-integration run, an external API, or another process). Waiting on continuous integration is plausible, but confirm it by investigation before concluding — do not assume. Based on the check: if it is genuinely stuck, take action (restart, hand off, or replace it); if it is legitimately waiting, state that conclusion together with the concrete evidence you found. Owner notification is not required, but you MUST output your investigation result in this session so it remains as a log.',
26
26
  ].join('\n');
27
27
  };
28
28
 
@@ -42,14 +42,14 @@ const composeLongRunningSubAgentSection = (
42
42
  ].join('\n');
43
43
  };
44
44
 
45
- const composeOwnerCallFormatGuidance = (
45
+ export const composeOwnerCallFormatGuidance = (
46
46
  ownerCallMarker: string | null,
47
47
  ): string => {
48
48
  const markerInstruction =
49
49
  ownerCallMarker !== null && ownerCallMarker.length > 0
50
50
  ? `Emit the owner-call as the configured owner-call marker tag "${ownerCallMarker}" as a complete matching pair — opening marker, content, then closing marker — on a single line with no newline inside the tag.`
51
51
  : `Emit the owner-call as the configured owner-call marker tag as a complete matching pair — opening marker, content, then closing marker — on a single line with no newline inside the tag.`;
52
- return `${markerInstruction} The content between the markers MUST begin with the 🔴 emoji immediately, with no space after it. The owner's app only surfaces the notification together with its content when the exact, well-formed tag with the leading 🔴 is present; a malformed tag (a broken or missing closing marker, or a missing leading 🔴) results in only a red indicator with no readable content.`;
52
+ return `${markerInstruction} The content between the markers MUST begin with the 🔴 emoji immediately, with no space after it. The owner's app only surfaces the notification together with its content when the exact, well-formed tag with the leading 🔴 is present; a malformed tag (a broken or missing closing marker, or a missing leading 🔴) results in only a red indicator with no readable content. Make the owner-call message fully self-contained: the owner MUST understand the whole situation — what happened, what you are asking, and any decision needed — from this single latest owner-call message alone, without reading or scrolling back to earlier messages. NEVER tell the owner to scroll up, go back, or read previous or above messages; if context is needed, restate it inside the owner-call message itself.`;
53
53
  };
54
54
 
55
55
  const composeMainStalledMessage = (
@@ -1 +1 @@
1
- {"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,EAOtB,MAAM,0DAA0D,CAAC;AAclE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAKrE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,0BAA0B,EAAE,MAAM,CAAC;IACnC,8BAA8B,EAAE,MAAM,CAAC;IACvC,+BAA+B,EAAE,MAAM,CAAC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC,EAAE,MAAM,CAAC;IAC9C,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACpD,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,CAAC;IACrC,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAoCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CAwEd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;;CAQ7C,CAAC"}
1
+ {"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,EAOtB,MAAM,0DAA0D,CAAC;AAclE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAKrE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,0BAA0B,EAAE,MAAM,CAAC;IACnC,8BAA8B,EAAE,MAAM,CAAC;IACvC,+BAA+B,EAAE,MAAM,CAAC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC,EAAE,MAAM,CAAC;IAC9C,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACpD,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,CAAC;IACrC,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAoCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CAyEd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;;CAQ7C,CAAC"}
@@ -10,7 +10,8 @@ export type SilentSessionMessageTemplates = {
10
10
  export declare class ConfigurableSilentSessionMessageComposer implements SilentSessionMessageComposer {
11
11
  private readonly templates;
12
12
  private readonly fallback;
13
- constructor(templates: SilentSessionMessageTemplates, fallback: SilentSessionMessageComposer);
13
+ private readonly ownerCallMarker;
14
+ constructor(templates: SilentSessionMessageTemplates, fallback: SilentSessionMessageComposer, ownerCallMarker?: string | null);
14
15
  composeMainStalledSection: (mainSilentSeconds: number) => string;
15
16
  composeSubAgentSection: (subAgents: SubAgentActivity[], thresholds: SubAgentStallThresholds) => string;
16
17
  private composeIdleSection;
@@ -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,EACL,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,uEAAuE,CAAC;AAQ/E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD,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,GACpB,WAAW,gBAAgB,EAAE,EAC7B,YAAY,uBAAuB,KAClC,MAAM,CA8CP;IAEF,OAAO,CAAC,kBAAkB,CAkBxB;IAEF,OAAO,CAAC,yBAAyB,CAkB/B;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;AAErF,OAAO,EACL,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,uEAAuE,CAAC;AAQ/E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD,CAAC;AAOF,qBAAa,wCAAyC,YAAW,4BAA4B;IAEzF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAFf,SAAS,EAAE,6BAA6B,EACxC,QAAQ,EAAE,4BAA4B,EACtC,eAAe,GAAE,MAAM,GAAG,IAAW;IAGxD,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAO7D;IAEF,sBAAsB,GACpB,WAAW,gBAAgB,EAAE,EAC7B,YAAY,uBAAuB,KAClC,MAAM,CAoDP;IAEF,OAAO,CAAC,kBAAkB,CAkBxB;IAEF,OAAO,CAAC,yBAAyB,CAkB/B;CACH"}
@@ -1,5 +1,6 @@
1
1
  import { SubAgentActivity } from '../entities/LiveSessionActivitySnapshot';
2
2
  import { SilentSessionMessageComposer, SubAgentStallThresholds } from './adapter-interfaces/SilentSessionMessageComposer';
3
+ export declare const composeOwnerCallFormatGuidance: (ownerCallMarker: string | null) => string;
3
4
  export declare class DefaultSilentSessionMessageComposer implements SilentSessionMessageComposer {
4
5
  private readonly ownerCallMarker;
5
6
  constructor(ownerCallMarker?: string | null);
@@ -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,EACL,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,mDAAmD,CAAC;AAiE3D,qBAAa,mCAAoC,YAAW,4BAA4B;IAC1E,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,GAAE,MAAM,GAAG,IAAW;IAElE,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAE7D;IAEF,sBAAsB,GACpB,WAAW,gBAAgB,EAAE,EAC7B,YAAY,uBAAuB,KAClC,MAAM,CAiBP;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,EACL,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,mDAAmD,CAAC;AAwC3D,eAAO,MAAM,8BAA8B,GACzC,iBAAiB,MAAM,GAAG,IAAI,KAC7B,MAMF,CAAC;AAiBF,qBAAa,mCAAoC,YAAW,4BAA4B;IAC1E,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,GAAE,MAAM,GAAG,IAAW;IAElE,yBAAyB,GAAI,mBAAmB,MAAM,KAAG,MAAM,CAE7D;IAEF,sBAAsB,GACpB,WAAW,gBAAgB,EAAE,EAC7B,YAAY,uBAAuB,KAClC,MAAM,CAiBP;CACH"}