@khorsheed/dsh-local-agent-dsh 0.1.0-rc.6

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.
Files changed (45) hide show
  1. package/README.en.md +150 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +151 -0
  4. package/cordis.patch.yml +24 -0
  5. package/lib/client.js +1115 -0
  6. package/lib/dsh-cli-provider-UhWvivte.js +1701 -0
  7. package/lib/dsh-cli-provider.js +3 -0
  8. package/lib/index.js +624 -0
  9. package/lib/invariant.js +33 -0
  10. package/lib/provision.js +602 -0
  11. package/lib/records.js +109 -0
  12. package/lib/session-log-DOsSz56-.js +88 -0
  13. package/lib/types/client/SettingsCard.d.ts +78 -0
  14. package/lib/types/client/SettingsCard.js +200 -0
  15. package/lib/types/client/icons.d.ts +18 -0
  16. package/lib/types/client/icons.js +2 -0
  17. package/lib/types/client/index.d.ts +45 -0
  18. package/lib/types/client/index.js +88 -0
  19. package/lib/types/client/locales.d.ts +76 -0
  20. package/lib/types/client/locales.js +67 -0
  21. package/lib/types/dsh-cli-provider.d.ts +213 -0
  22. package/lib/types/dsh-cli-provider.js +733 -0
  23. package/lib/types/index.d.ts +124 -0
  24. package/lib/types/index.js +347 -0
  25. package/lib/types/invariant.d.ts +16 -0
  26. package/lib/types/invariant.js +37 -0
  27. package/lib/types/live-driver.d.ts +180 -0
  28. package/lib/types/live-driver.js +887 -0
  29. package/lib/types/live-switch.d.ts +76 -0
  30. package/lib/types/live-switch.js +100 -0
  31. package/lib/types/model-broker.d.ts +86 -0
  32. package/lib/types/model-broker.js +149 -0
  33. package/lib/types/model-catalog.d.ts +27 -0
  34. package/lib/types/model-catalog.js +71 -0
  35. package/lib/types/provision.d.ts +382 -0
  36. package/lib/types/provision.js +656 -0
  37. package/lib/types/records.d.ts +41 -0
  38. package/lib/types/records.js +125 -0
  39. package/lib/types/session-log.d.ts +58 -0
  40. package/lib/types/session-log.js +83 -0
  41. package/lib/types/session-mirror.d.ts +155 -0
  42. package/lib/types/session-mirror.js +451 -0
  43. package/locale/en.json +6 -0
  44. package/locale/zh.json +6 -0
  45. package/package.json +121 -0
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Session records adapter for the `dsh` harness: list the sub-dsh's own
3
+ * sessions from its scoped-home store. The sub-dsh persists JSONL logs under
4
+ * `<scoped home>/sessions/<project>/<session-id>/` (the same physical layout
5
+ * the official JSONL persistence backend writes), so the adapter reads headers
6
+ * directly instead of asking the parent's persistence service, which is bound
7
+ * to the parent's own store root. WHICH file in that directory holds the log
8
+ * is the host's generation choice, resolved through the same
9
+ * {@link resolveDshSessionLog} the session mirror uses — the two readers must
10
+ * never disagree about where a session's history lives.
11
+ * @module @khorsheed/dsh-local-agent-dsh/records
12
+ */
13
+ import type { LocalAgentSessionRecord } from '@khorsheed/dsh-local-agent';
14
+ /** A stored session's header line, the subset this adapter reads. */
15
+ interface DshSessionHeader {
16
+ id: string;
17
+ cwd?: string;
18
+ createdAt?: number;
19
+ }
20
+ /** Parse the header line of a session log; undefined when it is not one. */
21
+ export declare function parseDshSessionHeader(firstLine: string): DshSessionHeader | undefined;
22
+ /**
23
+ * Read a session log's first line (its header). The JSONL backend stores one
24
+ * zstd frame per append batch; node's zstdDecompress decodes the first frame,
25
+ * which holds the header line written at session creation.
26
+ * @param logPath - the session log file to read (any generation, compressed
27
+ * or raw — the caller resolved which one).
28
+ * @param compressed - whether the file is zstd-compressed; a raw log is read
29
+ * as text. Defaults to compressed, the backend's own default.
30
+ * @returns the first line, or undefined when the file is empty/half-written.
31
+ */
32
+ export declare function readDshSessionHeaderLine(logPath: string, compressed?: boolean): Promise<string | undefined>;
33
+ /**
34
+ * List the sessions recorded in the harness scoped home, in the store's own
35
+ * order (project directories first, then session directories).
36
+ * @param homeDir - the `dsh` harness's scoped home (its `$DSH_HOME` root).
37
+ * @returns the session records; an absent store yields an empty list.
38
+ */
39
+ export declare function listDshSessions(homeDir: string): Promise<readonly LocalAgentSessionRecord[]>;
40
+ export {};
41
+ //# sourceMappingURL=records.d.ts.map
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Session records adapter for the `dsh` harness: list the sub-dsh's own
3
+ * sessions from its scoped-home store. The sub-dsh persists JSONL logs under
4
+ * `<scoped home>/sessions/<project>/<session-id>/` (the same physical layout
5
+ * the official JSONL persistence backend writes), so the adapter reads headers
6
+ * directly instead of asking the parent's persistence service, which is bound
7
+ * to the parent's own store root. WHICH file in that directory holds the log
8
+ * is the host's generation choice, resolved through the same
9
+ * {@link resolveDshSessionLog} the session mirror uses — the two readers must
10
+ * never disagree about where a session's history lives.
11
+ * @module @khorsheed/dsh-local-agent-dsh/records
12
+ */
13
+ import { readdir, readFile } from 'node:fs/promises';
14
+ import { join } from 'node:path';
15
+ import { zstdDecompressSync } from 'node:zlib';
16
+ import { resolveDshSessionLog } from './session-log.js';
17
+ /** Parse the header line of a session log; undefined when it is not one. */
18
+ export function parseDshSessionHeader(firstLine) {
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(firstLine);
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ if (typeof parsed !== 'object' || parsed === null)
27
+ return undefined;
28
+ const header = parsed;
29
+ if (header.type !== 'session' || typeof header.id !== 'string')
30
+ return undefined;
31
+ return {
32
+ id: header.id,
33
+ ...(typeof header.cwd === 'string' ? { cwd: header.cwd } : {}),
34
+ ...(typeof header.createdAt === 'number' ? { createdAt: header.createdAt } : {}),
35
+ };
36
+ }
37
+ /** Whether an fs error is a missing entry. */
38
+ function isENOENT(error) {
39
+ return error.code === 'ENOENT';
40
+ }
41
+ /**
42
+ * Read a session log's first line (its header). The JSONL backend stores one
43
+ * zstd frame per append batch; node's zstdDecompress decodes the first frame,
44
+ * which holds the header line written at session creation.
45
+ * @param logPath - the session log file to read (any generation, compressed
46
+ * or raw — the caller resolved which one).
47
+ * @param compressed - whether the file is zstd-compressed; a raw log is read
48
+ * as text. Defaults to compressed, the backend's own default.
49
+ * @returns the first line, or undefined when the file is empty/half-written.
50
+ */
51
+ export async function readDshSessionHeaderLine(logPath, compressed = true) {
52
+ let buffer;
53
+ try {
54
+ buffer = await readFile(logPath);
55
+ }
56
+ catch (error) {
57
+ if (isENOENT(error))
58
+ return undefined;
59
+ throw error;
60
+ }
61
+ if (buffer.length === 0)
62
+ return undefined;
63
+ let text;
64
+ try {
65
+ text = compressed ? zstdDecompressSync(buffer).toString('utf8') : buffer.toString('utf8');
66
+ }
67
+ catch {
68
+ // A torn or non-zstd file is not a readable session; treat as absent.
69
+ return undefined;
70
+ }
71
+ const firstLine = text.split('\n', 1)[0];
72
+ return firstLine === undefined ? undefined : firstLine;
73
+ }
74
+ /**
75
+ * List the sessions recorded in the harness scoped home, in the store's own
76
+ * order (project directories first, then session directories).
77
+ * @param homeDir - the `dsh` harness's scoped home (its `$DSH_HOME` root).
78
+ * @returns the session records; an absent store yields an empty list.
79
+ */
80
+ export async function listDshSessions(homeDir) {
81
+ const root = join(homeDir, 'sessions');
82
+ let projects;
83
+ try {
84
+ projects = await readdir(root);
85
+ }
86
+ catch (error) {
87
+ if (isENOENT(error))
88
+ return [];
89
+ throw error;
90
+ }
91
+ const records = [];
92
+ for (const project of projects) {
93
+ // Project directories are the `--<slug>--` grouping the backend writes.
94
+ if (!project.startsWith('--'))
95
+ continue;
96
+ let sessionIds;
97
+ try {
98
+ sessionIds = await readdir(join(root, project));
99
+ }
100
+ catch (error) {
101
+ if (isENOENT(error))
102
+ continue;
103
+ throw error;
104
+ }
105
+ for (const sessionId of sessionIds) {
106
+ // The generation the store actually wrote — hardcoding one filename is
107
+ // what made `/dsh sessions` list nothing at all on host 0.1.5.
108
+ const log = await resolveDshSessionLog(join(root, project, sessionId));
109
+ if (log === undefined)
110
+ continue;
111
+ const headerLine = await readDshSessionHeaderLine(log.path, log.compressed);
112
+ if (headerLine === undefined)
113
+ continue;
114
+ const header = parseDshSessionHeader(headerLine);
115
+ if (header === undefined)
116
+ continue;
117
+ records.push({
118
+ id: header.id,
119
+ ...(header.cwd !== undefined ? { workDir: header.cwd } : { workDir: project }),
120
+ ...(header.createdAt !== undefined ? { startedAt: header.createdAt } : {}),
121
+ });
122
+ }
123
+ }
124
+ return records;
125
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Locating one sub-dsh session's log file inside its session directory.
3
+ *
4
+ * The host's JSONL persistence backend addresses its logs by SESSION FORMAT
5
+ * GENERATION: version zero keeps the original `session.jsonl`, and every later
6
+ * generation carries a lowercase `vN` component — `session.v3.jsonl` on host
7
+ * 0.1.5. Either basename may additionally carry `.zstd` for the compressed
8
+ * default. The backend itself "selects the numerically highest canonical
9
+ * generation", and so does this module: a reader that hardcodes one filename
10
+ * reads nothing the moment the host advances a generation, which is exactly
11
+ * how a 0.1.5 sub-dsh round lost its observed model, its usage and its tool
12
+ * calls at once while still settling `completed`.
13
+ *
14
+ * Both readers in this package — the session mirror's read-back and the
15
+ * `/dsh sessions` records adapter — resolve through here, so the two can never
16
+ * disagree about which file a session's history lives in.
17
+ * @module @khorsheed/dsh-local-agent-dsh/session-log
18
+ */
19
+ /** One session directory's chosen log file. */
20
+ export interface DshSessionLog {
21
+ /** Absolute path to the file. */
22
+ readonly path: string;
23
+ /**
24
+ * Its basename — carried into the read-back result so a record says which
25
+ * generation it was read from, rather than leaving a future filename change
26
+ * to look like an empty session.
27
+ */
28
+ readonly filename: string;
29
+ /** The generation the name encodes; 0 is the original unversioned log. */
30
+ readonly version: number;
31
+ /** Whether the bytes are zstd-compressed (the backend's default). */
32
+ readonly compressed: boolean;
33
+ }
34
+ /**
35
+ * Read the generation one session-directory entry names.
36
+ * @param filename - one basename from a session directory.
37
+ * @returns its generation and encoding, or undefined when the name is not a
38
+ * canonical log (`session.lock`, a temporary file, `session.v0.jsonl`, …).
39
+ */
40
+ export declare function parseDshSessionLogFilename(filename: string): {
41
+ readonly version: number;
42
+ readonly compressed: boolean;
43
+ } | undefined;
44
+ /**
45
+ * Choose the log file of one session directory: the numerically HIGHEST
46
+ * canonical generation present, preferring the compressed artifact when a
47
+ * generation somehow has both (the backend writes one per its configured
48
+ * encoding, so the pair only appears when a store changed settings).
49
+ *
50
+ * A directory with no canonical log — including one that holds only a
51
+ * `session.lock` — yields undefined. Absence is reported, never guessed: a
52
+ * caller that cannot find a log records nothing rather than inventing an empty
53
+ * history.
54
+ * @param dir - the session-owned directory.
55
+ * @returns the chosen log, or undefined when the directory holds none.
56
+ */
57
+ export declare function resolveDshSessionLog(dir: string): Promise<DshSessionLog | undefined>;
58
+ //# sourceMappingURL=session-log.d.ts.map
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Locating one sub-dsh session's log file inside its session directory.
3
+ *
4
+ * The host's JSONL persistence backend addresses its logs by SESSION FORMAT
5
+ * GENERATION: version zero keeps the original `session.jsonl`, and every later
6
+ * generation carries a lowercase `vN` component — `session.v3.jsonl` on host
7
+ * 0.1.5. Either basename may additionally carry `.zstd` for the compressed
8
+ * default. The backend itself "selects the numerically highest canonical
9
+ * generation", and so does this module: a reader that hardcodes one filename
10
+ * reads nothing the moment the host advances a generation, which is exactly
11
+ * how a 0.1.5 sub-dsh round lost its observed model, its usage and its tool
12
+ * calls at once while still settling `completed`.
13
+ *
14
+ * Both readers in this package — the session mirror's read-back and the
15
+ * `/dsh sessions` records adapter — resolve through here, so the two can never
16
+ * disagree about which file a session's history lives in.
17
+ * @module @khorsheed/dsh-local-agent-dsh/session-log
18
+ */
19
+ import { readdir } from 'node:fs/promises';
20
+ import { join } from 'node:path';
21
+ /**
22
+ * The canonical raw-log basename, matched AFTER any compression suffix is
23
+ * removed. Deliberately the host's own rule, character for character
24
+ * (`@deepseek-ai/dsh-session-format`): `.v0`, leading zeros, uppercase and
25
+ * temporary names are NOT canonical generations, and a reader that accepted
26
+ * them would read a file the writer never published.
27
+ */
28
+ const CANONICAL_LOG_FILENAME = /^session(?:\.v([1-9][0-9]*))?\.jsonl$/u;
29
+ /**
30
+ * Read the generation one session-directory entry names.
31
+ * @param filename - one basename from a session directory.
32
+ * @returns its generation and encoding, or undefined when the name is not a
33
+ * canonical log (`session.lock`, a temporary file, `session.v0.jsonl`, …).
34
+ */
35
+ export function parseDshSessionLogFilename(filename) {
36
+ const compressed = filename.endsWith('.zstd');
37
+ const raw = compressed ? filename.slice(0, -'.zstd'.length) : filename;
38
+ const match = CANONICAL_LOG_FILENAME.exec(raw);
39
+ if (match === null)
40
+ return undefined;
41
+ if (match[1] === undefined)
42
+ return { version: 0, compressed };
43
+ const version = Number(match[1]);
44
+ return Number.isSafeInteger(version) ? { version, compressed } : undefined;
45
+ }
46
+ /**
47
+ * Choose the log file of one session directory: the numerically HIGHEST
48
+ * canonical generation present, preferring the compressed artifact when a
49
+ * generation somehow has both (the backend writes one per its configured
50
+ * encoding, so the pair only appears when a store changed settings).
51
+ *
52
+ * A directory with no canonical log — including one that holds only a
53
+ * `session.lock` — yields undefined. Absence is reported, never guessed: a
54
+ * caller that cannot find a log records nothing rather than inventing an empty
55
+ * history.
56
+ * @param dir - the session-owned directory.
57
+ * @returns the chosen log, or undefined when the directory holds none.
58
+ */
59
+ export async function resolveDshSessionLog(dir) {
60
+ let entries;
61
+ try {
62
+ entries = await readdir(dir);
63
+ }
64
+ catch {
65
+ return undefined;
66
+ }
67
+ let best;
68
+ for (const filename of entries) {
69
+ const parsed = parseDshSessionLogFilename(filename);
70
+ if (parsed === undefined)
71
+ continue;
72
+ // Higher generation wins; within one generation the compressed artifact
73
+ // is the backend's default, so it wins a tie.
74
+ if (best !== undefined) {
75
+ if (parsed.version < best.version)
76
+ continue;
77
+ if (parsed.version === best.version && !(parsed.compressed && !best.compressed))
78
+ continue;
79
+ }
80
+ best = { path: join(dir, filename), filename, ...parsed };
81
+ }
82
+ return best;
83
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Mirror a sub-dsh session's events into the parent-side dsh subagent session,
3
+ * so a dsh delegation shows its full conversation (task, replies, thinking)
4
+ * and real token usage in the standard 子代理 surface — the same fidelity the
5
+ * other family harnesses get from their CLI transcript files. The sub-dsh
6
+ * writes a real dsh session into the harness scoped home with the SAME id as
7
+ * the parent-side child session and the same SessionEvent format, so the
8
+ * mirror copies finalized `user/message`/`assistant/message` events verbatim
9
+ * (no parsing, no re-encoding): reasoning blocks render natively and usage
10
+ * rides the assistant events' own `usage` field into the tokenUsage
11
+ * projection.
12
+ *
13
+ * Round scoping: the parent provider appends one `turn/start` per delegation
14
+ * round, and the sub-dsh session numbers its turns identically (fresh = 1,
15
+ * each resume +1), so a round's span is the sub-dsh events from its
16
+ * `turn/start` with `turn === round` to the next `turn/start`. The sub-dsh's
17
+ * own scaffolding user messages (agent-instructions, plugin, skill-catalog
18
+ * sources) are filtered — only the caller task (`source.kind === 'user'`)
19
+ * crosses. The sub-dsh's own `step/start`/`step/end` pairs cross verbatim
20
+ * (each carries its (turn, step) coordinates): the real-time conversation
21
+ * assembler only registers a step — and materializes its assistant message —
22
+ * at a boundary, so without them the live subsession view renders the prompt
23
+ * and tool cards but no replies. Turn boundaries are NOT copied: the parent's
24
+ * real-time spawn→settle boundaries stay the authoritative timing.
25
+ *
26
+ * `todo/write` passthrough: the sub-dsh's task list is a standing whole-list
27
+ * snapshot (last-wins). Each pass appends the round's LATEST snapshot only
28
+ * when it differs from the child session's last mirrored one, so repeated
29
+ * passes never duplicate an identical snapshot and the child's `todos`
30
+ * projection (and the official TodoPanel in non-takeover views) reads the
31
+ * member's task list for free.
32
+ * @module @khorsheed/dsh-local-agent-dsh/session-mirror
33
+ */
34
+ import type { DshSessionLog } from './session-log.ts';
35
+ import type { Context } from '@deepseek-ai/cordis';
36
+ import type { TokenUsage } from '@deepseek-ai/dsh-llm';
37
+ import type { Session, SessionEvent } from '@deepseek-ai/dsh-session';
38
+ import type { LocalAgentToolCalls } from '@khorsheed/dsh-local-agent/types';
39
+ /** One session log read back: its events, and which file they came from. */
40
+ export interface SubDshEventLog {
41
+ /** The session's events, header line excluded. */
42
+ readonly events: SessionEvent[];
43
+ /** The log file the events were read from (its generation and encoding). */
44
+ readonly log: DshSessionLog;
45
+ }
46
+ /**
47
+ * Read a sub-dsh session's event log from the scoped home. The session lives
48
+ * at `<homeDir>/sessions/<workspace>/<id>/` (the runner passes the bare uuid);
49
+ * WHICH file inside that directory holds the history is the host's choice —
50
+ * `session.jsonl` on the original generation, `session.v3.jsonl.zstd` on host
51
+ * 0.1.5 — so the name is resolved, never assumed (see
52
+ * {@link resolveDshSessionLog}). The first line is the header, the rest are
53
+ * events.
54
+ * @param homeDir - the `dsh` harness's scoped home.
55
+ * @param sessionId - the sub-dsh session id (same uuid as the child session).
56
+ * @returns the events and their source file, or undefined when the session is
57
+ * absent or unreadable.
58
+ */
59
+ export declare function readSubDshEvents(homeDir: string, sessionId: string): Promise<SubDshEventLog | undefined>;
60
+ /** The result of one dsh session-mirror pass. */
61
+ export interface DshMirrorDelta {
62
+ /** Text of each content event this pass newly mirrored (delta progress payloads); mirrored step boundaries carry no text. */
63
+ texts: string[];
64
+ /** Total round events mirrored into the child session after this pass. */
65
+ total: number;
66
+ /**
67
+ * The round's observed model identifier, read from the sub-dsh session's
68
+ * own assistant events: the `message.source` of the round's LAST
69
+ * `assistant/message`, formatted `provider/model` (the same shape the
70
+ * effectiveSettings snapshot reports the configured model in) or the bare
71
+ * model when no provider is named. Absent when the round's events name no
72
+ * model — absence is recorded, never guessed.
73
+ */
74
+ observedModel?: string;
75
+ /**
76
+ * The round's token usage: the span's assistant events' own `usage` fields
77
+ * summed (each event carries one LLM call's accounting, the same caliber
78
+ * the tokenUsage projection sums). Absent when the round recorded none.
79
+ */
80
+ usage?: TokenUsage;
81
+ /**
82
+ * The round's tool-call accounting: the span's `tool/call` events counted
83
+ * under the names they carry. Read off the SAME round window the model and
84
+ * usage come from — the pre-skip span, so a settle pass a live poll already
85
+ * drained still reports the round's real count. Absent when the round made
86
+ * no tool call.
87
+ */
88
+ toolCalls?: LocalAgentToolCalls;
89
+ /**
90
+ * The session-log file this pass read, by basename (`session.jsonl.zstd`,
91
+ * `session.v3.jsonl.zstd`, …). Carried so a read-back says WHICH generation
92
+ * it read rather than leaving a future filename change to look like an empty
93
+ * session — the failure this field exists because of. Absent when no log was
94
+ * found, which is the same "nothing to read" the other fields report by
95
+ * staying absent.
96
+ */
97
+ sessionLogFile?: string;
98
+ }
99
+ /** How much of the live event stream crosses into the child session. */
100
+ export type DshLiveMirrorGranularity = 'event' | 'token';
101
+ /**
102
+ * Mirror ONE live-pushed sub-dsh session event (the serve mode's
103
+ * `session/event` wire notification) into the child session. This is the
104
+ * live driver's transport-side entry into the SAME fold the file mirror
105
+ * owns: the filter (only the caller task's `user/message`, every
106
+ * `assistant/message`, the tool events, and the `step/start`/`step/end`
107
+ * boundaries cross — verbatim; turn boundaries stay the parent's; scaffolding
108
+ * stays behind) and the verbatim append are exactly `mirrorDshSession`'s
109
+ * span-loop rules. The 0.1.5 sub-dsh emits no per-chunk events (the
110
+ * `assistant/chunk` type is retired — an interrupted attempt settles as
111
+ * `assistant/attempt`, which carries no surface content and never crosses),
112
+ * so the `token` granularity no longer streams deltas into the child log;
113
+ * live text rides the run-progress channel only.
114
+ * The caller owns offset/dedupe (the live runtime pushes
115
+ * each event once) and persistence batching.
116
+ * @param childSession - the parent-side dsh subagent session.
117
+ * @param event - the live event from the resident sub-dsh.
118
+ * @param _options - granularity; retained for caller compatibility — the
119
+ * retirement of per-chunk events leaves the mirror granularity-invariant.
120
+ * @returns the mirrored text for delta progress, or undefined when the event
121
+ * was filtered out or carries no progress text (a step boundary crosses but
122
+ * is structure, not content).
123
+ */
124
+ export declare function mirrorDshLiveEvent(childSession: Session, event: SessionEvent, _options?: {
125
+ granularity?: DshLiveMirrorGranularity;
126
+ }): string | undefined;
127
+ /**
128
+ * Mirror the current round's events from the sub-dsh session into the child
129
+ * session, then persist (a live session syncs through the core's cached write
130
+ * handle, a standalone one through a one-shot handle; see
131
+ * {@link persistIfStandalone}). Runs both
132
+ * from the provider's live poll (while the
133
+ * sub-dsh writes its log in batches — a torn final zstd frame is skipped
134
+ * until the next pass) and after the child process exits (the settle pass);
135
+ * the round's already-mirrored prefix in the child session is the offset, so
136
+ * the settle pass is a no-op when polling kept up. An aborted round still
137
+ * preserves its partial work and real usage. All failures degrade to a warn:
138
+ * the delegation result is already settled.
139
+ * @param ctx - host context carrying session persistence.
140
+ * @param childSession - the parent-side dsh subagent session.
141
+ * @param homeDir - the `dsh` harness's scoped home.
142
+ * @param subSessionId - the sub-dsh session id (same uuid as the child session).
143
+ * @returns the newly mirrored events' texts and the round's mirrored total.
144
+ */
145
+ export declare function mirrorDshSession(ctx: Context, childSession: Session, homeDir: string, subSessionId: string): Promise<DshMirrorDelta>;
146
+ /**
147
+ * Persist the child session's mirrored events. Thin wrapper over the core's
148
+ * {@link persistChildSession}: a live session syncs through the core's cached
149
+ * per-child write handle (host 0.1.5's live write-behind never checkpoints a
150
+ * mirror session — it has no agent loop — so production child logs stayed
151
+ * header-only until the explicit sync), a standalone one through a one-shot
152
+ * handle flow. Kept under the historical name for the live driver.
153
+ */
154
+ export declare function persistIfStandalone(ctx: Context, childSession: Session): Promise<void>;
155
+ //# sourceMappingURL=session-mirror.d.ts.map