@agi-fans/dsh-tui 0.26.0 → 0.27.0
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/CHANGELOG.md +19 -2
- package/lib/commands/auth-account.js +2 -1
- package/lib/commands/auth-account.js.map +1 -1
- package/lib/definition.d.ts +5 -0
- package/lib/input/clipboard.js +2 -1
- package/lib/input/clipboard.js.map +1 -1
- package/lib/runtime/account-settings.js +4 -2
- package/lib/runtime/account-settings.js.map +1 -1
- package/lib/runtime/file-recovery-recording.js +1 -1
- package/lib/runtime/file-recovery-recording.js.map +1 -1
- package/lib/runtime/herdr-agent.d.ts +2 -0
- package/lib/runtime/herdr-agent.js +7 -1
- package/lib/runtime/herdr-agent.js.map +1 -1
- package/lib/runtime/provider-local.d.ts +5 -0
- package/lib/runtime/provider-local.js +39 -22
- package/lib/runtime/provider-local.js.map +1 -1
- package/lib/runtime/runner.js +1 -1
- package/lib/runtime/terminal-state.d.ts +4 -1
- package/lib/runtime/terminal-state.js +28 -7
- package/lib/runtime/terminal-state.js.map +1 -1
- package/lib/session/session-controller.js +88 -34
- package/lib/session/session-controller.js.map +1 -1
- package/lib/session/subagent-tracker.d.ts +9 -3
- package/lib/session/subagent-tracker.js.map +1 -1
- package/package.json +57 -57
package/lib/runtime/runner.js
CHANGED
|
@@ -169,7 +169,7 @@ async function run(ctx, tui, cancelled) {
|
|
|
169
169
|
await Promise.race([agent.whenIdle(), stopped.promise]);
|
|
170
170
|
if (cancelled.aborted)
|
|
171
171
|
break;
|
|
172
|
-
const joined = await Promise.race([ctx.get('subagents')?.
|
|
172
|
+
const joined = await Promise.race([ctx.get('subagents')?.waitForTreeWork(agent), stopped.promise]);
|
|
173
173
|
if (!joined)
|
|
174
174
|
break;
|
|
175
175
|
}
|
|
@@ -8,6 +8,7 @@ export interface AgentActivityStatus {
|
|
|
8
8
|
export type TerminalProgramState = AgentActivityState | 'done' | 'error';
|
|
9
9
|
export interface TerminalProgramStatus {
|
|
10
10
|
readonly state: TerminalProgramState;
|
|
11
|
+
readonly title?: string;
|
|
11
12
|
readonly message?: string;
|
|
12
13
|
readonly kind?: TerminalAttentionKind;
|
|
13
14
|
}
|
|
@@ -24,13 +25,15 @@ export declare function terminalProgressSequence(state: TerminalProgramState | '
|
|
|
24
25
|
/** These lifecycle reports can themselves create a terminal notification. */
|
|
25
26
|
export declare function programStatusNeedsAttention(state: TerminalProgramState): boolean;
|
|
26
27
|
export declare function settledProgramState(reason?: string): 'idle' | 'done' | 'error';
|
|
27
|
-
export declare function terminalTitle(mode: TerminalTitleMode, status: TerminalProgramStatus, session: string | undefined, cwd: string, branch?: string): string;
|
|
28
|
+
export declare function terminalTitle(mode: TerminalTitleMode, status: TerminalProgramStatus, session: string | undefined, cwd: string, branch?: string, nativeStatus?: boolean): string;
|
|
28
29
|
export declare class AgentActivityController {
|
|
29
30
|
#private;
|
|
30
31
|
get current(): AgentActivityStatus;
|
|
31
32
|
get program(): TerminalProgramStatus;
|
|
32
33
|
start(): AgentActivityStatus;
|
|
33
34
|
setRunning(running: boolean): AgentActivityStatus | undefined;
|
|
35
|
+
compactionStart(id?: string): AgentActivityStatus | undefined;
|
|
36
|
+
compactionEnd(id?: string, error?: string): AgentActivityStatus | undefined;
|
|
34
37
|
settled(reason: string): void;
|
|
35
38
|
resetResult(): void;
|
|
36
39
|
prompt(message?: string, kind?: TerminalAttentionKind): AgentActivityStatus | undefined;
|
|
@@ -15,9 +15,10 @@ function clean(value, limit) {
|
|
|
15
15
|
export function programStatusSequence(status) {
|
|
16
16
|
if (status === 'clear')
|
|
17
17
|
return '\x1b]7501;state=clear:app=omdsh\x1b\\';
|
|
18
|
+
const title = clean(status.title, 240);
|
|
18
19
|
const message = clean(status.message, 240);
|
|
19
20
|
const kind = status.state === 'blocked' && status.kind !== undefined ? `:kind=${status.kind}` : '';
|
|
20
|
-
return `\x1b]7501;state=${status.state}:app=omdsh${kind}${message === undefined ? '' : `:msg=${Buffer.from(message).toString('base64')}`}\x1b\\`;
|
|
21
|
+
return `\x1b]7501;state=${status.state}:app=omdsh${kind}${title === undefined ? '' : `:title=${Buffer.from(title).toString('base64')}`}${message === undefined ? '' : `:msg=${Buffer.from(message).toString('base64')}`}\x1b\\`;
|
|
21
22
|
}
|
|
22
23
|
export function workingDirectorySequence(cwd) {
|
|
23
24
|
// A directory separator also preserves trailing control bytes in Node's URL conversion.
|
|
@@ -45,22 +46,28 @@ export function settledProgramState(reason = 'completed') {
|
|
|
45
46
|
return 'error';
|
|
46
47
|
}
|
|
47
48
|
const STATE_LABELS = { idle: 'Ready', working: 'Working', blocked: 'Needs input', done: 'Done', error: 'Failed' };
|
|
48
|
-
export function terminalTitle(mode, status, session, cwd, branch) {
|
|
49
|
+
export function terminalTitle(mode, status, session, cwd, branch, nativeStatus = false) {
|
|
49
50
|
if (mode === 'off')
|
|
50
51
|
return '';
|
|
51
|
-
if (mode === 'session')
|
|
52
|
+
if (mode === 'session' || (mode === 'status' && nativeStatus))
|
|
52
53
|
return clean(session, 120) ?? '';
|
|
53
|
-
const
|
|
54
|
+
const workspace = [basename(cwd), branch].filter(Boolean).join(' · ');
|
|
55
|
+
if (nativeStatus)
|
|
56
|
+
return clean(workspace, 120) ?? '';
|
|
57
|
+
const subject = mode === 'workspace' ? workspace : session ?? basename(cwd);
|
|
54
58
|
return clean([STATE_LABELS[status.state], subject, 'omdsh'].filter(Boolean).join(' · '), 120) ?? '';
|
|
55
59
|
}
|
|
56
60
|
export class AgentActivityController {
|
|
57
61
|
#running = false;
|
|
62
|
+
#compacting;
|
|
58
63
|
#prompts = [];
|
|
59
64
|
#last;
|
|
60
65
|
#result = 'idle';
|
|
61
66
|
get current() {
|
|
62
67
|
const prompt = this.#prompts.at(-1);
|
|
63
|
-
return prompt === undefined
|
|
68
|
+
return prompt === undefined
|
|
69
|
+
? { state: this.#running || this.#compacting !== undefined ? 'working' : 'idle' }
|
|
70
|
+
: { state: 'blocked', ...prompt };
|
|
64
71
|
}
|
|
65
72
|
get program() {
|
|
66
73
|
const current = this.current;
|
|
@@ -68,13 +75,27 @@ export class AgentActivityController {
|
|
|
68
75
|
}
|
|
69
76
|
start() { return this.#publish(true); }
|
|
70
77
|
setRunning(running) {
|
|
71
|
-
if (running && !this.#running)
|
|
78
|
+
if (running && !this.#running && this.#compacting === undefined)
|
|
72
79
|
this.#result = 'idle';
|
|
73
80
|
this.#running = running;
|
|
74
81
|
return this.#publish();
|
|
75
82
|
}
|
|
83
|
+
compactionStart(id = '') {
|
|
84
|
+
if (!this.#running && this.#compacting === undefined)
|
|
85
|
+
this.#result = 'idle';
|
|
86
|
+
this.#compacting = id;
|
|
87
|
+
return this.#publish();
|
|
88
|
+
}
|
|
89
|
+
compactionEnd(id, error) {
|
|
90
|
+
if (this.#compacting === undefined || (id !== undefined && this.#compacting !== id))
|
|
91
|
+
return undefined;
|
|
92
|
+
this.#compacting = undefined;
|
|
93
|
+
if (!this.#running)
|
|
94
|
+
this.#result = error === undefined ? 'done' : 'error';
|
|
95
|
+
return this.#publish();
|
|
96
|
+
}
|
|
76
97
|
settled(reason) { this.#result = settledProgramState(reason); }
|
|
77
|
-
resetResult() { this.#result = 'idle'; }
|
|
98
|
+
resetResult() { this.#result = 'idle'; this.#compacting = undefined; }
|
|
78
99
|
prompt(message, kind = 'question') {
|
|
79
100
|
const text = clean(message, 240);
|
|
80
101
|
this.#prompts.push({ kind, ...(text === undefined ? {} : { message: text }) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-state.js","sourceRoot":"","sources":["../../src/runtime/terminal-state.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"terminal-state.js","sourceRoot":"","sources":["../../src/runtime/terminal-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,6 @@ import { isModelInvocable, isUserInvocable } from '@deepseek-ai/dsh-skill';
|
|
|
20
20
|
import { BUILTIN_SLASH_COMMANDS } from "../views/autocomplete.js";
|
|
21
21
|
import { readColdSessionLog } from '@deepseek-ai/dsh-session-query';
|
|
22
22
|
import { appendPluginRecord, pluginRecordOf, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
23
|
-
import { queueHostSubagentPrompt, steerHostSubagentPrompt } from '@deepseek-ai/dsh-subagent/internal';
|
|
24
23
|
import { firstVisibleStreamTime } from "../views/stream-time.js";
|
|
25
24
|
import { LiveAttemptTracker } from "./live-attempt-tracker.js";
|
|
26
25
|
import { blocksText } from "./content-text.js";
|
|
@@ -364,6 +363,47 @@ export async function createSubmissionMessage(submission, attachments) {
|
|
|
364
363
|
throw new Error('Cannot submit an empty message.');
|
|
365
364
|
return createUserMessage({ content, source: { kind: 'user' } });
|
|
366
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Build the host prompt parts for a subagent continuation. Unlike an ordinary
|
|
368
|
+
* Session prompt, the subagent Host admits browser images itself, so the draft
|
|
369
|
+
* crosses the seam as encoded bytes and files are refused here.
|
|
370
|
+
*/
|
|
371
|
+
function submissionPromptContent(submission) {
|
|
372
|
+
const imagePart = (image) => ({
|
|
373
|
+
type: 'image',
|
|
374
|
+
mediaType: image.mediaType,
|
|
375
|
+
data: Buffer.from(image.data).toString('base64'),
|
|
376
|
+
...(image.name === undefined ? {} : { name: image.name }),
|
|
377
|
+
});
|
|
378
|
+
const rejectFiles = () => {
|
|
379
|
+
throw new Error('Subagent follow-up does not accept files.');
|
|
380
|
+
};
|
|
381
|
+
const parts = [];
|
|
382
|
+
if (submission.segments === undefined || submission.segments.length === 0) {
|
|
383
|
+
if (submission.text !== '')
|
|
384
|
+
parts.push({ type: 'text', text: submission.text });
|
|
385
|
+
for (const image of submission.images)
|
|
386
|
+
parts.push(imagePart(image));
|
|
387
|
+
if ((submission.files?.length ?? 0) > 0)
|
|
388
|
+
rejectFiles();
|
|
389
|
+
return parts;
|
|
390
|
+
}
|
|
391
|
+
for (const segment of submission.segments) {
|
|
392
|
+
if (segment.kind === 'text') {
|
|
393
|
+
if (segment.text !== '')
|
|
394
|
+
parts.push({ type: 'text', text: segment.text });
|
|
395
|
+
}
|
|
396
|
+
else if (segment.kind === 'image') {
|
|
397
|
+
const image = submission.images[segment.index];
|
|
398
|
+
if (image !== undefined)
|
|
399
|
+
parts.push(imagePart(image));
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
rejectFiles();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return parts;
|
|
406
|
+
}
|
|
367
407
|
/** Rehydrate one durable human inbox message into an editable composer draft. */
|
|
368
408
|
export async function restoreSubmissionMessage(message, attachments) {
|
|
369
409
|
const text = blocksText(message.content);
|
|
@@ -468,7 +508,14 @@ export class SessionRuntime {
|
|
|
468
508
|
},
|
|
469
509
|
agentStatus: id => this.#ctx.get('agents')?.get(SessionId(id))?.status,
|
|
470
510
|
sessions: () => this.#ctx.get('sessions')?.list() ?? [],
|
|
471
|
-
listChildren: rootId => this.#ctx.get('subagents')?.
|
|
511
|
+
listChildren: rootId => this.#ctx.get('subagents')?.listDescendants(SessionId(rootId))
|
|
512
|
+
.then(entries => entries
|
|
513
|
+
.filter((entry) => entry.kind === 'child' && entry.depth === 1)
|
|
514
|
+
.map((entry) => ({
|
|
515
|
+
id: entry.id,
|
|
516
|
+
mode: entry.mode,
|
|
517
|
+
...(entry.label === undefined ? {} : { label: entry.label }),
|
|
518
|
+
}))),
|
|
472
519
|
publish: (roster) => {
|
|
473
520
|
this.#tui.setSubagents(this.#workflows.roster(roster));
|
|
474
521
|
if (this.#inspectedId === undefined)
|
|
@@ -584,6 +631,22 @@ export class SessionRuntime {
|
|
|
584
631
|
if (session === active.handle.agent.session) {
|
|
585
632
|
if (event.type === 'turn/end')
|
|
586
633
|
tui.setStatus(active.handle.agent.status, { root: true, outcome: event.data.reason.kind });
|
|
634
|
+
else if (this.#inspectedId !== undefined && event.type === 'compaction/start') {
|
|
635
|
+
tui.setStatus(active.handle.agent.status, {
|
|
636
|
+
root: true,
|
|
637
|
+
compaction: { phase: 'start', id: event.data.compactionId },
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
else if (this.#inspectedId !== undefined && event.type === 'compaction/end') {
|
|
641
|
+
tui.setStatus(active.handle.agent.status, {
|
|
642
|
+
root: true,
|
|
643
|
+
compaction: {
|
|
644
|
+
phase: 'end',
|
|
645
|
+
id: event.data.compactionId,
|
|
646
|
+
...(event.data.error === undefined ? {} : { error: event.data.error }),
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
}
|
|
587
650
|
if (this.#workflows.apply(event))
|
|
588
651
|
tui.setSubagents(this.#workflows.roster(this.#tracker.snapshot()));
|
|
589
652
|
if (this.#inspectedId === undefined) {
|
|
@@ -679,20 +742,19 @@ export class SessionRuntime {
|
|
|
679
742
|
return true;
|
|
680
743
|
}
|
|
681
744
|
#isOwnedDescendant(agent, root) {
|
|
745
|
+
if (agent === root)
|
|
746
|
+
return false;
|
|
682
747
|
const agents = this.#ctx.get('agents');
|
|
683
|
-
if (agents?.get(agent.id) !== agent
|
|
684
|
-
|
|
748
|
+
if (agents?.get(agent.id) !== agent || agents.get(root.id) !== root)
|
|
749
|
+
return false;
|
|
750
|
+
try {
|
|
751
|
+
// Runtime tree membership replaced registry ownership; durable lineage
|
|
752
|
+
// alone still matches an independently resumed runtime root.
|
|
753
|
+
return this.#ctx.get('subagents')?.rootOf(agent) === root;
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
685
756
|
return false;
|
|
686
|
-
// Durable lineage alone also matches an independently resumed runtime root.
|
|
687
|
-
let child = agent;
|
|
688
|
-
while (child !== root) {
|
|
689
|
-
const parentId = child.session.header.parentSession;
|
|
690
|
-
const parent = parentId === undefined ? undefined : agents.get(parentId);
|
|
691
|
-
if (parent === undefined || !agents.isOwnedBy(child.id, parent))
|
|
692
|
-
return false;
|
|
693
|
-
child = parent;
|
|
694
757
|
}
|
|
695
|
-
return true;
|
|
696
758
|
}
|
|
697
759
|
/** Accept only live descendants of the active root for shared approval prompts. */
|
|
698
760
|
beginSubagentApproval(agent) {
|
|
@@ -1972,18 +2034,10 @@ export class SessionRuntime {
|
|
|
1972
2034
|
this.#tui.notice('This subagent is a completed run and cannot take more messages.');
|
|
1973
2035
|
return;
|
|
1974
2036
|
}
|
|
1975
|
-
const live = this.#ctx.get('sessions')?.get(SessionId(childId));
|
|
1976
|
-
const rosterParent = this.#tracker.snapshot()?.agents.find(agent => agent.id === childId)?.parentId;
|
|
1977
|
-
const parentId = live?.header.parentSession ?? rosterParent;
|
|
1978
|
-
const parent = parentId === undefined || parentId === root.id
|
|
1979
|
-
? root
|
|
1980
|
-
: this.#ctx.get('agents')?.get(SessionId(parentId));
|
|
1981
2037
|
const subagents = this.#ctx.get('subagents');
|
|
1982
|
-
if (
|
|
2038
|
+
if (subagents === undefined) {
|
|
1983
2039
|
this.#tui.restoreInput(submission, childId);
|
|
1984
|
-
this.#tui.notice(
|
|
1985
|
-
? 'The parent of this subagent is not live, so it cannot take a follow-up.'
|
|
1986
|
-
: 'Subagent follow-up is unavailable in this composition.');
|
|
2040
|
+
this.#tui.notice('Subagent follow-up is unavailable in this composition.');
|
|
1987
2041
|
return;
|
|
1988
2042
|
}
|
|
1989
2043
|
const previous = this.#inspectDeliveryTail;
|
|
@@ -1991,24 +2045,24 @@ export class SessionRuntime {
|
|
|
1991
2045
|
this.#inspectDeliveryTail = new Promise(resolve => { release = resolve; });
|
|
1992
2046
|
const delivery = new AbortController();
|
|
1993
2047
|
this.#inspectDeliveries.add(delivery);
|
|
1994
|
-
let
|
|
2048
|
+
let cancelWait = () => { };
|
|
1995
2049
|
const cancelled = new Promise((_resolve, reject) => {
|
|
1996
|
-
|
|
1997
|
-
delivery.signal.addEventListener('abort',
|
|
2050
|
+
cancelWait = () => { reject(delivery.signal.reason); };
|
|
2051
|
+
delivery.signal.addEventListener('abort', cancelWait, { once: true });
|
|
1998
2052
|
});
|
|
1999
2053
|
try {
|
|
2000
2054
|
// Recalled messages can include attachments; admission must retain submission order.
|
|
2001
2055
|
await Promise.race([previous, cancelled]);
|
|
2002
2056
|
delivery.signal.throwIfAborted();
|
|
2003
|
-
// Attachment storage need not support cancellation; release the draft now
|
|
2004
|
-
// and ignore a later upload result before crossing Harness admission.
|
|
2005
|
-
const message = await Promise.race([createSubmissionMessage(submission, this.#ctx.get('attachments')), cancelled]);
|
|
2006
|
-
delivery.signal.removeEventListener('abort', cancelUpload);
|
|
2007
|
-
delivery.signal.throwIfAborted();
|
|
2008
2057
|
if (this.#inspectedId !== childId || this.#active?.handle.agent !== root)
|
|
2009
2058
|
throw new Error('The viewed session changed before delivery.');
|
|
2010
|
-
const
|
|
2011
|
-
const messageId = await
|
|
2059
|
+
const content = submissionPromptContent(submission);
|
|
2060
|
+
const messageId = (await subagents.prompt({
|
|
2061
|
+
requestId: randomUUID(),
|
|
2062
|
+
childSessionId: SessionId(childId),
|
|
2063
|
+
delivery: submission.delivery === 'steer' ? 'steer' : 'queue',
|
|
2064
|
+
content,
|
|
2065
|
+
}, delivery.signal)).messageId;
|
|
2012
2066
|
this.#tui.pendingInputs?.prepare?.(submission, messageId);
|
|
2013
2067
|
this.#tui.pendingInputs?.acknowledge?.(submission);
|
|
2014
2068
|
}
|
|
@@ -2018,7 +2072,7 @@ export class SessionRuntime {
|
|
|
2018
2072
|
this.#tui.notice(error instanceof Error ? error.message : String(error), { level: 'error' });
|
|
2019
2073
|
}
|
|
2020
2074
|
finally {
|
|
2021
|
-
delivery.signal.removeEventListener('abort',
|
|
2075
|
+
delivery.signal.removeEventListener('abort', cancelWait);
|
|
2022
2076
|
this.#inspectDeliveries.delete(delivery);
|
|
2023
2077
|
release();
|
|
2024
2078
|
}
|