@xmoon76/dsh-pi-tui 0.5.0 → 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,27 +1,20 @@
1
- import { H as TuiAction } from "./public-types-YbB5Rhku.mjs";
1
+ import { H as TuiAction } from "./public-types-BEjj7FEb.mjs";
2
2
  import { SessionId } from "@deepseek-ai/dsh-session";
3
3
  import { ContentBlock } from "@deepseek-ai/dsh-llm";
4
4
  import { Context, Volatile } from "@deepseek-ai/cordis";
5
5
  import { Agent, ModelSelectionRef } from "@deepseek-ai/dsh-agent";
6
6
  import z from "@deepseek-ai/schemastery";
7
- //#region src/display-preset.d.ts
8
- /**
9
- * Canonical transcript display presets and their layered disclosure policy.
10
- *
11
- * DisplayPreset is the one runtime vocabulary for presentation state. Every
12
- * preset whose projection exists in this build is available; the availability
13
- * gate exists so a future preset can never be claimed by another preset's
14
- * renderer before its own projection ships.
15
- * @module @xmoon76/dsh-pi-tui/display-preset
16
- */
17
- /** The complete display vocabulary. */
18
- type DisplayPreset = 'focus' | 'compact' | 'full';
19
- /** The shared mutable display authority passed between the runner, prompt and UI. */
20
- interface DisplayState {
21
- preset: DisplayPreset;
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;
12
+ }
13
+ interface ResponseStyleState {
14
+ style: ResponseStyle;
22
15
  }
23
16
  //#endregion
24
- //#region src/diag.d.ts
17
+ //#region src/runtime/process/diagnostics.d.ts
25
18
  interface Diag {
26
19
  debug(message: string, fields?: Record<string, unknown>): void;
27
20
  info(message: string, fields?: Record<string, unknown>): void;
@@ -31,38 +24,18 @@ interface Diag {
31
24
  dispose(): void;
32
25
  }
33
26
  //#endregion
34
- //#region src/communication-policy.d.ts
35
- type ProgressUpdates = 'off' | 'milestones' | 'frequent';
36
- type ResponseStyle = 'default' | 'concise' | 'explanatory';
37
- interface ProgressUpdatesState {
38
- mode: ProgressUpdates;
39
- }
40
- interface ResponseStyleState {
41
- style: ResponseStyle;
42
- }
43
- //#endregion
44
- //#region src/skill-catalog.d.ts
27
+ //#region src/domain/catalog/skill.d.ts
45
28
  /**
46
- * The standing-scope skill catalog adapter: the SINGLE narrow seam between
47
- * the TUI and the dsh skill services (the non-public-API isolation
48
- * 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.
49
32
  *
50
- * Everything the TUI knows about dsh-skill and agent-presets lives here:
51
- * - structural service types (never upstream classes) with OPTIONAL
52
- * capability members — `snapshot` and `standingKeyFor` are detected at
53
- * call time, so an upstream change degrades instead of breaking;
54
- * - `readHumanSkillCatalog()`: snapshot-first collector (list() fallback),
55
- * official `isUserInvocable` filter, field validation, stable sort,
56
- * deep freeze, distinct abort propagation;
57
- * - `resolveColdSkillTarget()`: capability-gated cold target selection
58
- * (standing scope → rosterless global → degraded global with a notice);
59
- * - `resolveLiveSkillTarget()`: the agent's own registry + agent scope.
60
- *
61
- * Failure contract: ordinary provider/composition failures surface as
62
- * degraded targets or rethrown errors for the coordinator to classify;
63
- * aborts ALWAYS propagate as aborts. No failure may hang or crash the TUI —
64
- * the worst outcome is "the corresponding commands are missing".
65
- * @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
66
39
  */
67
40
  /** The human-facing slice of one skill: display fields only. */
68
41
  interface HumanSkillSummary {
@@ -80,18 +53,7 @@ interface HumanSkillCatalog {
80
53
  readonly complete: boolean;
81
54
  }
82
55
  //#endregion
83
- //#region src/surface-catalog.d.ts
84
- /** Minimal live-agent face consumed by the effective catalog readers — and by
85
- * {@link ResolveInitialCatalogOptions}, so the published option type stays
86
- * structural instead of inlining the Host Agent. */
87
- interface SurfaceCatalogAgent {
88
- readonly ctx: object;
89
- readonly session: {
90
- readonly header: {
91
- readonly cwd?: string;
92
- };
93
- };
94
- }
56
+ //#region src/domain/catalog/surface.d.ts
95
57
  /** Local structural command face, compatible with both npm and Source Mode DSH.
96
58
  * The optional identity is part of the official 0.1.6 descriptor; an older
97
59
  * runtime's descriptor may omit it. */
@@ -139,27 +101,42 @@ interface SurfaceCatalogSnapshot {
139
101
  readonly skills: readonly HumanSkillSummary[];
140
102
  readonly issues: readonly SurfaceCatalogIssue[];
141
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
+ }
142
127
  /** The commands-service surface the collector reads. */
143
128
  interface SurfaceCommandsService {
144
129
  list(agent: SurfaceCatalogAgent): readonly SurfaceCommandDescriptor[];
145
130
  }
146
131
  /** The narrow context surface {@link readSurfaceCatalog} consumes. The
147
132
  * SKILL services are NOT reached here: every dsh skill/agent-presets
148
- * access goes through `src/skill-catalog.ts` (the single-point adapter,
149
- * plan appendix B.1). */
133
+ * access goes through `runtime/direct/skill-catalog.ts` (the single-point
134
+ * adapter, plan appendix B.1). */
150
135
  interface SurfaceCatalogContext {
151
136
  get(name: 'commands'): SurfaceCommandsService | undefined;
152
137
  }
153
- interface InitialCatalogResolution {
154
- /** The resume prefetch snapshot to install at mount. */
155
- readonly snapshot?: SurfaceCatalogSnapshot;
156
- /** The cold standing-scope human skill catalog (deferred start). */
157
- readonly skills?: HumanSkillCatalog;
158
- /** A user-facing notice when the prefetch/standing read degraded. */
159
- readonly notice?: string;
160
- }
161
138
  //#endregion
162
- //#region src/tui-config.d.ts
139
+ //#region src/app/config/schema.d.ts
163
140
  /** Plugin config: the session to resume, resolved from the startup service,
164
141
  * plus the DSH 0.1.7 profile-owned live TUI preferences. Every preference
165
142
  * field is a schemastery `.volatile()` reference (PR A): the `tui-app`
@@ -203,7 +180,7 @@ interface Config {
203
180
  /** Busy-Enter delivery for plain Enter while the agent runs: 'queue'
204
181
  * (default) or 'steer'. */
205
182
  readonly busyEnter: Volatile<string>;
206
- /** Local-shell sandbox for user-typed `!`/`!!` commands: 'bypass'
183
+ /** User-shell sandbox policy for user-typed `!`/`!!` commands: 'bypass'
207
184
  * (default) runs them outside the dsh sandbox, 'sandbox' routes them
208
185
  * through the dsh shell capability's policy. */
209
186
  readonly localShellSandbox: Volatile<string>;
@@ -214,16 +191,22 @@ interface Config {
214
191
  readonly displayPreset: Volatile<string>;
215
192
  /** Mid-turn progress-update cadence (off | milestones | frequent). */
216
193
  readonly progressUpdates: Volatile<string>;
194
+ /** Git attribution guidance mode (off | product | product-model). */
195
+ readonly gitAttribution: Volatile<string>;
217
196
  /** Visible-answer density guidance (default | concise | explanatory). */
218
197
  readonly responseStyle: Volatile<string>;
219
198
  /** Completion-notification mode ('unfocused' | 'always' | 'off'). */
220
199
  readonly notificationMode: Volatile<string>;
221
200
  /** Completion-notification method ('auto' | 'osc9' | 'osc777' | 'bell'). */
222
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>;
223
206
  /** Fullscreen mouse-wheel step ('1' | '2' | '3' | '5' | '8'). */
224
207
  readonly wheelScrollLines: Volatile<string>;
225
208
  /** The user keybinding overrides as a whole-value RAW field. The
226
- * keybindings parser (src/keybindings/config.ts) is the only
209
+ * keybindings parser (src/tui/keybindings/config.ts) is the only
227
210
  * validation/parsing authority — the keybinding business schema
228
211
  * deliberately stays out of the plugin Config. */
229
212
  readonly keybindings: Volatile<unknown>;
@@ -234,160 +217,26 @@ interface Config {
234
217
  }
235
218
  declare const Config: z<Config>;
236
219
  //#endregion
237
- //#region src/image/admission.d.ts
238
- /** Structural subset of `ImageAttachmentRef`. */
239
- interface ImageAttachmentRefLike {
240
- readonly attachmentId: string;
241
- readonly mediaType: ImageMediaType;
242
- readonly bytes: number;
243
- readonly width: number;
244
- readonly height: number;
245
- readonly name?: string;
246
- }
247
- //#endregion
248
- //#region src/image/types.d.ts
220
+ //#region src/domain/command/policy.d.ts
249
221
  /**
250
- * Image draft vocabulary for the TUI input pipeline (plan M1).
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.
251
226
  *
252
- * The draft layer is TUI-owned: it stages clipboard/file bytes until a
253
- * submission turns them into durable `ImageAttachmentRef`s through the dsh
254
- * attachment service. Nothing in this module persists; the durable boundary
255
- * is the harness's `ctx.attachments.saveImages()`.
256
- *
257
- * The media-type union mirrors `@deepseek-ai/dsh-attachment`'s
258
- * `ImageMediaType` structurally, so drafts flow into the harness without a
259
- * runtime dependency on the attachment package (AGENTS.md decision 7:
260
- * structural typing for dsh services).
261
- * @module @xmoon76/dsh-pi-tui/image/types
262
- */
263
- /** Raster media types accepted by the TUI image intake. */
264
- type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
265
- /** Per-TUI draft image identity; only meaningful inside one TUI run. */
266
- type DraftImageId = number;
267
- /** Where a staged image's bytes came from. */
268
- type DraftImageSource = {
269
- readonly type: 'clipboard';
270
- } | {
271
- readonly type: 'path';
272
- readonly path: string;
273
- }
274
- /** The image was pulled back from a queued message: its bytes are ALREADY
275
- * durable with the harness (the recalled ref) — the draft holds no local
276
- * copy and re-submitting reuses the ref instead of re-uploading. */
277
- | {
278
- readonly type: 'recalled';
279
- };
280
- /**
281
- * One image staged in the editor draft. The draft holds the raw bytes so
282
- * clipboard and path entries share one shape and a submit never depends on
283
- * the source file still existing (plan §5.1). A RECALLED draft (pulled
284
- * back from the queue) instead carries the durable `ImageAttachmentRef`
285
- * and NO local bytes — the bytes stay in harness storage and re-submitting
286
- * reuses the ref.
287
- */
288
- interface DraftImage {
289
- /** Per-TUI draft identity (the `#N` of the placeholder). */
290
- readonly id: DraftImageId;
291
- readonly kind: 'image';
292
- readonly source: DraftImageSource;
293
- /** Exact encoded bytes, held only for the draft lifetime. Empty for a
294
- * recalled draft (its bytes live in harness storage). */
295
- readonly bytes: Uint8Array;
296
- readonly mediaType: ImageMediaType;
297
- /** Intrinsic encoded width in pixels. */
298
- readonly width: number;
299
- /** Intrinsic encoded height in pixels. */
300
- readonly height: number;
301
- /** Exact encoded byte length (`bytes.byteLength` for local drafts; the
302
- * durable ref's recorded `bytes` for recalled drafts). */
303
- readonly byteLength: number;
304
- /** Optional display name (basename for path sources), never a path. */
305
- readonly name?: string;
306
- /** The canonical editor placeholder text for this draft. */
307
- readonly placeholder: string;
308
- /** The durable ref this draft reuses on submit; present ONLY for
309
- * recalled drafts (already-durable images are never re-uploaded). */
310
- readonly recalledRef?: ImageAttachmentRefLike;
311
- }
312
- /** The store surface the placeholder expansion needs (structural). */
313
- interface DraftImageStoreLike {
314
- values(): readonly DraftImage[];
315
- }
316
- //#endregion
317
- //#region src/task-presentation.d.ts
318
- declare function subagentJobTranscriptId(snapshot: unknown): string | undefined;
319
- /**
320
- * The Task Center row-selection disposition (plan §4.3/§4.4). A subagent
321
- * transcript opens a session/viewer surface that REPLACES the browser; a
322
- * Job row's detail keeps it mounted (the caller passes {@link openJobView}'s
323
- * disposition). An UNKNOWN row — a stale panel selection after a live
324
- * re-projection — also keeps the parent usable instead of dismissing it.
325
- */
326
- declare function taskRowSelectionDisposition(row: {
327
- readonly kind: 'job' | 'subagent';
328
- } | undefined, jobDetail: 'close' | 'keep-open'): 'close' | 'keep-open';
329
- /** Viewer body for a subagent job with no uniquely matched child. */
330
- declare function subagentJobViewHint(status: string, detail: string | undefined): string;
331
- //#endregion
332
- //#region src/tui-app.d.ts
333
- /** The compaction lifecycle phase the working row advertises: idle (no
334
- * compaction), summarizing (compaction/start seen, the summary is being
335
- * generated), or applying (the summary landed, the compacted surface is
336
- * being committed). Derived by the runner from compaction/start →
337
- * compaction/summary → compaction/end (foldCompactionEvent). */
338
- type CompactionPhase = 'idle' | 'summarizing' | 'applying';
339
- /**
340
- * The WEB composer submit gestures (DSH `ComposerSubmitGesture`): plain
341
- * Enter, or the Cmd/Ctrl-accelerated chord. The busy-Enter policy resolves
342
- * each to a delivery mode — plain Enter to the preferred mode, the
343
- * accelerated chord to its OPPOSITE — so the chord is never a fixed mode.
344
- */
345
- type ComposerSubmitGesture = 'enter' | 'accelerated';
346
- /** One semantic queued pending-input row for the queue pane. */
347
- interface QueueItem {
348
- /** The pending message id (agent inbox identity), or a local request id. */
349
- id: string;
350
- /** The message text, single-line display form. */
351
- text: string;
352
- /** next-turn followup vs next-step steer. */
353
- mode: 'followup' | 'steer';
354
- /** The correlation identity (authoritative rpc id or local request id). */
355
- rpcId?: string;
356
- /** A client-local echo not yet backed by an authoritative occurrence. */
357
- local?: boolean;
358
- }
359
- //#endregion
360
- //#region src/commands.d.ts
361
- /**
362
- * The effective mode ONE submission resolved at the submit boundary: whether
363
- * an agent-facing delivery steers into the running turn or queues behind it
364
- * (web `ComposerSubmissionPolicy` parity — an idle agent queues, plain Enter
365
- * takes the preference, and the accelerated chord takes its OPPOSITE).
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`.
366
233
  *
367
- * It is a property of the GESTURE, not of the persisted settings: a one-shot
368
- * gesture exists only inside the dispatch that resolved it, so every
369
- * downstream consumer (the TUI skill delivery) accepts this value instead of
370
- * re-deriving the mode from `busyEnter`.
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
371
239
  */
372
- type SubmitDelivery = 'steer' | 'queue';
373
- /**
374
- * The CURRENT host catalog's view of ONE parsed line (the DSH client
375
- * `CommandUiRuntime.matchEnter` decision table): whether the host CLAIMS the
376
- * line, and — for a claimed line — the claiming descriptor's attachment
377
- * declaration, so the dispatch can never consult two different catalog views
378
- * for the claim and the declaration.
379
- */
380
- type HostCommandClaim = {
381
- readonly claimed: true;
382
- /** The claiming descriptor's `input.attachments`
383
- * (DSH `CommandInputDescriptor.attachments`): only a declaring command
384
- * may be invoked with composer attachments. */
385
- readonly attachments: boolean;
386
- } | {
387
- readonly claimed: false;
388
- };
389
- //#endregion
390
- //#region src/command-policy.d.ts
391
240
  /**
392
241
  * Slash commands that need no session: before the first user message
393
242
  * (deferred start) they run locally without creating one. Everything else
@@ -400,38 +249,29 @@ type HostCommandClaim = {
400
249
  */
401
250
  declare const SESSIONLESS_COMMANDS: Set<string>;
402
251
  /**
403
- * The LOCAL-execute command set: TUI-owned UI/control commands AND core
404
- * control commands the TUI does not itself register (e.g. /kill) that
405
- * must ALWAYS run locally through the commands service, never steered,
406
- * regardless of the busyEnter preference. Everything NOT in this set —
407
- * plain prompts AND non-local commands (the per-skill slash commands like
408
- * /grilling or /matrix-cli) — flows through the busy-Enter submission
409
- * policy while the agent is running: web parity, where a skill invocation
410
- * is a plain `session.prompt` whose leading `/name` line the host's
411
- * pre-step listener (dsh-tool-skill) resolves into the injected skill
412
- * 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.
413
273
  */
414
274
  declare const LOCAL_COMMANDS: Set<string>;
415
- /**
416
- * Command semantics matrix (plan §19.3/M12): a LOCAL command line carrying a
417
- * staged image placeholder is REJECTED — local commands are pure UI
418
- * controls, never LLM prompts. AGENT-FACING input (plain prompts AND
419
- * per-skill slash invocations like `/grilling`) SUPPORTS images: the skill
420
- * wrapper builds its message through the same prepared-input path, so an
421
- * image-bearing skill line is a real multimodal prompt (review finding 4).
422
- * There is never a silent drop.
423
- * @param parsed - the parsed slash command, undefined for a plain prompt.
424
- * @param text - the submission text.
425
- * @param store - the live draft store.
426
- * @param isLocal - whether THIS LINE is a LOCAL (TUI-owned/UI) command; skill
427
- * names answer false. Never derived from the name alone: a host command's
428
- * input KIND decides which line it claims (see
429
- * {@link commandIsLocalForAttachments}).
430
- */
431
- declare function commandRejectsImages(parsed: {
432
- name: string;
433
- rawInput?: string;
434
- } | undefined, text: string, store: DraftImageStoreLike, isLocal: boolean): boolean;
435
275
  /**
436
276
  * The STATIC host-owned command catalog (P1-04): the ownership sets
437
277
  * (LOCAL_COMMANDS and SESSIONLESS_COMMANDS — the TUI's own local/UI command
@@ -454,15 +294,17 @@ declare const HOST_COMMAND_CATALOG: ReadonlySet<string>;
454
294
  * @param name - the slash name.
455
295
  * @param isSkillWrapper - the live skill-wrapper test (absent = none).
456
296
  * @param isDynamicLocal - the live client-contribution test (absent = none).
457
- * @param hostView - the host catalog's view of THIS LINE (`undefined` = the
458
- * catalog does not resolve the name at all; see {@link HostCommandClaim}).
459
- * A name the catalog RESOLVES is never a client-local line: when the
460
- * catalog claims the line the host's own `input.attachments` declaration
461
- * decides, and when it does not (an argued line of an execute-kind
462
- * command) the line is an ordinary submission — never a same-named client
463
- * 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.
464
306
  */
465
- 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;
466
308
  /**
467
309
  * Whether one parsed line is the BARE slash token (DSH `matchEnter`'s `bare`:
468
310
  * no input follows the name — trailing whitespace is not input). It is the
@@ -496,30 +338,16 @@ declare function isBareCommandLine(parsed: {
496
338
  * @param parsed - the parsed slash command (undefined = plain prompt).
497
339
  * @param isSkillWrapper - the live skill-wrapper test (absent = none).
498
340
  * @param isDynamicLocal - the live client-contribution test (absent = none).
499
- * @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).
500
345
  * @returns whether the line is a local command line.
501
346
  */
502
347
  declare function commandIsLocalForAttachments(parsed: {
503
348
  name: string;
504
349
  rawInput?: string;
505
- } | undefined, isSkillWrapper: ((name: string) => boolean) | undefined, isDynamicLocal: ((name: string) => boolean) | undefined, hostClaim?: ((parsed: {
506
- name: string;
507
- rawInput?: string;
508
- }) => HostCommandClaim | undefined) | undefined): boolean;
509
- /**
510
- * The TUI dispatch boundary's delivery resolution: the WEB composer policy
511
- * ({@link resolveComposerDelivery}) applied to agent-facing input, with the
512
- * TUI's own ownership terms on top. Pure so the dispatch gate (inside the
513
- * runner closure) is testable headless.
514
- * @param parsed - the parsed slash command, undefined for a plain prompt.
515
- * @param running - whether the live agent reports running.
516
- * @param gesture - the composer gesture that raised the submission.
517
- * @param busyEnter - the persisted preference value (''/undefined = queue).
518
- */
519
- declare function resolveSubmitDelivery(parsed: {
520
- name: string;
521
- rawInput?: string;
522
- } | undefined, running: boolean, gesture: ComposerSubmitGesture, busyEnter: string | undefined): SubmitDelivery;
350
+ } | undefined, isSkillWrapper: ((name: string) => boolean) | undefined, isDynamicLocal: ((name: string) => boolean) | undefined, hostResolvesName: boolean): boolean;
523
351
  /**
524
352
  * Normalize an explicit `/skill <name> <args>` invocation to the skill's
525
353
  * own slash line `/<name> <args>` (review finding 2). The harness's
@@ -560,6 +388,14 @@ declare function shouldConsumeAdvertisedMiss(execution: {
560
388
  * @param text - the submitted draft.
561
389
  */
562
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
+ */
563
399
  /**
564
400
  * Whether a shell command matches a destructive pattern. `rm` is treated
565
401
  * specially: any spelling of recursive + force flags (`rm -rf`, `rm -r -f`,
@@ -567,37 +403,333 @@ declare function isPlainExitPrompt(text: string): boolean;
567
403
  */
568
404
  declare function dangerCommand(command: string): boolean;
569
405
  //#endregion
570
- //#region src/interrupt.d.ts
406
+ //#region src/domain/media/types.d.ts
571
407
  /**
572
- * The live-turn interrupt helper (web Stop parity): abort the current
573
- * turn/tool run while PRESERVING the pending queue. Kept out of the
574
- * composition root so the rule is testable headless, and its structural
575
- * faces keep the internal runtime port module out of the published entry.
576
- * @module @xmoon76/dsh-pi-tui/interrupt
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
577
423
  */
578
- /** Public settlement shape for the interrupt helper. Kept local so the
579
- * entry-point declaration does not expose the internal runtime port module. */
580
- type InterruptWriteOutcome = {
581
- readonly kind: 'committed';
582
- readonly value: undefined;
583
- } | {
584
- readonly kind: 'rejected';
585
- readonly error: {
586
- readonly code: string;
587
- readonly message: string;
588
- readonly details?: Readonly<Record<string, unknown>>;
589
- };
590
- } | {
591
- readonly kind: 'cancelled';
592
- } | {
593
- readonly kind: 'indeterminate';
594
- readonly error: {
595
- readonly code: string;
596
- readonly message: string;
597
- readonly details?: Readonly<Record<string, unknown>>;
598
- };
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';
599
442
  } | {
600
- readonly kind: 'unsupported';
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';
601
733
  readonly reason: string;
602
734
  };
603
735
  /** The live-agent surface {@link interruptAgent} needs (structural — the
@@ -640,7 +772,7 @@ interface InterruptWriterLike {
640
772
  */
641
773
  declare function interruptAgent(agent: InterruptAgentLike | undefined, writer: InterruptWriterLike): Promise<InterruptWriteOutcome>;
642
774
  //#endregion
643
- //#region src/subagent-viewer.d.ts
775
+ //#region src/app/surface/viewer-policy.d.ts
644
776
  /** One unsettled subagent delegation, in tool/call order. */
645
777
  interface PendingSubagentCall {
646
778
  readonly callId: string;
@@ -713,153 +845,66 @@ declare function viewerActionCapability(action: TuiAction, viewer: {
713
845
  */
714
846
  declare function matchPendingSubagentCall(pending: PendingSubagentCall[], label: string | undefined): PendingSubagentCall | undefined;
715
847
  //#endregion
716
- //#region src/pending-presentation.d.ts
717
- /** One semantic pending-input item as the queue mirror sees it. */
718
- interface QueueInboxMessage {
719
- readonly id: string;
720
- readonly content: readonly ContentBlock[];
721
- }
722
- /** The queue-pane rows for one semantic pending-input batch. */
723
- interface QueueFoldResult {
724
- readonly rows: QueueItem[];
725
- }
726
- /** Build queue-pane rows from semantic pending-input occurrences, preserving
727
- * their order and content without inspecting backend-specific metadata. */
728
- declare function foldQueueRows(messages: readonly QueueInboxMessage[], mode: 'followup' | 'steer'): QueueFoldResult;
729
- //#endregion
730
- //#region src/compaction-presentation.d.ts
731
- /** One fold of a compaction lifecycle event over the in-flight compaction
732
- * state. Pure (the routing applies the returned surface effects):
733
- * dsh-compaction is not a peer, so the event is read structurally. */
734
- interface CompactionFold {
735
- /** The updated in-flight compaction id (the newest start wins). */
736
- id: string | undefined;
737
- /** compaction/start: the compacting flag turns ON (footer/working row). */
738
- active: boolean;
739
- /** A MATCHED compaction/end: the compacting flag turns OFF and busy is
740
- * re-derived from the turn log (a stale end never clears a newer
741
- * compaction's state). */
742
- clear: boolean;
743
- /** The compaction phase the event implies: compaction/start →
744
- * 'summarizing', a MATCHED compaction/summary → 'applying'. Undefined
745
- * when the event does not advance the phase (a stale summary, any
746
- * compaction/end — the settle clears via {@link clear}). */
747
- phase?: Exclude<CompactionPhase, 'idle'>;
748
- /** The settle notification for compaction/end, when one fires. */
749
- notify: {
750
- text: string;
751
- kind: 'info' | 'error';
752
- } | undefined;
753
- }
754
- /** Fold one compaction lifecycle event over the in-flight state. */
755
- declare function foldCompactionEvent(state: {
756
- id: string | undefined;
757
- }, event: {
758
- type: string;
759
- data: {
760
- compactionId?: unknown;
761
- error?: unknown;
762
- };
763
- }): CompactionFold;
764
- /** The minimal compaction-settle surface the routing passes in — STRUCTURAL
765
- * on purpose: referencing the full {@link TuiApp} class from a public
766
- * export would inline the whole surface (and its internal registry/
767
- * presentation dependencies) into the published declaration bundle. The
768
- * settle contract only needs the three phase/busy/working setters. */
769
- interface CompactionSettleSurface {
770
- setCompactionPhase(phase: 'idle'): void;
771
- setBusy(busy: boolean): void;
772
- setWorking(busy: boolean): void;
773
- }
774
- /** The UI side effects of a MATCHED compaction settle: clear the phase,
775
- * hand the working row back to the turn state, and re-measure the session
776
- * surface so the footer context reflects the compacted log IMMEDIATELY —
777
- * the next step/start or turn/end would otherwise delay the refresh.
778
- * Exported as a seam so the settle contract is testable without a full
779
- * runner driver (the firehose closure is not). */
780
- declare function settleCompactionSurface(app: CompactionSettleSurface, refreshStatus: () => void, busyNow: boolean): void;
781
- /** The busy flag after a turn-boundary event: a turn end must NOT clear
782
- * the busy state while a compaction is still in flight — an interrupted
783
- * turn can close (turn/end) before its compaction settles, and the
784
- * single-Esc cancel must stay armed until compaction/end. */
785
- declare function busyAfterTurnBoundary(eventType: 'turn/start' | 'turn/end', compacting: boolean): boolean;
786
- /** PR D2 test seam: whether a session event type marks the model-visible
787
- * context dirty (re-measure through the SessionReader port) or only
788
- * repaints cheaply (cached measurement). The firehose routes every event
789
- * through this classification — the single source of truth for the
790
- * status/measurement split. `compaction/end` is classified 'measure' but
791
- * the firehose deliberately SKIPS it here: a matched compaction settle
792
- * re-measures through the fold-outcome path (settleCompactionSurface), so
793
- * a STALE compaction/end can never trigger a measurement. */
794
- declare function contextRefreshKind(eventType: string): 'measure' | 'cheap';
795
- /**
796
- * The in-flight compaction state a resumed session log implies: the newest
797
- * compaction bracket decides. A `session/end-seed` boundary makes any
798
- * EARLIER unmatched `compaction/start` STALE — the upstream invariant
799
- * (inheritedOrphanStartSeqs) treats seed compactions that never settled
800
- * inside the seed as abandoned, so they must not re-arm the compacting
801
- * surface on resume.
802
- */
803
- declare function compactingFromLog(events: readonly {
804
- type: unknown;
805
- data?: unknown;
806
- }[]): {
807
- active: boolean;
808
- id: string | undefined;
809
- };
810
- //#endregion
811
- //#region src/dsh-profile.d.ts
848
+ //#region src/client/launcher/profile.d.ts
812
849
  /**
813
- * The DSH launcher PROFILE identity and the resume hint (plan §27).
850
+ * The Client-local DSH launcher PROFILE reads: the `--profile` argv scrape and
851
+ * the interactive-quit resume hint.
814
852
  *
815
853
  * Deliberately NOT part of `src/startup.ts`: the startup row is a
816
854
  * zero-dependency island that must not share a bundle chunk with the runner,
817
- * so these runner-side helpers live in their own module and the entry
818
- * re-exports them unchanged.
819
- * @module @xmoon76/dsh-pi-tui/dsh-profile
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
820
859
  */
821
860
  /**
822
861
  * The dsh profile named by the `--profile` flag in `argv`, in both spellings.
823
862
  * This is the ARGV-ONLY fallback: it cannot see the positional `dsh <name>`
824
863
  * launch form, because the launcher consumes that bare name by synthesizing
825
864
  * `['--profile', ...argv]` for its OWN commander parse and leaves
826
- * `process.argv` untouched. Prefer {@link hostRunningProfile}, which reads the
827
- * 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.
828
867
  * @param argv - the process argument vector.
829
868
  * @param fallback - the default profile.
830
869
  */
831
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
+ */
832
896
  /** The one context read {@link hostRunningProfile} needs (a cordis Context satisfies it). */
833
897
  interface ProfileContextReadLike {
834
898
  get(name: string): unknown;
835
899
  }
836
900
  /**
837
- * The profile this process actually runs — the official Host
838
- * `profileContext.name` when it is composed, else {@link runningProfile}'s
839
- * argv scrape.
840
- *
841
- * `profileContext.name` is the launcher's own profile identity, so it is
842
- * correct for EVERY launch form: `--profile <name>`, `--profile=<name>`, the
843
- * positional `dsh <name>`, and `--from-default-profile <name>`. The argv
844
- * scrape only knows the flag form, which is why it is the fallback for a
845
- * profile-less mount (tests, an embedded surface) rather than the primary
846
- * source.
901
+ * The profile this process actually runs.
847
902
  * @param ctx - the plugin context (or any structural `get`-only stand-in).
848
903
  * @param argv - the process argument vector for the fallback path.
849
904
  * @param fallback - the default profile for the fallback path.
850
905
  * @returns the running profile name.
851
906
  */
852
907
  declare function hostRunningProfile(ctx: ProfileContextReadLike, argv?: readonly string[], fallback?: string): string;
853
- /**
854
- * The interactive-quit resume hint (pi parity): `dsh --profile <p>
855
- * --session <id>`, printed after the terminal restores so the user can
856
- * re-enter the session later. Returns undefined when there is no session
857
- * to resume (deferred start never created one).
858
- * @param profile - the running profile ({@link hostRunningProfile}).
859
- * @param sessionId - the live session id.
860
- * @returns the resume command line, or undefined without a session.
861
- */
862
- declare function resumeCommand(profile: string, sessionId: string): string | undefined;
863
908
  //#endregion
864
909
  //#region src/index.d.ts
865
910
  /** Stable Cordis plugin name. */
@@ -872,7 +917,7 @@ declare const inject: string[];
872
917
  * The public declaration stays here (A5a review P1): `liveAgent` keeps the Host
873
918
  * `Agent` type it has always had, so consumers that READ the property keep
874
919
  * compiling. The implementation consumes the structural
875
- * `SurfaceCatalogResolutionOptions` in `surface-catalog.ts`.
920
+ * `SurfaceCatalogResolutionOptions` in `app/direct/initial-catalog.ts`.
876
921
  */
877
922
  interface ResolveInitialCatalogOptions {
878
923
  /** The resumed live agent, if any (prefetch path). */
@@ -896,12 +941,21 @@ interface ResolveInitialCatalogOptions {
896
941
  * prefetches the resumed agent's effective catalog; the deferred start reads the
897
942
  * cold HUMAN SKILL catalog through the preset's STANDING SCOPE — no Agent, no
898
943
  * session, no turn — so the first input sees human-invocable skills without any
899
- * durable side effect. The implementation and its failure taxonomy live in
900
- * `surface-catalog.ts`.
944
+ * durable side effect. The implementation and its pre-mount failure taxonomy
945
+ * live in `app/direct/initial-catalog.ts`.
901
946
  * @param options - injected dependencies (see {@link ResolveInitialCatalogOptions}).
902
947
  * @returns the snapshot / skill catalog to install and an optional notice.
903
948
  */
904
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';
958
+ };
905
959
  /** One agent's preset composition: the id to record and the setup that installs it. */
906
960
  interface AgentComposition {
907
961
  /** Preset id for the session header, absent when the deployment composes no roster. */