oh-my-codex 0.21.26 → 0.21.28

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 (34) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/dist/auth/hotswap.d.ts.map +1 -1
  4. package/dist/auth/hotswap.js +37 -0
  5. package/dist/auth/hotswap.js.map +1 -1
  6. package/dist/cli/__tests__/jetbrains-scroll-diagnostic.test.d.ts +2 -0
  7. package/dist/cli/__tests__/jetbrains-scroll-diagnostic.test.d.ts.map +1 -0
  8. package/dist/cli/__tests__/jetbrains-scroll-diagnostic.test.js +60 -0
  9. package/dist/cli/__tests__/jetbrains-scroll-diagnostic.test.js.map +1 -0
  10. package/dist/cli/index.d.ts +6 -0
  11. package/dist/cli/index.d.ts.map +1 -1
  12. package/dist/cli/index.js +36 -0
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/leader/__tests__/contract.test.js +2 -2
  15. package/dist/leader/__tests__/contract.test.js.map +1 -1
  16. package/dist/leader/contract.d.ts +1 -1
  17. package/dist/leader/contract.d.ts.map +1 -1
  18. package/dist/leader/contract.js +1 -1
  19. package/dist/leader/contract.js.map +1 -1
  20. package/dist/scripts/__tests__/codex-native-hook.test.js +73 -4
  21. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  22. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  23. package/dist/scripts/codex-native-hook.js +20 -0
  24. package/dist/scripts/codex-native-hook.js.map +1 -1
  25. package/dist/subagents/__tests__/tracker.test.js +106 -10
  26. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  27. package/dist/subagents/tracker.d.ts +7 -0
  28. package/dist/subagents/tracker.d.ts.map +1 -1
  29. package/dist/subagents/tracker.js +54 -8
  30. package/dist/subagents/tracker.js.map +1 -1
  31. package/package.json +1 -1
  32. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  33. package/src/scripts/__tests__/codex-native-hook.test.ts +87 -4
  34. package/src/scripts/codex-native-hook.ts +23 -0
@@ -179,7 +179,7 @@ describe('subagents/tracker', () => {
179
179
  nested: { thread_id: 'nested', kind: 'subagent', provenance_kind: NATIVE_SUBAGENT_PROVENANCE, direct_child_root_id: 'root', direct_child_parent_id: 'direct', status: 'available', first_seen_at: '2026-07-23T00:00:00.000Z', last_seen_at: '2026-07-23T00:00:00.000Z', turn_count: 1 },
180
180
  revoked: { thread_id: 'revoked', kind: 'subagent', provenance_kind: NATIVE_SUBAGENT_PROVENANCE, direct_child_root_id: 'root', direct_child_parent_id: 'root', reopen_authority_revoked: true, status: 'available', first_seen_at: '2026-07-23T00:00:00.000Z', last_seen_at: '2026-07-23T00:00:00.000Z', turn_count: 1 },
181
181
  } } } }));
182
- const context = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume' }) ?? '';
182
+ const context = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '';
183
183
  assert.match(context, /resume_agent\("direct"\)/);
184
184
  assert.doesNotMatch(context, /resume_agent\("root"\)|resume_agent\("nested"\)|resume_agent\("revoked"\)/);
185
185
  const persisted = JSON.parse(readFileSync(path, 'utf8'));
@@ -204,7 +204,7 @@ describe('subagents/tracker', () => {
204
204
  const repaired = JSON.parse(readFileSync(subagentTrackingPath(cwd), 'utf8'));
205
205
  assert.equal(repaired.sessions.canonical.threads.child.reopen_authority_revoked, undefined);
206
206
  assert.equal(repaired.sessions['root-a'].threads.child.reopen_authority_revoked, undefined);
207
- assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root-a', source: 'resume' }) ?? '', /resume_agent\("child"\)/);
207
+ assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root-a', source: 'resume', availableTools: ['resume_agent'] }) ?? '', /resume_agent\("child"\)/);
208
208
  }
209
209
  finally {
210
210
  rmSync(cwd, { recursive: true, force: true });
@@ -312,7 +312,7 @@ describe('subagents/tracker', () => {
312
312
  recordNativeSubagentAuthorityObservation(cwd, { sessionIds: ['canonical', 'root'], childThreadId: 'child', parentThreadId: 'root' });
313
313
  const state = JSON.parse(readFileSync(subagentTrackingPath(cwd), 'utf8'));
314
314
  assert.equal(state.sessions.canonical.threads.child.reopen_authority_revoked, undefined);
315
- assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '', /resume_agent\("child"\)/);
315
+ assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '', /resume_agent\("child"\)/);
316
316
  }
317
317
  finally {
318
318
  rmSync(cwd, { recursive: true, force: true });
@@ -341,7 +341,7 @@ describe('subagents/tracker', () => {
341
341
  legacy: { thread_id: 'legacy', kind: 'subagent', provenance_kind: 'adapted', status: 'available', first_seen_at: '2026-07-23T00:00:00.000Z', last_seen_at: '2026-07-23T00:00:00.000Z', turn_count: 1 },
342
342
  } } } }));
343
343
  recordNativeSubagentAuthorityObservation(cwd, { sessionIds: ['canonical', 'root'], childThreadId: 'child', parentThreadId: 'root', rootNativeSessionId: 'root', authorityEvidence: 'valid' });
344
- const context = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '';
344
+ const context = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '';
345
345
  assert.match(context, /resume_agent\("child"\)/);
346
346
  assert.doesNotMatch(context, /resume_agent\("legacy"\)/);
347
347
  }
@@ -360,7 +360,7 @@ describe('subagents/tracker', () => {
360
360
  } },
361
361
  } }));
362
362
  recordNativeSubagentAuthorityObservation(cwd, { sessionIds: ['canonical', 'root'], childThreadId: 'child', parentThreadId: 'root', rootNativeSessionId: 'root', authorityEvidence: 'valid' });
363
- const context = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '';
363
+ const context = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '';
364
364
  assert.match(context, /resume_agent\("child"\)/);
365
365
  const persisted = JSON.parse(readFileSync(path, 'utf8'));
366
366
  assert.equal(persisted.sessions.correlation.threads.child.provenance_kind, DESCRIPTIVE_ADAPTED_PROVENANCE);
@@ -528,7 +528,7 @@ describe('subagents/tracker', () => {
528
528
  const snapshot = await readSubagentTrackingState(cwd);
529
529
  const descriptive = recordSubagentTurn(snapshot, { sessionId: 'canonical', threadId: 'child', kind: 'subagent' });
530
530
  await writeSubagentTrackingState(cwd, descriptive);
531
- assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '', /resume_agent\("child"\)/);
531
+ assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '', /resume_agent\("child"\)/);
532
532
  }
533
533
  finally {
534
534
  rmSync(cwd, { recursive: true, force: true });
@@ -584,7 +584,7 @@ describe('subagents/tracker', () => {
584
584
  const cwd = mkdtempSync(join(tmpdir(), 'omx-3284-tracker-authority-fence-'));
585
585
  try {
586
586
  recordNativeSubagentAuthorityObservation(cwd, { sessionIds: ['canonical', 'root'], childThreadId: 'child', parentThreadId: 'root', rootNativeSessionId: 'root', authorityEvidence: 'valid' });
587
- assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '', /resume_agent\("child"\)/);
587
+ assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '', /resume_agent\("child"\)/);
588
588
  // Simulate a revocation whose transaction failed: the tracker still shows
589
589
  // valid authority, but the fence records the unpublished denial.
590
590
  fenceNativeSubagentAuthorities(cwd, ['child'], 'identity_untrusted_revocation_failed');
@@ -675,7 +675,7 @@ describe('subagents/tracker', () => {
675
675
  const cwd = mkdtempSync(join(tmpdir(), 'omx-3284-tracker-global-denial-'));
676
676
  try {
677
677
  recordNativeSubagentAuthorityObservation(cwd, { sessionIds: ['canonical', 'root'], childThreadId: 'child', parentThreadId: 'root', rootNativeSessionId: 'root', authorityEvidence: 'valid' });
678
- assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '', /resume_agent\("child"\)/);
678
+ assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '', /resume_agent\("child"\)/);
679
679
  // Simulates both the tracker revocation AND the locked fence failing.
680
680
  assert.equal(forceGlobalAuthorityDenial(cwd, 'identity_untrusted_denial_escalated'), true);
681
681
  assert.match(consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '', /unreadable_authority_fence/);
@@ -840,6 +840,102 @@ describe('subagents/tracker', () => {
840
840
  rmSync(cwd, { recursive: true, force: true });
841
841
  }
842
842
  });
843
+ it('covers hosts without resume_agent in SessionStart reopen guidance', () => {
844
+ const cwd = mkdtempSync(join(tmpdir(), 'omx-3866-tracker-no-resume-tool-'));
845
+ try {
846
+ const path = subagentTrackingPath(cwd);
847
+ mkdirSync(join(cwd, '.omx', 'state'), { recursive: true });
848
+ writeFileSync(path, JSON.stringify({ schemaVersion: 1, sessions: { root: { session_id: 'root', updated_at: '2026-07-23T00:00:00.000Z', threads: {
849
+ child: { thread_id: 'child', kind: 'subagent', provenance_kind: NATIVE_SUBAGENT_PROVENANCE, direct_child_root_id: 'root', direct_child_parent_id: 'root', status: 'available', first_seen_at: '2026-07-23T00:00:00.000Z', last_seen_at: '2026-07-23T00:00:00.000Z', turn_count: 1 },
850
+ } } } }));
851
+ // With partial availableTools not including resume_agent, availability is unknown:
852
+ // guidance lists targets and explicitly checks availability before requiring invocation (issue #3866)
853
+ const contextUnknown = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume', availableTools: ['Read', 'Edit'] }) ?? '';
854
+ assert.match(contextUnknown, /Unknown: resume_agent tool availability could not be determined/);
855
+ assert.match(contextUnknown, /"child"/);
856
+ assert.match(contextUnknown, /If resume_agent is callable, use resume_agent\(<id>\)/);
857
+ assert.match(contextUnknown, /If resume_agent is not callable, continue in the root/);
858
+ // Without availableTools provided, availability is also unknown (no inventory to check)
859
+ const contextNoInventory = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume' }) ?? '';
860
+ assert.match(contextNoInventory, /Unknown: resume_agent tool availability could not be determined/);
861
+ assert.match(contextNoInventory, /"child"/);
862
+ // With resume_agent explicitly in availableTools, guidance instructs the call
863
+ const contextWithTool = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume', availableTools: ['read', 'resume_agent', 'edit'] }) ?? '';
864
+ assert.match(contextWithTool, /Reopen these persisted subagents/);
865
+ assert.match(contextWithTool, /resume_agent\("child"\)/);
866
+ assert.doesNotMatch(contextWithTool, /Unknown/);
867
+ }
868
+ finally {
869
+ rmSync(cwd, { recursive: true, force: true });
870
+ }
871
+ });
872
+ it('handles tool inventory with object entry shapes (name/tool_name/toolName fields)', () => {
873
+ const cwd = mkdtempSync(join(tmpdir(), 'omx-3866-tracker-tool-objects-'));
874
+ try {
875
+ const path = subagentTrackingPath(cwd);
876
+ mkdirSync(join(cwd, '.omx', 'state'), { recursive: true });
877
+ writeFileSync(path, JSON.stringify({ schemaVersion: 1, sessions: { root: { session_id: 'root', updated_at: '2026-07-23T00:00:00.000Z', threads: {
878
+ child: { thread_id: 'child', kind: 'subagent', provenance_kind: NATIVE_SUBAGENT_PROVENANCE, direct_child_root_id: 'root', direct_child_parent_id: 'root', status: 'available', first_seen_at: '2026-07-23T00:00:00.000Z', last_seen_at: '2026-07-23T00:00:00.000Z', turn_count: 1 },
879
+ } } } }));
880
+ // Object entry with 'name' field
881
+ const contextNameField = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume', availableTools: [{ name: 'Read' }, { name: 'resume_agent' }, { name: 'Edit' }] }) ?? '';
882
+ assert.match(contextNameField, /Reopen these persisted subagents/);
883
+ assert.match(contextNameField, /resume_agent\("child"\)/);
884
+ // Object entry with 'tool_name' field
885
+ const contextToolName = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume', availableTools: [{ tool_name: 'Read' }, { tool_name: 'resume_agent' }] }) ?? '';
886
+ assert.match(contextToolName, /Reopen these persisted subagents/);
887
+ assert.match(contextToolName, /resume_agent\("child"\)/);
888
+ // Object entry with 'toolName' field
889
+ const contextToolNameCamel = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume', availableTools: [{ toolName: 'Read' }, { toolName: 'resume_agent' }] }) ?? '';
890
+ assert.match(contextToolNameCamel, /Reopen these persisted subagents/);
891
+ assert.match(contextToolNameCamel, /resume_agent\("child"\)/);
892
+ // Object entry without matching fields (unknown)
893
+ const contextEmptyObject = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume', availableTools: [{ name: undefined, tool_name: undefined, toolName: undefined }] }) ?? '';
894
+ assert.match(contextEmptyObject, /Unknown: resume_agent tool availability could not be determined/);
895
+ assert.match(contextEmptyObject, /If resume_agent is callable/);
896
+ }
897
+ finally {
898
+ rmSync(cwd, { recursive: true, force: true });
899
+ }
900
+ });
901
+ it('emits unavailable warning when explicit unsupported native subagent evidence is provided', () => {
902
+ const cwd = mkdtempSync(join(tmpdir(), 'omx-3866-tracker-unsupported-evidence-'));
903
+ try {
904
+ const path = subagentTrackingPath(cwd);
905
+ mkdirSync(join(cwd, '.omx', 'state'), { recursive: true });
906
+ writeFileSync(path, JSON.stringify({ schemaVersion: 1, sessions: { root: { session_id: 'root', updated_at: '2026-07-23T00:00:00.000Z', threads: {
907
+ child: { thread_id: 'child', kind: 'subagent', provenance_kind: NATIVE_SUBAGENT_PROVENANCE, direct_child_root_id: 'root', direct_child_parent_id: 'root', status: 'available', first_seen_at: '2026-07-23T00:00:00.000Z', last_seen_at: '2026-07-23T00:00:00.000Z', turn_count: 1 },
908
+ } } } }));
909
+ // With explicit unsupported evidence, resume_agent is unavailable even with tool in inventory
910
+ const unsupportedEvidence = { status: 'unsupported', reason: 'native_subagents_unsupported', source: 'hook_payload_capability', evidenceSummary: 'test evidence' };
911
+ const contextUnavailable = consumeDirectChildReopenContext(cwd, {
912
+ sessionId: 'root',
913
+ rootNativeSessionId: 'root',
914
+ source: 'resume',
915
+ availableTools: ['read', 'resume_agent', 'edit'],
916
+ unsupportedNativeSubagentEvidence: unsupportedEvidence,
917
+ }) ?? '';
918
+ // Should emit warning about unavailable resume_agent
919
+ assert.match(contextUnavailable, /Warning: resume_agent tool is not available/);
920
+ assert.doesNotMatch(contextUnavailable, /resume_agent\(/);
921
+ assert.match(contextUnavailable, /Do not spawn a same-role\/same-lane replacement/);
922
+ // With unsupported evidence but without availableTools, still unavailable (not unknown)
923
+ const contextUnavailableNoTools = consumeDirectChildReopenContext(cwd, {
924
+ sessionId: 'root',
925
+ rootNativeSessionId: 'root',
926
+ source: 'resume',
927
+ unsupportedNativeSubagentEvidence: unsupportedEvidence,
928
+ }) ?? '';
929
+ assert.match(contextUnavailableNoTools, /Warning: resume_agent tool is not available/);
930
+ assert.doesNotMatch(contextUnavailableNoTools, /Unknown:/);
931
+ // Without unsupported evidence, back to unknown state (no tools in inventory)
932
+ const contextUnknown = consumeDirectChildReopenContext(cwd, { sessionId: 'root', rootNativeSessionId: 'root', source: 'resume' }) ?? '';
933
+ assert.match(contextUnknown, /Unknown: resume_agent tool availability could not be determined/);
934
+ }
935
+ finally {
936
+ rmSync(cwd, { recursive: true, force: true });
937
+ }
938
+ });
843
939
  it('revokes direct-child authority when an authoritative observation moves it under a nested parent', () => {
844
940
  const cwd = mkdtempSync(join(tmpdir(), 'omx-3284-tracker-direct-to-nested-'));
845
941
  try {
@@ -872,7 +968,7 @@ describe('subagents/tracker', () => {
872
968
  const cwd = mkdtempSync(join(tmpdir(), 'omx-3284-tracker-reopen-first-'));
873
969
  try {
874
970
  recordNativeSubagentAuthorityObservation(cwd, { sessionIds: ['canonical', 'root'], childThreadId: 'child', parentThreadId: 'root', rootNativeSessionId: 'root' });
875
- const first = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '';
971
+ const first = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '';
876
972
  assert.match(first, /resume_agent\("child"\)/);
877
973
  const requestedAt = JSON.parse(readFileSync(subagentTrackingPath(cwd), 'utf8')).sessions.canonical.threads.child.resume_requested_at;
878
974
  assert.match(requestedAt, /^\d{4}-\d{2}-\d{2}T/);
@@ -925,7 +1021,7 @@ describe('subagents/tracker', () => {
925
1021
  competitor = spawnAuthorityCompetitor(cwd, 'revoke', readyPath, resultPath);
926
1022
  waitForFileSync(readyPath, 10_000);
927
1023
  });
928
- const first = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume' }) ?? '';
1024
+ const first = consumeDirectChildReopenContext(cwd, { sessionId: 'canonical', rootNativeSessionId: 'root', source: 'resume', availableTools: ['resume_agent'] }) ?? '';
929
1025
  __setCrossProcessPublishBarrierForTest(null);
930
1026
  assert.match(first, /resume_agent\("child"\)/);
931
1027
  const requestedAt = JSON.parse(readFileSync(subagentTrackingPath(cwd), 'utf8')).sessions.canonical.threads.child.resume_requested_at;