| @@ -436,6 +436,82 @@ export abstract class McpAgentSessionDOBase< · 435 unmodified lines |
| 436 | 436 | }); |
| 437 | 437 | } |
| 438 | 438 | |
| 439 | + private logExecutionOwnerDirectoryFailure(input: { |
| 440 | + readonly operation: "put" | "get" | "delete"; |
| 441 | + readonly executionId: string; |
| 442 | + readonly cause: Cause.Cause<unknown>; |
| 443 | + }): Effect.Effect<void> { |
| 444 | + const self = this; |
| 445 | + return Effect.gen(function* () { |
| 446 | + const first = Cause.prettyErrors(input.cause)[0]; |
| 447 | + console.error( |
| 448 | + JSON.stringify({ |
| 449 | + event: "mcp_execution_owner_directory_error", |
| 450 | + operation: input.operation, |
| 451 | + executionId: input.executionId, |
| 452 | + sessionId: self.sessionId, |
| 453 | + exceptionType: first?.name ?? "Error", |
| 454 | + exceptionMessage: first?.message ?? "unknown", |
| 455 | + cause: Cause.pretty(input.cause), |
| 456 | + }), |
| 457 | + ); |
| 458 | + yield* Effect.annotateCurrentSpan({ |
| 459 | + "mcp.execution_owner.directory.operation": input.operation, |
| 460 | + }); |
| 461 | + yield* self.recordCauseOnSpan(input.cause); |
| 462 | + }); |
| 463 | + } |
| 464 | + |
| 465 | + private logModelResumeForwardFailure(input: { |
| 466 | + readonly executionId: string; |
| 467 | + readonly owner: McpExecutionOwnerRoute; |
| 468 | + readonly cause: Cause.Cause<unknown>; |
| 469 | + }): Effect.Effect<void> { |
| 470 | + const self = this; |
| 471 | + return Effect.gen(function* () { |
| 472 | + const first = Cause.prettyErrors(input.cause)[0]; |
| 473 | + console.error( |
| 474 | + JSON.stringify({ |
| 475 | + event: "mcp_model_resume_forward_error", |
| 476 | + executionId: input.executionId, |
| 477 | + sessionId: self.sessionId, |
| 478 | + ownerSessionId: input.owner.sessionId, |
| 479 | + exceptionType: first?.name ?? "Error", |
| 480 | + exceptionMessage: first?.message ?? "unknown", |
| 481 | + cause: Cause.pretty(input.cause), |
| 482 | + }), |
| 483 | + ); |
| 484 | + yield* Effect.annotateCurrentSpan({ |
| 485 | + "mcp.execution_owner.forward.owner_session_id": input.owner.sessionId, |
| 486 | + }); |
| 487 | + yield* self.recordCauseOnSpan(input.cause); |
| 488 | + }); |
| 489 | + } |
| 490 | + |
| 491 | + private logModelResumeForwardTimeout(input: { |
| 492 | + readonly executionId: string; |
| 493 | + readonly owner: McpExecutionOwnerRoute; |
| 494 | + readonly timeoutMs: number; |
| 495 | + }): Effect.Effect<void> { |
| 496 | + const self = this; |
| 497 | + return Effect.gen(function* () { |
| 498 | + console.error( |
| 499 | + JSON.stringify({ |
| 500 | + event: "mcp_model_resume_forward_error", |
| 501 | + reason: "timeout", |
| 502 | + executionId: input.executionId, |
| 503 | + sessionId: self.sessionId, |
| 504 | + ownerSessionId: input.owner.sessionId, |
| 505 | + timeoutMs: input.timeoutMs, |
| 506 | + }), |
| 507 | + ); |
| 508 | + yield* Effect.annotateCurrentSpan({ |
| 509 | + "mcp.execution_owner.forward.owner_session_id": input.owner.sessionId, |
| 510 | + "mcp.execution_owner.forward.error": "timeout", |
| 511 | + }); |
| 512 | + }); |
| 513 | + } |
| 514 | + |
| 439 | 515 | private withSpanFlush<A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> { |
| 440 | 516 | const self = this; |
| 441 | 517 | return effect.pipe(Effect.ensuring(Effect.promise(() => self.flushTelemetry()))); |
| @@ -813,6 +889,9 @@ export abstract class McpAgentSessionDOBase< · 371 unmodified lines |
| 813 | 889 | Effect.map((record) => |
| 814 | 890 | record ? { expiresAt: record.expiresAt, ttlMs: record.ttlMs } : undefined, |
| 815 | 891 | ), |
| 892 | + Effect.tapCause((cause) => |
| 893 | + this.logExecutionOwnerDirectoryFailure({ operation: "get", executionId, cause }), |
| 894 | + ), |
| 816 | 895 | Effect.catchCause(() => noDeadline), |
| 817 | 896 | ); |
| 818 | 897 | } |
| @@ -835,13 +914,26 @@ export abstract class McpAgentSessionDOBase< · 16 unmodified lines |
| 835 | 914 | expiresAt: deadline.expiresAt, |
| 836 | 915 | ttlMs: deadline.ttlMs, |
| 837 | 916 | }; |
| 838 | | − yield* directory.put(record); |
| 917 | + yield* directory |
| 918 | + .put(record) |
| 919 | + .pipe( |
| 920 | + Effect.tapCause((cause) => |
| 921 | + self.logExecutionOwnerDirectoryFailure({ operation: "put", executionId, cause }), |
| 922 | + ), |
| 923 | + ); |
| 839 | 924 | }).pipe(Effect.ignore); |
| 840 | 925 | } |
| 841 | 926 | |
| 842 | 927 | private deleteExecutionOwnerEntry(executionId: string): Effect.Effect<void> { |
| 843 | 928 | const directory = this.executionOwnerDirectory(); |
| 844 | | − return directory?.delete(executionId).pipe(Effect.ignore) ?? Effect.void; |
| 929 | + return ( |
| 930 | + directory?.delete(executionId).pipe( |
| 931 | + Effect.tapCause((cause) => |
| 932 | + this.logExecutionOwnerDirectoryFailure({ operation: "delete", executionId, cause }), |
| 933 | + ), |
| 934 | + Effect.ignore, |
| 935 | + ) ?? Effect.void |
| 936 | + ); |
| 845 | 937 | } |
| 846 | 938 | |
| 847 | 939 | private resumeEngineWithLifecycle( |
| @@ -864,9 +956,12 @@ export abstract class McpAgentSessionDOBase< · 16 unmodified lines |
| 864 | 956 | if (!directory) return Effect.succeed(null); |
| 865 | 957 | const self = this; |
| 866 | 958 | return Effect.gen(function* () { |
| 867 | | − const record = yield* directory |
| 868 | | − .get(executionId) |
| 869 | | − .pipe(Effect.catchCause(() => Effect.succeed(null))); |
| 959 | + const record = yield* directory.get(executionId).pipe( |
| 960 | + Effect.tapCause((cause) => |
| 961 | + self.logExecutionOwnerDirectoryFailure({ operation: "get", executionId, cause }), |
| 962 | + ), |
| 963 | + Effect.catchCause(() => Effect.succeed(null)), |
| 964 | + ); |
| 870 | 965 | if (!record) return null; |
| 871 | 966 | |
| 872 | 967 | const sessionMeta = yield* self.loadSessionMeta(); |
| @@ -893,10 +988,24 @@ export abstract class McpAgentSessionDOBase< · 20 unmodified lines |
| 893 | 988 | Effect.timeoutOrElse({ |
| 894 | 989 | duration: `${MODEL_RESUME_FORWARD_TIMEOUT_MS} millis`, |
| 895 | 990 | orElse: () => |
| 896 | | − Effect.succeed({ status: "execution_expired" as const, ttlMs: record.ttlMs }), |
| 991 | + Effect.gen(function* () { |
| 992 | + yield* self.logModelResumeForwardTimeout({ |
| 993 | + executionId, |
| 994 | + owner: record.owner, |
| 995 | + timeoutMs: MODEL_RESUME_FORWARD_TIMEOUT_MS, |
| 996 | + }); |
| 997 | + return { status: "execution_expired" as const, ttlMs: record.ttlMs }; |
| 998 | + }), |
| 897 | 999 | }), |
| 898 | | − Effect.catchCause(() => |
| 899 | | − Effect.succeed({ status: "execution_expired" as const, ttlMs: record.ttlMs }), |
| 1000 | + Effect.catchCause((cause) => |
| 1001 | + Effect.gen(function* () { |
| 1002 | + yield* self.logModelResumeForwardFailure({ |
| 1003 | + executionId, |
| 1004 | + owner: record.owner, |
| 1005 | + cause, |
| 1006 | + }); |
| 1007 | + return { status: "execution_expired" as const, ttlMs: record.ttlMs }; |
| 1008 | + }), |
| 900 | 1009 | ), |
| 901 | 1010 | ); |
| 902 | 1011 | if ( |
| @@ -994,10 +1103,7 @@ export abstract class McpAgentSessionDOBase< · 91 unmodified lines |
| 994 | 1103 | const self = this; |
| 995 | 1104 | return Effect.gen(function* () { |
| 996 | 1105 | const lease = self.pendingApprovalLeases.get(executionId); |
| 997 | | − if (!lease) { |
| 998 | | − yield* self.deleteExecutionOwnerEntry(executionId); |
| 999 | | − return; |
| 1000 | | − } |
| 1106 | + if (!lease) return; |
| 1001 | 1107 | if (lease.timeout) clearTimeout(lease.timeout); |
| 1002 | 1108 | self.pendingApprovalLeases.delete(executionId); |
| 1003 | 1109 | lease.disposeKeepAlive(); |