@timurproko/a1 0.2.7-dev.754 → 0.2.7-dev.772

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 (217) hide show
  1. package/README.md +30 -0
  2. package/bin/cli.js +4 -0
  3. package/bin/guardian.js +5 -0
  4. package/bin/tab-launch-context.d.ts +26 -0
  5. package/bin/tab-launch-context.js +94 -0
  6. package/bin/ui.js +24 -6
  7. package/dist/app/session-shell/helper-pool.d.ts +3 -5
  8. package/dist/app/session-shell/helper-pool.js +3 -5
  9. package/dist/app/session-shell/session-shell-root.d.ts +28 -1
  10. package/dist/app/session-shell/session-shell-root.js +4 -3
  11. package/dist/app/session-shell/session-shell.js +149 -31
  12. package/dist/app/session-shell/session-viewport-controller.d.ts +1 -1
  13. package/dist/app/session-shell/session-viewport-controller.js +3 -3
  14. package/dist/app/session-shell/shell-host.d.ts +2 -0
  15. package/dist/app/session-shell/shell-host.js +20 -1
  16. package/dist/cli/dispatch.d.ts +11 -1
  17. package/dist/cli/dispatch.js +79 -5
  18. package/dist/cli/tabs-request.d.ts +7 -0
  19. package/dist/cli/tabs-request.js +1 -0
  20. package/dist/cli/tabs.d.ts +14 -0
  21. package/dist/cli/tabs.js +140 -0
  22. package/dist/composition/owned-ui.d.ts +26 -0
  23. package/dist/composition/owned-ui.js +176 -6
  24. package/dist/contracts/owned-ui/session-backend.d.ts +1 -0
  25. package/dist/features/launch/index.d.ts +1 -1
  26. package/dist/features/launch/intent.d.ts +3 -1
  27. package/dist/features/launch/intent.js +7 -4
  28. package/dist/features/owned-ui/settings-app.js +8 -35
  29. package/dist/features/resident-tab/auto-name.d.ts +11 -0
  30. package/dist/features/resident-tab/auto-name.js +42 -0
  31. package/dist/features/resident-tab/index.d.ts +3 -0
  32. package/dist/features/resident-tab/index.js +3 -0
  33. package/dist/features/resident-tab/shortcuts.d.ts +10 -0
  34. package/dist/features/resident-tab/shortcuts.js +35 -0
  35. package/dist/features/resident-tab/status-deriver.d.ts +9 -0
  36. package/dist/features/resident-tab/status-deriver.js +15 -0
  37. package/dist/foundation/launch-context/index.d.ts +10 -0
  38. package/dist/foundation/launch-context/index.js +25 -0
  39. package/dist/foundation/launch-guardian/main.d.ts +5 -0
  40. package/dist/foundation/launch-guardian/main.js +2 -1
  41. package/dist/foundation/release/bootstrap.d.ts +6 -0
  42. package/dist/foundation/release/bootstrap.js +51 -10
  43. package/dist/foundation/release/cohort-state.d.ts +1 -1
  44. package/dist/foundation/release/cohort-state.js +1 -1
  45. package/dist/foundation/release/dependency-certification.js +54 -19
  46. package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
  47. package/dist/foundation/release/progress-palette.generated.js +2 -2
  48. package/dist/foundation/release/release-gc.d.ts +3 -0
  49. package/dist/foundation/release/release-gc.js +44 -8
  50. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +16 -0
  51. package/dist/foundation/resident-tabs/bounded-host-command.js +35 -0
  52. package/dist/foundation/resident-tabs/bridge-client.d.ts +56 -0
  53. package/dist/foundation/resident-tabs/bridge-client.js +279 -0
  54. package/dist/foundation/resident-tabs/doctor.d.ts +29 -0
  55. package/dist/foundation/resident-tabs/doctor.js +166 -0
  56. package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
  57. package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
  58. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +31 -0
  59. package/dist/foundation/resident-tabs/ensure-terminal-host.js +147 -0
  60. package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
  61. package/dist/foundation/resident-tabs/fault-injection.js +26 -0
  62. package/dist/foundation/resident-tabs/index.d.ts +12 -0
  63. package/dist/foundation/resident-tabs/index.js +6 -0
  64. package/dist/foundation/resident-tabs/maintenance.d.ts +42 -0
  65. package/dist/foundation/resident-tabs/maintenance.js +78 -0
  66. package/dist/foundation/resident-tabs/prompt-journal.d.ts +65 -0
  67. package/dist/foundation/resident-tabs/prompt-journal.js +214 -0
  68. package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
  69. package/dist/foundation/resident-tabs/protocol.js +195 -0
  70. package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
  71. package/dist/foundation/resident-tabs/recovery.js +205 -0
  72. package/dist/foundation/resident-tabs/release-holds.d.ts +27 -0
  73. package/dist/foundation/resident-tabs/release-holds.js +77 -0
  74. package/dist/foundation/resident-tabs/routing.d.ts +11 -0
  75. package/dist/foundation/resident-tabs/routing.js +10 -0
  76. package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
  77. package/dist/foundation/resident-tabs/session-sync.js +37 -0
  78. package/dist/foundation/resident-tabs/settings.d.ts +14 -0
  79. package/dist/foundation/resident-tabs/settings.js +66 -0
  80. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +18 -0
  81. package/dist/foundation/resident-tabs/terminal-host-artifact.js +53 -0
  82. package/dist/foundation/resident-tabs/zip-archive.d.ts +9 -0
  83. package/dist/foundation/resident-tabs/zip-archive.js +56 -0
  84. package/dist/foundation/session-writer-lock/index.d.ts +32 -0
  85. package/dist/foundation/session-writer-lock/index.js +201 -0
  86. package/dist/foundation/startup/startup-descriptor.js +2 -2
  87. package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
  88. package/dist/foundation/terminal-cleanup/index.js +1 -1
  89. package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +1 -0
  90. package/dist/foundation/terminal-cleanup/terminal-reset.js +1 -0
  91. package/dist/integrations/pi/components/owned-editor-ux.d.ts +1 -1
  92. package/dist/integrations/pi/components/owned-editor-ux.js +2 -2
  93. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
  94. package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
  95. package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
  96. package/dist/integrations/pi/components/shell-presenters-info.d.ts +4 -2
  97. package/dist/integrations/pi/components/shell-presenters-info.js +38 -4
  98. package/dist/integrations/pi/components/shell-shared-facade.d.ts +1 -1
  99. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  100. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
  101. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  102. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  103. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  104. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  105. package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
  106. package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
  107. package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
  108. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  109. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
  110. package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
  111. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  112. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  113. package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
  114. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  115. package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
  116. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
  117. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
  118. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
  119. package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
  120. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  121. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  122. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  123. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
  124. package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +0 -2
  125. package/dist/integrations/pi/components/upstream/components/tool-execution.js +41 -20
  126. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
  127. package/dist/integrations/pi/components/upstream/components/tree-selector.js +1 -1
  128. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  129. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  130. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
  131. package/dist/integrations/pi/components/upstream/history/editor-core.js +5 -5
  132. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  133. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  134. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  135. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  136. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  137. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  138. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  139. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  140. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
  141. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  142. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +3 -0
  143. package/dist/integrations/pi/components/upstream/theme/theme.js +24 -1
  144. package/dist/integrations/pi/engine/adapter.d.ts +1 -0
  145. package/dist/integrations/pi/engine/adapter.js +4 -0
  146. package/dist/integrations/pi/engine/pi-settings-metadata.json +2 -2
  147. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +1 -0
  148. package/dist/integrations/pi/engine/prompt-suggestions.js +40 -0
  149. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  150. package/dist/integrations/pi/engine/runtime-integration.js +102 -29
  151. package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
  152. package/dist/integrations/pi/engine/session-runtime.js +8 -3
  153. package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
  154. package/dist/integrations/pi/engine/session-selection.js +45 -9
  155. package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
  156. package/dist/integrations/pi/startup-public.js +152 -222
  157. package/dist/integrations/pi/startup-public.manifest.json +266 -177
  158. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  159. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  160. package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
  161. package/dist/native/darwin-arm64/manifest.json +1 -1
  162. package/dist/native/darwin-arm64/terminal-host +0 -0
  163. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +10500 -0
  164. package/dist/native/darwin-arm64/terminal-host.manifest.json +44 -0
  165. package/dist/native/darwin-x64/a1_session_lock.node +0 -0
  166. package/dist/native/darwin-x64/manifest.json +20 -0
  167. package/dist/native/darwin-x64/process-guardian +0 -0
  168. package/dist/native/darwin-x64/terminal-host +0 -0
  169. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10737 -0
  170. package/dist/native/darwin-x64/terminal-host.manifest.json +44 -0
  171. package/dist/native/linux-x64/a1_session_lock.node +0 -0
  172. package/dist/native/linux-x64/manifest.json +1 -1
  173. package/dist/native/linux-x64/terminal-host +0 -0
  174. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +11226 -0
  175. package/dist/native/linux-x64/terminal-host.manifest.json +44 -0
  176. package/dist/native/win32-x64/a1_session_lock.node +0 -0
  177. package/dist/native/win32-x64/manifest.json +2 -2
  178. package/dist/native/win32-x64/process-guardian.exe +0 -0
  179. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10757 -0
  180. package/dist/native/win32-x64/terminal-host.exe +0 -0
  181. package/dist/native/win32-x64/terminal-host.manifest.json +44 -0
  182. package/dist/product-identity.d.ts +1 -1
  183. package/dist/product-identity.js +1 -1
  184. package/dist/product-identity.json +5 -0
  185. package/dist/runtime-payload-inventory.json +5349 -6035
  186. package/dist/ui/components/line-input.d.ts +1 -1
  187. package/dist/ui/components/prompt-input.d.ts +7 -5
  188. package/dist/ui/components/prompt-input.js +7 -7
  189. package/dist/ui/components/transcript-viewport.d.ts +6 -3
  190. package/dist/ui/components/transcript-viewport.js +13 -15
  191. package/dist/ui/settings/declarations.d.ts +25 -1
  192. package/dist/ui/settings/declarations.js +21 -1
  193. package/dist/ui/settings/index.d.ts +4 -2
  194. package/dist/ui/settings/index.js +2 -1
  195. package/dist/ui/settings/manager.d.ts +10 -3
  196. package/dist/ui/settings/manager.js +90 -30
  197. package/dist/ui/settings/migrations.d.ts +6 -0
  198. package/dist/ui/settings/migrations.js +44 -0
  199. package/dist/ui/settings/profile-document.d.ts +26 -0
  200. package/dist/ui/settings/profile-document.js +164 -0
  201. package/docs/architecture/evidence/resident-tabs/linux-x64.md +37 -0
  202. package/docs/architecture/evidence/resident-tabs/macos-arm64.md +36 -0
  203. package/docs/architecture/evidence/resident-tabs/macos-x64.md +35 -0
  204. package/docs/architecture/evidence/resident-tabs/windows-x64.md +36 -0
  205. package/docs/architecture/internal-naming.md +3 -1
  206. package/docs/architecture/process-guardian-provenance.md +2 -0
  207. package/docs/architecture/resident-tabs-requirements.md +18 -0
  208. package/docs/architecture/resident-tabs.md +594 -0
  209. package/docs/architecture/terminal-host-proof-gate.md +2 -0
  210. package/docs/architecture/terminal-host-provenance.md +13 -3
  211. package/docs/architecture/toolchain.md +1 -1
  212. package/docs/ci-release-runbook.md +2 -2
  213. package/docs/features/launch-profiles.md +11 -8
  214. package/docs/manual-resident-tabs-acceptance.md +51 -0
  215. package/docs/openspec-archive-automation.md +17 -16
  216. package/docs/validation.md +9 -1
  217. package/package.json +10 -4
@@ -3,7 +3,7 @@ import { createResponseCopyExecutor, hasAsyncClipboardOutput, responseCopyDestin
3
3
  import { MAX_COPY_CONTROL_BYTES } from "./response-copy-protocol.js";
4
4
  import { PromptHistoryController } from "./prompt-history-controller.js";
5
5
  import { PRODUCT_TEXT } from "../../product-identity.js";
6
- import { boundedCleanup } from "../../foundation/terminal-cleanup/terminal-reset.js";
6
+ import { CLEAR_EXIT_INTERVAL_MS, boundedCleanup } from "../../foundation/terminal-cleanup/terminal-reset.js";
7
7
  import { assertOwnedUiCommand, OWNED_UI_HIDDEN_COMMAND_NAMES } from "../../contracts/owned-ui/index.js";
8
8
  import { assertPromptImages, ImageAttachmentError } from "../../contracts/owned-ui/index.js";
9
9
  import { ContextualPromptSuggestionController } from "./prompt-suggestion-controller.js";
@@ -34,6 +34,7 @@ export class OwnedUiSessionPresenter {
34
34
  #host;
35
35
  #terminal;
36
36
  #cwd;
37
+ #residentTabOptions;
37
38
  #listeners = new Set();
38
39
  #unsubscribe;
39
40
  #unsubscribePromptSuggestions;
@@ -53,6 +54,9 @@ export class OwnedUiSessionPresenter {
53
54
  #routeHost;
54
55
  #startupRoute;
55
56
  #startupRouteTimer;
57
+ #residentProgressTimer;
58
+ #residentWorking = false;
59
+ #residentStalled = false;
56
60
  #dialogHandle;
57
61
  #sequence = 0;
58
62
  #editorRevision = 0;
@@ -104,6 +108,7 @@ export class OwnedUiSessionPresenter {
104
108
  this.#sessionBindingGeneration = this.backend.identity.sessionBindingGeneration;
105
109
  this.#suggestionModelKey = modelKey(this.backend.session.view());
106
110
  this.#cwd = cwd;
111
+ this.#residentTabOptions = options.engine.residentTab;
107
112
  this.#routeHost = routeHost ?? null;
108
113
  this.#startupRoute = options.engine.startupRoute;
109
114
  this.#customViewport = sessionLayout === "custom-viewport";
@@ -202,6 +207,7 @@ export class OwnedUiSessionPresenter {
202
207
  onDequeue: () => this.restoreQueuedInput(),
203
208
  onEditorChange: text => {
204
209
  this.#editorRevision++;
210
+ this.#residentTabOptions?.checkpoint?.(text);
205
211
  promptSuggestionController?.abortPending();
206
212
  if (text.length === 0) {
207
213
  // Concurrency: the editor synchronizes deletion-owned autocomplete after onChange.
@@ -218,9 +224,9 @@ export class OwnedUiSessionPresenter {
218
224
  promptSuggestionController?.invalidate();
219
225
  this.#promptHistory?.synchronize();
220
226
  },
221
- onCopyText: text => {
227
+ onCopyText: (text, cut) => {
222
228
  if (this.#responseCopy !== null) {
223
- void this.#responseCopy.submitText(text);
229
+ this.#trackCopy(this.#responseCopy.submitText(text), cut ? undefined : text.length);
224
230
  return;
225
231
  }
226
232
  const write = () => {
@@ -263,6 +269,7 @@ export class OwnedUiSessionPresenter {
263
269
  // Invariant: the presenter owns its root (theme subscription and helper clients) and disposes it once the
264
270
  // terminal no longer renders it.
265
271
  constructed.push(() => this.root.dispose());
272
+ this.#restoreResidentInput();
266
273
  const initialPiSettings = this.backend.settings.snapshot();
267
274
  this.#programStatus = new ProgramStatusReporter(status => this.#terminal.setProgramStatus(status), () => this.view().status.footer?.sessionName ?? undefined);
268
275
  this.#reloadPresentation = reloadPresentation;
@@ -474,6 +481,7 @@ export class OwnedUiSessionPresenter {
474
481
  this.#settleStoppedLifecycle();
475
482
  return;
476
483
  }
484
+ this.#observeResidentProgress(event);
477
485
  // Performance: a streamed chunk names one block, and touching only that block is what keeps the
478
486
  // cost of a chunk the same in a long session as in a new one. Everything else
479
487
  // resynchronizes the view, which is cheap next to re-reading the transcript.
@@ -626,21 +634,21 @@ export class OwnedUiSessionPresenter {
626
634
  const routed = this.root.handleViewportPreInput(data, true, Date.now(), this.root.usesDefaultInputSurface() && !this.#terminal.hasFocusedOverlay());
627
635
  if (routed.copySelection !== undefined) {
628
636
  const snapshot = routed.copySelection;
629
- const acknowledgement = copyAcknowledgement(snapshot);
630
- const intent = ++this.#copyIntentSequence;
631
- void this.#responseCopy?.submit(snapshot, this.#pendingClipboardWrite).then(result => {
632
- if (this.#disposed || !this.#terminal.running || intent !== this.#copyIntentSequence
633
- || result.outcome !== "delivered" && result.outcome !== "submitted-unverified")
634
- return;
635
- if (acknowledgement.hasNonWhitespace) {
636
- this.root.showCopyAcknowledgement(`copied ${acknowledgement.characters} chars to clipboard`);
637
- }
638
- });
637
+ this.#trackCopy(this.#responseCopy?.submit(snapshot, this.#pendingClipboardWrite), copyCharacters(snapshot));
639
638
  }
640
639
  if (!routed.consumed)
641
640
  return routed.data === data ? undefined : { data: routed.data };
642
641
  return routed.data.length === 0 ? { consume: true } : { data: routed.data };
643
642
  }
643
+ #trackCopy(delivery, count) {
644
+ const intent = ++this.#copyIntentSequence;
645
+ void delivery?.then(({ outcome }) => {
646
+ if (count === undefined || this.#disposed || !this.#terminal.running || intent !== this.#copyIntentSequence
647
+ || outcome !== "delivered" && outcome !== "submitted-unverified")
648
+ return;
649
+ this.root.showCopyAcknowledgement(`copied ${count} chars to clipboard`);
650
+ });
651
+ }
644
652
  #promptSuggestionPresentationBlockReason(identity) {
645
653
  const view = this.view();
646
654
  if (this.#disposed)
@@ -779,15 +787,38 @@ export class OwnedUiSessionPresenter {
779
787
  const type = this.view().lifecycle === "busy" ? "steer" : "prompt";
780
788
  this.#rememberInput(this.root.omitUnsentPromptImages(displayInput).trim(), type);
781
789
  this.root.resumeViewportFollowing();
782
- const finishSending = prepared.images.length === 0 ? () => { } : this.root.beginImageSubmissionStatus();
790
+ const command = {
791
+ type,
792
+ correlationId: this.#correlation(type),
793
+ sessionId: this.backend.identity.sessionId,
794
+ text: input,
795
+ ...(prepared.images.length === 0 ? {} : { images: prepared.images }),
796
+ };
797
+ let submission;
783
798
  try {
784
- return await this.#execute({
785
- type,
786
- correlationId: this.#correlation(type),
787
- sessionId: this.backend.identity.sessionId,
799
+ submission = await this.#residentTabOptions?.admit?.({
800
+ sessionId: command.sessionId,
801
+ sessionFile: this.#currentSessionFile(),
802
+ kind: type,
788
803
  text: input,
789
804
  ...(prepared.images.length === 0 ? {} : { images: prepared.images }),
790
- }, displayInput);
805
+ });
806
+ }
807
+ catch {
808
+ if (this.root.editor.getText().length === 0)
809
+ this.root.editor.setText(displayInput);
810
+ const message = "Prompt was not sent because its recovery journal could not be synchronized.";
811
+ this.#reportSubmissionError(undefined, message);
812
+ return rejected(message);
813
+ }
814
+ const finishSending = prepared.images.length === 0 ? () => { } : this.root.beginImageSubmissionStatus();
815
+ try {
816
+ const result = await this.#execute(command, displayInput);
817
+ if (submission !== undefined && result.outcome !== "rejected") {
818
+ this.#residentTabOptions?.dispatched?.();
819
+ await this.#residentTabOptions?.correlated?.(submission.id, command.correlationId).catch(() => undefined);
820
+ }
821
+ return result;
791
822
  }
792
823
  finally {
793
824
  finishSending();
@@ -795,7 +826,7 @@ export class OwnedUiSessionPresenter {
795
826
  }
796
827
  async clearOrExit(now = Date.now()) {
797
828
  this.#promptSuggestions?.abortPending();
798
- if (now - this.#lastClearTime < 500)
829
+ if (now - this.#lastClearTime < CLEAR_EXIT_INTERVAL_MS)
799
830
  return this.shutdown();
800
831
  this.root.editor.setText("");
801
832
  this.#lastClearTime = now;
@@ -1149,10 +1180,23 @@ export class OwnedUiSessionPresenter {
1149
1180
  this.#presenters.openSettingsSelector(this.#dialogHost);
1150
1181
  }
1151
1182
  shutdown() {
1152
- this.#shutdownPromise ??= this.#shutdown();
1183
+ this.#shutdownPromise ??= this.#shutdown().finally(() => {
1184
+ // Invariant: a rejected resident detach is retryable; ordinary shutdown has disposed the shell already.
1185
+ if (!this.#disposed)
1186
+ this.#shutdownPromise = undefined;
1187
+ });
1153
1188
  return this.#shutdownPromise;
1154
1189
  }
1155
1190
  async #shutdown() {
1191
+ const residentTab = this.#residentTab();
1192
+ if (residentTab !== undefined) {
1193
+ const detached = await residentTab.requestDetach().catch(() => false);
1194
+ if (detached)
1195
+ return { outcome: "completed", diagnostic: null };
1196
+ this.root.appendWorkflowStatus(residentTab.unavailableNotice ?? "Press Ctrl+C twice to leave; this agent keeps running.");
1197
+ this.#terminal.requestRender();
1198
+ return rejected("resident tab detach is unavailable");
1199
+ }
1156
1200
  try {
1157
1201
  const quit = this.backend.workflows.executeWorkflow({ command: "quit", argument: "" });
1158
1202
  // Invariant: a slow or hung engine quit never holds the terminal past the cleanup deadline. The
@@ -1168,6 +1212,75 @@ export class OwnedUiSessionPresenter {
1168
1212
  this.#resolveStoppedLifecycle();
1169
1213
  }
1170
1214
  }
1215
+ #restoreResidentInput() {
1216
+ const recovered = this.#residentTabOptions?.recovered;
1217
+ if (recovered === undefined)
1218
+ return;
1219
+ const prompts = recovered.prompts.map(prompt => prompt.text).filter(Boolean);
1220
+ if (prompts.length > 0) {
1221
+ this.root.editor.setText(prompts[0]);
1222
+ if (recovered.draft)
1223
+ this.root.editor.addToHistory(recovered.draft);
1224
+ for (const additional of prompts.slice(1))
1225
+ this.root.editor.addToHistory(additional);
1226
+ this.root.appendWorkflowStatus(recovered.draft
1227
+ ? `Recovered an unfinished prompt; the prior draft and ${Math.max(0, prompts.length - 1)} additional prompt${prompts.length === 2 ? "" : "s"} are in editor history.`
1228
+ : `Recovered an unfinished prompt${prompts.length > 1 ? `; ${prompts.length - 1} additional prompts are in editor history` : ""}.`);
1229
+ }
1230
+ else if (recovered.draft) {
1231
+ this.root.editor.setText(recovered.draft);
1232
+ this.root.appendWorkflowStatus("Recovered the editor draft from the previous tab process.");
1233
+ }
1234
+ }
1235
+ #currentSessionFile() {
1236
+ const direct = this.backend.identity.currentSessionFile?.();
1237
+ if (direct !== undefined && direct !== null)
1238
+ return direct;
1239
+ const resume = this.backend.identity.currentSessionResumeMetadata();
1240
+ return resume === null ? null : `${resume.sessionDir}/${resume.sessionId}`;
1241
+ }
1242
+ #residentTab() {
1243
+ return this.#residentTabOptions;
1244
+ }
1245
+ #observeResidentProgress(event) {
1246
+ const resident = this.#residentTabOptions;
1247
+ const seconds = resident?.stallNoticeSeconds;
1248
+ if (resident === undefined || seconds === undefined)
1249
+ return;
1250
+ if (event.type === "session-lifecycle") {
1251
+ this.#residentWorking = event.lifecycle === "busy";
1252
+ if (!this.#residentWorking) {
1253
+ if (this.#residentProgressTimer !== undefined)
1254
+ clearTimeout(this.#residentProgressTimer);
1255
+ this.#residentProgressTimer = undefined;
1256
+ this.#residentStalled = false;
1257
+ resident.reportStatus?.("idle");
1258
+ return;
1259
+ }
1260
+ }
1261
+ else if (!this.#residentWorking || ![
1262
+ "transcript-block", "session-view", "assistant-message-completed", "agent-run-started",
1263
+ ].includes(event.type)) {
1264
+ return;
1265
+ }
1266
+ if (this.#residentStalled) {
1267
+ this.#residentStalled = false;
1268
+ resident.reportStatus?.("working");
1269
+ }
1270
+ if (this.#residentProgressTimer !== undefined)
1271
+ clearTimeout(this.#residentProgressTimer);
1272
+ this.#residentProgressTimer = setTimeout(() => {
1273
+ this.#residentProgressTimer = undefined;
1274
+ if (this.#disposed || !this.#residentWorking)
1275
+ return;
1276
+ this.#residentStalled = true;
1277
+ resident.reportStatus?.("stalled");
1278
+ const minutes = Math.max(1, Math.round(seconds / 60));
1279
+ this.root.appendWorkflowStatus(`No progress for ${minutes} min — Esc interrupts · use the tab restart action to restart`);
1280
+ this.#terminal.requestRender();
1281
+ }, seconds * 1_000);
1282
+ this.#residentProgressTimer.unref?.();
1283
+ }
1171
1284
  async runWorkflow(request) {
1172
1285
  if (request.command === "quit")
1173
1286
  return this.shutdown();
@@ -1425,6 +1538,9 @@ export class OwnedUiSessionPresenter {
1425
1538
  if (this.#startupRouteTimer !== undefined)
1426
1539
  clearTimeout(this.#startupRouteTimer);
1427
1540
  this.#startupRouteTimer = undefined;
1541
+ if (this.#residentProgressTimer !== undefined)
1542
+ clearTimeout(this.#residentProgressTimer);
1543
+ this.#residentProgressTimer = undefined;
1428
1544
  });
1429
1545
  attempt(() => this.#promptSuggestions?.dispose());
1430
1546
  attempt(() => this.#unsubscribePromptSuggestions());
@@ -1461,6 +1577,8 @@ export class OwnedUiSessionPresenter {
1461
1577
  }
1462
1578
  await historyCleanup.catch(() => false); // Security: background durability outcomes never enter terminal output.
1463
1579
  await boundedCleanup(() => pasteCleanup).catch(error => settled.push(error));
1580
+ await boundedCleanup(() => this.#residentTabOptions?.gracefulStop?.(this.#currentSessionFile()) ?? Promise.resolve())
1581
+ .catch(error => settled.push(error));
1464
1582
  await boundedCleanup(() => this.backend.extensions.unbindExtensionUi()).catch(error => settled.push(error));
1465
1583
  return settled;
1466
1584
  };
@@ -1641,7 +1759,8 @@ export class OwnedUiSessionPresenter {
1641
1759
  this.#dialogId = dialog.id;
1642
1760
  }
1643
1761
  hotkeysPresentation() {
1644
- return this.root.hotkeysPresentation();
1762
+ const presentation = this.root.hotkeysPresentation();
1763
+ return this.#residentTabOptions === undefined ? presentation : { ...presentation, residentTabs: true };
1645
1764
  }
1646
1765
  async #slashCommand(text) {
1647
1766
  const body = text.slice(1).trim();
@@ -1920,16 +2039,15 @@ export class OwnedUiSessionShell extends OwnedUiSessionPresenter {
1920
2039
  function isCoalescedStreamBlock(kind) {
1921
2040
  return kind === "assistant" || kind === "thinking" || kind === "tool-call" || kind === "tool-result";
1922
2041
  }
1923
- function copyAcknowledgement(snapshot) {
2042
+ function copyCharacters(snapshot) {
1924
2043
  if (snapshot.literal !== true)
1925
- return { characters: snapshot.sourceUnits, hasNonWhitespace: true };
1926
- let characters = Math.max(0, snapshot.rows.length - 1);
1927
- let hasNonWhitespace = false;
1928
- for (const row of snapshot.rows) {
1929
- characters += row.text.length;
1930
- hasNonWhitespace ||= /\S/u.test(row.text);
1931
- }
1932
- return { characters, hasNonWhitespace };
2044
+ return snapshot.sourceUnits;
2045
+ let count = Math.max(0, snapshot.rows.length - 1), hasContent = false;
2046
+ for (const { text } of snapshot.rows) {
2047
+ count += text.length;
2048
+ hasContent ||= /\S/u.test(text);
2049
+ }
2050
+ return hasContent ? count : undefined;
1933
2051
  }
1934
2052
  export function formatSessionResumeCommand(metadata) {
1935
2053
  const args = [PRODUCT_TEXT.commandName];
@@ -64,7 +64,7 @@ export declare class SessionViewportController {
64
64
  setOverlaySurfaces(surfaces: readonly PiTuiPointerSurface[] | null): void;
65
65
  get inputGeometryReady(): boolean;
66
66
  compose(input: Omit<TranscriptViewportFrameInput, "pointerPosition">): TranscriptViewportFrame;
67
- composeDockOnly(dockRows: readonly string[], width: number, height: number): TranscriptViewportFrame | null;
67
+ composeDockOnly(dockRows: readonly string[], width: number, height: number, explicitCopyDockRows?: number): TranscriptViewportFrame | null;
68
68
  /** Returns true when document wrapping may have changed. */
69
69
  setConfig(config: OwnedUiViewportSettings): boolean;
70
70
  resumeFollowing(): void;
@@ -165,8 +165,8 @@ export class SessionViewportController {
165
165
  }
166
166
  return frame;
167
167
  }
168
- composeDockOnly(dockRows, width, height) {
169
- return this.#viewport.composeDockOnly(dockRows, width, height);
168
+ composeDockOnly(dockRows, width, height, explicitCopyDockRows = 0) {
169
+ return this.#viewport.composeDockOnly(dockRows, width, height, explicitCopyDockRows);
170
170
  }
171
171
  /** Returns true when document wrapping may have changed. */
172
172
  setConfig(config) {
@@ -307,7 +307,7 @@ export class SessionViewportController {
307
307
  this.#requestRender();
308
308
  }
309
309
  else if (this.#viewport.hasSelection) {
310
- const copySelection = this.#viewport.captureSelectedText();
310
+ const copySelection = this.#viewport.captureSelectedText("explicit");
311
311
  this.#viewport.clearSelection();
312
312
  this.#stopSelectionAutoScroll();
313
313
  this.#requestRender();
@@ -98,6 +98,8 @@ export declare class OwnedUiTerminalHost implements OwnedUiTerminalHostContract<
98
98
  active(): OwnedUiHostedPresenter | null;
99
99
  requestRender(from: OwnedUiHostedPresenter, force?: boolean): void;
100
100
  start(): void;
101
+ /** Hidden resident tabs retain engine/status work but stop producing terminal animation frames. */
102
+ setPresentationVisible(visible: boolean): void;
101
103
  /** Closes one presenter. Closing the last one ends the terminal. */
102
104
  close(presenter: OwnedUiHostedPresenter): Promise<void>;
103
105
  dispose(): Promise<void>;
@@ -42,6 +42,8 @@ export class OwnedUiTerminalHost {
42
42
  #focused = false;
43
43
  #pointerReporting = false;
44
44
  #started = false;
45
+ #presentationVisible = true;
46
+ #releaseVisibilityHold;
45
47
  #ending;
46
48
  constructor(options) {
47
49
  this.#customViewport = options.layout === "custom-viewport";
@@ -147,7 +149,7 @@ export class OwnedUiTerminalHost {
147
149
  return this.#active;
148
150
  }
149
151
  requestRender(from, force = false) {
150
- if (from === this.#active)
152
+ if (this.#presentationVisible && from === this.#active)
151
153
  this.runtime.requestRender(force);
152
154
  }
153
155
  start() {
@@ -155,9 +157,26 @@ export class OwnedUiTerminalHost {
155
157
  return;
156
158
  this.#started = true;
157
159
  this.runtime.start();
160
+ if (!this.#presentationVisible)
161
+ this.#releaseVisibilityHold = this.runtime.beginPresentationHold();
158
162
  if (this.#customViewport)
159
163
  this.#setPointerReporting(true);
160
164
  }
165
+ /** Hidden resident tabs retain engine/status work but stop producing terminal animation frames. */
166
+ setPresentationVisible(visible) {
167
+ if (this.#presentationVisible === visible)
168
+ return;
169
+ this.#presentationVisible = visible;
170
+ if (!this.#started || this.#ending !== undefined)
171
+ return;
172
+ if (visible) {
173
+ this.#releaseVisibilityHold?.();
174
+ this.#releaseVisibilityHold = undefined;
175
+ }
176
+ else if (this.#releaseVisibilityHold === undefined) {
177
+ this.#releaseVisibilityHold = this.runtime.beginPresentationHold();
178
+ }
179
+ }
161
180
  /** Closes one presenter. Closing the last one ends the terminal. */
162
181
  close(presenter) {
163
182
  if (this.#ending !== undefined || !this.#presenters.has(presenter))
@@ -1,7 +1,8 @@
1
- import { type InteractiveLaunchIntent, type LaunchProfileId, type SessionSelection } from "../features/launch/index.js";
1
+ import { type InteractiveLaunchIntent, type InteractiveLaunchMode, type LaunchProfileId, type SessionSelection } from "../features/launch/index.js";
2
2
  import type { CliCapabilities } from "./capabilities.js";
3
3
  import type { PackageCommandRequest } from "./packages.js";
4
4
  import type { SessionContextRequest } from "./session-context.js";
5
+ import type { TabsRequest } from "./tabs-request.js";
5
6
  import { type PackageCliVerb, type PackageCommandNamespace, type PackageSyntaxDiagnostic } from "./package-messages.js";
6
7
  export type UpdateChannel = "stable" | "next";
7
8
  export interface CliHandlers {
@@ -10,6 +11,7 @@ export interface CliHandlers {
10
11
  readonly update: (channel: UpdateChannel, target?: string) => Promise<number>;
11
12
  readonly packages: (request: PackageCommandRequest) => Promise<number>;
12
13
  readonly sessionContext: (request: SessionContextRequest) => Promise<number>;
14
+ readonly tabs: (request: TabsRequest) => Promise<number>;
13
15
  }
14
16
  export interface CliOutput {
15
17
  readonly stdout: (message: string) => void;
@@ -17,6 +19,8 @@ export interface CliOutput {
17
19
  }
18
20
  export declare function cliUsage(capabilities: CliCapabilities): string;
19
21
  export declare function cliHelp(capabilities: CliCapabilities): string;
22
+ /** Focused help for the resident-tab maintenance forms; printing it contacts no host. */
23
+ export declare function tabsHelp(): string;
20
24
  export declare function dispatchCli(arguments_: readonly string[], handlers: CliHandlers, output: CliOutput, capabilities: CliCapabilities): Promise<number>;
21
25
  export type CliCommand = {
22
26
  readonly kind: "noop";
@@ -33,6 +37,7 @@ export type CliCommand = {
33
37
  } | {
34
38
  readonly kind: "launch";
35
39
  readonly profileId: LaunchProfileId;
40
+ readonly launchMode: InteractiveLaunchMode;
36
41
  readonly sessionSelection?: SessionSelection;
37
42
  } | {
38
43
  readonly kind: "version";
@@ -46,6 +51,11 @@ export type CliCommand = {
46
51
  } | {
47
52
  readonly kind: "session-context";
48
53
  readonly request: SessionContextRequest;
54
+ } | {
55
+ readonly kind: "tabs";
56
+ readonly request: TabsRequest;
57
+ } | {
58
+ readonly kind: "tabs-help";
49
59
  } | {
50
60
  readonly kind: "error";
51
61
  readonly message: string;
@@ -6,6 +6,7 @@ export function cliUsage(capabilities) {
6
6
  "",
7
7
  "--session <path|id>",
8
8
  "--session-dir <dir> --session <path|id>",
9
+ "solo",
9
10
  ...(capabilities.developmentComparison ? ["pi"] : []),
10
11
  "help",
11
12
  "version",
@@ -21,6 +22,10 @@ export function cliUsage(capabilities) {
21
22
  "session worktrees",
22
23
  "session link-worktree <path>",
23
24
  "session unlink-worktree",
25
+ "tabs",
26
+ "tabs stop <id>|--all",
27
+ "tabs host status|stop",
28
+ "tabs doctor",
24
29
  ]);
25
30
  }
26
31
  export function cliHelp(capabilities) {
@@ -30,6 +35,7 @@ export function cliHelp(capabilities) {
30
35
  ` ${command}`,
31
36
  ` ${command} --session <path|id>`,
32
37
  ` ${command} --session-dir <dir> --session <path|id>`,
38
+ ` ${command} solo`,
33
39
  ...(capabilities.developmentComparison ? [` ${command} pi`] : []),
34
40
  ` ${command} help`,
35
41
  ` ${command} version`,
@@ -52,9 +58,32 @@ export function cliHelp(capabilities) {
52
58
  ` ${command} session link-worktree <path>`,
53
59
  ` ${command} session unlink-worktree`,
54
60
  "",
61
+ "Tabs:",
62
+ ` ${command} tabs`,
63
+ ` ${command} tabs stop <id>|--all`,
64
+ ` ${command} tabs host status|stop`,
65
+ ` ${command} tabs doctor`,
66
+ "",
67
+ ].join("\n");
68
+ }
69
+ /** Focused help for the resident-tab maintenance forms; printing it contacts no host. */
70
+ export function tabsHelp() {
71
+ const command = PRODUCT_TEXT.commandName;
72
+ return [
73
+ "Resident tabs:",
74
+ ` ${command} tabs List running tabs`,
75
+ ` ${command} tabs stop <id> Stop one tab; its session stays resumable`,
76
+ ` ${command} tabs stop --all Stop every tab and keep the host`,
77
+ ` ${command} tabs host status Show host identity, limits, and health counters`,
78
+ ` ${command} tabs host stop Stop every tab and the host`,
79
+ ` ${command} tabs doctor Write a redacted diagnostic bundle`,
80
+ "",
81
+ "Options:",
82
+ " --json Print the versioned machine-readable report",
83
+ "",
55
84
  ].join("\n");
56
85
  }
57
- const PROFILE_WORDS = new Set(["pi"]);
86
+ const PROFILE_WORDS = new Set(["pi", "solo"]);
58
87
  export async function dispatchCli(arguments_, handlers, output, capabilities) {
59
88
  const command = parseCliCommand(arguments_, capabilities);
60
89
  if (command.kind === "noop")
@@ -76,23 +105,29 @@ export async function dispatchCli(arguments_, handlers, output, capabilities) {
76
105
  return 2;
77
106
  }
78
107
  if (command.kind === "launch")
79
- return await handlers.launch(interactiveLaunchIntent(command.profileId, command.sessionSelection));
108
+ return await handlers.launch(interactiveLaunchIntent(command.profileId, command.sessionSelection, command.launchMode));
80
109
  if (command.kind === "version")
81
110
  return await handlers.version();
82
111
  if (command.kind === "packages")
83
112
  return await handlers.packages(command.request);
84
113
  if (command.kind === "session-context")
85
114
  return await handlers.sessionContext(command.request);
115
+ if (command.kind === "tabs-help") {
116
+ output.stdout(tabsHelp());
117
+ return 0;
118
+ }
119
+ if (command.kind === "tabs")
120
+ return await handlers.tabs(command.request);
86
121
  return await handlers.update(command.channel, command.target);
87
122
  }
88
123
  export function parseCliCommand(arguments_, capabilities) {
89
124
  if (arguments_.length === 0)
90
- return { kind: "launch", profileId: "a1" };
125
+ return { kind: "launch", profileId: "a1", launchMode: "tabs" };
91
126
  const [command, ...rest] = arguments_;
92
127
  if (command === "--session" || command === "--session-dir") {
93
128
  try {
94
129
  const sessionSelection = parseSessionSelection(arguments_);
95
- return { kind: "launch", profileId: "a1", ...(sessionSelection === undefined ? {} : { sessionSelection }) };
130
+ return { kind: "launch", profileId: "a1", launchMode: "tabs", ...(sessionSelection === undefined ? {} : { sessionSelection }) };
96
131
  }
97
132
  catch (error) {
98
133
  return { kind: "error", message: PRODUCT_TEXT.diagnostic(`could not parse session launch: ${error instanceof Error ? error.message : String(error)}`) };
@@ -108,18 +143,57 @@ export function parseCliCommand(arguments_, capabilities) {
108
143
  if (command === "pi") {
109
144
  if (rest.length > 0)
110
145
  return parsePackageCommand(rest, "pi");
111
- return capabilities.developmentComparison ? { kind: "launch", profileId: "pi" } : { kind: "noop" };
146
+ return capabilities.developmentComparison ? { kind: "launch", profileId: "pi", launchMode: "solo" } : { kind: "noop" };
112
147
  }
148
+ if (command === "solo")
149
+ return withoutArguments(rest, { kind: "launch", profileId: "a1", launchMode: "solo" });
113
150
  if (command === "update")
114
151
  return parseUpdate(rest);
115
152
  if (command === "session")
116
153
  return parseSessionContext(rest);
154
+ if (command === "tabs")
155
+ return parseTabs(rest);
117
156
  if (command?.startsWith("update:"))
118
157
  return { kind: "noop" };
119
158
  // Rationale: unsupported and reserved command spaces are deliberately quiet. Help is
120
159
  // explicit, and a typo must never start an interactive or maintenance path.
121
160
  return { kind: "noop" };
122
161
  }
162
+ /** Parses `a1 tabs` before any I/O; a malformed form fails with one line and contacts no host. */
163
+ function parseTabs(rest) {
164
+ if (rest.some(value => value === "--help" || value === "-h"))
165
+ return { kind: "tabs-help" };
166
+ const json = rest.includes("--json");
167
+ const words = rest.filter(value => value !== "--json");
168
+ const fail = (detail) => ({ kind: "error", message: PRODUCT_TEXT.diagnostic(`could not parse tabs: ${detail}`) });
169
+ const [action, value, extra] = words;
170
+ if (action === undefined)
171
+ return { kind: "tabs", request: { action: "list", json } };
172
+ if (action === "stop") {
173
+ if (value === undefined)
174
+ return fail("stop requires a tab id or --all.");
175
+ if (extra !== undefined)
176
+ return fail("stop accepts one tab id or --all.");
177
+ if (value === "--all")
178
+ return { kind: "tabs", request: { action: "stop-all", json } };
179
+ if (value.startsWith("-"))
180
+ return fail(`unknown option ${value}.`);
181
+ return { kind: "tabs", request: { action: "stop", tabId: value, json } };
182
+ }
183
+ if (action === "host") {
184
+ if (extra !== undefined || (value !== "status" && value !== "stop"))
185
+ return fail("host accepts status or stop.");
186
+ return { kind: "tabs", request: { action: value === "status" ? "host-status" : "host-stop", json } };
187
+ }
188
+ if (action === "doctor") {
189
+ if (value !== undefined)
190
+ return fail("doctor does not accept arguments.");
191
+ return { kind: "tabs", request: { action: "doctor", json } };
192
+ }
193
+ if (action.startsWith("-"))
194
+ return fail(`unknown option ${action}.`);
195
+ return fail(`unknown subcommand ${action}; use list, stop, host, or doctor.`);
196
+ }
123
197
  function parseSessionContext(rest) {
124
198
  const [action, value, extra] = rest;
125
199
  if (action === "link-worktree") {
@@ -0,0 +1,7 @@
1
+ export type TabsAction = "list" | "stop" | "stop-all" | "host-status" | "host-stop" | "doctor";
2
+ /** One parsed `a1 tabs` form. Parsing finishes before any host is contacted. */
3
+ export interface TabsRequest {
4
+ readonly action: TabsAction;
5
+ readonly tabId?: string;
6
+ readonly json: boolean;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { runTerminalHostMaintenance, writeDoctorBundle } from "../foundation/resident-tabs/index.js";
2
+ import type { TabsRequest } from "./tabs-request.js";
3
+ export type { TabsRequest } from "./tabs-request.js";
4
+ export interface TabsCommandOptions {
5
+ /** The running package root; its terminal host is a candidate after the active release. */
6
+ readonly packageRoot?: string;
7
+ readonly environment?: NodeJS.ProcessEnv;
8
+ readonly stdout?: (message: string) => void;
9
+ readonly stderr?: (message: string) => void;
10
+ readonly maintenance?: typeof runTerminalHostMaintenance;
11
+ readonly doctor?: typeof writeDoctorBundle;
12
+ }
13
+ /** `a1 tabs`: inspect or stop resident tabs without attaching, and never start a host to report state. */
14
+ export declare function runTabsCommand(request: TabsRequest, options?: TabsCommandOptions): Promise<number>;