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

224 lines
7.4 KiB
TypeScript

import { memo, useMemo, type JSX } from "react";
import type { I18nRuntime } from "@tutti-os/ui-i18n-runtime";
import { TooltipProvider } from "@tutti-os/ui-system";
import type { AgentGUIRuntime } from "./agentActivityRuntime";
import type { AgentHostInputApi } from "./host/agentHostApi";
import type {
AgentGUIAgentDirectorySnapshot,
AgentGUIAllAgentsPresentation,
AgentGUIHomeSuggestionId
} from "./types";
import type { AgentGUIAgentsEmptyRenderer } from "./agent-gui/agentGuiNode/AgentGUINodeView";
import {
normalizeAgentGUIAgents,
projectAgentGUIAgentsToTargets
} from "./agents";
import {
AgentGUINode,
type AgentGUINodeProps
} from "./agent-gui/agentGuiNode/AgentGUINode";
import { AgentGUIActivityHostProvider } from "./agentActivityHost";
import { AgentGuiI18nProvider, type AgentGuiI18nLocale } from "./i18n/index";
import {
TuttiModePlanReviewRuntimeProvider,
type TuttiModePlanReviewRuntime
} from "./workspaceWorkflow";
import { AgentVisibleErrorPresentationProvider } from "./shared/visibleError/AgentVisibleErrorPresentationContext";
import {
AgentSideConversationRuntimeProvider,
type AgentSideConversationRuntime
} from "./agentSideConversationRuntime";
export type { AgentGUIHomeSuggestionId } from "./types";
export type { ReferenceProvenanceCatalog as AgentGUIReferenceProvenanceFilterCatalog } from "@tutti-os/workspace-file-reference/contracts";
type AgentGUIPublicHostCapabilities = Omit<
AgentGUINodeProps["hostCapabilities"],
| "agentTargets"
| "agentTargetsLoading"
| "mentionAgentTargets"
| "handoffAgentTargets"
| "handoffAgentTargetsLoading"
| "providerRailAllPresentation"
| "providerRailMode"
| "disabledHomeSuggestions"
>;
type AgentGUIPublicRenderSlots = Omit<
AgentGUINodeProps["renderSlots"],
"providerRailEmpty"
>;
export interface AgentGUIProps extends Omit<
AgentGUINodeProps,
"hostCapabilities" | "renderSlots"
> {
agentDirectory: AgentGUIAgentDirectorySnapshot;
/**
* Complete host-owned identity catalog for rendered Agent mentions. Unlike
* handoff, this directory may include unavailable targets because it does
* not grant launch capability.
*/
mentionAgentDirectory?: AgentGUIAgentDirectorySnapshot;
/**
* Host-owned launch catalog for conversation handoff. When omitted, handoff
* uses `agentDirectory`, preserving the single-runtime host contract.
*/
handoffAgentDirectory?: AgentGUIAgentDirectorySnapshot;
allAgentsPresentation?: AgentGUIAllAgentsPresentation | null;
renderAgentsEmpty?: AgentGUIAgentsEmptyRenderer;
agentActivityRuntime: AgentGUIRuntime;
agentSideConversationRuntime?: AgentSideConversationRuntime | null;
agentHostApi?: AgentHostInputApi | null;
tuttiModePlanReviewRuntime?: TuttiModePlanReviewRuntime | null;
/** Starter entries to hide below the empty new-session composer. */
disabled?: readonly AgentGUIHomeSuggestionId[];
i18n?: I18nRuntime<string> | null;
locale?: AgentGuiI18nLocale;
hostCapabilities: AgentGUIPublicHostCapabilities;
renderSlots: AgentGUIPublicRenderSlots;
}
export const AgentGUI = memo(function AgentGUI({
agentActivityRuntime,
agentSideConversationRuntime,
agentHostApi,
tuttiModePlanReviewRuntime,
agentDirectory,
mentionAgentDirectory,
handoffAgentDirectory,
allAgentsPresentation = null,
renderAgentsEmpty,
disabled,
i18n,
locale,
...props
}: AgentGUIProps): JSX.Element {
const normalizedAgents = useMemo(
() => normalizeAgentGUIAgents(agentDirectory.agents),
[agentDirectory.agents]
);
const agentTargets = useMemo(
() => projectAgentGUIAgentsToTargets(normalizedAgents),
[normalizedAgents]
);
const hostCapabilities = props.hostCapabilities;
const effectiveHandoffAgentDirectory =
handoffAgentDirectory ?? agentDirectory;
const normalizedHandoffAgents = useMemo(
() =>
effectiveHandoffAgentDirectory.agents === agentDirectory.agents
? normalizedAgents
: normalizeAgentGUIAgents(effectiveHandoffAgentDirectory.agents),
[
agentDirectory.agents,
effectiveHandoffAgentDirectory.agents,
normalizedAgents
]
);
const handoffAgentTargets = useMemo(() => {
const targets =
normalizedHandoffAgents === normalizedAgents
? agentTargets
: projectAgentGUIAgentsToTargets(normalizedHandoffAgents);
if (
!targets.some(
(target) =>
target.disabled !== true &&
(target.availability?.status !== "ready" ||
hostCapabilities?.providerReadinessGates?.[target.provider] != null)
)
) {
return targets;
}
return targets.map((target) =>
(target.availability?.status === "ready" &&
hostCapabilities?.providerReadinessGates?.[target.provider] == null) ||
target.disabled === true
? target
: { ...target, disabled: true }
);
}, [
agentTargets,
hostCapabilities?.providerReadinessGates,
normalizedAgents,
normalizedHandoffAgents
]);
const renderSlots = props.renderSlots;
const nodeHostCapabilities = useMemo<AgentGUINodeProps["hostCapabilities"]>(
() => ({
...hostCapabilities,
agentTargets,
agentTargetsLoading:
agentDirectory.agents.length === 0 &&
(agentDirectory.status === "idle" ||
agentDirectory.status === "loading"),
mentionAgentTargets: mentionAgentDirectory
? projectAgentGUIAgentsToTargets(
normalizeAgentGUIAgents(mentionAgentDirectory.agents)
)
: undefined,
handoffAgentTargets,
handoffAgentTargetsLoading:
effectiveHandoffAgentDirectory.agents.length === 0 &&
(effectiveHandoffAgentDirectory.status === "idle" ||
effectiveHandoffAgentDirectory.status === "loading"),
disabledHomeSuggestions: disabled,
providerRailAllPresentation: allAgentsPresentation ?? null,
providerRailMode: "exact"
}),
[
agentDirectory.agents.length,
agentDirectory.status,
agentTargets,
allAgentsPresentation,
disabled,
effectiveHandoffAgentDirectory.agents.length,
effectiveHandoffAgentDirectory.status,
handoffAgentTargets,
hostCapabilities,
mentionAgentDirectory?.agents
]
);
const nodeRenderSlots = useMemo<AgentGUINodeProps["renderSlots"]>(
() => ({
...renderSlots,
providerRailEmpty: renderAgentsEmpty
}),
[renderAgentsEmpty, renderSlots]
);
const nodeProps: AgentGUINodeProps = {
...props,
hostCapabilities: nodeHostCapabilities,
renderSlots: nodeRenderSlots
};
const content = (
<AgentGuiI18nProvider runtime={i18n} locale={locale}>
<AgentSideConversationRuntimeProvider
runtime={agentSideConversationRuntime}
>
<TuttiModePlanReviewRuntimeProvider
runtime={tuttiModePlanReviewRuntime}
>
<AgentGUIActivityHostProvider
agentActivityRuntime={agentActivityRuntime}
agentHostApi={agentHostApi}
>
<AgentVisibleErrorPresentationProvider
scope={props.hostCapabilities?.visibleErrorPresentationScope}
value={props.hostCapabilities?.visibleErrorPresentationOverrides}
>
<AgentGUINode {...nodeProps} />
</AgentVisibleErrorPresentationProvider>
</AgentGUIActivityHostProvider>
</TuttiModePlanReviewRuntimeProvider>
</AgentSideConversationRuntimeProvider>
</AgentGuiI18nProvider>
);
return (
<TooltipProvider delayDuration={120} skipDelayDuration={0}>
{content}
</TooltipProvider>
);
});