remote-codex 0.11.30 → 0.11.31

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.
@@ -3,14 +3,20 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Remote Codex Supervisor</title>
7
- <script type="module" crossorigin src="/assets/index-BnpZn_3_.js"></script>
6
+ <meta name="theme-color" content="#101722" />
7
+ <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
8
+ <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
9
+ <link rel="icon" type="image/png" sizes="48x48" href="/favicon-48x48.png" />
10
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
11
+ <link rel="manifest" href="/site.webmanifest" />
12
+ <title>Remote Codex</title>
13
+ <script type="module" crossorigin src="/assets/index-BkwJP0KB.js"></script>
8
14
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-Dfg_6BLf.js">
9
15
  <link rel="modulepreload" crossorigin href="/assets/ui-vendor-CuR8GHb0.js">
10
16
  <link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVQUpZ8C.js">
11
17
  <link rel="modulepreload" crossorigin href="/assets/terminal-vendor-C5bTa-Ka.js">
12
18
  <link rel="modulepreload" crossorigin href="/assets/markdown-vendor-RZk8L7-L.js">
13
- <link rel="modulepreload" crossorigin href="/assets/thread-ui-C0VPL4Uk.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/thread-ui-BOqbjoiB.js">
14
20
  <link rel="stylesheet" crossorigin href="/assets/graph-vendor-C5ap-Sga.css">
15
21
  <link rel="stylesheet" crossorigin href="/assets/terminal-vendor-Beg8tuEN.css">
16
22
  <link rel="stylesheet" crossorigin href="/assets/index-CJFMmjP5.css">
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "Remote Codex",
3
+ "short_name": "Remote Codex",
4
+ "icons": [
5
+ {
6
+ "src": "/icon-192.png",
7
+ "sizes": "192x192",
8
+ "type": "image/png"
9
+ },
10
+ {
11
+ "src": "/icon-512.png",
12
+ "sizes": "512x512",
13
+ "type": "image/png"
14
+ }
15
+ ],
16
+ "theme_color": "#101722",
17
+ "background_color": "#101722",
18
+ "display": "standalone"
19
+ }
@@ -9,6 +9,50 @@
9
9
  }
10
10
  },
11
11
  "models": {
12
+ "gpt-5.6": {
13
+ "inputUsdPerMillion": 5,
14
+ "cachedInputUsdPerMillion": 0.5,
15
+ "cacheWriteInputUsdPerMillion": 6.25,
16
+ "outputUsdPerMillion": 30,
17
+ "supportsFastMode": true,
18
+ "contextWindowTokens": 353400,
19
+ "longContextThresholdTokens": 272000,
20
+ "longContextInputMultiplier": 2,
21
+ "longContextOutputMultiplier": 1.5
22
+ },
23
+ "gpt-5.6-sol": {
24
+ "inputUsdPerMillion": 5,
25
+ "cachedInputUsdPerMillion": 0.5,
26
+ "cacheWriteInputUsdPerMillion": 6.25,
27
+ "outputUsdPerMillion": 30,
28
+ "supportsFastMode": true,
29
+ "contextWindowTokens": 353400,
30
+ "longContextThresholdTokens": 272000,
31
+ "longContextInputMultiplier": 2,
32
+ "longContextOutputMultiplier": 1.5
33
+ },
34
+ "gpt-5.6-terra": {
35
+ "inputUsdPerMillion": 2.5,
36
+ "cachedInputUsdPerMillion": 0.25,
37
+ "cacheWriteInputUsdPerMillion": 3.125,
38
+ "outputUsdPerMillion": 15,
39
+ "supportsFastMode": true,
40
+ "contextWindowTokens": 353400,
41
+ "longContextThresholdTokens": 272000,
42
+ "longContextInputMultiplier": 2,
43
+ "longContextOutputMultiplier": 1.5
44
+ },
45
+ "gpt-5.6-luna": {
46
+ "inputUsdPerMillion": 1,
47
+ "cachedInputUsdPerMillion": 0.1,
48
+ "cacheWriteInputUsdPerMillion": 1.25,
49
+ "outputUsdPerMillion": 6,
50
+ "supportsFastMode": true,
51
+ "contextWindowTokens": 353400,
52
+ "longContextThresholdTokens": 272000,
53
+ "longContextInputMultiplier": 2,
54
+ "longContextOutputMultiplier": 1.5
55
+ },
12
56
  "gpt-5.5": {
13
57
  "inputUsdPerMillion": 5,
14
58
  "cachedInputUsdPerMillion": 0.5,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-codex",
3
- "version": "0.11.30",
3
+ "version": "0.11.31",
4
4
  "description": "Local web supervisor for Codex workspaces and threads.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -11,10 +11,14 @@ export type PricingTierKey = 'standard' | 'fast';
11
11
  interface ModelPricingEntry {
12
12
  inputUsdPerMillion: number;
13
13
  cachedInputUsdPerMillion: number;
14
+ cacheWriteInputUsdPerMillion?: number;
14
15
  outputUsdPerMillion: number;
15
16
  supportsFastMode: boolean;
16
17
  fastMultiplier?: number;
17
18
  contextWindowTokens?: number;
19
+ longContextThresholdTokens?: number;
20
+ longContextInputMultiplier?: number;
21
+ longContextOutputMultiplier?: number;
18
22
  }
19
23
 
20
24
  interface PricingTierConfig {
@@ -119,10 +123,14 @@ function parsePricingConfig(raw: unknown): PricingConfig {
119
123
  const entry = value as {
120
124
  inputUsdPerMillion?: unknown;
121
125
  cachedInputUsdPerMillion?: unknown;
126
+ cacheWriteInputUsdPerMillion?: unknown;
122
127
  outputUsdPerMillion?: unknown;
123
128
  supportsFastMode?: unknown;
124
129
  fastMultiplier?: unknown;
125
130
  contextWindowTokens?: unknown;
131
+ longContextThresholdTokens?: unknown;
132
+ longContextInputMultiplier?: unknown;
133
+ longContextOutputMultiplier?: unknown;
126
134
  };
127
135
 
128
136
  if (
@@ -133,6 +141,12 @@ function parsePricingConfig(raw: unknown): PricingConfig {
133
141
  ) {
134
142
  throw new Error(`Pricing config model "${modelKey}" has invalid fields.`);
135
143
  }
144
+ if (
145
+ entry.cacheWriteInputUsdPerMillion !== undefined &&
146
+ !isPositiveNumber(entry.cacheWriteInputUsdPerMillion)
147
+ ) {
148
+ throw new Error(`Pricing config model "${modelKey}" cacheWriteInputUsdPerMillion must be a non-negative number.`);
149
+ }
136
150
  if (
137
151
  entry.fastMultiplier !== undefined &&
138
152
  !isPositiveNumber(entry.fastMultiplier)
@@ -145,10 +159,22 @@ function parsePricingConfig(raw: unknown): PricingConfig {
145
159
  ) {
146
160
  throw new Error(`Pricing config model "${modelKey}" contextWindowTokens must be a non-negative number.`);
147
161
  }
162
+ for (const field of [
163
+ 'longContextThresholdTokens',
164
+ 'longContextInputMultiplier',
165
+ 'longContextOutputMultiplier',
166
+ ] as const) {
167
+ if (entry[field] !== undefined && !isPositiveNumber(entry[field])) {
168
+ throw new Error(`Pricing config model "${modelKey}" ${field} must be a non-negative number.`);
169
+ }
170
+ }
148
171
 
149
172
  models[modelKey] = {
150
173
  inputUsdPerMillion: entry.inputUsdPerMillion as number,
151
174
  cachedInputUsdPerMillion: entry.cachedInputUsdPerMillion as number,
175
+ ...(entry.cacheWriteInputUsdPerMillion !== undefined
176
+ ? { cacheWriteInputUsdPerMillion: entry.cacheWriteInputUsdPerMillion as number }
177
+ : {}),
152
178
  outputUsdPerMillion: entry.outputUsdPerMillion as number,
153
179
  supportsFastMode: entry.supportsFastMode,
154
180
  ...(entry.fastMultiplier !== undefined
@@ -157,6 +183,15 @@ function parsePricingConfig(raw: unknown): PricingConfig {
157
183
  ...(entry.contextWindowTokens !== undefined
158
184
  ? { contextWindowTokens: entry.contextWindowTokens as number }
159
185
  : {}),
186
+ ...(entry.longContextThresholdTokens !== undefined
187
+ ? { longContextThresholdTokens: entry.longContextThresholdTokens as number }
188
+ : {}),
189
+ ...(entry.longContextInputMultiplier !== undefined
190
+ ? { longContextInputMultiplier: entry.longContextInputMultiplier as number }
191
+ : {}),
192
+ ...(entry.longContextOutputMultiplier !== undefined
193
+ ? { longContextOutputMultiplier: entry.longContextOutputMultiplier as number }
194
+ : {}),
160
195
  };
161
196
  }
162
197
 
@@ -293,24 +328,48 @@ export function estimateTurnPrice(
293
328
  }
294
329
 
295
330
  const nonCachedInputTokens = Math.max(
296
- usage.total.inputTokens - usage.total.cachedInputTokens,
331
+ usage.total.inputTokens -
332
+ usage.total.cachedInputTokens -
333
+ (usage.total.cacheWriteInputTokens ?? 0),
297
334
  0,
298
335
  );
299
336
  const cachedInputTokens = Math.max(usage.total.cachedInputTokens, 0);
337
+ const cacheWriteInputTokens = Math.max(
338
+ usage.total.cacheWriteInputTokens ?? 0,
339
+ 0,
340
+ );
300
341
  const outputTokens = Math.max(usage.total.outputTokens, 0);
301
- const multiplier =
342
+ const tierMultiplier =
302
343
  tierKey === 'fast' && modelPricing.fastMultiplier !== undefined
303
344
  ? modelPricing.fastMultiplier
304
345
  : tier.multiplier;
346
+ const usesLongContextPricing =
347
+ typeof modelPricing.longContextThresholdTokens === 'number' &&
348
+ usage.total.inputTokens > modelPricing.longContextThresholdTokens;
349
+ const inputMultiplier =
350
+ tierMultiplier *
351
+ (usesLongContextPricing
352
+ ? modelPricing.longContextInputMultiplier ?? 1
353
+ : 1);
354
+ const outputMultiplier =
355
+ tierMultiplier *
356
+ (usesLongContextPricing
357
+ ? modelPricing.longContextOutputMultiplier ?? 1
358
+ : 1);
305
359
 
306
360
  const inputUsd =
307
- (nonCachedInputTokens * modelPricing.inputUsdPerMillion * multiplier) /
361
+ (nonCachedInputTokens * modelPricing.inputUsdPerMillion * inputMultiplier) /
308
362
  TOKEN_PRICE_DENOMINATOR;
309
363
  const cachedInputUsd =
310
- (cachedInputTokens * modelPricing.cachedInputUsdPerMillion * multiplier) /
364
+ (cachedInputTokens * modelPricing.cachedInputUsdPerMillion * inputMultiplier) /
365
+ TOKEN_PRICE_DENOMINATOR;
366
+ const cacheWriteInputUsd =
367
+ (cacheWriteInputTokens *
368
+ (modelPricing.cacheWriteInputUsdPerMillion ?? modelPricing.inputUsdPerMillion) *
369
+ inputMultiplier) /
311
370
  TOKEN_PRICE_DENOMINATOR;
312
371
  const outputUsd =
313
- (outputTokens * modelPricing.outputUsdPerMillion * multiplier) /
372
+ (outputTokens * modelPricing.outputUsdPerMillion * outputMultiplier) /
314
373
  TOKEN_PRICE_DENOMINATOR;
315
374
 
316
375
  return {
@@ -319,7 +378,8 @@ export function estimateTurnPrice(
319
378
  currency: pricingConfig.currency,
320
379
  inputUsd,
321
380
  cachedInputUsd,
381
+ cacheWriteInputUsd,
322
382
  outputUsd,
323
- totalUsd: inputUsd + cachedInputUsd + outputUsd,
383
+ totalUsd: inputUsd + cachedInputUsd + cacheWriteInputUsd + outputUsd,
324
384
  };
325
385
  }
@@ -364,6 +364,7 @@ interface ClaudeTokenUsageBreakdown {
364
364
  totalTokens: number;
365
365
  inputTokens: number;
366
366
  cachedInputTokens: number;
367
+ cacheWriteInputTokens: number;
367
368
  outputTokens: number;
368
369
  reasoningOutputTokens: number;
369
370
  }
@@ -792,6 +793,8 @@ function addClaudeUsage(
792
793
  totalTokens: left.totalTokens + right.totalTokens,
793
794
  inputTokens: left.inputTokens + right.inputTokens,
794
795
  cachedInputTokens: left.cachedInputTokens + right.cachedInputTokens,
796
+ cacheWriteInputTokens:
797
+ left.cacheWriteInputTokens + right.cacheWriteInputTokens,
795
798
  outputTokens: left.outputTokens + right.outputTokens,
796
799
  reasoningOutputTokens: left.reasoningOutputTokens + right.reasoningOutputTokens,
797
800
  };
@@ -821,6 +824,7 @@ function normalizeClaudeUsage(value: unknown): ClaudeTokenUsageBreakdown | null
821
824
  totalTokens,
822
825
  inputTokens,
823
826
  cachedInputTokens: cacheReadInputTokens,
827
+ cacheWriteInputTokens: cacheCreationInputTokens,
824
828
  outputTokens,
825
829
  reasoningOutputTokens: 0,
826
830
  };
@@ -52,6 +52,90 @@ describe('modelPricing', () => {
52
52
  expect(estimate?.totalUsd).toBeCloseTo(0.05025, 10);
53
53
  });
54
54
 
55
+ it('prices GPT-5.6 aliases and cache writes with the published rates', () => {
56
+ const usage = {
57
+ ...sampleUsage,
58
+ total: {
59
+ ...sampleUsage.total,
60
+ cacheWriteInputTokens: 200,
61
+ },
62
+ last: {
63
+ ...sampleUsage.last,
64
+ cacheWriteInputTokens: 200,
65
+ },
66
+ modelContextWindow: 353_400,
67
+ };
68
+
69
+ expect(contextWindowForModel('gpt-5.6')).toBe(353_400);
70
+ expect(contextWindowForModel('openai/gpt-5.6-sol')).toBe(353_400);
71
+ expect(supportsFastMode('gpt-5.6-terra')).toBe(true);
72
+ expect(buildTurnPricingSnapshot('gpt-5.6-luna', false)).toEqual({
73
+ pricingModelKey: 'gpt-5.6-luna',
74
+ pricingTierKey: 'standard',
75
+ });
76
+
77
+ const solEstimate = estimateTurnPrice(usage, {
78
+ pricingModelKey: 'gpt-5.6-sol',
79
+ pricingTierKey: 'standard',
80
+ });
81
+ expect(solEstimate).toMatchObject({
82
+ pricingModelKey: 'gpt-5.6-sol',
83
+ inputUsd: 0.004,
84
+ cachedInputUsd: 0.00025,
85
+ cacheWriteInputUsd: 0.00125,
86
+ outputUsd: 0.045,
87
+ });
88
+ expect(solEstimate?.totalUsd).toBeCloseTo(0.0505, 10);
89
+
90
+ const terraEstimate = estimateTurnPrice(usage, {
91
+ pricingModelKey: 'gpt-5.6-terra',
92
+ pricingTierKey: 'standard',
93
+ });
94
+ expect(terraEstimate?.cacheWriteInputUsd).toBeCloseTo(0.000625, 10);
95
+
96
+ const lunaEstimate = estimateTurnPrice(usage, {
97
+ pricingModelKey: 'gpt-5.6-luna',
98
+ pricingTierKey: 'standard',
99
+ });
100
+ expect(lunaEstimate?.cacheWriteInputUsd).toBeCloseTo(0.00025, 10);
101
+ });
102
+
103
+ it('applies GPT-5.6 long-context rates above 272K input tokens', () => {
104
+ const estimate = estimateTurnPrice(
105
+ {
106
+ total: {
107
+ totalTokens: 301_000,
108
+ inputTokens: 300_000,
109
+ cachedInputTokens: 0,
110
+ cacheWriteInputTokens: 0,
111
+ outputTokens: 1_000,
112
+ reasoningOutputTokens: 0,
113
+ },
114
+ last: {
115
+ totalTokens: 301_000,
116
+ inputTokens: 300_000,
117
+ cachedInputTokens: 0,
118
+ cacheWriteInputTokens: 0,
119
+ outputTokens: 1_000,
120
+ reasoningOutputTokens: 0,
121
+ },
122
+ modelContextWindow: 353_400,
123
+ },
124
+ {
125
+ pricingModelKey: 'gpt-5.6-sol',
126
+ pricingTierKey: 'standard',
127
+ },
128
+ );
129
+
130
+ expect(estimate).toMatchObject({
131
+ inputUsd: 3,
132
+ cachedInputUsd: 0,
133
+ cacheWriteInputUsd: 0,
134
+ outputUsd: 0.045,
135
+ });
136
+ expect(estimate?.totalUsd).toBeCloseTo(3.045, 10);
137
+ });
138
+
55
139
  it('uses the gpt-5.5-specific fast multiplier and marks it fast-capable', () => {
56
140
  expect(supportsFastMode('gpt-5.5')).toBe(true);
57
141
  expect(contextWindowForModel('gpt-5.5')).toBe(272000);
@@ -63,7 +63,9 @@ export type ReasoningEffort =
63
63
  | 'low'
64
64
  | 'medium'
65
65
  | 'high'
66
- | 'xhigh';
66
+ | 'xhigh'
67
+ | 'max'
68
+ | 'ultra';
67
69
 
68
70
  export type CollaborationModeKind = 'default' | 'plan';
69
71
  export type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
@@ -439,6 +439,13 @@ function openCodeUsageFromTokens(tokens: unknown) {
439
439
  const cachedInputTokens = nonNegativeNumberValue(
440
440
  tokens.cachedInputTokens ?? tokens.cached_input_tokens ?? cache?.read,
441
441
  ) ?? 0;
442
+ const cacheWriteInputTokens = nonNegativeNumberValue(
443
+ tokens.cacheWriteInputTokens ??
444
+ tokens.cache_write_input_tokens ??
445
+ tokens.cacheWriteTokens ??
446
+ tokens.cache_write_tokens ??
447
+ cache?.write,
448
+ ) ?? 0;
442
449
  const totalTokens = nonNegativeNumberValue(tokens.total ?? tokens.totalTokens ?? tokens.total_tokens)
443
450
  ?? inputTokens + outputTokens;
444
451
 
@@ -451,6 +458,7 @@ function openCodeUsageFromTokens(tokens: unknown) {
451
458
  totalTokens,
452
459
  inputTokens,
453
460
  cachedInputTokens,
461
+ ...(cacheWriteInputTokens > 0 ? { cacheWriteInputTokens } : {}),
454
462
  outputTokens,
455
463
  reasoningOutputTokens,
456
464
  },
@@ -524,6 +532,13 @@ function turnTokenUsage(messages: unknown[], model: ReturnType<typeof parseModel
524
532
  totalTokens: sum.totalTokens + usage.totalTokens,
525
533
  inputTokens: sum.inputTokens + usage.inputTokens,
526
534
  cachedInputTokens: sum.cachedInputTokens + usage.cachedInputTokens,
535
+ ...((sum.cacheWriteInputTokens ?? 0) + (usage.cacheWriteInputTokens ?? 0) > 0
536
+ ? {
537
+ cacheWriteInputTokens:
538
+ (sum.cacheWriteInputTokens ?? 0) +
539
+ (usage.cacheWriteInputTokens ?? 0),
540
+ }
541
+ : {}),
527
542
  outputTokens: sum.outputTokens + usage.outputTokens,
528
543
  reasoningOutputTokens: sum.reasoningOutputTokens + usage.reasoningOutputTokens,
529
544
  }), firstRecord);
@@ -702,7 +702,8 @@ export type ReasoningEffortDto =
702
702
  | 'medium'
703
703
  | 'high'
704
704
  | 'xhigh'
705
- | 'max';
705
+ | 'max'
706
+ | 'ultra';
706
707
  export type CollaborationModeDto = 'default' | 'plan';
707
708
  export type SandboxModeDto = 'read-only' | 'workspace-write' | 'danger-full-access';
708
709
 
@@ -894,6 +895,7 @@ export interface ThreadTurnTokenBreakdownDto {
894
895
  totalTokens: number;
895
896
  inputTokens: number;
896
897
  cachedInputTokens: number;
898
+ cacheWriteInputTokens?: number;
897
899
  outputTokens: number;
898
900
  reasoningOutputTokens: number;
899
901
  }
@@ -912,6 +914,7 @@ export interface ThreadTurnPriceEstimateDto {
912
914
  currency: 'USD';
913
915
  inputUsd: number;
914
916
  cachedInputUsd: number;
917
+ cacheWriteInputUsd?: number;
915
918
  outputUsd: number;
916
919
  totalUsd: number;
917
920
  }