@yemi33/minions 0.1.2335 → 0.1.2337

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.
@@ -125,7 +125,7 @@ evaluation pass) can see what tooling drove a dispatch:
125
125
  and ⚠️-marked), and returns `''` for an empty/absent record. The GitHub
126
126
  comment path folds it in via `engine/gh-comment.js#buildMinionsCommentBody`
127
127
  (optional `harnessUsed` arg, threaded through `postPrComment` /
128
- `postPrReviewComment` / `postPrReview`); the Azure DevOps path mirrors it via
128
+ `postPrReviewComment`); the Azure DevOps path mirrors it via
129
129
  `engine/ado-comment.js#postAdoPrComment` (same builder, same `harnessUsed`
130
130
  arg). Both posters are reached from the `minions pr comment` CLI, which turns
131
131
  `--harness-file` / `--harness-json` into the grounded record and folds the
@@ -210,50 +210,6 @@ function postPrReviewComment({
210
210
  }
211
211
  }
212
212
 
213
- const _REVIEW_EVENT_FLAGS = Object.freeze({
214
- APPROVE: '--approve',
215
- REQUEST_CHANGES: '--request-changes',
216
- COMMENT: '--comment',
217
- });
218
-
219
- function postPrReview({
220
- event,
221
- repo,
222
- prNumber,
223
- body,
224
- agentId,
225
- kind,
226
- workItemId,
227
- harnessUsed,
228
- skillSkipped,
229
- timeoutMs = 30000,
230
- execFileSync = _execFileSync,
231
- resolveTokenForSlug,
232
- } = {}) {
233
- const flag = _REVIEW_EVENT_FLAGS[event];
234
- if (!flag) {
235
- throw new Error(
236
- `invalid event: ${JSON.stringify(event)} (expected APPROVE | REQUEST_CHANGES | COMMENT)`,
237
- );
238
- }
239
- _validateRepo(repo);
240
- _validatePrNumber(prNumber);
241
- const finalBody = buildMinionsCommentBody({ agentId, kind, workItemId, body, harnessUsed, skillSkipped });
242
- const file = _writeTempBodyFile(finalBody);
243
- const env = _resolveTokenEnvForRepo(repo, resolveTokenForSlug);
244
- try {
245
- const output = _runGh(
246
- execFileSync,
247
- ['pr', 'review', String(prNumber), flag, '--repo', repo, '--body-file', file],
248
- timeoutMs,
249
- env,
250
- );
251
- return { output: String(output || '').trim(), bodyFile: file };
252
- } finally {
253
- try { fs.unlinkSync(file); } catch {}
254
- }
255
- }
256
-
257
213
  module.exports = {
258
214
  // Builders / parsers (pure functions — usable from anywhere)
259
215
  buildMinionsCommentBody,
@@ -271,7 +227,6 @@ module.exports = {
271
227
  // gh wrappers (argv-form, --body-file)
272
228
  postPrComment,
273
229
  postPrReviewComment,
274
- postPrReview,
275
230
  // Internal helpers exported for tests / advanced callers
276
231
  _buildMarker,
277
232
  _writeTempBodyFile,
@@ -652,8 +652,26 @@ function createStreamConsumer(ctx) {
652
652
  let claudeJoinedText = '';
653
653
  const claudeStreamBlocks = new Map();
654
654
 
655
+ // Only the TRAILING run of text blocks (since the last non-text block)
656
+ // contributes to claudeJoinedText — a tool_use/thinking block closes the
657
+ // run so an earlier text block never welds onto a later one within the
658
+ // SAME message (W-mr9jj2vx000 — CC response duplicates/welds the final
659
+ // answer after ~15 tool calls). Claude can emit content as
660
+ // [text, tool_use, tool_use, ..., text] in one message when
661
+ // --include-partial-messages streams multiple parallel tool calls before
662
+ // a trailing answer; without this, content_block_delta blindly appended
663
+ // every text delta onto the same buffer regardless of the tool_use block
664
+ // in between, producing "<narration><tool icon><narration><answer>" with
665
+ // no paragraph break and no new segment (segmentSeq never bumped because
666
+ // stream-event tool_use blocks don't call ctx.pushToolUse).
655
667
  function _rebuildClaudeJoinedText() {
656
- claudeJoinedText = Array.from(claudeStreamBlocks.keys()).sort((a, b) => a - b)
668
+ const indices = Array.from(claudeStreamBlocks.keys()).sort((a, b) => a - b);
669
+ let start = 0;
670
+ for (let i = indices.length - 1; i >= 0; i--) {
671
+ const block = claudeStreamBlocks.get(indices[i]);
672
+ if (!block || block.type !== 'text') { start = i + 1; break; }
673
+ }
674
+ claudeJoinedText = indices.slice(start)
657
675
  .map(index => claudeStreamBlocks.get(index))
658
676
  .filter(block => block && block.type === 'text' && block.text)
659
677
  .map(block => block.text)
@@ -673,6 +691,13 @@ function createStreamConsumer(ctx) {
673
691
  const block = event.content_block || {};
674
692
  claudeStreamBlocks.set(index, { type: block.type || '', text: block.text || '' });
675
693
  if (THINKING_BLOCK_TYPES.has(block.type)) ctx.notifyThinking();
694
+ // A non-text block (tool_use/thinking) closes the current text run —
695
+ // reset the hot-path accumulator so the NEXT text block starts fresh
696
+ // instead of welding onto text from before this block. `ctx.pushText`
697
+ // already opens a new segment when the pushed value doesn't extend
698
+ // `lastTextSent` (see engine/llm.js), so this reset is sufficient to
699
+ // both stop the weld and trigger the segment boundary.
700
+ if (block.type !== 'text') claudeJoinedText = '';
676
701
  // Out-of-order block landing: rebuild from the Map. Common case is
677
702
  // monotonic in-order arrival, where the trailing-append branch wins.
678
703
  const indices = Array.from(claudeStreamBlocks.keys());
@@ -735,21 +760,27 @@ function createStreamConsumer(ctx) {
735
760
 
736
761
  if (obj.type === 'assistant' && Array.isArray(obj.message?.content)) {
737
762
  // Claude assistant turn: content blocks (text + tool_use).
738
- // Multi-text-block messages (with --include-partial-messages) need their
739
- // text JOINED before pushText, otherwise each block overwrites the prior.
763
+ // Multi-text-block messages (with --include-partial-messages) need
764
+ // consecutive text blocks JOINED before pushText, otherwise each block
765
+ // overwrites the prior. But a tool_use block closes the current text
766
+ // run — reset the accumulator on tool_use so trailing text starts a
767
+ // fresh segment instead of welding onto text from before the tool
768
+ // call(s) (W-mr9jj2vx0004bbb9 follow-up: this full-envelope path hits
769
+ // the same weld bug as the stream_event path above).
740
770
  let assistantText = '';
741
771
  for (const block of obj.message.content) {
742
772
  if (block?.type === 'text' && block.text) {
743
773
  assistantText += block.text;
774
+ ctx.pushText(assistantText);
744
775
  } else if (THINKING_BLOCK_TYPES.has(block?.type)) {
745
776
  ctx.notifyThinking();
746
777
  } else if (block?.type === 'tool_use' && block.name) {
747
778
  // Thread the tool_use id so the dashboard can flip this chip from
748
779
  // pending → completed/failed when the matching tool_result lands.
749
780
  ctx.pushToolUse(block.name, block.input || {}, block.id);
781
+ assistantText = '';
750
782
  }
751
783
  }
752
- if (assistantText) ctx.pushText(assistantText);
753
784
  }
754
785
 
755
786
  if (obj.type === 'user' && Array.isArray(obj.message?.content)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2335",
3
+ "version": "0.1.2337",
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"