@selesai/code 0.13.12 → 0.13.14
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/CHANGELOG.md +9 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +77 -1
- package/dist/extensions/pi-subagents/VISION.md +12 -1
- package/dist/extensions/pi-subagents/docs/agents.md +13 -6
- package/dist/extensions/pi-subagents/docs/configuration.md +35 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +1 -1
- package/dist/extensions/pi-subagents/docs/missions.md +1 -1
- package/dist/extensions/pi-subagents/docs/models.md +6 -6
- package/dist/extensions/pi-subagents/docs/observability.md +6 -3
- package/dist/extensions/pi-subagents/docs/tool-reference.md +3 -3
- package/dist/extensions/pi-subagents/docs/watchdog.md +92 -114
- package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
- package/dist/extensions/pi-subagents/install.mjs +1 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +6 -6
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +4 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +3 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +120 -124
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -0
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +3 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +20 -0
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +6 -2
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +26 -8
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -23
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +54 -3
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +22 -2
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +63 -6
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +69 -12
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -8
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +59 -6
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +95 -18
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +11 -43
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +24 -3
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -6
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +33 -18
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +73 -37
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +33 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +467 -63
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +3 -1
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +0 -12
- package/dist/extensions/pi-subagents/src/shared/fork-session-cwd.ts +27 -0
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +3 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +42 -4
- package/dist/extensions/pi-subagents/src/shared/utils.ts +18 -7
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +26 -12
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +61 -2
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +12 -7
- package/dist/extensions/pi-subagents/src/tui/render.ts +227 -14
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +56 -34
- package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +77 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +5 -3
- package/dist/extensions/pi-subagents/src/watchdog/guidance.ts +20 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +16 -13
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +10 -9
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +4 -5
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +15 -4
- package/dist/extensions/pi-subagents/src/watchdog/rules.ts +70 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +75 -92
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +48 -104
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +18 -32
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -2
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +439 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +28 -1
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +335 -21
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +22 -18
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +21 -18
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +307 -22
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +25 -0
- package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +15 -4
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -2
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +96 -15
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +53 -5
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +221 -8
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +45 -24
- package/dist/extensions/pi-subagents/test/unit/agent-scan-dirs.test.ts +133 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +18 -4
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +38 -0
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +19 -6
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +22 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +66 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -10
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +136 -18
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +127 -48
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +17 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +9 -3
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +22 -9
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +91 -8
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +73 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +2 -4
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -8
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +48 -3
- package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +36 -118
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +37 -14
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +45 -26
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +50 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +136 -4
- package/dist/extensions/pi-zentui/README.md +19 -5
- package/dist/extensions/pi-zentui/docs/configuration.md +37 -9
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +29 -0
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +31 -2
- package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +55 -11
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +115 -3
- package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +119 -2
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +1768 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-status.ts +69 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +155 -0
- package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +2 -1
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +16 -2
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
- package/dist/extensions/pi-zentui/test/config-load-lifecycle.test.ts +124 -0
- package/dist/extensions/pi-zentui/test/config.test.ts +82 -2
- package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +19 -4
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +58 -9
- package/dist/extensions/pi-zentui/test/package-contents.mjs +33 -0
- package/dist/extensions/pi-zentui/test/prototype-patch-registry.test.ts +131 -0
- package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +4 -4
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +289 -7
- package/dist/extensions/pi-zentui/test/settings-previews.test.ts +106 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +51 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-loader-diagnostics.mjs +54 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-missing-export.test.ts +44 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +297 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +2329 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +1668 -0
- package/dist/extensions/pi-zentui/test/thinking-status.test.ts +85 -0
- package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +122 -0
- package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +58 -0
- package/dist/skills/brandkit/SKILL.md +0 -2
- package/dist/skills/{industrial-brutalist-ui → brutalist}/SKILL.md +1 -3
- package/dist/skills/gpt-taste/SKILL.md +0 -2
- package/dist/skills/image-to-code/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -2
- package/dist/skills/{minimalist-ui → minimalist}/SKILL.md +1 -3
- package/dist/skills/{full-output-enforcement → output}/SKILL.md +1 -3
- package/dist/skills/{redesign-existing-projects → redesign}/SKILL.md +1 -3
- package/dist/skills/{high-end-visual-design → soft}/SKILL.md +1 -3
- package/dist/skills/stitch/DESIGN.md +121 -0
- package/dist/skills/{stitch-design-taste → stitch}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend → taste}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend-v1 → taste-v1}/SKILL.md +2 -4
- package/package.json +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => ({
|
|
6
|
+
...(await importOriginal<typeof import("@earendil-works/pi-coding-agent")>()),
|
|
7
|
+
AssistantMessageComponent: undefined,
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("Streaming (Experimental) optional private export", () => {
|
|
11
|
+
it("loads fail-open and preserves the selected mode when the private constructor is absent", async () => {
|
|
12
|
+
const { ThinkingExperimentalController } = await import(
|
|
13
|
+
"../extensions/zentui/thinking-experimental"
|
|
14
|
+
);
|
|
15
|
+
const config = { enabled: true, mode: "streaming" as const };
|
|
16
|
+
const value = new ThinkingExperimentalController(() => config);
|
|
17
|
+
const ctx = {
|
|
18
|
+
mode: "tui",
|
|
19
|
+
hasUI: true,
|
|
20
|
+
ui: { onTerminalInput: vi.fn() },
|
|
21
|
+
sessionManager: { getEntries: () => [] },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
expect(value.state).toMatchObject({ available: false, active: false });
|
|
25
|
+
expect(value.startSession(ctx as never)).toMatchObject({
|
|
26
|
+
applied: false,
|
|
27
|
+
reason: expect.stringContaining("unavailable"),
|
|
28
|
+
});
|
|
29
|
+
expect(config).toEqual({ enabled: true, mode: "streaming" });
|
|
30
|
+
value.shutdown();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("does not statically import or deep-import the private renderer", () => {
|
|
34
|
+
const source = readFileSync(
|
|
35
|
+
join(import.meta.dirname, "../extensions/zentui/thinking-experimental.ts"),
|
|
36
|
+
"utf8",
|
|
37
|
+
);
|
|
38
|
+
expect(source).not.toMatch(
|
|
39
|
+
/import\s*\{[^}]*AssistantMessageComponent[^}]*\}\s*from\s*["']@earendil-works\/pi-coding-agent["']/s,
|
|
40
|
+
);
|
|
41
|
+
expect(source).not.toMatch(/pi-coding-agent\/(?:dist|src)\//);
|
|
42
|
+
expect(source).toContain("AssistantMessageComponent?: PrivateAssistantConstructor");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { Markdown, type MarkdownTheme, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { createThinkingStepsRows } from "../extensions/zentui/thinking-experimental";
|
|
4
|
+
import { parseThinkingSteps } from "../extensions/zentui/thinking-steps";
|
|
5
|
+
|
|
6
|
+
const identityTheme = Object.fromEntries(
|
|
7
|
+
[
|
|
8
|
+
"heading",
|
|
9
|
+
"link",
|
|
10
|
+
"linkUrl",
|
|
11
|
+
"code",
|
|
12
|
+
"codeBlock",
|
|
13
|
+
"codeBlockBorder",
|
|
14
|
+
"quote",
|
|
15
|
+
"quoteBorder",
|
|
16
|
+
"hr",
|
|
17
|
+
"listBullet",
|
|
18
|
+
"bold",
|
|
19
|
+
"italic",
|
|
20
|
+
"strikethrough",
|
|
21
|
+
"underline",
|
|
22
|
+
].map((key) => [key, (text: string) => text]),
|
|
23
|
+
) as unknown as MarkdownTheme;
|
|
24
|
+
|
|
25
|
+
function plain(row: string): string {
|
|
26
|
+
return row
|
|
27
|
+
.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "")
|
|
28
|
+
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
|
|
29
|
+
.trimEnd();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function rows(
|
|
33
|
+
source: string,
|
|
34
|
+
mode: "rail" | "tree",
|
|
35
|
+
width = 80,
|
|
36
|
+
incomplete = false,
|
|
37
|
+
paddingX = 0,
|
|
38
|
+
markdownTheme = identityTheme,
|
|
39
|
+
options: ConstructorParameters<typeof Markdown>[5] = { renderLatex: true },
|
|
40
|
+
) {
|
|
41
|
+
const style = { color: (text: string) => text, italic: true };
|
|
42
|
+
const native = new Markdown(source, paddingX, 0, markdownTheme, style, options);
|
|
43
|
+
const steps = parseThinkingSteps(source);
|
|
44
|
+
if (!steps) throw new Error("fixture must parse");
|
|
45
|
+
const accent = vi.fn((_: "accent", text: string) => `\x1b[35m${text}\x1b[0m`);
|
|
46
|
+
const component = createThinkingStepsRows(
|
|
47
|
+
native,
|
|
48
|
+
{
|
|
49
|
+
text: source,
|
|
50
|
+
paddingX,
|
|
51
|
+
paddingY: 0,
|
|
52
|
+
theme: markdownTheme,
|
|
53
|
+
defaultTextStyle: style,
|
|
54
|
+
options,
|
|
55
|
+
},
|
|
56
|
+
steps,
|
|
57
|
+
mode,
|
|
58
|
+
incomplete,
|
|
59
|
+
() => ({ fg: accent }),
|
|
60
|
+
);
|
|
61
|
+
return { rendered: component.render(width), component, native, accent };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe("Experimental native Rail/Tree rows", () => {
|
|
65
|
+
it("renders all Rail labels and the latest five Tree labels with active/settled markers", () => {
|
|
66
|
+
const source = Array.from({ length: 7 }, (_, index) => `# Label ${index + 1}`).join("\n");
|
|
67
|
+
expect(rows(source, "rail", 80, true).rendered.map(plain)).toEqual([
|
|
68
|
+
"│ Thinking",
|
|
69
|
+
"│ Label 1",
|
|
70
|
+
"│ Label 2",
|
|
71
|
+
"│ Label 3",
|
|
72
|
+
"│ Label 4",
|
|
73
|
+
"│ Label 5",
|
|
74
|
+
"│ Label 6",
|
|
75
|
+
"│ • Label 7",
|
|
76
|
+
]);
|
|
77
|
+
expect(rows(source, "tree").rendered.map(plain)).toEqual([
|
|
78
|
+
"┆ Thinking",
|
|
79
|
+
"├─ · Label 3",
|
|
80
|
+
"├─ · Label 4",
|
|
81
|
+
"├─ · Label 5",
|
|
82
|
+
"├─ · Label 6",
|
|
83
|
+
"└─ · Label 7",
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it.each(["rail", "tree"] as const)(
|
|
88
|
+
"renders SGR-decorated thinking structurally in %s without carrying source SGR forward",
|
|
89
|
+
(mode) => {
|
|
90
|
+
const truecolor = "\x1b[38;2;137;180;250m";
|
|
91
|
+
const muted = "\x1b[38;2;186;194;222m";
|
|
92
|
+
const reset = "\x1b[39m";
|
|
93
|
+
const source = `${truecolor}# First${reset}\n${muted}body${reset}\n${truecolor}# Second${reset}`;
|
|
94
|
+
const output = rows(source, mode).rendered;
|
|
95
|
+
expect(output.map(plain)).toEqual(
|
|
96
|
+
mode === "rail"
|
|
97
|
+
? ["│ Thinking", "│ First", "│ Second"]
|
|
98
|
+
: ["┆ Thinking", "├─ · First", "└─ · Second"],
|
|
99
|
+
);
|
|
100
|
+
expect(output.join("\n")).not.toContain(truecolor);
|
|
101
|
+
expect(output.join("\n")).not.toContain(muted);
|
|
102
|
+
expect(output.join("\n")).not.toContain(reset);
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
it("fails open when stripping SGR reconstructs image syntax", () => {
|
|
107
|
+
const source = "# ";
|
|
108
|
+
const value = rows(source, "tree");
|
|
109
|
+
expect(value.rendered).toEqual(value.native.render(80));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("preserves native label Markdown callbacks and accents connectors separately", () => {
|
|
113
|
+
const italic = vi.fn((text: string) => `<i>${text}</i>`);
|
|
114
|
+
const code = vi.fn((text: string) => `<c>${text}</c>`);
|
|
115
|
+
const link = vi.fn((text: string) => `<a>${text}</a>`);
|
|
116
|
+
const current = rows("# *emphasis* `code` [link](https://example.com)", "rail", 80, false, 0, {
|
|
117
|
+
...identityTheme,
|
|
118
|
+
italic,
|
|
119
|
+
code,
|
|
120
|
+
link,
|
|
121
|
+
});
|
|
122
|
+
current.rendered.map(plain);
|
|
123
|
+
expect(italic).toHaveBeenCalled();
|
|
124
|
+
expect(code).toHaveBeenCalledWith("code");
|
|
125
|
+
expect(link).toHaveBeenCalled();
|
|
126
|
+
expect(current.accent).toHaveBeenCalledWith("accent", "│ ");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("preserves host transform, HTML, and literal-LaTeX behavior on fresh label Markdown", () => {
|
|
130
|
+
const capabilityTransform = vi.fn(() => "**transformed-capability**");
|
|
131
|
+
const capabilityOutput = plain(
|
|
132
|
+
new Markdown(
|
|
133
|
+
"plain-capability",
|
|
134
|
+
0,
|
|
135
|
+
0,
|
|
136
|
+
{
|
|
137
|
+
...identityTheme,
|
|
138
|
+
bold: (text) => `<b>${text}</b>`,
|
|
139
|
+
},
|
|
140
|
+
undefined,
|
|
141
|
+
{
|
|
142
|
+
transform: capabilityTransform,
|
|
143
|
+
},
|
|
144
|
+
).render(78)[0] ?? "",
|
|
145
|
+
);
|
|
146
|
+
const hostExecutesTransform = capabilityOutput === "<b>transformed-capability</b>";
|
|
147
|
+
expect(capabilityOutput).toBe(
|
|
148
|
+
hostExecutesTransform ? "<b>transformed-capability</b>" : "plain-capability",
|
|
149
|
+
);
|
|
150
|
+
expect(capabilityTransform).toHaveBeenCalledTimes(hostExecutesTransform ? 1 : 0);
|
|
151
|
+
|
|
152
|
+
const source = "native <span>HTML</span> $x^2$";
|
|
153
|
+
const transform = vi.fn((text: string) => text.replace("native", "**native**"));
|
|
154
|
+
const bold = vi.fn((text: string) => `<b>${text}</b>`);
|
|
155
|
+
const theme = { ...identityTheme, bold };
|
|
156
|
+
const options = { transform, renderLatex: false };
|
|
157
|
+
const nativeLabel = plain(
|
|
158
|
+
new Markdown(source, 0, 0, theme, undefined, options).render(78)[0] ?? "",
|
|
159
|
+
);
|
|
160
|
+
expect(nativeLabel).toContain("$x^2$");
|
|
161
|
+
expect(transform).toHaveBeenCalledTimes(hostExecutesTransform ? 1 : 0);
|
|
162
|
+
transform.mockClear();
|
|
163
|
+
bold.mockClear();
|
|
164
|
+
|
|
165
|
+
const output = rows(`# ${source}`, "rail", 80, false, 0, theme, options).rendered.map(plain);
|
|
166
|
+
expect(output[1]).toBe(`│ ${nativeLabel}`);
|
|
167
|
+
if (hostExecutesTransform) {
|
|
168
|
+
expect(transform.mock.calls).toEqual([
|
|
169
|
+
["**Thinking**", 78],
|
|
170
|
+
[source, 78],
|
|
171
|
+
]);
|
|
172
|
+
expect(bold.mock.calls).toEqual([["Thinking"], ["native"]]);
|
|
173
|
+
} else {
|
|
174
|
+
expect(transform).not.toHaveBeenCalled();
|
|
175
|
+
expect(bold.mock.calls).toEqual([["Thinking"]]);
|
|
176
|
+
}
|
|
177
|
+
expect(output[1]).toContain("HTML");
|
|
178
|
+
expect(output[1]).toContain("$x^2$");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it.each([
|
|
182
|
+
{
|
|
183
|
+
name: "ordered-list source markers",
|
|
184
|
+
source: "7) ordered marker",
|
|
185
|
+
option: "preserveOrderedListMarkers" as const,
|
|
186
|
+
expectedWhenSupported: "7) ordered marker",
|
|
187
|
+
expectedWhenDisabled: "7. ordered marker",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "backslash escapes",
|
|
191
|
+
source: String.raw`escaped \*asterisks\*`,
|
|
192
|
+
option: "preserveBackslashEscapes" as const,
|
|
193
|
+
expectedWhenSupported: String.raw`escaped \*asterisks\*`,
|
|
194
|
+
expectedWhenDisabled: "escaped *asterisks*",
|
|
195
|
+
},
|
|
196
|
+
])("preserves $name behavior exactly when the host supports it", (fixture) => {
|
|
197
|
+
const enabledOptions = { [fixture.option]: true, renderLatex: false };
|
|
198
|
+
const disabledOptions = { [fixture.option]: false, renderLatex: false };
|
|
199
|
+
const nativeEnabled = plain(
|
|
200
|
+
new Markdown(fixture.source, 0, 0, identityTheme, undefined, enabledOptions).render(78)[0] ??
|
|
201
|
+
"",
|
|
202
|
+
);
|
|
203
|
+
const nativeDisabled = plain(
|
|
204
|
+
new Markdown(fixture.source, 0, 0, identityTheme, undefined, disabledOptions).render(78)[0] ??
|
|
205
|
+
"",
|
|
206
|
+
);
|
|
207
|
+
const output = rows(
|
|
208
|
+
`# ${fixture.source}`,
|
|
209
|
+
"rail",
|
|
210
|
+
80,
|
|
211
|
+
false,
|
|
212
|
+
0,
|
|
213
|
+
identityTheme,
|
|
214
|
+
enabledOptions,
|
|
215
|
+
).rendered.map(plain);
|
|
216
|
+
|
|
217
|
+
expect(nativeDisabled).toBe(fixture.expectedWhenDisabled);
|
|
218
|
+
expect(output[1]).toBe(`│ ${nativeEnabled}`);
|
|
219
|
+
if (nativeEnabled !== nativeDisabled) {
|
|
220
|
+
expect(nativeEnabled).toBe(fixture.expectedWhenSupported);
|
|
221
|
+
} else {
|
|
222
|
+
expect(output[1]).toBe(`│ ${nativeDisabled}`);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it.each(["界語 emoji 👨👩👧👦", "cafe\u0301 punctuation /().;:"])(
|
|
227
|
+
"crops %s to one ANSI-safe bounded row",
|
|
228
|
+
(label) => {
|
|
229
|
+
for (const width of [10, 20, 80]) {
|
|
230
|
+
const rendered = rows(`# ${label.repeat(10)}`, "tree", width, true).rendered;
|
|
231
|
+
expect(rendered).toHaveLength(2);
|
|
232
|
+
expect(rendered.every((row) => visibleWidth(row) <= width)).toBe(true);
|
|
233
|
+
expect(plain(rendered[1] ?? "")).toContain("…");
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
it("uses one-cell ellipsis, preserves outer padding, and invalidates derived Markdown", () => {
|
|
239
|
+
const value = rows("# long label", "rail", 5, false, 1);
|
|
240
|
+
expect(value.rendered.every((row) => visibleWidth(row) <= 5)).toBe(true);
|
|
241
|
+
expect(plain(value.rendered[1] ?? "")).toBe(" │ …");
|
|
242
|
+
const invalidate = vi.spyOn(value.native, "invalidate");
|
|
243
|
+
value.component.invalidate();
|
|
244
|
+
expect(invalidate).toHaveBeenCalledOnce();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("renders an Error label through native Markdown semantics", () => {
|
|
248
|
+
const value = rows("# Error: **retry** with `safe-code`", "rail");
|
|
249
|
+
expect(value.rendered.map(plain)).toEqual(["│ Thinking", "│ Error: retry with safe-code"]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("fails open to the complete native run for an image label", () => {
|
|
253
|
+
const value = rows("# ", "rail");
|
|
254
|
+
expect(value.rendered).toEqual(value.native.render(80));
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it.each(["empty", "throw"] as const)(
|
|
258
|
+
"fails open to the complete native run when a derived label render is %s",
|
|
259
|
+
(failure) => {
|
|
260
|
+
const original = Markdown.prototype.render;
|
|
261
|
+
const render = vi
|
|
262
|
+
.spyOn(Markdown.prototype, "render")
|
|
263
|
+
.mockImplementation(function derivedFailure(this: Markdown, width: number) {
|
|
264
|
+
const text = (this as unknown as { text?: string }).text;
|
|
265
|
+
if (text === "fallback label") {
|
|
266
|
+
if (failure === "throw") throw new Error("derived render failed");
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
return Reflect.apply(original, this, [width]);
|
|
270
|
+
});
|
|
271
|
+
try {
|
|
272
|
+
const value = rows("# fallback label", "rail");
|
|
273
|
+
expect(value.rendered).toEqual(value.native.render(80));
|
|
274
|
+
} finally {
|
|
275
|
+
render.mockRestore();
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
it("fails open to native rows when current-theme connector rendering throws", () => {
|
|
281
|
+
const source = "# theme fallback";
|
|
282
|
+
const native = new Markdown(source, 0, 0, identityTheme);
|
|
283
|
+
const steps = parseThinkingSteps(source);
|
|
284
|
+
if (!steps) throw new Error("fixture must parse");
|
|
285
|
+
const component = createThinkingStepsRows(
|
|
286
|
+
native,
|
|
287
|
+
{ text: source, paddingX: 0, paddingY: 0, theme: identityTheme },
|
|
288
|
+
steps,
|
|
289
|
+
"tree",
|
|
290
|
+
false,
|
|
291
|
+
() => {
|
|
292
|
+
throw new Error("theme unavailable");
|
|
293
|
+
},
|
|
294
|
+
);
|
|
295
|
+
expect(component.render(80)).toEqual(native.render(80));
|
|
296
|
+
});
|
|
297
|
+
});
|