Files
agent-radar 2ed3415fb7
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
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

227 lines
7.1 KiB
TypeScript

import type { AgentGUIProviderReadinessGate } from "../../../types";
import type {
AgentGUIComposerEditorBlockedReason,
AgentGUIComposerGate,
AgentGUIComposerSubmissionBlockedReason,
AgentGUIRuntimeBlockedReason
} from "./agentGuiNodeTypes";
export interface ResolveAgentGUIComposerGateInput {
activeConversationBusy: boolean;
activeConversationId: string | null;
activeEngineHasPendingInteractions: boolean;
activeLiveState: "inactive" | "activating" | "active" | "failed";
activeConversationResumeUnavailable: boolean;
agentTargetsLoading: boolean;
authBlocked: boolean;
hasNonRetryableRecoveryFailure: boolean;
isCollaboratorConversation: boolean;
isCreatingConversation: boolean;
isInterrupting: boolean;
isAwaitingTurnStart: boolean;
isSubmitting: boolean;
pendingApproval: boolean;
pendingInteractivePrompt: boolean;
providerReadinessGate: AgentGUIProviderReadinessGate | null;
selectedAgentTargetUnavailable: boolean;
/**
* True while a session settings update is unsettled (in flight, waiting for
* runtime, timed out/unknown, or failed). Blocks submit until idle so send
* cannot race UpdateSettings on the daemon settings lock.
*/
settingsUpdatePending: boolean;
sessionRuntimeBlockedReason: AgentGUIRuntimeBlockedReason | null;
targetConnectionBlocked: boolean;
}
export function resolveAgentGUIComposerGate(
input: ResolveAgentGUIComposerGateInput
): AgentGUIComposerGate {
const conversationBusy = input.activeConversationBusy;
const sharingRevoked =
input.sessionRuntimeBlockedReason === "agent_sharing_revoked";
const runtime: AgentGUIComposerGate["runtime"] = sharingRevoked
? {
status: "blocked",
reason: "session_runtime",
sessionRuntimeReason: "agent_sharing_revoked"
}
: input.targetConnectionBlocked
? {
status: "blocked",
reason: "target_connection",
sessionRuntimeReason: null
}
: input.sessionRuntimeBlockedReason !== null
? {
status: "blocked",
reason: "session_runtime",
sessionRuntimeReason: input.sessionRuntimeBlockedReason
}
: {
status: "ready",
reason: null,
sessionRuntimeReason: null
};
const canQueue =
input.activeConversationId !== null &&
runtime.status === "ready" &&
(conversationBusy || input.activeEngineHasPendingInteractions);
const canSubmit =
!input.agentTargetsLoading &&
input.providerReadinessGate === null &&
!input.selectedAgentTargetUnavailable &&
runtime.status === "ready" &&
input.activeLiveState !== "activating" &&
input.activeLiveState !== "failed" &&
!input.activeConversationResumeUnavailable &&
!input.pendingApproval &&
!input.pendingInteractivePrompt &&
!input.authBlocked &&
!input.settingsUpdatePending &&
!input.isAwaitingTurnStart &&
!conversationBusy &&
!input.isCreatingConversation &&
!input.isInterrupting;
const hardBlockReason = resolveHardBlockReason(input, runtime);
const editor = hardBlockReason
? {
status: "blocked" as const,
reason: hardBlockReason
}
: canQueue
? {
status: "editable" as const,
reason: null
}
: resolveEditorGate(input);
const submission = hardBlockReason
? {
status: "blocked" as const,
reason: hardBlockReason
}
: canSubmit
? {
status: "ready" as const,
reason: null
}
: canQueue
? {
status: "queue" as const,
reason: "conversation_busy" as const
}
: {
status: "blocked" as const,
reason: resolveSubmissionBlockReason(input)
};
return {
conversationBusy,
isAwaitingTurnStart: input.isAwaitingTurnStart,
runtime,
editor,
submission
};
}
function resolveHardBlockReason(
input: ResolveAgentGUIComposerGateInput,
runtime: AgentGUIComposerGate["runtime"]
): AgentGUIComposerEditorBlockedReason | null {
if (input.isCollaboratorConversation) {
return "collaborator_read_only";
}
if (runtime.status === "blocked") {
return "runtime_blocked";
}
if (input.hasNonRetryableRecoveryFailure) {
return "non_retryable_recovery";
}
return null;
}
function resolveEditorGate(
input: ResolveAgentGUIComposerGateInput
): AgentGUIComposerGate["editor"] {
const reason: AgentGUIComposerEditorBlockedReason | null =
input.providerReadinessGate !== null || input.selectedAgentTargetUnavailable
? "provider_readiness"
: input.pendingApproval
? "pending_approval"
: input.pendingInteractivePrompt
? "pending_interactive_prompt"
: input.isSubmitting
? "submitting"
: input.isInterrupting
? "interrupting"
: input.isCreatingConversation
? "creating_conversation"
: null;
return reason
? { status: "blocked", reason }
: { status: "editable", reason: null };
}
function resolveSubmissionBlockReason(
input: ResolveAgentGUIComposerGateInput
): AgentGUIComposerSubmissionBlockedReason {
if (input.agentTargetsLoading) return "agent_targets_loading";
if (
input.providerReadinessGate !== null ||
input.selectedAgentTargetUnavailable
) {
return "provider_readiness";
}
if (input.activeLiveState === "activating") return "activation_pending";
if (input.activeLiveState === "failed") return "activation_failed";
if (input.activeConversationResumeUnavailable) return "resume_unavailable";
if (input.pendingApproval) return "pending_approval";
if (input.pendingInteractivePrompt) return "pending_interactive_prompt";
if (input.authBlocked) return "authentication_required";
if (input.settingsUpdatePending) return "settings_update_pending";
if (input.isAwaitingTurnStart) return "submitting";
if (input.activeConversationBusy) return "conversation_busy";
if (input.isCreatingConversation) return "creating_conversation";
if (input.isSubmitting) return "submitting";
if (input.isInterrupting) return "interrupting";
return "conversation_busy";
}
export function isDifferentKnownConversationOwner(input: {
conversationUserId?: string | null;
currentUserId?: string | null;
}): boolean {
const conversationUserId = input.conversationUserId?.trim() ?? "";
const currentUserId = input.currentUserId?.trim() ?? "";
if (
!conversationUserId ||
!currentUserId ||
conversationUserId === "local" ||
currentUserId === "local"
) {
return false;
}
return conversationUserId !== currentUserId;
}
export function projectAgentGUIComposerGateControls(input: {
gate: AgentGUIComposerGate;
presentationEditorDisabled: boolean;
presentationSubmitDisabled: boolean;
}): {
canQueueWhileBusy: boolean;
editorDisabled: boolean;
submissionDisabled: boolean;
} {
return {
canQueueWhileBusy: input.gate.submission.status === "queue",
editorDisabled:
input.gate.editor.status === "blocked" ||
input.presentationEditorDisabled,
submissionDisabled:
input.gate.submission.status === "blocked" ||
input.presentationSubmitDisabled
};
}