@timurproko/a1 0.1.8-dev.528 → 0.1.8-dev.544

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 (108) hide show
  1. package/bin/update-recovery.js +25 -3
  2. package/dist/app/session-shell/session-shell-root.d.ts +1 -0
  3. package/dist/app/session-shell/session-shell-root.js +3 -1
  4. package/dist/app/session-shell/session-shell.d.ts +1 -1
  5. package/dist/app/session-shell/session-shell.js +10 -4
  6. package/dist/composition/owned-ui.js +4 -3
  7. package/dist/composition/settings-route-host.d.ts +5 -9
  8. package/dist/composition/settings-route-host.js +2 -2
  9. package/dist/contracts/owned-ui/model.d.ts +4 -0
  10. package/dist/contracts/owned-ui/validation.js +25 -0
  11. package/dist/features/owned-ui/index.d.ts +1 -1
  12. package/dist/features/owned-ui/reference-screen-app.d.ts +9 -3
  13. package/dist/features/owned-ui/reference-screen-app.js +68 -20
  14. package/dist/features/owned-ui/settings-app.js +69 -24
  15. package/dist/foundation/release/index.d.ts +2 -2
  16. package/dist/foundation/release/index.js +1 -1
  17. package/dist/foundation/release/update-recovery.d.ts +7 -1
  18. package/dist/foundation/release/update-recovery.js +39 -9
  19. package/dist/foundation/release/update.d.ts +7 -0
  20. package/dist/foundation/release/update.js +115 -24
  21. package/dist/foundation/startup/startup-descriptor.js +2 -2
  22. package/dist/integrations/pi/components/index.d.ts +3 -1
  23. package/dist/integrations/pi/components/index.js +2 -1
  24. package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +9 -1
  25. package/dist/integrations/pi/components/shell-editor-autocomplete.js +10 -9
  26. package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
  27. package/dist/integrations/pi/components/shell-footer-status.js +2 -2
  28. package/dist/integrations/pi/components/shell-hotkey-sections.d.ts +7 -0
  29. package/dist/integrations/pi/components/shell-hotkey-sections.js +29 -0
  30. package/dist/integrations/pi/components/shell-presenters-info.d.ts +1 -0
  31. package/dist/integrations/pi/components/shell-presenters-info.js +1 -1
  32. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +0 -1
  33. package/dist/integrations/pi/components/shell-selectors-dialogs.js +1 -7
  34. package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +8 -0
  35. package/dist/integrations/pi/components/thinking-selector-dialog.js +22 -0
  36. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  37. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +1 -1
  38. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  39. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  40. package/dist/integrations/pi/components/upstream/components/daxnuts.d.ts +1 -1
  41. package/dist/integrations/pi/components/upstream/components/daxnuts.js +1 -1
  42. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  43. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +2 -1
  44. package/dist/integrations/pi/components/upstream/components/extension-editor.js +7 -2
  45. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  46. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
  47. package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
  48. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  49. package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +2 -1
  50. package/dist/integrations/pi/components/upstream/components/session-footer.js +22 -11
  51. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  52. package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
  53. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
  54. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
  55. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
  56. package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
  57. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +33 -0
  58. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +140 -0
  59. package/dist/integrations/pi/components/upstream/components/tool-execution.js +1 -1
  60. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
  61. package/dist/integrations/pi/components/upstream/components/tree-selector.js +8 -1
  62. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  63. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  64. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +1 -1
  65. package/dist/integrations/pi/components/upstream/history/editor-core.js +1 -1
  66. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  67. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  68. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  69. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  70. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  71. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  72. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  73. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  74. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  75. package/dist/integrations/pi/components/upstream/theme/theme.js +1 -1
  76. package/dist/integrations/pi/engine/adapter.js +1 -0
  77. package/dist/integrations/pi/engine/repository-pr.d.ts +8 -0
  78. package/dist/integrations/pi/engine/repository-pr.js +62 -0
  79. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  80. package/dist/integrations/pi/engine/session-runtime.d.ts +5 -0
  81. package/dist/integrations/pi/engine/session-runtime.js +60 -1
  82. package/dist/integrations/pi/engine/workflow-support.js +3 -1
  83. package/dist/integrations/pi/engine/workflows.d.ts +1 -1
  84. package/dist/integrations/pi/engine/workflows.js +4 -2
  85. package/dist/integrations/pi/startup-public.js +63 -56
  86. package/dist/integrations/pi/startup-public.manifest.json +200 -169
  87. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  88. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  89. package/dist/native/darwin-arm64/manifest.json +1 -1
  90. package/dist/native/linux-x64/manifest.json +1 -1
  91. package/dist/native/win32-x64/manifest.json +2 -2
  92. package/dist/native/win32-x64/process-guardian.exe +0 -0
  93. package/dist/product-identity.d.ts +1 -1
  94. package/dist/product-identity.js +1 -1
  95. package/dist/product-identity.json +1 -0
  96. package/dist/runtime-payload-inventory.json +2948 -2646
  97. package/dist/ui/components/index.d.ts +1 -1
  98. package/dist/ui/components/list-block.d.ts +10 -6
  99. package/dist/ui/components/list-block.js +28 -15
  100. package/dist/ui/components/list-view.d.ts +1 -1
  101. package/dist/ui/components/list-view.js +4 -2
  102. package/dist/ui/components/mouse.js +4 -2
  103. package/dist/ui/components/theme.d.ts +1 -5
  104. package/dist/ui/components/theme.js +0 -4
  105. package/dist/ui/components/value-menu.d.ts +3 -1
  106. package/dist/ui/components/value-menu.js +13 -5
  107. package/docs/local-worktree-cleanup.md +1 -1
  108. package/package.json +3 -3
@@ -21,11 +21,11 @@ export type { RestartSeal, RestartValidationEvent } from "./restart-certificatio
21
21
  export { STABLE_RELEASE_SCHEMA, createStableReleaseEvidence, verifyStableRegistry } from "./stable-release.js";
22
22
  export type { StableRegistryState, StableRegistryVerificationOptions, StableReleaseEvidence, StableReleaseEvidenceInput } from "./stable-release.js";
23
23
  export { PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
24
- export type { ProcessRequest, ProcessResult, SelfUpdateOptions, UpdateActivationPhase, UpdateChannel, UpdateFileSystem, UpdateLifecycleCoordinator, UpdateMaterializationProgress, UpdateMeasuredPhase, UpdateOutput, UpdateOwnershipAction, UpdatePackageReplacementInput, UpdatePerformanceEvidence, UpdatePhaseTimingEvent, UpdateProcessRunner, UpdateTransactionJournal, } from "./update.js";
24
+ export type { ProcessRequest, ProcessResult, SelfUpdateOptions, UpdateActivationPhase, UpdateChannel, UpdateFileMetadata, UpdateFileSystem, UpdateLifecycleCoordinator, UpdateMaterializationProgress, UpdateMeasuredPhase, UpdateOutput, UpdateOwnershipAction, UpdatePackageReplacementInput, UpdatePerformanceEvidence, UpdatePhaseTimingEvent, UpdateProcessRunner, UpdateTransactionJournal, } from "./update.js";
25
25
  export { UPDATE_ACTIVATION_CONTRACT, UPDATE_ACTIVATION_ENTRY, UPDATE_ACTIVATION_MANIFEST_FIELD, activateInstalledRelease, delegateActivation, readActivationContracts, runActivationEntry, } from "./update-activation.js";
26
26
  export type { UpdateActivationCallbacks, UpdateActivationEvent, UpdateActivationRequest } from "./update-activation.js";
27
27
  export { selectSupervisorLaunchReleaseId, selectUpdateLaunchRelease } from "./update-launch.js";
28
- export { UPDATE_RECOVERY_SCHEMA, cleanupUpdateRecoveryCapsules, inspectUpdateLauncherSet, prepareUpdateRecoveryCapsule, readUpdateRecoveryCapsule, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, } from "./update-recovery.js";
28
+ export { UPDATE_RECOVERY_SCHEMA, cleanupUpdateRecoveryCapsules, inspectUpdateLauncherSet, npmPrefixForGlobalRoot, prepareUpdateRecoveryCapsule, readUpdateRecoveryCapsule, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, updateNpmInstallArguments, } from "./update-recovery.js";
29
29
  export type { ProtectedPackageReplacementOptions, ProtectedPackageReplacementResult, UpdateRecoveryCapsule, UpdateRecoveryResult } from "./update-recovery.js";
30
30
  export { UPDATE_JOURNAL_SCHEMA, UpdateTransactionStore } from "./update-transaction.js";
31
31
  export type { UpdateRecoveryState, UpdateTransaction, UpdateTransactionPhase } from "./update-transaction.js";
@@ -12,6 +12,6 @@ export { STABLE_RELEASE_SCHEMA, createStableReleaseEvidence, verifyStableRegistr
12
12
  export { PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
13
13
  export { UPDATE_ACTIVATION_CONTRACT, UPDATE_ACTIVATION_ENTRY, UPDATE_ACTIVATION_MANIFEST_FIELD, activateInstalledRelease, delegateActivation, readActivationContracts, runActivationEntry, } from "./update-activation.js";
14
14
  export { selectSupervisorLaunchReleaseId, selectUpdateLaunchRelease } from "./update-launch.js";
15
- export { UPDATE_RECOVERY_SCHEMA, cleanupUpdateRecoveryCapsules, inspectUpdateLauncherSet, prepareUpdateRecoveryCapsule, readUpdateRecoveryCapsule, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, } from "./update-recovery.js";
15
+ export { UPDATE_RECOVERY_SCHEMA, cleanupUpdateRecoveryCapsules, inspectUpdateLauncherSet, npmPrefixForGlobalRoot, prepareUpdateRecoveryCapsule, readUpdateRecoveryCapsule, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, updateNpmInstallArguments, } from "./update-recovery.js";
16
16
  export { UPDATE_JOURNAL_SCHEMA, UpdateTransactionStore } from "./update-transaction.js";
17
17
  export { warmMaterializedRelease } from "./warmup.js";
@@ -39,6 +39,8 @@ export interface UpdateRecoveryResult {
39
39
  export interface ProtectedPackageReplacementOptions {
40
40
  readonly dataDir: string;
41
41
  readonly globalRoot: string;
42
+ /** Root used only to locate the active npm implementation; installation remains pinned to globalRoot. */
43
+ readonly npmCliRoot?: string;
42
44
  readonly packageRoot: string;
43
45
  readonly transaction: UpdateTransaction;
44
46
  readonly priorRelease: {
@@ -58,7 +60,11 @@ export interface ProtectedPackageReplacementOptions {
58
60
  export interface ProtectedPackageReplacementResult extends UpdateRecoveryResult {
59
61
  readonly recovery: UpdateRecoveryState;
60
62
  }
61
- /** Resolve the complete public launcher set npm owns for the active platform. */
63
+ /** Derive the only npm prefix that can own a platform's canonical global package root. */
64
+ export declare function npmPrefixForGlobalRoot(globalRoot: string, platform?: NodeJS.Platform): string;
65
+ /** Fixed replacement arguments pin npm to the installation that owns the invoked launcher. */
66
+ export declare function updateNpmInstallArguments(globalRoot: string, targetVersion: string, platform?: NodeJS.Platform): readonly string[];
67
+ /** Resolve the complete public launcher set npm owns for the selected platform and prefix. */
62
68
  export declare function updateLauncherPaths(globalRoot: string, platform?: NodeJS.Platform): readonly string[];
63
69
  /** Validate transaction-scoped recovery authority without trusting npm temporary names. */
64
70
  export declare function readUpdateRecoveryCapsule(manifestPath: string, platform?: NodeJS.Platform): Promise<UpdateRecoveryCapsule>;
@@ -1,7 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
3
  import { chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, writeFile } from "node:fs/promises";
4
- import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
4
+ import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
7
7
  import { processIsAlive } from "./process-cleanup.js";
@@ -9,9 +9,29 @@ import { PRIVATE_LAUNCH_CONTRACT, assertCurrentLaunchContract } from "../launch-
9
9
  export const UPDATE_RECOVERY_SCHEMA = "a1-update-recovery-v1";
10
10
  const WORKER_TIMEOUT_MS = 15 * 60 * 1_000;
11
11
  const POLL_MS = 50;
12
- /** Resolve the complete public launcher set npm owns for the active platform. */
12
+ /** Derive the only npm prefix that can own a platform's canonical global package root. */
13
+ export function npmPrefixForGlobalRoot(globalRoot, platform = process.platform) {
14
+ const root = resolve(globalRoot);
15
+ if (!sameSegment(basename(root), "node_modules", platform))
16
+ throw new Error(`npm global root has an unsupported layout: ${globalRoot}`);
17
+ if (platform === "win32")
18
+ return dirname(root);
19
+ const libraryRoot = dirname(root);
20
+ if (!sameSegment(basename(libraryRoot), "lib", platform))
21
+ throw new Error(`npm global root has an unsupported layout: ${globalRoot}`);
22
+ return dirname(libraryRoot);
23
+ }
24
+ /** Fixed replacement arguments pin npm to the installation that owns the invoked launcher. */
25
+ export function updateNpmInstallArguments(globalRoot, targetVersion, platform = process.platform) {
26
+ return [
27
+ "install", "--global", "--prefix", npmPrefixForGlobalRoot(globalRoot, platform),
28
+ "--loglevel=error", "--no-fund", "--no-audit", `${PRODUCT_TEXT.packageName}@${targetVersion}`,
29
+ ];
30
+ }
31
+ /** Resolve the complete public launcher set npm owns for the selected platform and prefix. */
13
32
  export function updateLauncherPaths(globalRoot, platform = process.platform) {
14
- const launcherRoot = platform === "win32" ? dirname(globalRoot) : resolve(globalRoot, "..", "..", "bin");
33
+ const prefix = npmPrefixForGlobalRoot(globalRoot, platform);
34
+ const launcherRoot = platform === "win32" ? prefix : resolve(prefix, "bin");
15
35
  return platform === "win32"
16
36
  ? [resolve(launcherRoot, "a1"), resolve(launcherRoot, "a1.cmd"), resolve(launcherRoot, "a1.ps1")]
17
37
  : [resolve(launcherRoot, "a1")];
@@ -33,9 +53,11 @@ export async function readUpdateRecoveryCapsule(manifestPath, platform = process
33
53
  const expectedPackage = resolve(canonicalGlobal, ...capsule.packageName.split("/"));
34
54
  if (!samePath(canonicalPackage, expectedPackage) || !containedBy(canonicalGlobal, canonicalPackage))
35
55
  throw new Error("recovery package root is outside npm global root");
36
- const expectedNpmArguments = ["install", "--global", "--loglevel=error", "--no-fund", "--no-audit", `${capsule.packageName}@${capsule.targetVersion}`];
37
- if (JSON.stringify(capsule.npmArguments) !== JSON.stringify(expectedNpmArguments))
38
- throw new Error("recovery npm arguments differ from the selected target");
56
+ const expectedNpmArguments = updateNpmInstallArguments(canonicalGlobal, capsule.targetVersion, platform);
57
+ const legacyNpmArguments = ["install", "--global", "--loglevel=error", "--no-fund", "--no-audit", `${capsule.packageName}@${capsule.targetVersion}`];
58
+ if (![expectedNpmArguments, legacyNpmArguments].some(arguments_ => sameArguments(capsule.npmArguments, arguments_))) {
59
+ throw new Error("recovery npm arguments differ from the selected target and prefix");
60
+ }
39
61
  const expectedLaunchers = updateLauncherPaths(canonicalGlobal, platform);
40
62
  if (JSON.stringify(capsule.launchers.map(path => resolve(path))) !== JSON.stringify(expectedLaunchers.map(path => resolve(path)))) {
41
63
  throw new Error("recovery launcher set differs from the canonical npm launcher set");
@@ -94,8 +116,10 @@ export async function prepareUpdateRecoveryCapsule(options) {
94
116
  await chmod(candidateEntry, 0o500);
95
117
  const canonicalGlobal = await realpath(options.globalRoot);
96
118
  const platform = options.platform ?? process.platform;
97
- const launcherRoot = platform === "win32" ? dirname(canonicalGlobal) : resolve(canonicalGlobal, "..", "..", "bin");
98
- const npmCli = await resolveNpmCli(canonicalGlobal, options.environment ?? process.env);
119
+ const prefix = npmPrefixForGlobalRoot(canonicalGlobal, platform);
120
+ const launcherRoot = platform === "win32" ? prefix : resolve(prefix, "bin");
121
+ const npmCliRoot = await realpath(options.npmCliRoot ?? canonicalGlobal);
122
+ const npmCli = await resolveNpmCli(npmCliRoot, options.environment ?? process.env);
99
123
  const capsule = {
100
124
  schema: UPDATE_RECOVERY_SCHEMA,
101
125
  launchContract: PRIVATE_LAUNCH_CONTRACT,
@@ -114,7 +138,7 @@ export async function prepareUpdateRecoveryCapsule(options) {
114
138
  recoveryEntryDigest: createHash("sha256").update(entryBytes).digest("hex"),
115
139
  nodeExecutable: process.execPath,
116
140
  npmCli,
117
- npmArguments: ["install", "--global", "--loglevel=error", "--no-fund", "--no-audit", `${PRODUCT_TEXT.packageName}@${options.transaction.targetVersion}`],
141
+ npmArguments: updateNpmInstallArguments(canonicalGlobal, options.transaction.targetVersion, platform),
118
142
  cancellationPath: resolve(finalRoot, "cancel.json"),
119
143
  resultPath: resolve(finalRoot, "result.json"),
120
144
  ownerPath: resolve(finalRoot, "owner.json"),
@@ -341,6 +365,12 @@ async function readJson(path) { return JSON.parse(await readFile(path, "utf8"));
341
365
  function samePath(left, right) {
342
366
  return process.platform === "win32" ? resolve(left).toLowerCase() === resolve(right).toLowerCase() : resolve(left) === resolve(right);
343
367
  }
368
+ function sameSegment(left, right, platform) {
369
+ return platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
370
+ }
371
+ function sameArguments(left, right) {
372
+ return left.length === right.length && left.every((value, index) => value === right[index]);
373
+ }
344
374
  function containedBy(parent, child) {
345
375
  const fromParent = relative(parent, child);
346
376
  return fromParent.length > 0 && fromParent !== ".." && !fromParent.startsWith(`..${sep}`) && !isAbsolute(fromParent);
@@ -13,9 +13,15 @@ export interface ProcessResult {
13
13
  stderr?: string;
14
14
  }
15
15
  export type UpdateProcessRunner = (command: string, arguments_: readonly string[], request: ProcessRequest) => Promise<ProcessResult>;
16
+ export interface UpdateFileMetadata {
17
+ readonly mode: number;
18
+ isFile(): boolean;
19
+ isSymbolicLink(): boolean;
20
+ }
16
21
  export interface UpdateFileSystem {
17
22
  readFile(path: string): Promise<string>;
18
23
  realpath(path: string): Promise<string>;
24
+ lstat?(path: string): Promise<UpdateFileMetadata>;
19
25
  access?(path: string): Promise<void>;
20
26
  }
21
27
  export interface UpdateOutput {
@@ -85,6 +91,7 @@ export interface UpdateTransactionJournal {
85
91
  export interface UpdatePackageReplacementInput {
86
92
  readonly dataDir: string;
87
93
  readonly globalRoot: string;
94
+ readonly npmCliRoot: string;
88
95
  readonly packageRoot: string;
89
96
  readonly transaction: UpdateTransaction;
90
97
  readonly priorRelease: {
@@ -1,7 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
- import { access, readFile, realpath } from "node:fs/promises";
2
+ import { access, lstat, readFile, realpath } from "node:fs/promises";
3
3
  import { connect } from "node:net";
4
- import { isAbsolute, relative, resolve, sep } from "node:path";
4
+ import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
5
5
  import crossSpawn from "cross-spawn";
6
6
  import { valid as validSemver } from "semver";
7
7
  import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
@@ -15,12 +15,13 @@ import { materializeRelease, readMaterializedRelease } from "./release-store.js"
15
15
  import { scheduleReleaseCleanup } from "./release-gc.js";
16
16
  import { UPDATE_ACTIVATION_CONTRACT, activateInstalledRelease, delegateActivation, readActivationContracts, } from "./update-activation.js";
17
17
  import { UpdateTransactionStore } from "./update-transaction.js";
18
- import { removeUpdateRecoveryCapsule, runProtectedPackageReplacement } from "./update-recovery.js";
18
+ import { npmPrefixForGlobalRoot, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, updateNpmInstallArguments, } from "./update-recovery.js";
19
19
  export const PRODUCT_PACKAGE = PRODUCT_TEXT.packageName;
20
20
  const UPDATE_DIST_TAGS = { stable: "latest", next: "next" };
21
21
  const defaultFileSystem = {
22
22
  async readFile(path) { return await readFile(path, "utf8"); },
23
23
  realpath,
24
+ lstat,
24
25
  access,
25
26
  };
26
27
  const defaultOutput = {
@@ -308,6 +309,106 @@ async function resolveRequestedPreview(runner, requested, output) {
308
309
  output.stderr(`${PRODUCT_TEXT.diagnostic(`published no preview for ${requested}.`)}\n`);
309
310
  return { version: null, exitCode: 1 };
310
311
  }
312
+ async function resolveManagedNpmInstallation(requestedPackageRoot, fileSystem, runner, output) {
313
+ const rootLookup = await runNpm(runner, ["root", "--global"], true, output, "resolve npm's global package root");
314
+ if (rootLookup.result === null)
315
+ return { installation: null, exitCode: rootLookup.exitCode };
316
+ const activeRootText = rootLookup.result.stdout.trim();
317
+ if (activeRootText.length === 0) {
318
+ output.stderr(`${PRODUCT_TEXT.diagnostic("could not verify its installation because npm returned an empty global package root.")}\n`);
319
+ return { installation: null, exitCode: 1 };
320
+ }
321
+ let packageRoot;
322
+ let activeGlobalRoot;
323
+ try {
324
+ [packageRoot, activeGlobalRoot] = await Promise.all([fileSystem.realpath(requestedPackageRoot), fileSystem.realpath(activeRootText)]);
325
+ }
326
+ catch (error) {
327
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`could not canonicalize the running and global npm paths: ${errorMessage(error)}`)}\n`);
328
+ return { installation: null, exitCode: 1 };
329
+ }
330
+ const expectedActivePackage = resolve(activeGlobalRoot, ...PRODUCT_PACKAGE.split("/"));
331
+ if (samePath(packageRoot, expectedActivePackage)) {
332
+ try {
333
+ npmPrefixForGlobalRoot(activeGlobalRoot);
334
+ return {
335
+ installation: {
336
+ packageRoot,
337
+ globalRoot: activeGlobalRoot,
338
+ npmCliRoot: activeGlobalRoot,
339
+ },
340
+ exitCode: 0,
341
+ };
342
+ }
343
+ catch (error) {
344
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`could not verify npm's active global package layout: ${errorMessage(error)}`)}\n`);
345
+ return { installation: null, exitCode: 1 };
346
+ }
347
+ }
348
+ let candidateGlobalRoot = packageRoot;
349
+ for (const _segment of PRODUCT_PACKAGE.split("/"))
350
+ candidateGlobalRoot = dirname(candidateGlobalRoot);
351
+ let candidatePrefix;
352
+ try {
353
+ candidatePrefix = npmPrefixForGlobalRoot(candidateGlobalRoot);
354
+ }
355
+ catch {
356
+ reportUnmanagedInstallation(output, packageRoot, activeGlobalRoot);
357
+ return { installation: null, exitCode: 1 };
358
+ }
359
+ if (!samePath(packageRoot, resolve(candidateGlobalRoot, ...PRODUCT_PACKAGE.split("/")))) {
360
+ reportUnmanagedInstallation(output, packageRoot, activeGlobalRoot);
361
+ return { installation: null, exitCode: 1 };
362
+ }
363
+ const confirmation = await runNpm(runner, ["root", "--global", "--prefix", candidatePrefix], true, output, "confirm npm ownership of the running installation");
364
+ if (confirmation.result === null)
365
+ return { installation: null, exitCode: confirmation.exitCode };
366
+ const confirmedRootText = confirmation.result.stdout.trim();
367
+ if (confirmedRootText.length === 0) {
368
+ output.stderr(`${PRODUCT_TEXT.diagnostic("could not verify its installation because npm returned an empty global package root for the inferred prefix.")}\n`);
369
+ return { installation: null, exitCode: 1 };
370
+ }
371
+ let confirmedRoot;
372
+ try {
373
+ confirmedRoot = await fileSystem.realpath(confirmedRootText);
374
+ }
375
+ catch (error) {
376
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`could not canonicalize npm's confirmed global package root: ${errorMessage(error)}`)}\n`);
377
+ return { installation: null, exitCode: 1 };
378
+ }
379
+ if (!samePath(confirmedRoot, candidateGlobalRoot)
380
+ || !await launchersOwnPackage(candidateGlobalRoot, fileSystem)) {
381
+ reportUnmanagedInstallation(output, packageRoot, activeGlobalRoot);
382
+ return { installation: null, exitCode: 1 };
383
+ }
384
+ return {
385
+ installation: {
386
+ packageRoot,
387
+ globalRoot: candidateGlobalRoot,
388
+ npmCliRoot: activeGlobalRoot,
389
+ },
390
+ exitCode: 0,
391
+ };
392
+ }
393
+ async function launchersOwnPackage(globalRoot, fileSystem) {
394
+ const token = `node_modules/${PRODUCT_PACKAGE}/bin/cli.js`;
395
+ try {
396
+ for (const launcher of updateLauncherPaths(globalRoot)) {
397
+ const metadata = await (fileSystem.lstat ?? lstat)(launcher);
398
+ if (!metadata.isFile() || metadata.isSymbolicLink() || (process.platform !== "win32" && (metadata.mode & 0o111) === 0))
399
+ return false;
400
+ if (!(await fileSystem.readFile(launcher)).replaceAll("\\", "/").includes(token))
401
+ return false;
402
+ }
403
+ return true;
404
+ }
405
+ catch {
406
+ return false;
407
+ }
408
+ }
409
+ function reportUnmanagedInstallation(output, packageRoot, activeGlobalRoot) {
410
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`refused to update automatically because ${packageRoot} could not be verified as an npm-managed ${PRODUCT_PACKAGE} installation; npm's active global package root is ${activeGlobalRoot}. Run npm install --global ${PRODUCT_PACKAGE} to install it manually.`)}\n`);
411
+ }
311
412
  export async function runSelfUpdate(options) {
312
413
  const fileSystem = options.fileSystem ?? defaultFileSystem;
313
414
  const output = options.output ?? defaultOutput;
@@ -328,6 +429,8 @@ export async function runSelfUpdate(options) {
328
429
  try {
329
430
  const packageJson = JSON.parse(await measure("package-version", async () => await fileSystem.readFile(resolve(options.packageRoot, "package.json"))));
330
431
  const parsedVersion = typeof packageJson.version === "string" ? validSemver(packageJson.version) : null;
432
+ if (packageJson.name !== PRODUCT_PACKAGE)
433
+ throw new Error(`package.json does not identify ${PRODUCT_PACKAGE}`);
331
434
  if (parsedVersion === null)
332
435
  throw new Error("package.json does not contain a valid semantic version");
333
436
  runningVersion = parsedVersion;
@@ -347,26 +450,10 @@ export async function runSelfUpdate(options) {
347
450
  // and a trailing one reads as part of the version rather than as punctuation.
348
451
  output.stdout(`${PRODUCT_TEXT.commandName} update: ${runningVersion} → ${targetVersion}\n`);
349
452
  const progress = createUpdateProgress(output, options.progress ?? (options.output === undefined && process.stdout.isTTY === true));
350
- const rootLookup = await measure("global-root", async () => await runNpm(runner, ["root", "--global"], true, output, "resolve npm's global package root"));
351
- if (rootLookup.result === null)
352
- return rootLookup.exitCode;
353
- if (rootLookup.result.stdout.trim().length === 0) {
354
- output.stderr(`${PRODUCT_TEXT.diagnostic("could not verify its installation because npm returned an empty global package root.")}\n`);
355
- return 1;
356
- }
357
- let packageRoot;
358
- let globalRoot;
359
- try {
360
- [packageRoot, globalRoot] = await Promise.all([fileSystem.realpath(options.packageRoot), fileSystem.realpath(rootLookup.result.stdout.trim())]);
361
- }
362
- catch (error) {
363
- output.stderr(`${PRODUCT_TEXT.diagnostic(`could not canonicalize the running and global npm paths: ${errorMessage(error)}`)}\n`);
364
- return 1;
365
- }
366
- if (!isContainedBy(globalRoot, packageRoot)) {
367
- output.stderr(`${PRODUCT_TEXT.diagnostic(`refused to update automatically because ${packageRoot} is not managed beneath npm's global package root ${globalRoot}.`)}\n`);
368
- return 1;
369
- }
453
+ const managed = await measure("global-root", async () => await resolveManagedNpmInstallation(options.packageRoot, fileSystem, runner, output));
454
+ if (managed.installation === null)
455
+ return managed.exitCode;
456
+ const { packageRoot, globalRoot, npmCliRoot } = managed.installation;
370
457
  const environment = options.environment ?? process.env;
371
458
  const paths = resolveProductPaths(environment);
372
459
  const lifecycle = options.lifecycle ?? createUpdateLifecycleCoordinator(environment, fileSystem, output);
@@ -427,6 +514,7 @@ export async function runSelfUpdate(options) {
427
514
  const replacement = await measure("npm-install", async () => await (options.packageReplacement ?? runProtectedPackageReplacement)({
428
515
  dataDir: paths.dataDir,
429
516
  globalRoot,
517
+ npmCliRoot,
430
518
  packageRoot,
431
519
  transaction: replacementTransaction,
432
520
  priorRelease,
@@ -453,7 +541,7 @@ export async function runSelfUpdate(options) {
453
541
  }
454
542
  }
455
543
  else {
456
- const installation = await measure("npm-install", async () => await runNpm(runner, ["install", "--global", "--loglevel=error", "--no-fund", "--no-audit", `${PRODUCT_PACKAGE}@${targetVersion}`], true, output, "start the global npm installation", false));
544
+ const installation = await measure("npm-install", async () => await runNpm(runner, updateNpmInstallArguments(globalRoot, targetVersion), true, output, "start the global npm installation", false));
457
545
  if (installation.result === null)
458
546
  throw new UpdateFailure(installation.exitCode, "npm process failed");
459
547
  if (installation.result.code !== 0) {
@@ -624,6 +712,9 @@ async function canonicalImmutableRoot(dataDir, releaseRoot) {
624
712
  return false;
625
713
  }
626
714
  }
715
+ function samePath(left, right) {
716
+ return process.platform === "win32" ? resolve(left).toLowerCase() === resolve(right).toLowerCase() : resolve(left) === resolve(right);
717
+ }
627
718
  function isContainedBy(parent, child) {
628
719
  const pathFromParent = relative(parent, child);
629
720
  return pathFromParent.length > 0 && pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep}`) && !isAbsolute(pathFromParent);
@@ -10,7 +10,7 @@ export const STARTUP_DESCRIPTOR = Object.freeze({
10
10
  ],
11
11
  "generatedArtifact": {
12
12
  "path": "dist/integrations/pi/startup-public.js",
13
- "sha256": "2e2d8f9a19e4c8546f9fe48c940ee369b386fcb393d615b7c41023d6c9cdd001"
13
+ "sha256": "fdd81188cf24be1f187fdcc18d131e7432b114c2094dc51543ef27edbe99b23c"
14
14
  },
15
15
  "dependencyLayerIdentity": {
16
16
  "source": "A1_RELEASE_LAYERS",
@@ -19,7 +19,7 @@ export const STARTUP_DESCRIPTOR = Object.freeze({
19
19
  "compileCacheNamespace": {
20
20
  "strategy": "node-abi-version-and-ordered-layer-sha256-v1"
21
21
  },
22
- "identity": "faaa1fcd4379153d81e962bdc003e44bc81a93a91fbd851341dc2dc498dd8b96"
22
+ "identity": "4bf2be3027c6230ec34cfd8dab3df61030de6715ed1f1e9e9dd1fb83edba8522"
23
23
  });
24
24
 
25
25
  export function loadDeclaredStartupGraph() {
@@ -2,7 +2,9 @@ export { adaptPiAssistantMessage, adaptPiToolExecution, adaptPiUserMessage } fro
2
2
  export { loadHistoryEditor, type HistoryEditorConstructor } from "./history-editor-loader.js";
3
3
  export { PiComponentConformanceError, runPiComponentConformance } from "./conformance.js";
4
4
  export type { PiComponentConformanceReport, PiComponentConformanceResult } from "./conformance.js";
5
- export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, componentFromPort, componentPort, createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellShowImagesSelector, createPiShellSkillsSelector, createPiShellStatus, createPiShellThemeSelector, createPiShellThinkingSelector, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, createTuiFacade, ensureTheme, formatSessionTokens, isAutocompleteProvider, isPiPromptStyleCompaction, isRecord, paintPiSubmittedPromptTimestamp, piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, renderPiShellChangelogLines, renderPiShellCommandMessage, renderPiShellHotkeysLines, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, validatedAssistantMessage, } from "./shell-components.js";
5
+ export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, componentFromPort, componentPort, createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellShowImagesSelector, createPiShellSkillsSelector, createPiShellStatus, createPiShellThemeSelector, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, createTuiFacade, ensureTheme, formatSessionTokens, isAutocompleteProvider, isPiPromptStyleCompaction, isRecord, paintPiSubmittedPromptTimestamp, piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, renderPiShellChangelogLines, renderPiShellCommandMessage, renderPiShellHotkeysLines, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, validatedAssistantMessage, } from "./shell-components.js";
6
+ export { renderPiShellHotkeySections } from "./shell-hotkey-sections.js";
7
+ export type { PiShellHotkeySection } from "./shell-hotkey-sections.js";
6
8
  export type { PiExtensionUiBridge, PiExtensionUiBridgeHost, PiShellAuthProviderOption, PiShellAutocompleteCommand, PiShellClipboardContent, PiShellCommandMessagePresentation, PiShellComponentPort, PiShellEditorBodyGeometry, PiShellEditorOptions, PiShellEditorPointerEvent, PiShellEditorPort, PiShellEditorTextRange, PiShellExtensionRendererResolver, PiShellHeaderOptions, PiShellHeaderPort, PiShellHotkeysPresentation, PiShellImageAssetResolver, PiShellLoadedResourcesPort, PiShellLoginDialogPort, PiShellModelSelectorOptions, PiShellModelsDialogOptions, PiShellModelsDialogPort, PiShellOperationLoaderPort, PiShellPasteReservation, PiShellQueuedInputPort, PiShellResourceEntry, PiShellResourceSection, PiShellScopedModelDescriptor, PiShellScopedModelsSelectorOptions, PiShellScopedModelsSelectorPort, PiShellSelectorOption, PiShellSelectorOptions, PiShellSessionInfoPresentation, PiShellSettingsSelectorOptions, PiShellStartupNotice, PiShellStatusPlacement, PiShellStatusPort, PiShellSubmittedPromptComposer, PiShellTranscriptComponentPort, PiShellViewComponentPort, } from "./shell-components.js";
7
9
  export { PINNED_PI_LAYOUT, applyConfiguredPiTheme, applyPiTheme, applyPiThemeInstance, currentPiThemeName, detectPiTerminalBackgroundFromEnv, detectPiTerminalBackgroundTheme, detectPiTerminalThemeForAuto, ensurePiTheme, getAvailablePiThemes, loadPiTheme, onPiThemeChange, parsePiAutoThemeSetting, piTheme, resolvePiThemeSetting, stopPiThemeWatcher, } from "./theme.js";
8
10
  export type { PiColorMode, PiTerminalTheme, PiTerminalThemeDetection, PiTerminalThemeDetector, PiThemeBackground, PiThemeResult } from "./theme.js";
@@ -1,7 +1,8 @@
1
1
  export { adaptPiAssistantMessage, adaptPiToolExecution, adaptPiUserMessage } from "./components.js";
2
2
  export { loadHistoryEditor } from "./history-editor-loader.js";
3
3
  export { PiComponentConformanceError, runPiComponentConformance } from "./conformance.js";
4
- export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, componentFromPort, componentPort, createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellShowImagesSelector, createPiShellSkillsSelector, createPiShellStatus, createPiShellThemeSelector, createPiShellThinkingSelector, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, createTuiFacade, ensureTheme, formatSessionTokens, isAutocompleteProvider, isPiPromptStyleCompaction, isRecord, paintPiSubmittedPromptTimestamp, piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, renderPiShellChangelogLines, renderPiShellCommandMessage, renderPiShellHotkeysLines, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, validatedAssistantMessage, } from "./shell-components.js";
4
+ export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, componentFromPort, componentPort, createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellShowImagesSelector, createPiShellSkillsSelector, createPiShellStatus, createPiShellThemeSelector, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, createTuiFacade, ensureTheme, formatSessionTokens, isAutocompleteProvider, isPiPromptStyleCompaction, isRecord, paintPiSubmittedPromptTimestamp, piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, renderPiShellChangelogLines, renderPiShellCommandMessage, renderPiShellHotkeysLines, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, validatedAssistantMessage, } from "./shell-components.js";
5
+ export { renderPiShellHotkeySections } from "./shell-hotkey-sections.js";
5
6
  export { PINNED_PI_LAYOUT, applyConfiguredPiTheme, applyPiTheme, applyPiThemeInstance, currentPiThemeName, detectPiTerminalBackgroundFromEnv, detectPiTerminalBackgroundTheme, detectPiTerminalThemeForAuto, ensurePiTheme, getAvailablePiThemes, loadPiTheme, onPiThemeChange, parsePiAutoThemeSetting, piTheme, resolvePiThemeSetting, stopPiThemeWatcher, } from "./theme.js";
6
7
  export { OwnedPiThemeController } from "./upstream/theme/theme-controller.js";
7
8
  export { ModelsDialogComponent } from "./models-dialog.js";
@@ -1,5 +1,9 @@
1
1
  import { type PiShellEditorPort, type PiShellEditorOptions } from "./shell-shared-facade.js";
2
2
  export declare const PINNED_PI_BUILTIN_SLASH_COMMANDS: ({
3
+ readonly name: "thinking";
4
+ readonly description: "Set thinking level";
5
+ readonly argumentHint: "<level>";
6
+ } | {
3
7
  name: string;
4
8
  description: string;
5
9
  argumentHint?: never;
@@ -8,8 +12,12 @@ export declare const PINNED_PI_BUILTIN_SLASH_COMMANDS: ({
8
12
  description: string;
9
13
  argumentHint: string;
10
14
  })[];
11
- /** Bare A1's built-in catalog: one `models` command replaces the pinned `model` and `scoped-models` entries. */
15
+ /** Bare A1's built-in catalog: `models` replaces the pinned pair and is followed by `thinking`. */
12
16
  export declare const OWNED_BUILTIN_SLASH_COMMANDS: ({
17
+ readonly name: "thinking";
18
+ readonly description: "Set thinking level";
19
+ readonly argumentHint: "<level>";
20
+ } | {
13
21
  name: string;
14
22
  description: string;
15
23
  argumentHint?: never;
@@ -7,13 +7,14 @@ import { OwnedEditorUxInterception, createPromptSelectionInterceptor, editorVisu
7
7
  import { PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
8
8
  import { createTuiFacade, ensureTheme, isAutocompleteProvider, } from "./shell-shared-facade.js";
9
9
  import { SKILLS_COMMAND_NAME, collapseSkillCommands, createSkillsTunnelProvider, } from "./skills-command.js";
10
- /** A selected tunnel row: the accent label, then at least two spaces, then the description. */
11
- const SELECTED_TUNNEL_ROW = /^(→ skills:\S+)(\s{2,}.*)$/u;
10
+ /** A selected autocomplete row: the accent primary column, then the aligned description. */
11
+ const SELECTED_DESCRIBED_ROW = /^(→ .*?\S)(\s{2,}.*)$/u;
12
+ const THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level", argumentHint: "<level>" };
12
13
  export const PINNED_PI_BUILTIN_SLASH_COMMANDS = [
13
14
  { name: "settings", description: "Open settings menu" },
14
15
  { name: "model", description: "Select model (opens selector UI)", argumentHint: "<provider/model>" },
15
16
  { name: "tree", description: "Navigate session tree (switch branches)" },
16
- { name: "thinking", description: "Set thinking level", argumentHint: "<level>" },
17
+ THINKING_SLASH_COMMAND,
17
18
  { name: "scoped-models", description: "Enable/disable models for Ctrl+P cycling" },
18
19
  { name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },
19
20
  { name: "import", description: "Import and resume a session from a JSONL file" },
@@ -34,10 +35,10 @@ export const PINNED_PI_BUILTIN_SLASH_COMMANDS = [
34
35
  { name: "reload", description: "Reload keybindings, extensions, skills, prompts, themes, and context files" },
35
36
  { name: "quit", description: "Quit" },
36
37
  ];
37
- /** Bare A1's built-in catalog: one `models` command replaces the pinned `model` and `scoped-models` entries. */
38
+ /** Bare A1's built-in catalog: `models` replaces the pinned pair and is followed by `thinking`. */
38
39
  export const OWNED_BUILTIN_SLASH_COMMANDS = PINNED_PI_BUILTIN_SLASH_COMMANDS.flatMap(command => command.name === "model"
39
- ? [{ name: "models", description: "Switch models and manage scoped model cycling" }]
40
- : command.name === "scoped-models" ? [] : [command]);
40
+ ? [{ name: "models", description: "Switch models and manage scoped model cycling" }, THINKING_SLASH_COMMAND]
41
+ : command.name === "thinking" || command.name === "scoped-models" ? [] : [command]);
41
42
  export function createPiShellEditor(options) {
42
43
  ensureTheme();
43
44
  const tui = createTuiFacade(options);
@@ -64,9 +65,9 @@ export function createPiShellEditor(options) {
64
65
  return selectListTheme.scrollInfo(text);
65
66
  },
66
67
  selectedText: text => {
67
- // Rationale: a selected tunnel row keeps its description muted like the unselected rows (v2 behavior);
68
- // the pinned list styles the whole selected row, so the split happens in the owned theme.
69
- const row = tunnelSkills.length === 0 ? null : SELECTED_TUNNEL_ROW.exec(text);
68
+ // Rationale: the pinned list styles a whole selected row at once. Bare A1 keeps the
69
+ // actionable primary column accented while every aligned description remains muted.
70
+ const row = SELECTED_DESCRIBED_ROW.exec(text);
70
71
  return row === null ? selectListTheme.selectedText(text) : selectListTheme.selectedText(row[1]) + selectListTheme.description(row[2]);
71
72
  },
72
73
  },
@@ -3,5 +3,5 @@ import { type PiShellViewComponentPort, type PiShellStatusPort, type PiShellQueu
3
3
  export declare function createPiShellHeader(options?: PiShellHeaderOptions): PiShellHeaderPort;
4
4
  export declare function createPiShellLoadedResources(resources: readonly PiShellResourceEntry[], initialExpanded?: boolean): PiShellLoadedResourcesPort;
5
5
  export declare function createPiShellStatus(view: OwnedUiSessionViewModel, formatProgressStatus: (message: string) => string, runtime?: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">): PiShellStatusPort;
6
- export declare function createPiShellFooter(view: OwnedUiSessionViewModel, cwd: string, profile?: "pi" | "a1"): PiShellViewComponentPort;
6
+ export declare function createPiShellFooter(view: OwnedUiSessionViewModel, cwd: string, profile?: "pi" | "a1", showLevel?: () => boolean): PiShellViewComponentPort;
7
7
  export declare function createPiQueuedInputStatus(submissions: readonly string[], presentation?: "pinned" | "custom-viewport"): PiShellQueuedInputPort;
@@ -128,9 +128,9 @@ export function createPiShellStatus(view, formatProgressStatus, runtime) {
128
128
  },
129
129
  };
130
130
  }
131
- export function createPiShellFooter(view, cwd, profile = "pi") {
131
+ export function createPiShellFooter(view, cwd, profile = "pi", showLevel = () => true) {
132
132
  ensureTheme();
133
- const footer = new SessionFooter(() => view, cwd, profile);
133
+ const footer = new SessionFooter(() => view, cwd, profile, showLevel);
134
134
  return {
135
135
  render: width => footer.render(width),
136
136
  invalidate: () => footer.invalidate(),
@@ -0,0 +1,7 @@
1
+ import { type PiShellHotkeysPresentation } from "./shell-presenters-info.js";
2
+ export interface PiShellHotkeySection {
3
+ readonly title: string;
4
+ readonly rows: readonly string[];
5
+ }
6
+ /** Turns heading-led hotkeys Markdown into reusable grouped-document data before row rendering. */
7
+ export declare function renderPiShellHotkeySections(presentation: PiShellHotkeysPresentation, width: number): readonly PiShellHotkeySection[];
@@ -0,0 +1,29 @@
1
+ import { getMarkdownTheme } from "../startup-public.js";
2
+ import { Markdown } from "@earendil-works/pi-tui";
3
+ import { ensureTheme } from "./shell-shared-facade.js";
4
+ import { hotkeysMarkdown } from "./shell-presenters-info.js";
5
+ /** Turns heading-led hotkeys Markdown into reusable grouped-document data before row rendering. */
6
+ export function renderPiShellHotkeySections(presentation, width) {
7
+ ensureTheme();
8
+ const shortcuts = presentation.getShortcuts ?? (() => []);
9
+ const markdown = hotkeysMarkdown(presentation.bindings, shortcuts, presentation.profile ?? "pi");
10
+ const sections = [];
11
+ for (const line of markdown.split("\n")) {
12
+ const heading = /^\*\*(.+)\*\*$/.exec(line)?.[1];
13
+ if (heading !== undefined)
14
+ sections.push({ title: heading, lines: [] });
15
+ else
16
+ sections.at(-1)?.lines.push(line);
17
+ }
18
+ for (const section of sections)
19
+ while (section.lines.at(-1) === "")
20
+ section.lines.pop();
21
+ return sections.map(section => {
22
+ const rows = [...new Markdown(section.lines.join("\n"), 1, 1, getMarkdownTheme()).render(width)];
23
+ while (rows[0]?.trim().length === 0)
24
+ rows.shift();
25
+ while (rows.at(-1)?.trim().length === 0)
26
+ rows.pop();
27
+ return { title: section.title, rows };
28
+ });
29
+ }
@@ -70,3 +70,4 @@ export interface PiShellHotkeysPresentation {
70
70
  export declare function createPiShellHotkeys(bindings?: KeybindingsConfig, getShortcuts?: NonNullable<PiShellExtensionRendererResolver["getShortcuts"]>, profile?: "pi" | "a1"): PiShellComponentPort;
71
71
  /** The keyboard-shortcut document rows the feed presenter shows, without its spacer, borders, and heading. */
72
72
  export declare function renderPiShellHotkeysLines(presentation: PiShellHotkeysPresentation, width: number): readonly string[];
73
+ export declare function hotkeysMarkdown(bindings: KeybindingsConfig | undefined, getShortcuts: NonNullable<PiShellExtensionRendererResolver["getShortcuts"]>, profile: "pi" | "a1"): string;
@@ -166,7 +166,7 @@ export function renderPiShellHotkeysLines(presentation, width) {
166
166
  function hotkeysMarkdownComponent(markdown) {
167
167
  return new Markdown(markdown, 1, 1, getMarkdownTheme());
168
168
  }
169
- function hotkeysMarkdown(bindings, getShortcuts, profile) {
169
+ export function hotkeysMarkdown(bindings, getShortcuts, profile) {
170
170
  const keys = profile === "a1" ? KeybindingsManager.fromOwnedBindings(bindings) : new KeybindingsManager(bindings);
171
171
  // Compatibility: the owned viewport consumes these physical chords before editor actions.
172
172
  const display = (action) => keys.getKeys(action)
@@ -126,7 +126,6 @@ export interface PiShellAuthProviderOption extends PiShellSelectorOption {
126
126
  }
127
127
  export declare function createPiShellAuthProviderSelector(mode: "login" | "logout", providers: readonly PiShellAuthProviderOption[], onSelect: (id: string) => void, onCancel: () => void, initialSearchInput?: string): PiShellComponentPort;
128
128
  export declare function createPiShellExtensionSelector(title: string, options: readonly string[], onSelect: (value: string) => void, onCancel: () => void): PiShellComponentPort;
129
- export declare function createPiShellThinkingSelector(currentLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", availableLevels: readonly ("off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max")[], onSelect: (level: string) => void, onCancel: () => void, onSelectAsDefault?: (level: string) => void, defaultLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"): PiShellComponentPort;
130
129
  export declare function createPiShellThemeSelector(currentTheme: string, onSelect: (theme: string) => void, onCancel: () => void, onPreview: (theme: string) => void): PiShellComponentPort;
131
130
  export declare function createPiShellShowImagesSelector(currentValue: boolean, onSelect: (show: boolean) => void, onCancel: () => void): PiShellComponentPort;
132
131
  export declare function createPiShellDialog(dialog: OwnedUiDialog, handlers?: {
@@ -1,4 +1,4 @@
1
- import { ArminComponent, BorderedLoader, DynamicBorder, ExtensionSelectorComponent, getSelectListTheme, LoginDialogComponent, ModelSelectorComponent, OAuthSelectorComponent, SettingsSelectorComponent, ShowImagesSelectorComponent, ThemeSelectorComponent, ThinkingSelectorComponent, UserMessageSelectorComponent, } from "../startup-public.js";
1
+ import { ArminComponent, BorderedLoader, DynamicBorder, ExtensionSelectorComponent, getSelectListTheme, LoginDialogComponent, ModelSelectorComponent, OAuthSelectorComponent, SettingsSelectorComponent, ShowImagesSelectorComponent, ThemeSelectorComponent, UserMessageSelectorComponent, } from "../startup-public.js";
2
2
  import { DaxnutsComponent, } from "./upstream/components/daxnuts.js";
3
3
  import { EarendilAnnouncementComponent, } from "./upstream/components/earendil-announcement.js";
4
4
  import { Box, Container, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
@@ -200,12 +200,6 @@ export function createPiShellExtensionSelector(title, options, onSelect, onCance
200
200
  ensureTheme();
201
201
  return componentPort(new ExtensionSelectorComponent(title, [...options], onSelect, onCancel));
202
202
  }
203
- export function createPiShellThinkingSelector(currentLevel, availableLevels, onSelect, onCancel, onSelectAsDefault, defaultLevel) {
204
- ensureTheme();
205
- const selector = new ThinkingSelectorComponent(currentLevel, [...availableLevels], onSelect, onCancel, onSelectAsDefault, defaultLevel);
206
- const list = selector.getSelectList();
207
- return componentPort(selector, data => list.handleInput(data));
208
- }
209
203
  export function createPiShellThemeSelector(currentTheme, onSelect, onCancel, onPreview) {
210
204
  ensureTheme();
211
205
  const selector = new ThemeSelectorComponent(currentTheme, onSelect, onCancel, onPreview);
@@ -0,0 +1,8 @@
1
+ import { type ThinkingSelectorLevel } from "./upstream/components/thinking-selector.js";
2
+ import { type PiShellComponentPort } from "./shell-shared-facade.js";
3
+ export interface PiShellThinkingSelectorPresentation {
4
+ readonly profile: "bare";
5
+ readonly cycleBinding: string | readonly string[];
6
+ }
7
+ /** Lazily loaded because thinking-selector code does not belong to the startup graph before `/thinking` opens. */
8
+ export declare function createPiShellThinkingSelector(currentLevel: ThinkingSelectorLevel, availableLevels: readonly ThinkingSelectorLevel[], onSelect: (level: string) => void, onCancel: () => void, onSelectAsDefault?: (level: string) => void, defaultLevel?: ThinkingSelectorLevel, presentation?: PiShellThinkingSelectorPresentation): PiShellComponentPort;