@yemi33/minions 0.1.2346 → 0.1.2347

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.
package/dashboard.js CHANGED
@@ -6973,12 +6973,35 @@ const server = http.createServer(async (req, res) => {
6973
6973
  if (target.error) return jsonReply(res, 404, { error: target.error });
6974
6974
  const wiPath = target.wiPath;
6975
6975
 
6976
+ // W-mrazefzz000358e3 — body.project reassigns which projects/<name>/
6977
+ // work-items.json (or central) file owns this item. Resolve + validate
6978
+ // it up front (mirrors resolveWorkItemsCreateTarget's create-path
6979
+ // contract) so an unknown project name 400s cleanly instead of the
6980
+ // historical silent no-op. `projectMove` stays null when body.project
6981
+ // is omitted (no location change requested).
6982
+ let projectMove = null;
6983
+ if (body.project !== undefined) {
6984
+ const projTarget = resolveProjectSourceTarget(body.project, PROJECTS);
6985
+ if (projTarget.error) {
6986
+ return jsonReply(res, 400, {
6987
+ error: projTarget.error,
6988
+ knownProjects: PROJECTS.map(p => p.name),
6989
+ });
6990
+ }
6991
+ projectMove = { wiPath: projTarget.wiPath, projectName: projTarget.project ? projTarget.project.name : null };
6992
+ }
6993
+
6976
6994
  let result = null;
6977
6995
  let agentChanged = false;
6996
+ // Set inside the source-file lock below when a project move is needed;
6997
+ // inserted into the target file's own lock scope afterward so we never
6998
+ // hold both files' locks at once.
6999
+ let movedItem = null;
6978
7000
  mutateJsonFileLocked(wiPath, (items) => {
6979
7001
  if (!Array.isArray(items)) items = [];
6980
- const item = items.find(i => i.id === id);
6981
- if (!item) { result = { code: 404, body: { error: 'item not found' } }; return items; }
7002
+ const idx = items.findIndex(i => i.id === id);
7003
+ if (idx === -1) { result = { code: 404, body: { error: 'item not found' } }; return items; }
7004
+ const item = items[idx];
6982
7005
  if (item.status === WI_STATUS.DISPATCHED) { result = { code: 400, body: { error: 'Cannot edit dispatched items' } }; return items; }
6983
7006
 
6984
7007
  if (title !== undefined) item.title = title;
@@ -7010,10 +7033,32 @@ const server = http.createServer(async (req, res) => {
7010
7033
  }
7011
7034
  }
7012
7035
  item.updatedAt = new Date().toISOString();
7036
+
7037
+ // W-mrazefzz000358e3 — actually MOVE the item between work-items.json
7038
+ // files when the resolved target differs from where it lives today,
7039
+ // rather than just stamping item.project in place. Only trip this
7040
+ // when the resolved wiPath actually changes — re-assigning the same
7041
+ // project the item already lives in is a no-op move.
7042
+ if (projectMove && path.resolve(projectMove.wiPath) !== path.resolve(wiPath)) {
7043
+ if (projectMove.projectName) item.project = projectMove.projectName;
7044
+ else delete item.project;
7045
+ movedItem = item;
7046
+ items.splice(idx, 1);
7047
+ }
7048
+
7013
7049
  result = { code: 200, body: { ok: true, item } };
7014
7050
  return items;
7015
7051
  });
7016
7052
  if (!result) return jsonReply(res, 500, { error: 'unexpected state' });
7053
+
7054
+ if (movedItem) {
7055
+ mutateJsonFileLocked(projectMove.wiPath, (targetItems) => {
7056
+ if (!Array.isArray(targetItems)) targetItems = [];
7057
+ targetItems.push(movedItem);
7058
+ return targetItems;
7059
+ });
7060
+ }
7061
+
7017
7062
  // Clear stale pending dispatch entries outside lock
7018
7063
  if (agentChanged) cleanDispatchEntries(d => d.meta?.item?.id === id);
7019
7064
  return jsonReply(res, result.code, result.body);
@@ -9886,10 +9931,20 @@ What would you like to discuss or change? When you're happy, say "approve" and I
9886
9931
  * test enforces engine.js does not import cc-worker-pool).
9887
9932
  */
9888
9933
  function _invokeCcStream({ prompt, sessionId, liveState, toolUses, model, effort, maxTurns, engineConfig, systemPrompt = CC_STATIC_SYSTEM_PROMPT, tabId, images }) {
9889
- if (shared.resolveCcUseWorkerPool(engineConfig)) {
9890
- // The ACP worker pool is copilot-only (shared.resolveCcUseWorkerPool
9891
- // forces it OFF for non-copilot CC) and copilot has imageInput:false, so
9892
- // image turns never reach this branch — drop `images` here intentionally.
9934
+ // W-mray463s001zcee2 — image-bearing turns bypass the worker pool.
9935
+ // Copilot itself supports images fine (imageInput: true, `--attachment
9936
+ // <path>` in engine/runtimes/copilot.js buildArgs), but the ACP worker-pool
9937
+ // protocol (engine/cc-worker-pool.js session/prompt) only ever sends
9938
+ // `[{ type: 'text', text: prompt }]` — it has no image/attachment
9939
+ // content-block support today. That is a worker-pool PROTOCOL gap, not a
9940
+ // Copilot capability gap (the old comment here claiming "copilot has
9941
+ // imageInput:false" was factually wrong). Until the pool's ACP session
9942
+ // protocol is extended to carry attachments, route image turns around the
9943
+ // pool for THIS TURN ONLY: cold-spawn a one-off CLI process via the
9944
+ // already-working --attachment path below, while image-less turns keep
9945
+ // using the fast warm pool.
9946
+ const hasImages = Array.isArray(images) && images.length > 0;
9947
+ if (!hasImages && shared.resolveCcUseWorkerPool(engineConfig)) {
9893
9948
  return _invokeCcStreamViaPool({ prompt, liveState, toolUses, model, effort, engineConfig, systemPrompt, tabId });
9894
9949
  }
9895
9950
  const { callLLMStreaming } = require('./engine/llm');
@@ -13514,7 +13569,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
13514
13569
 
13515
13570
  // Work items
13516
13571
  { method: 'POST', path: '/api/work-items', desc: 'Create a new work item', params: 'title, type?, description?, priority?, project?, agent?, agents?, scope?, references?, acceptanceCriteria?, skipPr?, oneShot?, meta?, meta.pr_followup?, meta.workdir?, X-Minions-Agent?, X-Minions-Origin-Wi?', handler: handleWorkItemsCreate },
13517
- { method: 'POST', path: '/api/work-items/update', desc: 'Edit a pending/failed work item', params: 'id, source?, title?, description?, type?, priority?, agent?, references?, acceptanceCriteria?, depends_on?, workdir?, meta?', handler: handleWorkItemsUpdate },
13572
+ { method: 'POST', path: '/api/work-items/update', desc: 'Edit a pending/failed work item', params: 'id, source?, title?, description?, type?, priority?, agent?, project?, references?, acceptanceCriteria?, depends_on?, workdir?, meta?', handler: handleWorkItemsUpdate },
13518
13573
  { method: 'POST', path: '/api/work-items/retry', desc: 'Reset a failed/dispatched item to pending', params: 'id, source?', handler: handleWorkItemsRetry },
13519
13574
  { method: 'POST', path: '/api/work-items/delete', desc: 'Remove a work item, kill agent, clear dispatch', params: 'id, source?', handler: handleWorkItemsDelete },
13520
13575
  { method: 'POST', path: '/api/work-items/cancel', desc: 'Cancel a work item, kill agent, clear dispatch', params: 'id, source?, reason?', handler: handleWorkItemsCancel },
@@ -65,9 +65,11 @@ Any violation rejects the **whole turn** with a typed error envelope (`code: 'in
65
65
  | Runtime | `imageInput` | Behavior |
66
66
  |---------|--------------|----------|
67
67
  | claude | `true` | Images delivered as an Anthropic Messages `content[]` envelope of base64 blocks over stream-json (`claude.buildPrompt` + `--input-format stream-json` in `claude.buildArgs`). |
68
- | copilot | `true` | Images materialized to tmp files and passed as `--attachment <path>` (W-mqv7324u0021db5d). **Exception:** when the opt-in Copilot worker pool is active (`engine.ccUseWorkerPool: true`), images are silently dropped — the pool's ACP `stream()` does not forward images; a note in `_invokeCcStream` marks this intentional. |
68
+ | copilot | `true` | Images materialized to tmp files and passed as `--attachment <path>` (W-mqv7324u0021db5d). |
69
69
  | codex | `false` | Degraded — same typed `model-unavailable` envelope. |
70
70
 
71
+ **Worker-pool interaction (W-mray463s001zcee2).** The opt-in Copilot ACP worker pool (`engine.ccUseWorkerPool: true`, default ON for Copilot CC) is a **separate protocol path** from the table above — `engine/cc-worker-pool.js`'s `session/prompt` call only ever sends a single text content block (`[{ type: 'text', text: prompt }]`) and has no image/attachment content-block support today. This used to mean image turns were silently dropped whenever the pool was active, with a comment incorrectly blaming a Copilot capability gap (`imageInput:false`) that doesn't exist — Copilot's `imageInput` is `true` and fully supports images on the direct spawn path. The fix: `_invokeCcStream` (dashboard.js) now checks for a non-empty `images` array **before** routing through `resolveCcUseWorkerPool` — image-bearing turns bypass the warm pool for that turn only and cold-spawn a one-off CLI process via the working `--attachment` path (same as the non-pooled path always used), while image-less turns keep using the fast warm pool. If `engine/cc-worker-pool.js`'s ACP session protocol is later extended to carry attachments (Option A), this per-turn bypass in `_invokeCcStream` should be revisited/removed.
72
+
71
73
  `_resolveImageOpts` (in `engine/llm.js`) is pure and unit-tested: it forwards the `images` list only when `runtime.capabilities.imageInput` is truthy, otherwise returns the typed error so CC/doc-chat surface the envelope instead of a silently-text-only reply. `_spawnProcess` re-applies the same gate (`if (!caps.imageInput) adapterOpts.images = undefined`) as defense in depth. Image **filenames** are run through the untrusted-input fence (`buildSource('cc-image-filename', …)`) before they reach prompt text, since they are user-supplied.
72
74
 
73
75
  **No new `engine.*` flag.** The limits above are module-level constants in `dashboard.js` (`CC_IMAGE_MAX_COUNT`, `CC_IMAGE_MAX_DECODED_BYTES`, `CC_IMAGE_MIME_ALLOWLIST`), not config keys; `engine/shared.js` `ENGINE_DEFAULTS` was not touched. Per CLAUDE.md Best Practice #9 (Settings parity), no Settings toggle is added because no new `engine.*` flag was introduced. Runtime selection that determines whether images are accepted is the existing `engine.ccCli` / `ccModel` override, which already has a Settings control.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2346",
3
+ "version": "0.1.2347",
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"