@ccpocket/bridge 1.63.6 → 1.65.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.
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from "node:events";
2
- import type { ServerMessage, ProcessStatus } from "./parser.js";
2
+ import type { CodexGoal, CodexGoalStatus, ServerMessage, ProcessStatus } from "./parser.js";
3
3
  import { buildCodexSpawnSpec } from "./codex-transport.js";
4
4
  export { buildCodexSpawnSpec };
5
5
  export interface CodexStartOptions {
@@ -11,7 +11,7 @@ export interface CodexStartOptions {
11
11
  codexPermissionsMode?: "default" | "autoReview" | "fullAccess" | "custom";
12
12
  sandboxMode?: "read-only" | "workspace-write" | "danger-full-access";
13
13
  model?: string;
14
- modelReasoningEffort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
14
+ modelReasoningEffort?: string;
15
15
  networkAccessEnabled?: boolean;
16
16
  webSearchMode?: "disabled" | "cached" | "live";
17
17
  collaborationMode?: "plan" | "default";
@@ -164,6 +164,15 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
164
164
  * Sends thread/name/set which persists to ~/.codex/session_index.jsonl.
165
165
  */
166
166
  renameThread(name: string): Promise<void>;
167
+ /** Read the persisted goal attached to this Codex thread. */
168
+ getGoal(): Promise<CodexGoal | null>;
169
+ /** Create or update the persisted goal attached to this Codex thread. */
170
+ setGoal(update: {
171
+ objective?: string;
172
+ status?: CodexGoalStatus;
173
+ }): Promise<CodexGoal>;
174
+ /** Remove the persisted goal attached to this Codex thread. */
175
+ clearGoal(): Promise<boolean>;
167
176
  /**
168
177
  * Archive a Codex thread via the app-server `thread/archive` RPC.
169
178
  * Accepts an explicit threadId so that historical (non-active) sessions
@@ -294,3 +303,5 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
294
303
  private extractToolUseId;
295
304
  private handleServerRequestResolved;
296
305
  }
306
+ /** Validate the app-server ThreadGoal payload at the process boundary. */
307
+ export declare function parseCodexGoal(value: unknown): CodexGoal;
@@ -179,6 +179,43 @@ export class CodexProcess extends EventEmitter {
179
179
  name,
180
180
  });
181
181
  }
182
+ /** Read the persisted goal attached to this Codex thread. */
183
+ async getGoal() {
184
+ if (!this._threadId) {
185
+ throw new Error("No thread ID available for goal lookup");
186
+ }
187
+ const response = (await this.request("thread/goal/get", {
188
+ threadId: this._threadId,
189
+ }));
190
+ return response.goal == null ? null : parseCodexGoal(response.goal);
191
+ }
192
+ /** Create or update the persisted goal attached to this Codex thread. */
193
+ async setGoal(update) {
194
+ if (!this._threadId) {
195
+ throw new Error("No thread ID available for goal update");
196
+ }
197
+ const response = (await this.request("thread/goal/set", {
198
+ threadId: this._threadId,
199
+ ...(update.objective !== undefined
200
+ ? { objective: update.objective.trim() }
201
+ : {}),
202
+ ...(update.status !== undefined ? { status: update.status } : {}),
203
+ }));
204
+ return parseCodexGoal(response.goal);
205
+ }
206
+ /** Remove the persisted goal attached to this Codex thread. */
207
+ async clearGoal() {
208
+ if (!this._threadId) {
209
+ throw new Error("No thread ID available for goal clear");
210
+ }
211
+ const response = (await this.request("thread/goal/clear", {
212
+ threadId: this._threadId,
213
+ }));
214
+ if (typeof response.cleared !== "boolean") {
215
+ throw new Error("thread/goal/clear returned an invalid response");
216
+ }
217
+ return response.cleared;
218
+ }
182
219
  /**
183
220
  * Archive a Codex thread via the app-server `thread/archive` RPC.
184
221
  * Accepts an explicit threadId so that historical (non-active) sessions
@@ -1388,6 +1425,22 @@ export class CodexProcess extends EventEmitter {
1388
1425
  // Name change notification — handled by session manager
1389
1426
  break;
1390
1427
  }
1428
+ case "thread/goal/updated": {
1429
+ try {
1430
+ this.emitMessage({
1431
+ type: "goal_state",
1432
+ goal: parseCodexGoal(params.goal),
1433
+ });
1434
+ }
1435
+ catch (err) {
1436
+ console.warn(`[codex-process] Ignoring invalid goal notification: ${err instanceof Error ? err.message : String(err)}`);
1437
+ }
1438
+ break;
1439
+ }
1440
+ case "thread/goal/cleared": {
1441
+ this.emitMessage({ type: "goal_state", goal: null });
1442
+ break;
1443
+ }
1391
1444
  case "thread/tokenUsage/updated": {
1392
1445
  const usage = params.usage;
1393
1446
  if (usage) {
@@ -2149,9 +2202,15 @@ function extractReasoningEfforts(raw) {
2149
2202
  const seen = new Set();
2150
2203
  const efforts = [];
2151
2204
  for (const value of values) {
2152
- if (typeof value !== "string")
2205
+ const effort = typeof value === "string"
2206
+ ? value
2207
+ : value && typeof value === "object"
2208
+ ? (value.reasoningEffort ??
2209
+ value.effort)
2210
+ : undefined;
2211
+ if (typeof effort !== "string")
2153
2212
  continue;
2154
- const normalized = value.trim();
2213
+ const normalized = effort.trim();
2155
2214
  if (!normalized || seen.has(normalized))
2156
2215
  continue;
2157
2216
  seen.add(normalized);
@@ -2235,6 +2294,48 @@ function notificationThreadId(params) {
2235
2294
  }
2236
2295
  return null;
2237
2296
  }
2297
+ const CODEX_GOAL_STATUSES = new Set([
2298
+ "active",
2299
+ "paused",
2300
+ "blocked",
2301
+ "usageLimited",
2302
+ "budgetLimited",
2303
+ "complete",
2304
+ ]);
2305
+ /** Validate the app-server ThreadGoal payload at the process boundary. */
2306
+ export function parseCodexGoal(value) {
2307
+ if (!value || typeof value !== "object") {
2308
+ throw new Error("Goal payload is missing");
2309
+ }
2310
+ const goal = value;
2311
+ const status = goal.status;
2312
+ const requiredNumbers = [
2313
+ "tokensUsed",
2314
+ "timeUsedSeconds",
2315
+ "createdAt",
2316
+ "updatedAt",
2317
+ ];
2318
+ if (typeof goal.threadId !== "string" ||
2319
+ typeof goal.objective !== "string" ||
2320
+ !CODEX_GOAL_STATUSES.has(status) ||
2321
+ requiredNumbers.some((field) => typeof goal[field] !== "number" ||
2322
+ !Number.isFinite(goal[field])) ||
2323
+ (goal.tokenBudget !== null &&
2324
+ (typeof goal.tokenBudget !== "number" ||
2325
+ !Number.isFinite(goal.tokenBudget)))) {
2326
+ throw new Error("Goal payload has an invalid shape");
2327
+ }
2328
+ return {
2329
+ threadId: goal.threadId,
2330
+ objective: goal.objective,
2331
+ status,
2332
+ tokenBudget: goal.tokenBudget,
2333
+ tokensUsed: goal.tokensUsed,
2334
+ timeUsedSeconds: goal.timeUsedSeconds,
2335
+ createdAt: goal.createdAt,
2336
+ updatedAt: goal.updatedAt,
2337
+ };
2338
+ }
2238
2339
  function numberOrUndefined(value) {
2239
2340
  return typeof value === "number" && Number.isFinite(value)
2240
2341
  ? value