@sayknow-cli/coding-agent 0.3.7 → 0.3.8
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/append-only-mode.d.ts +31 -0
- package/dist/types/capability/rule.d.ts +8 -0
- package/dist/types/cli/completion-cli.d.ts +41 -0
- package/dist/types/cli/config-cli.d.ts +1 -0
- package/dist/types/cli/fixture-report.d.ts +20 -0
- package/dist/types/cli/fixture-report.test.d.ts +1 -0
- package/dist/types/cli/nofile-limit.d.ts +11 -0
- package/dist/types/cli/notify-cli.d.ts +7 -0
- package/dist/types/cli/update-cli.d.ts +22 -0
- package/dist/types/cli.d.ts +124 -1
- package/dist/types/commands/completion.d.ts +29 -0
- package/dist/types/commands/config.d.ts +3 -0
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/file-lock.d.ts +7 -6
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-profile-activation.d.ts +6 -0
- package/dist/types/config/model-profiles.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +105 -1
- package/dist/types/config/settings.d.ts +8 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/export/ttsr.d.ts +14 -3
- package/dist/types/extensibility/extensions/runner.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/goals/runtime.d.ts +1 -0
- package/dist/types/hooks/native-skill-hook.d.ts +12 -0
- package/dist/types/main.d.ts +4 -1
- package/dist/types/modes/components/assistant-message.d.ts +5 -1
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +9 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +5 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/print-mode.d.ts +13 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/notifications/config.d.ts +14 -1
- package/dist/types/notifications/daemon-paths.d.ts +1 -0
- package/dist/types/notifications/index.d.ts +6 -3
- package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
- package/dist/types/notifications/telegram-daemon.d.ts +23 -3
- package/dist/types/notifications/telegram-reference.d.ts +0 -5
- package/dist/types/notifications/threaded-render.d.ts +6 -0
- package/dist/types/runtime-api-key.d.ts +7 -0
- package/dist/types/sdk.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +61 -6
- package/dist/types/session/agent-storage.d.ts +1 -0
- package/dist/types/session/file-mention-pruning.d.ts +8 -0
- package/dist/types/session/messages.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +17 -1
- package/dist/types/skc-runtime/deep-interview-ambiguity.d.ts +39 -0
- package/dist/types/skc-runtime/deep-interview-recorder.d.ts +10 -1
- package/dist/types/skc-runtime/deep-interview-state.d.ts +17 -2
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +7 -0
- package/dist/types/skc-runtime/state-writer.d.ts +3 -0
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +120 -13
- package/dist/types/skill-state/active-state.d.ts +18 -1
- package/dist/types/system-prompt.d.ts +18 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/fork-context-budget.d.ts +7 -0
- package/dist/types/task/token-log.d.ts +30 -0
- package/dist/types/task/token-log.test.d.ts +1 -0
- package/dist/types/tools/bisect.d.ts +81 -0
- package/dist/types/tools/computer.d.ts +2 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/search.d.ts +2 -2
- package/dist/types/tools/ultragoal-ask-guard.d.ts +9 -2
- package/dist/types/utils/file-mentions.d.ts +14 -0
- package/dist/types/utils/git.d.ts +32 -0
- package/dist/types/web/scrapers/youtube.d.ts +8 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
- package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
- package/package.json +8 -8
- package/src/append-only-mode.ts +58 -0
- package/src/autoresearch/prompt.md +1 -1
- package/src/autoresearch/tools/log-experiment.ts +1 -1
- package/src/capability/rule.ts +9 -0
- package/src/cli/completion-cli.ts +234 -0
- package/src/cli/config-cli.ts +61 -9
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/fixture-report.test.ts +230 -0
- package/src/cli/fixture-report.ts +171 -0
- package/src/cli/nofile-limit.ts +48 -0
- package/src/cli/notify-cli.ts +92 -8
- package/src/cli/telegram-cli.ts +1 -1
- package/src/cli/update-cli.ts +85 -9
- package/src/cli.ts +29 -1
- package/src/commands/completion.ts +84 -0
- package/src/commands/config.ts +2 -0
- package/src/commands/contribution-prep.ts +1 -1
- package/src/commands/deep-interview.ts +2 -1
- package/src/config/file-lock.ts +73 -34
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-profile-activation.ts +65 -0
- package/src/config/model-profiles.ts +31 -2
- package/src/config/settings-schema.ts +68 -1
- package/src/config/settings.ts +22 -0
- package/src/defaults/skc/skills/team/SKILL.md +17 -14
- package/src/defaults/skc/skills/ultragoal/SKILL.md +59 -4
- package/src/defaults/skc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
- package/src/defaults/skc-defaults.ts +9 -0
- package/src/discovery/helpers.ts +10 -0
- package/src/discovery/index.ts +1 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +60 -2
- package/src/export/ttsr.ts +96 -18
- package/src/extensibility/extensions/runner.ts +183 -219
- package/src/extensibility/extensions/types.ts +9 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +156 -44
- package/src/goals/runtime.ts +10 -2
- package/src/hindsight/backend.ts +1 -3
- package/src/hooks/native-skill-hook.ts +124 -4
- package/src/internal-urls/docs-index.generated.ts +14 -5
- package/src/main.ts +95 -43
- package/src/memories/index.ts +12 -1
- package/src/modes/components/assistant-message.ts +241 -82
- package/src/modes/components/custom-editor.ts +1 -0
- package/src/modes/components/model-selector.ts +54 -11
- package/src/modes/components/queued-message-selector.ts +92 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/components/status-line.ts +167 -54
- package/src/modes/components/tool-execution.ts +44 -19
- package/src/modes/components/welcome.ts +181 -44
- package/src/modes/controllers/command-controller.ts +40 -4
- package/src/modes/controllers/event-controller.ts +66 -47
- package/src/modes/controllers/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +180 -18
- package/src/modes/controllers/selector-controller.ts +127 -24
- package/src/modes/interactive-mode.ts +51 -16
- package/src/modes/print-mode.ts +43 -4
- package/src/modes/prompt-action-autocomplete.ts +21 -20
- package/src/modes/rpc/rpc-mode.ts +29 -3
- package/src/modes/rpc/rpc-types.ts +9 -0
- package/src/modes/shared/agent-wire/command-validation.ts +4 -0
- package/src/modes/shared/agent-wire/event-contract.ts +22 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +20 -7
- package/src/notifications/config.ts +25 -5
- package/src/notifications/daemon-paths.ts +2 -0
- package/src/notifications/index.ts +117 -14
- package/src/notifications/lifecycle-commands.ts +37 -7
- package/src/notifications/lifecycle-orchestrator.ts +19 -6
- package/src/notifications/telegram-daemon-control.ts +5 -3
- package/src/notifications/telegram-daemon.ts +302 -51
- package/src/notifications/telegram-reference.ts +0 -16
- package/src/notifications/threaded-render.ts +17 -1
- package/src/prompts/agents/architect.md +19 -10
- package/src/prompts/agents/critic.md +16 -18
- package/src/prompts/agents/executor.md +14 -11
- package/src/prompts/agents/planner.md +18 -13
- package/src/prompts/ci-green-request.md +3 -8
- package/src/prompts/goals/goal-continuation.md +0 -5
- package/src/prompts/goals/goal-mode-active.md +0 -5
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/system/plan-mode-subagent.md +3 -3
- package/src/prompts/system/project-prompt.md +0 -13
- package/src/prompts/system/subagent-system-prompt.md +3 -2
- package/src/prompts/system/system-prompt.md +29 -17
- package/src/prompts/system/volatile-project-context.md +10 -0
- package/src/prompts/system/web-search.md +4 -7
- package/src/prompts/tools/apply-patch.md +6 -0
- package/src/prompts/tools/ast-edit.md +1 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/bisect.md +22 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/computer.md +2 -2
- package/src/prompts/tools/cron.md +0 -2
- package/src/prompts/tools/eval.md +2 -2
- package/src/prompts/tools/find.md +1 -1
- package/src/prompts/tools/github.md +6 -6
- package/src/prompts/tools/goal.md +1 -3
- package/src/prompts/tools/image-gen.md +12 -5
- package/src/prompts/tools/irc.md +5 -4
- package/src/prompts/tools/job.md +2 -2
- package/src/prompts/tools/lsp.md +1 -1
- package/src/prompts/tools/monitor.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/replace.md +2 -21
- package/src/prompts/tools/rewind.md +1 -1
- package/src/prompts/tools/search-tool-bm25.md +1 -11
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/skill.md +3 -2
- package/src/prompts/tools/ssh.md +2 -1
- package/src/prompts/tools/subagent.md +2 -2
- package/src/prompts/tools/task.md +4 -5
- package/src/prompts/tools/todo-write.md +1 -1
- package/src/prompts/tools/vim.md +1 -0
- package/src/prompts/tools/web-search.md +2 -0
- package/src/prompts/tools/write.md +8 -2
- package/src/runtime-api-key.ts +16 -0
- package/src/sdk.ts +52 -28
- package/src/session/agent-session.ts +1011 -141
- package/src/session/agent-storage.ts +4 -0
- package/src/session/file-mention-pruning.ts +64 -0
- package/src/session/messages.ts +4 -0
- package/src/session/session-manager.ts +103 -4
- package/src/skc-runtime/deep-interview-ambiguity.ts +206 -0
- package/src/skc-runtime/deep-interview-recorder.ts +62 -8
- package/src/skc-runtime/deep-interview-state.ts +44 -2
- package/src/skc-runtime/session-state-sidecar.ts +263 -20
- package/src/skc-runtime/state-runtime.ts +12 -2
- package/src/skc-runtime/state-writer.ts +15 -0
- package/src/skc-runtime/ultragoal-guard.ts +55 -66
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +315 -0
- package/src/skc-runtime/ultragoal-runtime.ts +1663 -159
- package/src/skc-runtime/workflow-manifest.generated.json +113 -2
- package/src/skc-runtime/workflow-manifest.ts +74 -1
- package/src/skill-state/active-state.ts +141 -5
- package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
- package/src/slash-commands/builtin-registry.ts +218 -71
- package/src/system-prompt.ts +57 -16
- package/src/task/executor.ts +132 -24
- package/src/task/fork-context-advisory.ts +2 -7
- package/src/task/fork-context-budget.ts +9 -0
- package/src/task/index.ts +13 -6
- package/src/task/output-manager.ts +20 -8
- package/src/task/receipt.ts +26 -7
- package/src/task/token-log.test.ts +188 -0
- package/src/task/token-log.ts +179 -0
- package/src/tools/bisect.ts +367 -0
- package/src/tools/computer.ts +246 -43
- package/src/tools/index.ts +3 -0
- package/src/tools/output-meta.ts +116 -4
- package/src/tools/search.ts +8 -2
- package/src/tools/skill.ts +15 -0
- package/src/tools/telegram-send.ts +6 -0
- package/src/tools/ultragoal-ask-guard.ts +39 -5
- package/src/utils/file-mentions.ts +39 -7
- package/src/utils/git.ts +73 -0
- package/src/web/scrapers/youtube.ts +24 -12
- package/src/workflow/workflow-intent-diff.ts +29 -6
- package/src/workflow/workflow-intent-report-consumer.ts +221 -0
- package/src/workflow/workflow-intent-report-schema.ts +129 -0
- package/src/workflow/workflow-intent-report.ts +135 -0
|
@@ -22,6 +22,25 @@ export interface WelcomeComponentOptions {
|
|
|
22
22
|
collapseChangelog?: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const WELCOME_STATIC_RIGHT_ROWS_EXCLUDING_DYNAMIC_SECTIONS = 15;
|
|
26
|
+
const DEFAULT_WHATS_NEW_ROWS = 3;
|
|
27
|
+
const MAX_WHATS_NEW_ROWS = 12;
|
|
28
|
+
const NEWLINE_FLOW_KEY =
|
|
29
|
+
process.platform === "win32" ? { key: "alt+enter", label: "newline" } : { key: "ctrl+j", label: "newline" };
|
|
30
|
+
|
|
31
|
+
const FLOW_KEY_ITEMS: ReadonlyArray<{ key: string; label: string }> = [
|
|
32
|
+
{ key: "/", label: "commands" },
|
|
33
|
+
{ key: "#", label: "actions" },
|
|
34
|
+
{ key: "!", label: "shell" },
|
|
35
|
+
{ key: "$", label: "python" },
|
|
36
|
+
{ key: "?", label: "keymap" },
|
|
37
|
+
{ key: "ctrl+l", label: "model" },
|
|
38
|
+
{ key: "shift+tab", label: "reasoning" },
|
|
39
|
+
{ key: "tab", label: "complete" },
|
|
40
|
+
NEWLINE_FLOW_KEY,
|
|
41
|
+
{ key: "ctrl+c", label: "clear" },
|
|
42
|
+
];
|
|
43
|
+
|
|
25
44
|
/**
|
|
26
45
|
* Sayknow-CLI launch surface: a blue-gradient SAYKNOW wordmark, compact
|
|
27
46
|
* command affordances, and project signals — a distinct identity, not a
|
|
@@ -99,7 +118,6 @@ export class WelcomeComponent implements Component {
|
|
|
99
118
|
}
|
|
100
119
|
const targetContentRows = targetRows === undefined ? undefined : Math.max(0, targetRows - 2);
|
|
101
120
|
const dualContentWidth = boxWidth - 3; // 3 = │ + │ + │
|
|
102
|
-
const preferredLeftCol = 40;
|
|
103
121
|
const minLeftCol = 20; // wordmark plus Sayknow identity labels
|
|
104
122
|
const minRightCol = 24;
|
|
105
123
|
const modelPill = this.#pill(theme.icon.model || "model", this.modelName, "statusLineModel");
|
|
@@ -113,10 +131,12 @@ export class WelcomeComponent implements Component {
|
|
|
113
131
|
visibleWidth(modelPill),
|
|
114
132
|
visibleWidth(providerPill),
|
|
115
133
|
);
|
|
116
|
-
const
|
|
134
|
+
const evenLeftCol = Math.floor(dualContentWidth / 2);
|
|
135
|
+
const maxLeftColWithRightMinimum = Math.max(1, dualContentWidth - minRightCol);
|
|
136
|
+
const desiredLeftCol = Math.max(leftMinContentWidth, evenLeftCol);
|
|
117
137
|
const dualLeftCol =
|
|
118
138
|
dualContentWidth >= minRightCol + 1
|
|
119
|
-
? Math.min(desiredLeftCol,
|
|
139
|
+
? Math.min(desiredLeftCol, maxLeftColWithRightMinimum)
|
|
120
140
|
: Math.max(1, dualContentWidth - 1);
|
|
121
141
|
const dualRightCol = Math.max(1, dualContentWidth - dualLeftCol);
|
|
122
142
|
const showRightColumn = dualLeftCol >= leftMinContentWidth && dualRightCol >= minRightCol;
|
|
@@ -152,26 +172,6 @@ export class WelcomeComponent implements Component {
|
|
|
152
172
|
|
|
153
173
|
const rightColumnWidth = showRightColumn ? rightCol : leftCol;
|
|
154
174
|
const separator = buildSeparator(rightColumnWidth);
|
|
155
|
-
const changelogBudget = Math.max(1, Math.min(6, Math.floor((targetContentRows ?? 18) * 0.3)));
|
|
156
|
-
|
|
157
|
-
const sessionLines: string[] = [];
|
|
158
|
-
if (this.recentSessions.length === 0) {
|
|
159
|
-
sessionLines.push(` ${theme.fg("dim", t("welcome.noSessions"))}`);
|
|
160
|
-
} else {
|
|
161
|
-
const bulletPrefix = ` ${theme.md.bullet} `;
|
|
162
|
-
const prefixWidth = visibleWidth(bulletPrefix);
|
|
163
|
-
for (const session of this.recentSessions.slice(0, 3)) {
|
|
164
|
-
const timeSuffixRaw = ` (${session.timeAgo})`;
|
|
165
|
-
const timeWidth = visibleWidth(timeSuffixRaw);
|
|
166
|
-
const nameBudget = Math.max(1, rightColumnWidth - prefixWidth - timeWidth);
|
|
167
|
-
const nameVis = visibleWidth(session.name);
|
|
168
|
-
const name = nameVis > nameBudget ? truncateToWidth(session.name, nameBudget) : session.name;
|
|
169
|
-
sessionLines.push(
|
|
170
|
-
`${theme.fg("dim", bulletPrefix)}${theme.fg("muted", name)}${theme.fg("dim", timeSuffixRaw)}`,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
175
|
const lspLines: string[] = [];
|
|
176
176
|
if (this.lspServers.length === 0) {
|
|
177
177
|
lspLines.push(` ${theme.fg("dim", t("welcome.noLsp"))}`);
|
|
@@ -188,6 +188,24 @@ export class WelcomeComponent implements Component {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
const flowPreferredRows = this.#flowKeyRows(rightColumnWidth).length;
|
|
192
|
+
const changelogRowLimit = this.#whatsNewRowLimit(targetContentRows, lspLines.length, flowPreferredRows);
|
|
193
|
+
const changelogLines = this.#whatsNewLines(rightColumnWidth, changelogRowLimit);
|
|
194
|
+
const flowRowLimit = this.#flowKeyRowLimit(
|
|
195
|
+
targetContentRows,
|
|
196
|
+
changelogLines.length,
|
|
197
|
+
lspLines.length,
|
|
198
|
+
rightColumnWidth,
|
|
199
|
+
);
|
|
200
|
+
const flowLines = this.#flowKeyLines(rightColumnWidth, flowRowLimit);
|
|
201
|
+
const sessionLimit = this.#sessionTrailLimit(
|
|
202
|
+
targetContentRows,
|
|
203
|
+
changelogLines.length,
|
|
204
|
+
lspLines.length,
|
|
205
|
+
flowLines.length,
|
|
206
|
+
);
|
|
207
|
+
const sessionLines = this.#sessionTrailLines(rightColumnWidth, sessionLimit);
|
|
208
|
+
|
|
191
209
|
// Workflow affordances: pad the command so descriptions align.
|
|
192
210
|
const wf = (cmd: string, desc: string): string => {
|
|
193
211
|
const pad = " ".repeat(Math.max(1, 17 - visibleWidth(cmd)));
|
|
@@ -197,7 +215,7 @@ export class WelcomeComponent implements Component {
|
|
|
197
215
|
const rightLines = [
|
|
198
216
|
"",
|
|
199
217
|
` ${theme.bold(theme.fg("accent", "What's New"))}`,
|
|
200
|
-
...
|
|
218
|
+
...changelogLines,
|
|
201
219
|
separator,
|
|
202
220
|
` ${theme.bold(theme.fg("accent", t("welcome.workflows")))}`,
|
|
203
221
|
wf("/deep-interview", t("welcome.wf.deepInterview")),
|
|
@@ -206,19 +224,7 @@ export class WelcomeComponent implements Component {
|
|
|
206
224
|
wf("/team", t("welcome.wf.team")),
|
|
207
225
|
separator,
|
|
208
226
|
` ${theme.bold(theme.fg("accent", t("welcome.flowKeys")))}`,
|
|
209
|
-
|
|
210
|
-
"dim",
|
|
211
|
-
"#",
|
|
212
|
-
)}${theme.fg("muted", ` ${t("welcome.actions")}`)}`,
|
|
213
|
-
` ${theme.fg("dim", "!")}${theme.fg("muted", ` ${t("welcome.shell")}`)} ${theme.fg("dim", "·")} ${theme.fg("dim", "$")}${theme.fg(
|
|
214
|
-
"muted",
|
|
215
|
-
` ${t("welcome.python")}`,
|
|
216
|
-
)}`,
|
|
217
|
-
` ${theme.fg("dim", "?")}${theme.fg("muted", ` ${t("welcome.keymap")}`)} ${theme.fg("dim", "·")} ${theme.fg(
|
|
218
|
-
"dim",
|
|
219
|
-
"ctrl+l",
|
|
220
|
-
)}${theme.fg("muted", ` ${t("welcome.model")}`)}`,
|
|
221
|
-
` ${theme.fg("dim", "shift+tab")}${theme.fg("muted", ` ${t("welcome.reasoning")}`)}`,
|
|
227
|
+
...flowLines,
|
|
222
228
|
separator,
|
|
223
229
|
` ${theme.bold(theme.fg("accent", t("welcome.projectPulse")))}`,
|
|
224
230
|
...lspLines,
|
|
@@ -337,7 +343,139 @@ export class WelcomeComponent implements Component {
|
|
|
337
343
|
return [...Array.from({ length: topPad }, () => ""), ...clipped, ...Array.from({ length: bottomPad }, () => "")];
|
|
338
344
|
}
|
|
339
345
|
|
|
340
|
-
#
|
|
346
|
+
#flowKeyItemText(item: { key: string; label: string }): string {
|
|
347
|
+
return `${theme.fg("dim", item.key)}${theme.fg("muted", ` ${this.#flowKeyLabel(item.label)}`)}`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
#flowKeyLabel(label: string): string {
|
|
351
|
+
switch (label) {
|
|
352
|
+
case "commands":
|
|
353
|
+
return t("welcome.commands");
|
|
354
|
+
case "actions":
|
|
355
|
+
return t("welcome.actions");
|
|
356
|
+
case "shell":
|
|
357
|
+
return t("welcome.shell");
|
|
358
|
+
case "python":
|
|
359
|
+
return t("welcome.python");
|
|
360
|
+
case "keymap":
|
|
361
|
+
return t("welcome.keymap");
|
|
362
|
+
case "model":
|
|
363
|
+
return t("welcome.model");
|
|
364
|
+
case "reasoning":
|
|
365
|
+
return t("welcome.reasoning");
|
|
366
|
+
default:
|
|
367
|
+
return label;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
#flowKeyRows(width: number): string[] {
|
|
372
|
+
const contentWidth = Math.max(1, width - 1);
|
|
373
|
+
const separator = ` ${theme.fg("dim", "·")} `;
|
|
374
|
+
const rows: string[] = [];
|
|
375
|
+
let current = "";
|
|
376
|
+
for (const item of FLOW_KEY_ITEMS) {
|
|
377
|
+
const segment = this.#flowKeyItemText(item);
|
|
378
|
+
const next = current ? `${current}${separator}${segment}` : segment;
|
|
379
|
+
if (current && visibleWidth(next) > contentWidth) {
|
|
380
|
+
rows.push(` ${current}`);
|
|
381
|
+
current = segment;
|
|
382
|
+
} else {
|
|
383
|
+
current = next;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (current) rows.push(` ${current}`);
|
|
387
|
+
return rows.length > 0 ? rows : [` ${theme.fg("dim", "No flow keys")}`];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
#flowKeyLines(width: number, maxRows: number): string[] {
|
|
391
|
+
const rows = this.#flowKeyRows(width);
|
|
392
|
+
const rowLimit = Math.max(1, Math.floor(maxRows));
|
|
393
|
+
if (rows.length <= rowLimit) return rows;
|
|
394
|
+
if (rowLimit === 1) {
|
|
395
|
+
const firstItem = FLOW_KEY_ITEMS[0];
|
|
396
|
+
const firstSegment = firstItem ? this.#flowKeyItemText(firstItem) : theme.fg("dim", "keys");
|
|
397
|
+
return [this.#fitToWidth(` ${firstSegment} ${theme.fg("dim", "· … ")}${theme.bold("/help")}`, width)];
|
|
398
|
+
}
|
|
399
|
+
return [...rows.slice(0, rowLimit - 1), ` ${theme.fg("dim", `… ${theme.bold("/help")} for more`)}`];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#flowKeyRowLimit(
|
|
403
|
+
targetContentRows: number | undefined,
|
|
404
|
+
changelogLineCount: number,
|
|
405
|
+
lspLineCount: number,
|
|
406
|
+
rightColumnWidth: number,
|
|
407
|
+
): number {
|
|
408
|
+
const preferredRows = this.#flowKeyRows(rightColumnWidth).length;
|
|
409
|
+
if (targetContentRows === undefined) return preferredRows;
|
|
410
|
+
|
|
411
|
+
const sessionBaselineRows = this.recentSessions.length === 0 ? 1 : Math.min(3, this.recentSessions.length);
|
|
412
|
+
const availableRows =
|
|
413
|
+
targetContentRows -
|
|
414
|
+
WELCOME_STATIC_RIGHT_ROWS_EXCLUDING_DYNAMIC_SECTIONS -
|
|
415
|
+
changelogLineCount -
|
|
416
|
+
lspLineCount -
|
|
417
|
+
sessionBaselineRows;
|
|
418
|
+
return Math.max(1, Math.min(preferredRows, availableRows));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#whatsNewRowLimit(targetContentRows: number | undefined, lspLineCount: number, flowLineCount: number): number {
|
|
422
|
+
if (targetContentRows === undefined) return 5;
|
|
423
|
+
|
|
424
|
+
const sessionBaselineRows = this.recentSessions.length === 0 ? 1 : Math.min(3, this.recentSessions.length);
|
|
425
|
+
const dynamicRows = Math.max(
|
|
426
|
+
1,
|
|
427
|
+
targetContentRows - WELCOME_STATIC_RIGHT_ROWS_EXCLUDING_DYNAMIC_SECTIONS - flowLineCount - lspLineCount,
|
|
428
|
+
);
|
|
429
|
+
const rowsAfterBaselineSessions = Math.max(1, dynamicRows - sessionBaselineRows);
|
|
430
|
+
const spareRows = Math.max(0, rowsAfterBaselineSessions - DEFAULT_WHATS_NEW_ROWS);
|
|
431
|
+
return Math.max(
|
|
432
|
+
1,
|
|
433
|
+
Math.min(MAX_WHATS_NEW_ROWS, rowsAfterBaselineSessions, DEFAULT_WHATS_NEW_ROWS + Math.floor(spareRows / 2)),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
#sessionTrailLimit(
|
|
438
|
+
targetContentRows: number | undefined,
|
|
439
|
+
changelogLineCount: number,
|
|
440
|
+
lspLineCount: number,
|
|
441
|
+
flowLineCount: number,
|
|
442
|
+
): number {
|
|
443
|
+
if (this.recentSessions.length === 0) return 0;
|
|
444
|
+
|
|
445
|
+
const defaultLimit = Math.min(3, this.recentSessions.length);
|
|
446
|
+
if (targetContentRows === undefined) return defaultLimit;
|
|
447
|
+
|
|
448
|
+
const rowsWithDefaultTrail =
|
|
449
|
+
WELCOME_STATIC_RIGHT_ROWS_EXCLUDING_DYNAMIC_SECTIONS +
|
|
450
|
+
changelogLineCount +
|
|
451
|
+
flowLineCount +
|
|
452
|
+
lspLineCount +
|
|
453
|
+
defaultLimit;
|
|
454
|
+
const extraRows = Math.max(0, targetContentRows - rowsWithDefaultTrail);
|
|
455
|
+
return Math.min(this.recentSessions.length, defaultLimit + extraRows);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
#sessionTrailLines(rightColumnWidth: number, limit: number): string[] {
|
|
459
|
+
if (this.recentSessions.length === 0) {
|
|
460
|
+
return [` ${theme.fg("dim", t("welcome.noSessions"))}`];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const bulletPrefix = ` ${theme.md.bullet} `;
|
|
464
|
+
const prefixWidth = visibleWidth(bulletPrefix);
|
|
465
|
+
const lines: string[] = [];
|
|
466
|
+
for (const session of this.recentSessions.slice(0, limit)) {
|
|
467
|
+
const timeSuffixRaw = ` (${session.timeAgo})`;
|
|
468
|
+
const timeWidth = visibleWidth(timeSuffixRaw);
|
|
469
|
+
const nameBudget = Math.max(1, rightColumnWidth - prefixWidth - timeWidth);
|
|
470
|
+
const nameVis = visibleWidth(session.name);
|
|
471
|
+
const name = nameVis > nameBudget ? truncateToWidth(session.name, nameBudget) : session.name;
|
|
472
|
+
lines.push(`${theme.fg("dim", bulletPrefix)}${theme.fg("muted", name)}${theme.fg("dim", timeSuffixRaw)}`);
|
|
473
|
+
}
|
|
474
|
+
return lines;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
#whatsNewLines(width: number, maxRows: number): string[] {
|
|
478
|
+
const rowLimit = Math.max(1, Math.floor(maxRows));
|
|
341
479
|
const changelog = this.options.changelogMarkdown?.trim();
|
|
342
480
|
if (!changelog) {
|
|
343
481
|
return [` ${theme.fg("dim", "Ready for your next prompt")}`];
|
|
@@ -348,25 +486,25 @@ export class WelcomeComponent implements Component {
|
|
|
348
486
|
return [
|
|
349
487
|
` ${theme.fg("muted", `Updated to v${version}`)}`,
|
|
350
488
|
` ${theme.fg("dim", `Use ${theme.bold("/changelog")} for details`)}`,
|
|
351
|
-
];
|
|
489
|
+
].slice(0, rowLimit);
|
|
352
490
|
}
|
|
353
491
|
|
|
354
|
-
const itemLimit = Math.max(1, Math.floor(maxItems));
|
|
355
492
|
const items = this.#changelogItems(changelog);
|
|
356
493
|
if (items.length === 0) {
|
|
357
494
|
return [
|
|
358
495
|
` ${theme.fg("muted", `Updated to v${version}`)}`,
|
|
359
496
|
` ${theme.fg("dim", `Use ${theme.bold("/changelog")} for details`)}`,
|
|
360
|
-
];
|
|
497
|
+
].slice(0, rowLimit);
|
|
361
498
|
}
|
|
362
499
|
|
|
363
500
|
const prefix = ` ${theme.md.bullet} `;
|
|
364
501
|
const textWidth = Math.max(1, width - visibleWidth(prefix));
|
|
365
|
-
const
|
|
502
|
+
const visibleItemCount = items.length > rowLimit ? Math.max(1, rowLimit - 1) : rowLimit;
|
|
503
|
+
const visibleItems = items.slice(0, visibleItemCount).map(item => {
|
|
366
504
|
const text = visibleWidth(item) > textWidth ? truncateToWidth(item, textWidth) : item;
|
|
367
505
|
return `${theme.fg("dim", prefix)}${theme.fg("muted", text)}`;
|
|
368
506
|
});
|
|
369
|
-
if (items.length > visibleItems.length) {
|
|
507
|
+
if (items.length > visibleItems.length && visibleItems.length < rowLimit) {
|
|
370
508
|
visibleItems.push(` ${theme.fg("dim", `… ${theme.bold("/changelog")} for full notes`)}`);
|
|
371
509
|
}
|
|
372
510
|
return visibleItems;
|
|
@@ -469,7 +607,6 @@ interface ShineConfig {
|
|
|
469
607
|
/** Center of the shine band along the diagonal, in [0, 1]. */
|
|
470
608
|
pos: number;
|
|
471
609
|
}
|
|
472
|
-
|
|
473
610
|
/**
|
|
474
611
|
* Apply a multi-stop diagonal gradient (bottom-left → top-right) plus an
|
|
475
612
|
* optional sliding shine band across multi-line art. `phase` (0..1) shifts the
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { Loader, Markdown, padding, Spacer, Text, visibleWidth } from "@sayknow-cli/tui";
|
|
14
14
|
import { formatDuration, Snowflake, setProjectDir } from "@sayknow-cli/utils";
|
|
15
15
|
import { $ } from "bun";
|
|
16
|
+
import { resolveAppendOnlyMode } from "../../append-only-mode";
|
|
16
17
|
import { jobElapsedMs } from "../../async";
|
|
17
18
|
import { reset as resetCapabilities } from "../../capability";
|
|
18
19
|
import { clearClaudePluginRootsCache } from "../../discovery/helpers";
|
|
@@ -400,7 +401,7 @@ export class CommandController {
|
|
|
400
401
|
{
|
|
401
402
|
const setting = this.ctx.settings.get("provider.appendOnlyContext") ?? "auto";
|
|
402
403
|
const provider = this.ctx.session.model?.provider;
|
|
403
|
-
const mode = setting
|
|
404
|
+
const mode = resolveAppendOnlyMode(setting, provider ?? "");
|
|
404
405
|
const activeLabel = mode ? theme.fg("success", "active") : theme.fg("dim", "inactive");
|
|
405
406
|
const settingLabel = setting === "auto" ? `${setting} (${provider ?? "?"})` : setting;
|
|
406
407
|
info += `${theme.fg("dim", "Append-Only:")} ${activeLabel} (setting: ${settingLabel})\n`;
|
|
@@ -540,7 +541,7 @@ export class CommandController {
|
|
|
540
541
|
const title = showFull ? "Full Changelog" : "Recent Changes";
|
|
541
542
|
const hint = showFull
|
|
542
543
|
? ""
|
|
543
|
-
: `\n\n${theme.fg("dim", "Use")} ${theme.bold("/changelog full")} ${theme.fg("dim", "to view the complete changelog.")}`;
|
|
544
|
+
: `\n\n${theme.fg("dim", "Use")} ${theme.bold("/changelog --full")} ${theme.fg("dim", "to view the complete changelog.")}`;
|
|
544
545
|
|
|
545
546
|
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
546
547
|
this.ctx.chatContainer.addChild(new DynamicBorder());
|
|
@@ -926,8 +927,7 @@ export class CommandController {
|
|
|
926
927
|
this.ctx.streamingMessage = undefined;
|
|
927
928
|
this.ctx.pendingTools.clear();
|
|
928
929
|
|
|
929
|
-
this.ctx.chatContainer.addChild(new
|
|
930
|
-
this.ctx.chatContainer.addChild(new Text(`${theme.fg("accent", `${theme.status.success} ${label}`)}`, 1, 1));
|
|
930
|
+
this.ctx.chatContainer.addChild(new Text(`${theme.fg("accent", `${theme.status.success} ${label}`)}`, 1, 0));
|
|
931
931
|
await this.ctx.reloadTodos();
|
|
932
932
|
this.ctx.ui.requestRender();
|
|
933
933
|
}
|
|
@@ -936,6 +936,42 @@ export class CommandController {
|
|
|
936
936
|
await this.#runNewSessionFlow();
|
|
937
937
|
}
|
|
938
938
|
|
|
939
|
+
async handleContextClearCommand(): Promise<void> {
|
|
940
|
+
if (this.ctx.loadingAnimation) {
|
|
941
|
+
this.ctx.loadingAnimation.stop();
|
|
942
|
+
this.ctx.loadingAnimation = undefined;
|
|
943
|
+
}
|
|
944
|
+
this.ctx.statusContainer.clear();
|
|
945
|
+
|
|
946
|
+
if (this.ctx.session.isCompacting) {
|
|
947
|
+
this.ctx.session.abortCompaction();
|
|
948
|
+
while (this.ctx.session.isCompacting) {
|
|
949
|
+
await Bun.sleep(10);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (!(await this.ctx.session.clearContext())) return;
|
|
953
|
+
setSessionTerminalTitle(this.ctx.sessionManager.getSessionName(), this.ctx.sessionManager.getCwd());
|
|
954
|
+
|
|
955
|
+
this.ctx.statusLine.invalidate();
|
|
956
|
+
this.ctx.statusLine.setSessionStartTime(Date.now());
|
|
957
|
+
this.ctx.updateEditorTopBorder();
|
|
958
|
+
this.ctx.updateEditorBorderColor();
|
|
959
|
+
this.ctx.ui.requestRender();
|
|
960
|
+
|
|
961
|
+
this.ctx.chatContainer.clear();
|
|
962
|
+
this.ctx.pendingMessagesContainer.clear();
|
|
963
|
+
this.ctx.compactionQueuedMessages = [];
|
|
964
|
+
this.ctx.streamingComponent = undefined;
|
|
965
|
+
this.ctx.streamingMessage = undefined;
|
|
966
|
+
this.ctx.pendingTools.clear();
|
|
967
|
+
|
|
968
|
+
this.ctx.chatContainer.addChild(
|
|
969
|
+
new Text(`${theme.fg("accent", `${theme.status.success} Context cleared`)}`, 1, 0),
|
|
970
|
+
);
|
|
971
|
+
await this.ctx.reloadTodos();
|
|
972
|
+
this.ctx.ui.requestRender();
|
|
973
|
+
}
|
|
974
|
+
|
|
939
975
|
async handleDropCommand(): Promise<void> {
|
|
940
976
|
if (!this.ctx.sessionManager.getSessionFile()) {
|
|
941
977
|
this.ctx.showError("Nothing to drop (in-memory session)");
|
|
@@ -24,10 +24,20 @@ import type { ResolveToolDetails } from "../../tools/resolve";
|
|
|
24
24
|
import { interruptHint } from "../shared";
|
|
25
25
|
import { buildAbortDisplayMessage } from "../utils/abort-message";
|
|
26
26
|
import { ringTerminalBell } from "../utils/terminal-bell";
|
|
27
|
+
import { argsWithPartialJson } from "../utils/ui-helpers";
|
|
27
28
|
|
|
28
29
|
type AgentSessionEventKind = AgentSessionEvent["type"];
|
|
29
30
|
|
|
30
31
|
const IRC_MESSAGE_VISIBLE_TTL_MS = 10_000;
|
|
32
|
+
|
|
33
|
+
/** Test-only performance counters for advisory baseline tests. */
|
|
34
|
+
export const __eventControllerPerfCounters = {
|
|
35
|
+
messageUpdateContentVisits: 0,
|
|
36
|
+
reset(): void {
|
|
37
|
+
this.messageUpdateContentVisits = 0;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
31
41
|
const COMPLETION_NOTIFY_COMMAND_TIMEOUT_MS = 10_000;
|
|
32
42
|
|
|
33
43
|
interface CompletionNotifyPayload {
|
|
@@ -100,6 +110,8 @@ export class EventController {
|
|
|
100
110
|
#lastAssistantComponent: AssistantMessageComponent | undefined = undefined;
|
|
101
111
|
#idleCompactionTimer?: NodeJS.Timeout;
|
|
102
112
|
#ircExpiryTimers = new Map<string, NodeJS.Timeout>();
|
|
113
|
+
#toolIntentCache = new Map<string, { args: unknown; intent: string | undefined }>();
|
|
114
|
+
#thinkingContentIndices = new Set<number>();
|
|
103
115
|
#handlers: AgentSessionEventHandlers;
|
|
104
116
|
|
|
105
117
|
constructor(private ctx: InteractiveModeContext) {
|
|
@@ -307,12 +319,14 @@ export class EventController {
|
|
|
307
319
|
} else if (event.message.role === "assistant") {
|
|
308
320
|
this.#lastThinkingCount = 0;
|
|
309
321
|
this.#resetReadGroup();
|
|
322
|
+
this.#toolIntentCache.clear();
|
|
323
|
+
this.#thinkingContentIndices.clear();
|
|
310
324
|
this.ctx.streamingComponent = new AssistantMessageComponent(undefined, this.ctx.hideThinkingBlock, () =>
|
|
311
325
|
this.ctx.ui.requestRender(),
|
|
312
326
|
);
|
|
313
327
|
this.ctx.streamingMessage = event.message;
|
|
314
328
|
this.ctx.chatContainer.addChild(this.ctx.streamingComponent);
|
|
315
|
-
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage);
|
|
329
|
+
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage, { streaming: true });
|
|
316
330
|
this.ctx.ui.requestRender();
|
|
317
331
|
}
|
|
318
332
|
}
|
|
@@ -375,46 +389,53 @@ export class EventController {
|
|
|
375
389
|
async #handleMessageUpdate(event: Extract<AgentSessionEvent, { type: "message_update" }>): Promise<void> {
|
|
376
390
|
if (this.ctx.streamingComponent && event.message.role === "assistant") {
|
|
377
391
|
this.ctx.streamingMessage = event.message;
|
|
378
|
-
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage);
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
392
|
+
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage, { streaming: true });
|
|
393
|
+
const contentIndex = event.assistantMessageEvent?.contentIndex;
|
|
394
|
+
const changedContent =
|
|
395
|
+
typeof contentIndex === "number" && contentIndex >= 0
|
|
396
|
+
? this.ctx.streamingMessage.content[contentIndex]
|
|
397
|
+
: undefined;
|
|
398
|
+
const contentsToProcess = changedContent ? [changedContent] : this.ctx.streamingMessage.content;
|
|
399
|
+
|
|
400
|
+
if (changedContent?.type === "thinking" && changedContent.thinking.trim()) {
|
|
401
|
+
if (typeof contentIndex === "number") this.#thinkingContentIndices.add(contentIndex);
|
|
402
|
+
const thinkingCount = this.#thinkingContentIndices.size;
|
|
403
|
+
if (thinkingCount > this.#lastThinkingCount) {
|
|
404
|
+
this.#resetReadGroup();
|
|
405
|
+
this.#lastThinkingCount = thinkingCount;
|
|
406
|
+
}
|
|
407
|
+
} else if (!changedContent) {
|
|
408
|
+
const thinkingCount = this.ctx.streamingMessage.content.filter(
|
|
409
|
+
content => content.type === "thinking" && content.thinking.trim(),
|
|
410
|
+
).length;
|
|
411
|
+
if (thinkingCount > this.#lastThinkingCount) {
|
|
412
|
+
this.#resetReadGroup();
|
|
413
|
+
this.#lastThinkingCount = thinkingCount;
|
|
414
|
+
}
|
|
386
415
|
}
|
|
387
416
|
|
|
388
|
-
for (const content of
|
|
417
|
+
for (const content of contentsToProcess) {
|
|
418
|
+
__eventControllerPerfCounters.messageUpdateContentVisits += 1;
|
|
389
419
|
if (content.type !== "toolCall") continue;
|
|
390
420
|
if (content.name === "read") {
|
|
391
|
-
if (!readArgsHaveTarget(content.arguments))
|
|
392
|
-
// Args still streaming — defer until path is parseable so we can route to the
|
|
393
|
-
// read group (regular files) vs ToolExecutionComponent (internal URLs).
|
|
394
|
-
// Creating either component now would lock the read into the wrong shape.
|
|
395
|
-
continue;
|
|
396
|
-
}
|
|
421
|
+
if (!readArgsHaveTarget(content.arguments)) continue;
|
|
397
422
|
if (!readArgsTargetInternalUrl(content.arguments)) {
|
|
398
423
|
this.#trackReadToolCall(content.id, content.arguments);
|
|
399
424
|
const component = this.ctx.pendingTools.get(content.id);
|
|
400
|
-
if (component)
|
|
401
|
-
|
|
402
|
-
} else {
|
|
425
|
+
if (component) component.updateArgs(content.arguments, content.id);
|
|
426
|
+
else {
|
|
403
427
|
const group = this.#getReadGroup();
|
|
404
428
|
group.updateArgs(content.arguments, content.id);
|
|
405
429
|
this.ctx.pendingTools.set(content.id, group);
|
|
406
430
|
}
|
|
407
431
|
continue;
|
|
408
432
|
}
|
|
409
|
-
// Internal URL read falls through to ToolExecutionComponent below.
|
|
410
433
|
}
|
|
411
434
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
? { ...content.arguments, __partialJson: content.partialJson }
|
|
417
|
-
: content.arguments;
|
|
435
|
+
const renderArgs = argsWithPartialJson(
|
|
436
|
+
content.arguments,
|
|
437
|
+
"partialJson" in content ? content.partialJson : undefined,
|
|
438
|
+
);
|
|
418
439
|
if (!this.ctx.pendingTools.has(content.id)) {
|
|
419
440
|
this.#resetReadGroup();
|
|
420
441
|
this.ctx.chatContainer.addChild(new Text("", 0, 0));
|
|
@@ -437,32 +458,30 @@ export class EventController {
|
|
|
437
458
|
this.ctx.chatContainer.addChild(component);
|
|
438
459
|
this.ctx.pendingTools.set(content.id, component);
|
|
439
460
|
} else {
|
|
440
|
-
|
|
441
|
-
if (component) {
|
|
442
|
-
component.updateArgs(renderArgs, content.id);
|
|
443
|
-
}
|
|
461
|
+
this.ctx.pendingTools.get(content.id)?.updateArgs(renderArgs, content.id);
|
|
444
462
|
}
|
|
445
|
-
}
|
|
446
463
|
|
|
447
|
-
// Update working message with intent from streamed tool arguments
|
|
448
|
-
for (const content of this.ctx.streamingMessage.content) {
|
|
449
|
-
if (content.type !== "toolCall") continue;
|
|
450
464
|
const args = content.arguments;
|
|
451
465
|
if (!args || typeof args !== "object") continue;
|
|
466
|
+
let intent: string | undefined;
|
|
452
467
|
if (INTENT_FIELD in args) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
468
|
+
intent = String(args[INTENT_FIELD]);
|
|
469
|
+
} else {
|
|
470
|
+
const cached = this.#toolIntentCache.get(content.id);
|
|
471
|
+
if (cached?.args === args) intent = cached.intent;
|
|
472
|
+
else {
|
|
473
|
+
const tool = this.ctx.session.getToolByName(content.name);
|
|
474
|
+
if (typeof tool?.intent === "function") {
|
|
475
|
+
try {
|
|
476
|
+
intent = tool.intent(args as never)?.trim();
|
|
477
|
+
} catch {
|
|
478
|
+
// intent function must never break the UI
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
this.#toolIntentCache.set(content.id, { args, intent });
|
|
462
482
|
}
|
|
463
|
-
} catch {
|
|
464
|
-
// intent function must never break the UI
|
|
465
483
|
}
|
|
484
|
+
if (intent) this.#updateWorkingMessageFromIntent(intent);
|
|
466
485
|
}
|
|
467
486
|
|
|
468
487
|
this.ctx.ui.requestRender();
|
|
@@ -495,9 +514,9 @@ export class EventController {
|
|
|
495
514
|
// display only — does NOT mutate the persisted message's stopReason
|
|
496
515
|
// (the marker on errorMessage drives replay-side suppression).
|
|
497
516
|
const msgWithoutAbort = { ...this.ctx.streamingMessage, stopReason: "stop" as const };
|
|
498
|
-
this.ctx.streamingComponent.updateContent(msgWithoutAbort);
|
|
517
|
+
this.ctx.streamingComponent.updateContent(msgWithoutAbort, { streaming: false });
|
|
499
518
|
} else {
|
|
500
|
-
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage);
|
|
519
|
+
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage, { streaming: false });
|
|
501
520
|
}
|
|
502
521
|
|
|
503
522
|
if (this.ctx.streamingMessage.stopReason !== "aborted" && this.ctx.streamingMessage.stopReason !== "error") {
|
|
@@ -192,9 +192,8 @@ export class ExtensionUiController {
|
|
|
192
192
|
this.ctx.streamingMessage = undefined;
|
|
193
193
|
this.ctx.pendingTools.clear();
|
|
194
194
|
|
|
195
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
196
195
|
this.ctx.chatContainer.addChild(
|
|
197
|
-
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1,
|
|
196
|
+
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1, 0),
|
|
198
197
|
);
|
|
199
198
|
await this.ctx.reloadTodos();
|
|
200
199
|
this.ctx.ui.requestRender();
|
|
@@ -300,15 +299,14 @@ export class ExtensionUiController {
|
|
|
300
299
|
}
|
|
301
300
|
|
|
302
301
|
#rebuildHookWidgets(): void {
|
|
303
|
-
this.#renderHookWidgetContainer(this.ctx.hookWidgetContainerAbove, this.#hookWidgetsAbove, true
|
|
304
|
-
this.#renderHookWidgetContainer(this.ctx.hookWidgetContainerBelow, this.#hookWidgetsBelow, false
|
|
302
|
+
this.#renderHookWidgetContainer(this.ctx.hookWidgetContainerAbove, this.#hookWidgetsAbove, true);
|
|
303
|
+
this.#renderHookWidgetContainer(this.ctx.hookWidgetContainerBelow, this.#hookWidgetsBelow, false);
|
|
305
304
|
this.ctx.ui.requestRender();
|
|
306
305
|
}
|
|
307
306
|
|
|
308
307
|
#renderHookWidgetContainer(
|
|
309
308
|
container: Container,
|
|
310
309
|
widgets: Map<string, ExtensionUiComponent>,
|
|
311
|
-
spacerWhenEmpty: boolean,
|
|
312
310
|
leadingSpacer: boolean,
|
|
313
311
|
): void {
|
|
314
312
|
// Detach (not dispose): hook widgets are persistent instances owned by the
|
|
@@ -318,9 +316,6 @@ export class ExtensionUiController {
|
|
|
318
316
|
container.detachAll();
|
|
319
317
|
|
|
320
318
|
if (widgets.size === 0) {
|
|
321
|
-
if (spacerWhenEmpty) {
|
|
322
|
-
container.addChild(new Spacer(1));
|
|
323
|
-
}
|
|
324
319
|
return;
|
|
325
320
|
}
|
|
326
321
|
|
|
@@ -435,9 +430,8 @@ export class ExtensionUiController {
|
|
|
435
430
|
this.ctx.streamingMessage = undefined;
|
|
436
431
|
this.ctx.pendingTools.clear();
|
|
437
432
|
|
|
438
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
439
433
|
this.ctx.chatContainer.addChild(
|
|
440
|
-
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1,
|
|
434
|
+
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1, 0),
|
|
441
435
|
);
|
|
442
436
|
await this.ctx.reloadTodos();
|
|
443
437
|
this.ctx.ui.requestRender();
|