@antst/dashi 0.1.0-alpha.17 → 0.1.0-alpha.18
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/README.md +2 -2
- package/lib/diff-view.js +9 -3
- package/lib/history-records.d.ts +2 -2
- package/lib/history-records.js +2 -12
- package/lib/rewind.d.ts +5 -0
- package/lib/rewind.js +9 -2
- package/lib/session-runtime.js +44 -7
- package/lib/state.js +7 -0
- package/lib/transcript.d.ts +3 -1
- package/lib/transcript.js +57 -44
- package/package.json +35 -35
- package/validated-dsh-versions.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ via `@antst/dashi-app`, not as a standalone dependency:
|
|
|
6
6
|
Install the exact validated DSH release and start dashi:
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
|
-
pnpm install @deepseek-ai/dsh@0.1.
|
|
9
|
+
pnpm install @deepseek-ai/dsh@0.1.5-rc.2
|
|
10
10
|
dsh plugin --profile dashi add @antst/dashi-app
|
|
11
11
|
dsh --profile dashi
|
|
12
12
|
```
|
|
@@ -19,7 +19,7 @@ dashi validates each DSH release before adopting it, and its packages pin DSH
|
|
|
19
19
|
dependencies exactly.
|
|
20
20
|
|
|
21
21
|
Status: pre-release alpha. Interfaces and behavior may change between
|
|
22
|
-
alpha versions; the validated DSH version is `0.1.
|
|
22
|
+
alpha versions; the validated DSH version is `0.1.5-rc.2`.
|
|
23
23
|
|
|
24
24
|
Full documentation, source, and issue tracker:
|
|
25
25
|
https://forgejo.antst.net/ai/dashi
|
package/lib/diff-view.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { HUMAN_SHELL_STREAM_BYTES } from './shell-command.js';
|
|
2
2
|
import { boundedBody } from './tool-presentation.js';
|
|
3
3
|
import { foldCells } from './transcript.js';
|
|
4
|
+
import { latestCompletedTurn } from './rewind.js';
|
|
4
5
|
export async function diffOverlay(ctx, agent, presenter, mode, signal) {
|
|
5
6
|
if (mode === 'turn') {
|
|
6
7
|
const events = agent.session.snapshotEvents();
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const boundary = latestCompletedTurn(events);
|
|
9
|
+
const end = boundary.atSeq === undefined ? -1 : events.findIndex(event => event.seq === boundary.atSeq);
|
|
10
|
+
let start = end;
|
|
11
|
+
while (start >= 0 && events[start]?.type !== 'turn/start')
|
|
12
|
+
start--;
|
|
13
|
+
const cells = (start < 0 ? [] : foldCells(events.slice(start, end + 1), presenter)).filter(cell => cell.pending !== true && cell.tool?.card === 'diff' && (cell.tool.diffs?.length ?? 0) > 0);
|
|
14
|
+
return { cells, kind: 'info', lines: cells.length === 0 ? ['No write/edit changes in the last completed turn.'] : [],
|
|
15
|
+
title: `Last completed turn diff${boundary.open ? ' · trailing turn open' : ''}` };
|
|
10
16
|
}
|
|
11
17
|
const command = 'git diff --no-ext-diff --no-color HEAD --';
|
|
12
18
|
const result = await ctx.shell.run(ctx.shell.resolve({
|
package/lib/history-records.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { SessionHistoryRecord } from '@deepseek-ai/dsh-api-session-controller';
|
|
2
|
-
import {
|
|
3
|
-
/**
|
|
2
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
3
|
+
/** Read the Controller's canonical Session event history. */
|
|
4
4
|
export declare function eventsFromRecords(records: readonly SessionHistoryRecord[]): SessionEvent[];
|
package/lib/history-records.js
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
/** Expand the Controller's compact history wire records to canonical Session events. */
|
|
1
|
+
/** Read the Controller's canonical Session event history. */
|
|
3
2
|
export function eventsFromRecords(records) {
|
|
4
|
-
return records.
|
|
5
|
-
if (record.type === 'event')
|
|
6
|
-
return [record.event];
|
|
7
|
-
const encoded = record.event.type === 'chunkrow/text-chunks'
|
|
8
|
-
? { type: 'text-chunks', seq0: record.event.seq, time0: record.event.time, data: record.event.data }
|
|
9
|
-
: record.event.type === 'chunkrow/reasoning-chunks'
|
|
10
|
-
? { type: 'reasoning-chunks', seq0: record.event.seq, time0: record.event.time, data: record.event.data }
|
|
11
|
-
: { type: 'tool-call-chunks', seq0: record.event.seq, time0: record.event.time, data: record.event.data };
|
|
12
|
-
return decodeStorageRecord(encoded);
|
|
13
|
-
});
|
|
3
|
+
return records.map(record => record.event);
|
|
14
4
|
}
|
package/lib/rewind.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export interface RewindBoundary {
|
|
|
6
6
|
readonly label: string;
|
|
7
7
|
readonly prompt: string;
|
|
8
8
|
}
|
|
9
|
+
export declare function latestCompletedTurn(events: readonly SessionEvent[]): {
|
|
10
|
+
readonly atSeq?: number;
|
|
11
|
+
readonly open: boolean;
|
|
12
|
+
readonly turn?: number;
|
|
13
|
+
};
|
|
9
14
|
/** Return only a selection that DSH recorded for the source session. */
|
|
10
15
|
export declare function rewindModelSelection(projection: {
|
|
11
16
|
readonly lastUsed: ModelSelection | null;
|
package/lib/rewind.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export function latestCompletedTurn(events) {
|
|
2
|
+
const end = events.filter((event) => event.type === 'turn/end' && event.data.reason.kind !== 'interrupted').at(-1);
|
|
3
|
+
const start = events.filter((event) => event.type === 'turn/start').at(-1);
|
|
4
|
+
return { open: start !== undefined && (end === undefined || start.seq > end.seq),
|
|
5
|
+
...(end === undefined ? {} : { atSeq: end.seq, turn: end.data.turn }) };
|
|
6
|
+
}
|
|
1
7
|
/** Return only a selection that DSH recorded for the source session. */
|
|
2
8
|
export function rewindModelSelection(projection) {
|
|
3
9
|
return projection?.next ?? projection?.lastUsed ?? undefined;
|
|
@@ -44,7 +50,7 @@ export function rewindBoundaries(events) {
|
|
|
44
50
|
openTurn = event.data.turn;
|
|
45
51
|
prompted = false;
|
|
46
52
|
}
|
|
47
|
-
else if (event.type === 'turn/end') {
|
|
53
|
+
else if (event.type === 'turn/end' && event.data.reason.kind !== 'interrupted') {
|
|
48
54
|
previousEnd = event;
|
|
49
55
|
openTurn = undefined;
|
|
50
56
|
prompted = false;
|
|
@@ -65,6 +71,7 @@ export function rewindBoundaries(events) {
|
|
|
65
71
|
}
|
|
66
72
|
export function rewindOverlay(events, roller) {
|
|
67
73
|
const boundaries = rewindBoundaries(events);
|
|
74
|
+
const trailing = latestCompletedTurn(events);
|
|
68
75
|
return {
|
|
69
76
|
cursor: Math.max(0, boundaries.length - 1),
|
|
70
77
|
kind: 'list',
|
|
@@ -76,7 +83,7 @@ export function rewindOverlay(events, roller) {
|
|
|
76
83
|
},
|
|
77
84
|
})),
|
|
78
85
|
purpose: 'rewind',
|
|
79
|
-
title:
|
|
86
|
+
title: `Rewind to a prompt${trailing.open ? ' · trailing turn open; completed boundaries only' : ''}`,
|
|
80
87
|
};
|
|
81
88
|
}
|
|
82
89
|
export function rewindActionOverlay(boundary) {
|
package/lib/session-runtime.js
CHANGED
|
@@ -3,19 +3,20 @@ import { AuthorizationDeclinedError } from '@deepseek-ai/dsh-authorization';
|
|
|
3
3
|
import { parseCredentialKey } from '@deepseek-ai/dsh-credentials';
|
|
4
4
|
import { JobId } from '@deepseek-ai/dsh-jobs';
|
|
5
5
|
import { createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm';
|
|
6
|
+
import { expandAssistantStream } from '@deepseek-ai/dsh-llm/assistant-stream';
|
|
6
7
|
import { SessionId, SessionSeq } from '@deepseek-ai/dsh-session';
|
|
7
8
|
import { agentPresetOverlay, completionOptions, contextOverlay, currentModel, modelOverlay, permissionOverlay, searchOverlay, sessionStatusLine, sessionOverlay, skillOverlay, statusOverlay, usageOverlay, } from './catalogs.js';
|
|
8
9
|
import { diffOverlay } from './diff-view.js';
|
|
9
10
|
import { eventsFromRecords } from './history-records.js';
|
|
10
11
|
import { admitDraftImages, encodeDraftImages, readDraftImages } from './image-input.js';
|
|
11
12
|
import { contextPercent, jobViews, subagentViews } from './presentation.js';
|
|
12
|
-
import { historyInput, historyInputs, humanPrompt, inheritedTurn, rewindActionOverlay, rewindOverlay, rewindModelSelection, } from './rewind.js';
|
|
13
|
+
import { historyInput, historyInputs, humanPrompt, inheritedTurn, latestCompletedTurn, rewindActionOverlay, rewindOverlay, rewindModelSelection, } from './rewind.js';
|
|
13
14
|
import { createToolPresenter } from './tool-presentation.js';
|
|
14
15
|
import { quoteShellWord, runHumanShell } from './shell-command.js';
|
|
15
16
|
import { markdownTranscript } from './session-export.js';
|
|
16
17
|
import { isSessionId, sessionMatches, sessionNotFound } from './session-list.js';
|
|
17
18
|
import { memoryOverlay } from './memory.js';
|
|
18
|
-
import { foldCells, pendingShellCells } from './transcript.js';
|
|
19
|
+
import { foldAssistantStream, foldCells, pendingShellCells } from './transcript.js';
|
|
19
20
|
export class LaunchArgumentError extends Error {
|
|
20
21
|
}
|
|
21
22
|
function starterAgents(cwd) {
|
|
@@ -60,6 +61,20 @@ function turnStartTime(events, turn) {
|
|
|
60
61
|
return event.time;
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
function latestStep(events) {
|
|
65
|
+
for (let index = events.length - 1; index >= 0; index--) {
|
|
66
|
+
const event = events[index];
|
|
67
|
+
if (event?.type === 'step/start')
|
|
68
|
+
return event;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function openingStream(baseline) {
|
|
72
|
+
const attempt = baseline?.activeAttempt;
|
|
73
|
+
if (attempt === undefined)
|
|
74
|
+
return [];
|
|
75
|
+
return expandAssistantStream(attempt.stream).slice(0, attempt.nextIndex)
|
|
76
|
+
.reduce((cells, member) => foldAssistantStream(cells, attempt.turn, attempt.step, member.chunk), []);
|
|
77
|
+
}
|
|
63
78
|
function titleFromEvent(event) {
|
|
64
79
|
if (String(event.type) !== 'session/title')
|
|
65
80
|
return undefined;
|
|
@@ -164,7 +179,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
164
179
|
title = (await ctx.sessionController.rename({ sessionId, title: name })).title;
|
|
165
180
|
await ctx.sessions.flush(agent.session);
|
|
166
181
|
const iterator = ctx.sessionController.follow({
|
|
167
|
-
address: { kind: 'session', sessionId }, maxMessages: 50,
|
|
182
|
+
address: { kind: 'session', sessionId }, assistantStream: true, maxMessages: 50,
|
|
168
183
|
}, abort.signal)[Symbol.asyncIterator]();
|
|
169
184
|
const controlIterator = ctx.sessionController.control(abort.signal)[Symbol.asyncIterator]();
|
|
170
185
|
const [opening, controlOpening, subagents, repo] = await Promise.all([
|
|
@@ -190,6 +205,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
190
205
|
iterator,
|
|
191
206
|
presenter: createToolPresenter(tool => ctx.tools.get(tool, agent)),
|
|
192
207
|
presentationTimer: undefined,
|
|
208
|
+
streamCells: openingStream(opening.assistantStream),
|
|
193
209
|
root: {
|
|
194
210
|
...(initialContextPercent === undefined ? {} : { contextPercent: initialContextPercent }),
|
|
195
211
|
cwd: opening.header.cwd ?? options.cwd,
|
|
@@ -222,7 +238,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
222
238
|
};
|
|
223
239
|
const executeAccepted = async (bound, line, images, onSettled) => {
|
|
224
240
|
const from = bound.agent.session.seq;
|
|
225
|
-
const execution = ctx.commands.execute(bound.agent, line, images, lifetime.signal);
|
|
241
|
+
const execution = ctx.commands.execute(bound.agent, line, images.map(image => ({ type: 'image', ...image })), lifetime.signal);
|
|
226
242
|
const accepted = bound.agent.session.snapshotEvents(from).some(event => event.type === 'command/run');
|
|
227
243
|
if (!accepted) {
|
|
228
244
|
const result = await execution;
|
|
@@ -260,6 +276,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
260
276
|
});
|
|
261
277
|
const transcriptCells = (bound) => [
|
|
262
278
|
...foldCells(bound.events, bound.presenter, { truncatedStart: bound.hasMore }),
|
|
279
|
+
...bound.streamCells,
|
|
263
280
|
...pendingShellCells(bound.agent.inbox.nextStep),
|
|
264
281
|
];
|
|
265
282
|
const publish = (bound) => {
|
|
@@ -299,10 +316,25 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
299
316
|
dispatch({ type: 'redraw' });
|
|
300
317
|
};
|
|
301
318
|
const consume = (bound, frame) => {
|
|
319
|
+
if (frame.type === 'assistant-stream') {
|
|
320
|
+
if (frame.frame.type !== 'chunk') {
|
|
321
|
+
bound.streamCells = [];
|
|
322
|
+
schedule(bound);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const step = latestStep(bound.events);
|
|
326
|
+
if (step === undefined)
|
|
327
|
+
return;
|
|
328
|
+
bound.streamCells = foldAssistantStream(bound.streamCells, step.data.turn, step.data.step, frame.frame.chunk);
|
|
329
|
+
schedule(bound);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
302
332
|
const [event] = eventsFromRecords([frame]);
|
|
303
333
|
if (event === undefined)
|
|
304
334
|
return;
|
|
305
335
|
bound.events.push(event);
|
|
336
|
+
if (event.type === 'assistant/message' || event.type === 'assistant/attempt')
|
|
337
|
+
bound.streamCells = [];
|
|
306
338
|
if (event.type === 'turn/end') {
|
|
307
339
|
void refreshRepo(bound);
|
|
308
340
|
const startedAt = turnStartTime(bound.events, event.data.turn);
|
|
@@ -589,7 +621,12 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
589
621
|
return stale;
|
|
590
622
|
if (prompt === '')
|
|
591
623
|
return { kind: 'error', text: 'usage: /btw TEXT' };
|
|
592
|
-
|
|
624
|
+
if (bound.agent.status !== 'idle')
|
|
625
|
+
return { kind: 'error', text: 'finish or interrupt the turn first' };
|
|
626
|
+
const boundary = latestCompletedTurn(bound.agent.session.snapshotEvents());
|
|
627
|
+
if (boundary.atSeq === undefined)
|
|
628
|
+
throw new Error('the current session has no completed turn to fork');
|
|
629
|
+
const forked = await ctx.sessionController.fork({ atSeq: boundary.atSeq, sessionId: bound.agent.id });
|
|
593
630
|
const resolved = await ctx.sessionController.resolveAgent(forked.sessionId);
|
|
594
631
|
if ('error' in resolved)
|
|
595
632
|
throw resolved.error;
|
|
@@ -611,7 +648,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
611
648
|
const next = await iterator.next();
|
|
612
649
|
if (next.done === true)
|
|
613
650
|
throw new Error('DSH session follow ended before the side turn completed');
|
|
614
|
-
if (next.value.type
|
|
651
|
+
if (next.value.type === 'event' && eventsFromRecords([next.value])[0]?.type === 'turn/end')
|
|
615
652
|
break;
|
|
616
653
|
}
|
|
617
654
|
}
|
|
@@ -622,7 +659,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
622
659
|
const cells = foldCells(child.session.snapshotEvents(from), createToolPresenter(tool => ctx.tools.get(tool, child)))
|
|
623
660
|
.filter(cell => cell.kind !== 'user');
|
|
624
661
|
dispatch({ type: 'open-overlay', overlay: {
|
|
625
|
-
cells, kind: 'info', lines: [], title: `Btw · turn ${String(turn)}`,
|
|
662
|
+
cells, kind: 'info', lines: [], title: `Btw · turn ${String(turn)}${boundary.open ? ' · trailing turn open' : ''}`,
|
|
626
663
|
} });
|
|
627
664
|
return { kind: 'success' };
|
|
628
665
|
};
|
package/lib/state.js
CHANGED
|
@@ -576,6 +576,13 @@ export function reduce(state, action) {
|
|
|
576
576
|
return reduce(state, { type: 'decision-submit' });
|
|
577
577
|
if (state.root === undefined || state.composer === '' && state.attachments.length === 0)
|
|
578
578
|
return [state, []];
|
|
579
|
+
if (state.root.status === 'running' && /^\/(?:btw|recap)(?:\s|$)/u.test(state.composer)) {
|
|
580
|
+
const cleared = { ...state, attachments: [], composer: '', exitArmed: false, recall: undefined, rewindArmed: false };
|
|
581
|
+
const [reported, effects] = reduce(cleared, {
|
|
582
|
+
type: 'runtime-error', message: 'finish or interrupt the turn first', rootId: state.root.id,
|
|
583
|
+
});
|
|
584
|
+
return [reported, [{ type: 'set-composer', text: '' }, ...effects]];
|
|
585
|
+
}
|
|
579
586
|
const mode = state.root.status === 'running' ? state.sendMode : 'next-turn';
|
|
580
587
|
return [{
|
|
581
588
|
...state, attachments: [], composer: '', exitArmed: false, recall: undefined, rewindArmed: false,
|
package/lib/transcript.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UserMessage } from '@deepseek-ai/dsh-llm';
|
|
1
|
+
import type { StreamChunk, UserMessage } from '@deepseek-ai/dsh-llm';
|
|
2
2
|
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
3
3
|
import type { TerminalCell } from './state.js';
|
|
4
4
|
import { type ToolPresenter } from './tool-presentation.js';
|
|
@@ -7,6 +7,8 @@ interface FoldOptions {
|
|
|
7
7
|
}
|
|
8
8
|
/** Project DSH-owned pending injections beside the durable transcript. */
|
|
9
9
|
export declare function pendingShellCells(messages: readonly UserMessage[]): readonly TerminalCell[];
|
|
10
|
+
/** Fold one transient DSH Assistant frame into the current pending cells. */
|
|
11
|
+
export declare function foldAssistantStream(current: readonly TerminalCell[], turn: number, step: number, chunk: StreamChunk): readonly TerminalCell[];
|
|
10
12
|
/** Fold one chronological DSH event slice into terminal-owned presentation cells. */
|
|
11
13
|
export declare function foldCells(events: readonly SessionEvent[], presenter?: ToolPresenter | undefined, options?: FoldOptions): readonly TerminalCell[];
|
|
12
14
|
export {};
|
package/lib/transcript.js
CHANGED
|
@@ -81,6 +81,57 @@ function compactSource(event) {
|
|
|
81
81
|
...(typeof source.sourceCommandId === 'string' ? { sourceCommandId: source.sourceCommandId } : {}),
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
+
function putAssistant(cells, assistants, turn, step, kind, text, complete) {
|
|
85
|
+
const key = `${turn}:${step}`;
|
|
86
|
+
const pair = assistants.get(key) ?? {};
|
|
87
|
+
const slot = kind === 'text' ? pair.text : pair.reasoning;
|
|
88
|
+
const cellKind = kind === 'text' ? 'assistant' : 'reasoning';
|
|
89
|
+
if (slot === undefined) {
|
|
90
|
+
const next = cells.length;
|
|
91
|
+
cells.push({
|
|
92
|
+
key: `assistant:${key}:${kind}`,
|
|
93
|
+
kind: cellKind,
|
|
94
|
+
...(!complete && kind === 'text' ? { pending: true } : {}),
|
|
95
|
+
text,
|
|
96
|
+
...(complete && kind === 'reasoning' ? { collapsed: true } : {}),
|
|
97
|
+
});
|
|
98
|
+
if (kind === 'text')
|
|
99
|
+
pair.text = next;
|
|
100
|
+
else
|
|
101
|
+
pair.reasoning = next;
|
|
102
|
+
assistants.set(key, pair);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const previous = cells[slot];
|
|
106
|
+
if (previous === undefined)
|
|
107
|
+
return;
|
|
108
|
+
const { pending: _pending, ...settled } = previous;
|
|
109
|
+
cells[slot] = {
|
|
110
|
+
...(complete ? settled : previous),
|
|
111
|
+
text: complete ? text : previous.text + text,
|
|
112
|
+
...(complete && kind === 'reasoning' ? { collapsed: true } : {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Fold one transient DSH Assistant frame into the current pending cells. */
|
|
116
|
+
export function foldAssistantStream(current, turn, step, chunk) {
|
|
117
|
+
const cells = [...current];
|
|
118
|
+
const pair = {};
|
|
119
|
+
cells.forEach((cell, slot) => {
|
|
120
|
+
if (cell.kind === 'assistant')
|
|
121
|
+
pair.text = slot;
|
|
122
|
+
else if (cell.kind === 'reasoning')
|
|
123
|
+
pair.reasoning = slot;
|
|
124
|
+
});
|
|
125
|
+
const assistants = new Map([[`${turn}:${step}`, pair]]);
|
|
126
|
+
if (chunk.type === 'text-delta')
|
|
127
|
+
putAssistant(cells, assistants, turn, step, 'text', chunk.text, false);
|
|
128
|
+
else if (chunk.type === 'reasoning-delta')
|
|
129
|
+
putAssistant(cells, assistants, turn, step, 'reasoning', chunk.text, false);
|
|
130
|
+
else if (chunk.type === 'block-end' && (chunk.block.type === 'text' || chunk.block.type === 'reasoning')) {
|
|
131
|
+
putAssistant(cells, assistants, turn, step, chunk.block.type, chunk.block.text, true);
|
|
132
|
+
}
|
|
133
|
+
return cells;
|
|
134
|
+
}
|
|
84
135
|
/** Fold one chronological DSH event slice into terminal-owned presentation cells. */
|
|
85
136
|
export function foldCells(events, presenter = createToolPresenter(() => undefined), options = {}) {
|
|
86
137
|
const toolPresenter = presenter ?? createToolPresenter(() => undefined);
|
|
@@ -94,39 +145,10 @@ export function foldCells(events, presenter = createToolPresenter(() => undefine
|
|
|
94
145
|
let planSlot;
|
|
95
146
|
let todoSlot;
|
|
96
147
|
let truncatedPrefix = options.truncatedStart === true;
|
|
97
|
-
const putAssistant = (turn, step, kind, text, complete) => {
|
|
98
|
-
const key = `${turn}:${step}`;
|
|
99
|
-
const pair = assistants.get(key) ?? {};
|
|
100
|
-
const slot = kind === 'text' ? pair.text : pair.reasoning;
|
|
101
|
-
const cellKind = kind === 'text' ? 'assistant' : 'reasoning';
|
|
102
|
-
if (slot === undefined) {
|
|
103
|
-
const next = cells.length;
|
|
104
|
-
cells.push({
|
|
105
|
-
key: `assistant:${key}:${kind}`,
|
|
106
|
-
kind: cellKind,
|
|
107
|
-
...(!complete && kind === 'text' ? { pending: true } : {}),
|
|
108
|
-
text,
|
|
109
|
-
...(complete && kind === 'reasoning' ? { collapsed: true } : {}),
|
|
110
|
-
});
|
|
111
|
-
if (kind === 'text')
|
|
112
|
-
pair.text = next;
|
|
113
|
-
else
|
|
114
|
-
pair.reasoning = next;
|
|
115
|
-
assistants.set(key, pair);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
const previous = cells[slot];
|
|
119
|
-
if (previous === undefined)
|
|
120
|
-
return;
|
|
121
|
-
const { pending: _pending, ...settled } = previous;
|
|
122
|
-
cells[slot] = {
|
|
123
|
-
...(complete ? settled : previous),
|
|
124
|
-
text: complete ? text : previous.text + text,
|
|
125
|
-
...(complete && kind === 'reasoning' ? { collapsed: true } : {}),
|
|
126
|
-
};
|
|
127
|
-
};
|
|
128
148
|
for (const event of events) {
|
|
129
149
|
switch (event.type) {
|
|
150
|
+
case 'system/message':
|
|
151
|
+
break;
|
|
130
152
|
case 'turn/start':
|
|
131
153
|
truncatedPrefix = false;
|
|
132
154
|
openTurns.add(event.data.turn);
|
|
@@ -160,25 +182,14 @@ export function foldCells(events, presenter = createToolPresenter(() => undefine
|
|
|
160
182
|
});
|
|
161
183
|
break;
|
|
162
184
|
}
|
|
163
|
-
case 'assistant/chunk': {
|
|
164
|
-
const { chunk, step, turn } = event.data;
|
|
165
|
-
if (chunk.type === 'text-delta')
|
|
166
|
-
putAssistant(turn, step, 'text', chunk.text, false);
|
|
167
|
-
else if (chunk.type === 'reasoning-delta')
|
|
168
|
-
putAssistant(turn, step, 'reasoning', chunk.text, false);
|
|
169
|
-
else if (chunk.type === 'block-end' && (chunk.block.type === 'text' || chunk.block.type === 'reasoning')) {
|
|
170
|
-
putAssistant(turn, step, chunk.block.type, chunk.block.text, true);
|
|
171
|
-
}
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
185
|
case 'assistant/message': {
|
|
175
186
|
const { message, step, turn } = event.data;
|
|
176
187
|
const text = blockText(message.content, 'text');
|
|
177
188
|
const reasoning = blockText(message.content, 'reasoning');
|
|
178
189
|
if (reasoning !== '')
|
|
179
|
-
putAssistant(turn, step, 'reasoning', reasoning, true);
|
|
190
|
+
putAssistant(cells, assistants, turn, step, 'reasoning', reasoning, true);
|
|
180
191
|
if (text !== '')
|
|
181
|
-
putAssistant(turn, step, 'text', text, true);
|
|
192
|
+
putAssistant(cells, assistants, turn, step, 'text', text, true);
|
|
182
193
|
break;
|
|
183
194
|
}
|
|
184
195
|
case 'tool/call': {
|
|
@@ -299,6 +310,8 @@ export function foldCells(events, presenter = createToolPresenter(() => undefine
|
|
|
299
310
|
break;
|
|
300
311
|
}
|
|
301
312
|
case 'turn/end':
|
|
313
|
+
if (event.data.reason.kind === 'interrupted')
|
|
314
|
+
break;
|
|
302
315
|
if (!openTurns.delete(event.data.turn)) {
|
|
303
316
|
if (!truncatedPrefix) {
|
|
304
317
|
cells.push({ key: `${event.seq}:error`, kind: 'error', text: `turn ${event.data.turn} ended without starting` });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antst/dashi",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.18",
|
|
4
4
|
"description": "A terminal UI for DeepSeek Harness",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -38,39 +38,39 @@
|
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@deepseek-ai/cordis": "4.0.2",
|
|
40
40
|
"@deepseek-ai/cordis-plugin-loader": "1.0.3",
|
|
41
|
-
"@deepseek-ai/dsh-agent": "0.1.
|
|
42
|
-
"@deepseek-ai/dsh-agent-instructions": "0.1.
|
|
43
|
-
"@deepseek-ai/dsh-agent-presets": "0.1.
|
|
44
|
-
"@deepseek-ai/dsh-api-session-controller": "0.1.
|
|
45
|
-
"@deepseek-ai/dsh-attachment": "0.1.
|
|
46
|
-
"@deepseek-ai/dsh-authorization": "0.1.
|
|
47
|
-
"@deepseek-ai/dsh-cmdline": "0.1.
|
|
48
|
-
"@deepseek-ai/dsh-commands": "0.1.
|
|
49
|
-
"@deepseek-ai/dsh-credentials": "0.1.
|
|
50
|
-
"@deepseek-ai/dsh-compaction": "0.1.
|
|
51
|
-
"@deepseek-ai/dsh-file-reference": "0.1.
|
|
52
|
-
"@deepseek-ai/dsh-fs": "0.1.
|
|
53
|
-
"@deepseek-ai/dsh-home-paths": "0.1.
|
|
54
|
-
"@deepseek-ai/dsh-host-plugin-inventory": "0.1.
|
|
55
|
-
"@deepseek-ai/dsh-jobs": "0.1.
|
|
56
|
-
"@deepseek-ai/dsh-llm": "0.1.
|
|
57
|
-
"@deepseek-ai/dsh-permission-presets": "0.1.
|
|
58
|
-
"@deepseek-ai/dsh-plan-mode": "0.1.
|
|
59
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.1.
|
|
60
|
-
"@deepseek-ai/dsh-schedule": "0.1.
|
|
61
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
62
|
-
"@deepseek-ai/dsh-session-projection": "0.1.
|
|
63
|
-
"@deepseek-ai/dsh-session-query": "0.1.
|
|
64
|
-
"@deepseek-ai/dsh-session-stats": "0.1.
|
|
65
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
66
|
-
"@deepseek-ai/dsh-shell": "0.1.
|
|
67
|
-
"@deepseek-ai/dsh-skill": "0.1.
|
|
68
|
-
"@deepseek-ai/dsh-subagent": "0.1.
|
|
69
|
-
"@deepseek-ai/dsh-system-prompt": "0.1.
|
|
70
|
-
"@deepseek-ai/dsh-token-meter": "0.1.
|
|
71
|
-
"@deepseek-ai/dsh-tool-todo": "0.1.
|
|
72
|
-
"@deepseek-ai/dsh-tools": "0.1.
|
|
73
|
-
"@deepseek-ai/dsh-user-approval": "0.1.
|
|
74
|
-
"@deepseek-ai/dsh-user-questions": "0.1.
|
|
41
|
+
"@deepseek-ai/dsh-agent": "0.1.5-rc.2",
|
|
42
|
+
"@deepseek-ai/dsh-agent-instructions": "0.1.5-rc.2",
|
|
43
|
+
"@deepseek-ai/dsh-agent-presets": "0.1.5-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-attachment": "0.1.5-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-authorization": "0.1.5-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-cmdline": "0.1.5-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-commands": "0.1.5-rc.2",
|
|
49
|
+
"@deepseek-ai/dsh-credentials": "0.1.5-rc.2",
|
|
50
|
+
"@deepseek-ai/dsh-compaction": "0.1.5-rc.2",
|
|
51
|
+
"@deepseek-ai/dsh-file-reference": "0.1.5-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-fs": "0.1.5-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-host-plugin-inventory": "0.1.5-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-jobs": "0.1.5-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-permission-presets": "0.1.5-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-plan-mode": "0.1.5-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.5-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-schedule": "0.1.5-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-session-projection": "0.1.5-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-session-query": "0.1.5-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-session-stats": "0.1.5-rc.2",
|
|
65
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
66
|
+
"@deepseek-ai/dsh-shell": "0.1.5-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-skill": "0.1.5-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-subagent": "0.1.5-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.5-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-token-meter": "0.1.5-rc.2",
|
|
71
|
+
"@deepseek-ai/dsh-tool-todo": "0.1.5-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-tools": "0.1.5-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-user-approval": "0.1.5-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-user-questions": "0.1.5-rc.2"
|
|
75
75
|
}
|
|
76
76
|
}
|