oh-my-codex 0.11.12 → 0.11.13

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 (248) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +23 -0
  4. package/README.vi.md +144 -185
  5. package/crates/omx-runtime-core/src/engine.rs +122 -4
  6. package/crates/omx-runtime-core/src/lib.rs +17 -0
  7. package/dist/cli/__tests__/autoresearch.test.js +11 -0
  8. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  9. package/dist/cli/__tests__/cleanup.test.js +117 -4
  10. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  11. package/dist/cli/__tests__/error-handling-warnings.test.js +13 -0
  12. package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
  13. package/dist/cli/__tests__/exec.test.js +6 -0
  14. package/dist/cli/__tests__/exec.test.js.map +1 -1
  15. package/dist/cli/__tests__/index.test.js +94 -1
  16. package/dist/cli/__tests__/index.test.js.map +1 -1
  17. package/dist/cli/__tests__/launch-fallback.test.js +3 -0
  18. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  19. package/dist/cli/__tests__/package-bin-contract.test.js +10 -0
  20. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  21. package/dist/cli/__tests__/packaged-script-resolution.test.js +4 -3
  22. package/dist/cli/__tests__/packaged-script-resolution.test.js.map +1 -1
  23. package/dist/cli/__tests__/resume.test.js +6 -0
  24. package/dist/cli/__tests__/resume.test.js.map +1 -1
  25. package/dist/cli/__tests__/setup-refresh.test.js +29 -12
  26. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  27. package/dist/cli/__tests__/star-prompt.test.js +16 -0
  28. package/dist/cli/__tests__/star-prompt.test.js.map +1 -1
  29. package/dist/cli/__tests__/uninstall.test.js +112 -1
  30. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  31. package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts +2 -0
  32. package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts.map +1 -0
  33. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +30 -0
  34. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -0
  35. package/dist/cli/cleanup.d.ts +2 -0
  36. package/dist/cli/cleanup.d.ts.map +1 -1
  37. package/dist/cli/cleanup.js +26 -1
  38. package/dist/cli/cleanup.js.map +1 -1
  39. package/dist/cli/index.d.ts +7 -0
  40. package/dist/cli/index.d.ts.map +1 -1
  41. package/dist/cli/index.js +161 -50
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/cli/setup.d.ts.map +1 -1
  44. package/dist/cli/setup.js +15 -14
  45. package/dist/cli/setup.js.map +1 -1
  46. package/dist/cli/star-prompt.d.ts.map +1 -1
  47. package/dist/cli/star-prompt.js +1 -0
  48. package/dist/cli/star-prompt.js.map +1 -1
  49. package/dist/cli/team.d.ts.map +1 -1
  50. package/dist/cli/team.js +5 -1
  51. package/dist/cli/team.js.map +1 -1
  52. package/dist/cli/uninstall.d.ts.map +1 -1
  53. package/dist/cli/uninstall.js +26 -0
  54. package/dist/cli/uninstall.js.map +1 -1
  55. package/dist/cli/update.d.ts.map +1 -1
  56. package/dist/cli/update.js +1 -0
  57. package/dist/cli/update.js.map +1 -1
  58. package/dist/config/__tests__/generator-idempotent.test.js +4 -4
  59. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  60. package/dist/config/__tests__/mcp-registry.test.js +13 -16
  61. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  62. package/dist/config/mcp-registry.d.ts +1 -0
  63. package/dist/config/mcp-registry.d.ts.map +1 -1
  64. package/dist/config/mcp-registry.js +4 -4
  65. package/dist/config/mcp-registry.js.map +1 -1
  66. package/dist/config/models.d.ts +1 -0
  67. package/dist/config/models.d.ts.map +1 -1
  68. package/dist/config/models.js +39 -1
  69. package/dist/config/models.js.map +1 -1
  70. package/dist/hooks/__tests__/keyword-detector.test.js +12 -1
  71. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  72. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +499 -17
  73. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +140 -14
  75. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-modules.test.js +5 -0
  77. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts +2 -0
  79. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts.map +1 -0
  80. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +597 -0
  81. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -0
  82. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +15 -1
  83. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +73 -53
  85. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +193 -2
  87. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  88. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +183 -0
  89. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  90. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +255 -97
  91. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
  93. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  94. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +46 -0
  95. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  96. package/dist/hooks/keyword-detector.d.ts +1 -0
  97. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  98. package/dist/hooks/keyword-detector.js +48 -0
  99. package/dist/hooks/keyword-detector.js.map +1 -1
  100. package/dist/hooks/session.d.ts.map +1 -1
  101. package/dist/hooks/session.js +1 -0
  102. package/dist/hooks/session.js.map +1 -1
  103. package/dist/hud/__tests__/state.test.js +70 -1
  104. package/dist/hud/__tests__/state.test.js.map +1 -1
  105. package/dist/hud/state.d.ts.map +1 -1
  106. package/dist/hud/state.js +10 -37
  107. package/dist/hud/state.js.map +1 -1
  108. package/dist/mcp/state-server.d.ts.map +1 -1
  109. package/dist/mcp/state-server.js +5 -0
  110. package/dist/mcp/state-server.js.map +1 -1
  111. package/dist/modes/__tests__/base-session-scope.test.js +46 -0
  112. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  113. package/dist/modes/base.d.ts.map +1 -1
  114. package/dist/modes/base.js +4 -0
  115. package/dist/modes/base.js.map +1 -1
  116. package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts +2 -0
  117. package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts.map +1 -0
  118. package/dist/notifications/__tests__/custom-alias-enablement.test.js +84 -0
  119. package/dist/notifications/__tests__/custom-alias-enablement.test.js.map +1 -0
  120. package/dist/notifications/__tests__/idle-cooldown.test.js +55 -0
  121. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  122. package/dist/notifications/idle-cooldown.d.ts +8 -6
  123. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  124. package/dist/notifications/idle-cooldown.js +53 -22
  125. package/dist/notifications/idle-cooldown.js.map +1 -1
  126. package/dist/notifications/notifier.js +1 -1
  127. package/dist/notifications/notifier.js.map +1 -1
  128. package/dist/notifications/reply-listener.d.ts.map +1 -1
  129. package/dist/notifications/reply-listener.js +1 -0
  130. package/dist/notifications/reply-listener.js.map +1 -1
  131. package/dist/openclaw/config.js +2 -2
  132. package/dist/openclaw/config.js.map +1 -1
  133. package/dist/runtime/bridge.d.ts +1 -0
  134. package/dist/runtime/bridge.d.ts.map +1 -1
  135. package/dist/runtime/bridge.js +2 -6
  136. package/dist/runtime/bridge.js.map +1 -1
  137. package/dist/scripts/notify-fallback-watcher.js +97 -59
  138. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  139. package/dist/scripts/notify-hook/auto-nudge.d.ts +2 -1
  140. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  141. package/dist/scripts/notify-hook/auto-nudge.js +72 -238
  142. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  143. package/dist/scripts/notify-hook/managed-tmux.d.ts +19 -0
  144. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -0
  145. package/dist/scripts/notify-hook/managed-tmux.js +320 -0
  146. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -0
  147. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +22 -0
  148. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -0
  149. package/dist/scripts/notify-hook/ralph-session-resume.js +277 -0
  150. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -0
  151. package/dist/scripts/notify-hook/state-io.d.ts +1 -1
  152. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  153. package/dist/scripts/notify-hook/state-io.js +2 -10
  154. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  155. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  156. package/dist/scripts/notify-hook/team-dispatch.js +60 -59
  157. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  158. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -1
  159. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  160. package/dist/scripts/notify-hook/team-leader-nudge.js +13 -5
  161. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  162. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  163. package/dist/scripts/notify-hook/team-tmux-guard.js +1 -19
  164. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  165. package/dist/scripts/notify-hook/team-worker.js +4 -4
  166. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  167. package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -1
  168. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  169. package/dist/scripts/notify-hook/tmux-injection.js +102 -35
  170. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  171. package/dist/scripts/notify-hook.js +144 -20
  172. package/dist/scripts/notify-hook.js.map +1 -1
  173. package/dist/scripts/tmux-hook-engine.d.ts +1 -0
  174. package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
  175. package/dist/scripts/tmux-hook-engine.js +3 -0
  176. package/dist/scripts/tmux-hook-engine.js.map +1 -1
  177. package/dist/team/__tests__/api-interop.test.js +96 -4
  178. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  179. package/dist/team/__tests__/leader-activity.test.js +107 -2
  180. package/dist/team/__tests__/leader-activity.test.js.map +1 -1
  181. package/dist/team/__tests__/runtime-cli.test.js +32 -0
  182. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  183. package/dist/team/__tests__/runtime.test.js +148 -0
  184. package/dist/team/__tests__/runtime.test.js.map +1 -1
  185. package/dist/team/__tests__/shutdown-fallback.test.js +13 -0
  186. package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
  187. package/dist/team/__tests__/state-root.test.js +11 -1
  188. package/dist/team/__tests__/state-root.test.js.map +1 -1
  189. package/dist/team/__tests__/state.test.js +16 -5
  190. package/dist/team/__tests__/state.test.js.map +1 -1
  191. package/dist/team/__tests__/tmux-session.test.js +460 -2
  192. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  193. package/dist/team/api-interop.d.ts.map +1 -1
  194. package/dist/team/api-interop.js +34 -7
  195. package/dist/team/api-interop.js.map +1 -1
  196. package/dist/team/commit-hygiene.d.ts +60 -0
  197. package/dist/team/commit-hygiene.d.ts.map +1 -0
  198. package/dist/team/commit-hygiene.js +232 -0
  199. package/dist/team/commit-hygiene.js.map +1 -0
  200. package/dist/team/leader-activity.d.ts.map +1 -1
  201. package/dist/team/leader-activity.js +17 -35
  202. package/dist/team/leader-activity.js.map +1 -1
  203. package/dist/team/runtime-cli.d.ts +9 -1
  204. package/dist/team/runtime-cli.d.ts.map +1 -1
  205. package/dist/team/runtime-cli.js +15 -6
  206. package/dist/team/runtime-cli.js.map +1 -1
  207. package/dist/team/runtime.d.ts +7 -2
  208. package/dist/team/runtime.d.ts.map +1 -1
  209. package/dist/team/runtime.js +391 -63
  210. package/dist/team/runtime.js.map +1 -1
  211. package/dist/team/state/dispatch.js +1 -1
  212. package/dist/team/state/dispatch.js.map +1 -1
  213. package/dist/team/state/mailbox.d.ts +1 -0
  214. package/dist/team/state/mailbox.d.ts.map +1 -1
  215. package/dist/team/state/mailbox.js +54 -8
  216. package/dist/team/state/mailbox.js.map +1 -1
  217. package/dist/team/state-root.d.ts +1 -1
  218. package/dist/team/state-root.d.ts.map +1 -1
  219. package/dist/team/state-root.js +8 -3
  220. package/dist/team/state-root.js.map +1 -1
  221. package/dist/team/state.d.ts.map +1 -1
  222. package/dist/team/state.js +66 -3
  223. package/dist/team/state.js.map +1 -1
  224. package/dist/team/tmux-session.d.ts.map +1 -1
  225. package/dist/team/tmux-session.js +69 -27
  226. package/dist/team/tmux-session.js.map +1 -1
  227. package/dist/utils/__tests__/platform-command.test.js +101 -2
  228. package/dist/utils/__tests__/platform-command.test.js.map +1 -1
  229. package/dist/utils/git-layout.d.ts +8 -0
  230. package/dist/utils/git-layout.d.ts.map +1 -0
  231. package/dist/utils/git-layout.js +58 -0
  232. package/dist/utils/git-layout.js.map +1 -0
  233. package/dist/utils/platform-command.d.ts.map +1 -1
  234. package/dist/utils/platform-command.js +32 -1
  235. package/dist/utils/platform-command.js.map +1 -1
  236. package/package.json +6 -6
  237. package/src/scripts/notify-fallback-watcher.ts +96 -58
  238. package/src/scripts/notify-hook/auto-nudge.ts +75 -230
  239. package/src/scripts/notify-hook/managed-tmux.ts +324 -0
  240. package/src/scripts/notify-hook/ralph-session-resume.ts +337 -0
  241. package/src/scripts/notify-hook/state-io.ts +2 -10
  242. package/src/scripts/notify-hook/team-dispatch.ts +70 -54
  243. package/src/scripts/notify-hook/team-leader-nudge.ts +19 -5
  244. package/src/scripts/notify-hook/team-tmux-guard.ts +0 -20
  245. package/src/scripts/notify-hook/team-worker.ts +4 -4
  246. package/src/scripts/notify-hook/tmux-injection.ts +103 -33
  247. package/src/scripts/notify-hook.ts +150 -21
  248. package/src/scripts/tmux-hook-engine.ts +4 -0
@@ -39,6 +39,10 @@ import { isLeaderStale, resolveLeaderStalenessThresholdMs, maybeNudgeTeamLeader
39
39
  import { drainPendingTeamDispatch } from './notify-hook/team-dispatch.js';
40
40
  import { handleTmuxInjection } from './notify-hook/tmux-injection.js';
41
41
  import { maybeAutoNudge, resolveNudgePaneTarget, isDeepInterviewStateActive } from './notify-hook/auto-nudge.js';
42
+ import { isManagedOmxSession } from './notify-hook/managed-tmux.js';
43
+ import { logNotifyHookEvent } from './notify-hook/log.js';
44
+ import { reconcileRalphSessionResume } from './notify-hook/ralph-session-resume.js';
45
+ import { sendPaneInput } from './notify-hook/team-tmux-guard.js';
42
46
  import {
43
47
  buildOperationalContext,
44
48
  deriveAssistantSignalEvents,
@@ -68,6 +72,79 @@ const RALPH_ACTIVE_PROGRESS_PHASES = new Set([
68
72
  'fixing',
69
73
  ]);
70
74
 
75
+ const IDLE_NOTIFICATION_SUMMARY_MAX_LENGTH = 240;
76
+
77
+ function summarizeIdleNotificationMessage(message: unknown): string {
78
+ const source = safeString(message)
79
+ .split('\n')
80
+ .map((line) => line.trim())
81
+ .filter(Boolean);
82
+ const preferred = source.at(-1) || '';
83
+ const normalized = preferred.replace(/\s+/g, ' ').trim();
84
+ if (!normalized) return '';
85
+ return normalized.length > IDLE_NOTIFICATION_SUMMARY_MAX_LENGTH
86
+ ? `${normalized.slice(0, IDLE_NOTIFICATION_SUMMARY_MAX_LENGTH - 1)}…`
87
+ : normalized;
88
+ }
89
+
90
+ function classifyIdleNotificationPhase(message: unknown): 'idle' | 'progress' | 'finished' | 'failed' {
91
+ const lower = safeString(message).toLowerCase();
92
+ if (!lower) return 'idle';
93
+
94
+ if (/(error|failed|exception|invalid|timed out|timeout)/i.test(lower)) {
95
+ return 'failed';
96
+ }
97
+
98
+ if ([
99
+ 'all tests pass',
100
+ 'build succeeded',
101
+ 'completed',
102
+ 'complete',
103
+ 'done',
104
+ 'final summary',
105
+ 'summary',
106
+ ].some((pattern) => lower.includes(pattern))) {
107
+ return 'finished';
108
+ }
109
+
110
+ if ([
111
+ 'verify',
112
+ 'verified',
113
+ 'verification',
114
+ 'review',
115
+ 'reviewed',
116
+ 'diagnostic',
117
+ 'typecheck',
118
+ 'test',
119
+ 'implement',
120
+ 'implemented',
121
+ 'apply patch',
122
+ 'change',
123
+ 'fix',
124
+ 'update',
125
+ 'refactor',
126
+ 'resume',
127
+ 'resumed',
128
+ 'progress',
129
+ 'continue',
130
+ 'continued',
131
+ ].some((pattern) => lower.includes(pattern))) {
132
+ return 'progress';
133
+ }
134
+
135
+ return 'idle';
136
+ }
137
+
138
+ function buildIdleNotificationFingerprint(payload: Record<string, unknown>): string {
139
+ const lastAssistantMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
140
+ const summary = summarizeIdleNotificationMessage(lastAssistantMessage);
141
+ const phase = classifyIdleNotificationPhase(lastAssistantMessage);
142
+ return JSON.stringify({
143
+ phase,
144
+ ...(summary ? { summary } : {}),
145
+ });
146
+ }
147
+
71
148
  async function main() {
72
149
  const rawPayload = process.argv[process.argv.length - 1];
73
150
  if (!rawPayload || rawPayload.startsWith('-')) {
@@ -83,6 +160,9 @@ async function main() {
83
160
 
84
161
  const cwd = payload.cwd || payload['cwd'] || process.cwd();
85
162
  const payloadSessionId = safeString(payload.session_id || payload['session-id'] || '');
163
+ const payloadThreadId = safeString(payload['thread-id'] || payload.thread_id || '');
164
+ const inputMessages = normalizeInputMessages(payload);
165
+ const latestUserInput = safeString(inputMessages.length > 0 ? inputMessages[inputMessages.length - 1] : '');
86
166
 
87
167
  // Team worker detection via environment variable
88
168
  const teamWorkerEnv = process.env.OMX_TEAM_WORKER; // e.g., "fix-ts/worker-1"
@@ -94,6 +174,7 @@ async function main() {
94
174
  : join(cwd, '.omx', 'state');
95
175
  const logsDir = join(cwd, '.omx', 'logs');
96
176
  const omxDir = join(cwd, '.omx');
177
+ let currentOmxSessionId = '';
97
178
 
98
179
  // Ensure directories exist
99
180
  await mkdir(logsDir, { recursive: true }).catch(() => {});
@@ -158,11 +239,45 @@ async function main() {
158
239
  const logFile = join(logsDir, `turns-${new Date().toISOString().split('T')[0]}.jsonl`);
159
240
  await appendFile(logFile, JSON.stringify(logEntry) + '\n').catch(() => {});
160
241
 
242
+ // Reconcile Ralph ownership for same-Codex-session continuation before
243
+ // lifecycle counters or injection read the active scope.
244
+ if (!isTeamWorker) {
245
+ try {
246
+ const resumeResult = await reconcileRalphSessionResume({
247
+ stateDir,
248
+ payloadSessionId,
249
+ payloadThreadId,
250
+ });
251
+ currentOmxSessionId = resumeResult.currentOmxSessionId;
252
+ if (resumeResult.resumed || resumeResult.updatedCurrentOwner) {
253
+ await logNotifyHookEvent(logsDir, {
254
+ timestamp: new Date().toISOString(),
255
+ type: 'ralph_session_resume',
256
+ reason: resumeResult.reason,
257
+ current_omx_session_id: resumeResult.currentOmxSessionId || null,
258
+ payload_codex_session_id: payloadSessionId || null,
259
+ source_path: resumeResult.sourcePath || null,
260
+ target_path: resumeResult.targetPath || null,
261
+ owner_updated: resumeResult.updatedCurrentOwner,
262
+ resumed: resumeResult.resumed,
263
+ });
264
+ }
265
+ } catch (error) {
266
+ await logNotifyHookEvent(logsDir, {
267
+ timestamp: new Date().toISOString(),
268
+ level: 'warn',
269
+ type: 'ralph_session_resume_failure',
270
+ payload_codex_session_id: payloadSessionId || null,
271
+ error: error instanceof Error ? error.message : String(error),
272
+ });
273
+ }
274
+ }
275
+
161
276
  // 2. Update active mode state (increment iteration)
162
277
  // GUARD: Skip when running inside a team worker to prevent state corruption
163
278
  if (!isTeamWorker) {
164
279
  try {
165
- const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir, payloadSessionId);
280
+ const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir);
166
281
  for (const scopedDir of scopedDirs) {
167
282
  const stateFiles = await readdir(scopedDir).catch(() => []);
168
283
  for (const f of stateFiles) {
@@ -298,7 +413,9 @@ async function main() {
298
413
  if (existsSync(hudStatePath)) {
299
414
  hudState = JSON.parse(await readFile(hudStatePath, 'utf-8'));
300
415
  }
301
- hudState.last_turn_at = new Date().toISOString();
416
+ const nowIso = new Date().toISOString();
417
+ hudState.last_turn_at = nowIso;
418
+ (hudState as any).last_progress_at = nowIso;
302
419
  hudState.turn_count = (hudState.turn_count || 0) + 1;
303
420
  (hudState as any).last_agent_output = (payload['last-assistant-message'] || payload.last_assistant_message || '')
304
421
  .slice(0, 100);
@@ -323,14 +440,12 @@ async function main() {
323
440
  // 4.45. Skill activation tracking: update skill-active-state.json before any nudge logic.
324
441
  try {
325
442
  const { recordSkillActivation } = await import('../hooks/keyword-detector.js');
326
- const inputMessages = normalizeInputMessages(payload);
327
- const latestUserInput = safeString(inputMessages.length > 0 ? inputMessages[inputMessages.length - 1] : '');
328
443
  if (latestUserInput) {
329
444
  await recordSkillActivation({
330
445
  stateDir,
331
446
  text: latestUserInput,
332
447
  sessionId: payloadSessionId,
333
- threadId: safeString(payload['thread-id'] || payload.thread_id || ''),
448
+ threadId: payloadThreadId,
334
449
  turnId: safeString(payload['turn-id'] || payload.turn_id || ''),
335
450
  });
336
451
  }
@@ -443,19 +558,20 @@ async function main() {
443
558
  const { notifyLifecycle } = await import('../notifications/index.js');
444
559
  const { shouldSendIdleNotification, recordIdleNotificationSent } = await import('../notifications/idle-cooldown.js');
445
560
  const sessionJsonPath = join(stateDir, 'session.json');
561
+ const idleFingerprint = buildIdleNotificationFingerprint(payload);
446
562
  let notifySessionId = '';
447
563
  try {
448
564
  const sessionData = JSON.parse(await readFile(sessionJsonPath, 'utf-8'));
449
565
  notifySessionId = safeString(sessionData && sessionData.session_id ? sessionData.session_id : '');
450
566
  } catch { /* no session file */ }
451
567
 
452
- if (notifySessionId && shouldSendIdleNotification(stateDir, notifySessionId)) {
568
+ if (notifySessionId && shouldSendIdleNotification(stateDir, notifySessionId, idleFingerprint)) {
453
569
  const idleResult = await notifyLifecycle('session-idle', {
454
570
  sessionId: notifySessionId,
455
571
  projectPath: cwd,
456
572
  });
457
573
  if (idleResult && idleResult.anySuccess) {
458
- recordIdleNotificationSent(stateDir, notifySessionId);
574
+ recordIdleNotificationSent(stateDir, notifySessionId, idleFingerprint);
459
575
  }
460
576
  try {
461
577
  const { buildNativeHookEvent } = await import('../hooks/extensibility/events.js');
@@ -506,7 +622,7 @@ async function main() {
506
622
  payload,
507
623
  stateDir,
508
624
  logsDir,
509
- sessionId: payloadSessionId,
625
+ sessionId: currentOmxSessionId || payloadSessionId,
510
626
  turnId: safeString(payload['turn-id'] || payload.turn_id || ''),
511
627
  });
512
628
  } catch (err) {
@@ -531,23 +647,36 @@ async function main() {
531
647
  const { processCodeSimplifier } = await import('../hooks/code-simplifier/index.js');
532
648
  const csResult = processCodeSimplifier(cwd, stateDir);
533
649
  if (csResult.triggered) {
534
- const csPaneId = await resolveNudgePaneTarget(stateDir);
535
- if (csPaneId) {
536
- const csText = `${csResult.message} ${DEFAULT_MARKER}`;
537
- const { runProcess } = await import('./notify-hook/process-runner.js');
538
- await runProcess('tmux', ['send-keys', '-t', csPaneId, '-l', csText], 3000);
539
- await new Promise(r => setTimeout(r, 100));
540
- await runProcess('tmux', ['send-keys', '-t', csPaneId, 'C-m'], 3000);
541
- await new Promise(r => setTimeout(r, 100));
542
- await runProcess('tmux', ['send-keys', '-t', csPaneId, 'C-m'], 3000);
543
-
650
+ const managedSession = await isManagedOmxSession(cwd, payload, { allowTeamWorker: false });
651
+ if (!managedSession) {
544
652
  const { logTmuxHookEvent } = await import('./notify-hook/log.js');
545
653
  await logTmuxHookEvent(logsDir, {
546
654
  timestamp: new Date().toISOString(),
547
- type: 'code_simplifier_triggered',
548
- pane_id: csPaneId,
549
- file_count: csResult.message.split('\n').filter(l => l.trimStart().startsWith('- ')).length,
655
+ type: 'code_simplifier_skipped',
656
+ reason: 'unmanaged_session',
550
657
  });
658
+ } else {
659
+ const csPaneId = await resolveNudgePaneTarget(stateDir, cwd, payload);
660
+ if (csPaneId) {
661
+ const csText = `${csResult.message} ${DEFAULT_MARKER}`;
662
+ const sendResult = await sendPaneInput({
663
+ paneTarget: csPaneId,
664
+ prompt: csText,
665
+ submitKeyPresses: 2,
666
+ submitDelayMs: 100,
667
+ });
668
+ if (!sendResult.ok) {
669
+ throw new Error(sendResult.error || sendResult.reason || 'send_failed');
670
+ }
671
+
672
+ const { logTmuxHookEvent } = await import('./notify-hook/log.js');
673
+ await logTmuxHookEvent(logsDir, {
674
+ timestamp: new Date().toISOString(),
675
+ type: 'code_simplifier_triggered',
676
+ pane_id: csPaneId,
677
+ file_count: csResult.message.split('\n').filter(l => l.trimStart().startsWith('- ')).length,
678
+ });
679
+ }
551
680
  }
552
681
  }
553
682
  } catch {
@@ -80,6 +80,10 @@ export function normalizeTmuxHookConfig(raw: any): any {
80
80
  };
81
81
  }
82
82
 
83
+ export function tmuxHookExplicitlyDisablesInjection(raw: any): boolean {
84
+ return Boolean(raw && typeof raw === 'object' && raw.enabled === false);
85
+ }
86
+
83
87
  export function pickActiveMode(activeModes: any, allowedModes: any): string | null {
84
88
  const activeSet = new Set((activeModes || []).filter((mode: any) => typeof mode === 'string'));
85
89
  for (const mode of allowedModes || []) {