remote-codex 0.11.25 → 0.11.26

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.
@@ -4,16 +4,16 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Remote Codex Supervisor</title>
7
- <script type="module" crossorigin src="/assets/index-JrzRGVx3.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-CIcJHgFF.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-CgLzZcV4.js">
9
9
  <link rel="modulepreload" crossorigin href="/assets/ui-vendor-CeKGesq3.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVPtkh3h.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/terminal-vendor-B365Go3Z.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/markdown-vendor-BQJfKm05.js">
13
- <link rel="modulepreload" crossorigin href="/assets/thread-ui-Ck4oSYRQ.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/thread-ui-CaDgVQIY.js">
14
14
  <link rel="stylesheet" crossorigin href="/assets/graph-vendor-C5ap-Sga.css">
15
15
  <link rel="stylesheet" crossorigin href="/assets/terminal-vendor-Beg8tuEN.css">
16
- <link rel="stylesheet" crossorigin href="/assets/index-Ch_S2n2I.css">
16
+ <link rel="stylesheet" crossorigin href="/assets/index-BVYGmxwf.css">
17
17
  </head>
18
18
  <body class="bg-stone-950">
19
19
  <div id="root"></div>
@@ -54,16 +54,23 @@
54
54
  "supportsFastMode": false
55
55
  },
56
56
  "sonnet": {
57
- "inputUsdPerMillion": 3,
58
- "cachedInputUsdPerMillion": 0.3,
59
- "outputUsdPerMillion": 15,
57
+ "inputUsdPerMillion": 2,
58
+ "cachedInputUsdPerMillion": 0.2,
59
+ "outputUsdPerMillion": 10,
60
60
  "supportsFastMode": false,
61
- "contextWindowTokens": 200000
61
+ "contextWindowTokens": 1000000
62
62
  },
63
63
  "sonnet[1m]": {
64
- "inputUsdPerMillion": 6,
65
- "cachedInputUsdPerMillion": 0.6,
66
- "outputUsdPerMillion": 22.5,
64
+ "inputUsdPerMillion": 2,
65
+ "cachedInputUsdPerMillion": 0.2,
66
+ "outputUsdPerMillion": 10,
67
+ "supportsFastMode": false,
68
+ "contextWindowTokens": 1000000
69
+ },
70
+ "fable": {
71
+ "inputUsdPerMillion": 10,
72
+ "cachedInputUsdPerMillion": 1,
73
+ "outputUsdPerMillion": 50,
67
74
  "supportsFastMode": false,
68
75
  "contextWindowTokens": 1000000
69
76
  },
@@ -72,7 +79,7 @@
72
79
  "cachedInputUsdPerMillion": 0.5,
73
80
  "outputUsdPerMillion": 25,
74
81
  "supportsFastMode": false,
75
- "contextWindowTokens": 200000
82
+ "contextWindowTokens": 1000000
76
83
  },
77
84
  "haiku": {
78
85
  "inputUsdPerMillion": 1,
@@ -81,6 +88,27 @@
81
88
  "supportsFastMode": false,
82
89
  "contextWindowTokens": 200000
83
90
  },
91
+ "claude-fable-5": {
92
+ "inputUsdPerMillion": 10,
93
+ "cachedInputUsdPerMillion": 1,
94
+ "outputUsdPerMillion": 50,
95
+ "supportsFastMode": false,
96
+ "contextWindowTokens": 1000000
97
+ },
98
+ "claude-mythos-5": {
99
+ "inputUsdPerMillion": 10,
100
+ "cachedInputUsdPerMillion": 1,
101
+ "outputUsdPerMillion": 50,
102
+ "supportsFastMode": false,
103
+ "contextWindowTokens": 1000000
104
+ },
105
+ "claude-sonnet-5": {
106
+ "inputUsdPerMillion": 2,
107
+ "cachedInputUsdPerMillion": 0.2,
108
+ "outputUsdPerMillion": 10,
109
+ "supportsFastMode": false,
110
+ "contextWindowTokens": 1000000
111
+ },
84
112
  "claude-sonnet-4-5": {
85
113
  "inputUsdPerMillion": 3,
86
114
  "cachedInputUsdPerMillion": 0.3,
@@ -93,7 +121,15 @@
93
121
  "cachedInputUsdPerMillion": 0.3,
94
122
  "outputUsdPerMillion": 15,
95
123
  "supportsFastMode": false,
96
- "contextWindowTokens": 200000
124
+ "contextWindowTokens": 1000000
125
+ },
126
+ "claude-opus-4-8": {
127
+ "inputUsdPerMillion": 5,
128
+ "cachedInputUsdPerMillion": 0.5,
129
+ "outputUsdPerMillion": 25,
130
+ "supportsFastMode": true,
131
+ "fastMultiplier": 2,
132
+ "contextWindowTokens": 1000000
97
133
  },
98
134
  "claude-opus-4-5": {
99
135
  "inputUsdPerMillion": 5,
@@ -102,10 +138,32 @@
102
138
  "supportsFastMode": false,
103
139
  "contextWindowTokens": 200000
104
140
  },
141
+ "claude-opus-4-6": {
142
+ "inputUsdPerMillion": 5,
143
+ "cachedInputUsdPerMillion": 0.5,
144
+ "outputUsdPerMillion": 25,
145
+ "supportsFastMode": false,
146
+ "contextWindowTokens": 1000000
147
+ },
105
148
  "claude-opus-4-7": {
106
149
  "inputUsdPerMillion": 5,
107
150
  "cachedInputUsdPerMillion": 0.5,
108
151
  "outputUsdPerMillion": 25,
152
+ "supportsFastMode": true,
153
+ "fastMultiplier": 6,
154
+ "contextWindowTokens": 1000000
155
+ },
156
+ "claude-opus-4-1": {
157
+ "inputUsdPerMillion": 15,
158
+ "cachedInputUsdPerMillion": 1.5,
159
+ "outputUsdPerMillion": 75,
160
+ "supportsFastMode": false,
161
+ "contextWindowTokens": 200000
162
+ },
163
+ "claude-opus-4": {
164
+ "inputUsdPerMillion": 15,
165
+ "cachedInputUsdPerMillion": 1.5,
166
+ "outputUsdPerMillion": 75,
109
167
  "supportsFastMode": false,
110
168
  "contextWindowTokens": 200000
111
169
  },
@@ -115,6 +173,13 @@
115
173
  "outputUsdPerMillion": 5,
116
174
  "supportsFastMode": false,
117
175
  "contextWindowTokens": 200000
176
+ },
177
+ "claude-haiku-3-5": {
178
+ "inputUsdPerMillion": 0.8,
179
+ "cachedInputUsdPerMillion": 0.08,
180
+ "outputUsdPerMillion": 4,
181
+ "supportsFastMode": false,
182
+ "contextWindowTokens": 200000
118
183
  }
119
184
  }
120
185
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-codex",
3
- "version": "0.11.25",
3
+ "version": "0.11.26",
4
4
  "description": "Local web supervisor for Codex workspaces and threads.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -94,6 +94,22 @@ function compactJson(value: unknown): string {
94
94
  }
95
95
  }
96
96
 
97
+ function joinReasoningText(left: string, right: string) {
98
+ if (!left) {
99
+ return right;
100
+ }
101
+ if (!right) {
102
+ return left;
103
+ }
104
+ if (right.startsWith(left)) {
105
+ return right;
106
+ }
107
+ if (left.endsWith(right)) {
108
+ return left;
109
+ }
110
+ return `${left}\n\n${right}`;
111
+ }
112
+
97
113
  function readableToolName(toolName: string) {
98
114
  if (CLAUDE_TOOL_LABELS[toolName]) {
99
115
  return CLAUDE_TOOL_LABELS[toolName];
@@ -486,11 +502,16 @@ export function assistantMessageToHistoryItems(
486
502
  if (type === 'thinking') {
487
503
  const thinking = thinkingTextFromBlock(block);
488
504
  if (thinking) {
489
- items.push({
490
- id: `${input.messageId}:content:${index}`,
491
- kind: 'reasoning',
492
- text: thinking,
493
- });
505
+ const previous = items.at(-1);
506
+ if (previous?.kind === 'reasoning') {
507
+ previous.text = joinReasoningText(previous.text, thinking);
508
+ } else {
509
+ items.push({
510
+ id: `${input.messageId}:content:${index}`,
511
+ kind: 'reasoning',
512
+ text: thinking,
513
+ });
514
+ }
494
515
  }
495
516
  continue;
496
517
  }
@@ -715,12 +736,15 @@ export function buildAgentTurn(input: {
715
736
  items: AgentHistoryItem[];
716
737
  rawTurn?: unknown;
717
738
  }): AgentTurn {
739
+ const startedAt = input.startedAt ?? null;
718
740
  const turn: AgentTurn = {
719
741
  providerTurnId: input.providerTurnId,
720
- startedAt: input.startedAt ?? null,
742
+ startedAt,
721
743
  status: input.status,
722
744
  error: input.error ? { message: input.error } : null,
723
- items: input.items,
745
+ items: input.items.map((item) =>
746
+ item.createdAt || !startedAt ? item : { ...item, createdAt: startedAt },
747
+ ),
724
748
  };
725
749
  if (input.rawTurn !== undefined) {
726
750
  turn.rawTurn = input.rawTurn;
@@ -4,7 +4,7 @@ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
 
6
6
  import { ClaudeRuntimeAdapter } from './runtimeAdapter';
7
- import { hiddenInitPrompt } from './historyItems';
7
+ import { assistantMessageToHistoryItems, hiddenInitPrompt } from './historyItems';
8
8
  import type { AgentRuntimeEvent } from '../../agent-runtime/src/index';
9
9
 
10
10
  type SDKMessage = Record<string, any>;
@@ -665,6 +665,225 @@ describe('ClaudeRuntimeAdapter', () => {
665
665
  await expect(adapter.listLoadedSessions()).resolves.toEqual(['claude-session-1']);
666
666
  });
667
667
 
668
+ it('keeps active Claude sessions running in list summaries', async () => {
669
+ const heldQuery = new FakeQuery([systemInit()], { holdOpen: true });
670
+ const adapter = makeAdapter((prompt) => {
671
+ if (prompt === hiddenInitPrompt()) {
672
+ return [systemInit(), result()];
673
+ }
674
+ return heldQuery;
675
+ });
676
+
677
+ await adapter.startSession({
678
+ cwd: '/tmp/workspace',
679
+ model: 'sonnet',
680
+ approvalMode: 'guarded',
681
+ sandboxMode: 'workspace-write',
682
+ });
683
+ await adapter.startTurn({
684
+ providerSessionId: 'claude-session-1',
685
+ providerTurnId: 'turn-1',
686
+ prompt: 'Keep running',
687
+ } as any);
688
+ await wait();
689
+
690
+ await expect(adapter.listSessions()).resolves.toEqual([
691
+ expect.objectContaining({
692
+ providerSessionId: 'claude-session-1',
693
+ status: 'running',
694
+ }),
695
+ ]);
696
+
697
+ heldQuery.close();
698
+ });
699
+
700
+ it('merges adjacent Claude thinking blocks into one reasoning item', () => {
701
+ const items = assistantMessageToHistoryItems({
702
+ messageId: 'msg_1',
703
+ message: {
704
+ role: 'assistant',
705
+ content: [
706
+ { type: 'thinking', thinking: 'First thought.' },
707
+ { type: 'thinking', thinking: 'Second thought.' },
708
+ { type: 'text', text: 'Final answer.' },
709
+ ],
710
+ },
711
+ });
712
+
713
+ expect(items).toEqual([
714
+ expect.objectContaining({
715
+ kind: 'reasoning',
716
+ text: 'First thought.\n\nSecond thought.',
717
+ }),
718
+ expect.objectContaining({
719
+ kind: 'agentMessage',
720
+ text: 'Final answer.',
721
+ }),
722
+ ]);
723
+ });
724
+
725
+ it('merges adjacent Claude reasoning messages while reading history', async () => {
726
+ const adapter = new ClaudeRuntimeAdapter({
727
+ home: '/tmp/claude-home',
728
+ query: (() => new FakeQuery([])) as any,
729
+ getSessionInfo: (async () => ({
730
+ sessionId: 'claude-session-1',
731
+ summary: 'Existing session',
732
+ lastModified: 1_772_000_000_000,
733
+ createdAt: 1_771_000_000_000,
734
+ cwd: '/tmp/workspace',
735
+ })) as any,
736
+ listSessions: (async () => []) as any,
737
+ getSessionMessages: (async () => [
738
+ {
739
+ type: 'user',
740
+ uuid: 'reasoning-user',
741
+ session_id: 'claude-session-1',
742
+ message: { role: 'user', content: 'Inspect the dev branch.' },
743
+ parent_tool_use_id: null,
744
+ },
745
+ {
746
+ type: 'assistant',
747
+ uuid: 'reasoning-1',
748
+ session_id: 'claude-session-1',
749
+ message: {
750
+ role: 'assistant',
751
+ content: [{ type: 'thinking', thinking: 'I should inspect dev.' }],
752
+ },
753
+ parent_tool_use_id: null,
754
+ },
755
+ {
756
+ type: 'assistant',
757
+ uuid: 'reasoning-2',
758
+ session_id: 'claude-session-1',
759
+ message: {
760
+ role: 'assistant',
761
+ content: [{ type: 'thinking', thinking: 'I should inspect dev.' }],
762
+ },
763
+ parent_tool_use_id: null,
764
+ },
765
+ {
766
+ type: 'assistant',
767
+ uuid: 'reasoning-3',
768
+ session_id: 'claude-session-1',
769
+ message: {
770
+ role: 'assistant',
771
+ content: [
772
+ {
773
+ type: 'thinking',
774
+ thinking: 'I should inspect dev. Then compare the merge.',
775
+ },
776
+ ],
777
+ },
778
+ parent_tool_use_id: null,
779
+ },
780
+ ] satisfies SessionMessage[]) as any,
781
+ });
782
+
783
+ const session = await adapter.readSession('claude-session-1');
784
+ expect(session.turns[0]?.items).toEqual([
785
+ expect.objectContaining({ kind: 'userMessage' }),
786
+ expect.objectContaining({
787
+ kind: 'reasoning',
788
+ text: 'I should inspect dev. Then compare the merge.',
789
+ }),
790
+ ]);
791
+ });
792
+
793
+ it('keeps late Claude subagent tool results in order before completing the turn', async () => {
794
+ const adapter = makeAdapter((prompt) => {
795
+ if (prompt === hiddenInitPrompt()) {
796
+ return [systemInit(), result()];
797
+ }
798
+ return [
799
+ systemInit(),
800
+ {
801
+ type: 'assistant',
802
+ message: {
803
+ id: 'msg_agent',
804
+ type: 'message',
805
+ role: 'assistant',
806
+ model: 'sonnet',
807
+ content: [
808
+ {
809
+ type: 'tool_use',
810
+ id: 'toolu_agent',
811
+ name: 'Task',
812
+ input: {
813
+ description: 'Inspect dependency graph',
814
+ prompt: 'Look for stale imports.',
815
+ },
816
+ },
817
+ ],
818
+ stop_reason: null,
819
+ stop_sequence: null,
820
+ stop_details: null,
821
+ usage: {} as any,
822
+ container: null,
823
+ context_management: null,
824
+ diagnostics: null,
825
+ },
826
+ parent_tool_use_id: null,
827
+ uuid: '00000000-0000-4000-8000-000000000031' as any,
828
+ session_id: 'claude-session-1',
829
+ },
830
+ result(),
831
+ {
832
+ type: 'user',
833
+ message: {
834
+ role: 'user',
835
+ content: [
836
+ {
837
+ type: 'tool_result',
838
+ tool_use_id: 'toolu_agent',
839
+ content: 'The subagent found one stale import.',
840
+ },
841
+ ],
842
+ },
843
+ parent_tool_use_id: null,
844
+ tool_use_result: 'The subagent found one stale import.',
845
+ uuid: '00000000-0000-4000-8000-000000000032' as any,
846
+ session_id: 'claude-session-1',
847
+ },
848
+ ];
849
+ });
850
+ const events: AgentRuntimeEvent[] = [];
851
+ adapter.on('event', (event) => events.push(event));
852
+
853
+ await adapter.startTurn({
854
+ providerSessionId: 'claude-session-1',
855
+ displayTurnId: 'turn-subagent',
856
+ prompt: 'Use a subagent',
857
+ } as any);
858
+ await wait();
859
+
860
+ const completed = events.at(-1);
861
+ expect(completed).toMatchObject({
862
+ type: 'turn.completed',
863
+ turn: {
864
+ providerTurnId: 'turn-subagent',
865
+ status: 'completed',
866
+ },
867
+ });
868
+ expect(completed?.type === 'turn.completed' ? completed.turn.items : []).toEqual([
869
+ expect.objectContaining({ id: 'turn-subagent:user', kind: 'userMessage' }),
870
+ expect.objectContaining({
871
+ id: 'toolu_agent',
872
+ kind: 'agentToolCall',
873
+ status: 'completed',
874
+ detailText: expect.stringContaining('The subagent found one stale import.'),
875
+ }),
876
+ ]);
877
+ expect(
878
+ events.findIndex(
879
+ (event) =>
880
+ event.type === 'item.completed' &&
881
+ event.item.id === 'toolu_agent' &&
882
+ event.item.status === 'completed',
883
+ ),
884
+ ).toBeLessThan(events.length - 1);
885
+ });
886
+
668
887
  it('filters Claude generated titles derived from the synthetic init prompt', async () => {
669
888
  const adapter = new ClaudeRuntimeAdapter({
670
889
  home: '/tmp/claude-home',