@timurproko/a1 0.2.7-dev.810 → 0.2.7-dev.831

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 (124) hide show
  1. package/README.md +7 -1
  2. package/bin/automatic-update.js +7 -0
  3. package/bin/ui.js +1 -0
  4. package/dist/app/session-shell/resident-tab-coordinator.d.ts +6 -9
  5. package/dist/app/session-shell/resident-tab-coordinator.js +9 -52
  6. package/dist/app/session-shell/session-shell-root.d.ts +9 -3
  7. package/dist/app/session-shell/session-shell.js +70 -20
  8. package/dist/app/session-shell/session-viewport-controller.js +6 -4
  9. package/dist/app/session-shell/transcript-document.js +1 -1
  10. package/dist/composition/owned-ui.d.ts +3 -11
  11. package/dist/composition/owned-ui.js +93 -43
  12. package/dist/composition/resident-tab-bridge.d.ts +10 -1
  13. package/dist/composition/resident-tab-bridge.js +21 -0
  14. package/dist/composition/settings-route-host.d.ts +12 -2
  15. package/dist/composition/settings-route-host.js +27 -9
  16. package/dist/composition/settings-search-input.d.ts +9 -0
  17. package/dist/composition/settings-search-input.js +58 -0
  18. package/dist/contracts/owned-ui/session-backend.d.ts +2 -0
  19. package/dist/contracts/owned-ui/session-workflows.d.ts +7 -3
  20. package/dist/contracts/presentation/index.d.ts +11 -0
  21. package/dist/contracts/presentation/index.js +12 -0
  22. package/dist/features/launch/development-launch.js +6 -0
  23. package/dist/features/launch/profile-paths.js +4 -0
  24. package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
  25. package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
  26. package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
  27. package/dist/features/owned-ui/reference-screen-app.js +12 -2
  28. package/dist/features/owned-ui/settings-app.d.ts +10 -3
  29. package/dist/features/owned-ui/settings-app.js +98 -45
  30. package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
  31. package/dist/features/owned-ui/settings-search-input.js +1 -0
  32. package/dist/features/prompt-history/index.d.ts +1 -1
  33. package/dist/features/prompt-history/paths.d.ts +2 -1
  34. package/dist/features/prompt-history/paths.js +10 -2
  35. package/dist/features/prompt-history/service.d.ts +5 -0
  36. package/dist/features/prompt-history/service.js +1 -1
  37. package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
  38. package/dist/features/resident-tab/shortcut-declarations.js +2 -0
  39. package/dist/foundation/launch-context/index.d.ts +2 -0
  40. package/dist/foundation/launch-context/index.js +1 -0
  41. package/dist/foundation/release/automatic-update.d.ts +37 -0
  42. package/dist/foundation/release/automatic-update.js +265 -0
  43. package/dist/foundation/release/bootstrap.d.ts +1 -9
  44. package/dist/foundation/release/bootstrap.js +7 -3
  45. package/dist/foundation/release/index.d.ts +1 -1
  46. package/dist/foundation/release/index.js +1 -1
  47. package/dist/foundation/release/latest-release.d.ts +3 -2
  48. package/dist/foundation/release/latest-release.js +1 -1
  49. package/dist/foundation/release/update-execution-lease.d.ts +36 -0
  50. package/dist/foundation/release/update-execution-lease.js +145 -0
  51. package/dist/foundation/release/update.d.ts +8 -6
  52. package/dist/foundation/release/update.js +232 -167
  53. package/dist/foundation/resident-tabs/bridge-client.d.ts +9 -3
  54. package/dist/foundation/resident-tabs/bridge-client.js +14 -7
  55. package/dist/foundation/resident-tabs/routes.d.ts +5 -0
  56. package/dist/foundation/resident-tabs/routes.js +5 -0
  57. package/dist/foundation/resident-tabs/settings.d.ts +0 -1
  58. package/dist/foundation/resident-tabs/settings.js +0 -2
  59. package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
  60. package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
  61. package/dist/integrations/pi/components/modal-frame.js +48 -9
  62. package/dist/integrations/pi/components/models-dialog.js +4 -52
  63. package/dist/integrations/pi/components/owned-editor-ux.d.ts +3 -0
  64. package/dist/integrations/pi/components/owned-editor-ux.js +75 -4
  65. package/dist/integrations/pi/components/shell-editor-autocomplete.js +57 -37
  66. package/dist/integrations/pi/components/shell-footer-status.js +19 -3
  67. package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
  68. package/dist/integrations/pi/components/shell-selectors-dialogs.js +4 -1
  69. package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
  70. package/dist/integrations/pi/components/skills-dialog.js +2 -2
  71. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -5
  72. package/dist/integrations/pi/components/upstream/components/owned-editor.js +15 -8
  73. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -3
  74. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
  75. package/dist/integrations/pi/components/upstream/components/session-footer.js +26 -23
  76. package/dist/integrations/pi/components/upstream/components/session-selector.js +7 -6
  77. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
  78. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +9 -8
  79. package/dist/integrations/pi/components/upstream/components/tool-execution.js +6 -4
  80. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +8 -8
  81. package/dist/integrations/pi/components/upstream/components/tree-selector.js +10 -10
  82. package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -0
  83. package/dist/integrations/pi/engine/adapter.js +7 -2
  84. package/dist/integrations/pi/engine/ports.d.ts +1 -1
  85. package/dist/integrations/pi/engine/ports.js +4 -0
  86. package/dist/integrations/pi/engine/workflow-runner.js +9 -2
  87. package/dist/native/darwin-arm64/manifest.json +1 -1
  88. package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
  89. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  90. package/dist/native/darwin-x64/manifest.json +1 -1
  91. package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
  92. package/dist/native/darwin-x64/terminal-host.node +0 -0
  93. package/dist/native/linux-x64/manifest.json +1 -1
  94. package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
  95. package/dist/native/linux-x64/terminal-host.node +0 -0
  96. package/dist/native/win32-x64/manifest.json +2 -2
  97. package/dist/native/win32-x64/process-guardian.node +0 -0
  98. package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
  99. package/dist/native/win32-x64/terminal-host.node +0 -0
  100. package/dist/product-identity.d.ts +1 -1
  101. package/dist/product-identity.js +1 -1
  102. package/dist/product-identity.json +1 -0
  103. package/dist/ui/apps/host.d.ts +2 -0
  104. package/dist/ui/apps/host.js +7 -1
  105. package/dist/ui/components/dialog-panel.js +35 -30
  106. package/dist/ui/components/line-input.d.ts +3 -0
  107. package/dist/ui/components/line-input.js +3 -1
  108. package/dist/ui/components/prompt-input.d.ts +5 -4
  109. package/dist/ui/components/prompt-input.js +8 -6
  110. package/dist/ui/components/submitted-prompt.d.ts +2 -2
  111. package/dist/ui/components/submitted-prompt.js +3 -3
  112. package/dist/ui/components/surface.js +4 -2
  113. package/dist/ui/components/transcript-viewport.d.ts +1 -1
  114. package/dist/ui/components/transcript-viewport.js +141 -24
  115. package/dist/ui/settings/declarations.d.ts +65 -15
  116. package/dist/ui/settings/declarations.js +45 -13
  117. package/dist/ui/settings/manager.js +7 -3
  118. package/dist/ui/settings/migrations.js +22 -0
  119. package/dist/ui/settings/sections.d.ts +4 -0
  120. package/dist/ui/settings/sections.js +9 -3
  121. package/docs/architecture/resident-tabs.md +39 -11
  122. package/docs/architecture/tool-image-presentation.md +2 -1
  123. package/docs/features/prompt-history.md +41 -20
  124. package/package.json +1 -1
@@ -3,11 +3,11 @@
3
3
  * packages/coding-agent/src/modes/interactive/components/custom-editor.ts.
4
4
  * Modifications: A1-owned class name and synchronized A1 keybinding contract replace the nominal
5
5
  * private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
6
- * frame, transient contextual-suggestion branch, and explicit body geometry for selection and
7
- * above-prompt autocomplete with a one-cell outer gutter. Bare A1 also clears a sole top-level
8
- * slash-command search on Escape and completes a selected or exact completed tunnel command with `:`
9
- * and reopens the menu on its tunnel rows, and synchronously retains then refreshes an exact top-level
10
- * command menu after Tab applies its row.
6
+ * frame, configurable empty-input placeholder branch, transient contextual-suggestion branch, and
7
+ * explicit body geometry for selection and above-prompt autocomplete with a one-cell outer gutter.
8
+ * Bare A1 also clears a sole top-level slash-command search on Escape and completes a selected or
9
+ * exact completed tunnel command with `:` and reopens the menu on its tunnel rows, and synchronously
10
+ * retains then refreshes an exact top-level command menu after Tab applies its row.
11
11
  * Deviations: owned-shared-input-frame, above-prompt-autocomplete-placement,
12
12
  * clear-command-search-on-escape, command-tunnel-colon-completion, keep-command-menu-open-after-tab.
13
13
  */
@@ -21,6 +21,8 @@ export interface OwnedEditorOptions extends EditorOptions {
21
21
  readonly inputPresentation?: PiShellPromptInputPresentation;
22
22
  readonly styleSuggestion?: (text: string) => string;
23
23
  readonly styleSuggestionCaret?: (text: string) => string;
24
+ /** Non-accepting empty-input guidance rendered through the shared suggestion presentation. */
25
+ readonly placeholder?: string;
24
26
  readonly terminalRows?: () => number;
25
27
  /** Reuses the editor's established atomic-aware visual layout when available. */
26
28
  readonly getVisualLineCount?: (width: number) => number | undefined;
@@ -3,11 +3,11 @@
3
3
  * packages/coding-agent/src/modes/interactive/components/custom-editor.ts.
4
4
  * Modifications: A1-owned class name and synchronized A1 keybinding contract replace the nominal
5
5
  * private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
6
- * frame, transient contextual-suggestion branch, and explicit body geometry for selection and
7
- * above-prompt autocomplete with a one-cell outer gutter. Bare A1 also clears a sole top-level
8
- * slash-command search on Escape and completes a selected or exact completed tunnel command with `:`
9
- * and reopens the menu on its tunnel rows, and synchronously retains then refreshes an exact top-level
10
- * command menu after Tab applies its row.
6
+ * frame, configurable empty-input placeholder branch, transient contextual-suggestion branch, and
7
+ * explicit body geometry for selection and above-prompt autocomplete with a one-cell outer gutter.
8
+ * Bare A1 also clears a sole top-level slash-command search on Escape and completes a selected or
9
+ * exact completed tunnel command with `:` and reopens the menu on its tunnel rows, and synchronously
10
+ * retains then refreshes an exact top-level command menu after Tab applies its row.
11
11
  * Deviations: owned-shared-input-frame, above-prompt-autocomplete-placement,
12
12
  * clear-command-search-on-escape, command-tunnel-colon-completion, keep-command-menu-open-after-tab.
13
13
  */
@@ -25,6 +25,7 @@ export function createOwnedEditorClass(Base) {
25
25
  #inputPresentation;
26
26
  #styleSuggestion;
27
27
  #styleSuggestionCaret;
28
+ #placeholder;
28
29
  #terminalRows;
29
30
  #getVisualLineCount;
30
31
  #clearCommandSearchOnEscape;
@@ -37,6 +38,7 @@ export function createOwnedEditorClass(Base) {
37
38
  this.#inputPresentation = options.inputPresentation;
38
39
  this.#styleSuggestion = options.styleSuggestion ?? (text => text);
39
40
  this.#styleSuggestionCaret = options.styleSuggestionCaret ?? (text => `\u001b[7m${text}\u001b[27m`);
41
+ this.#placeholder = options.placeholder ?? null;
40
42
  this.#terminalRows = options.terminalRows ?? (() => 24);
41
43
  this.#getVisualLineCount = options.getVisualLineCount;
42
44
  this.#clearCommandSearchOnEscape = options.clearCommandSearchOnEscape === true;
@@ -70,7 +72,12 @@ export function createOwnedEditorClass(Base) {
70
72
  return rows;
71
73
  }
72
74
  if (this.#promptSuggestion !== null && this.canPresentPromptSuggestion()) {
73
- const rows = this.#renderSuggestion(width);
75
+ const rows = this.#renderSuggestion(width, this.#promptSuggestion);
76
+ this.#renderedBodyRowCount = rows.length;
77
+ return rows;
78
+ }
79
+ if (this.#placeholder !== null && this.focused && this.getText().length === 0 && !this.isShowingAutocomplete()) {
80
+ const rows = this.#renderSuggestion(width, this.#placeholder);
74
81
  this.#renderedBodyRowCount = rows.length;
75
82
  return rows;
76
83
  }
@@ -176,10 +183,10 @@ export function createOwnedEditorClass(Base) {
176
183
  triggerAutocomplete(this);
177
184
  return true;
178
185
  }
179
- #renderSuggestion(width) {
186
+ #renderSuggestion(width, suggestion) {
180
187
  const presentation = this.#inputPresentation;
181
188
  const { paddingX, contentWidth, layoutWidth } = presentation.geometry(width, this.getPaddingX());
182
- const chunks = wrapTextWithAnsi(this.#promptSuggestion ?? "", layoutWidth).map(text => ({ text }));
189
+ const chunks = wrapTextWithAnsi(suggestion, layoutWidth).map(text => ({ text }));
183
190
  const maxVisible = Math.max(5, Math.floor(this.#terminalRows() * 0.3));
184
191
  const visible = chunks.slice(0, maxVisible);
185
192
  const leftPadding = " ".repeat(paddingX);
@@ -5,9 +5,9 @@
5
5
  * search, bulk/provider/reorder actions, dirty state, Ctrl+S persistence, refresh status,
6
6
  * cancellation, and focus while remapping theme and public helper imports; local key labels preserve
7
7
  * pinned platform formatting before layout without changing binding identities, and bare A1 uses the
8
- * shared semantic modal shortcut row and compact padded modal frame while immediately closing on the
9
- * implicit Ctrl+C selection-cancel alias without advertising it.
10
- * Deviations: owned-modal-shortcut-hints, owned-dialog-ctrl-c-cancel.
8
+ * shared semantic modal shortcut row, accent search marker, and compact padded modal frame while
9
+ * immediately closing on the implicit Ctrl+C selection-cancel alias without advertising it.
10
+ * Deviations: owned-modal-shortcut-hints, owned-dialog-ctrl-c-cancel, owned-dialog-search-marker.
11
11
  */
12
12
  interface ScopedModel {
13
13
  readonly provider: string;
@@ -1,7 +1,7 @@
1
1
  import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, } from "@earendil-works/pi-tui";
2
2
  import { DynamicBorder } from "../../../startup-public.js";
3
3
  import { getModelSearchText } from "../model-search.js";
4
- import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
4
+ import { addPiModalHeader, addPiModalSearchInput, adoptPiModalFrame } from "../../modal-frame.js";
5
5
  import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalShortcutHints } from "../../theme.js";
6
6
  // Rationale: upstream's capitalized hint formatter is private to the package; the same mapping lives here.
7
7
  function keyDisplayText(keybinding) {
@@ -118,7 +118,7 @@ export class ScopedModelsSelectorComponent extends Container {
118
118
  this.addChild(new Spacer(1));
119
119
  // Search input
120
120
  this.searchInput = new Input();
121
- this.addChild(this.searchInput);
121
+ addPiModalSearchInput(this, this.searchInput);
122
122
  this.addChild(new Spacer(1));
123
123
  // List container
124
124
  this.listContainer = new Container();
@@ -1,13 +1,10 @@
1
1
  /**
2
2
  * Provenance: @earendil-works/pi-coding-agent 1.4.0 (MIT), commit 4a080252f06caf63a525d7873c5d94d3c23887e0,
3
3
  * packages/coding-agent/src/modes/interactive/components/footer.ts.
4
- * Modifications: Consumes neutral owned-UI view data instead of a fabricated concrete AgentSession;
5
- * preserves the latest physical route for virtual models; an explicit bare-A1 profile colors the
6
- * thinking-level name, renders its repository-context path, and reserves its linked pull-request badge
7
- * before path truncation while preserving the remaining footer layout and formatting; replacement
8
- * surfaces may suppress a thinking level they already present; a resident tab whose chip already shows
9
- * the session name may suppress its path-row suffix.
10
- * Deviations: owned-status-level-color, owned-pull-request-badge, owned-resident-session-name.
4
+ * Modifications: Owned UI/routing; A1 level color, linked PRs, resident names, dim ellipsis and model
5
+ * priority; Pi unchanged.
6
+ * Deviations: owned-status-level-color, owned-pull-request-badge, owned-resident-session-name,
7
+ * owned-footer-ellipsis, owned-footer-model-priority.
11
8
  */
12
9
  import { isAbsolute, relative, resolve, sep } from "node:path";
13
10
  import { hyperlink, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
@@ -31,6 +28,7 @@ export class SessionFooter {
31
28
  const view = this.getView();
32
29
  const usage = view.status.usage;
33
30
  const theme = piTheme();
31
+ const dimEllipsis = theme.fg("dim", this.profile === "a1" ? "…" : "...");
34
32
  const input = usage?.input ?? 0;
35
33
  const output = usage?.output ?? 0;
36
34
  const cacheRead = usage?.cacheRead ?? 0;
@@ -38,8 +36,8 @@ export class SessionFooter {
38
36
  const cost = usage?.cost ?? 0;
39
37
  const contextWindow = usage?.contextWindow ?? 0;
40
38
  const contextPercentValue = usage?.contextPercent ?? 0;
41
- const contextPercentSource = usage?.contextAvailable === false ? undefined : usage?.contextPercent;
42
- const contextPercent = contextPercentSource !== null ? contextPercentValue.toFixed(1) : "?";
39
+ const contextPercent = usage?.contextAvailable !== false && usage?.contextPercent === null
40
+ ? "?" : contextPercentValue.toFixed(1);
43
41
  const repositoryPath = this.profile === "a1" ? view.status.footer?.repositoryPath ?? this.cwd : this.cwd;
44
42
  let pwd = formatCwd(repositoryPath, process.env.HOME || process.env.USERPROFILE);
45
43
  const branch = view.status.footer?.branch;
@@ -48,7 +46,7 @@ export class SessionFooter {
48
46
  const sessionName = this.showSessionName ? view.status.footer?.sessionName : null;
49
47
  const pullRequest = this.profile === "a1" ? view.status.footer?.pullRequest : null;
50
48
  let pathRow;
51
- if (pullRequest === undefined || pullRequest === null) {
49
+ if (pullRequest == null) {
52
50
  pathRow = theme.fg("dim", sessionName ? `${pwd} • ${sessionName}` : pwd);
53
51
  }
54
52
  else {
@@ -59,12 +57,10 @@ export class SessionFooter {
59
57
  if (visibleWidth(full) <= width)
60
58
  pathRow = full;
61
59
  else if (visibleWidth(badge) <= width) {
62
- const pathWidth = Math.max(0, width - visibleWidth(badge));
63
- const path = pathWidth === 0 ? "" : truncateToWidth(theme.fg("dim", pwd), pathWidth, theme.fg("dim", "..."));
64
- pathRow = path + badge;
60
+ pathRow = truncateToWidth(theme.fg("dim", pwd), width - visibleWidth(badge), dimEllipsis) + badge;
65
61
  }
66
62
  else {
67
- pathRow = truncateToWidth(badge, width, theme.fg("dim", "..."));
63
+ pathRow = truncateToWidth(badge, width, dimEllipsis);
68
64
  }
69
65
  }
70
66
  const parts = [];
@@ -88,7 +84,7 @@ export class SessionFooter {
88
84
  let left = parts.join(" ");
89
85
  let leftWidth = visibleWidth(left);
90
86
  if (leftWidth > width) {
91
- left = truncateToWidth(left, width, "...");
87
+ left = truncateToWidth(left, width, this.profile === "a1" ? dimEllipsis : "...");
92
88
  leftWidth = visibleWidth(left);
93
89
  }
94
90
  const modelName = view.activeModel?.modelId ?? "no-model";
@@ -96,6 +92,7 @@ export class SessionFooter {
96
92
  ? view.activeModel === null || !this.show() ? theme.fg("dim", modelName)
97
93
  : theme.fg("dim", `${modelName} • `) + theme.getThinkingBorderColor(view.thinkingLevel)(view.thinkingLevel)
98
94
  : view.activeModel === null || view.thinkingLevel === "off" || !this.show() ? modelName : `${modelName} • ${view.thinkingLevel}`;
95
+ const modelLevel = rightWithoutProvider;
99
96
  const routed = view.routedModel;
100
97
  if (routed) {
101
98
  const level = routed.thinkingLevel === undefined ? "" : ` • ${routed.thinkingLevel}`;
@@ -109,9 +106,18 @@ export class SessionFooter {
109
106
  if (leftWidth + 2 + visibleWidth(right) > width)
110
107
  right = rightWithoutProvider;
111
108
  }
109
+ if (this.profile === "a1" && leftWidth + 2 + visibleWidth(right) > width) {
110
+ right = [rightWithoutProvider, modelLevel].find(value => visibleWidth(value) + 2 <= width)
111
+ ?? truncateToWidth(theme.fg("dim", modelName), width, dimEllipsis);
112
+ left = truncateToWidth(left, Math.max(0, width - visibleWidth(right) - 2), dimEllipsis);
113
+ leftWidth = visibleWidth(left);
114
+ }
112
115
  const rightWidth = visibleWidth(right);
113
116
  let line;
114
- if (leftWidth + 2 + rightWidth <= width) {
117
+ if (this.profile === "a1" && leftWidth === 0) {
118
+ line = " ".repeat(Math.max(0, width - rightWidth)) + right;
119
+ }
120
+ else if (leftWidth + 2 + rightWidth <= width) {
115
121
  line = left + " ".repeat(width - leftWidth - rightWidth) + right;
116
122
  }
117
123
  else {
@@ -124,13 +130,13 @@ export class SessionFooter {
124
130
  line = left;
125
131
  }
126
132
  const lines = [
127
- truncateToWidth(pathRow, width, theme.fg("dim", "...")),
133
+ truncateToWidth(pathRow, width, dimEllipsis),
128
134
  theme.fg("dim", left) + (this.profile === "a1" ? line.slice(left.length) : theme.fg("dim", line.slice(left.length))),
129
135
  ];
130
136
  const statuses = view.status.footer?.extensionStatuses ?? [];
131
137
  if (statuses.length > 0) {
132
138
  const status = [...statuses].sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([, text]) => sanitize(text)).join(" ");
133
- lines.push(truncateToWidth(status, width, theme.fg("dim", "...")));
139
+ lines.push(truncateToWidth(status, width, dimEllipsis));
134
140
  }
135
141
  return lines;
136
142
  }
@@ -150,11 +156,8 @@ function formatTokens(count) {
150
156
  function formatCwd(cwd, home) {
151
157
  if (!home)
152
158
  return cwd;
153
- const resolvedCwd = resolve(cwd);
154
- const resolvedHome = resolve(home);
155
- const relativeToHome = relative(resolvedHome, resolvedCwd);
156
- const inside = relativeToHome === "" || (relativeToHome !== ".." && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));
157
- if (!inside)
159
+ const relativeToHome = relative(resolve(home), resolve(cwd));
160
+ if (isAbsolute(relativeToHome) || relativeToHome === ".." || relativeToHome.startsWith(`..${sep}`))
158
161
  return cwd;
159
162
  return relativeToHome === "" ? "~" : `~${sep}${relativeToHome}`;
160
163
  }
@@ -7,11 +7,12 @@
7
7
  * keybindings/theme, canonical path handling, reverse-Tab scope cycling, and the shared bare-A1 modal
8
8
  * frame with standalone title, standard outer rules, stable filter/status row, progressive result
9
9
  * paging, aligned result columns, Session Tree selection roles, quiet in-field query syntax guidance,
10
- * and one-line bottom dynamic feedback and searchable-dialog shortcut footer. Selected session rows
11
- * use the blue selection surface while preserving title and metadata foregrounds; the active session
12
- * title uses success green independently of keyboard selection, with ordinary text weight.
10
+ * an accent dialog-search marker, and one-line bottom dynamic feedback and searchable-dialog shortcut
11
+ * footer. Selected session rows use the blue selection surface while preserving title and metadata
12
+ * foregrounds; the active session title uses success green independently of keyboard selection, with
13
+ * ordinary text weight.
13
14
  * Deviations: owned-modal-shortcut-hints, owned-resume-session-dialog,
14
- * owned-standard-dialog-selection.
15
+ * owned-standard-dialog-selection, owned-dialog-search-marker.
15
16
  */
16
17
  import { spawnSync } from "node:child_process";
17
18
  import { existsSync, realpathSync } from "node:fs";
@@ -20,7 +21,7 @@ import * as os from "node:os";
20
21
  import { Container, getKeybindings, Input, matchesKey, sliceByColumn, Spacer, Text, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
21
22
  import { KeybindingsManager } from "../adjacent/core/keybindings.js";
22
23
  import { DynamicBorder } from "../../../startup-public.js";
23
- import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
24
+ import { addPiModalHeader, adoptPiModalFrame, renderPiModalSearchInput } from "../../modal-frame.js";
24
25
  import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalShortcutHints } from "../../theme.js";
25
26
  const theme = new Proxy({}, {
26
27
  get(_target, property) {
@@ -408,7 +409,7 @@ class SessionList {
408
409
  render(width) {
409
410
  const lines = [];
410
411
  // Render search input
411
- lines.push(...this.searchInput.render(width));
412
+ lines.push(...renderPiModalSearchInput(this.searchInput, width));
412
413
  lines.push(""); // Blank line after search
413
414
  if (this.filteredSessions.length === 0) {
414
415
  let emptyMessage;
@@ -8,13 +8,14 @@
8
8
  * levels, and rendering exclusive radio-style default markers before level names, an item-adjacent
9
9
  * active checkmark, and aligned muted descriptions. Bare A1 persists defaults immediately on Space,
10
10
  * closes through the implicit Ctrl+C-capable selection-cancel action, and uses the shared compact
11
- * semantic shortcut row with Type/search guidance without advertising that alias. All list and border
12
- * colors use the owned theme and its explicit color mode. Selected rows use the shared bare-A1 blue
13
- * selection surface only around the rendered item span, with the existing accent arrow, normal-text
14
- * level, and preserved current/default/description roles. The comparison profile retains the public
15
- * pinned component.
11
+ * semantic shortcut row with Type/search guidance and an accent search marker without advertising that
12
+ * alias. All list and border colors use the owned theme and its explicit color mode. Selected rows use
13
+ * the shared bare-A1 blue selection surface only around the rendered item span, with the existing
14
+ * accent arrow, normal-text level, and preserved current/default/description roles. The comparison
15
+ * profile retains the public pinned component.
16
16
  * Deviations: owned-modal-shortcut-hints, owned-level-cycle-shortcut, owned-thinking-selector-heading,
17
- * owned-thinking-selector-controls, owned-dialog-ctrl-c-cancel, owned-standard-dialog-selection.
17
+ * owned-thinking-selector-controls, owned-dialog-ctrl-c-cancel, owned-standard-dialog-selection,
18
+ * owned-dialog-search-marker.
18
19
  */
19
20
  import { Container, type Focusable, SelectList } from "@earendil-works/pi-tui";
20
21
  export type ThinkingSelectorLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
@@ -8,17 +8,18 @@
8
8
  * levels, and rendering exclusive radio-style default markers before level names, an item-adjacent
9
9
  * active checkmark, and aligned muted descriptions. Bare A1 persists defaults immediately on Space,
10
10
  * closes through the implicit Ctrl+C-capable selection-cancel action, and uses the shared compact
11
- * semantic shortcut row with Type/search guidance without advertising that alias. All list and border
12
- * colors use the owned theme and its explicit color mode. Selected rows use the shared bare-A1 blue
13
- * selection surface only around the rendered item span, with the existing accent arrow, normal-text
14
- * level, and preserved current/default/description roles. The comparison profile retains the public
15
- * pinned component.
11
+ * semantic shortcut row with Type/search guidance and an accent search marker without advertising that
12
+ * alias. All list and border colors use the owned theme and its explicit color mode. Selected rows use
13
+ * the shared bare-A1 blue selection surface only around the rendered item span, with the existing
14
+ * accent arrow, normal-text level, and preserved current/default/description roles. The comparison
15
+ * profile retains the public pinned component.
16
16
  * Deviations: owned-modal-shortcut-hints, owned-level-cycle-shortcut, owned-thinking-selector-heading,
17
- * owned-thinking-selector-controls, owned-dialog-ctrl-c-cancel, owned-standard-dialog-selection.
17
+ * owned-thinking-selector-controls, owned-dialog-ctrl-c-cancel, owned-standard-dialog-selection,
18
+ * owned-dialog-search-marker.
18
19
  */
19
20
  import { Container, fuzzyFilter, getKeybindings, Input, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
20
21
  import { DynamicBorder } from "../../../startup-public.js";
21
- import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
22
+ import { addPiModalHeader, addPiModalSearchInput, adoptPiModalFrame } from "../../modal-frame.js";
22
23
  import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalListRow, renderPiModalShortcutHints } from "../../theme.js";
23
24
  const THINKING_SELECT_LIST_LAYOUT = {
24
25
  minPrimaryColumnWidth: 12,
@@ -88,7 +89,7 @@ export class OwnedThinkingSelectorComponent extends Container {
88
89
  this.addChild(new Spacer(1));
89
90
  this.searchInput = new Input();
90
91
  this.searchInput.onSubmit = () => this.selectList.handleInput("\r");
91
- this.addChild(this.searchInput);
92
+ addPiModalSearchInput(this, this.searchInput);
92
93
  this.addChild(new Spacer(1));
93
94
  const initialList = this.buildSelectList(this.allItems, currentLevel);
94
95
  this.selectList = initialList.list;
@@ -3,13 +3,15 @@
3
3
  * packages/coding-agent/src/modes/interactive/components/tool-execution.ts.
4
4
  * Modifications: Retain pinned shell/public renderers. Own image conversion; replace private APIs with
5
5
  * public/local equivalents. Use bare-A1 secondary-accent read ranges and concise collapse shortcut
6
- * hints. Restore the enclosing default-shell background after nested renderer resets. See
6
+ * hints. Restore the enclosing default-shell background after nested renderer resets. Omit
7
+ * non-actionable image fallback rows when the terminal has no image protocol. See
7
8
  * docs/architecture/tool-image-presentation.md.
8
9
  * Deviations: current-tool-image-conversion-ownership, read-range-secondary-accent,
9
- * concise-tool-expand-shortcut-hint, default-shell-background-reset-scoping.
10
+ * concise-tool-expand-shortcut-hint, default-shell-background-reset-scoping,
11
+ * unsupported-image-fallback-omission.
10
12
  */
11
13
  import { stripVTControlCharacters } from "node:util";
12
- import { Box, Container, getCapabilities, getImageDimensions, imageFallback, MouseRegion, Spacer, Text } from "@earendil-works/pi-tui";
14
+ import { Box, Container, getImageDimensions, imageFallback, MouseRegion, Spacer, Text } from "@earendil-works/pi-tui";
13
15
  import { createReadToolDefinition, createBashToolDefinition, createEditToolDefinition, createWriteToolDefinition, createGrepToolDefinition, createFindToolDefinition, createLsToolDefinition, keyHint, keyText, } from "../../../startup-public.js";
14
16
  import { renderPiModalShortcutHints } from "../../theme.js";
15
17
  import { isPiAccentProjectionEnabled, piTheme } from "../theme/theme.js";
@@ -379,7 +381,7 @@ export class ToolExecutionComponent extends Container {
379
381
  if (!this.result)
380
382
  return "";
381
383
  const text = this.result.content.filter(part => part.type === "text").map(part => stripVTControlCharacters(part.text ?? "").replace(/[\u0000-\u0008\u000b-\u001f\ufff9-\ufffb]/gu, "")).join("\n");
382
- if (getCapabilities().images && this.showImages)
384
+ if (this.showImages)
383
385
  return text;
384
386
  const indicators = this.result.content.filter(part => part.type === "image").map(part => imageFallback(part.mimeType ?? "image/unknown", part.data && part.mimeType ? getImageDimensions(part.data, part.mimeType) ?? undefined : undefined)).join("\n");
385
387
  return text && indicators ? `${text}\n${indicators}` : text || indicators;
@@ -4,15 +4,15 @@
4
4
  * Modifications: Port remaps public types/components plus owned keybindings/theme helpers while
5
5
  * preserving tree behavior; bare A1 uses compact modal chrome and label editing, four-mode
6
6
  * Models-style filter status with Tab/Shift+Tab directional cycling and concise all-first presentation
7
- * excluding internal bookkeeping, standard search input, blue full-row menu-arrow selection that
8
- * preserves per-entry semantic foregrounds and has no path bullets, accent entry labels, bracketed
9
- * timestamps, numeric result counters without duplicate label-time status, and a stateful time on/off
10
- * shortcut, semantic role colors with session naming for the system root, standard
11
- * paging/first-last/non-root containing-branch folding keys with a permanently expanded system session
12
- * entry even behind hidden metadata, single-character ellipses on both clipped edges with
13
- * bracket-delimiter preservation and selected-fragment highlighting, and Models-ordered semantic
7
+ * excluding internal bookkeeping, a standard accent-marked search input, blue full-row menu-arrow
8
+ * selection that preserves per-entry semantic foregrounds and has no path bullets, accent entry
9
+ * labels, bracketed timestamps, numeric result counters without duplicate label-time status, and a
10
+ * stateful time on/off shortcut, semantic role colors with session naming for the system root,
11
+ * standard paging/first-last/non-root containing-branch folding keys with a permanently expanded
12
+ * system session entry even behind hidden metadata, single-character ellipses on both clipped edges
13
+ * with bracket-delimiter preservation and selected-fragment highlighting, and Models-ordered semantic
14
14
  * Type/search shortcut footers without a redundant select hint.
15
- * Deviations: owned-modal-shortcut-hints, owned-session-tree-dialog.
15
+ * Deviations: owned-modal-shortcut-hints, owned-session-tree-dialog, owned-dialog-search-marker.
16
16
  */
17
17
  import { type Component, Container, type Focusable } from "@earendil-works/pi-tui";
18
18
  import { type SessionTreeNode } from "@earendil-works/pi-coding-agent";
@@ -4,20 +4,20 @@
4
4
  * Modifications: Port remaps public types/components plus owned keybindings/theme helpers while
5
5
  * preserving tree behavior; bare A1 uses compact modal chrome and label editing, four-mode
6
6
  * Models-style filter status with Tab/Shift+Tab directional cycling and concise all-first presentation
7
- * excluding internal bookkeeping, standard search input, blue full-row menu-arrow selection that
8
- * preserves per-entry semantic foregrounds and has no path bullets, accent entry labels, bracketed
9
- * timestamps, numeric result counters without duplicate label-time status, and a stateful time on/off
10
- * shortcut, semantic role colors with session naming for the system root, standard
11
- * paging/first-last/non-root containing-branch folding keys with a permanently expanded system session
12
- * entry even behind hidden metadata, single-character ellipses on both clipped edges with
13
- * bracket-delimiter preservation and selected-fragment highlighting, and Models-ordered semantic
7
+ * excluding internal bookkeeping, a standard accent-marked search input, blue full-row menu-arrow
8
+ * selection that preserves per-entry semantic foregrounds and has no path bullets, accent entry
9
+ * labels, bracketed timestamps, numeric result counters without duplicate label-time status, and a
10
+ * stateful time on/off shortcut, semantic role colors with session naming for the system root,
11
+ * standard paging/first-last/non-root containing-branch folding keys with a permanently expanded
12
+ * system session entry even behind hidden metadata, single-character ellipses on both clipped edges
13
+ * with bracket-delimiter preservation and selected-fragment highlighting, and Models-ordered semantic
14
14
  * Type/search shortcut footers without a redundant select hint.
15
- * Deviations: owned-modal-shortcut-hints, owned-session-tree-dialog.
15
+ * Deviations: owned-modal-shortcut-hints, owned-session-tree-dialog, owned-dialog-search-marker.
16
16
  */
17
17
  import { Container, Input, matchesKey, Spacer, sliceByColumn, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
18
18
  import { DynamicBorder, formatReadLineRange } from "../../../startup-public.js";
19
19
  import { KeybindingsManager } from "../adjacent/core/keybindings.js";
20
- import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
20
+ import { addPiModalHeader, adoptPiModalFrame, renderPiModalSearchInput } from "../../modal-frame.js";
21
21
  import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalShortcutHints } from "../../theme.js";
22
22
  const theme = new Proxy({}, {
23
23
  get(_target, property) {
@@ -1077,7 +1077,7 @@ class TreeSearchInput {
1077
1077
  this.input.handleInput(query);
1078
1078
  this.renderedQuery = query;
1079
1079
  }
1080
- return this.input.render(width);
1080
+ return renderPiModalSearchInput(this.input, width);
1081
1081
  }
1082
1082
  }
1083
1083
  /** Models-style summary of the active tree filter. */
@@ -287,6 +287,8 @@ export class HistoryEditorCore {
287
287
  delete this.onHistoryChange; };
288
288
  },
289
289
  reset: () => this.resetHistoryBrowsing(),
290
+ restoreDraft: () => { while (this.historyIndex >= 0)
291
+ this.navigateHistory(1); },
290
292
  };
291
293
  persistentHistory = false;
292
294
  styleHistoryLabel;
@@ -211,8 +211,13 @@ export class PiEngineAdapter {
211
211
  #announceReleaseUpdate(release) {
212
212
  if (this.#disposed)
213
213
  return;
214
- const changelog = release.changelogUrl === null ? "" : `\nChangelog: ${release.changelogUrl}`;
215
- this.#addDiagnostic("info", "release-update", `New version ${release.version} is available. Run ${release.command}${changelog}`, true);
214
+ if (release.status === "installed") {
215
+ this.#addDiagnostic("info", "release-update-installed", `Update ${release.version} installed; restart to update.`, true);
216
+ }
217
+ else {
218
+ const changelog = release.changelogUrl === null ? "" : `\nChangelog: ${release.changelogUrl}`;
219
+ this.#addDiagnostic("info", "release-update", `New version ${release.version} is available. Run ${release.command}${changelog}`, true);
220
+ }
216
221
  this.#emitView();
217
222
  }
218
223
  suggestionReasoningPolicy() {
@@ -37,7 +37,7 @@ export interface PiEngineAdapterPorts {
37
37
  export interface EnginePortParts {
38
38
  readonly sessionId: string;
39
39
  readonly agentDir: string;
40
- readonly engine: Pick<PiEngineRuntime, "cwd" | "generation" | "bindingGeneration" | "currentSessionResumeMetadata" | "currentSessionFile">;
40
+ readonly engine: Pick<PiEngineRuntime, "cwd" | "generation" | "bindingGeneration" | "currentSessionResumeMetadata" | "currentSessionFile" | "session">;
41
41
  readonly contexts: PiWorkflowContexts;
42
42
  readonly workflows: PiWorkflowRunner;
43
43
  readonly settings: PiEngineSettings;
@@ -11,6 +11,10 @@ export function createEnginePorts(parts) {
11
11
  get disposed() { return parts.disposed(); },
12
12
  currentSessionResumeMetadata: () => engine.currentSessionResumeMetadata(),
13
13
  currentSessionFile: () => engine.currentSessionFile(),
14
+ currentHistorySessionId: () => {
15
+ const id = engine.session?.sessionManager?.getSessionId?.();
16
+ return typeof id === "string" && id.length > 0 && id.length <= 256 && !id.includes("\0") ? id : null;
17
+ },
14
18
  },
15
19
  session: parts.session,
16
20
  workflows: {
@@ -456,8 +456,8 @@ export class PiWorkflowRunner {
456
456
  : workflowResult(request.command, "failed", "Usage: /name <name>", undefined, "warning");
457
457
  }
458
458
  requireCapability(session.setSessionName, "setSessionName").call(session, argument);
459
- const normalized = manager?.getSessionName();
460
- const actual = typeof normalized === "string" ? normalized : argument;
459
+ this.#ports.emitView();
460
+ const actual = sessionNameOr(manager?.getSessionName(), argument);
461
461
  return workflowResult(request.command, "completed", `Session name set: ${actual}`, actual === argument ? undefined : `Session name was normalized from ${JSON.stringify(argument)} to ${JSON.stringify(actual)}`);
462
462
  }
463
463
  case "session": {
@@ -597,3 +597,10 @@ export class PiWorkflowRunner {
597
597
  }
598
598
  }
599
599
  }
600
+ /**
601
+ * The name the session normalized the request to, or the request when it reports none. `/name`
602
+ * publishes a view right after setting it, so a resident tab takes the name as its tab name.
603
+ */
604
+ function sessionNameOr(normalized, requested) {
605
+ return typeof normalized === "string" ? normalized : requested;
606
+ }
@@ -7,7 +7,7 @@
7
7
  "platform": "darwin",
8
8
  "architecture": "arm64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-23T09:16:51.110Z",
10
+ "builtAt": "2026-10-24T19:42:42.626Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "a87eaaffcd986b624c8b044b756446cc1bdaf4334a1775b97820542fe34f9fb7",
@@ -9,11 +9,11 @@
9
9
  "architecture": "arm64",
10
10
  "target": "aarch64-apple-darwin",
11
11
  "capability": "supported",
12
- "builtAt": "2026-10-23T09:19:57.936Z",
12
+ "builtAt": "2026-10-24T19:46:17.340Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "62c8a0656c7496194cd20649e3f2011f460cb381da2a04fde7ce54cbd38d5a50",
16
- "size": 5748592
15
+ "sha256": "e4de01b7bec86cbced1c52a6d81be5978a0de558a92bbfed9537c5dfc1af4605",
16
+ "size": 5925216
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -7,7 +7,7 @@
7
7
  "platform": "darwin",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-23T09:16:58.302Z",
10
+ "builtAt": "2026-10-24T19:42:48.347Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "783a65f0de287a1c02cd5429dbe97bc8633f4cc8a7b92e3e961180b9bdcff814",
@@ -9,11 +9,11 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-apple-darwin",
11
11
  "capability": "supported",
12
- "builtAt": "2026-10-23T09:22:31.367Z",
12
+ "builtAt": "2026-10-24T19:48:24.843Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "f74aafbd050eaba8737a9e5bd82b2836f86e8b73168310e7c3316af99442e1f2",
16
- "size": 6272408
15
+ "sha256": "a0eb75eb03c62a5bab59db0fd60e43a79b91767dfb0661af0b00a6a8c23cb3c4",
16
+ "size": 6476744
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -7,7 +7,7 @@
7
7
  "platform": "linux",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-23T09:16:44.112Z",
10
+ "builtAt": "2026-10-24T19:42:38.190Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "118e5954e3864672bd8482ab7edf419cb27dcdf2533720316979b7f5cc5a53a5",
@@ -9,11 +9,11 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-unknown-linux-gnu",
11
11
  "capability": "supported",
12
- "builtAt": "2026-10-23T09:18:34.630Z",
12
+ "builtAt": "2026-10-24T19:46:11.090Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "707e4140e1c1b4b2c78c45373d8471bbbcc6232e7ad5d14ad8f5a76a90b6265b",
16
- "size": 6997376
15
+ "sha256": "fd28a219164f83256eba84c61ba55b662a01a9af77292ff8f5106970a5cd0dc4",
16
+ "size": 7210032
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -7,10 +7,10 @@
7
7
  "platform": "win32",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-10-23T09:17:41.474Z",
10
+ "builtAt": "2026-10-24T19:43:33.634Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
- "sha256": "f0fb48d96ea06a21d77a4df332ec863d3830caf8ce6adfebcb2a4bb4d86fabdf",
13
+ "sha256": "eec176bb7bc151266c5097b6f78e3ea95d11780aa13d045c7940f0d397e3d8e6",
14
14
  "size": 402432
15
15
  },
16
16
  "provenance": {