codex-relay 1.4.3 → 1.4.5

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,2 +1,2 @@
1
- import { $ as RunThreadResponseSchema, $t as promptSkillMentionTextCandidates, A as PairRequestSchema, At as WorkspaceFileMentionSchema, B as PushNotificationIntentSchema, Bt as WorkspaceTerminalOutputResponseSchema, C as ListModelsResponseSchema, Ct as VersionResponseSchema, D as ListWorkspaceDirectoriesResponseSchema, Dt as WorkspaceChangesResponseSchema, E as ListThreadsResponseSchema, Et as WebPreviewTargetSchema, F as PromptAttachmentSchema, Ft as WorkspaceSelectionRequestSchema, G as RateLimitBucketSchema, Gt as chatMessageDetailsFromPromptContext, H as PushNotificationSettingsResponseSchema, Ht as WorkspaceTerminalSessionResponseSchema, I as PromptAttachmentSummarySchema, It as WorkspaceTailscaleServeRequestSchema, J as ReasoningEffortSchema, Jt as normalizePromptContext, K as RateLimitWindowSchema, Kt as createOpenApiDocument, L as PromptContextInputSchema, Lt as WorkspaceTailscaleServeResponseSchema, M as PendingInputRequestOptionSchema, Mt as WorkspaceMarkdownPreviewTargetSchema, N as PendingInputRequestQuestionSchema, Nt as WorkspacePreviewNavigationRequestSchema, O as ListWorkspaceFilesResponseSchema, Ot as WorkspaceDirectoryEntrySchema, P as PendingInputRequestSchema, Pt as WorkspacePreviewTabSchema, Q as RunThreadRequestSchema, Qt as promptSkillMentionMarkdown, R as PromptContextSchema, Rt as WorkspaceTerminalInputRequestSchema, S as KnownReasoningEffortSchema, St as UpdateWorkspaceFileContentRequestSchema, T as ListSkillsResponseSchema, Tt as WORKSPACE_PREVIEW_TAB_VALUES, U as QueuedThreadInputActionResponseSchema, Ut as WorkspaceTerminalStartRequestSchema, V as PushNotificationPreferencesSchema, Vt as WorkspaceTerminalResizeRequestSchema, W as QueuedThreadInputSchema, Wt as apiPaths, X as ResolveApprovalRequestSchema, Xt as promptSkillDisplayName, Y as RegisterPushNotificationRequestSchema, Yt as promptMarkdownWithSkills, Z as ResolveApprovalResponseSchema, Zt as promptSkillMentionLabel, _ as EncryptedPayloadSchema, _t as ThreadRunOptionsSchema, a as ArchiveThreadResponseSchema, at as StatusResponseSchema, b as InterruptThreadRunResponseSchema, bt as UpdateRuntimePreferencesRequestSchema, c as ChatMessageRoleSchema, ct as SubmitThreadInputResponseSchema, d as CheckoutWorkspaceBranchRequestSchema, dt as ThreadDetailResponseSchema, en as stripPromptSkillMentions, et as RuntimeModeSchema, f as CodexModelSchema, ft as ThreadGoalResponseSchema, g as CreateThreadResponseSchema, gt as ThreadMessageDetailResponseSchema, h as CreateThreadRequestSchema, ht as ThreadMessageDetailFieldSchema, i as ApprovalModeSchema, it as SandboxModeSchema, j as PairResponseSchema, jt as WorkspaceGitActionResponseSchema, k as PairEncryptedPayloadSchema, kt as WorkspaceFileContentResponseSchema, l as ChatMessageSchema, lt as ThreadCollaborationModeSchema, m as ContextWindowUsageSchema, mt as ThreadGoalStatusSchema, n as AgentSkillSourceSchema, nt as RuntimePreferencesResponseSchema, o as ChatMessageKindSchema, ot as StreamThreadRunEventSchema, p as CommitPushWorkspaceRequestSchema, pt as ThreadGoalSchema, q as RateLimitsResponseSchema, qt as isPromptSkillMarkdownMention, r as ApprovalDecisionSchema, rt as RuntimePreferencesSchema, s as ChatMessagePromptDetailsSchema, st as StreamThreadRunRequestSchema, t as AgentSkillSchema, tt as RuntimePreferencesByWorkspacePathSchema, u as ChatMessageStateSchema, ut as ThreadContextWindowResponseSchema, v as ErrorResponseSchema, vt as ThreadStateSchema, w as ListQueuedThreadInputsResponseSchema, wt as WORKSPACE_PREVIEW_OPEN_PROTOCOL, x as IsoDateTimeSchema, xt as UpdateThreadGoalRequestSchema, y as ImageAttachmentUploadResponseSchema, yt as ThreadSummarySchema, z as PromptSkillSchema, zt as WorkspaceTerminalOutputChunkSchema } from "./api-schema2.js";
2
- export { AgentSkillSchema, AgentSkillSourceSchema, ApprovalDecisionSchema, ApprovalModeSchema, ArchiveThreadResponseSchema, ChatMessageKindSchema, ChatMessagePromptDetailsSchema, ChatMessageRoleSchema, ChatMessageSchema, ChatMessageStateSchema, CheckoutWorkspaceBranchRequestSchema, CodexModelSchema, CommitPushWorkspaceRequestSchema, ContextWindowUsageSchema, CreateThreadRequestSchema, CreateThreadResponseSchema, EncryptedPayloadSchema, ErrorResponseSchema, ImageAttachmentUploadResponseSchema, InterruptThreadRunResponseSchema, IsoDateTimeSchema, KnownReasoningEffortSchema, ListModelsResponseSchema, ListQueuedThreadInputsResponseSchema, ListSkillsResponseSchema, ListThreadsResponseSchema, ListWorkspaceDirectoriesResponseSchema, ListWorkspaceFilesResponseSchema, PairEncryptedPayloadSchema, PairRequestSchema, PairResponseSchema, PendingInputRequestOptionSchema, PendingInputRequestQuestionSchema, PendingInputRequestSchema, PromptAttachmentSchema, PromptAttachmentSummarySchema, PromptContextInputSchema, PromptContextSchema, PromptSkillSchema, PushNotificationIntentSchema, PushNotificationPreferencesSchema, PushNotificationSettingsResponseSchema, QueuedThreadInputActionResponseSchema, QueuedThreadInputSchema, RateLimitBucketSchema, RateLimitWindowSchema, RateLimitsResponseSchema, ReasoningEffortSchema, RegisterPushNotificationRequestSchema, ResolveApprovalRequestSchema, ResolveApprovalResponseSchema, RunThreadRequestSchema, RunThreadResponseSchema, RuntimeModeSchema, RuntimePreferencesByWorkspacePathSchema, RuntimePreferencesResponseSchema, RuntimePreferencesSchema, SandboxModeSchema, StatusResponseSchema, StreamThreadRunEventSchema, StreamThreadRunRequestSchema, SubmitThreadInputResponseSchema, ThreadCollaborationModeSchema, ThreadContextWindowResponseSchema, ThreadDetailResponseSchema, ThreadGoalResponseSchema, ThreadGoalSchema, ThreadGoalStatusSchema, ThreadMessageDetailFieldSchema, ThreadMessageDetailResponseSchema, ThreadRunOptionsSchema, ThreadStateSchema, ThreadSummarySchema, UpdateRuntimePreferencesRequestSchema, UpdateThreadGoalRequestSchema, UpdateWorkspaceFileContentRequestSchema, VersionResponseSchema, WORKSPACE_PREVIEW_OPEN_PROTOCOL, WORKSPACE_PREVIEW_TAB_VALUES, WebPreviewTargetSchema, WorkspaceChangesResponseSchema, WorkspaceDirectoryEntrySchema, WorkspaceFileContentResponseSchema, WorkspaceFileMentionSchema, WorkspaceGitActionResponseSchema, WorkspaceMarkdownPreviewTargetSchema, WorkspacePreviewNavigationRequestSchema, WorkspacePreviewTabSchema, WorkspaceSelectionRequestSchema, WorkspaceTailscaleServeRequestSchema, WorkspaceTailscaleServeResponseSchema, WorkspaceTerminalInputRequestSchema, WorkspaceTerminalOutputChunkSchema, WorkspaceTerminalOutputResponseSchema, WorkspaceTerminalResizeRequestSchema, WorkspaceTerminalSessionResponseSchema, WorkspaceTerminalStartRequestSchema, apiPaths, chatMessageDetailsFromPromptContext, createOpenApiDocument, isPromptSkillMarkdownMention, normalizePromptContext, promptMarkdownWithSkills, promptSkillDisplayName, promptSkillMentionLabel, promptSkillMentionMarkdown, promptSkillMentionTextCandidates, stripPromptSkillMentions };
1
+ import { $ as ResolveApprovalResponseSchema, $t as promptSkillDisplayName, A as PairRequestSchema, At as WorkspaceChangesResponseSchema, B as PushNotificationIntentSchema, Bt as WorkspaceTailscaleServeResponseSchema, C as ListModelsResponseSchema, Ct as UpdateRuntimePreferencesRequestSchema, D as ListWorkspaceDirectoriesResponseSchema, Dt as WORKSPACE_PREVIEW_OPEN_PROTOCOL, E as ListThreadsResponseSchema, Et as VersionResponseSchema, F as PromptAttachmentSchema, Ft as WorkspaceMarkdownPreviewTargetSchema, G as RateLimitBucketSchema, Gt as WorkspaceTerminalSessionResponseSchema, H as PushNotificationSettingsResponseSchema, Ht as WorkspaceTerminalOutputChunkSchema, I as PromptAttachmentSummarySchema, It as WorkspacePreviewNavigationRequestSchema, J as ReasoningEffortSchema, Jt as chatMessageDetailsFromPromptContext, K as RateLimitWindowSchema, Kt as WorkspaceTerminalStartRequestSchema, L as PromptContextInputSchema, Lt as WorkspacePreviewTabSchema, M as PendingInputRequestOptionSchema, Mt as WorkspaceFileContentResponseSchema, N as PendingInputRequestQuestionSchema, Nt as WorkspaceFileMentionSchema, O as ListWorkspaceFilesResponseSchema, Ot as WORKSPACE_PREVIEW_TAB_VALUES, P as PendingInputRequestSchema, Pt as WorkspaceGitActionResponseSchema, Q as ResolveApprovalRequestSchema, Qt as promptMarkdownWithSkills, R as PromptContextSchema, Rt as WorkspaceSelectionRequestSchema, S as KnownReasoningEffortSchema, St as ThreadSummarySchema, T as ListSkillsResponseSchema, Tt as UpdateWorkspaceFileContentRequestSchema, U as QueuedThreadInputActionResponseSchema, Ut as WorkspaceTerminalOutputResponseSchema, V as PushNotificationPreferencesSchema, Vt as WorkspaceTerminalInputRequestSchema, W as QueuedThreadInputSchema, Wt as WorkspaceTerminalResizeRequestSchema, X as RenameThreadRequestSchema, Xt as isPromptSkillMarkdownMention, Y as RegisterPushNotificationRequestSchema, Yt as createOpenApiDocument, Z as RenameThreadResponseSchema, Zt as normalizePromptContext, _ as EncryptedPayloadSchema, _t as ThreadGoalStatusSchema, a as ArchiveThreadResponseSchema, at as RuntimePreferencesResponseSchema, b as InterruptThreadRunResponseSchema, bt as ThreadRunOptionsSchema, c as ChatMessageRoleSchema, ct as StatusResponseSchema, d as CheckoutWorkspaceBranchRequestSchema, dt as SubmitThreadInputResponseSchema, en as promptSkillMentionLabel, et as RewindThreadRequestSchema, f as CodexModelSchema, ft as ThreadCollaborationModeSchema, g as CreateThreadResponseSchema, gt as ThreadGoalSchema, h as CreateThreadRequestSchema, ht as ThreadGoalResponseSchema, i as ApprovalModeSchema, it as RuntimePreferencesByWorkspacePathSchema, j as PairResponseSchema, jt as WorkspaceDirectoryEntrySchema, k as PairEncryptedPayloadSchema, kt as WebPreviewTargetSchema, l as ChatMessageSchema, lt as StreamThreadRunEventSchema, m as ContextWindowUsageSchema, mt as ThreadDetailResponseSchema, n as AgentSkillSourceSchema, nn as promptSkillMentionTextCandidates, nt as RunThreadResponseSchema, o as ChatMessageKindSchema, ot as RuntimePreferencesSchema, p as CommitPushWorkspaceRequestSchema, pt as ThreadContextWindowResponseSchema, q as RateLimitsResponseSchema, qt as apiPaths, r as ApprovalDecisionSchema, rn as stripPromptSkillMentions, rt as RuntimeModeSchema, s as ChatMessagePromptDetailsSchema, st as SandboxModeSchema, t as AgentSkillSchema, tn as promptSkillMentionMarkdown, tt as RunThreadRequestSchema, u as ChatMessageStateSchema, ut as StreamThreadRunRequestSchema, v as ErrorResponseSchema, vt as ThreadMessageDetailFieldSchema, w as ListQueuedThreadInputsResponseSchema, wt as UpdateThreadGoalRequestSchema, x as IsoDateTimeSchema, xt as ThreadStateSchema, y as ImageAttachmentUploadResponseSchema, yt as ThreadMessageDetailResponseSchema, z as PromptSkillSchema, zt as WorkspaceTailscaleServeRequestSchema } from "./api-schema2.js";
2
+ export { AgentSkillSchema, AgentSkillSourceSchema, ApprovalDecisionSchema, ApprovalModeSchema, ArchiveThreadResponseSchema, ChatMessageKindSchema, ChatMessagePromptDetailsSchema, ChatMessageRoleSchema, ChatMessageSchema, ChatMessageStateSchema, CheckoutWorkspaceBranchRequestSchema, CodexModelSchema, CommitPushWorkspaceRequestSchema, ContextWindowUsageSchema, CreateThreadRequestSchema, CreateThreadResponseSchema, EncryptedPayloadSchema, ErrorResponseSchema, ImageAttachmentUploadResponseSchema, InterruptThreadRunResponseSchema, IsoDateTimeSchema, KnownReasoningEffortSchema, ListModelsResponseSchema, ListQueuedThreadInputsResponseSchema, ListSkillsResponseSchema, ListThreadsResponseSchema, ListWorkspaceDirectoriesResponseSchema, ListWorkspaceFilesResponseSchema, PairEncryptedPayloadSchema, PairRequestSchema, PairResponseSchema, PendingInputRequestOptionSchema, PendingInputRequestQuestionSchema, PendingInputRequestSchema, PromptAttachmentSchema, PromptAttachmentSummarySchema, PromptContextInputSchema, PromptContextSchema, PromptSkillSchema, PushNotificationIntentSchema, PushNotificationPreferencesSchema, PushNotificationSettingsResponseSchema, QueuedThreadInputActionResponseSchema, QueuedThreadInputSchema, RateLimitBucketSchema, RateLimitWindowSchema, RateLimitsResponseSchema, ReasoningEffortSchema, RegisterPushNotificationRequestSchema, RenameThreadRequestSchema, RenameThreadResponseSchema, ResolveApprovalRequestSchema, ResolveApprovalResponseSchema, RewindThreadRequestSchema, RunThreadRequestSchema, RunThreadResponseSchema, RuntimeModeSchema, RuntimePreferencesByWorkspacePathSchema, RuntimePreferencesResponseSchema, RuntimePreferencesSchema, SandboxModeSchema, StatusResponseSchema, StreamThreadRunEventSchema, StreamThreadRunRequestSchema, SubmitThreadInputResponseSchema, ThreadCollaborationModeSchema, ThreadContextWindowResponseSchema, ThreadDetailResponseSchema, ThreadGoalResponseSchema, ThreadGoalSchema, ThreadGoalStatusSchema, ThreadMessageDetailFieldSchema, ThreadMessageDetailResponseSchema, ThreadRunOptionsSchema, ThreadStateSchema, ThreadSummarySchema, UpdateRuntimePreferencesRequestSchema, UpdateThreadGoalRequestSchema, UpdateWorkspaceFileContentRequestSchema, VersionResponseSchema, WORKSPACE_PREVIEW_OPEN_PROTOCOL, WORKSPACE_PREVIEW_TAB_VALUES, WebPreviewTargetSchema, WorkspaceChangesResponseSchema, WorkspaceDirectoryEntrySchema, WorkspaceFileContentResponseSchema, WorkspaceFileMentionSchema, WorkspaceGitActionResponseSchema, WorkspaceMarkdownPreviewTargetSchema, WorkspacePreviewNavigationRequestSchema, WorkspacePreviewTabSchema, WorkspaceSelectionRequestSchema, WorkspaceTailscaleServeRequestSchema, WorkspaceTailscaleServeResponseSchema, WorkspaceTerminalInputRequestSchema, WorkspaceTerminalOutputChunkSchema, WorkspaceTerminalOutputResponseSchema, WorkspaceTerminalResizeRequestSchema, WorkspaceTerminalSessionResponseSchema, WorkspaceTerminalStartRequestSchema, apiPaths, chatMessageDetailsFromPromptContext, createOpenApiDocument, isPromptSkillMarkdownMention, normalizePromptContext, promptMarkdownWithSkills, promptSkillDisplayName, promptSkillMentionLabel, promptSkillMentionMarkdown, promptSkillMentionTextCandidates, stripPromptSkillMentions };
@@ -268,6 +268,7 @@ const ChatMessageSchema = z.object({
268
268
  });
269
269
  const ThreadSummarySchema = z.object({
270
270
  id: z.string().min(1),
271
+ parentThreadId: z.string().min(1).optional(),
271
272
  title: z.string().min(1),
272
273
  createdAt: IsoDateTimeSchema,
273
274
  updatedAt: IsoDateTimeSchema,
@@ -552,6 +553,9 @@ const ArchiveThreadResponseSchema = z.object({
552
553
  threads: z.array(ThreadSummarySchema),
553
554
  source: z.enum(["app-server", "memory"]).default("memory")
554
555
  });
556
+ const RenameThreadRequestSchema = z.object({ title: z.string().trim().min(1).max(120) });
557
+ const RenameThreadResponseSchema = z.object({ thread: ThreadSummarySchema });
558
+ const RewindThreadRequestSchema = z.object({ turnId: z.string().trim().min(1) });
555
559
  const ListModelsResponseSchema = z.object({ models: z.array(CodexModelSchema) });
556
560
  const ListSkillsResponseSchema = z.object({ skills: z.array(AgentSkillSchema) });
557
561
  const ThreadDetailResponseSchema = z.object({
@@ -757,6 +761,8 @@ const apiPaths = {
757
761
  threads: "/v1/threads",
758
762
  thread: (threadId) => `/v1/threads/${encodeURIComponent(threadId)}`,
759
763
  threadArchive: (threadId) => `/v1/threads/${encodeURIComponent(threadId)}`,
764
+ threadName: (threadId) => `/v1/threads/${encodeURIComponent(threadId)}/name`,
765
+ threadRollback: (threadId) => `/v1/threads/${encodeURIComponent(threadId)}/rollback`,
760
766
  threadContextWindow: (threadId) => `/v1/threads/${encodeURIComponent(threadId)}/context-window`,
761
767
  threadGoal: (threadId) => `/v1/threads/${encodeURIComponent(threadId)}/goal`,
762
768
  threadMessageDetail: (threadId, messageId, field) => `/v1/threads/${encodeURIComponent(threadId)}/messages/${encodeURIComponent(messageId)}/details/${encodeURIComponent(field)}`,
@@ -839,6 +845,10 @@ function createOpenApiDocument() {
839
845
  in: "path",
840
846
  required: true,
841
847
  schema: { type: "string" }
848
+ }, {
849
+ name: "refresh",
850
+ in: "query",
851
+ schema: { type: "boolean" }
842
852
  }],
843
853
  responses: {
844
854
  "200": jsonResponse("ThreadDetailResponse"),
@@ -860,6 +870,40 @@ function createOpenApiDocument() {
860
870
  }
861
871
  }
862
872
  },
873
+ "/v1/threads/{threadId}/name": { post: {
874
+ summary: "Rename a Codex app-server thread",
875
+ parameters: [{
876
+ name: "threadId",
877
+ in: "path",
878
+ required: true,
879
+ schema: { type: "string" }
880
+ }],
881
+ requestBody: jsonRequest("RenameThreadRequest"),
882
+ responses: {
883
+ "200": jsonResponse("RenameThreadResponse"),
884
+ "400": jsonResponse("ErrorResponse"),
885
+ "404": jsonResponse("ErrorResponse"),
886
+ "409": jsonResponse("ErrorResponse"),
887
+ "502": jsonResponse("ErrorResponse")
888
+ }
889
+ } },
890
+ "/v1/threads/{threadId}/rollback": { post: {
891
+ summary: "Rewind a Codex app-server thread to before a selected turn",
892
+ parameters: [{
893
+ name: "threadId",
894
+ in: "path",
895
+ required: true,
896
+ schema: { type: "string" }
897
+ }],
898
+ requestBody: jsonRequest("RewindThreadRequest"),
899
+ responses: {
900
+ "200": jsonResponse("ThreadDetailResponse"),
901
+ "400": jsonResponse("ErrorResponse"),
902
+ "404": jsonResponse("ErrorResponse"),
903
+ "409": jsonResponse("ErrorResponse"),
904
+ "502": jsonResponse("ErrorResponse")
905
+ }
906
+ } },
863
907
  "/v1/workspace-directories": { get: {
864
908
  summary: "List directories under the configured workspace",
865
909
  responses: {
@@ -1159,6 +1203,28 @@ function createOpenApiDocument() {
1159
1203
  }, { type: "null" }] }
1160
1204
  }
1161
1205
  },
1206
+ RenameThreadRequest: {
1207
+ type: "object",
1208
+ required: ["title"],
1209
+ properties: { title: {
1210
+ type: "string",
1211
+ minLength: 1,
1212
+ maxLength: 120
1213
+ } }
1214
+ },
1215
+ RenameThreadResponse: {
1216
+ type: "object",
1217
+ required: ["thread"],
1218
+ properties: { thread: { $ref: "#/components/schemas/ThreadSummary" } }
1219
+ },
1220
+ RewindThreadRequest: {
1221
+ type: "object",
1222
+ required: ["turnId"],
1223
+ properties: { turnId: {
1224
+ type: "string",
1225
+ minLength: 1
1226
+ } }
1227
+ },
1162
1228
  ThreadGoalResponse: {
1163
1229
  type: "object",
1164
1230
  required: ["goal", "thread"],
@@ -1800,4 +1866,4 @@ function jsonResponse(schemaName) {
1800
1866
  };
1801
1867
  }
1802
1868
  //#endregion
1803
- export { RunThreadResponseSchema as $, promptSkillMentionTextCandidates as $t, PairRequestSchema as A, WorkspaceFileMentionSchema as At, PushNotificationIntentSchema as B, WorkspaceTerminalOutputResponseSchema as Bt, ListModelsResponseSchema as C, VersionResponseSchema as Ct, ListWorkspaceDirectoriesResponseSchema as D, WorkspaceChangesResponseSchema as Dt, ListThreadsResponseSchema as E, WebPreviewTargetSchema as Et, PromptAttachmentSchema as F, WorkspaceSelectionRequestSchema as Ft, RateLimitBucketSchema as G, chatMessageDetailsFromPromptContext as Gt, PushNotificationSettingsResponseSchema as H, WorkspaceTerminalSessionResponseSchema as Ht, PromptAttachmentSummarySchema as I, WorkspaceTailscaleServeRequestSchema as It, ReasoningEffortSchema as J, normalizePromptContext as Jt, RateLimitWindowSchema as K, createOpenApiDocument as Kt, PromptContextInputSchema as L, WorkspaceTailscaleServeResponseSchema as Lt, PendingInputRequestOptionSchema as M, WorkspaceMarkdownPreviewTargetSchema as Mt, PendingInputRequestQuestionSchema as N, WorkspacePreviewNavigationRequestSchema as Nt, ListWorkspaceFilesResponseSchema as O, WorkspaceDirectoryEntrySchema as Ot, PendingInputRequestSchema as P, WorkspacePreviewTabSchema as Pt, RunThreadRequestSchema as Q, promptSkillMentionMarkdown as Qt, PromptContextSchema as R, WorkspaceTerminalInputRequestSchema as Rt, KnownReasoningEffortSchema as S, UpdateWorkspaceFileContentRequestSchema as St, ListSkillsResponseSchema as T, WORKSPACE_PREVIEW_TAB_VALUES as Tt, QueuedThreadInputActionResponseSchema as U, WorkspaceTerminalStartRequestSchema as Ut, PushNotificationPreferencesSchema as V, WorkspaceTerminalResizeRequestSchema as Vt, QueuedThreadInputSchema as W, apiPaths as Wt, ResolveApprovalRequestSchema as X, promptSkillDisplayName as Xt, RegisterPushNotificationRequestSchema as Y, promptMarkdownWithSkills as Yt, ResolveApprovalResponseSchema as Z, promptSkillMentionLabel as Zt, EncryptedPayloadSchema as _, ThreadRunOptionsSchema as _t, ArchiveThreadResponseSchema as a, StatusResponseSchema as at, InterruptThreadRunResponseSchema as b, UpdateRuntimePreferencesRequestSchema as bt, ChatMessageRoleSchema as c, SubmitThreadInputResponseSchema as ct, CheckoutWorkspaceBranchRequestSchema as d, ThreadDetailResponseSchema as dt, stripPromptSkillMentions as en, RuntimeModeSchema as et, CodexModelSchema as f, ThreadGoalResponseSchema as ft, CreateThreadResponseSchema as g, ThreadMessageDetailResponseSchema as gt, CreateThreadRequestSchema as h, ThreadMessageDetailFieldSchema as ht, ApprovalModeSchema as i, SandboxModeSchema as it, PairResponseSchema as j, WorkspaceGitActionResponseSchema as jt, PairEncryptedPayloadSchema as k, WorkspaceFileContentResponseSchema as kt, ChatMessageSchema as l, ThreadCollaborationModeSchema as lt, ContextWindowUsageSchema as m, ThreadGoalStatusSchema as mt, AgentSkillSourceSchema as n, RuntimePreferencesResponseSchema as nt, ChatMessageKindSchema as o, StreamThreadRunEventSchema as ot, CommitPushWorkspaceRequestSchema as p, ThreadGoalSchema as pt, RateLimitsResponseSchema as q, isPromptSkillMarkdownMention as qt, ApprovalDecisionSchema as r, RuntimePreferencesSchema as rt, ChatMessagePromptDetailsSchema as s, StreamThreadRunRequestSchema as st, AgentSkillSchema as t, RuntimePreferencesByWorkspacePathSchema as tt, ChatMessageStateSchema as u, ThreadContextWindowResponseSchema as ut, ErrorResponseSchema as v, ThreadStateSchema as vt, ListQueuedThreadInputsResponseSchema as w, WORKSPACE_PREVIEW_OPEN_PROTOCOL as wt, IsoDateTimeSchema as x, UpdateThreadGoalRequestSchema as xt, ImageAttachmentUploadResponseSchema as y, ThreadSummarySchema as yt, PromptSkillSchema as z, WorkspaceTerminalOutputChunkSchema as zt };
1869
+ export { ResolveApprovalResponseSchema as $, promptSkillDisplayName as $t, PairRequestSchema as A, WorkspaceChangesResponseSchema as At, PushNotificationIntentSchema as B, WorkspaceTailscaleServeResponseSchema as Bt, ListModelsResponseSchema as C, UpdateRuntimePreferencesRequestSchema as Ct, ListWorkspaceDirectoriesResponseSchema as D, WORKSPACE_PREVIEW_OPEN_PROTOCOL as Dt, ListThreadsResponseSchema as E, VersionResponseSchema as Et, PromptAttachmentSchema as F, WorkspaceMarkdownPreviewTargetSchema as Ft, RateLimitBucketSchema as G, WorkspaceTerminalSessionResponseSchema as Gt, PushNotificationSettingsResponseSchema as H, WorkspaceTerminalOutputChunkSchema as Ht, PromptAttachmentSummarySchema as I, WorkspacePreviewNavigationRequestSchema as It, ReasoningEffortSchema as J, chatMessageDetailsFromPromptContext as Jt, RateLimitWindowSchema as K, WorkspaceTerminalStartRequestSchema as Kt, PromptContextInputSchema as L, WorkspacePreviewTabSchema as Lt, PendingInputRequestOptionSchema as M, WorkspaceFileContentResponseSchema as Mt, PendingInputRequestQuestionSchema as N, WorkspaceFileMentionSchema as Nt, ListWorkspaceFilesResponseSchema as O, WORKSPACE_PREVIEW_TAB_VALUES as Ot, PendingInputRequestSchema as P, WorkspaceGitActionResponseSchema as Pt, ResolveApprovalRequestSchema as Q, promptMarkdownWithSkills as Qt, PromptContextSchema as R, WorkspaceSelectionRequestSchema as Rt, KnownReasoningEffortSchema as S, ThreadSummarySchema as St, ListSkillsResponseSchema as T, UpdateWorkspaceFileContentRequestSchema as Tt, QueuedThreadInputActionResponseSchema as U, WorkspaceTerminalOutputResponseSchema as Ut, PushNotificationPreferencesSchema as V, WorkspaceTerminalInputRequestSchema as Vt, QueuedThreadInputSchema as W, WorkspaceTerminalResizeRequestSchema as Wt, RenameThreadRequestSchema as X, isPromptSkillMarkdownMention as Xt, RegisterPushNotificationRequestSchema as Y, createOpenApiDocument as Yt, RenameThreadResponseSchema as Z, normalizePromptContext as Zt, EncryptedPayloadSchema as _, ThreadGoalStatusSchema as _t, ArchiveThreadResponseSchema as a, RuntimePreferencesResponseSchema as at, InterruptThreadRunResponseSchema as b, ThreadRunOptionsSchema as bt, ChatMessageRoleSchema as c, StatusResponseSchema as ct, CheckoutWorkspaceBranchRequestSchema as d, SubmitThreadInputResponseSchema as dt, promptSkillMentionLabel as en, RewindThreadRequestSchema as et, CodexModelSchema as f, ThreadCollaborationModeSchema as ft, CreateThreadResponseSchema as g, ThreadGoalSchema as gt, CreateThreadRequestSchema as h, ThreadGoalResponseSchema as ht, ApprovalModeSchema as i, RuntimePreferencesByWorkspacePathSchema as it, PairResponseSchema as j, WorkspaceDirectoryEntrySchema as jt, PairEncryptedPayloadSchema as k, WebPreviewTargetSchema as kt, ChatMessageSchema as l, StreamThreadRunEventSchema as lt, ContextWindowUsageSchema as m, ThreadDetailResponseSchema as mt, AgentSkillSourceSchema as n, promptSkillMentionTextCandidates as nn, RunThreadResponseSchema as nt, ChatMessageKindSchema as o, RuntimePreferencesSchema as ot, CommitPushWorkspaceRequestSchema as p, ThreadContextWindowResponseSchema as pt, RateLimitsResponseSchema as q, apiPaths as qt, ApprovalDecisionSchema as r, stripPromptSkillMentions as rn, RuntimeModeSchema as rt, ChatMessagePromptDetailsSchema as s, SandboxModeSchema as st, AgentSkillSchema as t, promptSkillMentionMarkdown as tn, RunThreadRequestSchema as tt, ChatMessageStateSchema as u, StreamThreadRunRequestSchema as ut, ErrorResponseSchema as v, ThreadMessageDetailFieldSchema as vt, ListQueuedThreadInputsResponseSchema as w, UpdateThreadGoalRequestSchema as wt, IsoDateTimeSchema as x, ThreadStateSchema as xt, ImageAttachmentUploadResponseSchema as y, ThreadMessageDetailResponseSchema as yt, PromptSkillSchema as z, WorkspaceTailscaleServeRequestSchema as zt };
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Wt as apiPaths } from "./api-schema2.js";
2
+ import { qt as apiPaths } from "./api-schema2.js";
3
3
  import { n as codexRelayHome, o as getConnectUrlGuidance, r as legacyCodexRelayDataPath, s as createTursoPairingSessionStore, t as codexRelayDataPath } from "./paths.js";
4
4
  import { Command } from "@commander-js/extra-typings";
5
5
  import qrcode from "qrcode-terminal";
package/dist/src.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as PairRequestSchema, Bt as WorkspaceTerminalOutputResponseSchema, C as ListModelsResponseSchema, Ct as VersionResponseSchema, D as ListWorkspaceDirectoriesResponseSchema, Dt as WorkspaceChangesResponseSchema, E as ListThreadsResponseSchema, Gt as chatMessageDetailsFromPromptContext, H as PushNotificationSettingsResponseSchema, Ht as WorkspaceTerminalSessionResponseSchema, It as WorkspaceTailscaleServeRequestSchema, J as ReasoningEffortSchema, Jt as normalizePromptContext, Kt as createOpenApiDocument, Lt as WorkspaceTailscaleServeResponseSchema, O as ListWorkspaceFilesResponseSchema, Q as RunThreadRequestSchema, Rt as WorkspaceTerminalInputRequestSchema, S as KnownReasoningEffortSchema, St as UpdateWorkspaceFileContentRequestSchema, T as ListSkillsResponseSchema, U as QueuedThreadInputActionResponseSchema, Ut as WorkspaceTerminalStartRequestSchema, Vt as WorkspaceTerminalResizeRequestSchema, Wt as apiPaths, X as ResolveApprovalRequestSchema, Y as RegisterPushNotificationRequestSchema, Yt as promptMarkdownWithSkills, Z as ResolveApprovalResponseSchema, _ as EncryptedPayloadSchema, a as ArchiveThreadResponseSchema, at as StatusResponseSchema, b as InterruptThreadRunResponseSchema, bt as UpdateRuntimePreferencesRequestSchema, ct as SubmitThreadInputResponseSchema, d as CheckoutWorkspaceBranchRequestSchema, dt as ThreadDetailResponseSchema, en as stripPromptSkillMentions, ft as ThreadGoalResponseSchema, gt as ThreadMessageDetailResponseSchema, h as CreateThreadRequestSchema, ht as ThreadMessageDetailFieldSchema, j as PairResponseSchema, jt as WorkspaceGitActionResponseSchema, kt as WorkspaceFileContentResponseSchema, l as ChatMessageSchema, m as ContextWindowUsageSchema, mt as ThreadGoalStatusSchema, nt as RuntimePreferencesResponseSchema, ot as StreamThreadRunEventSchema, p as CommitPushWorkspaceRequestSchema, pt as ThreadGoalSchema, q as RateLimitsResponseSchema, rt as RuntimePreferencesSchema, st as StreamThreadRunRequestSchema, ut as ThreadContextWindowResponseSchema, w as ListQueuedThreadInputsResponseSchema, xt as UpdateThreadGoalRequestSchema, y as ImageAttachmentUploadResponseSchema, yt as ThreadSummarySchema } from "./api-schema2.js";
1
+ import { $ as ResolveApprovalResponseSchema, A as PairRequestSchema, At as WorkspaceChangesResponseSchema, Bt as WorkspaceTailscaleServeResponseSchema, C as ListModelsResponseSchema, Ct as UpdateRuntimePreferencesRequestSchema, D as ListWorkspaceDirectoriesResponseSchema, E as ListThreadsResponseSchema, Et as VersionResponseSchema, Gt as WorkspaceTerminalSessionResponseSchema, H as PushNotificationSettingsResponseSchema, J as ReasoningEffortSchema, Jt as chatMessageDetailsFromPromptContext, Kt as WorkspaceTerminalStartRequestSchema, Mt as WorkspaceFileContentResponseSchema, O as ListWorkspaceFilesResponseSchema, Pt as WorkspaceGitActionResponseSchema, Q as ResolveApprovalRequestSchema, Qt as promptMarkdownWithSkills, S as KnownReasoningEffortSchema, St as ThreadSummarySchema, T as ListSkillsResponseSchema, Tt as UpdateWorkspaceFileContentRequestSchema, U as QueuedThreadInputActionResponseSchema, Ut as WorkspaceTerminalOutputResponseSchema, Vt as WorkspaceTerminalInputRequestSchema, Wt as WorkspaceTerminalResizeRequestSchema, X as RenameThreadRequestSchema, Y as RegisterPushNotificationRequestSchema, Yt as createOpenApiDocument, Z as RenameThreadResponseSchema, Zt as normalizePromptContext, _ as EncryptedPayloadSchema, _t as ThreadGoalStatusSchema, a as ArchiveThreadResponseSchema, at as RuntimePreferencesResponseSchema, b as InterruptThreadRunResponseSchema, ct as StatusResponseSchema, d as CheckoutWorkspaceBranchRequestSchema, dt as SubmitThreadInputResponseSchema, et as RewindThreadRequestSchema, gt as ThreadGoalSchema, h as CreateThreadRequestSchema, ht as ThreadGoalResponseSchema, j as PairResponseSchema, l as ChatMessageSchema, lt as StreamThreadRunEventSchema, m as ContextWindowUsageSchema, mt as ThreadDetailResponseSchema, ot as RuntimePreferencesSchema, p as CommitPushWorkspaceRequestSchema, pt as ThreadContextWindowResponseSchema, q as RateLimitsResponseSchema, qt as apiPaths, rn as stripPromptSkillMentions, tt as RunThreadRequestSchema, ut as StreamThreadRunRequestSchema, vt as ThreadMessageDetailFieldSchema, w as ListQueuedThreadInputsResponseSchema, wt as UpdateThreadGoalRequestSchema, y as ImageAttachmentUploadResponseSchema, yt as ThreadMessageDetailResponseSchema, zt as WorkspaceTailscaleServeRequestSchema } from "./api-schema2.js";
2
2
  import { a as getConnectUrlCandidates, i as createPairingQrPayload, o as getConnectUrlGuidance, r as legacyCodexRelayDataPath, s as createTursoPairingSessionStore, t as codexRelayDataPath } from "./paths.js";
3
3
  import { createRequire } from "node:module";
4
4
  import qrcode from "qrcode-terminal";
@@ -209,6 +209,12 @@ var CodexAppServerClient = class {
209
209
  async archiveThread(params) {
210
210
  await this.request("thread/archive", params);
211
211
  }
212
+ async setThreadName(params) {
213
+ await this.request("thread/name/set", params);
214
+ }
215
+ async rollbackThread(params) {
216
+ return (await this.request("thread/rollback", params)).thread;
217
+ }
212
218
  async getThreadGoal(params) {
213
219
  return (await this.request("thread/goal/get", params)).goal;
214
220
  }
@@ -1463,6 +1469,7 @@ function createApp(options = {}) {
1463
1469
  const resolvedApprovals = /* @__PURE__ */ new Map();
1464
1470
  const queuedInputsByThreadId = /* @__PURE__ */ new Map();
1465
1471
  const threadOperationTails = /* @__PURE__ */ new Map();
1472
+ const appServerMutationTails = /* @__PURE__ */ new Map();
1466
1473
  const workspaceTerminalSessions = /* @__PURE__ */ new Map();
1467
1474
  const activeAppServerTurnIdsByThreadId = /* @__PURE__ */ new Map();
1468
1475
  const appServerHistoryLoadsByThreadId = /* @__PURE__ */ new Map();
@@ -1479,6 +1486,15 @@ function createApp(options = {}) {
1479
1486
  });
1480
1487
  return result;
1481
1488
  }
1489
+ function runAppServerMutation(threadId, operation) {
1490
+ const result = (appServerMutationTails.get(threadId) ?? Promise.resolve()).then(operation, operation);
1491
+ const tail = result.then(() => void 0, () => void 0);
1492
+ appServerMutationTails.set(threadId, tail);
1493
+ tail.finally(() => {
1494
+ if (appServerMutationTails.get(threadId) === tail) appServerMutationTails.delete(threadId);
1495
+ });
1496
+ return result;
1497
+ }
1482
1498
  const pushNotificationDispatcher = options.pairing ? createPushNotificationDispatcher({
1483
1499
  sender: options.pushNotificationSender ?? createExpoPushNotificationSender(),
1484
1500
  sessions: options.pairing.sessions
@@ -1492,7 +1508,7 @@ function createApp(options = {}) {
1492
1508
  threadId
1493
1509
  });
1494
1510
  const load = appServer.readThread(threadId, { includeTurns: true }).then((threadWithTurns) => {
1495
- const mappedThread = rememberAppServerThread(threads, threadWithTurns);
1511
+ const mappedThread = rememberAppServerThread(threads, threadWithTurns, { authoritativeMessageCount: true });
1496
1512
  const messages = mergeAppServerMessagesWithLocalStatus(mapAppServerMessages(threadWithTurns), messagesByThreadId.get(threadId) ?? cachedMessages);
1497
1513
  messagesByThreadId.set(threadId, messages);
1498
1514
  relayDebugLog("thread.detail.history.background_completed", {
@@ -2106,7 +2122,7 @@ function createApp(options = {}) {
2106
2122
  if (appServer) try {
2107
2123
  const appServerThreads = await appServer.listThreads();
2108
2124
  const response = ListThreadsResponseSchema.parse({
2109
- threads: appServerThreads.map((thread) => rememberAppServerThread(threads, thread)),
2125
+ threads: appServerThreads.filter((thread) => !isSubagentThread(thread)).map((thread) => rememberAppServerThread(threads, thread)),
2110
2126
  source: "app-server"
2111
2127
  });
2112
2128
  return secureJson(c, options.pairing, secureSessionsByTokenHash, response);
@@ -2129,7 +2145,7 @@ function createApp(options = {}) {
2129
2145
  const appServerThreads = await appServer.listThreads();
2130
2146
  const response = ArchiveThreadResponseSchema.parse({
2131
2147
  archivedThreadId: threadId,
2132
- threads: appServerThreads.map((thread) => rememberAppServerThread(threads, thread)),
2148
+ threads: appServerThreads.filter((thread) => !isSubagentThread(thread)).map((thread) => rememberAppServerThread(threads, thread)),
2133
2149
  source: "app-server"
2134
2150
  });
2135
2151
  return secureJson(c, options.pairing, secureSessionsByTokenHash, response);
@@ -2151,33 +2167,109 @@ function createApp(options = {}) {
2151
2167
  });
2152
2168
  return secureJson(c, options.pairing, secureSessionsByTokenHash, response);
2153
2169
  });
2170
+ app.post("/v1/threads/:threadId/name", async (c) => {
2171
+ const threadId = c.req.param("threadId");
2172
+ if (!appServer) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("unsupported", "Renaming threads requires the Codex app-server."), 409);
2173
+ if (!await ensureKnownThread({
2174
+ appServer,
2175
+ threadId,
2176
+ messagesByThreadId,
2177
+ threads
2178
+ })) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Thread ${threadId} is not known to this server.`), 404);
2179
+ const parsed = await parseRequestJson(c, options.pairing, secureSessionsByTokenHash, RenameThreadRequestSchema);
2180
+ if (!parsed.success) return secureJson(c, options.pairing, secureSessionsByTokenHash, validationError(parsed.error), 400);
2181
+ try {
2182
+ const body = parsed.data;
2183
+ const thread = await runAppServerMutation(threadId, async () => {
2184
+ await appServer.setThreadName({
2185
+ name: body.title,
2186
+ threadId
2187
+ });
2188
+ return rememberAppServerThread(threads, await appServer.readThread(threadId, { includeTurns: false }));
2189
+ });
2190
+ return secureJson(c, options.pairing, secureSessionsByTokenHash, RenameThreadResponseSchema.parse({ thread }));
2191
+ } catch (error) {
2192
+ const message = errorMessage(error);
2193
+ const status = /not found|no rollout found/i.test(message) ? 404 : 502;
2194
+ return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError(status === 404 ? "not_found" : "rename_unavailable", message), status);
2195
+ }
2196
+ });
2197
+ app.post("/v1/threads/:threadId/rollback", async (c) => {
2198
+ const threadId = c.req.param("threadId");
2199
+ if (!appServer) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("unsupported", "Rewinding threads requires the Codex app-server."), 409);
2200
+ const parsed = await parseRequestJson(c, options.pairing, secureSessionsByTokenHash, RewindThreadRequestSchema);
2201
+ if (!parsed.success) return secureJson(c, options.pairing, secureSessionsByTokenHash, validationError(parsed.error), 400);
2202
+ return runThreadOperation(threadId, () => runAppServerMutation(threadId, async () => {
2203
+ try {
2204
+ const threadBeforeRewind = await appServer.readThread(threadId, { includeTurns: true });
2205
+ if (isSubagentThread(threadBeforeRewind)) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Thread ${threadId} is not known to this server.`), 404);
2206
+ if (mapAppServerThreadState(threadBeforeRewind.status, threadBeforeRewind.turns) === "running" || activeAppServerTurnIdsByThreadId.has(threadId)) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("thread_running", "Stop the current turn before rewinding this chat."), 409);
2207
+ const turns = threadBeforeRewind.turns ?? [];
2208
+ const turnIndex = turns.findIndex((turn) => turn.id === parsed.data.turnId);
2209
+ if (turnIndex < 0) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", "The selected message is no longer available to rewind."), 404);
2210
+ const rolledBackThread = await appServer.rollbackThread({
2211
+ threadId,
2212
+ numTurns: turns.length - turnIndex
2213
+ });
2214
+ const threadWithTurns = rolledBackThread.turns === void 0 ? await appServer.readThread(threadId, { includeTurns: true }) : rolledBackThread;
2215
+ const thread = rememberAppServerThread(threads, threadWithTurns, { authoritativeMessageCount: true });
2216
+ const messages = mapAppServerMessages(threadWithTurns);
2217
+ messagesByThreadId.set(threadId, messages);
2218
+ liveThreads.delete(threadId);
2219
+ activeAppServerTurnIdsByThreadId.delete(threadId);
2220
+ queuedInputsByThreadId.delete(threadId);
2221
+ steeringThreads.delete(threadId);
2222
+ return secureJson(c, options.pairing, secureSessionsByTokenHash, threadDetailResponse({
2223
+ thread,
2224
+ messages,
2225
+ pendingInputRequests: []
2226
+ }));
2227
+ } catch (error) {
2228
+ const message = errorMessage(error);
2229
+ const status = /not found|no rollout found/i.test(message) ? 404 : 502;
2230
+ return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError(status === 404 ? "not_found" : "rewind_unavailable", message), status);
2231
+ }
2232
+ }));
2233
+ });
2154
2234
  app.get("/v1/threads/:threadId", async (c) => {
2155
2235
  const threadId = c.req.param("threadId");
2236
+ const forceRefresh = c.req.query("refresh") === "true";
2156
2237
  const detailStartedAt = Date.now();
2157
2238
  relayDebugLog("thread.detail.requested", { threadId });
2158
2239
  const knownThread = threads.get(threadId);
2240
+ if (knownThread && isSubagentThread(knownThread)) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Thread ${threadId} is not known to this server.`), 404);
2159
2241
  const wasKnownRunning = knownThread?.state === "running" || activeAppServerTurnIdsByThreadId.has(threadId);
2160
2242
  if (appServer) try {
2161
- const mappedThread = rememberAppServerThread(threads, await appServer.readThread(threadId, { includeTurns: false }));
2243
+ const thread = await appServer.readThread(threadId, { includeTurns: false });
2244
+ if (isSubagentThread(thread)) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Thread ${threadId} is not known to this server.`), 404);
2245
+ const mappedThread = rememberAppServerThread(threads, thread);
2162
2246
  const cachedMessages = messagesByThreadId.get(threadId) ?? [];
2163
2247
  let loadedMessages = false;
2164
2248
  let messages = cachedMessages;
2165
2249
  let responseThread = preserveKnownRunningThreadState(mappedThread, wasKnownRunning);
2166
- const rolloutHistory = readRolloutThreadMessages(threadId, workspacePath);
2167
- if (rolloutHistory.messages.length > 0) {
2168
- messages = mergeThreadMessagePages(rolloutHistory.messages, cachedMessages);
2169
- responseThread = rememberRolloutThreadMessages(threads, responseThread, messages, rolloutHistory.messageCountLowerBound);
2170
- responseThread = preserveKnownRunningThreadState(responseThread, wasKnownRunning);
2250
+ if (forceRefresh && responseThread.state !== "running") {
2251
+ const threadWithTurns = await appServer.readThread(threadId, { includeTurns: true });
2252
+ responseThread = rememberAppServerThread(threads, threadWithTurns, { authoritativeMessageCount: true });
2253
+ messages = mapAppServerMessages(threadWithTurns);
2171
2254
  messagesByThreadId.set(threadId, messages);
2172
2255
  loadedMessages = true;
2173
- } else if (cachedMessages.length > 0) {
2174
- messages = dedupeThreadMessages(cachedMessages);
2175
- if (messages.length !== cachedMessages.length) messagesByThreadId.set(threadId, messages);
2176
- loadedMessages = true;
2256
+ } else {
2257
+ const rolloutHistory = readRolloutThreadMessages(threadId, workspacePath);
2258
+ if (rolloutHistory.messages.length > 0) {
2259
+ messages = mergeThreadMessagePages(rolloutHistory.messages, cachedMessages);
2260
+ responseThread = rememberRolloutThreadMessages(threads, responseThread, messages, rolloutHistory.messageCountLowerBound);
2261
+ responseThread = preserveKnownRunningThreadState(responseThread, wasKnownRunning);
2262
+ messagesByThreadId.set(threadId, messages);
2263
+ loadedMessages = true;
2264
+ } else if (cachedMessages.length > 0) {
2265
+ messages = dedupeThreadMessages(cachedMessages);
2266
+ if (messages.length !== cachedMessages.length) messagesByThreadId.set(threadId, messages);
2267
+ loadedMessages = true;
2268
+ }
2177
2269
  }
2178
2270
  if (!loadedMessages && responseThread.state !== "running") {
2179
2271
  const threadWithTurns = await appServer.readThread(threadId, { includeTurns: true });
2180
- responseThread = rememberAppServerThread(threads, threadWithTurns);
2272
+ responseThread = rememberAppServerThread(threads, threadWithTurns, { authoritativeMessageCount: true });
2181
2273
  messages = mergeAppServerMessagesWithLocalStatus(mapAppServerMessages(threadWithTurns), cachedMessages);
2182
2274
  messagesByThreadId.set(threadId, messages);
2183
2275
  loadedMessages = true;
@@ -2504,30 +2596,32 @@ function createApp(options = {}) {
2504
2596
  app.post("/v1/threads/:threadId/input/:inputId/steer", async (c) => {
2505
2597
  const threadId = c.req.param("threadId");
2506
2598
  const inputId = c.req.param("inputId");
2507
- const knownThread = await ensureKnownThread({
2508
- appServer,
2509
- threadId,
2510
- messagesByThreadId,
2511
- threads
2512
- });
2513
- if (!knownThread) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Thread ${threadId} is not known to this server.`), 404);
2514
- if (!appServer) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("unsupported", "Running-thread input requires the Codex app-server."), 409);
2515
- if (knownThread.state !== "running") return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("thread_not_running", `Thread ${threadId} is not currently running.`), 409);
2516
- const queuedInput = removeQueuedInput(queuedInputsByThreadId, threadId, inputId);
2517
- if (!queuedInput) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Queued input ${inputId} is not known to this thread.`), 404);
2518
- steeringThreads.add(threadId);
2519
- await startAppServerTurn(appServer, threadId, queuedInput);
2520
- const thread = updateThread(threads, messagesByThreadId, threadId, {
2521
- state: "running",
2522
- lastPrompt: promptWithAttachmentReferences(queuedInput.prompt, queuedInput.attachments),
2523
- lastError: void 0
2524
- });
2525
- const response = QueuedThreadInputActionResponseSchema.parse({
2526
- input: queuedThreadInputSummary(queuedInput),
2527
- queueLength: queuedInputsByThreadId.get(threadId)?.length ?? 0,
2528
- thread
2599
+ return runThreadOperation(threadId, async () => {
2600
+ const knownThread = await ensureKnownThread({
2601
+ appServer,
2602
+ threadId,
2603
+ messagesByThreadId,
2604
+ threads
2605
+ });
2606
+ if (!knownThread) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Thread ${threadId} is not known to this server.`), 404);
2607
+ if (!appServer) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("unsupported", "Running-thread input requires the Codex app-server."), 409);
2608
+ if (knownThread.state !== "running") return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("thread_not_running", `Thread ${threadId} is not currently running.`), 409);
2609
+ const queuedInput = removeQueuedInput(queuedInputsByThreadId, threadId, inputId);
2610
+ if (!queuedInput) return secureJson(c, options.pairing, secureSessionsByTokenHash, apiError("not_found", `Queued input ${inputId} is not known to this thread.`), 404);
2611
+ steeringThreads.add(threadId);
2612
+ await runAppServerMutation(threadId, () => startAppServerTurn(appServer, threadId, queuedInput));
2613
+ const thread = updateThread(threads, messagesByThreadId, threadId, {
2614
+ state: "running",
2615
+ lastPrompt: promptWithAttachmentReferences(queuedInput.prompt, queuedInput.attachments),
2616
+ lastError: void 0
2617
+ });
2618
+ const response = QueuedThreadInputActionResponseSchema.parse({
2619
+ input: queuedThreadInputSummary(queuedInput),
2620
+ queueLength: queuedInputsByThreadId.get(threadId)?.length ?? 0,
2621
+ thread
2622
+ });
2623
+ return secureJson(c, options.pairing, secureSessionsByTokenHash, response, 202);
2529
2624
  });
2530
- return secureJson(c, options.pairing, secureSessionsByTokenHash, response, 202);
2531
2625
  });
2532
2626
  app.post("/v1/threads/:threadId/runs/interrupt", async (c) => {
2533
2627
  const threadId = c.req.param("threadId");
@@ -2714,6 +2808,7 @@ function createApp(options = {}) {
2714
2808
  messagesByThreadId,
2715
2809
  pendingApprovals,
2716
2810
  queuedInputsByThreadId,
2811
+ runAppServerMutation,
2717
2812
  runThreadOperation,
2718
2813
  prompt: rawPrompt,
2719
2814
  attachments: runOptions.attachments ?? [],
@@ -2926,6 +3021,7 @@ async function runPromptStreamed(input) {
2926
3021
  messagesByThreadId: input.messagesByThreadId,
2927
3022
  pendingApprovals: input.pendingApprovals,
2928
3023
  queuedInputsByThreadId: input.queuedInputsByThreadId,
3024
+ runAppServerMutation: input.runAppServerMutation,
2929
3025
  runThreadOperation: input.runThreadOperation,
2930
3026
  prompt: input.prompt,
2931
3027
  runOptions: input.runOptions,
@@ -3546,7 +3642,7 @@ async function runAppServerPromptStreamed(input) {
3546
3642
  observedInputRequest = false;
3547
3643
  producedTurnOutput = false;
3548
3644
  }
3549
- function finishTerminalTurn(options) {
3645
+ async function finishTerminalTurn(options) {
3550
3646
  const terminalTurnId = options.turnId ?? activeTurnId;
3551
3647
  if (terminalTurnId && finalizedTurnIds.has(terminalTurnId)) return;
3552
3648
  if (terminalTurnId) {
@@ -3624,25 +3720,36 @@ async function runAppServerPromptStreamed(input) {
3624
3720
  handedOffToQueuedTurn = true;
3625
3721
  resetTurnTracking();
3626
3722
  input.steeringThreads.add(activeThreadId);
3627
- startAppServerTurn(input.appServer, activeThreadId, nextQueuedInput).then(processReturnedTurn).catch((error) => {
3628
- cleanupNotificationHandler();
3629
- rejectCompleted(error);
3630
- });
3723
+ await processReturnedTurn(await startAppServerTurn(input.appServer, activeThreadId, nextQueuedInput));
3631
3724
  return;
3632
3725
  }
3633
3726
  input.steeringThreads.delete(activeThreadId);
3634
3727
  cleanupNotificationHandler();
3635
3728
  resolveCompleted();
3636
3729
  }
3637
- function processReturnedTurn(turn) {
3730
+ async function processReturnedTurn(turn) {
3638
3731
  if (finalizedTurnIds.has(turn.id)) return;
3639
3732
  activeTurnId = turn.id;
3640
3733
  input.activeAppServerTurnIdsByThreadId.set(activeThreadId, turn.id);
3641
3734
  const turnIsRunning = isAppServerTurnRunning(turn);
3642
3735
  for (const item of turn.items) {
3736
+ const previousUserMessage = userMessage;
3643
3737
  const canonicalUserMessage = replaceDuplicateInitialUserMessage(input.messagesByThreadId, activeThreadId, turn.id, item, userMessage.id, displayPrompt);
3644
3738
  if (canonicalUserMessage) {
3645
3739
  userMessage = canonicalUserMessage;
3740
+ if (canonicalUserMessage.id !== previousUserMessage.id || canonicalUserMessage.turnId !== previousUserMessage.turnId) {
3741
+ relayDebugLog("app_server.user_message.canonicalized", {
3742
+ messageId: canonicalUserMessage.id,
3743
+ previousMessageId: previousUserMessage.id,
3744
+ threadId: activeThreadId,
3745
+ turnId: canonicalUserMessage.turnId
3746
+ });
3747
+ sendSse(input.controller, input.encoder, input.secureSession, {
3748
+ type: "thread.message.created",
3749
+ thread: threadSummary,
3750
+ message: canonicalUserMessage
3751
+ });
3752
+ }
3646
3753
  continue;
3647
3754
  }
3648
3755
  const message = upsertAppServerItemMessage(input.messagesByThreadId, activeThreadId, turn.id, item);
@@ -3662,13 +3769,20 @@ async function runAppServerPromptStreamed(input) {
3662
3769
  if (turnIsRunning) return;
3663
3770
  const params = { turn };
3664
3771
  const state = terminalTurnState("turn/completed", params);
3665
- finishTerminalTurn({
3772
+ await finishTerminalTurn({
3666
3773
  lastError: state === "failed" ? turnErrorMessage(params) : void 0,
3667
3774
  method: "turn/returned",
3668
3775
  state,
3669
3776
  turnId: turn.id
3670
3777
  });
3671
3778
  }
3779
+ function startAndProcessAppServerTurn(threadId, queuedInput) {
3780
+ return input.runAppServerMutation(threadId, async () => {
3781
+ const turn = await startAppServerTurn(input.appServer, threadId, queuedInput);
3782
+ await processReturnedTurn(turn);
3783
+ return turn;
3784
+ });
3785
+ }
3672
3786
  const completed = new Promise((resolve, reject) => {
3673
3787
  resolveCompleted = resolve;
3674
3788
  rejectCompleted = reject;
@@ -3708,9 +3822,23 @@ async function runAppServerPromptStreamed(input) {
3708
3822
  case "item/completed": {
3709
3823
  const item = params?.item;
3710
3824
  if (!item || typeof item !== "object") return;
3825
+ const previousUserMessage = userMessage;
3711
3826
  const canonicalUserMessage = replaceDuplicateInitialUserMessage(input.messagesByThreadId, activeThreadId, firstString(params, ["turnId"]) ?? activeTurnId, item, userMessage.id, displayPrompt);
3712
3827
  if (canonicalUserMessage) {
3713
3828
  userMessage = canonicalUserMessage;
3829
+ if (canonicalUserMessage.id !== previousUserMessage.id || canonicalUserMessage.turnId !== previousUserMessage.turnId) {
3830
+ relayDebugLog("app_server.user_message.canonicalized", {
3831
+ messageId: canonicalUserMessage.id,
3832
+ previousMessageId: previousUserMessage.id,
3833
+ threadId: activeThreadId,
3834
+ turnId: canonicalUserMessage.turnId
3835
+ });
3836
+ sendSse(input.controller, input.encoder, input.secureSession, {
3837
+ type: "thread.message.created",
3838
+ thread: threadSummary,
3839
+ message: canonicalUserMessage
3840
+ });
3841
+ }
3714
3842
  return;
3715
3843
  }
3716
3844
  const turnId = firstString(params, ["turnId"]) ?? activeTurnId;
@@ -3781,12 +3909,12 @@ async function runAppServerPromptStreamed(input) {
3781
3909
  const state = terminalTurnState(notification.method, params);
3782
3910
  const terminalTurnId = firstString(params, ["turnId"]) ?? turnIdFromParams(params) ?? activeTurnId;
3783
3911
  input.runThreadOperation(activeThreadId, async () => {
3784
- finishTerminalTurn({
3912
+ await input.runAppServerMutation(activeThreadId, () => finishTerminalTurn({
3785
3913
  lastError: state === "failed" ? turnErrorMessage(params) : void 0,
3786
3914
  method: notification.method,
3787
3915
  state,
3788
3916
  turnId: terminalTurnId
3789
- });
3917
+ }));
3790
3918
  }).catch((error) => {
3791
3919
  cleanupNotificationHandler();
3792
3920
  rejectCompleted(error);
@@ -3847,7 +3975,7 @@ async function runAppServerPromptStreamed(input) {
3847
3975
  debugStream("start turn begin", activeThreadId);
3848
3976
  let turn;
3849
3977
  try {
3850
- turn = await startAppServerTurn(input.appServer, activeThreadId, {
3978
+ turn = await startAndProcessAppServerTurn(activeThreadId, {
3851
3979
  attachments: input.attachments,
3852
3980
  id: randomUUID(),
3853
3981
  prompt,
@@ -3881,7 +4009,7 @@ async function runAppServerPromptStreamed(input) {
3881
4009
  thread: threadSummary,
3882
4010
  message: userMessage
3883
4011
  });
3884
- turn = await startAppServerTurn(input.appServer, activeThreadId, {
4012
+ turn = await startAndProcessAppServerTurn(activeThreadId, {
3885
4013
  attachments: input.attachments,
3886
4014
  id: randomUUID(),
3887
4015
  prompt,
@@ -3891,7 +4019,6 @@ async function runAppServerPromptStreamed(input) {
3891
4019
  });
3892
4020
  }
3893
4021
  debugStream("start turn complete", activeThreadId, turn.id);
3894
- processReturnedTurn(turn);
3895
4022
  await completed;
3896
4023
  } catch (error) {
3897
4024
  debugStream(`failed ${errorMessage(error)}`, activeThreadId, activeTurnId);
@@ -4099,14 +4226,15 @@ function createSecureSessionHandle(pairing, tokenHash, session) {
4099
4226
  };
4100
4227
  }
4101
4228
  function sortedThreads(threads) {
4102
- return [...threads.values()].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
4229
+ return [...threads.values()].filter((thread) => !isSubagentThread(thread)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
4103
4230
  }
4104
4231
  async function ensureKnownThread(input) {
4105
4232
  const knownThread = input.threads.get(input.threadId);
4106
- if (knownThread) return knownThread;
4233
+ if (knownThread) return isSubagentThread(knownThread) ? void 0 : knownThread;
4107
4234
  if (!input.appServer) return;
4108
4235
  try {
4109
4236
  const appServerThread = await input.appServer.readThread(input.threadId, { includeTurns: false });
4237
+ if (isSubagentThread(appServerThread)) return;
4110
4238
  return rememberAppServerThread(input.threads, appServerThread);
4111
4239
  } catch {
4112
4240
  return;
@@ -4846,6 +4974,7 @@ function mapAppServerThread(thread, fallbackMessageCount) {
4846
4974
  const mappedState = mapAppServerThreadState(thread.status, thread.turns);
4847
4975
  return ThreadSummarySchema.parse({
4848
4976
  id: thread.id,
4977
+ parentThreadId: thread.parentThreadId ?? void 0,
4849
4978
  title: thread.name ?? preview(thread.preview || "Untitled thread"),
4850
4979
  createdAt,
4851
4980
  updatedAt,
@@ -4857,9 +4986,9 @@ function mapAppServerThread(thread, fallbackMessageCount) {
4857
4986
  lastActivityAt: updatedAt
4858
4987
  });
4859
4988
  }
4860
- function rememberAppServerThread(threads, thread) {
4989
+ function rememberAppServerThread(threads, thread, options = {}) {
4861
4990
  const existingThread = threads.get(thread.id);
4862
- const mappedThread = mapAppServerThread(thread, existingThread?.messageCount);
4991
+ const mappedThread = mapAppServerThread(thread, options.authoritativeMessageCount ? void 0 : existingThread?.messageCount);
4863
4992
  const threadWithLocalRuntime = ThreadSummarySchema.parse({
4864
4993
  ...mappedThread,
4865
4994
  goal: existingThread?.goal ?? mappedThread.goal,
@@ -4869,6 +4998,9 @@ function rememberAppServerThread(threads, thread) {
4869
4998
  threads.set(threadWithLocalRuntime.id, threadWithLocalRuntime);
4870
4999
  return threadWithLocalRuntime;
4871
5000
  }
5001
+ function isSubagentThread(thread) {
5002
+ return Boolean(thread.parentThreadId);
5003
+ }
4872
5004
  function mapAppServerThreadGoal(goal) {
4873
5005
  if (!goal) return null;
4874
5006
  return ThreadGoalSchema.parse({
@@ -5045,6 +5177,7 @@ function readRolloutThreadMessages(threadId, workspacePath = defaultWorkspacePat
5045
5177
  const applyPatchInputs = /* @__PURE__ */ new Map();
5046
5178
  const handledApplyPatchCallIds = /* @__PURE__ */ new Set();
5047
5179
  const pendingApplyPatchChanges = [];
5180
+ let activeTurnId;
5048
5181
  const lines = readFileSync(rolloutPath, "utf8").split("\n");
5049
5182
  for (let index = 0; index < lines.length; index += 1) {
5050
5183
  const line = lines[index];
@@ -5053,14 +5186,17 @@ function readRolloutThreadMessages(threadId, workspacePath = defaultWorkspacePat
5053
5186
  if (!isRolloutMessageLine(line)) continue;
5054
5187
  try {
5055
5188
  const record = JSON.parse(line);
5189
+ activeTurnId = firstString(record.payload, ["turn_id", "turnId"]) ?? activeTurnId;
5056
5190
  rememberRolloutApplyPatchInput(record, applyPatchInputs);
5057
5191
  collectRolloutApplyPatchOutput(record, workspacePath, applyPatchInputs, handledApplyPatchCallIds, pendingApplyPatchChanges);
5058
5192
  if (isRolloutTaskComplete(record) && pendingApplyPatchChanges.length > 0) {
5059
5193
  collected.push(rolloutApplyPatchSummaryMessage(threadId, record, `rollout:${lineNumber}:apply_patch`, pendingApplyPatchChanges));
5060
5194
  pendingApplyPatchChanges.length = 0;
5195
+ activeTurnId = void 0;
5061
5196
  continue;
5062
5197
  }
5063
- const message = rolloutRecordMessage(threadId, record, `rollout:${lineNumber}`, workspacePath);
5198
+ const message = rolloutRecordMessage(threadId, record, `rollout:${lineNumber}`, workspacePath, activeTurnId);
5199
+ if (isRolloutTaskComplete(record)) activeTurnId = void 0;
5064
5200
  if (!message) continue;
5065
5201
  collected.push(message);
5066
5202
  const patchApplyEndCallId = rolloutPatchApplyEndCallId(record);
@@ -5095,7 +5231,7 @@ function findRolloutFileForThread(threadId) {
5095
5231
  function isRolloutTaskComplete(record) {
5096
5232
  return record.type === "event_msg" && record.payload?.type === "task_complete";
5097
5233
  }
5098
- function rolloutRecordMessage(threadId, record, messageKey, workspacePath = defaultWorkspacePath) {
5234
+ function rolloutRecordMessage(threadId, record, messageKey, workspacePath = defaultWorkspacePath, turnId) {
5099
5235
  const timestamp = typeof record.timestamp === "string" ? record.timestamp : (/* @__PURE__ */ new Date()).toISOString();
5100
5236
  const payload = record.payload;
5101
5237
  if (!payload) return;
@@ -5109,7 +5245,8 @@ function rolloutRecordMessage(threadId, record, messageKey, workspacePath = defa
5109
5245
  content,
5110
5246
  details: rolloutUserMessageDetails(payload),
5111
5247
  createdAt: timestamp,
5112
- state: "completed"
5248
+ state: "completed",
5249
+ turnId
5113
5250
  });
5114
5251
  }
5115
5252
  if (record.type === "event_msg" && payload.type === "agent_message") {
@@ -5123,7 +5260,8 @@ function rolloutRecordMessage(threadId, record, messageKey, workspacePath = defa
5123
5260
  content: messageParts.content,
5124
5261
  details: messageParts.details,
5125
5262
  createdAt: timestamp,
5126
- state: "completed"
5263
+ state: "completed",
5264
+ turnId
5127
5265
  });
5128
5266
  }
5129
5267
  if (record.type === "event_msg" && payload.type === "patch_apply_end") {
@@ -5331,7 +5469,7 @@ function fileUrlPath(value) {
5331
5469
  }
5332
5470
  }
5333
5471
  function isRolloutMessageLine(line) {
5334
- return line.includes("\"type\":\"event_msg\"") && (line.includes("\"type\":\"user_message\"") || line.includes("\"type\":\"agent_message\"") || line.includes("\"type\":\"patch_apply_end\"") || line.includes("\"type\":\"task_complete\"") || line.includes("\"type\":\"exec_command_end\"") || line.includes("\"type\":\"mcp_tool_call_end\"")) || line.includes("\"type\":\"response_item\"") && (line.includes("\"type\":\"custom_tool_call\"") || line.includes("\"type\":\"custom_tool_call_output\""));
5472
+ return line.includes("\"type\":\"event_msg\"") && (line.includes("\"type\":\"user_message\"") || line.includes("\"type\":\"agent_message\"") || line.includes("\"type\":\"patch_apply_end\"") || line.includes("\"type\":\"task_started\"") || line.includes("\"type\":\"task_complete\"") || line.includes("\"type\":\"exec_command_end\"") || line.includes("\"type\":\"mcp_tool_call_end\"")) || line.includes("\"type\":\"response_item\"") && (line.includes("\"type\":\"custom_tool_call\"") || line.includes("\"type\":\"custom_tool_call_output\""));
5335
5473
  }
5336
5474
  function threadDetailResponse(input) {
5337
5475
  return ThreadDetailResponseSchema.parse({
@@ -5828,7 +5966,6 @@ function isApprovalServerRequest(method) {
5828
5966
  }
5829
5967
  function observeAppServerPushNotifications(appServer, dispatcher) {
5830
5968
  const dispatchedEventIds = /* @__PURE__ */ new Set();
5831
- const subagentThreadIds = /* @__PURE__ */ new Set();
5832
5969
  const rememberEventId = (eventId) => {
5833
5970
  if (dispatchedEventIds.size >= 1e3) dispatchedEventIds.clear();
5834
5971
  dispatchedEventIds.add(eventId);
@@ -5836,7 +5973,9 @@ function observeAppServerPushNotifications(appServer, dispatcher) {
5836
5973
  const dispatch = (event, eventId) => {
5837
5974
  if (dispatchedEventIds.has(eventId)) return;
5838
5975
  rememberEventId(eventId);
5839
- dispatcher.dispatch(event).catch((error) => {
5976
+ appServer.readThread(event.threadId, { includeTurns: false }).then((thread) => {
5977
+ if (!isSubagentThread(thread)) return dispatcher.dispatch(event);
5978
+ }).catch((error) => {
5840
5979
  relayDebugLog("push_notification.dispatch_failed", {
5841
5980
  error: errorMessage(error),
5842
5981
  intent: event.intent,
@@ -5846,15 +5985,9 @@ function observeAppServerPushNotifications(appServer, dispatcher) {
5846
5985
  });
5847
5986
  };
5848
5987
  appServer.onNotification((notification) => {
5849
- rememberSubagentThreadIds(notification, subagentThreadIds);
5850
5988
  const event = pushNotificationEventFromTerminalNotification(notification);
5851
5989
  if (!event) return;
5852
- const eventId = `${event.intent}:${event.threadId}:${event.turnId ?? ""}`;
5853
- if (subagentThreadIds.delete(event.threadId)) {
5854
- rememberEventId(eventId);
5855
- return;
5856
- }
5857
- dispatch(event, eventId);
5990
+ dispatch(event, `${event.intent}:${event.threadId}:${event.turnId ?? ""}`);
5858
5991
  });
5859
5992
  appServer.onRequest((request) => {
5860
5993
  const event = pushNotificationEventFromApprovalRequest(request);
@@ -5862,15 +5995,6 @@ function observeAppServerPushNotifications(appServer, dispatcher) {
5862
5995
  dispatch(event, `${event.intent}:${event.threadId}:${event.turnId ?? ""}:${request.id}`);
5863
5996
  });
5864
5997
  }
5865
- function rememberSubagentThreadIds(notification, subagentThreadIds) {
5866
- const item = objectRecord(recordParams(notification)?.item);
5867
- if (item?.type !== "collabAgentToolCall" || ![
5868
- "spawnAgent",
5869
- "sendInput",
5870
- "resumeAgent"
5871
- ].includes(String(item.tool))) return;
5872
- for (const threadId of stringArray(item.receiverThreadIds)) subagentThreadIds.add(threadId);
5873
- }
5874
5998
  function pushNotificationEventFromTerminalNotification(notification) {
5875
5999
  if (notification.method !== "turn/completed" && notification.method !== "turn/failed") return;
5876
6000
  const params = recordParams(notification);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-relay",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "description": "Local Codex Relay CLI bridge for the Codex Relay mobile app.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/api-schema.ts CHANGED
@@ -304,6 +304,7 @@ export const ChatMessageSchema = z.object({
304
304
 
305
305
  export const ThreadSummarySchema = z.object({
306
306
  id: z.string().min(1),
307
+ parentThreadId: z.string().min(1).optional(),
307
308
  title: z.string().min(1),
308
309
  createdAt: IsoDateTimeSchema,
309
310
  updatedAt: IsoDateTimeSchema,
@@ -680,6 +681,18 @@ export const ArchiveThreadResponseSchema = z.object({
680
681
  source: z.enum(["app-server", "memory"]).default("memory"),
681
682
  });
682
683
 
684
+ export const RenameThreadRequestSchema = z.object({
685
+ title: z.string().trim().min(1).max(120),
686
+ });
687
+
688
+ export const RenameThreadResponseSchema = z.object({
689
+ thread: ThreadSummarySchema,
690
+ });
691
+
692
+ export const RewindThreadRequestSchema = z.object({
693
+ turnId: z.string().trim().min(1),
694
+ });
695
+
683
696
  export const ListModelsResponseSchema = z.object({
684
697
  models: z.array(CodexModelSchema),
685
698
  });
@@ -843,6 +856,9 @@ export type UpdateRuntimePreferencesRequest = z.infer<typeof UpdateRuntimePrefer
843
856
  export type VersionResponse = z.infer<typeof VersionResponseSchema>;
844
857
  export type ListThreadsResponse = z.infer<typeof ListThreadsResponseSchema>;
845
858
  export type ArchiveThreadResponse = z.infer<typeof ArchiveThreadResponseSchema>;
859
+ export type RenameThreadRequest = z.infer<typeof RenameThreadRequestSchema>;
860
+ export type RenameThreadResponse = z.infer<typeof RenameThreadResponseSchema>;
861
+ export type RewindThreadRequest = z.infer<typeof RewindThreadRequestSchema>;
846
862
  export type ListModelsResponse = z.infer<typeof ListModelsResponseSchema>;
847
863
  export type ListSkillsResponse = z.infer<typeof ListSkillsResponseSchema>;
848
864
  export type ListWorkspaceFilesResponse = z.infer<typeof ListWorkspaceFilesResponseSchema>;
@@ -1088,6 +1104,8 @@ export const apiPaths = {
1088
1104
  threads: "/v1/threads",
1089
1105
  thread: (threadId: string) => `/v1/threads/${encodeURIComponent(threadId)}`,
1090
1106
  threadArchive: (threadId: string) => `/v1/threads/${encodeURIComponent(threadId)}`,
1107
+ threadName: (threadId: string) => `/v1/threads/${encodeURIComponent(threadId)}/name`,
1108
+ threadRollback: (threadId: string) => `/v1/threads/${encodeURIComponent(threadId)}/rollback`,
1091
1109
  threadContextWindow: (threadId: string) =>
1092
1110
  `/v1/threads/${encodeURIComponent(threadId)}/context-window`,
1093
1111
  threadGoal: (threadId: string) => `/v1/threads/${encodeURIComponent(threadId)}/goal`,
@@ -1190,6 +1208,11 @@ export function createOpenApiDocument() {
1190
1208
  required: true,
1191
1209
  schema: { type: "string" },
1192
1210
  },
1211
+ {
1212
+ name: "refresh",
1213
+ in: "query",
1214
+ schema: { type: "boolean" },
1215
+ },
1193
1216
  ],
1194
1217
  responses: {
1195
1218
  "200": jsonResponse("ThreadDetailResponse"),
@@ -1213,6 +1236,48 @@ export function createOpenApiDocument() {
1213
1236
  },
1214
1237
  },
1215
1238
  },
1239
+ "/v1/threads/{threadId}/name": {
1240
+ post: {
1241
+ summary: "Rename a Codex app-server thread",
1242
+ parameters: [
1243
+ {
1244
+ name: "threadId",
1245
+ in: "path",
1246
+ required: true,
1247
+ schema: { type: "string" },
1248
+ },
1249
+ ],
1250
+ requestBody: jsonRequest("RenameThreadRequest"),
1251
+ responses: {
1252
+ "200": jsonResponse("RenameThreadResponse"),
1253
+ "400": jsonResponse("ErrorResponse"),
1254
+ "404": jsonResponse("ErrorResponse"),
1255
+ "409": jsonResponse("ErrorResponse"),
1256
+ "502": jsonResponse("ErrorResponse"),
1257
+ },
1258
+ },
1259
+ },
1260
+ "/v1/threads/{threadId}/rollback": {
1261
+ post: {
1262
+ summary: "Rewind a Codex app-server thread to before a selected turn",
1263
+ parameters: [
1264
+ {
1265
+ name: "threadId",
1266
+ in: "path",
1267
+ required: true,
1268
+ schema: { type: "string" },
1269
+ },
1270
+ ],
1271
+ requestBody: jsonRequest("RewindThreadRequest"),
1272
+ responses: {
1273
+ "200": jsonResponse("ThreadDetailResponse"),
1274
+ "400": jsonResponse("ErrorResponse"),
1275
+ "404": jsonResponse("ErrorResponse"),
1276
+ "409": jsonResponse("ErrorResponse"),
1277
+ "502": jsonResponse("ErrorResponse"),
1278
+ },
1279
+ },
1280
+ },
1216
1281
  "/v1/workspace-directories": {
1217
1282
  get: {
1218
1283
  summary: "List directories under the configured workspace",
@@ -1451,6 +1516,27 @@ export function createOpenApiDocument() {
1451
1516
  tokenBudget: { anyOf: [{ type: "integer", minimum: 1 }, { type: "null" }] },
1452
1517
  },
1453
1518
  },
1519
+ RenameThreadRequest: {
1520
+ type: "object",
1521
+ required: ["title"],
1522
+ properties: {
1523
+ title: { type: "string", minLength: 1, maxLength: 120 },
1524
+ },
1525
+ },
1526
+ RenameThreadResponse: {
1527
+ type: "object",
1528
+ required: ["thread"],
1529
+ properties: {
1530
+ thread: { $ref: "#/components/schemas/ThreadSummary" },
1531
+ },
1532
+ },
1533
+ RewindThreadRequest: {
1534
+ type: "object",
1535
+ required: ["turnId"],
1536
+ properties: {
1537
+ turnId: { type: "string", minLength: 1 },
1538
+ },
1539
+ },
1454
1540
  ThreadGoalResponse: {
1455
1541
  type: "object",
1456
1542
  required: ["goal", "thread"],