@oh-my-pi/pi-coding-agent 16.2.1 → 16.2.3
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 +72 -0
- package/dist/cli.js +3621 -3579
- package/dist/types/advisor/__tests__/config.test.d.ts +1 -0
- package/dist/types/advisor/advise-tool.d.ts +8 -4
- package/dist/types/advisor/config.d.ts +88 -0
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/runtime.d.ts +15 -1
- package/dist/types/advisor/transcript-recorder.d.ts +13 -2
- package/dist/types/advisor/watchdog.d.ts +20 -0
- package/dist/types/collab/guest.d.ts +29 -0
- package/dist/types/config/model-roles.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +113 -12
- package/dist/types/debug/log-viewer.d.ts +1 -0
- package/dist/types/debug/raw-sse.d.ts +1 -0
- package/dist/types/discovery/omp-extension-roots.d.ts +3 -3
- package/dist/types/edit/hashline/diff.d.ts +0 -11
- package/dist/types/edit/index.d.ts +18 -0
- package/dist/types/edit/streaming.d.ts +30 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/extensibility/tool-event-input.d.ts +7 -0
- package/dist/types/extensibility/utils.d.ts +12 -0
- package/dist/types/mcp/oauth-discovery.d.ts +0 -11
- package/dist/types/mcp/transports/index.d.ts +1 -0
- package/dist/types/mcp/transports/sse.d.ts +20 -0
- package/dist/types/modes/components/advisor-config.d.ts +59 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +9 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/component.d.ts +30 -3
- package/dist/types/modes/components/status-line/types.d.ts +13 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -4
- package/dist/types/modes/skill-command.d.ts +32 -0
- package/dist/types/modes/types.d.ts +7 -2
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +84 -12
- package/dist/types/session/indexed-session-storage.d.ts +7 -1
- package/dist/types/session/messages.d.ts +32 -7
- package/dist/types/session/messages.test.d.ts +1 -0
- package/dist/types/session/session-entries.d.ts +31 -3
- package/dist/types/session/session-history-format.d.ts +6 -0
- package/dist/types/session/session-loader.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +6 -4
- package/dist/types/session/session-storage.d.ts +11 -0
- package/dist/types/session/session-title-slot.d.ts +19 -0
- package/dist/types/session/settings-stream-fn.d.ts +21 -0
- package/dist/types/session/turn-persistence.d.ts +88 -0
- package/dist/types/ssh/connection-manager.d.ts +47 -0
- package/dist/types/ssh/utils.d.ts +16 -0
- package/dist/types/task/executor.d.ts +3 -16
- package/dist/types/task/render.d.ts +0 -5
- package/dist/types/task/renderer.d.ts +13 -0
- package/dist/types/task/types.d.ts +16 -0
- package/dist/types/task/yield-assembly.d.ts +28 -0
- package/dist/types/tiny/text.d.ts +8 -0
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/review.d.ts +6 -4
- package/dist/types/tools/ssh.d.ts +1 -1
- package/dist/types/tools/todo.d.ts +6 -0
- package/dist/types/tools/yield.d.ts +8 -3
- package/dist/types/utils/thinking-display.d.ts +4 -0
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +438 -10
- package/src/advisor/__tests__/config.test.ts +173 -0
- package/src/advisor/advise-tool.ts +11 -6
- package/src/advisor/config.ts +256 -0
- package/src/advisor/index.ts +1 -0
- package/src/advisor/runtime.ts +77 -4
- package/src/advisor/transcript-recorder.ts +25 -2
- package/src/advisor/watchdog.ts +57 -31
- package/src/auto-thinking/classifier.ts +2 -2
- package/src/autoresearch/index.ts +7 -2
- package/src/cli/gc-cli.ts +17 -10
- package/src/collab/guest.ts +43 -7
- package/src/config/model-registry.ts +80 -18
- package/src/config/model-resolver.ts +5 -1
- package/src/config/model-roles.ts +3 -3
- package/src/config/settings-schema.ts +107 -8
- package/src/debug/index.ts +32 -7
- package/src/debug/log-viewer.ts +111 -53
- package/src/debug/raw-sse.ts +68 -48
- package/src/discovery/codex.ts +13 -5
- package/src/discovery/omp-extension-roots.ts +38 -13
- package/src/edit/hashline/diff.ts +57 -4
- package/src/edit/index.ts +21 -0
- package/src/edit/streaming.ts +170 -0
- package/src/eval/js/shared/local-module-loader.ts +23 -1
- package/src/export/html/template.js +13 -7
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/loader.ts +5 -3
- package/src/extensibility/extensions/wrapper.ts +9 -3
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/hooks/tool-wrapper.ts +13 -4
- package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +18 -1
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +59 -2
- package/src/extensibility/plugins/manager.ts +76 -5
- package/src/extensibility/shared-events.ts +1 -0
- package/src/extensibility/tool-event-input.ts +23 -0
- package/src/extensibility/utils.ts +74 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/mcp/client.ts +3 -1
- package/src/mcp/manager.ts +12 -5
- package/src/mcp/oauth-discovery.ts +5 -29
- package/src/mcp/transports/http.ts +3 -1
- package/src/mcp/transports/index.ts +1 -0
- package/src/mcp/transports/sse.ts +377 -0
- package/src/memories/index.ts +15 -6
- package/src/mnemopi/backend.ts +2 -2
- package/src/modes/acp/acp-agent.ts +1 -1
- package/src/modes/components/advisor-config.ts +555 -0
- package/src/modes/components/advisor-message.ts +9 -2
- package/src/modes/components/agent-hub.ts +9 -4
- package/src/modes/components/assistant-message.ts +5 -5
- package/src/modes/components/index.ts +2 -0
- package/src/modes/components/model-selector.ts +79 -48
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/component.ts +145 -14
- package/src/modes/components/status-line/segments.ts +47 -22
- package/src/modes/components/status-line/types.ts +13 -1
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/controllers/command-controller.ts +23 -2
- package/src/modes/controllers/event-controller.ts +114 -11
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +61 -61
- package/src/modes/controllers/selector-controller.ts +100 -9
- package/src/modes/interactive-mode.ts +65 -10
- package/src/modes/print-mode.ts +1 -1
- package/src/modes/skill-command.ts +116 -0
- package/src/modes/types.ts +7 -2
- package/src/modes/utils/transcript-render-helpers.ts +2 -2
- package/src/modes/utils/ui-helpers.ts +46 -27
- package/src/prompts/agents/reviewer.md +11 -10
- package/src/prompts/review-custom-request.md +1 -2
- package/src/prompts/review-request.md +1 -2
- package/src/prompts/system/interrupted-thinking.md +7 -0
- package/src/prompts/system/recap-user.md +9 -0
- package/src/prompts/system/subagent-system-prompt.md +8 -5
- package/src/prompts/system/subagent-yield-reminder.md +6 -5
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/irc.md +2 -2
- package/src/prompts/tools/read.md +2 -2
- package/src/sdk.ts +40 -50
- package/src/session/agent-session.ts +1139 -600
- package/src/session/indexed-session-storage.ts +86 -13
- package/src/session/messages.test.ts +125 -0
- package/src/session/messages.ts +192 -21
- package/src/session/redis-session-storage.ts +49 -2
- package/src/session/session-entries.ts +39 -2
- package/src/session/session-history-format.ts +29 -2
- package/src/session/session-listing.ts +54 -24
- package/src/session/session-loader.ts +66 -3
- package/src/session/session-manager.ts +113 -19
- package/src/session/session-persistence.ts +96 -3
- package/src/session/session-storage.ts +36 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/settings-stream-fn.ts +49 -0
- package/src/session/sql-session-storage.ts +71 -11
- package/src/session/turn-persistence.ts +142 -0
- package/src/session/unexpected-stop-classifier.ts +2 -2
- package/src/slash-commands/builtin-registry.ts +16 -3
- package/src/slash-commands/helpers/mcp.ts +2 -1
- package/src/ssh/__tests__/connection-manager-args.test.ts +123 -1
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +55 -18
- package/src/ssh/connection-manager.ts +139 -12
- package/src/ssh/file-transfer.ts +23 -18
- package/src/ssh/ssh-executor.ts +2 -13
- package/src/ssh/utils.ts +19 -0
- package/src/task/executor.ts +21 -23
- package/src/task/render.ts +162 -20
- package/src/task/renderer.ts +14 -0
- package/src/task/types.ts +17 -0
- package/src/task/yield-assembly.ts +207 -0
- package/src/tiny/models.ts +8 -6
- package/src/tiny/text.ts +37 -7
- package/src/tools/ask.ts +55 -4
- package/src/tools/image-gen.ts +2 -1
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/renderers.ts +8 -2
- package/src/tools/review.ts +17 -7
- package/src/tools/ssh.ts +8 -4
- package/src/tools/todo.ts +17 -1
- package/src/tools/tts.ts +2 -1
- package/src/tools/yield.ts +140 -31
- package/src/utils/thinking-display.ts +15 -0
- package/src/utils/title-generator.ts +1 -1
- package/src/web/search/providers/tavily.ts +36 -19
|
@@ -600,6 +600,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
600
600
|
"Use the terminal's default background for the status line instead of the theme's `statusLineBg`. Powerline end caps are dropped because they need a contrasting fill to bridge into the surrounding terminal.",
|
|
601
601
|
},
|
|
602
602
|
},
|
|
603
|
+
"statusLine.compactThinkingLevel": {
|
|
604
|
+
type: "boolean",
|
|
605
|
+
default: false,
|
|
606
|
+
ui: {
|
|
607
|
+
tab: "appearance",
|
|
608
|
+
group: "Status Line",
|
|
609
|
+
label: "Compact Thinking Level",
|
|
610
|
+
description:
|
|
611
|
+
"Show the thinking level as a single icon on the model name instead of a separate ` · <level>` suffix.",
|
|
612
|
+
},
|
|
613
|
+
},
|
|
603
614
|
"tools.artifactSpillThreshold": {
|
|
604
615
|
type: "number",
|
|
605
616
|
default: 50,
|
|
@@ -1183,6 +1194,23 @@ export const SETTINGS_SCHEMA = {
|
|
|
1183
1194
|
},
|
|
1184
1195
|
},
|
|
1185
1196
|
|
|
1197
|
+
textVerbosity: {
|
|
1198
|
+
type: "enum",
|
|
1199
|
+
values: ["low", "medium", "high"] as const,
|
|
1200
|
+
default: "high",
|
|
1201
|
+
ui: {
|
|
1202
|
+
tab: "model",
|
|
1203
|
+
group: "Sampling",
|
|
1204
|
+
label: "Text Verbosity",
|
|
1205
|
+
description: "OpenAI Responses and Codex response verbosity (low, medium, or high)",
|
|
1206
|
+
options: [
|
|
1207
|
+
{ value: "low", label: "Low", description: "Prefer concise responses" },
|
|
1208
|
+
{ value: "medium", label: "Medium", description: "Balance brevity and detail" },
|
|
1209
|
+
{ value: "high", label: "High", description: "Prefer detailed responses (default)" },
|
|
1210
|
+
],
|
|
1211
|
+
},
|
|
1212
|
+
},
|
|
1213
|
+
|
|
1186
1214
|
serviceTier: {
|
|
1187
1215
|
type: "enum",
|
|
1188
1216
|
values: SERVICE_TIER_SETTING_VALUES,
|
|
@@ -1616,6 +1644,35 @@ export const SETTINGS_SCHEMA = {
|
|
|
1616
1644
|
},
|
|
1617
1645
|
},
|
|
1618
1646
|
|
|
1647
|
+
"recap.enabled": {
|
|
1648
|
+
type: "boolean",
|
|
1649
|
+
default: true,
|
|
1650
|
+
ui: {
|
|
1651
|
+
tab: "interaction",
|
|
1652
|
+
group: "Notifications",
|
|
1653
|
+
label: "Idle Recap",
|
|
1654
|
+
description: "Generate a brief LLM recap of where things stand after the terminal has been idle",
|
|
1655
|
+
},
|
|
1656
|
+
},
|
|
1657
|
+
|
|
1658
|
+
"recap.idleSeconds": {
|
|
1659
|
+
type: "number",
|
|
1660
|
+
default: 240,
|
|
1661
|
+
ui: {
|
|
1662
|
+
tab: "interaction",
|
|
1663
|
+
group: "Notifications",
|
|
1664
|
+
label: "Idle Recap Delay",
|
|
1665
|
+
description: "Seconds to wait while idle before showing the recap",
|
|
1666
|
+
options: [
|
|
1667
|
+
{ value: "60", label: "1 minute" },
|
|
1668
|
+
{ value: "120", label: "2 minutes" },
|
|
1669
|
+
{ value: "240", label: "4 minutes" },
|
|
1670
|
+
{ value: "300", label: "5 minutes" },
|
|
1671
|
+
{ value: "600", label: "10 minutes" },
|
|
1672
|
+
],
|
|
1673
|
+
},
|
|
1674
|
+
},
|
|
1675
|
+
|
|
1619
1676
|
// Collab
|
|
1620
1677
|
"collab.relayUrl": {
|
|
1621
1678
|
type: "string",
|
|
@@ -1872,6 +1929,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
1872
1929
|
},
|
|
1873
1930
|
},
|
|
1874
1931
|
|
|
1932
|
+
"compaction.remoteStreamingV2Enabled": {
|
|
1933
|
+
type: "boolean",
|
|
1934
|
+
default: true,
|
|
1935
|
+
ui: {
|
|
1936
|
+
tab: "context",
|
|
1937
|
+
group: "Compaction",
|
|
1938
|
+
label: "Remote Compaction V2",
|
|
1939
|
+
description: "Use Responses streaming compaction for compatible remote compaction models",
|
|
1940
|
+
},
|
|
1941
|
+
},
|
|
1942
|
+
|
|
1875
1943
|
"compaction.reserveTokens": { type: "number", default: 16384 },
|
|
1876
1944
|
|
|
1877
1945
|
"compaction.keepRecentTokens": { type: "number", default: 20000 },
|
|
@@ -1880,6 +1948,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
1880
1948
|
|
|
1881
1949
|
"compaction.remoteEndpoint": { type: "string", default: undefined },
|
|
1882
1950
|
|
|
1951
|
+
"compaction.v2RetainedMessageBudget": { type: "number", default: 64000 },
|
|
1952
|
+
|
|
1883
1953
|
// Idle compaction
|
|
1884
1954
|
"compaction.idleEnabled": {
|
|
1885
1955
|
type: "boolean",
|
|
@@ -2007,7 +2077,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
2007
2077
|
"anthropic",
|
|
2008
2078
|
"deepseek",
|
|
2009
2079
|
"harmony",
|
|
2010
|
-
"pi",
|
|
2011
2080
|
"qwen3",
|
|
2012
2081
|
"gemini",
|
|
2013
2082
|
"gemma",
|
|
@@ -2034,7 +2103,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
2034
2103
|
{ value: "anthropic", label: "Anthropic", description: "Use Anthropic-style in-band tool calls." },
|
|
2035
2104
|
{ value: "deepseek", label: "DeepSeek", description: "Use DeepSeek-style in-band tool calls." },
|
|
2036
2105
|
{ value: "harmony", label: "Harmony", description: "Use Harmony-style in-band tool calls." },
|
|
2037
|
-
{ value: "pi", label: "Pi", description: "Use the Pi owned dialect (compact sigil-delimited tool calls)." },
|
|
2038
2106
|
{ value: "qwen3", label: "Qwen3", description: "Use the Qwen3 owned dialect." },
|
|
2039
2107
|
{ value: "gemini", label: "Gemini", description: "Use the Gemini owned dialect." },
|
|
2040
2108
|
{ value: "gemma", label: "Gemma", description: "Use the Gemma owned dialect." },
|
|
@@ -2440,11 +2508,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
2440
2508
|
tab: "memory",
|
|
2441
2509
|
group: "Mnemopi",
|
|
2442
2510
|
label: "Mnemopi LLM Mode",
|
|
2443
|
-
description:
|
|
2511
|
+
description:
|
|
2512
|
+
"Use no LLM, the online tiny model (the TINY role from /models, else pi/smol), or a remote OpenAI-compatible endpoint",
|
|
2444
2513
|
condition: "mnemopiActive",
|
|
2445
2514
|
options: [
|
|
2446
2515
|
{ value: "none", label: "None", description: "Disable Mnemopi LLM-backed extraction" },
|
|
2447
|
-
{
|
|
2516
|
+
{
|
|
2517
|
+
value: "smol",
|
|
2518
|
+
label: "Online (tiny)",
|
|
2519
|
+
description: "Use the online tiny model (the TINY role from /models, else pi/smol)",
|
|
2520
|
+
},
|
|
2448
2521
|
{ value: "remote", label: "Remote", description: "Use the Mnemopi remote LLM settings below" },
|
|
2449
2522
|
],
|
|
2450
2523
|
},
|
|
@@ -3776,6 +3849,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
3776
3849
|
},
|
|
3777
3850
|
},
|
|
3778
3851
|
|
|
3852
|
+
"title.refreshOnReplan": {
|
|
3853
|
+
type: "boolean",
|
|
3854
|
+
default: true,
|
|
3855
|
+
ui: {
|
|
3856
|
+
tab: "tasks",
|
|
3857
|
+
group: "Modes",
|
|
3858
|
+
label: "Refresh Title on Replan",
|
|
3859
|
+
description: "Refresh generated session titles after todo init replans unless the title was set by the user",
|
|
3860
|
+
},
|
|
3861
|
+
},
|
|
3862
|
+
|
|
3779
3863
|
// Delegation
|
|
3780
3864
|
"task.isolation.mode": {
|
|
3781
3865
|
type: "enum",
|
|
@@ -4342,7 +4426,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
4342
4426
|
tab: "providers",
|
|
4343
4427
|
group: "Tiny Model",
|
|
4344
4428
|
label: "Tiny Model",
|
|
4345
|
-
description:
|
|
4429
|
+
description:
|
|
4430
|
+
"Session-title model: online (the TINY role from /models, else pi/smol) by default, or a local on-device model",
|
|
4346
4431
|
options: TINY_TITLE_MODEL_OPTIONS,
|
|
4347
4432
|
},
|
|
4348
4433
|
},
|
|
@@ -4381,7 +4466,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
4381
4466
|
group: "General",
|
|
4382
4467
|
label: "Memory Model",
|
|
4383
4468
|
description:
|
|
4384
|
-
"Mnemopi LLM for fact extraction + consolidation: online (smol/remote) by default, or a local on-device model",
|
|
4469
|
+
"Mnemopi LLM for fact extraction + consolidation: online (the TINY role from /models, else smol/remote) by default, or a local on-device model",
|
|
4385
4470
|
condition: "mnemopiActive",
|
|
4386
4471
|
options: TINY_MEMORY_MODEL_OPTIONS,
|
|
4387
4472
|
},
|
|
@@ -4396,7 +4481,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
4396
4481
|
group: "Thinking",
|
|
4397
4482
|
label: "Auto Thinking Model",
|
|
4398
4483
|
description:
|
|
4399
|
-
"Difficulty classifier for the `auto` thinking level: online smol by default, or a local on-device model",
|
|
4484
|
+
"Difficulty classifier for the `auto` thinking level: online (the TINY role from /models, else smol) by default, or a local on-device model",
|
|
4400
4485
|
condition: "autoThinkingActive",
|
|
4401
4486
|
options: AUTO_THINKING_MODEL_OPTIONS,
|
|
4402
4487
|
},
|
|
@@ -4420,7 +4505,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
4420
4505
|
tab: "providers",
|
|
4421
4506
|
group: "Tiny Model",
|
|
4422
4507
|
label: "Unexpected Stop Model",
|
|
4423
|
-
description:
|
|
4508
|
+
description:
|
|
4509
|
+
"Classifier for unexpected-stop detection: online (the TINY role from /models, else smol) by default, or a local on-device model.",
|
|
4424
4510
|
condition: "unexpectedStopDetection",
|
|
4425
4511
|
options: TINY_MEMORY_MODEL_OPTIONS,
|
|
4426
4512
|
},
|
|
@@ -4828,6 +4914,8 @@ export interface CompactionSettings {
|
|
|
4828
4914
|
autoContinue: boolean;
|
|
4829
4915
|
remoteEnabled: boolean;
|
|
4830
4916
|
remoteEndpoint: string | undefined;
|
|
4917
|
+
remoteStreamingV2Enabled: boolean;
|
|
4918
|
+
v2RetainedMessageBudget: number;
|
|
4831
4919
|
idleEnabled: boolean;
|
|
4832
4920
|
idleThresholdTokens: number;
|
|
4833
4921
|
idleTimeoutSeconds: number;
|
|
@@ -4835,6 +4923,15 @@ export interface CompactionSettings {
|
|
|
4835
4923
|
dropUseless: boolean;
|
|
4836
4924
|
}
|
|
4837
4925
|
|
|
4926
|
+
export interface RecapSettings {
|
|
4927
|
+
enabled: boolean;
|
|
4928
|
+
idleSeconds: number;
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4931
|
+
export interface TitleSettings {
|
|
4932
|
+
refreshOnReplan: boolean;
|
|
4933
|
+
}
|
|
4934
|
+
|
|
4838
4935
|
export interface ContextPromotionSettings {
|
|
4839
4936
|
enabled: boolean;
|
|
4840
4937
|
}
|
|
@@ -4980,6 +5077,8 @@ export interface GcSettings {
|
|
|
4980
5077
|
/** Map group prefix -> typed settings interface */
|
|
4981
5078
|
export interface GroupTypeMap {
|
|
4982
5079
|
compaction: CompactionSettings;
|
|
5080
|
+
recap: RecapSettings;
|
|
5081
|
+
title: TitleSettings;
|
|
4983
5082
|
contextPromotion: ContextPromotionSettings;
|
|
4984
5083
|
retry: RetrySettings;
|
|
4985
5084
|
memories: MemoriesSettings;
|
package/src/debug/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Container,
|
|
11
11
|
isNotificationSuppressed,
|
|
12
12
|
Loader,
|
|
13
|
+
type OverlayHandle,
|
|
13
14
|
type SelectItem,
|
|
14
15
|
SelectList,
|
|
15
16
|
Spacer,
|
|
@@ -327,18 +328,29 @@ export class DebugSelectorComponent extends Container {
|
|
|
327
328
|
return;
|
|
328
329
|
}
|
|
329
330
|
|
|
331
|
+
let overlay: OverlayHandle | undefined;
|
|
332
|
+
const close = (): void => {
|
|
333
|
+
overlay?.hide();
|
|
334
|
+
overlay = undefined;
|
|
335
|
+
void this.ctx.showDebugSelector();
|
|
336
|
+
};
|
|
330
337
|
const viewer = new DebugLogViewerComponent({
|
|
331
338
|
logs,
|
|
332
339
|
terminalRows: this.ctx.ui.terminal.rows,
|
|
333
|
-
onExit:
|
|
340
|
+
onExit: close,
|
|
334
341
|
onStatus: message => this.ctx.showStatus(message, { dim: true }),
|
|
335
342
|
onError: message => this.ctx.showError(message),
|
|
336
343
|
onUpdate: () => this.ctx.ui.requestRender(),
|
|
337
344
|
logSource,
|
|
338
345
|
});
|
|
339
346
|
|
|
340
|
-
this.ctx.
|
|
341
|
-
|
|
347
|
+
overlay = this.ctx.ui.showOverlay(viewer, {
|
|
348
|
+
anchor: "top-left",
|
|
349
|
+
width: "100%",
|
|
350
|
+
maxHeight: "100%",
|
|
351
|
+
margin: 0,
|
|
352
|
+
fullscreen: true,
|
|
353
|
+
});
|
|
342
354
|
this.ctx.ui.setFocus(viewer);
|
|
343
355
|
} catch (err) {
|
|
344
356
|
this.ctx.showError(`Failed to read logs: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -348,16 +360,29 @@ export class DebugSelectorComponent extends Container {
|
|
|
348
360
|
}
|
|
349
361
|
|
|
350
362
|
async #handleViewRawSse(): Promise<void> {
|
|
351
|
-
|
|
363
|
+
let overlay: OverlayHandle | undefined;
|
|
364
|
+
let viewer: RawSseViewerComponent | undefined;
|
|
365
|
+
const close = (): void => {
|
|
366
|
+
viewer?.dispose();
|
|
367
|
+
overlay?.hide();
|
|
368
|
+
overlay = undefined;
|
|
369
|
+
void this.ctx.showDebugSelector();
|
|
370
|
+
};
|
|
371
|
+
viewer = new RawSseViewerComponent({
|
|
352
372
|
buffer: resolveRawSseDebugBuffer(this.ctx.session),
|
|
353
373
|
terminalRows: this.ctx.ui.terminal.rows,
|
|
354
|
-
onExit:
|
|
374
|
+
onExit: close,
|
|
355
375
|
onStatus: message => this.ctx.showStatus(message, { dim: true }),
|
|
356
376
|
onUpdate: () => this.ctx.ui.requestRender(),
|
|
357
377
|
});
|
|
358
378
|
|
|
359
|
-
this.ctx.
|
|
360
|
-
|
|
379
|
+
overlay = this.ctx.ui.showOverlay(viewer, {
|
|
380
|
+
anchor: "top-left",
|
|
381
|
+
width: "100%",
|
|
382
|
+
maxHeight: "100%",
|
|
383
|
+
margin: 0,
|
|
384
|
+
fullscreen: true,
|
|
385
|
+
});
|
|
361
386
|
this.ctx.ui.setFocus(viewer);
|
|
362
387
|
this.ctx.ui.requestRender();
|
|
363
388
|
}
|
package/src/debug/log-viewer.ts
CHANGED
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
extractPrintableText,
|
|
4
4
|
matchesKey,
|
|
5
5
|
padding,
|
|
6
|
+
parseSgrMouse,
|
|
6
7
|
replaceTabs,
|
|
7
8
|
truncateToWidth,
|
|
8
9
|
visibleWidth,
|
|
9
10
|
} from "@oh-my-pi/pi-tui";
|
|
10
11
|
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
12
|
+
import { bottomBorder, divider, row, topBorder } from "../modes/components/overlay-box";
|
|
11
13
|
import { theme } from "../modes/theme/theme";
|
|
12
14
|
import { copyToClipboard } from "../utils/clipboard";
|
|
13
15
|
import {
|
|
@@ -23,6 +25,8 @@ export const LOAD_OLDER_LABEL = "### MOVE UP TO LOAD MORE...";
|
|
|
23
25
|
|
|
24
26
|
const INITIAL_LOG_CHUNK = 50;
|
|
25
27
|
const LOAD_OLDER_CHUNK = 50;
|
|
28
|
+
const MIN_LOG_VIEWER_WIDTH = 48;
|
|
29
|
+
const LOG_VIEWER_CHROME_LINES = 8;
|
|
26
30
|
|
|
27
31
|
type LogEntry = {
|
|
28
32
|
rawLine: string;
|
|
@@ -184,6 +188,31 @@ export class DebugLogViewerModel {
|
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
|
|
191
|
+
moveCursorToRow(rowIndex: number, extendSelection: boolean): boolean {
|
|
192
|
+
const selectableIndex = this.#selectableRowIndices.indexOf(rowIndex);
|
|
193
|
+
if (selectableIndex < 0) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (extendSelection && this.#selectionAnchorSelectableIndex === undefined) {
|
|
198
|
+
const row = this.#getCursorRow();
|
|
199
|
+
if (row?.kind === "log") {
|
|
200
|
+
this.#selectionAnchorSelectableIndex = this.#cursorSelectableIndex;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.#cursorSelectableIndex = selectableIndex;
|
|
205
|
+
|
|
206
|
+
if (!extendSelection) {
|
|
207
|
+
this.#selectionAnchorSelectableIndex = undefined;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (this.#getCursorRow()?.kind !== "log" && !extendSelection) {
|
|
211
|
+
this.#selectionAnchorSelectableIndex = undefined;
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
187
216
|
getSelectedLogIndices(): number[] {
|
|
188
217
|
if (this.#selectableRowIndices.length === 0) {
|
|
189
218
|
return [];
|
|
@@ -484,6 +513,9 @@ export class DebugLogViewerComponent implements Component {
|
|
|
484
513
|
#scrollRowOffset = 0;
|
|
485
514
|
#statusMessage: string | undefined;
|
|
486
515
|
#loadingOlder = false;
|
|
516
|
+
#bodyRowStart = 0;
|
|
517
|
+
#bodyRowCount = 0;
|
|
518
|
+
#bodyLineToRowIndex: Array<number | undefined> = [];
|
|
487
519
|
|
|
488
520
|
constructor(options: DebugLogViewerComponentOptions) {
|
|
489
521
|
this.#logSource = options.logSource;
|
|
@@ -501,6 +533,10 @@ export class DebugLogViewerComponent implements Component {
|
|
|
501
533
|
}
|
|
502
534
|
|
|
503
535
|
handleInput(keyData: string): void {
|
|
536
|
+
if (keyData.startsWith("\x1b[<") && this.#handleMouse(keyData)) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
504
540
|
if (matchesKey(keyData, "escape") || matchesKey(keyData, "esc")) {
|
|
505
541
|
this.#onExit();
|
|
506
542
|
return;
|
|
@@ -598,59 +634,99 @@ export class DebugLogViewerComponent implements Component {
|
|
|
598
634
|
}
|
|
599
635
|
}
|
|
600
636
|
|
|
637
|
+
#handleMouse(keyData: string): boolean {
|
|
638
|
+
const event = parseSgrMouse(keyData);
|
|
639
|
+
if (!event) return false;
|
|
640
|
+
|
|
641
|
+
const overBody = event.row >= this.#bodyRowStart && event.row < this.#bodyRowStart + this.#bodyRowCount;
|
|
642
|
+
if (event.wheel !== null && overBody) {
|
|
643
|
+
this.#statusMessage = undefined;
|
|
644
|
+
const maxOffset = Math.max(0, this.#model.rows.length - this.#bodyHeight());
|
|
645
|
+
this.#scrollRowOffset = Math.max(0, Math.min(maxOffset, this.#scrollRowOffset + event.wheel * 3));
|
|
646
|
+
this.#onUpdate?.();
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (!event.leftClick || !overBody) return false;
|
|
651
|
+
const rowIndex = this.#bodyLineToRowIndex[event.row - this.#bodyRowStart];
|
|
652
|
+
if (rowIndex === undefined) return false;
|
|
653
|
+
|
|
654
|
+
const target = this.#model.rows[rowIndex];
|
|
655
|
+
if (!target || target.kind === "warning") return false;
|
|
656
|
+
this.#statusMessage = undefined;
|
|
657
|
+
this.#model.moveCursorToRow(rowIndex, false);
|
|
658
|
+
if (target.kind === "load-older") {
|
|
659
|
+
void this.#handleLoadOlder();
|
|
660
|
+
return true;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (this.#model.isExpanded(target.logIndex)) {
|
|
664
|
+
this.#model.collapseSelected();
|
|
665
|
+
} else {
|
|
666
|
+
this.#model.expandSelected();
|
|
667
|
+
}
|
|
668
|
+
this.#ensureCursorVisible();
|
|
669
|
+
this.#onUpdate?.();
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
|
|
601
673
|
invalidate(): void {
|
|
602
674
|
// no cached child state
|
|
603
675
|
}
|
|
604
676
|
|
|
605
677
|
render(width: number): readonly string[] {
|
|
606
|
-
this.#lastRenderWidth = Math.max(
|
|
678
|
+
this.#lastRenderWidth = Math.max(MIN_LOG_VIEWER_WIDTH, width);
|
|
607
679
|
this.#ensureCursorVisible();
|
|
608
680
|
|
|
609
|
-
const
|
|
681
|
+
const contentWidth = Math.max(1, this.#lastRenderWidth - 4);
|
|
610
682
|
const bodyHeight = this.#bodyHeight();
|
|
611
683
|
|
|
612
|
-
const rows = this.#renderRows(
|
|
613
|
-
|
|
684
|
+
const rows = this.#renderRows(contentWidth);
|
|
685
|
+
this.#bodyRowStart = 4;
|
|
686
|
+
this.#bodyRowCount = bodyHeight;
|
|
687
|
+
const visibleBodyLines = this.#renderVisibleBodyLines(rows, bodyHeight);
|
|
614
688
|
|
|
615
689
|
return [
|
|
616
|
-
this.#
|
|
617
|
-
|
|
618
|
-
this.#
|
|
619
|
-
|
|
620
|
-
this.#frameSeparator(innerWidth),
|
|
690
|
+
topBorder(this.#lastRenderWidth, "Recent Logs"),
|
|
691
|
+
row(this.#summaryText(), this.#lastRenderWidth),
|
|
692
|
+
row(this.#filterText(), this.#lastRenderWidth),
|
|
693
|
+
divider(this.#lastRenderWidth),
|
|
621
694
|
...visibleBodyLines,
|
|
622
|
-
|
|
623
|
-
this.#
|
|
695
|
+
divider(this.#lastRenderWidth),
|
|
696
|
+
row(this.#statusText(), this.#lastRenderWidth),
|
|
697
|
+
row(theme.fg("dim", this.#controlsText()), this.#lastRenderWidth),
|
|
698
|
+
bottomBorder(this.#lastRenderWidth),
|
|
624
699
|
];
|
|
625
700
|
}
|
|
626
701
|
|
|
627
702
|
#summaryText(): string {
|
|
628
|
-
|
|
703
|
+
const selected = this.#model.getSelectedCount();
|
|
704
|
+
const expanded = this.#model.expandedCount;
|
|
705
|
+
return `${theme.fg("muted", "showing")} ${theme.fg("accent", `${this.#model.visibleLogCount}/${this.#model.logCount}`)} ${theme.fg("muted", "selected")} ${theme.fg(selected > 0 ? "accent" : "muted", String(selected))} ${theme.fg("muted", "expanded")} ${theme.fg(expanded > 0 ? "accent" : "muted", String(expanded))}`;
|
|
629
706
|
}
|
|
630
707
|
|
|
631
708
|
#controlsText(): string {
|
|
632
|
-
return "Esc
|
|
709
|
+
return "Esc close · Ctrl+C copy · ↑/↓/wheel move · click toggle · Shift+↑/↓ select · ←/→ collapse/expand · Ctrl+A all · Ctrl+O older · Ctrl+P pid";
|
|
633
710
|
}
|
|
634
711
|
|
|
635
712
|
#filterText(): string {
|
|
636
713
|
const sanitized = replaceTabs(sanitizeText(this.#model.filterQuery));
|
|
637
|
-
const query = sanitized.length === 0 ? "" : theme.fg("accent", sanitized);
|
|
714
|
+
const query = sanitized.length === 0 ? theme.fg("muted", "type to filter") : theme.fg("accent", sanitized);
|
|
638
715
|
const pidStatus = this.#model.isProcessFilterEnabled()
|
|
639
|
-
? theme.fg("success", "pid
|
|
640
|
-
: theme.fg("muted", "pid
|
|
641
|
-
|
|
716
|
+
? theme.fg("success", "pid on")
|
|
717
|
+
: theme.fg("muted", "pid off");
|
|
718
|
+
const loading = this.#loadingOlder ? ` ${theme.fg("warning", "loading older…")}` : "";
|
|
719
|
+
return `${theme.fg("muted", "filter")} ${query} ${pidStatus}${loading}`;
|
|
642
720
|
}
|
|
643
721
|
|
|
644
722
|
#statusText(): string {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
return base;
|
|
723
|
+
return this.#statusMessage
|
|
724
|
+
? theme.fg("success", this.#statusMessage)
|
|
725
|
+
: theme.fg("dim", "Enter loads older when highlighted; printable keys update filter");
|
|
650
726
|
}
|
|
651
727
|
|
|
652
728
|
#bodyHeight(): number {
|
|
653
|
-
return Math.max(3, this.#terminalRows -
|
|
729
|
+
return Math.max(3, (process.stdout.rows || this.#terminalRows || 24) - LOG_VIEWER_CHROME_LINES);
|
|
654
730
|
}
|
|
655
731
|
|
|
656
732
|
async #handleLoadOlder(additionalCount: number = LOAD_OLDER_CHUNK): Promise<void> {
|
|
@@ -780,26 +856,25 @@ export class DebugLogViewerComponent implements Component {
|
|
|
780
856
|
return rendered;
|
|
781
857
|
}
|
|
782
858
|
|
|
783
|
-
#renderVisibleBodyLines(
|
|
784
|
-
|
|
785
|
-
innerWidth: number,
|
|
786
|
-
bodyHeight: number,
|
|
787
|
-
): string[] {
|
|
859
|
+
#renderVisibleBodyLines(rows: Array<{ lines: string[]; rowIndex: number }>, bodyHeight: number): string[] {
|
|
860
|
+
this.#bodyLineToRowIndex = [];
|
|
788
861
|
const lines: string[] = [];
|
|
789
862
|
if (rows.length === 0) {
|
|
790
|
-
|
|
863
|
+
this.#bodyLineToRowIndex.push(undefined);
|
|
864
|
+
lines.push(row(theme.fg("muted", "no matches"), this.#lastRenderWidth));
|
|
791
865
|
}
|
|
792
866
|
for (let i = this.#scrollRowOffset; i < rows.length; i++) {
|
|
793
|
-
const
|
|
794
|
-
if (!
|
|
867
|
+
const renderedRow = rows[i];
|
|
868
|
+
if (!renderedRow) {
|
|
795
869
|
continue;
|
|
796
870
|
}
|
|
797
871
|
|
|
798
|
-
for (const line of
|
|
872
|
+
for (const line of renderedRow.lines) {
|
|
799
873
|
if (lines.length >= bodyHeight) {
|
|
800
874
|
break;
|
|
801
875
|
}
|
|
802
|
-
|
|
876
|
+
this.#bodyLineToRowIndex.push(renderedRow.rowIndex);
|
|
877
|
+
lines.push(row(line, this.#lastRenderWidth));
|
|
803
878
|
}
|
|
804
879
|
|
|
805
880
|
if (lines.length >= bodyHeight) {
|
|
@@ -808,7 +883,8 @@ export class DebugLogViewerComponent implements Component {
|
|
|
808
883
|
}
|
|
809
884
|
|
|
810
885
|
while (lines.length < bodyHeight) {
|
|
811
|
-
|
|
886
|
+
this.#bodyLineToRowIndex.push(undefined);
|
|
887
|
+
lines.push(row("", this.#lastRenderWidth));
|
|
812
888
|
}
|
|
813
889
|
|
|
814
890
|
return lines;
|
|
@@ -840,7 +916,7 @@ export class DebugLogViewerComponent implements Component {
|
|
|
840
916
|
return;
|
|
841
917
|
}
|
|
842
918
|
const bodyHeight = Math.max(1, this.#bodyHeight());
|
|
843
|
-
const innerWidth = Math.max(1, this.#lastRenderWidth -
|
|
919
|
+
const innerWidth = Math.max(1, this.#lastRenderWidth - 4);
|
|
844
920
|
|
|
845
921
|
// Scroll up: cursor is above viewport
|
|
846
922
|
if (cursorRowIndex < this.#scrollRowOffset) {
|
|
@@ -865,24 +941,6 @@ export class DebugLogViewerComponent implements Component {
|
|
|
865
941
|
}
|
|
866
942
|
}
|
|
867
943
|
|
|
868
|
-
#frameTop(innerWidth: number): string {
|
|
869
|
-
return `${theme.boxRound.topLeft}${theme.boxRound.horizontal.repeat(innerWidth)}${theme.boxRound.topRight}`;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
#frameSeparator(innerWidth: number): string {
|
|
873
|
-
return `${theme.boxRound.teeRight}${theme.boxRound.horizontal.repeat(innerWidth)}${theme.boxRound.teeLeft}`;
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
#frameBottom(innerWidth: number): string {
|
|
877
|
-
return `${theme.boxRound.bottomLeft}${theme.boxRound.horizontal.repeat(innerWidth)}${theme.boxRound.bottomRight}`;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
#frameLine(content: string, innerWidth: number): string {
|
|
881
|
-
const truncated = truncateToWidth(content, innerWidth);
|
|
882
|
-
const remaining = Math.max(0, innerWidth - visibleWidth(truncated));
|
|
883
|
-
return `${theme.boxRound.vertical}${truncated}${padding(remaining)}${theme.boxRound.vertical}`;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
944
|
#copySelected() {
|
|
887
945
|
const selectedPayload = buildLogCopyPayload(this.#model.getSelectedRawLines());
|
|
888
946
|
const selected = selectedPayload.length === 0 ? [] : selectedPayload.split("\n");
|