@tickernelz/paperclip-pro-adapter-utils 2026.1002.8 → 2026.1004.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,7 +79,7 @@ function createLocalSandboxRunner(onExecute) {
79
79
  },
80
80
  };
81
81
  }
82
- function buildRuntime(onSetConfigOption, onEnsureSession, onStartTurn) {
82
+ function buildRuntime(onSetConfigOption, onEnsureSession, onStartTurn, onSetMode) {
83
83
  return {
84
84
  ensureSession: async (input) => {
85
85
  onEnsureSession?.(input);
@@ -102,12 +102,16 @@ function buildRuntime(onSetConfigOption, onEnsureSession, onStartTurn) {
102
102
  setConfigOption: async (input) => {
103
103
  onSetConfigOption?.(input);
104
104
  },
105
+ setMode: async (input) => {
106
+ onSetMode?.(input.mode);
107
+ },
105
108
  close: async () => { },
106
109
  };
107
110
  }
108
111
  async function runExecutor(config, options = {}) {
109
112
  const runtimeOptions = [];
110
113
  const configOptions = [];
114
+ const sessionModes = [];
111
115
  const sessionInputs = [];
112
116
  const turnInputs = [];
113
117
  const meta = [];
@@ -119,7 +123,7 @@ async function runExecutor(config, options = {}) {
119
123
  : {}),
120
124
  createRuntime: (options) => {
121
125
  runtimeOptions.push(options);
122
- return buildRuntime(({ key, value }) => configOptions.push({ key, value }), (input) => sessionInputs.push(input), (input) => turnInputs.push(input));
126
+ return buildRuntime(({ key, value }) => configOptions.push({ key, value }), (input) => sessionInputs.push(input), (input) => turnInputs.push(input), (mode) => sessionModes.push(mode));
123
127
  },
124
128
  });
125
129
  const result = await execute({
@@ -147,7 +151,7 @@ async function runExecutor(config, options = {}) {
147
151
  },
148
152
  });
149
153
  expect(result.exitCode).toBe(0);
150
- return { logs, meta, events, runtimeOptions, configOptions, sessionInputs, turnInputs, result };
154
+ return { logs, meta, events, runtimeOptions, configOptions, sessionModes, sessionInputs, turnInputs, result };
151
155
  }
152
156
  // Under `vi.useFakeTimers()`, setup before `ensureSession` still performs real
153
157
  // filesystem work. Advancing the fake clock before that work reaches the
@@ -282,6 +286,31 @@ describe("shared ACPX engine runtime behavior", () => {
282
286
  const run = await runExecutor({ agent: "custom", agentCommand: "node ./fake-acp.js", cwd: root, stateDir: path.join(root, "state"), permissionMode });
283
287
  expect(run.runtimeOptions[0]?.permissionMode).toBe(expected);
284
288
  });
289
+ it.each([
290
+ ["codex", "read-only"],
291
+ ["claude", "default"],
292
+ ])("applies the read_only tool profile to a %s ACP session through mode %s and a permission hook", async (agent, mode) => {
293
+ const root = await makeTempRoot();
294
+ const config = { agent, cwd: root, stateDir: path.join(root, "state") };
295
+ const full = await runExecutor(config);
296
+ const explicitFull = await runExecutor(config, { context: { paperclipToolProfile: "full" } });
297
+ const readOnly = await runExecutor(config, { context: { paperclipToolProfile: "read_only" } });
298
+ for (const run of [full, explicitFull]) {
299
+ expect(run.sessionModes).toEqual([]);
300
+ expect(run.runtimeOptions[0]).not.toHaveProperty("onPermissionRequest");
301
+ }
302
+ expect(explicitFull.result.sessionParams?.configFingerprint).toBe(full.result.sessionParams?.configFingerprint);
303
+ expect(readOnly.sessionModes).toEqual([mode]);
304
+ expect(typeof readOnly.runtimeOptions[0]?.onPermissionRequest).toBe("function");
305
+ expect(readOnly.runtimeOptions[0]?.permissionMode).toBe("approve-all");
306
+ expect(readOnly.result.sessionParams?.configFingerprint).not.toBe(full.result.sessionParams?.configFingerprint);
307
+ });
308
+ it("leaves agents without a read-only session mode on their configured mode in a read_only run", async () => {
309
+ const root = await makeTempRoot();
310
+ const run = await runExecutor({ agent: "gemini", cwd: root, stateDir: path.join(root, "state") }, { context: { paperclipToolProfile: "read_only" } });
311
+ expect(run.sessionModes).toEqual([]);
312
+ expect(typeof run.runtimeOptions[0]?.onPermissionRequest).toBe("function");
313
+ });
285
314
  it("persists ACP agent process identity before prompting on each run (host lane re-creates, no warm reuse)", async () => {
286
315
  const root = await makeTempRoot();
287
316
  const startedAt = "2026-07-30T07:00:00.000Z";