remote-codex 0.11.42 → 0.11.44

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.
@@ -10,13 +10,13 @@
10
10
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
11
11
  <link rel="manifest" href="/site.webmanifest" />
12
12
  <title>Remote Codex</title>
13
- <script type="module" crossorigin src="/assets/index-BvxWpPKW.js"></script>
13
+ <script type="module" crossorigin src="/assets/index-CO8cU7a0.js"></script>
14
14
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-Dfg_6BLf.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/ui-vendor-CuR8GHb0.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVQUpZ8C.js">
17
17
  <link rel="modulepreload" crossorigin href="/assets/terminal-vendor-C5bTa-Ka.js">
18
18
  <link rel="modulepreload" crossorigin href="/assets/markdown-vendor-RZk8L7-L.js">
19
- <link rel="modulepreload" crossorigin href="/assets/thread-ui-BLThton-.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/thread-ui-Dmrigdek.js">
20
20
  <link rel="stylesheet" crossorigin href="/assets/graph-vendor-C5ap-Sga.css">
21
21
  <link rel="stylesheet" crossorigin href="/assets/terminal-vendor-Beg8tuEN.css">
22
22
  <link rel="stylesheet" crossorigin href="/assets/index-BcCLYWAf.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-codex",
3
- "version": "0.11.42",
3
+ "version": "0.11.44",
4
4
  "description": "Local web supervisor for Codex workspaces and threads.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -88,11 +88,28 @@ class FakeQuery implements Query {
88
88
  return [
89
89
  {
90
90
  value: 'claude-sonnet-4-5',
91
+ resolvedModel: 'claude-sonnet-4-5-20250929',
91
92
  displayName: 'Claude Sonnet 4.5',
92
93
  description: 'Test model',
93
94
  supportsEffort: true,
94
95
  supportedEffortLevels: ['low', 'medium', 'high'],
95
96
  },
97
+ {
98
+ value: 'claude-fable-5[1m]',
99
+ resolvedModel: 'claude-fable-5',
100
+ displayName: 'Fable',
101
+ description: 'Fable 5 test model',
102
+ supportsEffort: true,
103
+ supportedEffortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
104
+ },
105
+ {
106
+ value: 'opus[1m]',
107
+ resolvedModel: 'claude-opus-5[1m]',
108
+ displayName: 'Opus (1M context)',
109
+ description: 'Opus 5 test model',
110
+ supportsEffort: true,
111
+ supportedEffortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
112
+ },
96
113
  ];
97
114
  }
98
115
  async supportedAgents(): Promise<any[]> {
@@ -801,14 +818,17 @@ describe('ClaudeRuntimeAdapter', () => {
801
818
  await expect(adapter.listLoadedSessions()).resolves.toEqual(['claude-session-1']);
802
819
  });
803
820
 
804
- it('exposes Claude Code model aliases and enables the 1M context beta', async () => {
821
+ it('discovers versioned Claude Code model labels while idle and enables the 1M context beta', async () => {
805
822
  const sdkOptions: Record<string, unknown>[] = [];
823
+ const queries: FakeQuery[] = [];
806
824
  const adapter = new ClaudeRuntimeAdapter({
807
825
  home: '/tmp/claude-home',
808
826
  command: 'claude',
809
827
  query: ((params: { prompt: string; options: Record<string, unknown> }) => {
810
828
  sdkOptions.push(params.options);
811
- return new FakeQuery([systemInit(), result()]);
829
+ const query = new FakeQuery([systemInit(), result()]);
830
+ queries.push(query);
831
+ return query;
812
832
  }) as any,
813
833
  listSessions: (async () => [] satisfies SDKSessionInfo[]) as any,
814
834
  getSessionInfo: (async () => null) as any,
@@ -819,23 +839,38 @@ describe('ClaudeRuntimeAdapter', () => {
819
839
  expect.arrayContaining([
820
840
  expect.objectContaining({
821
841
  model: 'sonnet',
822
- displayName: 'Claude Sonnet',
842
+ displayName: 'Sonnet · 4.5',
823
843
  isDefault: true,
824
844
  }),
825
845
  expect.objectContaining({
826
846
  model: 'sonnet[1m]',
827
- displayName: 'Claude Sonnet 1M',
847
+ displayName: 'Sonnet · 4.5 (1M context)',
828
848
  }),
829
849
  expect.objectContaining({
830
850
  model: 'fable',
831
- displayName: 'Claude Fable',
851
+ displayName: 'Fable · 5',
832
852
  defaultReasoningEffort: 'medium',
833
853
  supportedReasoningEfforts: expect.arrayContaining([
834
854
  expect.objectContaining({ reasoningEffort: 'max' }),
835
855
  ]),
836
856
  }),
857
+ expect.objectContaining({
858
+ model: 'opus',
859
+ displayName: 'Opus · 5',
860
+ }),
861
+ expect.objectContaining({
862
+ model: 'opus[1m]',
863
+ displayName: 'Opus · 5 (1M context)',
864
+ }),
865
+ ]),
866
+ );
867
+ await expect(adapter.listModels()).resolves.not.toEqual(
868
+ expect.arrayContaining([
869
+ expect.objectContaining({ model: 'claude-fable-5[1m]' }),
837
870
  ]),
838
871
  );
872
+ expect(queries[0]?.closed).toBe(true);
873
+ expect(queries).toHaveLength(1);
839
874
 
840
875
  await adapter.startSession({
841
876
  cwd: '/tmp/workspace',
@@ -844,7 +879,7 @@ describe('ClaudeRuntimeAdapter', () => {
844
879
  sandboxMode: 'workspace-write',
845
880
  });
846
881
 
847
- expect(sdkOptions[0]).toMatchObject({
882
+ expect(sdkOptions.at(-1)).toMatchObject({
848
883
  model: 'sonnet',
849
884
  betas: ['context-1m-2025-08-07'],
850
885
  });
@@ -177,6 +177,7 @@ interface SDKSessionInfo {
177
177
  type SessionMessage = SDKMessage;
178
178
  interface ModelInfo {
179
179
  value: string;
180
+ resolvedModel?: string;
180
181
  displayName: string;
181
182
  description: string;
182
183
  supportedEffortLevels?: string[];
@@ -711,7 +712,7 @@ function mapModelInfo(model: ModelInfo, index: number): AgentModel {
711
712
  return {
712
713
  id: model.value,
713
714
  model: model.value,
714
- displayName: model.displayName,
715
+ displayName: versionedClaudeModelDisplayName(model),
715
716
  description: model.description,
716
717
  isDefault: index === 0,
717
718
  hidden: false,
@@ -723,11 +724,56 @@ function mapModelInfo(model: ModelInfo, index: number): AgentModel {
723
724
  };
724
725
  }
725
726
 
727
+ function versionedClaudeModelDisplayName(model: ModelInfo) {
728
+ const resolved = model.resolvedModel?.match(
729
+ /^claude-(sonnet|opus|haiku|fable)-(\d+)(?:-(\d+))?(?:-|\[|$)/i,
730
+ );
731
+ if (!resolved) {
732
+ return model.displayName;
733
+ }
734
+
735
+ const family = `${resolved[1]![0]!.toUpperCase()}${resolved[1]!.slice(1).toLowerCase()}`;
736
+ const version = resolved[3] ? `${resolved[2]}.${resolved[3]}` : resolved[2]!;
737
+ if (model.value === 'default') {
738
+ return `Default · ${family} ${version}`;
739
+ }
740
+
741
+ const qualifier = model.displayName.match(/\s*(\([^)]+\))\s*$/)?.[1];
742
+ return `${family} · ${version}${qualifier ? ` ${qualifier}` : ''}`;
743
+ }
744
+
745
+ function versionedAlias(
746
+ alias: AgentModel,
747
+ discovered: AgentModel | undefined,
748
+ qualifier?: string,
749
+ ) {
750
+ if (!discovered) {
751
+ return alias;
752
+ }
753
+ return {
754
+ ...alias,
755
+ displayName: `${discovered.displayName.replace(/\s+\([^)]+\)\s*$/, '')}${qualifier ?? ''}`,
756
+ description: discovered.description,
757
+ supportedReasoningEfforts: discovered.supportedReasoningEfforts,
758
+ defaultReasoningEffort: discovered.defaultReasoningEffort,
759
+ };
760
+ }
761
+
726
762
  function withClaudeCodeModelAliases(models: AgentModel[]) {
727
- const output = [...models];
728
- const defaultSonnet = DEFAULT_CLAUDE_MODELS[0]!;
729
- const oneMillionSonnet = DEFAULT_CLAUDE_MODELS[1]!;
730
- const fable = DEFAULT_CLAUDE_MODELS[2]!;
763
+ const discoveredSonnet = models.find((model) => /^Sonnet · /.test(model.displayName));
764
+ const discoveredFable = models.find((model) => /^Fable · /.test(model.displayName));
765
+ const discoveredOpus = models.find((model) => /^Opus · /.test(model.displayName));
766
+ const output = models.filter(
767
+ (model) => model !== discoveredFable || discoveredFable.model === 'fable',
768
+ );
769
+ const defaultSonnet = versionedAlias(DEFAULT_CLAUDE_MODELS[0]!, discoveredSonnet);
770
+ const oneMillionSonnet = versionedAlias(
771
+ DEFAULT_CLAUDE_MODELS[1]!,
772
+ discoveredSonnet,
773
+ ' (1M context)',
774
+ );
775
+ const fable = versionedAlias(DEFAULT_CLAUDE_MODELS[2]!, discoveredFable);
776
+ const opus = versionedAlias(DEFAULT_CLAUDE_MODELS[3]!, discoveredOpus);
731
777
  const hasSonnetAlias = output.some((model) => model.model === 'sonnet');
732
778
  if (!hasSonnetAlias) {
733
779
  output.unshift(defaultSonnet);
@@ -738,6 +784,10 @@ function withClaudeCodeModelAliases(models: AgentModel[]) {
738
784
  if (!output.some((model) => model.model === 'fable')) {
739
785
  output.splice(2, 0, fable);
740
786
  }
787
+ if (!output.some((model) => model.model === 'opus')) {
788
+ const discoveredOpusIndex = output.findIndex((model) => /^Opus · /.test(model.displayName));
789
+ output.splice(discoveredOpusIndex >= 0 ? discoveredOpusIndex : output.length, 0, opus);
790
+ }
741
791
  return output.map((model, index) => ({
742
792
  ...model,
743
793
  isDefault: index === 0,
@@ -1344,6 +1394,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1344
1394
  >();
1345
1395
  private subscriptionAuthKind: 'subscription' | 'apiKey' | 'unknown' = 'unknown';
1346
1396
  private subscriptionUsageObservedAt: string | null = null;
1397
+ private modelOptionsCache: AgentModel[] | null = null;
1347
1398
  private readonly historicalTurnIdAliases = new Map<string, Map<string, string>>();
1348
1399
  private readonly clientApp: string;
1349
1400
  private sdkLoadError: string | null = null;
@@ -1509,15 +1560,35 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1509
1560
 
1510
1561
  async listModels(): Promise<AgentModel[]> {
1511
1562
  const active = [...this.activeTurns.values()][0];
1512
- if (!active) {
1513
- return DEFAULT_CLAUDE_MODELS;
1563
+ if (!active && this.modelOptionsCache) {
1564
+ return this.modelOptionsCache;
1514
1565
  }
1515
1566
 
1567
+ let query = active?.query ?? null;
1568
+
1516
1569
  try {
1517
- const models = await active.query.supportedModels();
1518
- return withClaudeCodeModelAliases(models.map(mapModelInfo));
1570
+ query ??= this.queryFactory({
1571
+ prompt: '',
1572
+ options: queryOptionsForRuntime({
1573
+ home: this.options.home,
1574
+ command: this.options.command,
1575
+ clientApp: this.clientApp,
1576
+ approvalMode: 'guarded',
1577
+ includePartialMessages: false,
1578
+ tools: [],
1579
+ maxTurns: 1,
1580
+ }),
1581
+ });
1582
+ const models = await query.supportedModels();
1583
+ const mapped = withClaudeCodeModelAliases(models.map(mapModelInfo));
1584
+ this.modelOptionsCache = mapped;
1585
+ return mapped;
1519
1586
  } catch {
1520
- return DEFAULT_CLAUDE_MODELS;
1587
+ return this.modelOptionsCache ?? DEFAULT_CLAUDE_MODELS;
1588
+ } finally {
1589
+ if (!active && query) {
1590
+ query.close();
1591
+ }
1521
1592
  }
1522
1593
  }
1523
1594