oh-my-codex 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +40 -6
  4. package/crates/omx-explore/src/main.rs +221 -10
  5. package/dist/catalog/__tests__/generator.test.js +2 -0
  6. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  7. package/dist/cli/__tests__/index.test.js +150 -1
  8. package/dist/cli/__tests__/index.test.js.map +1 -1
  9. package/dist/cli/__tests__/setup-skills-overwrite.test.js +41 -3
  10. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  11. package/dist/cli/__tests__/update.test.js +25 -1
  12. package/dist/cli/__tests__/update.test.js.map +1 -1
  13. package/dist/cli/index.d.ts +1 -0
  14. package/dist/cli/index.d.ts.map +1 -1
  15. package/dist/cli/index.js +73 -9
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/setup.d.ts.map +1 -1
  18. package/dist/cli/setup.js +15 -0
  19. package/dist/cli/setup.js.map +1 -1
  20. package/dist/cli/update.js +1 -1
  21. package/dist/cli/update.js.map +1 -1
  22. package/dist/hooks/__tests__/agents-overlay.test.js +20 -2
  23. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  24. package/dist/hooks/__tests__/analyze-routing-contract.test.d.ts +2 -0
  25. package/dist/hooks/__tests__/analyze-routing-contract.test.d.ts.map +1 -0
  26. package/dist/hooks/__tests__/analyze-routing-contract.test.js +36 -0
  27. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -0
  28. package/dist/hooks/__tests__/analyze-skill-contract.test.d.ts +2 -0
  29. package/dist/hooks/__tests__/analyze-skill-contract.test.d.ts.map +1 -0
  30. package/dist/hooks/__tests__/analyze-skill-contract.test.js +48 -0
  31. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -0
  32. package/dist/hooks/__tests__/keyword-detector.test.js +32 -0
  33. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  34. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +185 -8
  35. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  36. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +26 -0
  37. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  38. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +44 -0
  39. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  40. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +126 -0
  41. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  42. package/dist/hooks/__tests__/session.test.js +21 -0
  43. package/dist/hooks/__tests__/session.test.js.map +1 -1
  44. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  45. package/dist/hooks/agents-overlay.js +9 -0
  46. package/dist/hooks/agents-overlay.js.map +1 -1
  47. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  48. package/dist/hooks/keyword-detector.js +8 -1
  49. package/dist/hooks/keyword-detector.js.map +1 -1
  50. package/dist/hooks/session.d.ts.map +1 -1
  51. package/dist/hooks/session.js +9 -0
  52. package/dist/hooks/session.js.map +1 -1
  53. package/dist/hud/__tests__/state.test.js +55 -0
  54. package/dist/hud/__tests__/state.test.js.map +1 -1
  55. package/dist/hud/state.d.ts.map +1 -1
  56. package/dist/hud/state.js +23 -4
  57. package/dist/hud/state.js.map +1 -1
  58. package/dist/mcp/__tests__/bootstrap.test.js +38 -0
  59. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  60. package/dist/mcp/bootstrap.d.ts +1 -1
  61. package/dist/mcp/bootstrap.d.ts.map +1 -1
  62. package/dist/mcp/bootstrap.js +11 -3
  63. package/dist/mcp/bootstrap.js.map +1 -1
  64. package/dist/notifications/__tests__/reply-listener.test.js +34 -1
  65. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  66. package/dist/notifications/reply-listener.d.ts +1 -0
  67. package/dist/notifications/reply-listener.d.ts.map +1 -1
  68. package/dist/notifications/reply-listener.js +14 -2
  69. package/dist/notifications/reply-listener.js.map +1 -1
  70. package/dist/scripts/__tests__/codex-native-hook.test.js +248 -15
  71. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  72. package/dist/scripts/__tests__/generate-release-body.test.d.ts +2 -0
  73. package/dist/scripts/__tests__/generate-release-body.test.d.ts.map +1 -0
  74. package/dist/scripts/__tests__/generate-release-body.test.js +144 -0
  75. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -0
  76. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  77. package/dist/scripts/codex-native-hook.js +39 -49
  78. package/dist/scripts/codex-native-hook.js.map +1 -1
  79. package/dist/scripts/generate-release-body.d.ts +34 -0
  80. package/dist/scripts/generate-release-body.d.ts.map +1 -0
  81. package/dist/scripts/generate-release-body.js +249 -0
  82. package/dist/scripts/generate-release-body.js.map +1 -0
  83. package/dist/scripts/notify-fallback-watcher.js +43 -20
  84. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  85. package/dist/scripts/notify-hook/active-team.d.ts.map +1 -1
  86. package/dist/scripts/notify-hook/active-team.js +2 -1
  87. package/dist/scripts/notify-hook/active-team.js.map +1 -1
  88. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  89. package/dist/scripts/notify-hook/ralph-session-resume.js +17 -2
  90. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  91. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  92. package/dist/scripts/notify-hook/state-io.js +16 -0
  93. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  94. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  95. package/dist/scripts/notify-hook/team-leader-nudge.js +26 -5
  96. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  97. package/dist/scripts/notify-hook.js +1 -7
  98. package/dist/scripts/notify-hook.js.map +1 -1
  99. package/dist/team/__tests__/model-contract.test.js +6 -0
  100. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  101. package/dist/team/__tests__/tmux-session.test.js +1 -1
  102. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  103. package/dist/team/__tests__/worker-runtime-identity.test.d.ts +2 -0
  104. package/dist/team/__tests__/worker-runtime-identity.test.d.ts.map +1 -0
  105. package/dist/team/__tests__/worker-runtime-identity.test.js +250 -0
  106. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -0
  107. package/dist/team/leader-activity.d.ts.map +1 -1
  108. package/dist/team/leader-activity.js +26 -15
  109. package/dist/team/leader-activity.js.map +1 -1
  110. package/dist/team/model-contract.d.ts.map +1 -1
  111. package/dist/team/model-contract.js.map +1 -1
  112. package/dist/team/runtime.d.ts.map +1 -1
  113. package/dist/team/runtime.js +9 -8
  114. package/dist/team/runtime.js.map +1 -1
  115. package/dist/team/scaling.d.ts.map +1 -1
  116. package/dist/team/scaling.js +10 -9
  117. package/dist/team/scaling.js.map +1 -1
  118. package/dist/team/tmux-session.d.ts.map +1 -1
  119. package/dist/team/tmux-session.js +3 -2
  120. package/dist/team/tmux-session.js.map +1 -1
  121. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +3 -0
  122. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  123. package/dist/wiki/__tests__/slug-nonascii.test.js +11 -5
  124. package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -1
  125. package/dist/wiki/storage.d.ts.map +1 -1
  126. package/dist/wiki/storage.js +2 -1
  127. package/dist/wiki/storage.js.map +1 -1
  128. package/package.json +3 -1
  129. package/skills/analyze/SKILL.md +101 -134
  130. package/src/scripts/__tests__/codex-native-hook.test.ts +297 -17
  131. package/src/scripts/__tests__/generate-release-body.test.ts +166 -0
  132. package/src/scripts/codex-native-hook.ts +99 -66
  133. package/src/scripts/generate-release-body.ts +295 -0
  134. package/src/scripts/notify-fallback-watcher.ts +44 -21
  135. package/src/scripts/notify-hook/active-team.ts +2 -1
  136. package/src/scripts/notify-hook/ralph-session-resume.ts +17 -2
  137. package/src/scripts/notify-hook/state-io.ts +16 -0
  138. package/src/scripts/notify-hook/team-leader-nudge.ts +24 -4
  139. package/src/scripts/notify-hook.ts +1 -6
  140. package/templates/AGENTS.md +1 -1
  141. package/templates/catalog-manifest.json +2 -4
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { pathToFileURL } from 'node:url';
6
+ function usage() {
7
+ console.error('Usage: node scripts/generate-release-body.mjs --template <path> --out <path> [--current-tag <tag>] [--previous-tag <tag>] [--repo <owner/name>]');
8
+ process.exit(1);
9
+ }
10
+ function arg(name) {
11
+ const index = process.argv.indexOf(name);
12
+ if (index === -1)
13
+ return undefined;
14
+ return process.argv[index + 1];
15
+ }
16
+ function runGit(args, cwd, allowFailure = false) {
17
+ const result = spawnSync('git', args, {
18
+ cwd,
19
+ encoding: 'utf-8',
20
+ stdio: 'pipe',
21
+ });
22
+ if (result.status !== 0) {
23
+ if (allowFailure)
24
+ return '';
25
+ throw new Error(`git ${args.join(' ')} failed: ${(result.stderr || result.stdout || '').trim()}`.trim());
26
+ }
27
+ return String(result.stdout || '').trim();
28
+ }
29
+ function resolveRepositoryFromRemote(cwd) {
30
+ const remote = runGit(['config', '--get', 'remote.origin.url'], cwd, true);
31
+ if (!remote)
32
+ return undefined;
33
+ const httpsMatch = remote.match(/github\.com[:/](.+?)(?:\.git)?$/);
34
+ return httpsMatch?.[1];
35
+ }
36
+ export function resolveCurrentTag(cwd, explicit) {
37
+ if (explicit)
38
+ return explicit;
39
+ if (process.env.GITHUB_REF_NAME)
40
+ return process.env.GITHUB_REF_NAME;
41
+ const described = runGit(['describe', '--tags', '--exact-match'], cwd, true);
42
+ if (described)
43
+ return described;
44
+ throw new Error('unable to determine current release tag; pass --current-tag or set GITHUB_REF_NAME');
45
+ }
46
+ export function resolvePreviousTag(cwd, currentTag, explicit) {
47
+ if (explicit)
48
+ return explicit;
49
+ const tags = runGit(['tag', '--list', 'v*', '--sort=-v:refname'], cwd, true)
50
+ .split(/\r?\n/)
51
+ .map((line) => line.trim())
52
+ .filter(Boolean);
53
+ if (tags.length === 0)
54
+ return undefined;
55
+ const currentIndex = tags.indexOf(currentTag);
56
+ if (currentIndex >= 0)
57
+ return tags.slice(currentIndex + 1).find(Boolean);
58
+ return tags.find((tag) => tag !== currentTag);
59
+ }
60
+ function normalizeContributors(contributors) {
61
+ const deduped = new Map();
62
+ for (const contributor of contributors) {
63
+ const login = contributor.login?.trim();
64
+ const displayName = contributor.displayName.trim();
65
+ if (!displayName && !login)
66
+ continue;
67
+ const key = (login || displayName).toLowerCase();
68
+ if (!deduped.has(key)) {
69
+ deduped.set(key, {
70
+ displayName: displayName || `@${login}`,
71
+ ...(login ? { login } : {}),
72
+ ...(contributor.url ? { url: contributor.url } : {}),
73
+ });
74
+ }
75
+ }
76
+ return [...deduped.values()].sort((left, right) => {
77
+ const leftKey = (left.login || left.displayName).toLowerCase();
78
+ const rightKey = (right.login || right.displayName).toLowerCase();
79
+ return leftKey.localeCompare(rightKey);
80
+ });
81
+ }
82
+ export function formatContributor(contributor) {
83
+ if (contributor.login && contributor.url) {
84
+ return `[@${contributor.login}](${contributor.url})`;
85
+ }
86
+ if (contributor.login) {
87
+ return `@${contributor.login}`;
88
+ }
89
+ if (contributor.url) {
90
+ return `[${contributor.displayName}](${contributor.url})`;
91
+ }
92
+ return contributor.displayName;
93
+ }
94
+ function joinHumanList(values) {
95
+ if (values.length === 0)
96
+ return 'the contributors';
97
+ if (values.length === 1)
98
+ return values[0];
99
+ if (values.length === 2)
100
+ return `${values[0]} and ${values[1]}`;
101
+ return `${values.slice(0, -1).join(', ')}, and ${values.at(-1)}`;
102
+ }
103
+ export function renderContributorsSection(contributors) {
104
+ const normalized = normalizeContributors(contributors);
105
+ if (normalized.length === 0) {
106
+ return 'Thanks to the contributors who made this release possible.';
107
+ }
108
+ return `Thanks to ${joinHumanList(normalized.map((contributor) => formatContributor(contributor)))} for contributing to this release.`;
109
+ }
110
+ export function buildFullChangelogLine(repo, currentTag, previousTag) {
111
+ if (!repo) {
112
+ throw new Error('unable to determine GitHub repository; pass --repo or set GITHUB_REPOSITORY');
113
+ }
114
+ if (previousTag) {
115
+ return `**Full Changelog**: [\`${previousTag}...${currentTag}\`](https://github.com/${repo}/compare/${previousTag}...${currentTag})`;
116
+ }
117
+ return `**Full Changelog**: [\`${currentTag}\`](https://github.com/${repo}/releases/tag/${currentTag})`;
118
+ }
119
+ function replaceTitle(markdown, currentTag) {
120
+ if (!/^#\s+/m.test(markdown)) {
121
+ throw new Error('release body template is missing a top-level title');
122
+ }
123
+ return markdown.replace(/^#\s+.*$/m, `# oh-my-codex ${currentTag}`);
124
+ }
125
+ function findSectionEnd(lines, startIndex) {
126
+ for (let index = startIndex + 1; index < lines.length; index += 1) {
127
+ if (/^##\s+/.test(lines[index] ?? ''))
128
+ return index;
129
+ if (/^\*\*Full Changelog\*\*:/.test(lines[index] ?? ''))
130
+ return index;
131
+ }
132
+ return lines.length;
133
+ }
134
+ export function replaceSectionBody(markdown, heading, body) {
135
+ const lines = markdown.split(/\r?\n/);
136
+ const headingLine = `## ${heading}`;
137
+ const startIndex = lines.findIndex((line) => line.trim() === headingLine);
138
+ if (startIndex === -1) {
139
+ throw new Error(`release body template is missing section: ${headingLine}`);
140
+ }
141
+ const endIndex = findSectionEnd(lines, startIndex);
142
+ lines.splice(startIndex + 1, endIndex - startIndex - 1, '', ...body.split('\n'), '');
143
+ return `${lines.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd()}\n`;
144
+ }
145
+ export function replaceFullChangelogLine(markdown, fullChangelogLine) {
146
+ const lines = markdown.split(/\r?\n/);
147
+ const lineIndex = lines.findIndex((line) => /^\*\*Full Changelog\*\*:/.test(line));
148
+ if (lineIndex === -1) {
149
+ throw new Error('release body template is missing the Full Changelog line');
150
+ }
151
+ lines[lineIndex] = fullChangelogLine;
152
+ return `${lines.join('\n').trimEnd()}\n`;
153
+ }
154
+ function parseShortlogLine(line) {
155
+ const match = line.match(/^\s*\d+\s+(.+?)(?:\s+<[^>]+>)?$/);
156
+ if (!match)
157
+ return undefined;
158
+ const displayName = match[1]?.trim();
159
+ if (!displayName)
160
+ return undefined;
161
+ return { displayName };
162
+ }
163
+ export function getGitContributors(cwd, currentTag, previousTag) {
164
+ const range = previousTag ? `${previousTag}..${currentTag}` : currentTag;
165
+ const shortlog = runGit(['shortlog', '-sne', range], cwd, true);
166
+ if (!shortlog)
167
+ return [];
168
+ return normalizeContributors(shortlog.split(/\r?\n/).map((line) => parseShortlogLine(line)).filter((value) => Boolean(value)));
169
+ }
170
+ export async function getGitHubCompareContributors(repo, currentTag, previousTag, githubToken, fetchImpl = fetch) {
171
+ const response = await fetchImpl(`https://api.github.com/repos/${repo}/compare/${previousTag}...${currentTag}`, {
172
+ headers: {
173
+ Accept: 'application/vnd.github+json',
174
+ Authorization: `Bearer ${githubToken}`,
175
+ 'User-Agent': 'oh-my-codex-release-body-generator',
176
+ 'X-GitHub-Api-Version': '2022-11-28',
177
+ },
178
+ });
179
+ if (!response.ok) {
180
+ throw new Error(`GitHub compare API failed (${response.status})`);
181
+ }
182
+ const payload = await response.json();
183
+ const contributors = (payload.commits ?? []).map((commit) => {
184
+ if (commit.author?.login) {
185
+ return {
186
+ displayName: `@${commit.author.login}`,
187
+ login: commit.author.login,
188
+ ...(commit.author.html_url ? { url: commit.author.html_url } : {}),
189
+ };
190
+ }
191
+ const name = commit.commit?.author?.name?.trim();
192
+ return name ? { displayName: name } : undefined;
193
+ }).filter((value) => Boolean(value));
194
+ return normalizeContributors(contributors);
195
+ }
196
+ export async function resolveContributors(options) {
197
+ const { cwd, repo, currentTag, previousTag, githubToken } = options;
198
+ if (repo && previousTag && githubToken) {
199
+ try {
200
+ return await getGitHubCompareContributors(repo, currentTag, previousTag, githubToken);
201
+ }
202
+ catch (error) {
203
+ console.error(`[generate-release-body] falling back to git shortlog: ${error instanceof Error ? error.message : String(error)}`);
204
+ }
205
+ }
206
+ return getGitContributors(cwd, currentTag, previousTag);
207
+ }
208
+ export async function generateReleaseBody(options) {
209
+ const cwd = resolve(options.cwd ?? process.cwd());
210
+ const templatePath = resolve(cwd, options.templatePath);
211
+ const outPath = resolve(cwd, options.outPath);
212
+ const currentTag = resolveCurrentTag(cwd, options.currentTag);
213
+ const previousTag = resolvePreviousTag(cwd, currentTag, options.previousTag);
214
+ const repo = options.repo || process.env.GITHUB_REPOSITORY || resolveRepositoryFromRemote(cwd);
215
+ const contributors = await resolveContributors({
216
+ cwd,
217
+ repo,
218
+ currentTag,
219
+ previousTag,
220
+ githubToken: options.githubToken || process.env.GITHUB_TOKEN,
221
+ });
222
+ let markdown = readFileSync(templatePath, 'utf-8');
223
+ markdown = replaceTitle(markdown, currentTag);
224
+ markdown = replaceSectionBody(markdown, 'Contributors', renderContributorsSection(contributors));
225
+ markdown = replaceFullChangelogLine(markdown, buildFullChangelogLine(repo || '', currentTag, previousTag));
226
+ writeFileSync(outPath, markdown);
227
+ return markdown;
228
+ }
229
+ async function main() {
230
+ const templatePath = arg('--template');
231
+ const outPath = arg('--out');
232
+ if (!templatePath || !outPath)
233
+ usage();
234
+ await generateReleaseBody({
235
+ templatePath,
236
+ outPath,
237
+ currentTag: arg('--current-tag'),
238
+ previousTag: arg('--previous-tag'),
239
+ repo: arg('--repo'),
240
+ });
241
+ console.log(resolve(process.cwd(), outPath));
242
+ }
243
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
244
+ main().catch((error) => {
245
+ console.error(error instanceof Error ? error.message : String(error));
246
+ process.exit(1);
247
+ });
248
+ }
249
+ //# sourceMappingURL=generate-release-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-release-body.js","sourceRoot":"","sources":["../../src/scripts/generate-release-body.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,8 @@ import { isSessionStale, isSessionStateAuthoritativeForCwd, readSessionState } f
16
16
  import { DEFAULT_SUBAGENT_ACTIVE_WINDOW_MS, readSubagentSessionSummary, } from '../subagents/tracker.js';
17
17
  import { listNotifyCanonicalActiveTeams } from './notify-hook/active-team.js';
18
18
  import { sameFilePath } from '../utils/paths.js';
19
+ import { validateSessionId } from '../mcp/state-paths.js';
20
+ import { TEAM_NAME_SAFE_PATTERN } from '../team/contracts.js';
19
21
  function argValue(name, fallback = '') {
20
22
  const idx = process.argv.indexOf(name);
21
23
  if (idx < 0 || idx + 1 >= process.argv.length)
@@ -29,6 +31,21 @@ function asNumber(value, fallback) {
29
31
  function safeString(v) {
30
32
  return typeof v === 'string' ? v : '';
31
33
  }
34
+ function normalizeValidSessionId(value) {
35
+ const trimmed = safeString(value).trim();
36
+ if (!trimmed)
37
+ return '';
38
+ try {
39
+ return validateSessionId(trimmed) ?? '';
40
+ }
41
+ catch {
42
+ return '';
43
+ }
44
+ }
45
+ function normalizeValidTeamName(value) {
46
+ const trimmed = safeString(value).trim();
47
+ return TEAM_NAME_SAFE_PATTERN.test(trimmed) ? trimmed : '';
48
+ }
32
49
  function parsePositivePid(value) {
33
50
  const pid = Math.trunc(asNumber(value, 0));
34
51
  return pid > 0 ? pid : null;
@@ -366,8 +383,8 @@ async function resolveActiveModeState(mode) {
366
383
  const session = await readSessionState(cwd);
367
384
  if (session?.session_id) {
368
385
  if (isSessionStateAuthoritativeForCwd(session, cwd)) {
369
- currentSessionId = safeString(session.session_id).trim();
370
- currentSessionIsLive = !isSessionStale(session);
386
+ currentSessionId = normalizeValidSessionId(session.session_id);
387
+ currentSessionIsLive = currentSessionId !== '' && !isSessionStale(session);
371
388
  }
372
389
  if (currentSessionId && currentSessionIsLive) {
373
390
  candidateDirs.push(join(stateDir, 'sessions', currentSessionId));
@@ -431,8 +448,8 @@ async function resolveActiveTeamState() {
431
448
  let currentSessionIsLive = false;
432
449
  const session = await readSessionState(cwd);
433
450
  if (session?.session_id) {
434
- currentSessionId = safeString(session.session_id).trim();
435
- currentSessionIsLive = !isSessionStale(session);
451
+ currentSessionId = normalizeValidSessionId(session.session_id);
452
+ currentSessionIsLive = currentSessionId !== '' && !isSessionStale(session);
436
453
  if (currentSessionId && currentSessionIsLive) {
437
454
  candidateDirs.push(join(stateDir, 'sessions', currentSessionId));
438
455
  }
@@ -451,7 +468,7 @@ async function resolveActiveTeamState() {
451
468
  .catch(() => null);
452
469
  if (!parsed || typeof parsed !== 'object' || parsed.active !== true)
453
470
  continue;
454
- const teamName = safeString(parsed.team_name).trim();
471
+ const teamName = normalizeValidTeamName(parsed.team_name);
455
472
  if (!teamName)
456
473
  continue;
457
474
  const teamConfigDir = join(stateDir, 'team', teamName);
@@ -493,7 +510,10 @@ async function resolveActiveTeamState() {
493
510
  }
494
511
  const canonicalFallbackTeams = await listNotifyCanonicalActiveTeams(cwd, currentSessionId).catch(() => []);
495
512
  for (const team of canonicalFallbackTeams) {
496
- const teamConfigDir = join(stateDir, 'team', team.teamName);
513
+ const teamName = normalizeValidTeamName(team.teamName);
514
+ if (!teamName)
515
+ continue;
516
+ const teamConfigDir = join(stateDir, 'team', teamName);
497
517
  const manifestPath = join(teamConfigDir, 'manifest.v2.json');
498
518
  const configPath = join(teamConfigDir, 'config.json');
499
519
  const teamConfigPath = existsSync(manifestPath) ? manifestPath : configPath;
@@ -518,10 +538,10 @@ async function resolveActiveTeamState() {
518
538
  path: team.path,
519
539
  state: {
520
540
  active: true,
521
- team_name: team.teamName,
541
+ team_name: teamName,
522
542
  current_phase: team.phase,
523
543
  },
524
- team_name: team.teamName,
544
+ team_name: teamName,
525
545
  pane_count: paneStatus.paneCount,
526
546
  };
527
547
  }
@@ -596,9 +616,11 @@ async function readRalphSteerLock(path) {
596
616
  return null;
597
617
  }
598
618
  }
619
+ const RALPH_STEER_LOCK_MAX_RETRIES = 5;
599
620
  async function withRalphSteerLock(task) {
600
621
  await mkdir(dirname(ralphSteerLockPath), { recursive: true }).catch(() => { });
601
- while (true) {
622
+ let acquired = false;
623
+ for (let attempt = 0; attempt < RALPH_STEER_LOCK_MAX_RETRIES; attempt++) {
602
624
  let handle;
603
625
  try {
604
626
  handle = await open(ralphSteerLockPath, 'wx');
@@ -607,6 +629,7 @@ async function withRalphSteerLock(task) {
607
629
  acquired_at: new Date().toISOString(),
608
630
  };
609
631
  await handle.writeFile(JSON.stringify(payload, null, 2));
632
+ acquired = true;
610
633
  break;
611
634
  }
612
635
  catch (error) {
@@ -628,6 +651,10 @@ async function withRalphSteerLock(task) {
628
651
  await handle?.close().catch(() => { });
629
652
  }
630
653
  }
654
+ if (!acquired) {
655
+ lastRalphContinueSteer.last_reason = 'global_lock_exhausted';
656
+ return null;
657
+ }
631
658
  try {
632
659
  return await task();
633
660
  }
@@ -672,18 +699,18 @@ async function readRalphProgressGate(activeRalphState, now) {
672
699
  }
673
700
  return { allow: true, reason: 'progress_stale', progress_at: progressAt, subagent_session_id: subagentSessionId };
674
701
  }
675
- function shouldSkipRalphContinue(now, candidateIso, startupIso) {
702
+ function shouldSkipRalphContinue(now, candidateIso) {
676
703
  const sharedMs = parseIsoMillis(candidateIso);
677
704
  const localMs = parseIsoMillis(lastRalphContinueSteer.last_sent_at);
678
- const startupAnchorIso = lastRalphContinueSteer.cooldown_anchor_at || startupIso;
705
+ const startupAnchorIso = lastRalphContinueSteer.cooldown_anchor_at;
679
706
  const startupAnchorMs = parseIsoMillis(startupAnchorIso);
680
- const startupCooldown = sharedMs === null && localMs === null;
681
- const anchorMs = sharedMs ?? localMs ?? startupAnchorMs ?? startedAt;
707
+ const startupCooldown = sharedMs === null && localMs === null && startupAnchorMs !== null;
708
+ const anchorMs = sharedMs ?? localMs ?? startupAnchorMs ?? 0;
682
709
  const anchorIso = sharedMs !== null
683
710
  ? candidateIso
684
711
  : (localMs !== null ? lastRalphContinueSteer.last_sent_at : startupAnchorIso);
685
712
  return {
686
- skip: now - anchorMs < RALPH_CONTINUE_CADENCE_MS,
713
+ skip: anchorMs > 0 && now - anchorMs < RALPH_CONTINUE_CADENCE_MS,
687
714
  reason: startupCooldown ? 'startup_cooldown' : (sharedMs !== null ? 'global_cooldown' : 'cooldown'),
688
715
  anchorMs,
689
716
  anchorIso,
@@ -821,7 +848,6 @@ async function writePidFileRecord() {
821
848
  async function runRalphContinueSteerTick() {
822
849
  const now = Date.now();
823
850
  const nowIso = new Date(now).toISOString();
824
- const startupIso = new Date(startedAt).toISOString();
825
851
  const activeRalph = await resolveActiveRalphState();
826
852
  const activePaneId = safeString(activeRalph.state?.tmux_pane_id).trim();
827
853
  lastRalphContinueSteer = {
@@ -845,12 +871,9 @@ async function runRalphContinueSteerTick() {
845
871
  }
846
872
  return;
847
873
  }
848
- if (parseIsoMillis(lastRalphContinueSteer.last_sent_at) === null && parseIsoMillis(lastRalphContinueSteer.cooldown_anchor_at) === null) {
849
- lastRalphContinueSteer.cooldown_anchor_at = startupIso;
850
- }
851
874
  const sharedBeforeLock = await readRalphSteerTimestamp();
852
875
  lastRalphContinueSteer.shared_last_sent_at = sharedBeforeLock;
853
- const initialCooldown = shouldSkipRalphContinue(now, sharedBeforeLock, startupIso);
876
+ const initialCooldown = shouldSkipRalphContinue(now, sharedBeforeLock);
854
877
  if (initialCooldown.skip) {
855
878
  lastRalphContinueSteer.last_reason = initialCooldown.reason;
856
879
  if (!sharedBeforeLock && initialCooldown.reason === 'startup_cooldown') {
@@ -861,7 +884,7 @@ async function runRalphContinueSteerTick() {
861
884
  const outcome = await withRalphSteerLock(async () => {
862
885
  const sharedLastSentAt = await readRalphSteerTimestamp();
863
886
  lastRalphContinueSteer.shared_last_sent_at = sharedLastSentAt;
864
- const cooldown = shouldSkipRalphContinue(Date.now(), sharedLastSentAt, startupIso);
887
+ const cooldown = shouldSkipRalphContinue(Date.now(), sharedLastSentAt);
865
888
  if (cooldown.skip) {
866
889
  lastRalphContinueSteer.last_reason = cooldown.reason;
867
890
  if (!sharedLastSentAt && cooldown.reason === 'startup_cooldown') {