@trim21/personal-pi-extensions 0.1.587 → 0.1.589
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/package.json +1 -1
- package/src/spawn-agent.ts +41 -75
package/package.json
CHANGED
package/src/spawn-agent.ts
CHANGED
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
* so one log entry is always exactly one rendered line. The final line is
|
|
23
23
|
* always the subagent name as a code span (`` `scout` ``), followed by the
|
|
24
24
|
* live usage stats when there are any; it rides outside the rolling window so
|
|
25
|
-
* it is never trimmed.
|
|
25
|
+
* it is never trimmed. While the model is thinking, a transient
|
|
26
|
+
* `thinking ( N chars )` line sits between the log and the footer showing the
|
|
27
|
+
* live character count of the streamed thinking; it disappears when the
|
|
28
|
+
* thinking block ends.
|
|
26
29
|
*
|
|
27
30
|
* Security default: without an explicit `tools:` in the frontmatter, the
|
|
28
31
|
* subagent only gets read-only tools (read/grep/find/ls) — no bash/write/edit.
|
|
@@ -52,7 +55,6 @@ import {
|
|
|
52
55
|
SessionManager,
|
|
53
56
|
SettingsManager,
|
|
54
57
|
truncateTail,
|
|
55
|
-
truncateToVisualLines,
|
|
56
58
|
} from "@earendil-works/pi-coding-agent";
|
|
57
59
|
import { Type } from "typebox";
|
|
58
60
|
|
|
@@ -148,7 +150,7 @@ interface UsageStats {
|
|
|
148
150
|
turns: number;
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
/** 子 agent 的一次运行结果:runAgent
|
|
153
|
+
/** 子 agent 的一次运行结果:runAgent 的返回值。 */
|
|
152
154
|
interface SubagentResult {
|
|
153
155
|
agent: string;
|
|
154
156
|
task: string;
|
|
@@ -162,8 +164,6 @@ interface SubagentResult {
|
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
interface SubagentDetails {
|
|
165
|
-
/** 子 agent 运行结果(除 pendant 外的字段都收在这里)。 */
|
|
166
|
-
result: SubagentResult;
|
|
167
167
|
/** 折叠 markdown 面板:父 agent 的 prompt 与父 agent 看到的子 agent 结果。 */
|
|
168
168
|
pendant?: ToolPendant;
|
|
169
169
|
}
|
|
@@ -421,6 +421,9 @@ export async function runAgent(
|
|
|
421
421
|
// 不同名按调用顺序罗列;任何非工具行都会打断合并。
|
|
422
422
|
let toolLineSegments: string[] = [];
|
|
423
423
|
let toolLine: { name: string; count: number } | undefined;
|
|
424
|
+
// 思考中状态:thinking_start 打开、thinking_delta 累计字符数、thinking_end
|
|
425
|
+
// 关闭;非 undefined 时 emitUpdate 在 footer 上方插一行瞬态 thinking 状态。
|
|
426
|
+
let thinkingChars: number | undefined;
|
|
424
427
|
|
|
425
428
|
const pushLogLine = (line: string) => {
|
|
426
429
|
logLines.push(line);
|
|
@@ -463,9 +466,12 @@ export async function runAgent(
|
|
|
463
466
|
const usageLine = formatUsageStats(result.usage, result.model);
|
|
464
467
|
const name = sanitizeProgressLine(result.agent);
|
|
465
468
|
const footer = usageLine ? `\`${name}\` ${usageLine}` : `\`${name}\``;
|
|
469
|
+
const lines = [...logLines];
|
|
470
|
+
if (thinkingChars !== undefined) lines.push(`thinking ( ${thinkingChars} chars )`);
|
|
471
|
+
lines.push(footer);
|
|
466
472
|
onUpdate?.({
|
|
467
|
-
content: [{ type: "text", text:
|
|
468
|
-
details: {
|
|
473
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
474
|
+
details: {},
|
|
469
475
|
});
|
|
470
476
|
};
|
|
471
477
|
|
|
@@ -473,11 +479,33 @@ export async function runAgent(
|
|
|
473
479
|
switch (event.type) {
|
|
474
480
|
case "message_update": {
|
|
475
481
|
// A completed text block (text_end carries the full content) becomes a
|
|
476
|
-
// `text:` log line. Deltas
|
|
482
|
+
// `text:` log line. Deltas are intentionally not logged; thinking
|
|
483
|
+
// deltas feed the transient `thinking ( N chars )` status line instead.
|
|
477
484
|
const delta = event.assistantMessageEvent;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
485
|
+
switch (delta.type) {
|
|
486
|
+
case "text_end": {
|
|
487
|
+
pushLogLine(`text: ${foldProgressLine(sanitizeProgressLine(delta.content))}`);
|
|
488
|
+
emitUpdate();
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
case "thinking_start": {
|
|
492
|
+
// 思考横跨轮次边界:打断工具行合并,下一批工具调用另起一行。
|
|
493
|
+
toolLine = undefined;
|
|
494
|
+
thinkingChars = 0;
|
|
495
|
+
emitUpdate();
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case "thinking_delta": {
|
|
499
|
+
thinkingChars = (thinkingChars ?? 0) + delta.delta.length;
|
|
500
|
+
emitUpdate();
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
case "thinking_end": {
|
|
504
|
+
thinkingChars = undefined;
|
|
505
|
+
emitUpdate();
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
// No default
|
|
481
509
|
}
|
|
482
510
|
|
|
483
511
|
break;
|
|
@@ -591,20 +619,7 @@ export default function spawnAgent(pi: ExtensionAPI) {
|
|
|
591
619
|
text: `Unknown agent "${params.agent}". Available agents: ${formatAgentList(agents)}`,
|
|
592
620
|
},
|
|
593
621
|
],
|
|
594
|
-
details: {
|
|
595
|
-
result: {
|
|
596
|
-
agent: params.agent,
|
|
597
|
-
task: params.task,
|
|
598
|
-
exitCode: 1,
|
|
599
|
-
messages: [],
|
|
600
|
-
stderr: "",
|
|
601
|
-
usage: {
|
|
602
|
-
cost: 0,
|
|
603
|
-
contextTokens: 0,
|
|
604
|
-
turns: 0,
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
},
|
|
622
|
+
details: {},
|
|
608
623
|
isError: true,
|
|
609
624
|
};
|
|
610
625
|
}
|
|
@@ -627,7 +642,6 @@ export default function spawnAgent(pi: ExtensionAPI) {
|
|
|
627
642
|
{ type: "text", text: `Subagent "${result.agent}" failed (${reason}):\n${message}` },
|
|
628
643
|
],
|
|
629
644
|
details: {
|
|
630
|
-
result,
|
|
631
645
|
pendant: {
|
|
632
646
|
subtitle: result.agent,
|
|
633
647
|
markdown: formatPendantMarkdown(params.task, message),
|
|
@@ -640,12 +654,11 @@ export default function spawnAgent(pi: ExtensionAPI) {
|
|
|
640
654
|
const output = getFinalOutput(result.messages) || "(no output)";
|
|
641
655
|
const truncation = truncateTail(output, { maxBytes: MAX_OUTPUT_BYTES });
|
|
642
656
|
const text = truncation.truncated
|
|
643
|
-
? `${truncation.content}\n\n[Output truncated to ${formatTokens(truncation.content.length)} bytes.
|
|
657
|
+
? `${truncation.content}\n\n[Output truncated to ${formatTokens(truncation.content.length)} bytes.]`
|
|
644
658
|
: output;
|
|
645
659
|
return {
|
|
646
660
|
content: [{ type: "text", text }],
|
|
647
661
|
details: {
|
|
648
|
-
result,
|
|
649
662
|
pendant: {
|
|
650
663
|
subtitle: result.agent,
|
|
651
664
|
markdown: formatPendantMarkdown(params.task, text),
|
|
@@ -653,52 +666,5 @@ export default function spawnAgent(pi: ExtensionAPI) {
|
|
|
653
666
|
},
|
|
654
667
|
};
|
|
655
668
|
},
|
|
656
|
-
|
|
657
|
-
renderCall(args, theme) {
|
|
658
|
-
const preview = args.task.length > 60 ? `${args.task.slice(0, 60)}...` : args.task;
|
|
659
|
-
const line =
|
|
660
|
-
theme.fg("toolTitle", theme.bold("spawn_agent ")) + theme.fg("accent", args.agent);
|
|
661
|
-
const detail = ` ${theme.fg("dim", preview)}`;
|
|
662
|
-
return {
|
|
663
|
-
render: (width: number) =>
|
|
664
|
-
truncateToVisualLines(`${line}\n${detail}`, 2, width).visualLines,
|
|
665
|
-
invalidate: (): void => undefined,
|
|
666
|
-
};
|
|
667
|
-
},
|
|
668
|
-
|
|
669
|
-
renderResult(result, { expanded }, theme) {
|
|
670
|
-
const details = result.details.result;
|
|
671
|
-
const isError =
|
|
672
|
-
details.exitCode !== 0 ||
|
|
673
|
-
details.stopReason === "error" ||
|
|
674
|
-
details.stopReason === "aborted";
|
|
675
|
-
const icon = isError ? theme.fg("error", "✗") : theme.fg("success", "✓");
|
|
676
|
-
const finalOutput = getFinalOutput(details.messages);
|
|
677
|
-
const usageStr = formatUsageStats(details.usage, details.model);
|
|
678
|
-
|
|
679
|
-
const lines: string[] = [];
|
|
680
|
-
let header = `${icon} ${theme.fg("toolTitle", theme.bold(details.agent))}`;
|
|
681
|
-
if (details.stopReason) header += ` ${theme.fg("error", `[${details.stopReason}]`)}`;
|
|
682
|
-
lines.push(header);
|
|
683
|
-
if (isError && details.errorMessage) {
|
|
684
|
-
lines.push(theme.fg("error", `Error: ${details.errorMessage}`));
|
|
685
|
-
}
|
|
686
|
-
if (expanded) {
|
|
687
|
-
lines.push("", theme.fg("muted", "─── Task ───"), theme.fg("dim", details.task));
|
|
688
|
-
if (finalOutput) {
|
|
689
|
-
lines.push("", theme.fg("muted", "─── Output ───"), finalOutput.trim());
|
|
690
|
-
}
|
|
691
|
-
} else if (finalOutput) {
|
|
692
|
-
lines.push(theme.fg("toolOutput", finalOutput.split("\n").slice(0, 5).join("\n")));
|
|
693
|
-
} else {
|
|
694
|
-
lines.push(theme.fg("muted", "(no output)"));
|
|
695
|
-
}
|
|
696
|
-
if (usageStr) lines.push(theme.fg("dim", usageStr));
|
|
697
|
-
return {
|
|
698
|
-
render: (width: number) =>
|
|
699
|
-
truncateToVisualLines(lines.join("\n"), Infinity, width).visualLines,
|
|
700
|
-
invalidate: (): void => undefined,
|
|
701
|
-
};
|
|
702
|
-
},
|
|
703
669
|
});
|
|
704
670
|
}
|