Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
import {
|
|
selectPlanTurnDismissed,
|
|
selectSessionMessages,
|
|
selectWorkspaceAgentConsumerSessions,
|
|
type AgentActivitySession,
|
|
type AgentActivityTurn
|
|
} from "@tutti-os/agent-activity-core";
|
|
import { latestPlanTurnId } from "./planImplementationPresentation";
|
|
|
|
type AgentSessionEngineSnapshot = Parameters<
|
|
typeof selectWorkspaceAgentConsumerSessions
|
|
>[0];
|
|
|
|
/**
|
|
* Shared predicate for the synthesized plan-implementation wait. Durable
|
|
* approval / question / exit-plan waits already ride pendingInteractions;
|
|
* this predicate is the matching cross-surface fact for plan confirmation so
|
|
* rail, conversation list, and attention do not each invent a private rule.
|
|
*
|
|
* Capability rules match the detail composer: an explicit `false` fails closed,
|
|
* but a missing capabilities record stays open so a settled plan turn can still
|
|
* surface waiting while composer options are the only advertised source.
|
|
*/
|
|
export function consumerAwaitingPlanImplementation(input: {
|
|
capabilities: AgentActivitySession["capabilities"] | null | undefined;
|
|
dismissed: boolean;
|
|
latestTurn: AgentActivityTurn | null | undefined;
|
|
messages: readonly {
|
|
turnId?: string | null;
|
|
occurredAtUnixMs?: number | null;
|
|
createdAtUnixMs?: number | null;
|
|
seq?: number | null;
|
|
payload?: Record<string, unknown> | null;
|
|
}[];
|
|
}): boolean {
|
|
const latestTurn = input.latestTurn;
|
|
if (
|
|
!latestTurn ||
|
|
latestTurn.phase !== "settled" ||
|
|
latestTurn.outcome !== "completed" ||
|
|
input.dismissed ||
|
|
!sessionAllowsPlanImplementation(input.capabilities)
|
|
) {
|
|
return false;
|
|
}
|
|
return latestPlanTurnId(input.messages) === latestTurn.turnId;
|
|
}
|
|
|
|
function sessionAllowsPlanImplementation(
|
|
capabilities: AgentActivitySession["capabilities"] | null | undefined
|
|
): boolean {
|
|
if (!capabilities) {
|
|
return true;
|
|
}
|
|
return (
|
|
capabilities.planImplementation === true && capabilities.planMode === true
|
|
);
|
|
}
|
|
|
|
export function selectRootAgentSessionIdsAwaitingPlanImplementation(
|
|
state: AgentSessionEngineSnapshot
|
|
): readonly string[] {
|
|
const sessionIds: string[] = [];
|
|
for (const consumer of selectWorkspaceAgentConsumerSessions(state)) {
|
|
const sessionId = consumer.session.agentSessionId;
|
|
if (
|
|
!consumerAwaitingPlanImplementation({
|
|
capabilities: consumer.session.capabilities,
|
|
dismissed: selectPlanTurnDismissed(
|
|
state,
|
|
sessionId,
|
|
consumer.latestTurn?.turnId
|
|
),
|
|
latestTurn: consumer.latestTurn,
|
|
messages: sessionMessagesForPlanDetection(state, consumer.session)
|
|
})
|
|
) {
|
|
continue;
|
|
}
|
|
sessionIds.push(sessionId);
|
|
}
|
|
return sessionIds;
|
|
}
|
|
|
|
function sessionMessagesForPlanDetection(
|
|
state: AgentSessionEngineSnapshot,
|
|
session: Pick<AgentActivitySession, "agentSessionId" | "providerSessionId">
|
|
) {
|
|
for (const id of [session.agentSessionId, session.providerSessionId]) {
|
|
const messages = selectSessionMessages(state, id);
|
|
if (messages.length > 0) {
|
|
return messages;
|
|
}
|
|
}
|
|
return [];
|
|
}
|