@yemi33/minions 0.1.2421 → 0.1.2422

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.
@@ -595,6 +595,21 @@ async function openSettings() {
595
595
  'Permission bypass is runtime-owned: Claude agents use <code>--dangerously-skip-permissions</code>; Copilot agents use <code>--autopilot --allow-all --no-ask-user</code>. There is no dashboard permission-mode setting.' +
596
596
  '</div>';
597
597
 
598
+ const paneCodex =
599
+ '<h3>Codex Tuning</h3>' +
600
+ '<div class="settings-pane-sub">Command Center and doc-chat need broader host access than dispatched agents. This setting only affects Codex-backed CC calls.</div>' +
601
+ '<div class="settings-grid-2">' +
602
+ '<div data-search="codex command center doc chat sandbox danger full access workspace write read only">' +
603
+ '<label style="font-size:var(--text-sm);color:var(--muted);display:block;margin-bottom:2px">CC sandbox</label>' +
604
+ '<select id="set-codexCcSandbox" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:var(--text-md)">' +
605
+ '<option value="danger-full-access"' + ((e.codexCcSandbox || 'danger-full-access') === 'danger-full-access' ? ' selected' : '') + '>danger-full-access (default)</option>' +
606
+ '<option value="workspace-write"' + (e.codexCcSandbox === 'workspace-write' ? ' selected' : '') + '>workspace-write</option>' +
607
+ '<option value="read-only"' + (e.codexCcSandbox === 'read-only' ? ' selected' : '') + '>read-only</option>' +
608
+ '</select>' +
609
+ '<div style="font-size:var(--text-xs);color:var(--muted);margin-top:4px"><strong>Security:</strong> <code>danger-full-access</code> lets Codex CC reach localhost Minions APIs and operator-authorized paths outside <code>~/.minions</code>. Dispatched Codex agents and non-CC calls remain <code>workspace-write</code>. The adapter emits an explicit <code>--sandbox</code> CLI flag, which takes precedence over Codex config-file values.</div>' +
610
+ '</div>' +
611
+ '</div>';
612
+
598
613
  const paneHarness =
599
614
  '<h3>Native Runtime Integration</h3>' +
600
615
  '<div class="settings-pane-sub">Repository instructions, skills, commands, and MCPs are discovered by the selected CLI in the dispatched checkout. Minions does not inject or suppress them.</div>' +
@@ -735,6 +750,7 @@ async function openSettings() {
735
750
  { id: 'features', label: 'Feature Flags', html: paneFeatures },
736
751
  { id: 'copilot', label: 'Copilot Tuning', html: paneCopilot, group: 'advanced' },
737
752
  { id: 'claude', label: 'Claude Tuning', html: paneClaude, group: 'advanced' },
753
+ { id: 'codex', label: 'Codex Tuning', html: paneCodex, group: 'advanced' },
738
754
  { id: 'budget', label: 'Budget', html: paneBudget, group: 'advanced' },
739
755
  { id: 'advanced', label: 'Advanced', html: paneAdvanced, group: 'advanced' },
740
756
  ];
@@ -1536,6 +1552,7 @@ async function saveSettings() {
1536
1552
  ccCli: (document.getElementById('set-ccCli')?.value ?? '').trim(),
1537
1553
  ccModel: (document.getElementById('set-ccModel')?.value ?? '').trim(),
1538
1554
  ccEffort: document.getElementById('set-ccEffort').value || null,
1555
+ codexCcSandbox: document.getElementById('set-codexCcSandbox')?.value || 'danger-full-access',
1539
1556
  ccTurnTimeoutMs: document.getElementById('set-ccTurnTimeoutMs')?.value,
1540
1557
  // Settings UI presents the idle timeout in minutes for readability; the
1541
1558
  // engine stores ms (ENGINE_DEFAULTS.ccWorkerIdleTimeoutMs). Convert here;
package/dashboard.js CHANGED
@@ -5072,7 +5072,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
5072
5072
  outOfBandOnly: !resumeNeedsCarryover,
5073
5073
  }), '', {
5074
5074
  timeout, label, model, maxTurns, allowedTools, sessionId, effort: ccEffort, direct: true,
5075
- engineConfig: CONFIG.engine, images,
5075
+ engineConfig: CONFIG.engine, invocationScope: 'cc', images,
5076
5076
  });
5077
5077
  if (onAbortReady) onAbortReady(p1.abort);
5078
5078
  result = await p1;
@@ -5110,7 +5110,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
5110
5110
  const freshPrompt = buildPrompt({ includeCarryover: freshNeedsCarryover });
5111
5111
  const p2 = llm.callLLM(freshPrompt, systemPrompt, {
5112
5112
  timeout, label, model, maxTurns, allowedTools, effort: ccEffort, direct: true,
5113
- engineConfig: CONFIG.engine, images,
5113
+ engineConfig: CONFIG.engine, invocationScope: 'cc', images,
5114
5114
  });
5115
5115
  if (onAbortReady) onAbortReady(p2.abort);
5116
5116
  result = await p2;
@@ -5128,7 +5128,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
5128
5128
  await new Promise(r => setTimeout(r, 2000));
5129
5129
  const p3 = llm.callLLM(freshPrompt, systemPrompt, {
5130
5130
  timeout, label, model, maxTurns, allowedTools, effort: ccEffort, direct: true,
5131
- engineConfig: CONFIG.engine, images,
5131
+ engineConfig: CONFIG.engine, invocationScope: 'cc', images,
5132
5132
  });
5133
5133
  if (onAbortReady) onAbortReady(p3.abort);
5134
5134
  result = await p3;
@@ -5220,7 +5220,7 @@ async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext
5220
5220
  outOfBandOnly: !resumeNeedsCarryover,
5221
5221
  }), '', {
5222
5222
  timeout, label, model, maxTurns, allowedTools, sessionId, effort: ccEffort, direct: true,
5223
- engineConfig: CONFIG.engine,
5223
+ engineConfig: CONFIG.engine, invocationScope: 'cc',
5224
5224
  onChunk,
5225
5225
  onToolUse,
5226
5226
  onToolUpdate,
@@ -5259,7 +5259,7 @@ async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext
5259
5259
  const freshPrompt = buildPrompt({ includeCarryover: freshNeedsCarryover });
5260
5260
  const p2 = llm.callLLMStreaming(freshPrompt, systemPrompt, {
5261
5261
  timeout, label, model, maxTurns, allowedTools, effort: ccEffort, direct: true,
5262
- engineConfig: CONFIG.engine,
5262
+ engineConfig: CONFIG.engine, invocationScope: 'cc',
5263
5263
  onChunk,
5264
5264
  onToolUse,
5265
5265
  onToolUpdate,
@@ -5280,7 +5280,7 @@ async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext
5280
5280
  if (onRetry) onRetry(3);
5281
5281
  const p3 = llm.callLLMStreaming(freshPrompt, systemPrompt, {
5282
5282
  timeout, label, model, maxTurns, allowedTools, effort: ccEffort, direct: true,
5283
- engineConfig: CONFIG.engine,
5283
+ engineConfig: CONFIG.engine, invocationScope: 'cc',
5284
5284
  onChunk,
5285
5285
  onToolUse,
5286
5286
  onToolUpdate,
@@ -9895,7 +9895,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
9895
9895
  label, model, maxTurns,
9896
9896
  allowedTools: 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch',
9897
9897
  effort, direct: true,
9898
- engineConfig: CONFIG.engine,
9898
+ engineConfig: CONFIG.engine, invocationScope: 'cc',
9899
9899
  });
9900
9900
  llm.trackEngineUsage(label, result.usage);
9901
9901
 
@@ -9985,7 +9985,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
9985
9985
  timeout: CC_CALL_TIMEOUT_MS, label: 'command-center', model, maxTurns,
9986
9986
  allowedTools: 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch',
9987
9987
  sessionId: directSessionId, effort, direct: true,
9988
- engineConfig, images,
9988
+ engineConfig, images, invocationScope: 'cc',
9989
9989
  onProgress: () => _touchCcLiveStream(liveState),
9990
9990
  onChunk: (text, segmentId) => {
9991
9991
  if (liveState.statusMessage) {
@@ -11406,6 +11406,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11406
11406
  else if (valid.includes(e.copilotStreamMode)) _setEngineConfig('copilotStreamMode', e.copilotStreamMode);
11407
11407
  else _clamped.push(`copilotStreamMode: "${e.copilotStreamMode}" not in [on, off] (kept previous value)`);
11408
11408
  }
11409
+ if (e.codexCcSandbox !== undefined) {
11410
+ if (_isClear(e.codexCcSandbox)) _deleteEngineConfig('codexCcSandbox');
11411
+ else _setEngineConfig('codexCcSandbox', shared.validateCodexSandbox(e.codexCcSandbox));
11412
+ }
11409
11413
  // W-mpmwxkrw000872ec — fontSize allowlist. Clamps invalid values
11410
11414
  // (rather than silently failing) so the dashboard bootstrap never
11411
11415
  // ends up with an unknown data-font-size attribute.
@@ -6,6 +6,24 @@ CC is intentionally a thin wrapper around the runtime CLI: state changes happen
6
6
 
7
7
  For canonical detail (system prompt, session lifecycle, turn-ID surfacing pipeline, doc-chat integration, and CC API contract), read [`CLAUDE.md`](../CLAUDE.md) — see the **CC API Contract** and **Sessions** sections — and the source in [`dashboard/js/command-center.js`](../dashboard/js/command-center.js), [`dashboard.js`](../dashboard.js) (`handleCommandCenter`), and [`prompts/cc-system.md`](../prompts/cc-system.md).
8
8
 
9
+ ## Codex sandbox policy
10
+
11
+ Codex-backed Command Center and doc-chat calls default to
12
+ `engine.codexCcSandbox: "danger-full-access"`. This elevated interactive policy
13
+ lets CC reach the dashboard's localhost APIs and operator-authorized paths
14
+ outside `~/.minions`; it does not grant blanket permission for unsolicited
15
+ writes, which remain governed by the CC system prompt and the
16
+ [operator-checkout write authority](#operator-checkout-write-authority-interactive-turns)
17
+ rules below.
18
+
19
+ The setting accepts `read-only`, `workspace-write`, or `danger-full-access` and
20
+ is available under **Settings → Advanced → Codex Tuning**. It is scoped by the
21
+ runtime invocation, not by the selected model: dispatched Codex agents and
22
+ non-CC direct calls still default to `workspace-write`, while Claude and Copilot
23
+ arguments are unchanged. The Codex adapter emits an explicit `--sandbox` flag;
24
+ an explicit invocation-level sandbox takes precedence over this engine default
25
+ and the CLI flag intentionally wins over Codex config-file values.
26
+
9
27
  ## Error surfacing contract (W-mpmwxni2000c25c7-d)
10
28
 
11
29
  Both CC and Doc-Chat emit a typed error envelope so the dashboard can render a red `.cc-error` bubble (role=alert) with a Retry button and stop the spinner immediately. Errors arrive in two shapes:
@@ -263,6 +263,26 @@ Tests in `test/unit/runtime-fleet-helpers.test.js` enforce that
263
263
  `resolveAgentCli` does NOT fall through to `engine.ccCli` and that `resolveCcCli`
264
264
  does NOT inspect any agent settings.
265
265
 
266
+ ### Codex sandbox boundary
267
+
268
+ Codex uses separate sandbox policies for the two runtime paths:
269
+
270
+ - Command Center and doc-chat calls carry `invocationScope: 'cc'`. The Codex
271
+ adapter resolves `engine.codexCcSandbox`, which defaults to
272
+ `danger-full-access`. Interactive CC needs this access to call the localhost
273
+ Minions API and to write paths the operator explicitly authorizes outside
274
+ `~/.minions`.
275
+ - Dispatched agents and every other Codex invocation omit the CC scope, so
276
+ `codex.buildArgs()` retains its `workspace-write` default. Changing
277
+ `engine.codexCcSandbox` cannot broaden an agent dispatch.
278
+
279
+ Accepted values are `read-only`, `workspace-write`, and `danger-full-access`;
280
+ the Settings API rejects anything else with HTTP 400. An explicit
281
+ invocation-level `sandbox` option takes precedence over the engine setting.
282
+ The adapter always renders the resolved policy as Codex's `--sandbox` CLI flag,
283
+ which intentionally takes precedence over sandbox values from Codex config
284
+ files.
285
+
266
286
  ## `--cli` / `--model` / `--effort` Knobs
267
287
 
268
288
  Three CLI flags switch the fleet without dashboard interaction. They are parsed
package/engine/llm.js CHANGED
@@ -737,6 +737,7 @@ function callLLM(promptText, sysPromptText, opts = {}) {
737
737
  // Cross-runtime + Copilot opts:
738
738
  maxBudget, bare, fallbackModel,
739
739
  stream, disableBuiltinMcps, reasoningSummaries,
740
+ invocationScope,
740
741
  // P-7b2e4d01 — optional image attachments: [{ mimeType, dataBase64, filename? }]
741
742
  images,
742
743
  } = opts;
@@ -752,7 +753,7 @@ function callLLM(promptText, sysPromptText, opts = {}) {
752
753
  model, maxTurns, allowedTools, effort, sessionId,
753
754
  maxBudget, bare, fallbackModel, images: imageGate.images,
754
755
  stream, disableBuiltinMcps, reasoningSummaries,
755
- }, { engineConfig: engineConfig || {} });
756
+ }, { engineConfig: engineConfig || {}, invocationScope });
756
757
 
757
758
  let _abort = null;
758
759
  const promise = new Promise((resolve) => {
@@ -865,6 +866,7 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
865
866
  model: modelOverride, cli: cliOverride, engineConfig,
866
867
  maxBudget, bare, fallbackModel,
867
868
  stream, disableBuiltinMcps, reasoningSummaries,
869
+ invocationScope,
868
870
  // P-7b2e4d01 — optional image attachments: [{ mimeType, dataBase64, filename? }]
869
871
  images,
870
872
  } = opts;
@@ -880,7 +882,7 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
880
882
  model, maxTurns, allowedTools, effort, sessionId,
881
883
  maxBudget, bare, fallbackModel, images: imageGate.images,
882
884
  stream, disableBuiltinMcps, reasoningSummaries,
883
- }, { engineConfig: engineConfig || {} });
885
+ }, { engineConfig: engineConfig || {}, invocationScope });
884
886
 
885
887
  let _abort = null;
886
888
  const promise = new Promise((resolve) => {
@@ -11,7 +11,13 @@ const fs = require('fs');
11
11
  const os = require('os');
12
12
  const path = require('path');
13
13
  const { execFileSync, execSync } = require('child_process');
14
- const { FAILURE_CLASS, safeWrite, ts, resolveEngineCacheDir } = require('../shared');
14
+ const {
15
+ FAILURE_CLASS,
16
+ safeWrite,
17
+ ts,
18
+ resolveEngineCacheDir,
19
+ resolveCodexCcSandbox,
20
+ } = require('../shared');
15
21
 
16
22
  const ENGINE_DIR = __dirname.replace(/[\\/]runtimes$/, '');
17
23
  const MINIONS_DIR = path.resolve(ENGINE_DIR, '..');
@@ -388,6 +394,18 @@ function buildSpawnFlags(opts = {}) {
388
394
  return flags;
389
395
  }
390
396
 
397
+ function resolveInvocationOptions({
398
+ options = {},
399
+ engineConfig = {},
400
+ invocationScope,
401
+ } = {}) {
402
+ const resolved = { ...options };
403
+ if (invocationScope === 'cc' && resolved.sandbox == null) {
404
+ resolved.sandbox = resolveCodexCcSandbox(engineConfig);
405
+ }
406
+ return resolved;
407
+ }
408
+
391
409
  function buildPrompt(promptText, sysPromptText, opts = {}) {
392
410
  const user = promptText == null ? '' : String(promptText);
393
411
  if (opts && opts.sessionId) return user;
@@ -967,6 +985,7 @@ module.exports = {
967
985
  spawnScript: path.join(ENGINE_DIR, 'spawn-agent.js'),
968
986
  installHint: INSTALL_HINT,
969
987
  buildSpawnFlags,
988
+ resolveInvocationOptions,
970
989
  buildArgs,
971
990
  buildPrompt,
972
991
  getUserAssetDirs,
package/engine/shared.js CHANGED
@@ -2867,6 +2867,29 @@ function validateLiveValidation(value, opts) {
2867
2867
  // Single source of truth for engine configuration defaults.
2868
2868
  // Used by: engine.js, minions.js (init). config.template.json only has the project schema.
2869
2869
 
2870
+ const CODEX_SANDBOX_MODES = Object.freeze([
2871
+ 'read-only',
2872
+ 'workspace-write',
2873
+ 'danger-full-access',
2874
+ ]);
2875
+
2876
+ function validateCodexSandbox(value) {
2877
+ if (value === undefined || value === null || value === '') return undefined;
2878
+ if (typeof value !== 'string' || !CODEX_SANDBOX_MODES.includes(value)) {
2879
+ throw _httpError(400,
2880
+ `Invalid codexCcSandbox: "${value}". Accepted values: ${CODEX_SANDBOX_MODES.map(v => `'${v}'`).join(', ')}.`);
2881
+ }
2882
+ return value;
2883
+ }
2884
+
2885
+ function resolveCodexCcSandbox(engine) {
2886
+ const configured = engine && engine.codexCcSandbox;
2887
+ if (configured === undefined || configured === null || configured === '') {
2888
+ return ENGINE_DEFAULTS.codexCcSandbox;
2889
+ }
2890
+ return validateCodexSandbox(configured);
2891
+ }
2892
+
2870
2893
  const ENGINE_DEFAULTS = {
2871
2894
  // W-mpxpckey000laa4f: tick interval lowered 60s → 10s so the shipped default
2872
2895
  // matches what operators already run at (live boxes have had `tickInterval:
@@ -3289,6 +3312,7 @@ const ENGINE_DEFAULTS = {
3289
3312
  defaultModel: undefined, // fleet-wide model; undefined = let the runtime adapter pick its own default
3290
3313
  ccCli: undefined, // CC/doc-chat CLI override; undefined = inherit defaultCli (independent of agent path)
3291
3314
  ccModel: undefined, // CC/doc-chat model override; undefined = inherit defaultModel
3315
+ codexCcSandbox: 'danger-full-access', // Codex CC/doc-chat only. Dispatched agents and other Codex calls keep the adapter's workspace-write default.
3292
3316
  claudeBareMode: false, // Claude --bare: suppress CLAUDE.md auto-discovery (per-agent override: agents.<id>.bareMode)
3293
3317
  claudeFallbackModel: undefined,// Claude --fallback-model — Claude CLI honors this on rate-limit (429) only; engine retry on FAILURE_CLASS.MODEL_UNAVAILABLE keeps the flag passed so the CLI can swap internally
3294
3318
  copilotFallbackModel: undefined,// W-mpg6isvy000xca4d: Copilot has no --fallback-model flag; engine retry on FAILURE_CLASS.MODEL_UNAVAILABLE OVERRIDES --model directly with this value (separate knob from claudeFallbackModel because model namespaces differ across runtimes)
@@ -8806,6 +8830,9 @@ module.exports = {
8806
8830
  classifyInboxItem,
8807
8831
  isEngineSystemAlert,
8808
8832
  ENGINE_DEFAULTS,
8833
+ CODEX_SANDBOX_MODES,
8834
+ validateCodexSandbox,
8835
+ resolveCodexCcSandbox,
8809
8836
  resolvePollFlag, // P-c4d8e1a3 — granular per-poller flag resolution
8810
8837
  resolveAgentCli, resolveCcCli, resolveCcUseWorkerPool, resolveAgentUseWorkerPool, resolveAgentAcpPoolSize, resolveAgentModel, resolveCcModel,
8811
8838
  resolveAgentAllowedTools, resolveAgentMaxBudget, resolveAgentBareMode, resolvePropagateClaudeMdForNonClaudeRuntimes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2421",
3
+ "version": "0.1.2422",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"