

























11import { describe, expect, it } from "vitest";
22import { resolveToolSearchCodeDisplayTarget } from "./tool-display-common.js";
3+import { resolveExecDetail } from "./tool-display-exec.js";
34import { formatToolDetail, formatToolSummary, resolveToolDisplay } from "./tool-display.js";
4556describe("tool display details", () => {
@@ -498,3 +499,87 @@ describe("tool display details", () => {
498499}
499500});
500501});
502+503+describe("compactRawCommand middle truncation", () => {
504+it("preserves start and end of long commands", () => {
505+// Use an unknown binary so resolveExecDetail returns the compact raw form directly.
506+const longCommand =
507+"/opt/custom/bin/my-processor --input /data/warehouse/2024/q1/transactions/raw/batch_001.csv --output /data/warehouse/2024/q1/transactions/processed/batch_001_clean.csv";
508+const result = resolveExecDetail({ command: longCommand });
509+// Should contain the start of the command
510+expect(result).toContain("/opt/custom/bin/my-processor");
511+// Should contain the end (filename)
512+expect(result).toContain("batch_001_clean.csv");
513+// Should contain the ellipsis for middle truncation
514+expect(result).toContain("…");
515+// Ellipsis should be in the middle, not at the end
516+expect(result).not.toMatch(/…$/);
517+});
518+519+it("does not truncate short commands", () => {
520+// Use an unknown binary so resolveExecDetail returns the compact raw form directly.
521+const result = resolveExecDetail({ command: "/opt/custom/bin/my-tool --version" });
522+expect(result).toBe("/opt/custom/bin/my-tool --version");
523+});
524+525+it("redacts credential-like tails before middle truncation", () => {
526+// The --token flag and its value sit in the middle of a long command.
527+// Without redaction-before-truncation, middle truncation could cut out
528+// the --token flag context but preserve the raw secret at the tail.
529+const longCommand =
530+"/opt/custom/bin/deploy --region us-east-1 --token sk-proj-ABCDEFGHIJKLMNOP1234567890abcdefghij --output /data/results/deploy-output.json";
531+const result = resolveExecDetail({ command: longCommand });
532+// The sk- prefixed token must be redacted (masked) before truncation
533+expect(result).not.toContain("ABCDEFGHIJKLMNOP1234567890abcdefghij");
534+});
535+});
536+537+describe("coerceDisplayValue middle truncation", () => {
538+it("preserves start and end of long string values", () => {
539+const longPath =
540+"/usr/local/share/very/deeply/nested/directory/structure/" +
541+"a".repeat(150) +
542+"/important-file.txt";
543+const detail = formatToolDetail(
544+resolveToolDisplay({
545+name: "sessions_spawn",
546+args: { task: longPath },
547+}),
548+);
549+// Should contain the start of the path
550+expect(detail).toContain("/usr/local/share/");
551+// Should contain the end (filename)
552+expect(detail).toContain("important-file.txt");
553+// Should contain the ellipsis for middle truncation
554+expect(detail).toContain("…");
555+});
556+557+it("does not truncate short string values", () => {
558+const detail = formatToolDetail(
559+resolveToolDisplay({
560+name: "sessions_spawn",
561+args: { task: "short-task-name" },
562+}),
563+);
564+expect(detail).toBe("short-task-name");
565+expect(detail).not.toContain("…");
566+});
567+568+it("redacts credential-like values in long generic string details", () => {
569+// A long string whose tail contains a GitHub PAT. Without
570+// redaction-before-truncation, middle truncation could preserve
571+// the raw token at the tail after its prefix context is cut.
572+const longValue =
573+"Deploying service to production cluster with auth ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop and " +
574+"x".repeat(200) +
575+" final-step";
576+const detail = formatToolDetail(
577+resolveToolDisplay({
578+name: "sessions_spawn",
579+args: { task: longValue },
580+}),
581+);
582+// The ghp_ token must be redacted before truncation
583+expect(detail).not.toContain("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop");
584+});
585+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。