@timurproko/a1 0.1.8-dev.157 → 0.1.8-dev.214

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 (171) hide show
  1. package/README.md +32 -0
  2. package/bin/cli.js +2 -2
  3. package/bin/sync-pi-tui-proxy.js +1 -1
  4. package/bin/ui.js +18 -3
  5. package/dist/cli/dispatch.js +3 -3
  6. package/dist/cli/packages.js +1 -1
  7. package/dist/cli/version-stats.js +3 -3
  8. package/dist/composition/owned-ui.d.ts +4 -1
  9. package/dist/composition/owned-ui.js +9 -6
  10. package/dist/composition/settings-route-host.js +6 -4
  11. package/dist/contracts/agent-engine/capability-ports.d.ts +2 -2
  12. package/dist/contracts/agent-engine/domain-validation.js +22 -4
  13. package/dist/contracts/agent-engine/domain.d.ts +17 -0
  14. package/dist/contracts/owned-ui/model.d.ts +8 -0
  15. package/dist/contracts/owned-ui/validation.js +11 -0
  16. package/dist/features/launch/development-launch.js +1 -1
  17. package/dist/features/owned-ui/customization.d.ts +2 -0
  18. package/dist/features/owned-ui/customization.js +2 -0
  19. package/dist/features/owned-ui/diagnostics.d.ts +1 -0
  20. package/dist/features/owned-ui/diagnostics.js +1 -0
  21. package/dist/features/owned-ui/index.d.ts +1 -0
  22. package/dist/features/owned-ui/index.js +1 -0
  23. package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
  24. package/dist/features/owned-ui/project-trust-prompt.js +149 -0
  25. package/dist/features/owned-ui/settings-app.js +69 -44
  26. package/dist/features/workspace/reducer.d.ts +1 -0
  27. package/dist/features/workspace/reducer.js +1 -0
  28. package/dist/features/workspace/router.d.ts +1 -0
  29. package/dist/features/workspace/router.js +1 -0
  30. package/dist/features/workspace/store.d.ts +1 -0
  31. package/dist/features/workspace/store.js +1 -0
  32. package/dist/foundation/launch-guardian/main.js +1 -1
  33. package/dist/foundation/native-host-protocol/codec.d.ts +2 -0
  34. package/dist/foundation/native-host-protocol/codec.js +2 -0
  35. package/dist/foundation/process-containment/linux-process-inspector.d.ts +1 -0
  36. package/dist/foundation/process-containment/linux-process-inspector.js +1 -0
  37. package/dist/foundation/process-containment/native-guardian-containment.d.ts +1 -0
  38. package/dist/foundation/process-containment/native-guardian-containment.js +1 -0
  39. package/dist/foundation/process-containment/windows-process-inspector.d.ts +1 -0
  40. package/dist/foundation/process-containment/windows-process-inspector.js +1 -0
  41. package/dist/foundation/protocol/client.d.ts +1 -0
  42. package/dist/foundation/protocol/client.js +1 -0
  43. package/dist/foundation/protocol/messages.d.ts +1 -0
  44. package/dist/foundation/protocol/messages.js +1 -0
  45. package/dist/foundation/release/bootstrap.js +6 -6
  46. package/dist/foundation/release/cohort-state.d.ts +1 -0
  47. package/dist/foundation/release/cohort-state.js +1 -0
  48. package/dist/foundation/release/release-gc.js +1 -1
  49. package/dist/foundation/release/release.js +1 -1
  50. package/dist/foundation/release/update-transaction.d.ts +1 -0
  51. package/dist/foundation/release/update-transaction.js +1 -0
  52. package/dist/foundation/release/update.js +12 -12
  53. package/dist/foundation/storage/control-store.d.ts +1 -0
  54. package/dist/foundation/storage/control-store.js +1 -0
  55. package/dist/foundation/structured-agent-runtime/backpressure.d.ts +1 -0
  56. package/dist/foundation/structured-agent-runtime/backpressure.js +1 -0
  57. package/dist/foundation/structured-agent-runtime/commands.d.ts +1 -0
  58. package/dist/foundation/structured-agent-runtime/commands.js +1 -0
  59. package/dist/foundation/structured-agent-runtime/reconnection.d.ts +1 -0
  60. package/dist/foundation/structured-agent-runtime/reconnection.js +1 -0
  61. package/dist/foundation/structured-agent-runtime/state.d.ts +1 -0
  62. package/dist/foundation/structured-agent-runtime/state.js +1 -0
  63. package/dist/foundation/supervision/main.js +1 -1
  64. package/dist/foundation/supervision/server.d.ts +1 -0
  65. package/dist/foundation/supervision/server.js +4 -6
  66. package/dist/integrations/pi/components/conformance.d.ts +1 -0
  67. package/dist/integrations/pi/components/conformance.js +1 -0
  68. package/dist/integrations/pi/components/shell-components.d.ts +1 -0
  69. package/dist/integrations/pi/components/shell-components.js +1 -0
  70. package/dist/integrations/pi/components/shell-editor-autocomplete.js +10 -0
  71. package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
  72. package/dist/integrations/pi/components/shell-footer-status.js +19 -13
  73. package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
  74. package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
  75. package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +3 -36
  76. package/dist/integrations/pi/components/shell-presenters-transcript.js +88 -128
  77. package/dist/integrations/pi/components/shell-shared-facade.d.ts +11 -1
  78. package/dist/integrations/pi/components/shell-shared-facade.js +1 -1
  79. package/dist/integrations/pi/engine/adapter.d.ts +22 -4
  80. package/dist/integrations/pi/engine/adapter.js +220 -70
  81. package/dist/integrations/pi/engine/conformance.d.ts +2 -0
  82. package/dist/integrations/pi/engine/conformance.js +2 -0
  83. package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
  84. package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
  85. package/dist/integrations/pi/engine/index.d.ts +3 -0
  86. package/dist/integrations/pi/engine/index.js +3 -0
  87. package/dist/integrations/pi/engine/model-auth-integration.d.ts +1 -0
  88. package/dist/integrations/pi/engine/model-auth-integration.js +1 -0
  89. package/dist/integrations/pi/engine/package-integration.js +1 -1
  90. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
  91. package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
  92. package/dist/integrations/pi/engine/resource-extension-integration.d.ts +1 -0
  93. package/dist/integrations/pi/engine/resource-extension-integration.js +1 -0
  94. package/dist/integrations/pi/engine/runtime-integration.d.ts +16 -1
  95. package/dist/integrations/pi/engine/runtime-integration.js +34 -4
  96. package/dist/integrations/pi/engine/session-integration.d.ts +1 -0
  97. package/dist/integrations/pi/engine/session-integration.js +2 -1
  98. package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
  99. package/dist/integrations/pi/engine/settings-effects.js +249 -0
  100. package/dist/integrations/pi/engine/settings-integration.d.ts +14 -26
  101. package/dist/integrations/pi/engine/settings-integration.js +102 -108
  102. package/dist/integrations/pi/engine/workflow-controllers.d.ts +2 -1
  103. package/dist/integrations/pi/engine/workflow-controllers.js +1 -0
  104. package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
  105. package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
  106. package/dist/integrations/pi/session-ui/index.d.ts +1 -0
  107. package/dist/integrations/pi/session-ui/index.js +1 -0
  108. package/dist/integrations/pi/session-ui/prompt-chips.js +6 -2
  109. package/dist/integrations/pi/session-ui/session-shell-root.d.ts +39 -4
  110. package/dist/integrations/pi/session-ui/session-shell-root.js +215 -80
  111. package/dist/integrations/pi/session-ui/session-shell.d.ts +10 -1
  112. package/dist/integrations/pi/session-ui/session-shell.js +256 -45
  113. package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +5 -0
  114. package/dist/integrations/pi/session-ui/session-viewport-controller.js +76 -11
  115. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
  116. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
  117. package/dist/integrations/pi/session-ui/system-clipboard.d.ts +9 -0
  118. package/dist/integrations/pi/session-ui/system-clipboard.js +38 -9
  119. package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
  120. package/dist/integrations/pi/tui-runtime/adapter.js +166 -8
  121. package/dist/integrations/pi/tui-runtime/contracts.d.ts +25 -0
  122. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +63 -0
  123. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +240 -0
  124. package/dist/integrations/pi/tui-runtime/index.d.ts +2 -0
  125. package/dist/integrations/pi/tui-runtime/index.js +2 -0
  126. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
  127. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
  128. package/dist/native/darwin-arm64/manifest.json +1 -1
  129. package/dist/native/linux-x64/manifest.json +1 -1
  130. package/dist/native/win32-x64/manifest.json +2 -2
  131. package/dist/native/win32-x64/process-guardian.exe +0 -0
  132. package/dist/ui/apps/host.js +5 -9
  133. package/dist/ui/apps/registry.d.ts +1 -0
  134. package/dist/ui/apps/registry.js +1 -0
  135. package/dist/ui/components/dialog-panel.js +5 -3
  136. package/dist/ui/components/frame.d.ts +1 -0
  137. package/dist/ui/components/frame.js +1 -0
  138. package/dist/ui/components/index.d.ts +1 -0
  139. package/dist/ui/components/index.js +1 -0
  140. package/dist/ui/components/line-input.d.ts +1 -6
  141. package/dist/ui/components/line-input.js +4 -9
  142. package/dist/ui/components/list-view.d.ts +0 -6
  143. package/dist/ui/components/list-view.js +14 -7
  144. package/dist/ui/components/progress-status.d.ts +2 -0
  145. package/dist/ui/components/progress-status.js +4 -0
  146. package/dist/ui/components/scrollbar.js +1 -1
  147. package/dist/ui/components/shortcuts.d.ts +1 -0
  148. package/dist/ui/components/shortcuts.js +1 -0
  149. package/dist/ui/components/spans.d.ts +2 -5
  150. package/dist/ui/components/spans.js +28 -8
  151. package/dist/ui/components/surface.d.ts +0 -5
  152. package/dist/ui/components/surface.js +1 -5
  153. package/dist/ui/components/text-selection.d.ts +10 -1
  154. package/dist/ui/components/text-selection.js +96 -29
  155. package/dist/ui/components/text.js +3 -3
  156. package/dist/ui/components/transcript-viewport.d.ts +42 -0
  157. package/dist/ui/components/transcript-viewport.js +287 -39
  158. package/dist/ui/settings/sections.d.ts +6 -19
  159. package/dist/ui/settings/sections.js +17 -15
  160. package/dist/ui/settings/session.d.ts +8 -18
  161. package/dist/ui/settings/session.js +60 -52
  162. package/dist/ui/settings/store.d.ts +1 -0
  163. package/dist/ui/settings/store.js +2 -1
  164. package/docs/architecture/code-documentation.md +77 -0
  165. package/docs/architecture/project-structure.md +15 -1
  166. package/docs/architecture/ui-reference-provenance.md +23 -3
  167. package/docs/ci-release-runbook.md +98 -7
  168. package/docs/manual-owned-ui-checkpoint.md +28 -1
  169. package/docs/manual-terminal-colour-check.md +4 -3
  170. package/docs/repository-governance-live-acceptance.md +77 -0
  171. package/package.json +11 -3
@@ -96,18 +96,18 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
96
96
  await removeEndpointArtifacts(owner.paths.endpointMetadataPath, owner.paths.endpoint);
97
97
  return { priorActiveVersion };
98
98
  }
99
- // Leaving sessions alone is the expected outcome, and saying so mid-update tears
99
+ // Rationale: leaving sessions alone is the expected outcome, and saying so mid-update tears
100
100
  // the progress bar; only ending a session (below) is worth interrupting it for.
101
101
  if (plan === "leave-running")
102
102
  return { priorActiveVersion };
103
- // Say whose work is ending before it ends.
103
+ // Rationale: say whose work is ending before it ends.
104
104
  const live = endpoint.ownership.liveInstanceIds.length;
105
105
  if (live > 0) {
106
106
  output.stderr(`${PRODUCT_TEXT.diagnostic(`ending ${live === 1 ? "one session" : `${live} sessions`} that run from the installation being replaced; a session started by an installed release would have been left alone.`)}\n`);
107
107
  }
108
108
  const identity = await requestUpdateShutdown(endpoint, targetVersion, 2_000);
109
109
  if (!identity.accepted && processIsAlive(endpoint.pid)) {
110
- // Explicit update consent permits bounded cleanup of an authenticated
110
+ // Security: explicit update consent permits bounded cleanup of an authenticated
111
111
  // older owner that predates the update-shutdown message.
112
112
  const cleanup = await cleanupVerifiedOwner(endpoint, { allowLiveInstances: true, reason: "legacy-mutable-install" });
113
113
  if (!cleanup.terminated)
@@ -133,7 +133,7 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
133
133
  await rename(probe, packageRoot);
134
134
  }
135
135
  catch (error) {
136
- // Best-effort rollback if the first rename succeeded and the second did not.
136
+ // Invariant: best-effort rollback runs if the first rename succeeded and the second did not.
137
137
  await rename(probe, packageRoot).catch(() => { });
138
138
  throw new Error(PRODUCT_TEXT.diagnostic(`package remains locked after verified shutdown: ${errorMessage(error)}`));
139
139
  }
@@ -214,7 +214,7 @@ function createUpdateProgress(output, enabled) {
214
214
  draw();
215
215
  if (creepTo <= current)
216
216
  return;
217
- // Creep asymptotically toward (but never reach) the next milestone so
217
+ // Rationale: creep asymptotically toward (but never reach) the next milestone so
218
218
  // long opaque phases such as npm install still show visible motion. The
219
219
  // ceiling stays a whole point below the milestone: settling on `creepTo`
220
220
  // itself would render as that milestone and make arriving at it invisible,
@@ -225,7 +225,7 @@ function createUpdateProgress(output, enabled) {
225
225
  }, PROGRESS_TICK_MS);
226
226
  timer.unref?.();
227
227
  },
228
- // The bar exists to say the update is still moving. Once it has finished
228
+ // Rationale: the bar exists to say the update is still moving. Once it has finished
229
229
  // there is a better line to occupy that row — the one naming what is now
230
230
  // installed — so the bar gives the row back rather than leaving a full
231
231
  // meter above a message that already implies it.
@@ -283,7 +283,7 @@ async function resolveRequestedPreview(runner, requested, output) {
283
283
  const versions = (Array.isArray(published) ? published : [published]).filter((value) => typeof value === "string");
284
284
  const exact = versions.find(version => version === requested);
285
285
  if (exact !== undefined) {
286
- // Naming a release here would install it through the preview path, which is a
286
+ // Rationale: naming a release here would install it through the preview path, which is a
287
287
  // different command with a different meaning.
288
288
  if (!exact.includes("-dev.")) {
289
289
  output.stderr(`${PRODUCT_TEXT.diagnostic(`${exact} is a release, not a preview; run ${PRODUCT_TEXT.commandName} update to move to the current release.`)}\n`);
@@ -336,7 +336,7 @@ export async function runSelfUpdate(options) {
336
336
  if (resolved.version === null)
337
337
  return resolved.exitCode;
338
338
  const targetVersion = resolved.version;
339
- // No full stop after a version: it already ends in a dot-separated identifier,
339
+ // Rationale: no full stop after a version: it already ends in a dot-separated identifier,
340
340
  // and a trailing one reads as part of the version rather than as punctuation.
341
341
  output.stdout(`${PRODUCT_TEXT.commandName} update: ${runningVersion} → ${targetVersion}\n`);
342
342
  const progress = createUpdateProgress(output, options.progress ?? (options.output === undefined && process.stdout.isTTY === true));
@@ -375,7 +375,7 @@ export async function runSelfUpdate(options) {
375
375
  output.stdout(`${PRODUCT_TEXT.commandName} is up to date — no update needed.\n`);
376
376
  return 0;
377
377
  }
378
- // The bar first appears here so a no-change run never flashes it.
378
+ // Rationale: the bar first appears here so a no-change run never flashes it.
379
379
  progress.set(3, 15);
380
380
  const cohortState = await new CohortStateStore(paths.dataDir).read();
381
381
  transaction = await transactionStore.begin({
@@ -403,7 +403,7 @@ export async function runSelfUpdate(options) {
403
403
  }
404
404
  transaction = await transactionStore.advance("package-installed");
405
405
  }
406
- // npm 12 blocks install scripts unless allowScripts covers the package, so
406
+ // Compatibility: npm 12 blocks install scripts unless allowScripts covers the package, so
407
407
  // the postinstall that points the #pi-tui proxy at the tree npm just built
408
408
  // may never have run. Run the shipped script directly: the proxy must be
409
409
  // correct before the release store copies this tree. A failure is reported
@@ -417,7 +417,7 @@ export async function runSelfUpdate(options) {
417
417
  output.stderr(`${PRODUCT_TEXT.diagnostic(`could not point the #pi-tui proxy at the installed tree: ${errorMessage(error)}`)}\n`);
418
418
  }
419
419
  progress.set(70, 75);
420
- // Ownership can be reacquired after an interrupted installation (for
420
+ // Concurrency: ownership can be reacquired after an interrupted installation (for
421
421
  // example, if bare A1 is launched before the update is resumed). Recheck
422
422
  // immediately before activation so recovery cannot start a second cohort.
423
423
  await measure("ownership-release", async () => { await lifecycle.shutdownVerifiedOwners(targetVersion); });
@@ -530,7 +530,7 @@ async function rollbackPriorCohort(dataDir, environment, priorReleaseId) {
530
530
  }
531
531
  const release = await readMaterializedRelease(prior.releaseRoot);
532
532
  const paths = resolveProductPaths(environment);
533
- // Rollback re-points the active reference and starts the prior cohort on its own endpoint;
533
+ // Invariant: rollback re-points the active reference and starts the prior cohort on its own endpoint;
534
534
  // a cohort that survived the update keeps serving the work it already had.
535
535
  await startSupervisor(release, environment);
536
536
  await waitForVerifiedEndpoint(resolveCohortEndpoint(paths, release.releaseId, environment).endpointMetadataPath, release, 8_000);
@@ -14,6 +14,7 @@ export interface StoredWorkspaceAgent {
14
14
  readonly agent: ManagedAgentDescriptor;
15
15
  readonly presentation: WorkspaceAgentPresentation;
16
16
  }
17
+ /** Owns WAL-backed control persistence and reconciles prior-boot records before current use. */
17
18
  export declare class ControlStore {
18
19
  #private;
19
20
  readonly bootNonce: string | null;
@@ -6,6 +6,7 @@ import { assertManagedAgentDescriptor, assertRecoveryAuthority, assertTerminalSe
6
6
  import { assertLaunchInstance, assertLaunchInstanceOutcome, assertNativeProcessIdentity, assertProcessContainmentIdentity, } from "../lifecycle/index.js";
7
7
  export const DEFAULT_WORKSPACE_ID = "workspace-default";
8
8
  const INITIAL_WORKSPACE_ID = DEFAULT_WORKSPACE_ID;
9
+ /** Owns WAL-backed control persistence and reconciles prior-boot records before current use. */
9
10
  export class ControlStore {
10
11
  bootNonce;
11
12
  database;
@@ -33,6 +33,7 @@ export type StructuredBackpressureReleaseResult = {
33
33
  readonly code: "unknown-lease" | "invalid-request";
34
34
  readonly diagnostic: string;
35
35
  };
36
+ /** Issues bounded resource leases and selects the negotiated backpressure action on exhaustion. */
36
37
  export declare class StructuredBackpressureController {
37
38
  #private;
38
39
  readonly adapterId: string;
@@ -1,4 +1,5 @@
1
1
  import { assertStructuredCapability, } from "../../contracts/workspace/index.js";
2
+ /** Issues bounded resource leases and selects the negotiated backpressure action on exhaustion. */
2
3
  export class StructuredBackpressureController {
3
4
  adapterId;
4
5
  #budgets;
@@ -38,6 +38,7 @@ export type StructuredCommandOutcomeResult = {
38
38
  readonly code: string;
39
39
  readonly diagnostic: string;
40
40
  };
41
+ /** Tracks structured commands by correlation identity through one idempotent terminal outcome. */
41
42
  export declare class StructuredCommandTracker {
42
43
  #private;
43
44
  readonly agentId: string;
@@ -1,4 +1,5 @@
1
1
  import { assertStructuredCapability, assertWorkspaceCommand, } from "../../contracts/workspace/index.js";
2
+ /** Tracks structured commands by correlation identity through one idempotent terminal outcome. */
2
3
  export class StructuredCommandTracker {
3
4
  agentId;
4
5
  #capability;
@@ -30,6 +30,7 @@ export type StructuredReconnectionResult = {
30
30
  readonly code: string;
31
31
  readonly diagnostic: string;
32
32
  };
33
+ /** Accepts a recovery boundary once after validating negotiated identity, proof, and protocol continuity. */
33
34
  export declare class StructuredReconnectionManager {
34
35
  #private;
35
36
  constructor(capability: StructuredCapabilityContract);
@@ -1,5 +1,6 @@
1
1
  import { WORKSPACE_CONTRACT_VERSION, assertRecoveryAuthority, assertStructuredAgentSnapshot, assertStructuredCapability, } from "../../contracts/workspace/index.js";
2
2
  import { StructuredEventReducer } from "./state.js";
3
+ /** Accepts a recovery boundary once after validating negotiated identity, proof, and protocol continuity. */
3
4
  export class StructuredReconnectionManager {
4
5
  #capability;
5
6
  #acceptedBoundaries = new Set();
@@ -44,6 +44,7 @@ export type StructuredSnapshotApplyResult = {
44
44
  readonly code: string;
45
45
  readonly diagnostic: string;
46
46
  };
47
+ /** Applies ordered structured events and snapshots while exposing gaps as explicit resynchronization. */
47
48
  export declare class StructuredEventReducer {
48
49
  #private;
49
50
  readonly agentId: string;
@@ -1,4 +1,5 @@
1
1
  import { assertStructuredAgentSnapshot, assertStructuredCapability, assertWorkspaceEvent, } from "../../contracts/workspace/index.js";
2
+ /** Applies ordered structured events and snapshots while exposing gaps as explicit resynchronization. */
2
3
  export class StructuredEventReducer {
3
4
  agentId;
4
5
  #capability;
@@ -19,7 +19,7 @@ export async function runSupervisor() {
19
19
  throw new Error(PRODUCT_TEXT.diagnostic("supervisor must be launched from a verified immutable release"));
20
20
  const release = await readCertifiedReleaseManifest({ releaseRoot, releaseId, contentDigest }, resolve(paths.dataDir, "releases"));
21
21
  await assertImmutableExecutionRoot(release, paths.dataDir);
22
- // One endpoint per cohort: a superseded cohort keeps serving what it already has while the
22
+ // Protocol: one endpoint per cohort: a superseded cohort keeps serving what it already has while the
23
23
  // installed release listens on its own address.
24
24
  paths = { ...productPaths, ...resolveCohortEndpoint(productPaths, release.releaseId) };
25
25
  const bootNonce = randomUUID();
@@ -2,6 +2,7 @@ import { type SupervisorSnapshot } from "../lifecycle/index.js";
2
2
  import { type MaterializedRelease } from "../release/index.js";
3
3
  import { ControlStore } from "../storage/index.js";
4
4
  import { type ProductPaths } from "./paths.js";
5
+ /** Owns one release-cohort endpoint and the authenticated launch instances registered through it. */
5
6
  export declare class SupervisorServer {
6
7
  #private;
7
8
  readonly store: ControlStore;
@@ -8,6 +8,7 @@ import { processIsAlive } from "../release/index.js";
8
8
  import { ControlStore } from "../storage/index.js";
9
9
  import { resolveProductPaths } from "./paths.js";
10
10
  import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
11
+ /** Owns one release-cohort endpoint and the authenticated launch instances registered through it. */
11
12
  export class SupervisorServer {
12
13
  store;
13
14
  release;
@@ -90,10 +91,7 @@ export class SupervisorServer {
90
91
  this.#supersededPoll = poll;
91
92
  }
92
93
  }
93
- /**
94
- * Notices that another release has become the one new sessions start on. Nothing is
95
- * interrupted by that: this cohort finishes what it is holding and then leaves.
96
- */
94
+ // Invariant: superseding a cohort never interrupts its held instances; it retires only when empty.
97
95
  async #refreshCohortRole() {
98
96
  if (this.#closing || !this.readActiveReleaseId)
99
97
  return;
@@ -327,7 +325,7 @@ export class SupervisorServer {
327
325
  this.#uncertainInstances.delete(stored.id);
328
326
  this.#unbindOwner(stored.id, socket);
329
327
  this.#mutated();
330
- // The last session on a superseded cohort has left; there is nothing here to keep.
328
+ // Invariant: the last session on a superseded cohort has left; there is nothing here to keep.
331
329
  if (this.#superseded && this.#instances.size === 0)
332
330
  void this.retire();
333
331
  }
@@ -421,7 +419,7 @@ export class SupervisorServer {
421
419
  const deadline = Date.now() + timeoutMs;
422
420
  while (Date.now() < deadline) {
423
421
  if (this.#instances.size === 0) {
424
- // Let the terminal command result flush before closing owner sockets.
422
+ // Concurrency: allow the terminal command result to flush before closing owner sockets.
425
423
  await new Promise(resolvePromise => setTimeout(resolvePromise, 25));
426
424
  return this.#instances.size === 0;
427
425
  }
@@ -10,6 +10,7 @@ export interface PiComponentConformanceReport {
10
10
  readonly componentResults: readonly PiComponentConformanceResult[];
11
11
  readonly componentFamilies: readonly string[];
12
12
  }
13
+ /** Identifies the Pi presentation-conformance stage that failed against the pinned component surface. */
13
14
  export declare class PiComponentConformanceError extends Error {
14
15
  readonly stage: "theme" | "components";
15
16
  constructor(stage: "theme" | "components", cause: unknown);
@@ -5,6 +5,7 @@ import { AssistantMessageComponent, ExtensionSelectorComponent, initTheme, Login
5
5
  import { OWNED_UI_CONTRACT_VERSION } from "../../../contracts/owned-ui/index.js";
6
6
  import { createTuiFacade, validatedAssistantMessage } from "./shell-components.js";
7
7
  import { PRODUCT_IDENTITY } from "../../../product-identity.js";
8
+ /** Identifies the Pi presentation-conformance stage that failed against the pinned component surface. */
8
9
  export class PiComponentConformanceError extends Error {
9
10
  stage;
10
11
  constructor(stage, cause) {
@@ -2,5 +2,6 @@ export * from "./shell-shared-facade.js";
2
2
  export * from "./shell-editor-autocomplete.js";
3
3
  export * from "./shell-selectors-dialogs.js";
4
4
  export * from "./shell-presenters-transcript.js";
5
+ export * from "./shell-presenters-info.js";
5
6
  export * from "./shell-footer-status.js";
6
7
  export * from "./shell-extension-ui.js";
@@ -2,5 +2,6 @@ export * from "./shell-shared-facade.js";
2
2
  export * from "./shell-editor-autocomplete.js";
3
3
  export * from "./shell-selectors-dialogs.js";
4
4
  export * from "./shell-presenters-transcript.js";
5
+ export * from "./shell-presenters-info.js";
5
6
  export * from "./shell-footer-status.js";
6
7
  export * from "./shell-extension-ui.js";
@@ -158,6 +158,16 @@ export function createPiShellEditor(options) {
158
158
  setSubmitHandler: handler => { submitHandler = handler; },
159
159
  setInterruptHandler: handler => { interruptHandler = handler; },
160
160
  setAutocompleteCommands,
161
+ setPaddingX(padding) {
162
+ editor.setPaddingX(padding);
163
+ editor.invalidate();
164
+ tui.requestRender();
165
+ },
166
+ setAutocompleteMaxVisible(maxVisible) {
167
+ editor.setAutocompleteMaxVisible(maxVisible);
168
+ editor.invalidate();
169
+ tui.requestRender();
170
+ },
161
171
  addAutocompleteProvider(factory) {
162
172
  if (typeof factory !== "function")
163
173
  throw new TypeError("extension autocomplete factory must be a function");
@@ -2,6 +2,6 @@ import type { OwnedUiSessionViewModel } from "../../../contracts/owned-ui/index.
2
2
  import { type PiShellViewComponentPort, type PiShellStatusPort, type PiShellQueuedInputPort, type PiShellHeaderPort, type PiShellResourceEntry, type PiShellLoadedResourcesPort, type PiShellHeaderOptions, type PiShellEditorOptions } from "./shell-shared-facade.js";
3
3
  export declare function createPiShellHeader(options?: PiShellHeaderOptions): PiShellHeaderPort;
4
4
  export declare function createPiShellLoadedResources(resources: readonly PiShellResourceEntry[], initialExpanded?: boolean): PiShellLoadedResourcesPort;
5
- export declare function createPiShellStatus(view: OwnedUiSessionViewModel, runtime?: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">): PiShellStatusPort;
5
+ export declare function createPiShellStatus(view: OwnedUiSessionViewModel, formatProgressStatus: (message: string) => string, runtime?: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">): PiShellStatusPort;
6
6
  export declare function createPiShellFooter(view: OwnedUiSessionViewModel, cwd: string): PiShellViewComponentPort;
7
7
  export declare function createPiQueuedInputStatus(submissions: readonly string[], presentation?: "pinned" | "custom-viewport"): PiShellQueuedInputPort;
@@ -67,20 +67,21 @@ export function createPiShellLoadedResources(resources, initialExpanded = false)
67
67
  invalidate() { },
68
68
  };
69
69
  }
70
- export function createPiShellStatus(view, runtime) {
70
+ export function createPiShellStatus(view, formatProgressStatus, runtime) {
71
71
  ensureTheme();
72
72
  const statusUi = createTuiFacade(runtime ?? { getColumns: () => 80, getRows: () => 24, requestRender() { } });
73
73
  let workingOverride;
74
- let component = statusComponent(view, statusUi, workingOverride);
75
- let signature = statusSignature(view, workingOverride);
74
+ let outputPad = PINNED_PI_LAYOUT.outputPad;
75
+ let component = statusComponent(view, statusUi, workingOverride, outputPad, formatProgressStatus);
76
+ let signature = statusSignature(view, workingOverride, outputPad);
76
77
  const rebuild = () => {
77
- const nextSignature = statusSignature(view, workingOverride);
78
+ const nextSignature = statusSignature(view, workingOverride, outputPad);
78
79
  if (nextSignature === signature)
79
80
  return;
80
81
  if (component !== undefined && "dispose" in component && typeof component.dispose === "function")
81
82
  component.dispose();
82
83
  signature = nextSignature;
83
- component = statusComponent(view, statusUi, workingOverride);
84
+ component = statusComponent(view, statusUi, workingOverride, outputPad, formatProgressStatus);
84
85
  };
85
86
  return {
86
87
  render: width => component?.render(width) ?? [],
@@ -93,6 +94,10 @@ export function createPiShellStatus(view, runtime) {
93
94
  workingOverride = message;
94
95
  rebuild();
95
96
  },
97
+ setOutputPad(padding) {
98
+ outputPad = padding;
99
+ rebuild();
100
+ },
96
101
  dispose() {
97
102
  if (component !== undefined && "dispose" in component && typeof component.dispose === "function")
98
103
  component.dispose();
@@ -121,19 +126,20 @@ export function createPiQueuedInputStatus(submissions, presentation = "pinned")
121
126
  },
122
127
  };
123
128
  }
124
- function statusComponent(view, ui, workingOverride) {
125
- if (view.lifecycle === "busy")
126
- return new WorkingStatusIndicator(ui, workingOverride ?? view.status.workingMessage ?? "Working...");
129
+ function statusComponent(view, ui, workingOverride, outputPad, formatProgressStatus) {
130
+ if (view.lifecycle === "busy") {
131
+ return new WorkingStatusIndicator(ui, formatProgressStatus(workingOverride ?? view.status.workingMessage ?? "Working"));
132
+ }
127
133
  if (view.lifecycle === "failed") {
128
- return new Text(piTheme().fg("error", view.status.diagnostics.at(-1) ?? "Session failed"), PINNED_PI_LAYOUT.outputPad, 0);
134
+ return new Text(piTheme().fg("error", view.status.diagnostics.at(-1) ?? "Session failed"), outputPad, 0);
129
135
  }
130
136
  if (view.status.workingMessage !== null) {
131
- return new Text(piTheme().fg("muted", view.status.workingMessage), PINNED_PI_LAYOUT.outputPad, 0);
137
+ return new Text(piTheme().fg("muted", view.status.workingMessage), outputPad, 0);
132
138
  }
133
139
  return undefined;
134
140
  }
135
- function statusSignature(view, workingOverride) {
136
- return `${view.lifecycle}\u0000${workingOverride ?? ""}\u0000${view.status.workingMessage ?? ""}\u0000${view.status.diagnostics.at(-1) ?? ""}`;
141
+ function statusSignature(view, workingOverride, outputPad) {
142
+ return `${outputPad}\u0000${view.lifecycle}\u0000${workingOverride ?? ""}\u0000${view.status.workingMessage ?? ""}\u0000${view.status.diagnostics.at(-1) ?? ""}`;
137
143
  }
138
144
  function queuedInputText(submissions, presentation) {
139
145
  if (submissions.length === 0)
@@ -144,7 +150,7 @@ function queuedInputText(submissions, presentation) {
144
150
  }
145
151
  const messages = submissions.map(submission => theme.fg("dim", `Steering: ${submission.replaceAll("\n", " ⏎ ")}`));
146
152
  const dequeueHint = theme.fg("dim", "↳ Alt+Up to edit all queued messages");
147
- // The custom viewport matches Pi's interactive queue presentation while the
153
+ // Compatibility: the custom viewport matches Pi's interactive queue presentation while the
148
154
  // comparison shell remains byte-for-byte compatible with its pinned fixture.
149
155
  return ["", ...messages, dequeueHint].join("\n");
150
156
  }
@@ -0,0 +1,36 @@
1
+ import { type PiShellComponentPort } from "./shell-shared-facade.js";
2
+ export interface PiShellSessionInfoPresentation {
3
+ readonly sessionName?: string;
4
+ readonly stats: {
5
+ readonly sessionFile?: string;
6
+ readonly sessionId: string;
7
+ readonly userMessages: number;
8
+ readonly assistantMessages: number;
9
+ readonly toolCalls: number;
10
+ readonly toolResults: number;
11
+ readonly totalMessages: number;
12
+ readonly tokens: {
13
+ readonly input: number;
14
+ readonly output: number;
15
+ readonly cacheRead: number;
16
+ readonly cacheWrite: number;
17
+ readonly total: number;
18
+ };
19
+ readonly cost: number;
20
+ };
21
+ readonly cacheWaste: {
22
+ readonly missedTokens: number;
23
+ readonly missedCost: number;
24
+ readonly missCount: number;
25
+ };
26
+ readonly usageBreakdown: readonly {
27
+ readonly key: string;
28
+ readonly cost: number;
29
+ readonly tokens: number;
30
+ }[];
31
+ }
32
+ export declare function renderPiShellStatusText(message: string, width: number, outputPad?: 0 | 1): readonly string[];
33
+ export declare function createPiShellSessionInfo(presentation: PiShellSessionInfoPresentation): PiShellComponentPort;
34
+ export declare function createPiShellCollapsedChangelog(): PiShellComponentPort;
35
+ export declare function createPiShellChangelog(markdown: string): PiShellComponentPort;
36
+ export declare function createPiShellHotkeys(): PiShellComponentPort;
@@ -0,0 +1,78 @@
1
+ import { DynamicBorder, getMarkdownTheme } from "@earendil-works/pi-coding-agent";
2
+ import { Container, Markdown, Spacer, Text } from "#pi-tui";
3
+ import { KeybindingsManager } from "./upstream/adjacent/core/keybindings.js";
4
+ import { PINNED_PI_LAYOUT, piTheme } from "./theme.js";
5
+ import { componentPort, ensureTheme, formatSessionTokens } from "./shell-shared-facade.js";
6
+ export function renderPiShellStatusText(message, width, outputPad = PINNED_PI_LAYOUT.outputPad) {
7
+ ensureTheme();
8
+ return new Text(piTheme().fg("dim", message), outputPad, 0).render(width);
9
+ }
10
+ export function createPiShellSessionInfo(presentation) {
11
+ ensureTheme();
12
+ const { stats, sessionName, cacheWaste, usageBreakdown } = presentation;
13
+ let info = `${piTheme().bold("Session Info")}\n\n`;
14
+ if (sessionName)
15
+ info += `${piTheme().fg("dim", "Name:")} ${sessionName}\n`;
16
+ info += `${piTheme().fg("dim", "File:")} ${stats.sessionFile ?? "In-memory"}\n${piTheme().fg("dim", "ID:")} ${stats.sessionId}\n\n`;
17
+ info += `${piTheme().bold("Messages")}\n${piTheme().fg("dim", "Total:")} ${stats.totalMessages}\n${piTheme().fg("dim", "User:")} ${stats.userMessages}\n`;
18
+ info += `${piTheme().fg("dim", "Assistant:")} ${stats.assistantMessages}\n${piTheme().fg("dim", "Tools:")} ${stats.toolCalls} calls, ${stats.toolResults} results\n\n`;
19
+ info += `${piTheme().bold("Tokens")}\n`;
20
+ const { input, cacheRead, cacheWrite } = stats.tokens;
21
+ const promptTokens = input + cacheRead + cacheWrite;
22
+ info += `${piTheme().fg("dim", "Input:")} ${promptTokens.toLocaleString()}\n`;
23
+ if (promptTokens > 0 && (cacheRead > 0 || cacheWrite > 0)) {
24
+ info += ` ${piTheme().fg("dim", "Cached:")} ${cacheRead.toLocaleString()} ${piTheme().fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`)}\n`;
25
+ const written = cacheWrite > 0 ? ` ${piTheme().fg("dim", `(${cacheWrite.toLocaleString()} written to cache)`)}` : "";
26
+ info += ` ${piTheme().fg("dim", "Uncached:")} ${(input + cacheWrite).toLocaleString()}${written}\n`;
27
+ }
28
+ info += `${piTheme().fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n${piTheme().fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
29
+ if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
30
+ info += `\n${piTheme().bold("Cost")}\n${piTheme().fg("dim", "Total:")} $${stats.cost.toFixed(3)}`;
31
+ if (usageBreakdown.length > 1)
32
+ for (const entry of usageBreakdown)
33
+ info += `\n ${piTheme().fg("dim", `${entry.key}:`)} $${entry.cost.toFixed(3)} ${piTheme().fg("dim", `(${formatSessionTokens(entry.tokens)} tokens)`)}`;
34
+ if (cacheWaste.missedTokens > 0) {
35
+ const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${cacheWaste.missCount === 1 ? "1 miss" : `${cacheWaste.missCount} misses`}`;
36
+ info += cacheWaste.missedCost >= 0.0001 ? `\n${piTheme().fg("dim", "Cache Re-billed:")} $${cacheWaste.missedCost.toFixed(3)} ${piTheme().fg("dim", `(${detail})`)}` : `\n${piTheme().fg("dim", "Cache Re-billed:")} ${detail}`;
37
+ }
38
+ }
39
+ const container = new Container();
40
+ container.addChild(new Spacer(1));
41
+ container.addChild(new Text(info, 1, 0));
42
+ return componentPort(container);
43
+ }
44
+ export function createPiShellCollapsedChangelog() {
45
+ ensureTheme();
46
+ const container = new Container();
47
+ container.addChild(new Spacer(1));
48
+ container.addChild(new DynamicBorder());
49
+ container.addChild(new Text(`${piTheme().bold(piTheme().fg("accent", "What's New"))}\n${piTheme().fg("muted", "Run /changelog to view the full release notes.")}`, 1, 0));
50
+ container.addChild(new DynamicBorder());
51
+ return componentPort(container);
52
+ }
53
+ export function createPiShellChangelog(markdown) {
54
+ ensureTheme();
55
+ const container = new Container();
56
+ container.addChild(new Spacer(1));
57
+ container.addChild(new DynamicBorder());
58
+ container.addChild(new Text(piTheme().bold(piTheme().fg("accent", "What's New")), 1, 0));
59
+ container.addChild(new Spacer(1));
60
+ container.addChild(new Markdown(markdown.trim() || "No changelog entries found.", 1, 1, getMarkdownTheme()));
61
+ container.addChild(new DynamicBorder());
62
+ return componentPort(container);
63
+ }
64
+ export function createPiShellHotkeys() {
65
+ ensureTheme();
66
+ const keys = new KeybindingsManager();
67
+ const display = (action) => keys.getKeys(action).map(key => key.split("+").map(part => part.charAt(0).toUpperCase() + part.slice(1)).join("+")).join("/");
68
+ const row = (actions, description) => `| ${actions.map(action => `\`${display(action)}\``).join(" / ")} | ${description} |`;
69
+ const markdown = ["**Navigation**", "| Key | Action |", "|-----|--------|", row(["tui.editor.cursorUp", "tui.editor.cursorDown", "tui.editor.cursorLeft", "tui.editor.cursorRight"], "Move cursor / browse history"), row(["tui.editor.cursorWordLeft", "tui.editor.cursorWordRight"], "Move by word"), row(["tui.editor.cursorLineStart"], "Start of line"), row(["tui.editor.cursorLineEnd"], "End of line"), row(["tui.editor.jumpForward"], "Jump forward to character"), row(["tui.editor.jumpBackward"], "Jump backward to character"), row(["tui.editor.pageUp", "tui.editor.pageDown"], "Scroll by page"), "", "**Editing**", "| Key | Action |", "|-----|--------|", row(["tui.input.submit"], "Send message"), row(["tui.input.newLine"], `New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""}`), row(["tui.editor.deleteWordBackward"], "Delete word backwards"), row(["tui.editor.deleteWordForward"], "Delete word forwards"), row(["tui.editor.deleteToLineStart"], "Delete to start of line"), row(["tui.editor.deleteToLineEnd"], "Delete to end of line"), row(["tui.editor.yank"], "Paste the most-recently-deleted text"), row(["tui.editor.yankPop"], "Cycle through the deleted text after pasting"), row(["tui.editor.undo"], "Undo"), "", "**Other**", "| Key | Action |", "|-----|--------|", row(["tui.input.tab"], "Path completion / accept autocomplete"), row(["app.interrupt"], "Cancel autocomplete / abort streaming"), row(["app.clear"], "Clear editor (first) / exit (second)"), row(["app.exit"], "Exit (when editor is empty)"), row(["app.suspend"], "Suspend to background"), row(["app.thinking.cycle"], "Cycle thinking level"), row(["app.model.cycleForward", "app.model.cycleBackward"], "Cycle models"), row(["app.model.select"], "Open model selector"), row(["app.tools.expand"], "Toggle tool output expansion"), row(["app.thinking.toggle"], "Toggle thinking block visibility"), row(["app.editor.external"], "Edit message in external editor"), row(["app.message.copy"], "Copy last assistant message"), row(["app.message.followUp"], "Queue follow-up message"), row(["app.message.dequeue"], "Restore queued messages"), row(["app.clipboard.pasteImage"], "Paste image or text from clipboard"), "| `/` | Slash commands |", "| `!` | Run bash command |", "| `!!` | Run bash command (excluded from context) |"].join("\n");
70
+ const container = new Container();
71
+ container.addChild(new Spacer(1));
72
+ container.addChild(new DynamicBorder());
73
+ container.addChild(new Text(piTheme().bold(piTheme().fg("accent", "Keyboard Shortcuts")), 1, 0));
74
+ container.addChild(new Spacer(1));
75
+ container.addChild(new Markdown(markdown, 1, 1, getMarkdownTheme()));
76
+ container.addChild(new DynamicBorder());
77
+ return componentPort(container);
78
+ }
@@ -1,43 +1,10 @@
1
1
  import { AssistantMessageComponent } from "@earendil-works/pi-coding-agent";
2
+ import { type MermaidRenderingMode } from "./upstream/components/mermaid.js";
2
3
  import type { OwnedUiTranscriptBlock } from "../../../contracts/owned-ui/index.js";
3
4
  import { type PiShellSubmittedPromptComposer } from "./submitted-prompt-adapter.js";
4
5
  export type { PiShellSubmittedPromptComposer } from "./submitted-prompt-adapter.js";
5
- import { type PiShellComponentPort, type PiShellExtensionRendererResolver, type PiShellTranscriptComponentPort } from "./shell-shared-facade.js";
6
- export interface PiShellSessionInfoPresentation {
7
- readonly sessionName?: string;
8
- readonly stats: {
9
- readonly sessionFile?: string;
10
- readonly sessionId: string;
11
- readonly userMessages: number;
12
- readonly assistantMessages: number;
13
- readonly toolCalls: number;
14
- readonly toolResults: number;
15
- readonly totalMessages: number;
16
- readonly tokens: {
17
- readonly input: number;
18
- readonly output: number;
19
- readonly cacheRead: number;
20
- readonly cacheWrite: number;
21
- readonly total: number;
22
- };
23
- readonly cost: number;
24
- };
25
- readonly cacheWaste: {
26
- readonly missedTokens: number;
27
- readonly missedCost: number;
28
- readonly missCount: number;
29
- };
30
- readonly usageBreakdown: readonly {
31
- readonly key: string;
32
- readonly cost: number;
33
- readonly tokens: number;
34
- }[];
35
- }
36
- export declare function renderPiShellStatusText(message: string, width: number): readonly string[];
37
- export declare function createPiShellSessionInfo(presentation: PiShellSessionInfoPresentation): PiShellComponentPort;
38
- export declare function createPiShellChangelog(markdown: string): PiShellComponentPort;
39
- export declare function createPiShellHotkeys(): PiShellComponentPort;
40
- export declare function createPiShellTranscriptComponent(initial: OwnedUiTranscriptBlock, cwd: string, extensions?: PiShellExtensionRendererResolver, submittedPrompt?: PiShellSubmittedPromptComposer): PiShellTranscriptComponentPort;
6
+ import { type PiShellExtensionRendererResolver, type PiShellImageAssetResolver, type PiShellTranscriptComponentPort } from "./shell-shared-facade.js";
7
+ export declare function createPiShellTranscriptComponent(initial: OwnedUiTranscriptBlock, cwd: string, extensions?: PiShellExtensionRendererResolver, submittedPrompt?: PiShellSubmittedPromptComposer, initialOutputPad?: 0 | 1, initialHideThinkingBlock?: boolean, initialMermaidRenderingMode?: MermaidRenderingMode, initialShowImages?: boolean, initialImageWidthCells?: number, imageAssets?: PiShellImageAssetResolver): PiShellTranscriptComponentPort;
41
8
  export declare function renderPiShellTranscriptBlock(block: OwnedUiTranscriptBlock, width: number, cwd: string): readonly string[];
42
9
  /**
43
10
  * Pinned Pi's CLI prints startup diagnostics with `reportDiagnostics` before