oh-my-codex 0.15.2 → 0.15.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 (166) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/dist/agents/__tests__/native-config.test.js +33 -0
  4. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  5. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -1
  6. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
  7. package/dist/cli/__tests__/doctor-context-window-warning.test.d.ts +2 -0
  8. package/dist/cli/__tests__/doctor-context-window-warning.test.d.ts.map +1 -0
  9. package/dist/cli/__tests__/doctor-context-window-warning.test.js +122 -0
  10. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -0
  11. package/dist/cli/__tests__/doctor-warning-copy.test.js +2 -2
  12. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  13. package/dist/cli/__tests__/exec.test.js +1 -0
  14. package/dist/cli/__tests__/exec.test.js.map +1 -1
  15. package/dist/cli/__tests__/explore.test.js +40 -17
  16. package/dist/cli/__tests__/explore.test.js.map +1 -1
  17. package/dist/cli/__tests__/index.test.js +141 -8
  18. package/dist/cli/__tests__/index.test.js.map +1 -1
  19. package/dist/cli/__tests__/mcp-serve.test.js +27 -1
  20. package/dist/cli/__tests__/mcp-serve.test.js.map +1 -1
  21. package/dist/cli/__tests__/ralph.test.js +59 -1
  22. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  23. package/dist/cli/__tests__/setup-scope.test.js +2 -1
  24. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  25. package/dist/cli/__tests__/team.test.js +55 -10
  26. package/dist/cli/__tests__/team.test.js.map +1 -1
  27. package/dist/cli/doctor.d.ts.map +1 -1
  28. package/dist/cli/doctor.js +46 -3
  29. package/dist/cli/doctor.js.map +1 -1
  30. package/dist/cli/index.d.ts +16 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +126 -15
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/mcp-serve.d.ts +1 -0
  35. package/dist/cli/mcp-serve.d.ts.map +1 -1
  36. package/dist/cli/mcp-serve.js +8 -0
  37. package/dist/cli/mcp-serve.js.map +1 -1
  38. package/dist/cli/ralph.d.ts +2 -0
  39. package/dist/cli/ralph.d.ts.map +1 -1
  40. package/dist/cli/ralph.js +17 -1
  41. package/dist/cli/ralph.js.map +1 -1
  42. package/dist/cli/team.d.ts +4 -0
  43. package/dist/cli/team.d.ts.map +1 -1
  44. package/dist/cli/team.js +47 -22
  45. package/dist/cli/team.js.map +1 -1
  46. package/dist/config/__tests__/generator-idempotent.test.js +27 -5
  47. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  48. package/dist/config/generator.d.ts +11 -2
  49. package/dist/config/generator.d.ts.map +1 -1
  50. package/dist/config/generator.js +114 -58
  51. package/dist/config/generator.js.map +1 -1
  52. package/dist/hooks/__tests__/agents-overlay.test.js +59 -0
  53. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  54. package/dist/hooks/__tests__/anti-slop-workflow.test.js +109 -18
  55. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  56. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  57. package/dist/hooks/agents-overlay.js +21 -0
  58. package/dist/hooks/agents-overlay.js.map +1 -1
  59. package/dist/hud/__tests__/index.test.js +30 -14
  60. package/dist/hud/__tests__/index.test.js.map +1 -1
  61. package/dist/openclaw/__tests__/dispatcher.test.js +1 -1
  62. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  63. package/dist/pipeline/__tests__/stages.test.js +398 -14
  64. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  65. package/dist/pipeline/stages/team-exec.d.ts +8 -4
  66. package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
  67. package/dist/pipeline/stages/team-exec.js +198 -13
  68. package/dist/pipeline/stages/team-exec.js.map +1 -1
  69. package/dist/planning/__tests__/artifacts.test.js +246 -1
  70. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  71. package/dist/planning/artifact-names.d.ts +13 -0
  72. package/dist/planning/artifact-names.d.ts.map +1 -0
  73. package/dist/planning/artifact-names.js +108 -0
  74. package/dist/planning/artifact-names.js.map +1 -0
  75. package/dist/planning/artifacts.d.ts +22 -1
  76. package/dist/planning/artifacts.d.ts.map +1 -1
  77. package/dist/planning/artifacts.js +165 -50
  78. package/dist/planning/artifacts.js.map +1 -1
  79. package/dist/ralph/__tests__/persistence.test.js +21 -1
  80. package/dist/ralph/__tests__/persistence.test.js.map +1 -1
  81. package/dist/ralph/persistence.d.ts.map +1 -1
  82. package/dist/ralph/persistence.js +6 -4
  83. package/dist/ralph/persistence.js.map +1 -1
  84. package/dist/scripts/__tests__/codex-native-hook.test.js +352 -2
  85. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  86. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  87. package/dist/scripts/codex-native-hook.js +85 -6
  88. package/dist/scripts/codex-native-hook.js.map +1 -1
  89. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  90. package/dist/scripts/codex-native-pre-post.js +123 -0
  91. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  92. package/dist/scripts/notify-hook/team-worker-posttooluse.js +1 -1
  93. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  94. package/dist/scripts/notify-hook.js +1 -1
  95. package/dist/scripts/notify-hook.js.map +1 -1
  96. package/dist/scripts/sync-plugin-mirror.d.ts +1 -0
  97. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  98. package/dist/scripts/sync-plugin-mirror.js +8 -2
  99. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  100. package/dist/state/__tests__/skill-active.test.js +41 -0
  101. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  102. package/dist/team/__tests__/api-interop.test.js +220 -0
  103. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  104. package/dist/team/__tests__/model-contract.test.js +40 -9
  105. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  106. package/dist/team/__tests__/repo-aware-decomposition.test.js +41 -0
  107. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -1
  108. package/dist/team/__tests__/runtime-cli.test.js +24 -0
  109. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  110. package/dist/team/__tests__/runtime.test.js +446 -67
  111. package/dist/team/__tests__/runtime.test.js.map +1 -1
  112. package/dist/team/__tests__/state.test.js +13 -0
  113. package/dist/team/__tests__/state.test.js.map +1 -1
  114. package/dist/team/__tests__/team-identity.test.d.ts +2 -0
  115. package/dist/team/__tests__/team-identity.test.d.ts.map +1 -0
  116. package/dist/team/__tests__/team-identity.test.js +166 -0
  117. package/dist/team/__tests__/team-identity.test.js.map +1 -0
  118. package/dist/team/__tests__/tmux-session.test.js +55 -1
  119. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  120. package/dist/team/__tests__/worker-bootstrap.test.js +12 -0
  121. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  122. package/dist/team/api-interop.d.ts +1 -0
  123. package/dist/team/api-interop.d.ts.map +1 -1
  124. package/dist/team/api-interop.js +159 -129
  125. package/dist/team/api-interop.js.map +1 -1
  126. package/dist/team/delivery-log.d.ts +1 -1
  127. package/dist/team/delivery-log.d.ts.map +1 -1
  128. package/dist/team/delivery-log.js.map +1 -1
  129. package/dist/team/repo-aware-decomposition.d.ts +3 -0
  130. package/dist/team/repo-aware-decomposition.d.ts.map +1 -1
  131. package/dist/team/repo-aware-decomposition.js +2 -0
  132. package/dist/team/repo-aware-decomposition.js.map +1 -1
  133. package/dist/team/runtime-cli.d.ts +32 -2
  134. package/dist/team/runtime-cli.d.ts.map +1 -1
  135. package/dist/team/runtime-cli.js +78 -26
  136. package/dist/team/runtime-cli.js.map +1 -1
  137. package/dist/team/runtime.d.ts +1 -1
  138. package/dist/team/runtime.d.ts.map +1 -1
  139. package/dist/team/runtime.js +338 -35
  140. package/dist/team/runtime.js.map +1 -1
  141. package/dist/team/state.d.ts +9 -0
  142. package/dist/team/state.d.ts.map +1 -1
  143. package/dist/team/state.js +21 -0
  144. package/dist/team/state.js.map +1 -1
  145. package/dist/team/team-identity.d.ts +26 -0
  146. package/dist/team/team-identity.d.ts.map +1 -0
  147. package/dist/team/team-identity.js +169 -0
  148. package/dist/team/team-identity.js.map +1 -0
  149. package/dist/team/tmux-session.d.ts +18 -0
  150. package/dist/team/tmux-session.d.ts.map +1 -1
  151. package/dist/team/tmux-session.js +61 -1
  152. package/dist/team/tmux-session.js.map +1 -1
  153. package/dist/team/worker-bootstrap.d.ts +2 -0
  154. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  155. package/dist/team/worker-bootstrap.js +10 -1
  156. package/dist/team/worker-bootstrap.js.map +1 -1
  157. package/package.json +1 -1
  158. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  159. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +30 -5
  160. package/skills/ai-slop-cleaner/SKILL.md +30 -5
  161. package/src/scripts/__tests__/codex-native-hook.test.ts +398 -2
  162. package/src/scripts/codex-native-hook.ts +115 -5
  163. package/src/scripts/codex-native-pre-post.ts +121 -0
  164. package/src/scripts/notify-hook/team-worker-posttooluse.ts +1 -1
  165. package/src/scripts/notify-hook.ts +1 -1
  166. package/src/scripts/sync-plugin-mirror.ts +11 -2
@@ -0,0 +1,166 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtemp, rm, writeFile } from 'fs/promises';
4
+ import { join } from 'path';
5
+ import { tmpdir } from 'os';
6
+ import { TEAM_NAME_SAFE_PATTERN } from '../contracts.js';
7
+ import { buildInternalTeamName, resolveTeamIdentityScope, resolveTeamNameForCurrentContext, TeamLookupAmbiguityError } from '../team-identity.js';
8
+ import { initTeamState } from '../state.js';
9
+ const longDisplay = 'this-is-a-very-long-team-display-name-that-would-overflow';
10
+ async function writePhase(cwd, teamName, currentPhase, updatedAt) {
11
+ await writeFile(join(cwd, '.omx', 'state', 'team', teamName, 'phase.json'), JSON.stringify({
12
+ current_phase: currentPhase,
13
+ max_fix_attempts: 3,
14
+ current_fix_attempt: 0,
15
+ transitions: [],
16
+ updated_at: updatedAt,
17
+ }, null, 2));
18
+ }
19
+ describe('team identity', () => {
20
+ it('builds stable valid internal names for same display and distinct sessions', () => {
21
+ const a = buildInternalTeamName(longDisplay, { sessionId: 'session-a', paneId: '', tmuxTarget: '', runId: '', source: 'env-session' });
22
+ const b = buildInternalTeamName(longDisplay, { sessionId: 'session-b', paneId: '', tmuxTarget: '', runId: '', source: 'env-session' });
23
+ const a2 = buildInternalTeamName(longDisplay, { sessionId: 'session-a', paneId: '', tmuxTarget: '', runId: '', source: 'env-session' });
24
+ const runA = buildInternalTeamName('demo', { sessionId: '', paneId: '', tmuxTarget: '', runId: 'run-a', source: 'run-id' });
25
+ const runB = buildInternalTeamName('demo', { sessionId: '', paneId: '', tmuxTarget: '', runId: 'run-b', source: 'run-id' });
26
+ assert.notEqual(a, b);
27
+ assert.notEqual(runA, runB);
28
+ assert.equal(a, a2);
29
+ assert.equal(a.length <= 30, true);
30
+ assert.match(a, TEAM_NAME_SAFE_PATTERN);
31
+ });
32
+ it('does not use cwd session.json as the identity source when env is absent', () => {
33
+ const scope = resolveTeamIdentityScope({ TMUX: '/tmp/tmux,1,0', TMUX_PANE: '%42' });
34
+ assert.equal(scope.source, 'tmux-pane');
35
+ assert.equal(scope.paneId, '%42');
36
+ });
37
+ it('resolves display names from OMX_TEAM_STATE_ROOT when cwd has no local team state', async () => {
38
+ const leaderCwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-leader-'));
39
+ const workerCwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-worker-'));
40
+ try {
41
+ await initTeamState('shared-demo-aaaaaaaa', 'task', 'executor', 1, leaderCwd, undefined, { OMX_SESSION_ID: 'session-shared' }, {
42
+ display_name: 'shared-demo', requested_name: 'shared-demo', identity_source: 'env-session',
43
+ });
44
+ assert.equal(resolveTeamNameForCurrentContext('shared-demo', workerCwd, {
45
+ OMX_SESSION_ID: 'session-shared',
46
+ OMX_TEAM_STATE_ROOT: join(leaderCwd, '.omx', 'state'),
47
+ }), 'shared-demo-aaaaaaaa');
48
+ }
49
+ finally {
50
+ await rm(leaderCwd, { recursive: true, force: true });
51
+ await rm(workerCwd, { recursive: true, force: true });
52
+ }
53
+ });
54
+ it('prefers active display-name candidates over retained terminal states', async () => {
55
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-active-'));
56
+ try {
57
+ await initTeamState('demo-active', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-active' }, {
58
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
59
+ });
60
+ await initTeamState('demo-terminal', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-terminal' }, {
61
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
62
+ });
63
+ await writePhase(cwd, 'demo-terminal', 'complete', '2026-01-01T00:00:00.000Z');
64
+ assert.equal(resolveTeamNameForCurrentContext('demo', cwd, {}), 'demo-active');
65
+ }
66
+ finally {
67
+ await rm(cwd, { recursive: true, force: true });
68
+ }
69
+ });
70
+ it('prefers active display-name candidates over an exact retained terminal directory', async () => {
71
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-exact-terminal-'));
72
+ try {
73
+ await initTeamState('demo', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-terminal' }, {
74
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
75
+ });
76
+ await initTeamState('demo-aaaaaaaa', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-active' }, {
77
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
78
+ });
79
+ await writePhase(cwd, 'demo', 'complete', '2026-01-01T00:00:00.000Z');
80
+ assert.equal(resolveTeamNameForCurrentContext('demo', cwd, {}), 'demo-aaaaaaaa');
81
+ }
82
+ finally {
83
+ await rm(cwd, { recursive: true, force: true });
84
+ }
85
+ });
86
+ it('uses current leader identity to break active display-name ties', async () => {
87
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-current-'));
88
+ try {
89
+ await initTeamState('demo-aaaaaaaa', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-a' }, {
90
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
91
+ });
92
+ await initTeamState('demo-bbbbbbbb', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-b' }, {
93
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
94
+ });
95
+ assert.equal(resolveTeamNameForCurrentContext('demo', cwd, { OMX_SESSION_ID: 'session-b' }), 'demo-bbbbbbbb');
96
+ }
97
+ finally {
98
+ await rm(cwd, { recursive: true, force: true });
99
+ }
100
+ });
101
+ it('uses current leader identity before latest retained terminal state', async () => {
102
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-terminal-current-'));
103
+ try {
104
+ await initTeamState('demo-old-current', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-current' }, {
105
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
106
+ });
107
+ await initTeamState('demo-new-other', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-other' }, {
108
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
109
+ });
110
+ await writePhase(cwd, 'demo-old-current', 'failed', '2026-01-01T00:00:00.000Z');
111
+ await writePhase(cwd, 'demo-new-other', 'complete', '2026-01-02T00:00:00.000Z');
112
+ assert.equal(resolveTeamNameForCurrentContext('demo', cwd, { OMX_SESSION_ID: 'session-current' }), 'demo-old-current');
113
+ }
114
+ finally {
115
+ await rm(cwd, { recursive: true, force: true });
116
+ }
117
+ });
118
+ it('resolves the latest retained terminal display-name state only when unambiguous', async () => {
119
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-latest-terminal-'));
120
+ try {
121
+ await initTeamState('demo-old', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-old' }, {
122
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
123
+ });
124
+ await initTeamState('demo-new', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-new' }, {
125
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
126
+ });
127
+ await writePhase(cwd, 'demo-old', 'failed', '2026-01-01T00:00:00.000Z');
128
+ await writePhase(cwd, 'demo-new', 'complete', '2026-01-03T00:00:00.000Z');
129
+ assert.equal(resolveTeamNameForCurrentContext('demo', cwd, {}), 'demo-new');
130
+ await writePhase(cwd, 'demo-old', 'failed', '2026-01-03T00:00:00.000Z');
131
+ assert.throws(() => resolveTeamNameForCurrentContext('demo', cwd, {}), TeamLookupAmbiguityError);
132
+ }
133
+ finally {
134
+ await rm(cwd, { recursive: true, force: true });
135
+ }
136
+ });
137
+ it('sanitizes unsafe lookup input instead of returning raw path-like names', async () => {
138
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-unsafe-'));
139
+ try {
140
+ assert.equal(resolveTeamNameForCurrentContext('../../victim', cwd, {}), 'victim');
141
+ assert.equal(resolveTeamNameForCurrentContext('Demo Team', cwd, {}), 'demo-team');
142
+ assert.throws(() => resolveTeamNameForCurrentContext('---', cwd, {}), /invalid_team_name/);
143
+ }
144
+ finally {
145
+ await rm(cwd, { recursive: true, force: true });
146
+ }
147
+ });
148
+ it('resolves display names to the current session candidate and fails closed on ambiguity', async () => {
149
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-team-identity-'));
150
+ try {
151
+ await initTeamState('demo-aaaaaaaa', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-a' }, {
152
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
153
+ });
154
+ await initTeamState('demo-bbbbbbbb', 'task', 'executor', 1, cwd, undefined, { OMX_SESSION_ID: 'session-b' }, {
155
+ display_name: 'demo', requested_name: 'demo', identity_source: 'env-session',
156
+ });
157
+ assert.equal(resolveTeamNameForCurrentContext('demo', cwd, { OMX_SESSION_ID: 'session-a' }), 'demo-aaaaaaaa');
158
+ assert.equal(resolveTeamNameForCurrentContext('demo-bbbbbbbb', cwd, {}), 'demo-bbbbbbbb');
159
+ assert.throws(() => resolveTeamNameForCurrentContext('demo', cwd, {}), TeamLookupAmbiguityError);
160
+ }
161
+ finally {
162
+ await rm(cwd, { recursive: true, force: true });
163
+ }
164
+ });
165
+ });
166
+ //# sourceMappingURL=team-identity.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-identity.test.js","sourceRoot":"","sources":["../../../src/team/__tests__/team-identity.test.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ import { PassThrough } from 'node:stream';
6
6
  import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises';
7
7
  import { join } from 'path';
8
8
  import { tmpdir } from 'os';
9
- import { buildClientAttachedReconcileHookName, assertTeamWorkerCliBinaryAvailable, buildWorkerProcessLaunchSpec, buildReconcileHudResizeArgs, buildRegisterClientAttachedReconcileArgs, buildRegisterResizeHookArgs, buildResizeHookName, buildResizeHookTarget, buildScheduleDelayedHudResizeArgs, buildUnregisterClientAttachedReconcileArgs, buildUnregisterResizeHookArgs, buildWorkerStartupCommand, buildHudPaneTarget, chooseTeamLeaderPaneId, createTeamSession, enableMouseScrolling, isMsysOrGitBash, isNativeWindows, isTmuxAvailable, restoreStandaloneHudPane, translatePathForMsys, isWsl2, isWorkerAlive, killWorker, killWorkerByPaneId, teardownWorkerPanes, listTeamSessions, resolveTeamWorkerCli, resolveTeamWorkerLaunchMode, resolveWorkerCliForSend, resolveTeamWorkerCliPlan, buildWorkerSubmitPlan, sanitizeTeamName, shouldAttemptAdaptiveRetry, sendToWorker, sendToWorkerStdin, sleepFractionalSeconds, translateWorkerLaunchArgsForCli, waitForWorkerReady, waitForWorkerReadyAsync, paneIsBootstrapping, dismissTrustPromptIfPresent, mitigateCopyModeUnderlineArtifacts, } from '../tmux-session.js';
9
+ import { buildClientAttachedReconcileHookName, assertTeamWorkerCliBinaryAvailable, buildWorkerProcessLaunchSpec, buildReconcileHudResizeArgs, buildRegisterClientAttachedReconcileArgs, buildRegisterResizeHookArgs, buildResizeHookName, buildResizeHookTarget, buildScheduleDelayedHudResizeArgs, buildUnregisterClientAttachedReconcileArgs, buildUnregisterResizeHookArgs, buildWorkerStartupCommand, buildHudPaneTarget, chooseTeamLeaderPaneId, createTeamSession, enableMouseScrolling, isMsysOrGitBash, isNativeWindows, isTmuxAvailable, restoreStandaloneHudPane, translatePathForMsys, isWsl2, isWorkerAlive, killWorker, killWorkerByPaneId, teardownWorkerPanes, listTeamSessions, resolveTeamWorkerCli, resolveTeamWorkerLaunchMode, resolveWorkerCliForSend, resolveTeamWorkerCliPlan, buildWorkerSubmitPlan, sanitizeTeamName, shouldAttemptAdaptiveRetry, sendToWorker, sendToWorkerStdin, sleepFractionalSeconds, translateWorkerLaunchArgsForCli, waitForWorkerReady, waitForWorkerReadyAsync, paneIsBootstrapping, classifyWorkerStartupInjectSafety, checkWorkerStartupInjectSafety, dismissTrustPromptIfPresent, evaluateStartupDirectTriggerSafetyCapture, mitigateCopyModeUnderlineArtifacts, } from '../tmux-session.js';
10
10
  import { HUD_RESIZE_RECONCILE_DELAY_SECONDS, HUD_TMUX_TEAM_HEIGHT_LINES } from '../../hud/constants.js';
11
11
  import * as tmuxSessionModule from '../tmux-session.js';
12
12
  import { OMX_ENTRY_PATH_ENV, OMX_STARTUP_CWD_ENV } from '../../utils/paths.js';
@@ -261,6 +261,29 @@ describe('HUD resize hook command builders', () => {
261
261
  }
262
262
  });
263
263
  });
264
+ describe('evaluateStartupDirectTriggerSafetyCapture', () => {
265
+ it('allows startup direct triggers on a ready prompt or Codex viewport', () => {
266
+ assert.deepEqual(evaluateStartupDirectTriggerSafetyCapture(READY_HELPER_CAPTURE, 'codex'), {
267
+ safe: true,
268
+ reason: 'ready_prompt',
269
+ });
270
+ assert.deepEqual(evaluateStartupDirectTriggerSafetyCapture(VIEWPORT_WITHOUT_VISIBLE_PROMPT_CAPTURE, 'codex'), {
271
+ safe: true,
272
+ reason: 'codex_viewport',
273
+ });
274
+ });
275
+ it('blocks startup direct triggers through trust and Claude bypass prompts', () => {
276
+ assert.deepEqual(evaluateStartupDirectTriggerSafetyCapture(`Do you trust the contents of this directory?
277
+ Press enter to continue`, 'codex'), {
278
+ safe: false,
279
+ reason: 'trust_prompt',
280
+ });
281
+ assert.deepEqual(evaluateStartupDirectTriggerSafetyCapture(CLAUDE_BYPASS_PROMPT_CAPTURE, 'claude'), {
282
+ safe: false,
283
+ reason: 'claude_bypass_prompt',
284
+ });
285
+ });
286
+ });
264
287
  describe('sendToWorker validation', () => {
265
288
  it('rejects text over 200 chars', async () => {
266
289
  await assert.rejects(sendToWorker('omx-team-x', 1, 'a'.repeat(200)), /< 200/i);
@@ -458,6 +481,37 @@ esac
458
481
  });
459
482
  });
460
483
  });
484
+ describe('startup direct trigger safety', () => {
485
+ it('classifies ready panes as safe and blocks trust, bypass, bootstrapping, and active-task captures', () => {
486
+ assert.equal(classifyWorkerStartupInjectSafety(READY_HELPER_CAPTURE), 'safe');
487
+ assert.equal(classifyWorkerStartupInjectSafety('Do you trust the contents of this directory?\nPress enter to continue'), 'trust_prompt');
488
+ assert.equal(classifyWorkerStartupInjectSafety(CLAUDE_BYPASS_PROMPT_CAPTURE), 'claude_bypass_prompt');
489
+ assert.equal(classifyWorkerStartupInjectSafety('OpenAI Codex\nmodel: loading'), 'bootstrapping');
490
+ assert.equal(classifyWorkerStartupInjectSafety('OpenAI Codex\nmodel: test\n• Running tests (esc to interrupt)'), 'active_task');
491
+ });
492
+ it('checks visible pane first and refuses direct injection through a trust prompt', async () => {
493
+ await withMockTmuxFixture('omx-tmux-startup-direct-trust-', (logPath) => `#!/bin/sh
494
+ set -eu
495
+ printf '%s\n' "$*" >> "${logPath}"
496
+ case "$1" in
497
+ capture-pane)
498
+ cat <<'EOF'
499
+ Do you trust the contents of this directory?
500
+ Press enter to continue
501
+ EOF
502
+ exit 0
503
+ ;;
504
+ *)
505
+ exit 0
506
+ ;;
507
+ esac
508
+ `, async ({ logPath }) => {
509
+ assert.deepEqual(await checkWorkerStartupInjectSafety('omx-team-x', 1), { safe: false, reason: 'trust_prompt' });
510
+ const log = await readFile(logPath, 'utf-8');
511
+ assert.doesNotMatch(log, /send-keys/);
512
+ });
513
+ });
514
+ });
461
515
  describe('shouldAttemptAdaptiveRetry', () => {
462
516
  it('returns false when adaptive retry is disabled', () => {
463
517
  assert.equal(shouldAttemptAdaptiveRetry('auto', true, false, '❯ hello', 'hello'), false);