@deepseek-harness-tui/dsh-tui 0.7.0 → 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 +9 -6
- package/bin/dsh-tui.js +67 -14
- 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 +23 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +190 -75
- 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 +60 -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 +122 -14
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +37 -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 +12 -5
- 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/invariant.js +0 -28
- 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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { TitleSource } from './types.js';
|
|
2
|
+
/** Facts derived from a log at one revision. */
|
|
3
|
+
export interface DerivedEntry {
|
|
4
|
+
readonly revision: string;
|
|
5
|
+
readonly title: string;
|
|
6
|
+
readonly titleSource: TitleSource;
|
|
7
|
+
readonly hasPrompt: boolean;
|
|
8
|
+
readonly model: string | undefined;
|
|
9
|
+
readonly label: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
/** One session's cached record: derived facts plus this install's own notes. */
|
|
12
|
+
export interface IndexEntry {
|
|
13
|
+
readonly derived: DerivedEntry | undefined;
|
|
14
|
+
/** Git branch this install was on when it last used the session. */
|
|
15
|
+
readonly branch: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
/** The whole cache, session id → entry. */
|
|
18
|
+
export type SessionIndex = Map<string, IndexEntry>;
|
|
19
|
+
/**
|
|
20
|
+
* Load the cache.
|
|
21
|
+
* @returns The parsed index; an unreadable, malformed, or stale-schema file
|
|
22
|
+
* yields an empty one, which costs a rebuild and never an error.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readIndex(): SessionIndex;
|
|
25
|
+
/**
|
|
26
|
+
* Persist the cache, atomically.
|
|
27
|
+
*
|
|
28
|
+
* The write goes to a per-process temporary name and is renamed into place, so
|
|
29
|
+
* a reader never observes a half-written file and a crash never leaves one.
|
|
30
|
+
* A concurrent writer may win the rename; the loser's derivations are simply
|
|
31
|
+
* recomputed next time.
|
|
32
|
+
*
|
|
33
|
+
* @param index - The index to store. Entries are written in insertion order.
|
|
34
|
+
*/
|
|
35
|
+
export declare function writeIndex(index: SessionIndex): void;
|
|
36
|
+
/**
|
|
37
|
+
* Record the git branch this install is on for a session.
|
|
38
|
+
*
|
|
39
|
+
* Kept here rather than in the log because it is not a fact about the session,
|
|
40
|
+
* it is a fact about how this machine used it — the same reason it is reported
|
|
41
|
+
* as "branch when last used" and omitted entirely for sessions that predate
|
|
42
|
+
* the note. Inventing a branch for them would be worse than showing none.
|
|
43
|
+
*
|
|
44
|
+
* @param sessionId - Session being used.
|
|
45
|
+
* @param branch - Current branch, or undefined to leave any note untouched.
|
|
46
|
+
*/
|
|
47
|
+
export declare function noteBranch(sessionId: string, branch: string | undefined): void;
|
|
48
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/sessions/store.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAW7C,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAAA;IAC1C,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CACpC;AAED,2CAA2C;AAC3C,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAoClD;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAkBxC;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAoBpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAO9E"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session index — a local cache keyed by the backend's own change token.
|
|
3
|
+
*
|
|
4
|
+
* Deriving a session's title costs a bounded read; doing it for every session
|
|
5
|
+
* on every picker open costs that read times the history. The persistence
|
|
6
|
+
* service already hands out the exact token needed to avoid it:
|
|
7
|
+
* `listSnapshots()` returns, per session, an opaque revision that changes
|
|
8
|
+
* whenever the stored log changes. An entry whose revision still matches is
|
|
9
|
+
* reused verbatim; anything else is re-derived. Steady state is therefore zero
|
|
10
|
+
* log reads, and a session edited by another client (the store is shared with
|
|
11
|
+
* dsh web) invalidates itself without any coordination.
|
|
12
|
+
*
|
|
13
|
+
* The revision is treated as opaque, as its contract requires. It happens to
|
|
14
|
+
* be stat-derived today, but parsing it to shortcut a `stat` would couple this
|
|
15
|
+
* cache to one backend's private format and break the moment a store without
|
|
16
|
+
* per-session files is used.
|
|
17
|
+
*
|
|
18
|
+
* Two halves live in one entry because they have different lifetimes:
|
|
19
|
+
* `derived` facts come from the log and die with the revision, while `branch`
|
|
20
|
+
* is a local note about how this install used the session and no log change
|
|
21
|
+
* can invalidate it. Keeping them in one record with one governing revision
|
|
22
|
+
* field makes that boundary explicit instead of implied.
|
|
23
|
+
*
|
|
24
|
+
* Every operation is best-effort. The index is a cache: a corrupt file, a
|
|
25
|
+
* losing concurrent write, or a read-only home directory costs a re-derivation
|
|
26
|
+
* and nothing else, so nothing here throws.
|
|
27
|
+
*
|
|
28
|
+
* @module @deepseek-harness-tui/dsh-tui/sessions/store
|
|
29
|
+
*/
|
|
30
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
31
|
+
import { join } from 'node:path';
|
|
32
|
+
import { DATA_DIR } from '../../utils/paths.js';
|
|
33
|
+
/**
|
|
34
|
+
* Bumped when an entry's shape changes. A mismatch drops the whole file
|
|
35
|
+
* rather than migrating it — re-deriving is cheap and bounded, whereas a
|
|
36
|
+
* migration path is code that runs once and is never exercised again.
|
|
37
|
+
*/
|
|
38
|
+
const SCHEMA_VERSION = 1;
|
|
39
|
+
const INDEX_FILE = join(DATA_DIR, 'session-index.json');
|
|
40
|
+
/** A title source as written to disk, or undefined for anything unexpected. */
|
|
41
|
+
function readTitleSource(value) {
|
|
42
|
+
return value === 'renamed' || value === 'auto' || value === 'prompt' || value === 'fallback'
|
|
43
|
+
? value
|
|
44
|
+
: undefined;
|
|
45
|
+
}
|
|
46
|
+
/** Narrow one persisted entry; an unrecognizable record is simply absent. */
|
|
47
|
+
function readEntry(value) {
|
|
48
|
+
if (value === null || typeof value !== 'object')
|
|
49
|
+
return undefined;
|
|
50
|
+
const record = value;
|
|
51
|
+
const branch = typeof record['branch'] === 'string' ? record['branch'] : undefined;
|
|
52
|
+
const raw = record['derived'];
|
|
53
|
+
if (raw === null || typeof raw !== 'object')
|
|
54
|
+
return { derived: undefined, branch };
|
|
55
|
+
const derived = raw;
|
|
56
|
+
const revision = derived['revision'];
|
|
57
|
+
const title = derived['title'];
|
|
58
|
+
const titleSource = readTitleSource(derived['titleSource']);
|
|
59
|
+
if (typeof revision !== 'string' || typeof title !== 'string' || titleSource === undefined) {
|
|
60
|
+
return { derived: undefined, branch };
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
branch,
|
|
64
|
+
derived: {
|
|
65
|
+
revision,
|
|
66
|
+
title,
|
|
67
|
+
titleSource,
|
|
68
|
+
hasPrompt: derived['hasPrompt'] === true,
|
|
69
|
+
model: typeof derived['model'] === 'string' ? derived['model'] : undefined,
|
|
70
|
+
label: typeof derived['label'] === 'string' ? derived['label'] : undefined,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Load the cache.
|
|
76
|
+
* @returns The parsed index; an unreadable, malformed, or stale-schema file
|
|
77
|
+
* yields an empty one, which costs a rebuild and never an error.
|
|
78
|
+
*/
|
|
79
|
+
export function readIndex() {
|
|
80
|
+
const index = new Map();
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
parsed = JSON.parse(readFileSync(INDEX_FILE, 'utf8'));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return index;
|
|
87
|
+
}
|
|
88
|
+
if (parsed === null || typeof parsed !== 'object')
|
|
89
|
+
return index;
|
|
90
|
+
const file = parsed;
|
|
91
|
+
if (file['version'] !== SCHEMA_VERSION)
|
|
92
|
+
return index;
|
|
93
|
+
const entries = file['entries'];
|
|
94
|
+
if (entries === null || typeof entries !== 'object')
|
|
95
|
+
return index;
|
|
96
|
+
for (const [id, value] of Object.entries(entries)) {
|
|
97
|
+
const entry = readEntry(value);
|
|
98
|
+
if (entry !== undefined)
|
|
99
|
+
index.set(id, entry);
|
|
100
|
+
}
|
|
101
|
+
return index;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Persist the cache, atomically.
|
|
105
|
+
*
|
|
106
|
+
* The write goes to a per-process temporary name and is renamed into place, so
|
|
107
|
+
* a reader never observes a half-written file and a crash never leaves one.
|
|
108
|
+
* A concurrent writer may win the rename; the loser's derivations are simply
|
|
109
|
+
* recomputed next time.
|
|
110
|
+
*
|
|
111
|
+
* @param index - The index to store. Entries are written in insertion order.
|
|
112
|
+
*/
|
|
113
|
+
export function writeIndex(index) {
|
|
114
|
+
const entries = {};
|
|
115
|
+
for (const [id, entry] of index) {
|
|
116
|
+
entries[id] = {
|
|
117
|
+
...(entry.derived === undefined ? {} : { derived: entry.derived }),
|
|
118
|
+
...(entry.branch === undefined ? {} : { branch: entry.branch }),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const temporary = `${INDEX_FILE}.${process.pid}.tmp`;
|
|
122
|
+
try {
|
|
123
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
124
|
+
writeFileSync(temporary, JSON.stringify({ version: SCHEMA_VERSION, entries }));
|
|
125
|
+
renameSync(temporary, INDEX_FILE);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
try {
|
|
129
|
+
rmSync(temporary, { force: true });
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Nothing left to do; the cache stays as it was.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Record the git branch this install is on for a session.
|
|
138
|
+
*
|
|
139
|
+
* Kept here rather than in the log because it is not a fact about the session,
|
|
140
|
+
* it is a fact about how this machine used it — the same reason it is reported
|
|
141
|
+
* as "branch when last used" and omitted entirely for sessions that predate
|
|
142
|
+
* the note. Inventing a branch for them would be worse than showing none.
|
|
143
|
+
*
|
|
144
|
+
* @param sessionId - Session being used.
|
|
145
|
+
* @param branch - Current branch, or undefined to leave any note untouched.
|
|
146
|
+
*/
|
|
147
|
+
export function noteBranch(sessionId, branch) {
|
|
148
|
+
if (branch === undefined || branch.length === 0)
|
|
149
|
+
return;
|
|
150
|
+
const index = readIndex();
|
|
151
|
+
const existing = index.get(sessionId);
|
|
152
|
+
if (existing?.branch === branch)
|
|
153
|
+
return;
|
|
154
|
+
index.set(sessionId, { derived: existing?.derived, branch });
|
|
155
|
+
writeIndex(index);
|
|
156
|
+
}
|
|
157
|
+
/*
|
|
158
|
+
* Deliberately absent: a "forget this entry" call for the picker's delete, and
|
|
159
|
+
* a "patch this title" call for its rename. Listing prunes entries whose
|
|
160
|
+
* session no longer exists, and a renamed log has a new revision whose
|
|
161
|
+
* re-derivation reads the very title event the rename just appended — both
|
|
162
|
+
* paths already converge on the right answer, so a second mechanism to reach
|
|
163
|
+
* it could only disagree with the first.
|
|
164
|
+
*/
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the TUI knows about a persisted session before opening it.
|
|
3
|
+
*
|
|
4
|
+
* The shape here is the whole point of this feature. The picker used to run on
|
|
5
|
+
* `{ id, title, cwd, createdAt, updatedAt }`, and every defect it had was
|
|
6
|
+
* downstream of that: it could not tell a delegated sub-agent run from a
|
|
7
|
+
* conversation because it never carried the distinction, and it could not show
|
|
8
|
+
* why a row was labelled the way it was because the label arrived without its
|
|
9
|
+
* provenance. So the types below carry both — the kind as a closed sum, and
|
|
10
|
+
* every derived label together with the evidence that produced it.
|
|
11
|
+
*
|
|
12
|
+
* @module @deepseek-harness-tui/dsh-tui/sessions/types
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* What a persisted session *is*, decided once from its immutable header.
|
|
16
|
+
*
|
|
17
|
+
* A closed sum rather than a pair of booleans: `isSubagent`/`isFork` would
|
|
18
|
+
* admit a fourth, meaningless combination, and every reader would have to
|
|
19
|
+
* re-derive the precedence between them. Deciding once, here, is also the fix
|
|
20
|
+
* for the defect that started this work — `origin` and `parentSession` reached
|
|
21
|
+
* the picker and were dropped on the floor.
|
|
22
|
+
*
|
|
23
|
+
* The discriminator is `origin`, never `parentSession`. A `/rewind` fork
|
|
24
|
+
* carries `parentSession` and no `origin`, so filtering on lineage alone would
|
|
25
|
+
* silently hide the user's own rewound branches along with the sub-agents.
|
|
26
|
+
*/
|
|
27
|
+
export type SessionKind =
|
|
28
|
+
/** A conversation the user started. */
|
|
29
|
+
{
|
|
30
|
+
readonly kind: 'root';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A `/rewind` fork: a real conversation that inherited a prefix of its
|
|
34
|
+
* parent's log. `parent` is what defines it, so it is never absent.
|
|
35
|
+
*/
|
|
36
|
+
| {
|
|
37
|
+
readonly kind: 'fork';
|
|
38
|
+
readonly parent: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A delegated sub-agent run. `origin: 'subagent'` is the authority, so the
|
|
42
|
+
* lineage link is reported as it is found — a run whose header records no
|
|
43
|
+
* parent is still a sub-agent run.
|
|
44
|
+
*/
|
|
45
|
+
| {
|
|
46
|
+
readonly kind: 'subagent';
|
|
47
|
+
readonly parent: string | undefined;
|
|
48
|
+
readonly depth: number;
|
|
49
|
+
};
|
|
50
|
+
/** Which evidence produced a session's display title. */
|
|
51
|
+
export type TitleSource =
|
|
52
|
+
/** A `session/title` event appended after the session's own opening — a rename. */
|
|
53
|
+
'renamed'
|
|
54
|
+
/** The session's first `session/title`, written for it automatically. */
|
|
55
|
+
| 'auto'
|
|
56
|
+
/** No title event; the opening user prompt stands in. */
|
|
57
|
+
| 'prompt'
|
|
58
|
+
/** Nothing readable; the working directory's basename stands in. */
|
|
59
|
+
| 'fallback';
|
|
60
|
+
/**
|
|
61
|
+
* A display title and the evidence behind it.
|
|
62
|
+
*
|
|
63
|
+
* The source travels with the text because the UI consumes it: a `fallback`
|
|
64
|
+
* title is dimmed rather than presented as if the session were named that, and
|
|
65
|
+
* a reader who wonders why a row says `dsh-cc-tui` can be told. A source that
|
|
66
|
+
* nothing consumed would be decoration; this one changes what is rendered.
|
|
67
|
+
*/
|
|
68
|
+
export interface SessionTitle {
|
|
69
|
+
readonly text: string;
|
|
70
|
+
readonly source: TitleSource;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Everything the browser shows for one session, and nothing that costs an
|
|
74
|
+
* unbounded read to learn.
|
|
75
|
+
*
|
|
76
|
+
* That second half is an invariant, not an accident: every field here comes
|
|
77
|
+
* from the persistence header, one `stat`, or a bounded window at one end of
|
|
78
|
+
* the log. A session's full statistics (turn counts, tool totals, token spend)
|
|
79
|
+
* require folding the entire event log, which is what the trajectory scene is
|
|
80
|
+
* for — the browser stays responsive on a 4 MB log because it never asks a
|
|
81
|
+
* question that big.
|
|
82
|
+
*/
|
|
83
|
+
export interface SessionSummary {
|
|
84
|
+
readonly id: string;
|
|
85
|
+
readonly kind: SessionKind;
|
|
86
|
+
readonly title: SessionTitle;
|
|
87
|
+
/** Working directory recorded in the header; '' when it recorded none. */
|
|
88
|
+
readonly cwd: string;
|
|
89
|
+
readonly createdAt: number;
|
|
90
|
+
/**
|
|
91
|
+
* Last activity: the later of the log's own mtime and this install's
|
|
92
|
+
* last-used note. Both are lower bounds on "when this session was last
|
|
93
|
+
* touched" — the mtime catches writes by another client (dsh web appends to
|
|
94
|
+
* the same store), the note catches a resume this install performed. The
|
|
95
|
+
* later of two lower bounds is the best available answer, and unlike a
|
|
96
|
+
* composite value there is no risk of stitching halves from different
|
|
97
|
+
* origins: this is one scalar with two witnesses.
|
|
98
|
+
*/
|
|
99
|
+
readonly updatedAt: number;
|
|
100
|
+
/** Log size in bytes; undefined when the backend owns no per-session file. */
|
|
101
|
+
readonly bytes: number | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Whether the log holds a user prompt at all.
|
|
104
|
+
*
|
|
105
|
+
* `list()` reports *materialized* sessions, not conversations: a session
|
|
106
|
+
* that only ever recorded its own boot policy is a real stored session with
|
|
107
|
+
* nothing to resume. Answering this is the consumer's job — the header does
|
|
108
|
+
* not promise it — and getting it wrong is why one empty row per launch used
|
|
109
|
+
* to accumulate in the picker.
|
|
110
|
+
*/
|
|
111
|
+
readonly hasPrompt: boolean;
|
|
112
|
+
/** Agent preset the session was composed from, when the deployment records one. */
|
|
113
|
+
readonly agentPreset: string | undefined;
|
|
114
|
+
/** Model last seen in the log's trailing window, when one is recorded. */
|
|
115
|
+
readonly model: string | undefined;
|
|
116
|
+
/** Sub-agent label from the run's descriptor (`subagent` kind only). */
|
|
117
|
+
readonly label: string | undefined;
|
|
118
|
+
/** Git branch noted when this install last used the session. */
|
|
119
|
+
readonly branch: string | undefined;
|
|
120
|
+
/** How many sub-agent runs name this session as their parent. */
|
|
121
|
+
readonly childCount: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Facts a bounded read can recover from one session log.
|
|
125
|
+
*
|
|
126
|
+
* No "last event time" here on purpose: for an append-only log the file's
|
|
127
|
+
* mtime says the same thing, is already read for {@link SessionSummary.bytes},
|
|
128
|
+
* and needs no cache entry of its own.
|
|
129
|
+
*/
|
|
130
|
+
export interface SessionDigest {
|
|
131
|
+
readonly title: SessionTitle | undefined;
|
|
132
|
+
readonly hasPrompt: boolean;
|
|
133
|
+
readonly model: string | undefined;
|
|
134
|
+
readonly label: string | undefined;
|
|
135
|
+
}
|
|
136
|
+
/** One exchange in the preview pane, newest last. */
|
|
137
|
+
export interface PreviewEntry {
|
|
138
|
+
readonly role: 'user' | 'assistant';
|
|
139
|
+
readonly text: string;
|
|
140
|
+
readonly at: number | undefined;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/sessions/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,WAAW;AACrB,uCAAuC;AACrC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC3B;;;GAGG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACpD;;;;GAIG;GACD;IACA,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB,CAAA;AAEH,yDAAyD;AACzD,MAAM,MAAM,WAAW;AACrB,mFAAmF;AACjF,SAAS;AACX,yEAAyE;GACvE,MAAM;AACR,yDAAyD;GACvD,QAAQ;AACV,oEAAoE;GAClE,UAAU,CAAA;AAEd;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAC7B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,iEAAiE;IACjE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the TUI knows about a persisted session before opening it.
|
|
3
|
+
*
|
|
4
|
+
* The shape here is the whole point of this feature. The picker used to run on
|
|
5
|
+
* `{ id, title, cwd, createdAt, updatedAt }`, and every defect it had was
|
|
6
|
+
* downstream of that: it could not tell a delegated sub-agent run from a
|
|
7
|
+
* conversation because it never carried the distinction, and it could not show
|
|
8
|
+
* why a row was labelled the way it was because the label arrived without its
|
|
9
|
+
* provenance. So the types below carry both — the kind as a closed sum, and
|
|
10
|
+
* every derived label together with the evidence that produced it.
|
|
11
|
+
*
|
|
12
|
+
* @module @deepseek-harness-tui/dsh-tui/sessions/types
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
package/lib/types/i18n.d.ts
CHANGED
|
@@ -403,6 +403,10 @@ declare const dict: {
|
|
|
403
403
|
readonly zh: "工具 {{n}}";
|
|
404
404
|
readonly en: "Tools {{n}}";
|
|
405
405
|
};
|
|
406
|
+
readonly 'skill-unavailable': {
|
|
407
|
+
readonly zh: "技能 {{name}} 已不可用或未开放用户直调";
|
|
408
|
+
readonly en: "Skill {{name}} is gone or not user-invocable";
|
|
409
|
+
};
|
|
406
410
|
readonly 'skill-audit-prompt': {
|
|
407
411
|
readonly zh: "请使用 audit 技能对当前项目做一次全面的代码审计,找出安全、正确性与质量问题。";
|
|
408
412
|
readonly en: "Use the audit skill to do a thorough code audit of the current project, finding security, correctness and quality issues.";
|
|
@@ -1167,14 +1171,6 @@ declare const dict: {
|
|
|
1167
1171
|
readonly zh: "已恢复会话";
|
|
1168
1172
|
readonly en: "Session resumed";
|
|
1169
1173
|
};
|
|
1170
|
-
readonly 'resume-more-above': {
|
|
1171
|
-
readonly zh: "↑ 还有 {{n}} 条";
|
|
1172
|
-
readonly en: "↑ {{n}} more";
|
|
1173
|
-
};
|
|
1174
|
-
readonly 'resume-more-below': {
|
|
1175
|
-
readonly zh: "↓ 还有 {{n}} 条";
|
|
1176
|
-
readonly en: "↓ {{n}} more";
|
|
1177
|
-
};
|
|
1178
1174
|
readonly 'resume-delete-confirm': {
|
|
1179
1175
|
readonly zh: "删除「{{name}}」?会话日志将被永久移除。";
|
|
1180
1176
|
readonly en: "Delete \"{{name}}\"? The session log is removed permanently.";
|
|
@@ -1195,10 +1191,6 @@ declare const dict: {
|
|
|
1195
1191
|
readonly zh: "无法重命名会话「{{name}}」";
|
|
1196
1192
|
readonly en: "Could not rename session {{name}}";
|
|
1197
1193
|
};
|
|
1198
|
-
readonly 'resume-hint-list': {
|
|
1199
|
-
readonly zh: "**Enter** 恢复 · Esc 退出 · {{mod}}d 删除 · {{mod}}r 重命名";
|
|
1200
|
-
readonly en: "**Enter** to confirm · Esc to exit · {{mod}}d to delete · {{mod}}r to rename";
|
|
1201
|
-
};
|
|
1202
1194
|
readonly 'resume-hint-delete': {
|
|
1203
1195
|
readonly zh: "**Enter** 删除 · Esc 取消";
|
|
1204
1196
|
readonly en: "**Enter** to delete · Esc to cancel";
|
|
@@ -1209,7 +1201,123 @@ declare const dict: {
|
|
|
1209
1201
|
};
|
|
1210
1202
|
readonly 'resume-title': {
|
|
1211
1203
|
readonly zh: "恢复会话";
|
|
1212
|
-
readonly en: "Resume";
|
|
1204
|
+
readonly en: "Resume session";
|
|
1205
|
+
};
|
|
1206
|
+
readonly 'session-loading': {
|
|
1207
|
+
readonly zh: "正在读取会话…";
|
|
1208
|
+
readonly en: "Reading sessions…";
|
|
1209
|
+
};
|
|
1210
|
+
readonly 'session-list-failed': {
|
|
1211
|
+
readonly zh: "无法读取会话列表 · {{err}}";
|
|
1212
|
+
readonly en: "Could not read the session list · {{err}}";
|
|
1213
|
+
};
|
|
1214
|
+
readonly 'session-resume-refused': {
|
|
1215
|
+
readonly zh: "无法恢复这个会话——原因已记录在对话里(模型正在工作时无法切换)";
|
|
1216
|
+
readonly en: "That session could not be resumed — the reason is in the conversation (switching is refused while the model is working)";
|
|
1217
|
+
};
|
|
1218
|
+
readonly 'session-resume-failed': {
|
|
1219
|
+
readonly zh: "恢复会话失败 · {{err}}";
|
|
1220
|
+
readonly en: "Resuming the session failed · {{err}}";
|
|
1221
|
+
};
|
|
1222
|
+
readonly 'session-when-now': {
|
|
1223
|
+
readonly zh: "刚刚";
|
|
1224
|
+
readonly en: "just now";
|
|
1225
|
+
};
|
|
1226
|
+
readonly 'session-when-minutes': {
|
|
1227
|
+
readonly zh: "{{n}} 分钟前";
|
|
1228
|
+
readonly en: "{{n}}m ago";
|
|
1229
|
+
};
|
|
1230
|
+
readonly 'session-when-hours': {
|
|
1231
|
+
readonly zh: "{{n}} 小时前";
|
|
1232
|
+
readonly en: "{{n}}h ago";
|
|
1233
|
+
};
|
|
1234
|
+
readonly 'session-when-days': {
|
|
1235
|
+
readonly zh: "{{n}} 天前";
|
|
1236
|
+
readonly en: "{{n}}d ago";
|
|
1237
|
+
};
|
|
1238
|
+
readonly 'session-when-date': {
|
|
1239
|
+
readonly zh: "{{month}} 月 {{day}} 日";
|
|
1240
|
+
readonly en: "{{month}}/{{day}}";
|
|
1241
|
+
};
|
|
1242
|
+
readonly 'session-children': {
|
|
1243
|
+
readonly zh: "{{n}} 个子运行";
|
|
1244
|
+
readonly en: "{{n}} runs";
|
|
1245
|
+
};
|
|
1246
|
+
readonly 'session-kind-root': {
|
|
1247
|
+
readonly zh: "对话";
|
|
1248
|
+
readonly en: "Conversation";
|
|
1249
|
+
};
|
|
1250
|
+
readonly 'session-kind-fork': {
|
|
1251
|
+
readonly zh: "回溯分支";
|
|
1252
|
+
readonly en: "Rewound branch";
|
|
1253
|
+
};
|
|
1254
|
+
readonly 'session-kind-subagent': {
|
|
1255
|
+
readonly zh: "子 agent 运行";
|
|
1256
|
+
readonly en: "Sub-agent run";
|
|
1257
|
+
};
|
|
1258
|
+
readonly 'session-project-unknown': {
|
|
1259
|
+
readonly zh: "(未记录目录)";
|
|
1260
|
+
readonly en: "(no directory recorded)";
|
|
1261
|
+
};
|
|
1262
|
+
readonly 'session-scope-all': {
|
|
1263
|
+
readonly zh: "全部项目";
|
|
1264
|
+
readonly en: "all projects";
|
|
1265
|
+
};
|
|
1266
|
+
readonly 'session-search-placeholder': {
|
|
1267
|
+
readonly zh: "输入以搜索 · {{scope}}";
|
|
1268
|
+
readonly en: "Type to search · {{scope}}";
|
|
1269
|
+
};
|
|
1270
|
+
readonly 'session-count-shown': {
|
|
1271
|
+
readonly zh: "{{n}} 个会话";
|
|
1272
|
+
readonly en: "{{n}} sessions";
|
|
1273
|
+
};
|
|
1274
|
+
readonly 'session-count-subagents': {
|
|
1275
|
+
readonly zh: "{{n}} 个子运行已折叠";
|
|
1276
|
+
readonly en: "{{n}} runs folded";
|
|
1277
|
+
};
|
|
1278
|
+
readonly 'session-count-empty': {
|
|
1279
|
+
readonly zh: "{{n}} 个空会话";
|
|
1280
|
+
readonly en: "{{n}} empty";
|
|
1281
|
+
};
|
|
1282
|
+
readonly 'session-clean-confirm': {
|
|
1283
|
+
readonly zh: "清理 {{n}} 个没有对话内容的会话?日志将被永久移除。";
|
|
1284
|
+
readonly en: "Remove {{n}} sessions that hold no conversation? Their logs are deleted permanently.";
|
|
1285
|
+
};
|
|
1286
|
+
readonly 'session-cleaned': {
|
|
1287
|
+
readonly zh: "已清理 {{n}} 个空会话";
|
|
1288
|
+
readonly en: "Removed {{n}} empty sessions";
|
|
1289
|
+
};
|
|
1290
|
+
readonly 'session-preview-times': {
|
|
1291
|
+
readonly zh: "创建于 {{created}} · 最后活动 {{updated}}";
|
|
1292
|
+
readonly en: "created {{created}} · last active {{updated}}";
|
|
1293
|
+
};
|
|
1294
|
+
readonly 'session-preview-loading': {
|
|
1295
|
+
readonly zh: "正在读取会话结尾…";
|
|
1296
|
+
readonly en: "Reading the end of this session…";
|
|
1297
|
+
};
|
|
1298
|
+
readonly 'session-preview-empty': {
|
|
1299
|
+
readonly zh: "这个会话没有可预览的往来消息";
|
|
1300
|
+
readonly en: "No exchanges to preview in this session";
|
|
1301
|
+
};
|
|
1302
|
+
readonly 'session-toggle-on': {
|
|
1303
|
+
readonly zh: "开";
|
|
1304
|
+
readonly en: "on";
|
|
1305
|
+
};
|
|
1306
|
+
readonly 'session-toggle-off': {
|
|
1307
|
+
readonly zh: "关";
|
|
1308
|
+
readonly en: "off";
|
|
1309
|
+
};
|
|
1310
|
+
readonly 'session-hint-list': {
|
|
1311
|
+
readonly zh: "**Enter** 恢复 · Tab 预览 · {{mod}}a 全部项目({{projects}}) · {{mod}}s 子运行({{runs}}) · {{mod}}b 本分支 · {{mod}}r 重命名 · {{mod}}d 删除 · {{mod}}x 清空壳 · Esc 退出";
|
|
1312
|
+
readonly en: "**Enter** resume · Tab preview · {{mod}}a all projects ({{projects}}) · {{mod}}s runs ({{runs}}) · {{mod}}b this branch · {{mod}}r rename · {{mod}}d delete · {{mod}}x clean · Esc exit";
|
|
1313
|
+
};
|
|
1314
|
+
readonly 'session-hint-list-mid': {
|
|
1315
|
+
readonly zh: "**Enter** 恢复 · Tab 预览 · {{mod}}a 全部项目 · {{mod}}s 子运行 · {{mod}}r 重命名 · {{mod}}d 删除 · Esc 退出";
|
|
1316
|
+
readonly en: "**Enter** resume · Tab preview · {{mod}}a projects · {{mod}}s runs · {{mod}}r rename · {{mod}}d delete · Esc exit";
|
|
1317
|
+
};
|
|
1318
|
+
readonly 'session-hint-list-short': {
|
|
1319
|
+
readonly zh: "**Enter** 恢复 · Tab 预览 · Esc 退出";
|
|
1320
|
+
readonly en: "**Enter** resume · Tab preview · Esc exit";
|
|
1213
1321
|
};
|
|
1214
1322
|
readonly 'hint-confirm-exit': {
|
|
1215
1323
|
readonly zh: "**Enter** 确认 · Esc 退出";
|
|
@@ -1772,7 +1880,7 @@ declare const dict: {
|
|
|
1772
1880
|
readonly zh: "切换工作状态指示器预设";
|
|
1773
1881
|
};
|
|
1774
1882
|
readonly 'cmd-desc-preset': {
|
|
1775
|
-
readonly zh: "切换 Agent
|
|
1883
|
+
readonly zh: "切换 Agent 预设(含梁神模式)";
|
|
1776
1884
|
};
|
|
1777
1885
|
readonly 'cmd-desc-theme': {
|
|
1778
1886
|
readonly zh: "切换配色主题(auto 跟随系统,或内置/自定义)";
|
package/lib/types/i18n.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C,QAAA,MAAM,IAAI
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"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"}
|
package/lib/types/i18n.js
CHANGED
|
@@ -123,6 +123,7 @@ const dict = {
|
|
|
123
123
|
'context-skills': { zh: '技能 {{n}}', en: 'Skills {{n}}' },
|
|
124
124
|
'context-tools': { zh: '工具 {{n}}', en: 'Tools {{n}}' },
|
|
125
125
|
// ── screens/Chat.tsx ────────────────────────────────────────────────
|
|
126
|
+
'skill-unavailable': { zh: '技能 {{name}} 已不可用或未开放用户直调', en: 'Skill {{name}} is gone or not user-invocable' },
|
|
126
127
|
'skill-audit-prompt': { zh: '请使用 audit 技能对当前项目做一次全面的代码审计,找出安全、正确性与质量问题。', en: 'Use the audit skill to do a thorough code audit of the current project, finding security, correctness and quality issues.' },
|
|
127
128
|
'skill-bug-prompt': { zh: '请使用 bug 技能协助我记录一份完整的 bug 报告(现象、复现步骤、期望行为)。', en: 'Use the bug skill to help me write a complete bug report (symptoms, reproduction steps, expected behavior).' },
|
|
128
129
|
'skill-practice-prompt': { zh: '请使用 practice 技能陪我进行一轮编程练习。', en: 'Use the practice skill to run a round of programming practice with me.' },
|
|
@@ -326,19 +327,49 @@ const dict = {
|
|
|
326
327
|
'load-earlier': { zh: ' ↑ 加载更早消息(会话日志完整,/export 导出全文) ', en: ' ↑ load earlier messages (full session log; /export for full text) ' },
|
|
327
328
|
'show-previous-messages': { zh: ' ctrl+e 显示前 {{n}} 条消息 ', en: ' ctrl+e to show {{n}} previous messages ' },
|
|
328
329
|
'resume-none-in-cwd': { zh: '当前目录没有可恢复的历史会话', en: 'No resumable sessions in the current directory' },
|
|
329
|
-
// ──
|
|
330
|
+
// ── screens/SessionBrowser.tsx + screens/Chat.tsx (/resume) ─────────
|
|
330
331
|
'resume-resumed': { zh: '已恢复会话', en: 'Session resumed' },
|
|
331
|
-
'resume-more-above': { zh: '↑ 还有 {{n}} 条', en: '↑ {{n}} more' },
|
|
332
|
-
'resume-more-below': { zh: '↓ 还有 {{n}} 条', en: '↓ {{n}} more' },
|
|
333
332
|
'resume-delete-confirm': { zh: '删除「{{name}}」?会话日志将被永久移除。', en: 'Delete "{{name}}"? The session log is removed permanently.' },
|
|
334
333
|
'resume-deleted': { zh: '已删除会话「{{name}}」', en: 'Deleted session {{name}}' },
|
|
335
334
|
'resume-delete-failed': { zh: '无法删除会话「{{name}}」', en: 'Could not delete session {{name}}' },
|
|
336
335
|
'resume-rename-placeholder': { zh: '新的会话名称…', en: 'New session name…' },
|
|
337
336
|
'resume-rename-failed': { zh: '无法重命名会话「{{name}}」', en: 'Could not rename session {{name}}' },
|
|
338
|
-
'resume-hint-list': { zh: '**Enter** 恢复 · Esc 退出 · {{mod}}d 删除 · {{mod}}r 重命名', en: '**Enter** to confirm · Esc to exit · {{mod}}d to delete · {{mod}}r to rename' },
|
|
339
337
|
'resume-hint-delete': { zh: '**Enter** 删除 · Esc 取消', en: '**Enter** to delete · Esc to cancel' },
|
|
340
338
|
'resume-hint-rename': { zh: '**Enter** 保存 · Esc 取消', en: '**Enter** to save · Esc to cancel' },
|
|
341
|
-
'resume-title': { zh: '恢复会话', en: 'Resume' },
|
|
339
|
+
'resume-title': { zh: '恢复会话', en: 'Resume session' },
|
|
340
|
+
// ── 会话浏览器:行、计数、筛选、预览 ───────────────────────────────
|
|
341
|
+
'session-loading': { zh: '正在读取会话…', en: 'Reading sessions…' },
|
|
342
|
+
'session-list-failed': { zh: '无法读取会话列表 · {{err}}', en: 'Could not read the session list · {{err}}' },
|
|
343
|
+
'session-resume-refused': { zh: '无法恢复这个会话——原因已记录在对话里(模型正在工作时无法切换)', en: 'That session could not be resumed — the reason is in the conversation (switching is refused while the model is working)' },
|
|
344
|
+
'session-resume-failed': { zh: '恢复会话失败 · {{err}}', en: 'Resuming the session failed · {{err}}' },
|
|
345
|
+
'session-when-now': { zh: '刚刚', en: 'just now' },
|
|
346
|
+
'session-when-minutes': { zh: '{{n}} 分钟前', en: '{{n}}m ago' },
|
|
347
|
+
'session-when-hours': { zh: '{{n}} 小时前', en: '{{n}}h ago' },
|
|
348
|
+
'session-when-days': { zh: '{{n}} 天前', en: '{{n}}d ago' },
|
|
349
|
+
'session-when-date': { zh: '{{month}} 月 {{day}} 日', en: '{{month}}/{{day}}' },
|
|
350
|
+
'session-children': { zh: '{{n}} 个子运行', en: '{{n}} runs' },
|
|
351
|
+
'session-kind-root': { zh: '对话', en: 'Conversation' },
|
|
352
|
+
'session-kind-fork': { zh: '回溯分支', en: 'Rewound branch' },
|
|
353
|
+
'session-kind-subagent': { zh: '子 agent 运行', en: 'Sub-agent run' },
|
|
354
|
+
'session-project-unknown': { zh: '(未记录目录)', en: '(no directory recorded)' },
|
|
355
|
+
'session-scope-all': { zh: '全部项目', en: 'all projects' },
|
|
356
|
+
'session-search-placeholder': { zh: '输入以搜索 · {{scope}}', en: 'Type to search · {{scope}}' },
|
|
357
|
+
'session-count-shown': { zh: '{{n}} 个会话', en: '{{n}} sessions' },
|
|
358
|
+
'session-count-subagents': { zh: '{{n}} 个子运行已折叠', en: '{{n}} runs folded' },
|
|
359
|
+
'session-count-empty': { zh: '{{n}} 个空会话', en: '{{n}} empty' },
|
|
360
|
+
'session-clean-confirm': { zh: '清理 {{n}} 个没有对话内容的会话?日志将被永久移除。', en: 'Remove {{n}} sessions that hold no conversation? Their logs are deleted permanently.' },
|
|
361
|
+
'session-cleaned': { zh: '已清理 {{n}} 个空会话', en: 'Removed {{n}} empty sessions' },
|
|
362
|
+
'session-preview-times': { zh: '创建于 {{created}} · 最后活动 {{updated}}', en: 'created {{created}} · last active {{updated}}' },
|
|
363
|
+
'session-preview-loading': { zh: '正在读取会话结尾…', en: 'Reading the end of this session…' },
|
|
364
|
+
'session-preview-empty': { zh: '这个会话没有可预览的往来消息', en: 'No exchanges to preview in this session' },
|
|
365
|
+
'session-toggle-on': { zh: '开', en: 'on' },
|
|
366
|
+
'session-toggle-off': { zh: '关', en: 'off' },
|
|
367
|
+
// Three widths of the same hint. The browser picks the widest that fits the
|
|
368
|
+
// terminal, because a hint that wraps costs the rows the list needs and can
|
|
369
|
+
// push its own tail off the bottom of the screen.
|
|
370
|
+
'session-hint-list': { zh: '**Enter** 恢复 · Tab 预览 · {{mod}}a 全部项目({{projects}}) · {{mod}}s 子运行({{runs}}) · {{mod}}b 本分支 · {{mod}}r 重命名 · {{mod}}d 删除 · {{mod}}x 清空壳 · Esc 退出', en: '**Enter** resume · Tab preview · {{mod}}a all projects ({{projects}}) · {{mod}}s runs ({{runs}}) · {{mod}}b this branch · {{mod}}r rename · {{mod}}d delete · {{mod}}x clean · Esc exit' },
|
|
371
|
+
'session-hint-list-mid': { zh: '**Enter** 恢复 · Tab 预览 · {{mod}}a 全部项目 · {{mod}}s 子运行 · {{mod}}r 重命名 · {{mod}}d 删除 · Esc 退出', en: '**Enter** resume · Tab preview · {{mod}}a projects · {{mod}}s runs · {{mod}}r rename · {{mod}}d delete · Esc exit' },
|
|
372
|
+
'session-hint-list-short': { zh: '**Enter** 恢复 · Tab 预览 · Esc 退出', en: '**Enter** resume · Tab preview · Esc exit' },
|
|
342
373
|
// ── picker 通用快捷键提示(整句本地化,zh 不用 "to" 结构;**段** 渲染为粗体主快捷键)─
|
|
343
374
|
'hint-confirm-exit': { zh: '**Enter** 确认 · Esc 退出', en: '**Enter** to confirm · Esc to exit' },
|
|
344
375
|
'hint-confirm-cancel': { zh: '**Enter** 确认 · Esc 取消', en: '**Enter** to confirm · Esc to cancel' },
|
|
@@ -504,7 +535,7 @@ const dict = {
|
|
|
504
535
|
'cmd-desc-agents': { zh: '查看本会话的子代理' },
|
|
505
536
|
// Model / display
|
|
506
537
|
'cmd-desc-activity': { zh: '切换工作状态指示器预设' },
|
|
507
|
-
'cmd-desc-preset': { zh: '切换 Agent
|
|
538
|
+
'cmd-desc-preset': { zh: '切换 Agent 预设(含梁神模式)' },
|
|
508
539
|
'cmd-desc-theme': { zh: '切换配色主题(auto 跟随系统,或内置/自定义)' },
|
|
509
540
|
'cmd-desc-lang': { zh: '切换界面语言(en / zh)' },
|
|
510
541
|
'cmd-desc-model': { zh: '查看当前模型' },
|