@timurproko/a1 0.2.5 → 0.2.6-dev.727

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 (148) hide show
  1. package/bin/cli.js +13 -2
  2. package/bin/ui.js +13 -0
  3. package/dist/app/session-shell/prompt-suggestion-controller.d.ts +5 -4
  4. package/dist/app/session-shell/prompt-suggestion-controller.js +148 -58
  5. package/dist/app/session-shell/session-shell-root.d.ts +5 -1
  6. package/dist/app/session-shell/session-shell-root.js +13 -6
  7. package/dist/app/session-shell/session-shell.js +132 -51
  8. package/dist/app/session-shell/session-viewport-controller.js +33 -21
  9. package/dist/cli/dispatch.js +5 -3
  10. package/dist/cli/session-context.d.ts +5 -1
  11. package/dist/cli/session-context.js +25 -8
  12. package/dist/composition/owned-ui.d.ts +3 -0
  13. package/dist/composition/owned-ui.js +66 -6
  14. package/dist/composition/settings-route-host.js +4 -3
  15. package/dist/contracts/owned-ui/index.d.ts +4 -2
  16. package/dist/contracts/owned-ui/index.js +1 -1
  17. package/dist/contracts/owned-ui/model.d.ts +4 -0
  18. package/dist/contracts/owned-ui/suggestion-diagnostics.d.ts +5 -1
  19. package/dist/contracts/owned-ui/suggestion-diagnostics.js +3 -1
  20. package/dist/contracts/owned-ui/validation.js +7 -0
  21. package/dist/features/launch/index.d.ts +1 -1
  22. package/dist/features/launch/session-context.d.ts +17 -2
  23. package/dist/features/launch/session-context.js +9 -5
  24. package/dist/features/owned-ui/reference-routes.d.ts +1 -1
  25. package/dist/features/owned-ui/reference-routes.js +1 -1
  26. package/dist/features/owned-ui/settings-app.js +21 -39
  27. package/dist/features/prompt-suggestions/diagnostics.js +4 -1
  28. package/dist/foundation/lifecycle/index.d.ts +2 -2
  29. package/dist/foundation/lifecycle/index.js +1 -1
  30. package/dist/foundation/lifecycle/session-repository-context.d.ts +19 -2
  31. package/dist/foundation/lifecycle/session-repository-context.js +434 -66
  32. package/dist/foundation/process-containment/index.d.ts +1 -0
  33. package/dist/foundation/process-containment/index.js +1 -0
  34. package/dist/foundation/process-containment/native-process-inspector.d.ts +3 -0
  35. package/dist/foundation/process-containment/native-process-inspector.js +21 -0
  36. package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
  37. package/dist/foundation/release/progress-palette.generated.js +2 -2
  38. package/dist/foundation/startup/startup-descriptor.js +2 -2
  39. package/dist/integrations/pi/components/components.js +2 -2
  40. package/dist/integrations/pi/components/index.d.ts +2 -2
  41. package/dist/integrations/pi/components/index.js +1 -1
  42. package/dist/integrations/pi/components/lazy-selectors.d.ts +42 -0
  43. package/dist/integrations/pi/components/lazy-selectors.js +32 -0
  44. package/dist/integrations/pi/components/modal-frame.js +3 -4
  45. package/dist/integrations/pi/components/models-dialog.js +5 -5
  46. package/dist/integrations/pi/components/prompt-input-port.d.ts +1 -0
  47. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
  48. package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
  49. package/dist/integrations/pi/components/share-operation-dialog.js +3 -3
  50. package/dist/integrations/pi/components/shell-editor-autocomplete.js +8 -7
  51. package/dist/integrations/pi/components/shell-extension-ui.d.ts +2 -0
  52. package/dist/integrations/pi/components/shell-extension-ui.js +20 -10
  53. package/dist/integrations/pi/components/shell-hotkey-sections.js +2 -2
  54. package/dist/integrations/pi/components/shell-presenters-info.js +10 -10
  55. package/dist/integrations/pi/components/shell-presenters-transcript.js +10 -10
  56. package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -6
  57. package/dist/integrations/pi/components/shell-shared-facade.js +1 -1
  58. package/dist/integrations/pi/components/skills-dialog.js +3 -3
  59. package/dist/integrations/pi/components/submitted-prompt-adapter.js +2 -3
  60. package/dist/integrations/pi/components/theme.d.ts +9 -0
  61. package/dist/integrations/pi/components/theme.js +30 -0
  62. package/dist/integrations/pi/components/tool-result-adapter.js +6 -1
  63. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  64. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +1 -1
  65. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  66. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  67. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  68. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  69. package/dist/integrations/pi/components/upstream/components/extension-editor.js +6 -6
  70. package/dist/integrations/pi/components/upstream/components/extension-input.js +4 -4
  71. package/dist/integrations/pi/components/upstream/components/extension-selector.js +4 -4
  72. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  73. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +5 -5
  74. package/dist/integrations/pi/components/upstream/components/owned-editor.js +8 -5
  75. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  76. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  77. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +3 -3
  78. package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
  79. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  80. package/dist/integrations/pi/components/upstream/components/session-selector.js +14 -20
  81. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +3 -2
  82. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +6 -3
  83. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
  84. package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
  85. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  86. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  87. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  88. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +2 -2
  89. package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +4 -0
  90. package/dist/integrations/pi/components/upstream/components/tool-execution.js +11 -1
  91. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +2 -2
  92. package/dist/integrations/pi/components/upstream/components/tree-selector.js +27 -18
  93. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  94. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  95. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +4 -4
  96. package/dist/integrations/pi/components/upstream/history/editor-core.js +11 -7
  97. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  98. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  99. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  100. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  101. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  102. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  103. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  104. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  105. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +31 -0
  106. package/dist/integrations/pi/components/upstream/program-status-reporter.js +89 -0
  107. package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +1 -1
  108. package/dist/integrations/pi/components/upstream/theme/system-theme.js +1 -1
  109. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  110. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +18 -1
  111. package/dist/integrations/pi/components/upstream/theme/theme.js +207 -10
  112. package/dist/integrations/pi/engine/pi-settings-metadata.json +1 -1
  113. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  114. package/dist/integrations/pi/engine/session-events.js +10 -2
  115. package/dist/integrations/pi/engine/transcript-projection.js +2 -1
  116. package/dist/integrations/pi/startup-public.js +306 -290
  117. package/dist/integrations/pi/startup-public.manifest.json +289 -265
  118. package/dist/integrations/pi/tui-runtime/adapter.d.ts +8 -1
  119. package/dist/integrations/pi/tui-runtime/adapter.js +31 -1
  120. package/dist/integrations/pi/tui-runtime/contracts.d.ts +3 -1
  121. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  122. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  123. package/dist/native/darwin-arm64/manifest.json +1 -1
  124. package/dist/native/linux-x64/manifest.json +1 -1
  125. package/dist/native/win32-x64/manifest.json +2 -2
  126. package/dist/native/win32-x64/process-guardian.exe +0 -0
  127. package/dist/product-identity.d.ts +2 -2
  128. package/dist/product-identity.js +2 -2
  129. package/dist/product-identity.json +3 -1
  130. package/dist/runtime-payload-inventory.json +3092 -2878
  131. package/dist/ui/components/prompt-input.d.ts +2 -0
  132. package/dist/ui/components/prompt-input.js +5 -1
  133. package/dist/ui/components/text-selection.d.ts +1 -1
  134. package/dist/ui/components/text-selection.js +3 -1
  135. package/dist/ui/components/theme.d.ts +2 -0
  136. package/dist/ui/components/transcript-viewport.d.ts +6 -4
  137. package/dist/ui/components/transcript-viewport.js +94 -16
  138. package/dist/ui/components/value-menu.d.ts +2 -0
  139. package/dist/ui/components/value-menu.js +13 -8
  140. package/dist/ui/settings/declarations.d.ts +26 -14
  141. package/dist/ui/settings/declarations.js +24 -11
  142. package/dist/ui/settings/migrations.js +16 -0
  143. package/docs/architecture/project-structure.md +4 -3
  144. package/docs/architecture/prompt-suggestions.md +14 -10
  145. package/docs/local-worktree-cleanup.md +23 -9
  146. package/docs/manual-owned-ui-checkpoint.md +1 -1
  147. package/docs/openspec-archive-automation.md +20 -19
  148. package/package.json +3 -3
@@ -11,10 +11,11 @@ import { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput } from "../../ui
11
11
  import { readVisibleHyperlinks } from "../../ui/components/visible-hyperlinks.js";
12
12
  import { PINNED_PI_HIDDEN_COMMAND_NAMES, workflowCommandNames } from "../../integrations/pi/engine/workflows.js";
13
13
  import { createPiExtensionUiBridge } from "../../integrations/pi/components/shell-extension-ui.js";
14
+ import { ProgramStatusReporter } from "../../integrations/pi/components/upstream/program-status-reporter.js";
14
15
  import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
15
16
  import { SKILLS_COMMAND_NAME, findSkillByArgument, rewriteSkillsTunnelSubmission, skillPrompt, skillsFromCommands, } from "../../integrations/pi/components/skills-command.js";
16
17
  import { createPiQueuedInputStatus, createPiShellFooter, createPiShellHeader, createPiShellLoadedResources, createPiShellStatus, } from "../../integrations/pi/components/shell-footer-status.js";
17
- import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellSkillsSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
18
+ import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellSkillsSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
18
19
  import { createPiShellChangelog, createPiShellHotkeys, createPiShellSessionInfo, renderPiShellPackageUpdateNotice, renderPiShellStatusText, } from "../../integrations/pi/components/shell-presenters-info.js";
19
20
  import { createPiShellTranscriptComponent, renderPiShellStartupDiagnostic, renderPiShellTranscriptBlock, } from "../../integrations/pi/components/shell-presenters-transcript.js";
20
21
  import { onPiThemeChange, piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
@@ -62,6 +63,8 @@ export class OwnedUiSessionShell {
62
63
  // Invariant: translated startup diagnostics create at most one dock notice per kind per shell.
63
64
  #startupTrustHandled = false;
64
65
  #customViewport;
66
+ #lazySelectors;
67
+ #lazySelectorsPromise;
65
68
  #responseCopy;
66
69
  // Rationale: only an executor this shell created owns a spare copy helper worth warming and disposing.
67
70
  #copyExecutor;
@@ -77,6 +80,7 @@ export class OwnedUiSessionShell {
77
80
  #unbindTerminalSettings;
78
81
  #unbindShutdownSettings;
79
82
  #terminalProgressEnabled = false;
83
+ #programStatus;
80
84
  #showImages = true;
81
85
  #imageWidthCells = 80;
82
86
  #fullscreenExitOutput = "transcript";
@@ -100,6 +104,7 @@ export class OwnedUiSessionShell {
100
104
  this.#routeHost = routeHost ?? null;
101
105
  this.#startupRoute = options.engine.startupRoute;
102
106
  this.#customViewport = sessionLayout === "custom-viewport";
107
+ this.#lazySelectors = options.diagnostics?.lazySelectors;
103
108
  this.#stopped = new Promise(resolve => {
104
109
  this.#resolveStopped = resolve;
105
110
  });
@@ -196,6 +201,9 @@ export class OwnedUiSessionShell {
196
201
  this.root.reconcilePromptImageLimitNotice(text);
197
202
  },
198
203
  onPromptSuggestionAccepted: () => promptSuggestionController?.accept(),
204
+ onPromptSuggestionEligibilityChanged: () => {
205
+ queueMicrotask(() => promptSuggestionController?.restoreAvailable());
206
+ },
199
207
  onInputSurfaceChanged: () => {
200
208
  promptSuggestionController?.invalidate();
201
209
  this.#promptHistory?.synchronize();
@@ -289,6 +297,8 @@ export class OwnedUiSessionShell {
289
297
  };
290
298
  runtime = new PiTuiRuntimeAdapter(runtimeOptions);
291
299
  this.runtime = runtime;
300
+ this.#programStatus = new ProgramStatusReporter(status => { if (this.runtime.active)
301
+ this.runtime.setProgramStatus(status); }, () => this.view().status.footer?.sessionName ?? undefined);
292
302
  this.#damageTerminal = damageTerminal ?? null;
293
303
  this.#quitOutro = quitOutro;
294
304
  this.#reloadPresentation = reloadPresentation;
@@ -500,6 +510,7 @@ export class OwnedUiSessionShell {
500
510
  },
501
511
  agentDir: this.backend.agentDir,
502
512
  setInputSurface: component => this.root.setInputSurface(component, true, "opaque"),
513
+ setProgramStatusBlocked: status => this.#programStatus.setBlocked("extension-dialog", status),
503
514
  showOverlay: (component, overlayOptions) => this.runtime.showOverlay(component, overlayOptions),
504
515
  listenInput: handler => this.runtime.addInputListener(handler),
505
516
  replaceWidget: (key, component, placement) => this.root.setExtensionWidget(key, component, placement),
@@ -537,6 +548,12 @@ export class OwnedUiSessionShell {
537
548
  this.#promptSuggestions?.invalidate();
538
549
  this.root.resumeViewportFollowing();
539
550
  }
551
+ if (event.type === "assistant-message-completed" && !event.successful) {
552
+ this.#programStatus.setErrorMessage(this.view().status.diagnostics.at(-1));
553
+ }
554
+ if (event.type === "agent-run-started" || event.type === "assistant-message-completed" || event.type === "agent-run-settled") {
555
+ this.#programStatus.handleEvent(event);
556
+ }
540
557
  if (event.type === "assistant-message-completed") {
541
558
  this.root.noteCompletedAssistantMessage();
542
559
  const identity = {
@@ -568,14 +585,22 @@ export class OwnedUiSessionShell {
568
585
  if (event.type === "status" && /^(Retrying|Compacting)/.test(event.status.workingMessage ?? "")) {
569
586
  this.#promptSuggestions?.invalidate();
570
587
  }
571
- if (event.type === "agent-run-settled" && event.model !== null) {
572
- this.#promptSuggestions?.settle({
588
+ if (event.type === "agent-run-settled") {
589
+ const identity = {
573
590
  sessionId: event.sessionId,
574
591
  sessionGeneration: event.sessionGeneration,
575
592
  runSequence: event.runSequence,
576
593
  responseSequence: event.responseSequence,
577
594
  model: event.model,
578
- });
595
+ };
596
+ if (event.model === null)
597
+ this.#promptSuggestions?.skip(identity, "no-model");
598
+ else
599
+ this.#promptSuggestions?.settle({ ...identity, model: event.model }, !event.successful ? "failed-response"
600
+ : event.toolContinuation || event.stopReason === "toolUse" ? "tool-continuation"
601
+ : event.stopReason !== "stop" ? "incomplete-response"
602
+ : event.assistantMessageCount < 2 ? "early-conversation"
603
+ : this.root.promptSuggestionPrepareBlockReason());
579
604
  }
580
605
  if (event.type === "session-lifecycle" && event.lifecycle === "stopped")
581
606
  this.#settleStoppedLifecycle();
@@ -622,6 +647,7 @@ export class OwnedUiSessionShell {
622
647
  if (this.#disposed || !this.#customViewport)
623
648
  return;
624
649
  this.#presentStartupRoute();
650
+ void this.#loadLazySelectors().then(selectors => selectors.prepare()).catch(() => { });
625
651
  this.root.warmPastePreparation();
626
652
  this.#copyExecutor?.warm();
627
653
  });
@@ -900,6 +926,14 @@ export class OwnedUiSessionShell {
900
926
  this.#dialogHandle = handle;
901
927
  return handle;
902
928
  }
929
+ #loadLazySelectors() {
930
+ if (this.#lazySelectors !== undefined)
931
+ return Promise.resolve(this.#lazySelectors);
932
+ return this.#lazySelectorsPromise ??= import("../../integrations/pi/components/lazy-selectors.js").then(module => {
933
+ this.#lazySelectors = module.piShellLazySelectors;
934
+ return module.piShellLazySelectors;
935
+ });
936
+ }
903
937
  async showThinkingSelector() {
904
938
  const snapshot = this.backend.pinnedSettingsSnapshot();
905
939
  const close = () => {
@@ -911,14 +945,32 @@ export class OwnedUiSessionShell {
911
945
  close();
912
946
  void this.runWorkflow({ command: "thinking", argument: "", selection: level });
913
947
  };
914
- const { createPiShellThinkingSelector } = await import("../../integrations/pi/components/thinking-selector-dialog.js");
915
- const component = createPiShellThinkingSelector(snapshot.thinkingLevel, snapshot.availableThinkingLevels, select, close, level => this.backend.setDefaultThinkingLevel(level), snapshot.defaultThinkingLevel, this.#customViewport ? {
916
- profile: "bare",
917
- cycleBinding: this.root.editor.keybindingConfig()["app.thinking.cycle"] ?? [],
918
- } : undefined);
919
- this.root.setFooterLevel(false);
920
- this.root.setInputSurface(component);
921
- this.runtime.requestRender();
948
+ const releasePresentation = this.#customViewport ? this.runtime.beginPresentationHold() : undefined;
949
+ try {
950
+ const selectors = await this.#loadLazySelectors();
951
+ const component = await selectors.createThinking({
952
+ currentLevel: snapshot.thinkingLevel,
953
+ availableLevels: snapshot.availableThinkingLevels,
954
+ onSelect: select,
955
+ onCancel: close,
956
+ onSelectAsDefault: level => this.backend.setDefaultThinkingLevel(level),
957
+ defaultLevel: snapshot.defaultThinkingLevel,
958
+ ...(this.#customViewport ? {
959
+ presentation: {
960
+ profile: "bare",
961
+ cycleBinding: this.root.editor.keybindingConfig()["app.thinking.cycle"] ?? [],
962
+ },
963
+ } : {}),
964
+ });
965
+ if (this.#disposed)
966
+ return;
967
+ this.root.setFooterLevel(false);
968
+ this.root.setInputSurface(component);
969
+ this.runtime.requestRender();
970
+ }
971
+ finally {
972
+ releasePresentation?.();
973
+ }
922
974
  }
923
975
  /** The Skills dialog: a replacement input like the model selector, applying the chosen skill through the prompt path. */
924
976
  showSkillsSelector(skills = this.#skillSummaries()) {
@@ -1050,47 +1102,56 @@ export class OwnedUiSessionShell {
1050
1102
  this.root.setInputSurface(null);
1051
1103
  this.runtime.requestRender();
1052
1104
  };
1053
- const component = await createPiShellTreeSelector({
1054
- tree: context.tree,
1055
- currentLeafId: context.currentLeafId,
1056
- terminalHeight: this.runtime.viewport().rows,
1057
- initialFilterMode: context.filterMode,
1058
- ...(initialSelectedId === undefined ? {} : { initialSelectedId }),
1059
- onLabelChange: context.appendLabelChange,
1060
- onCopy: text => {
1061
- if (!text) {
1062
- this.root.appendWorkflowResult({ command: "tree", outcome: "failed", message: "Selected entry has no text to copy" });
1063
- this.runtime.requestRender();
1064
- return;
1065
- }
1066
- const generation = this.backend.sessionBindingGeneration;
1067
- void this.backend.copyWorkflowText(text).then(acknowledged => {
1068
- if (this.#disposed || generation !== this.backend.sessionBindingGeneration)
1105
+ const releasePresentation = this.#customViewport ? this.runtime.beginPresentationHold() : undefined;
1106
+ try {
1107
+ const selectors = await this.#loadLazySelectors();
1108
+ const component = await selectors.createTree({
1109
+ tree: context.tree,
1110
+ currentLeafId: context.currentLeafId,
1111
+ terminalHeight: this.runtime.viewport().rows,
1112
+ initialFilterMode: context.filterMode,
1113
+ ...(initialSelectedId === undefined ? {} : { initialSelectedId }),
1114
+ onLabelChange: context.appendLabelChange,
1115
+ onCopy: text => {
1116
+ if (!text) {
1117
+ this.root.appendWorkflowResult({ command: "tree", outcome: "failed", message: "Selected entry has no text to copy" });
1118
+ this.runtime.requestRender();
1069
1119
  return;
1070
- this.root.appendWorkflowStatus(acknowledged ? "Copied selected message to clipboard" : "Submitted selected message to clipboard");
1071
- this.runtime.requestRender();
1072
- }).catch(error => {
1073
- if (this.#disposed || generation !== this.backend.sessionBindingGeneration)
1120
+ }
1121
+ const generation = this.backend.sessionBindingGeneration;
1122
+ void this.backend.copyWorkflowText(text).then(acknowledged => {
1123
+ if (this.#disposed || generation !== this.backend.sessionBindingGeneration)
1124
+ return;
1125
+ this.root.appendWorkflowStatus(acknowledged ? "Copied selected message to clipboard" : "Submitted selected message to clipboard");
1126
+ this.runtime.requestRender();
1127
+ }).catch(error => {
1128
+ if (this.#disposed || generation !== this.backend.sessionBindingGeneration)
1129
+ return;
1130
+ this.root.appendWorkflowResult({ command: "tree", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
1131
+ this.runtime.requestRender();
1132
+ });
1133
+ },
1134
+ onCancel: close,
1135
+ onSelect: entryId => {
1136
+ if (entryId === context.currentLeafId) {
1137
+ close();
1138
+ this.root.appendWorkflowStatus("Already at this point");
1139
+ this.runtime.requestRender();
1074
1140
  return;
1075
- this.root.appendWorkflowResult({ command: "tree", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
1076
- this.runtime.requestRender();
1077
- });
1078
- },
1079
- onCancel: close,
1080
- onSelect: entryId => {
1081
- if (entryId === context.currentLeafId) {
1082
- close();
1083
- this.root.appendWorkflowStatus("Already at this point");
1084
- this.runtime.requestRender();
1085
- return;
1086
- }
1087
- if (context.skipSummaryPrompt)
1088
- close();
1089
- void this.#completeTreeSelection(entryId, context.skipSummaryPrompt);
1090
- },
1091
- });
1092
- this.root.setInputSurface(component);
1093
- this.runtime.requestRender();
1141
+ }
1142
+ if (context.skipSummaryPrompt)
1143
+ close();
1144
+ void this.#completeTreeSelection(entryId, context.skipSummaryPrompt);
1145
+ },
1146
+ });
1147
+ if (this.#disposed)
1148
+ return;
1149
+ this.root.setInputSurface(component);
1150
+ this.runtime.requestRender();
1151
+ }
1152
+ finally {
1153
+ releasePresentation?.();
1154
+ }
1094
1155
  }
1095
1156
  showLoginAuthTypeSelector() {
1096
1157
  const close = () => {
@@ -1646,6 +1707,7 @@ export class OwnedUiSessionShell {
1646
1707
  attempt(() => this.#unsubscribe());
1647
1708
  attempt(() => this.#dialogHandle?.hide());
1648
1709
  attempt(() => this.#extensionBridge.dispose());
1710
+ attempt(() => this.#programStatus.clear());
1649
1711
  // Invariant: from here to the leave nothing but the outro paints. A throttled frame the
1650
1712
  // renderer still has queued would otherwise land during the stop-time input drain and
1651
1713
  // flash the prompt and footer, whether or not an effect plays.
@@ -1739,6 +1801,17 @@ export class OwnedUiSessionShell {
1739
1801
  if (!this.runtime.active)
1740
1802
  return;
1741
1803
  this.runtime.setTerminalProgress(this.#terminalProgressEnabled && view.lifecycle === "busy");
1804
+ if (view.lifecycle === "stopped" || view.lifecycle === "stopping") {
1805
+ this.#programStatus.clear();
1806
+ return;
1807
+ }
1808
+ this.#programStatus.setBlocked("dialog", view.dialog === null ? undefined : {
1809
+ kind: "question",
1810
+ message: view.dialog.title,
1811
+ });
1812
+ if (view.lifecycle === "failed")
1813
+ this.#programStatus.setErrorMessage(view.status.diagnostics.at(-1));
1814
+ this.#programStatus.report();
1742
1815
  }
1743
1816
  #syncView() {
1744
1817
  this.#streamPresentation.noteImmediatePresentation();
@@ -1750,6 +1823,7 @@ export class OwnedUiSessionShell {
1750
1823
  this.root.resetPendingPastes();
1751
1824
  this.#promptSuggestions?.invalidate();
1752
1825
  this.#sessionGeneration = this.backend.sessionGeneration;
1826
+ this.#programStatus.reset();
1753
1827
  // Invariant: delivery recovery invalidates callbacks, not same-session local recall or its draft.
1754
1828
  if (this.#sessionBindingGeneration !== this.backend.sessionBindingGeneration) {
1755
1829
  this.#sessionBindingGeneration = this.backend.sessionBindingGeneration;
@@ -2085,6 +2159,11 @@ export class OwnedUiSessionShell {
2085
2159
  }, request.providerName);
2086
2160
  this.#activeLoginDialog = dialog;
2087
2161
  this.root.setInputSurface(dialog);
2162
+ this.#programStatus.setBlocked("authentication", {
2163
+ kind: "auth",
2164
+ message: `Log in to ${request.providerName}`,
2165
+ });
2166
+ this.#syncTerminalProgress(this.view());
2088
2167
  this.runtime.requestRender();
2089
2168
  }
2090
2169
  #requestWorkflowInput(request) {
@@ -2141,6 +2220,8 @@ export class OwnedUiSessionShell {
2141
2220
  return;
2142
2221
  this.#activeLoginDialog = undefined;
2143
2222
  this.root.setInputSurface(null);
2223
+ this.#programStatus.setBlocked("authentication", undefined);
2224
+ this.#syncTerminalProgress(this.view());
2144
2225
  this.runtime.requestRender();
2145
2226
  }
2146
2227
  async #execute(command, draft) {
@@ -306,14 +306,14 @@ export class SessionViewportController {
306
306
  if (this.#viewport.clearSelection())
307
307
  this.#requestRender();
308
308
  }
309
- else {
309
+ else if (this.#viewport.hasSelection) {
310
310
  const copySelection = this.#viewport.captureSelectedText();
311
- if (copySelection !== null) {
312
- this.#viewport.clearSelection();
313
- this.#stopSelectionAutoScroll();
314
- this.#requestRender();
315
- return { data: "", consumed: true, copySelection };
316
- }
311
+ this.#viewport.clearSelection();
312
+ this.#stopSelectionAutoScroll();
313
+ this.#requestRender();
314
+ return copySelection === null
315
+ ? { data: "", consumed: true }
316
+ : { data: "", consumed: true, copySelection };
317
317
  }
318
318
  }
319
319
  const previousPrompt = SHIFT_UP_INPUTS.has(data) || this.#editor.matchesTerminalKey(data, "alt+home");
@@ -412,12 +412,19 @@ export class SessionViewportController {
412
412
  }
413
413
  if (this.#dockPointerSuppressed || this.#tailPointerSuppressed)
414
414
  return true;
415
+ const pendingEditorClick = this.#pendingEditorClick;
416
+ if (pendingEditorClick !== undefined) {
417
+ if (event.column === pendingEditorClick.column && event.row === pendingEditorClick.row)
418
+ return true;
419
+ this.#pendingEditorClick = undefined;
420
+ this.#viewport.pressSelection(pendingEditorClick.column, pendingEditorClick.row, now, { continueMultiClick: false });
421
+ this.#viewport.extendSelection(event.column, event.row, now, false);
422
+ this.#updateSelectionAutoScroll(event.column, event.row, hits.viewportHeight, frame.rows.length);
423
+ activity = true;
424
+ return true;
425
+ }
415
426
  if (this.#viewport.selectionActive) {
416
427
  this.#viewport.extendSelection(event.column, event.row, now, false);
417
- if (this.#pendingEditorClick !== undefined
418
- && (event.column !== this.#pendingEditorClick.column || event.row !== this.#pendingEditorClick.row)) {
419
- this.#pendingEditorClick = undefined;
420
- }
421
428
  this.#updateSelectionAutoScroll(event.column, event.row, hits.viewportHeight, frame.rows.length);
422
429
  activity = true;
423
430
  return true;
@@ -500,11 +507,13 @@ export class SessionViewportController {
500
507
  }
501
508
  if (event.row >= 1 && event.row <= frame.rows.length && event.column <= frame.descriptor.width) {
502
509
  if (editorFrame !== undefined && event.row >= editorFrame.rowStart && event.row <= editorFrame.rowEnd) {
510
+ // Invariant: no-drag prompt clicks remain visually editor-owned. Distinct
511
+ // motion promotes from this exact origin into complete-frame selection.
503
512
  this.#pendingEditorClick = { column: event.column, row: event.row };
504
513
  }
505
- // Invariant: frame selection includes transient and dock rows without changing
506
- // semantic ownership; no-drag editor presses replay on release.
507
- this.#viewport.pressSelection(event.column, event.row, now);
514
+ else {
515
+ this.#viewport.pressSelection(event.column, event.row, now);
516
+ }
508
517
  repaint = true;
509
518
  return true;
510
519
  }
@@ -525,19 +534,22 @@ export class SessionViewportController {
525
534
  repaint = true;
526
535
  return true;
527
536
  }
528
- if (this.#viewport.releaseSelection()) {
529
- this.#stopSelectionAutoScroll();
530
- const pendingEditorClick = this.#pendingEditorClick;
537
+ const pendingEditorClick = this.#pendingEditorClick;
538
+ if (pendingEditorClick !== undefined) {
531
539
  this.#pendingEditorClick = undefined;
532
- if (pendingEditorClick !== undefined && this.#editorPointerFrame !== undefined) {
533
- this.#viewport.clearSelection();
540
+ if (this.#editorPointerFrame !== undefined) {
534
541
  const editorRow = pendingEditorClick.row - this.#editorPointerFrame.rowStart + 1;
535
542
  this.#editor.handlePointer({ kind: "press", button: 0, column: pendingEditorClick.column, row: editorRow });
536
543
  this.#editor.handlePointer({ kind: "release", button: 0, column: pendingEditorClick.column, row: editorRow });
537
544
  }
538
- else if (this.#copyOnSelect) {
545
+ forceRepaint = true;
546
+ repaint = true;
547
+ return true;
548
+ }
549
+ if (this.#viewport.releaseSelection()) {
550
+ this.#stopSelectionAutoScroll();
551
+ if (this.#copyOnSelect)
539
552
  completedCopy = this.#viewport.captureSelectedText() ?? undefined;
540
- }
541
553
  // Platform: restore OSC 8 links only after the held-button selection paint has
542
554
  // ended, then overwrite any terminal-cached hover cells immediately.
543
555
  forceRepaint = true;
@@ -18,6 +18,7 @@ export function cliUsage(capabilities) {
18
18
  "remove <source>",
19
19
  "uninstall <source>",
20
20
  "list",
21
+ "session worktrees",
21
22
  "session link-worktree <path>",
22
23
  "session unlink-worktree",
23
24
  ]);
@@ -47,6 +48,7 @@ export function cliHelp(capabilities) {
47
48
  ` ${command} list`,
48
49
  "",
49
50
  "Session context:",
51
+ ` ${command} session worktrees`,
50
52
  ` ${command} session link-worktree <path>`,
51
53
  ` ${command} session unlink-worktree`,
52
54
  "",
@@ -127,12 +129,12 @@ function parseSessionContext(rest) {
127
129
  return { kind: "error", message: PRODUCT_TEXT.diagnostic("session link-worktree accepts one path.") };
128
130
  return { kind: "session-context", request: { action, path: value } };
129
131
  }
130
- if (action === "unlink-worktree") {
132
+ if (action === "unlink-worktree" || action === "worktrees") {
131
133
  if (value !== undefined)
132
- return { kind: "error", message: PRODUCT_TEXT.diagnostic("session unlink-worktree does not accept arguments.") };
134
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`session ${action} does not accept arguments.`) };
133
135
  return { kind: "session-context", request: { action } };
134
136
  }
135
- return { kind: "error", message: PRODUCT_TEXT.diagnostic("session accepts link-worktree <path> or unlink-worktree.") };
137
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic("session accepts worktrees, link-worktree <path>, or unlink-worktree.") };
136
138
  }
137
139
  /**
138
140
  * Stable update is the empty form. `--develop` selects the moving development
@@ -2,8 +2,12 @@ import { type SessionContextRequest } from "../features/launch/index.js";
2
2
  export type { SessionContextRequest } from "../features/launch/index.js";
3
3
  export interface SessionContextCommandOptions {
4
4
  readonly environment?: NodeJS.ProcessEnv;
5
+ readonly inspectProcess?: (pid: number) => Promise<{
6
+ readonly pid: number;
7
+ readonly startIdentity: string;
8
+ } | null>;
5
9
  readonly stdout?: (message: string) => void;
6
10
  readonly stderr?: (message: string) => void;
7
11
  }
8
- /** Apply repository presentation context only when invoked by an active Pi session shell tool. */
12
+ /** Apply repository context only when invoked by an exact live A1 shell-tool runtime. */
9
13
  export declare function runSessionContextCommand(request: SessionContextRequest, options?: SessionContextCommandOptions): Promise<number>;
@@ -1,29 +1,46 @@
1
1
  import { updateSessionRepositoryContext, } from "../features/launch/index.js";
2
- import { PRODUCT_TEXT } from "../product-identity.js";
3
- /** Apply repository presentation context only when invoked by an active Pi session shell tool. */
2
+ import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../product-identity.js";
3
+ /** Apply repository context only when invoked by an exact live A1 shell-tool runtime. */
4
4
  export async function runSessionContextCommand(request, options = {}) {
5
5
  const environment = options.environment ?? process.env;
6
6
  const stdout = options.stdout ?? (message => process.stdout.write(message));
7
7
  const stderr = options.stderr ?? (message => process.stderr.write(message));
8
8
  const identity = readIdentity(environment);
9
9
  if (identity === null) {
10
- stderr(`${PRODUCT_TEXT.diagnostic("session context requires an active A1 shell-tool session.")}\n`);
10
+ stderr(`${PRODUCT_TEXT.diagnostic("session context requires an active A1 shell-tool session runtime.")}\n`);
11
11
  return 2;
12
12
  }
13
13
  try {
14
- const cwd = await updateSessionRepositoryContext(identity, request);
15
- stdout(cwd === null ? "Session worktree context cleared.\n" : `Session worktree context linked: ${cwd}\n`);
14
+ if (options.inspectProcess === undefined)
15
+ throw Object.assign(new Error("native process identity inspection is unavailable"), { code: "process-inspection-unavailable" });
16
+ const outcome = await updateSessionRepositoryContext({ sessionId: identity.sessionId, sessionFile: identity.sessionFile }, request, { runtimeId: identity.runtimeId, inspectProcess: options.inspectProcess });
17
+ if (outcome.kind === "unlinked")
18
+ stdout("Session worktree context cleared.\n");
19
+ if (outcome.kind === "linked")
20
+ stdout(`Session worktree context linked: ${outcome.cwd}\n`);
21
+ if (outcome.kind === "worktrees") {
22
+ for (const entry of outcome.entries)
23
+ stdout(`${entry.status}: ${entry.cwd} (${entry.branch})\n`);
24
+ }
16
25
  return 0;
17
26
  }
18
27
  catch (error) {
19
- stderr(`${PRODUCT_TEXT.diagnostic(`could not update session context: ${error instanceof Error ? error.message : String(error)}`)}\n`);
28
+ const code = errorCode(error);
29
+ const detail = error instanceof Error ? error.message : String(error);
30
+ stderr(`${PRODUCT_TEXT.diagnostic(`could not update session context${code === null ? "" : ` (${code})`}: ${detail}`)}\n`);
20
31
  return 1;
21
32
  }
22
33
  }
23
34
  function readIdentity(environment) {
24
35
  const sessionId = environment.PI_SESSION_ID;
25
36
  const sessionFile = environment.PI_SESSION_FILE;
26
- if (!sessionId || !sessionFile)
37
+ const runtimeId = environment[PRODUCT_IDENTITY.environment.sessionRuntimeId];
38
+ if (!sessionId || !sessionFile || !runtimeId)
27
39
  return null;
28
- return { sessionId, sessionFile };
40
+ return { sessionId, sessionFile, runtimeId };
41
+ }
42
+ function errorCode(error) {
43
+ if (error instanceof Error && "code" in error && typeof error.code === "string")
44
+ return error.code;
45
+ return null;
29
46
  }
@@ -1,4 +1,5 @@
1
1
  import type { AvailableRelease, StartupReleaseCheckOptions } from "../foundation/release/latest-release.js";
2
+ import type { NativeProcessIdentity } from "../foundation/lifecycle/model.js";
2
3
  import type { SessionSelection } from "../foundation/lifecycle/session-selection.js";
3
4
  import type { PiEngineAdapter } from "../integrations/pi/engine/adapter.js";
4
5
  import type { PiProjectTrustPreflightPrompt } from "../integrations/pi/engine/project-trust-preflight.js";
@@ -34,6 +35,8 @@ export interface OwnedUiCompositionOptions {
34
35
  readonly releaseNotes?: ReleaseNoteCatalog;
35
36
  /** Startup release-availability seam; defaults to the throttled registry check. */
36
37
  readonly checkForNewerRelease?: (options: StartupReleaseCheckOptions) => Promise<AvailableRelease | null>;
38
+ /** Exact native process inspection injected by the shipped entry; absent SDK seams do not publish claims. */
39
+ readonly inspectProcess?: (pid: number) => Promise<NativeProcessIdentity | null>;
37
40
  }
38
41
  export interface OwnedUiComposition {
39
42
  readonly application: OwnedUiApplicationPort;