mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-29 04:25:52 +01:00
fix(runner): discover assigned tools when direct catalogs exceed limits (#14218)
Keep assigned app tools accessible when the combined Runner catalog exceeds its operation or byte limits. Reserve task and completion tools, then expose bounded discovery and call tools for large catalogs. Fetch oversized schemas in reauthorized chunks without blocking later search results. Retain task ownership, work-mode restrictions, pinned assignments, current gateway authorization, approvals, and audit. Small catalogs stay direct. Validation: 46 focused server tests, two Runner capacity tests, local typecheck/build, 52 passing CI checks, and Greptile 5/5 with no open findings. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
640dee1802
commit
5ee9e751fb
9 files changed
+319
-12
No files matched your search
@@ -52,6 +52,7 @@ export * from "./drivers/acpx/sidecar-protocol.js";
|
||||
export * from "./drivers/runner-tool-bridge.js";
|
||||
export {
|
||||
createRunnerdCodexTransport,
|
||||
runnerCodexDynamicToolsFit,
|
||||
defaultCapabilityRunnerdBinary,
|
||||
readRunnerdArtifactBinding,
|
||||
drainRetainedRunnerdMaintenanceOperations,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { runnerCodexDynamicToolsFit } from "./runnerd-codex-transport.js";
|
||||
|
||||
function tools(count: number, description = "Read a fixture") {
|
||||
return Array.from({ length: count }, (_, i) => ({
|
||||
name: `fixture_${i}`, description, inputSchema: { type: "object" },
|
||||
}));
|
||||
}
|
||||
|
||||
it("reserves completion tools at the runner's operation boundary", () => {
|
||||
expect(runnerCodexDynamicToolsFit(tools(254))).toBe(true);
|
||||
expect(runnerCodexDynamicToolsFit(tools(255))).toBe(false);
|
||||
});
|
||||
|
||||
it("accounts for UTF-8 catalog bytes even below the operation limit", () => {
|
||||
expect(runnerCodexDynamicToolsFit(tools(200, "x".repeat(1000)))).toBe(true);
|
||||
expect(runnerCodexDynamicToolsFit(tools(200, "🙂".repeat(1000)))).toBe(false);
|
||||
});
|
||||
@@ -3045,6 +3045,17 @@ export function authorizedToolSetForProvider(
|
||||
return authorizedToolSet(tools);
|
||||
}
|
||||
|
||||
/** Include the completion tools that the Codex driver adds at session startup. */
|
||||
export function runnerCodexDynamicToolsFit(
|
||||
tools: readonly Readonly<Record<string, unknown>>[],
|
||||
): boolean {
|
||||
const supplied = [...tools, ...codexSemanticToolSpecs()];
|
||||
// Keep these bounds aligned with runner-core/provider_bridge.rs. Project
|
||||
// large optional catalogs before run.prepare; never raise the protocol caps.
|
||||
return supplied.length <= 256 &&
|
||||
Buffer.byteLength(JSON.stringify(authorizedToolSet(supplied)), "utf8") <= 768 * 1024;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw provider tracing is consumed by runnerd itself. The provider child still
|
||||
* receives the narrower allowlist enforced by Rust's `SupervisedProcess`, so
|
||||
|
||||
Reference in new issue
Block a user