@sayknow-cli/coding-agent 0.3.7 → 0.3.9
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 +119 -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/components/thinking-selector.d.ts +2 -2
- 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/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +4 -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-mode.d.ts +1 -3
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +3 -0
- 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 +3 -0
- package/dist/types/modes/utils/injected-user-submission.d.ts +52 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/notifications/config-commands.d.ts +9 -0
- package/dist/types/notifications/config.d.ts +30 -1
- package/dist/types/notifications/daemon-paths.d.ts +1 -0
- package/dist/types/notifications/index.d.ts +8 -3
- package/dist/types/notifications/lifecycle-commands.d.ts +12 -3
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +2 -1
- package/dist/types/notifications/reply-sent-store.d.ts +53 -0
- package/dist/types/notifications/rich-draft.d.ts +68 -0
- package/dist/types/notifications/rich-render.d.ts +90 -0
- package/dist/types/notifications/telegram-daemon.d.ts +45 -3
- package/dist/types/notifications/telegram-reference.d.ts +7 -5
- package/dist/types/notifications/threaded-render.d.ts +14 -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/tmux-common.d.ts +1 -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/utils/pasted-image-path.d.ts +30 -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 +103 -17
- package/src/cli/telegram-cli.ts +1 -1
- package/src/cli/update-cli.ts +148 -33
- 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 +3 -3
- package/src/config/model-profile-activation.ts +65 -0
- package/src/config/model-profiles.ts +31 -2
- package/src/config/settings-schema.ts +76 -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 +17 -8
- package/src/lsp/client.ts +1 -0
- 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/footer.ts +7 -2
- 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/thinking-selector.ts +10 -8
- 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 +77 -49
- package/src/modes/controllers/extension-ui-controller.ts +12 -11
- package/src/modes/controllers/input-controller.ts +192 -35
- package/src/modes/controllers/selector-controller.ts +168 -24
- package/src/modes/interactive-mode.ts +56 -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 +32 -12
- package/src/modes/rpc/rpc-socket-security.ts +13 -1
- 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 +3 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/injected-user-submission.ts +94 -0
- package/src/modes/utils/ui-helpers.ts +20 -7
- package/src/notifications/config-commands.ts +20 -0
- package/src/notifications/config.ts +52 -5
- package/src/notifications/daemon-paths.ts +2 -0
- package/src/notifications/index.ts +144 -18
- package/src/notifications/lifecycle-commands.ts +74 -23
- package/src/notifications/lifecycle-control-runtime.ts +8 -3
- package/src/notifications/lifecycle-orchestrator.ts +19 -6
- package/src/notifications/reply-sent-store.ts +134 -0
- package/src/notifications/rich-draft.ts +107 -0
- package/src/notifications/rich-render.ts +142 -0
- package/src/notifications/telegram-daemon-control.ts +5 -3
- package/src/notifications/telegram-daemon.ts +606 -98
- package/src/notifications/telegram-reference.ts +17 -16
- package/src/notifications/threaded-render.ts +45 -2
- 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 +6 -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 +88 -36
- package/src/session/agent-session.ts +1013 -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/tmux-common.ts +1 -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 +1672 -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 +296 -72
- 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/utils/pasted-image-path.ts +170 -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
|
@@ -55,6 +55,15 @@ function elideRunawayThinkingRepetition(text: string): string {
|
|
|
55
55
|
return text;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
interface AssistantMessageUpdateOptions {
|
|
59
|
+
streaming?: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type AssistantChildDescriptor = {
|
|
63
|
+
key: string;
|
|
64
|
+
component: Component;
|
|
65
|
+
};
|
|
66
|
+
|
|
58
67
|
/**
|
|
59
68
|
* Component that renders a complete assistant message
|
|
60
69
|
*/
|
|
@@ -67,6 +76,11 @@ export class AssistantMessageComponent extends Container {
|
|
|
67
76
|
#kittyConversionsInFlight = new Set<string>();
|
|
68
77
|
#responseHeader = new Text(theme.bold(theme.fg("statusLineModel", "sayknow")), 1, 0);
|
|
69
78
|
#contentBlocksCache = new WeakMap<object, { source: string; component: Component }>();
|
|
79
|
+
#lastStreaming = false;
|
|
80
|
+
#childComponents = new Map<string, Component>();
|
|
81
|
+
#contentBlockKeys = new WeakMap<object, string>();
|
|
82
|
+
#nextContentBlockKey = 0;
|
|
83
|
+
#reusableChildren = new WeakSet<Component>();
|
|
70
84
|
|
|
71
85
|
constructor(
|
|
72
86
|
message?: AssistantMessage,
|
|
@@ -87,7 +101,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
87
101
|
override invalidate(): void {
|
|
88
102
|
super.invalidate();
|
|
89
103
|
if (this.#lastMessage) {
|
|
90
|
-
this.updateContent(this.#lastMessage);
|
|
104
|
+
this.updateContent(this.#lastMessage, { streaming: this.#lastStreaming });
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
107
|
|
|
@@ -115,7 +129,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
115
129
|
this.#convertToolImagesForKitty(toolCallId, validImages);
|
|
116
130
|
}
|
|
117
131
|
if (this.#lastMessage) {
|
|
118
|
-
this.updateContent(this.#lastMessage);
|
|
132
|
+
this.updateContent(this.#lastMessage, { streaming: this.#lastStreaming });
|
|
119
133
|
}
|
|
120
134
|
}
|
|
121
135
|
|
|
@@ -138,7 +152,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
138
152
|
mimeType: "image/png",
|
|
139
153
|
});
|
|
140
154
|
if (this.#lastMessage) {
|
|
141
|
-
this.updateContent(this.#lastMessage);
|
|
155
|
+
this.updateContent(this.#lastMessage, { streaming: this.#lastStreaming });
|
|
142
156
|
}
|
|
143
157
|
this.onImageUpdate?.();
|
|
144
158
|
})
|
|
@@ -151,153 +165,298 @@ export class AssistantMessageComponent extends Container {
|
|
|
151
165
|
setUsageInfo(usage: Usage): void {
|
|
152
166
|
this.#usageInfo = usage;
|
|
153
167
|
if (this.#lastMessage) {
|
|
154
|
-
this.updateContent(this.#lastMessage);
|
|
168
|
+
this.updateContent(this.#lastMessage, { streaming: this.#lastStreaming });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
#contentBlockKey(content: object): string {
|
|
173
|
+
let key = this.#contentBlockKeys.get(content);
|
|
174
|
+
if (!key) {
|
|
175
|
+
key = `block:${this.#nextContentBlockKey++}`;
|
|
176
|
+
this.#contentBlockKeys.set(content, key);
|
|
155
177
|
}
|
|
178
|
+
return key;
|
|
156
179
|
}
|
|
157
180
|
|
|
158
|
-
#
|
|
181
|
+
#cachedChild<T extends Component>(key: string, create: () => T): T {
|
|
182
|
+
const cached = this.#childComponents.get(key);
|
|
183
|
+
if (cached) return cached as T;
|
|
184
|
+
const component = create();
|
|
185
|
+
this.#childComponents.set(key, component);
|
|
186
|
+
this.#reusableChildren.add(component);
|
|
187
|
+
return component;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#renderTextBlock(content: { text: string }, streaming: boolean): Component {
|
|
159
191
|
const cached = this.#contentBlocksCache.get(content);
|
|
160
|
-
if (cached?.source === content.text)
|
|
192
|
+
if (cached?.source === content.text) {
|
|
193
|
+
if (cached.component instanceof Markdown) {
|
|
194
|
+
cached.component.setOnStaleThrottle(this.onImageUpdate);
|
|
195
|
+
cached.component.setStreaming(streaming);
|
|
196
|
+
}
|
|
197
|
+
return cached.component;
|
|
198
|
+
}
|
|
161
199
|
const trimmed = content.text.trim();
|
|
162
200
|
const deepInterview = renderDeepInterviewAssistantText(trimmed, theme);
|
|
163
201
|
// Reuse the same Markdown instance across streaming chunks (update text in place)
|
|
164
202
|
// instead of constructing a new one each chunk; combined with the markdown
|
|
165
203
|
// per-code-block highlight cache, appends no longer re-highlight the whole prefix.
|
|
166
204
|
if (!deepInterview && cached && cached.component instanceof Markdown) {
|
|
167
|
-
cached.component.
|
|
205
|
+
cached.component.setOnStaleThrottle(this.onImageUpdate);
|
|
206
|
+
cached.component.setText(trimmed, { streaming });
|
|
168
207
|
cached.source = content.text;
|
|
169
208
|
return cached.component;
|
|
170
209
|
}
|
|
171
210
|
const component = deepInterview ?? new Markdown(trimmed, 1, 0, getMarkdownTheme());
|
|
211
|
+
if (component instanceof Markdown) {
|
|
212
|
+
component.setOnStaleThrottle(this.onImageUpdate);
|
|
213
|
+
component.setStreaming(streaming);
|
|
214
|
+
}
|
|
172
215
|
this.#contentBlocksCache.set(content, { source: content.text, component });
|
|
216
|
+
this.#reusableChildren.add(component);
|
|
173
217
|
return component;
|
|
174
218
|
}
|
|
175
219
|
|
|
176
|
-
#renderThinkingBlock(content: { thinking: string }): Markdown {
|
|
220
|
+
#renderThinkingBlock(content: { thinking: string }, streaming: boolean): Markdown {
|
|
177
221
|
const cached = this.#contentBlocksCache.get(content);
|
|
178
|
-
if (cached?.source === content.thinking)
|
|
222
|
+
if (cached?.source === content.thinking) {
|
|
223
|
+
if (cached.component instanceof Markdown) {
|
|
224
|
+
cached.component.setOnStaleThrottle(this.onImageUpdate);
|
|
225
|
+
cached.component.setStreaming(streaming);
|
|
226
|
+
}
|
|
227
|
+
return cached.component as Markdown;
|
|
228
|
+
}
|
|
179
229
|
const trimmed = elideRunawayThinkingRepetition(content.thinking.trim());
|
|
230
|
+
if (cached?.component instanceof Markdown) {
|
|
231
|
+
cached.component.setOnStaleThrottle(this.onImageUpdate);
|
|
232
|
+
cached.component.setText(trimmed, { streaming });
|
|
233
|
+
cached.source = content.thinking;
|
|
234
|
+
return cached.component;
|
|
235
|
+
}
|
|
180
236
|
const component = new Markdown(trimmed, 1, 0, getMarkdownTheme(), {
|
|
181
237
|
color: (text: string) => theme.fg("thinkingText", text),
|
|
182
238
|
italic: true,
|
|
183
239
|
});
|
|
240
|
+
component.setOnStaleThrottle(this.onImageUpdate);
|
|
241
|
+
component.setStreaming(streaming);
|
|
184
242
|
this.#contentBlocksCache.set(content, { source: content.thinking, component });
|
|
243
|
+
this.#reusableChildren.add(component);
|
|
185
244
|
return component;
|
|
186
245
|
}
|
|
187
246
|
|
|
188
|
-
#
|
|
247
|
+
#toolImageDescriptors(): AssistantChildDescriptor[] {
|
|
189
248
|
const imageEntries = Array.from(this.#toolImagesByCallId.entries()).flatMap(([toolCallId, images]) =>
|
|
190
249
|
images.map((image, index) => ({ image, key: `${toolCallId}:${index}` })),
|
|
191
250
|
);
|
|
192
|
-
if (imageEntries.length === 0) return;
|
|
251
|
+
if (imageEntries.length === 0) return [];
|
|
193
252
|
|
|
194
|
-
|
|
253
|
+
const descriptors: AssistantChildDescriptor[] = [
|
|
254
|
+
{ key: "tool-images:spacer", component: this.#cachedChild("tool-images:spacer", () => new Spacer(1)) },
|
|
255
|
+
];
|
|
195
256
|
for (const { image, key } of imageEntries) {
|
|
196
257
|
const displayImage =
|
|
197
258
|
TERMINAL.imageProtocol === ImageProtocol.Kitty && image.mimeType !== "image/png"
|
|
198
259
|
? this.#convertedKittyImages.get(key)
|
|
199
260
|
: image;
|
|
200
261
|
if (TERMINAL.imageProtocol && displayImage) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
262
|
+
const imageKey = `tool-image:${key}:${displayImage.mimeType}:${displayImage.data}`;
|
|
263
|
+
descriptors.push({
|
|
264
|
+
key: imageKey,
|
|
265
|
+
component: this.#cachedChild(
|
|
266
|
+
imageKey,
|
|
267
|
+
() =>
|
|
268
|
+
new Image(
|
|
269
|
+
displayImage.data,
|
|
270
|
+
displayImage.mimeType,
|
|
271
|
+
{ fallbackColor: (text: string) => theme.fg("toolOutput", text) },
|
|
272
|
+
resolveImageOptions(),
|
|
273
|
+
),
|
|
207
274
|
),
|
|
208
|
-
);
|
|
275
|
+
});
|
|
209
276
|
continue;
|
|
210
277
|
}
|
|
211
|
-
|
|
278
|
+
const fallbackKey = `tool-image-fallback:${key}:${image.mimeType}`;
|
|
279
|
+
descriptors.push({
|
|
280
|
+
key: fallbackKey,
|
|
281
|
+
component: this.#cachedChild(
|
|
282
|
+
fallbackKey,
|
|
283
|
+
() => new Text(theme.fg("toolOutput", `[Image: ${image.mimeType}]`), 1, 0),
|
|
284
|
+
),
|
|
285
|
+
});
|
|
212
286
|
}
|
|
287
|
+
return descriptors;
|
|
213
288
|
}
|
|
214
289
|
|
|
215
|
-
|
|
216
|
-
|
|
290
|
+
#reconcileChildren(descriptors: AssistantChildDescriptor[]): void {
|
|
291
|
+
const nextChildren = descriptors.map(descriptor => descriptor.component);
|
|
292
|
+
if (
|
|
293
|
+
nextChildren.length === this.#contentContainer.children.length &&
|
|
294
|
+
nextChildren.every((child, index) => child === this.#contentContainer.children[index])
|
|
295
|
+
) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
217
298
|
|
|
218
|
-
|
|
219
|
-
this.#contentContainer.
|
|
299
|
+
const nextSet = new Set(nextChildren);
|
|
300
|
+
for (const child of this.#contentContainer.children) {
|
|
301
|
+
if (!nextSet.has(child) && !this.#reusableChildren.has(child)) {
|
|
302
|
+
child.dispose?.();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
this.#contentContainer.children = nextChildren;
|
|
306
|
+
}
|
|
220
307
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
308
|
+
updateContent(message: AssistantMessage, options?: AssistantMessageUpdateOptions): void {
|
|
309
|
+
this.#lastMessage = message;
|
|
310
|
+
this.#lastStreaming = options?.streaming ?? false;
|
|
224
311
|
|
|
312
|
+
const visibleContentAfter = new Array<boolean>(message.content.length).fill(false);
|
|
313
|
+
let hasVisibleContentAfter = false;
|
|
314
|
+
let hasVisibleContent = false;
|
|
315
|
+
let activeContentIndex = -1;
|
|
316
|
+
for (let i = message.content.length - 1; i >= 0; i--) {
|
|
317
|
+
visibleContentAfter[i] = hasVisibleContentAfter;
|
|
318
|
+
const content = message.content[i];
|
|
319
|
+
const isVisible =
|
|
320
|
+
(content.type === "text" && Boolean(content.text.trim())) ||
|
|
321
|
+
(content.type === "thinking" && Boolean(content.thinking.trim()));
|
|
322
|
+
if (isVisible) {
|
|
323
|
+
hasVisibleContent = true;
|
|
324
|
+
hasVisibleContentAfter = true;
|
|
325
|
+
if (this.#lastStreaming && activeContentIndex === -1) {
|
|
326
|
+
activeContentIndex = i;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const streaming = this.#lastStreaming;
|
|
332
|
+
const descriptors: AssistantChildDescriptor[] = [];
|
|
225
333
|
if (hasVisibleContent) {
|
|
226
|
-
|
|
227
|
-
|
|
334
|
+
descriptors.push({
|
|
335
|
+
key: "response-header:spacer",
|
|
336
|
+
component: this.#cachedChild("response-header:spacer", () => new Spacer(1)),
|
|
337
|
+
});
|
|
338
|
+
descriptors.push({ key: "response-header", component: this.#responseHeader });
|
|
228
339
|
}
|
|
229
340
|
|
|
230
341
|
// Render content in order
|
|
231
342
|
for (let i = 0; i < message.content.length; i++) {
|
|
232
343
|
const content = message.content[i];
|
|
344
|
+
const blockKey = this.#contentBlockKey(content);
|
|
233
345
|
if (content.type === "text" && content.text.trim()) {
|
|
234
|
-
|
|
346
|
+
descriptors.push({
|
|
347
|
+
key: `${blockKey}:text`,
|
|
348
|
+
component: this.#renderTextBlock(content, streaming && i === activeContentIndex),
|
|
349
|
+
});
|
|
235
350
|
} else if (content.type === "thinking" && content.thinking.trim()) {
|
|
236
351
|
// Add spacing only when another visible assistant content block follows.
|
|
237
352
|
// This avoids a superfluous blank line before separately-rendered tool execution blocks.
|
|
238
|
-
const hasVisibleContentAfter = message.content
|
|
239
|
-
.slice(i + 1)
|
|
240
|
-
.some(c => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
|
|
241
|
-
|
|
242
353
|
if (this.hideThinkingBlock) {
|
|
243
354
|
// Show static "Thinking..." label when hidden
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
this.#
|
|
247
|
-
|
|
355
|
+
descriptors.push({
|
|
356
|
+
key: `${blockKey}:thinking-hidden`,
|
|
357
|
+
component: this.#cachedChild(
|
|
358
|
+
`${blockKey}:thinking-hidden`,
|
|
359
|
+
() => new Text(theme.italic(theme.fg("thinkingText", "Thinking...")), 1, 0),
|
|
360
|
+
),
|
|
361
|
+
});
|
|
248
362
|
} else {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
this.#
|
|
252
|
-
}
|
|
363
|
+
descriptors.push({
|
|
364
|
+
key: `${blockKey}:thinking`,
|
|
365
|
+
component: this.#renderThinkingBlock(content, streaming && i === activeContentIndex),
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
if (visibleContentAfter[i]) {
|
|
369
|
+
descriptors.push({
|
|
370
|
+
key: `${blockKey}:thinking-spacer`,
|
|
371
|
+
component: this.#cachedChild(`${blockKey}:thinking-spacer`, () => new Spacer(1)),
|
|
372
|
+
});
|
|
253
373
|
}
|
|
254
374
|
}
|
|
255
375
|
}
|
|
256
376
|
|
|
257
|
-
this.#
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
377
|
+
descriptors.push(...this.#toolImageDescriptors());
|
|
378
|
+
|
|
379
|
+
const isTerminal = !streaming || Boolean(message.stopReason);
|
|
380
|
+
if (isTerminal) {
|
|
381
|
+
// Check if aborted - show after partial content
|
|
382
|
+
// But only if there are no tool calls (tool execution components will show the error)
|
|
383
|
+
const hasToolCalls = message.content.some(c => c.type === "toolCall");
|
|
384
|
+
if (!hasToolCalls) {
|
|
385
|
+
if (message.stopReason === "aborted" && !isSilentAbort(message.errorMessage)) {
|
|
386
|
+
const abortMessage =
|
|
387
|
+
message.errorMessage && message.errorMessage !== "Request was aborted"
|
|
388
|
+
? message.errorMessage
|
|
389
|
+
: "Operation aborted";
|
|
390
|
+
descriptors.push({
|
|
391
|
+
key: "abort:spacer",
|
|
392
|
+
component: this.#cachedChild("abort:spacer", () => new Spacer(1)),
|
|
393
|
+
});
|
|
394
|
+
descriptors.push({
|
|
395
|
+
key: `abort:text:${abortMessage}`,
|
|
396
|
+
component: this.#cachedChild(
|
|
397
|
+
`abort:text:${abortMessage}`,
|
|
398
|
+
() => new Text(theme.fg("error", abortMessage), 1, 0),
|
|
399
|
+
),
|
|
400
|
+
});
|
|
401
|
+
} else if (message.stopReason === "error") {
|
|
402
|
+
const errorMsg = message.errorMessage || "Unknown error";
|
|
403
|
+
descriptors.push({
|
|
404
|
+
key: "error:spacer",
|
|
405
|
+
component: this.#cachedChild("error:spacer", () => new Spacer(1)),
|
|
406
|
+
});
|
|
407
|
+
descriptors.push({
|
|
408
|
+
key: `error:text:${errorMsg}`,
|
|
409
|
+
component: this.#cachedChild(
|
|
410
|
+
`error:text:${errorMsg}`,
|
|
411
|
+
() => new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0),
|
|
412
|
+
),
|
|
413
|
+
});
|
|
271
414
|
}
|
|
272
|
-
this.#contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
|
|
273
|
-
} else if (message.stopReason === "error") {
|
|
274
|
-
const errorMsg = message.errorMessage || "Unknown error";
|
|
275
|
-
this.#contentContainer.addChild(new Spacer(1));
|
|
276
|
-
this.#contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
|
|
277
415
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
416
|
+
if (
|
|
417
|
+
message.errorMessage &&
|
|
418
|
+
!isSilentAbort(message.errorMessage) &&
|
|
419
|
+
message.stopReason !== "aborted" &&
|
|
420
|
+
message.stopReason !== "error"
|
|
421
|
+
) {
|
|
422
|
+
descriptors.push({
|
|
423
|
+
key: "message-error:spacer",
|
|
424
|
+
component: this.#cachedChild("message-error:spacer", () => new Spacer(1)),
|
|
425
|
+
});
|
|
426
|
+
descriptors.push({
|
|
427
|
+
key: `message-error:text:${message.errorMessage}`,
|
|
428
|
+
component: this.#cachedChild(
|
|
429
|
+
`message-error:text:${message.errorMessage}`,
|
|
430
|
+
() => new Text(theme.fg("error", `Error: ${message.errorMessage}`), 1, 0),
|
|
431
|
+
),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
288
434
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
435
|
+
// Token usage metadata
|
|
436
|
+
if (settings.get("display.showTokenUsage") && this.#usageInfo) {
|
|
437
|
+
const usage = this.#usageInfo;
|
|
438
|
+
const totalInput = usage.input + usage.cacheWrite;
|
|
439
|
+
const parts: string[] = [];
|
|
440
|
+
parts.push(`${theme.icon.input} ${formatNumber(totalInput)}`);
|
|
441
|
+
parts.push(`${theme.icon.output} ${formatNumber(usage.output)}`);
|
|
442
|
+
if (usage.cacheRead > 0) {
|
|
443
|
+
parts.push(`cache: ${formatNumber(usage.cacheRead)}`);
|
|
444
|
+
}
|
|
445
|
+
const usageText = parts.join(" ");
|
|
446
|
+
descriptors.push({
|
|
447
|
+
key: "usage:spacer",
|
|
448
|
+
component: this.#cachedChild("usage:spacer", () => new Spacer(1)),
|
|
449
|
+
});
|
|
450
|
+
descriptors.push({
|
|
451
|
+
key: `usage:text:${usageText}`,
|
|
452
|
+
component: this.#cachedChild(
|
|
453
|
+
`usage:text:${usageText}`,
|
|
454
|
+
() => new Text(theme.fg("dim", usageText), 1, 0),
|
|
455
|
+
),
|
|
456
|
+
});
|
|
298
457
|
}
|
|
299
|
-
this.#contentContainer.addChild(new Spacer(1));
|
|
300
|
-
this.#contentContainer.addChild(new Text(theme.fg("dim", parts.join(" ")), 1, 0));
|
|
301
458
|
}
|
|
459
|
+
|
|
460
|
+
this.#reconcileChildren(descriptors);
|
|
302
461
|
}
|
|
303
462
|
}
|
|
@@ -16,6 +16,7 @@ import { resolveCurrentBranch } from "./status-line/git-utils";
|
|
|
16
16
|
export class FooterComponent implements Component {
|
|
17
17
|
#cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name
|
|
18
18
|
#gitWatcher: fs.FSWatcher | null = null;
|
|
19
|
+
#gitWatcherGeneration = 0;
|
|
19
20
|
#onBranchChange: (() => void) | null = null;
|
|
20
21
|
#autoCompactEnabled: boolean = true;
|
|
21
22
|
#extensionStatuses: Map<string, string> = new Map();
|
|
@@ -51,7 +52,10 @@ export class FooterComponent implements Component {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
#setupGitWatcher(): void {
|
|
54
|
-
// Clean up existing watcher
|
|
55
|
+
// Clean up existing watcher. The generation guard below keeps a stale
|
|
56
|
+
// in-flight resolve — one that dispose() or a newer setup already
|
|
57
|
+
// superseded — from installing a watcher nothing would ever close.
|
|
58
|
+
const generation = ++this.#gitWatcherGeneration;
|
|
55
59
|
if (this.#gitWatcher) {
|
|
56
60
|
this.#gitWatcher.close();
|
|
57
61
|
this.#gitWatcher = null;
|
|
@@ -60,7 +64,7 @@ export class FooterComponent implements Component {
|
|
|
60
64
|
void git.head
|
|
61
65
|
.resolve(getProjectDir())
|
|
62
66
|
.then(head => {
|
|
63
|
-
if (!head) {
|
|
67
|
+
if (!head || generation !== this.#gitWatcherGeneration) {
|
|
64
68
|
return;
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -84,6 +88,7 @@ export class FooterComponent implements Component {
|
|
|
84
88
|
* Clean up the file watcher
|
|
85
89
|
*/
|
|
86
90
|
dispose(): void {
|
|
91
|
+
this.#gitWatcherGeneration++;
|
|
87
92
|
if (this.#gitWatcher) {
|
|
88
93
|
this.#gitWatcher.close();
|
|
89
94
|
this.#gitWatcher = null;
|
|
@@ -98,6 +98,7 @@ export type ModelSelectorSelection =
|
|
|
98
98
|
kind: "assignment";
|
|
99
99
|
model: Model;
|
|
100
100
|
role: SkcModelAssignmentTargetId | null;
|
|
101
|
+
roles?: readonly SkcModelAssignmentTargetId[];
|
|
101
102
|
thinkingLevel?: ThinkingLevel;
|
|
102
103
|
selector?: string;
|
|
103
104
|
}
|
|
@@ -1374,7 +1375,11 @@ export class ModelSelectorComponent extends Container {
|
|
|
1374
1375
|
for (let i = 0; i < actionCount; i++) {
|
|
1375
1376
|
const prefix = i === this.#selectedActionIndex ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
|
|
1376
1377
|
const role = SKC_MODEL_ASSIGNMENT_TARGET_IDS[i];
|
|
1377
|
-
const label =
|
|
1378
|
+
const label = role
|
|
1379
|
+
? `Set as ${SKC_MODEL_ASSIGNMENT_TARGETS[role].tag ?? role.toUpperCase()} (${SKC_MODEL_ASSIGNMENT_TARGETS[role].name})`
|
|
1380
|
+
: i === SKC_MODEL_ASSIGNMENT_TARGET_IDS.length
|
|
1381
|
+
? "Set for all role agents"
|
|
1382
|
+
: "Set for all targets";
|
|
1378
1383
|
this.#listContainer.addChild(
|
|
1379
1384
|
new Text(`${prefix}${i === this.#selectedActionIndex ? theme.fg("accent", label) : label}`, 0, 0),
|
|
1380
1385
|
);
|
|
@@ -1411,7 +1416,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
1411
1416
|
return this.#roles[role]?.thinkingLevel ?? ThinkingLevel.Inherit;
|
|
1412
1417
|
}
|
|
1413
1418
|
#getActionCount(_model: Model): number {
|
|
1414
|
-
return SKC_MODEL_ASSIGNMENT_TARGET_IDS.length;
|
|
1419
|
+
return SKC_MODEL_ASSIGNMENT_TARGET_IDS.length + 2;
|
|
1415
1420
|
}
|
|
1416
1421
|
|
|
1417
1422
|
#getSelectedItem(): ModelItem | CanonicalModelItem | undefined {
|
|
@@ -1659,7 +1664,15 @@ export class ModelSelectorComponent extends Container {
|
|
|
1659
1664
|
if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
1660
1665
|
this.#pendingActionItem = undefined;
|
|
1661
1666
|
const role = SKC_MODEL_ASSIGNMENT_TARGET_IDS[this.#selectedActionIndex];
|
|
1662
|
-
if (role)
|
|
1667
|
+
if (role) {
|
|
1668
|
+
this.#handleSelect(item, role);
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
const roles =
|
|
1672
|
+
this.#selectedActionIndex === SKC_MODEL_ASSIGNMENT_TARGET_IDS.length
|
|
1673
|
+
? (["executor", "architect", "planner", "critic"] as const)
|
|
1674
|
+
: SKC_MODEL_ASSIGNMENT_TARGET_IDS;
|
|
1675
|
+
this.#handleSelect(item, "default", undefined, roles);
|
|
1663
1676
|
return;
|
|
1664
1677
|
}
|
|
1665
1678
|
if (getKeybindings().matches(keyData, "tui.select.cancel")) {
|
|
@@ -1698,17 +1711,34 @@ export class ModelSelectorComponent extends Container {
|
|
|
1698
1711
|
this.#updateList();
|
|
1699
1712
|
}
|
|
1700
1713
|
}
|
|
1714
|
+
#getInitialThinkingChoiceIndex(item: ModelItem | CanonicalModelItem, levels: ThinkingLevel[]): number {
|
|
1715
|
+
const preferred = this.#getPreferredThinkingLevel(item);
|
|
1716
|
+
if (preferred && preferred !== ThinkingLevel.Inherit) {
|
|
1717
|
+
const index = levels.indexOf(preferred);
|
|
1718
|
+
if (index !== -1) return index;
|
|
1719
|
+
}
|
|
1720
|
+
return 0;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
#getPreferredThinkingLevel(item: ModelItem | CanonicalModelItem): ThinkingLevel | undefined {
|
|
1724
|
+
if (item.thinkingLevel && item.thinkingLevel !== ThinkingLevel.Inherit) {
|
|
1725
|
+
return item.thinkingLevel;
|
|
1726
|
+
}
|
|
1727
|
+
return undefined;
|
|
1728
|
+
}
|
|
1701
1729
|
|
|
1702
1730
|
#handleSelect(
|
|
1703
1731
|
item: ModelItem | CanonicalModelItem,
|
|
1704
1732
|
role: SkcModelAssignmentTargetId | null,
|
|
1705
1733
|
thinkingLevel?: ThinkingLevel,
|
|
1734
|
+
roles?: readonly SkcModelAssignmentTargetId[],
|
|
1706
1735
|
): void {
|
|
1707
1736
|
const itemThinkingLevel = thinkingLevel ?? item.thinkingLevel;
|
|
1708
1737
|
const hasExplicitThinkingChoice = thinkingLevel !== undefined || item.explicitThinkingLevel === true;
|
|
1709
|
-
if (!hasExplicitThinkingChoice && requiresExplicitThinkingChoice(item.model)) {
|
|
1710
|
-
|
|
1711
|
-
this.#
|
|
1738
|
+
if (!hasExplicitThinkingChoice && requiresExplicitThinkingChoice(item.model, role)) {
|
|
1739
|
+
const levels = getSelectableThinkingLevels(item.model);
|
|
1740
|
+
this.#pendingThinkingChoice = { item, role, levels };
|
|
1741
|
+
this.#selectedThinkingIndex = this.#getInitialThinkingChoiceIndex(item, levels);
|
|
1712
1742
|
this.#updateList();
|
|
1713
1743
|
return;
|
|
1714
1744
|
}
|
|
@@ -1726,17 +1756,23 @@ export class ModelSelectorComponent extends Container {
|
|
|
1726
1756
|
}
|
|
1727
1757
|
|
|
1728
1758
|
const selectedThinkingLevel = itemThinkingLevel ?? this.#getCurrentRoleThinkingLevel(role);
|
|
1729
|
-
const selectorValue =
|
|
1730
|
-
|
|
1759
|
+
const selectorValue = roles
|
|
1760
|
+
? formatModelSelectorValue(item.selector, selectedThinkingLevel)
|
|
1761
|
+
: role === "default"
|
|
1762
|
+
? item.selector
|
|
1763
|
+
: formatModelSelectorValue(item.selector, selectedThinkingLevel);
|
|
1731
1764
|
|
|
1732
1765
|
// Update local state for UI
|
|
1733
|
-
|
|
1766
|
+
for (const targetRole of roles ?? [role]) {
|
|
1767
|
+
this.#roles[targetRole] = { model: item.model, thinkingLevel: selectedThinkingLevel };
|
|
1768
|
+
}
|
|
1734
1769
|
|
|
1735
1770
|
// Notify caller (for updating agent state if needed)
|
|
1736
1771
|
this.#onSelectCallback({
|
|
1737
1772
|
kind: "assignment",
|
|
1738
1773
|
model: item.model,
|
|
1739
1774
|
role,
|
|
1775
|
+
roles,
|
|
1740
1776
|
thinkingLevel: selectedThinkingLevel,
|
|
1741
1777
|
selector: selectorValue,
|
|
1742
1778
|
});
|
|
@@ -1751,10 +1787,17 @@ export class ModelSelectorComponent extends Container {
|
|
|
1751
1787
|
async __testSelectProfile(profileName: string, setDefault: boolean): Promise<void> {
|
|
1752
1788
|
await this.#onSelectCallback({ kind: "profile", profileName, setDefault });
|
|
1753
1789
|
}
|
|
1790
|
+
async __testSelectAssignment(
|
|
1791
|
+
selection: Omit<Extract<ModelSelectorSelection, { kind: "assignment" }>, "kind">,
|
|
1792
|
+
): Promise<void> {
|
|
1793
|
+
await this.#onSelectCallback({ kind: "assignment", ...selection });
|
|
1794
|
+
}
|
|
1754
1795
|
}
|
|
1755
1796
|
|
|
1756
|
-
function requiresExplicitThinkingChoice(model: Model): boolean {
|
|
1757
|
-
|
|
1797
|
+
function requiresExplicitThinkingChoice(model: Model, role: SkcModelAssignmentTargetId | null): boolean {
|
|
1798
|
+
if (model.reasoning !== true) return false;
|
|
1799
|
+
if (model.provider === "openai" || model.provider === "openai-codex") return true;
|
|
1800
|
+
return role !== null && SKC_MODEL_ASSIGNMENT_TARGETS[role].settingsPath === "task.agentModelOverrides";
|
|
1758
1801
|
}
|
|
1759
1802
|
|
|
1760
1803
|
function getSelectableThinkingLevels(model: Model): ThinkingLevel[] {
|