@yemi33/minions 0.1.2420 → 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.
@@ -0,0 +1,208 @@
1
+ (function attachMemoryExplainer(globalObject, factory) {
2
+ const api = factory();
3
+
4
+ if (typeof module !== 'undefined' && module.exports) {
5
+ module.exports = api;
6
+ }
7
+ if (globalObject) {
8
+ globalObject.MemoryExplainer = api;
9
+ }
10
+
11
+ if (typeof document !== 'undefined') {
12
+ const start = () => api.init(document);
13
+ if (document.readyState === 'loading') {
14
+ document.addEventListener('DOMContentLoaded', start, { once: true });
15
+ } else {
16
+ start();
17
+ }
18
+ }
19
+ })(typeof globalThis !== 'undefined' ? globalThis : this, function memoryExplainerFactory() {
20
+ 'use strict';
21
+
22
+ const FORWARD_KEYS = new Set(['ArrowRight', 'ArrowDown', 'next']);
23
+ const BACKWARD_KEYS = new Set(['ArrowLeft', 'ArrowUp', 'previous']);
24
+
25
+ function createSelectionModel(inputIds, initialId) {
26
+ if (!Array.isArray(inputIds) || inputIds.length === 0) {
27
+ throw new TypeError('Selection model requires at least one item');
28
+ }
29
+ const ids = inputIds.map(id => String(id || '').trim());
30
+ if (ids.some(id => !id)) {
31
+ throw new TypeError('Selection model ids must be non-empty');
32
+ }
33
+ if (new Set(ids).size !== ids.length) {
34
+ throw new TypeError('Selection model ids must be unique');
35
+ }
36
+
37
+ let index = ids.indexOf(String(initialId || ''));
38
+ if (index < 0) index = 0;
39
+
40
+ function current() {
41
+ return ids[index];
42
+ }
43
+
44
+ function select(id) {
45
+ const nextIndex = ids.indexOf(String(id || ''));
46
+ if (nextIndex >= 0) index = nextIndex;
47
+ return current();
48
+ }
49
+
50
+ function move(action) {
51
+ if (FORWARD_KEYS.has(action)) {
52
+ index = (index + 1) % ids.length;
53
+ } else if (BACKWARD_KEYS.has(action)) {
54
+ index = (index - 1 + ids.length) % ids.length;
55
+ } else if (action === 'Home') {
56
+ index = 0;
57
+ } else if (action === 'End') {
58
+ index = ids.length - 1;
59
+ }
60
+ return current();
61
+ }
62
+
63
+ return { current, select, move };
64
+ }
65
+
66
+ function setupSelectionGroup(root, options) {
67
+ if (!root) return null;
68
+ const items = Array.from(root.querySelectorAll(options.itemSelector));
69
+ if (!items.length) return null;
70
+ const ids = items.map(item => item.dataset[options.dataKey]);
71
+ const initial = ids.find((id, index) => options.isInitiallySelected(items[index])) || ids[0];
72
+ const model = createSelectionModel(ids, initial);
73
+ const panels = Array.from(root.querySelectorAll(options.panelSelector));
74
+
75
+ function render(id, focusItem) {
76
+ items.forEach(item => {
77
+ const selected = item.dataset[options.dataKey] === id;
78
+ item.setAttribute(options.selectedAttribute, String(selected));
79
+ item.tabIndex = selected ? 0 : -1;
80
+ item.classList.toggle('is-active', selected);
81
+ if (selected && focusItem) item.focus();
82
+ });
83
+ panels.forEach(panel => {
84
+ const selected = panel.id === options.panelId(id);
85
+ panel.hidden = !selected;
86
+ panel.classList.toggle('is-active', selected);
87
+ });
88
+ if (typeof options.onRender === 'function') options.onRender(id, ids.indexOf(id), ids.length);
89
+ return id;
90
+ }
91
+
92
+ items.forEach(item => {
93
+ item.addEventListener('click', () => render(model.select(item.dataset[options.dataKey]), false));
94
+ item.addEventListener('keydown', event => {
95
+ if (!FORWARD_KEYS.has(event.key)
96
+ && !BACKWARD_KEYS.has(event.key)
97
+ && event.key !== 'Home'
98
+ && event.key !== 'End') return;
99
+ event.preventDefault();
100
+ render(model.move(event.key), true);
101
+ });
102
+ });
103
+
104
+ render(model.current(), false);
105
+ return {
106
+ current: model.current,
107
+ select(id, focusItem) {
108
+ return render(model.select(id), !!focusItem);
109
+ },
110
+ move(action, focusItem) {
111
+ return render(model.move(action), !!focusItem);
112
+ },
113
+ };
114
+ }
115
+
116
+ function initJourney(doc) {
117
+ const root = doc.getElementById('memory-journey');
118
+ const shell = root && root.querySelector('.journey-shell');
119
+ if (!root || !shell) return null;
120
+ const liveRegion = root.querySelector('[data-journey-status]');
121
+ const controller = setupSelectionGroup(root, {
122
+ itemSelector: '[data-step-id]',
123
+ panelSelector: '.journey-panel',
124
+ dataKey: 'stepId',
125
+ selectedAttribute: 'aria-selected',
126
+ panelId: id => `journey-${id}`,
127
+ isInitiallySelected: item => item.getAttribute('aria-selected') === 'true',
128
+ onRender(id, index, count) {
129
+ const progress = count > 1 ? (index * 100) / (count - 1) : 0;
130
+ shell.style.setProperty('--journey-progress', `${progress}%`);
131
+ if (liveRegion) {
132
+ const label = root.querySelector(`[data-step-id="${id}"] .step-label`);
133
+ liveRegion.textContent = `Showing step ${index + 1} of 7: ${label ? label.textContent : id}`;
134
+ }
135
+ },
136
+ });
137
+ if (!controller) return null;
138
+
139
+ const playButton = root.querySelector('[data-play-journey]');
140
+ const reducedMotion = typeof matchMedia === 'function'
141
+ && matchMedia('(prefers-reduced-motion: reduce)').matches;
142
+ let timer = null;
143
+
144
+ function stop() {
145
+ if (timer) clearInterval(timer);
146
+ timer = null;
147
+ shell.classList.remove('is-playing');
148
+ if (playButton) {
149
+ playButton.setAttribute('aria-pressed', 'false');
150
+ playButton.querySelector('span').textContent = 'Play the flow';
151
+ }
152
+ }
153
+
154
+ function advance() {
155
+ controller.move('next', false);
156
+ }
157
+
158
+ if (playButton) {
159
+ playButton.addEventListener('click', () => {
160
+ if (reducedMotion) {
161
+ advance();
162
+ return;
163
+ }
164
+ if (timer) {
165
+ stop();
166
+ return;
167
+ }
168
+ advance();
169
+ shell.classList.add('is-playing');
170
+ playButton.setAttribute('aria-pressed', 'true');
171
+ playButton.querySelector('span').textContent = 'Pause the flow';
172
+ timer = setInterval(advance, 3200);
173
+ });
174
+ }
175
+
176
+ doc.addEventListener('visibilitychange', () => {
177
+ if (doc.hidden) stop();
178
+ });
179
+ return controller;
180
+ }
181
+
182
+ function initArchitectureMap(doc) {
183
+ const root = doc.getElementById('architecture-map');
184
+ return setupSelectionGroup(root, {
185
+ itemSelector: '[data-map-node]',
186
+ panelSelector: '.map-detail',
187
+ dataKey: 'mapNode',
188
+ selectedAttribute: 'aria-pressed',
189
+ panelId: id => `map-detail-${id}`,
190
+ isInitiallySelected: item => item.getAttribute('aria-pressed') === 'true',
191
+ });
192
+ }
193
+
194
+ function init(doc) {
195
+ doc.documentElement.classList.remove('no-js');
196
+ doc.documentElement.classList.add('js');
197
+ const journey = initJourney(doc);
198
+ const architectureMap = initArchitectureMap(doc);
199
+ return { journey, architectureMap };
200
+ }
201
+
202
+ return {
203
+ createSelectionModel,
204
+ init,
205
+ initJourney,
206
+ initArchitectureMap,
207
+ };
208
+ });
package/docs/index.html CHANGED
@@ -27,6 +27,7 @@
27
27
  .cta-primary:hover { background: #79c0ff; }
28
28
  .cta-secondary { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
29
29
  .cta-secondary:hover { border-color: var(--blue); color: var(--blue); }
30
+ .cta a:focus-visible, .feature-link:focus-visible { outline: 3px solid var(--yellow); outline-offset: 4px; }
30
31
 
31
32
  /* Container */
32
33
  .container { max-width: 1100px; margin: 0 auto; padding: 0 20px; }
@@ -43,6 +44,8 @@
43
44
  .feature { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 24px; }
44
45
  .feature h3 { color: var(--blue); font-size: 1em; margin-bottom: 6px; }
45
46
  .feature p { color: var(--muted); font-size: 13px; line-height: 1.5; }
47
+ .feature-link { color: inherit; text-decoration: none; transition: border-color 0.2s, transform 0.2s; }
48
+ .feature-link:hover { border-color: var(--blue); transform: translateY(-2px); }
46
49
 
47
50
  /* Scenario sections */
48
51
  .scenario { margin: 60px 0; }
@@ -93,6 +96,7 @@
93
96
  <div class="cta">
94
97
  <a href="https://www.npmjs.com/package/@yemi33/minions" class="cta-primary">Install from npm</a>
95
98
  <a href="#quickstart" class="cta-secondary">Quick Start</a>
99
+ <a href="demo/memory-system.html" class="cta-secondary">Explore the Memory System</a>
96
100
  </div>
97
101
  </div>
98
102
 
@@ -114,7 +118,7 @@
114
118
  <div class="feature"><h3>Multi-Stage Pipelines</h3><p>Chain tasks, meetings, plans, and more into automated workflows. Cron triggers or manual. Artifacts flow between stages.</p></div>
115
119
  <div class="feature"><h3>Review / Fix Loop</h3><p>After implementation, Minions can dispatch reviews, react to human PR comments, and send fixes back to the original author agent.</p></div>
116
120
  <div class="feature"><h3>Git Worktree Isolation</h3><p>Each agent works in its own worktree. No conflicts, no cross-contamination, safe parallel execution.</p></div>
117
- <div class="feature"><h3>Knowledge Consolidation</h3><p>Agent findings, quick notes, pinned notes, and feedback consolidate into team notes and a categorized knowledge base.</p></div>
121
+ <a class="feature feature-link" href="demo/memory-system.html"><h3>Interactive Memory System</h3><p>Follow findings from inbox consolidation through SQL/FTS5 retrieval, prompt bounds, provenance, fencing, and episodic capture.</p></a>
118
122
  <div class="feature"><h3>PR Integration</h3><p>Tracks Azure DevOps and GitHub PRs, including review votes, build status, merge status, human comments, and context-only links.</p></div>
119
123
  <div class="feature"><h3>Scheduled Tasks &amp; Watches</h3><p>Cron-style recurring work plus watches for PRs, work items, dispatches, meetings, pipelines, agents, and status changes.</p></div>
120
124
  <div class="feature"><h3>Command Center &amp; Doc Chat</h3><p>Natural language orchestration, persistent CC tabs, document Q&amp;A/editing, issue filing, routing updates, and local API actions.</p></div>
@@ -277,4 +281,3 @@ minions update</pre>
277
281
 
278
282
  </body>
279
283
  </html>
280
-
@@ -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.2420",
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"