mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 09:07:46 +02:00
Persist optional connection instructions and deliver authorized snapshots to agent execution prompts. Keep provider templates with each app definition, preserve edits and opt-outs, and replace sessions when guidance or access changes. Use shared production settings across setup and Permissions, with source visibility in agent Instructions. Add the initial memory-provider defaults and managed Honcho workspace configuration. Include migration 0298 and regression coverage for generic providers, runtime delivery, authorization, and catalog regeneration. Co-Authored-By: Paperclip <noreply@paperclip.ing>
122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { CONNECTION_INTENT_AGENT_GUIDANCE } from "@paperclipai/shared";
|
|
import { execute } from "./execute.js";
|
|
|
|
const guardedFetchMock = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("./remote-fetch.js", () => ({
|
|
guardedHttpAdapterFetch: guardedFetchMock,
|
|
}));
|
|
|
|
afterEach(() => {
|
|
guardedFetchMock.mockReset();
|
|
});
|
|
|
|
describe("http adapter execute", () => {
|
|
it("delivers the complete runtime connection descriptor and shared guidance", async () => {
|
|
const onDispatch = vi.fn();
|
|
guardedFetchMock.mockImplementation(async (_url: string, init?: RequestInit) => {
|
|
expect(onDispatch).toHaveBeenCalledOnce();
|
|
const body = JSON.parse(String(init?.body)) as Record<string, unknown>;
|
|
expect(body.paperclipRuntimeTools).toEqual({
|
|
version: 1,
|
|
guidance: CONNECTION_INTENT_AGENT_GUIDANCE,
|
|
mcpEndpoint: "https://paperclip.test/mcp/runtime-tools",
|
|
rest: {
|
|
connectionsSearch: "https://paperclip.test/runtime-tools/connections/search",
|
|
connectionRequest: "https://paperclip.test/runtime-tools/connections/request",
|
|
},
|
|
bearerToken: "run-token",
|
|
expiresAt: "2026-08-26T15:00:00.000Z",
|
|
tools: ["connections_search", "connection_request"],
|
|
});
|
|
return new Response(null, { status: 204 });
|
|
});
|
|
|
|
await execute({
|
|
runId: "run-1",
|
|
agent: {
|
|
id: "agent-1",
|
|
companyId: "company-1",
|
|
name: "Agent",
|
|
adapterType: "http",
|
|
adapterConfig: {},
|
|
},
|
|
runtime: {
|
|
sessionId: null,
|
|
sessionParams: null,
|
|
sessionDisplayId: null,
|
|
taskKey: null,
|
|
},
|
|
config: { url: "https://example.test/webhook" },
|
|
context: {},
|
|
runtimeTools: {
|
|
version: 1,
|
|
guidance: CONNECTION_INTENT_AGENT_GUIDANCE,
|
|
mcpEndpoint: "https://paperclip.test/mcp/runtime-tools",
|
|
rest: {
|
|
connectionsSearch: "https://paperclip.test/runtime-tools/connections/search",
|
|
connectionRequest: "https://paperclip.test/runtime-tools/connections/request",
|
|
},
|
|
bearerToken: "run-token",
|
|
expiresAt: "2026-08-26T15:00:00.000Z",
|
|
tools: ["connections_search", "connection_request"],
|
|
},
|
|
onLog: async () => {},
|
|
onDispatch,
|
|
});
|
|
|
|
expect(guardedFetchMock).toHaveBeenCalledOnce();
|
|
expect(onDispatch).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("sends the server snapshot instead of a configured payload instruction block", async () => {
|
|
const snapshot = { text: "Use the release handbook.", digest: "server-digest" };
|
|
guardedFetchMock.mockImplementation(async (_url: string, init?: RequestInit) => {
|
|
expect(JSON.parse(String(init?.body)).connectionInstructions).toEqual(snapshot);
|
|
return new Response(null, { status: 204 });
|
|
});
|
|
await execute({ runId: "run-1", agent: { id: "agent-1", companyId: "company-1", name: "Agent", adapterType: "http", adapterConfig: {} }, runtime: { sessionId: null, sessionParams: null, sessionDisplayId: null, taskKey: null },
|
|
config: { url: "https://example.test/webhook", payloadTemplate: { connectionInstructions: { text: "forged" } } }, context: { connectionInstructions: snapshot }, onLog: async () => {},
|
|
});
|
|
expect(guardedFetchMock).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("reports configured request timeout as timed_out", async () => {
|
|
guardedFetchMock.mockImplementation(
|
|
(_url: string, init?: RequestInit) => new Promise((_resolve, reject) => {
|
|
init?.signal?.addEventListener("abort", () => {
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
});
|
|
}),
|
|
);
|
|
|
|
const result = await execute({
|
|
runId: "run-1",
|
|
agent: {
|
|
id: "agent-1",
|
|
companyId: "company-1",
|
|
name: "Agent",
|
|
adapterType: "http",
|
|
adapterConfig: {},
|
|
},
|
|
runtime: {
|
|
sessionId: null,
|
|
sessionParams: null,
|
|
sessionDisplayId: null,
|
|
taskKey: null,
|
|
},
|
|
config: {
|
|
url: "https://example.test/webhook",
|
|
timeoutMs: 1,
|
|
},
|
|
context: {},
|
|
onLog: async () => {},
|
|
});
|
|
|
|
expect(result.timedOut).toBe(true);
|
|
expect(result.errorCode).toBe("timeout");
|
|
expect(result.errorMessage).toContain("timed out after 1ms");
|
|
});
|
|
});
|