@junctionpanel/server 0.1.42 → 0.1.43
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.
- package/dist/server/server/agent/agent-manager.d.ts +1 -0
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +73 -31
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +125 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/package.json +2 -2
|
@@ -11,6 +11,7 @@ import { buildCodexRuntimeExtra, DEFAULT_CODEX_MODE_ID, isCodexPlanModeEnabled,
|
|
|
11
11
|
import { writeImageAttachment } from "./image-attachments.js";
|
|
12
12
|
const DEFAULT_TIMEOUT_MS = 14 * 24 * 60 * 60 * 1000;
|
|
13
13
|
const TURN_START_TIMEOUT_MS = 90 * 1000;
|
|
14
|
+
const TURN_COMPLETION_SETTLE_DELAY_MS = 75;
|
|
14
15
|
const CODEX_PROVIDER = "codex";
|
|
15
16
|
const CODEX_APP_SERVER_CAPABILITIES = {
|
|
16
17
|
supportsStreaming: true,
|
|
@@ -1593,6 +1594,7 @@ class CodexAppServerAgentSession {
|
|
|
1593
1594
|
this.pendingPlanTexts = new Map();
|
|
1594
1595
|
this.cachedSkills = [];
|
|
1595
1596
|
this.turnCompletionInFlight = false;
|
|
1597
|
+
this.deferredTurnCompletion = null;
|
|
1596
1598
|
this.logger = logger.child({ module: "agent", provider: CODEX_PROVIDER });
|
|
1597
1599
|
if (config.modeId === undefined) {
|
|
1598
1600
|
throw new Error("Codex agent requires modeId to be specified");
|
|
@@ -2115,6 +2117,7 @@ class CodexAppServerAgentSession {
|
|
|
2115
2117
|
? "cancel"
|
|
2116
2118
|
: "decline");
|
|
2117
2119
|
pending.resolve({ decision });
|
|
2120
|
+
this.scheduleDeferredTurnCompletionAfterPermissionResponse();
|
|
2118
2121
|
return;
|
|
2119
2122
|
}
|
|
2120
2123
|
if (pending.kind === "file") {
|
|
@@ -2125,14 +2128,17 @@ class CodexAppServerAgentSession {
|
|
|
2125
2128
|
? "cancel"
|
|
2126
2129
|
: "decline");
|
|
2127
2130
|
pending.resolve({ decision });
|
|
2131
|
+
this.scheduleDeferredTurnCompletionAfterPermissionResponse();
|
|
2128
2132
|
return;
|
|
2129
2133
|
}
|
|
2130
2134
|
if (pending.kind === "permissions") {
|
|
2131
2135
|
pending.resolve(buildCodexPermissionsResponse(response, pending.permissions));
|
|
2136
|
+
this.scheduleDeferredTurnCompletionAfterPermissionResponse();
|
|
2132
2137
|
return;
|
|
2133
2138
|
}
|
|
2134
2139
|
if (pending.kind === "elicitation") {
|
|
2135
2140
|
pending.resolve(buildCodexElicitationResponse(response));
|
|
2141
|
+
this.scheduleDeferredTurnCompletionAfterPermissionResponse();
|
|
2136
2142
|
return;
|
|
2137
2143
|
}
|
|
2138
2144
|
// tool/requestUserInput
|
|
@@ -2161,6 +2167,7 @@ class CodexAppServerAgentSession {
|
|
|
2161
2167
|
answers["default"] = { answers: fallbackAnswers && fallbackAnswers.length > 0 ? fallbackAnswers : [decision] };
|
|
2162
2168
|
}
|
|
2163
2169
|
pending.resolve({ answers });
|
|
2170
|
+
this.scheduleDeferredTurnCompletionAfterPermissionResponse();
|
|
2164
2171
|
}
|
|
2165
2172
|
describePersistence() {
|
|
2166
2173
|
if (!this.currentThreadId)
|
|
@@ -2204,6 +2211,7 @@ class CodexAppServerAgentSession {
|
|
|
2204
2211
|
this.pendingPermissionHandlers.clear();
|
|
2205
2212
|
this.pendingPermissions.clear();
|
|
2206
2213
|
this.resolvedPermissionRequests.clear();
|
|
2214
|
+
this.clearDeferredTurnCompletion();
|
|
2207
2215
|
this.eventQueue?.end();
|
|
2208
2216
|
this.eventQueue = null;
|
|
2209
2217
|
if (this.client) {
|
|
@@ -2328,7 +2336,84 @@ class CodexAppServerAgentSession {
|
|
|
2328
2336
|
emitEvent(event) {
|
|
2329
2337
|
this.eventQueue?.push(event);
|
|
2330
2338
|
}
|
|
2339
|
+
hasOutstandingPermissionRequests() {
|
|
2340
|
+
return this.pendingPermissionHandlers.size > 0;
|
|
2341
|
+
}
|
|
2342
|
+
clearDeferredTurnCompletion() {
|
|
2343
|
+
if (this.deferredTurnCompletion?.timer) {
|
|
2344
|
+
clearTimeout(this.deferredTurnCompletion.timer);
|
|
2345
|
+
}
|
|
2346
|
+
this.deferredTurnCompletion = null;
|
|
2347
|
+
}
|
|
2348
|
+
scheduleDeferredTurnCompletion(parsed, options) {
|
|
2349
|
+
const waitingOnPermission = options?.waitingOnPermission ??
|
|
2350
|
+
this.deferredTurnCompletion?.waitingOnPermission ??
|
|
2351
|
+
this.hasOutstandingPermissionRequests();
|
|
2352
|
+
this.clearDeferredTurnCompletion();
|
|
2353
|
+
this.turnCompletionInFlight = true;
|
|
2354
|
+
const deferred = {
|
|
2355
|
+
parsed,
|
|
2356
|
+
turnId: this.currentTurnId,
|
|
2357
|
+
waitingOnPermission,
|
|
2358
|
+
timer: null,
|
|
2359
|
+
};
|
|
2360
|
+
deferred.timer = setTimeout(() => {
|
|
2361
|
+
void this.flushDeferredTurnCompletion(deferred.turnId);
|
|
2362
|
+
}, TURN_COMPLETION_SETTLE_DELAY_MS);
|
|
2363
|
+
this.deferredTurnCompletion = deferred;
|
|
2364
|
+
}
|
|
2365
|
+
markDeferredTurnCompletionAwaitingPermissions() {
|
|
2366
|
+
if (!this.deferredTurnCompletion) {
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
this.scheduleDeferredTurnCompletion(this.deferredTurnCompletion.parsed, {
|
|
2370
|
+
waitingOnPermission: true,
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
discardDeferredTurnCompletion(reason) {
|
|
2374
|
+
const deferred = this.deferredTurnCompletion;
|
|
2375
|
+
if (!deferred) {
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
this.logger.trace({
|
|
2379
|
+
reason,
|
|
2380
|
+
threadId: this.currentThreadId,
|
|
2381
|
+
turnId: deferred.turnId,
|
|
2382
|
+
}, "Discarding deferred Codex turn completion after resumed activity");
|
|
2383
|
+
this.clearDeferredTurnCompletion();
|
|
2384
|
+
this.turnCompletionInFlight = false;
|
|
2385
|
+
}
|
|
2386
|
+
noteTurnActivityAfterPermission(activity) {
|
|
2387
|
+
const deferred = this.deferredTurnCompletion;
|
|
2388
|
+
if (!deferred || !deferred.waitingOnPermission || this.hasOutstandingPermissionRequests()) {
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
this.discardDeferredTurnCompletion(activity);
|
|
2392
|
+
}
|
|
2393
|
+
async flushDeferredTurnCompletion(turnId) {
|
|
2394
|
+
const deferred = this.deferredTurnCompletion;
|
|
2395
|
+
if (!deferred || deferred.turnId !== turnId) {
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
if (this.hasOutstandingPermissionRequests()) {
|
|
2399
|
+
this.scheduleDeferredTurnCompletion(deferred.parsed, {
|
|
2400
|
+
waitingOnPermission: true,
|
|
2401
|
+
});
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
this.clearDeferredTurnCompletion();
|
|
2405
|
+
await this.finalizeTurnCompletion(deferred.parsed);
|
|
2406
|
+
}
|
|
2407
|
+
scheduleDeferredTurnCompletionAfterPermissionResponse() {
|
|
2408
|
+
if (!this.deferredTurnCompletion || this.hasOutstandingPermissionRequests()) {
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
this.scheduleDeferredTurnCompletion(this.deferredTurnCompletion.parsed, {
|
|
2412
|
+
waitingOnPermission: true,
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2331
2415
|
clearTurnState() {
|
|
2416
|
+
this.clearDeferredTurnCompletion();
|
|
2332
2417
|
this.currentTurnId = null;
|
|
2333
2418
|
this.emittedItemStartedIds.clear();
|
|
2334
2419
|
this.emittedItemCompletedIds.clear();
|
|
@@ -2506,7 +2591,24 @@ class CodexAppServerAgentSession {
|
|
|
2506
2591
|
return;
|
|
2507
2592
|
}
|
|
2508
2593
|
if (parsed.kind === "turn_completed") {
|
|
2594
|
+
// A later failed/interrupted terminal status must replace a deferred
|
|
2595
|
+
// "completed" status so we do not hide a real terminal error behind
|
|
2596
|
+
// the settle window.
|
|
2509
2597
|
if (this.turnCompletionInFlight) {
|
|
2598
|
+
if (parsed.status === "completed") {
|
|
2599
|
+
this.scheduleDeferredTurnCompletion(parsed);
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
if (this.deferredTurnCompletion) {
|
|
2603
|
+
this.clearDeferredTurnCompletion();
|
|
2604
|
+
this.turnCompletionInFlight = false;
|
|
2605
|
+
}
|
|
2606
|
+
else {
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
if (parsed.status === "completed") {
|
|
2611
|
+
this.scheduleDeferredTurnCompletion(parsed);
|
|
2510
2612
|
return;
|
|
2511
2613
|
}
|
|
2512
2614
|
this.turnCompletionInFlight = true;
|
|
@@ -2514,6 +2616,7 @@ class CodexAppServerAgentSession {
|
|
|
2514
2616
|
return;
|
|
2515
2617
|
}
|
|
2516
2618
|
if (parsed.kind === "plan_updated") {
|
|
2619
|
+
this.noteTurnActivityAfterPermission("plan_updated");
|
|
2517
2620
|
const items = planStepsToTodoItems(parsed.plan.map((entry) => ({
|
|
2518
2621
|
step: entry.step ?? "",
|
|
2519
2622
|
status: entry.status ?? "pending",
|
|
@@ -2526,6 +2629,7 @@ class CodexAppServerAgentSession {
|
|
|
2526
2629
|
return;
|
|
2527
2630
|
}
|
|
2528
2631
|
if (parsed.kind === "plan_delta") {
|
|
2632
|
+
this.noteTurnActivityAfterPermission("plan_delta");
|
|
2529
2633
|
const previous = this.pendingPlanTexts.get(parsed.itemId) ?? "";
|
|
2530
2634
|
const next = previous + parsed.delta;
|
|
2531
2635
|
this.pendingPlanTexts.set(parsed.itemId, next);
|
|
@@ -2567,25 +2671,30 @@ class CodexAppServerAgentSession {
|
|
|
2567
2671
|
return;
|
|
2568
2672
|
}
|
|
2569
2673
|
if (parsed.kind === "agent_message_delta") {
|
|
2674
|
+
this.noteTurnActivityAfterPermission("agent_message_delta");
|
|
2570
2675
|
const prev = this.pendingAgentMessages.get(parsed.itemId) ?? "";
|
|
2571
2676
|
this.pendingAgentMessages.set(parsed.itemId, prev + parsed.delta);
|
|
2572
2677
|
return;
|
|
2573
2678
|
}
|
|
2574
2679
|
if (parsed.kind === "reasoning_delta") {
|
|
2680
|
+
this.noteTurnActivityAfterPermission("reasoning_delta");
|
|
2575
2681
|
const prev = this.pendingReasoning.get(parsed.itemId) ?? [];
|
|
2576
2682
|
prev.push(parsed.delta);
|
|
2577
2683
|
this.pendingReasoning.set(parsed.itemId, prev);
|
|
2578
2684
|
return;
|
|
2579
2685
|
}
|
|
2580
2686
|
if (parsed.kind === "exec_command_output_delta") {
|
|
2687
|
+
this.noteTurnActivityAfterPermission("exec_command_output_delta");
|
|
2581
2688
|
this.appendOutputDeltaChunk(this.pendingCommandOutputDeltas, parsed.callId, parsed.chunk, { decodeBase64: true });
|
|
2582
2689
|
return;
|
|
2583
2690
|
}
|
|
2584
2691
|
if (parsed.kind === "file_change_output_delta") {
|
|
2692
|
+
this.noteTurnActivityAfterPermission("file_change_output_delta");
|
|
2585
2693
|
this.appendOutputDeltaChunk(this.pendingFileChangeOutputDeltas, parsed.itemId, parsed.delta);
|
|
2586
2694
|
return;
|
|
2587
2695
|
}
|
|
2588
2696
|
if (parsed.kind === "exec_command_started") {
|
|
2697
|
+
this.noteTurnActivityAfterPermission("exec_command_started");
|
|
2589
2698
|
if (parsed.callId) {
|
|
2590
2699
|
this.emittedExecCommandStartedCallIds.add(parsed.callId);
|
|
2591
2700
|
this.pendingCommandOutputDeltas.delete(parsed.callId);
|
|
@@ -2602,6 +2711,7 @@ class CodexAppServerAgentSession {
|
|
|
2602
2711
|
return;
|
|
2603
2712
|
}
|
|
2604
2713
|
if (parsed.kind === "exec_command_completed") {
|
|
2714
|
+
this.noteTurnActivityAfterPermission("exec_command_completed");
|
|
2605
2715
|
const bufferedOutput = this.consumeOutputDelta(this.pendingCommandOutputDeltas, parsed.callId);
|
|
2606
2716
|
const timelineItem = mapCodexExecNotificationToToolCall({
|
|
2607
2717
|
callId: parsed.callId,
|
|
@@ -2620,6 +2730,7 @@ class CodexAppServerAgentSession {
|
|
|
2620
2730
|
return;
|
|
2621
2731
|
}
|
|
2622
2732
|
if (parsed.kind === "patch_apply_started") {
|
|
2733
|
+
this.noteTurnActivityAfterPermission("patch_apply_started");
|
|
2623
2734
|
if (parsed.callId) {
|
|
2624
2735
|
this.pendingFileChangeOutputDeltas.delete(parsed.callId);
|
|
2625
2736
|
}
|
|
@@ -2639,6 +2750,7 @@ class CodexAppServerAgentSession {
|
|
|
2639
2750
|
return;
|
|
2640
2751
|
}
|
|
2641
2752
|
if (parsed.kind === "patch_apply_completed") {
|
|
2753
|
+
this.noteTurnActivityAfterPermission("patch_apply_completed");
|
|
2642
2754
|
const bufferedOutput = this.consumeOutputDelta(this.pendingFileChangeOutputDeltas, parsed.callId);
|
|
2643
2755
|
const timelineItem = mapCodexPatchNotificationToToolCall({
|
|
2644
2756
|
callId: parsed.callId,
|
|
@@ -2660,6 +2772,7 @@ class CodexAppServerAgentSession {
|
|
|
2660
2772
|
return;
|
|
2661
2773
|
}
|
|
2662
2774
|
if (parsed.kind === "item_completed") {
|
|
2775
|
+
this.noteTurnActivityAfterPermission("item_completed");
|
|
2663
2776
|
// Codex emits mirrored lifecycle notifications via both `codex/event/item_*`
|
|
2664
2777
|
// and canonical `item/*`. We render only the canonical channel to avoid
|
|
2665
2778
|
// duplicated assistant/reasoning rows.
|
|
@@ -2670,6 +2783,7 @@ class CodexAppServerAgentSession {
|
|
|
2670
2783
|
return;
|
|
2671
2784
|
}
|
|
2672
2785
|
if (parsed.kind === "item_started") {
|
|
2786
|
+
this.noteTurnActivityAfterPermission("item_started");
|
|
2673
2787
|
if (parsed.source === "codex_event") {
|
|
2674
2788
|
return;
|
|
2675
2789
|
}
|
|
@@ -2775,6 +2889,7 @@ class CodexAppServerAgentSession {
|
|
|
2775
2889
|
: parsed.networkApprovalContext
|
|
2776
2890
|
? "Allow network access"
|
|
2777
2891
|
: "Run command";
|
|
2892
|
+
this.markDeferredTurnCompletionAwaitingPermissions();
|
|
2778
2893
|
const request = {
|
|
2779
2894
|
id: requestId,
|
|
2780
2895
|
provider: CODEX_PROVIDER,
|
|
@@ -2818,6 +2933,7 @@ class CodexAppServerAgentSession {
|
|
|
2818
2933
|
handleFileChangeApprovalRequest(requestMethod, params, rawRequestId) {
|
|
2819
2934
|
const parsed = params;
|
|
2820
2935
|
const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
|
|
2936
|
+
this.markDeferredTurnCompletionAwaitingPermissions();
|
|
2821
2937
|
const request = {
|
|
2822
2938
|
id: requestId,
|
|
2823
2939
|
provider: CODEX_PROVIDER,
|
|
@@ -2851,6 +2967,7 @@ class CodexAppServerAgentSession {
|
|
|
2851
2967
|
const parsed = params;
|
|
2852
2968
|
const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
|
|
2853
2969
|
const questions = normalizeCodexQuestionDescriptors(parsed.questions);
|
|
2970
|
+
this.markDeferredTurnCompletionAwaitingPermissions();
|
|
2854
2971
|
const request = {
|
|
2855
2972
|
id: requestId,
|
|
2856
2973
|
provider: CODEX_PROVIDER,
|
|
@@ -2889,6 +3006,7 @@ class CodexAppServerAgentSession {
|
|
|
2889
3006
|
handlePermissionsApprovalRequest(requestMethod, params, rawRequestId) {
|
|
2890
3007
|
const parsed = params;
|
|
2891
3008
|
const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
|
|
3009
|
+
this.markDeferredTurnCompletionAwaitingPermissions();
|
|
2892
3010
|
const request = {
|
|
2893
3011
|
id: requestId,
|
|
2894
3012
|
provider: CODEX_PROVIDER,
|
|
@@ -2936,6 +3054,7 @@ class CodexAppServerAgentSession {
|
|
|
2936
3054
|
: parsed.serverName
|
|
2937
3055
|
? `Respond to ${parsed.serverName}`
|
|
2938
3056
|
: "Respond to MCP server";
|
|
3057
|
+
this.markDeferredTurnCompletionAwaitingPermissions();
|
|
2939
3058
|
const request = {
|
|
2940
3059
|
id: requestId,
|
|
2941
3060
|
provider: CODEX_PROVIDER,
|
|
@@ -2985,6 +3104,12 @@ class CodexAppServerAgentSession {
|
|
|
2985
3104
|
});
|
|
2986
3105
|
}
|
|
2987
3106
|
}
|
|
3107
|
+
Object.assign(__codexAppServerInternals, {
|
|
3108
|
+
TURN_COMPLETION_SETTLE_DELAY_MS,
|
|
3109
|
+
createTestSession(config, resumeHandle, logger, spawnAppServer) {
|
|
3110
|
+
return new CodexAppServerAgentSession(config, resumeHandle, logger, spawnAppServer);
|
|
3111
|
+
},
|
|
3112
|
+
});
|
|
2988
3113
|
export class CodexAppServerAgentClient {
|
|
2989
3114
|
constructor(logger, runtimeSettings) {
|
|
2990
3115
|
this.logger = logger;
|