mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +02:00
## Thinking Path > - Paperclip manages AI agents that perform work. > - Paperclip Runner connects durable task runs to local provider processes. > - The full-stack paid matrix exposed failures after the runner integrity repair. > - Verified JavaScript entrypoints lost their relative module graph when Linux executed them through descriptor paths. > - Returned provider startup errors also remained pending and became indeterminate after recovery. > - Sparse Codex tool lifecycle events lost the `write_document` identity before task transcript projection. > - This pull request repairs those three boundaries and makes the structured-question fixture deterministic. > - The benefit is repeatable provider startup, exact failure replay, and correct inline Plan placement. ## Linked Issues or Issue Description Refs #12721 and #12700. **What happened?** The paid runner matrix failed ACPX and OpenCode startup before provider session creation. The runner journal then replaced the original startup error with an indeterminate recovery result. Native Codex saved a Plan but rendered it only as a fallback card. A legacy Claude waiting reply could also echo the reserved terminal marker before the answer arrived. **Expected behavior** Verified JavaScript providers must start from immutable descriptor-backed artifacts. Returned startup failures must persist as terminal failed command results. Native tool lifecycle updates must preserve the `write_document` boundary. Pre-answer fixture output must not contain the reserved terminal marker. **Steps to reproduce** 1. Run the local provider cells in the Runner Full-Stack E2E workflow. 2. Observe ACPX and OpenCode fail during `session.open` before provider execution. 3. Observe recovery report `execution_indeterminate` instead of the original startup error. 4. Run the native Codex Plan cell and observe the fallback Plan card after the tool activity row. 5. Run the legacy Claude structured-question resume cell and observe an early marker echo in waiting prose. **Paperclip version or commit** `0f9452101740835ce0b1488a204bf48acd5bafc3` **Deployment mode** Local development with the paid GitHub Actions acceptance workflow. ## What Changed - Bundle the ACPX sidecar and OpenCode proxy as self-contained Node ESM entrypoints before hashing and verified descriptor launch. - Anchor ACPX dynamic provider package resolution at a controller-derived provider-pack root and keep that root out of the provider child environment. - Persist executor-returned startup errors as redacted durable failed command results while retaining indeterminate recovery for true process death. - Coalesce sparse native tool items by stable ID so a late `write_document` name, input, and result reach the transcript boundary once. - Forbid the structured-question fixture from spelling or announcing its reserved terminal marker before the user answers. ## Verification - Rust and TypeScript regression tests cover durable failed replay, true crash ambiguity, bundle closure, package-root derivation, environment filtering, exact Codex tool lifecycle coalescing, and prompt determinism. - Local execution is intentionally limited to formatters and static diff checks. GitHub Actions will run tests, type checks, builds, and security checks. - After ordinary CI is green, scoped paid cells will validate one ACPX launch, one OpenCode launch, native Codex Plan projection, and legacy Claude structured resume before a complete matrix rerun. - Prior failing matrix: https://github.com/paperclipai/paperclip/actions/runs/33682434315 ## Risks - Bundling changes the bytes covered by provider launch hashes. Provider-pack generation already hashes the final built files. - ACPX still loads qualified provider packages dynamically. The controller supplies a normalized package root, while existing version, digest, path, and descriptor checks remain active. - Durable `failed` is terminal. Replays return the same redacted result and do not execute the provider effect twice. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex based on GPT-5 with agentic reasoning, repository inspection, code editing, Git, parallel subagents, and GitHub Actions coordination. The exact deployed snapshot and context-window size are not exposed to this task. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either linked related public work or described the bug in this PR - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket id - [ ] I have run tests locally and they pass (intentionally deferred to GitHub Actions) - [x] I have added or updated tests where applicable - [x] No documentation change is required for this runtime repair - [x] I have considered and documented the risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge
112 lines
4.4 KiB
Diff
112 lines
4.4 KiB
Diff
diff --git a/package.json b/package.json
|
|
--- a/package.json
|
|
+++ b/package.json
|
|
@@ -65,7 +65,7 @@
|
|
},
|
|
"dependencies": {
|
|
"@agentclientprotocol/sdk": "^1.3.0",
|
|
- "@openai/codex": "^0.148.0",
|
|
+ "@openai/codex": "0.148.0",
|
|
"diff": "^9.0.0",
|
|
"open": "^11.0.0",
|
|
"vscode-jsonrpc": "^9.0.1",
|
|
diff --git a/dist/index.js b/dist/index.js
|
|
--- a/dist/index.js
|
|
+++ b/dist/index.js
|
|
@@ -25341,7 +25341,7 @@
|
|
async handleElicitation(params) {
|
|
try {
|
|
const context = this.createMcpElicitationContext(params);
|
|
- if (this.shouldUseAcpElicitation(params)) {
|
|
+ if (!context.isToolApproval && this.shouldUseAcpElicitation(params)) {
|
|
const response2 = await this.connection.request(
|
|
methods.client.elicitation.create,
|
|
this.buildElicitationRequest(params, context),
|
|
@@ -25563,7 +25563,7 @@
|
|
toolCall: {
|
|
toolCallId: context.correlatedCallId,
|
|
kind: "execute",
|
|
- status: "pending"
|
|
+ status: "pending",
|
|
+ rawInput: { serverName: params.serverName }
|
|
// content: [messageContent], — omitted: already rendered via item/started
|
|
- // rawInput: { ... } — omitted: same reason
|
|
},
|
|
@@ -26988,4 +26988,13 @@
|
|
};
|
|
+function paperclipBaseInstructions(request) {
|
|
+ if (process.env.PAPERCLIP_ACPX_ISOLATED_CONTEXT !== "1") return void 0;
|
|
+ const prompt = request?._meta?.systemPrompt;
|
|
+ if (typeof prompt === "string") return prompt;
|
|
+ if (prompt && typeof prompt === "object" && typeof prompt.append === "string") {
|
|
+ return prompt.append;
|
|
+ }
|
|
+ return void 0;
|
|
+}
|
|
var CodexAcpClient = class {
|
|
codexClient;
|
|
config;
|
|
@@ -27288,6 +27297,7 @@
|
|
const response = await this.codexClient.threadResume({
|
|
config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
|
|
cwd: request.cwd,
|
|
+ baseInstructions: paperclipBaseInstructions(request),
|
|
modelProvider: await this.getResumeModelProvider(),
|
|
threadId: request.sessionId
|
|
});
|
|
@@ -27310,6 +27320,7 @@
|
|
const response = await this.codexClient.threadResume({
|
|
config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
|
|
cwd: request.cwd,
|
|
+ baseInstructions: paperclipBaseInstructions(request),
|
|
modelProvider: await this.getResumeModelProvider(),
|
|
threadId: request.sessionId
|
|
});
|
|
@@ -27337,5 +27348,6 @@
|
|
const response = await this.codexClient.threadStart({
|
|
config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers),
|
|
modelProvider: this.getModelProvider(),
|
|
+ baseInstructions: paperclipBaseInstructions(request),
|
|
cwd: request.cwd
|
|
});
|
|
@@ -27437,5 +27449,12 @@
|
|
const mergedConfig = {
|
|
...mergeGatewayConfig(this.config, this.gatewayConfig),
|
|
+ ...(process.env.PAPERCLIP_ACPX_ISOLATED_CONTEXT === "1" ? {
|
|
+ "include_apps_instructions": false,
|
|
+ "features.apps": false,
|
|
+ "features.memory_tool": false,
|
|
+ "skills.include_instructions": true,
|
|
+ "mcp_servers": {}
|
|
+ } : {}),
|
|
projects: Object.fromEntries(sessionRoots.map((root) => [root, {
|
|
trust_level: "trusted"
|
|
}]))
|
|
@@ -27449,7 +27468,7 @@
|
|
server: mcp
|
|
}));
|
|
let serversToConfigure = requestedServers;
|
|
- if (shouldDeduplicateMcpConflicts()) {
|
|
+ if (process.env.PAPERCLIP_ACPX_ISOLATED_CONTEXT !== "1" && shouldDeduplicateMcpConflicts()) {
|
|
const existingNames = await this.getConfigMcpServerNames(projectPath);
|
|
serversToConfigure = requestedServers.filter((mcp) => !existingNames.has(mcp.name));
|
|
}
|
|
@@ -27483,14 +27502,15 @@
|
|
async refreshSkills(cwd, additionalRoots) {
|
|
if (!cwd) {
|
|
return;
|
|
}
|
|
- const skillExtraRoots = additionalRoots.map((root) => path6.join(root, ".agents", "skills"));
|
|
+ const isolated = process.env.PAPERCLIP_ACPX_ISOLATED_CONTEXT === "1";
|
|
+ const skillExtraRoots = isolated ? [] : additionalRoots.map((root) => path6.join(root, ".agents", "skills"));
|
|
if (!arraysEqual(this.skillExtraRoots, skillExtraRoots)) {
|
|
await this.codexClient.skillsExtraRootsSet({ extraRoots: skillExtraRoots });
|
|
this.skillExtraRoots = skillExtraRoots;
|
|
}
|
|
await this.codexClient.listSkills({
|
|
- cwds: [cwd, ...additionalRoots],
|
|
+ cwds: isolated ? [process.env.CODEX_HOME] : [cwd, ...additionalRoots],
|
|
forceReload: true
|
|
});
|
|
}
|