@ccpocket/bridge 1.65.0 → 1.66.0

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.
package/README.md CHANGED
@@ -44,7 +44,7 @@ ccpocket-bridge --version
44
44
  | `BRIDGE_CODEX_SHARED_APP_SERVER_URL` | `ws://127.0.0.1:8767` in `managed` mode | Experimental shared Codex app-server URL for Codex CLI co-presence |
45
45
  | `BRIDGE_DEMO_MODE` | (none) | Demo mode: hide Tailscale IPs and API key from QR code / logs |
46
46
  | `BRIDGE_RECORDING` | (none) | Enable session recording for debugging (enabled when set) |
47
- | `BRIDGE_DISABLE_MDNS` | (none) | Disable mDNS auto-discovery advertisement (enabled when set) |
47
+ | `BRIDGE_DISABLE_MDNS` | (none) | Disable mDNS auto-discovery advertisement (macOS disables it automatically) |
48
48
  | `BRIDGE_PROMPT_HISTORY_FILE` | `$HOME/.ccpocket/prompt-history-v2.json` | Custom prompt history store path |
49
49
  | `BRIDGE_RECENT_SESSIONS_PROFILE` | (none) | Log recent-session index timing when set to `1` or `true` |
50
50
  | `BRIDGE_FILE_LIST_MAX_ENTRIES` | `5000` | Maximum file and directory entries returned to a client; non-positive or invalid values use the default |
@@ -12,6 +12,7 @@ export interface CodexStartOptions {
12
12
  sandboxMode?: "read-only" | "workspace-write" | "danger-full-access";
13
13
  model?: string;
14
14
  modelReasoningEffort?: string;
15
+ serviceTier?: string;
15
16
  networkAccessEnabled?: boolean;
16
17
  webSearchMode?: "disabled" | "cached" | "live";
17
18
  collaborationMode?: "plan" | "default";
@@ -80,6 +81,8 @@ export interface CodexModelMetadata {
80
81
  model: string;
81
82
  supportedReasoningEfforts: string[];
82
83
  defaultReasoningEffort?: string;
84
+ supportedServiceTiers: string[];
85
+ defaultServiceTier?: string;
83
86
  }
84
87
  export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
85
88
  private transport;
@@ -117,6 +120,7 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
117
120
  private _collaborationMode;
118
121
  private _runtimeModel;
119
122
  private _runtimeModelReasoningEffort;
123
+ private _runtimeServiceTier;
120
124
  private lastPlanItemText;
121
125
  /** Last assistant text message — used as `result` in completion notification. */
122
126
  private lastResultText;
@@ -138,11 +142,14 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
138
142
  get codexPermissionsMode(): CodexStartOptions["codexPermissionsMode"] | undefined;
139
143
  get model(): string;
140
144
  get modelReasoningEffort(): CodexStartOptions["modelReasoningEffort"] | undefined;
145
+ get serviceTier(): string;
141
146
  /**
142
147
  * Update Codex model at runtime.
143
148
  * Takes effect on the next `turn/start` RPC call.
144
149
  */
145
150
  setModel(model: string, modelReasoningEffort?: CodexStartOptions["modelReasoningEffort"]): void;
151
+ /** Update Codex speed for the next turn without restarting the thread. */
152
+ setServiceTier(serviceTier: string): void;
146
153
  /**
147
154
  * Update approval policy at runtime.
148
155
  * Takes effect on the next `turn/start` RPC call.
@@ -70,6 +70,7 @@ export class CodexProcess extends EventEmitter {
70
70
  _collaborationMode = "default";
71
71
  _runtimeModel;
72
72
  _runtimeModelReasoningEffort;
73
+ _runtimeServiceTier;
73
74
  lastPlanItemText = null;
74
75
  /** Last assistant text message — used as `result` in completion notification. */
75
76
  lastResultText = null;
@@ -120,6 +121,9 @@ export class CodexProcess extends EventEmitter {
120
121
  get modelReasoningEffort() {
121
122
  return this._runtimeModelReasoningEffort;
122
123
  }
124
+ get serviceTier() {
125
+ return this._runtimeServiceTier ?? "standard";
126
+ }
123
127
  /**
124
128
  * Update Codex model at runtime.
125
129
  * Takes effect on the next `turn/start` RPC call.
@@ -139,6 +143,11 @@ export class CodexProcess extends EventEmitter {
139
143
  ? ` (${this._runtimeModelReasoningEffort})`
140
144
  : ""));
141
145
  }
146
+ /** Update Codex speed for the next turn without restarting the thread. */
147
+ setServiceTier(serviceTier) {
148
+ this._runtimeServiceTier = normalizeServiceTier(serviceTier);
149
+ console.log(`[codex-process] Speed changed to: ${this.serviceTier}`);
150
+ }
142
151
  /**
143
152
  * Update approval policy at runtime.
144
153
  * Takes effect on the next `turn/start` RPC call.
@@ -328,6 +337,12 @@ export class CodexProcess extends EventEmitter {
328
337
  : typeof raw.default_reasoning_effort === "string"
329
338
  ? raw.default_reasoning_effort
330
339
  : undefined,
340
+ supportedServiceTiers: extractServiceTiers(raw),
341
+ defaultServiceTier: typeof raw.defaultServiceTier === "string"
342
+ ? raw.defaultServiceTier
343
+ : typeof raw.default_service_tier === "string"
344
+ ? raw.default_service_tier
345
+ : undefined,
331
346
  });
332
347
  }
333
348
  }
@@ -390,6 +405,12 @@ export class CodexProcess extends EventEmitter {
390
405
  this.cleanupSteerTempPaths();
391
406
  this.lastTokenUsage = null;
392
407
  this.startModel = sanitizeCodexModel(options?.model);
408
+ this._runtimeModel = undefined;
409
+ this._runtimeModelReasoningEffort = options?.modelReasoningEffort;
410
+ this._runtimeServiceTier =
411
+ options?.serviceTier === undefined
412
+ ? undefined
413
+ : normalizeServiceTier(options.serviceTier);
393
414
  this._approvalPolicy = options?.approvalPolicy;
394
415
  this._approvalsReviewer =
395
416
  options?.approvalsReviewer === undefined
@@ -761,6 +782,9 @@ export class CodexProcess extends EventEmitter {
761
782
  : undefined;
762
783
  if (requestedModel)
763
784
  threadParams.model = requestedModel;
785
+ if (options?.serviceTier !== undefined) {
786
+ threadParams.serviceTier = normalizeServiceTier(options.serviceTier);
787
+ }
764
788
  if (requestedReasoningEffort) {
765
789
  // app-server applies reasoning effort on thread start via config overrides,
766
790
  // not the top-level thread/start payload.
@@ -853,6 +877,7 @@ export class CodexProcess extends EventEmitter {
853
877
  : requestedReasoningEffort
854
878
  ? { modelReasoningEffort: requestedReasoningEffort }
855
879
  : {}),
880
+ serviceTier: normalizeServiceTierForClient(resolvedSettings.serviceTier ?? options?.serviceTier),
856
881
  ...(resolvedSettings.networkAccessEnabled !== undefined
857
882
  ? { networkAccessEnabled: resolvedSettings.networkAccessEnabled }
858
883
  : {}),
@@ -1130,6 +1155,9 @@ export class CodexProcess extends EventEmitter {
1130
1155
  if (requestedReasoningEffort) {
1131
1156
  params.effort = requestedReasoningEffort;
1132
1157
  }
1158
+ if (this._runtimeServiceTier !== undefined) {
1159
+ params.serviceTier = this._runtimeServiceTier;
1160
+ }
1133
1161
  // Always send collaborationMode so the server switches modes correctly.
1134
1162
  // Omitting it causes the server to persist the previous turn's mode.
1135
1163
  const modeSettings = {
@@ -2218,6 +2246,51 @@ function extractReasoningEfforts(raw) {
2218
2246
  }
2219
2247
  return efforts;
2220
2248
  }
2249
+ function extractServiceTiers(raw) {
2250
+ // Recent app-server versions expose the user-facing Fast option as
2251
+ // `additionalSpeedTiers: ["fast"]`, while the lower-level service tier is
2252
+ // advertised as `{ id: "priority", name: "Fast" }`. Merge both shapes and
2253
+ // normalize them to the value accepted by `service_tier` in config/RPCs.
2254
+ const values = [
2255
+ ...(Array.isArray(raw.additionalSpeedTiers)
2256
+ ? raw.additionalSpeedTiers
2257
+ : []),
2258
+ ...(Array.isArray(raw.serviceTiers) ? raw.serviceTiers : []),
2259
+ ];
2260
+ const seen = new Set();
2261
+ const tiers = [];
2262
+ for (const value of values) {
2263
+ const metadata = value && typeof value === "object"
2264
+ ? value
2265
+ : undefined;
2266
+ const rawTier = typeof value === "string" ? value : metadata?.id;
2267
+ const rawName = metadata?.name;
2268
+ const tier = rawTier === "priority" ||
2269
+ (typeof rawName === "string" && rawName.toLowerCase() === "fast")
2270
+ ? "fast"
2271
+ : rawTier;
2272
+ if (typeof tier !== "string")
2273
+ continue;
2274
+ const normalized = tier.trim();
2275
+ if (!normalized || seen.has(normalized))
2276
+ continue;
2277
+ seen.add(normalized);
2278
+ tiers.push(normalized);
2279
+ }
2280
+ return tiers;
2281
+ }
2282
+ function normalizeServiceTier(value) {
2283
+ const normalized = value.trim();
2284
+ return !normalized || normalized === "standard" || normalized === "default"
2285
+ ? null
2286
+ : normalized;
2287
+ }
2288
+ function normalizeServiceTierForClient(value) {
2289
+ if (typeof value !== "string")
2290
+ return "standard";
2291
+ const normalized = value.trim();
2292
+ return !normalized || normalized === "default" ? "standard" : normalized;
2293
+ }
2221
2294
  function sanitizeCodexModel(value) {
2222
2295
  if (typeof value !== "string")
2223
2296
  return undefined;
@@ -2246,6 +2319,9 @@ function extractResolvedSettingsFromThreadResponse(response) {
2246
2319
  : typeof collaborationSettings?.reasoning_effort === "string"
2247
2320
  ? collaborationSettings.reasoning_effort
2248
2321
  : undefined,
2322
+ serviceTier: typeof response.serviceTier === "string"
2323
+ ? response.serviceTier
2324
+ : undefined,
2249
2325
  networkAccessEnabled: typeof sandbox?.networkAccess === "boolean"
2250
2326
  ? sandbox.networkAccess
2251
2327
  : undefined,