@sayknow-cli/coding-agent 0.3.12 → 0.3.15
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/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/log-experiment.d.ts +2 -2
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +5 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commands/web-search.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +4 -4
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +9 -9
- package/dist/types/commit/agentic/tools/schemas.d.ts +9 -9
- package/dist/types/commit/agentic/tools/split-commit.d.ts +9 -9
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +3 -3
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/commit/shared-llm.d.ts +9 -9
- package/dist/types/config/keybindings.d.ts +134 -129
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +35 -35
- package/dist/types/config/models-config-schema.d.ts +111 -111
- package/dist/types/config/settings-schema.d.ts +56 -16
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/policy.d.ts +3 -0
- package/dist/types/coordinator-mcp/server.d.ts +79 -6
- package/dist/types/coordinator-mcp/session-reaper.d.ts +53 -0
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/shared/errors.d.ts +12 -12
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/edit/modes/patch.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +3 -1
- package/dist/types/goals/tools/goal-tool.d.ts +6 -6
- package/dist/types/i18n/messages/en.d.ts +8 -8
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/lsp/index.d.ts +9 -9
- package/dist/types/lsp/types.d.ts +9 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/sayknow-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +0 -10
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/print-mode.d.ts +3 -4
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +2 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +17 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/context-usage.d.ts +4 -19
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +40 -3
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/context-estimation.d.ts +31 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/skc-runtime/state-schema.d.ts +19 -19
- package/dist/types/skc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/skc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/skc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/task/agents.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +35 -27
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/browser.d.ts +28 -28
- package/dist/types/tools/computer.d.ts +20 -20
- package/dist/types/tools/cron.d.ts +2 -2
- package/dist/types/tools/debug.d.ts +40 -40
- package/dist/types/tools/gh.d.ts +10 -10
- package/dist/types/tools/image-gen.d.ts +2 -2
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/monitor.d.ts +6 -6
- package/dist/types/tools/skill-discovery.d.ts +4 -4
- package/dist/types/tools/subagent.d.ts +12 -12
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/tools/todo-write.d.ts +8 -8
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/dist/types/web/insane/bridge.d.ts +9 -9
- package/dist/types/web/search/index.d.ts +4 -4
- package/package.json +8 -8
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +51 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +14 -14
- package/src/config/settings-schema.ts +41 -1
- package/src/config/settings.ts +96 -45
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/policy.ts +30 -0
- package/src/coordinator-mcp/server.ts +2958 -430
- package/src/coordinator-mcp/session-reaper.ts +129 -0
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/skc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/skc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +301 -80
- package/src/modes/acp/acp-agent.ts +730 -100
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/footer.ts +3 -3
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/sayknow-pet-widget.ts +360 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/status-line/segments.ts +14 -7
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +4 -171
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +37 -18
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +199 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/print-mode.ts +228 -92
- package/src/modes/rpc/rpc-client.ts +58 -1
- package/src/modes/rpc/rpc-mode.ts +286 -85
- package/src/modes/rpc/rpc-types.ts +15 -1
- package/src/modes/shared/agent-wire/command-dispatch.ts +33 -1
- package/src/modes/shared/agent-wire/command-validation.ts +10 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/context-usage.ts +56 -92
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +670 -217
- package/src/session/cache-economics.ts +275 -0
- package/src/session/context-estimation.ts +91 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/skc-runtime/session-state-sidecar.ts +756 -135
- package/src/skc-runtime/tmux-common.ts +26 -7
- package/src/skc-runtime/tmux-gc.ts +52 -9
- package/src/skc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/skc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/skc-runtime/tmux-sessions.ts +871 -68
- package/src/slash-commands/builtin-registry.ts +153 -9
- package/src/slash-commands/helpers/context-report.ts +26 -4
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
getRenderCacheRetainedBytes,
|
|
22
22
|
Loader,
|
|
23
23
|
Markdown,
|
|
24
|
+
onImageProtocolChanged,
|
|
24
25
|
ProcessTerminal,
|
|
25
26
|
Spacer,
|
|
26
27
|
Text,
|
|
@@ -85,6 +86,8 @@ import type { EvalExecutionComponent } from "./components/eval-execution";
|
|
|
85
86
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
86
87
|
import type { HookInputComponent } from "./components/hook-input";
|
|
87
88
|
import type { HookSelectorComponent } from "./components/hook-selector";
|
|
89
|
+
import { IrcSplitViewComponent } from "./components/irc-sidebar";
|
|
90
|
+
import { type PetMode, SayknowPetWidget } from "./components/sayknow-pet-widget";
|
|
88
91
|
import { StatusLineComponent } from "./components/status-line";
|
|
89
92
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
90
93
|
import {
|
|
@@ -100,6 +103,7 @@ import { InputController } from "./controllers/input-controller";
|
|
|
100
103
|
import { SelectorController } from "./controllers/selector-controller";
|
|
101
104
|
import { SSHCommandController } from "./controllers/ssh-command-controller";
|
|
102
105
|
import { TodoCommandController } from "./controllers/todo-command-controller";
|
|
106
|
+
import { IrcObservationLedger } from "./irc-observation-ledger";
|
|
103
107
|
import { JobsObserver } from "./jobs-observer";
|
|
104
108
|
import { OAuthManualInputManager } from "./oauth-manual-input";
|
|
105
109
|
import { SessionObserverRegistry } from "./session-observer-registry";
|
|
@@ -115,13 +119,42 @@ import {
|
|
|
115
119
|
onThemeChange,
|
|
116
120
|
theme,
|
|
117
121
|
} from "./theme/theme";
|
|
118
|
-
import type {
|
|
119
|
-
|
|
122
|
+
import type {
|
|
123
|
+
CompactionQueuedMessage,
|
|
124
|
+
InteractiveModeContext,
|
|
125
|
+
IrcArrivalSnapshot,
|
|
126
|
+
SubmittedUserInput,
|
|
127
|
+
TodoItem,
|
|
128
|
+
TodoPhase,
|
|
129
|
+
TranscriptRebuildPolicy,
|
|
130
|
+
} from "./types";
|
|
131
|
+
import type { ParsedIrcMessage } from "./utils/irc-message";
|
|
132
|
+
import { addChatChild, prepareTranscriptRebuild, UiHelpers } from "./utils/ui-helpers";
|
|
120
133
|
|
|
121
134
|
const INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS = 5_000;
|
|
122
135
|
const COMPOSER_NEWLINE_HINT = process.platform === "win32" ? "Alt+Enter/Ctrl+J" : "Shift+Enter/Ctrl+J";
|
|
123
136
|
export const DEFAULT_COMPOSER_PLACEHOLDER = `Type your message... ${COMPOSER_NEWLINE_HINT}: New line · Ctrl+C: Clear · Ctrl+R: Search history · Shift+Tab: Reasoning`;
|
|
124
137
|
const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;
|
|
138
|
+
|
|
139
|
+
const IRC_SIDEBAR_TOGGLE_SHADOWING_ACTIONS: readonly AppKeybinding[] = [
|
|
140
|
+
"app.plan.toggle",
|
|
141
|
+
"app.session.new",
|
|
142
|
+
"app.session.tree",
|
|
143
|
+
"app.session.fork",
|
|
144
|
+
"app.session.resume",
|
|
145
|
+
"app.message.followUp",
|
|
146
|
+
"app.stt.toggle",
|
|
147
|
+
"app.clipboard.copyLine",
|
|
148
|
+
"app.session.observe",
|
|
149
|
+
"app.jobs.open",
|
|
150
|
+
"app.tool.backgroundFold",
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
export function getWelcomeTranscriptReservedRows(chatContainer: Container, width: number): number {
|
|
154
|
+
return chatContainer.children.length === 0 || chatContainer.children.length > WELCOME_RESERVED_CONTAINER_CHILD_LIMIT
|
|
155
|
+
? 0
|
|
156
|
+
: chatContainer.render(width).length;
|
|
157
|
+
}
|
|
125
158
|
const FRIENDLY_KEY_PARTS: Record<string, string> = {
|
|
126
159
|
alt: "Alt",
|
|
127
160
|
cmd: "Cmd",
|
|
@@ -281,6 +314,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
281
314
|
keybindings: KeybindingsManager;
|
|
282
315
|
agent: Agent;
|
|
283
316
|
historyStorage?: HistoryStorage;
|
|
317
|
+
readonly ircLedger = new IrcObservationLedger();
|
|
284
318
|
|
|
285
319
|
ui: TUI;
|
|
286
320
|
chatContainer: Container;
|
|
@@ -292,6 +326,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
292
326
|
editorContainer: Container;
|
|
293
327
|
hookWidgetContainerAbove: Container;
|
|
294
328
|
hookWidgetContainerBelow: Container;
|
|
329
|
+
petFloorContainer: Container = new Container();
|
|
330
|
+
petWidget: SayknowPetWidget | undefined;
|
|
295
331
|
statusLine: StatusLineComponent;
|
|
296
332
|
|
|
297
333
|
isInitialized = false;
|
|
@@ -353,6 +389,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
353
389
|
#baseReservedSlashCommandNames: Set<string> = new Set();
|
|
354
390
|
#cleanupUnsubscribe?: () => void;
|
|
355
391
|
#subprocessTeardownUnsubscribe?: () => void;
|
|
392
|
+
#petProtocolUnsubscribe?: () => void;
|
|
356
393
|
readonly #version: string;
|
|
357
394
|
readonly #changelogMarkdown: string | undefined;
|
|
358
395
|
#planModePreviousTools: string[] | undefined;
|
|
@@ -388,6 +425,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
388
425
|
#eventBus?: EventBus;
|
|
389
426
|
#eventBusUnsubscribers: Array<() => void> = [];
|
|
390
427
|
#welcomeComponent?: WelcomeComponent;
|
|
428
|
+
#ircSplitView: IrcSplitViewComponent;
|
|
429
|
+
#ircSidebarAvailable = false;
|
|
430
|
+
#ircSidebarRequestedVisible = false;
|
|
391
431
|
|
|
392
432
|
constructor(
|
|
393
433
|
session: AgentSession,
|
|
@@ -424,6 +464,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
424
464
|
this.ui = new TUI(new ProcessTerminal(), settings.get("showHardwareCursor"));
|
|
425
465
|
this.ui.setClearOnShrink(settings.get("clearOnShrink"));
|
|
426
466
|
this.chatContainer = new Container();
|
|
467
|
+
this.#ircSplitView = new IrcSplitViewComponent(this.chatContainer, this.ircLedger, () => theme);
|
|
427
468
|
this.pendingMessagesContainer = new Container();
|
|
428
469
|
this.statusContainer = new Container();
|
|
429
470
|
this.todoContainer = new Container();
|
|
@@ -443,6 +484,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
443
484
|
this.#syncEditorMaxHeight();
|
|
444
485
|
this.updateEditorChrome();
|
|
445
486
|
this.editor.invalidate();
|
|
487
|
+
this.#invalidateIrcSidebarRender();
|
|
446
488
|
this.ui.requestResizeRender();
|
|
447
489
|
};
|
|
448
490
|
process.stdout.on("resize", this.#resizeHandler);
|
|
@@ -562,7 +604,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
562
604
|
this.#welcomeComponent.playIntro(() => this.ui.requestRender());
|
|
563
605
|
}
|
|
564
606
|
|
|
565
|
-
this.ui.addChild(this
|
|
607
|
+
this.ui.addChild(this.#ircSplitView);
|
|
608
|
+
this.ui.setViewportAnchorComponent(this.#ircSplitView);
|
|
609
|
+
|
|
566
610
|
this.ui.addChild(this.pendingMessagesContainer);
|
|
567
611
|
this.ui.addChild(this.statusContainer);
|
|
568
612
|
this.ui.addChild(this.todoContainer);
|
|
@@ -570,9 +614,24 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
570
614
|
this.ui.addChild(this.statusLine); // Main status rail + hook statuses; composer chrome is rendered by the editor.
|
|
571
615
|
this.ui.addChild(this.hookWidgetContainerAbove);
|
|
572
616
|
this.ui.addChild(this.editorContainer);
|
|
617
|
+
this.ui.addChild(this.petFloorContainer);
|
|
573
618
|
this.ui.addChild(this.hookWidgetContainerBelow);
|
|
574
619
|
this.ui.setBottomPinnedComponent(this.statusLine);
|
|
575
620
|
this.ui.setFocus(this.editor);
|
|
621
|
+
this.petWidget = this.#createPetWidget(this.editor);
|
|
622
|
+
this.petWidget.setMode(settings.get("pet.mode"));
|
|
623
|
+
// The async sixel capability probe can enable graphics after the saved
|
|
624
|
+
// pet mode was applied and dropped (no protocol yet at startup).
|
|
625
|
+
// Re-apply the configured mode when capability arrives so the pet
|
|
626
|
+
// appears without the user re-running /pet.
|
|
627
|
+
this.#petProtocolUnsubscribe?.();
|
|
628
|
+
this.#petProtocolUnsubscribe = onImageProtocolChanged(protocol => {
|
|
629
|
+
if (!protocol) return;
|
|
630
|
+
const saved = settings.get("pet.mode");
|
|
631
|
+
if (saved !== "off" && this.petWidget && this.petWidget.mode === "off") {
|
|
632
|
+
this.petWidget.setMode(saved);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
576
635
|
|
|
577
636
|
this.#inputController.setupKeyHandlers();
|
|
578
637
|
this.#inputController.setupEditorSubmitHandler();
|
|
@@ -610,6 +669,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
610
669
|
this.updateEditorChrome();
|
|
611
670
|
this.#syncEditorMaxHeight();
|
|
612
671
|
this.isInitialized = true;
|
|
672
|
+
this.#syncIrcSidebarAvailabilityFromSettings();
|
|
613
673
|
this.ui.requestRender(true);
|
|
614
674
|
|
|
615
675
|
// GitHub star reminder (interactive-only). Register the decline-driven
|
|
@@ -875,7 +935,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
875
935
|
}
|
|
876
936
|
if (!submission.customType) {
|
|
877
937
|
this.pendingImages = submission.images ? [...submission.images] : [];
|
|
878
|
-
this.rebuildChatFromMessages();
|
|
938
|
+
this.rebuildChatFromMessages("reconcile-same-transcript");
|
|
879
939
|
this.editor.setText(submission.text);
|
|
880
940
|
}
|
|
881
941
|
this.updateEditorChrome();
|
|
@@ -951,8 +1011,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
951
1011
|
}
|
|
952
1012
|
|
|
953
1013
|
#getWelcomeReservedRows(width: number): number {
|
|
1014
|
+
const transcriptRows = getWelcomeTranscriptReservedRows(this.chatContainer, width);
|
|
1015
|
+
|
|
954
1016
|
const transientRows = [
|
|
955
|
-
this.chatContainer,
|
|
956
1017
|
this.pendingMessagesContainer,
|
|
957
1018
|
this.statusContainer,
|
|
958
1019
|
this.todoContainer,
|
|
@@ -966,7 +1027,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
966
1027
|
this.hookWidgetContainerBelow,
|
|
967
1028
|
].reduce((rows, component) => rows + component.render(width).length, 0);
|
|
968
1029
|
|
|
969
|
-
return transientRows + pinnedRows;
|
|
1030
|
+
return transcriptRows + transientRows + pinnedRows;
|
|
970
1031
|
}
|
|
971
1032
|
|
|
972
1033
|
#renderShortContainerRowsForWelcomeReservation(width: number, container: Container): number {
|
|
@@ -1018,7 +1079,42 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1018
1079
|
this.editor.setTopBorder(undefined);
|
|
1019
1080
|
}
|
|
1020
1081
|
|
|
1021
|
-
|
|
1082
|
+
setPetMode(mode: PetMode): void {
|
|
1083
|
+
this.petWidget?.setMode(mode);
|
|
1084
|
+
settings.set("pet.mode", mode);
|
|
1085
|
+
this.ui.requestRender();
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
previewPetMode(mode: PetMode): void {
|
|
1089
|
+
this.petWidget?.previewMode(mode);
|
|
1090
|
+
this.ui.requestRender();
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
restoreComposer(): void {
|
|
1094
|
+
if (this.petWidget) {
|
|
1095
|
+
this.petWidget.remountComposer();
|
|
1096
|
+
} else {
|
|
1097
|
+
this.editorContainer.clear();
|
|
1098
|
+
this.editorContainer.addChild(this.editor);
|
|
1099
|
+
}
|
|
1100
|
+
this.ui.setFocus(this.editor);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
#createPetWidget(editor: CustomEditor): SayknowPetWidget {
|
|
1104
|
+
return new SayknowPetWidget({
|
|
1105
|
+
ui: this.ui,
|
|
1106
|
+
editor,
|
|
1107
|
+
editorContainer: this.editorContainer,
|
|
1108
|
+
floorContainer: this.petFloorContainer,
|
|
1109
|
+
isWorking: () => this.loadingAnimation !== undefined,
|
|
1110
|
+
getComposerBottomOffset: () =>
|
|
1111
|
+
this.petFloorContainer.render(this.ui.terminal.columns).length +
|
|
1112
|
+
this.hookWidgetContainerBelow.render(this.ui.terminal.columns).length,
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
rebuildChatFromMessages(policy: TranscriptRebuildPolicy): void {
|
|
1117
|
+
prepareTranscriptRebuild(this.ui, policy);
|
|
1022
1118
|
this.chatContainer.clear();
|
|
1023
1119
|
const context = this.session.buildDisplaySessionContext();
|
|
1024
1120
|
this.renderSessionContext(context);
|
|
@@ -1993,6 +2089,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1993
2089
|
}
|
|
1994
2090
|
|
|
1995
2091
|
stop(): void {
|
|
2092
|
+
this.#petProtocolUnsubscribe?.();
|
|
2093
|
+
this.#petProtocolUnsubscribe = undefined;
|
|
2094
|
+
this.petWidget?.dispose();
|
|
2095
|
+
this.petWidget = undefined;
|
|
1996
2096
|
if (this.loadingAnimation) {
|
|
1997
2097
|
this.loadingAnimation.stop();
|
|
1998
2098
|
this.loadingAnimation = undefined;
|
|
@@ -2124,11 +2224,17 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2124
2224
|
nextEditor.setText(previousText);
|
|
2125
2225
|
previousEditor.dispose();
|
|
2126
2226
|
|
|
2227
|
+
const petMode = this.petWidget?.mode ?? settings.get("pet.mode");
|
|
2228
|
+
this.petWidget?.dispose();
|
|
2229
|
+
|
|
2127
2230
|
this.editorContainer.clear();
|
|
2128
2231
|
this.editor = nextEditor;
|
|
2129
2232
|
this.editorContainer.addChild(nextEditor);
|
|
2130
2233
|
this.ui.setFocus(nextEditor);
|
|
2131
2234
|
|
|
2235
|
+
this.petWidget = this.#createPetWidget(nextEditor);
|
|
2236
|
+
this.petWidget.setMode(petMode);
|
|
2237
|
+
|
|
2132
2238
|
this.#inputController.setupKeyHandlers();
|
|
2133
2239
|
this.#inputController.setupEditorSubmitHandler();
|
|
2134
2240
|
|
|
@@ -2285,13 +2391,32 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2285
2391
|
return this.#uiHelpers.addMessageToChat(message, options);
|
|
2286
2392
|
}
|
|
2287
2393
|
|
|
2394
|
+
addLiveIrcObservationToChat(message: ParsedIrcMessage, arrival: IrcArrivalSnapshot): Component[] {
|
|
2395
|
+
return this.#uiHelpers.addLiveIrcObservationToChat(message, arrival);
|
|
2396
|
+
}
|
|
2397
|
+
removeRenderedIrcInlineComponents(observationId: string): readonly Component[] | undefined {
|
|
2398
|
+
return this.#uiHelpers.removeRenderedIrcInlineComponents(observationId);
|
|
2399
|
+
}
|
|
2400
|
+
resetRenderedIrcInlineComponents(): readonly (readonly Component[])[] {
|
|
2401
|
+
return this.#uiHelpers.resetRenderedIrcInlineComponents();
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2288
2404
|
renderSessionContext(
|
|
2289
2405
|
sessionContext: SessionContext,
|
|
2290
2406
|
options?: { updateFooter?: boolean; populateHistory?: boolean },
|
|
2291
2407
|
): void {
|
|
2292
2408
|
this.#uiHelpers.renderSessionContext(sessionContext, options);
|
|
2409
|
+
this.#eventController.reconcileIrcExpiryTimers(this.#uiHelpers.getRenderedIrcInlineComponents());
|
|
2293
2410
|
}
|
|
2294
2411
|
|
|
2412
|
+
rebuildInitialMessages(
|
|
2413
|
+
policy: TranscriptRebuildPolicy,
|
|
2414
|
+
prebuiltContext?: SessionContext,
|
|
2415
|
+
options?: { preserveExistingChat?: boolean },
|
|
2416
|
+
): void {
|
|
2417
|
+
prepareTranscriptRebuild(this.ui, policy);
|
|
2418
|
+
this.#uiHelpers.renderInitialMessages(prebuiltContext, options);
|
|
2419
|
+
}
|
|
2295
2420
|
renderInitialMessages(prebuiltContext?: SessionContext, options?: { preserveExistingChat?: boolean }): void {
|
|
2296
2421
|
this.#uiHelpers.renderInitialMessages(prebuiltContext, options);
|
|
2297
2422
|
}
|
|
@@ -2304,6 +2429,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2304
2429
|
return this.#uiHelpers.findLastAssistantMessage();
|
|
2305
2430
|
}
|
|
2306
2431
|
|
|
2432
|
+
getAssistantViewportAnchorId(message: AssistantMessage): string {
|
|
2433
|
+
return this.#uiHelpers.assistantViewportAnchorId(message);
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2307
2436
|
extractAssistantText(message: AssistantMessage): string {
|
|
2308
2437
|
return this.#uiHelpers.extractAssistantText(message);
|
|
2309
2438
|
}
|
|
@@ -2554,6 +2683,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2554
2683
|
this.#selectorController.showThemeSelector();
|
|
2555
2684
|
}
|
|
2556
2685
|
|
|
2686
|
+
showPetSelector(): void {
|
|
2687
|
+
this.#selectorController.showPetSelector();
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2557
2690
|
showHistorySearch(): void {
|
|
2558
2691
|
this.#selectorController.showHistorySearch();
|
|
2559
2692
|
}
|
|
@@ -2669,6 +2802,65 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2669
2802
|
this.#inputController.setToolsExpanded(expanded);
|
|
2670
2803
|
}
|
|
2671
2804
|
|
|
2805
|
+
#resolveEffectiveIrcSidebarToggleKey(): string | null {
|
|
2806
|
+
for (const key of this.keybindings.getKeys("app.irc.sidebar.toggle")) {
|
|
2807
|
+
if (this.editor.hasActionKey(key)) continue;
|
|
2808
|
+
const shadowed = IRC_SIDEBAR_TOGGLE_SHADOWING_ACTIONS.some(action =>
|
|
2809
|
+
this.keybindings.getKeys(action).includes(key),
|
|
2810
|
+
);
|
|
2811
|
+
if (!shadowed) return key;
|
|
2812
|
+
}
|
|
2813
|
+
return null;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
captureIrcArrivalSnapshot(): IrcArrivalSnapshot {
|
|
2817
|
+
return {
|
|
2818
|
+
panelVisible: this.#ircSplitView.effectiveSidebarVisible(this.ui.terminal.columns),
|
|
2819
|
+
panelRequestedVisible: this.#ircSidebarRequestedVisible,
|
|
2820
|
+
sidebarAvailable: this.#ircSidebarAvailable,
|
|
2821
|
+
resolvedToggleKey: this.#resolveEffectiveIrcSidebarToggleKey(),
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
toggleIrcSidebar(): void {
|
|
2825
|
+
if (
|
|
2826
|
+
!this.#ircSidebarAvailable ||
|
|
2827
|
+
this.settings.get("irc.enabled") !== true ||
|
|
2828
|
+
this.settings.get("irc.sidebar.enabled") !== true
|
|
2829
|
+
)
|
|
2830
|
+
return;
|
|
2831
|
+
this.#ircSidebarRequestedVisible = !this.#ircSidebarRequestedVisible;
|
|
2832
|
+
this.#ircSplitView.setVisible(this.#ircSidebarRequestedVisible);
|
|
2833
|
+
this.#invalidateIrcSidebarRender();
|
|
2834
|
+
this.ui.requestRender();
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
applyIrcSidebarAvailability(enabled: boolean): void {
|
|
2838
|
+
this.#ircSidebarAvailable = enabled;
|
|
2839
|
+
this.#ircSplitView.setVisible(enabled && this.#ircSidebarRequestedVisible);
|
|
2840
|
+
this.#invalidateIrcSidebarRender();
|
|
2841
|
+
this.ui.requestRender();
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
#syncIrcSidebarAvailabilityFromSettings(): void {
|
|
2845
|
+
this.applyIrcSidebarAvailability(
|
|
2846
|
+
this.settings.get("irc.enabled") === true && this.settings.get("irc.sidebar.enabled") === true,
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
resetIrcSidebarSession(): void {
|
|
2851
|
+
this.ircLedger.reset();
|
|
2852
|
+
this.#eventController.resetIrcObservations();
|
|
2853
|
+
this.#ircSidebarRequestedVisible = false;
|
|
2854
|
+
this.#ircSplitView.setVisible(false);
|
|
2855
|
+
this.#uiHelpers.resetIrcSidebarHint();
|
|
2856
|
+
this.#syncIrcSidebarAvailabilityFromSettings();
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
#invalidateIrcSidebarRender(): void {
|
|
2860
|
+
clearRenderCache();
|
|
2861
|
+
this.#ircSplitView.invalidate();
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2672
2864
|
toggleThinkingBlockVisibility(): void {
|
|
2673
2865
|
this.#inputController.toggleThinkingBlockVisibility();
|
|
2674
2866
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type { ParsedIrcMessage } from "./utils/irc-message";
|
|
4
|
+
|
|
5
|
+
type InlineMode = "persistent" | "ephemeral";
|
|
6
|
+
|
|
7
|
+
const MAX_RECORDS = 10_000;
|
|
8
|
+
const MAX_RETAINED_UTF8_BYTES = 16 * 1024 * 1024;
|
|
9
|
+
const MAX_SEEN_IDENTITIES = 100_000;
|
|
10
|
+
|
|
11
|
+
export type IrcObservationRecord = Readonly<
|
|
12
|
+
ParsedIrcMessage & {
|
|
13
|
+
mode: InlineMode;
|
|
14
|
+
observedAt: number;
|
|
15
|
+
sequence: number;
|
|
16
|
+
expiresAt?: number;
|
|
17
|
+
retainedUtf8Bytes: number;
|
|
18
|
+
}
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
function measureRetainedUtf8Bytes(message: ParsedIrcMessage): number {
|
|
22
|
+
let bytes = 4;
|
|
23
|
+
for (const value of [message.observationId, message.from, message.to, message.text, message.kind]) {
|
|
24
|
+
bytes += Buffer.byteLength(value, "utf8");
|
|
25
|
+
if (bytes > MAX_RETAINED_UTF8_BYTES) return bytes;
|
|
26
|
+
}
|
|
27
|
+
return bytes;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function tombstoneIdentity(observationId: string): string {
|
|
31
|
+
const hash = crypto.createHash("sha256");
|
|
32
|
+
hash.update("skc:irc:observation-id:utf16le:v1\0");
|
|
33
|
+
for (let offset = 0; offset < observationId.length; offset += 4_096) {
|
|
34
|
+
hash.update(Buffer.from(observationId.slice(offset, offset + 4_096), "utf16le"));
|
|
35
|
+
}
|
|
36
|
+
return hash.digest("hex");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Runtime-only IRC observations. This intentionally has no persistence layer. */
|
|
40
|
+
export class IrcObservationLedger {
|
|
41
|
+
#records = new Map<string, IrcObservationRecord>();
|
|
42
|
+
#retainedUtf8Bytes = 0;
|
|
43
|
+
#nextSequence = 0;
|
|
44
|
+
#seenObservationIdentities = new Set<string>();
|
|
45
|
+
#identityCapacityExhausted = false;
|
|
46
|
+
#evictedObservationIds = new Set<string>();
|
|
47
|
+
|
|
48
|
+
#rememberObservationIdentity(observationId: string): boolean {
|
|
49
|
+
const identity = tombstoneIdentity(observationId);
|
|
50
|
+
if (this.#seenObservationIdentities.has(identity) || this.#identityCapacityExhausted) return false;
|
|
51
|
+
if (this.#seenObservationIdentities.size >= MAX_SEEN_IDENTITIES) {
|
|
52
|
+
this.#identityCapacityExhausted = true;
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
this.#seenObservationIdentities.add(identity);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#evict(observationId: string): void {
|
|
60
|
+
const record = this.#records.get(observationId);
|
|
61
|
+
if (!record) return;
|
|
62
|
+
this.#records.delete(observationId);
|
|
63
|
+
this.#retainedUtf8Bytes -= record.retainedUtf8Bytes;
|
|
64
|
+
this.#evictedObservationIds.add(observationId);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#enforceBounds(): void {
|
|
68
|
+
while (this.#records.size > MAX_RECORDS || this.#retainedUtf8Bytes > MAX_RETAINED_UTF8_BYTES) {
|
|
69
|
+
const oldestObservationId = this.#records.keys().next().value;
|
|
70
|
+
if (oldestObservationId === undefined) return;
|
|
71
|
+
this.#evict(oldestObservationId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Inline observations expire after 10 seconds; closed-panel observations persist inline. */
|
|
76
|
+
observe(message: ParsedIrcMessage, panelVisibleAtObservation: boolean): IrcObservationRecord | undefined {
|
|
77
|
+
const existing = this.#records.get(message.observationId);
|
|
78
|
+
if (existing) return existing;
|
|
79
|
+
if (!this.#rememberObservationIdentity(message.observationId)) return undefined;
|
|
80
|
+
|
|
81
|
+
const estimatedBytes = measureRetainedUtf8Bytes(message);
|
|
82
|
+
if (estimatedBytes > MAX_RETAINED_UTF8_BYTES) return undefined;
|
|
83
|
+
|
|
84
|
+
const observedAt = Date.now();
|
|
85
|
+
const mode: InlineMode = panelVisibleAtObservation ? "ephemeral" : "persistent";
|
|
86
|
+
const record: IrcObservationRecord = Object.freeze({
|
|
87
|
+
...message,
|
|
88
|
+
mode,
|
|
89
|
+
observedAt,
|
|
90
|
+
sequence: this.#nextSequence++,
|
|
91
|
+
...(mode === "ephemeral" ? { expiresAt: observedAt + 10_000 } : {}),
|
|
92
|
+
retainedUtf8Bytes: estimatedBytes,
|
|
93
|
+
});
|
|
94
|
+
this.#records.set(record.observationId, record);
|
|
95
|
+
this.#retainedUtf8Bytes += estimatedBytes;
|
|
96
|
+
this.#enforceBounds();
|
|
97
|
+
return this.#records.get(record.observationId);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getRecord(observationId: string): IrcObservationRecord | undefined {
|
|
101
|
+
return this.#records.get(observationId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getSidebarRecords(): readonly IrcObservationRecord[] {
|
|
105
|
+
return [...this.#records.values()];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
getInlineProjection(now: number): readonly IrcObservationRecord[] {
|
|
109
|
+
return [...this.#records.values()].filter(record => record.mode === "persistent" || now < record.expiresAt!);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Returns and clears IDs whose retained payload was released. */
|
|
113
|
+
drainEvictedObservationIds(): readonly string[] {
|
|
114
|
+
const observationIds = [...this.#evictedObservationIds];
|
|
115
|
+
this.#evictedObservationIds.clear();
|
|
116
|
+
return observationIds;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
reset(): void {
|
|
120
|
+
for (const observationId of this.#records.keys()) {
|
|
121
|
+
this.#evictedObservationIds.add(observationId);
|
|
122
|
+
}
|
|
123
|
+
this.#records.clear();
|
|
124
|
+
this.#retainedUtf8Bytes = 0;
|
|
125
|
+
this.#nextSequence = 0;
|
|
126
|
+
}
|
|
127
|
+
}
|