@antst/dashi 0.1.0-alpha.13 → 0.1.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/catalogs.d.ts +1 -1
- package/lib/catalogs.js +19 -5
- package/lib/renderer.js +2 -2
- package/lib/rewind.d.ts +6 -0
- package/lib/rewind.js +13 -4
- package/lib/session-runtime.js +22 -20
- package/package.json +1 -1
package/lib/catalogs.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare function sessionOverlay(items: readonly SessionSummary[], current
|
|
|
14
14
|
export declare function modelOverlay(catalog: ModelCatalog, selected?: ModelSelection): Overlay;
|
|
15
15
|
/** Build the preset picker from the native preset service. */
|
|
16
16
|
export declare function permissionOverlay(ctx: Context, agent: Agent): Overlay;
|
|
17
|
-
export declare function sessionStatusLine(ctx: Context, agent: Agent, root: RootView,
|
|
17
|
+
export declare function sessionStatusLine(ctx: Context, agent: Agent, root: RootView, repo?: string): string;
|
|
18
18
|
/** Read DSH-owned root facts for /status in one projection cut. */
|
|
19
19
|
export declare function statusOverlay(ctx: Context, agent: Agent, root: RootView): Overlay;
|
|
20
20
|
/** Show DSH's heuristic next-request composition without treating it as an exact total. */
|
package/lib/catalogs.js
CHANGED
|
@@ -177,18 +177,32 @@ function number(value) {
|
|
|
177
177
|
function milliseconds(value) {
|
|
178
178
|
return typeof value === 'number' ? `${number(value)} ms` : 'unavailable';
|
|
179
179
|
}
|
|
180
|
-
|
|
180
|
+
function compactTokens(value) {
|
|
181
|
+
return Intl.NumberFormat('en-US', { maximumFractionDigits: 1, notation: 'compact' }).format(value).replace('K', 'k');
|
|
182
|
+
}
|
|
183
|
+
export function sessionStatusLine(ctx, agent, root, repo) {
|
|
181
184
|
const { modelSelection, permissions, tokenUsage, contextPressure } = ctx.sessionProjections
|
|
182
185
|
.snapshot(agent.session, ['modelSelection', 'permissions', 'tokenUsage', 'contextPressure']).values;
|
|
183
186
|
const model = modelSelection?.next ?? modelSelection?.lastUsed;
|
|
184
187
|
const input = tokenUsage === undefined ? 0 : tokenUsage.uncachedInputTokens + tokenUsage.cacheReadTokens + tokenUsage.cacheWriteTokens;
|
|
188
|
+
const total = input + (tokenUsage?.outputTokens ?? 0);
|
|
185
189
|
const used = contextPressure?.projectedTokens ?? contextPressure?.pressureTokens;
|
|
190
|
+
const context = used === undefined ? undefined
|
|
191
|
+
: contextPressure?.contextWindow === undefined ? `ctx ${compactTokens(used)}`
|
|
192
|
+
: `ctx ${compactTokens(used)}/${compactTokens(contextPressure.contextWindow)} ${String(Math.min(100, Math.round(used / contextPressure.contextWindow * 100)))}%`;
|
|
193
|
+
const agents = root.subagents?.filter(item => item.state === 'running').length ?? 0;
|
|
194
|
+
const jobs = root.jobs?.filter(item => item.status === 'running' || item.status === 'stopping').length ?? 0;
|
|
186
195
|
return [
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
196
|
+
model?.model ?? root.model,
|
|
197
|
+
...(model?.reasoningEffort === undefined ? [] : [model.reasoningEffort]),
|
|
198
|
+
permissions?.currentValue ?? root.permission ?? 'unknown',
|
|
199
|
+
...(context === undefined ? [] : [context]),
|
|
190
200
|
...(input === 0 ? [] : [`cache ${String(Math.min(tokenUsage?.cacheReadTokens === input ? 100 : 99, Math.round((tokenUsage?.cacheReadTokens ?? 0) / input * 100)))}%`]),
|
|
191
|
-
...(
|
|
201
|
+
...(tokenUsage === undefined ? [] : [`${compactTokens(total)} tok`]),
|
|
202
|
+
...(agents === 0 ? [] : [`${String(agents)} agents`]),
|
|
203
|
+
...(jobs === 0 ? [] : [`${String(jobs)} jobs`]),
|
|
204
|
+
...(root.title === undefined ? [] : [root.title]),
|
|
205
|
+
...(repo === undefined ? [] : [repo]),
|
|
192
206
|
].join(' · ');
|
|
193
207
|
}
|
|
194
208
|
/** Read DSH-owned root facts for /status in one projection cut. */
|
package/lib/renderer.js
CHANGED
|
@@ -401,8 +401,8 @@ export function createRenderer(options) {
|
|
|
401
401
|
: root === undefined
|
|
402
402
|
? `${DIM}no session · cards ${state.toolMode} · Ctrl+J newline · Ctrl+L redraw${RESET}`
|
|
403
403
|
: root.status === 'running'
|
|
404
|
-
? `${DIM}running · ${plain(root.model)} · Enter ${state.sendMode === 'steer' ? 'steer' : 'queue next turn'} · Ctrl+T toggle · cards ${state.toolMode} · Esc interrupt${RESET}`
|
|
405
|
-
: `${DIM}idle · ${plain(root.model)} · Enter send · cards ${state.toolMode}${RESET}`;
|
|
404
|
+
? `${DIM}running${accessible ? ` · ${plain(root.model)}` : ''} · Enter ${state.sendMode === 'steer' ? 'steer' : 'queue next turn'} · Ctrl+T toggle · cards ${state.toolMode} · Esc interrupt${RESET}`
|
|
405
|
+
: `${DIM}idle${accessible ? ` · ${plain(root.model)}` : ''} · Enter send · cards ${state.toolMode}${RESET}`;
|
|
406
406
|
const activityCount = activities(state).length;
|
|
407
407
|
const baseStatus = `${baseStatusText}${root?.contextPercent === undefined ? '' : ` · ${String(root.contextPercent)}% context`}${activityCount === 0 ? '' : ' · Ctrl+B activity'}`;
|
|
408
408
|
const status = state.newOutput === 0 ? baseStatus
|
package/lib/rewind.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ModelSelection } from '@deepseek-ai/dsh-api-session-controller';
|
|
1
2
|
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
2
3
|
import type { Overlay, OverlayValue } from './state.js';
|
|
3
4
|
export interface RewindBoundary {
|
|
@@ -5,6 +6,11 @@ export interface RewindBoundary {
|
|
|
5
6
|
readonly label: string;
|
|
6
7
|
readonly prompt: string;
|
|
7
8
|
}
|
|
9
|
+
/** Return only a selection that DSH recorded for the source session. */
|
|
10
|
+
export declare function rewindModelSelection(projection: {
|
|
11
|
+
readonly lastUsed: ModelSelection | null;
|
|
12
|
+
readonly next: ModelSelection | null;
|
|
13
|
+
} | undefined): ModelSelection | undefined;
|
|
8
14
|
/** Return the text of a direct human message, excluding injected context. */
|
|
9
15
|
export declare function humanPrompt(event: SessionEvent): string | undefined;
|
|
10
16
|
/** Recover one composer input from DSH's durable log. */
|
package/lib/rewind.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Return only a selection that DSH recorded for the source session. */
|
|
2
|
+
export function rewindModelSelection(projection) {
|
|
3
|
+
return projection?.next ?? projection?.lastUsed ?? undefined;
|
|
4
|
+
}
|
|
1
5
|
/** Return the text of a direct human message, excluding injected context. */
|
|
2
6
|
export function humanPrompt(event) {
|
|
3
7
|
if (event.type !== 'user/message' || event.data.source.kind !== 'user')
|
|
@@ -7,6 +11,13 @@ export function humanPrompt(event) {
|
|
|
7
11
|
.map(block => block.text).join('');
|
|
8
12
|
return text === '' ? undefined : text;
|
|
9
13
|
}
|
|
14
|
+
function shellInput(message) {
|
|
15
|
+
if (message.source.kind !== 'plugin' || message.source.plugin !== 'dashi'
|
|
16
|
+
|| message.source.form !== 'notice')
|
|
17
|
+
return undefined;
|
|
18
|
+
const first = message.content.find(block => block.type === 'text')?.text.split(/\r?\n/u)[0];
|
|
19
|
+
return first?.startsWith('$ ') === true ? `!${first.slice(2)}` : undefined;
|
|
20
|
+
}
|
|
10
21
|
/** Recover one composer input from DSH's durable log. */
|
|
11
22
|
export function historyInput(event) {
|
|
12
23
|
const prompt = humanPrompt(event);
|
|
@@ -15,11 +26,9 @@ export function historyInput(event) {
|
|
|
15
26
|
if (event.type === 'command/run' && event.data.args !== undefined) {
|
|
16
27
|
return `/${event.data.name}${event.data.args}`;
|
|
17
28
|
}
|
|
18
|
-
if (event.type !== '
|
|
19
|
-
|| event.data.source.plugin !== 'dashi' || event.data.source.form !== 'notice')
|
|
29
|
+
if (event.type !== 'agent/inbox/spliced')
|
|
20
30
|
return undefined;
|
|
21
|
-
|
|
22
|
-
return first?.startsWith('$ ') === true ? `!${first.slice(2)}` : undefined;
|
|
31
|
+
return event.data.inserted.map(shellInput).find(input => input !== undefined);
|
|
23
32
|
}
|
|
24
33
|
export function historyInputs(events) {
|
|
25
34
|
return events.flatMap(event => historyInput(event) ?? []);
|
package/lib/session-runtime.js
CHANGED
|
@@ -9,7 +9,7 @@ import { diffOverlay } from './diff-view.js';
|
|
|
9
9
|
import { eventsFromRecords } from './history-records.js';
|
|
10
10
|
import { admitDraftImages, encodeDraftImages, readDraftImages } from './image-input.js';
|
|
11
11
|
import { contextPercent, jobViews, subagentViews } from './presentation.js';
|
|
12
|
-
import { historyInput, historyInputs, humanPrompt, inheritedTurn, rewindActionOverlay, rewindOverlay, } from './rewind.js';
|
|
12
|
+
import { historyInput, historyInputs, humanPrompt, inheritedTurn, rewindActionOverlay, rewindOverlay, rewindModelSelection, } from './rewind.js';
|
|
13
13
|
import { createToolPresenter } from './tool-presentation.js';
|
|
14
14
|
import { quoteShellWord, runHumanShell } from './shell-command.js';
|
|
15
15
|
import { markdownTranscript } from './session-export.js';
|
|
@@ -80,16 +80,18 @@ async function firstControl(iterator) {
|
|
|
80
80
|
}
|
|
81
81
|
return first.value;
|
|
82
82
|
}
|
|
83
|
-
async function
|
|
83
|
+
async function gitRepo(ctx, agent, signal) {
|
|
84
|
+
const fallback = basename(agent.session.header.cwd ?? '');
|
|
84
85
|
try {
|
|
85
86
|
const result = await ctx.shell.run(ctx.shell.resolve({
|
|
86
|
-
command: 'git branch --show-current', sandboxPolicy: ctx.sandboxPolicy.resolve({ mode: 'read-only' }),
|
|
87
|
-
signal, stdoutMaxBytes:
|
|
87
|
+
command: 'git rev-parse --show-toplevel && git branch --show-current', sandboxPolicy: ctx.sandboxPolicy.resolve({ mode: 'read-only' }),
|
|
88
|
+
signal, stdoutMaxBytes: 1_024, timeoutMs: 2_000, workdir: agent.session.header.cwd,
|
|
88
89
|
}));
|
|
89
|
-
|
|
90
|
+
const [root, branch] = result.stdout.text.trim().split(/\r?\n/u);
|
|
91
|
+
return result.exitCode === 0 && root !== undefined && branch !== undefined ? `${basename(root)}/${branch}` : fallback;
|
|
90
92
|
}
|
|
91
93
|
catch {
|
|
92
|
-
return
|
|
94
|
+
return fallback;
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
function usage(raw, expected) {
|
|
@@ -165,9 +167,9 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
165
167
|
address: { kind: 'session', sessionId }, maxMessages: 50,
|
|
166
168
|
}, abort.signal)[Symbol.asyncIterator]();
|
|
167
169
|
const controlIterator = ctx.sessionController.control(abort.signal)[Symbol.asyncIterator]();
|
|
168
|
-
const [opening, controlOpening, subagents,
|
|
170
|
+
const [opening, controlOpening, subagents, repo] = await Promise.all([
|
|
169
171
|
firstSnapshot(iterator), firstControl(controlIterator), subagentViews(ctx, sessionId, abort.signal),
|
|
170
|
-
|
|
172
|
+
gitRepo(ctx, agent, abort.signal),
|
|
171
173
|
]);
|
|
172
174
|
title ??= projectionString(opening.projections, 'title');
|
|
173
175
|
const selected = projectionModel(opening.projections);
|
|
@@ -179,7 +181,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
179
181
|
return {
|
|
180
182
|
abort,
|
|
181
183
|
agent,
|
|
182
|
-
|
|
184
|
+
repo,
|
|
183
185
|
controlIterator,
|
|
184
186
|
cursor: opening.cursor,
|
|
185
187
|
disposers,
|
|
@@ -289,11 +291,11 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
289
291
|
});
|
|
290
292
|
}, 34);
|
|
291
293
|
};
|
|
292
|
-
const
|
|
293
|
-
const
|
|
294
|
-
if (bound.abort.signal.aborted || current() !== bound || bound.
|
|
294
|
+
const refreshRepo = async (bound) => {
|
|
295
|
+
const repo = await gitRepo(ctx, bound.agent, bound.abort.signal);
|
|
296
|
+
if (bound.abort.signal.aborted || current() !== bound || bound.repo === repo)
|
|
295
297
|
return;
|
|
296
|
-
bound.
|
|
298
|
+
bound.repo = repo;
|
|
297
299
|
dispatch({ type: 'redraw' });
|
|
298
300
|
};
|
|
299
301
|
const consume = (bound, frame) => {
|
|
@@ -302,7 +304,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
302
304
|
return;
|
|
303
305
|
bound.events.push(event);
|
|
304
306
|
if (event.type === 'turn/end') {
|
|
305
|
-
void
|
|
307
|
+
void refreshRepo(bound);
|
|
306
308
|
const startedAt = turnStartTime(bound.events, event.data.turn);
|
|
307
309
|
if (startedAt !== undefined) {
|
|
308
310
|
dispatch({
|
|
@@ -327,7 +329,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
327
329
|
...(model.reasoningEffort === undefined ? {} : { effort: model.reasoningEffort }),
|
|
328
330
|
});
|
|
329
331
|
}
|
|
330
|
-
const prompt = historyInput(event);
|
|
332
|
+
const prompt = event.type === 'agent/inbox/spliced' ? undefined : historyInput(event);
|
|
331
333
|
if (prompt !== undefined)
|
|
332
334
|
dispatch({ type: 'prompt-recorded', rootId: bound.root.id, text: prompt });
|
|
333
335
|
schedule(bound);
|
|
@@ -447,13 +449,12 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
447
449
|
?? ctx.agentPresets.defaultId;
|
|
448
450
|
const modelProjection = ctx.sessionProjections.snapshot(bound.agent.session, ['modelSelection'])
|
|
449
451
|
.values.modelSelection;
|
|
450
|
-
const selection = modelProjection
|
|
452
|
+
const selection = rewindModelSelection(modelProjection);
|
|
451
453
|
const created = await ctx.sessionController.create({ cwd: bound.root.cwd, agentPreset });
|
|
452
454
|
let selectionFailure;
|
|
453
455
|
try {
|
|
454
|
-
if (selection
|
|
455
|
-
|
|
456
|
-
await ctx.sessionController.selectModel({ sessionId: created.sessionId, ...selection });
|
|
456
|
+
if (selection !== undefined)
|
|
457
|
+
await ctx.sessionController.selectModel({ sessionId: created.sessionId, ...selection });
|
|
457
458
|
}
|
|
458
459
|
catch (error) {
|
|
459
460
|
selectionFailure = error;
|
|
@@ -1279,7 +1280,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
1279
1280
|
get initialHasMore() { return current().hasMore; },
|
|
1280
1281
|
get initialPrompts() { return historyInputs(current().events); },
|
|
1281
1282
|
get root() { return current().root; },
|
|
1282
|
-
get statusLine() { const bound = current(); return sessionStatusLine(ctx, bound.agent, bound.root, bound.
|
|
1283
|
+
get statusLine() { const bound = current(); return sessionStatusLine(ctx, bound.agent, bound.root, bound.repo); },
|
|
1283
1284
|
get summary() { return `Resume with: dsh --profile dashi --resume ${current().root.id}`; },
|
|
1284
1285
|
activate,
|
|
1285
1286
|
async attach(path) {
|
|
@@ -1428,6 +1429,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
1428
1429
|
bound.agent.inject(message);
|
|
1429
1430
|
await ctx.sessions.flush(bound.agent.session);
|
|
1430
1431
|
});
|
|
1432
|
+
dispatch({ type: 'prompt-recorded', rootId: bound.root.id, text: `!${command}` });
|
|
1431
1433
|
publish(bound);
|
|
1432
1434
|
return;
|
|
1433
1435
|
}
|