@deepseek-harness-tui/dsh-tui 0.8.4 → 0.8.6
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/lib/types/activityPrefs.d.ts +10 -0
- package/lib/types/activityPrefs.d.ts.map +1 -1
- package/lib/types/activityPrefs.js +17 -0
- package/lib/types/cc/figures.d.ts +7 -0
- package/lib/types/cc/figures.d.ts.map +1 -1
- package/lib/types/cc/figures.js +23 -0
- package/lib/types/cc/markdown.d.ts.map +1 -1
- package/lib/types/cc/markdown.js +47 -14
- package/lib/types/cc/syntaxTheme.d.ts +12 -0
- package/lib/types/cc/syntaxTheme.d.ts.map +1 -0
- package/lib/types/cc/syntaxTheme.js +76 -0
- package/lib/types/components/EffortChargeGlyph.d.ts +26 -0
- package/lib/types/components/EffortChargeGlyph.d.ts.map +1 -0
- package/lib/types/components/EffortChargeGlyph.js +70 -0
- package/lib/types/components/EffortInputBorder.d.ts +36 -0
- package/lib/types/components/EffortInputBorder.d.ts.map +1 -0
- package/lib/types/components/EffortInputBorder.js +73 -0
- package/lib/types/components/EffortTierBadge.d.ts +26 -0
- package/lib/types/components/EffortTierBadge.d.ts.map +1 -0
- package/lib/types/components/EffortTierBadge.js +97 -0
- package/lib/types/components/LoadedContextPanel.js +1 -1
- package/lib/types/components/MessageList.d.ts +4 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +9 -11
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +8 -3
- package/lib/types/components/SplitDiffView.d.ts +4 -5
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +18 -65
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +10 -2
- package/lib/types/components/activityFrames.d.ts +7 -28
- package/lib/types/components/activityFrames.d.ts.map +1 -1
- package/lib/types/components/activityFrames.js +7 -94
- package/lib/types/components/design-system/ThemedBox.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedBox.js +4 -1
- package/lib/types/components/design-system/ThemedText.js +2 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +12 -6
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +25 -9
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +4 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +45 -17
- package/lib/types/components/messages/UserPromptMessage.d.ts +4 -6
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +5 -9
- package/lib/types/dsh-adapter/channel.d.ts +22 -0
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +204 -19
- package/lib/types/dsh-adapter/index.d.ts +5 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +19 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +176 -4
- package/lib/types/dsh-adapter/settings-sections.d.ts +13 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +31 -6
- package/lib/types/ink/ink.d.ts +9 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +24 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +35 -6
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +109 -50
- package/lib/types/screens/StatusLine.d.ts +8 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +62 -39
- package/lib/types/settings-sections.d.ts +1 -1
- package/lib/types/settings-sections.d.ts.map +1 -1
- package/lib/types/theme.d.ts +12 -0
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +51 -21
- package/lib/types/tips.js +2 -2
- package/lib/types/trajectory/effortIgnition.d.ts +66 -0
- package/lib/types/trajectory/effortIgnition.d.ts.map +1 -0
- package/lib/types/trajectory/effortIgnition.js +142 -0
- package/lib/types/tuiDisplayPrefs.d.ts +47 -0
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -0
- package/lib/types/tuiDisplayPrefs.js +60 -0
- package/package.json +6 -3
|
@@ -24,7 +24,10 @@ import { homeDir, LEGACY_DATA_DIR } from '../utils/paths.js';
|
|
|
24
24
|
import { extractMentions } from '../utils/mentions.js';
|
|
25
25
|
import { t } from '../i18n.js';
|
|
26
26
|
import { modeDisplayName, resolveSessionModes } from '../sessionModes.js';
|
|
27
|
+
import { normalizeStatusBar, normalizeToolBackground } from '../tuiDisplayPrefs.js';
|
|
27
28
|
import { ActivityTracker } from 'dsh-working-activity/status';
|
|
29
|
+
import { featureOn } from 'dsh-working-activity/config';
|
|
30
|
+
import { readActivityConfig } from '../activityPrefs.js';
|
|
28
31
|
import { attachSessionToWorkspace } from './workspace.js';
|
|
29
32
|
import { createLocalWorkspaceRuntime, getHostWorkspaceRuntime } from './workspaces.js';
|
|
30
33
|
import { getHostCommandTrees } from './command-trees.js';
|
|
@@ -700,6 +703,7 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
700
703
|
return;
|
|
701
704
|
try {
|
|
702
705
|
const info = await llmRuntime.resolveModelInfo(state.provider, state.model);
|
|
706
|
+
state.effortLevels = (info.reasoning?.efforts ?? []).map(level => level.id);
|
|
703
707
|
if (!info.reasoning?.efforts.some(effort => effort.id === preferredEffort))
|
|
704
708
|
return;
|
|
705
709
|
selection.current = {
|
|
@@ -713,6 +717,31 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
713
717
|
// provider default in effect.
|
|
714
718
|
}
|
|
715
719
|
};
|
|
720
|
+
/** Best-effort refresh of the live route's effort-level table for
|
|
721
|
+
* top-tier-triggered UI (effort ignition): fire-and-forget on route
|
|
722
|
+
* changes (bind/model switch/resume); the /effort paths refresh it
|
|
723
|
+
* authoritatively via resolveEfforts. */
|
|
724
|
+
let effortLevelsGeneration = 0;
|
|
725
|
+
const refreshEffortLevels = () => {
|
|
726
|
+
if (llmRuntime === undefined || typeof llmRuntime.resolveModelInfo !== 'function')
|
|
727
|
+
return;
|
|
728
|
+
// 代际保护:快速连续切路由时并发的 resolveModelInfo 可能乱序返回,
|
|
729
|
+
// 只有最新一代的解析才允许落表;落表后 emit 让 useSyncExternalStore
|
|
730
|
+
// 消费者立刻可见(否则要等下一次无关 emit)。
|
|
731
|
+
const generation = ++effortLevelsGeneration;
|
|
732
|
+
void llmRuntime
|
|
733
|
+
.resolveModelInfo(state.provider, state.model)
|
|
734
|
+
.then(info => {
|
|
735
|
+
if (generation !== effortLevelsGeneration)
|
|
736
|
+
return;
|
|
737
|
+
state.effortLevels = (info.reasoning?.efforts ?? []).map(level => level.id);
|
|
738
|
+
state.emit();
|
|
739
|
+
})
|
|
740
|
+
.catch(() => {
|
|
741
|
+
// Route metadata resolution is best-effort; a failure keeps the
|
|
742
|
+
// previous table until the next /effort interaction clears it.
|
|
743
|
+
});
|
|
744
|
+
};
|
|
716
745
|
/** Resolve the live route's effort levels + adapter default through the
|
|
717
746
|
* llm runtime; 'unavailable' when the service is unmounted, 'error' when
|
|
718
747
|
* resolution throws (notified here). */
|
|
@@ -721,6 +750,7 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
721
750
|
return 'unavailable';
|
|
722
751
|
try {
|
|
723
752
|
const info = await llmRuntime.resolveModelInfo(state.provider, state.model);
|
|
753
|
+
state.effortLevels = (info.reasoning?.efforts ?? []).map(level => level.id);
|
|
724
754
|
return {
|
|
725
755
|
efforts: info.reasoning?.efforts ?? [],
|
|
726
756
|
defaultEffort: info.reasoning?.defaultEffort,
|
|
@@ -938,6 +968,7 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
938
968
|
await applyMode(sessionModes[(index + 1) % sessionModes.length]);
|
|
939
969
|
};
|
|
940
970
|
const state = {
|
|
971
|
+
effortLevels: undefined,
|
|
941
972
|
version: 0,
|
|
942
973
|
rows: [],
|
|
943
974
|
status: 'starting',
|
|
@@ -968,6 +999,8 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
968
999
|
activityFrames: options.activityFrames,
|
|
969
1000
|
diffLayout: options.diffLayout ?? 'auto',
|
|
970
1001
|
thinkingFold: options.thinkingFold ?? 'preview',
|
|
1002
|
+
toolBackground: normalizeToolBackground(options.toolBackground),
|
|
1003
|
+
statusBar: normalizeStatusBar(options.statusBar),
|
|
971
1004
|
activityEnabled: options.activity !== false,
|
|
972
1005
|
contextBarEnabled: options.contextBar !== false,
|
|
973
1006
|
agentPreset: options.agentPreset,
|
|
@@ -1505,6 +1538,11 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1505
1538
|
state.lastUsage = undefined;
|
|
1506
1539
|
state.workingActivity = undefined;
|
|
1507
1540
|
state.contextWindow = undefined;
|
|
1541
|
+
// Route changed: a stale tier table would let top-tier UI fire on the
|
|
1542
|
+
// wrong level (or never fire on the real one); clear and re-resolve.
|
|
1543
|
+
state.effortLevels = undefined;
|
|
1544
|
+
state.reasoningEffort = undefined;
|
|
1545
|
+
refreshEffortLevels();
|
|
1508
1546
|
state.contextSegments = {
|
|
1509
1547
|
system: 0,
|
|
1510
1548
|
prompt: 0,
|
|
@@ -1613,10 +1651,16 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1613
1651
|
streaming = undefined;
|
|
1614
1652
|
reasoning = undefined;
|
|
1615
1653
|
// Stale sealed/thinking bookkeeping belongs to the OLD agent's rows;
|
|
1616
|
-
// keep it out of the next turn's settle logs and revive cache.
|
|
1654
|
+
// keep it out of the next turn's settle logs and revive cache. Event
|
|
1655
|
+
// sequence numbers restart in the fresh session, so its dedupe ledgers
|
|
1656
|
+
// must not retain the old session's sequence ids.
|
|
1617
1657
|
sealedReasoning.length = 0;
|
|
1618
1658
|
lastReasoningRow = undefined;
|
|
1619
1659
|
toolCards.clear();
|
|
1660
|
+
handledAssistantMessages.clear();
|
|
1661
|
+
handledAssistantChunks.clear();
|
|
1662
|
+
assistantRowsByStep.clear();
|
|
1663
|
+
lastTextDelta.clear();
|
|
1620
1664
|
nextRowId = 0;
|
|
1621
1665
|
state.rows.length = 0;
|
|
1622
1666
|
// Goal/todo/title are session-scoped; the replay re-derives them for
|
|
@@ -1644,7 +1688,13 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1644
1688
|
state.tpsSamples = [];
|
|
1645
1689
|
state.lastUsage = undefined;
|
|
1646
1690
|
state.workingActivity = undefined;
|
|
1691
|
+
state.loadedContext = undefined;
|
|
1647
1692
|
state.contextWindow = undefined;
|
|
1693
|
+
// Route changed: a stale tier table would let top-tier UI fire on the
|
|
1694
|
+
// wrong level (or never fire on the real one); clear and re-resolve.
|
|
1695
|
+
state.effortLevels = undefined;
|
|
1696
|
+
state.reasoningEffort = undefined;
|
|
1697
|
+
refreshEffortLevels();
|
|
1648
1698
|
state.contextSegments = {
|
|
1649
1699
|
system: 0,
|
|
1650
1700
|
prompt: 0,
|
|
@@ -1822,6 +1872,11 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1822
1872
|
state.lastUsage = undefined;
|
|
1823
1873
|
state.workingActivity = undefined;
|
|
1824
1874
|
state.contextWindow = undefined;
|
|
1875
|
+
// Route changed: a stale tier table would let top-tier UI fire on the
|
|
1876
|
+
// wrong level (or never fire on the real one); clear and re-resolve.
|
|
1877
|
+
state.effortLevels = undefined;
|
|
1878
|
+
state.reasoningEffort = undefined;
|
|
1879
|
+
refreshEffortLevels();
|
|
1825
1880
|
state.contextSegments = {
|
|
1826
1881
|
system: 0,
|
|
1827
1882
|
prompt: 0,
|
|
@@ -1837,6 +1892,8 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1837
1892
|
agent = handle.agent;
|
|
1838
1893
|
currentHandle = handle;
|
|
1839
1894
|
bindAgent();
|
|
1895
|
+
// Model-switch quip rides the fresh tracker (pi parity).
|
|
1896
|
+
activityTracker.onModelSwitch(model);
|
|
1840
1897
|
refreshCommandList();
|
|
1841
1898
|
void refreshLoadedContext();
|
|
1842
1899
|
void refreshSkillCommands();
|
|
@@ -1913,6 +1970,21 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1913
1970
|
state.thinkingFold = mode;
|
|
1914
1971
|
state.emit();
|
|
1915
1972
|
},
|
|
1973
|
+
setToolBackground(background) {
|
|
1974
|
+
const normalized = normalizeToolBackground(background);
|
|
1975
|
+
if (normalized === state.toolBackground)
|
|
1976
|
+
return;
|
|
1977
|
+
state.toolBackground = normalized;
|
|
1978
|
+
state.emit();
|
|
1979
|
+
},
|
|
1980
|
+
setStatusBar(config) {
|
|
1981
|
+
const next = normalizeStatusBar({ ...state.statusBar, ...config });
|
|
1982
|
+
const changed = Object.keys(next).some(key => next[key] !== state.statusBar[key]);
|
|
1983
|
+
if (!changed)
|
|
1984
|
+
return;
|
|
1985
|
+
state.statusBar = next;
|
|
1986
|
+
state.emit();
|
|
1987
|
+
},
|
|
1916
1988
|
setActivityFrames(name) {
|
|
1917
1989
|
if (!isPresetName(name)) {
|
|
1918
1990
|
state.notify(t('unknown-activity-preset', { name }), { color: 'error' });
|
|
@@ -2318,6 +2390,9 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
2318
2390
|
.compactNow(agent, signal)
|
|
2319
2391
|
.then((result) => {
|
|
2320
2392
|
state.notify(result ? t('compact-done') : t('compact-nothing'));
|
|
2393
|
+
// Compaction quip rides the next thinking rotation (pi parity).
|
|
2394
|
+
if (result)
|
|
2395
|
+
activityTracker.onCompact('done');
|
|
2321
2396
|
})
|
|
2322
2397
|
.catch((error) => {
|
|
2323
2398
|
state.notify(t('compact-failed', { err: error instanceof Error ? error.message : String(error) }), { color: 'error', timeoutMs: 8000 });
|
|
@@ -2983,6 +3058,39 @@ ${output}
|
|
|
2983
3058
|
let tpsStep;
|
|
2984
3059
|
/** Tool cards by callId, so tool/result can settle the running card. */
|
|
2985
3060
|
const toolCards = new Map();
|
|
3061
|
+
/**
|
|
3062
|
+
* Session events are delivered live and can also be replayed around a
|
|
3063
|
+
* reconnect. A repeated sealed message must not create a second assistant
|
|
3064
|
+
* row for the same durable sequence number.
|
|
3065
|
+
*/
|
|
3066
|
+
const handledAssistantMessages = new Set();
|
|
3067
|
+
const handledAssistantChunks = new Set();
|
|
3068
|
+
const assistantRowsByStep = new Map();
|
|
3069
|
+
const lastTextDelta = new Map();
|
|
3070
|
+
const stepKey = (turn, step) => `${turn}:${step}`;
|
|
3071
|
+
/** Append a stream delta idempotently. Providers normally send a pure
|
|
3072
|
+
* delta, but reconnect/proxy paths can resend a cumulative prefix or a
|
|
3073
|
+
* delta whose beginning overlaps the previous tail. Merge the overlap
|
|
3074
|
+
* instead of blindly concatenating it into the visible transcript. */
|
|
3075
|
+
const appendTextDelta = (row, delta) => {
|
|
3076
|
+
if (delta === '')
|
|
3077
|
+
return;
|
|
3078
|
+
if (lastTextDelta.get(row) === delta)
|
|
3079
|
+
return;
|
|
3080
|
+
lastTextDelta.set(row, delta);
|
|
3081
|
+
if (delta.startsWith(row.text)) {
|
|
3082
|
+
row.text = delta;
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
const maxOverlap = Math.min(row.text.length, delta.length, 4096);
|
|
3086
|
+
for (let size = maxOverlap; size > 0; size--) {
|
|
3087
|
+
if (row.text.endsWith(delta.slice(0, size))) {
|
|
3088
|
+
row.text += delta.slice(size);
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
row.text += delta;
|
|
3093
|
+
};
|
|
2986
3094
|
/** The host-plane tools registry (dsh-tools). Resolved once; absent in
|
|
2987
3095
|
* bare embedders — every presenter call soft-fails to undefined and the
|
|
2988
3096
|
* card falls back to raw text. */
|
|
@@ -3034,11 +3142,22 @@ ${output}
|
|
|
3034
3142
|
*/
|
|
3035
3143
|
const firstTextOf = (content) => (content ?? []).find(block => block.type === 'text')?.text.trim() ?? '';
|
|
3036
3144
|
const ensureStreaming = (seq) => {
|
|
3037
|
-
if (streaming
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3145
|
+
if (streaming !== undefined)
|
|
3146
|
+
return streaming;
|
|
3147
|
+
// A reconnect can replay the first delta after the sealed message was
|
|
3148
|
+
// already observed. Reuse that durable row instead of opening a second
|
|
3149
|
+
// assistant bubble for the same event sequence.
|
|
3150
|
+
const existing = seq === undefined
|
|
3151
|
+
? undefined
|
|
3152
|
+
: [...state.rows].reverse().find(row => row.kind === 'assistant' && row.seq === seq);
|
|
3153
|
+
if (existing !== undefined) {
|
|
3154
|
+
existing.streaming = true;
|
|
3155
|
+
streaming = existing;
|
|
3156
|
+
return existing;
|
|
3041
3157
|
}
|
|
3158
|
+
streaming = { id: nextRowId, kind: 'assistant', text: '', streaming: true, ...seq !== undefined ? { seq } : {} };
|
|
3159
|
+
nextRowId += 1;
|
|
3160
|
+
state.rows.push(streaming);
|
|
3042
3161
|
return streaming;
|
|
3043
3162
|
};
|
|
3044
3163
|
/** Latest reasoning row keyed by its (turn, step) — lets a resumed
|
|
@@ -3169,6 +3288,13 @@ ${output}
|
|
|
3169
3288
|
* using it would rebuild a second thinking block per step. */
|
|
3170
3289
|
let replaying = false;
|
|
3171
3290
|
const replayEvents = (events) => {
|
|
3291
|
+
// Event sequence numbers restart with a replacement session; reset the
|
|
3292
|
+
// idempotency ledger before replay so an old session cannot suppress a
|
|
3293
|
+
// legitimate message in the new transcript.
|
|
3294
|
+
handledAssistantMessages.clear();
|
|
3295
|
+
handledAssistantChunks.clear();
|
|
3296
|
+
assistantRowsByStep.clear();
|
|
3297
|
+
lastTextDelta.clear();
|
|
3172
3298
|
replaying = true;
|
|
3173
3299
|
try {
|
|
3174
3300
|
for (const event of prepareReplayEvents(events))
|
|
@@ -3256,6 +3382,9 @@ ${output}
|
|
|
3256
3382
|
break;
|
|
3257
3383
|
}
|
|
3258
3384
|
case 'assistant/chunk': {
|
|
3385
|
+
if (handledAssistantChunks.has(event.seq))
|
|
3386
|
+
break;
|
|
3387
|
+
handledAssistantChunks.add(event.seq);
|
|
3259
3388
|
const chunk = event.data.chunk;
|
|
3260
3389
|
if (chunk.type === 'text-delta') {
|
|
3261
3390
|
if (chunk.text) {
|
|
@@ -3263,13 +3392,20 @@ ${output}
|
|
|
3263
3392
|
// window (see foldLiveReasoning) — before this text grows the
|
|
3264
3393
|
// transcript and pushes the block into scrollback.
|
|
3265
3394
|
foldLiveReasoning('first text token');
|
|
3266
|
-
|
|
3267
|
-
|
|
3395
|
+
const key = stepKey(event.data.turn, event.data.step);
|
|
3396
|
+
const row = assistantRowsByStep.get(key) ?? ensureStreaming(event.seq);
|
|
3397
|
+
assistantRowsByStep.set(key, row);
|
|
3398
|
+
streaming = row;
|
|
3399
|
+
row.streaming = true;
|
|
3400
|
+
const before = row.text.length;
|
|
3401
|
+
appendTextDelta(row, chunk.text);
|
|
3402
|
+
state.responseChars += Math.max(0, row.text.length - before);
|
|
3268
3403
|
}
|
|
3269
3404
|
}
|
|
3270
3405
|
else if (chunk.type === 'reasoning-delta') {
|
|
3271
3406
|
if (chunk.text) {
|
|
3272
|
-
ensureReasoning(event.seq, event.data.turn, event.data.step)
|
|
3407
|
+
const row = ensureReasoning(event.seq, event.data.turn, event.data.step);
|
|
3408
|
+
appendTextDelta(row, chunk.text);
|
|
3273
3409
|
}
|
|
3274
3410
|
}
|
|
3275
3411
|
const step = tpsStep;
|
|
@@ -3290,6 +3426,9 @@ ${output}
|
|
|
3290
3426
|
break;
|
|
3291
3427
|
}
|
|
3292
3428
|
case 'assistant/message': {
|
|
3429
|
+
if (handledAssistantMessages.has(event.seq))
|
|
3430
|
+
break;
|
|
3431
|
+
handledAssistantMessages.add(event.seq);
|
|
3293
3432
|
const text = textOf(event.data.message.content);
|
|
3294
3433
|
// Replay without chunk deltas (prepareReplayEvents drops settled
|
|
3295
3434
|
// ones): rebuild the reasoning row from the sealed message's
|
|
@@ -3319,8 +3458,22 @@ ${output}
|
|
|
3319
3458
|
// anyway leaves an empty `●` bullet in the transcript. A pre-existing
|
|
3320
3459
|
// streaming row always has text (ensureStreaming is only reached on
|
|
3321
3460
|
// non-empty text deltas), so only create one when text arrives.
|
|
3322
|
-
|
|
3461
|
+
// Key the step→row ledger only when the event carries a durable
|
|
3462
|
+
// turn/step; a message without them must never collide onto a
|
|
3463
|
+
// previous step's row (a bare `undefined:undefined` key would make
|
|
3464
|
+
// every turn/step-less message reuse the FIRST one's assistant row).
|
|
3465
|
+
const msgTurn = event.data.turn;
|
|
3466
|
+
const msgStep = event.data.step;
|
|
3467
|
+
const msgKey = msgTurn !== undefined && msgStep !== undefined
|
|
3468
|
+
? stepKey(msgTurn, msgStep)
|
|
3469
|
+
: undefined;
|
|
3470
|
+
const row = (msgKey !== undefined ? assistantRowsByStep.get(msgKey) : undefined) ?? streaming ??
|
|
3471
|
+
(text
|
|
3472
|
+
? ([...state.rows].reverse().find(candidate => candidate.kind === 'assistant' && candidate.seq === event.seq) ?? ensureStreaming(event.seq))
|
|
3473
|
+
: undefined);
|
|
3323
3474
|
if (row !== undefined) {
|
|
3475
|
+
if (msgKey !== undefined)
|
|
3476
|
+
assistantRowsByStep.set(msgKey, row);
|
|
3324
3477
|
row.time = event.time;
|
|
3325
3478
|
if (text)
|
|
3326
3479
|
row.text = text;
|
|
@@ -3616,11 +3769,36 @@ ${output}
|
|
|
3616
3769
|
// Live subscription list and activity timer, rebound to every replacement
|
|
3617
3770
|
// agent so no status from the previous session can leak across a swap.
|
|
3618
3771
|
let agentSubscriptions = [];
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3772
|
+
/** Tracker knobs + custom actions from the persisted pi-style config
|
|
3773
|
+
* (`~/.dsh-tui/working-activity.json`); a missing file means lively
|
|
3774
|
+
* defaults (all eggs on). */
|
|
3775
|
+
const activityPrefsSnapshot = () => {
|
|
3776
|
+
const cfg = readActivityConfig();
|
|
3777
|
+
if (cfg === undefined) {
|
|
3778
|
+
return { config: { phrases: true, detailLimit: 40, showIdle: false } };
|
|
3779
|
+
}
|
|
3780
|
+
return {
|
|
3781
|
+
config: {
|
|
3782
|
+
phrases: featureOn(cfg, 'phrases'),
|
|
3783
|
+
detailLimit: 40,
|
|
3784
|
+
showIdle: false,
|
|
3785
|
+
features: {
|
|
3786
|
+
rareEggs: featureOn(cfg, 'rareEggs'),
|
|
3787
|
+
weekend: featureOn(cfg, 'weekend'),
|
|
3788
|
+
holidays: featureOn(cfg, 'holidays'),
|
|
3789
|
+
nightPhrases: featureOn(cfg, 'nightPhrases'),
|
|
3790
|
+
},
|
|
3791
|
+
customPhrases: cfg.customPhrases,
|
|
3792
|
+
showTokPerSec: cfg.showTokPerSec,
|
|
3793
|
+
workRemindAt: cfg.workRemindAt,
|
|
3794
|
+
},
|
|
3795
|
+
customActions: cfg.customActions,
|
|
3796
|
+
};
|
|
3797
|
+
};
|
|
3798
|
+
let activityTracker = (() => {
|
|
3799
|
+
const prefs = activityPrefsSnapshot();
|
|
3800
|
+
return new ActivityTracker(prefs.config, Date.now, prefs.customActions);
|
|
3801
|
+
})();
|
|
3624
3802
|
let activityTickTimer;
|
|
3625
3803
|
const stopActivityTick = () => {
|
|
3626
3804
|
if (activityTickTimer === undefined)
|
|
@@ -3642,11 +3820,8 @@ ${output}
|
|
|
3642
3820
|
for (const dispose of agentSubscriptions)
|
|
3643
3821
|
dispose();
|
|
3644
3822
|
stopActivityTick();
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
detailLimit: 40,
|
|
3648
|
-
showIdle: false,
|
|
3649
|
-
});
|
|
3823
|
+
const prefs = activityPrefsSnapshot();
|
|
3824
|
+
activityTracker = new ActivityTracker(prefs.config, Date.now, prefs.customActions);
|
|
3650
3825
|
activityTracker.onAgentStatus(agent.status);
|
|
3651
3826
|
renderWorkingActivity();
|
|
3652
3827
|
activityTickTimer = setInterval(() => {
|
|
@@ -3742,6 +3917,14 @@ ${output}
|
|
|
3742
3917
|
// publish never throws into this arm.
|
|
3743
3918
|
messageObserver?.publish(session, event);
|
|
3744
3919
|
activityTracker.onSessionEvent(event);
|
|
3920
|
+
// Interrupt quip: an aborted/interrupted turn ends the round; the
|
|
3921
|
+
// comeback copy shows on the next thinking rotation (pi parity).
|
|
3922
|
+
if (event.type === 'turn/end') {
|
|
3923
|
+
const reason = event.data.reason;
|
|
3924
|
+
if (reason?.kind === 'aborted' || reason?.kind === 'interrupted') {
|
|
3925
|
+
activityTracker.onInterrupted();
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3745
3928
|
renderWorkingActivity();
|
|
3746
3929
|
// Mode-affecting atoms fold into the Shift+Tab mode indicator the
|
|
3747
3930
|
// moment they land (whether appended by cycleMode or by hand).
|
|
@@ -3809,6 +3992,8 @@ ${output}
|
|
|
3809
3992
|
// only show a branch for sessions this install actually used — which
|
|
3810
3993
|
// is exactly what the column claims.
|
|
3811
3994
|
noteBranch(agent.session.id, branch);
|
|
3995
|
+
// Feed the working line so git tools can show ` · git <branch>`.
|
|
3996
|
+
activityTracker.onGitBranch(branch);
|
|
3812
3997
|
state.emit();
|
|
3813
3998
|
}
|
|
3814
3999
|
})
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
10
|
import Schema from '@deepseek-ai/schemastery';
|
|
11
11
|
import type { SessionModeSpec } from '../sessionModes.js';
|
|
12
|
+
import { type StatusBarConfig, type ToolBackground } from '../tuiDisplayPrefs.js';
|
|
12
13
|
export declare const name = "dsh-tui";
|
|
13
14
|
export declare const inject: string[];
|
|
14
15
|
/**
|
|
@@ -70,6 +71,10 @@ export interface Config {
|
|
|
70
71
|
* preview and folds each step when it settles; `full` keeps thinking
|
|
71
72
|
* expanded until the whole turn ends. Editable live from `/settings`. */
|
|
72
73
|
thinkingFold?: 'preview' | 'full';
|
|
74
|
+
/** Tool-card background strength; defaults to no added background. */
|
|
75
|
+
toolBackground?: ToolBackground;
|
|
76
|
+
/** Status-footer field visibility and compact presentation preferences. */
|
|
77
|
+
statusBar?: Partial<StatusBarConfig>;
|
|
73
78
|
/** Shift+Tab session-mode cycle (array order IS the cycle order; index 0
|
|
74
79
|
* is the unmarked base mode). Each entry bundles any subset of the
|
|
75
80
|
* `plan`/`sandbox`/`approval` atoms; absent → the built-in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAsB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAErG,eAAO,MAAM,IAAI,YAAY,CAAA;AAU7B,eAAO,MAAM,MAAM,UAAa,CAAA;AAEhC;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;yEAKqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;gDAC4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;gFAE4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;sBAIkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;4BAGwB;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;wDAEoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;kFAC8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;gCAG4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;6DAEyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC;;8EAE0E;IAC1E,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IACjC,sEAAsE;IACtE,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;IACpC;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CA8ChC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Schema from '@deepseek-ai/schemastery';
|
|
2
|
+
import { DEFAULT_STATUS_BAR } from '../tuiDisplayPrefs.js';
|
|
2
3
|
export const name = 'dsh-tui';
|
|
3
4
|
// `tuiWorkspaces` must stay OUT of this code-level inject (issue #183): the
|
|
4
5
|
// dsh CLI resolves the bundle's cordis.patch.yml from the FIRST copy of this
|
|
@@ -29,6 +30,24 @@ export const Config = Schema.object({
|
|
|
29
30
|
preset: Schema.string().required(false),
|
|
30
31
|
diffLayout: Schema.union(['auto', 'split', 'unified']).default('auto'),
|
|
31
32
|
thinkingFold: Schema.union(['preview', 'full']).default('preview'),
|
|
33
|
+
toolBackground: Schema.union(['none', 'subtle', 'strong']).default('none'),
|
|
34
|
+
statusBar: Schema.object({
|
|
35
|
+
compact: Schema.boolean().default(DEFAULT_STATUS_BAR.compact),
|
|
36
|
+
model: Schema.boolean().default(DEFAULT_STATUS_BAR.model),
|
|
37
|
+
thinking: Schema.boolean().default(DEFAULT_STATUS_BAR.thinking),
|
|
38
|
+
cwd: Schema.boolean().default(DEFAULT_STATUS_BAR.cwd),
|
|
39
|
+
contextUsage: Schema.boolean().default(DEFAULT_STATUS_BAR.contextUsage),
|
|
40
|
+
cache: Schema.boolean().default(DEFAULT_STATUS_BAR.cache),
|
|
41
|
+
tokens: Schema.boolean().default(DEFAULT_STATUS_BAR.tokens),
|
|
42
|
+
tps: Schema.boolean().default(DEFAULT_STATUS_BAR.tps),
|
|
43
|
+
gitBranch: Schema.boolean().default(DEFAULT_STATUS_BAR.gitBranch),
|
|
44
|
+
sessionTitle: Schema.boolean().default(DEFAULT_STATUS_BAR.sessionTitle),
|
|
45
|
+
mode: Schema.boolean().default(DEFAULT_STATUS_BAR.mode),
|
|
46
|
+
contextBar: Schema.boolean().default(DEFAULT_STATUS_BAR.contextBar),
|
|
47
|
+
activity: Schema.boolean().default(DEFAULT_STATUS_BAR.activity),
|
|
48
|
+
trajectory: Schema.boolean().default(DEFAULT_STATUS_BAR.trajectory),
|
|
49
|
+
shortcutHint: Schema.boolean().default(DEFAULT_STATUS_BAR.shortcutHint),
|
|
50
|
+
}).default({ ...DEFAULT_STATUS_BAR }),
|
|
32
51
|
modes: Schema.array(Schema.object({
|
|
33
52
|
id: Schema.string(),
|
|
34
53
|
label: Schema.string().required(false),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAoCnC;;;;;;;;GAQG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsyBvE;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV"}
|