Skip to content
Merged
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
168 changes: 150 additions & 18 deletions apps/server/src/orchestration-v2/Orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5275,6 +5275,33 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
});
});

const ensureSteeringMessage = Effect.fn("Orchestrator.ensureSteeringMessage")(function* (
command: Extract<
OrchestrationV2Command,
{ readonly type: "message.dispatch" | "queued-message.promote-to-steer" }
>,
message: Pick<OrchestrationV2ConversationMessage, "text" | "attachments" | "context">,
) {
if (isNativeMaintenanceCommand(message)) {
return yield* new OrchestratorDispatchError({
commandId: command.commandId,
commandType: command.type,
cause:
message.text.trim().toLowerCase() === "/compact"
? "Context compaction must run as a separate turn. Queue it or wait for the active turn to finish."
: "Signing out must run as a separate turn. Queue it or wait for the active turn to finish.",
});
}
if (parseGoalCommand(message) !== null) {
return yield* new OrchestratorDispatchError({
commandId: command.commandId,
commandType: command.type,
cause:
"Goal commands must run as a separate turn. Queue it or wait for the active turn to finish.",
});
}
});

const dispatchSteerIntoRun = Effect.fn("Orchestrator.dispatchSteerIntoRun")(function* (input: {
readonly command: Extract<
OrchestrationV2Command,
Expand Down Expand Up @@ -5318,24 +5345,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
cause: `Target run ${input.targetRunId} was not found.`,
});
}
if (isNativeMaintenanceCommand(input)) {
return yield* new OrchestratorDispatchError({
commandId: input.command.commandId,
commandType: input.command.type,
cause:
input.text.trim().toLowerCase() === "/compact"
? "Context compaction must run as a separate turn. Queue it or wait for the active turn to finish."
: "Signing out must run as a separate turn. Queue it or wait for the active turn to finish.",
});
}
if (parseGoalCommand(input) !== null) {
return yield* new OrchestratorDispatchError({
commandId: input.command.commandId,
commandType: input.command.type,
cause:
"Goal commands must run as a separate turn. Queue it or wait for the active turn to finish.",
});
}
yield* ensureSteeringMessage(input.command, input);
const targetMessage = input.projection.messages.find(
(message) => message.id === targetRun.userMessageId,
);
Expand Down Expand Up @@ -6523,6 +6533,36 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
payload: { ...sourcePlan, status: "completed" },
});

let steerWhenReadyRunId: RunId | undefined;
if (dispatchMode.type === "steer_active") {
const targetRunId = dispatchMode.targetRunId;
const target = projection.runs.find((run) => run.id === targetRunId);
if (
(target?.status === "preparing" ||
target?.status === "starting" ||
(target?.status === "running" &&
!projection.providerTurns.some(
(turn) => turn.runAttemptId === target.activeAttemptId && turn.status === "running",
))) &&
!(yield* stopReachedRun(command, command.threadId, target.id))
) {
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Resolved
const adapter = yield* providerAdapters
.get(target.providerInstanceId)
.pipe(mapDispatchError(command));
const capabilities = yield* adapter.getCapabilities().pipe(mapDispatchError(command));
yield* commandPolicy
.decideSteeringExecution({
commandId: command.commandId,
threadId: command.threadId,
providerInstanceId: target.providerInstanceId,
capabilities,
})
.pipe(mapDispatchError(command));
yield* ensureSteeringMessage(command, command);
steerWhenReadyRunId = target.id;
dispatchMode = { type: "queue_after_active" };
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Resolved
}
}
if (dispatchMode.type === "steer_active" || dispatchMode.type === "restart_active") {
yield* ensureGoalCommandSupported(command, command, modelSelection.instanceId);
yield* dispatchSteerIntoRun({
Expand Down Expand Up @@ -6583,6 +6623,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
const incomingMessage = { ...command, delegatedCompletion };
if (
shouldQueue &&
steerWhenReadyRunId === undefined &&
command.modelSelection === undefined &&
queuedRunStop === undefined &&
!followsCurrentConversation(incomingMessage) &&
Expand Down Expand Up @@ -6737,6 +6778,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
rootNodeId,
activeAttemptId: attemptId,
status: "queued",
...(steerWhenReadyRunId === undefined ? {} : { steerWhenReadyRunId }),
...(worktreeMissingRun !== undefined ||
(heldRunsToRelease.length === 0 &&
queueIsHeld &&
Expand Down Expand Up @@ -10373,6 +10415,39 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
now,
});
}
if (
(target?.status === "preparing" ||
target?.status === "starting" ||
(target?.status === "running" &&
!projection.providerTurns.some(
(turn) => turn.runAttemptId === target.activeAttemptId && turn.status === "running",
))) &&
!(yield* stopReachedRun(command, command.threadId, target.id))
) {
yield* ensureSteeringMessage(command, queuedMessage);
const adapter = yield* providerAdapters
.get(target.providerInstanceId)
.pipe(mapDispatchError(command));
const capabilities = yield* adapter.getCapabilities().pipe(mapDispatchError(command));
yield* commandPolicy
.decideSteeringExecution({
commandId: command.commandId,
threadId: command.threadId,
providerInstanceId: target.providerInstanceId,
capabilities,
})
.pipe(mapDispatchError(command));
yield* emitEvent({
type: "run.updated",
threadId: command.threadId,
runId: queuedRun.id,
nodeId: queuedRootNode.id,
providerInstanceId: queuedRun.providerInstanceId,
occurredAt: now,
payload: { ...queuedRun, steerWhenReadyRunId: target.id },
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Resolved
});
return;
}
yield* emitEvent({
type: "run.updated",
threadId: command.threadId,
Expand Down Expand Up @@ -14573,6 +14648,63 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
Effect.forkScoped,
);

yield* eventSink
.stream({ afterSequence: terminalEventsAfterSequence, eventType: "provider-turn.updated" })
.pipe(
Stream.filter(
(stored) =>
stored.event.type === "provider-turn.updated" &&
stored.event.payload.status === "running",
),
Stream.runForEach(
Effect.fn("Orchestrator.deliverDeferredSteers")(
function* (stored) {
if (stored.event.type !== "provider-turn.updated") return;
const threadId = stored.event.threadId;
const turn = stored.event.payload;
const projection = yield* projectionStore.getThreadRecords(threadId, ["runs"]);
const run = projection.runs.find(
(candidate) =>
candidate.activeAttemptId === turn.runAttemptId && candidate.status === "running",
);
if (run === undefined) return;
const pending = projection.runs
.filter(
(queued) => queued.status === "queued" && queued.steerWhenReadyRunId === run.id,
)
.toSorted(
(left, right) =>
(left.queuePosition ?? left.ordinal) - (right.queuePosition ?? right.ordinal),
);
for (const queued of pending) {
yield* dispatchWithReceiptEffect({
Comment thread
Yash-Singh1 marked this conversation as resolved.
Resolved
type: "queued-message.promote-to-steer",
commandId: CommandId.make(
`command:deferred-steer:${queued.id}:${run.id}:${turn.runAttemptId}`,
),
threadId,
queuedRunId: queued.id,
targetRunId: run.id,
}).pipe(
Effect.catchCause((cause) =>
Effect.logWarning("Failed to deliver a deferred steer", {
threadId,
runId: queued.id,
cause,
}),
),
);
}
},
(effect, stored) => threadDispatch.withLock(stored.event.threadId, effect),
Effect.catchCause((cause) =>
Effect.logWarning("Failed to deliver deferred steering", { cause }),
),
),
),
Effect.forkScoped,
);

// A child whose run ends while its background tasks or its own subagents still run has no
// result yet. When that work settles there is no run update to finalize it, so these events
// do it. A background roster counts once it stops holding after it was seen holding.
Expand Down
Loading
Loading