@pasko70/pibo 2.5.1 → 2.5.2

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.
@@ -733,6 +733,7 @@ export class CodexNativeAgentRuntimeAdapter {
733
733
  resources: input.services?.resources,
734
734
  nativeSubagentsEnabled: input.profile.nativeSubagents,
735
735
  };
736
+ const profileOptions = parseCodexNativeProfileOptions(input.profile.runtimeOptions);
736
737
  const startProcessBundle = async (processGeneration) => {
737
738
  let delivery;
738
739
  let ownedProcess;
@@ -745,6 +746,7 @@ export class CodexNativeAgentRuntimeAdapter {
745
746
  sessionGeneration: processGeneration,
746
747
  workspace: input.workspace,
747
748
  clientVersion: CODEX_NATIVE_ADAPTER_VERSION,
749
+ experimentalApi: profileOptions.permissionMode === "plan",
748
750
  resourceEnvironment: delivery.environment,
749
751
  });
750
752
  await delivery.configureProcess(ownedProcess.client, input.workspace);
@@ -765,7 +767,6 @@ export class CodexNativeAgentRuntimeAdapter {
765
767
  process = initial.process;
766
768
  const compatibility = input.services?.compatibility;
767
769
  const catalog = await this.loadModelCatalog(process.client);
768
- const profileOptions = parseCodexNativeProfileOptions(input.profile.runtimeOptions);
769
770
  const persisted = binding.state === "bound"
770
771
  ? readCodexNativePersistedSettings(binding.metadata)
771
772
  : { profileOptions: {} };
@@ -790,6 +791,7 @@ export class CodexNativeAgentRuntimeAdapter {
790
791
  reasoningSummary: Object.hasOwn(persistedOptions, "reasoningSummary")
791
792
  ? persistedOptions.reasoningSummary
792
793
  : profileOptions.reasoningSummary,
794
+ permissionMode: profileOptions.permissionMode,
793
795
  },
794
796
  });
795
797
  const threadSelection = {
@@ -19,6 +19,13 @@ export const CODEX_NATIVE_MODEL_OPTIONS_SCHEMA = {
19
19
  enum: ["auto", "concise", "detailed", "none"],
20
20
  description: "Optional native Codex reasoning-summary mode.",
21
21
  },
22
+ permissionMode: {
23
+ type: "string",
24
+ title: "Permission mode",
25
+ enum: ["approval", "yolo", "plan"],
26
+ default: "approval",
27
+ description: "Codex permissions: Approval asks before privileged work, YOLO disables approvals with unrestricted host access, and Plan uses a read-only planning turn.",
28
+ },
22
29
  },
23
30
  };
24
31
  const MAX_MODEL_PAGES = 20;
@@ -34,6 +41,7 @@ const MAX_DESCRIPTION_LENGTH = 4_096;
34
41
  const MAX_CURSOR_LENGTH = 1_024;
35
42
  const PERSONALITIES = new Set(["none", "friendly", "pragmatic"]);
36
43
  const REASONING_SUMMARIES = new Set(["auto", "concise", "detailed", "none"]);
44
+ const PERMISSION_MODES = new Set(["approval", "yolo", "plan"]);
37
45
  const BINDING_MODEL_KEY = "codexNativeModelId";
38
46
  const BINDING_REASONING_KEY = "codexNativeReasoningEffort";
39
47
  const BINDING_SERVICE_TIER_KEY = "codexNativeServiceTier";
@@ -271,7 +279,7 @@ export function readCodexNativePersistedSettings(metadata) {
271
279
  };
272
280
  }
273
281
  export function parseCodexNativeProfileOptions(value) {
274
- const allowed = new Set(["serviceTier", "personality", "reasoningSummary"]);
282
+ const allowed = new Set(["serviceTier", "personality", "reasoningSummary", "permissionMode"]);
275
283
  const unknown = Object.keys(value).filter((key) => !allowed.has(key));
276
284
  if (unknown.length > 0)
277
285
  throw new Error(`Unsupported native Codex runtime option${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}.`);
@@ -297,8 +305,43 @@ export function parseCodexNativeProfileOptions(value) {
297
305
  }
298
306
  options.reasoningSummary = value.reasoningSummary;
299
307
  }
308
+ if (Object.hasOwn(value, "permissionMode")) {
309
+ if (typeof value.permissionMode !== "string" || !PERMISSION_MODES.has(value.permissionMode)) {
310
+ throw new Error("Native Codex runtime option permissionMode must be one of: approval, yolo, plan.");
311
+ }
312
+ options.permissionMode = value.permissionMode;
313
+ }
300
314
  return options;
301
315
  }
316
+ export function codexNativePermissionMode(options) {
317
+ return options.permissionMode ?? "approval";
318
+ }
319
+ export function codexNativeThreadPermissionSelection(mode) {
320
+ if (mode === "yolo")
321
+ return { approvalPolicy: "never", sandbox: "danger-full-access" };
322
+ if (mode === "plan")
323
+ return { approvalPolicy: "on-request", sandbox: "read-only" };
324
+ return { approvalPolicy: "on-request", sandbox: "workspace-write" };
325
+ }
326
+ export function codexNativeTurnPermissionOptions(mode, model, effort) {
327
+ if (mode === "yolo") {
328
+ return { approvalPolicy: "never", sandboxPolicy: { type: "dangerFullAccess" } };
329
+ }
330
+ if (mode === "plan") {
331
+ return {
332
+ approvalPolicy: "on-request",
333
+ sandboxPolicy: { type: "readOnly", networkAccess: false },
334
+ collaborationMode: {
335
+ mode: "plan",
336
+ settings: { model, reasoning_effort: effort, developer_instructions: null },
337
+ },
338
+ };
339
+ }
340
+ return {
341
+ approvalPolicy: "on-request",
342
+ sandboxPolicy: { type: "workspaceWrite", networkAccess: false },
343
+ };
344
+ }
302
345
  function findModel(catalog, value) {
303
346
  return catalog.models.find((model) => model.id === value || model.model === value);
304
347
  }
@@ -374,6 +417,7 @@ export class CodexNativeSessionSettingsController {
374
417
  serviceTier;
375
418
  personality;
376
419
  reasoningSummary;
420
+ permissionMode;
377
421
  activeThreadId;
378
422
  contextUsage;
379
423
  pendingContextUsage = new Map();
@@ -394,6 +438,7 @@ export class CodexNativeSessionSettingsController {
394
438
  this.serviceTier = validateServiceTier(this.currentModel, selectedTier);
395
439
  this.personality = initial.profileOptions.personality;
396
440
  this.reasoningSummary = initial.profileOptions.reasoningSummary;
441
+ this.permissionMode = codexNativePermissionMode(initial.profileOptions);
397
442
  this.bindClient(client);
398
443
  }
399
444
  bindClient(client) {
@@ -459,6 +504,7 @@ export class CodexNativeSessionSettingsController {
459
504
  model: this.currentModel.id,
460
505
  serviceTier: this.serviceTier,
461
506
  ...(this.personality !== undefined ? { personality: this.personality } : {}),
507
+ ...codexNativeThreadPermissionSelection(this.permissionMode),
462
508
  };
463
509
  }
464
510
  get turnOptions() {
@@ -468,6 +514,7 @@ export class CodexNativeSessionSettingsController {
468
514
  serviceTier: this.serviceTier,
469
515
  ...(this.reasoningSummary !== undefined ? { summary: this.reasoningSummary } : {}),
470
516
  ...(this.personality !== undefined ? { personality: this.personality } : {}),
517
+ ...codexNativeTurnPermissionOptions(this.permissionMode, this.currentModel.id, this.reasoningLevel),
471
518
  };
472
519
  }
473
520
  setModel(model) {
@@ -422,7 +422,7 @@ export async function startCodexNativeAppServer(input) {
422
422
  const paths = await prepareCodexNativeSessionPaths(input);
423
423
  let client;
424
424
  try {
425
- const capabilities = { experimentalApi: false };
425
+ const capabilities = { experimentalApi: input.experimentalApi === true };
426
426
  const invocation = codexExecutableInvocation(input.config.executable, [
427
427
  "app-server",
428
428
  "--stdio",
@@ -214,13 +214,13 @@ export class CodexNativeThreadController {
214
214
  client;
215
215
  currentThread;
216
216
  currentConfiguration;
217
- resourceSelection;
217
+ inheritedSelection;
218
218
  knownThreads = new Map();
219
- constructor(client, currentThread, currentConfiguration, resourceSelection) {
219
+ constructor(client, currentThread, currentConfiguration, inheritedSelection) {
220
220
  this.client = client;
221
221
  this.currentThread = currentThread;
222
222
  this.currentConfiguration = currentConfiguration;
223
- this.resourceSelection = resourceSelection;
223
+ this.inheritedSelection = inheritedSelection;
224
224
  this.knownThreads.set(currentThread.id, structuredClone(currentThread));
225
225
  }
226
226
  static async start(client, workspace, selection = {}) {
@@ -230,11 +230,15 @@ export class CodexNativeThreadController {
230
230
  ...(selection.model ? { model: selection.model } : {}),
231
231
  ...(selection.serviceTier !== undefined ? { serviceTier: selection.serviceTier } : {}),
232
232
  ...(selection.personality !== undefined ? { personality: selection.personality } : {}),
233
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
234
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
233
235
  ...(selection.config ? { config: selection.config } : {}),
234
236
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
235
237
  });
236
238
  const selected = threadSessionFromResponse(response, "thread/start");
237
239
  return new CodexNativeThreadController(client, selected.thread, selected.configuration, {
240
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
241
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
238
242
  ...(selection.config ? { config: structuredClone(selection.config) } : {}),
239
243
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
240
244
  });
@@ -247,6 +251,8 @@ export class CodexNativeThreadController {
247
251
  ...(selection.model ? { model: selection.model } : {}),
248
252
  ...(selection.serviceTier !== undefined ? { serviceTier: selection.serviceTier } : {}),
249
253
  ...(selection.personality !== undefined ? { personality: selection.personality } : {}),
254
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
255
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
250
256
  ...(selection.config ? { config: selection.config } : {}),
251
257
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
252
258
  });
@@ -255,6 +261,8 @@ export class CodexNativeThreadController {
255
261
  if (thread.id !== threadId)
256
262
  throw new CodexNativeThreadProtocolError("Codex resumed a different thread than requested.");
257
263
  return new CodexNativeThreadController(client, thread, selected.configuration, {
264
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
265
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
258
266
  ...(selection.config ? { config: structuredClone(selection.config) } : {}),
259
267
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
260
268
  });
@@ -353,9 +361,11 @@ export class CodexNativeThreadController {
353
361
  ...(lastTurnId ? { lastTurnId } : {}),
354
362
  cwd: workspace,
355
363
  ephemeral: false,
356
- ...(this.resourceSelection.config ? { config: structuredClone(this.resourceSelection.config) } : {}),
357
- ...(this.resourceSelection.developerInstructions
358
- ? { developerInstructions: this.resourceSelection.developerInstructions }
364
+ ...(this.inheritedSelection.approvalPolicy ? { approvalPolicy: this.inheritedSelection.approvalPolicy } : {}),
365
+ ...(this.inheritedSelection.sandbox ? { sandbox: this.inheritedSelection.sandbox } : {}),
366
+ ...(this.inheritedSelection.config ? { config: structuredClone(this.inheritedSelection.config) } : {}),
367
+ ...(this.inheritedSelection.developerInstructions
368
+ ? { developerInstructions: this.inheritedSelection.developerInstructions }
359
369
  : {}),
360
370
  };
361
371
  try {
@@ -291,6 +291,9 @@ export class CodexNativeTurnController {
291
291
  model: modelOptions.model,
292
292
  effort: modelOptions.effort,
293
293
  serviceTier: modelOptions.serviceTier,
294
+ approvalPolicy: modelOptions.approvalPolicy,
295
+ sandboxPolicy: modelOptions.sandboxPolicy,
296
+ ...(modelOptions.collaborationMode ? { collaborationMode: modelOptions.collaborationMode } : {}),
294
297
  ...(modelOptions.summary !== undefined ? { summary: modelOptions.summary } : {}),
295
298
  ...(modelOptions.personality !== undefined ? { personality: modelOptions.personality } : {}),
296
299
  };
@@ -793,6 +793,9 @@ function sanitizeSubagents(value) {
793
793
  const thinkingLevel = sanitizeThinkingLevel(candidate.thinkingLevel);
794
794
  if (thinkingLevel)
795
795
  subagent.thinkingLevel = thinkingLevel;
796
+ if (candidate.runtimeOptions && typeof candidate.runtimeOptions === "object" && !Array.isArray(candidate.runtimeOptions)) {
797
+ subagent.runtimeOptions = structuredClone(candidate.runtimeOptions);
798
+ }
796
799
  if (typeof candidate.timeoutMs === "number")
797
800
  subagent.timeoutMs = candidate.timeoutMs;
798
801
  if (typeof candidate.maxDepth === "number")
@@ -407,6 +407,12 @@ export function normalizeAgentSubagents(value) {
407
407
  const thinkingLevel = normalizeThinkingLevel(raw.thinkingLevel, "subagent thinkingLevel");
408
408
  if (thinkingLevel)
409
409
  subagent.thinkingLevel = thinkingLevel;
410
+ if (raw.runtimeOptions !== undefined) {
411
+ if (!raw.runtimeOptions || typeof raw.runtimeOptions !== "object" || Array.isArray(raw.runtimeOptions)) {
412
+ throw new PiboWebHttpError("subagent runtimeOptions must be a JSON object", 400);
413
+ }
414
+ subagent.runtimeOptions = normalizeAgentRuntimeJsonObject(raw.runtimeOptions, "subagent runtimeOptions", 0);
415
+ }
410
416
  if (raw.timeoutMs !== undefined) {
411
417
  if (typeof raw.timeoutMs !== "number" || !Number.isFinite(raw.timeoutMs) || raw.timeoutMs <= 0) {
412
418
  throw new PiboWebHttpError("subagent timeoutMs must be a positive number", 400);