Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ export const CodexProviderCapabilitiesV2 = {
canRollbackThread: true,
canForkThread: true,
canForkFromTurn: true,
canForkWhileRunning: true,
canForkFromSubagentThread: true,
exposesNativeThreadId: true,
},
Expand Down
52 changes: 34 additions & 18 deletions apps/server/src/orchestration-v2/CommandPolicy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ layer("CommandPolicyV2", (it) => {
capabilities: CursorProviderCapabilitiesV2,
sameProvider: true,
hasStrongNativeSource: true,
hasNativeTurnBoundary: false,
sourceRunStatus: "completed",
fromSpecificTurn: true,
sourceBusy: false,
Expand All @@ -267,24 +268,39 @@ layer("CommandPolicyV2", (it) => {
}),
);

it.effect("forks a busy source through portable context", () =>
Effect.gen(function* () {
const policy = yield* CommandPolicy.CommandPolicyV2;
const decide = (sourceBusy: boolean) =>
policy.decideForkExecution({
commandId,
threadId,
providerInstanceId: ProviderInstanceId.make("codex"),
capabilities: CodexProviderCapabilitiesV2,
sameProvider: true,
hasStrongNativeSource: true,
sourceRunStatus: "completed",
fromSpecificTurn: true,
sourceBusy,
});
it.effect.each([
{ canForkWhileRunning: true, hasNativeTurnBoundary: true, busyStrategy: "native_fork" },
{ canForkWhileRunning: false, hasNativeTurnBoundary: true, busyStrategy: "portable_context" },
{
canForkWhileRunning: undefined,
hasNativeTurnBoundary: true,
busyStrategy: "portable_context",
},
{ canForkWhileRunning: true, hasNativeTurnBoundary: false, busyStrategy: "portable_context" },
] as const)(
"forks a busy source with canForkWhileRunning=$canForkWhileRunning and hasNativeTurnBoundary=$hasNativeTurnBoundary",
({ canForkWhileRunning, hasNativeTurnBoundary, busyStrategy }) =>
Effect.gen(function* () {
const policy = yield* CommandPolicy.CommandPolicyV2;
const decide = (sourceBusy: boolean) =>
policy.decideForkExecution({
commandId,
threadId,
providerInstanceId: ProviderInstanceId.make("codex"),
capabilities: {
...CodexProviderCapabilitiesV2,
threads: { ...CodexProviderCapabilitiesV2.threads, canForkWhileRunning },
},
sameProvider: true,
hasStrongNativeSource: true,
hasNativeTurnBoundary,
sourceRunStatus: "completed",
fromSpecificTurn: true,
sourceBusy,
});

assert.equal(yield* decide(false), "native_fork");
assert.equal(yield* decide(true), "portable_context");
}),
assert.equal(yield* decide(false), "native_fork");
assert.equal(yield* decide(true), busyStrategy);
}),
);
});
6 changes: 4 additions & 2 deletions apps/server/src/orchestration-v2/CommandPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,10 @@ export interface CommandPolicyV2Shape {
input: CapabilityCheckInput & {
readonly sameProvider: boolean;
readonly hasStrongNativeSource: boolean;
readonly hasNativeTurnBoundary: boolean;
readonly sourceRunStatus: OrchestrationV2Run["status"];
readonly fromSpecificTurn: boolean;
/** Later source runs are still active; providers cannot fork a thread mid-turn. */
/** Later source runs are still active. */
readonly sourceBusy: boolean;
},
) => Effect.Effect<ForkExecutionPolicyV2, CommandPolicyV2Error>;
Expand Down Expand Up @@ -376,7 +377,8 @@ const decideForkExecution: CommandPolicyV2Shape["decideForkExecution"] = (input)
(input.sourceRunStatus === "completed" || input.sourceRunStatus === "waiting") &&
input.sameProvider &&
input.hasStrongNativeSource &&
!input.sourceBusy &&
(!input.sourceBusy ||
(input.capabilities.threads.canForkWhileRunning === true && input.hasNativeTurnBoundary)) &&
input.capabilities.threads.canForkThread &&
(!input.fromSpecificTurn || input.capabilities.threads.canForkFromTurn) &&
input.capabilities.identity.nativeThreadIds === "strong";
Expand Down
3 changes: 3 additions & 0 deletions apps/server/src/orchestration-v2/Orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6775,6 +6775,9 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
sameProvider:
pendingForkTransfer.sourceProviderInstanceId === modelSelection.instanceId,
hasStrongNativeSource: sourceProviderThread?.nativeThreadRef?.strength === "strong",
hasNativeTurnBoundary:
sourceProjection?.providerTurns.find((turn) => turn.id === sourceProviderTurnId)
?.nativeTurnRef != null,
sourceRunStatus: sourceRun.status,
fromSpecificTurn: sourceRun !== null,
sourceBusy: sourceRunsAfterFork.some(isActiveRun),
Expand Down
Loading
Loading