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
244 lines
7.7 KiB
TypeScript
244 lines
7.7 KiB
TypeScript
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();
|
|
});
|
|
});
|