@timurproko/a1 0.1.8-dev.563 → 0.1.8-dev.572

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 (34) hide show
  1. package/README.md +13 -11
  2. package/dist/app/session-shell/session-shell-root.js +6 -3
  3. package/dist/app/session-shell/session-shell.js +7 -1
  4. package/dist/app/session-shell/session-viewport-controller.js +48 -30
  5. package/dist/cli/dispatch.d.ts +3 -1
  6. package/dist/cli/dispatch.js +56 -56
  7. package/dist/cli/package-messages.d.ts +4 -3
  8. package/dist/cli/package-messages.js +23 -11
  9. package/dist/integrations/pi/components/shell-footer-status.d.ts +4 -3
  10. package/dist/integrations/pi/components/shell-footer-status.js +53 -15
  11. package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -1
  12. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
  13. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +3 -2
  14. package/dist/integrations/pi/engine/compaction-progress.d.ts +1 -1
  15. package/dist/integrations/pi/engine/compaction-progress.js +1 -0
  16. package/dist/integrations/pi/engine/repository-pr.d.ts +3 -3
  17. package/dist/integrations/pi/engine/repository-pr.js +6 -6
  18. package/dist/integrations/pi/engine/session-runtime.js +5 -4
  19. package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -0
  20. package/dist/integrations/pi/tui-runtime/adapter.js +5 -0
  21. package/dist/native/darwin-arm64/manifest.json +1 -1
  22. package/dist/native/linux-x64/manifest.json +1 -1
  23. package/dist/native/win32-x64/manifest.json +3 -3
  24. package/dist/native/win32-x64/process-guardian.exe +0 -0
  25. package/dist/ui/components/index.d.ts +2 -1
  26. package/dist/ui/components/index.js +1 -1
  27. package/dist/ui/components/progress-status.d.ts +9 -2
  28. package/dist/ui/components/progress-status.js +24 -3
  29. package/dist/ui/components/transcript-viewport.d.ts +9 -3
  30. package/dist/ui/components/transcript-viewport.js +90 -132
  31. package/docs/architecture/ui-reference-provenance.md +8 -5
  32. package/docs/openspec-archive-automation.md +2 -2
  33. package/docs/validation.md +1 -1
  34. package/package.json +1 -1
@@ -1,7 +1,8 @@
1
1
  import type { OwnedUiSessionViewModel } from "../../../contracts/owned-ui/index.js";
2
- import { type PiShellViewComponentPort, type PiShellStatusPort, type PiShellQueuedInputPort, type PiShellHeaderPort, type PiShellResourceEntry, type PiShellLoadedResourcesPort, type PiShellHeaderOptions, type PiShellEditorOptions } from "./shell-shared-facade.js";
2
+ import { type KeybindingsConfig } from "./upstream/adjacent/core/keybindings.js";
3
+ import { type PiShellViewComponentPort, type PiShellStatusPort, type PiShellProgressStatusPresentation, type PiShellQueuedInputPort, type PiShellHeaderPort, type PiShellResourceEntry, type PiShellLoadedResourcesPort, type PiShellHeaderOptions, type PiShellEditorOptions } from "./shell-shared-facade.js";
3
4
  export declare function createPiShellHeader(options?: PiShellHeaderOptions): PiShellHeaderPort;
4
5
  export declare function createPiShellLoadedResources(resources: readonly PiShellResourceEntry[], initialExpanded?: boolean): PiShellLoadedResourcesPort;
5
- export declare function createPiShellStatus(view: OwnedUiSessionViewModel, formatProgressStatus: (message: string) => string, runtime?: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">): PiShellStatusPort;
6
+ export declare function createPiShellStatus(view: OwnedUiSessionViewModel, progressStatus: PiShellProgressStatusPresentation, runtime?: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">): PiShellStatusPort;
6
7
  export declare function createPiShellFooter(view: OwnedUiSessionViewModel, cwd: string, profile?: "pi" | "a1", showLevel?: () => boolean): PiShellViewComponentPort;
7
- export declare function createPiQueuedInputStatus(submissions: readonly string[], presentation?: "pinned" | "custom-viewport"): PiShellQueuedInputPort;
8
+ export declare function createPiQueuedInputStatus(submissions: readonly string[], presentation?: "pinned" | "custom-viewport", getKeybindings?: () => KeybindingsConfig): PiShellQueuedInputPort;
@@ -2,7 +2,7 @@ import { rawKeyHint, VERSION } from "../startup-public.js";
2
2
  import { Spacer, Text, } from "@earendil-works/pi-tui";
3
3
  import { SessionFooter } from "./upstream/components/session-footer.js";
4
4
  import { KeybindingsManager } from "./upstream/adjacent/core/keybindings.js";
5
- import { WorkingStatusIndicator } from "./upstream/components/status-indicator.js";
5
+ import { StatusIndicator, WorkingStatusIndicator } from "./upstream/components/status-indicator.js";
6
6
  import { PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
7
7
  import { createTuiFacade, ensureTheme, } from "./shell-shared-facade.js";
8
8
  export function createPiShellHeader(options = {}) {
@@ -70,15 +70,15 @@ export function createPiShellLoadedResources(resources, initialExpanded = false)
70
70
  invalidate() { },
71
71
  };
72
72
  }
73
- export function createPiShellStatus(view, formatProgressStatus, runtime) {
73
+ export function createPiShellStatus(view, progressStatus, runtime) {
74
74
  ensureTheme();
75
75
  const statusUi = createTuiFacade(runtime ?? { getColumns: () => 80, getRows: () => 24, requestRender() { } });
76
76
  let workingOverride;
77
77
  let outputPad = PINNED_PI_LAYOUT.outputPad;
78
78
  let progressPresentation = "pinned";
79
79
  let placement = statusPlacement(view, workingOverride);
80
- const liveStatusText = () => formatProgressStatus(liveWorkingText(view, workingOverride, progressPresentation));
81
- let component = statusComponent(view, statusUi, outputPad, liveStatusText, placement);
80
+ const liveStatusText = () => progressStatus.text(liveWorkingText(view, workingOverride, progressPresentation), progressPresentation);
81
+ let component = statusComponent(view, statusUi, outputPad, liveStatusText, placement, progressPresentation, progressStatus);
82
82
  let signature = statusSignature(view, workingOverride, outputPad, placement, progressPresentation);
83
83
  const rebuild = () => {
84
84
  const nextPlacement = statusPlacement(view, workingOverride);
@@ -86,7 +86,10 @@ export function createPiShellStatus(view, formatProgressStatus, runtime) {
86
86
  if (nextSignature === signature)
87
87
  return;
88
88
  // Performance: progress ticks change only the live message; the spinner keeps its frame and timer.
89
- if (placement === "live" && nextPlacement === "live" && component instanceof WorkingStatusIndicator
89
+ const componentMatchesPresentation = progressPresentation === "custom-viewport"
90
+ ? component instanceof OwnedWorkingStatusIndicator
91
+ : component instanceof WorkingStatusIndicator;
92
+ if (placement === "live" && nextPlacement === "live" && component instanceof StatusIndicator && componentMatchesPresentation
90
93
  && statusSignature(view, workingOverride, outputPad, nextPlacement, progressPresentation, false)
91
94
  === statusSignature(view, workingOverride, outputPad, placement, progressPresentation, false)) {
92
95
  component.setMessage(liveStatusText());
@@ -97,7 +100,7 @@ export function createPiShellStatus(view, formatProgressStatus, runtime) {
97
100
  component.dispose();
98
101
  placement = nextPlacement;
99
102
  signature = nextSignature;
100
- component = statusComponent(view, statusUi, outputPad, liveStatusText, placement);
103
+ component = statusComponent(view, statusUi, outputPad, liveStatusText, placement, progressPresentation, progressStatus);
101
104
  };
102
105
  return {
103
106
  render: width => component?.render(width) ?? [],
@@ -138,14 +141,27 @@ export function createPiShellFooter(view, cwd, profile = "pi", showLevel = () =>
138
141
  dispose: () => footer.dispose(),
139
142
  };
140
143
  }
141
- export function createPiQueuedInputStatus(submissions, presentation = "pinned") {
142
- const text = new Text(queuedInputText(submissions, presentation), 1, 0);
144
+ export function createPiQueuedInputStatus(submissions, presentation = "pinned", getKeybindings) {
145
+ let renderedText = queuedInputText(submissions, presentation, getKeybindings?.());
146
+ const text = new Text(renderedText, 1, 0);
147
+ const refresh = () => {
148
+ const next = queuedInputText(submissions, presentation, getKeybindings?.());
149
+ if (next === renderedText)
150
+ return;
151
+ renderedText = next;
152
+ text.setText(next);
153
+ };
143
154
  return {
144
- render: width => submissions.length === 0 ? [] : text.render(width),
155
+ render(width) {
156
+ if (submissions.length === 0)
157
+ return [];
158
+ refresh();
159
+ return text.render(width);
160
+ },
145
161
  invalidate: () => text.invalidate(),
146
162
  update(next) {
147
163
  submissions = next;
148
- text.setText(queuedInputText(next, presentation));
164
+ refresh();
149
165
  },
150
166
  };
151
167
  }
@@ -168,9 +184,23 @@ function liveWorkingText(view, workingOverride, progressPresentation) {
168
184
  ? `${message}(${progress}%)`
169
185
  : message;
170
186
  }
171
- function statusComponent(view, ui, outputPad, liveStatusText, placement) {
187
+ class OwnedWorkingStatusIndicator extends StatusIndicator {
188
+ constructor(ui, message, frame) {
189
+ let phase = -1;
190
+ super("working", ui, spinner => {
191
+ phase += 1;
192
+ return piTheme().fg("accent", spinner);
193
+ }, text => frame(text, phase, {
194
+ muted: value => piTheme().fg("muted", value),
195
+ accent: value => piTheme().fg("accent", value),
196
+ }), message);
197
+ }
198
+ }
199
+ function statusComponent(view, ui, outputPad, liveStatusText, placement, progressPresentation, progressStatus) {
172
200
  if (placement === "live") {
173
- return new WorkingStatusIndicator(ui, liveStatusText());
201
+ return progressPresentation === "custom-viewport"
202
+ ? new OwnedWorkingStatusIndicator(ui, liveStatusText(), progressStatus.frame)
203
+ : new WorkingStatusIndicator(ui, liveStatusText());
174
204
  }
175
205
  if (placement === "dock") {
176
206
  if (view.lifecycle === "failed") {
@@ -184,7 +214,7 @@ function statusSignature(view, workingOverride, outputPad, placement, progressPr
184
214
  const message = withMessage ? `${workingOverride ?? ""}\u0000${view.status.workingMessage ?? ""}\u0000${view.status.workingProgress ?? ""}` : "";
185
215
  return `${placement}\u0000${outputPad}\u0000${view.lifecycle}\u0000${progressPresentation}\u0000${message}\u0000${view.status.diagnostics.at(-1) ?? ""}`;
186
216
  }
187
- function queuedInputText(submissions, presentation) {
217
+ function queuedInputText(submissions, presentation, bindings) {
188
218
  if (submissions.length === 0)
189
219
  return "";
190
220
  const theme = piTheme();
@@ -192,11 +222,19 @@ function queuedInputText(submissions, presentation) {
192
222
  return submissions.map(submission => theme.fg("muted", `Steering: ${submission.replaceAll("\n", " ⏎ ")}`)).join("\n");
193
223
  }
194
224
  const messages = submissions.map(submission => theme.fg("dim", `Steering: ${submission.replaceAll("\n", " ⏎ ")}`));
195
- const dequeueHint = theme.fg("dim", "↳ Alt+Up to edit all queued messages");
225
+ const keys = KeybindingsManager.fromOwnedBindings(bindings).getKeys("app.message.dequeue");
226
+ const label = keys.length === 0 ? "Unbound" : keys.map(displayKeybinding).join("/");
227
+ const dequeueHint = theme.fg("dim", `↳ ${label} to edit all queued messages`);
196
228
  // Compatibility: the custom viewport matches Pi's interactive queue presentation while the
197
229
  // comparison shell remains byte-for-byte compatible with its pinned fixture.
198
230
  return ["", ...messages, dequeueHint].join("\n");
199
231
  }
232
+ function displayKeybinding(key) {
233
+ return key.split("+").map(part => {
234
+ const display = process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part;
235
+ return display.charAt(0).toUpperCase() + display.slice(1);
236
+ }).join("+");
237
+ }
200
238
  function compactHeaderText() {
201
239
  const theme = piTheme();
202
240
  const instructions = [
@@ -231,7 +269,7 @@ function expandedHeaderText(bindings) {
231
269
  rawKeyHint("!", "to run bash"),
232
270
  rawKeyHint("!!", "to run bash (no context)"),
233
271
  rawKeyHint("alt+enter", "to queue follow-up"),
234
- rawKeyHint("alt+up", "to edit all queued messages"),
272
+ rawKeyHint(keys === undefined ? "alt+up" : keys.getKeys("app.message.dequeue").join("/") || "unbound", "to edit all queued messages"),
235
273
  rawKeyHint(process.platform === "win32" ? "alt+v" : "ctrl+v", "to paste image (with text fallback)"),
236
274
  rawKeyHint("drop files", "to attach"),
237
275
  ].join("\n");
@@ -80,11 +80,19 @@ export interface PiShellViewComponentPort extends PiShellComponentPort {
80
80
  update(view: OwnedUiSessionViewModel): void;
81
81
  }
82
82
  export type PiShellStatusPlacement = "live" | "dock" | "hidden";
83
+ export type PiShellProgressPresentationMode = "pinned" | "custom-viewport";
84
+ export interface PiShellProgressStatusPresentation {
85
+ readonly text: (message: string, mode: PiShellProgressPresentationMode) => string;
86
+ readonly frame: (message: string, phase: number, styles: {
87
+ readonly muted: (text: string) => string;
88
+ readonly accent: (text: string) => string;
89
+ }) => string;
90
+ }
83
91
  export interface PiShellStatusPort extends PiShellViewComponentPort {
84
92
  setWorkingOverride(message: string | undefined): void;
85
93
  setOutputPad(padding: 0 | 1): void;
86
94
  /** Bare A1 shows engine-measured progress beside the working word; the pinned route keeps the bare word. */
87
- setProgressPresentation(presentation: "pinned" | "custom-viewport"): void;
95
+ setProgressPresentation(presentation: PiShellProgressPresentationMode): void;
88
96
  /** Semantic row placement; callers must not inspect rendered text. */
89
97
  placement(): PiShellStatusPlacement;
90
98
  renderDock(width: number): readonly string[];
@@ -2,8 +2,8 @@
2
2
  * Provenance: @earendil-works/pi-coding-agent 0.87.1 (MIT), commit f07218c4d4bbc12bef056a7058c3dd49dfe41abe,
3
3
  * packages/coding-agent/src/core/keybindings.ts.
4
4
  * Modifications: Mechanical source port with Node import prefixes, public package-root agent-directory
5
- * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling and unbound model
6
- * selection.
5
+ * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, unbound model
6
+ * selection, and cross-platform Alt+Up queued-message restoration.
7
7
  * Deviations: keybindings-public-config-boundary, owned-level-cycle-shortcut,
8
8
  * owned-input-keybinding-aliases.
9
9
  */
@@ -2,8 +2,8 @@
2
2
  * Provenance: @earendil-works/pi-coding-agent 0.87.1 (MIT), commit f07218c4d4bbc12bef056a7058c3dd49dfe41abe,
3
3
  * packages/coding-agent/src/core/keybindings.ts.
4
4
  * Modifications: Mechanical source port with Node import prefixes, public package-root agent-directory
5
- * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling and unbound model
6
- * selection.
5
+ * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, unbound model
6
+ * selection, and cross-platform Alt+Up queued-message restoration.
7
7
  * Deviations: keybindings-public-config-boundary, owned-level-cycle-shortcut,
8
8
  * owned-input-keybinding-aliases.
9
9
  */
@@ -184,6 +184,7 @@ const OWNED_INPUT_KEYBINDINGS = {
184
184
  ...KEYBINDINGS,
185
185
  "app.thinking.cycle": { ...KEYBINDINGS["app.thinking.cycle"], defaultKeys: "ctrl+l" },
186
186
  "app.model.select": { ...KEYBINDINGS["app.model.select"], defaultKeys: [] },
187
+ "app.message.dequeue": { ...KEYBINDINGS["app.message.dequeue"], defaultKeys: "alt+up" },
187
188
  "tui.editor.deleteWordBackward": {
188
189
  ...KEYBINDINGS["tui.editor.deleteWordBackward"],
189
190
  defaultKeys: [...KEYBINDINGS["tui.editor.deleteWordBackward"].defaultKeys, "ctrl+backspace"],
@@ -10,7 +10,7 @@ export interface CompactionProgressSession {
10
10
  } | undefined;
11
11
  }
12
12
  export interface CompactionProgressObserver {
13
- /** Marks `compaction_start`: resets the streamed count and captures the expected summary size. */
13
+ /** Marks `compaction_start`: resets the estimate and immediately reports zero percent. */
14
14
  begin(): void;
15
15
  /** Marks `compaction_end`: later stream chunks are ignored. */
16
16
  end(): void;
@@ -60,6 +60,7 @@ export function observeCompactionProgress(session, onProgress) {
60
60
  reported = null;
61
61
  expected = latestCompactionSummaryLength(session) ?? DEFAULT_EXPECTED_COMPACTION_SUMMARY_CHARS;
62
62
  active = true;
63
+ report();
63
64
  },
64
65
  end() {
65
66
  active = false;
@@ -3,6 +3,6 @@ export interface PiPullRequestIdentity {
3
3
  readonly url: string;
4
4
  }
5
5
  export type PiPullRequestProbe = (cwd: string, branch: string, signal: AbortSignal) => Promise<PiPullRequestIdentity | null>;
6
- /** Read the open GitHub pull request associated with one exact local branch. */
7
- export declare function readOpenPullRequest(cwd: string, branch: string, signal: AbortSignal, environment?: NodeJS.ProcessEnv): Promise<PiPullRequestIdentity | null>;
8
- export declare function parseOpenPullRequest(stdout: string, branch: string): PiPullRequestIdentity | null;
6
+ /** Read an exact branch's open or merged PR. */
7
+ export declare function readPullRequest(cwd: string, branch: string, signal: AbortSignal, environment?: NodeJS.ProcessEnv): Promise<PiPullRequestIdentity | null>;
8
+ export declare function parsePullRequest(stdout: string, branch: string): PiPullRequestIdentity | null;
@@ -2,19 +2,19 @@ import { execFile } from "node:child_process";
2
2
  import { PRODUCT_IDENTITY } from "../../../product-identity.js";
3
3
  const GH_TIMEOUT_MS = 5_000;
4
4
  const GH_MAX_BUFFER_BYTES = 64 * 1024;
5
- /** Read the open GitHub pull request associated with one exact local branch. */
6
- export async function readOpenPullRequest(cwd, branch, signal, environment = process.env) {
5
+ /** Read an exact branch's open or merged PR. */
6
+ export async function readPullRequest(cwd, branch, signal, environment = process.env) {
7
7
  if (branch.length === 0 || signal.aborted)
8
8
  return null;
9
9
  const preview = environment[PRODUCT_IDENTITY.environment.prFooterPreview];
10
10
  if (preview !== undefined)
11
- return parseOpenPullRequest(preview, branch);
11
+ return parsePullRequest(preview, branch);
12
12
  const stdout = await executeGh(cwd, signal);
13
13
  if (stdout === null)
14
14
  return null;
15
- return parseOpenPullRequest(stdout, branch);
15
+ return parsePullRequest(stdout, branch);
16
16
  }
17
- export function parseOpenPullRequest(stdout, branch) {
17
+ export function parsePullRequest(stdout, branch) {
18
18
  let value;
19
19
  try {
20
20
  value = JSON.parse(stdout);
@@ -25,7 +25,7 @@ export function parseOpenPullRequest(stdout, branch) {
25
25
  if (!isRecord(value)
26
26
  || !Number.isSafeInteger(value.number)
27
27
  || value.number <= 0
28
- || value.state !== "OPEN"
28
+ || (value.state !== "OPEN" && value.state !== "MERGED")
29
29
  || value.headRefName !== branch
30
30
  || typeof value.url !== "string")
31
31
  return null;
@@ -6,7 +6,7 @@ import { observeCompactionProgress } from "./compaction-progress.js";
6
6
  import { createPiRuntimeIntegration } from "./runtime-integration.js";
7
7
  import { PiSessionCommandIntegration } from "./session-integration.js";
8
8
  import { DefaultPackageManager, VERSION } from "../startup-public.js";
9
- import { readOpenPullRequest, } from "./repository-pr.js";
9
+ import { readPullRequest, } from "./repository-pr.js";
10
10
  const execFileAsync = promisify(execFile);
11
11
  const PULL_REQUEST_REFRESH_MS = 60_000;
12
12
  const REPOSITORY_CONTEXT_POLL_MS = 5_000;
@@ -56,7 +56,7 @@ export class PiEngineRuntime {
56
56
  ?? (options.createRuntime
57
57
  ? async () => []
58
58
  : settingsManager => checkDefaultPiPackageUpdates(this.#cwd, options.agentDir, settingsManager));
59
- this.#pullRequestProbe = options.pullRequestProbe ?? readOpenPullRequest;
59
+ this.#pullRequestProbe = options.pullRequestProbe ?? readPullRequest;
60
60
  this.#pullRequestRefreshMs = options.pullRequestRefreshMs ?? PULL_REQUEST_REFRESH_MS;
61
61
  this.#repositoryContextPollMs = options.repositoryContextPollMs ?? REPOSITORY_CONTEXT_POLL_MS;
62
62
  this.#repositoryContextReader = options.repositoryContextReader ?? (async () => null);
@@ -393,6 +393,8 @@ function samePullRequest(left, right) {
393
393
  return left?.number === right?.number && left?.url === right?.url;
394
394
  }
395
395
  async function readGitBranch(cwd, signal) {
396
+ if (!existsSync(cwd))
397
+ return null;
396
398
  try {
397
399
  const { stdout } = await execFileAsync("git", ["branch", "--show-current"], {
398
400
  cwd,
@@ -402,8 +404,7 @@ async function readGitBranch(cwd, signal) {
402
404
  signal,
403
405
  encoding: "utf8",
404
406
  });
405
- const branch = stdout.trim();
406
- return branch.length > 0 ? branch : null;
407
+ return stdout.trim() || null;
407
408
  }
408
409
  catch {
409
410
  return null;
@@ -47,6 +47,8 @@ export declare class PiTuiRuntimeAdapter {
47
47
  getClearOnShrink(): boolean;
48
48
  setTerminalProgress(active: boolean): void;
49
49
  setTitle(title: string): void;
50
+ /** Shows a bounded fullscreen acknowledgement without changing root layout geometry. */
51
+ showFlash(message: string, durationMs?: number): void;
50
52
  writeAfterStop(text: string): void;
51
53
  invalidate(): void;
52
54
  requestRender(force?: boolean): void;
@@ -392,6 +392,11 @@ export class PiTuiRuntimeAdapter {
392
392
  setTitle(title) {
393
393
  this.#terminal.setTitle(title);
394
394
  }
395
+ /** Shows a bounded fullscreen acknowledgement without changing root layout geometry. */
396
+ showFlash(message, durationMs) {
397
+ this.#assertRunning("flash");
398
+ this.#requireFullscreen().flash(message, durationMs);
399
+ }
395
400
  writeAfterStop(text) {
396
401
  if (this.#state !== "stopped")
397
402
  throw new Error("terminal output requires a stopped Pi TUI runtime");
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-09-23T16:42:04.513Z",
8
+ "builtAt": "2026-09-23T23:03:13.628Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "9db1726bbe3fc2e8292f2ead1e7e9d0fd4d7d0dc9217b372582bf354b0f565dc",
@@ -5,7 +5,7 @@
5
5
  "platform": "linux",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-09-23T16:42:11.967Z",
8
+ "builtAt": "2026-09-23T23:03:11.352Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "d8cda6b0c7cb36c0cc41e90802aceebf6c02eaebbc08a83d7d963d2e918dbd7a",
@@ -5,11 +5,11 @@
5
5
  "platform": "win32",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-09-23T16:43:16.752Z",
8
+ "builtAt": "2026-09-23T23:03:54.826Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "051b57f8b1980f1cf2accf7d9f102941e5fcf74b4264f9375d924581e950b0d6",
12
- "size": 177664
11
+ "sha256": "6c21b828ef2eb284a783609ad28dee4744f81cf0d53a4de1e38bddba4da08079",
12
+ "size": 178176
13
13
  },
14
14
  "provenance": {
15
15
  "language": "Rust",
@@ -15,7 +15,8 @@ export { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput, routeMouseInput
15
15
  export type { ParsedMouseInput, RoutedMouseInput } from "./mouse.js";
16
16
  export { isInsidePane, toPaneLocalMouse } from "./pane.js";
17
17
  export type { Pane, PaneInputResult, PaneMouseEvent } from "./pane.js";
18
- export { progressStatusText } from "./progress-status.js";
18
+ export { progressStatusFrame, progressStatusText } from "./progress-status.js";
19
+ export type { ProgressStatusFrameStyles, ProgressStatusMarker } from "./progress-status.js";
19
20
  export { FrameCache, RENDER_REVISION_KINDS, RenderRevisionTracker, ZERO_REVISIONS, normalizeRevisions, revisionsEqual } from "./revision.js";
20
21
  export type { RenderCacheContract, RenderRevisionKind, RenderRevisions } from "./revision.js";
21
22
  export { MAX_COPY_ROWS, MAX_COPY_SOURCE_UNITS, selectionCopyLineContent, selectionCopyRowText } from "./selection-copy.js";
@@ -7,7 +7,7 @@ export { blockJumpTarget, blockRowSpan, clampScroll, indexOfKey, layoutList, max
7
7
  export { STEPPER_RESERVE, placementFor, regionAt, renderGroupHeader, renderListRow, renderNote, valueColumnFor } from "./list-view.js";
8
8
  export { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput, routeMouseInput } from "./mouse.js";
9
9
  export { isInsidePane, toPaneLocalMouse } from "./pane.js";
10
- export { progressStatusText } from "./progress-status.js";
10
+ export { progressStatusFrame, progressStatusText } from "./progress-status.js";
11
11
  export { FrameCache, RENDER_REVISION_KINDS, RenderRevisionTracker, ZERO_REVISIONS, normalizeRevisions, revisionsEqual } from "./revision.js";
12
12
  export { MAX_COPY_ROWS, MAX_COPY_SOURCE_UNITS, selectionCopyLineContent, selectionCopyRowText } from "./selection-copy.js";
13
13
  export { ScrollbarRails, isThumbRow, scrollForThumbRow, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, scrollbarReservesSpace, scrollbarSelectionRows, scrollbarWheelRows, } from "./scrollbar.js";
@@ -1,2 +1,9 @@
1
- /** Returns spinner-backed status text with A1's canonical three-period marker. */
2
- export declare function progressStatusText(message: string): string;
1
+ export type ProgressStatusMarker = "…" | "...";
2
+ export interface ProgressStatusFrameStyles {
3
+ readonly muted: (text: string) => string;
4
+ readonly accent: (text: string) => string;
5
+ }
6
+ /** Returns spinner-backed status text with the selected terminal progress marker. */
7
+ export declare function progressStatusText(message: string, marker?: ProgressStatusMarker): string;
8
+ /** Paints one width-stable, grapheme-safe frame of the quiet progress-label sweep. */
9
+ export declare function progressStatusFrame(message: string, phase: number, styles: ProgressStatusFrameStyles): string;
@@ -1,4 +1,25 @@
1
- /** Returns spinner-backed status text with A1's canonical three-period marker. */
2
- export function progressStatusText(message) {
3
- return `${message.replace(/(?:…|\.+)$/u, "")}...`;
1
+ const GRAPHEMES = new Intl.Segmenter(undefined, { granularity: "grapheme" });
2
+ const HIGHLIGHT_WIDTH = 2;
3
+ const UPDATES_PER_STEP = 3;
4
+ /** Returns spinner-backed status text with the selected terminal progress marker. */
5
+ export function progressStatusText(message, marker = "…") {
6
+ return `${message.replace(/(?:…|\.+)$/u, "")}${marker}`;
7
+ }
8
+ /** Paints one width-stable, grapheme-safe frame of the quiet progress-label sweep. */
9
+ export function progressStatusFrame(message, phase, styles) {
10
+ const marker = message.endsWith("…") ? "…" : "";
11
+ const label = marker === "" ? message : message.slice(0, -marker.length);
12
+ const graphemes = [...GRAPHEMES.segment(label)].map(segment => segment.segment);
13
+ if (graphemes.length === 0)
14
+ return paint(marker, styles.muted);
15
+ const step = Math.floor(Math.max(0, phase) / UPDATES_PER_STEP) % (graphemes.length * 2);
16
+ if (step >= graphemes.length)
17
+ return paint(`${label}${marker}`, styles.muted);
18
+ const before = graphemes.slice(0, step).join("");
19
+ const highlight = graphemes.slice(step, step + HIGHLIGHT_WIDTH).join("");
20
+ const after = `${graphemes.slice(step + HIGHLIGHT_WIDTH).join("")}${marker}`;
21
+ return `${paint(before, styles.muted)}${paint(highlight, styles.accent)}${paint(after, styles.muted)}`;
22
+ }
23
+ function paint(text, style) {
24
+ return text === "" ? "" : style(text);
4
25
  }
@@ -50,6 +50,12 @@ export interface TranscriptViewportFrameInput {
50
50
  readonly column: number;
51
51
  readonly row: number;
52
52
  };
53
+ /** Base-frame cells hidden by modal/replacement surfaces and therefore excluded from visual copy. */
54
+ readonly coveredCells?: readonly {
55
+ readonly row: number;
56
+ readonly columnStart: number;
57
+ readonly columnEnd: number;
58
+ }[];
53
59
  readonly now?: number;
54
60
  readonly theme?: TranscriptViewportTheme;
55
61
  }
@@ -150,12 +156,12 @@ export declare class TranscriptViewport {
150
156
  setStickyHovered(hovered: boolean): void;
151
157
  get selectionActive(): boolean;
152
158
  get hasSelection(): boolean;
153
- pressSelection(column: number, viewportRow: number, now?: number): boolean;
154
- extendSelection(column: number, viewportRow: number, now?: number, autoScroll?: boolean): boolean;
159
+ pressSelection(column: number, frameRow: number, now?: number): boolean;
160
+ extendSelection(column: number, frameRow: number, now?: number, autoScroll?: boolean): boolean;
155
161
  releaseSelection(): boolean;
156
162
  clearSelection(): boolean;
157
163
  selectedText(): string | null;
158
- /** Captures selected source references, never the entire mutable transcript or prompt graph. */
164
+ /** Captures only the selected visible frame text, never off-screen transcript history. */
159
165
  captureSelectedText(): SelectionCopySnapshot | null;
160
166
  scrollBy(lines: number, now?: number): boolean;
161
167
  scrollTo(position: number, now?: number): boolean;