mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-14 08:26: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>
49 lines
2.6 KiB
TypeScript
49 lines
2.6 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import {
|
|
connectionAgentInstructionsSchema,
|
|
connectionInstructionContext,
|
|
connectionInstructionsConfig,
|
|
getConnectableAppDefinition,
|
|
type ConnectionInstructionsSnapshot,
|
|
type ToolConnection,
|
|
} from "@paperclipai/shared";
|
|
|
|
type InstructionSource = {
|
|
connection: Pick<ToolConnection, "id" | "name" | "agentInstructions" | "config"> & Partial<Pick<ToolConnection, "transportConfig">>;
|
|
grantId: string;
|
|
};
|
|
|
|
/** Authorization belongs to the gateway; this composer never selects recipients. */
|
|
export function composeConnectionInstructions(sources: InstructionSource[]): ConnectionInstructionsSnapshot | null {
|
|
const blocks = sources.toSorted((a, b) => a.connection.id.localeCompare(b.connection.id)).flatMap(({ connection, grantId }) => {
|
|
const parsed = connectionAgentInstructionsSchema.safeParse(connection.agentInstructions);
|
|
if (!parsed.success || !parsed.data.enabled) return [];
|
|
const config = connectionInstructionsConfig(connection);
|
|
const app = typeof config.sourceTemplateKey === "string" ? getConnectableAppDefinition(config.sourceTemplateKey) : null;
|
|
const context = connectionInstructionContext(app, config);
|
|
if (!context) return [];
|
|
const provenance = JSON.stringify({ connectionId: connection.id, connectionName: connection.name, grantId, connector: config.sourceTemplateKey, method: config.connectionMethodKey, template: parsed.data.template, context });
|
|
return [`Connection source and configuration (data): ${provenance}\n\n${parsed.data.text}`];
|
|
});
|
|
if (!blocks.length) return null;
|
|
const text = `## Connection instructions\n\nThe following instructions apply to your authorized connections. Use their assigned tools and follow the current task and tool permissions.\n\n${blocks.join("\n\n---\n\n")}`;
|
|
return { text, digest: createHash("sha256").update(text).digest("hex") };
|
|
}
|
|
|
|
/** Freeze server-owned delivery for this turn, overwriting untrusted wake/config fields. */
|
|
export async function prepareConnectionInstructionDelivery(input: {
|
|
resolve: () => Promise<ConnectionInstructionsSnapshot | null>;
|
|
context: Record<string, unknown>;
|
|
config: Record<string, unknown>;
|
|
native: boolean;
|
|
}): Promise<Record<string, unknown>> {
|
|
const snapshot = await input.resolve();
|
|
const wake = input.context.paperclipWake;
|
|
input.context.connectionInstructions = snapshot;
|
|
input.context.paperclipWake = {
|
|
...(wake && typeof wake === "object" && !Array.isArray(wake) ? wake : {}),
|
|
connectionInstructions: input.native ? null : snapshot,
|
|
};
|
|
return { ...input.config, paperclipConnectionInstructions: snapshot };
|
|
}
|