mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 11:20:42 +01:00
perf(runner): omit unused launch context from tool authority reads (#17591)
Omit nine unused named launch values from the unlocked Native authority query. Preserve complete wake payloads, other context, result receipts, stored input, and locked execution and mutation readers. Verified with full build and typecheck, 2,359 affected tests across 20 suites, real PostgreSQL byte/retention/cancellation checks, a counterfactual, complete exact-head CI, and Greptile 5/5 Medium with no unresolved comments. An existing cleanup scheduling test passed on CI retry after its suite passed locally with both base and branch source. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
bd77ffe78d
commit
2e47c42fad
3 files changed
+45
-2
No files matched your search
+4
-1
@@ -127,7 +127,10 @@ Native tool authority lookups select current run identity, status, invocation,
|
||||
responsible user, context, and receipt data. Saved profiles and output excerpts
|
||||
stay in storage and are omitted from that read. Task and agent binding, current
|
||||
status, budget, and task privacy checks retain their existing predicates.
|
||||
Mutation transactions still lock and read complete run records.
|
||||
Mutation transactions still lock and read complete run records. The unlocked
|
||||
authority read also omits named launch renderings and preparation data that its
|
||||
tools do not consume. Full wake payloads, other context, and result receipts
|
||||
remain available; the complete saved context stays in storage for execution.
|
||||
|
||||
WebSocket delivery uses one fresh authorization batch for an event's task and
|
||||
run bindings. When both bindings name the same task, the batch reads its current
|
||||
|
||||
@@ -244,6 +244,42 @@ describe("PaperclipRunnerToolAuthority", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("reads tool authority without transferring unused launch renderings", async () => {
|
||||
const binding = { companyId, agentId, issueId, runId };
|
||||
const authority = new PaperclipRunnerToolAuthority(db, binding);
|
||||
const contextCall = { tool: "get_task_context", callId: "launch-context-read", arguments: {} };
|
||||
const apiCall = { tool: "search_api", callId: "launch-api-read", arguments: { query: "read task" } };
|
||||
const expectedContext = await authority.execute(contextCall), expectedApi = await authority.execute(apiCall);
|
||||
const [original] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, runId));
|
||||
const payload = "retained-launch-rendering🙂".repeat(8192);
|
||||
const context = { ...original.contextSnapshot, executionContinuation: { savedInput: payload }, paperclipTaskMarkdown: payload,
|
||||
paperclipTaskMarkdownCompact: payload, paperclipTaskMarkdownAssignment: payload, paperclipTaskMarkdownAssignmentCompact: payload,
|
||||
paperclipWorkspace: { savedInput: payload }, paperclipEnvironment: { savedInput: payload },
|
||||
paperclipContinuationSummary: payload, paperclipWakeComment: payload };
|
||||
await db.update(heartbeatRuns).set({ contextSnapshot: context }).where(eq(heartbeatRuns.id, runId));
|
||||
const [expanded] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, runId));
|
||||
const observed = createDb(temporary!.connectionString), events: Array<Record<string, unknown>> = [], now = Date.now();
|
||||
const capture = createDatabaseResponseCapture({ start: now - 100, end: now + 59_000 }, event => events.push(event));
|
||||
installDatabaseResponseCapture(observed, { heartbeatRuns, issues, agents, companies }, capture);
|
||||
try {
|
||||
const reader = new PaperclipRunnerToolAuthority(observed, binding);
|
||||
const actualContext = await reader.execute(contextCall), actualApi = await reader.execute(apiCall);
|
||||
capture.flush();
|
||||
const reads = events.filter(event => event.table === "heartbeat_runs" && event.operation === "select");
|
||||
expect(reads.length).toBeGreaterThan(0);
|
||||
expect(reads.reduce((bytes, event) => bytes + Number(event.responseValueBytes), 0)).toBeLessThan(16 * 1024);
|
||||
expect(actualContext).toEqual(expectedContext);
|
||||
expect(actualApi).toEqual(expectedApi);
|
||||
expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, runId)))[0]).toEqual(expanded);
|
||||
await db.update(heartbeatRuns).set({ status: "cancelled" }).where(eq(heartbeatRuns.id, runId));
|
||||
await expect(reader.execute(contextCall)).rejects.toThrow("paperclip_runner_tool_binding_not_authorized");
|
||||
await expect(reader.execute(apiCall)).rejects.toThrow("paperclip_runner_tool_binding_not_authorized");
|
||||
} finally {
|
||||
capture.flush(); await observed.$client.end();
|
||||
await db.update(heartbeatRuns).set({ contextSnapshot: original.contextSnapshot, status: original.status }).where(eq(heartbeatRuns.id, runId));
|
||||
}
|
||||
});
|
||||
|
||||
it("preserves platform receipt provenance in both mixed task history readers", async () => {
|
||||
const at = new Date("2026-10-10T12:00:00Z");
|
||||
const commentIds = Array.from({ length: 4 }, () => randomUUID());
|
||||
|
||||
@@ -1235,7 +1235,11 @@ export class PaperclipRunnerToolAuthority {
|
||||
status: heartbeatRuns.status,
|
||||
invocationSource: heartbeatRuns.invocationSource,
|
||||
responsibleUserId: heartbeatRuns.responsibleUserId,
|
||||
contextSnapshot: heartbeatRuns.contextSnapshot,
|
||||
contextSnapshot: sql<Record<string, unknown> | null>`case when jsonb_typeof(${heartbeatRuns.contextSnapshot}) = 'object'
|
||||
then ${heartbeatRuns.contextSnapshot} - array['executionContinuation', 'paperclipTaskMarkdown',
|
||||
'paperclipTaskMarkdownCompact', 'paperclipTaskMarkdownAssignment', 'paperclipTaskMarkdownAssignmentCompact',
|
||||
'paperclipWorkspace', 'paperclipEnvironment', 'paperclipContinuationSummary', 'paperclipWakeComment']
|
||||
else ${heartbeatRuns.contextSnapshot} end`,
|
||||
resultJson: heartbeatRuns.resultJson,
|
||||
} })
|
||||
.from(heartbeatRuns)
|
||||
|
||||
Reference in new issue
Block a user