@timurproko/a1 0.2.7-dev.1269 → 0.2.7-dev.1275

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +6 -0
  2. package/dist/app/session-shell/apps-dialog.d.ts +33 -0
  3. package/dist/app/session-shell/apps-dialog.js +61 -0
  4. package/dist/app/session-shell/dialog-coordinator.d.ts +8 -0
  5. package/dist/app/session-shell/dialog-coordinator.js +72 -3
  6. package/dist/app/session-shell/session-selectors.d.ts +1 -1
  7. package/dist/app/session-shell/session-selectors.js +1 -1
  8. package/dist/app/session-shell/session-shell.js +9 -3
  9. package/dist/app/session-shell/shell-host.d.ts +1 -0
  10. package/dist/app/session-shell/shell-host.js +22 -11
  11. package/dist/app/session-shell/workflow-output.js +12 -0
  12. package/dist/app/session-shell/workflow-router.d.ts +1 -1
  13. package/dist/composition/owned-ui.js +17 -13
  14. package/dist/contracts/owned-ui/session-presenters.d.ts +1 -1
  15. package/dist/contracts/owned-ui/session-workflows.d.ts +3 -1
  16. package/dist/contracts/owned-ui/shell-components.d.ts +2 -0
  17. package/dist/contracts/presentation/progress-palette.generated.d.ts +1 -1
  18. package/dist/contracts/presentation/progress-palette.generated.js +1 -1
  19. package/dist/contracts/presentation/terminal.d.ts +4 -0
  20. package/dist/contracts/presentation/theme.d.ts +2 -0
  21. package/dist/foundation/lifecycle/user-exports.d.ts +5 -0
  22. package/dist/foundation/lifecycle/user-exports.js +37 -0
  23. package/dist/foundation/startup/startup-descriptor.js +2 -2
  24. package/dist/integrations/pi/components/models-dialog-loader.d.ts +11 -0
  25. package/dist/integrations/pi/components/models-dialog-loader.js +45 -0
  26. package/dist/integrations/pi/components/models-dialog.d.ts +2 -0
  27. package/dist/integrations/pi/components/models-dialog.js +13 -7
  28. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
  29. package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
  30. package/dist/integrations/pi/components/shell-editor-autocomplete.js +21 -15
  31. package/dist/integrations/pi/components/shell-footer-status.js +3 -2
  32. package/dist/integrations/pi/components/shell-presenters-info.js +1 -1
  33. package/dist/integrations/pi/components/shell-presenters-transcript.js +3 -1
  34. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +2 -1
  35. package/dist/integrations/pi/components/shell-selectors-dialogs.js +2 -8
  36. package/dist/integrations/pi/components/theme.d.ts +2 -0
  37. package/dist/integrations/pi/components/theme.js +5 -0
  38. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +8 -7
  39. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +13 -7
  40. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  41. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  42. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  43. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  44. package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
  45. package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
  46. package/dist/integrations/pi/components/upstream/components/extension-selector.js +9 -2
  47. package/dist/integrations/pi/components/upstream/components/mermaid.d.ts +2 -1
  48. package/dist/integrations/pi/components/upstream/components/mermaid.js +26 -21
  49. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
  50. package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
  51. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  52. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  53. package/dist/integrations/pi/components/upstream/components/session-footer.js +9 -14
  54. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  55. package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
  56. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +6 -2
  57. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +27 -3
  58. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
  59. package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
  60. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  61. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  62. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  63. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
  64. package/dist/integrations/pi/components/upstream/components/tool-execution.js +1 -1
  65. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
  66. package/dist/integrations/pi/components/upstream/components/tree-selector.js +7 -6
  67. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  68. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  69. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +1 -1
  70. package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -2
  71. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  72. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  73. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  74. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  75. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  76. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  77. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  78. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  79. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
  80. package/dist/integrations/pi/components/upstream/theme/theme.js +1 -1
  81. package/dist/integrations/pi/engine/adapter.d.ts +1 -1
  82. package/dist/integrations/pi/engine/adapter.js +3 -3
  83. package/dist/integrations/pi/engine/host.d.ts +1 -1
  84. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  85. package/dist/integrations/pi/engine/workflow-export.d.ts +4 -0
  86. package/dist/integrations/pi/engine/workflow-export.js +25 -0
  87. package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -0
  88. package/dist/integrations/pi/engine/workflow-runner.js +6 -10
  89. package/dist/integrations/pi/engine/workflow-support.d.ts +2 -1
  90. package/dist/integrations/pi/engine/workflow-support.js +28 -25
  91. package/dist/integrations/pi/engine/workflows.d.ts +6 -1
  92. package/dist/integrations/pi/engine/workflows.js +1 -1
  93. package/dist/integrations/pi/session-presenters/presentation.js +2 -1
  94. package/dist/integrations/pi/session-presenters/presenters.js +22 -10
  95. package/dist/integrations/pi/startup-public.js +289 -240
  96. package/dist/integrations/pi/startup-public.manifest.json +579 -363
  97. package/dist/integrations/pi/tui-runtime/adapter.js +3 -1
  98. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  99. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  100. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +5 -2
  101. package/dist/integrations/pi/tui-runtime/overlay-geometry.js +12 -2
  102. package/dist/runtime-payload-inventory.json +8044 -6930
  103. package/dist/ui/components/dialog-panel.d.ts +4 -3
  104. package/dist/ui/components/dialog-panel.js +9 -13
  105. package/dist/ui/components/index.d.ts +1 -1
  106. package/dist/ui/components/index.js +1 -1
  107. package/dist/ui/components/scrollbar.d.ts +2 -2
  108. package/dist/ui/components/scrollbar.js +2 -2
  109. package/dist/ui/components/spans.d.ts +7 -0
  110. package/dist/ui/components/spans.js +63 -1
  111. package/dist/ui/components/surface.js +1 -1
  112. package/docs/architecture/prompt-suggestions.md +1 -1
  113. package/docs/architecture/ui-reference-provenance.md +2 -1
  114. package/docs/features/prompt-history.md +1 -1
  115. package/docs/features/session-exports.md +15 -0
  116. package/docs/local-worktree-cleanup.md +24 -0
  117. package/package.json +6 -6
@@ -1,6 +1,7 @@
1
1
  import type { PaneRect } from "./frame.js";
2
2
  import { LineInput } from "./line-input.js";
3
3
  import { type ShortcutHintEntry } from "./shortcut-hints.js";
4
+ export { renderFloatingDialogSurface, renderModalBackdrop } from "./spans.js";
4
5
  import type { UiTheme } from "./theme.js";
5
6
  /**
6
7
  * Semantic content and overlay composition for Settings configure dialogs. The setting supplies
@@ -32,18 +33,18 @@ export interface DialogPanelFrame {
32
33
  readonly valueColumn: number;
33
34
  }
34
35
  export interface SteppedDialogPanelState {
36
+ /** Tight terminal geometry retains controls and guidance before optional spacing. */
37
+ readonly compact?: boolean;
35
38
  readonly title: string;
36
39
  /** Omitted for a one-step searchable selector whose title needs no step marker. */
37
40
  readonly step?: number;
38
41
  readonly steps?: number;
39
- readonly description: string;
42
+ readonly description?: string;
40
43
  readonly input?: LineInput;
41
44
  readonly rows: readonly DialogRow[];
42
45
  readonly index: number;
43
46
  readonly hint: readonly ShortcutHintEntry[];
44
47
  }
45
- /** Canonical native-matching background for every owned floating dialog surface. */
46
- export declare function renderFloatingDialogSurface(text: string, mode: "truecolor" | "256color"): string;
47
48
  /** Existing raised-menu background, centralized beside the darker dialog surface. */
48
49
  export declare function renderFloatingMenuSurface(text: string, mode: "truecolor" | "256color"): string;
49
50
  /** The shared compact width used by Settings configure dialogs and native tab dialogs. */
@@ -1,17 +1,12 @@
1
1
  import { LineInput, renderInputRow } from "./line-input.js";
2
2
  import { renderShortcutHintsWithClose } from "./shortcut-hints.js";
3
- import { overlaySpan } from "./spans.js";
3
+ import { overlaySpan, renderDialogBackdropLine as dimStyledLine } from "./spans.js";
4
+ export { renderFloatingDialogSurface, renderModalBackdrop } from "./spans.js";
4
5
  import { displayWidth, padToWidth, truncateToWidth } from "./text.js";
5
6
  const LABEL_COLUMN_CAP = 30;
6
7
  const STEPPED_LABEL_COLUMN_CAP = 46;
7
8
  const CONFIGURE_MODAL_MAX_WIDTH = 60;
8
9
  const CONFIGURE_MODAL_PADDING_X = 2;
9
- const SGR = /\u001b\[[0-9;]*m/gu;
10
- /** Canonical native-matching background for every owned floating dialog surface. */
11
- export function renderFloatingDialogSurface(text, mode) {
12
- const background = mode === "truecolor" ? "\u001b[48;2;38;38;38m" : "\u001b[48;5;235m";
13
- return `${background}${text}\u001b[49m`;
14
- }
15
10
  /** Existing raised-menu background, centralized beside the darker dialog surface. */
16
11
  export function renderFloatingMenuSurface(text, mode) {
17
12
  const background = mode === "truecolor" ? "\u001b[48;2;55;55;55m" : "\u001b[48;5;237m";
@@ -23,7 +18,7 @@ export function configureDialogWidth(width) {
23
18
  }
24
19
  /** Rows available to a selector after retaining its title, description, control, and guidance. */
25
20
  export function configureDialogChoiceCapacity(height, hasInput, descriptionRows = 1) {
26
- const wrappedDescriptionRows = Math.max(1, descriptionRows);
21
+ const wrappedDescriptionRows = Math.max(0, descriptionRows);
27
22
  return Math.max(1, Math.max(0, height) - (hasInput ? 9 : 7) - (wrappedDescriptionRows - 1));
28
23
  }
29
24
  /** Number of modal rows a plain description occupies at the shared content width. */
@@ -128,12 +123,17 @@ export function renderSteppedDialogPanel(state, width, theme) {
128
123
  }).lines.map(row => layout.contentRow(row)),
129
124
  layout.blank,
130
125
  ];
131
- const descriptionRows = wrapText(state.description, layout.contentWidth);
126
+ const descriptionRows = state.description === undefined ? [] : wrapText(state.description, layout.contentWidth);
132
127
  const step = state.step === undefined || state.steps === undefined ? "" : `(step ${state.step}/${state.steps})`;
133
128
  const title = step.length === 0 ? state.title : `${state.title} ${step}`;
134
129
  const paintedTitle = step.length === 0
135
130
  ? theme.bold(theme.fg("accent", state.title))
136
131
  : `${theme.bold(theme.fg("accent", state.title))} ${theme.fg("dim", step)}`;
132
+ if (state.compact)
133
+ return [
134
+ layout.contentRow(paintedTitle, title), ...input.slice(0, -1), ...rows,
135
+ layout.contentRow(renderShortcutHintsWithClose(state.hint, theme, layout.contentWidth)),
136
+ ];
137
137
  return [
138
138
  layout.blank,
139
139
  layout.contentRow(paintedTitle, title),
@@ -158,10 +158,6 @@ function pad(line, width, raw) {
158
158
  const visible = displayWidth(raw);
159
159
  return visible >= width ? line : line + " ".repeat(width - visible);
160
160
  }
161
- function dimStyledLine(line, width) {
162
- const padded = padToWidth(line, width);
163
- return `\u001b[2m${padded.replace(SGR, sequence => `${sequence}\u001b[2m`)}\u001b[22m`;
164
- }
165
161
  function wrapText(text, width) {
166
162
  if (text.length === 0 || width <= 0)
167
163
  return [""];
@@ -1,4 +1,4 @@
1
- export { configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, dialogRowAt, dialogValueColumn, renderCenteredDialogOverlay, renderDialogPanel, renderSteppedDialogPanel, } from "./dialog-panel.js";
1
+ export { configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, dialogRowAt, dialogValueColumn, renderCenteredDialogOverlay, renderModalBackdrop, renderFloatingDialogSurface, renderDialogPanel, renderSteppedDialogPanel, } from "./dialog-panel.js";
2
2
  export type { DialogPanelFrame, DialogPanelState, DialogRow, SteppedDialogPanelState } from "./dialog-panel.js";
3
3
  export { FrameContractError, assertPaneRect, finalizeFrame, validateFrame } from "./frame.js";
4
4
  export type { PaneRect } from "./frame.js";
@@ -1,4 +1,4 @@
1
- export { configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, dialogRowAt, dialogValueColumn, renderCenteredDialogOverlay, renderDialogPanel, renderSteppedDialogPanel, } from "./dialog-panel.js";
1
+ export { configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, dialogRowAt, dialogValueColumn, renderCenteredDialogOverlay, renderModalBackdrop, renderFloatingDialogSurface, renderDialogPanel, renderSteppedDialogPanel, } from "./dialog-panel.js";
2
2
  export { FrameContractError, assertPaneRect, finalizeFrame, validateFrame } from "./frame.js";
3
3
  export { humanizeLabel, humanizeTitle } from "./label.js";
4
4
  export { LineInput, caretCell, handleLineInputKey, renderInputRow, wordLeft, wordRight } from "./line-input.js";
@@ -38,8 +38,8 @@ export interface ScrollbarPresentationInput {
38
38
  export interface ScrollbarPresentation {
39
39
  readonly visible: boolean;
40
40
  readonly reservesSpace: boolean;
41
- readonly trackGlyph: "│" | "┃";
42
- readonly thumbGlyph: "│" | "┃";
41
+ readonly trackGlyph: "┃" | "▐";
42
+ readonly thumbGlyph: "┃" | "▐";
43
43
  }
44
44
  /** Pure appearance decision; activity time is supplied so tests own the clock. */
45
45
  export declare function scrollbarPresentation(input: ScrollbarPresentationInput): ScrollbarPresentation;
@@ -26,8 +26,8 @@ export function scrollbarPresentation(input) {
26
26
  return {
27
27
  visible,
28
28
  reservesSpace,
29
- trackGlyph: thick ? "┃" : "│",
30
- thumbGlyph: thick || input.hovered || input.dragging ? "┃" : "│",
29
+ trackGlyph: thick ? "▐" : "┃",
30
+ thumbGlyph: thick ? "▐" : "┃",
31
31
  };
32
32
  }
33
33
  /** Null when the content fits: no scrollbar is drawn and no width is reserved. */
@@ -1,3 +1,4 @@
1
+ import type { PresentationPointerSurface } from "../../contracts/presentation/index.js";
1
2
  /**
2
3
  * Replaces the visible columns `[from, to)` of `line` with `span`, preserving
3
4
  * the styling before it and re-asserting whatever was still open afterwards, so
@@ -8,6 +9,12 @@
8
9
  export declare function overlaySpan(line: string, from: number, to: number, span: string, options?: {
9
10
  readonly inheritStartStyle?: boolean;
10
11
  }): string;
12
+ /** Raised background shared by every owned floating dialog surface. */
13
+ export declare function renderFloatingDialogSurface(text: string, mode: "truecolor" | "256color"): string;
14
+ /** Indexed backdrop colors remain deterministic in both supported terminal color modes. */
15
+ export declare function renderDialogBackdropLine(line: string, width: number): string;
16
+ /** Darkens a completed overlay stack everywhere outside its topmost floating panel. */
17
+ export declare function renderModalBackdrop(frame: readonly string[], width: number, height: number, panel: PresentationPointerSurface): string[];
11
18
  /** Wraps visible columns `[from,to)` in an explicit OSC 8 hyperlink. */
12
19
  export declare function hyperlinkSgrSpan(line: string, from: number, to: number, target: string, widthOf?: (text: string) => number): string;
13
20
  /** Returns the OSC 8 target painted at a zero-based visible terminal column. */
@@ -1,4 +1,4 @@
1
- import { displayWidth, graphemeWidth } from "./text.js";
1
+ import { displayWidth, graphemeWidth, padToWidth } from "./text.js";
2
2
  /**
3
3
  * Ported from the A1 UI reference implementation (`core/presentation/spans.ts`).
4
4
  * Writing over part of a rendered row is not string slicing: the row carries
@@ -69,6 +69,68 @@ export function overlaySpan(line, from, to, span, options = {}) {
69
69
  head += " ".repeat(from - column);
70
70
  return head + (hyperlinkOpen ? HYPERLINK_OFF : "") + span + replay + tail;
71
71
  }
72
+ /** Extracts exact styled cells, replacing partial boundary graphemes with spaces. */
73
+ function sliceStyledColumns(line, from, to) {
74
+ let column = 0;
75
+ let styles = "";
76
+ let output = "";
77
+ let started = false;
78
+ for (const token of line.split(ANSI_SPLIT)) {
79
+ if (token.startsWith("\u001b")) {
80
+ if (column < from)
81
+ styles += token;
82
+ else if (column < to)
83
+ output += token;
84
+ continue;
85
+ }
86
+ for (const { segment } of GRAPHEMES.segment(token)) {
87
+ const end = column + graphemeWidth(segment);
88
+ if (end > from && column < to) {
89
+ if (!started) {
90
+ output = styles + output;
91
+ started = true;
92
+ }
93
+ output += column >= from && end <= to ? segment : " ".repeat(Math.min(to, end) - Math.max(from, column));
94
+ }
95
+ column = end;
96
+ }
97
+ }
98
+ return `${output}\u001b[0m${HYPERLINK_OFF}`;
99
+ }
100
+ /** Raised background shared by every owned floating dialog surface. */
101
+ export function renderFloatingDialogSurface(text, mode) {
102
+ const background = mode === "truecolor" ? "\u001b[48;2;65;65;65m" : "\u001b[48;5;238m";
103
+ const painted = text.replace(/\u001b\[[0-9;]*m/gu, sequence => {
104
+ const codes = sequence.slice(2, -1).split(";").map(Number);
105
+ let restore = false;
106
+ for (let index = 0; index < codes.length; index++) {
107
+ const code = codes[index];
108
+ if (code === 0 || code === 49)
109
+ restore = true;
110
+ if (code === 48)
111
+ restore = false;
112
+ if (code === 38 || code === 48)
113
+ index += codes[index + 1] === 2 ? 4 : codes[index + 1] === 5 ? 2 : 0;
114
+ }
115
+ return restore ? sequence + background : sequence;
116
+ });
117
+ return `${background}${painted}\u001b[49m`;
118
+ }
119
+ /** Indexed backdrop colors remain deterministic in both supported terminal color modes. */
120
+ export function renderDialogBackdropLine(line, width) {
121
+ return `\u001b[0;38;5;240;48;5;233m${padToWidth(line, width).replace(/\u001b\[[0-9;]*m/gu, "")}\u001b[0m`;
122
+ }
123
+ /** Darkens a completed overlay stack everywhere outside its topmost floating panel. */
124
+ export function renderModalBackdrop(frame, width, height, panel) {
125
+ return Array.from({ length: height }, (_, row) => {
126
+ const source = padToWidth(frame[row] ?? "", width);
127
+ if (row + 1 < panel.rowStart || row + 1 > panel.rowEnd)
128
+ return renderDialogBackdropLine(source, width);
129
+ const left = Math.max(0, panel.columnStart - 1);
130
+ const right = Math.min(width, panel.columnEnd);
131
+ return overlaySpan(renderDialogBackdropLine(source, width), left, right, sliceStyledColumns(source, left, right));
132
+ });
133
+ }
72
134
  /** Wraps visible columns `[from,to)` in an explicit OSC 8 hyperlink. */
73
135
  export function hyperlinkSgrSpan(line, from, to, target, widthOf = displayWidth) {
74
136
  const on = `\u001b]8;;${target}\u001b\\`;
@@ -10,7 +10,7 @@ export const RAIL_COLUMNS = 2;
10
10
  export function withScrollbarRail(lines, geometry, contentWidth, theme, options = {}) {
11
11
  const inset = options.topInset ?? 0;
12
12
  const presentation = options.presentation
13
- ?? { visible: geometry !== null, reservesSpace: true, trackGlyph: "│", thumbGlyph: "│" };
13
+ ?? { visible: geometry !== null, reservesSpace: true, trackGlyph: "┃", thumbGlyph: "┃" };
14
14
  if (!presentation.reservesSpace)
15
15
  return lines;
16
16
  const drawn = presentation.visible && geometry !== null;
@@ -51,7 +51,7 @@ Eligibility/presentation reasons include `disabled`, `disposed`, `early-conversa
51
51
 
52
52
  Writes are asynchronous and coalesced: at most one write and one pending latest snapshot. Sink failures do not affect generation, input, or rendering and do not appear in the UI. An unwritable destination may therefore produce no file; check the destination locally rather than diagnosing the model from that absence. Disposal clears in-memory capture and pending writes; a write already in progress can finish. Exported local files remain until you remove them.
53
53
 
54
- To disable capture, omit/unset `A1_SUGGESTION_DIAGNOSTICS` before the next launch and remove the dedicated snapshot if no longer needed. This does not disable suggestions; `/settings` -> Agent -> Prompt suggestions controls generation independently.
54
+ To disable capture, omit/unset `A1_SUGGESTION_DIAGNOSTICS` before the next launch and remove the dedicated snapshot if no longer needed. This does not disable suggestions; Launcher (`Ctrl+P`) -> Settings -> Agent -> Prompt suggestions controls generation independently.
55
55
 
56
56
  ## Verification and evidence limits
57
57
 
@@ -72,7 +72,8 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
72
72
  transcripts, widgets, pointer selection, and extension-editor restoration. Physical
73
73
  acceptance of the exact build remains required before merging the implementation.
74
74
  - **Owned in-session routes are overlays.** The A1 UI reference owns its surface and can switch screens; A1 renders in-session owned routes through the pinned Pi TUI as full-viewport overlays. The pre-resource trust selector is separate and uses a bounded alternate startup surface solely so every completion path can restore the untouched parent terminal before engine activation or a fail-closed diagnostic.
75
- - **A1 release notes replace Pi startup changelog behavior only in bare A1.** Bare A1's `/changelog` screen reads the bounded packaged resource generated from reviewed `docs/releases/*.md` documents, and an exact stable current note may open in the same full-screen route after startup safety surfaces. Product-owned per-profile claim/acknowledgement state is written only after a rendered close. The `a1 pi` comparison profile retains Pi's pinned changelog data, settings, startup announcement, and in-feed command presentation. Evidence is in `test/features/owned-ui/release-notes.test.ts`, `release-note-state.test.ts`, `test/composition/settings-route-host.test.ts`, and `test/app/session-shell/session-shell-reference-screens.test.ts`.
75
+ - **Launcher replaces Settings, Changelog, and Hotkeys Agent commands in bare A1.** The searchable Ctrl+P launcher switches among Agent, Settings, Changelog, and Hotkeys while retaining the session draft. Hotkeys reuses the existing full-screen reference route and live effective-binding document. Ctrl+M directly cycles all available models when unscoped or only available scoped models when scoped, using the existing engine cycle action when the terminal distinguishes it from Enter. `/models` opens the original in-place input-dock selector atomically, without flashing loading or refresh-status text; no floating Models overlay is introduced. Launcher has no description beneath its title. A shared raised floating surface and deterministic dark backdrop replace faint-only modal separation. Successful active-model changes use the status bar rather than a second notice. The first-row PR badge follows the same dim dot separator as the stats-row session name, remains the only linked span, and takes priority over clipped path text. Comparison commands and defaults remain pinned. Evidence: `test/app/session-shell/session-shell-apps.test.ts`, `session-shell-models.test.ts`, `test/ui/components/dialog-panel.test.ts`, and `test/integrations/pi/components/prompt-input-ux.test.ts`.
76
+ - **A1 release notes replace Pi startup changelog behavior only in bare A1.** Bare A1's Launcher → Changelog screen reads the bounded packaged resource generated from reviewed `docs/releases/*.md` documents, and an exact stable current note may open in the same full-screen route after startup safety surfaces. Product-owned per-profile claim/acknowledgement state is written only after a rendered close. The `a1 pi` comparison profile retains Pi's pinned changelog data, settings, startup announcement, and in-feed command presentation. Evidence is in `test/features/owned-ui/release-notes.test.ts`, `release-note-state.test.ts`, `test/composition/settings-route-host.test.ts`, and `test/app/session-shell/session-shell-reference-screens.test.ts`.
76
77
  - **Colour is a port, not an import.** The reference takes a Pi `Theme` directly. A1 defines
77
78
  `UiTheme` so the component layer never imports a Pi adapter and can be rendered plainly in
78
79
  tests.
@@ -85,7 +85,7 @@ must not appear in history diagnostics or test evidence.
85
85
 
86
86
  ## Settings and limits
87
87
 
88
- The History section in `/settings` provides:
88
+ The History section in Settings (**Ctrl+P → Settings**) provides:
89
89
 
90
90
  - **Persistent history:** on by default; applies on the next start. Off disables
91
91
  storage and durable recall for subsequent launches, not current-session
@@ -0,0 +1,15 @@
1
+ # Session exports
2
+
3
+ Bare A1 stores exports without an explicit destination in `~/.a1/exports/`, including development runs. On Windows this normally means `C:/Users/your-name/.a1/exports/`. An explicit `A1_PROFILE_HOME` selects the user home consistently with Agent profiles; temporary development config/data directories do not relocate exports.
4
+
5
+ - `/export` or `/export --html` saves HTML there.
6
+ - `/export --jsonl` saves JSONL there.
7
+ - Supplying a filename or path retains its existing meaning, including relative paths. Format flags can also precede an explicit destination. Quote destinations containing spaces.
8
+ - Default filenames include a timestamp, session identity, and unique suffix. Existing files are not overwritten by a default export.
9
+ - The completion message reports the actual output path. Export failures remain visible.
10
+
11
+ Automatic export storage refuses linked storage-directory entries rather than following them into an unexpected checkout. SDK hosts constructing a bare engine directly supply the `reserveExport` product-storage service; an absent service fails visibly rather than falling back to the current directory. Normal product composition supplies it automatically.
12
+
13
+ Exports are user data and can contain private session content. They are not disposable build artifacts, do not belong in `.artifacts/`, and are never removed by ordinary worktree cleanup. The `a1 pi` comparison profile retains its pinned export behavior.
14
+
15
+ For an old export already blocking an eligible merged worktree, see the explicit named-export preservation operation in [local worktree cleanup](../local-worktree-cleanup.md). It preserves and verifies the selected export before removing only its original pathname; it does not remove the worktree or bypass other local-content checks.
@@ -80,6 +80,14 @@ The central disposable policy is `node_modules`, `dist`, `.builds`, `.artifacts`
80
80
 
81
81
  Each approved root must be an ignored regular file, directory, symbolic link, or Windows junction at that exact path inside the exact worktree. An exact-root link may target an external generated-content directory; cleanup records its link classification, filesystem identity, and canonical target, revalidates all three immediately before deletion, and removes only the link entry through a non-recursive primitive. It never traverses, removes, or mutates the external target. A descendant link remains stricter: its canonical target must stay inside that same exact disposable root. Escapes by descendant links, drift, replacements, unresolved/cyclic targets, special files, and nested repository boundaries remain blocking. Authority is component-exact: near matches such as `.artifacts-user`, `artifacts`, or `pi-settings-metadata-user.json`, arbitrary `target` directories, and sibling native projects remain blocking. Tracked/staged/unstaged/untracked content and every unknown ignored path still block. A tracked regular `.gitmodules` file alone is ordinary content; actual nested `.git` metadata, gitlinks, configured submodules, and submodule changes block. Ordinary content and these approved generated paths are traversed under separate finite entry allowances, so a normal dependency installation does not consume the ordinary source-tree allowance; both allowances retain the same deadline and content-boundary checks. Once those checks pass, cleanup deletes the declared disposable roots or files itself with a bounded retry for transient Windows sharing violations before handing the worktree to Git, so non-force Git removal only has to delete tracked content. A disposable path that stays locked after the retry budget reports `blocked` with `disposable-path-locked` (or `disposable-link-locked` for a link entry) and the path; the worktree, its `.git` pointer, and its journal are untouched, so stop the process holding the handle and rerun the same command.
82
82
 
83
+ Eligible candidates now receive a combined local preflight before purge. JSON reports retain the primary `reason` and add `blockers` plus `developmentProcesses`, so an export and a running development instance are reported together. Only paths, PIDs, parent/start identities, and script paths are reported; file contents and full command lines are never included. Process-inspection failure is a named blocker, not an empty inventory.
84
+
85
+ An exact `complete` invocation may additionally use `--stop-dev-processes`. This is explicit candidate-scoped authority, available only after merged/released eligibility and clean-content checks. It rechecks exact checkout code and process-start identity before stopping, protects the caller and its ancestors, bounds settlement, and repeats ordinary cleanup checks. Other worktrees, installed releases, unverifiable processes, and genuine local changes remain protected. Ordinary sweeps and previews never stop processes. Example:
86
+
87
+ ```bash
88
+ cd /d/Git/a1 && node scripts/governance/local-worktree-cleanup.mjs complete --repo D:/Git/a1 --path D:/Git/a1/.worktrees/example-task --change example-change --pr 123 --stop-dev-processes
89
+ ```
90
+
83
91
  Agents do not manually remove generated content, call `git worktree remove`, or delete the local branch after delivery. The JSON result is authoritative: report success only for `removed` or verified `already-absent`; otherwise retain the worktree and report the exact blocker. A handed-off entry whose worktree was deleted by hand completes through its journal, and its branch is deleted under the accepted-ancestry rule. Legacy roles can supply separate `--source-pr`, `--candidate-pr`, and `--role` values.
84
92
 
85
93
  ## Explicit closed-unmerged discard
@@ -111,6 +119,22 @@ Branch mode verifies the named same-repository PR is still closed-unmerged into
111
119
 
112
120
  A branch-scoped `branch-discard-<ref-digest>.json` journal beside the ordinary cleanup state pins the repository, exact ref, change, PR, expected head, and deletion step without changing existing worktree records. After local preflight, the command journals intent, revalidates live evidence, expected-head deletes only the remote ref, verifies remote absence, and compare-and-deletes the unchanged local ref. Absent refs support resumption. Repeated completed requests report `already-discarded` only while both refs remain absent; a recreated ref is retained as `discard-ref-recreated`. Command reports, not the worktree-only registration list, describe this branch-scoped state. Sweep and queue never invoke branch-only discard.
113
121
 
122
+ ## Explicit named-export preservation
123
+
124
+ Default bare-A1 exports now live in the persistent user directory `~/.a1/exports/`; see [session exports](features/session-exports.md). Existing exports in a worktree remain user data, not cleanup disposables. Adding a filename pattern to `.gitignore` or the disposable policy is not a preservation operation.
125
+
126
+ After the maintainer explicitly chooses one named legacy export to preserve, run the integrated repository operation from primary:
127
+
128
+ ```bash
129
+ cd /d/Git/a1 && node scripts/governance/local-worktree-cleanup.mjs preserve-export --repo D:/Git/a1 --path D:/Git/a1/.worktrees/example-task --change example-change --pr 123 --file session.html --confirm-preserve-export
130
+ ```
131
+
132
+ The exact candidate must already be registered and released, with verified merged/archive/CI evidence and absent remote topic refs. The selected path must be a regular untracked HTML/JSONL file with real non-link ancestors. Tracked/staged/modified selected content, another unknown-content blocker, active/foreign ownership, links, path escapes, or unverifiable evidence retain the worktree and source.
133
+
134
+ The operation exclusively creates a private destination in `~/.a1/exports/`, flushes and verifies its bytes, journals identities and hashes outside the worktree, revalidates the source/candidate/destination, then removes only the selected original pathname. It does not remove the worktree, delete refs, stop processes, enable automation, or widen ordinary cleanup authority. Reports contain metadata only. An interrupted verified copy or source-unlink step resumes through the same exact command and journal; changed or unverifiable source/destination paths remain protected. A partial or conflicting unverified copy is retained for inspection, never overwritten.
135
+
136
+ After `preserved`, run ordinary `complete` or a sweep again. Only those commands can remove the checkout, and they repeat all normal safeguards. Do not run an unmerged worktree's new cleanup tooling against live worktrees; use integrated policy from primary.
137
+
114
138
  ## Explicit disposable-link remediation
115
139
 
116
140
  Ordinary completion and sweep still refuse external descendant links. For one released, eligible merged candidate, a separately confirmed `unlink-disposable-link` operation can remove one explicitly named ignored descendant link beneath a central disposable root without admitting its external target into purge:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.2.7-dev.1269",
3
+ "version": "0.2.7-dev.1275",
4
4
  "description": "Standalone single-agent terminal UI on pinned Pi",
5
5
  "type": "module",
6
6
  "privateLaunchContract": "neutral-launch-v1",
@@ -69,12 +69,12 @@
69
69
  "typecheck:bin": "tsgo -p tsconfig.bin.json"
70
70
  },
71
71
  "dependencies": {
72
- "@earendil-works/pi-coding-agent": "1.5.0",
73
- "@earendil-works/pi-tui": "1.5.0",
72
+ "@earendil-works/pi-coding-agent": "1.5.1",
73
+ "@earendil-works/pi-tui": "1.5.1",
74
74
  "@silvia-odwyer/photon-node": "0.3.4",
75
75
  "chalk": "5.6.2",
76
76
  "cross-spawn": "7.0.6",
77
- "grok-mermaid": "0.2.2",
77
+ "lovely-mermaid": "0.3.3",
78
78
  "proper-lockfile": "4.1.2",
79
79
  "semver": "7.8.5",
80
80
  "undici": "8.9.0"
@@ -113,7 +113,7 @@
113
113
  "@mariozechner/clipboard": "^0.3.9"
114
114
  },
115
115
  "publication": {
116
- "source": "0783454e6931f7478be0a1bbfe27d9e886a0c3a3",
117
- "pullRequest": 896
116
+ "source": "5cf34be529863e10748e1ff0339de8ddc9bb5639",
117
+ "pullRequest": 902
118
118
  }
119
119
  }