源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
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

This commit is contained in:
agent-radar
2026-09-01 20:17:27 +08:00
commit 2ed3415fb7
8018 changed files with 1936476 additions and 0 deletions
@@ -0,0 +1 @@
export * from "../../../../../actions/workspaceLinkActions";
@@ -0,0 +1,775 @@
import { act, renderHook } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { createAgentSessionEngine } from "@tutti-os/agent-activity-core";
import { createLocalAgentGUIAgentTarget } from "../../../../../agentTargets";
import { createTestEngineCommandPort } from "../../../../../shared/testing/createTestAgentSessionEngine";
import { useAgentGuiConversationList } from "./useAgentGuiConversationList";
describe("useAgentGuiConversationList", () => {
it("projects a child interaction as user action on its root conversation", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "claude-code" as const,
sessionOrigin: "test"
};
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurn: {
agentSessionId: "root",
origin: "user_prompt",
phase: "waiting",
startedAtUnixMs: 1,
turnId: "root-turn",
updatedAtUnixMs: 2
},
activeTurnId: "root-turn",
agentSessionId: "root",
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "claude-code",
title: "Root conversation",
updatedAtUnixMs: 2,
workspaceId: "workspace-1"
},
{
activeTurn: {
agentSessionId: "child",
origin: "provider_initiated",
phase: "waiting",
startedAtUnixMs: 1,
turnId: "child-turn",
updatedAtUnixMs: 2
},
activeTurnId: "child-turn",
agentSessionId: "child",
cwd: "/workspace",
kind: "child",
latestTurnInteractions: [],
parentAgentSessionId: "root",
parentToolCallId: "tool-call-1",
parentTurnId: "root-turn",
pendingInteractions: [
{
agentSessionId: "child",
createdAtUnixMs: 2,
input: { questions: [{ id: "question-1" }] },
kind: "question",
requestId: "request-1",
status: "pending",
toolName: "AskUserQuestion",
turnId: "child-turn",
updatedAtUnixMs: 2
}
],
provider: "claude-code",
rootAgentSessionId: "root",
rootTurnId: "root-turn",
title: "Child task",
updatedAtUnixMs: 2,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current?.conversations).toEqual([
expect.objectContaining({
id: "root",
needsUserAction: true,
status: "waiting"
})
]);
});
it("projects a plain @ title from a matching mention-rich initial prompt", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const displayPrompt =
"[@Task](mention://workspace-issue/issue-1?workspaceId=workspace-1) 看看";
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "activation/requested",
agentSessionId: "session-1",
agentTargetId: "local:codex",
clientSubmitId: "submit-1",
content: [{ type: "text", text: displayPrompt }],
cwd: "/workspace",
expiresAtUnixMs: 100,
initialDisplayPrompt: displayPrompt,
mode: "new",
optimisticTitle: "@Task 看看",
railSectionKey: "project:/workspace",
requestedAtUnixMs: 1,
requestId: "activation-1",
workspaceId: "workspace-1"
});
});
expect(result.current?.conversations[0]).toEqual(
expect.objectContaining({
railSectionKey: "project:/workspace",
title: "@Task 看看",
titleLeadingMentionKind: null
})
);
});
it("shows only conversation text for a browser-element activation", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const displayPrompt =
"[@<div>](mention://browser-element/browser-element%3A1?tag=div&workspaceId=workspace-1) 这里说的什么";
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "activation/requested",
agentSessionId: "session-1",
agentTargetId: "local:codex",
clientSubmitId: "submit-1",
content: [{ type: "text", text: displayPrompt }],
cwd: "/workspace",
expiresAtUnixMs: 100,
initialDisplayPrompt: displayPrompt,
mode: "new",
optimisticTitle: "@<div> 这里说的什么",
requestedAtUnixMs: 1,
requestId: "activation-1",
workspaceId: "workspace-1"
});
});
expect(result.current?.conversations[0]).toEqual(
expect.objectContaining({
title: "这里说的什么",
titleLeadingMentionKind: null,
titleFallback: null
})
);
});
it("shows only conversation text for a canonical browser-element session", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const displayPrompt =
"[@<div>](mention://browser-element/browser-element%3A1?tag=div&workspaceId=workspace-1) 这里说的什么";
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurnId: null,
agentSessionId: "session-1",
createdAtUnixMs: 1,
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "@<div> 这里说的什么",
updatedAtUnixMs: 1,
workspaceId: "workspace-1"
}
]
});
engine.dispatch({
type: "message/snapshotReceived",
messages: [
{
agentSessionId: "session-1",
kind: "text",
messageId: "message-1",
occurredAtUnixMs: 1,
payload: {
displayPrompt,
text: displayPrompt
},
role: "user",
status: null,
turnId: "turn-1",
version: 1,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current?.conversations[0]).toEqual(
expect.objectContaining({
title: "这里说的什么",
titleLeadingMentionKind: null,
titleFallback: null
})
);
});
it("shows a historical session marker after its first message is loaded", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const displayPrompt =
"[@Previous chat](mention://agent-session/session-previous?workspaceId=workspace-1) 看看";
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurnId: null,
agentSessionId: "session-1",
createdAtUnixMs: 1,
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "@Previous chat 看看",
updatedAtUnixMs: 1,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current?.conversations[0]).toEqual(
expect.objectContaining({
title: "@Previous chat 看看",
titleLeadingMentionKind: null
})
);
act(() => {
engine.dispatch({
type: "message/snapshotReceived",
messages: [
{
agentSessionId: "session-1",
kind: "text",
messageId: "message-1",
occurredAtUnixMs: 1,
payload: { displayPrompt, text: displayPrompt },
role: "user",
status: null,
turnId: "turn-1",
version: 1,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current?.conversations[0]).toEqual(
expect.objectContaining({
title: "@Previous chat 看看",
titleLeadingMentionKind: null
})
);
});
it("keeps the projected list stable while an assistant message streams", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurnId: null,
agentSessionId: "session-1",
createdAtUnixMs: 1,
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "@Task inspect",
updatedAtUnixMs: 1,
workspaceId: "workspace-1"
}
]
});
const displayPrompt =
"[@Task](mention://workspace-issue/issue-1?workspaceId=workspace-1) inspect";
engine.dispatch({
type: "message/snapshotReceived",
messages: [
{
agentSessionId: "session-1",
kind: "text",
messageId: "user-1",
occurredAtUnixMs: 1,
payload: { displayPrompt, text: displayPrompt },
role: "user",
status: null,
turnId: "turn-1",
version: 1,
workspaceId: "workspace-1"
}
]
});
});
const beforeStreaming = result.current;
act(() => {
engine.dispatch({
type: "message/snapshotReceived",
messages: [
{
agentSessionId: "session-1",
kind: "text",
messageId: "assistant-1",
occurredAtUnixMs: 2,
payload: { text: "partial response" },
role: "assistant",
status: "streaming",
turnId: "turn-1",
version: 2,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current).toBe(beforeStreaming);
});
it("keeps the optimistic activation title until canonical title arrives", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "activation/requested",
agentSessionId: "session-1",
agentTargetId: "local:codex",
clientSubmitId: "submit-1",
content: [{ type: "text", text: "test1" }],
cwd: "/workspace",
expiresAtUnixMs: 100,
mode: "new",
requestedAtUnixMs: 1,
requestId: "activation-1",
optimisticTitle: "test1",
workspaceId: "workspace-1"
});
});
expect(result.current?.conversations).toEqual([
expect.objectContaining({
id: "session-1",
provider: "codex",
status: "working",
title: "test1",
titleFallback: null
})
]);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurnId: null,
agentSessionId: "session-1",
createdAtUnixMs: 2,
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "test1",
updatedAtUnixMs: 2,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current?.conversations).toEqual([
expect.objectContaining({
id: "session-1",
status: "ready",
title: "test1",
titleFallback: null
})
]);
});
it("projects canonical sessions and pending activation records without a list store", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "activation/requested",
agentSessionId: "session-1",
agentTargetId: "local:codex",
clientSubmitId: "submit-1",
cwd: "/workspace",
expiresAtUnixMs: 100,
mode: "new",
requestedAtUnixMs: 1,
requestId: "activation-1",
title: "Pending task",
workspaceId: "workspace-1"
});
});
expect(result.current?.conversations).toEqual([
expect.objectContaining({
id: "session-1",
status: "ready",
title: "Pending task"
})
]);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
...{
activeTurnId: null,
latestTurnInteractions: [],
pendingInteractions: []
},
agentSessionId: "session-1",
createdAtUnixMs: 2,
cwd: "/workspace",
provider: "codex",
title: "Durable task",
updatedAtUnixMs: 2,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current?.conversations).toEqual([
expect.objectContaining({
id: "session-1",
status: "ready",
title: "Durable task"
})
]);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurn: {
agentSessionId: "session-1",
origin: "user_prompt",
phase: "running",
startedAtUnixMs: 3,
turnId: "turn-1",
updatedAtUnixMs: 3
},
activeTurnId: "turn-1",
agentSessionId: "session-1",
createdAtUnixMs: 2,
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "Durable task",
updatedAtUnixMs: 3,
workspaceId: "workspace-1"
}
]
});
});
expect(result.current?.conversations).toEqual([
expect.objectContaining({
id: "session-1",
status: "working",
title: "Durable task"
})
]);
});
it("keeps concurrent pending activations ordered and replaces only the canonicalized row", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
conversationFilter: { kind: "all" as const },
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const targets = [
createLocalAgentGUIAgentTarget("codex"),
createLocalAgentGUIAgentTarget("claude-code")
];
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query, targets)
);
act(() => {
engine.dispatch({
type: "activation/requested",
agentSessionId: "session-1",
agentTargetId: "local:codex",
clientSubmitId: "submit-1",
cwd: "/workspace",
expiresAtUnixMs: 100,
mode: "new",
requestedAtUnixMs: 10,
requestId: "activation-1",
title: "First",
workspaceId: "workspace-1"
});
engine.dispatch({
type: "activation/requested",
agentSessionId: "session-2",
agentTargetId: "local:claude-code",
clientSubmitId: "submit-2",
cwd: "/workspace",
expiresAtUnixMs: 100,
mode: "new",
requestedAtUnixMs: 20,
requestId: "activation-2",
title: "Second",
workspaceId: "workspace-1"
});
});
expect(
result.current?.conversations.map((conversation) => ({
id: conversation.id,
provider: conversation.provider,
source: conversation.projectionSource
}))
).toEqual([
{
id: "session-2",
provider: "claude-code",
source: "pending_activation"
},
{
id: "session-1",
provider: "codex",
source: "pending_activation"
}
]);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurnId: null,
agentSessionId: "session-1",
createdAtUnixMs: 30,
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "First durable",
updatedAtUnixMs: 30,
workspaceId: "workspace-1"
}
]
});
});
expect(
result.current?.conversations.map((conversation) => ({
id: conversation.id,
source: conversation.projectionSource,
title: conversation.title
}))
).toEqual([
{ id: "session-1", source: undefined, title: "First durable" },
{
id: "session-2",
source: "pending_activation",
title: "Second"
}
]);
});
it("orders canonical sessions by latest turn start instead of update timestamps", () => {
const engine = createAgentSessionEngine({
clock: { nowUnixMs: () => 1 },
commandPort: createTestEngineCommandPort({ execute: async () => ({}) }),
identity: { origin: "test", workspaceId: "workspace-1" },
scheduler: { schedule: () => ({ cancel() {} }) }
});
const query = {
conversationFilter: { kind: "all" as const },
workspaceId: "workspace-1",
userId: "user-1",
provider: "codex" as const,
sessionOrigin: "test"
};
const { result } = renderHook(() =>
useAgentGuiConversationList(engine, query)
);
act(() => {
engine.dispatch({
type: "session/snapshotReceived",
sessions: [
{
activeTurnId: null,
agentSessionId: "older-start-newer-update",
createdAtUnixMs: 1_000,
cwd: "/workspace",
latestTurn: {
agentSessionId: "older-start-newer-update",
origin: "user_prompt",
phase: "running",
startedAtUnixMs: 2_000,
turnId: "turn-older",
updatedAtUnixMs: 9_000
},
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "Older turn",
updatedAtUnixMs: 9_000,
workspaceId: "workspace-1"
},
{
activeTurnId: null,
agentSessionId: "newer-start-older-update",
createdAtUnixMs: 500,
cwd: "/workspace",
latestTurn: {
agentSessionId: "newer-start-older-update",
origin: "user_prompt",
phase: "running",
startedAtUnixMs: 3_000,
turnId: "turn-newer",
updatedAtUnixMs: 4_000
},
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
title: "Newer turn",
updatedAtUnixMs: 4_000,
workspaceId: "workspace-1"
}
]
});
});
expect(
result.current?.conversations.map((conversation) => ({
id: conversation.id,
sortTimeUnixMs: conversation.sortTimeUnixMs
}))
).toEqual([
{ id: "newer-start-older-update", sortTimeUnixMs: 3_000 },
{ id: "older-start-newer-update", sortTimeUnixMs: 2_000 }
]);
});
});
@@ -0,0 +1,290 @@
import { useMemo } from "react";
import {
isPendingActivationViable,
selectPendingActivations,
selectRootAgentSessionIdsWithPendingInteractions,
selectWorkspaceAgentConsumerSessions,
selectWorkspaceReconcileState,
type AgentSessionEngine,
type PendingActivationIntentRecord
} from "@tutti-os/agent-activity-core";
import { useEngineSelector } from "../../../../../shared/engine/useEngineSelector";
import {
matchesAgentGUIConversationSummaryFilter,
normalizeAgentGUIConversationFilter,
type AgentGUIConversationFilter
} from "../../../../../agent-gui/agentGuiNode/model/agentGuiConversationFilter";
import type { AgentGUIProvider } from "../../../../../types";
import type { AgentGUIAgentTarget } from "../../../../../types";
import type { AgentGUIConversationSummary } from "../../../../../agent-gui/agentGuiNode/model/agentGuiConversationModel";
import {
isAgentGUIProviderUnresolved,
resolveAgentGUIConversationBrowserFreeTitle,
resolveAgentGUIConversationTitleDisplayPrompt,
resolveAgentGUIConversationTitleLeadingMentionKind,
resolveAgentGUIConversationTitle,
resolveAgentGUIProviderIdentity
} from "../../../../../shared/agentConversationTitleProjection.ts";
import { createAgentGUIConversationRailTitlePromptSelector } from "../../../../../shared/agentConversationRailTitlePromptSelector.ts";
import { projectCanonicalAgentGUIConversationSummaries } from "../../../../../shared/agentGUIConversationSummaryProjection.ts";
import { selectRootAgentSessionIdsAwaitingPlanImplementation } from "../../../../../shared/agentConversation/planImplementationAwaiting";
export interface AgentGUIConversationListQuery {
conversationFilter?: AgentGUIConversationFilter | null;
workspaceId: string;
userId: string;
provider: AgentGUIProvider;
sessionOrigin: string;
}
const EMPTY_AGENT_GUI_AGENT_TARGETS: readonly AgentGUIAgentTarget[] = [];
export function useAgentGuiConversationList(
engine: AgentSessionEngine,
query: AgentGUIConversationListQuery | null,
agentTargets: readonly AgentGUIAgentTarget[] = EMPTY_AGENT_GUI_AGENT_TARGETS
) {
const workspaceReconcile = useEngineSelector(
engine,
selectWorkspaceReconcileState
);
const sessions = useEngineSelector(
engine,
selectWorkspaceAgentConsumerSessions,
consumerSessionsEqual
);
const rootAgentSessionIdsWithPendingInteractions = useEngineSelector(
engine,
selectRootAgentSessionIdsWithPendingInteractions,
stringArraysEqual
);
const rootAgentSessionIdsAwaitingPlanImplementation = useEngineSelector(
engine,
selectRootAgentSessionIdsAwaitingPlanImplementation,
stringArraysEqual
);
const pendingActivations = useEngineSelector(
engine,
selectPendingActivations,
pendingActivationsEqual
);
const selectRailTitlePrompts = useMemo(
() => createAgentGUIConversationRailTitlePromptSelector(),
[engine]
);
const firstUserDisplayPromptsBySessionId = useEngineSelector(
engine,
selectRailTitlePrompts,
Object.is
);
return useMemo(() => {
if (!query) return null;
const rootSessionIdsAwaitingUserAction = new Set([
...rootAgentSessionIdsWithPendingInteractions,
...rootAgentSessionIdsAwaitingPlanImplementation
]);
const canonicalIds = new Set(
sessions.map((item) => item.session.agentSessionId)
);
const latestNewActivationBySessionId = new Map(
pendingActivations
.filter(
(activation) =>
activation.mode === "new" && isPendingActivationViable(activation)
)
.map((activation) => [activation.agentSessionId, activation] as const)
);
const pendingConversations = pendingActivations
.filter(
(activation) =>
activation.mode === "new" &&
isPendingActivationViable(activation) &&
!canonicalIds.has(activation.agentSessionId)
)
.map((activation): AgentGUIConversationSummary => {
const target = agentTargets.find(
(candidate) => candidate.agentTargetId === activation.agentTargetId
);
const provider = resolveAgentGUIProviderIdentity({
sessionProvider: target?.provider ?? query.provider
});
const { title: canonicalTitle } = resolveAgentGUIConversationTitle(
activation.optimisticTitle ?? activation.title ?? ""
);
const titleDisplayPrompt =
resolveAgentGUIConversationTitleDisplayPrompt({
activation,
allowEmptyTitle: true,
title: canonicalTitle
});
const { title, titleFallback } = resolveAgentGUIConversationTitle(
resolveAgentGUIConversationBrowserFreeTitle({
activation,
allowEmptyTitle: true,
title: canonicalTitle
})
);
const titleLeadingMentionKind =
resolveAgentGUIConversationTitleLeadingMentionKind(
titleDisplayPrompt
);
return {
agentTargetId: activation.agentTargetId,
cwd: activation.cwd,
id: activation.agentSessionId,
provider,
sortTimeUnixMs: activation.requestedAtUnixMs,
status: activation.initialTurnExpected ? "working" : "ready",
projectionSource: "pending_activation",
railSectionKey: activation.railSectionKey,
title,
titleLeadingMentionKind,
titleFallback,
updatedAtUnixMs: activation.requestedAtUnixMs,
userId: query.userId
};
});
const conversations = [
...projectCanonicalAgentGUIConversationSummaries(
sessions.filter(
(item) => item.session.workspaceId === query.workspaceId
),
firstUserDisplayPromptsBySessionId,
rootSessionIdsAwaitingUserAction
).map((conversation): AgentGUIConversationSummary => {
const canonicalUpdatedAtUnixMs = conversation.updatedAtUnixMs;
const activation = latestNewActivationBySessionId.get(conversation.id);
const activationIsNewer =
activation !== undefined &&
activation.requestedAtUnixMs > canonicalUpdatedAtUnixMs;
const optimisticTitle = activation?.optimisticTitle?.trim() ?? "";
const shouldUseOptimisticTitle =
conversation.title.trim().length === 0 && optimisticTitle.length > 0;
const projectedTitle = shouldUseOptimisticTitle
? optimisticTitle
: activationIsNewer && activation?.title
? activation.title
: conversation.title;
const { title: projectedCanonicalTitle } =
resolveAgentGUIConversationTitle(projectedTitle);
const titleDisplayPrompt =
resolveAgentGUIConversationTitleDisplayPrompt({
activation,
allowEmptyTitle: shouldUseOptimisticTitle,
title: projectedCanonicalTitle
});
const { title, titleFallback } = resolveAgentGUIConversationTitle(
resolveAgentGUIConversationBrowserFreeTitle({
activation,
allowEmptyTitle: shouldUseOptimisticTitle,
title: projectedCanonicalTitle
})
);
const titleLeadingMentionKind =
resolveAgentGUIConversationTitleLeadingMentionKind(
titleDisplayPrompt
) ?? conversation.titleLeadingMentionKind;
return {
...conversation,
sortTimeUnixMs: activationIsNewer
? activation.requestedAtUnixMs
: conversation.sortTimeUnixMs,
title,
titleLeadingMentionKind,
titleFallback,
updatedAtUnixMs: activationIsNewer
? activation.requestedAtUnixMs
: canonicalUpdatedAtUnixMs
};
}),
...pendingConversations
]
.filter((conversation) => {
if (query.conversationFilter) {
return matchesAgentGUIConversationSummaryFilter(
conversation,
normalizeAgentGUIConversationFilter(query.conversationFilter)
);
}
return (
conversation.provider === query.provider ||
isAgentGUIProviderUnresolved(conversation.provider)
);
})
.sort(
(left, right) =>
(right.sortTimeUnixMs ?? right.updatedAtUnixMs) -
(left.sortTimeUnixMs ?? left.updatedAtUnixMs) ||
left.id.localeCompare(right.id)
);
return {
conversations,
error: workspaceReconcile.errorMessage,
initialized:
workspaceReconcile.status === "ready" ||
workspaceReconcile.status === "failed" ||
workspaceReconcile.status === "unknown",
isLoading:
workspaceReconcile.status === "idle" ||
workspaceReconcile.status === "loading",
query,
queryKey: [
query.workspaceId,
query.userId,
query.provider,
query.sessionOrigin
].join("::")
};
}, [
agentTargets,
firstUserDisplayPromptsBySessionId,
pendingActivations,
query,
rootAgentSessionIdsAwaitingPlanImplementation,
rootAgentSessionIdsWithPendingInteractions,
sessions,
workspaceReconcile
]);
}
function pendingActivationsEqual(
left: readonly PendingActivationIntentRecord[],
right: readonly PendingActivationIntentRecord[]
): boolean {
return (
left.length === right.length &&
left.every((item, index) => item === right[index])
);
}
function consumerSessionsEqual(
left: ReturnType<typeof selectWorkspaceAgentConsumerSessions>,
right: ReturnType<typeof selectWorkspaceAgentConsumerSessions>
): boolean {
if (left.length !== right.length) return false;
return left.every((item, index) => {
const other = right[index];
return (
other !== undefined &&
item.session === other.session &&
item.activeTurn === other.activeTurn &&
item.latestTurn === other.latestTurn &&
item.pendingInteractions.length === other.pendingInteractions.length &&
item.pendingInteractions.every(
(interaction, interactionIndex) =>
interaction === other.pendingInteractions[interactionIndex]
) &&
item.displayStatus === other.displayStatus
);
});
}
function stringArraysEqual(
left: readonly string[],
right: readonly string[]
): boolean {
return (
left.length === right.length &&
left.every((item, index) => item === right[index])
);
}
@@ -0,0 +1,75 @@
import { renderHook } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import type { AgentActivityMessage } from "@tutti-os/agent-activity-core";
import { useAgentSessionControllerState } from "./useAgentSessionControllerState";
const ACTIVE_REF = {
agentSessionId: "session-1",
origin: "test",
workspaceId: "workspace-1"
};
function message(version: number): AgentActivityMessage {
return {
agentSessionId: "session-1",
kind: "text",
messageId: `message-${version}`,
occurredAtUnixMs: version,
payload: {},
role: "assistant",
turnId: "turn-1",
version
};
}
describe("useAgentSessionControllerState", () => {
it("does not infer older history from a streamed message version", () => {
const { result, rerender } = renderHook(
({ version }) =>
useAgentSessionControllerState(ACTIVE_REF, [message(version)]),
{ initialProps: { version: 1 } }
);
expect(result.current.activeSessionView).toMatchObject({
hasOlderMessages: false,
oldestLoadedVersion: 1
});
rerender({ version: 446 });
expect(result.current.activeSessionView).toMatchObject({
hasOlderMessages: false,
oldestLoadedVersion: 446
});
});
it("reads terminal older-page coverage from the canonical Engine window", () => {
const { result, rerender } = renderHook(
({ hasOlderMessages, messages, oldestLoadedVersion }) =>
useAgentSessionControllerState(ACTIVE_REF, messages, {
hasOlderMessages,
oldestLoadedVersion
}),
{
initialProps: {
hasOlderMessages: true,
messages: [message(446)],
oldestLoadedVersion: 446
}
}
);
expect(result.current.activeSessionView?.hasOlderMessages).toBe(true);
rerender({
hasOlderMessages: false,
messages: [message(1), message(446)],
oldestLoadedVersion: 1
});
expect(result.current.activeSessionView).toMatchObject({
hasOlderMessages: false,
oldestLoadedVersion: 1
});
});
});
@@ -0,0 +1,71 @@
import { useCallback } from "react";
import type {
AgentActivityMessage,
AgentActivitySessionMessageWindow
} from "@tutti-os/agent-activity-core";
import { type AgentSessionViewRef } from "./useAgentSessionPagingState";
import { useAgentSessionPagingState } from "./useAgentSessionPagingState";
export function useAgentSessionControllerState(
activeRef: AgentSessionViewRef,
canonicalMessages: readonly AgentActivityMessage[] = [],
canonicalWindow: AgentActivitySessionMessageWindow | null = null
) {
const paging = useAgentSessionPagingState();
const getAgentSessionView = useCallback(
(ref: AgentSessionViewRef) => {
const pagingEntry = paging.get(ref);
return pagingEntry
? {
...pagingEntry,
hasOlderMessages: false,
oldestLoadedVersion: null
}
: null;
},
[paging.get]
);
const deleteAgentSessionView = useCallback(
(ref: AgentSessionViewRef) => paging.remove(ref),
[paging.remove]
);
const setAgentSessionViewMessagesLoading = useCallback(
(ref: AgentSessionViewRef, value: boolean) =>
paging.flag("isLoadingMessages", ref, value),
[paging.flag]
);
const setAgentSessionViewOlderMessagesLoading = useCallback(
(ref: AgentSessionViewRef, value: boolean) =>
paging.flag("isLoadingOlderMessages", ref, value),
[paging.flag]
);
void paging.entries;
const storedActiveView = getAgentSessionView(activeRef);
const canonicalOldestVersion =
canonicalWindow?.oldestLoadedVersion ?? oldestVersion(canonicalMessages);
const activeSessionView =
storedActiveView === null && canonicalOldestVersion === null
? null
: {
...(storedActiveView ?? {
error: null,
isLoadingMessages: false,
isLoadingOlderMessages: false
}),
hasOlderMessages: canonicalWindow?.hasOlderMessages ?? false,
oldestLoadedVersion: canonicalOldestVersion
};
return {
activeSessionView,
deleteAgentSessionView,
getAgentSessionView,
setAgentSessionViewError: paging.setError,
setAgentSessionViewMessagesLoading,
setAgentSessionViewOlderMessagesLoading
};
}
function oldestVersion(messages: readonly AgentActivityMessage[]) {
const versions = messages.map((item) => item.version).filter(Number.isFinite);
return versions.length > 0 ? Math.min(...versions) : null;
}
@@ -0,0 +1,79 @@
import { useCallback, useRef, useState } from "react";
export interface AgentSessionViewRef {
workspaceId: string | null | undefined;
agentSessionId: string | null | undefined;
origin?: string | null;
}
export interface AgentSessionPagingEntry {
error: string | null;
isLoadingMessages: boolean;
isLoadingOlderMessages: boolean;
}
const EMPTY_ENTRY: AgentSessionPagingEntry = {
error: null,
isLoadingMessages: false,
isLoadingOlderMessages: false
};
export function useAgentSessionPagingState() {
const [entries, setEntries] = useState<
Record<string, AgentSessionPagingEntry>
>({});
const entriesRef = useRef(entries);
entriesRef.current = entries;
const update = useCallback(
(
ref: AgentSessionViewRef,
updater: (entry: AgentSessionPagingEntry) => AgentSessionPagingEntry
) => {
const key = sessionKey(ref);
if (!key) return;
setEntries((current) => {
const previous = current[key] ?? EMPTY_ENTRY;
const next = updater(previous);
return next === previous ? current : { ...current, [key]: next };
});
},
[]
);
const get = useCallback((ref: AgentSessionViewRef) => {
const key = sessionKey(ref);
return key ? (entriesRef.current[key] ?? null) : null;
}, []);
const remove = useCallback((ref: AgentSessionViewRef) => {
const key = sessionKey(ref);
if (!key) return;
setEntries((current) => {
if (!current[key]) return current;
const next = { ...current };
delete next[key];
return next;
});
}, []);
const flag = useCallback(
(
field: "isLoadingMessages" | "isLoadingOlderMessages",
ref: AgentSessionViewRef,
value: boolean
) => update(ref, (entry) => ({ ...entry, [field]: value })),
[update]
);
const setError = useCallback(
(ref: AgentSessionViewRef, error: string | null) =>
update(ref, (entry) => ({ ...entry, error })),
[update]
);
return { entries, flag, get, remove, setError };
}
function sessionKey(ref: AgentSessionViewRef): string | null {
const workspaceId = ref.workspaceId?.trim() ?? "";
const origin = ref.origin?.trim() ?? "";
const agentSessionId = ref.agentSessionId?.trim() ?? "";
return workspaceId && origin && agentSessionId
? `${workspaceId}::${origin}::${agentSessionId}`
: null;
}
@@ -0,0 +1,66 @@
import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react";
import { cn } from "../../../../../../app/renderer/lib/utils";
export type CanvasNodeGhostIconButtonProps = Omit<
ButtonHTMLAttributes<HTMLButtonElement>,
"type"
> & {
children: ReactNode;
/**
* When true (default), pointer/click stopPropagation — keeps React Flow from stealing node chrome
* interactions. Set false for Radix `DropdownMenuTrigger asChild` and other portaled triggers.
*/
stopsEventPropagation?: boolean;
};
/**
* Ghost icon control for canvas node chrome.
*/
export const CanvasNodeGhostIconButton = forwardRef<
HTMLButtonElement,
CanvasNodeGhostIconButtonProps
>(function CanvasNodeGhostIconButton(
{
className,
children,
onPointerDown,
onMouseDown,
onClick,
stopsEventPropagation = true,
...rest
},
ref
) {
"use memo";
return (
<button
ref={ref}
{...rest}
type="button"
className={cn(
"nodrag inline-flex h-7 min-h-7 w-7 min-w-7 shrink-0 items-center justify-center rounded-[4px] border border-transparent bg-transparent p-0 text-[var(--text-secondary)] transition-[background-color,color,border-color,opacity] duration-200 ease-in-out [-webkit-app-region:no-drag] hover:border-transparent hover:bg-[var(--transparency-block)] hover:text-[var(--text-primary)] active:bg-[var(--transparency-block-active)] active:text-[var(--text-primary)] disabled:cursor-not-allowed disabled:opacity-45 focus-visible:outline-2 focus-visible:outline-[color:color-mix(in_srgb,var(--tsh-shell-accent)_80%,white)] focus-visible:outline-offset-2 [&_svg]:block [&_svg]:h-3.5 [&_svg]:w-3.5 [&_svg]:shrink-0 [&_svg:not([data-nexight-chrome-glyph=fill]):not([data-tutti-chrome-glyph=fill])]:fill-none [&_svg:not([data-nexight-chrome-glyph=fill]):not([data-tutti-chrome-glyph=fill])]:stroke-current data-[menu-open=true]:border-transparent data-[menu-open=true]:bg-[var(--transparency-block)] data-[menu-open=true]:text-[var(--text-primary)] data-[menu-open=true]:hover:bg-[var(--transparency-block-active)]",
className
)}
onPointerDown={(event) => {
if (stopsEventPropagation) {
event.stopPropagation();
}
onPointerDown?.(event);
}}
onMouseDown={(event) => {
if (stopsEventPropagation) {
event.stopPropagation();
}
onMouseDown?.(event);
}}
onClick={(event) => {
if (stopsEventPropagation) {
event.stopPropagation();
}
onClick?.(event);
}}
>
{children}
</button>
);
});