源码快照 @ 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:
+243
@@ -0,0 +1,243 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
appendAgentGUIComposerConnector,
|
||||
appendAgentGUIComposerFiles,
|
||||
appendAgentGUIComposerPrompt,
|
||||
resolveAgentGUIComposerAppendRequest,
|
||||
type AgentGUIComposerAppendRequest
|
||||
} from "./useAgentGUIComposerAppendRequest";
|
||||
import {
|
||||
agentComposerDraftFiles,
|
||||
agentComposerDraftConnectors,
|
||||
agentComposerDraftPrompt,
|
||||
agentComposerDraftToPromptContent,
|
||||
buildAgentComposerDraft
|
||||
} from "../model/agentComposerDraft";
|
||||
|
||||
const existingFile = {
|
||||
id: "existing",
|
||||
name: "existing.txt",
|
||||
path: "/tmp/existing.txt"
|
||||
};
|
||||
const archivedContextFile = {
|
||||
id: "archived-context-1",
|
||||
mimeType: "text/plain",
|
||||
name: "context.txt",
|
||||
path: "/tmp/context.txt",
|
||||
sizeBytes: 42
|
||||
};
|
||||
|
||||
describe("appendAgentGUIComposerConnector", () => {
|
||||
it("selects a connector semantically without changing the typed prompt", () => {
|
||||
const draft = buildAgentComposerDraft({ prompt: "Summarize my workspace" });
|
||||
const next = appendAgentGUIComposerConnector(draft, "notion");
|
||||
|
||||
expect(agentComposerDraftPrompt(next)).toBe("Summarize my workspace");
|
||||
expect(agentComposerDraftConnectors(next)).toEqual([
|
||||
{ connectorKey: "notion" }
|
||||
]);
|
||||
expect(appendAgentGUIComposerConnector(next, "notion")).toBe(next);
|
||||
expect(
|
||||
agentComposerDraftToPromptContent({ draft: next, skills: [] })
|
||||
).toEqual([
|
||||
{ type: "text", text: "Summarize my workspace" },
|
||||
{ type: "connector", connectorKey: "notion" }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("appendAgentGUIComposerFiles", () => {
|
||||
it("preserves the public files contract and appends landed attachments", () => {
|
||||
const draft = buildAgentComposerDraft({
|
||||
files: [existingFile],
|
||||
prompt: "Fix this element"
|
||||
});
|
||||
const next = appendAgentGUIComposerFiles(draft, [archivedContextFile]);
|
||||
|
||||
expect(agentComposerDraftPrompt(next)).toBe("Fix this element");
|
||||
expect(agentComposerDraftFiles(next)).toEqual([
|
||||
existingFile,
|
||||
archivedContextFile
|
||||
]);
|
||||
});
|
||||
|
||||
it("deduplicates appended attachments by id", () => {
|
||||
const draft = buildAgentComposerDraft({
|
||||
files: [archivedContextFile],
|
||||
prompt: ""
|
||||
});
|
||||
|
||||
expect(
|
||||
agentComposerDraftFiles(
|
||||
appendAgentGUIComposerFiles(draft, [archivedContextFile])
|
||||
)
|
||||
).toEqual([archivedContextFile]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("appendAgentGUIComposerPrompt", () => {
|
||||
it("appends a host-provided inline mention without creating an attachment", () => {
|
||||
const draft = buildAgentComposerDraft({ prompt: "Fix this" });
|
||||
const mention =
|
||||
"[@a](mention://browser-element/browser-element%3A1?path=%2Ftmp%2Felement.txt&tag=a&workspaceId=workspace-1)";
|
||||
const next = appendAgentGUIComposerPrompt(draft, mention);
|
||||
|
||||
expect(agentComposerDraftPrompt(next)).toBe(`Fix this ${mention} `);
|
||||
expect(agentComposerDraftFiles(next)).toHaveLength(0);
|
||||
expect(appendAgentGUIComposerPrompt(next, mention)).toBe(next);
|
||||
});
|
||||
|
||||
it("keeps multiple browser element mentions in one text-only message", () => {
|
||||
const first =
|
||||
"[@a](mention://browser-element/browser-element%3A1?path=%2Ftmp%2Fa.txt&tag=a&workspaceId=workspace-1)";
|
||||
const second =
|
||||
"[@div](mention://browser-element/browser-element%3A2?path=%2Ftmp%2Fdiv.txt&tag=div&workspaceId=workspace-1)";
|
||||
const withFirst = appendAgentGUIComposerPrompt(
|
||||
buildAgentComposerDraft({ prompt: "Review these" }),
|
||||
first
|
||||
);
|
||||
const withBoth = appendAgentGUIComposerPrompt(withFirst, second);
|
||||
|
||||
expect(agentComposerDraftPrompt(withBoth)).toBe(
|
||||
`Review these ${first} ${second} `
|
||||
);
|
||||
expect(
|
||||
agentComposerDraftToPromptContent({ draft: withBoth, skills: [] })
|
||||
).toEqual([
|
||||
{
|
||||
type: "text",
|
||||
text: `Review these ${first} ${second}`
|
||||
}
|
||||
]);
|
||||
expect(agentComposerDraftFiles(withBoth)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAgentGUIComposerAppendRequest", () => {
|
||||
it("routes a connector selection to the exact active draft once", () => {
|
||||
const request: AgentGUIComposerAppendRequest = {
|
||||
agentSessionId: "session-1",
|
||||
connectorKey: "notion",
|
||||
sequence: 9
|
||||
};
|
||||
const resolved = resolveAgentGUIComposerAppendRequest({
|
||||
activeConversationId: "session-1",
|
||||
draftByScopeKey: {
|
||||
"session:session-1": buildAgentComposerDraft({ prompt: "Keep me" })
|
||||
},
|
||||
handledSequence: null,
|
||||
request
|
||||
});
|
||||
|
||||
expect(agentComposerDraftPrompt(resolved!.nextDraft)).toBe("Keep me");
|
||||
expect(agentComposerDraftConnectors(resolved!.nextDraft)).toEqual([
|
||||
{ connectorKey: "notion" }
|
||||
]);
|
||||
expect(
|
||||
resolveAgentGUIComposerAppendRequest({
|
||||
activeConversationId: "session-1",
|
||||
draftByScopeKey: { "session:session-1": resolved!.nextDraft },
|
||||
handledSequence: 9,
|
||||
request
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("waits for the exact target session before appending a routed prompt", () => {
|
||||
const request: AgentGUIComposerAppendRequest = {
|
||||
agentSessionId: "source-session",
|
||||
prompt: "Modify the managed issue",
|
||||
sequence: 8
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveAgentGUIComposerAppendRequest({
|
||||
activeConversationId: "other-session",
|
||||
draftByScopeKey: {
|
||||
"session:other-session": buildAgentComposerDraft({
|
||||
prompt: "Keep this draft"
|
||||
})
|
||||
},
|
||||
handledSequence: null,
|
||||
request
|
||||
})
|
||||
).toBeNull();
|
||||
|
||||
const resolved = resolveAgentGUIComposerAppendRequest({
|
||||
activeConversationId: "source-session",
|
||||
draftByScopeKey: {
|
||||
"session:source-session": buildAgentComposerDraft({
|
||||
prompt: "Existing source draft"
|
||||
})
|
||||
},
|
||||
handledSequence: null,
|
||||
request
|
||||
});
|
||||
expect(agentComposerDraftPrompt(resolved!.nextDraft)).toBe(
|
||||
"Existing source draft Modify the managed issue "
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps legacy external file requests as draft attachments", () => {
|
||||
const request: AgentGUIComposerAppendRequest = {
|
||||
files: [archivedContextFile],
|
||||
sequence: 6
|
||||
};
|
||||
|
||||
const resolved = resolveAgentGUIComposerAppendRequest({
|
||||
activeConversationId: "session-1",
|
||||
draftByScopeKey: {
|
||||
"session:session-1": buildAgentComposerDraft({ prompt: "Keep me" })
|
||||
},
|
||||
handledSequence: null,
|
||||
request
|
||||
});
|
||||
|
||||
expect(agentComposerDraftPrompt(resolved!.nextDraft)).toBe("Keep me");
|
||||
expect(agentComposerDraftFiles(resolved!.nextDraft)).toEqual([
|
||||
archivedContextFile
|
||||
]);
|
||||
});
|
||||
|
||||
it("resolves each request sequence once against the active draft scope", () => {
|
||||
const initialDraft = buildAgentComposerDraft({ prompt: "Keep me" });
|
||||
const browserElementMention =
|
||||
"[@a](mention://browser-element/browser-element%3A1?path=%2Ftmp%2Fbrowser-element.txt&tag=a&workspaceId=workspace-1)";
|
||||
const request: AgentGUIComposerAppendRequest = {
|
||||
prompt: browserElementMention,
|
||||
sequence: 7
|
||||
};
|
||||
|
||||
const resolved = resolveAgentGUIComposerAppendRequest({
|
||||
activeConversationId: "session-1",
|
||||
draftByScopeKey: { "session:session-1": initialDraft },
|
||||
handledSequence: null,
|
||||
request
|
||||
});
|
||||
|
||||
expect(resolved?.draftKey).toBe("session:session-1");
|
||||
expect(agentComposerDraftPrompt(resolved!.nextDraft)).toContain(
|
||||
"mention://browser-element/"
|
||||
);
|
||||
expect(agentComposerDraftFiles(resolved!.nextDraft)).toHaveLength(0);
|
||||
expect(
|
||||
agentComposerDraftToPromptContent({
|
||||
draft: resolved!.nextDraft,
|
||||
skills: []
|
||||
})
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
type: "text",
|
||||
text: expect.stringContaining("mention://browser-element/")
|
||||
})
|
||||
]);
|
||||
expect(
|
||||
resolveAgentGUIComposerAppendRequest({
|
||||
activeConversationId: "session-1",
|
||||
draftByScopeKey: { "session:session-1": resolved!.nextDraft },
|
||||
handledSequence: 7,
|
||||
request
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user