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

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 (166) hide show
  1. package/README.md +7 -1
  2. package/bin/automatic-update.js +7 -0
  3. package/bin/tab-launch-context.d.ts +1 -0
  4. package/bin/tab-launch-context.js +3 -3
  5. package/bin/ui.js +96 -66
  6. package/dist/app/session-shell/resident-tab-coordinator.d.ts +6 -9
  7. package/dist/app/session-shell/resident-tab-coordinator.js +9 -52
  8. package/dist/app/session-shell/session-shell-root.d.ts +14 -8
  9. package/dist/app/session-shell/session-shell-root.js +9 -5
  10. package/dist/app/session-shell/session-shell.js +70 -20
  11. package/dist/app/session-shell/session-viewport-controller.js +6 -4
  12. package/dist/app/session-shell/shell-host.js +6 -3
  13. package/dist/app/session-shell/transcript-document.js +1 -1
  14. package/dist/cli/tabs.d.ts +1 -1
  15. package/dist/cli/tabs.js +10 -14
  16. package/dist/composition/owned-settings.d.ts +12 -0
  17. package/dist/composition/owned-settings.js +36 -0
  18. package/dist/composition/owned-ui.d.ts +5 -14
  19. package/dist/composition/owned-ui.js +93 -64
  20. package/dist/composition/resident-tab-bridge.d.ts +10 -1
  21. package/dist/composition/resident-tab-bridge.js +21 -0
  22. package/dist/composition/settings-route-host.d.ts +12 -2
  23. package/dist/composition/settings-route-host.js +27 -9
  24. package/dist/composition/settings-search-input.d.ts +9 -0
  25. package/dist/composition/settings-search-input.js +58 -0
  26. package/dist/composition/utility-tab.d.ts +28 -0
  27. package/dist/composition/utility-tab.js +242 -0
  28. package/dist/contracts/owned-ui/session-backend.d.ts +2 -0
  29. package/dist/contracts/owned-ui/session-workflows.d.ts +7 -3
  30. package/dist/contracts/presentation/index.d.ts +11 -0
  31. package/dist/contracts/presentation/index.js +12 -0
  32. package/dist/features/launch/development-launch.js +6 -0
  33. package/dist/features/launch/profile-paths.js +4 -0
  34. package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
  35. package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
  36. package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
  37. package/dist/features/owned-ui/project-trust-dialog.js +6 -8
  38. package/dist/features/owned-ui/project-trust-prompt.d.ts +1 -0
  39. package/dist/features/owned-ui/project-trust-prompt.js +7 -3
  40. package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
  41. package/dist/features/owned-ui/reference-screen-app.js +12 -2
  42. package/dist/features/owned-ui/settings-app.d.ts +10 -3
  43. package/dist/features/owned-ui/settings-app.js +98 -45
  44. package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
  45. package/dist/features/owned-ui/settings-search-input.js +1 -0
  46. package/dist/features/prompt-history/index.d.ts +1 -1
  47. package/dist/features/prompt-history/paths.d.ts +2 -1
  48. package/dist/features/prompt-history/paths.js +10 -2
  49. package/dist/features/prompt-history/service.d.ts +5 -0
  50. package/dist/features/prompt-history/service.js +1 -1
  51. package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
  52. package/dist/features/resident-tab/shortcut-declarations.js +2 -0
  53. package/dist/foundation/launch-context/index.d.ts +2 -0
  54. package/dist/foundation/launch-context/index.js +1 -0
  55. package/dist/foundation/lifecycle/session-repository-context.js +16 -2
  56. package/dist/foundation/release/automatic-update.d.ts +37 -0
  57. package/dist/foundation/release/automatic-update.js +265 -0
  58. package/dist/foundation/release/bootstrap.d.ts +1 -9
  59. package/dist/foundation/release/bootstrap.js +7 -3
  60. package/dist/foundation/release/early-accent.d.ts +12 -0
  61. package/dist/foundation/release/early-accent.js +59 -0
  62. package/dist/foundation/release/index.d.ts +1 -1
  63. package/dist/foundation/release/index.js +1 -1
  64. package/dist/foundation/release/latest-release.d.ts +3 -2
  65. package/dist/foundation/release/latest-release.js +1 -1
  66. package/dist/foundation/release/progress-palette.generated.d.ts +137 -3
  67. package/dist/foundation/release/progress-palette.generated.js +137 -3
  68. package/dist/foundation/release/release-gc.js +19 -4
  69. package/dist/foundation/release/update-execution-lease.d.ts +36 -0
  70. package/dist/foundation/release/update-execution-lease.js +145 -0
  71. package/dist/foundation/release/update.d.ts +9 -7
  72. package/dist/foundation/release/update.js +244 -175
  73. package/dist/foundation/resident-tabs/bridge-client.d.ts +10 -3
  74. package/dist/foundation/resident-tabs/bridge-client.js +20 -8
  75. package/dist/foundation/resident-tabs/maintenance.d.ts +2 -6
  76. package/dist/foundation/resident-tabs/maintenance.js +5 -23
  77. package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
  78. package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
  79. package/dist/foundation/resident-tabs/progress-observation.d.ts +3 -6
  80. package/dist/foundation/resident-tabs/progress-observation.js +43 -12
  81. package/dist/foundation/resident-tabs/release-holds.js +3 -14
  82. package/dist/foundation/resident-tabs/routes.d.ts +5 -0
  83. package/dist/foundation/resident-tabs/routes.js +5 -0
  84. package/dist/foundation/resident-tabs/settings.d.ts +0 -1
  85. package/dist/foundation/resident-tabs/settings.js +0 -2
  86. package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
  87. package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
  88. package/dist/integrations/pi/components/modal-frame.js +48 -9
  89. package/dist/integrations/pi/components/models-dialog.js +4 -52
  90. package/dist/integrations/pi/components/owned-editor-ux.d.ts +3 -0
  91. package/dist/integrations/pi/components/owned-editor-ux.js +75 -4
  92. package/dist/integrations/pi/components/shell-editor-autocomplete.js +57 -37
  93. package/dist/integrations/pi/components/shell-footer-status.js +19 -3
  94. package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
  95. package/dist/integrations/pi/components/shell-selectors-dialogs.js +4 -1
  96. package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
  97. package/dist/integrations/pi/components/skills-dialog.js +2 -2
  98. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -5
  99. package/dist/integrations/pi/components/upstream/components/owned-editor.js +15 -8
  100. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -3
  101. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
  102. package/dist/integrations/pi/components/upstream/components/session-footer.js +26 -23
  103. package/dist/integrations/pi/components/upstream/components/session-selector.js +7 -6
  104. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
  105. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +9 -8
  106. package/dist/integrations/pi/components/upstream/components/tool-execution.js +6 -4
  107. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +8 -8
  108. package/dist/integrations/pi/components/upstream/components/tree-selector.js +10 -10
  109. package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -0
  110. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +2 -0
  111. package/dist/integrations/pi/components/upstream/theme/theme.js +5 -1
  112. package/dist/integrations/pi/engine/adapter.js +7 -2
  113. package/dist/integrations/pi/engine/offline-settings-port.d.ts +23 -0
  114. package/dist/integrations/pi/engine/offline-settings-port.js +53 -0
  115. package/dist/integrations/pi/engine/ports.d.ts +1 -1
  116. package/dist/integrations/pi/engine/ports.js +4 -0
  117. package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
  118. package/dist/integrations/pi/engine/settings-bridge.js +13 -0
  119. package/dist/integrations/pi/engine/settings-port.js +1 -14
  120. package/dist/integrations/pi/engine/workflow-runner.js +9 -2
  121. package/dist/native/darwin-arm64/manifest.json +1 -1
  122. package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
  123. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  124. package/dist/native/darwin-x64/manifest.json +1 -1
  125. package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
  126. package/dist/native/darwin-x64/terminal-host.node +0 -0
  127. package/dist/native/linux-x64/manifest.json +1 -1
  128. package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
  129. package/dist/native/linux-x64/terminal-host.node +0 -0
  130. package/dist/native/win32-x64/manifest.json +2 -2
  131. package/dist/native/win32-x64/process-guardian.node +0 -0
  132. package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
  133. package/dist/native/win32-x64/terminal-host.node +0 -0
  134. package/dist/product-identity.d.ts +1 -1
  135. package/dist/product-identity.js +2 -2
  136. package/dist/product-identity.json +2 -0
  137. package/dist/ui/apps/host.d.ts +2 -0
  138. package/dist/ui/apps/host.js +7 -1
  139. package/dist/ui/components/dialog-panel.js +35 -30
  140. package/dist/ui/components/line-input.d.ts +3 -0
  141. package/dist/ui/components/line-input.js +3 -1
  142. package/dist/ui/components/prompt-input.d.ts +5 -4
  143. package/dist/ui/components/prompt-input.js +8 -6
  144. package/dist/ui/components/spans.js +2 -2
  145. package/dist/ui/components/submitted-prompt.d.ts +2 -2
  146. package/dist/ui/components/submitted-prompt.js +3 -3
  147. package/dist/ui/components/surface.js +4 -2
  148. package/dist/ui/components/text.d.ts +3 -6
  149. package/dist/ui/components/text.js +6 -8
  150. package/dist/ui/components/transcript-viewport.d.ts +1 -1
  151. package/dist/ui/components/transcript-viewport.js +141 -24
  152. package/dist/ui/settings/declarations.d.ts +67 -17
  153. package/dist/ui/settings/declarations.js +47 -15
  154. package/dist/ui/settings/early-accent.d.ts +81 -0
  155. package/dist/ui/settings/early-accent.js +50 -0
  156. package/dist/ui/settings/manager.js +7 -3
  157. package/dist/ui/settings/migrations.js +22 -0
  158. package/dist/ui/settings/progress-palette.generated.d.ts +139 -0
  159. package/dist/ui/settings/progress-palette.generated.js +139 -0
  160. package/dist/ui/settings/sections.d.ts +4 -0
  161. package/dist/ui/settings/sections.js +9 -3
  162. package/docs/architecture/resident-tabs.md +52 -21
  163. package/docs/architecture/tool-image-presentation.md +2 -1
  164. package/docs/features/prompt-history.md +41 -20
  165. package/docs/validation.md +25 -5
  166. package/package.json +1 -1
@@ -15,6 +15,19 @@ const IDENTITY_ROW = (row) => row;
15
15
  function documentSelectionAnchor(row, source, liveFromBottom) {
16
16
  return { kind: "document", row, source, identity: stripAnsi(source).trimEnd(), liveFromBottom };
17
17
  }
18
+ function sameSelectionRowAnchor(left, right) {
19
+ if (left.kind !== right.kind)
20
+ return false;
21
+ if (left.kind === "document" && right.kind === "document") {
22
+ return left.row === right.row && left.source === right.source && left.liveFromBottom === right.liveFromBottom;
23
+ }
24
+ if (left.kind === "screen" && right.kind === "screen") {
25
+ return left.row === right.row && left.source === right.source;
26
+ }
27
+ if (left.kind === "transient" && right.kind === "transient")
28
+ return left.fromBottom === right.fromBottom;
29
+ return left.kind === "dock" && right.kind === "dock" && left.fromBottom === right.fromBottom;
30
+ }
18
31
  function viewportSelectionAnchor(row, source, selectableDocumentRowCount, documentRowCount) {
19
32
  return row < selectableDocumentRowCount
20
33
  ? documentSelectionAnchor(row, source)
@@ -140,7 +153,12 @@ export class TranscriptViewport {
140
153
  this.scrollBy(1, now);
141
154
  else if (autoScroll && frameRow <= 1 && this.#scrollTop > 0)
142
155
  this.scrollBy(-1, now);
143
- const line = clamp(frameRow - 1, 0, this.#selectionRows.length - 1);
156
+ const pointerLine = clamp(frameRow - 1, 0, this.#selectionRows.length - 1);
157
+ // Invariant: a pinned prompt alias is chrome; an upward drag reaching it keeps the pointer
158
+ // column on the first visible content row.
159
+ const line = pointerLine < this.#firstSelectableRow && pointerLine < selection.anchor.line
160
+ ? this.#firstSelectableRow
161
+ : pointerLine;
144
162
  const selectionWidth = line < (this.#frame?.hits.viewportHeight ?? 0)
145
163
  ? this.#contentWidth
146
164
  : this.#selectionWidth;
@@ -151,12 +169,13 @@ export class TranscriptViewport {
151
169
  ? { line, column: targetColumn }
152
170
  : textSelectionPointAt(line, targetColumn, this.#selectionRows[line] ?? "");
153
171
  const next = extendTextSelection(selection, point);
154
- if (next !== selection) {
155
- const headAnchor = this.#selectionAnchorAt(line);
156
- if (headAnchor === undefined || this.#selectionAnchors === undefined) {
157
- this.clearSelection();
158
- return true;
159
- }
172
+ const headAnchor = this.#selectionAnchorAt(line);
173
+ if (headAnchor === undefined || this.#selectionAnchors === undefined) {
174
+ this.clearSelection();
175
+ return true;
176
+ }
177
+ // Invariant: edge auto-scroll changes the document row under a still pointer; that advances the head.
178
+ if (next !== selection || !sameSelectionRowAnchor(this.#selectionAnchors.head, headAnchor)) {
160
179
  this.#selection = next;
161
180
  this.#selectionAnchors = { ...this.#selectionAnchors, head: headAnchor };
162
181
  this.#selectionRevision += 1;
@@ -184,25 +203,66 @@ export class TranscriptViewport {
184
203
  return true;
185
204
  }
186
205
  selectedText() {
187
- const selection = this.#visibleSelection();
188
- if (selection === undefined)
206
+ const visible = this.#visibleSelection();
207
+ if (visible === undefined)
189
208
  return null;
190
- const copiedLines = Array.from({ length: Math.max(0, selection.end.line - selection.start.line + 1) }, (_value, index) => selection.start.line + index).filter(line => {
209
+ const offscreen = offscreenSelectionRows(this.#selection, this.#selectionAnchors, this.#scrollTop, this.#firstSelectableRow, this.#viewportHeight, this.#documentRows.length);
210
+ // Rationale: edge auto-scroll grows a drag beyond one screen. Paint stays clipped to the
211
+ // frame, while copy reads the selected document rows past either viewport edge and beneath
212
+ // the floating bottom control.
213
+ const selectionRows = [];
214
+ const rowAnchors = [];
215
+ const controlLine = (this.#frame?.hits.bottom?.row ?? 0) - 1;
216
+ const pushDocumentRow = (row) => {
217
+ const source = this.#documentRows[row] ?? "";
218
+ selectionRows.push(truncateToWidth(source, this.#contentWidth));
219
+ rowAnchors.push(documentSelectionAnchor(row, source));
220
+ };
221
+ const pushBelow = () => {
222
+ for (let row = offscreen.below?.from ?? 0; row <= (offscreen.below?.to ?? -1); row += 1)
223
+ pushDocumentRow(row);
224
+ };
225
+ for (let row = offscreen.above?.from ?? 0; row <= (offscreen.above?.to ?? -1); row += 1)
226
+ pushDocumentRow(row);
227
+ if (offscreen.belowFirst)
228
+ pushBelow();
229
+ for (let line = visible.start.line; line <= visible.end.line; line += 1) {
191
230
  const anchor = this.#selectionRowAnchors[line];
231
+ if (line === controlLine && anchor?.kind === "document")
232
+ pushDocumentRow(anchor.row);
233
+ else {
234
+ selectionRows.push(this.#selectionRows[line] ?? "");
235
+ rowAnchors.push(anchor);
236
+ }
237
+ }
238
+ if (!offscreen.belowFirst)
239
+ pushBelow();
240
+ const offscreenStart = offscreen.above !== undefined || offscreen.belowFirst === true;
241
+ const offscreenEnd = offscreen.below !== undefined && !offscreen.belowFirst;
242
+ const selection = {
243
+ ...visible,
244
+ start: { line: 0, column: offscreenStart ? offscreen.startColumn ?? 0 : visible.start.column },
245
+ end: {
246
+ line: selectionRows.length - 1,
247
+ column: offscreenEnd ? offscreen.endColumn ?? Number.MAX_SAFE_INTEGER : visible.end.column,
248
+ },
249
+ };
250
+ const copiedLines = Array.from({ length: Math.max(0, selection.end.line - selection.start.line + 1) }, (_value, index) => selection.start.line + index).filter(line => {
251
+ const anchor = rowAnchors[line];
192
252
  return anchor?.kind === "document" && anchor.row < this.#selectableDocumentRowCount
193
253
  || anchor?.kind === "dock" && this.#copyableDockRows.has(anchor.fromBottom);
194
254
  });
195
255
  if (copiedLines.length === 0)
196
256
  return null;
197
- const transcriptLines = copiedLines.filter(line => this.#selectionRowAnchors[line]?.kind === "document");
257
+ const transcriptLines = copiedLines.filter(line => rowAnchors[line]?.kind === "document");
198
258
  const prompt = transcriptLines.length === 0 ? undefined : this.#promptAnchors.find(candidate => transcriptLines.every(line => {
199
- const anchor = this.#selectionRowAnchors[line];
259
+ const anchor = rowAnchors[line];
200
260
  return anchor?.kind === "document" && anchor.row >= candidate.firstRow && anchor.row <= candidate.lastRow;
201
261
  }));
202
262
  const included = new Set(copiedLines);
203
- const text = textSelectionText(selection, this.#selectionRows, line => {
204
- const row = this.#selectionRows[line] ?? "";
205
- const anchor = this.#selectionRowAnchors[line];
263
+ const text = textSelectionText(selection, selectionRows, line => {
264
+ const row = selectionRows[line] ?? "";
265
+ const anchor = rowAnchors[line];
206
266
  const dock = anchor?.kind === "dock" ? this.#copyableDockRows.get(anchor.fromBottom) : undefined;
207
267
  if (prompt !== undefined && anchor?.kind === "document") {
208
268
  const first = anchor.row === prompt.firstRow;
@@ -211,11 +271,12 @@ export class TranscriptViewport {
211
271
  : row;
212
272
  return selectionCopyLineContent(copyRow, first ? "first" : "continuation");
213
273
  }
214
- return dock?.prompt !== undefined ? selectionCopyLineContent(row, dock.prompt) : usefulTextLineContent(row);
274
+ // Invariant: the dock editor has no prefix; transcript prompts still carry chrome.
275
+ return dock?.prompt !== undefined ? usefulTextLineContent(row, 1) : usefulTextLineContent(row);
215
276
  }, line => included.has(line));
216
277
  return text.length === 0 ? null : text;
217
278
  }
218
- /** Captures visible selected text, not off-screen history. */
279
+ /** Captures selected text, including document rows of a visible selection scrolled off-screen. */
219
280
  captureSelectedText() {
220
281
  if (!this.#copyableSelection)
221
282
  return null;
@@ -470,7 +531,7 @@ export class TranscriptViewport {
470
531
  const bottomHovered = pointer !== undefined && pointer.row === bottomHit.row
471
532
  && pointer.column >= bottomHit.columnStart && pointer.column <= bottomHit.columnEnd;
472
533
  control = { row, left, text: `${CONTROL_STYLE_RESET}${theme.bottomControl(label, bottomHovered)}` };
473
- // Invariant: the control floats above selection; its cells are never selected or copied.
534
+ // Invariant: the control floats above selection; copy restores the text beneath it.
474
535
  selectionRows[row] = overlaySpan(padRowPreservingBackground(selectionRows[row] ?? "", width), left, left + labelWidth, " ".repeat(labelWidth));
475
536
  }
476
537
  }
@@ -701,7 +762,7 @@ export class TranscriptViewport {
701
762
  const anchors = this.#selectionAnchors;
702
763
  if (selection === undefined || anchors === undefined)
703
764
  return;
704
- const project = (anchor) => {
765
+ const project = (anchor, moving) => {
705
766
  if (anchor.kind === "dock") {
706
767
  const dockIndex = dockRows.length - anchor.fromBottom - 1;
707
768
  return dockIndex < 0 || dockIndex >= dockRows.length
@@ -743,7 +804,7 @@ export class TranscriptViewport {
743
804
  // Concurrency: a streaming chunk may replace or reflow the pointer's current live-tail
744
805
  // row before the next motion report. During the held gesture only, retain its distance
745
806
  // from the persistent tail; release restores identity-only projection.
746
- if (selection.selecting && anchor.liveFromBottom !== undefined && this.#liveTailStartRow !== undefined) {
807
+ if (selection.selecting && moving && anchor.liveFromBottom !== undefined && this.#liveTailStartRow !== undefined) {
747
808
  const documentRow = this.#selectableDocumentRowCount - anchor.liveFromBottom - 1;
748
809
  if (documentRow >= this.#liveTailStartRow && documentRow < this.#selectableDocumentRowCount) {
749
810
  const visible = this.#selectionRowAnchors.findIndex(candidate => candidate?.kind === "document" && candidate.row === documentRow);
@@ -755,10 +816,18 @@ export class TranscriptViewport {
755
816
  }
756
817
  }
757
818
  }
819
+ // Concurrency: streaming may restyle a held endpoint's row; it keeps its document index.
820
+ // Following the tail would drag the press row toward the newest output.
821
+ if (selection.selecting && anchor.row >= 0 && anchor.row < documentRows.length) {
822
+ return {
823
+ line: anchor.row - this.#scrollTop,
824
+ anchor: documentSelectionAnchor(anchor.row, documentRows[anchor.row] ?? "", anchor.liveFromBottom),
825
+ };
826
+ }
758
827
  return undefined;
759
828
  };
760
- const projectedAnchor = project(anchors.anchor);
761
- const projectedHead = project(anchors.head);
829
+ const projectedAnchor = project(anchors.anchor, false);
830
+ const projectedHead = project(anchors.head, true);
762
831
  if (projectedAnchor === undefined || projectedHead === undefined) {
763
832
  this.clearSelection();
764
833
  return;
@@ -778,11 +847,59 @@ export class TranscriptViewport {
778
847
  // Invariant: mixed viewport/dock ranges project across the same complete frame regardless
779
848
  // of origin. Document-only ranges remain clipped to the scrollable viewport when off-screen.
780
849
  const rows = crossesDock ? this.#selectionRows.length : this.#viewportHeight;
781
- return visibleTextSelection(orderedTextSelection(this.#selection), rows, this.#firstSelectableRow);
850
+ const ordered = orderedTextSelection(dockOrderedSelection(this.#selection, anchors, this.#viewportHeight));
851
+ return visibleTextSelection(ordered, rows, this.#firstSelectableRow);
782
852
  }
783
853
  #updateCopyableSelection() {
784
- this.#copyableSelection = this.selectedText() !== null;
854
+ // Performance: off-screen rows count as copyable by range alone; capture trims whitespace later.
855
+ const offscreen = offscreenSelectionRows(this.#selection, this.#selectionAnchors, this.#scrollTop, this.#firstSelectableRow, this.#viewportHeight, this.#documentRows.length);
856
+ const selectable = [offscreen.above, offscreen.below]
857
+ .some(range => range !== undefined && range.from < this.#selectableDocumentRowCount);
858
+ this.#copyableSelection = selectable && this.#visibleSelection() !== undefined || this.selectedText() !== null;
859
+ }
860
+ }
861
+ /**
862
+ * Invariant: dock rows follow the whole transcript, so a document endpoint scrolled below the
863
+ * viewport orders before every dock row instead of at its numeric frame line among them.
864
+ */
865
+ function dockOrderedSelection(selection, anchors, viewportHeight) {
866
+ if (selection === undefined || anchors === undefined || anchors.anchor.kind !== "dock" && anchors.head.kind !== "dock") {
867
+ return selection;
785
868
  }
869
+ const place = (point, kind) => kind === "document" && point.line >= viewportHeight ? { line: viewportHeight, column: 1, from: 0, to: 0 } : point;
870
+ return { ...selection, anchor: place(selection.anchor, anchors.anchor.kind), head: place(selection.head, anchors.head.kind) };
871
+ }
872
+ /** Document rows of a selection whose endpoints lie beyond the visible viewport edges. */
873
+ function offscreenSelectionRows(selection, anchors, scrollTop, firstSelectableRow, viewportHeight, documentRowCount) {
874
+ const ordered = orderedTextSelection(dockOrderedSelection(selection, anchors, viewportHeight));
875
+ if (selection === undefined || ordered === undefined || anchors === undefined)
876
+ return {};
877
+ const placed = dockOrderedSelection(selection, anchors, viewportHeight);
878
+ const anchorFirst = placed.anchor.line < placed.head.line
879
+ || placed.anchor.line === placed.head.line && placed.anchor.column <= placed.head.column;
880
+ const top = anchorFirst ? anchors.anchor : anchors.head;
881
+ const bottom = anchorFirst ? anchors.head : anchors.anchor;
882
+ const topPoint = anchorFirst ? selection.anchor : selection.head;
883
+ const bottomPoint = anchorFirst ? selection.head : selection.anchor;
884
+ const firstVisibleRow = scrollTop + firstSelectableRow;
885
+ const pastVisibleRow = scrollTop + viewportHeight;
886
+ const columns = {
887
+ startColumn: selection.fullRow ? 0 : topPoint.from ?? Math.max(0, topPoint.column - 1),
888
+ endColumn: selection.fullRow ? Number.MAX_SAFE_INTEGER : bottomPoint.to ?? bottomPoint.column,
889
+ };
890
+ if (top.kind === "document" && bottom.kind === "dock" && top.row >= pastVisibleRow) {
891
+ return { ...columns, below: { from: top.row, to: documentRowCount - 1 }, belowFirst: true };
892
+ }
893
+ return {
894
+ ...columns,
895
+ ...(top.kind === "document" && top.row < firstVisibleRow && ordered.start.line < firstSelectableRow
896
+ ? { above: { from: top.row, to: firstVisibleRow - 1 } }
897
+ : {}),
898
+ ...(top.kind === "document" && bottom.kind === "document" && bottom.row >= pastVisibleRow
899
+ && ordered.end.line >= viewportHeight
900
+ ? { below: { from: pastVisibleRow, to: bottom.row } }
901
+ : {}),
902
+ };
786
903
  }
787
904
  function visibleTextSelection(selection, rowCount, firstRow) {
788
905
  if (selection === undefined || rowCount <= firstRow || selection.end.line < firstRow || selection.start.line >= rowCount)
@@ -1,11 +1,9 @@
1
- export declare const OWNED_UI_SETTINGS_VERSION = 17;
1
+ export declare const OWNED_UI_SETTINGS_VERSION = 19;
2
2
  export type OwnedUiSettingValue = string | number | boolean;
3
3
  export type OwnedUiSettingApplication = "live" | "restart";
4
4
  export interface OwnedUiSettingDeclaration {
5
5
  readonly id: string;
6
- /** Optional presentation label; the id remains the persistence key. */
7
6
  readonly label?: string;
8
- /** Optional settings-screen group for related owned controls. */
9
7
  readonly section?: {
10
8
  readonly id: string;
11
9
  readonly title: string;
@@ -14,23 +12,28 @@ export interface OwnedUiSettingDeclaration {
14
12
  readonly application: OwnedUiSettingApplication;
15
13
  readonly defaultValue: OwnedUiSettingValue;
16
14
  readonly allowedValues: readonly OwnedUiSettingValue[];
15
+ /**
16
+ * Parts of a structured setting, edited through their own dialog: the value is the comma-separated
17
+ * keys of the enabled parts in this order.
18
+ */
19
+ readonly flags?: readonly {
20
+ readonly key: string;
21
+ readonly label: string;
22
+ readonly description: string;
23
+ }[];
17
24
  }
18
- /**
19
- * Every A1 setting, keyed by its id. The table is the one declaration: ids, defaults, and allowed
20
- * values are read from it, and `OwnedSettingValueOf` derives each getter's type from it.
21
- */
22
25
  export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
23
26
  readonly updateCheck: Readonly<{
24
27
  id: "updateCheck";
25
- label: "Update check";
28
+ label: "Update";
26
29
  section: Readonly<{
27
30
  id: "generic";
28
31
  title: "Generic";
29
32
  }>;
30
- description: "Check once a day at startup whether a newer A1 release exists on this channel and show a notice. Uses the public npm registry; applies on next start.";
33
+ description: "Auto installs verified channel updates silently, Notify shows the current update notice, and No leaves updates manual. Checks use the public npm registry; applies on next start.";
31
34
  application: "restart";
32
- defaultValue: true;
33
- allowedValues: readonly [true, false];
35
+ defaultValue: "auto";
36
+ allowedValues: readonly ["auto", "notify", "no"];
34
37
  }>;
35
38
  readonly timeFormat: Readonly<{
36
39
  id: "timeFormat";
@@ -53,8 +56,8 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
53
56
  }>;
54
57
  description: "Color used by highlighted interface controls, list markers, and tonal interface surfaces.";
55
58
  application: "live";
56
- defaultValue: "purple";
57
- allowedValues: readonly ["purple", "blue", "cyan", "green", "orange", "pink"];
59
+ defaultValue: "blue";
60
+ allowedValues: readonly ["blue", "purple", "cyan", "green", "orange", "pink"];
58
61
  }>;
59
62
  readonly backgroundStyle: Readonly<{
60
63
  id: "backgroundStyle";
@@ -123,11 +126,23 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
123
126
  id: "history";
124
127
  title: "History";
125
128
  }>;
126
- description: "Retain reusable prompts across sessions. Applies on next start; disabling does not erase saved history or stop existing instances.";
129
+ description: "Retain reusable prompts after restart. Applies on next start; disabling does not erase saved history or stop existing instances.";
127
130
  application: "restart";
128
131
  defaultValue: true;
129
132
  allowedValues: readonly [true, false];
130
133
  }>;
134
+ readonly promptHistoryScope: Readonly<{
135
+ id: "promptHistoryScope";
136
+ label: "History scope";
137
+ section: Readonly<{
138
+ id: "history";
139
+ title: "History";
140
+ }>;
141
+ description: "Unique shows each agent session's history; Shared shows the profile-wide history. Switches running agents without moving or erasing saved prompts.";
142
+ application: "live";
143
+ defaultValue: "unique";
144
+ allowedValues: readonly ["unique", "shared"];
145
+ }>;
131
146
  readonly promptHistoryMaxItems: Readonly<{
132
147
  id: "promptHistoryMaxItems";
133
148
  label: "History limit";
@@ -176,12 +191,49 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
176
191
  defaultValue: 8;
177
192
  allowedValues: readonly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
178
193
  }>;
194
+ readonly tabsPosition: Readonly<{
195
+ id: "tabsPosition";
196
+ label: "Position";
197
+ section: Readonly<{
198
+ id: "tabs";
199
+ title: "Tab Bar";
200
+ }>;
201
+ description: "Place the resident tab bar at the top or bottom of the terminal.";
202
+ application: "live";
203
+ defaultValue: "top";
204
+ allowedValues: readonly ["top", "bottom"];
205
+ }>;
206
+ readonly tabBarPlusMenu: Readonly<{
207
+ id: "tabBarPlusMenu";
208
+ label: "Plus menu";
209
+ section: Readonly<{
210
+ id: "tabs";
211
+ title: "Tab Bar";
212
+ }>;
213
+ description: "The tabs + offers. With only Agent, + adds an Agent at once; with more, it opens a menu; with none, + is hidden.";
214
+ application: "live";
215
+ defaultValue: "agent";
216
+ allowedValues: readonly ["", "agent", "settings", "changelog", "agent,settings", "agent,changelog", "settings,changelog", "agent,settings,changelog"];
217
+ flags: readonly (Readonly<{
218
+ key: "agent";
219
+ label: "Agent";
220
+ description: "Add a new Agent tab every time.";
221
+ }> | Readonly<{
222
+ key: "settings";
223
+ label: "Settings";
224
+ description: "Show the one Settings tab.";
225
+ }> | Readonly<{
226
+ key: "changelog";
227
+ label: "Changelog";
228
+ description: "Show the one Changelog tab.";
229
+ }>)[];
230
+ }>;
179
231
  readonly tabsAutoName: Readonly<{
180
232
  id: "tabsAutoName";
181
233
  label: "Auto name";
182
234
  section: Readonly<{
183
235
  id: "tabs";
184
- title: "Tabs";
236
+ title: "Tab Bar";
185
237
  }>;
186
238
  description: "Automatically name a new tab from its first completed turn with the tab's current model.";
187
239
  application: "live";
@@ -190,9 +242,7 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
190
242
  }>;
191
243
  }>;
192
244
  export type OwnedSettingId = keyof typeof OWNED_SETTING_DECLARATIONS;
193
- /** The value type a setting's declaration allows: a literal union for a choice, `number` for a stepped range. */
194
245
  export type OwnedSettingValueOf<Id extends OwnedSettingId> = (typeof OWNED_SETTING_DECLARATIONS)[Id]["allowedValues"][number];
195
- /** The declarations in declaration order, for resolution, persistence, and the settings screen. */
196
246
  export declare const OWNED_UI_SETTING_DECLARATIONS: readonly OwnedUiSettingDeclaration[];
197
247
  export declare function isOwnedSettingId(id: string): id is OwnedSettingId;
198
248
  export declare function assertOwnedUiSettingDeclarations(declarations: readonly OwnedUiSettingDeclaration[]): void;
@@ -1,28 +1,33 @@
1
- export const OWNED_UI_SETTINGS_VERSION = 17;
1
+ export const OWNED_UI_SETTINGS_VERSION = 19;
2
2
  const UI_ACCENT_COLOR_CHOICES = Object.freeze([
3
- "purple", "blue", "cyan", "green", "orange", "pink",
3
+ "blue", "purple", "cyan", "green", "orange", "pink",
4
4
  ]);
5
5
  const MAX_ID_LENGTH = 64;
6
6
  const ID_PATTERN = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
7
- /** Declared first so the settings screen opens on it; sections follow first-declaration order. */
8
7
  const GENERIC_SECTION = Object.freeze({ id: "generic", title: "Generic" });
9
8
  const APPEARANCE_SECTION = Object.freeze({ id: "appearance", title: "Appearance" });
10
9
  const SCROLL_SECTION = Object.freeze({ id: "scroll", title: "Scroll" });
11
10
  const AGENT_SECTION = Object.freeze({ id: "agent", title: "Agent" });
12
- const TABS_SECTION = Object.freeze({ id: "tabs", title: "Tabs" });
13
- /**
14
- * Every A1 setting, keyed by its id. The table is the one declaration: ids, defaults, and allowed
15
- * values are read from it, and `OwnedSettingValueOf` derives each getter's type from it.
16
- */
11
+ const TABS_SECTION = Object.freeze({ id: "tabs", title: "Tab Bar" });
12
+ /** The tab kinds the strip's `+` can offer, in the order it shows them. */
13
+ const PLUS_MENU_KINDS = Object.freeze([
14
+ Object.freeze({ key: "agent", label: "Agent", description: "Add a new Agent tab every time." }),
15
+ Object.freeze({ key: "settings", label: "Settings", description: "Show the one Settings tab." }),
16
+ Object.freeze({ key: "changelog", label: "Changelog", description: "Show the one Changelog tab." }),
17
+ ]);
18
+ /** Every enabled subset of the plus-menu kinds, each in kind order. */
19
+ const PLUS_MENU_VALUES = Object.freeze([
20
+ "", "agent", "settings", "changelog", "agent,settings", "agent,changelog", "settings,changelog", "agent,settings,changelog",
21
+ ]);
17
22
  export const OWNED_SETTING_DECLARATIONS = Object.freeze({
18
23
  updateCheck: Object.freeze({
19
24
  id: "updateCheck",
20
- label: "Update check",
25
+ label: "Update",
21
26
  section: GENERIC_SECTION,
22
- description: "Check once a day at startup whether a newer A1 release exists on this channel and show a notice. Uses the public npm registry; applies on next start.",
27
+ description: "Auto installs verified channel updates silently, Notify shows the current update notice, and No leaves updates manual. Checks use the public npm registry; applies on next start.",
23
28
  application: "restart",
24
- defaultValue: true,
25
- allowedValues: Object.freeze([true, false]),
29
+ defaultValue: "auto",
30
+ allowedValues: Object.freeze(["auto", "notify", "no"]),
26
31
  }),
27
32
  timeFormat: Object.freeze({
28
33
  id: "timeFormat",
@@ -41,7 +46,7 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
41
46
  section: APPEARANCE_SECTION,
42
47
  description: "Color used by highlighted interface controls, list markers, and tonal interface surfaces.",
43
48
  application: "live",
44
- defaultValue: "purple",
49
+ defaultValue: "blue",
45
50
  allowedValues: UI_ACCENT_COLOR_CHOICES,
46
51
  }),
47
52
  backgroundStyle: Object.freeze({
@@ -95,11 +100,20 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
95
100
  id: "promptHistoryEnabled",
96
101
  label: "Persistent history",
97
102
  section: Object.freeze({ id: "history", title: "History" }),
98
- description: "Retain reusable prompts across sessions. Applies on next start; disabling does not erase saved history or stop existing instances.",
103
+ description: "Retain reusable prompts after restart. Applies on next start; disabling does not erase saved history or stop existing instances.",
99
104
  application: "restart",
100
105
  defaultValue: true,
101
106
  allowedValues: Object.freeze([true, false]),
102
107
  }),
108
+ promptHistoryScope: Object.freeze({
109
+ id: "promptHistoryScope",
110
+ label: "History scope",
111
+ section: Object.freeze({ id: "history", title: "History" }),
112
+ description: "Unique shows each agent session's history; Shared shows the profile-wide history. Switches running agents without moving or erasing saved prompts.",
113
+ application: "live",
114
+ defaultValue: "unique",
115
+ allowedValues: Object.freeze(["unique", "shared"]),
116
+ }),
103
117
  promptHistoryMaxItems: Object.freeze({
104
118
  id: "promptHistoryMaxItems",
105
119
  label: "History limit",
@@ -136,6 +150,25 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
136
150
  defaultValue: 8,
137
151
  allowedValues: Object.freeze([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
138
152
  }),
153
+ tabsPosition: Object.freeze({
154
+ id: "tabsPosition",
155
+ label: "Position",
156
+ section: TABS_SECTION,
157
+ description: "Place the resident tab bar at the top or bottom of the terminal.",
158
+ application: "live",
159
+ defaultValue: "top",
160
+ allowedValues: Object.freeze(["top", "bottom"]),
161
+ }),
162
+ tabBarPlusMenu: Object.freeze({
163
+ id: "tabBarPlusMenu",
164
+ label: "Plus menu",
165
+ section: TABS_SECTION,
166
+ description: "The tabs + offers. With only Agent, + adds an Agent at once; with more, it opens a menu; with none, + is hidden.",
167
+ application: "live",
168
+ defaultValue: "agent",
169
+ allowedValues: PLUS_MENU_VALUES,
170
+ flags: PLUS_MENU_KINDS,
171
+ }),
139
172
  tabsAutoName: Object.freeze({
140
173
  id: "tabsAutoName",
141
174
  label: "Auto name",
@@ -146,7 +179,6 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
146
179
  allowedValues: Object.freeze([true, false]),
147
180
  }),
148
181
  });
149
- /** The declarations in declaration order, for resolution, persistence, and the settings screen. */
150
182
  export const OWNED_UI_SETTING_DECLARATIONS = Object.freeze(Object.values(OWNED_SETTING_DECLARATIONS));
151
183
  export function isOwnedSettingId(id) {
152
184
  return Object.hasOwn(OWNED_SETTING_DECLARATIONS, id);
@@ -0,0 +1,81 @@
1
+ import type { UiAccentColor } from "../../contracts/owned-ui/index.js";
2
+ type Appearance = "dark" | "light";
3
+ type ColorMode = "truecolor" | "256color";
4
+ /** Global settings only: project resources must remain unavailable until trust is resolved. */
5
+ export declare function readPreTrustAccent(environment?: NodeJS.ProcessEnv): UiAccentColor;
6
+ export declare function preTrustPalette(color: UiAccentColor, appearance: Appearance, mode: ColorMode): {
7
+ accent: string;
8
+ border: string;
9
+ } | {
10
+ accent: string;
11
+ border: string;
12
+ } | {
13
+ accent: string;
14
+ border: string;
15
+ } | {
16
+ accent: string;
17
+ border: string;
18
+ } | {
19
+ accent: string;
20
+ border: string;
21
+ } | {
22
+ accent: string;
23
+ border: string;
24
+ } | {
25
+ accent: string;
26
+ border: string;
27
+ } | {
28
+ accent: string;
29
+ border: string;
30
+ } | {
31
+ accent: string;
32
+ border: string;
33
+ } | {
34
+ accent: string;
35
+ border: string;
36
+ } | {
37
+ accent: string;
38
+ border: string;
39
+ } | {
40
+ accent: string;
41
+ border: string;
42
+ } | {
43
+ accent: string;
44
+ border: string;
45
+ } | {
46
+ accent: string;
47
+ border: string;
48
+ } | {
49
+ accent: string;
50
+ border: string;
51
+ } | {
52
+ accent: string;
53
+ border: string;
54
+ } | {
55
+ accent: string;
56
+ border: string;
57
+ } | {
58
+ accent: string;
59
+ border: string;
60
+ } | {
61
+ accent: string;
62
+ border: string;
63
+ } | {
64
+ accent: string;
65
+ border: string;
66
+ } | {
67
+ accent: string;
68
+ border: string;
69
+ } | {
70
+ accent: string;
71
+ border: string;
72
+ } | {
73
+ accent: string;
74
+ border: string;
75
+ } | {
76
+ accent: string;
77
+ border: string;
78
+ };
79
+ export declare function preTrustTerminalAppearance(environment?: NodeJS.ProcessEnv): Appearance;
80
+ export declare function preTrustTerminalColorMode(environment?: NodeJS.ProcessEnv): ColorMode;
81
+ export {};
@@ -0,0 +1,50 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join } from "node:path";
4
+ import { PRODUCT_IDENTITY } from "../../product-identity.js";
5
+ import { SHARED_PROGRESS_PALETTE } from "./progress-palette.generated.js";
6
+ /** Global settings only: project resources must remain unavailable until trust is resolved. */
7
+ export function readPreTrustAccent(environment = process.env) {
8
+ try {
9
+ const home = environment[PRODUCT_IDENTITY.environment.profileHome] ?? homedir();
10
+ if (!isAbsolute(home) || home.includes("\0"))
11
+ return "blue";
12
+ const file = join(home, PRODUCT_IDENTITY.state.piAgentProfile, "settings.json");
13
+ if (statSync(file).size > 256 * 1024)
14
+ return "blue";
15
+ const raw = readFileSync(file, "utf8");
16
+ if (Buffer.byteLength(raw, "utf8") > 256 * 1024)
17
+ return "blue";
18
+ const root = JSON.parse(raw.replace(/^\uFEFF/u, ""));
19
+ if (root === null || typeof root !== "object" || Array.isArray(root))
20
+ return "blue";
21
+ const namespace = root[PRODUCT_IDENTITY.commandName];
22
+ if (namespace === null || typeof namespace !== "object" || Array.isArray(namespace))
23
+ return "blue";
24
+ const value = namespace.accentColor;
25
+ if (value === "default")
26
+ return "purple";
27
+ return typeof value === "string" && Object.hasOwn(SHARED_PROGRESS_PALETTE, value) ? value : "blue";
28
+ }
29
+ catch {
30
+ return "blue";
31
+ }
32
+ }
33
+ export function preTrustPalette(color, appearance, mode) {
34
+ return SHARED_PROGRESS_PALETTE[color][appearance][mode];
35
+ }
36
+ export function preTrustTerminalAppearance(environment = process.env) {
37
+ const index = Number.parseInt((environment.COLORFGBG ?? "").split(";").at(-1) ?? "", 10);
38
+ return Number.isInteger(index) && (index === 7 || index === 15 || index >= 231) ? "light" : "dark";
39
+ }
40
+ export function preTrustTerminalColorMode(environment = process.env) {
41
+ if (environment.PI_TRUE_COLOR === "1")
42
+ return "truecolor";
43
+ if (environment.PI_TRUE_COLOR === "0")
44
+ return "256color";
45
+ const program = (environment.TERM_PROGRAM ?? "").toLowerCase();
46
+ const known = ["kitty", "ghostty", "wezterm", "rex", "warpterminal", "iterm.app", "alacritty", "vscode", "zed"];
47
+ return /^(truecolor|24bit)$/iu.test(environment.COLORTERM ?? "") || (environment.TERM ?? "").endsWith("-direct")
48
+ || process.platform === "win32" || environment.WT_SESSION !== undefined || known.includes(program)
49
+ ? "truecolor" : "256color";
50
+ }
@@ -188,9 +188,13 @@ export class OwnedSettingsManager {
188
188
  return () => this.#listeners.delete(listener);
189
189
  }
190
190
  async changeStructured(backend, id, value) {
191
- if (backend !== "agent")
192
- return failed("only an agent setting holds a structured value");
193
- return await this.#changeAgentValue(id, value);
191
+ if (backend === "agent")
192
+ return await this.#changeAgentValue(id, value);
193
+ // Invariant: a flagged A1 setting stores its enabled parts as one value, in declaration order.
194
+ const flags = findOwnedUiSettingsEntry(this.sections(), id, "a1")?.flags ?? [];
195
+ if (flags.length === 0)
196
+ return failed(`${id} holds no structured value`);
197
+ return await this.change("a1", id, flags.filter(flag => value[flag.key] === true).map(flag => flag.key).join(","));
194
198
  }
195
199
  async change(backend, id, value) {
196
200
  const entry = findOwnedUiSettingsEntry(this.sections(), id, backend);