oh-my-codex 0.4.1 → 0.4.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.
- package/README.es.md +36 -0
- package/README.ja.md +36 -0
- package/README.ko.md +36 -0
- package/README.md +13 -1
- package/README.pt.md +36 -0
- package/README.ru.md +36 -0
- package/README.vi.md +36 -0
- package/README.zh.md +39 -0
- package/bin/omx.js +2 -1
- package/dist/cli/__tests__/index.test.js +11 -0
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/star-prompt.test.d.ts +2 -0
- package/dist/cli/__tests__/star-prompt.test.d.ts.map +1 -0
- package/dist/cli/__tests__/star-prompt.test.js +82 -0
- package/dist/cli/__tests__/star-prompt.test.js.map +1 -0
- package/dist/cli/__tests__/update.test.d.ts +2 -0
- package/dist/cli/__tests__/update.test.d.ts.map +1 -0
- package/dist/cli/__tests__/update.test.js +66 -0
- package/dist/cli/__tests__/update.test.js.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +13 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/star-prompt.d.ts +11 -0
- package/dist/cli/star-prompt.d.ts.map +1 -0
- package/dist/cli/star-prompt.js +74 -0
- package/dist/cli/star-prompt.js.map +1 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +7 -29
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +3 -3
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +1 -1
- package/dist/config/generator.js +8 -8
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +427 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +17 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/emulator.d.ts +1 -1
- package/dist/hooks/emulator.js +5 -5
- package/dist/hooks/emulator.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/dispatcher.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +152 -0
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -0
- package/dist/hooks/extensibility/__tests__/events.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/events.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/events.test.js +117 -0
- package/dist/hooks/extensibility/__tests__/events.test.js.map +1 -0
- package/dist/hooks/extensibility/__tests__/loader.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/loader.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/loader.test.js +229 -0
- package/dist/hooks/extensibility/__tests__/loader.test.js.map +1 -0
- package/dist/hooks/extensibility/__tests__/logging.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/logging.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/logging.test.js +74 -0
- package/dist/hooks/extensibility/__tests__/logging.test.js.map +1 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +202 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -0
- package/dist/hooks/extensibility/__tests__/runtime.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/runtime.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/runtime.test.js +117 -0
- package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -0
- package/dist/hooks/extensibility/__tests__/sdk.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/sdk.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/sdk.test.js +277 -0
- package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -0
- package/dist/hud/__tests__/colors.test.d.ts +2 -0
- package/dist/hud/__tests__/colors.test.d.ts.map +1 -0
- package/dist/hud/__tests__/colors.test.js +194 -0
- package/dist/hud/__tests__/colors.test.js.map +1 -0
- package/dist/hud/__tests__/render.test.d.ts +2 -0
- package/dist/hud/__tests__/render.test.d.ts.map +1 -0
- package/dist/hud/__tests__/render.test.js +449 -0
- package/dist/hud/__tests__/render.test.js.map +1 -0
- package/dist/hud/__tests__/types.test.d.ts +2 -0
- package/dist/hud/__tests__/types.test.d.ts.map +1 -0
- package/dist/hud/__tests__/types.test.js +17 -0
- package/dist/hud/__tests__/types.test.js.map +1 -0
- package/dist/notifications/__tests__/tmux-detector.test.js +69 -1
- package/dist/notifications/__tests__/tmux-detector.test.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts +1 -1
- package/dist/notifications/reply-listener.js +1 -1
- package/dist/notifications/tmux-detector.d.ts +16 -0
- package/dist/notifications/tmux-detector.d.ts.map +1 -1
- package/dist/notifications/tmux-detector.js +37 -22
- package/dist/notifications/tmux-detector.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +61 -1
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/orchestrator.d.ts +1 -1
- package/dist/team/orchestrator.js +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +14 -2
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/state.d.ts +4 -0
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts +14 -2
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +42 -6
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/utils/__tests__/package.test.d.ts +2 -0
- package/dist/utils/__tests__/package.test.d.ts.map +1 -0
- package/dist/utils/__tests__/package.test.js +21 -0
- package/dist/utils/__tests__/package.test.js.map +1 -0
- package/dist/utils/__tests__/paths.test.d.ts +2 -0
- package/dist/utils/__tests__/paths.test.d.ts.map +1 -0
- package/dist/utils/__tests__/paths.test.js +117 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -0
- package/dist/verification/__tests__/verifier.test.d.ts +2 -0
- package/dist/verification/__tests__/verifier.test.d.ts.map +1 -0
- package/dist/verification/__tests__/verifier.test.js +94 -0
- package/dist/verification/__tests__/verifier.test.js.map +1 -0
- package/package.json +1 -1
- package/scripts/notify-hook.js +164 -3
- package/templates/AGENTS.md +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.test.d.ts","sourceRoot":"","sources":["../../../src/verification/__tests__/verifier.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { getVerificationInstructions, determineTaskSize, getFixLoopInstructions, } from '../verifier.js';
|
|
4
|
+
describe('determineTaskSize', () => {
|
|
5
|
+
it('returns small for low file count and line changes', () => {
|
|
6
|
+
assert.equal(determineTaskSize(1, 10), 'small');
|
|
7
|
+
assert.equal(determineTaskSize(3, 99), 'small');
|
|
8
|
+
});
|
|
9
|
+
it('returns standard for moderate file count and line changes', () => {
|
|
10
|
+
assert.equal(determineTaskSize(4, 50), 'standard');
|
|
11
|
+
assert.equal(determineTaskSize(15, 499), 'standard');
|
|
12
|
+
});
|
|
13
|
+
it('returns large for high file count', () => {
|
|
14
|
+
assert.equal(determineTaskSize(16, 100), 'large');
|
|
15
|
+
});
|
|
16
|
+
it('returns large for high line changes', () => {
|
|
17
|
+
assert.equal(determineTaskSize(5, 500), 'large');
|
|
18
|
+
});
|
|
19
|
+
it('returns small at exact boundary (3 files, 99 lines)', () => {
|
|
20
|
+
assert.equal(determineTaskSize(3, 99), 'small');
|
|
21
|
+
});
|
|
22
|
+
it('returns standard when file count exceeds small threshold', () => {
|
|
23
|
+
assert.equal(determineTaskSize(4, 99), 'standard');
|
|
24
|
+
});
|
|
25
|
+
it('returns standard when line changes hit 100 but files are low', () => {
|
|
26
|
+
assert.equal(determineTaskSize(3, 100), 'standard');
|
|
27
|
+
});
|
|
28
|
+
it('returns large at exact boundary (15 files, 500 lines)', () => {
|
|
29
|
+
assert.equal(determineTaskSize(15, 500), 'large');
|
|
30
|
+
});
|
|
31
|
+
it('returns large when file count exceeds standard threshold', () => {
|
|
32
|
+
assert.equal(determineTaskSize(16, 0), 'large');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('getVerificationInstructions', () => {
|
|
36
|
+
it('includes the task description in all sizes', () => {
|
|
37
|
+
const desc = 'Add login button';
|
|
38
|
+
for (const size of ['small', 'standard', 'large']) {
|
|
39
|
+
const result = getVerificationInstructions(size, desc);
|
|
40
|
+
assert.ok(result.includes(desc), `${size} should include task description`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
it('includes Verification Protocol header for all sizes', () => {
|
|
44
|
+
for (const size of ['small', 'standard', 'large']) {
|
|
45
|
+
const result = getVerificationInstructions(size, 'task');
|
|
46
|
+
assert.ok(result.includes('## Verification Protocol'));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
it('returns small-specific instructions', () => {
|
|
50
|
+
const result = getVerificationInstructions('small', 'fix typo');
|
|
51
|
+
assert.ok(result.includes('type checker on modified files'));
|
|
52
|
+
assert.ok(result.includes('PASS/FAIL'));
|
|
53
|
+
// Should NOT contain large/standard-specific items
|
|
54
|
+
assert.ok(!result.includes('Security review'));
|
|
55
|
+
assert.ok(!result.includes('Run linter'));
|
|
56
|
+
});
|
|
57
|
+
it('returns standard-specific instructions', () => {
|
|
58
|
+
const result = getVerificationInstructions('standard', 'add feature');
|
|
59
|
+
assert.ok(result.includes('tsc --noEmit'));
|
|
60
|
+
assert.ok(result.includes('Run linter'));
|
|
61
|
+
assert.ok(result.includes('regressions'));
|
|
62
|
+
// Should NOT contain large-specific items
|
|
63
|
+
assert.ok(!result.includes('Security review'));
|
|
64
|
+
assert.ok(!result.includes('Performance impact'));
|
|
65
|
+
});
|
|
66
|
+
it('returns large-specific instructions', () => {
|
|
67
|
+
const result = getVerificationInstructions('large', 'refactor auth');
|
|
68
|
+
assert.ok(result.includes('Security review'));
|
|
69
|
+
assert.ok(result.includes('Performance impact'));
|
|
70
|
+
assert.ok(result.includes('API compatibility'));
|
|
71
|
+
assert.ok(result.includes('confidence level'));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('getFixLoopInstructions', () => {
|
|
75
|
+
it('returns instructions with default maxRetries of 3', () => {
|
|
76
|
+
const result = getFixLoopInstructions();
|
|
77
|
+
assert.ok(result.includes('Fix-Verify Loop'));
|
|
78
|
+
assert.ok(result.includes('up to 3 times'));
|
|
79
|
+
assert.ok(result.includes('after 3 attempts'));
|
|
80
|
+
});
|
|
81
|
+
it('respects custom maxRetries', () => {
|
|
82
|
+
const result = getFixLoopInstructions(5);
|
|
83
|
+
assert.ok(result.includes('up to 5 times'));
|
|
84
|
+
assert.ok(result.includes('after 5 attempts'));
|
|
85
|
+
assert.ok(!result.includes('up to 3 times'));
|
|
86
|
+
});
|
|
87
|
+
it('includes escalation guidance', () => {
|
|
88
|
+
const result = getFixLoopInstructions();
|
|
89
|
+
assert.ok(result.includes('escalate'));
|
|
90
|
+
assert.ok(result.includes('What was attempted'));
|
|
91
|
+
assert.ok(result.includes('Recommended next steps'));
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=verifier.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.test.js","sourceRoot":"","sources":["../../../src/verification/__tests__/verifier.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/scripts/notify-hook.js
CHANGED
|
@@ -245,11 +245,29 @@ const DEFAULT_STALL_PATTERNS = [
|
|
|
245
245
|
'if you want',
|
|
246
246
|
'would you like',
|
|
247
247
|
'shall i',
|
|
248
|
+
'next i can',
|
|
248
249
|
'do you want me to',
|
|
249
250
|
'let me know if',
|
|
251
|
+
'do you want',
|
|
252
|
+
'want me to',
|
|
253
|
+
'let me know',
|
|
254
|
+
'just let me know',
|
|
250
255
|
'i can also',
|
|
256
|
+
'i could also',
|
|
251
257
|
'ready to proceed',
|
|
252
258
|
'should i',
|
|
259
|
+
'whenever you',
|
|
260
|
+
'say go',
|
|
261
|
+
'say yes',
|
|
262
|
+
'type continue',
|
|
263
|
+
'and i\'ll continue',
|
|
264
|
+
'and i\'ll proceed',
|
|
265
|
+
'keep driving',
|
|
266
|
+
'keep pushing',
|
|
267
|
+
'move forward',
|
|
268
|
+
'drive forward',
|
|
269
|
+
'proceed from here',
|
|
270
|
+
'i\'ll continue from',
|
|
253
271
|
];
|
|
254
272
|
|
|
255
273
|
function normalizeAutoNudgeConfig(raw) {
|
|
@@ -289,9 +307,16 @@ async function loadAutoNudgeConfig() {
|
|
|
289
307
|
|
|
290
308
|
function detectStallPattern(text, patterns) {
|
|
291
309
|
if (!text || typeof text !== 'string') return false;
|
|
292
|
-
//
|
|
293
|
-
const tail = text.slice(-
|
|
294
|
-
|
|
310
|
+
// Broader tail window (~800 chars / ~15-20 lines) for context
|
|
311
|
+
const tail = text.slice(-800).toLowerCase();
|
|
312
|
+
const lowerPatterns = patterns.map(p => p.toLowerCase());
|
|
313
|
+
// Focus on last few lines where stall prompts typically appear
|
|
314
|
+
const lines = tail.split('\n').filter(l => l.trim());
|
|
315
|
+
const hotZone = lines.slice(-3).join('\n');
|
|
316
|
+
// Primary: check last few lines (highest signal)
|
|
317
|
+
if (lowerPatterns.some(p => hotZone.includes(p))) return true;
|
|
318
|
+
// Secondary: check broader tail window
|
|
319
|
+
return lowerPatterns.some(p => tail.includes(p));
|
|
295
320
|
}
|
|
296
321
|
|
|
297
322
|
async function capturePane(paneId, lines = 10) {
|
|
@@ -1084,6 +1109,133 @@ function parseTeamWorkerEnv(rawValue) {
|
|
|
1084
1109
|
return { teamName: match[1], workerName: match[2] };
|
|
1085
1110
|
}
|
|
1086
1111
|
|
|
1112
|
+
function resolveAllWorkersIdleCooldownMs() {
|
|
1113
|
+
const raw = safeString(process.env.OMX_TEAM_ALL_IDLE_COOLDOWN_MS || '');
|
|
1114
|
+
const parsed = asNumber(raw);
|
|
1115
|
+
// Default: 60 seconds. Guard against unreasonable values.
|
|
1116
|
+
if (parsed !== null && parsed >= 5_000 && parsed <= 10 * 60_000) return parsed;
|
|
1117
|
+
return 60_000;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
async function readWorkerStatusState(stateDir, teamName, workerName) {
|
|
1121
|
+
if (!workerName) return 'unknown';
|
|
1122
|
+
const statusPath = join(stateDir, 'team', teamName, 'workers', workerName, 'status.json');
|
|
1123
|
+
try {
|
|
1124
|
+
if (!existsSync(statusPath)) return 'unknown';
|
|
1125
|
+
const raw = await readFile(statusPath, 'utf-8');
|
|
1126
|
+
const parsed = JSON.parse(raw);
|
|
1127
|
+
if (parsed && typeof parsed.state === 'string') return parsed.state;
|
|
1128
|
+
return 'unknown';
|
|
1129
|
+
} catch {
|
|
1130
|
+
return 'unknown';
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
async function readTeamWorkersForIdleCheck(stateDir, teamName) {
|
|
1135
|
+
// Try manifest.v2.json first (preferred), then config.json
|
|
1136
|
+
const manifestPath = join(stateDir, 'team', teamName, 'manifest.v2.json');
|
|
1137
|
+
const configPath = join(stateDir, 'team', teamName, 'config.json');
|
|
1138
|
+
const srcPath = existsSync(manifestPath) ? manifestPath : existsSync(configPath) ? configPath : null;
|
|
1139
|
+
if (!srcPath) return null;
|
|
1140
|
+
|
|
1141
|
+
try {
|
|
1142
|
+
const raw = await readFile(srcPath, 'utf-8');
|
|
1143
|
+
const parsed = JSON.parse(raw);
|
|
1144
|
+
if (!parsed || typeof parsed !== 'object') return null;
|
|
1145
|
+
const workers = parsed.workers;
|
|
1146
|
+
if (!Array.isArray(workers) || workers.length === 0) return null;
|
|
1147
|
+
const tmuxSession = safeString(parsed.tmux_session || '').trim();
|
|
1148
|
+
return { workers, tmuxSession };
|
|
1149
|
+
} catch {
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
async function maybeNotifyLeaderAllWorkersIdle({ cwd, stateDir, logsDir, parsedTeamWorker }) {
|
|
1155
|
+
const { teamName, workerName } = parsedTeamWorker;
|
|
1156
|
+
const nowMs = Date.now();
|
|
1157
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
1158
|
+
|
|
1159
|
+
// Only trigger check when this worker is idle
|
|
1160
|
+
const myState = await readWorkerStatusState(stateDir, teamName, workerName);
|
|
1161
|
+
if (myState !== 'idle') return;
|
|
1162
|
+
|
|
1163
|
+
// Read team config to get worker list and leader tmux target
|
|
1164
|
+
const teamInfo = await readTeamWorkersForIdleCheck(stateDir, teamName);
|
|
1165
|
+
if (!teamInfo) return;
|
|
1166
|
+
const { workers, tmuxSession } = teamInfo;
|
|
1167
|
+
if (!tmuxSession) return;
|
|
1168
|
+
|
|
1169
|
+
// Check cooldown to prevent notification spam
|
|
1170
|
+
const idleStatePath = join(stateDir, 'team', teamName, 'all-workers-idle.json');
|
|
1171
|
+
const idleState = (await readJsonIfExists(idleStatePath, null)) || {};
|
|
1172
|
+
const cooldownMs = resolveAllWorkersIdleCooldownMs();
|
|
1173
|
+
const lastNotifiedMs = asNumber(idleState.last_notified_at_ms) ?? 0;
|
|
1174
|
+
if ((nowMs - lastNotifiedMs) < cooldownMs) return;
|
|
1175
|
+
|
|
1176
|
+
// Check if ALL workers are idle (or done)
|
|
1177
|
+
const states = await Promise.all(
|
|
1178
|
+
workers.map(w => readWorkerStatusState(stateDir, teamName, safeString(w && w.name ? w.name : '')))
|
|
1179
|
+
);
|
|
1180
|
+
const allIdle = states.length > 0 && states.every(s => s === 'idle' || s === 'done');
|
|
1181
|
+
if (!allIdle) return;
|
|
1182
|
+
|
|
1183
|
+
const N = workers.length;
|
|
1184
|
+
const message = `[OMX] All ${N} worker${N === 1 ? '' : 's'} idle. Ready for next instructions. ${DEFAULT_MARKER}`;
|
|
1185
|
+
|
|
1186
|
+
try {
|
|
1187
|
+
await runProcess('tmux', ['send-keys', '-t', tmuxSession, '-l', message], 3000);
|
|
1188
|
+
await new Promise(r => setTimeout(r, 100));
|
|
1189
|
+
await runProcess('tmux', ['send-keys', '-t', tmuxSession, 'C-m'], 3000);
|
|
1190
|
+
await new Promise(r => setTimeout(r, 100));
|
|
1191
|
+
await runProcess('tmux', ['send-keys', '-t', tmuxSession, 'C-m'], 3000);
|
|
1192
|
+
|
|
1193
|
+
// Update cooldown state (atomic: use a tmp file pattern)
|
|
1194
|
+
const nextIdleState = {
|
|
1195
|
+
...idleState,
|
|
1196
|
+
last_notified_at_ms: nowMs,
|
|
1197
|
+
last_notified_at: nowIso,
|
|
1198
|
+
worker_count: N,
|
|
1199
|
+
};
|
|
1200
|
+
await writeFile(idleStatePath, JSON.stringify(nextIdleState, null, 2)).catch(() => {});
|
|
1201
|
+
|
|
1202
|
+
// Emit team event for the notification
|
|
1203
|
+
const eventsDir = join(stateDir, 'team', teamName, 'events');
|
|
1204
|
+
const eventsPath = join(eventsDir, 'events.ndjson');
|
|
1205
|
+
try {
|
|
1206
|
+
await mkdir(eventsDir, { recursive: true });
|
|
1207
|
+
const event = {
|
|
1208
|
+
event_id: `all-idle-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
|
|
1209
|
+
team: teamName,
|
|
1210
|
+
type: 'all_workers_idle',
|
|
1211
|
+
worker: workerName,
|
|
1212
|
+
worker_count: N,
|
|
1213
|
+
created_at: nowIso,
|
|
1214
|
+
};
|
|
1215
|
+
await appendFile(eventsPath, JSON.stringify(event) + '\n');
|
|
1216
|
+
} catch { /* best effort */ }
|
|
1217
|
+
|
|
1218
|
+
// Log the notification
|
|
1219
|
+
await logTmuxHookEvent(logsDir, {
|
|
1220
|
+
timestamp: nowIso,
|
|
1221
|
+
type: 'all_workers_idle_notification',
|
|
1222
|
+
team: teamName,
|
|
1223
|
+
tmux_target: tmuxSession,
|
|
1224
|
+
worker: workerName,
|
|
1225
|
+
worker_count: N,
|
|
1226
|
+
});
|
|
1227
|
+
} catch (err) {
|
|
1228
|
+
await logTmuxHookEvent(logsDir, {
|
|
1229
|
+
timestamp: nowIso,
|
|
1230
|
+
type: 'all_workers_idle_notification',
|
|
1231
|
+
team: teamName,
|
|
1232
|
+
tmux_target: tmuxSession,
|
|
1233
|
+
worker: workerName,
|
|
1234
|
+
error: err instanceof Error ? err.message : safeString(err),
|
|
1235
|
+
}).catch(() => {});
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1087
1239
|
async function dispatchNativeHookEvent(cwd, eventName, payload, context = {}) {
|
|
1088
1240
|
try {
|
|
1089
1241
|
const { buildNativeHookEvent } = await import('../dist/hooks/extensibility/events.js');
|
|
@@ -1312,6 +1464,15 @@ async function main() {
|
|
|
1312
1464
|
}
|
|
1313
1465
|
}
|
|
1314
1466
|
|
|
1467
|
+
// 4.6. Notify leader when all workers are idle (worker session only)
|
|
1468
|
+
if (isTeamWorker && parsedTeamWorker) {
|
|
1469
|
+
try {
|
|
1470
|
+
await maybeNotifyLeaderAllWorkersIdle({ cwd, stateDir, logsDir, parsedTeamWorker });
|
|
1471
|
+
} catch {
|
|
1472
|
+
// Non-critical
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1315
1476
|
// 5. Optional tmux prompt injection workaround (non-fatal, opt-in)
|
|
1316
1477
|
// Skip for team workers - only the lead should inject prompts
|
|
1317
1478
|
if (!isTeamWorker) {
|
package/templates/AGENTS.md
CHANGED
|
@@ -44,7 +44,7 @@ For non-trivial SDK/API/framework usage, delegate to `dependency-expert` to chec
|
|
|
44
44
|
</delegation_rules>
|
|
45
45
|
|
|
46
46
|
<child_agent_protocol>
|
|
47
|
-
Codex CLI spawns child agents via the `spawn_agent` tool (requires `
|
|
47
|
+
Codex CLI spawns child agents via the `spawn_agent` tool (requires `multi_agent = true`).
|
|
48
48
|
To inject role-specific behavior, the parent MUST read the role prompt and pass it in the spawned agent message.
|
|
49
49
|
|
|
50
50
|
Delegation steps:
|