@antst/dashi 0.1.0-alpha.13 → 0.1.0-alpha.14
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.js +9 -4
- package/lib/session-runtime.js +18 -15
- 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.js
CHANGED
|
@@ -7,6 +7,13 @@ export function humanPrompt(event) {
|
|
|
7
7
|
.map(block => block.text).join('');
|
|
8
8
|
return text === '' ? undefined : text;
|
|
9
9
|
}
|
|
10
|
+
function shellInput(message) {
|
|
11
|
+
if (message.source.kind !== 'plugin' || message.source.plugin !== 'dashi'
|
|
12
|
+
|| message.source.form !== 'notice')
|
|
13
|
+
return undefined;
|
|
14
|
+
const first = message.content.find(block => block.type === 'text')?.text.split(/\r?\n/u)[0];
|
|
15
|
+
return first?.startsWith('$ ') === true ? `!${first.slice(2)}` : undefined;
|
|
16
|
+
}
|
|
10
17
|
/** Recover one composer input from DSH's durable log. */
|
|
11
18
|
export function historyInput(event) {
|
|
12
19
|
const prompt = humanPrompt(event);
|
|
@@ -15,11 +22,9 @@ export function historyInput(event) {
|
|
|
15
22
|
if (event.type === 'command/run' && event.data.args !== undefined) {
|
|
16
23
|
return `/${event.data.name}${event.data.args}`;
|
|
17
24
|
}
|
|
18
|
-
if (event.type !== '
|
|
19
|
-
|| event.data.source.plugin !== 'dashi' || event.data.source.form !== 'notice')
|
|
25
|
+
if (event.type !== 'agent/inbox/spliced')
|
|
20
26
|
return undefined;
|
|
21
|
-
|
|
22
|
-
return first?.startsWith('$ ') === true ? `!${first.slice(2)}` : undefined;
|
|
27
|
+
return event.data.inserted.map(shellInput).find(input => input !== undefined);
|
|
23
28
|
}
|
|
24
29
|
export function historyInputs(events) {
|
|
25
30
|
return events.flatMap(event => historyInput(event) ?? []);
|
package/lib/session-runtime.js
CHANGED
|
@@ -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);
|
|
@@ -1279,7 +1281,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
1279
1281
|
get initialHasMore() { return current().hasMore; },
|
|
1280
1282
|
get initialPrompts() { return historyInputs(current().events); },
|
|
1281
1283
|
get root() { return current().root; },
|
|
1282
|
-
get statusLine() { const bound = current(); return sessionStatusLine(ctx, bound.agent, bound.root, bound.
|
|
1284
|
+
get statusLine() { const bound = current(); return sessionStatusLine(ctx, bound.agent, bound.root, bound.repo); },
|
|
1283
1285
|
get summary() { return `Resume with: dsh --profile dashi --resume ${current().root.id}`; },
|
|
1284
1286
|
activate,
|
|
1285
1287
|
async attach(path) {
|
|
@@ -1428,6 +1430,7 @@ export async function createSessionRuntime(ctx, options, tuiRoot, dispatch, deci
|
|
|
1428
1430
|
bound.agent.inject(message);
|
|
1429
1431
|
await ctx.sessions.flush(bound.agent.session);
|
|
1430
1432
|
});
|
|
1433
|
+
dispatch({ type: 'prompt-recorded', rootId: bound.root.id, text: `!${command}` });
|
|
1431
1434
|
publish(bound);
|
|
1432
1435
|
return;
|
|
1433
1436
|
}
|