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
70 pending: Map<PendingRequestKey, PendingRequest>;70 pending: Map<PendingRequestKey, PendingRequest>;
71 pendingApprovals: Map<ApprovalRequestId, PendingApprovalRequest>;71 pendingApprovals: Map<ApprovalRequestId, PendingApprovalRequest>;
72 pendingUserInputs: Map<ApprovalRequestId, PendingUserInputRequest>;72 pendingUserInputs: Map<ApprovalRequestId, PendingUserInputRequest>;
73 collabReceiverTurns: Map<string, TurnId>;
73 nextRequestId: number;74 nextRequestId: number;
74 stopping: boolean;75 stopping: boolean;
75}76}
@@ -571,6 +572,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 495 unmodified lines
571 pending: new Map(),572 pending: new Map(),
572 pendingApprovals: new Map(),573 pendingApprovals: new Map(),
573 pendingUserInputs: new Map(),574 pendingUserInputs: new Map(),
575 collabReceiverTurns: new Map(),
574 nextRequestId: 1,576 nextRequestId: 1,
575 stopping: false,577 stopping: false,
576 };578 };
@@ -732,6 +734,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 155 unmodified lines
732 734
733 async sendTurn(input: CodexAppServerSendTurnInput): Promise<ProviderTurnStartResult> {735 async sendTurn(input: CodexAppServerSendTurnInput): Promise<ProviderTurnStartResult> {
734 const context = this.requireSession(input.threadId);736 const context = this.requireSession(input.threadId);
737 context.collabReceiverTurns.clear();
735 738
736 const turnInput: Array<739 const turnInput: Array<
737 { type: "text"; text: string; text_elements: [] } | { type: "image"; url: string }740 { type: "text"; text: string; text_elements: [] } | { type: "image"; url: string }
@@ -1121,7 +1124,16 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 383 unmodified lines
1121 context: CodexSessionContext,1124 context: CodexSessionContext,
1122 notification: JsonRpcNotification,1125 notification: JsonRpcNotification,
1123 ): void {1126 ): 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 }
1125 const textDelta =1137 const textDelta =
1126 notification.method === "item/agentMessage/delta"1138 notification.method === "item/agentMessage/delta"
1127 ? this.readString(notification.params, "delta")1139 ? this.readString(notification.params, "delta")
@@ -1134,8 +1146,10 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 6 unmodified lines
1134 threadId: context.session.threadId,1146 threadId: context.session.threadId,
1135 createdAt: new Date().toISOString(),1147 createdAt: new Date().toISOString(),
1136 method: notification.method,1148 method: notification.method,
1137 turnId: route.turnId,1149 ...((childParentTurnId ?? rawRoute.turnId)
1138 itemId: route.itemId,1150 ? { turnId: childParentTurnId ?? rawRoute.turnId }
1151 : {}),
1152 ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
1139 textDelta,1153 textDelta,
1140 payload: notification.params,1154 payload: notification.params,
1141 });1155 });
@@ -1151,6 +1165,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 9 unmodified lines
1151 }1165 }
1152 1166
1153 if (notification.method === "turn/started") {1167 if (notification.method === "turn/started") {
1168 if (isChildConversation) {
1169 return;
1170 }
1154 const turnId = toTurnId(this.readString(this.readObject(notification.params)?.turn, "id"));1171 const turnId = toTurnId(this.readString(this.readObject(notification.params)?.turn, "id"));
1155 this.updateSession(context, {1172 this.updateSession(context, {
1156 status: "running",1173 status: "running",
@@ -1160,6 +1177,10 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 3 unmodified lines
1160 }1177 }
1161 1178
1162 if (notification.method === "turn/completed") {1179 if (notification.method === "turn/completed") {
1180 if (isChildConversation) {
1181 return;
1182 }
1183 context.collabReceiverTurns.clear();
1163 const turn = this.readObject(notification.params, "turn");1184 const turn = this.readObject(notification.params, "turn");
1164 const status = this.readString(turn, "status");1185 const status = this.readString(turn, "status");
1165 const errorMessage = this.readString(this.readObject(turn, "error"), "message");1186 const errorMessage = this.readString(this.readObject(turn, "error"), "message");
@@ -1172,6 +1193,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 6 unmodified lines
1172 }1193 }
1173 1194
1174 if (notification.method === "error") {1195 if (notification.method === "error") {
1196 if (isChildConversation) {
1197 return;
1198 }
1175 const message = this.readString(this.readObject(notification.params)?.error, "message");1199 const message = this.readString(this.readObject(notification.params)?.error, "message");
1176 const willRetry = this.readBoolean(notification.params, "willRetry");1200 const willRetry = this.readBoolean(notification.params, "willRetry");
1177 1201
@@ -1183,7 +1207,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 5 unmodified lines
1183 }1207 }
1184 1208
1185 private handleServerRequest(context: CodexSessionContext, request: JsonRpcRequest): void {1209 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;
1187 const requestKind = this.requestKindForMethod(request.method);1213 const requestKind = this.requestKindForMethod(request.method);
1188 let requestId: ApprovalRequestId | undefined;1214 let requestId: ApprovalRequestId | undefined;
1189 if (requestKind) {1215 if (requestKind) {
@@ -1199,8 +1225,8 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 9 unmodified lines
1199 : "item/fileChange/requestApproval",1225 : "item/fileChange/requestApproval",
1200 requestKind,1226 requestKind,
1201 threadId: context.session.threadId,1227 threadId: context.session.threadId,
1202 ...(route.turnId ? { turnId: route.turnId } : {}),1228 ...(effectiveTurnId ? { turnId: effectiveTurnId } : {}),
1203 ...(route.itemId ? { itemId: route.itemId } : {}),1229 ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
1204 };1230 };
1205 context.pendingApprovals.set(requestId, pendingRequest);1231 context.pendingApprovals.set(requestId, pendingRequest);
1206 }1232 }
@@ -1211,8 +1237,8 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 4 unmodified lines
1211 requestId,1237 requestId,
1212 jsonRpcId: request.id,1238 jsonRpcId: request.id,
1213 threadId: context.session.threadId,1239 threadId: context.session.threadId,
1214 ...(route.turnId ? { turnId: route.turnId } : {}),1240 ...(effectiveTurnId ? { turnId: effectiveTurnId } : {}),
1215 ...(route.itemId ? { itemId: route.itemId } : {}),1241 ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
1216 });1242 });
1217 }1243 }
1218 1244
@@ -1223,8 +1249,8 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 4 unmodified lines
1223 threadId: context.session.threadId,1249 threadId: context.session.threadId,
1224 createdAt: new Date().toISOString(),1250 createdAt: new Date().toISOString(),
1225 method: request.method,1251 method: request.method,
1226 turnId: route.turnId,1252 ...(effectiveTurnId ? { turnId: effectiveTurnId } : {}),
1227 itemId: route.itemId,1253 ...(rawRoute.itemId ? { itemId: rawRoute.itemId } : {}),
1228 requestId,1254 requestId,
1229 requestKind,1255 requestKind,
1230 payload: request.params,1256 payload: request.params,
@@ -1451,6 +1477,64 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 220 unmodified lines
1451 return route;1477 return route;
1452 }1478 }
1453 1479
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
1454 private readObject(value: unknown, key?: string): Record<string, unknown> | undefined {1538 private readObject(value: unknown, key?: string): Record<string, unknown> | undefined {
1455 const target =1539 const target =
1456 key === undefined1540 key === undefined