@mariozechner/pi-coding-agent 0.30.2 → 0.31.0
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 +244 -1
- package/README.md +105 -84
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +5 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts +3 -3
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +7 -10
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +73 -34
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +464 -210
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -2
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +2 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -2
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -2
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +84 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/core/compaction/branch-summarization.js +233 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -0
- package/dist/core/{compaction.d.ts → compaction/compaction.d.ts} +38 -19
- package/dist/core/compaction/compaction.d.ts.map +1 -0
- package/dist/core/compaction/compaction.js +558 -0
- package/dist/core/compaction/compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +7 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +35 -0
- package/dist/core/compaction/utils.d.ts.map +1 -0
- package/dist/core/compaction/utils.js +138 -0
- package/dist/core/compaction/utils.js.map +1 -0
- package/dist/core/custom-tools/index.d.ts +2 -1
- package/dist/core/custom-tools/index.d.ts.map +1 -1
- package/dist/core/custom-tools/index.js +1 -0
- package/dist/core/custom-tools/index.js.map +1 -1
- package/dist/core/custom-tools/loader.d.ts.map +1 -1
- package/dist/core/custom-tools/loader.js +13 -80
- package/dist/core/custom-tools/loader.js.map +1 -1
- package/dist/core/custom-tools/types.d.ts +84 -59
- package/dist/core/custom-tools/types.d.ts.map +1 -1
- package/dist/core/custom-tools/types.js.map +1 -1
- package/dist/core/custom-tools/wrapper.d.ts +15 -0
- package/dist/core/custom-tools/wrapper.d.ts.map +1 -0
- package/dist/core/custom-tools/wrapper.js +23 -0
- package/dist/core/custom-tools/wrapper.js.map +1 -0
- package/dist/core/exec.d.ts +29 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +71 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/export-html/index.d.ts +17 -0
- package/dist/core/export-html/index.d.ts.map +1 -0
- package/dist/core/export-html/index.js +171 -0
- package/dist/core/export-html/index.js.map +1 -0
- package/dist/core/export-html/template.css +781 -0
- package/dist/core/export-html/template.html +54 -0
- package/dist/core/export-html/template.js +1185 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +6 -0
- package/dist/core/hooks/index.d.ts +4 -4
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/index.js +3 -3
- package/dist/core/hooks/index.js.map +1 -1
- package/dist/core/hooks/loader.d.ts +40 -5
- package/dist/core/hooks/loader.d.ts.map +1 -1
- package/dist/core/hooks/loader.js +43 -10
- package/dist/core/hooks/loader.js.map +1 -1
- package/dist/core/hooks/runner.d.ts +94 -18
- package/dist/core/hooks/runner.d.ts.map +1 -1
- package/dist/core/hooks/runner.js +199 -120
- package/dist/core/hooks/runner.js.map +1 -1
- package/dist/core/hooks/tool-wrapper.d.ts +1 -1
- package/dist/core/hooks/tool-wrapper.d.ts.map +1 -1
- package/dist/core/hooks/tool-wrapper.js +36 -19
- package/dist/core/hooks/tool-wrapper.js.map +1 -1
- package/dist/core/hooks/types.d.ts +407 -96
- package/dist/core/hooks/types.d.ts.map +1 -1
- package/dist/core/hooks/types.js.map +1 -1
- package/dist/core/index.d.ts +4 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/messages.d.ts +44 -12
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +82 -34
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -5
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +7 -7
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +7 -7
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +45 -14
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/sdk.d.ts +7 -10
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +88 -32
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +202 -36
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +565 -133
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +9 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +13 -12
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +6 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +33 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +171 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +7 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -95
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +22 -21
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +3 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +6 -2
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +12 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.js +30 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +14 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.js +35 -0
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +14 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +36 -0
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +5 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +5 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +12 -6
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +57 -25
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/hook-editor.d.ts +15 -0
- package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/hook-editor.js +95 -0
- package/dist/modes/interactive/components/hook-editor.js.map +1 -0
- package/dist/modes/interactive/components/hook-message.d.ts +18 -0
- package/dist/modes/interactive/components/hook-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/hook-message.js +80 -0
- package/dist/modes/interactive/components/hook-message.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +3 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +15 -2
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +70 -21
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +52 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-selector.js +745 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +3 -3
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +1 -1
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -5
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +29 -12
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +589 -208
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +13 -1
- package/dist/modes/interactive/theme/light.json +13 -1
- package/dist/modes/interactive/theme/theme-schema.json +34 -0
- package/dist/modes/interactive/theme/theme.d.ts +20 -2
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +135 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +3 -3
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +26 -20
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +13 -10
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +11 -10
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +88 -35
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +30 -11
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/shell.d.ts +4 -2
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +36 -7
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +2 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +388 -0
- package/docs/custom-tools.md +146 -43
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +562 -596
- package/docs/rpc.md +33 -19
- package/docs/sdk.md +93 -21
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +172 -21
- package/docs/skills.md +2 -0
- package/docs/theme.md +31 -2
- package/docs/tree.md +197 -0
- package/docs/tui.md +343 -0
- package/examples/README.md +1 -9
- package/examples/custom-tools/hello/index.ts +4 -3
- package/examples/custom-tools/question/index.ts +4 -4
- package/examples/custom-tools/subagent/index.ts +7 -6
- package/examples/custom-tools/todo/index.ts +11 -5
- package/examples/hooks/README.md +29 -71
- package/examples/hooks/auto-commit-on-exit.ts +8 -9
- package/examples/hooks/confirm-destructive.ts +29 -30
- package/examples/hooks/custom-compaction.ts +20 -21
- package/examples/hooks/dirty-repo-guard.ts +41 -40
- package/examples/hooks/file-trigger.ts +10 -5
- package/examples/hooks/git-checkpoint.ts +16 -12
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +1 -1
- package/examples/hooks/protected-paths.ts +1 -1
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/05-tools.ts +4 -4
- package/examples/sdk/06-hooks.ts +1 -1
- package/examples/sdk/07-context-files.ts +1 -1
- package/examples/sdk/08-slash-commands.ts +6 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
- package/examples/sdk/10-settings.ts +1 -1
- package/examples/sdk/11-sessions.ts +1 -1
- package/examples/sdk/12-full-control.ts +4 -7
- package/package.json +6 -6
- package/dist/core/compaction.d.ts.map +0 -1
- package/dist/core/compaction.js +0 -412
- package/dist/core/compaction.js.map +0 -1
- package/dist/core/export-html.d.ts +0 -23
- package/dist/core/export-html.d.ts.map +0 -1
- package/dist/core/export-html.js +0 -1185
- package/dist/core/export-html.js.map +0 -1
- package/dist/modes/interactive/components/compaction.d.ts +0 -15
- package/dist/modes/interactive/components/compaction.d.ts.map +0 -1
- package/dist/modes/interactive/components/compaction.js +0 -41
- package/dist/modes/interactive/components/compaction.js.map +0 -1
- package/docs/hooks-v2.md +0 -385
- package/docs/session-tree.md +0 -452
package/dist/core/export-html.js
DELETED
|
@@ -1,1185 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import hljs from "highlight.js";
|
|
3
|
-
import { marked } from "marked";
|
|
4
|
-
import { homedir } from "os";
|
|
5
|
-
import * as path from "path";
|
|
6
|
-
import { basename } from "path";
|
|
7
|
-
import { APP_NAME, getCustomThemesDir, getThemesDir, VERSION } from "../config.js";
|
|
8
|
-
import { isBashExecutionMessage } from "./messages.js";
|
|
9
|
-
/** Resolve a theme color value, following variable references until we get a final value. */
|
|
10
|
-
function resolveColorValue(value, vars, defaultValue, visited = new Set()) {
|
|
11
|
-
if (value === "")
|
|
12
|
-
return defaultValue;
|
|
13
|
-
if (typeof value !== "string")
|
|
14
|
-
return defaultValue;
|
|
15
|
-
if (visited.has(value))
|
|
16
|
-
return defaultValue;
|
|
17
|
-
if (!(value in vars))
|
|
18
|
-
return value; // Return as-is (hex colors work in CSS)
|
|
19
|
-
visited.add(value);
|
|
20
|
-
return resolveColorValue(vars[value], vars, defaultValue, visited);
|
|
21
|
-
}
|
|
22
|
-
/** Load theme JSON from built-in or custom themes directory. */
|
|
23
|
-
function loadThemeJson(name) {
|
|
24
|
-
// Try built-in themes first
|
|
25
|
-
const themesDir = getThemesDir();
|
|
26
|
-
const builtinPath = path.join(themesDir, `${name}.json`);
|
|
27
|
-
if (existsSync(builtinPath)) {
|
|
28
|
-
try {
|
|
29
|
-
return JSON.parse(readFileSync(builtinPath, "utf-8"));
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
// Try custom themes
|
|
36
|
-
const customThemesDir = getCustomThemesDir();
|
|
37
|
-
const customPath = path.join(customThemesDir, `${name}.json`);
|
|
38
|
-
if (existsSync(customPath)) {
|
|
39
|
-
try {
|
|
40
|
-
return JSON.parse(readFileSync(customPath, "utf-8"));
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
/** Build complete theme colors object, resolving theme JSON values against defaults. */
|
|
49
|
-
function getThemeColors(themeName) {
|
|
50
|
-
const isLight = isLightTheme(themeName);
|
|
51
|
-
// Default colors based on theme type
|
|
52
|
-
const defaultColors = isLight
|
|
53
|
-
? {
|
|
54
|
-
// Light theme defaults
|
|
55
|
-
accent: "rgb(95, 135, 135)",
|
|
56
|
-
border: "rgb(95, 135, 175)",
|
|
57
|
-
borderAccent: "rgb(95, 135, 135)",
|
|
58
|
-
success: "rgb(135, 175, 135)",
|
|
59
|
-
error: "rgb(175, 95, 95)",
|
|
60
|
-
warning: "rgb(215, 175, 95)",
|
|
61
|
-
muted: "rgb(108, 108, 108)",
|
|
62
|
-
dim: "rgb(138, 138, 138)",
|
|
63
|
-
text: "rgb(0, 0, 0)",
|
|
64
|
-
userMessageBg: "rgb(232, 232, 232)",
|
|
65
|
-
userMessageText: "rgb(0, 0, 0)",
|
|
66
|
-
toolPendingBg: "rgb(232, 232, 240)",
|
|
67
|
-
toolSuccessBg: "rgb(232, 240, 232)",
|
|
68
|
-
toolErrorBg: "rgb(240, 232, 232)",
|
|
69
|
-
toolOutput: "rgb(108, 108, 108)",
|
|
70
|
-
mdHeading: "rgb(215, 175, 95)",
|
|
71
|
-
mdLink: "rgb(95, 135, 175)",
|
|
72
|
-
mdLinkUrl: "rgb(138, 138, 138)",
|
|
73
|
-
mdCode: "rgb(95, 135, 135)",
|
|
74
|
-
mdCodeBlock: "rgb(135, 175, 135)",
|
|
75
|
-
mdCodeBlockBorder: "rgb(108, 108, 108)",
|
|
76
|
-
mdQuote: "rgb(108, 108, 108)",
|
|
77
|
-
mdQuoteBorder: "rgb(108, 108, 108)",
|
|
78
|
-
mdHr: "rgb(108, 108, 108)",
|
|
79
|
-
mdListBullet: "rgb(135, 175, 135)",
|
|
80
|
-
toolDiffAdded: "rgb(135, 175, 135)",
|
|
81
|
-
toolDiffRemoved: "rgb(175, 95, 95)",
|
|
82
|
-
toolDiffContext: "rgb(108, 108, 108)",
|
|
83
|
-
syntaxComment: "rgb(0, 128, 0)",
|
|
84
|
-
syntaxKeyword: "rgb(0, 0, 255)",
|
|
85
|
-
syntaxFunction: "rgb(121, 94, 38)",
|
|
86
|
-
syntaxVariable: "rgb(0, 16, 128)",
|
|
87
|
-
syntaxString: "rgb(163, 21, 21)",
|
|
88
|
-
syntaxNumber: "rgb(9, 134, 88)",
|
|
89
|
-
syntaxType: "rgb(38, 127, 153)",
|
|
90
|
-
syntaxOperator: "rgb(0, 0, 0)",
|
|
91
|
-
syntaxPunctuation: "rgb(0, 0, 0)",
|
|
92
|
-
}
|
|
93
|
-
: {
|
|
94
|
-
// Dark theme defaults
|
|
95
|
-
accent: "rgb(138, 190, 183)",
|
|
96
|
-
border: "rgb(95, 135, 255)",
|
|
97
|
-
borderAccent: "rgb(0, 215, 255)",
|
|
98
|
-
success: "rgb(181, 189, 104)",
|
|
99
|
-
error: "rgb(204, 102, 102)",
|
|
100
|
-
warning: "rgb(255, 255, 0)",
|
|
101
|
-
muted: "rgb(128, 128, 128)",
|
|
102
|
-
dim: "rgb(102, 102, 102)",
|
|
103
|
-
text: "rgb(229, 229, 231)",
|
|
104
|
-
userMessageBg: "rgb(52, 53, 65)",
|
|
105
|
-
userMessageText: "rgb(229, 229, 231)",
|
|
106
|
-
toolPendingBg: "rgb(40, 40, 50)",
|
|
107
|
-
toolSuccessBg: "rgb(40, 50, 40)",
|
|
108
|
-
toolErrorBg: "rgb(60, 40, 40)",
|
|
109
|
-
toolOutput: "rgb(128, 128, 128)",
|
|
110
|
-
mdHeading: "rgb(240, 198, 116)",
|
|
111
|
-
mdLink: "rgb(129, 162, 190)",
|
|
112
|
-
mdLinkUrl: "rgb(102, 102, 102)",
|
|
113
|
-
mdCode: "rgb(138, 190, 183)",
|
|
114
|
-
mdCodeBlock: "rgb(181, 189, 104)",
|
|
115
|
-
mdCodeBlockBorder: "rgb(128, 128, 128)",
|
|
116
|
-
mdQuote: "rgb(128, 128, 128)",
|
|
117
|
-
mdQuoteBorder: "rgb(128, 128, 128)",
|
|
118
|
-
mdHr: "rgb(128, 128, 128)",
|
|
119
|
-
mdListBullet: "rgb(138, 190, 183)",
|
|
120
|
-
toolDiffAdded: "rgb(181, 189, 104)",
|
|
121
|
-
toolDiffRemoved: "rgb(204, 102, 102)",
|
|
122
|
-
toolDiffContext: "rgb(128, 128, 128)",
|
|
123
|
-
syntaxComment: "rgb(106, 153, 85)",
|
|
124
|
-
syntaxKeyword: "rgb(86, 156, 214)",
|
|
125
|
-
syntaxFunction: "rgb(220, 220, 170)",
|
|
126
|
-
syntaxVariable: "rgb(156, 220, 254)",
|
|
127
|
-
syntaxString: "rgb(206, 145, 120)",
|
|
128
|
-
syntaxNumber: "rgb(181, 206, 168)",
|
|
129
|
-
syntaxType: "rgb(78, 201, 176)",
|
|
130
|
-
syntaxOperator: "rgb(212, 212, 212)",
|
|
131
|
-
syntaxPunctuation: "rgb(212, 212, 212)",
|
|
132
|
-
};
|
|
133
|
-
if (!themeName)
|
|
134
|
-
return defaultColors;
|
|
135
|
-
const themeJson = loadThemeJson(themeName);
|
|
136
|
-
if (!themeJson)
|
|
137
|
-
return defaultColors;
|
|
138
|
-
const vars = themeJson.vars || {};
|
|
139
|
-
const colors = themeJson.colors;
|
|
140
|
-
const resolve = (key) => {
|
|
141
|
-
const value = colors[key];
|
|
142
|
-
if (value === undefined)
|
|
143
|
-
return defaultColors[key];
|
|
144
|
-
return resolveColorValue(value, vars, defaultColors[key]);
|
|
145
|
-
};
|
|
146
|
-
return {
|
|
147
|
-
accent: resolve("accent"),
|
|
148
|
-
border: resolve("border"),
|
|
149
|
-
borderAccent: resolve("borderAccent"),
|
|
150
|
-
success: resolve("success"),
|
|
151
|
-
error: resolve("error"),
|
|
152
|
-
warning: resolve("warning"),
|
|
153
|
-
muted: resolve("muted"),
|
|
154
|
-
dim: resolve("dim"),
|
|
155
|
-
text: resolve("text"),
|
|
156
|
-
userMessageBg: resolve("userMessageBg"),
|
|
157
|
-
userMessageText: resolve("userMessageText"),
|
|
158
|
-
toolPendingBg: resolve("toolPendingBg"),
|
|
159
|
-
toolSuccessBg: resolve("toolSuccessBg"),
|
|
160
|
-
toolErrorBg: resolve("toolErrorBg"),
|
|
161
|
-
toolOutput: resolve("toolOutput"),
|
|
162
|
-
mdHeading: resolve("mdHeading"),
|
|
163
|
-
mdLink: resolve("mdLink"),
|
|
164
|
-
mdLinkUrl: resolve("mdLinkUrl"),
|
|
165
|
-
mdCode: resolve("mdCode"),
|
|
166
|
-
mdCodeBlock: resolve("mdCodeBlock"),
|
|
167
|
-
mdCodeBlockBorder: resolve("mdCodeBlockBorder"),
|
|
168
|
-
mdQuote: resolve("mdQuote"),
|
|
169
|
-
mdQuoteBorder: resolve("mdQuoteBorder"),
|
|
170
|
-
mdHr: resolve("mdHr"),
|
|
171
|
-
mdListBullet: resolve("mdListBullet"),
|
|
172
|
-
toolDiffAdded: resolve("toolDiffAdded"),
|
|
173
|
-
toolDiffRemoved: resolve("toolDiffRemoved"),
|
|
174
|
-
toolDiffContext: resolve("toolDiffContext"),
|
|
175
|
-
syntaxComment: resolve("syntaxComment"),
|
|
176
|
-
syntaxKeyword: resolve("syntaxKeyword"),
|
|
177
|
-
syntaxFunction: resolve("syntaxFunction"),
|
|
178
|
-
syntaxVariable: resolve("syntaxVariable"),
|
|
179
|
-
syntaxString: resolve("syntaxString"),
|
|
180
|
-
syntaxNumber: resolve("syntaxNumber"),
|
|
181
|
-
syntaxType: resolve("syntaxType"),
|
|
182
|
-
syntaxOperator: resolve("syntaxOperator"),
|
|
183
|
-
syntaxPunctuation: resolve("syntaxPunctuation"),
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
/** Check if theme is a light theme (currently only matches "light" exactly). */
|
|
187
|
-
function isLightTheme(themeName) {
|
|
188
|
-
return themeName === "light";
|
|
189
|
-
}
|
|
190
|
-
// ============================================================================
|
|
191
|
-
// Utility functions
|
|
192
|
-
// ============================================================================
|
|
193
|
-
function escapeHtml(text) {
|
|
194
|
-
return text
|
|
195
|
-
.replace(/&/g, "&")
|
|
196
|
-
.replace(/</g, "<")
|
|
197
|
-
.replace(/>/g, ">")
|
|
198
|
-
.replace(/"/g, """)
|
|
199
|
-
.replace(/'/g, "'");
|
|
200
|
-
}
|
|
201
|
-
function shortenPath(path) {
|
|
202
|
-
const home = homedir();
|
|
203
|
-
return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
204
|
-
}
|
|
205
|
-
function replaceTabs(text) {
|
|
206
|
-
return text.replace(/\t/g, " ");
|
|
207
|
-
}
|
|
208
|
-
function formatTimestamp(timestamp) {
|
|
209
|
-
if (!timestamp)
|
|
210
|
-
return "";
|
|
211
|
-
const date = new Date(typeof timestamp === "string" ? timestamp : timestamp);
|
|
212
|
-
return date.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
213
|
-
}
|
|
214
|
-
/** Highlight code using highlight.js. Returns HTML with syntax highlighting spans. */
|
|
215
|
-
function highlightCode(code, lang) {
|
|
216
|
-
if (!lang) {
|
|
217
|
-
return escapeHtml(code);
|
|
218
|
-
}
|
|
219
|
-
try {
|
|
220
|
-
// Check if language is supported
|
|
221
|
-
if (hljs.getLanguage(lang)) {
|
|
222
|
-
return hljs.highlight(code, { language: lang, ignoreIllegals: true }).value;
|
|
223
|
-
}
|
|
224
|
-
// Try common aliases
|
|
225
|
-
const aliases = {
|
|
226
|
-
ts: "typescript",
|
|
227
|
-
js: "javascript",
|
|
228
|
-
py: "python",
|
|
229
|
-
rb: "ruby",
|
|
230
|
-
sh: "bash",
|
|
231
|
-
yml: "yaml",
|
|
232
|
-
md: "markdown",
|
|
233
|
-
};
|
|
234
|
-
const aliasedLang = aliases[lang];
|
|
235
|
-
if (aliasedLang && hljs.getLanguage(aliasedLang)) {
|
|
236
|
-
return hljs.highlight(code, { language: aliasedLang, ignoreIllegals: true }).value;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
catch {
|
|
240
|
-
// Fall through to escaped output
|
|
241
|
-
}
|
|
242
|
-
return escapeHtml(code);
|
|
243
|
-
}
|
|
244
|
-
/** Get language from file path extension. */
|
|
245
|
-
function getLanguageFromPath(filePath) {
|
|
246
|
-
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
247
|
-
if (!ext)
|
|
248
|
-
return undefined;
|
|
249
|
-
const extToLang = {
|
|
250
|
-
ts: "typescript",
|
|
251
|
-
tsx: "typescript",
|
|
252
|
-
js: "javascript",
|
|
253
|
-
jsx: "javascript",
|
|
254
|
-
mjs: "javascript",
|
|
255
|
-
cjs: "javascript",
|
|
256
|
-
py: "python",
|
|
257
|
-
rb: "ruby",
|
|
258
|
-
rs: "rust",
|
|
259
|
-
go: "go",
|
|
260
|
-
java: "java",
|
|
261
|
-
kt: "kotlin",
|
|
262
|
-
swift: "swift",
|
|
263
|
-
c: "c",
|
|
264
|
-
h: "c",
|
|
265
|
-
cpp: "cpp",
|
|
266
|
-
cc: "cpp",
|
|
267
|
-
cxx: "cpp",
|
|
268
|
-
hpp: "cpp",
|
|
269
|
-
cs: "csharp",
|
|
270
|
-
php: "php",
|
|
271
|
-
sh: "bash",
|
|
272
|
-
bash: "bash",
|
|
273
|
-
zsh: "bash",
|
|
274
|
-
fish: "bash",
|
|
275
|
-
ps1: "powershell",
|
|
276
|
-
sql: "sql",
|
|
277
|
-
html: "html",
|
|
278
|
-
htm: "html",
|
|
279
|
-
xml: "xml",
|
|
280
|
-
css: "css",
|
|
281
|
-
scss: "scss",
|
|
282
|
-
sass: "scss",
|
|
283
|
-
less: "less",
|
|
284
|
-
json: "json",
|
|
285
|
-
yaml: "yaml",
|
|
286
|
-
yml: "yaml",
|
|
287
|
-
toml: "toml",
|
|
288
|
-
ini: "ini",
|
|
289
|
-
md: "markdown",
|
|
290
|
-
markdown: "markdown",
|
|
291
|
-
dockerfile: "dockerfile",
|
|
292
|
-
makefile: "makefile",
|
|
293
|
-
cmake: "cmake",
|
|
294
|
-
lua: "lua",
|
|
295
|
-
r: "r",
|
|
296
|
-
scala: "scala",
|
|
297
|
-
clj: "clojure",
|
|
298
|
-
cljs: "clojure",
|
|
299
|
-
ex: "elixir",
|
|
300
|
-
exs: "elixir",
|
|
301
|
-
erl: "erlang",
|
|
302
|
-
hrl: "erlang",
|
|
303
|
-
hs: "haskell",
|
|
304
|
-
ml: "ocaml",
|
|
305
|
-
mli: "ocaml",
|
|
306
|
-
fs: "fsharp",
|
|
307
|
-
fsx: "fsharp",
|
|
308
|
-
vue: "vue",
|
|
309
|
-
svelte: "xml",
|
|
310
|
-
tf: "hcl",
|
|
311
|
-
hcl: "hcl",
|
|
312
|
-
proto: "protobuf",
|
|
313
|
-
graphql: "graphql",
|
|
314
|
-
gql: "graphql",
|
|
315
|
-
};
|
|
316
|
-
return extToLang[ext];
|
|
317
|
-
}
|
|
318
|
-
/** Render markdown to HTML server-side with TUI-style code block formatting and syntax highlighting. */
|
|
319
|
-
function renderMarkdown(text) {
|
|
320
|
-
// Custom renderer for code blocks to match TUI style
|
|
321
|
-
const renderer = new marked.Renderer();
|
|
322
|
-
renderer.code = ({ text: code, lang }) => {
|
|
323
|
-
const language = lang || "";
|
|
324
|
-
const highlighted = highlightCode(code, lang);
|
|
325
|
-
return ('<div class="code-block-wrapper">' +
|
|
326
|
-
`<div class="code-block-header">\`\`\`${language}</div>` +
|
|
327
|
-
`<pre><code class="hljs">${highlighted}</code></pre>` +
|
|
328
|
-
'<div class="code-block-footer">```</div>' +
|
|
329
|
-
"</div>");
|
|
330
|
-
};
|
|
331
|
-
// Configure marked for safe rendering
|
|
332
|
-
marked.setOptions({
|
|
333
|
-
breaks: true,
|
|
334
|
-
gfm: true,
|
|
335
|
-
});
|
|
336
|
-
// Parse markdown (marked escapes HTML by default in newer versions)
|
|
337
|
-
return marked.parse(text, { renderer });
|
|
338
|
-
}
|
|
339
|
-
function formatExpandableOutput(lines, maxLines, lang) {
|
|
340
|
-
const displayLines = lines.slice(0, maxLines);
|
|
341
|
-
const remaining = lines.length - maxLines;
|
|
342
|
-
// If language is provided, highlight the entire code block
|
|
343
|
-
if (lang) {
|
|
344
|
-
const code = lines.join("\n");
|
|
345
|
-
const highlighted = highlightCode(code, lang);
|
|
346
|
-
if (remaining > 0) {
|
|
347
|
-
// For expandable, we need preview and full versions
|
|
348
|
-
const previewCode = displayLines.join("\n");
|
|
349
|
-
const previewHighlighted = highlightCode(previewCode, lang);
|
|
350
|
-
let out = '<div class="tool-output expandable" onclick="this.classList.toggle(\'expanded\')">';
|
|
351
|
-
out += `<div class="output-preview"><pre><code class="hljs">${previewHighlighted}</code></pre>`;
|
|
352
|
-
out += `<div class="expand-hint">... (${remaining} more lines) - click to expand</div>`;
|
|
353
|
-
out += "</div>";
|
|
354
|
-
out += `<div class="output-full"><pre><code class="hljs">${highlighted}</code></pre></div></div>`;
|
|
355
|
-
return out;
|
|
356
|
-
}
|
|
357
|
-
return `<div class="tool-output"><pre><code class="hljs">${highlighted}</code></pre></div>`;
|
|
358
|
-
}
|
|
359
|
-
// No language - plain text output
|
|
360
|
-
if (remaining > 0) {
|
|
361
|
-
let out = '<div class="tool-output expandable" onclick="this.classList.toggle(\'expanded\')">';
|
|
362
|
-
out += '<div class="output-preview">';
|
|
363
|
-
for (const line of displayLines) {
|
|
364
|
-
out += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
365
|
-
}
|
|
366
|
-
out += `<div class="expand-hint">... (${remaining} more lines) - click to expand</div>`;
|
|
367
|
-
out += "</div>";
|
|
368
|
-
out += '<div class="output-full">';
|
|
369
|
-
for (const line of lines) {
|
|
370
|
-
out += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
371
|
-
}
|
|
372
|
-
out += "</div></div>";
|
|
373
|
-
return out;
|
|
374
|
-
}
|
|
375
|
-
let out = '<div class="tool-output">';
|
|
376
|
-
for (const line of displayLines) {
|
|
377
|
-
out += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
378
|
-
}
|
|
379
|
-
out += "</div>";
|
|
380
|
-
return out;
|
|
381
|
-
}
|
|
382
|
-
// ============================================================================
|
|
383
|
-
// Parsing functions
|
|
384
|
-
// ============================================================================
|
|
385
|
-
function parseSessionManagerFormat(lines) {
|
|
386
|
-
const data = {
|
|
387
|
-
sessionId: "unknown",
|
|
388
|
-
timestamp: new Date().toISOString(),
|
|
389
|
-
modelsUsed: new Set(),
|
|
390
|
-
messages: [],
|
|
391
|
-
toolResultsMap: new Map(),
|
|
392
|
-
sessionEvents: [],
|
|
393
|
-
tokenStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
394
|
-
costStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
395
|
-
};
|
|
396
|
-
for (const line of lines) {
|
|
397
|
-
let entry;
|
|
398
|
-
try {
|
|
399
|
-
entry = JSON.parse(line);
|
|
400
|
-
}
|
|
401
|
-
catch {
|
|
402
|
-
continue;
|
|
403
|
-
}
|
|
404
|
-
switch (entry.type) {
|
|
405
|
-
case "session":
|
|
406
|
-
data.sessionId = entry.id || "unknown";
|
|
407
|
-
data.timestamp = entry.timestamp || data.timestamp;
|
|
408
|
-
data.systemPrompt = entry.systemPrompt;
|
|
409
|
-
if (entry.modelId) {
|
|
410
|
-
const modelInfo = entry.provider ? `${entry.provider}/${entry.modelId}` : entry.modelId;
|
|
411
|
-
data.modelsUsed.add(modelInfo);
|
|
412
|
-
}
|
|
413
|
-
break;
|
|
414
|
-
case "message": {
|
|
415
|
-
const message = entry.message;
|
|
416
|
-
data.messages.push(message);
|
|
417
|
-
data.sessionEvents.push({
|
|
418
|
-
type: "message",
|
|
419
|
-
message,
|
|
420
|
-
timestamp: entry.timestamp,
|
|
421
|
-
});
|
|
422
|
-
if (message.role === "toolResult") {
|
|
423
|
-
const toolResult = message;
|
|
424
|
-
data.toolResultsMap.set(toolResult.toolCallId, toolResult);
|
|
425
|
-
}
|
|
426
|
-
else if (message.role === "assistant") {
|
|
427
|
-
const assistantMsg = message;
|
|
428
|
-
if (assistantMsg.usage) {
|
|
429
|
-
data.tokenStats.input += assistantMsg.usage.input || 0;
|
|
430
|
-
data.tokenStats.output += assistantMsg.usage.output || 0;
|
|
431
|
-
data.tokenStats.cacheRead += assistantMsg.usage.cacheRead || 0;
|
|
432
|
-
data.tokenStats.cacheWrite += assistantMsg.usage.cacheWrite || 0;
|
|
433
|
-
if (assistantMsg.usage.cost) {
|
|
434
|
-
data.costStats.input += assistantMsg.usage.cost.input || 0;
|
|
435
|
-
data.costStats.output += assistantMsg.usage.cost.output || 0;
|
|
436
|
-
data.costStats.cacheRead += assistantMsg.usage.cost.cacheRead || 0;
|
|
437
|
-
data.costStats.cacheWrite += assistantMsg.usage.cost.cacheWrite || 0;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
case "model_change":
|
|
444
|
-
data.sessionEvents.push({
|
|
445
|
-
type: "model_change",
|
|
446
|
-
provider: entry.provider,
|
|
447
|
-
modelId: entry.modelId,
|
|
448
|
-
timestamp: entry.timestamp,
|
|
449
|
-
});
|
|
450
|
-
if (entry.modelId) {
|
|
451
|
-
const modelInfo = entry.provider ? `${entry.provider}/${entry.modelId}` : entry.modelId;
|
|
452
|
-
data.modelsUsed.add(modelInfo);
|
|
453
|
-
}
|
|
454
|
-
break;
|
|
455
|
-
case "compaction":
|
|
456
|
-
data.sessionEvents.push({
|
|
457
|
-
type: "compaction",
|
|
458
|
-
timestamp: entry.timestamp,
|
|
459
|
-
summary: entry.summary,
|
|
460
|
-
tokensBefore: entry.tokensBefore,
|
|
461
|
-
});
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
return data;
|
|
466
|
-
}
|
|
467
|
-
function parseStreamingEventFormat(lines) {
|
|
468
|
-
const data = {
|
|
469
|
-
sessionId: "unknown",
|
|
470
|
-
timestamp: new Date().toISOString(),
|
|
471
|
-
modelsUsed: new Set(),
|
|
472
|
-
messages: [],
|
|
473
|
-
toolResultsMap: new Map(),
|
|
474
|
-
sessionEvents: [],
|
|
475
|
-
tokenStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
476
|
-
costStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
477
|
-
isStreamingFormat: true,
|
|
478
|
-
};
|
|
479
|
-
let timestampSet = false;
|
|
480
|
-
for (const line of lines) {
|
|
481
|
-
let entry;
|
|
482
|
-
try {
|
|
483
|
-
entry = JSON.parse(line);
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
if (entry.type === "message_end" && entry.message) {
|
|
489
|
-
const msg = entry.message;
|
|
490
|
-
data.messages.push(msg);
|
|
491
|
-
data.sessionEvents.push({
|
|
492
|
-
type: "message",
|
|
493
|
-
message: msg,
|
|
494
|
-
timestamp: msg.timestamp,
|
|
495
|
-
});
|
|
496
|
-
if (msg.role === "toolResult") {
|
|
497
|
-
const toolResult = msg;
|
|
498
|
-
data.toolResultsMap.set(toolResult.toolCallId, toolResult);
|
|
499
|
-
}
|
|
500
|
-
else if (msg.role === "assistant") {
|
|
501
|
-
const assistantMsg = msg;
|
|
502
|
-
if (assistantMsg.model) {
|
|
503
|
-
const modelInfo = assistantMsg.provider
|
|
504
|
-
? `${assistantMsg.provider}/${assistantMsg.model}`
|
|
505
|
-
: assistantMsg.model;
|
|
506
|
-
data.modelsUsed.add(modelInfo);
|
|
507
|
-
}
|
|
508
|
-
if (assistantMsg.usage) {
|
|
509
|
-
data.tokenStats.input += assistantMsg.usage.input || 0;
|
|
510
|
-
data.tokenStats.output += assistantMsg.usage.output || 0;
|
|
511
|
-
data.tokenStats.cacheRead += assistantMsg.usage.cacheRead || 0;
|
|
512
|
-
data.tokenStats.cacheWrite += assistantMsg.usage.cacheWrite || 0;
|
|
513
|
-
if (assistantMsg.usage.cost) {
|
|
514
|
-
data.costStats.input += assistantMsg.usage.cost.input || 0;
|
|
515
|
-
data.costStats.output += assistantMsg.usage.cost.output || 0;
|
|
516
|
-
data.costStats.cacheRead += assistantMsg.usage.cost.cacheRead || 0;
|
|
517
|
-
data.costStats.cacheWrite += assistantMsg.usage.cost.cacheWrite || 0;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
if (!timestampSet && msg.timestamp) {
|
|
522
|
-
data.timestamp = new Date(msg.timestamp).toISOString();
|
|
523
|
-
timestampSet = true;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
data.sessionId = `stream-${data.timestamp.replace(/[:.]/g, "-")}`;
|
|
528
|
-
return data;
|
|
529
|
-
}
|
|
530
|
-
function detectFormat(lines) {
|
|
531
|
-
for (const line of lines) {
|
|
532
|
-
try {
|
|
533
|
-
const entry = JSON.parse(line);
|
|
534
|
-
if (entry.type === "session")
|
|
535
|
-
return "session-manager";
|
|
536
|
-
if (entry.type === "agent_start" || entry.type === "message_start" || entry.type === "turn_start") {
|
|
537
|
-
return "streaming-events";
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
catch { }
|
|
541
|
-
}
|
|
542
|
-
return "unknown";
|
|
543
|
-
}
|
|
544
|
-
function parseSessionFile(content) {
|
|
545
|
-
const lines = content
|
|
546
|
-
.trim()
|
|
547
|
-
.split("\n")
|
|
548
|
-
.filter((l) => l.trim());
|
|
549
|
-
if (lines.length === 0) {
|
|
550
|
-
throw new Error("Empty session file");
|
|
551
|
-
}
|
|
552
|
-
const format = detectFormat(lines);
|
|
553
|
-
if (format === "unknown") {
|
|
554
|
-
throw new Error("Unknown session file format");
|
|
555
|
-
}
|
|
556
|
-
return format === "session-manager" ? parseSessionManagerFormat(lines) : parseStreamingEventFormat(lines);
|
|
557
|
-
}
|
|
558
|
-
// ============================================================================
|
|
559
|
-
// HTML formatting functions
|
|
560
|
-
// ============================================================================
|
|
561
|
-
function formatToolExecution(toolName, args, result, colors) {
|
|
562
|
-
let html = "";
|
|
563
|
-
const isError = result?.isError || false;
|
|
564
|
-
const bgColor = result ? (isError ? colors.toolErrorBg : colors.toolSuccessBg) : colors.toolPendingBg;
|
|
565
|
-
const getTextOutput = () => {
|
|
566
|
-
if (!result)
|
|
567
|
-
return "";
|
|
568
|
-
const textBlocks = result.content.filter((c) => c.type === "text");
|
|
569
|
-
return textBlocks.map((c) => c.text).join("\n");
|
|
570
|
-
};
|
|
571
|
-
switch (toolName) {
|
|
572
|
-
case "bash": {
|
|
573
|
-
const command = args?.command || "";
|
|
574
|
-
html = `<div class="tool-command">$ ${escapeHtml(command || "...")}</div>`;
|
|
575
|
-
if (result) {
|
|
576
|
-
const output = getTextOutput().trim();
|
|
577
|
-
if (output) {
|
|
578
|
-
html += formatExpandableOutput(output.split("\n"), 5);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
break;
|
|
582
|
-
}
|
|
583
|
-
case "read": {
|
|
584
|
-
const filePath = args?.file_path || args?.path || "";
|
|
585
|
-
const shortenedPath = shortenPath(filePath);
|
|
586
|
-
const offset = args?.offset;
|
|
587
|
-
const limit = args?.limit;
|
|
588
|
-
const lang = getLanguageFromPath(filePath);
|
|
589
|
-
// Build path display with offset/limit suffix
|
|
590
|
-
let pathHtml = escapeHtml(shortenedPath || "...");
|
|
591
|
-
if (offset !== undefined || limit !== undefined) {
|
|
592
|
-
const startLine = offset ?? 1;
|
|
593
|
-
const endLine = limit !== undefined ? startLine + limit - 1 : "";
|
|
594
|
-
pathHtml += `<span class="line-numbers">:${startLine}${endLine ? `-${endLine}` : ""}</span>`;
|
|
595
|
-
}
|
|
596
|
-
html = `<div class="tool-header"><span class="tool-name">read</span> <span class="tool-path">${pathHtml}</span></div>`;
|
|
597
|
-
if (result) {
|
|
598
|
-
const output = getTextOutput();
|
|
599
|
-
if (output) {
|
|
600
|
-
html += formatExpandableOutput(output.split("\n"), 10, lang);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
break;
|
|
604
|
-
}
|
|
605
|
-
case "write": {
|
|
606
|
-
const filePath = args?.file_path || args?.path || "";
|
|
607
|
-
const shortenedPath = shortenPath(filePath);
|
|
608
|
-
const fileContent = args?.content || "";
|
|
609
|
-
const lines = fileContent ? fileContent.split("\n") : [];
|
|
610
|
-
const lang = getLanguageFromPath(filePath);
|
|
611
|
-
html = `<div class="tool-header"><span class="tool-name">write</span> <span class="tool-path">${escapeHtml(shortenedPath || "...")}</span>`;
|
|
612
|
-
if (lines.length > 10) {
|
|
613
|
-
html += ` <span class="line-count">(${lines.length} lines)</span>`;
|
|
614
|
-
}
|
|
615
|
-
html += "</div>";
|
|
616
|
-
if (fileContent) {
|
|
617
|
-
html += formatExpandableOutput(lines, 10, lang);
|
|
618
|
-
}
|
|
619
|
-
if (result) {
|
|
620
|
-
const output = getTextOutput().trim();
|
|
621
|
-
if (output) {
|
|
622
|
-
html += `<div class="tool-output"><div>${escapeHtml(output)}</div></div>`;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
break;
|
|
626
|
-
}
|
|
627
|
-
case "edit": {
|
|
628
|
-
const path = shortenPath(args?.file_path || args?.path || "");
|
|
629
|
-
html = `<div class="tool-header"><span class="tool-name">edit</span> <span class="tool-path">${escapeHtml(path || "...")}</span></div>`;
|
|
630
|
-
if (result?.details?.diff) {
|
|
631
|
-
const diffLines = result.details.diff.split("\n");
|
|
632
|
-
html += '<div class="tool-diff">';
|
|
633
|
-
for (const line of diffLines) {
|
|
634
|
-
if (line.startsWith("+")) {
|
|
635
|
-
html += `<div class="diff-line-new">${escapeHtml(line)}</div>`;
|
|
636
|
-
}
|
|
637
|
-
else if (line.startsWith("-")) {
|
|
638
|
-
html += `<div class="diff-line-old">${escapeHtml(line)}</div>`;
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
html += `<div class="diff-line-context">${escapeHtml(line)}</div>`;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
html += "</div>";
|
|
645
|
-
}
|
|
646
|
-
if (result) {
|
|
647
|
-
const output = getTextOutput().trim();
|
|
648
|
-
if (output) {
|
|
649
|
-
html += `<div class="tool-output"><div>${escapeHtml(output)}</div></div>`;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
break;
|
|
653
|
-
}
|
|
654
|
-
default: {
|
|
655
|
-
html = `<div class="tool-header"><span class="tool-name">${escapeHtml(toolName)}</span></div>`;
|
|
656
|
-
html += `<div class="tool-output"><pre>${escapeHtml(JSON.stringify(args, null, 2))}</pre></div>`;
|
|
657
|
-
if (result) {
|
|
658
|
-
const output = getTextOutput();
|
|
659
|
-
if (output) {
|
|
660
|
-
html += `<div class="tool-output"><div>${escapeHtml(output)}</div></div>`;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
return { html, bgColor };
|
|
666
|
-
}
|
|
667
|
-
function formatMessage(message, toolResultsMap, colors) {
|
|
668
|
-
let html = "";
|
|
669
|
-
const timestamp = message.timestamp;
|
|
670
|
-
const timestampHtml = timestamp ? `<div class="message-timestamp">${formatTimestamp(timestamp)}</div>` : "";
|
|
671
|
-
// Handle bash execution messages (user-executed via ! command)
|
|
672
|
-
if (isBashExecutionMessage(message)) {
|
|
673
|
-
const bashMsg = message;
|
|
674
|
-
const isError = bashMsg.cancelled || (bashMsg.exitCode !== 0 && bashMsg.exitCode !== null);
|
|
675
|
-
html += `<div class="tool-execution user-bash${isError ? " user-bash-error" : ""}">`;
|
|
676
|
-
html += timestampHtml;
|
|
677
|
-
html += `<div class="tool-command">$ ${escapeHtml(bashMsg.command)}</div>`;
|
|
678
|
-
if (bashMsg.output) {
|
|
679
|
-
const lines = bashMsg.output.split("\n");
|
|
680
|
-
html += formatExpandableOutput(lines, 10);
|
|
681
|
-
}
|
|
682
|
-
if (bashMsg.cancelled) {
|
|
683
|
-
html += `<div class="bash-status warning">(cancelled)</div>`;
|
|
684
|
-
}
|
|
685
|
-
else if (bashMsg.exitCode !== 0 && bashMsg.exitCode !== null) {
|
|
686
|
-
html += `<div class="bash-status error">(exit ${bashMsg.exitCode})</div>`;
|
|
687
|
-
}
|
|
688
|
-
if (bashMsg.truncated && bashMsg.fullOutputPath) {
|
|
689
|
-
html += `<div class="bash-truncation warning">Output truncated. Full output: ${escapeHtml(bashMsg.fullOutputPath)}</div>`;
|
|
690
|
-
}
|
|
691
|
-
html += `</div>`;
|
|
692
|
-
return html;
|
|
693
|
-
}
|
|
694
|
-
if (message.role === "user") {
|
|
695
|
-
const userMsg = message;
|
|
696
|
-
let textContent = "";
|
|
697
|
-
const images = [];
|
|
698
|
-
if (typeof userMsg.content === "string") {
|
|
699
|
-
textContent = userMsg.content;
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
for (const block of userMsg.content) {
|
|
703
|
-
if (block.type === "text") {
|
|
704
|
-
textContent += block.text;
|
|
705
|
-
}
|
|
706
|
-
else if (block.type === "image") {
|
|
707
|
-
images.push(block);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
html += `<div class="user-message">${timestampHtml}`;
|
|
712
|
-
// Render images first
|
|
713
|
-
if (images.length > 0) {
|
|
714
|
-
html += `<div class="message-images">`;
|
|
715
|
-
for (const img of images) {
|
|
716
|
-
html += `<img src="data:${img.mimeType};base64,${img.data}" alt="User uploaded image" class="message-image" />`;
|
|
717
|
-
}
|
|
718
|
-
html += `</div>`;
|
|
719
|
-
}
|
|
720
|
-
// Render text as markdown (server-side)
|
|
721
|
-
if (textContent.trim()) {
|
|
722
|
-
html += `<div class="markdown-content">${renderMarkdown(textContent)}</div>`;
|
|
723
|
-
}
|
|
724
|
-
html += `</div>`;
|
|
725
|
-
}
|
|
726
|
-
else if (message.role === "assistant") {
|
|
727
|
-
const assistantMsg = message;
|
|
728
|
-
html += timestampHtml ? `<div class="assistant-message">${timestampHtml}` : "";
|
|
729
|
-
for (const content of assistantMsg.content) {
|
|
730
|
-
if (content.type === "text" && content.text.trim()) {
|
|
731
|
-
// Render markdown server-side
|
|
732
|
-
html += `<div class="assistant-text markdown-content">${renderMarkdown(content.text)}</div>`;
|
|
733
|
-
}
|
|
734
|
-
else if (content.type === "thinking" && content.thinking.trim()) {
|
|
735
|
-
html += `<div class="thinking-text">${escapeHtml(content.thinking.trim()).replace(/\n/g, "<br>")}</div>`;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
for (const content of assistantMsg.content) {
|
|
739
|
-
if (content.type === "toolCall") {
|
|
740
|
-
const toolResult = toolResultsMap.get(content.id);
|
|
741
|
-
const { html: toolHtml, bgColor } = formatToolExecution(content.name, content.arguments, toolResult, colors);
|
|
742
|
-
html += `<div class="tool-execution" style="background-color: ${bgColor}">${toolHtml}</div>`;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
const hasToolCalls = assistantMsg.content.some((c) => c.type === "toolCall");
|
|
746
|
-
if (!hasToolCalls) {
|
|
747
|
-
if (assistantMsg.stopReason === "aborted") {
|
|
748
|
-
html += '<div class="error-text">Aborted</div>';
|
|
749
|
-
}
|
|
750
|
-
else if (assistantMsg.stopReason === "error") {
|
|
751
|
-
html += `<div class="error-text">Error: ${escapeHtml(assistantMsg.errorMessage || "Unknown error")}</div>`;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
if (timestampHtml) {
|
|
755
|
-
html += "</div>";
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return html;
|
|
759
|
-
}
|
|
760
|
-
function formatModelChange(event) {
|
|
761
|
-
const timestamp = formatTimestamp(event.timestamp);
|
|
762
|
-
const timestampHtml = timestamp ? `<div class="message-timestamp">${timestamp}</div>` : "";
|
|
763
|
-
const modelInfo = `${event.provider}/${event.modelId}`;
|
|
764
|
-
return `<div class="model-change">${timestampHtml}<div class="model-change-text">Switched to model: <span class="model-name">${escapeHtml(modelInfo)}</span></div></div>`;
|
|
765
|
-
}
|
|
766
|
-
function formatCompaction(event) {
|
|
767
|
-
const timestamp = formatTimestamp(event.timestamp);
|
|
768
|
-
const timestampHtml = timestamp ? `<div class="message-timestamp">${timestamp}</div>` : "";
|
|
769
|
-
const summaryHtml = escapeHtml(event.summary).replace(/\n/g, "<br>");
|
|
770
|
-
return `<div class="compaction-container">
|
|
771
|
-
<div class="compaction-header" onclick="this.parentElement.classList.toggle('expanded')">
|
|
772
|
-
${timestampHtml}
|
|
773
|
-
<div class="compaction-header-row">
|
|
774
|
-
<span class="compaction-toggle">▶</span>
|
|
775
|
-
<span class="compaction-title">Context compacted from ${event.tokensBefore.toLocaleString()} tokens</span>
|
|
776
|
-
<span class="compaction-hint">(click to expand summary)</span>
|
|
777
|
-
</div>
|
|
778
|
-
</div>
|
|
779
|
-
<div class="compaction-content">
|
|
780
|
-
<div class="compaction-summary">
|
|
781
|
-
<div class="compaction-summary-header">Summary sent to model</div>
|
|
782
|
-
<div class="compaction-summary-content">${summaryHtml}</div>
|
|
783
|
-
</div>
|
|
784
|
-
</div>
|
|
785
|
-
</div>`;
|
|
786
|
-
}
|
|
787
|
-
// ============================================================================
|
|
788
|
-
// HTML generation
|
|
789
|
-
// ============================================================================
|
|
790
|
-
function generateHtml(data, filename, colors, isLight) {
|
|
791
|
-
const userMessages = data.messages.filter((m) => m.role === "user").length;
|
|
792
|
-
const assistantMessages = data.messages.filter((m) => m.role === "assistant").length;
|
|
793
|
-
let toolCallsCount = 0;
|
|
794
|
-
for (const message of data.messages) {
|
|
795
|
-
if (message.role === "assistant") {
|
|
796
|
-
toolCallsCount += message.content.filter((c) => c.type === "toolCall").length;
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
const lastAssistantMessage = data.messages
|
|
800
|
-
.slice()
|
|
801
|
-
.reverse()
|
|
802
|
-
.find((m) => m.role === "assistant" && m.stopReason !== "aborted");
|
|
803
|
-
const contextTokens = lastAssistantMessage
|
|
804
|
-
? lastAssistantMessage.usage.input +
|
|
805
|
-
lastAssistantMessage.usage.output +
|
|
806
|
-
lastAssistantMessage.usage.cacheRead +
|
|
807
|
-
lastAssistantMessage.usage.cacheWrite
|
|
808
|
-
: 0;
|
|
809
|
-
const lastModel = lastAssistantMessage?.model || "unknown";
|
|
810
|
-
const lastProvider = lastAssistantMessage?.provider || "";
|
|
811
|
-
const lastModelInfo = lastProvider ? `${lastProvider}/${lastModel}` : lastModel;
|
|
812
|
-
const contextWindow = data.contextWindow || 0;
|
|
813
|
-
const contextPercent = contextWindow > 0 ? ((contextTokens / contextWindow) * 100).toFixed(1) : null;
|
|
814
|
-
let messagesHtml = "";
|
|
815
|
-
for (const event of data.sessionEvents) {
|
|
816
|
-
switch (event.type) {
|
|
817
|
-
case "message":
|
|
818
|
-
if (event.message.role !== "toolResult") {
|
|
819
|
-
messagesHtml += formatMessage(event.message, data.toolResultsMap, colors);
|
|
820
|
-
}
|
|
821
|
-
break;
|
|
822
|
-
case "model_change":
|
|
823
|
-
messagesHtml += formatModelChange(event);
|
|
824
|
-
break;
|
|
825
|
-
case "compaction":
|
|
826
|
-
messagesHtml += formatCompaction(event);
|
|
827
|
-
break;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
const systemPromptHtml = data.systemPrompt
|
|
831
|
-
? `<div class="system-prompt">
|
|
832
|
-
<div class="system-prompt-header">System Prompt</div>
|
|
833
|
-
<div class="system-prompt-content">${escapeHtml(data.systemPrompt)}</div>
|
|
834
|
-
</div>`
|
|
835
|
-
: "";
|
|
836
|
-
const toolsHtml = data.tools
|
|
837
|
-
? `<div class="tools-list">
|
|
838
|
-
<div class="tools-header">Available Tools</div>
|
|
839
|
-
<div class="tools-content">
|
|
840
|
-
${data.tools.map((tool) => `<div class="tool-item"><span class="tool-item-name">${escapeHtml(tool.name)}</span> - ${escapeHtml(tool.description)}</div>`).join("")}
|
|
841
|
-
</div>
|
|
842
|
-
</div>`
|
|
843
|
-
: "";
|
|
844
|
-
const streamingNotice = data.isStreamingFormat
|
|
845
|
-
? `<div class="streaming-notice">
|
|
846
|
-
<em>Note: This session was reconstructed from raw agent event logs, which do not contain system prompt or tool definitions.</em>
|
|
847
|
-
</div>`
|
|
848
|
-
: "";
|
|
849
|
-
const contextUsageText = contextPercent
|
|
850
|
-
? `${contextTokens.toLocaleString()} / ${contextWindow.toLocaleString()} tokens (${contextPercent}%) - ${escapeHtml(lastModelInfo)}`
|
|
851
|
-
: `${contextTokens.toLocaleString()} tokens (last turn) - ${escapeHtml(lastModelInfo)}`;
|
|
852
|
-
// Compute body background based on theme
|
|
853
|
-
const bodyBg = isLight ? "rgb(248, 248, 248)" : "rgb(24, 24, 30)";
|
|
854
|
-
const containerBg = isLight ? "rgb(255, 255, 255)" : "rgb(30, 30, 36)";
|
|
855
|
-
const compactionBg = isLight ? "rgb(255, 248, 220)" : "rgb(60, 55, 35)";
|
|
856
|
-
const systemPromptBg = isLight ? "rgb(255, 250, 230)" : "rgb(60, 55, 40)";
|
|
857
|
-
const streamingNoticeBg = isLight ? "rgb(250, 245, 235)" : "rgb(50, 45, 35)";
|
|
858
|
-
const modelChangeBg = isLight ? "rgb(240, 240, 250)" : "rgb(40, 40, 50)";
|
|
859
|
-
const userBashBg = isLight ? "rgb(255, 250, 240)" : "rgb(50, 48, 35)";
|
|
860
|
-
const userBashErrorBg = isLight ? "rgb(255, 245, 235)" : "rgb(60, 45, 35)";
|
|
861
|
-
return `<!DOCTYPE html>
|
|
862
|
-
<html lang="en">
|
|
863
|
-
<head>
|
|
864
|
-
<meta charset="UTF-8">
|
|
865
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
866
|
-
<title>Session Export - ${escapeHtml(filename)}</title>
|
|
867
|
-
<style>
|
|
868
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
869
|
-
body {
|
|
870
|
-
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
|
|
871
|
-
font-size: 12px;
|
|
872
|
-
line-height: 1.6;
|
|
873
|
-
color: ${colors.text};
|
|
874
|
-
background: ${bodyBg};
|
|
875
|
-
padding: 24px;
|
|
876
|
-
}
|
|
877
|
-
.container { max-width: 700px; margin: 0 auto; }
|
|
878
|
-
.header {
|
|
879
|
-
margin-bottom: 24px;
|
|
880
|
-
padding: 16px;
|
|
881
|
-
background: ${containerBg};
|
|
882
|
-
border-radius: 4px;
|
|
883
|
-
}
|
|
884
|
-
.header h1 {
|
|
885
|
-
font-size: 14px;
|
|
886
|
-
font-weight: bold;
|
|
887
|
-
margin-bottom: 12px;
|
|
888
|
-
color: ${colors.borderAccent};
|
|
889
|
-
}
|
|
890
|
-
.header-info { display: flex; flex-direction: column; gap: 3px; font-size: 11px; }
|
|
891
|
-
.info-item { color: ${colors.dim}; display: flex; align-items: baseline; }
|
|
892
|
-
.info-label { font-weight: 600; margin-right: 8px; min-width: 100px; }
|
|
893
|
-
.info-value { color: ${colors.text}; flex: 1; }
|
|
894
|
-
.info-value.cost { font-family: 'SF Mono', monospace; }
|
|
895
|
-
.messages { display: flex; flex-direction: column; gap: 16px; }
|
|
896
|
-
.message-timestamp { font-size: 10px; color: ${colors.dim}; margin-bottom: 4px; opacity: 0.8; }
|
|
897
|
-
.user-message {
|
|
898
|
-
background: ${colors.userMessageBg};
|
|
899
|
-
color: ${colors.userMessageText};
|
|
900
|
-
padding: 12px 16px;
|
|
901
|
-
border-radius: 4px;
|
|
902
|
-
}
|
|
903
|
-
.assistant-message { padding: 0; }
|
|
904
|
-
.assistant-text, .thinking-text {
|
|
905
|
-
padding: 12px 16px;
|
|
906
|
-
}
|
|
907
|
-
.thinking-text { color: ${colors.dim}; font-style: italic; white-space: pre-wrap; }
|
|
908
|
-
.model-change { padding: 8px 16px; background: ${modelChangeBg}; border-radius: 4px; }
|
|
909
|
-
.model-change-text { color: ${colors.dim}; font-size: 11px; }
|
|
910
|
-
.model-name { color: ${colors.borderAccent}; font-weight: bold; }
|
|
911
|
-
.compaction-container { background: ${compactionBg}; border-radius: 4px; overflow: hidden; }
|
|
912
|
-
.compaction-header { padding: 12px 16px; cursor: pointer; }
|
|
913
|
-
.compaction-header:hover { background: rgba(${isLight ? "0, 0, 0" : "255, 255, 255"}, 0.05); }
|
|
914
|
-
.compaction-header-row { display: flex; align-items: center; gap: 8px; }
|
|
915
|
-
.compaction-toggle { color: ${colors.borderAccent}; font-size: 10px; transition: transform 0.2s; }
|
|
916
|
-
.compaction-container.expanded .compaction-toggle { transform: rotate(90deg); }
|
|
917
|
-
.compaction-title { color: ${colors.text}; font-weight: bold; }
|
|
918
|
-
.compaction-hint { color: ${colors.dim}; font-size: 11px; }
|
|
919
|
-
.compaction-content { display: none; padding: 0 16px 16px 16px; }
|
|
920
|
-
.compaction-container.expanded .compaction-content { display: block; }
|
|
921
|
-
.compaction-summary { background: rgba(0, 0, 0, 0.1); border-radius: 4px; padding: 12px; }
|
|
922
|
-
.compaction-summary-header { font-weight: bold; color: ${colors.borderAccent}; margin-bottom: 8px; font-size: 11px; }
|
|
923
|
-
.compaction-summary-content { color: ${colors.text}; white-space: pre-wrap; word-wrap: break-word; }
|
|
924
|
-
.tool-execution { padding: 12px 16px; border-radius: 4px; margin-top: 8px; }
|
|
925
|
-
.tool-execution.user-bash { background: ${userBashBg}; }
|
|
926
|
-
.tool-execution.user-bash-error { background: ${userBashErrorBg}; }
|
|
927
|
-
.tool-header, .tool-name { font-weight: bold; }
|
|
928
|
-
.tool-path { color: ${colors.borderAccent}; word-break: break-all; }
|
|
929
|
-
.line-numbers { color: ${colors.warning}; }
|
|
930
|
-
.line-count { color: ${colors.dim}; }
|
|
931
|
-
.tool-command { font-weight: bold; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; word-break: break-word; }
|
|
932
|
-
.tool-output {
|
|
933
|
-
margin-top: 12px;
|
|
934
|
-
color: ${colors.toolOutput};
|
|
935
|
-
white-space: pre-wrap;
|
|
936
|
-
word-wrap: break-word;
|
|
937
|
-
overflow-wrap: break-word;
|
|
938
|
-
word-break: break-word;
|
|
939
|
-
font-family: inherit;
|
|
940
|
-
overflow-x: auto;
|
|
941
|
-
}
|
|
942
|
-
.tool-output > div { line-height: 1.4; }
|
|
943
|
-
.tool-output pre { margin: 0; font-family: inherit; color: inherit; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; }
|
|
944
|
-
.tool-output.expandable { cursor: pointer; }
|
|
945
|
-
.tool-output.expandable:hover { opacity: 0.9; }
|
|
946
|
-
.tool-output.expandable .output-full { display: none; }
|
|
947
|
-
.tool-output.expandable.expanded .output-preview { display: none; }
|
|
948
|
-
.tool-output.expandable.expanded .output-full { display: block; }
|
|
949
|
-
.expand-hint { color: ${colors.borderAccent}; font-style: italic; margin-top: 4px; }
|
|
950
|
-
.system-prompt, .tools-list { background: ${systemPromptBg}; padding: 12px 16px; border-radius: 4px; margin-bottom: 16px; }
|
|
951
|
-
.system-prompt-header, .tools-header { font-weight: bold; color: ${colors.warning}; margin-bottom: 8px; }
|
|
952
|
-
.system-prompt-content, .tools-content { color: ${colors.dim}; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; word-break: break-word; font-size: 11px; }
|
|
953
|
-
.tool-item { margin: 4px 0; }
|
|
954
|
-
.tool-item-name { font-weight: bold; color: ${colors.text}; }
|
|
955
|
-
.tool-diff { margin-top: 12px; font-size: 11px; font-family: inherit; overflow-x: auto; max-width: 100%; }
|
|
956
|
-
.diff-line-old { color: ${colors.toolDiffRemoved}; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; }
|
|
957
|
-
.diff-line-new { color: ${colors.toolDiffAdded}; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; }
|
|
958
|
-
.diff-line-context { color: ${colors.toolDiffContext}; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; }
|
|
959
|
-
.error-text { color: ${colors.error}; padding: 12px 16px; }
|
|
960
|
-
.bash-status.warning { color: ${colors.warning}; }
|
|
961
|
-
.bash-status.error { color: ${colors.error}; }
|
|
962
|
-
.bash-truncation.warning { color: ${colors.warning}; }
|
|
963
|
-
.footer { margin-top: 48px; padding: 20px; text-align: center; color: ${colors.dim}; font-size: 10px; }
|
|
964
|
-
.streaming-notice { background: ${streamingNoticeBg}; padding: 12px 16px; border-radius: 4px; margin-bottom: 16px; color: ${colors.dim}; font-size: 11px; }
|
|
965
|
-
|
|
966
|
-
/* Image styles */
|
|
967
|
-
.message-images { margin-bottom: 12px; }
|
|
968
|
-
.message-image { max-width: 100%; max-height: 400px; border-radius: 4px; margin: 4px 0; }
|
|
969
|
-
|
|
970
|
-
/* Markdown styles */
|
|
971
|
-
.markdown-content h1, .markdown-content h2, .markdown-content h3,
|
|
972
|
-
.markdown-content h4, .markdown-content h5, .markdown-content h6 {
|
|
973
|
-
color: ${colors.mdHeading};
|
|
974
|
-
margin: 1em 0 0.5em 0;
|
|
975
|
-
font-weight: bold;
|
|
976
|
-
}
|
|
977
|
-
.markdown-content h1 { font-size: 1.4em; text-decoration: underline; }
|
|
978
|
-
.markdown-content h2 { font-size: 1.2em; }
|
|
979
|
-
.markdown-content h3 { font-size: 1.1em; }
|
|
980
|
-
.markdown-content p { margin: 0.5em 0; }
|
|
981
|
-
.markdown-content a { color: ${colors.mdLink}; text-decoration: underline; }
|
|
982
|
-
.markdown-content a:hover { opacity: 0.8; }
|
|
983
|
-
.markdown-content code {
|
|
984
|
-
background: rgba(${isLight ? "0, 0, 0" : "255, 255, 255"}, 0.1);
|
|
985
|
-
color: ${colors.mdCode};
|
|
986
|
-
padding: 2px 6px;
|
|
987
|
-
border-radius: 3px;
|
|
988
|
-
font-family: inherit;
|
|
989
|
-
}
|
|
990
|
-
.markdown-content pre {
|
|
991
|
-
background: transparent;
|
|
992
|
-
border: none;
|
|
993
|
-
border-radius: 0;
|
|
994
|
-
padding: 0;
|
|
995
|
-
margin: 0.5em 0;
|
|
996
|
-
overflow-x: auto;
|
|
997
|
-
}
|
|
998
|
-
.markdown-content pre code {
|
|
999
|
-
display: block;
|
|
1000
|
-
background: none;
|
|
1001
|
-
color: ${colors.mdCodeBlock};
|
|
1002
|
-
padding: 8px 12px;
|
|
1003
|
-
}
|
|
1004
|
-
.code-block-wrapper {
|
|
1005
|
-
margin: 0.5em 0;
|
|
1006
|
-
}
|
|
1007
|
-
.code-block-header {
|
|
1008
|
-
color: ${colors.mdCodeBlockBorder};
|
|
1009
|
-
font-size: 11px;
|
|
1010
|
-
}
|
|
1011
|
-
.code-block-footer {
|
|
1012
|
-
color: ${colors.mdCodeBlockBorder};
|
|
1013
|
-
font-size: 11px;
|
|
1014
|
-
}
|
|
1015
|
-
.markdown-content blockquote {
|
|
1016
|
-
border-left: 3px solid ${colors.mdQuoteBorder};
|
|
1017
|
-
padding-left: 12px;
|
|
1018
|
-
margin: 0.5em 0;
|
|
1019
|
-
color: ${colors.mdQuote};
|
|
1020
|
-
font-style: italic;
|
|
1021
|
-
}
|
|
1022
|
-
.markdown-content ul, .markdown-content ol {
|
|
1023
|
-
margin: 0.5em 0;
|
|
1024
|
-
padding-left: 24px;
|
|
1025
|
-
}
|
|
1026
|
-
.markdown-content li { margin: 0.25em 0; }
|
|
1027
|
-
.markdown-content li::marker { color: ${colors.mdListBullet}; }
|
|
1028
|
-
.markdown-content hr {
|
|
1029
|
-
border: none;
|
|
1030
|
-
border-top: 1px solid ${colors.mdHr};
|
|
1031
|
-
margin: 1em 0;
|
|
1032
|
-
}
|
|
1033
|
-
.markdown-content table {
|
|
1034
|
-
border-collapse: collapse;
|
|
1035
|
-
margin: 0.5em 0;
|
|
1036
|
-
width: 100%;
|
|
1037
|
-
}
|
|
1038
|
-
.markdown-content th, .markdown-content td {
|
|
1039
|
-
border: 1px solid ${colors.mdCodeBlockBorder};
|
|
1040
|
-
padding: 6px 10px;
|
|
1041
|
-
text-align: left;
|
|
1042
|
-
}
|
|
1043
|
-
.markdown-content th {
|
|
1044
|
-
background: rgba(${isLight ? "0, 0, 0" : "255, 255, 255"}, 0.05);
|
|
1045
|
-
font-weight: bold;
|
|
1046
|
-
}
|
|
1047
|
-
.markdown-content img {
|
|
1048
|
-
max-width: 100%;
|
|
1049
|
-
border-radius: 4px;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
/* Syntax highlighting (highlight.js) */
|
|
1053
|
-
.hljs { background: transparent; }
|
|
1054
|
-
.hljs-comment, .hljs-quote { color: ${colors.syntaxComment}; }
|
|
1055
|
-
.hljs-keyword, .hljs-selector-tag, .hljs-addition { color: ${colors.syntaxKeyword}; }
|
|
1056
|
-
.hljs-number, .hljs-literal, .hljs-symbol, .hljs-bullet { color: ${colors.syntaxNumber}; }
|
|
1057
|
-
.hljs-string, .hljs-doctag, .hljs-regexp { color: ${colors.syntaxString}; }
|
|
1058
|
-
.hljs-title, .hljs-section, .hljs-name, .hljs-selector-id, .hljs-selector-class { color: ${colors.syntaxFunction}; }
|
|
1059
|
-
.hljs-type, .hljs-class, .hljs-built_in { color: ${colors.syntaxType}; }
|
|
1060
|
-
.hljs-attr, .hljs-variable, .hljs-template-variable, .hljs-params { color: ${colors.syntaxVariable}; }
|
|
1061
|
-
.hljs-attribute { color: ${colors.syntaxVariable}; }
|
|
1062
|
-
.hljs-meta { color: ${colors.syntaxKeyword}; }
|
|
1063
|
-
.hljs-formula { background: rgba(${isLight ? "0, 0, 0" : "255, 255, 255"}, 0.05); }
|
|
1064
|
-
.hljs-deletion { color: ${colors.toolDiffRemoved}; }
|
|
1065
|
-
.hljs-emphasis { font-style: italic; }
|
|
1066
|
-
.hljs-strong { font-weight: bold; }
|
|
1067
|
-
.hljs-link { color: ${colors.mdLink}; text-decoration: underline; }
|
|
1068
|
-
|
|
1069
|
-
@media print { body { background: white; color: black; } .tool-execution { border: 1px solid #ddd; } }
|
|
1070
|
-
</style>
|
|
1071
|
-
</head>
|
|
1072
|
-
<body>
|
|
1073
|
-
<div class="container">
|
|
1074
|
-
<div class="header">
|
|
1075
|
-
<h1>${APP_NAME} v${VERSION}</h1>
|
|
1076
|
-
<div class="header-info">
|
|
1077
|
-
<div class="info-item"><span class="info-label">Session:</span><span class="info-value">${escapeHtml(data.sessionId)}</span></div>
|
|
1078
|
-
<div class="info-item"><span class="info-label">Date:</span><span class="info-value">${new Date(data.timestamp).toLocaleString()}</span></div>
|
|
1079
|
-
<div class="info-item"><span class="info-label">Models:</span><span class="info-value">${Array.from(data.modelsUsed)
|
|
1080
|
-
.map((m) => escapeHtml(m))
|
|
1081
|
-
.join(", ") || "unknown"}</span></div>
|
|
1082
|
-
</div>
|
|
1083
|
-
</div>
|
|
1084
|
-
|
|
1085
|
-
<div class="header">
|
|
1086
|
-
<h1>Messages</h1>
|
|
1087
|
-
<div class="header-info">
|
|
1088
|
-
<div class="info-item"><span class="info-label">User:</span><span class="info-value">${userMessages}</span></div>
|
|
1089
|
-
<div class="info-item"><span class="info-label">Assistant:</span><span class="info-value">${assistantMessages}</span></div>
|
|
1090
|
-
<div class="info-item"><span class="info-label">Tool Calls:</span><span class="info-value">${toolCallsCount}</span></div>
|
|
1091
|
-
</div>
|
|
1092
|
-
</div>
|
|
1093
|
-
|
|
1094
|
-
<div class="header">
|
|
1095
|
-
<h1>Tokens & Cost</h1>
|
|
1096
|
-
<div class="header-info">
|
|
1097
|
-
<div class="info-item"><span class="info-label">Input:</span><span class="info-value">${data.tokenStats.input.toLocaleString()} tokens</span></div>
|
|
1098
|
-
<div class="info-item"><span class="info-label">Output:</span><span class="info-value">${data.tokenStats.output.toLocaleString()} tokens</span></div>
|
|
1099
|
-
<div class="info-item"><span class="info-label">Cache Read:</span><span class="info-value">${data.tokenStats.cacheRead.toLocaleString()} tokens</span></div>
|
|
1100
|
-
<div class="info-item"><span class="info-label">Cache Write:</span><span class="info-value">${data.tokenStats.cacheWrite.toLocaleString()} tokens</span></div>
|
|
1101
|
-
<div class="info-item"><span class="info-label">Total:</span><span class="info-value">${(data.tokenStats.input + data.tokenStats.output + data.tokenStats.cacheRead + data.tokenStats.cacheWrite).toLocaleString()} tokens</span></div>
|
|
1102
|
-
<div class="info-item"><span class="info-label">Input Cost:</span><span class="info-value cost">$${data.costStats.input.toFixed(4)}</span></div>
|
|
1103
|
-
<div class="info-item"><span class="info-label">Output Cost:</span><span class="info-value cost">$${data.costStats.output.toFixed(4)}</span></div>
|
|
1104
|
-
<div class="info-item"><span class="info-label">Cache Read Cost:</span><span class="info-value cost">$${data.costStats.cacheRead.toFixed(4)}</span></div>
|
|
1105
|
-
<div class="info-item"><span class="info-label">Cache Write Cost:</span><span class="info-value cost">$${data.costStats.cacheWrite.toFixed(4)}</span></div>
|
|
1106
|
-
<div class="info-item"><span class="info-label">Total Cost:</span><span class="info-value cost"><strong>$${(data.costStats.input + data.costStats.output + data.costStats.cacheRead + data.costStats.cacheWrite).toFixed(4)}</strong></span></div>
|
|
1107
|
-
<div class="info-item"><span class="info-label">Context Usage:</span><span class="info-value">${contextUsageText}</span></div>
|
|
1108
|
-
</div>
|
|
1109
|
-
</div>
|
|
1110
|
-
|
|
1111
|
-
${systemPromptHtml}
|
|
1112
|
-
${toolsHtml}
|
|
1113
|
-
${streamingNotice}
|
|
1114
|
-
|
|
1115
|
-
<div class="messages">
|
|
1116
|
-
${messagesHtml}
|
|
1117
|
-
</div>
|
|
1118
|
-
|
|
1119
|
-
<div class="footer">
|
|
1120
|
-
Generated by ${APP_NAME} coding-agent on ${new Date().toLocaleString()}
|
|
1121
|
-
</div>
|
|
1122
|
-
</div>
|
|
1123
|
-
</body>
|
|
1124
|
-
</html>`;
|
|
1125
|
-
}
|
|
1126
|
-
/**
|
|
1127
|
-
* Export session to HTML using SessionManager and AgentState.
|
|
1128
|
-
* Used by TUI's /export command.
|
|
1129
|
-
* @param sessionManager The session manager
|
|
1130
|
-
* @param state The agent state
|
|
1131
|
-
* @param options Export options including output path and theme name
|
|
1132
|
-
*/
|
|
1133
|
-
export function exportSessionToHtml(sessionManager, state, options) {
|
|
1134
|
-
// Handle backwards compatibility: options can be just the output path string
|
|
1135
|
-
const opts = typeof options === "string" ? { outputPath: options } : options || {};
|
|
1136
|
-
const sessionFile = sessionManager.getSessionFile();
|
|
1137
|
-
const content = readFileSync(sessionFile, "utf8");
|
|
1138
|
-
const data = parseSessionFile(content);
|
|
1139
|
-
// Enrich with data from AgentState (tools, context window)
|
|
1140
|
-
data.tools = state.tools.map((t) => ({
|
|
1141
|
-
name: t.name,
|
|
1142
|
-
description: t.description,
|
|
1143
|
-
}));
|
|
1144
|
-
data.contextWindow = state.model?.contextWindow;
|
|
1145
|
-
if (!data.systemPrompt) {
|
|
1146
|
-
data.systemPrompt = state.systemPrompt;
|
|
1147
|
-
}
|
|
1148
|
-
let outputPath = opts.outputPath;
|
|
1149
|
-
if (!outputPath) {
|
|
1150
|
-
const sessionBasename = basename(sessionFile, ".jsonl");
|
|
1151
|
-
outputPath = `${APP_NAME}-session-${sessionBasename}.html`;
|
|
1152
|
-
}
|
|
1153
|
-
const colors = getThemeColors(opts.themeName);
|
|
1154
|
-
const isLight = isLightTheme(opts.themeName);
|
|
1155
|
-
const html = generateHtml(data, basename(sessionFile), colors, isLight);
|
|
1156
|
-
writeFileSync(outputPath, html, "utf8");
|
|
1157
|
-
return outputPath;
|
|
1158
|
-
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Export session file to HTML (standalone, without AgentState).
|
|
1161
|
-
* Auto-detects format: session manager format or streaming event format.
|
|
1162
|
-
* Used by CLI for exporting arbitrary session files.
|
|
1163
|
-
* @param inputPath Path to the session file
|
|
1164
|
-
* @param options Export options including output path and theme name
|
|
1165
|
-
*/
|
|
1166
|
-
export function exportFromFile(inputPath, options) {
|
|
1167
|
-
// Handle backwards compatibility: options can be just the output path string
|
|
1168
|
-
const opts = typeof options === "string" ? { outputPath: options } : options || {};
|
|
1169
|
-
if (!existsSync(inputPath)) {
|
|
1170
|
-
throw new Error(`File not found: ${inputPath}`);
|
|
1171
|
-
}
|
|
1172
|
-
const content = readFileSync(inputPath, "utf8");
|
|
1173
|
-
const data = parseSessionFile(content);
|
|
1174
|
-
let outputPath = opts.outputPath;
|
|
1175
|
-
if (!outputPath) {
|
|
1176
|
-
const inputBasename = basename(inputPath, ".jsonl");
|
|
1177
|
-
outputPath = `${APP_NAME}-session-${inputBasename}.html`;
|
|
1178
|
-
}
|
|
1179
|
-
const colors = getThemeColors(opts.themeName);
|
|
1180
|
-
const isLight = isLightTheme(opts.themeName);
|
|
1181
|
-
const html = generateHtml(data, basename(inputPath), colors, isLight);
|
|
1182
|
-
writeFileSync(outputPath, html, "utf8");
|
|
1183
|
-
return outputPath;
|
|
1184
|
-
}
|
|
1185
|
-
//# sourceMappingURL=export-html.js.map
|