@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
|
@@ -669,6 +669,8 @@ export interface SettingsRuntimeContext {
|
|
|
669
669
|
thinkingLevel: ThinkingLevel | undefined;
|
|
670
670
|
/** Available themes */
|
|
671
671
|
availableThemes: string[];
|
|
672
|
+
/** Available model profile names (from the model registry) */
|
|
673
|
+
availableModelProfiles: string[];
|
|
672
674
|
/** Working directory for plugins tab */
|
|
673
675
|
cwd: string;
|
|
674
676
|
}
|
|
@@ -861,6 +863,8 @@ export class SettingsSelectorComponent extends Container {
|
|
|
861
863
|
});
|
|
862
864
|
} else if (def.path === "theme.dark" || def.path === "theme.light") {
|
|
863
865
|
options = this.context.availableThemes.map(t => ({ value: t, label: t }));
|
|
866
|
+
} else if (def.path === "modelProfile.default") {
|
|
867
|
+
options = this.context.availableModelProfiles.map(p => ({ value: p, label: p }));
|
|
864
868
|
}
|
|
865
869
|
if (def.path === "statusLine.preset") {
|
|
866
870
|
options = options.filter(option => option.value !== "custom");
|
|
@@ -175,6 +175,9 @@ export class StatusLineComponent implements Component {
|
|
|
175
175
|
#settings: StatusLineSettings = {};
|
|
176
176
|
#cachedBranch: string | null | undefined = undefined;
|
|
177
177
|
#cachedBranchRepoId: string | null | undefined = undefined;
|
|
178
|
+
#branchProjectDir: string | undefined;
|
|
179
|
+
#branchLastFetch = 0;
|
|
180
|
+
#branchInFlight = false;
|
|
178
181
|
#gitWatcher: fs.FSWatcher | null = null;
|
|
179
182
|
#onBranchChange: (() => void) | null = null;
|
|
180
183
|
#autoCompactEnabled: boolean = true;
|
|
@@ -188,6 +191,14 @@ export class StatusLineComponent implements Component {
|
|
|
188
191
|
#skillHudLastFetch = 0;
|
|
189
192
|
#skillHudInFlight = false;
|
|
190
193
|
#version: string | undefined;
|
|
194
|
+
#resolvedSettingsCache:
|
|
195
|
+
| (Required<Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">> &
|
|
196
|
+
StatusLineSettings)
|
|
197
|
+
| undefined;
|
|
198
|
+
#resolvedSettingsFingerprint: string | undefined;
|
|
199
|
+
#renderedRowsCache: { key: string; rows: string[] } | undefined;
|
|
200
|
+
#renderedRowsCacheHits = 0;
|
|
201
|
+
#renderedRowsCacheMisses = 0;
|
|
191
202
|
|
|
192
203
|
// Git status caching (1s TTL)
|
|
193
204
|
#cachedGitStatus: { staged: number; unstaged: number; untracked: number } | null = null;
|
|
@@ -320,18 +331,39 @@ export class StatusLineComponent implements Component {
|
|
|
320
331
|
#invalidateGitCaches(): void {
|
|
321
332
|
this.#cachedBranch = undefined;
|
|
322
333
|
this.#cachedBranchRepoId = undefined;
|
|
334
|
+
this.#branchProjectDir = undefined;
|
|
323
335
|
this.#cachedPrContext = undefined;
|
|
336
|
+
this.#branchLastFetch = 0;
|
|
337
|
+
this.#branchInFlight = false;
|
|
338
|
+
this.#renderedRowsCache = undefined;
|
|
324
339
|
}
|
|
325
340
|
#getCurrentBranch(): string | null {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
341
|
+
const now = Date.now();
|
|
342
|
+
const projectDir = getProjectDir();
|
|
343
|
+
const withinTtl =
|
|
344
|
+
this.#cachedBranch !== undefined &&
|
|
345
|
+
this.#branchProjectDir === projectDir &&
|
|
346
|
+
Date.now() - this.#branchLastFetch < 1000;
|
|
347
|
+
if (withinTtl || this.#branchInFlight) {
|
|
348
|
+
return this.#cachedBranch ?? null;
|
|
329
349
|
}
|
|
330
350
|
|
|
331
|
-
this.#
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
351
|
+
this.#branchInFlight = true;
|
|
352
|
+
try {
|
|
353
|
+
const current = resolveCurrentBranch(projectDir);
|
|
354
|
+
this.#cachedBranchRepoId = current.repoId;
|
|
355
|
+
this.#cachedBranch = current.branch;
|
|
356
|
+
this.#branchProjectDir = projectDir;
|
|
357
|
+
return this.#cachedBranch ?? null;
|
|
358
|
+
} catch {
|
|
359
|
+
this.#cachedBranchRepoId = null;
|
|
360
|
+
this.#cachedBranch = null;
|
|
361
|
+
this.#branchProjectDir = projectDir;
|
|
362
|
+
return null;
|
|
363
|
+
} finally {
|
|
364
|
+
this.#branchLastFetch = now;
|
|
365
|
+
this.#branchInFlight = false;
|
|
366
|
+
}
|
|
335
367
|
}
|
|
336
368
|
|
|
337
369
|
#isDefaultBranch(branch: string): boolean {
|
|
@@ -467,7 +499,7 @@ export class StatusLineComponent implements Component {
|
|
|
467
499
|
const cwd = typeof getCwd === "function" ? getCwd.call(this.session.sessionManager) : getProjectDir();
|
|
468
500
|
const sessionId = typeof getSessionId === "function" ? getSessionId.call(this.session.sessionManager) : undefined;
|
|
469
501
|
this.#skillHudInFlight = true;
|
|
470
|
-
void readVisibleSkillActiveState(cwd, sessionId)
|
|
502
|
+
void readVisibleSkillActiveState(cwd, sessionId, { tier: "hud" })
|
|
471
503
|
.then(state => {
|
|
472
504
|
this.#skillHudEntries = state?.active_skills ?? [];
|
|
473
505
|
})
|
|
@@ -637,7 +669,13 @@ export class StatusLineComponent implements Component {
|
|
|
637
669
|
return `${modelId}|${sp.length}:${sp[0]?.length ?? 0}|${tools.length}|${skills.length}`;
|
|
638
670
|
}
|
|
639
671
|
|
|
640
|
-
#buildSegmentContext(
|
|
672
|
+
#buildSegmentContext(
|
|
673
|
+
width: number,
|
|
674
|
+
effectiveSettings: Required<
|
|
675
|
+
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
676
|
+
> &
|
|
677
|
+
StatusLineSettings,
|
|
678
|
+
): SegmentContext {
|
|
641
679
|
const state = this.session.state;
|
|
642
680
|
|
|
643
681
|
// Trigger background fetch (5-min TTL); render uses cached value
|
|
@@ -666,7 +704,7 @@ export class StatusLineComponent implements Component {
|
|
|
666
704
|
return {
|
|
667
705
|
session: this.session,
|
|
668
706
|
width,
|
|
669
|
-
options:
|
|
707
|
+
options: effectiveSettings.segmentOptions ?? {},
|
|
670
708
|
planMode: this.#planModeStatus,
|
|
671
709
|
goalMode: this.#goalModeStatus,
|
|
672
710
|
usageStats,
|
|
@@ -685,10 +723,19 @@ export class StatusLineComponent implements Component {
|
|
|
685
723
|
};
|
|
686
724
|
}
|
|
687
725
|
|
|
726
|
+
#settingsFingerprint(): string {
|
|
727
|
+
return JSON.stringify(this.#settings);
|
|
728
|
+
}
|
|
729
|
+
|
|
688
730
|
#resolveSettings(): Required<
|
|
689
731
|
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
690
732
|
> &
|
|
691
733
|
StatusLineSettings {
|
|
734
|
+
const fingerprint = this.#settingsFingerprint();
|
|
735
|
+
if (this.#resolvedSettingsCache && this.#resolvedSettingsFingerprint === fingerprint) {
|
|
736
|
+
return this.#resolvedSettingsCache;
|
|
737
|
+
}
|
|
738
|
+
|
|
692
739
|
const preset = this.#settings.preset ?? "default";
|
|
693
740
|
const presetDef = getPreset(preset);
|
|
694
741
|
const useCustomSegments = preset === "custom";
|
|
@@ -713,13 +760,15 @@ export class StatusLineComponent implements Component {
|
|
|
713
760
|
? (this.#settings.rightSegments ?? presetDef.rightSegments)
|
|
714
761
|
: presetDef.rightSegments;
|
|
715
762
|
|
|
716
|
-
|
|
763
|
+
this.#resolvedSettingsFingerprint = fingerprint;
|
|
764
|
+
this.#resolvedSettingsCache = {
|
|
717
765
|
...this.#settings,
|
|
718
766
|
leftSegments,
|
|
719
767
|
rightSegments,
|
|
720
768
|
separator: this.#settings.separator ?? presetDef.separator,
|
|
721
769
|
segmentOptions: mergedSegmentOptions,
|
|
722
770
|
};
|
|
771
|
+
return this.#resolvedSettingsCache;
|
|
723
772
|
}
|
|
724
773
|
|
|
725
774
|
#groupWidth(parts: string[], capWidth: number, sepWidth: number): number {
|
|
@@ -785,9 +834,14 @@ export class StatusLineComponent implements Component {
|
|
|
785
834
|
return reRendered.content;
|
|
786
835
|
}
|
|
787
836
|
|
|
788
|
-
#collectStatusSegments(
|
|
789
|
-
|
|
790
|
-
|
|
837
|
+
#collectStatusSegments(
|
|
838
|
+
width: number,
|
|
839
|
+
effectiveSettings: Required<
|
|
840
|
+
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
841
|
+
> &
|
|
842
|
+
StatusLineSettings,
|
|
843
|
+
): CollectedStatusSegments {
|
|
844
|
+
const ctx = this.#buildSegmentContext(width, effectiveSettings);
|
|
791
845
|
const separatorDef = getSeparator(effectiveSettings.separator ?? "powerline-thin", theme);
|
|
792
846
|
|
|
793
847
|
// Use the subtle surface tone (the same elevated background as user-message
|
|
@@ -856,7 +910,7 @@ export class StatusLineComponent implements Component {
|
|
|
856
910
|
}
|
|
857
911
|
|
|
858
912
|
#buildStatusLine(width: number, precollected?: CollectedStatusSegments): string {
|
|
859
|
-
const seg = precollected ?? this.#collectStatusSegments(width);
|
|
913
|
+
const seg = precollected ?? this.#collectStatusSegments(width, this.#resolveSettings());
|
|
860
914
|
const { ctx, separatorDef, bgAnsi, fgAnsi, sepAnsi, previewHighlightSegment } = seg;
|
|
861
915
|
const { leftSepWidth, rightSepWidth, leftCapWidth, rightCapWidth } = seg;
|
|
862
916
|
const left = [...seg.left];
|
|
@@ -915,8 +969,52 @@ export class StatusLineComponent implements Component {
|
|
|
915
969
|
* everything fits on one line.
|
|
916
970
|
*/
|
|
917
971
|
#buildStatusRows(width: number, maxRows: number): string[] {
|
|
918
|
-
const
|
|
919
|
-
|
|
972
|
+
const effectiveSettings = this.#resolveSettings();
|
|
973
|
+
const seg = this.#collectStatusSegments(width, effectiveSettings);
|
|
974
|
+
const cacheKey = JSON.stringify({
|
|
975
|
+
width,
|
|
976
|
+
maxRows,
|
|
977
|
+
settings: this.#resolvedSettingsFingerprint,
|
|
978
|
+
left: seg.left,
|
|
979
|
+
leftSegIds: seg.leftSegIds,
|
|
980
|
+
right: seg.right,
|
|
981
|
+
separator: effectiveSettings.separator,
|
|
982
|
+
previewHighlightSegment: seg.previewHighlightSegment,
|
|
983
|
+
sessionAccent: seg.sessionAccent,
|
|
984
|
+
theme: [seg.bgAnsi, seg.fgAnsi, seg.sepAnsi],
|
|
985
|
+
rowLayout: {
|
|
986
|
+
separatorLeft: seg.separatorDef.left,
|
|
987
|
+
separatorRight: seg.separatorDef.right,
|
|
988
|
+
separatorEndCapLeft: seg.separatorDef.endCaps?.left,
|
|
989
|
+
separatorEndCapRight: seg.separatorDef.endCaps?.right,
|
|
990
|
+
separatorEndCapUseBgAsFg: seg.separatorDef.endCaps?.useBgAsFg,
|
|
991
|
+
borderFgAnsi: theme.getFgAnsi("border"),
|
|
992
|
+
boxRoundHorizontal: theme.boxRound.horizontal,
|
|
993
|
+
},
|
|
994
|
+
context: [seg.ctx.contextPercent, seg.ctx.contextWindow],
|
|
995
|
+
usageStats: seg.ctx.usageStats,
|
|
996
|
+
usage: seg.ctx.usage,
|
|
997
|
+
git: seg.ctx.git,
|
|
998
|
+
modes: [seg.ctx.planMode, seg.ctx.goalMode, seg.ctx.autoCompactEnabled],
|
|
999
|
+
runtime: [seg.ctx.subagentCount, seg.ctx.jobs, seg.ctx.sessionStartTime],
|
|
1000
|
+
sessionName: seg.sessionAccent !== false ? this.session.sessionManager?.getSessionName() : undefined,
|
|
1001
|
+
asyncJobs: this.session
|
|
1002
|
+
.getAsyncJobSnapshot()
|
|
1003
|
+
?.running.filter(job => job.metadata?.monitor !== true)
|
|
1004
|
+
.map(job => job.id ?? job.metadata ?? job)
|
|
1005
|
+
.join(","),
|
|
1006
|
+
version: this.#version,
|
|
1007
|
+
});
|
|
1008
|
+
if (this.#renderedRowsCache?.key === cacheKey) {
|
|
1009
|
+
this.#renderedRowsCacheHits++;
|
|
1010
|
+
return [...this.#renderedRowsCache.rows];
|
|
1011
|
+
}
|
|
1012
|
+
this.#renderedRowsCacheMisses++;
|
|
1013
|
+
|
|
1014
|
+
if (seg.left.length === 0 && seg.right.length === 0) {
|
|
1015
|
+
this.#renderedRowsCache = { key: cacheKey, rows: [] };
|
|
1016
|
+
return [];
|
|
1017
|
+
}
|
|
920
1018
|
|
|
921
1019
|
const topFillWidth = Math.max(1, width);
|
|
922
1020
|
const leftWidth = this.#groupWidth(seg.left, seg.leftCapWidth, seg.leftSepWidth);
|
|
@@ -924,51 +1022,52 @@ export class StatusLineComponent implements Component {
|
|
|
924
1022
|
const gap = seg.left.length > 0 && seg.right.length > 0 ? 1 : 0;
|
|
925
1023
|
const fitsSingleRow = leftWidth + rightWidth + gap <= topFillWidth;
|
|
926
1024
|
|
|
1025
|
+
let rows: string[];
|
|
927
1026
|
if (maxRows <= 1 || fitsSingleRow) {
|
|
928
1027
|
const single = this.#buildStatusLine(width, seg);
|
|
929
|
-
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
const shrunk = this.#shrinkPathToWidth(item.content, seg.ctx, alone - topFillWidth);
|
|
944
|
-
if (shrunk !== null) item.content = shrunk;
|
|
1028
|
+
rows = single ? [single] : [];
|
|
1029
|
+
} else {
|
|
1030
|
+
const items: { content: string; isPath: boolean }[] = [
|
|
1031
|
+
...seg.left.map((content, i) => ({ content, isPath: seg.leftSegIds[i] === "path" })),
|
|
1032
|
+
...seg.right.map(content => ({ content, isPath: false })),
|
|
1033
|
+
];
|
|
1034
|
+
|
|
1035
|
+
for (const item of items) {
|
|
1036
|
+
if (!item.isPath) continue;
|
|
1037
|
+
const alone = this.#groupWidth([item.content], seg.leftCapWidth, seg.leftSepWidth);
|
|
1038
|
+
if (alone > topFillWidth) {
|
|
1039
|
+
const shrunk = this.#shrinkPathToWidth(item.content, seg.ctx, alone - topFillWidth);
|
|
1040
|
+
if (shrunk !== null) item.content = shrunk;
|
|
1041
|
+
}
|
|
945
1042
|
}
|
|
946
|
-
}
|
|
947
1043
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1044
|
+
const packedRows: string[][] = [];
|
|
1045
|
+
let current: string[] = [];
|
|
1046
|
+
for (const item of items) {
|
|
1047
|
+
if (current.length === 0) {
|
|
1048
|
+
current.push(item.content);
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
const tentative = [...current, item.content];
|
|
1052
|
+
if (this.#groupWidth(tentative, seg.leftCapWidth, seg.leftSepWidth) <= topFillWidth) {
|
|
1053
|
+
current = tentative;
|
|
1054
|
+
} else {
|
|
1055
|
+
packedRows.push(current);
|
|
1056
|
+
current = [item.content];
|
|
1057
|
+
if (packedRows.length >= maxRows) break;
|
|
1058
|
+
}
|
|
955
1059
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
current = tentative;
|
|
959
|
-
} else {
|
|
960
|
-
rows.push(current);
|
|
961
|
-
current = [item.content];
|
|
962
|
-
if (rows.length >= maxRows) break;
|
|
1060
|
+
if (packedRows.length < maxRows && current.length > 0) {
|
|
1061
|
+
packedRows.push(current);
|
|
963
1062
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1063
|
+
|
|
1064
|
+
rows = packedRows.map(row =>
|
|
1065
|
+
this.#renderStatusGroup(row, "left", seg.separatorDef, seg.bgAnsi, seg.fgAnsi, seg.sepAnsi),
|
|
1066
|
+
);
|
|
967
1067
|
}
|
|
968
1068
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
);
|
|
1069
|
+
this.#renderedRowsCache = { key: cacheKey, rows };
|
|
1070
|
+
return [...rows];
|
|
972
1071
|
}
|
|
973
1072
|
|
|
974
1073
|
getTopBorder(width: number): { content: string; width: number } {
|
|
@@ -989,6 +1088,20 @@ export class StatusLineComponent implements Component {
|
|
|
989
1088
|
return this.#buildStatusRows(width, this.#resolveMaxRows()).join("\n");
|
|
990
1089
|
}
|
|
991
1090
|
|
|
1091
|
+
getCacheStatsForTest(): { rowHits: number; rowMisses: number } {
|
|
1092
|
+
return { rowHits: this.#renderedRowsCacheHits, rowMisses: this.#renderedRowsCacheMisses };
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
invalidateBranchForTest(): void {
|
|
1096
|
+
this.#invalidateGitCaches();
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
setCachedPrForTest(pr: { number: number; url: string } | null): void {
|
|
1100
|
+
const branch = this.#getCurrentBranch();
|
|
1101
|
+
this.#cachedPr = pr;
|
|
1102
|
+
this.#cachedPrContext = branch ? createPrCacheContext(branch, this.#cachedBranchRepoId ?? null) : undefined;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
992
1105
|
render(width: number): string[] {
|
|
993
1106
|
const lines: string[] = [];
|
|
994
1107
|
this.#refreshSkillHudInBackground();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentTool } from "@sayknow-cli/agent-core";
|
|
2
2
|
import {
|
|
3
|
+
type AnimationRegistration,
|
|
3
4
|
Box,
|
|
4
5
|
type Component,
|
|
5
6
|
Container,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
Image,
|
|
8
9
|
ImageProtocol,
|
|
9
10
|
imageFallback,
|
|
11
|
+
registerAnimationCallback,
|
|
10
12
|
Spacer,
|
|
11
13
|
TERMINAL,
|
|
12
14
|
Text,
|
|
@@ -51,6 +53,21 @@ function cloneToolArgs<T>(args: T): T {
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
// Built-in tool renderers that treat call args as read-only, so streaming UI can
|
|
57
|
+
// avoid per-delta defensive clone churn. Kept here (not in the tool renderer
|
|
58
|
+
// registry) because it is a rendering-perf capability of these renderers, not a
|
|
59
|
+
// tool-registration concern.
|
|
60
|
+
const READONLY_ARG_RENDERER_TOOLS = new Set(["bash", "recipe", "eval", "edit", "apply_patch"]);
|
|
61
|
+
|
|
62
|
+
function argsCanBeSharedWithRenderer(toolName: string, tool: AgentTool | undefined): boolean {
|
|
63
|
+
return !tool?.renderCall && !tool?.renderResult && READONLY_ARG_RENDERER_TOOLS.has(toolName);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function partialJsonLength(args: unknown): number {
|
|
67
|
+
const value = args && typeof args === "object" ? (args as { __partialJson?: unknown }).__partialJson : undefined;
|
|
68
|
+
return typeof value === "string" ? value.length : -1;
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
/**
|
|
55
72
|
* Drop trailing removal/hunk-header lines that appear in a streaming diff
|
|
56
73
|
* before the matching `+added` lines have arrived. Without this, a partial
|
|
@@ -158,11 +175,14 @@ export class ToolExecutionComponent extends Container {
|
|
|
158
175
|
#editDiffPreview?: PerFileDiffPreview[];
|
|
159
176
|
#editDiffAbort?: AbortController;
|
|
160
177
|
#editDiffLastArgsKey?: string;
|
|
178
|
+
#argsIdentityVersion = 0;
|
|
179
|
+
#lastArgsReference: any;
|
|
180
|
+
#shareArgsWithRenderer = false;
|
|
161
181
|
// Cached converted images for Kitty protocol (which requires PNG), keyed by index
|
|
162
182
|
#convertedImages: Map<number, { data: string; mimeType: string }> = new Map();
|
|
163
183
|
// Spinner animation for partial task results
|
|
164
184
|
#spinnerFrame?: number;
|
|
165
|
-
#
|
|
185
|
+
#spinnerAnimation?: AnimationRegistration;
|
|
166
186
|
// Track if args are still being streamed (for edit/write spinner)
|
|
167
187
|
#argsComplete = false;
|
|
168
188
|
#renderState: {
|
|
@@ -194,7 +214,9 @@ export class ToolExecutionComponent extends Container {
|
|
|
194
214
|
this.#tool = tool;
|
|
195
215
|
this.#ui = ui;
|
|
196
216
|
this.#cwd = cwd;
|
|
197
|
-
this.#
|
|
217
|
+
this.#shareArgsWithRenderer = argsCanBeSharedWithRenderer(toolName, tool);
|
|
218
|
+
this.#lastArgsReference = args;
|
|
219
|
+
this.#args = this.#shareArgsWithRenderer ? args : cloneToolArgs(args);
|
|
198
220
|
|
|
199
221
|
this.addChild(new Spacer(1));
|
|
200
222
|
|
|
@@ -220,7 +242,11 @@ export class ToolExecutionComponent extends Container {
|
|
|
220
242
|
}
|
|
221
243
|
|
|
222
244
|
updateArgs(args: any, _toolCallId?: string): void {
|
|
223
|
-
|
|
245
|
+
if (args !== this.#lastArgsReference) {
|
|
246
|
+
this.#lastArgsReference = args;
|
|
247
|
+
this.#argsIdentityVersion += 1;
|
|
248
|
+
}
|
|
249
|
+
this.#args = this.#shareArgsWithRenderer ? args : cloneToolArgs(args);
|
|
224
250
|
this.#updateSpinnerAnimation();
|
|
225
251
|
void this.#runPreviewDiff();
|
|
226
252
|
this.#updateDisplay();
|
|
@@ -253,13 +279,13 @@ export class ToolExecutionComponent extends Container {
|
|
|
253
279
|
effectiveArgs = args;
|
|
254
280
|
}
|
|
255
281
|
|
|
256
|
-
// Coalesce duplicate computes
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
282
|
+
// Coalesce duplicate computes without serializing multi-KB streamed args every delta.
|
|
283
|
+
const argsKey = [
|
|
284
|
+
this.#toolName,
|
|
285
|
+
this.#argsIdentityVersion,
|
|
286
|
+
partialJsonLength(args),
|
|
287
|
+
this.#argsComplete ? 1 : 0,
|
|
288
|
+
].join(":");
|
|
263
289
|
if (argsKey === this.#editDiffLastArgsKey) return;
|
|
264
290
|
this.#editDiffLastArgsKey = argsKey;
|
|
265
291
|
|
|
@@ -367,18 +393,17 @@ export class ToolExecutionComponent extends Container {
|
|
|
367
393
|
(this.#result?.details as { async?: { state?: string } } | undefined)?.async?.state === "running";
|
|
368
394
|
const isPartialTask = this.#isPartial && this.#toolName === "task" && !isBackgroundAsyncTask;
|
|
369
395
|
const needsSpinner = isStreamingArgs || isPartialTask;
|
|
370
|
-
if (needsSpinner && !this.#
|
|
371
|
-
this.#
|
|
396
|
+
if (needsSpinner && !this.#spinnerAnimation) {
|
|
397
|
+
this.#spinnerAnimation = registerAnimationCallback(() => {
|
|
372
398
|
const frameCount = theme.spinnerFrames.length;
|
|
373
399
|
if (frameCount === 0) return;
|
|
374
400
|
this.#spinnerFrame = ((this.#spinnerFrame ?? -1) + 1) % frameCount;
|
|
375
401
|
this.#renderState.spinnerFrame = this.#spinnerFrame;
|
|
376
402
|
this.#ui.requestRender();
|
|
377
403
|
}, 80);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
this.#spinnerInterval = undefined;
|
|
404
|
+
} else if (!needsSpinner && this.#spinnerAnimation) {
|
|
405
|
+
this.#spinnerAnimation.unregister();
|
|
406
|
+
this.#spinnerAnimation = undefined;
|
|
382
407
|
}
|
|
383
408
|
}
|
|
384
409
|
|
|
@@ -386,9 +411,9 @@ export class ToolExecutionComponent extends Container {
|
|
|
386
411
|
* Stop spinner animation and cleanup resources.
|
|
387
412
|
*/
|
|
388
413
|
stopAnimation(): void {
|
|
389
|
-
if (this.#
|
|
390
|
-
|
|
391
|
-
this.#
|
|
414
|
+
if (this.#spinnerAnimation) {
|
|
415
|
+
this.#spinnerAnimation.unregister();
|
|
416
|
+
this.#spinnerAnimation = undefined;
|
|
392
417
|
this.#spinnerFrame = undefined;
|
|
393
418
|
}
|
|
394
419
|
this.#editDiffAbort?.abort();
|