History

maybe fix codex subturns

Julius Marminge committed 8 months ago

Commit ef843d41 parent 654ca0d4 files · +334 −16Browse files

Showing the changes to apps/server/src/codexAppServerManager.ts (1 of 4 files). Show all

Unified · Split

apps/server/src/codexAppServerManager.ts+94−10
@@ -70,6 +70,7 @@ interface CodexSessionContext { · 69 unmodified lines
7070 pending: Map<PendingRequestKey, PendingRequest>;
7171 pendingApprovals: Map<ApprovalRequestId, PendingApprovalRequest>;
7272 pendingUserInputs: Map<ApprovalRequestId, PendingUserInputRequest>;
73+ collabReceiverTurns: Map<string, TurnId>;
7374 nextRequestId: number;
7475 stopping: boolean;
7576 }
@@ -571,6 +572,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 495 unmodified lines
571572 pending: new Map(),
572573 pendingApprovals: new Map(),
573574 pendingUserInputs: new Map(),
575+ collabReceiverTurns: new Map(),
574576 nextRequestId: 1,
575577 stopping: false,
576578 };
@@ -732,6 +734,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 155 unmodified lines
732734
733735 async sendTurn(input: CodexAppServerSendTurnInput): Promise<ProviderTurnStartResult> {
734736 const context = this.requireSession(input.threadId);
737+ context.collabReceiverTurns.clear();
735738
736739 const turnInput: Array<
737740 { type: "text"; text: string; text_elements: [] } | { type: "image"; url: string }
@@ -1121,7 +1124,16 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 383 unmodified lines
11211124 context: CodexSessionContext,
11221125 notification: JsonRpcNotification,
11231126 ): void {
1124− const route = this.readRouteFields(notification.params);
1127+ const rawRoute = this.readRouteFields(notification.params);
1128+ this.rememberCollabReceiverTurns(context, notification.params, rawRoute.turnId);
1129+ const childParentTurnId = this.readChildParentTurnId(context, notification.params);
1130+ const isChildConversation = childParentTurnId !== undefined;
1131+ if (
1132+ isChildConversation &&
1133+ this.shouldSuppressChildConversationNotification(notification.method)
1134+ ) {
1135+ return;
1136+ }
11251137 const textDelta =
11261138 notification.method === "item/agentMessage/delta"
11271139 ? this.readString(notification.params, "delta")
@@ -1134,8 +1146,10 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 6 unmodified lines
11341146 threadId: context.session.threadId,
11351147 createdAt: new Date().toISOString(),
11361148 method: notification.method,
1137− turnId: route.turnId,
1138− itemId: route.itemId,
1149+ ...((childParentTurnId ?? rawRoute.turnId)
1150+ ? { turnId: childParentTurnId ?? rawRoute.turnId }
1151+ : {}),
1152+ ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
11391153 textDelta,
11401154 payload: notification.params,
11411155 });
@@ -1151,6 +1165,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 9 unmodified lines
11511165 }
11521166
11531167 if (notification.method === "turn/started") {
1168+ if (isChildConversation) {
1169+ return;
1170+ }
11541171 const turnId = toTurnId(this.readString(this.readObject(notification.params)?.turn, "id"));
11551172 this.updateSession(context, {
11561173 status: "running",
@@ -1160,6 +1177,10 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 3 unmodified lines
11601177 }
11611178
11621179 if (notification.method === "turn/completed") {
1180+ if (isChildConversation) {
1181+ return;
1182+ }
1183+ context.collabReceiverTurns.clear();
11631184 const turn = this.readObject(notification.params, "turn");
11641185 const status = this.readString(turn, "status");
11651186 const errorMessage = this.readString(this.readObject(turn, "error"), "message");
@@ -1172,6 +1193,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 6 unmodified lines
11721193 }
11731194
11741195 if (notification.method === "error") {
1196+ if (isChildConversation) {
1197+ return;
1198+ }
11751199 const message = this.readString(this.readObject(notification.params)?.error, "message");
11761200 const willRetry = this.readBoolean(notification.params, "willRetry");
11771201
@@ -1183,7 +1207,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 5 unmodified lines
11831207 }
11841208
11851209 private handleServerRequest(context: CodexSessionContext, request: JsonRpcRequest): void {
1186− const route = this.readRouteFields(request.params);
1210+ const rawRoute = this.readRouteFields(request.params);
1211+ const childParentTurnId = this.readChildParentTurnId(context, request.params);
1212+ const effectiveTurnId = childParentTurnId ?? rawRoute.turnId;
11871213 const requestKind = this.requestKindForMethod(request.method);
11881214 let requestId: ApprovalRequestId | undefined;
11891215 if (requestKind) {
@@ -1199,8 +1225,8 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 9 unmodified lines
11991225 : "item/fileChange/requestApproval",
12001226 requestKind,
12011227 threadId: context.session.threadId,
1202− ...(route.turnId ? { turnId: route.turnId } : {}),
1203− ...(route.itemId ? { itemId: route.itemId } : {}),
1228+ ...(effectiveTurnId ? { turnId: effectiveTurnId } : {}),
1229+ ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
12041230 };
12051231 context.pendingApprovals.set(requestId, pendingRequest);
12061232 }
@@ -1211,8 +1237,8 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 4 unmodified lines
12111237 requestId,
12121238 jsonRpcId: request.id,
12131239 threadId: context.session.threadId,
1214− ...(route.turnId ? { turnId: route.turnId } : {}),
1215− ...(route.itemId ? { itemId: route.itemId } : {}),
1240+ ...(effectiveTurnId ? { turnId: effectiveTurnId } : {}),
1241+ ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
12161242 });
12171243 }
12181244
@@ -1223,8 +1249,8 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 4 unmodified lines
12231249 threadId: context.session.threadId,
12241250 createdAt: new Date().toISOString(),
12251251 method: request.method,
1226− turnId: route.turnId,
1227− itemId: route.itemId,
1252+ ...(effectiveTurnId ? { turnId: effectiveTurnId } : {}),
1253+ ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
12281254 requestId,
12291255 requestKind,
12301256 payload: request.params,
@@ -1451,6 +1477,64 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 220 unmodified lines
14511477 return route;
14521478 }
14531479
1480+ private readProviderConversationId(params: unknown): string | undefined {
1481+ return (
1482+ this.readString(params, "threadId") ??
1483+ this.readString(this.readObject(params, "thread"), "id") ??
1484+ this.readString(params, "conversationId")
1485+ );
1486+ }
1487+
1488+ private readChildParentTurnId(context: CodexSessionContext, params: unknown): TurnId | undefined {
1489+ const providerConversationId = this.readProviderConversationId(params);
1490+ if (!providerConversationId) {
1491+ return undefined;
1492+ }
1493+ return context.collabReceiverTurns.get(providerConversationId);
1494+ }
1495+
1496+ private rememberCollabReceiverTurns(
1497+ context: CodexSessionContext,
1498+ params: unknown,
1499+ parentTurnId: TurnId | undefined,
1500+ ): void {
1501+ if (!parentTurnId) {
1502+ return;
1503+ }
1504+ const payload = this.readObject(params);
1505+ const item = this.readObject(payload, "item") ?? payload;
1506+ const itemType = this.readString(item, "type") ?? this.readString(item, "kind");
1507+ if (itemType !== "collabAgentToolCall") {
1508+ return;
1509+ }
1510+
1511+ const receiverThreadIds =
1512+ this.readArray(item, "receiverThreadIds")
1513+ ?.map((value) => (typeof value === "string" ? value : null))
1514+ .filter((value): value is string => value !== null) ?? [];
1515+ for (const receiverThreadId of receiverThreadIds) {
1516+ context.collabReceiverTurns.set(receiverThreadId, parentTurnId);
1517+ }
1518+ }
1519+
1520+ private shouldSuppressChildConversationNotification(method: string): boolean {
1521+ return (
1522+ method === "thread/started" ||
1523+ method === "thread/status/changed" ||
1524+ method === "thread/archived" ||
1525+ method === "thread/unarchived" ||
1526+ method === "thread/closed" ||
1527+ method === "thread/compacted" ||
1528+ method === "thread/name/updated" ||
1529+ method === "thread/tokenUsage/updated" ||
1530+ method === "turn/started" ||
1531+ method === "turn/completed" ||
1532+ method === "turn/aborted" ||
1533+ method === "turn/plan/updated" ||
1534+ method === "item/plan/delta"
1535+ );
1536+ }
1537+
14541538 private readObject(value: unknown, key?: string): Record<string, unknown> | undefined {
14551539 const target =
14561540 key === undefined