@xmoon76/dsh-pi-tui 0.4.9 → 0.5.1

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/dist/index.d.mts CHANGED
@@ -1,107 +1,20 @@
1
- import { H as TuiAction } from "./public-types-YbB5Rhku.mjs";
2
- import z from "@deepseek-ai/schemastery";
3
- import { Agent, ModelSelectionRef } from "@deepseek-ai/dsh-agent";
4
- import { ContentBlock } from "@deepseek-ai/dsh-llm";
1
+ import { H as TuiAction } from "./public-types-BEjj7FEb.mjs";
5
2
  import { SessionId } from "@deepseek-ai/dsh-session";
3
+ import { ContentBlock } from "@deepseek-ai/dsh-llm";
6
4
  import { Context, Volatile } from "@deepseek-ai/cordis";
7
- //#region src/image/admission.d.ts
8
- /** Structural subset of `ImageAttachmentRef`. */
9
- interface ImageAttachmentRefLike {
10
- readonly attachmentId: string;
11
- readonly mediaType: ImageMediaType;
12
- readonly bytes: number;
13
- readonly width: number;
14
- readonly height: number;
15
- readonly name?: string;
16
- }
17
- //#endregion
18
- //#region src/image/types.d.ts
19
- /**
20
- * Image draft vocabulary for the TUI input pipeline (plan M1).
21
- *
22
- * The draft layer is TUI-owned: it stages clipboard/file bytes until a
23
- * submission turns them into durable `ImageAttachmentRef`s through the dsh
24
- * attachment service. Nothing in this module persists; the durable boundary
25
- * is the harness's `ctx.attachments.saveImages()`.
26
- *
27
- * The media-type union mirrors `@deepseek-ai/dsh-attachment`'s
28
- * `ImageMediaType` structurally, so drafts flow into the harness without a
29
- * runtime dependency on the attachment package (AGENTS.md decision 7:
30
- * structural typing for dsh services).
31
- * @module @xmoon76/dsh-pi-tui/image/types
32
- */
33
- /** Raster media types accepted by the TUI image intake. */
34
- type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
35
- /** Per-TUI draft image identity; only meaningful inside one TUI run. */
36
- type DraftImageId = number;
37
- /** Where a staged image's bytes came from. */
38
- type DraftImageSource = {
39
- readonly type: 'clipboard';
40
- } | {
41
- readonly type: 'path';
42
- readonly path: string;
43
- }
44
- /** The image was pulled back from a queued message: its bytes are ALREADY
45
- * durable with the harness (the recalled ref) — the draft holds no local
46
- * copy and re-submitting reuses the ref instead of re-uploading. */
47
- | {
48
- readonly type: 'recalled';
49
- };
50
- /**
51
- * One image staged in the editor draft. The draft holds the raw bytes so
52
- * clipboard and path entries share one shape and a submit never depends on
53
- * the source file still existing (plan §5.1). A RECALLED draft (pulled
54
- * back from the queue) instead carries the durable `ImageAttachmentRef`
55
- * and NO local bytes — the bytes stay in harness storage and re-submitting
56
- * reuses the ref.
57
- */
58
- interface DraftImage {
59
- /** Per-TUI draft identity (the `#N` of the placeholder). */
60
- readonly id: DraftImageId;
61
- readonly kind: 'image';
62
- readonly source: DraftImageSource;
63
- /** Exact encoded bytes, held only for the draft lifetime. Empty for a
64
- * recalled draft (its bytes live in harness storage). */
65
- readonly bytes: Uint8Array;
66
- readonly mediaType: ImageMediaType;
67
- /** Intrinsic encoded width in pixels. */
68
- readonly width: number;
69
- /** Intrinsic encoded height in pixels. */
70
- readonly height: number;
71
- /** Exact encoded byte length (`bytes.byteLength` for local drafts; the
72
- * durable ref's recorded `bytes` for recalled drafts). */
73
- readonly byteLength: number;
74
- /** Optional display name (basename for path sources), never a path. */
75
- readonly name?: string;
76
- /** The canonical editor placeholder text for this draft. */
77
- readonly placeholder: string;
78
- /** The durable ref this draft reuses on submit; present ONLY for
79
- * recalled drafts (already-durable images are never re-uploaded). */
80
- readonly recalledRef?: ImageAttachmentRefLike;
81
- }
82
- /** The store surface the placeholder expansion needs (structural). */
83
- interface DraftImageStoreLike {
84
- values(): readonly DraftImage[];
5
+ import { Agent, ModelSelectionRef } from "@deepseek-ai/dsh-agent";
6
+ import z from "@deepseek-ai/schemastery";
7
+ //#region src/domain/communication/policy.d.ts
8
+ type ProgressUpdates = 'off' | 'milestones' | 'frequent';
9
+ type ResponseStyle = 'default' | 'concise' | 'explanatory';
10
+ interface ProgressUpdatesState {
11
+ mode: ProgressUpdates;
85
12
  }
86
- //#endregion
87
- //#region src/display-preset.d.ts
88
- /**
89
- * Canonical transcript display presets and their layered disclosure policy.
90
- *
91
- * DisplayPreset is the one runtime vocabulary for presentation state. Every
92
- * preset whose projection exists in this build is available; the availability
93
- * gate exists so a future preset can never be claimed by another preset's
94
- * renderer before its own projection ships.
95
- * @module @xmoon76/dsh-pi-tui/display-preset
96
- */
97
- /** The complete display vocabulary. */
98
- type DisplayPreset = 'focus' | 'compact' | 'full';
99
- /** The shared mutable display authority passed between the runner, prompt and UI. */
100
- interface DisplayState {
101
- preset: DisplayPreset;
13
+ interface ResponseStyleState {
14
+ style: ResponseStyle;
102
15
  }
103
16
  //#endregion
104
- //#region src/diag.d.ts
17
+ //#region src/runtime/process/diagnostics.d.ts
105
18
  interface Diag {
106
19
  debug(message: string, fields?: Record<string, unknown>): void;
107
20
  info(message: string, fields?: Record<string, unknown>): void;
@@ -111,38 +24,18 @@ interface Diag {
111
24
  dispose(): void;
112
25
  }
113
26
  //#endregion
114
- //#region src/communication-policy.d.ts
115
- type ProgressUpdates = 'off' | 'milestones' | 'frequent';
116
- type ResponseStyle = 'default' | 'concise' | 'explanatory';
117
- interface ProgressUpdatesState {
118
- mode: ProgressUpdates;
119
- }
120
- interface ResponseStyleState {
121
- style: ResponseStyle;
122
- }
123
- //#endregion
124
- //#region src/skill-catalog.d.ts
27
+ //#region src/domain/catalog/skill.d.ts
125
28
  /**
126
- * The standing-scope skill catalog adapter: the SINGLE narrow seam between
127
- * the TUI and the dsh skill services (the non-public-API isolation
128
- * contract, plan appendix B).
29
+ * Neutral skill catalog vocabulary (TS8-E): the human-facing skill summary
30
+ * shape, the detached catalog (with its completeness flag) and the official
31
+ * user-invocation policy guard.
129
32
  *
130
- * Everything the TUI knows about dsh-skill and agent-presets lives here:
131
- * - structural service types (never upstream classes) with OPTIONAL
132
- * capability members — `snapshot` and `standingKeyFor` are detected at
133
- * call time, so an upstream change degrades instead of breaking;
134
- * - `readHumanSkillCatalog()`: snapshot-first collector (list() fallback),
135
- * official `isUserInvocable` filter, field validation, stable sort,
136
- * deep freeze, distinct abort propagation;
137
- * - `resolveColdSkillTarget()`: capability-gated cold target selection
138
- * (standing scope → rosterless global → degraded global with a notice);
139
- * - `resolveLiveSkillTarget()`: the agent's own registry + agent scope.
140
- *
141
- * Failure contract: ordinary provider/composition failures surface as
142
- * degraded targets or rethrown errors for the coordinator to classify;
143
- * aborts ALWAYS propagate as aborts. No failure may hang or crash the TUI —
144
- * the worst outcome is "the corresponding commands are missing".
145
- * @module @xmoon76/dsh-pi-tui/skill-catalog
33
+ * This module is the transport/UI-neutral semantic owner: it consumes only the
34
+ * official `@deepseek-ai/dsh-skill` policy helper. It never resolves ctx
35
+ * services, acquires standing scopes, holds revision leases, reads a live
36
+ * Agent, or imports a Direct/Remote adapter — the structural Direct Host read
37
+ * lives in `runtime/direct/skill-catalog.ts`.
38
+ * @module @xmoon76/dsh-pi-tui/domain/catalog/skill
146
39
  */
147
40
  /** The human-facing slice of one skill: display fields only. */
148
41
  interface HumanSkillSummary {
@@ -160,44 +53,7 @@ interface HumanSkillCatalog {
160
53
  readonly complete: boolean;
161
54
  }
162
55
  //#endregion
163
- //#region src/tui-app.d.ts
164
- /** The compaction lifecycle phase the working row advertises: idle (no
165
- * compaction), summarizing (compaction/start seen, the summary is being
166
- * generated), or applying (the summary landed, the compacted surface is
167
- * being committed). Derived by the runner from compaction/start →
168
- * compaction/summary → compaction/end (foldCompactionEvent). */
169
- type CompactionPhase = 'idle' | 'summarizing' | 'applying';
170
- /**
171
- * The WEB composer submit gestures (DSH `ComposerSubmitGesture`): plain
172
- * Enter, or the Cmd/Ctrl-accelerated chord. The busy-Enter policy resolves
173
- * each to a delivery mode — plain Enter to the preferred mode, the
174
- * accelerated chord to its OPPOSITE — so the chord is never a fixed mode.
175
- */
176
- type ComposerSubmitGesture = 'enter' | 'accelerated';
177
- /** One semantic queued pending-input row for the queue pane. */
178
- interface QueueItem {
179
- /** The pending message id (agent inbox identity), or a local request id. */
180
- id: string;
181
- /** The message text, single-line display form. */
182
- text: string;
183
- /** next-turn followup vs next-step steer. */
184
- mode: 'followup' | 'steer';
185
- /** The correlation identity (authoritative rpc id or local request id). */
186
- rpcId?: string;
187
- /** A client-local echo not yet backed by an authoritative occurrence. */
188
- local?: boolean;
189
- }
190
- //#endregion
191
- //#region src/surface-catalog.d.ts
192
- /** Minimal live-agent face consumed by the effective catalog readers. */
193
- interface SurfaceCatalogAgent {
194
- readonly ctx: object;
195
- readonly session: {
196
- readonly header: {
197
- readonly cwd?: string;
198
- };
199
- };
200
- }
56
+ //#region src/domain/catalog/surface.d.ts
201
57
  /** Local structural command face, compatible with both npm and Source Mode DSH.
202
58
  * The optional identity is part of the official 0.1.6 descriptor; an older
203
59
  * runtime's descriptor may omit it. */
@@ -245,53 +101,42 @@ interface SurfaceCatalogSnapshot {
245
101
  readonly skills: readonly HumanSkillSummary[];
246
102
  readonly issues: readonly SurfaceCatalogIssue[];
247
103
  }
104
+ /** The pre-mount catalog resolution result: the resume prefetch snapshot or
105
+ * the cold standing-scope skill catalog, plus an optional degradation notice. */
106
+ interface InitialCatalogResolution {
107
+ /** The resume prefetch snapshot to install at mount. */
108
+ readonly snapshot?: SurfaceCatalogSnapshot;
109
+ /** The cold standing-scope human skill catalog (deferred start). */
110
+ readonly skills?: HumanSkillCatalog;
111
+ /** A user-facing notice when the prefetch/standing read degraded. */
112
+ readonly notice?: string;
113
+ }
114
+ //#endregion
115
+ //#region src/runtime/direct/surface-catalog.d.ts
116
+ /** Minimal live-agent face consumed by the effective catalog readers — and by
117
+ * {@link import('../../app/direct/initial-catalog.ts').SurfaceCatalogResolutionOptions}, so the
118
+ * published option type stays structural instead of inlining the Host Agent. */
119
+ interface SurfaceCatalogAgent {
120
+ readonly ctx: object;
121
+ readonly session: {
122
+ readonly header: {
123
+ readonly cwd?: string;
124
+ };
125
+ };
126
+ }
248
127
  /** The commands-service surface the collector reads. */
249
128
  interface SurfaceCommandsService {
250
129
  list(agent: SurfaceCatalogAgent): readonly SurfaceCommandDescriptor[];
251
130
  }
252
131
  /** The narrow context surface {@link readSurfaceCatalog} consumes. The
253
132
  * SKILL services are NOT reached here: every dsh skill/agent-presets
254
- * access goes through `src/skill-catalog.ts` (the single-point adapter,
255
- * plan appendix B.1). */
133
+ * access goes through `runtime/direct/skill-catalog.ts` (the single-point
134
+ * adapter, plan appendix B.1). */
256
135
  interface SurfaceCatalogContext {
257
136
  get(name: 'commands'): SurfaceCommandsService | undefined;
258
137
  }
259
138
  //#endregion
260
- //#region src/commands.d.ts
261
- /**
262
- * The effective mode ONE submission resolved at the submit boundary: whether
263
- * an agent-facing delivery steers into the running turn or queues behind it
264
- * (web `ComposerSubmissionPolicy` parity — an idle agent queues, plain Enter
265
- * takes the preference, and the accelerated chord takes its OPPOSITE).
266
- *
267
- * It is a property of the GESTURE, not of the persisted settings: a one-shot
268
- * gesture exists only inside the dispatch that resolved it, so every
269
- * downstream consumer (the TUI skill delivery) accepts this value instead of
270
- * re-deriving the mode from `busyEnter`.
271
- */
272
- type SubmitDelivery = 'steer' | 'queue';
273
- /**
274
- * The CURRENT host catalog's view of ONE parsed line (the DSH client
275
- * `CommandUiRuntime.matchEnter` decision table): whether the host CLAIMS the
276
- * line, and — for a claimed line — the claiming descriptor's attachment
277
- * declaration, so the dispatch can never consult two different catalog views
278
- * for the claim and the declaration.
279
- */
280
- type HostCommandClaim = {
281
- readonly claimed: true;
282
- /** The claiming descriptor's `input.attachments`
283
- * (DSH `CommandInputDescriptor.attachments`): only a declaring command
284
- * may be invoked with composer attachments. */
285
- readonly attachments: boolean;
286
- } | {
287
- readonly claimed: false;
288
- };
289
- //#endregion
290
- //#region src/index.d.ts
291
- /** Stable Cordis plugin name. */
292
- declare const name = "tui-runner";
293
- /** Core services required before the TUI can mount. */
294
- declare const inject: string[];
139
+ //#region src/app/config/schema.d.ts
295
140
  /** Plugin config: the session to resume, resolved from the startup service,
296
141
  * plus the DSH 0.1.7 profile-owned live TUI preferences. Every preference
297
142
  * field is a schemastery `.volatile()` reference (PR A): the `tui-app`
@@ -335,7 +180,7 @@ interface Config {
335
180
  /** Busy-Enter delivery for plain Enter while the agent runs: 'queue'
336
181
  * (default) or 'steer'. */
337
182
  readonly busyEnter: Volatile<string>;
338
- /** Local-shell sandbox for user-typed `!`/`!!` commands: 'bypass'
183
+ /** User-shell sandbox policy for user-typed `!`/`!!` commands: 'bypass'
339
184
  * (default) runs them outside the dsh sandbox, 'sandbox' routes them
340
185
  * through the dsh shell capability's policy. */
341
186
  readonly localShellSandbox: Volatile<string>;
@@ -346,16 +191,22 @@ interface Config {
346
191
  readonly displayPreset: Volatile<string>;
347
192
  /** Mid-turn progress-update cadence (off | milestones | frequent). */
348
193
  readonly progressUpdates: Volatile<string>;
194
+ /** Git attribution guidance mode (off | product | product-model). */
195
+ readonly gitAttribution: Volatile<string>;
349
196
  /** Visible-answer density guidance (default | concise | explanatory). */
350
197
  readonly responseStyle: Volatile<string>;
351
198
  /** Completion-notification mode ('unfocused' | 'always' | 'off'). */
352
199
  readonly notificationMode: Volatile<string>;
353
200
  /** Completion-notification method ('auto' | 'osc9' | 'osc777' | 'bell'). */
354
201
  readonly notificationMethod: Volatile<string>;
202
+ /** Terminal-progress protocol selection ('off' | '9;4' | '7501' |
203
+ * '9;4+7501'): which native terminal status protocols the TUI reports.
204
+ * Defaults to the dual protocol. */
205
+ readonly terminalProgress: Volatile<string>;
355
206
  /** Fullscreen mouse-wheel step ('1' | '2' | '3' | '5' | '8'). */
356
207
  readonly wheelScrollLines: Volatile<string>;
357
208
  /** The user keybinding overrides as a whole-value RAW field. The
358
- * keybindings parser (src/keybindings/config.ts) is the only
209
+ * keybindings parser (src/tui/keybindings/config.ts) is the only
359
210
  * validation/parsing authority — the keybinding business schema
360
211
  * deliberately stays out of the plugin Config. */
361
212
  readonly keybindings: Volatile<unknown>;
@@ -365,6 +216,27 @@ interface Config {
365
216
  readonly legacySettingsMigrationVersion: Volatile<number>;
366
217
  }
367
218
  declare const Config: z<Config>;
219
+ //#endregion
220
+ //#region src/domain/command/policy.d.ts
221
+ /**
222
+ * The transport/UI-neutral command-line policy (plan §27 / TS8-F3): which slash
223
+ * lines the TUI owns locally, which need no session, which lines are local for
224
+ * attachment purposes, and the ONE semantic line classification every dispatch
225
+ * gate consumes.
226
+ *
227
+ * Pure and Host-free (structural inputs plus DSH's own `parseCommand`), so the
228
+ * dispatch gates are testable headless and the presentation/submission paths
229
+ * share ONE classification. The application/submission-facing half (the
230
+ * image-rejection gate and the delivery resolver) lives in
231
+ * `app/submission/command-policy.ts`; the destructive-shell predicate lives in
232
+ * `domain/shell/danger.ts`.
233
+ *
234
+ * The AUTHORITATIVE Host-name fact stays an explicit input
235
+ * ({@link CommandLineClassificationFacts.hostOriginClaim}); this domain never
236
+ * derives Host authority from the effective claim union (which also carries the
237
+ * Client's own TUI registrations).
238
+ * @module @xmoon76/dsh-pi-tui/domain/command/policy
239
+ */
368
240
  /**
369
241
  * Slash commands that need no session: before the first user message
370
242
  * (deferred start) they run locally without creating one. Everything else
@@ -377,38 +249,29 @@ declare const Config: z<Config>;
377
249
  */
378
250
  declare const SESSIONLESS_COMMANDS: Set<string>;
379
251
  /**
380
- * The LOCAL-execute command set: TUI-owned UI/control commands AND core
381
- * control commands the TUI does not itself register (e.g. /kill) that
382
- * must ALWAYS run locally through the commands service, never steered,
383
- * regardless of the busyEnter preference. Everything NOT in this set —
384
- * plain prompts AND non-local commands (the per-skill slash commands like
385
- * /grilling or /matrix-cli) — flows through the busy-Enter submission
386
- * policy while the agent is running: web parity, where a skill invocation
387
- * is a plain `session.prompt` whose leading `/name` line the host's
388
- * pre-step listener (dsh-tool-skill) resolves into the injected skill
389
- * body — there is no command-execution wire for skills.
252
+ * The LOCAL-execute command set (PR5 v2 §1C-6 narrows its semantic role):
253
+ * TUI-owned UI/control names AND core control names the TUI dispatches but
254
+ * does not register (/kill). It remains the STATIC policy surface —
255
+ * reserved-name validation, the extension collision catalog
256
+ * (HOST_COMMAND_CATALOG), and the fallback term of the line classifier
257
+ * when no LIVE Client-registry test was supplied. It is NOT by itself an
258
+ * answer to "who owns this production line": ownership comes from the
259
+ * live sources (the genuine Host-origin descriptors, the Client registry,
260
+ * the extension registry, the skill-wrapper state) through
261
+ * {@link classifyCommandLine}.
262
+ *
263
+ * `/kill` is the explicit example of the rule and its DISPOSITION is
264
+ * deliberate: it has NO live Client registration (and no handler anywhere in
265
+ * this surface), so it is NOT a TUI-owned line and this set must never be
266
+ * consulted to decide ownership. What actually happens to a typed `/kill ...`
267
+ * follows the ordinary precedence, in this order: a genuine Host claim decides
268
+ * per that descriptor; a genuine Host NAME that resolves while its exact line
269
+ * is NOT claimed makes the line an ordinary submission; and only a name
270
+ * nothing owns keeps the command plane's resolution chance (the final,
271
+ * session-scoped catalog may still provide it). It stays in this set for
272
+ * reserved-name validation and the extension collision catalog only.
390
273
  */
391
274
  declare const LOCAL_COMMANDS: Set<string>;
392
- /**
393
- * Command semantics matrix (plan §19.3/M12): a LOCAL command line carrying a
394
- * staged image placeholder is REJECTED — local commands are pure UI
395
- * controls, never LLM prompts. AGENT-FACING input (plain prompts AND
396
- * per-skill slash invocations like `/grilling`) SUPPORTS images: the skill
397
- * wrapper builds its message through the same prepared-input path, so an
398
- * image-bearing skill line is a real multimodal prompt (review finding 4).
399
- * There is never a silent drop.
400
- * @param parsed - the parsed slash command, undefined for a plain prompt.
401
- * @param text - the submission text.
402
- * @param store - the live draft store.
403
- * @param isLocal - whether THIS LINE is a LOCAL (TUI-owned/UI) command; skill
404
- * names answer false. Never derived from the name alone: a host command's
405
- * input KIND decides which line it claims (see
406
- * {@link commandIsLocalForAttachments}).
407
- */
408
- declare function commandRejectsImages(parsed: {
409
- name: string;
410
- rawInput?: string;
411
- } | undefined, text: string, store: DraftImageStoreLike, isLocal: boolean): boolean;
412
275
  /**
413
276
  * The STATIC host-owned command catalog (P1-04): the ownership sets
414
277
  * (LOCAL_COMMANDS and SESSIONLESS_COMMANDS — the TUI's own local/UI command
@@ -431,15 +294,17 @@ declare const HOST_COMMAND_CATALOG: ReadonlySet<string>;
431
294
  * @param name - the slash name.
432
295
  * @param isSkillWrapper - the live skill-wrapper test (absent = none).
433
296
  * @param isDynamicLocal - the live client-contribution test (absent = none).
434
- * @param hostView - the host catalog's view of THIS LINE (`undefined` = the
435
- * catalog does not resolve the name at all; see {@link HostCommandClaim}).
436
- * A name the catalog RESOLVES is never a client-local line: when the
437
- * catalog claims the line the host's own `input.attachments` declaration
438
- * decides, and when it does not (an argued line of an execute-kind
439
- * command) the line is an ordinary submission — never a same-named client
440
- * contribution's.
297
+ * @param hostResolvesName - whether the AUTHORITATIVE HOST CATALOG resolves
298
+ * the NAME (PR5 supplement: derived from `hostCatalogResolves`, NEVER from
299
+ * the effective line claim — the claim-set union also carries this
300
+ * Client's own TUI registrations, so a self-claim must not disqualify the
301
+ * TUI's own local commands). A Host-resolved name is never a client-local
302
+ * line: when the catalog claims the line the host's own
303
+ * `input.attachments` declaration decides, and when it does not (an argued
304
+ * line of an execute-kind command) the line is an ordinary submission —
305
+ * never a same-named client contribution's.
441
306
  */
442
- declare function isLocalCommandLine(name: string, isSkillWrapper: ((name: string) => boolean) | undefined, isDynamicLocal: ((name: string) => boolean) | undefined, hostView?: HostCommandClaim | undefined): boolean;
307
+ declare function isLocalCommandLine(name: string, isSkillWrapper: ((name: string) => boolean) | undefined, isDynamicLocal: ((name: string) => boolean) | undefined, hostResolvesName: boolean): boolean;
443
308
  /**
444
309
  * Whether one parsed line is the BARE slash token (DSH `matchEnter`'s `bare`:
445
310
  * no input follows the name — trailing whitespace is not input). It is the
@@ -473,30 +338,16 @@ declare function isBareCommandLine(parsed: {
473
338
  * @param parsed - the parsed slash command (undefined = plain prompt).
474
339
  * @param isSkillWrapper - the live skill-wrapper test (absent = none).
475
340
  * @param isDynamicLocal - the live client-contribution test (absent = none).
476
- * @param hostClaim - the live HOST-catalog view of THIS LINE (absent = none).
341
+ * @param hostResolvesName - whether the AUTHORITATIVE HOST CATALOG resolves
342
+ * the name (`hostCatalogResolves` — PR5 supplement: never derived from the
343
+ * effective line claim, whose union carries the Client's own TUI
344
+ * registrations).
477
345
  * @returns whether the line is a local command line.
478
346
  */
479
347
  declare function commandIsLocalForAttachments(parsed: {
480
348
  name: string;
481
349
  rawInput?: string;
482
- } | undefined, isSkillWrapper: ((name: string) => boolean) | undefined, isDynamicLocal: ((name: string) => boolean) | undefined, hostClaim?: ((parsed: {
483
- name: string;
484
- rawInput?: string;
485
- }) => HostCommandClaim | undefined) | undefined): boolean;
486
- /**
487
- * The TUI dispatch boundary's delivery resolution: the WEB composer policy
488
- * ({@link resolveComposerDelivery}) applied to agent-facing input, with the
489
- * TUI's own ownership terms on top. Pure so the dispatch gate (inside the
490
- * runner closure) is testable headless.
491
- * @param parsed - the parsed slash command, undefined for a plain prompt.
492
- * @param running - whether the live agent reports running.
493
- * @param gesture - the composer gesture that raised the submission.
494
- * @param busyEnter - the persisted preference value (''/undefined = queue).
495
- */
496
- declare function resolveSubmitDelivery(parsed: {
497
- name: string;
498
- rawInput?: string;
499
- } | undefined, running: boolean, gesture: ComposerSubmitGesture, busyEnter: string | undefined): SubmitDelivery;
350
+ } | undefined, isSkillWrapper: ((name: string) => boolean) | undefined, isDynamicLocal: ((name: string) => boolean) | undefined, hostResolvesName: boolean): boolean;
500
351
  /**
501
352
  * Normalize an explicit `/skill <name> <args>` invocation to the skill's
502
353
  * own slash line `/<name> <args>` (review finding 2). The harness's
@@ -529,61 +380,388 @@ declare function normalizeSkillInvocation(text: string): string | undefined;
529
380
  declare function shouldConsumeAdvertisedMiss(execution: {
530
381
  readonly result: unknown;
531
382
  } | undefined, wasAdvertised: boolean): boolean;
532
- /** Public settlement shape for the interrupt helper. Kept local so the
533
- * entry-point declaration does not expose the internal runtime port module. */
534
- type InterruptWriteOutcome = {
535
- readonly kind: 'committed';
536
- readonly value: undefined;
537
- } | {
538
- readonly kind: 'rejected';
539
- readonly error: {
540
- readonly code: string;
541
- readonly message: string;
542
- readonly details?: Readonly<Record<string, unknown>>;
543
- };
544
- } | {
545
- readonly kind: 'cancelled';
546
- } | {
547
- readonly kind: 'indeterminate';
548
- readonly error: {
549
- readonly code: string;
550
- readonly message: string;
551
- readonly details?: Readonly<Record<string, unknown>>;
552
- };
553
- } | {
554
- readonly kind: 'unsupported';
555
- readonly reason: string;
556
- };
557
- /** The live-agent surface {@link interruptAgent} needs (structural — the
558
- * TUI never imports the agent runtime for this call). */
559
- interface InterruptAgentLike {
560
- readonly session: {
561
- readonly id: string;
562
- };
563
- readonly status: string;
564
- cancel(cause: {
565
- kind: 'user';
566
- }, options?: {
567
- keepInbox?: boolean;
568
- }): void;
569
- }
570
- /** The writer surface {@link interruptAgent} needs (structural — a LOCAL
571
- * type so the public declaration never inlines internal runtime modules;
572
- * the runner's SessionWriter satisfies it). */
573
- interface InterruptWriterLike {
574
- cancel(sessionId: string): Promise<InterruptWriteOutcome>;
575
- }
576
383
  /**
577
- * Interrupt the live agent (web Stop parity): abort the current
578
- * turn/tool run while PRESERVING the pending queue. dsh's DEFAULT
579
- * `cancel()` clears queued AND steering input, so a bare cancel would
580
- * destroy everything the user queued with Ctrl+S / queue-mode Enter —
581
- * the Esc-interrupt semantic is "stop the current thinking", never
582
- * "drop the queue". `keepInbox: true` parks the preserved work; dsh's
583
- * cancel is a documented no-op when nothing is active, so an idle
584
- * interrupt (double-Esc while idle) is harmless and still aborts a
585
- * local shell / maintenance task through the caller.
586
- *
384
+ * Whether a plain submitted draft is the quit word: exactly `exit` (trimmed,
385
+ * lowercase). The runner intercepts this BEFORE any session creation or
386
+ * submission (shell muscle memory); anything else — `exit!`, `Exit`, or a
387
+ * draft with a recalled entry still in it — is an ordinary message.
388
+ * @param text - the submitted draft.
389
+ */
390
+ declare function isPlainExitPrompt(text: string): boolean;
391
+ //#endregion
392
+ //#region src/domain/shell/danger.d.ts
393
+ /**
394
+ * The destructive-shell predicate the approval dialog uses (kimi-inspired).
395
+ * Transport/UI-neutral: it answers exactly one question — whether one shell
396
+ * command matches a known destructive pattern — and owns nothing else.
397
+ * @module @xmoon76/dsh-pi-tui/domain/shell/danger
398
+ */
399
+ /**
400
+ * Whether a shell command matches a destructive pattern. `rm` is treated
401
+ * specially: any spelling of recursive + force flags (`rm -rf`, `rm -r -f`,
402
+ * `rm -rf /`) is dangerous; the remaining patterns are verbatim matches.
403
+ */
404
+ declare function dangerCommand(command: string): boolean;
405
+ //#endregion
406
+ //#region src/domain/media/types.d.ts
407
+ /**
408
+ * Neutral media vocabulary shared by Client-local media capability, Direct
409
+ * application preparation, the transport-neutral prepared prompt and TUI
410
+ * presentation (TS8-C).
411
+ *
412
+ * Every shape here is a DETACHED structural fact — the accepted raster media
413
+ * types, one durable attachment reference and one deployment's image policy —
414
+ * that crosses several layers without carrying service, `Context`, `Agent` or
415
+ * Session authority. They deliberately mirror the `@deepseek-ai/dsh-attachment`
416
+ * shapes structurally, so no module needs a runtime dependency on that package
417
+ * (AGENTS.md decision 7).
418
+ *
419
+ * Host service interfaces (`saveImages`, `saveFileStream`, `resolveModelInfo`)
420
+ * MUST NOT enter this module: those are injected at the composition root and
421
+ * owned by the Direct application preparation.
422
+ * @module @xmoon76/dsh-pi-tui/domain/media/types
423
+ */
424
+ /** Raster media types accepted by the TUI image intake. */
425
+ type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
426
+ /** Structural subset of `ImageAttachmentRef`. */
427
+ interface ImageAttachmentRefLike {
428
+ readonly attachmentId: string;
429
+ readonly mediaType: ImageMediaType;
430
+ readonly bytes: number;
431
+ readonly width: number;
432
+ readonly height: number;
433
+ readonly name?: string;
434
+ }
435
+ //#endregion
436
+ //#region src/client/media/image/types.d.ts
437
+ /** Per-TUI draft image identity; only meaningful inside one TUI run. */
438
+ type DraftImageId = number;
439
+ /** Where a staged image's bytes came from. */
440
+ type DraftImageSource = {
441
+ readonly type: 'clipboard';
442
+ } | {
443
+ readonly type: 'path';
444
+ readonly path: string;
445
+ }
446
+ /** The image was pulled back from a queued message: its bytes are ALREADY
447
+ * durable with the harness (the recalled ref) — the draft holds no local
448
+ * copy and re-submitting reuses the ref instead of re-uploading. */
449
+ | {
450
+ readonly type: 'recalled';
451
+ };
452
+ /**
453
+ * One image staged in the editor draft. The draft holds the raw bytes so
454
+ * clipboard and path entries share one shape and a submit never depends on
455
+ * the source file still existing (plan §5.1). A RECALLED draft (pulled
456
+ * back from the queue) instead carries the durable `ImageAttachmentRef`
457
+ * and NO local bytes — the bytes stay in harness storage and re-submitting
458
+ * reuses the ref.
459
+ */
460
+ interface DraftImage {
461
+ /** Per-TUI draft identity (the `#N` of the placeholder). */
462
+ readonly id: DraftImageId;
463
+ readonly kind: 'image';
464
+ readonly source: DraftImageSource;
465
+ /** Exact encoded bytes, held only for the draft lifetime. Empty for a
466
+ * recalled draft (its bytes live in harness storage). */
467
+ readonly bytes: Uint8Array;
468
+ readonly mediaType: ImageMediaType;
469
+ /** Intrinsic encoded width in pixels. */
470
+ readonly width: number;
471
+ /** Intrinsic encoded height in pixels. */
472
+ readonly height: number;
473
+ /** Exact encoded byte length (`bytes.byteLength` for local drafts; the
474
+ * durable ref's recorded `bytes` for recalled drafts). */
475
+ readonly byteLength: number;
476
+ /** Optional display name (basename for path sources), never a path. */
477
+ readonly name?: string;
478
+ /** The canonical editor placeholder text for this draft. */
479
+ readonly placeholder: string;
480
+ /** The durable ref this draft reuses on submit; present ONLY for
481
+ * recalled drafts (already-durable images are never re-uploaded). */
482
+ readonly recalledRef?: ImageAttachmentRefLike;
483
+ }
484
+ /** The store surface the placeholder expansion needs (structural). */
485
+ interface DraftImageStoreLike {
486
+ values(): readonly DraftImage[];
487
+ }
488
+ //#endregion
489
+ //#region src/app/surface/task-presentation.d.ts
490
+ declare function subagentJobTranscriptId(snapshot: unknown): string | undefined;
491
+ /**
492
+ * The Task Center row-selection disposition (plan §4.3/§4.4). A subagent
493
+ * transcript opens a session/viewer surface that REPLACES the browser; a
494
+ * Job row's detail keeps it mounted (the caller passes {@link openJobView}'s
495
+ * disposition). An UNKNOWN row — a stale panel selection after a live
496
+ * re-projection — also keeps the parent usable instead of dismissing it.
497
+ */
498
+ declare function taskRowSelectionDisposition(row: {
499
+ readonly kind: 'job' | 'subagent';
500
+ } | undefined, jobDetail: 'close' | 'keep-open'): 'close' | 'keep-open';
501
+ /** Viewer body for a subagent job with no uniquely matched child. */
502
+ declare function subagentJobViewHint(status: string, detail: string | undefined): string;
503
+ //#endregion
504
+ //#region src/app/surface/compaction-presentation.d.ts
505
+ /**
506
+ * The compaction/context presentation module (A4-7, plan §16/§27): the pure
507
+ * compaction/context presentation folds consumed by the surface routing.
508
+ *
509
+ * These are the PURE presentation folds the `session/event` routing applies:
510
+ * compaction lifecycle pairing, the matched-settle surface effects, the
511
+ * turn-boundary busy rule, the context re-measure classification, and the
512
+ * resumed-session-log bootstrap folds (`workingFromLog`, `compactingFromLog`).
513
+ * They read no Host identity and no persistence, so they are an application-owned
514
+ * surface presentation module under `app/surface/**` (TS8-F5 moved them out of
515
+ * the top-level root).
516
+ *
517
+ * `src/index.ts` re-exports them unchanged to keep the public entry point
518
+ * byte-compatible (the published package and the regression suites import them
519
+ * from the package root).
520
+ *
521
+ * @module @xmoon76/dsh-pi-tui/app/surface/compaction-presentation
522
+ */
523
+ /** The canonical compaction phase the mounted surface renders (TS8-F5 moved
524
+ * the union out of the tui-app.ts stable root; the root type-re-exports it). */
525
+ type CompactionPhase = 'idle' | 'summarizing' | 'applying';
526
+ /** One fold of a compaction lifecycle event over the in-flight compaction
527
+ * state. Pure (the routing applies the returned surface effects):
528
+ * dsh-compaction is not a peer, so the event is read structurally. */
529
+ interface CompactionFold {
530
+ /** The updated in-flight compaction id (the newest start wins). */
531
+ id: string | undefined;
532
+ /** compaction/start: the compacting flag turns ON (footer/working row). */
533
+ active: boolean;
534
+ /** A MATCHED compaction/end: the compacting flag turns OFF and busy is
535
+ * re-derived from the turn log (a stale end never clears a newer
536
+ * compaction's state). */
537
+ clear: boolean;
538
+ /** The compaction phase the event implies: compaction/start →
539
+ * 'summarizing', a MATCHED compaction/summary → 'applying'. Undefined
540
+ * when the event does not advance the phase (a stale summary, any
541
+ * compaction/end — the settle clears via {@link clear}). */
542
+ phase?: Exclude<CompactionPhase, 'idle'>;
543
+ /** The settle notification for compaction/end, when one fires. */
544
+ notify: {
545
+ text: string;
546
+ kind: 'info' | 'error';
547
+ } | undefined;
548
+ }
549
+ /** Fold one compaction lifecycle event over the in-flight state. */
550
+ declare function foldCompactionEvent(state: {
551
+ id: string | undefined;
552
+ }, event: {
553
+ type: string;
554
+ data: {
555
+ compactionId?: unknown;
556
+ error?: unknown;
557
+ };
558
+ }): CompactionFold;
559
+ /** The minimal compaction-settle surface the routing passes in — STRUCTURAL
560
+ * on purpose: referencing the full {@link TuiApp} class from a public
561
+ * export would inline the whole surface (and its internal registry/
562
+ * presentation dependencies) into the published declaration bundle. The
563
+ * settle contract only needs the three phase/busy/working setters. */
564
+ interface CompactionSettleSurface {
565
+ setCompactionPhase(phase: 'idle'): void;
566
+ setBusy(busy: boolean): void;
567
+ setWorking(busy: boolean): void;
568
+ }
569
+ /** The UI side effects of a MATCHED compaction settle: clear the phase,
570
+ * hand the working row back to the turn state, and re-measure the session
571
+ * surface so the footer context reflects the compacted log IMMEDIATELY —
572
+ * the next step/start or turn/end would otherwise delay the refresh.
573
+ * Exported as a seam so the settle contract is testable without a full
574
+ * runner driver (the firehose closure is not). */
575
+ declare function settleCompactionSurface(app: CompactionSettleSurface, refreshStatus: () => void, busyNow: boolean): void;
576
+ /** The busy flag after a turn-boundary event: a turn end must NOT clear
577
+ * the busy state while a compaction is still in flight — an interrupted
578
+ * turn can close (turn/end) before its compaction settles, and the
579
+ * single-Esc cancel must stay armed until compaction/end. */
580
+ declare function busyAfterTurnBoundary(eventType: 'turn/start' | 'turn/end', compacting: boolean): boolean;
581
+ /** PR D2 test seam: whether a session event type marks the model-visible
582
+ * context dirty (re-measure through the SessionReader port) or only
583
+ * repaints cheaply (cached measurement). The firehose routes every event
584
+ * through this classification — the single source of truth for the
585
+ * status/measurement split. `compaction/end` is classified 'measure' but
586
+ * the firehose deliberately SKIPS it here: a matched compaction settle
587
+ * re-measures through the fold-outcome path (settleCompactionSurface), so
588
+ * a STALE compaction/end can never trigger a measurement. */
589
+ declare function contextRefreshKind(eventType: string): 'measure' | 'cheap';
590
+ /**
591
+ * The in-flight compaction state a resumed session log implies: the newest
592
+ * compaction bracket decides. A `session/end-seed` boundary makes any
593
+ * EARLIER unmatched `compaction/start` STALE — the upstream invariant
594
+ * (inheritedOrphanStartSeqs) treats seed compactions that never settled
595
+ * inside the seed as abandoned, so they must not re-arm the compacting
596
+ * surface on resume.
597
+ */
598
+ declare function compactingFromLog(events: readonly {
599
+ type: unknown;
600
+ data?: unknown;
601
+ }[]): {
602
+ active: boolean;
603
+ id: string | undefined;
604
+ };
605
+ //#endregion
606
+ //#region src/app/submission/pending-input.d.ts
607
+ /** One semantic pending-input item as the queue mirror sees it. */
608
+ interface QueueInboxMessage {
609
+ readonly id: string;
610
+ readonly content: readonly ContentBlock[];
611
+ }
612
+ //#endregion
613
+ //#region src/app/surface/pending-presentation.d.ts
614
+ /** One semantic queued pending-input row for the queue pane. */
615
+ interface QueueItem {
616
+ /** The pending message id (agent inbox identity), or a local request id. */
617
+ id: string;
618
+ /** The message text, single-line display form. */
619
+ text: string;
620
+ /** next-turn followup vs next-step steer. */
621
+ mode: 'followup' | 'steer';
622
+ /** The correlation identity (authoritative rpc id or local request id). */
623
+ rpcId?: string;
624
+ /** A client-local echo not yet backed by an authoritative occurrence. */
625
+ local?: boolean;
626
+ }
627
+ /** The queue-pane rows for one semantic pending-input batch. */
628
+ interface QueueFoldResult {
629
+ readonly rows: QueueItem[];
630
+ }
631
+ /** Build queue-pane rows from semantic pending-input occurrences, preserving
632
+ * their order and content without inspecting backend-specific metadata. */
633
+ declare function foldQueueRows(messages: readonly QueueInboxMessage[], mode: 'followup' | 'steer'): QueueFoldResult;
634
+ //#endregion
635
+ //#region src/tui-app.d.ts
636
+ /**
637
+ * The WEB composer submit gestures (DSH `ComposerSubmitGesture`): plain
638
+ * Enter, or the Cmd/Ctrl-accelerated chord. The busy-Enter policy resolves
639
+ * each to a delivery mode — plain Enter to the preferred mode, the
640
+ * accelerated chord to its OPPOSITE — so the chord is never a fixed mode.
641
+ */
642
+ type ComposerSubmitGesture = 'enter' | 'accelerated';
643
+ //#endregion
644
+ //#region src/commands.d.ts
645
+ /**
646
+ * The effective mode ONE submission resolved at the submit boundary: whether
647
+ * an agent-facing delivery steers into the running turn or queues behind it
648
+ * (web `ComposerSubmissionPolicy` parity — an idle agent queues, plain Enter
649
+ * takes the preference, and the accelerated chord takes its OPPOSITE).
650
+ *
651
+ * It is a property of the GESTURE, not of the persisted settings: a one-shot
652
+ * gesture exists only inside the dispatch that resolved it, so every
653
+ * downstream consumer (the TUI skill delivery) accepts this value instead of
654
+ * re-deriving the mode from `busyEnter`.
655
+ */
656
+ type SubmitDelivery = 'steer' | 'queue';
657
+ //#endregion
658
+ //#region src/app/submission/command-policy.d.ts
659
+ /**
660
+ * Command semantics matrix (plan §19.3/M12): a LOCAL command line carrying a
661
+ * staged image placeholder is REJECTED — local commands are pure UI
662
+ * controls, never LLM prompts. AGENT-FACING input (plain prompts AND
663
+ * per-skill slash invocations like `/grilling`) SUPPORTS images: the skill
664
+ * wrapper builds its message through the same prepared-input path, so an
665
+ * image-bearing skill line is a real multimodal prompt (review finding 4).
666
+ * There is never a silent drop.
667
+ * @param parsed - the parsed slash command, undefined for a plain prompt.
668
+ * @param text - the submission text.
669
+ * @param store - the live draft store.
670
+ * @param isLocal - whether THIS LINE is a LOCAL (TUI-owned/UI) command; skill
671
+ * names answer false. Never derived from the name alone: a host command's
672
+ * input KIND decides which line it claims (see
673
+ * {@link commandIsLocalForAttachments}).
674
+ */
675
+ declare function commandRejectsImages(parsed: {
676
+ name: string;
677
+ rawInput?: string;
678
+ } | undefined, text: string, store: DraftImageStoreLike, isLocal: boolean): boolean;
679
+ /**
680
+ * The TUI dispatch boundary's delivery resolution: the WEB composer policy
681
+ * ({@link resolveComposerDelivery}) applied to agent-facing input, with the
682
+ * TUI's own ownership terms on top. Pure so the dispatch gate (inside the
683
+ * runner closure) is testable headless.
684
+ * @param parsed - the parsed slash command, undefined for a plain prompt.
685
+ * @param running - whether the live agent reports running.
686
+ * @param gesture - the composer gesture that raised the submission.
687
+ * @param busyEnter - the persisted preference value (''/undefined = queue).
688
+ * @param isTuiLocalLine - whether the line is a TUI-LOCAL command line by
689
+ * the §D3 line authority ({@link isLocalCommandLine}'s order: a
690
+ * HOST-RESOLVED name — claimed or merely resolved — is never local). The
691
+ * caller derives it once and shares it with every gate, so an argued
692
+ * `/export foo` of an execute-kind Host command follows the ORDINARY
693
+ * queue/steer busy policy instead of a local-command placeholder.
694
+ * Absent = fall back to the legacy LOCAL_COMMANDS set (callers that have
695
+ * no host view yet, e.g. the pre-parse echo path).
696
+ */
697
+ declare function resolveSubmitDelivery(parsed: {
698
+ name: string;
699
+ rawInput?: string;
700
+ } | undefined, running: boolean, gesture: ComposerSubmitGesture, busyEnter: string | undefined, isTuiLocalLine?: boolean): SubmitDelivery;
701
+ //#endregion
702
+ //#region src/app/session/interrupt.d.ts
703
+ /**
704
+ * The live-turn interrupt helper (web Stop parity): abort the current
705
+ * turn/tool run while PRESERVING the pending queue. Kept out of the
706
+ * composition root so the rule is testable headless, and its structural
707
+ * faces keep the internal runtime port module out of the published entry.
708
+ * @module @xmoon76/dsh-pi-tui/app/session/interrupt
709
+ */
710
+ /** Public settlement shape for the interrupt helper. Kept local so the
711
+ * entry-point declaration does not expose the internal runtime port module. */
712
+ type InterruptWriteOutcome = {
713
+ readonly kind: 'committed';
714
+ readonly value: undefined;
715
+ } | {
716
+ readonly kind: 'rejected';
717
+ readonly error: {
718
+ readonly code: string;
719
+ readonly message: string;
720
+ readonly details?: Readonly<Record<string, unknown>>;
721
+ };
722
+ } | {
723
+ readonly kind: 'cancelled';
724
+ } | {
725
+ readonly kind: 'indeterminate';
726
+ readonly error: {
727
+ readonly code: string;
728
+ readonly message: string;
729
+ readonly details?: Readonly<Record<string, unknown>>;
730
+ };
731
+ } | {
732
+ readonly kind: 'unsupported';
733
+ readonly reason: string;
734
+ };
735
+ /** The live-agent surface {@link interruptAgent} needs (structural — the
736
+ * TUI never imports the agent runtime for this call). */
737
+ interface InterruptAgentLike {
738
+ readonly session: {
739
+ readonly id: string;
740
+ };
741
+ readonly status: string;
742
+ cancel(cause: {
743
+ kind: 'user';
744
+ }, options?: {
745
+ keepInbox?: boolean;
746
+ }): void;
747
+ }
748
+ /** The writer surface {@link interruptAgent} needs (structural — a LOCAL
749
+ * type so the public declaration never inlines internal runtime modules;
750
+ * the runner's SessionWriter satisfies it). */
751
+ interface InterruptWriterLike {
752
+ cancel(sessionId: string): Promise<InterruptWriteOutcome>;
753
+ }
754
+ /**
755
+ * Interrupt the live agent (web Stop parity): abort the current
756
+ * turn/tool run while PRESERVING the pending queue. dsh's DEFAULT
757
+ * `cancel()` clears queued AND steering input, so a bare cancel would
758
+ * destroy everything the user queued with Ctrl+S / queue-mode Enter —
759
+ * the Esc-interrupt semantic is "stop the current thinking", never
760
+ * "drop the queue". `keepInbox: true` parks the preserved work; dsh's
761
+ * cancel is a documented no-op when nothing is active, so an idle
762
+ * interrupt (double-Esc while idle) is harmless and still aborts a
763
+ * local shell / maintenance task through the caller.
764
+ *
587
765
  * NOTE (upstream dependency): dsh currently PARKS the preserved queue
588
766
  * after an abort — the "Esc with a queue continues immediately" UX
589
767
  * needs an upstream `wakePending`/`continueInbox` capability (not yet
@@ -593,6 +771,8 @@ interface InterruptWriterLike {
593
771
  * agreed web-parity behavior until upstream lands the capability.
594
772
  */
595
773
  declare function interruptAgent(agent: InterruptAgentLike | undefined, writer: InterruptWriterLike): Promise<InterruptWriteOutcome>;
774
+ //#endregion
775
+ //#region src/app/surface/viewer-policy.d.ts
596
776
  /** One unsettled subagent delegation, in tool/call order. */
597
777
  interface PendingSubagentCall {
598
778
  readonly callId: string;
@@ -664,218 +844,117 @@ declare function viewerActionCapability(action: TuiAction, viewer: {
664
844
  * @returns the matched call, or undefined when nothing matches.
665
845
  */
666
846
  declare function matchPendingSubagentCall(pending: PendingSubagentCall[], label: string | undefined): PendingSubagentCall | undefined;
847
+ //#endregion
848
+ //#region src/client/launcher/profile.d.ts
667
849
  /**
668
- * The pre-mount surface catalog resolution:
669
- * - an explicit `--session` start PREFETCHES the resumed agent's effective
670
- * catalog (a live read emits no session events);
671
- * - the deferred start (no `--session`) reads the cold HUMAN SKILL catalog
672
- * through the preset's STANDING SCOPE — no Agent, no session, no turn —
673
- * so the first input sees human-invocable skills without any durable
674
- * side effect (the mechanism that avoids the probe dead end: host
675
- * `session/created` observers write durable knob events into every fresh
676
- * session).
850
+ * The Client-local DSH launcher PROFILE reads: the `--profile` argv scrape and
851
+ * the interactive-quit resume hint.
677
852
  *
678
- * The snapshot (resume) or the skill catalog (cold) installs synchronously
679
- * after mount (the ready barrier).
680
- *
681
- * Failure taxonomy (plan appendix B):
682
- * - lifecycle cancellation: nothing installed, no notice;
683
- * - a prefetch/standing read failure degrades to a one-shot notice (the
684
- * TUI mounts with the global view and built-in commands);
685
- * - a missing/unknown preset or a broken standing mount degrades the cold
686
- * target to the global layer with a one-shot notice — never a probe
687
- * Agent, never a startup failure;
688
- * - an ordinary provider read failure never rejects here: it becomes an
689
- * empty field + detached issue inside the catalog.
690
- * @param options - injected dependencies (see {@link ResolveInitialCatalogOptions}).
691
- * @returns the snapshot / skill catalog to install and an optional notice.
692
- */
693
- interface InitialCatalogResolution {
694
- /** The resume prefetch snapshot to install at mount. */
695
- readonly snapshot?: SurfaceCatalogSnapshot;
696
- /** The cold standing-scope human skill catalog (deferred start). */
697
- readonly skills?: HumanSkillCatalog;
698
- /** A user-facing notice when the prefetch/standing read degraded. */
699
- readonly notice?: string;
700
- }
701
- /** Options for {@link resolveInitialCatalog}. */
702
- interface ResolveInitialCatalogOptions {
703
- /** The resumed live agent, if any (prefetch path). */
704
- readonly liveAgent?: Agent;
705
- /** The effective preset id for the cold standing read (undefined = the
706
- * deployment default; only consulted for the deferred start). */
707
- readonly presetId?: string;
708
- readonly signal: AbortSignal;
709
- /** The context surface the collectors read services from. */
710
- readonly ctx: SurfaceCatalogContext;
711
- readonly diag: Diag;
712
- /** Suspend the pre-mount startup status before an ordinary log write
713
- * (the status owns the current terminal line; a TTY shares one cursor
714
- * between stdout and stderr). Called right before every diag.warn this
715
- * function may emit. */
716
- readonly onLog?: () => void;
717
- }
718
- declare function resolveInitialCatalog(options: ResolveInitialCatalogOptions): Promise<InitialCatalogResolution>;
719
- declare function subagentJobTranscriptId(snapshot: unknown): string | undefined;
720
- /**
721
- * The Task Center row-selection disposition (plan §4.3/§4.4). A subagent
722
- * transcript opens a session/viewer surface that REPLACES the browser; a
723
- * Job row's detail keeps it mounted (the caller passes {@link openJobView}'s
724
- * disposition). An UNKNOWN row — a stale panel selection after a live
725
- * re-projection — also keeps the parent usable instead of dismissing it.
726
- */
727
- declare function taskRowSelectionDisposition(row: {
728
- readonly kind: 'job' | 'subagent';
729
- } | undefined, jobDetail: 'close' | 'keep-open'): 'close' | 'keep-open';
730
- /** Viewer body for a subagent job with no uniquely matched child. */
731
- declare function subagentJobViewHint(status: string, detail: string | undefined): string;
732
- /**
733
- * Whether a plain submitted draft is the quit word: exactly `exit` (trimmed,
734
- * lowercase). The runner intercepts this BEFORE any session creation or
735
- * submission (shell muscle memory); anything else — `exit!`, `Exit`, or a
736
- * draft with a recalled entry still in it — is an ordinary message.
737
- * @param text - the submitted draft.
738
- */
739
- declare function isPlainExitPrompt(text: string): boolean;
740
- /** One semantic pending-input item as the queue mirror sees it. */
741
- interface QueueInboxMessage {
742
- readonly id: string;
743
- readonly content: readonly ContentBlock[];
744
- }
745
- /** The queue-pane rows for one semantic pending-input batch. */
746
- interface QueueFoldResult {
747
- readonly rows: QueueItem[];
748
- }
749
- /** Build queue-pane rows from semantic pending-input occurrences, preserving
750
- * their order and content without inspecting backend-specific metadata. */
751
- declare function foldQueueRows(messages: readonly QueueInboxMessage[], mode: 'followup' | 'steer'): QueueFoldResult;
752
- /**
753
- * Whether a shell command matches a destructive pattern. `rm` is treated
754
- * specially: any spelling of recursive + force flags (`rm -rf`, `rm -r -f`,
755
- * `rm -rf /`) is dangerous; the remaining patterns are verbatim matches.
853
+ * Deliberately NOT part of `src/startup.ts`: the startup row is a
854
+ * zero-dependency island that must not share a bundle chunk with the runner,
855
+ * so these runner-side helpers live in their own module. The authoritative
856
+ * Host profile identity (`hostRunningProfile`) is application bootstrap
857
+ * ownership (`app/bootstrap/profile.ts`) and consumes this argv fallback.
858
+ * @module @xmoon76/dsh-pi-tui/client/launcher/profile
756
859
  */
757
- declare function dangerCommand(command: string): boolean;
758
860
  /**
759
861
  * The dsh profile named by the `--profile` flag in `argv`, in both spellings.
760
862
  * This is the ARGV-ONLY fallback: it cannot see the positional `dsh <name>`
761
863
  * launch form, because the launcher consumes that bare name by synthesizing
762
864
  * `['--profile', ...argv]` for its OWN commander parse and leaves
763
- * `process.argv` untouched. Prefer {@link hostRunningProfile}, which reads the
764
- * Host's profile identity instead of scraping the command line.
865
+ * `process.argv` untouched. Prefer `hostRunningProfile`, which reads the Host's
866
+ * profile identity instead of scraping the command line.
765
867
  * @param argv - the process argument vector.
766
868
  * @param fallback - the default profile.
767
869
  */
768
870
  declare function runningProfile(argv?: readonly string[], fallback?: string): string;
871
+ /**
872
+ * The interactive-quit resume hint (pi parity): `dsh --profile <p>
873
+ * --session <id>`, printed after the terminal restores so the user can
874
+ * re-enter the session later. Returns undefined when there is no session
875
+ * to resume (deferred start never created one).
876
+ * @param profile - the running profile (`hostRunningProfile`).
877
+ * @param sessionId - the live session id.
878
+ * @returns the resume command line, or undefined without a session.
879
+ */
880
+ declare function resumeCommand(profile: string, sessionId: string): string | undefined;
881
+ //#endregion
882
+ //#region src/app/bootstrap/profile.d.ts
883
+ /**
884
+ * The authoritative running-profile read for this process — the official Host
885
+ * `profileContext.name` when it is composed, else the Client-local argv scrape
886
+ * (`client/launcher/profile.ts`).
887
+ *
888
+ * `profileContext.name` is the launcher's own profile identity, so it is
889
+ * correct for EVERY launch form: `--profile <name>`, `--profile=<name>`, the
890
+ * positional `dsh <name>`, and `--from-default-profile <name>`. The argv scrape
891
+ * only knows the flag form, which is why it is the fallback for a profile-less
892
+ * mount (tests, an embedded surface) rather than the primary source. Bootstrap
893
+ * composition ownership: it reads one Host context service and nothing else.
894
+ * @module @xmoon76/dsh-pi-tui/app/bootstrap/profile
895
+ */
769
896
  /** The one context read {@link hostRunningProfile} needs (a cordis Context satisfies it). */
770
897
  interface ProfileContextReadLike {
771
898
  get(name: string): unknown;
772
899
  }
773
900
  /**
774
- * The profile this process actually runs — the official Host
775
- * `profileContext.name` when it is composed, else {@link runningProfile}'s
776
- * argv scrape.
777
- *
778
- * `profileContext.name` is the launcher's own profile identity, so it is
779
- * correct for EVERY launch form: `--profile <name>`, `--profile=<name>`, the
780
- * positional `dsh <name>`, and `--from-default-profile <name>`. The argv
781
- * scrape only knows the flag form, which is why it is the fallback for a
782
- * profile-less mount (tests, an embedded surface) rather than the primary
783
- * source.
901
+ * The profile this process actually runs.
784
902
  * @param ctx - the plugin context (or any structural `get`-only stand-in).
785
903
  * @param argv - the process argument vector for the fallback path.
786
904
  * @param fallback - the default profile for the fallback path.
787
905
  * @returns the running profile name.
788
906
  */
789
907
  declare function hostRunningProfile(ctx: ProfileContextReadLike, argv?: readonly string[], fallback?: string): string;
908
+ //#endregion
909
+ //#region src/index.d.ts
910
+ /** Stable Cordis plugin name. */
911
+ declare const name = "tui-runner";
912
+ /** Core services required before the TUI can mount. */
913
+ declare const inject: string[];
790
914
  /**
791
- * The interactive-quit resume hint (pi parity): `dsh --profile <p>
792
- * --session <id>`, printed after the terminal restores so the user can
793
- * re-enter the session later. Returns undefined when there is no session
794
- * to resume (deferred start never created one).
795
- * @param profile - the running profile ({@link hostRunningProfile}).
796
- * @param sessionId - the live session id.
797
- * @returns the resume command line, or undefined without a session.
915
+ * Options for {@link resolveInitialCatalog}.
916
+ *
917
+ * The public declaration stays here (A5a review P1): `liveAgent` keeps the Host
918
+ * `Agent` type it has always had, so consumers that READ the property keep
919
+ * compiling. The implementation consumes the structural
920
+ * `SurfaceCatalogResolutionOptions` in `app/direct/initial-catalog.ts`.
798
921
  */
799
- declare function resumeCommand(profile: string, sessionId: string): string | undefined;
800
- /** One fold of a compaction lifecycle event over the runner's in-flight
801
- * compaction state. Pure (the firehose applies the returned surface
802
- * effects): dsh-compaction is not a peer, so the event is read
803
- * structurally. */
804
- interface CompactionFold {
805
- /** The updated in-flight compaction id (the newest start wins). */
806
- id: string | undefined;
807
- /** compaction/start: the compacting flag turns ON (footer/working row). */
808
- active: boolean;
809
- /** A MATCHED compaction/end: the compacting flag turns OFF and busy is
810
- * re-derived from the turn log (a stale end never clears a newer
811
- * compaction's state). */
812
- clear: boolean;
813
- /** The compaction phase the event implies: compaction/start →
814
- * 'summarizing', a MATCHED compaction/summary → 'applying'. Undefined
815
- * when the event does not advance the phase (a stale summary, any
816
- * compaction/end — the settle clears via {@link clear}). */
817
- phase?: Exclude<CompactionPhase, 'idle'>;
818
- /** The settle notification for compaction/end, when one fires. */
819
- notify: {
820
- text: string;
821
- kind: 'info' | 'error';
822
- } | undefined;
823
- }
824
- /** Fold one compaction lifecycle event over the in-flight state. */
825
- declare function foldCompactionEvent(state: {
826
- id: string | undefined;
827
- }, event: {
828
- type: string;
829
- data: {
830
- compactionId?: unknown;
831
- error?: unknown;
832
- };
833
- }): CompactionFold;
834
- /** The minimal compaction-settle surface the runner passes in — STRUCTURAL
835
- * on purpose: referencing the full {@link TuiApp} class from a public
836
- * export would inline the whole surface (and its internal registry/
837
- * presentation dependencies) into the published declaration bundle. The
838
- * settle contract only needs the three phase/busy/working setters. */
839
- interface CompactionSettleSurface {
840
- setCompactionPhase(phase: 'idle'): void;
841
- setBusy(busy: boolean): void;
842
- setWorking(busy: boolean): void;
922
+ interface ResolveInitialCatalogOptions {
923
+ /** The resumed live agent, if any (prefetch path). */
924
+ readonly liveAgent?: Agent;
925
+ /** The effective preset id for the cold standing read (undefined = the
926
+ * deployment default; only consulted for the deferred start). */
927
+ readonly presetId?: string;
928
+ readonly signal: AbortSignal;
929
+ /** The context surface the collectors read services from. */
930
+ readonly ctx: SurfaceCatalogContext;
931
+ readonly diag: Diag;
932
+ /** Suspend the pre-mount startup status before an ordinary log write
933
+ * (the status owns the current terminal line; a TTY shares one cursor
934
+ * between stdout and stderr). Called right before every diag.warn this
935
+ * function may emit. */
936
+ readonly onLog?: () => void;
843
937
  }
844
- /** The UI side effects of a MATCHED compaction settle: clear the phase,
845
- * hand the working row back to the turn state, and re-measure the session
846
- * surface so the footer context reflects the compacted log IMMEDIATELY —
847
- * the next step/start or turn/end would otherwise delay the refresh.
848
- * Exported as a seam so the settle contract is testable without a full
849
- * runner driver (the firehose closure is not). */
850
- declare function settleCompactionSurface(app: CompactionSettleSurface, refreshStatus: () => void, busyNow: boolean): void;
851
- /** The busy flag after a turn-boundary event: a turn end must NOT clear
852
- * the busy state while a compaction is still in flight — an interrupted
853
- * turn can close (turn/end) before its compaction settles, and the
854
- * single-Esc cancel must stay armed until compaction/end. */
855
- declare function busyAfterTurnBoundary(eventType: 'turn/start' | 'turn/end', compacting: boolean): boolean;
856
- /** PR D2 test seam: whether a session event type marks the model-visible
857
- * context dirty (re-measure through the SessionReader port) or only
858
- * repaints cheaply (cached measurement). The firehose routes every event
859
- * through this classification — the single source of truth for the
860
- * status/measurement split. `compaction/end` is classified 'measure' but
861
- * the firehose deliberately SKIPS it here: a matched compaction settle
862
- * re-measures through the fold-outcome path (settleCompactionSurface), so
863
- * a STALE compaction/end can never trigger a measurement. */
864
- declare function contextRefreshKind(eventType: string): 'measure' | 'cheap';
865
938
  /**
866
- * The in-flight compaction state a resumed session log implies: the newest
867
- * compaction bracket decides. A `session/end-seed` boundary makes any
868
- * EARLIER unmatched `compaction/start` STALE — the upstream invariant
869
- * (inheritedOrphanStartSeqs) treats seed compactions that never settled
870
- * inside the seed as abandoned, so they must not re-arm the compacting
871
- * surface on resume.
939
+ * The pre-mount surface catalog resolution (plan §27): the published entry for
940
+ * {@link ResolveInitialCatalogOptions}. An explicit `--session` start
941
+ * prefetches the resumed agent's effective catalog; the deferred start reads the
942
+ * cold HUMAN SKILL catalog through the preset's STANDING SCOPE — no Agent, no
943
+ * session, no turn — so the first input sees human-invocable skills without any
944
+ * durable side effect. The implementation and its pre-mount failure taxonomy
945
+ * live in `app/direct/initial-catalog.ts`.
946
+ * @param options - injected dependencies (see {@link ResolveInitialCatalogOptions}).
947
+ * @returns the snapshot / skill catalog to install and an optional notice.
872
948
  */
873
- declare function compactingFromLog(events: readonly {
874
- type: unknown;
875
- data?: unknown;
876
- }[]): {
877
- active: boolean;
878
- id: string | undefined;
949
+ declare function resolveInitialCatalog(options: ResolveInitialCatalogOptions): Promise<InitialCatalogResolution>;
950
+ /**
951
+ * The PUBLIC structural shape of the shared mutable display authority. The
952
+ * canonical owner is `src/domain/display/preset.ts` (`DisplayState`); the
953
+ * published `composeAgent` signature stays STRUCTURAL so the internal source
954
+ * path never leaks into the declaration bundle (TS8-D).
955
+ */
956
+ type DisplayState = {
957
+ preset: 'focus' | 'compact' | 'full';
879
958
  };
880
959
  /** One agent's preset composition: the id to record and the setup that installs it. */
881
960
  interface AgentComposition {
@@ -945,4 +1024,4 @@ declare function recordedPreset(ctx: Context, sessionId: string): Promise<string
945
1024
  */
946
1025
  declare function apply(ctx: Context, config: Config): void;
947
1026
  //#endregion
948
- export { AgentComposition, CompactionFold, CompactionSettleSurface, Config, HOST_COMMAND_CATALOG, InitialCatalogResolution, InterruptAgentLike, InterruptWriteOutcome, InterruptWriterLike, LOCAL_COMMANDS, PendingSubagentCall, ProfileContextReadLike, QueueFoldResult, QueueInboxMessage, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, ViewerOpenToken, apply, busyAfterTurnBoundary, commandIsLocalForAttachments, commandRejectsImages, compactingFromLog, composeAgent, contextRefreshKind, createViewerOpenToken, dangerCommand, foldCompactionEvent, foldQueueRows, hostRunningProfile, inject, interruptAgent, isBareCommandLine, isLocalCommandLine, isPlainExitPrompt, matchPendingSubagentCall, name, normalizeSkillInvocation, recordedPreset, resolveInitialCatalog, resolveSubmitDelivery, resumeCommand, runningProfile, settleCompactionSurface, shouldConsumeAdvertisedMiss, subagentJobTranscriptId, subagentJobViewHint, taskRowSelectionDisposition, teardownViewerForSessionSwap, viewerActionCapability };
1027
+ export { AgentComposition, type CompactionFold, type CompactionSettleSurface, Config, HOST_COMMAND_CATALOG, type InitialCatalogResolution, type InterruptAgentLike, type InterruptWriteOutcome, type InterruptWriterLike, LOCAL_COMMANDS, type PendingSubagentCall, type ProfileContextReadLike, type QueueFoldResult, type QueueInboxMessage, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, type ViewerOpenToken, apply, busyAfterTurnBoundary, commandIsLocalForAttachments, commandRejectsImages, compactingFromLog, composeAgent, contextRefreshKind, createViewerOpenToken, dangerCommand, foldCompactionEvent, foldQueueRows, hostRunningProfile, inject, interruptAgent, isBareCommandLine, isLocalCommandLine, isPlainExitPrompt, matchPendingSubagentCall, name, normalizeSkillInvocation, recordedPreset, resolveInitialCatalog, resolveSubmitDelivery, resumeCommand, runningProfile, settleCompactionSurface, shouldConsumeAdvertisedMiss, subagentJobTranscriptId, subagentJobViewHint, taskRowSelectionDisposition, teardownViewerForSessionSwap, viewerActionCapability };