源码快照 @ 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
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:
+223
@@ -0,0 +1,223 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type {
|
||||
AgentActivityComposerOptions,
|
||||
AgentActivitySnapshot
|
||||
} from "@tutti-os/agent-activity-core";
|
||||
import {
|
||||
composerOptionsForTarget,
|
||||
composerOptionsLoadingForTarget,
|
||||
ownerDeviceLabelForConversation,
|
||||
resolveAgentGUIProviderRailTargetSelection,
|
||||
targetConnectionForAgentGUIView
|
||||
} from "./agentGuiController.providerHelpers";
|
||||
import { createSharedAgentGUIAgentTarget } from "../../../agentTargets";
|
||||
import type { AgentGUIComposerTargetData } from "./agentGuiController.composerPresentation";
|
||||
import type { AgentGUIConversationSummary } from "../model/agentGuiConversationModel";
|
||||
|
||||
const target = {
|
||||
agentTargetId: "target-1"
|
||||
} as AgentGUIComposerTargetData;
|
||||
|
||||
describe("composer options target state", () => {
|
||||
it("reports loading only before the target has cached options", () => {
|
||||
const loadingSnapshot = snapshot({
|
||||
composerOptionsLoadStatusByTargetKey: { "target-1": "loading" }
|
||||
});
|
||||
expect(
|
||||
composerOptionsLoadingForTarget({ snapshot: loadingSnapshot, target })
|
||||
).toBe(true);
|
||||
expect(
|
||||
composerOptionsForTarget({ snapshot: loadingSnapshot, target })
|
||||
).toBe(null);
|
||||
|
||||
const cachedOptions = {} as AgentActivityComposerOptions;
|
||||
const refreshingSnapshot = snapshot({
|
||||
composerOptionsByTargetKey: { "target-1": cachedOptions },
|
||||
composerOptionsLoadStatusByTargetKey: { "target-1": "loading" }
|
||||
});
|
||||
expect(
|
||||
composerOptionsLoadingForTarget({ snapshot: refreshingSnapshot, target })
|
||||
).toBe(false);
|
||||
expect(
|
||||
composerOptionsForTarget({ snapshot: refreshingSnapshot, target })
|
||||
).toBe(cachedOptions);
|
||||
});
|
||||
|
||||
it("does not leave the target loading after a request error", () => {
|
||||
expect(
|
||||
composerOptionsLoadingForTarget({
|
||||
snapshot: snapshot({
|
||||
composerOptionsLoadStatusByTargetKey: { "target-1": "error" }
|
||||
}),
|
||||
target
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("provider rail target selection", () => {
|
||||
it("keeps an active conversation that belongs to the selected target", () => {
|
||||
expect(
|
||||
resolveAgentGUIProviderRailTargetSelection({
|
||||
activeConversation: conversation(
|
||||
"claude-session",
|
||||
"local:claude-code",
|
||||
"claude-code"
|
||||
),
|
||||
nextFilter: {
|
||||
kind: "agentTarget",
|
||||
agentTargetId: "local:claude-code"
|
||||
}
|
||||
})
|
||||
).toBe("keep-active-conversation");
|
||||
});
|
||||
|
||||
it("opens the selected target home when the active conversation belongs elsewhere", () => {
|
||||
expect(
|
||||
resolveAgentGUIProviderRailTargetSelection({
|
||||
activeConversation: conversation(
|
||||
"codex-session",
|
||||
"local:codex",
|
||||
"codex"
|
||||
),
|
||||
nextFilter: {
|
||||
kind: "agentTarget",
|
||||
agentTargetId: "local:claude-code"
|
||||
}
|
||||
})
|
||||
).toBe("open-home-composer");
|
||||
});
|
||||
|
||||
it("opens the selected target home when there is no active conversation", () => {
|
||||
expect(
|
||||
resolveAgentGUIProviderRailTargetSelection({
|
||||
activeConversation: null,
|
||||
nextFilter: {
|
||||
kind: "agentTarget",
|
||||
agentTargetId: "local:claude-code"
|
||||
}
|
||||
})
|
||||
).toBe("open-home-composer");
|
||||
});
|
||||
|
||||
it("opens the selected target home when it has no agent target id", () => {
|
||||
expect(
|
||||
resolveAgentGUIProviderRailTargetSelection({
|
||||
activeConversation: conversation(
|
||||
"codex-session",
|
||||
"local:codex",
|
||||
"codex"
|
||||
),
|
||||
nextFilter: { kind: "all" }
|
||||
})
|
||||
).toBe("open-home-composer");
|
||||
});
|
||||
});
|
||||
|
||||
describe("conversation owner device label", () => {
|
||||
it("uses the exact active conversation target instead of the selected composer target", () => {
|
||||
const deviceA = createSharedAgentGUIAgentTarget({
|
||||
agentTargetId: "target-a",
|
||||
label: "Shared A",
|
||||
ownerDeviceLabel: "Device A",
|
||||
provider: "codex",
|
||||
sharedAgentId: "shared-a"
|
||||
});
|
||||
const deviceB = createSharedAgentGUIAgentTarget({
|
||||
agentTargetId: "target-b",
|
||||
label: "Shared B",
|
||||
ownerDeviceLabel: "Device B",
|
||||
provider: "codex",
|
||||
sharedAgentId: "shared-b"
|
||||
});
|
||||
|
||||
expect(
|
||||
ownerDeviceLabelForConversation(
|
||||
conversation("session-b", "target-b", "codex"),
|
||||
[deviceA, deviceB]
|
||||
)
|
||||
).toBe("Device B");
|
||||
});
|
||||
|
||||
it("fails closed when the active conversation target is unavailable", () => {
|
||||
expect(
|
||||
ownerDeviceLabelForConversation(
|
||||
conversation("session-b", "missing", "codex"),
|
||||
[
|
||||
createSharedAgentGUIAgentTarget({
|
||||
agentTargetId: "target-a",
|
||||
label: "Shared A",
|
||||
ownerDeviceLabel: "Device A",
|
||||
provider: "codex",
|
||||
sharedAgentId: "shared-a"
|
||||
})
|
||||
]
|
||||
)
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("target connection selection", () => {
|
||||
const selectedTarget = createSharedAgentGUIAgentTarget({
|
||||
agentTargetId: "target-home",
|
||||
label: "Shared Home",
|
||||
ownerDeviceLabel: "Home Device",
|
||||
provider: "codex",
|
||||
sharedAgentId: "shared-home"
|
||||
});
|
||||
|
||||
it("uses the selected Home target before a Session exists", () => {
|
||||
expect(
|
||||
targetConnectionForAgentGUIView({
|
||||
activeConversation: null,
|
||||
selectedTarget,
|
||||
targets: [selectedTarget]
|
||||
})
|
||||
).toEqual({
|
||||
agentTargetId: "target-home",
|
||||
ownerDeviceLabel: "Home Device"
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves the exact Session target when its presentation is stale", () => {
|
||||
expect(
|
||||
targetConnectionForAgentGUIView({
|
||||
activeConversation: conversation(
|
||||
"session-1",
|
||||
"target-missing",
|
||||
"codex"
|
||||
),
|
||||
selectedTarget,
|
||||
targets: [selectedTarget]
|
||||
})
|
||||
).toEqual({ agentTargetId: "target-missing", ownerDeviceLabel: null });
|
||||
});
|
||||
});
|
||||
|
||||
function snapshot(
|
||||
overrides: Partial<AgentActivitySnapshot>
|
||||
): AgentActivitySnapshot {
|
||||
return {
|
||||
workspaceId: "workspace-1",
|
||||
sessions: [],
|
||||
presences: [],
|
||||
sessionMessagesById: {},
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function conversation(
|
||||
id: string,
|
||||
agentTargetId: string,
|
||||
provider: AgentGUIConversationSummary["provider"]
|
||||
): AgentGUIConversationSummary {
|
||||
return {
|
||||
id,
|
||||
agentTargetId,
|
||||
cwd: "/repo",
|
||||
provider,
|
||||
status: "completed",
|
||||
title: id,
|
||||
updatedAtUnixMs: 1
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user