@deepseek-harness-tui/dsh-tui 0.7.1 → 0.7.2
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 +6 -4
- package/bin/dsh-tui.js +37 -1
- package/cordis.patch.yml +10 -0
- package/lib/types/commands.js +1 -1
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +1 -1
- package/lib/types/components/design-system/ThemedText.d.ts +2 -2
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedText.js +1 -3
- package/lib/types/components/sessions/SessionListRow.d.ts +26 -0
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionListRow.js +35 -0
- package/lib/types/components/sessions/SessionPreview.d.ts +25 -0
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionPreview.js +60 -0
- package/lib/types/dsh-adapter/channel.d.ts +9 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +31 -73
- package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +8 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +1 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +10 -1
- package/lib/types/dsh-adapter/packaged-presets.d.ts +22 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/packaged-presets.js +90 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +46 -4
- package/lib/types/dsh-adapter/presets.d.ts +16 -0
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +23 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts +29 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/digest.js +224 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts +100 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/frames.js +246 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts +50 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/header.js +62 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts +17 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/index.js +15 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts +41 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/list.js +213 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts +48 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/store.js +164 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts +142 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/types.js +14 -0
- package/lib/types/i18n.d.ts +118 -14
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +36 -6
- package/lib/types/screens/Chat.d.ts +3 -2
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +28 -144
- package/lib/types/screens/SessionBrowser.d.ts +37 -0
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -0
- package/lib/types/screens/SessionBrowser.js +437 -0
- package/lib/types/sessionHistory.d.ts +0 -8
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessions/format.d.ts +117 -0
- package/lib/types/sessions/format.d.ts.map +1 -0
- package/lib/types/sessions/format.js +244 -0
- package/lib/types/sessions/view.d.ts +125 -0
- package/lib/types/sessions/view.d.ts.map +1 -0
- package/lib/types/sessions/view.js +222 -0
- package/package.json +6 -2
- package/presets/liangshen/.dsh-tui-managed.json +5 -0
- package/presets/liangshen/agent.cordis.yml +389 -0
- package/presets/liangshen/compaction-epoch.mjs +81 -0
- package/presets/liangshen/custom-bash.mjs +126 -0
- package/presets/liangshen/instruction-hint.mjs +181 -0
- package/presets/liangshen/preset.yml +3 -0
- package/presets/liangshen/skill-search.mjs +142 -0
- package/presets/liangshen/tool-bootstrap.mjs +300 -0
- package/lib/types/components/ResumePicker.d.ts +0 -26
- package/lib/types/components/ResumePicker.d.ts.map +0 -1
- package/lib/types/components/ResumePicker.js +0 -50
|
@@ -8,8 +8,9 @@ import { renderContextSections, renderPrompt } from '@deepseek-ai/dsh-system-pro
|
|
|
8
8
|
import { loadBaselineInstructions } from '@deepseek-ai/dsh-agent-instructions';
|
|
9
9
|
import { extname, isAbsolute, join } from 'node:path';
|
|
10
10
|
import { completeCommands, isLocalCommandName, LOCAL_COMMANDS, parseCommandName } from '../commands.js';
|
|
11
|
-
import { clearResumeTarget, forgetSession,
|
|
12
|
-
import { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes,
|
|
11
|
+
import { clearResumeTarget, forgetSession, readResumeTarget, touchSession, writeResumeTarget } from '../sessionHistory.js';
|
|
12
|
+
import { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, sessionsRoots } from './compat/index.js';
|
|
13
|
+
import { listSummaries, locateSession, noteBranch, previewSession, } from './sessions/index.js';
|
|
13
14
|
import { writeActivityFrames } from '../activityPrefs.js';
|
|
14
15
|
import { readEffortPref, writeEffortPref } from '../effortPrefs.js';
|
|
15
16
|
import { readModelPref, writeModelPref } from '../modelPrefs.js';
|
|
@@ -256,18 +257,8 @@ function coalesceReplayEvents(events) {
|
|
|
256
257
|
}
|
|
257
258
|
/** Buffer below the context window at which CC warns (autoCompact.ts). */
|
|
258
259
|
const CONTEXT_WARNING_BUFFER_TOKENS = 20_000;
|
|
259
|
-
/** How many
|
|
260
|
-
|
|
261
|
-
const SESSION_TITLE_DEPTH = 20;
|
|
262
|
-
/** Picker title cap, in characters. */
|
|
263
|
-
const SESSION_TITLE_LIMIT = 40;
|
|
264
|
-
/** One-line session title: whitespace folded, capped with an ellipsis. */
|
|
265
|
-
function shortenTitle(text) {
|
|
266
|
-
const flat = text.replace(/\s+/g, ' ').trim();
|
|
267
|
-
return flat.length <= SESSION_TITLE_LIMIT
|
|
268
|
-
? flat
|
|
269
|
-
: `${flat.slice(0, SESSION_TITLE_LIMIT - 1)}…`;
|
|
270
|
-
}
|
|
260
|
+
/** How many trailing exchanges the browser's preview pane asks for. */
|
|
261
|
+
const PREVIEW_ENTRIES = 8;
|
|
271
262
|
/** Resolve once a `turn/end` event newer than `fromSeq` lands in the session
|
|
272
263
|
* log (Agent.cancel closes the turn asynchronously), or when the timeout
|
|
273
264
|
* expires. Polling the session log is race-free here: fork reads the same
|
|
@@ -303,7 +294,11 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
303
294
|
// command/run + command/done records). Absent the service, only the
|
|
304
295
|
// built-in local commands exist.
|
|
305
296
|
const commandService = ctx.get('commands');
|
|
306
|
-
|
|
297
|
+
// Workspace registry runtime (optional service, issue #183): mounted by
|
|
298
|
+
// the bundle patch's dsh-tui-workspaces row; absent the row (stale patch
|
|
299
|
+
// or a bare embedder), degrade to the local-only runtime. plugin.ts owns
|
|
300
|
+
// the degraded-boot warning for profile launches.
|
|
301
|
+
const workspaceService = ctx.get('tuiWorkspaces') ?? createLocalWorkspaceRuntime();
|
|
307
302
|
const commandTrees = ctx.get('tuiCommandTrees');
|
|
308
303
|
// Shift+Tab session-mode cycle: cordis.yml `modes` wins; absent/empty/
|
|
309
304
|
// atom-less → the built-in default/plan/full cycle (sessionModes.ts).
|
|
@@ -1670,61 +1665,21 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1670
1665
|
return listFilesDeep(fs, state.cwd);
|
|
1671
1666
|
},
|
|
1672
1667
|
async listSessions() {
|
|
1673
|
-
//
|
|
1674
|
-
//
|
|
1668
|
+
// Every stored session, classified and unfiltered. Which of them a
|
|
1669
|
+
// surface shows — this project only, conversations only, sub-agent runs
|
|
1670
|
+
// folded away — is a view decision, and keeping it out of here is what
|
|
1671
|
+
// lets the browser toggle those views without re-reading a single log.
|
|
1675
1672
|
const persistence = ctx.get('sessionPersistence');
|
|
1676
1673
|
if (!persistence)
|
|
1677
1674
|
return [];
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
// MRU ordering: DSH headers carry only createdAt, so dsh-tui keeps its
|
|
1684
|
-
// own last-used timestamps (touchSession on resume/submit/new) and
|
|
1685
|
-
// falls back to createdAt for sessions never touched in this install.
|
|
1686
|
-
const lastUsed = readLastUsed();
|
|
1687
|
-
const records = local
|
|
1688
|
-
.map(header => ({
|
|
1689
|
-
id: header.id,
|
|
1690
|
-
// Titles load lazily below (first user message); until then the
|
|
1691
|
-
// cwd basename stands in (matching the status line), with a
|
|
1692
|
-
// short id when absent.
|
|
1693
|
-
title: basename(header.cwd ?? '') || `session ${String(header.id).slice(0, 8)}`,
|
|
1694
|
-
cwd: header.cwd ?? '',
|
|
1695
|
-
createdAt: header.createdAt,
|
|
1696
|
-
updatedAt: lastUsed[header.id] ?? header.createdAt,
|
|
1697
|
-
}))
|
|
1698
|
-
.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
1699
|
-
// Title = the session's session/title event (auto: first prompt;
|
|
1700
|
-
// manual: /rename), else its FIRST user message — the picker's most
|
|
1701
|
-
// useful label. Read via the tolerant compat log reader instead of
|
|
1702
|
-
// persistence.load: the backend validates every event against
|
|
1703
|
-
// KNOWN_SESSION_EVENT_TYPES and throws the whole load on an unmarked
|
|
1704
|
-
// an unregistered third-party type, which would otherwise leave the
|
|
1705
|
-
// affected session titled with the cwd
|
|
1706
|
-
// basename. An unreadable log keeps the basename fallback.
|
|
1707
|
-
const empty = new Set();
|
|
1708
|
-
for (const record of records.slice(0, SESSION_TITLE_DEPTH)) {
|
|
1709
|
-
const info = readSessionTitleFromLog(String(record.id));
|
|
1710
|
-
if (info === undefined)
|
|
1711
|
-
continue; // keep the basename fallback
|
|
1712
|
-
if (!info.hasUserMessage) {
|
|
1713
|
-
// Launch artifact — a session with no user message holds no
|
|
1714
|
-
// conversation to resume, so drop it from the picker (its
|
|
1715
|
-
// createdAt-only updatedAt would otherwise pin it near the
|
|
1716
|
-
// top forever, one per dsh-tui launch).
|
|
1717
|
-
empty.add(record.id);
|
|
1718
|
-
continue;
|
|
1719
|
-
}
|
|
1720
|
-
if (info.title !== undefined)
|
|
1721
|
-
record.title = shortenTitle(info.title);
|
|
1722
|
-
}
|
|
1723
|
-
return records.filter(record => !empty.has(record.id));
|
|
1724
|
-
}
|
|
1725
|
-
catch {
|
|
1675
|
+
return listSummaries(persistence);
|
|
1676
|
+
},
|
|
1677
|
+
async previewSession(sessionId) {
|
|
1678
|
+
const persistence = ctx.get('sessionPersistence');
|
|
1679
|
+
if (!persistence)
|
|
1726
1680
|
return [];
|
|
1727
|
-
|
|
1681
|
+
const path = await locateSession(persistence, sessionId);
|
|
1682
|
+
return path === undefined ? [] : previewSession(path, PREVIEW_ENTRIES);
|
|
1728
1683
|
},
|
|
1729
1684
|
setResumeTarget(sessionId) {
|
|
1730
1685
|
writeResumeTarget(sessionId);
|
|
@@ -1764,12 +1719,10 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1764
1719
|
}
|
|
1765
1720
|
if (appendSessionTitle(sessionId, title) !== 'appended')
|
|
1766
1721
|
return false;
|
|
1767
|
-
//
|
|
1768
|
-
//
|
|
1769
|
-
//
|
|
1770
|
-
//
|
|
1771
|
-
// title event is durable. A rename IS user interaction with the
|
|
1772
|
-
// session — touching it pulls it into the title window.
|
|
1722
|
+
// The append changed the log, so the next listing sees a new revision,
|
|
1723
|
+
// re-derives, and reads back the very title event just written — no
|
|
1724
|
+
// second path to the same answer. Touching it is about ordering, not
|
|
1725
|
+
// titles: a rename is user interaction, so the row belongs at the top.
|
|
1773
1726
|
touchSession(sessionId);
|
|
1774
1727
|
return true;
|
|
1775
1728
|
},
|
|
@@ -3072,6 +3025,11 @@ ${output}
|
|
|
3072
3025
|
const branch = result.stdout.text.trim();
|
|
3073
3026
|
if (branch !== '') {
|
|
3074
3027
|
state.gitBranch = branch;
|
|
3028
|
+
// Note it against the session too. A session log records no branch,
|
|
3029
|
+
// and nothing can reconstruct one after the fact, so the browser can
|
|
3030
|
+
// only show a branch for sessions this install actually used — which
|
|
3031
|
+
// is exactly what the column claims.
|
|
3032
|
+
noteBranch(agent.session.id, branch);
|
|
3075
3033
|
state.emit();
|
|
3076
3034
|
}
|
|
3077
3035
|
})
|
|
@@ -3084,7 +3042,7 @@ ${output}
|
|
|
3084
3042
|
refreshGitBranch();
|
|
3085
3043
|
return state;
|
|
3086
3044
|
}
|
|
3087
|
-
/**
|
|
3045
|
+
/** Trailing path segment (`C:/a/b` → `b`). */
|
|
3088
3046
|
function basename(path) {
|
|
3089
3047
|
const parts = path.split(/[\\/]/);
|
|
3090
3048
|
return parts[parts.length - 1] ?? path;
|
|
@@ -18,5 +18,5 @@
|
|
|
18
18
|
* registration API (issue #153).
|
|
19
19
|
* @module @deepseek-harness-tui/dsh-tui/compat
|
|
20
20
|
*/
|
|
21
|
-
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
21
|
+
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, findSessionLogFile, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
22
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,6BAA6B,EAC7B,0BAA0B,EAC1B,uBAAuB,EACvB,aAAa,GACd,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,6BAA6B,EAC7B,kBAAkB,EAClB,0BAA0B,EAC1B,uBAAuB,EACvB,aAAa,GACd,MAAM,iBAAiB,CAAA"}
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
* registration API (issue #153).
|
|
19
19
|
* @module @deepseek-harness-tui/dsh-tui/compat
|
|
20
20
|
*/
|
|
21
|
-
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
21
|
+
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, findSessionLogFile, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
@@ -14,6 +14,14 @@ export declare const LEGACY_SESSION_EVENT_TYPES: readonly string[];
|
|
|
14
14
|
* explicit DSH_TUI_SESSION_ROOT always outranks the defaults.
|
|
15
15
|
*/
|
|
16
16
|
export declare function sessionsRoots(): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Locate a session's log by scanning workspace directories for the session
|
|
19
|
+
* id — deliberately NOT replicating the persistence plugin's workspace-key
|
|
20
|
+
* sanitization, so the helpers survive upstream key-scheme changes.
|
|
21
|
+
* @param sessionId - Session id (directory name under each workspace dir).
|
|
22
|
+
* @returns Absolute path of session.jsonl.zstd, or undefined when absent.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findSessionLogFile(sessionId: string): string | undefined;
|
|
17
25
|
/**
|
|
18
26
|
* Register every {@link LEGACY_SESSION_EVENT_TYPES} type as known in EVERY
|
|
19
27
|
* reachable KNOWN_SESSION_EVENT_TYPES copy, ahead of the strict read path
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionLog.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/sessionLog.ts"],"names":[],"mappings":"AAqDA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAAwB,CAAA;AAKhF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CASxC;
|
|
1
|
+
{"version":3,"file":"sessionLog.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/sessionLog.ts"],"names":[],"mappings":"AAqDA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAAwB,CAAA;AAKhF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CASxC;AAaD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAexE;AA+BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAkCpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAyBzD;AAyBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,aAAa,CAwB/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAwB7E"}
|
|
@@ -88,7 +88,7 @@ function isSafeSessionId(sessionId) {
|
|
|
88
88
|
* @param sessionId - Session id (directory name under each workspace dir).
|
|
89
89
|
* @returns Absolute path of session.jsonl.zstd, or undefined when absent.
|
|
90
90
|
*/
|
|
91
|
-
function findSessionLogFile(sessionId) {
|
|
91
|
+
export function findSessionLogFile(sessionId) {
|
|
92
92
|
if (!isSafeSessionId(sessionId))
|
|
93
93
|
return undefined;
|
|
94
94
|
for (const root of sessionsRoots()) {
|
|
@@ -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;AAEzD,eAAO,MAAM,IAAI,YAAY,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;AAEzD,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;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CA0BhC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvE"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import Schema from '@deepseek-ai/schemastery';
|
|
2
2
|
export const name = 'dsh-tui';
|
|
3
|
-
|
|
3
|
+
// `tuiWorkspaces` must stay OUT of this code-level inject (issue #183): the
|
|
4
|
+
// dsh CLI resolves the bundle's cordis.patch.yml from the FIRST copy of this
|
|
5
|
+
// package found from its own install anchor (typically the global launcher),
|
|
6
|
+
// while the Loader imports the plugin module from the profile's copy. When
|
|
7
|
+
// the two copies skew, the patch may predate the dsh-tui-workspaces row — a
|
|
8
|
+
// hard inject here then deadlocks the whole tree at boot ("pending (waiting
|
|
9
|
+
// for service: tuiWorkspaces)"). The bundle patch keeps tuiWorkspaces in the
|
|
10
|
+
// row-level inject purely as an ordering guarantee when the row exists; when
|
|
11
|
+
// it does not, plugin.ts/channel.ts fall back to a local workspace runtime.
|
|
12
|
+
export const inject = ['agents'];
|
|
4
13
|
export const Config = Schema.object({
|
|
5
14
|
sessionId: Schema.string().required(false),
|
|
6
15
|
// No schema defaults on the route: a `.default()` here would make an
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface PackagedPresetResult {
|
|
2
|
+
id: string;
|
|
3
|
+
status: 'installed' | 'updated' | 'current' | 'conflict';
|
|
4
|
+
}
|
|
5
|
+
export interface PackagedPresetOptions {
|
|
6
|
+
dshHome?: string;
|
|
7
|
+
sourceRoot?: string;
|
|
8
|
+
moduleUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Resolve the package asset in both src/tsx and compiled npm layouts. */
|
|
11
|
+
export declare function packagedPresetRoot(moduleUrl?: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Materialize presets shipped by dsh-tui into the Harness user preset root.
|
|
14
|
+
*
|
|
15
|
+
* The official launcher replaces the roster's configured roots with its own
|
|
16
|
+
* shipped root at the end of profile composition, so a bundle patch cannot
|
|
17
|
+
* add a second system root. The user root is the roster's supported extension
|
|
18
|
+
* seam and is discovered on every list/resolve call. Existing unmarked
|
|
19
|
+
* directories are never overwritten.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ensurePackagedPresets(options?: PackagedPresetOptions): PackagedPresetResult[];
|
|
22
|
+
//# sourceMappingURL=packaged-presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packaged-presets.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/packaged-presets.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAA;CACzD;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAmBD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,MAAwB,GAAG,MAAM,CAQ9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,GAAG,oBAAoB,EAAE,CAkDjG"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const OWNER = '@deepseek-harness-tui/dsh-tui';
|
|
7
|
+
const MARKER = '.dsh-tui-managed.json';
|
|
8
|
+
function readMarker(directory) {
|
|
9
|
+
try {
|
|
10
|
+
const value = JSON.parse(readFileSync(join(directory, MARKER), 'utf8'));
|
|
11
|
+
if (value.owner === OWNER
|
|
12
|
+
&& typeof value.preset === 'string'
|
|
13
|
+
&& typeof value.revision === 'string'
|
|
14
|
+
&& value.revision.length > 0) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// An absent or user-authored marker means this directory is not ours.
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Resolve the package asset in both src/tsx and compiled npm layouts. */
|
|
24
|
+
export function packagedPresetRoot(moduleUrl = import.meta.url) {
|
|
25
|
+
const directory = dirname(fileURLToPath(moduleUrl));
|
|
26
|
+
const candidates = [join(directory, '../../presets'), join(directory, '../../../presets')];
|
|
27
|
+
const found = candidates.find(candidate => existsSync(candidate));
|
|
28
|
+
if (found === undefined) {
|
|
29
|
+
throw new Error(`dsh-tui: packaged preset root is missing (checked ${candidates.join(', ')})`);
|
|
30
|
+
}
|
|
31
|
+
return found;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Materialize presets shipped by dsh-tui into the Harness user preset root.
|
|
35
|
+
*
|
|
36
|
+
* The official launcher replaces the roster's configured roots with its own
|
|
37
|
+
* shipped root at the end of profile composition, so a bundle patch cannot
|
|
38
|
+
* add a second system root. The user root is the roster's supported extension
|
|
39
|
+
* seam and is discovered on every list/resolve call. Existing unmarked
|
|
40
|
+
* directories are never overwritten.
|
|
41
|
+
*/
|
|
42
|
+
export function ensurePackagedPresets(options = {}) {
|
|
43
|
+
const sourceRoot = options.sourceRoot ?? packagedPresetRoot(options.moduleUrl);
|
|
44
|
+
const dshHome = options.dshHome ?? process.env.DSH_HOME ?? join(homedir(), '.dsh');
|
|
45
|
+
const targetRoot = join(dshHome, '.agent-presets');
|
|
46
|
+
const results = [];
|
|
47
|
+
for (const entry of readdirSync(sourceRoot, { withFileTypes: true })) {
|
|
48
|
+
if (!entry.isDirectory())
|
|
49
|
+
continue;
|
|
50
|
+
const id = entry.name;
|
|
51
|
+
const source = join(sourceRoot, id);
|
|
52
|
+
const sourceMarker = readMarker(source);
|
|
53
|
+
if (sourceMarker === undefined || sourceMarker.preset !== id) {
|
|
54
|
+
throw new Error(`dsh-tui: packaged preset ${id} has no valid ${MARKER}`);
|
|
55
|
+
}
|
|
56
|
+
const target = join(targetRoot, id);
|
|
57
|
+
if (!existsSync(target)) {
|
|
58
|
+
mkdirSync(targetRoot, { recursive: true });
|
|
59
|
+
cpSync(source, target, { recursive: true, force: false, errorOnExist: true });
|
|
60
|
+
results.push({ id, status: 'installed' });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const targetMarker = readMarker(target);
|
|
64
|
+
if (targetMarker === undefined || targetMarker.preset !== id) {
|
|
65
|
+
results.push({ id, status: 'conflict' });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (targetMarker.revision === sourceMarker.revision) {
|
|
69
|
+
results.push({ id, status: 'current' });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const suffix = `${process.pid}-${randomUUID()}`;
|
|
73
|
+
const staged = join(targetRoot, `.${id}.staged-${suffix}`);
|
|
74
|
+
const backup = join(targetRoot, `.${id}.backup-${suffix}`);
|
|
75
|
+
cpSync(source, staged, { recursive: true, force: false, errorOnExist: true });
|
|
76
|
+
try {
|
|
77
|
+
renameSync(target, backup);
|
|
78
|
+
renameSync(staged, target);
|
|
79
|
+
rmSync(backup, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (!existsSync(target) && existsSync(backup))
|
|
83
|
+
renameSync(backup, target);
|
|
84
|
+
rmSync(staged, { recursive: true, force: true });
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
results.push({ id, status: 'updated' });
|
|
88
|
+
}
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
@@ -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;AAClD,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;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AA6BnC;;;;;;;;GAQG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2bvE;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"}
|
|
@@ -13,7 +13,8 @@ import { readActivityFrames } from '../activityPrefs.js';
|
|
|
13
13
|
import { readModelPref } from '../modelPrefs.js';
|
|
14
14
|
import { explicitModelRoute, recordedModelRoute, resolveModelRoute, validateModelRoute } from '../modelRoute.js';
|
|
15
15
|
import { readPresetPref } from '../presetPrefs.js';
|
|
16
|
-
import { composePreset, resolvePersistedPreset, runningPresetOf } from './presets.js';
|
|
16
|
+
import { composePreset, filterMinimalPresetTools, resolvePersistedPreset, runningPresetOf } from './presets.js';
|
|
17
|
+
import { ensurePackagedPresets } from './packaged-presets.js';
|
|
17
18
|
import { ensureLegacySessionEventTypes } from './compat/index.js';
|
|
18
19
|
import { clearResumeTarget, writeResumeTarget } from '../sessionHistory.js';
|
|
19
20
|
import { resolveSessionCwd } from '../utils/workspaceRoot.js';
|
|
@@ -22,6 +23,7 @@ import { isLang, resolveStartupLang, setLang, t } from '../i18n.js';
|
|
|
22
23
|
import { detectLegacyEnv, migrateLegacyDataDir, RENAMED_ENV } from '../utils/paths.js';
|
|
23
24
|
import { Chat } from '../screens/Chat.js';
|
|
24
25
|
import { attachSessionToWorkspace } from './workspace.js';
|
|
26
|
+
import { createLocalWorkspaceRuntime } from './workspaces.js';
|
|
25
27
|
import { render, ThemeProvider, AlternateScreen } from '../ui.js';
|
|
26
28
|
import instances from '../ink/instances.js';
|
|
27
29
|
import { cursorMove, DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, DISABLE_WIN32_INPUT_MODE } from '../ink/termio/csi.js';
|
|
@@ -40,6 +42,22 @@ export async function apply(ctx, config) {
|
|
|
40
42
|
if (!process.stdout.isTTY) {
|
|
41
43
|
throw new Error('dsh-tui requires an interactive terminal (stdout must be a TTY).');
|
|
42
44
|
}
|
|
45
|
+
// The official profile launcher owns the system preset root and replaces
|
|
46
|
+
// any bundle-supplied roots at boot. Install dsh-tui's bundled presets via
|
|
47
|
+
// the roster's supported user-root seam before resolving the first agent.
|
|
48
|
+
// Never overwrite an existing directory unless it carries our marker.
|
|
49
|
+
try {
|
|
50
|
+
for (const result of ensurePackagedPresets()) {
|
|
51
|
+
if (result.status === 'conflict') {
|
|
52
|
+
ctx.logger.warn(`dsh-tui: packaged preset "${result.id}" was not installed because an unmanaged preset already uses that id`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// A read-only home must not make the whole terminal unusable; the other
|
|
58
|
+
// official and user presets remain available.
|
|
59
|
+
ctx.logger.warn(`dsh-tui: unable to install packaged presets (${error instanceof Error ? error.message : String(error)})`);
|
|
60
|
+
}
|
|
43
61
|
// Data-directory rename (~/.dsh-cc → ~/.dsh-tui, issue #120): copy the
|
|
44
62
|
// legacy directory before ANY preference read below (resolveStartupLang
|
|
45
63
|
// already touches lang.json). Copy, not move — old launchers keep working
|
|
@@ -98,6 +116,16 @@ export async function apply(ctx, config) {
|
|
|
98
116
|
// the inject proxy.
|
|
99
117
|
const userQuestions = ctx.get('userQuestions') ?? new UserQuestionService(ctx);
|
|
100
118
|
ctx.plugin(toolAskUser);
|
|
119
|
+
// The host-level tool mount above is intentional for the TUI and for user
|
|
120
|
+
// presets, but the official Minimal preset is a strict two-tool trajectory
|
|
121
|
+
// (persistent bash + str_replace_editor). Filter only that preset at the
|
|
122
|
+
// final assembly boundary. Reading the session on every assembly also makes
|
|
123
|
+
// blank-session /preset switches and resumed sessions behave correctly.
|
|
124
|
+
ctx.on('system-prompt/assemble', async (_assembly, context, next) => {
|
|
125
|
+
const assembled = await next();
|
|
126
|
+
const presetId = context.agent === undefined ? undefined : runningPresetOf(context.agent.session);
|
|
127
|
+
return filterMinimalPresetTools(assembled, presetId);
|
|
128
|
+
});
|
|
101
129
|
const questionStore = new QuestionStore();
|
|
102
130
|
// Packaged skills (/audit, /bug, …): contribute them through the host's
|
|
103
131
|
// skill registry so they resolve with zero manual copying.
|
|
@@ -170,9 +198,22 @@ export async function apply(ctx, config) {
|
|
|
170
198
|
// see the repository, not an arbitrary launch subdirectory. Resolved ONCE
|
|
171
199
|
// here — the agent meta and the channel must agree.
|
|
172
200
|
const requestedWorkspace = config.workspace ?? process.env.DSH_TUI_WORKSPACE_TARGET;
|
|
201
|
+
// Degraded boot (issue #183): a stale bundle patch without the
|
|
202
|
+
// dsh-tui-workspaces row leaves the service unmounted; resolve startup
|
|
203
|
+
// targets through the local-only runtime (provider URIs then fail loud
|
|
204
|
+
// below instead of crashing on an undefined service). A profile launch
|
|
205
|
+
// without the service means the patch came from an older dsh-tui copy
|
|
206
|
+
// than the running code — warn once so the skew is diagnosable. Bare
|
|
207
|
+
// embedders (no --profile) take the same fallback by design, silently.
|
|
208
|
+
const mountedWorkspaceService = ctx.get('tuiWorkspaces');
|
|
209
|
+
if (mountedWorkspaceService === undefined && resolveDshProfileName() !== undefined) {
|
|
210
|
+
ctx.logger.warn('dsh-tui: tuiWorkspaces service is not mounted; /workspace runs with the local-only fallback. ' +
|
|
211
|
+
'The bundle patch is older than the installed dsh-tui package — update the globally installed dsh-tui launcher to match the profile (issue #183).');
|
|
212
|
+
}
|
|
213
|
+
const workspaceService = mountedWorkspaceService ?? createLocalWorkspaceRuntime();
|
|
173
214
|
const initialWorkspace = requestedWorkspace === undefined
|
|
174
215
|
? undefined
|
|
175
|
-
: await
|
|
216
|
+
: await workspaceService.resolve(requestedWorkspace);
|
|
176
217
|
if (requestedWorkspace !== undefined && initialWorkspace === undefined) {
|
|
177
218
|
throw new Error(`dsh-tui: unsupported or unavailable workspace target: ${requestedWorkspace}`);
|
|
178
219
|
}
|
|
@@ -327,8 +368,9 @@ export async function apply(ctx, config) {
|
|
|
327
368
|
channel,
|
|
328
369
|
questionStore,
|
|
329
370
|
approvalStore,
|
|
330
|
-
//
|
|
331
|
-
//
|
|
371
|
+
// Full-screen surfaces inside Chat — the trajectory scene and the session
|
|
372
|
+
// browser — enter the alt screen themselves in inline mode; in fullscreen
|
|
373
|
+
// the tree is already wrapped below, so they must not nest.
|
|
332
374
|
fullscreen: config.fullscreen === true,
|
|
333
375
|
onExit: () => handleExit(),
|
|
334
376
|
// Only a `dsh --profile <name>` launch has a profile installation for
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import type { Context } from '@deepseek-ai/cordis';
|
|
19
19
|
import type { AgentSetup } from '@deepseek-ai/dsh-agent';
|
|
20
20
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
21
|
+
import type { PromptAssembly } from '@deepseek-ai/dsh-system-prompt';
|
|
21
22
|
/** One roster entry, as returned by `agentPresets.list()`/`resolve()`. */
|
|
22
23
|
export interface AgentPresetInfo {
|
|
23
24
|
readonly id: string;
|
|
@@ -93,6 +94,21 @@ export declare function runningPresetOf(session: {
|
|
|
93
94
|
data: unknown;
|
|
94
95
|
}[];
|
|
95
96
|
}): string | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Keep the official Minimal preset's model-facing contract at exactly two
|
|
99
|
+
* tools. The TUI mounts ask_user_question at the host layer so every other
|
|
100
|
+
* preset (including user presets) can use its questionnaire UI; host-layer
|
|
101
|
+
* tools otherwise merge into Minimal's scoped catalog as a third tool.
|
|
102
|
+
*
|
|
103
|
+
* This is a per-assembly filter rather than a startup-time decision because
|
|
104
|
+
* one TUI process can resume, create, or recompose sessions under different
|
|
105
|
+
* presets. The session log remains the source of truth for the active preset.
|
|
106
|
+
*
|
|
107
|
+
* @param assembly - Fully assembled prompt inputs for one model request.
|
|
108
|
+
* @param presetId - Preset recorded for the requesting session.
|
|
109
|
+
* @returns The original assembly, except ask_user_question is absent in Minimal.
|
|
110
|
+
*/
|
|
111
|
+
export declare function filterMinimalPresetTools(assembly: PromptAssembly, presetId: string | undefined): PromptAssembly;
|
|
96
112
|
/**
|
|
97
113
|
* Read a service the way a joined agent sees it: through the preset scope
|
|
98
114
|
* chain when a roster is mounted (preset realms hide e.g. `compaction` from
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/presets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/presets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAKpE,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAA;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,IAAI,IAAI,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC9C,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC/D,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAClE,yEAAyE;IACzE,UAAU,CAAC,CAAC,KAAK,EAAE;QAAE,GAAG,EAAE,OAAO,CAAA;KAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAEnE;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,kFAAkF;IAClF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAA;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAmBhG;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqB5G;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,MAAM,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAChC,MAAM,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;CACnD,GAAG,MAAM,GAAG,SAAS,CAErB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAM/G;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAKpG"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* before presets existed.
|
|
17
17
|
*/
|
|
18
18
|
import { resolveSessionPreset } from '@deepseek-ai/dsh-agent-presets';
|
|
19
|
+
const ASK_USER_TOOL = 'ask_user_question';
|
|
19
20
|
/**
|
|
20
21
|
* The mounted preset roster, or undefined when the composition has no
|
|
21
22
|
* `agent-presets` row (bare boots) — optional-service access via `ctx.get`.
|
|
@@ -94,6 +95,28 @@ export async function resolvePersistedPreset(ctx, sessionId) {
|
|
|
94
95
|
export function runningPresetOf(session) {
|
|
95
96
|
return resolveSessionPreset(session);
|
|
96
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Keep the official Minimal preset's model-facing contract at exactly two
|
|
100
|
+
* tools. The TUI mounts ask_user_question at the host layer so every other
|
|
101
|
+
* preset (including user presets) can use its questionnaire UI; host-layer
|
|
102
|
+
* tools otherwise merge into Minimal's scoped catalog as a third tool.
|
|
103
|
+
*
|
|
104
|
+
* This is a per-assembly filter rather than a startup-time decision because
|
|
105
|
+
* one TUI process can resume, create, or recompose sessions under different
|
|
106
|
+
* presets. The session log remains the source of truth for the active preset.
|
|
107
|
+
*
|
|
108
|
+
* @param assembly - Fully assembled prompt inputs for one model request.
|
|
109
|
+
* @param presetId - Preset recorded for the requesting session.
|
|
110
|
+
* @returns The original assembly, except ask_user_question is absent in Minimal.
|
|
111
|
+
*/
|
|
112
|
+
export function filterMinimalPresetTools(assembly, presetId) {
|
|
113
|
+
if (presetId !== 'minimal' || !assembly.tools.some(tool => tool.name === ASK_USER_TOOL))
|
|
114
|
+
return assembly;
|
|
115
|
+
return {
|
|
116
|
+
...assembly,
|
|
117
|
+
tools: assembly.tools.filter(tool => tool.name !== ASK_USER_TOOL),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
97
120
|
/**
|
|
98
121
|
* Read a service the way a joined agent sees it: through the preset scope
|
|
99
122
|
* chain when a roster is mounted (preset realms hide e.g. `compaction` from
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PreviewEntry, SessionDigest } from './types.js';
|
|
2
|
+
/** Head window budget. Eight times the measured worst-case prompt offset. */
|
|
3
|
+
export declare const HEAD_WINDOW_BYTES: number;
|
|
4
|
+
/** Head frame ceiling — a cost bound independent of how the bytes compress. */
|
|
5
|
+
export declare const HEAD_MAX_FRAMES = 128;
|
|
6
|
+
/** Tail window budget. Wider than the head: trailing frames carry payloads. */
|
|
7
|
+
export declare const TAIL_WINDOW_BYTES: number;
|
|
8
|
+
/**
|
|
9
|
+
* Read both windows of one session log.
|
|
10
|
+
*
|
|
11
|
+
* @param path - Absolute artifact path.
|
|
12
|
+
* @param cwd - Working directory, for the last-resort title.
|
|
13
|
+
* @returns The digest. An unreadable log still yields a usable record: the
|
|
14
|
+
* title falls back to the directory basename and says so through its source.
|
|
15
|
+
*/
|
|
16
|
+
export declare function digestSession(path: string, cwd: string): SessionDigest;
|
|
17
|
+
/**
|
|
18
|
+
* The last exchanges of a session, for the browser's preview pane.
|
|
19
|
+
*
|
|
20
|
+
* Bounded like everything else here: the preview shows the end of the
|
|
21
|
+
* conversation because that is what the tail window holds, and because the end
|
|
22
|
+
* is what tells you whether this is the session you meant.
|
|
23
|
+
*
|
|
24
|
+
* @param path - Absolute artifact path.
|
|
25
|
+
* @param limit - How many entries to keep, newest last.
|
|
26
|
+
* @returns Entries in log order.
|
|
27
|
+
*/
|
|
28
|
+
export declare function previewSession(path: string, limit: number): PreviewEntry[];
|
|
29
|
+
//# sourceMappingURL=digest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/sessions/digest.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAA;AAE3E,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,QAAY,CAAA;AAC1C,+EAA+E;AAC/E,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,QAAa,CAAA;AA+F3C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CA8CtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAyB1E"}
|