@timurproko/a1 0.1.1-dev.1 → 0.1.1-dev.10

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 (176) hide show
  1. package/README.md +8 -43
  2. package/bin/guardian.js +22 -0
  3. package/bin/{a1-ui.js → ui.js} +4 -8
  4. package/dist/native/darwin-arm64/manifest.json +20 -0
  5. package/dist/native/darwin-arm64/process-guardian +0 -0
  6. package/dist/native/linux-x64/manifest.json +20 -0
  7. package/dist/native/linux-x64/process-guardian +0 -0
  8. package/dist/native/win32-x64/manifest.json +20 -0
  9. package/dist/native/win32-x64/process-guardian.exe +0 -0
  10. package/dist/src/composition/index.d.ts +26 -0
  11. package/dist/src/composition/index.js +94 -2
  12. package/dist/src/features/launch/initialize-profile.js +23 -2
  13. package/dist/src/features/launch/profiles.d.ts +1 -1
  14. package/dist/src/features/launch/profiles.js +2 -2
  15. package/dist/src/features/launch/runtime-selection.d.ts +13 -8
  16. package/dist/src/features/launch/runtime-selection.js +6 -6
  17. package/dist/src/features/owned-ui/index.d.ts +1 -0
  18. package/dist/src/features/owned-ui/index.js +1 -0
  19. package/dist/src/features/owned-ui/run.d.ts +6 -0
  20. package/dist/src/features/owned-ui/run.js +3 -1
  21. package/dist/src/features/owned-ui/settings-app.d.ts +22 -0
  22. package/dist/src/features/owned-ui/settings-app.js +633 -0
  23. package/dist/src/foundation/agent-engine-contracts/domain.d.ts +26 -0
  24. package/dist/src/foundation/launch-guardian/index.d.ts +1 -0
  25. package/dist/src/foundation/launch-guardian/index.js +1 -0
  26. package/dist/src/foundation/launch-guardian/main.d.ts +31 -0
  27. package/dist/src/foundation/launch-guardian/main.js +165 -0
  28. package/dist/src/foundation/lifecycle/commands.d.ts +40 -0
  29. package/dist/src/foundation/lifecycle/commands.js +1 -0
  30. package/dist/src/foundation/lifecycle/index.d.ts +2 -0
  31. package/dist/src/foundation/lifecycle/index.js +2 -0
  32. package/dist/src/foundation/lifecycle/launch-instance.d.ts +46 -0
  33. package/dist/src/foundation/lifecycle/launch-instance.js +134 -0
  34. package/dist/src/foundation/lifecycle/model.d.ts +6 -42
  35. package/dist/src/foundation/owned-ui-settings/declarations.d.ts +13 -0
  36. package/dist/src/foundation/owned-ui-settings/declarations.js +39 -0
  37. package/dist/src/foundation/owned-ui-settings/index.d.ts +6 -0
  38. package/dist/src/foundation/owned-ui-settings/index.js +6 -0
  39. package/dist/src/foundation/owned-ui-settings/migrations.d.ts +13 -0
  40. package/dist/src/foundation/owned-ui-settings/migrations.js +28 -0
  41. package/dist/src/foundation/owned-ui-settings/resolution.d.ts +45 -0
  42. package/dist/src/foundation/owned-ui-settings/resolution.js +147 -0
  43. package/dist/src/foundation/owned-ui-settings/sections.d.ts +70 -0
  44. package/dist/src/foundation/owned-ui-settings/sections.js +93 -0
  45. package/dist/src/foundation/owned-ui-settings/session.d.ts +44 -0
  46. package/dist/src/foundation/owned-ui-settings/session.js +145 -0
  47. package/dist/src/foundation/owned-ui-settings/store.d.ts +28 -0
  48. package/dist/src/foundation/owned-ui-settings/store.js +90 -0
  49. package/dist/src/foundation/pi-component-adapter/index.d.ts +1 -0
  50. package/dist/src/foundation/pi-component-adapter/index.js +1 -0
  51. package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.d.ts +9 -1
  52. package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.js +10 -8
  53. package/dist/src/foundation/pi-component-adapter/upstream/theme/theme.d.ts +8 -0
  54. package/dist/src/foundation/pi-component-adapter/upstream/theme/theme.js +12 -0
  55. package/dist/src/foundation/pi-engine-adapter/adapter.d.ts +17 -3
  56. package/dist/src/foundation/pi-engine-adapter/adapter.js +80 -7
  57. package/dist/src/foundation/pi-engine-adapter/pi-settings-metadata.json +374 -0
  58. package/dist/src/foundation/pi-engine-adapter/public-main-entry.js +10 -1
  59. package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +40 -2
  60. package/dist/src/foundation/pi-engine-adapter/settings-integration.js +172 -24
  61. package/dist/src/foundation/pi-engine-adapter/workflows.d.ts +9 -0
  62. package/dist/src/foundation/pi-owned-ui-integration/index.d.ts +1 -0
  63. package/dist/src/foundation/pi-owned-ui-integration/index.js +1 -0
  64. package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +29 -0
  65. package/dist/src/foundation/pi-owned-ui-integration/route-host.js +1 -0
  66. package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +6 -0
  67. package/dist/src/foundation/pi-owned-ui-integration/session-shell.js +71 -1
  68. package/dist/src/foundation/pi-tui-runtime-adapter/adapter.d.ts +6 -0
  69. package/dist/src/foundation/pi-tui-runtime-adapter/adapter.js +9 -0
  70. package/dist/src/foundation/process-containment/artifact.d.ts +1 -0
  71. package/dist/src/foundation/process-containment/artifact.js +34 -0
  72. package/dist/src/foundation/process-containment/contracts.d.ts +31 -0
  73. package/dist/src/foundation/process-containment/contracts.js +35 -0
  74. package/dist/src/foundation/process-containment/index.d.ts +5 -0
  75. package/dist/src/foundation/process-containment/index.js +5 -0
  76. package/dist/src/foundation/process-containment/linux-process-inspector.d.ts +6 -0
  77. package/dist/src/foundation/process-containment/linux-process-inspector.js +26 -0
  78. package/dist/src/foundation/process-containment/native-guardian-containment.d.ts +16 -0
  79. package/dist/src/foundation/process-containment/native-guardian-containment.js +139 -0
  80. package/dist/src/foundation/process-containment/windows-process-inspector.d.ts +17 -0
  81. package/dist/src/foundation/process-containment/windows-process-inspector.js +53 -0
  82. package/dist/src/foundation/protocol/client.d.ts +2 -1
  83. package/dist/src/foundation/protocol/client.js +3 -0
  84. package/dist/src/foundation/protocol/messages.d.ts +5 -5
  85. package/dist/src/foundation/protocol/messages.js +1 -1
  86. package/dist/src/foundation/release/bootstrap.d.ts +2 -2
  87. package/dist/src/foundation/release/bootstrap.js +69 -18
  88. package/dist/src/foundation/release/cohort-selection.js +3 -3
  89. package/dist/src/foundation/release/cohort-state.d.ts +3 -2
  90. package/dist/src/foundation/release/concurrency.d.ts +1 -0
  91. package/dist/src/foundation/release/concurrency.js +17 -0
  92. package/dist/src/foundation/release/process-cleanup.d.ts +1 -1
  93. package/dist/src/foundation/release/process-cleanup.js +2 -2
  94. package/dist/src/foundation/release/release-store.d.ts +35 -2
  95. package/dist/src/foundation/release/release-store.js +90 -31
  96. package/dist/src/foundation/release/release.d.ts +15 -0
  97. package/dist/src/foundation/release/release.js +65 -36
  98. package/dist/src/foundation/release/update.d.ts +19 -2
  99. package/dist/src/foundation/release/update.js +132 -16
  100. package/dist/src/foundation/storage/control-store.d.ts +8 -7
  101. package/dist/src/foundation/storage/control-store.js +105 -55
  102. package/dist/src/foundation/supervision/main.js +6 -5
  103. package/dist/src/foundation/supervision/server.d.ts +4 -2
  104. package/dist/src/foundation/supervision/server.js +254 -87
  105. package/dist/src/foundation/ui-apps/contracts.d.ts +42 -0
  106. package/dist/src/foundation/ui-apps/contracts.js +12 -0
  107. package/dist/src/foundation/ui-apps/host.d.ts +42 -0
  108. package/dist/src/foundation/ui-apps/host.js +152 -0
  109. package/dist/src/foundation/ui-apps/index.d.ts +3 -0
  110. package/dist/src/foundation/ui-apps/index.js +3 -0
  111. package/dist/src/foundation/ui-apps/registry.d.ts +11 -0
  112. package/dist/src/foundation/ui-apps/registry.js +32 -0
  113. package/dist/src/foundation/ui-components/dialog-panel.d.ts +36 -0
  114. package/dist/src/foundation/ui-components/dialog-panel.js +47 -0
  115. package/dist/src/foundation/ui-components/frame.d.ts +21 -0
  116. package/dist/src/foundation/ui-components/frame.js +54 -0
  117. package/dist/src/foundation/ui-components/index.d.ts +18 -0
  118. package/dist/src/foundation/ui-components/index.js +18 -0
  119. package/dist/src/foundation/ui-components/label.d.ts +8 -0
  120. package/dist/src/foundation/ui-components/label.js +36 -0
  121. package/dist/src/foundation/ui-components/line-input.d.ts +74 -0
  122. package/dist/src/foundation/ui-components/line-input.js +245 -0
  123. package/dist/src/foundation/ui-components/list-block.d.ts +66 -0
  124. package/dist/src/foundation/ui-components/list-block.js +176 -0
  125. package/dist/src/foundation/ui-components/list-view.d.ts +66 -0
  126. package/dist/src/foundation/ui-components/list-view.js +76 -0
  127. package/dist/src/foundation/ui-components/mouse.d.ts +15 -0
  128. package/dist/src/foundation/ui-components/mouse.js +46 -0
  129. package/dist/src/foundation/ui-components/pane.d.ts +33 -0
  130. package/dist/src/foundation/ui-components/pane.js +7 -0
  131. package/dist/src/foundation/ui-components/revision.d.ts +35 -0
  132. package/dist/src/foundation/ui-components/revision.js +79 -0
  133. package/dist/src/foundation/ui-components/scrollbar.d.ts +62 -0
  134. package/dist/src/foundation/ui-components/scrollbar.js +102 -0
  135. package/dist/src/foundation/ui-components/shortcuts.d.ts +57 -0
  136. package/dist/src/foundation/ui-components/shortcuts.js +81 -0
  137. package/dist/src/foundation/ui-components/spans.d.ts +7 -0
  138. package/dist/src/foundation/ui-components/spans.js +59 -0
  139. package/dist/src/foundation/ui-components/status-line.d.ts +16 -0
  140. package/dist/src/foundation/ui-components/status-line.js +14 -0
  141. package/dist/src/foundation/ui-components/stepper.d.ts +24 -0
  142. package/dist/src/foundation/ui-components/stepper.js +33 -0
  143. package/dist/src/foundation/ui-components/surface.d.ts +20 -0
  144. package/dist/src/foundation/ui-components/surface.js +38 -0
  145. package/dist/src/foundation/ui-components/text.d.ts +24 -0
  146. package/dist/src/foundation/ui-components/text.js +123 -0
  147. package/dist/src/foundation/ui-components/theme.d.ts +23 -0
  148. package/dist/src/foundation/ui-components/theme.js +13 -0
  149. package/dist/src/foundation/ui-components/value-menu.d.ts +41 -0
  150. package/dist/src/foundation/ui-components/value-menu.js +33 -0
  151. package/dist/src/product-identity.d.ts +2 -2
  152. package/dist/src/product-identity.js +2 -1
  153. package/dist/src/product-identity.json +8 -5
  154. package/docs/architecture/boundaries.md +31 -13
  155. package/docs/architecture/process-guardian-provenance.md +26 -0
  156. package/docs/architecture/toolchain.md +9 -3
  157. package/docs/architecture/ui-reference-provenance.md +50 -0
  158. package/docs/ci-release-runbook.md +67 -0
  159. package/docs/features/launch-profiles.md +7 -1
  160. package/docs/manual-launch-instance-acceptance.md +66 -0
  161. package/docs/manual-terminal-colour-check.md +55 -0
  162. package/package.json +20 -14
  163. package/dist/src/composition/transparent-runtime.d.ts +0 -6
  164. package/dist/src/composition/transparent-runtime.js +0 -19
  165. package/dist/src/foundation/transparent-terminal/command-resolution.d.ts +0 -13
  166. package/dist/src/foundation/transparent-terminal/command-resolution.js +0 -69
  167. package/dist/src/foundation/transparent-terminal/foreground-broker.d.ts +0 -32
  168. package/dist/src/foundation/transparent-terminal/foreground-broker.js +0 -87
  169. package/dist/src/foundation/transparent-terminal/index.d.ts +0 -4
  170. package/dist/src/foundation/transparent-terminal/index.js +0 -4
  171. package/dist/src/foundation/transparent-terminal/main.d.ts +0 -9
  172. package/dist/src/foundation/transparent-terminal/main.js +0 -94
  173. package/dist/src/foundation/transparent-terminal/native-launcher.d.ts +0 -25
  174. package/dist/src/foundation/transparent-terminal/native-launcher.js +0 -146
  175. /package/bin/{a1.js → cli.js} +0 -0
  176. /package/bin/{a1-supervisor.js → supervisor.js} +0 -0
package/README.md CHANGED
@@ -1,20 +1,11 @@
1
1
  # A1
2
2
 
3
- A1 is a terminal-native agent launcher for Windows, Linux, and macOS. Bare `a1` runs the A1-owned Pi-compatible UI, while explicit fallback profiles can launch untouched Pi directly. A1 also manages release selection, process ownership, and updates.
4
-
5
3
  ## Install
6
4
 
7
- Requirements:
8
-
9
- - Node.js 22.19 through 24.x
10
- - npm 11
11
-
12
5
  ```sh
13
6
  npm install --global @timurproko/a1@latest
14
7
  ```
15
8
 
16
- The package installs only the `a1` command.
17
-
18
9
  ## Commands
19
10
 
20
11
  ```sh
@@ -26,45 +17,19 @@ a1 update # update to npm latest
26
17
  a1 update:next # update to npm next
27
18
  ```
28
19
 
29
- Bare `a1` owns its Pi-compatible TUI composition and uses the public Pi engine, components, and terminal APIs. It does not insert a PTY or terminal-byte relay. Default visuals and workflows track the pinned vanilla Pi baseline; A1-specific visual customization and structured tabs remain disabled.
30
-
31
- `a1 pi` is the untouched upstream fallback and comparison oracle. Use it to distinguish A1-owned UI problems from upstream Pi, profile, provider, or terminal problems. The `a1 ui` subcommand was removed and is not a compatibility alias; run bare `a1` for the owned UI.
32
-
33
- `a1 sandbox` is unchanged: it launches vanilla Pi with isolated Pi configuration and resources. “Sandbox” does not mean operating-system, filesystem, process, network, or credential security isolation. See [`docs/features/launch-profiles.md`](docs/features/launch-profiles.md) for first-run directories, independent `/login`, trust behavior, extension placement, and recovery.
34
-
35
20
  ## Develop
36
21
 
37
22
  ```sh
38
- npm ci
39
- npm run build
40
- npm start
23
+ npm ci # install exact locked dependencies
24
+ npm run build # compile TypeScript and the process guardian into dist
25
+ npm start # build and launch an isolated development instance
26
+ npm run test:fast # typecheck + fast suite, no build needed
27
+ npm test # same as test:fast
28
+ npm run test:full # complete non-physical suite
41
29
  ```
42
30
 
43
- `npm start` gives each invocation isolated A1 development state and an isolated development Pi profile. Use `npm start -- --print-environment` to inspect the selected paths without launching the UI.
44
-
45
- A1 control state uses `%APPDATA%\\A1` and `%LOCALAPPDATA%\\A1` on Windows, and the `a1` directory under XDG config/data/runtime roots on Unix. Override it only with declared `A1_*` variables such as `A1_CONFIG_DIR`, `A1_DATA_DIR`, `A1_RUNTIME_DIR`, `A1_DATABASE_PATH`, and `A1_ENDPOINT`. Pi profile roots remain `~/.a1/agent`, `~/.pi/agent`, and `~/.a1/sandbox`. This is a no-migration identity hard cut; see [`docs/architecture/toolchain.md`](docs/architecture/toolchain.md#identity-hard-cut-and-cleanup) before removing obsolete control state.
46
-
47
- Run the non-desktop gates with:
31
+ ## Release
48
32
 
49
33
  ```sh
50
- npm run typecheck
51
- npm run check:architecture
52
- npm run check:deprecated
53
- npm test
54
- npm run test:release
34
+ npm run release:next # publish current develop tip to npm next via trusted CI workflows
55
35
  ```
56
-
57
- Automated physical-terminal interaction must never run on an active workstation. Future physical certification may run only on dedicated disposable workers or VMs with exclusive test desktops.
58
-
59
- ## Releases
60
-
61
- An exact manually accepted `-dev.N` candidate may publish under npm tag `next` after applicable non-desktop gates pass. Such a preview is explicitly uncertified, cannot move `latest`, and cannot claim stable terminal parity or platform support. Stable claims require deferred physical and cross-platform certification against the exact package.
62
-
63
- Trusted preview publication uses `.github/workflows/publish-next.yml` and npm provenance. Local release mechanics are documented in [`docs/architecture/toolchain.md`](docs/architecture/toolchain.md).
64
-
65
- ## Architecture
66
-
67
- - [`docs/architecture/boundaries.md`](docs/architecture/boundaries.md)
68
- - [`docs/architecture/toolchain.md`](docs/architecture/toolchain.md)
69
- - [`docs/manual-transparent-checkpoint.md`](docs/manual-transparent-checkpoint.md)
70
- - [`docs/features/launch-profiles.md`](docs/features/launch-profiles.md)
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { fileURLToPath } = await import("node:url");
4
+ const { resolve } = await import("node:path");
5
+ const { runLaunchGuardian } = await import("../dist/src/foundation/launch-guardian/index.js");
6
+
7
+ const releaseRoot = process.env.A1_RELEASE_ROOT ?? fileURLToPath(new URL("..", import.meta.url));
8
+ const profileId = process.env.A1_LAUNCH_PROFILE ?? "a1";
9
+
10
+ runLaunchGuardian({
11
+ profileId,
12
+ releaseRoot,
13
+ uiEntry: resolve(releaseRoot, "bin", "ui.js"),
14
+ environment: process.env,
15
+ cwd: process.cwd(),
16
+ }).then(
17
+ code => { process.exitCode = code; },
18
+ error => {
19
+ console.error(error instanceof Error ? error.message : String(error));
20
+ process.exitCode = 1;
21
+ },
22
+ );
@@ -3,17 +3,13 @@
3
3
  const { runSelectedInteractiveRuntime } = await import("../dist/src/features/launch/index.js");
4
4
 
5
5
  runSelectedInteractiveRuntime(process.env.A1_LAUNCH_PROFILE ?? "a1", {
6
- ownedUi: async () => {
7
- const [{ runOwnedUi }, { composeOwnedUiApplication }] = await Promise.all([
6
+ ownedUi: async (profileId, ownedSurfaces) => {
7
+ const [{ runOwnedUi }, { composeOwnedUi }] = await Promise.all([
8
8
  import("../dist/src/features/owned-ui/index.js"),
9
9
  import("../dist/src/composition/index.js"),
10
10
  ]);
11
- const application = await composeOwnedUiApplication({ cwd: process.cwd() });
12
- return await runOwnedUi({ application });
13
- },
14
- transparent: async profileId => {
15
- const { runSelectedTransparentRuntime } = await import("../dist/src/composition/transparent-runtime.js");
16
- return await runSelectedTransparentRuntime(profileId);
11
+ const { application, settings } = await composeOwnedUi({ cwd: process.cwd(), profileId, ownedSurfaces });
12
+ return await runOwnedUi({ application, ...(settings === null ? {} : { settings }) });
17
13
  },
18
14
  }).then(
19
15
  code => { process.exitCode = code; },
@@ -0,0 +1,20 @@
1
+ {
2
+ "schema": "a1-process-guardian-artifact-v1",
3
+ "protocolVersion": 1,
4
+ "crateVersion": "0.1.0-dev",
5
+ "platform": "darwin",
6
+ "architecture": "arm64",
7
+ "capability": "unsupported",
8
+ "builtAt": "2026-08-23T09:02:26.759Z",
9
+ "artifact": {
10
+ "filename": "process-guardian",
11
+ "sha256": "7524bf568992517f50ed53196c861c5edeba0d7275633bb4735ab45bd5963a28",
12
+ "size": 356368
13
+ },
14
+ "provenance": {
15
+ "language": "Rust",
16
+ "cargoLock": "native/process-guardian/Cargo.lock",
17
+ "sourceRoot": "native/process-guardian",
18
+ "signatureStatus": "ci-attestation-required"
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "schema": "a1-process-guardian-artifact-v1",
3
+ "protocolVersion": 1,
4
+ "crateVersion": "0.1.0-dev",
5
+ "platform": "linux",
6
+ "architecture": "x64",
7
+ "capability": "supported",
8
+ "builtAt": "2026-08-23T09:02:48.237Z",
9
+ "artifact": {
10
+ "filename": "process-guardian",
11
+ "sha256": "29e22fe29de2828982bc67ef418c4adcaab1490281477b7bea592ec6fe621bcd",
12
+ "size": 415696
13
+ },
14
+ "provenance": {
15
+ "language": "Rust",
16
+ "cargoLock": "native/process-guardian/Cargo.lock",
17
+ "sourceRoot": "native/process-guardian",
18
+ "signatureStatus": "ci-attestation-required"
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "schema": "a1-process-guardian-artifact-v1",
3
+ "protocolVersion": 1,
4
+ "crateVersion": "0.1.0-dev",
5
+ "platform": "win32",
6
+ "architecture": "x64",
7
+ "capability": "supported",
8
+ "builtAt": "2026-08-23T09:02:55.602Z",
9
+ "artifact": {
10
+ "filename": "process-guardian.exe",
11
+ "sha256": "0487a97a0676a7cdbe7b5328b38d555a568ea54eaa26fadb2e9e62e18cf3211b",
12
+ "size": 172544
13
+ },
14
+ "provenance": {
15
+ "language": "Rust",
16
+ "cargoLock": "native/process-guardian/Cargo.lock",
17
+ "sourceRoot": "native/process-guardian",
18
+ "signatureStatus": "ci-attestation-required"
19
+ }
20
+ }
@@ -1,6 +1,8 @@
1
1
  import { type AgentEnginePort } from "../foundation/agent-engine-contracts/index.js";
2
2
  import { type PiEngineAdapter } from "../foundation/pi-engine-adapter/index.js";
3
3
  import { type OwnedUiApplicationPort, type PresentationComponentPort, type PresentationRuntimePort, type PresentationTerminalPort } from "../foundation/presentation-contracts/index.js";
4
+ import { OwnedUiSettingsSession } from "../foundation/owned-ui-settings/index.js";
5
+ import type { UiRouteHost } from "../foundation/pi-owned-ui-integration/index.js";
4
6
  export interface ProcessCompositionOptions {
5
7
  readonly cwd?: string;
6
8
  readonly sessionId?: string;
@@ -12,11 +14,35 @@ export interface OwnedUiCompositionOptions {
12
14
  readonly cwd?: string;
13
15
  readonly terminal?: PresentationTerminalPort;
14
16
  readonly createPiAdapter?: () => Promise<PiEngineAdapter>;
17
+ /**
18
+ * A1 profile whose settings this session reads and writes. Omitted keeps the
19
+ * session settings-free, which is what the pinned comparison paths use.
20
+ */
21
+ readonly profileId?: string;
22
+ /**
23
+ * Whether A1's own surfaces are reachable. Withheld for the rendering parity
24
+ * comparison, which measures this composition against pinned Pi and so must
25
+ * present Pi's interface and nothing of A1's own. It is the same composition
26
+ * either way: a parity run built beside the product would measure itself.
27
+ */
28
+ readonly ownedSurfaces?: "on" | "off";
29
+ }
30
+ export interface OwnedUiComposition {
31
+ readonly application: OwnedUiApplicationPort;
32
+ /** Present when a profile was supplied, so the caller can resolve settings before start. */
33
+ readonly settings: OwnedUiSettingsSession | null;
15
34
  }
16
35
  export declare function composeOwnedUiApplication(options?: OwnedUiCompositionOptions): Promise<OwnedUiApplicationPort>;
36
+ export declare function composeOwnedUi(options?: OwnedUiCompositionOptions): Promise<OwnedUiComposition>;
17
37
  export interface ProcessComposition {
18
38
  readonly engine: AgentEnginePort;
19
39
  createPresentation(root: PresentationComponentPort, terminal: PresentationTerminalPort): PresentationRuntimePort;
20
40
  dispose(): Promise<void>;
21
41
  }
22
42
  export declare function composeProcess(options?: ProcessCompositionOptions): Promise<ProcessComposition>;
43
+ /**
44
+ * Registers the A1-owned screens and adapts the app host to the shell's route
45
+ * seam. The shell mounts whatever surface it is handed and forwards keys; it
46
+ * never learns what an app is.
47
+ */
48
+ export declare function createOwnedRouteHost(settings: OwnedUiSettingsSession): UiRouteHost;
@@ -3,6 +3,12 @@ import { createPiEngineAdapter } from "../foundation/pi-engine-adapter/index.js"
3
3
  import { createPiPresentationRuntime, createPiTerminalBridge } from "../foundation/pi-tui-runtime-adapter/index.js";
4
4
  import { assertPresentationComponent, assertPresentationRuntime, } from "../foundation/presentation-contracts/index.js";
5
5
  import { OwnedUiSessionShell } from "../foundation/pi-owned-ui-integration/index.js";
6
+ import { OwnedUiSettingsSession, OwnedUiSettingsStore } from "../foundation/owned-ui-settings/index.js";
7
+ import { UiAppHost, UiAppRegistry } from "../foundation/ui-apps/index.js";
8
+ import { applyConfiguredPiTheme, getAvailablePiThemes, piTheme } from "../foundation/pi-component-adapter/index.js";
9
+ import { faint } from "../foundation/ui-components/index.js";
10
+ import { SETTINGS_APP_ID, SETTINGS_ROUTE, SettingsApp } from "../features/owned-ui/index.js";
11
+ import { resolveProductPaths } from "../foundation/lifecycle/index.js";
6
12
  const CAPABILITIES = {
7
13
  contractVersion: AGENT_ENGINE_CONTRACT_VERSION,
8
14
  commands: ["prompt", "steer", "follow-up", "abort", "retry", "compact", "bash", "replace-session"],
@@ -10,20 +16,37 @@ const CAPABILITIES = {
10
16
  snapshots: { supported: true, maxBytes: 1024 * 1024 },
11
17
  };
12
18
  export async function composeOwnedUiApplication(options = {}) {
19
+ return (await composeOwnedUi(options)).application;
20
+ }
21
+ export async function composeOwnedUi(options = {}) {
13
22
  const cwd = options.cwd ?? process.cwd();
14
- const adapter = options.createPiAdapter ? await options.createPiAdapter() : await createPiEngineAdapter({ cwd });
23
+ const adapter = options.createPiAdapter
24
+ ? await options.createPiAdapter()
25
+ : await createPiEngineAdapter({ cwd, availableThemes: () => getAvailablePiThemes().map(theme => theme.name) });
26
+ const settings = options.profileId === undefined
27
+ ? null
28
+ : new OwnedUiSettingsSession({
29
+ store: new OwnedUiSettingsStore({ configDir: resolveProductPaths().configDir, profileId: options.profileId }),
30
+ agentProvider: () => adapter.settingsPort(),
31
+ });
32
+ // Before anything is drawn, so every surface uses the configured theme rather
33
+ // than the one guessed from the terminal's background.
34
+ applyConfiguredPiTheme(adapter.configuredTheme());
35
+ const routeHost = settings === null || options.ownedSurfaces === "off" ? null : createOwnedRouteHost(settings);
15
36
  const shell = new OwnedUiSessionShell({
16
37
  backend: adapter,
17
38
  cwd,
18
39
  ...(options.terminal === undefined ? {} : { terminal: createPiTerminalBridge(options.terminal) }),
40
+ ...(routeHost === null ? {} : { routeHost }),
19
41
  });
20
- return {
42
+ const application = {
21
43
  get disposed() { return adapter.disposed; },
22
44
  start: () => shell.start(),
23
45
  flush: () => adapter.flushEvents(),
24
46
  waitUntilStopped: () => shell.waitUntilStopped(),
25
47
  dispose: () => shell.dispose(),
26
48
  };
49
+ return { application, settings };
27
50
  }
28
51
  export async function composeProcess(options = {}) {
29
52
  let engine = options.engine;
@@ -164,3 +187,72 @@ function toMessage(block) {
164
187
  const role = block.kind === "user" ? "user" : block.kind === "system" ? "system" : block.kind === "tool-call" || block.kind === "tool-result" ? "tool" : "assistant";
165
188
  return { id: block.id, role, status: block.status === "live" ? "streaming" : "final", content: [{ kind: "text", text: block.text }] };
166
189
  }
190
+ /**
191
+ * Registers the A1-owned screens and adapts the app host to the shell's route
192
+ * seam. The shell mounts whatever surface it is handed and forwards keys; it
193
+ * never learns what an app is.
194
+ */
195
+ export function createOwnedRouteHost(settings) {
196
+ const registry = new UiAppRegistry();
197
+ registry.register({ id: SETTINGS_APP_ID, route: SETTINGS_ROUTE, create: () => new SettingsApp(settings) });
198
+ return {
199
+ claims: route => registry.forRoute(route) !== null,
200
+ open: route => {
201
+ const registration = registry.forRoute(route);
202
+ if (registration === null)
203
+ return null;
204
+ let size = { width: 80, height: 24 };
205
+ let frame = [];
206
+ let closed = false;
207
+ let onRender = () => undefined;
208
+ let onExit = () => undefined;
209
+ const host = new UiAppHost({
210
+ registry,
211
+ closeOnInterrupt: true,
212
+ theme: pinnedTheme(),
213
+ surface: {
214
+ size: () => size,
215
+ requestRender: () => onRender(),
216
+ exit: () => onExit(),
217
+ present: lines => {
218
+ if (lines === null)
219
+ closed = true;
220
+ else
221
+ frame = lines;
222
+ },
223
+ },
224
+ });
225
+ host.open(registration.id);
226
+ const surface = {
227
+ id: registration.id,
228
+ render: (width, height) => {
229
+ size = { width, height };
230
+ host.render();
231
+ return frame.length === height ? frame : [...frame.slice(0, height), ...Array(Math.max(0, height - frame.length)).fill("")];
232
+ },
233
+ handleInput: data => host.handleInput(data).consumed,
234
+ handleMouse: event => host.handleMouse(event).consumed,
235
+ isClosed: () => closed || !host.isPresenting,
236
+ close: () => host.close(),
237
+ onRenderRequested: listener => { onRender = listener; },
238
+ onExitRequested: listener => { onExit = listener; },
239
+ };
240
+ return surface;
241
+ },
242
+ };
243
+ }
244
+ /** Maps A1 UI tokens onto the pinned Pi theme so owned screens match the shell. */
245
+ function pinnedTheme() {
246
+ return {
247
+ fg: (token, text) => piTheme().fg(token, text),
248
+ bold: (text) => piTheme().bold(text),
249
+ // Unpainted, so a list reads against the terminal the reader actually has
250
+ // rather than against the background the configured theme was built for.
251
+ plain: (text) => text,
252
+ // Quiet, and quieter again: the terminal's own faint attribute over the
253
+ // dimmest colour the theme has.
254
+ disabled: (text) => faint(piTheme().fg("dim", text)),
255
+ highlight: (text) => `${text}`,
256
+ panel: (text) => `${text}`,
257
+ };
258
+ }
@@ -38,7 +38,7 @@ async function ensureSafeDirectoryChain(root) {
38
38
  current = parent;
39
39
  }
40
40
  for (const path of missing.reverse())
41
- await mkdir(path, { mode: 0o700 });
41
+ await createOwnedDirectory(path, "profile path");
42
42
  }
43
43
  async function ensureOwnedDirectory(path) {
44
44
  const metadata = await lstat(path).catch(error => {
@@ -51,7 +51,28 @@ async function ensureOwnedDirectory(path) {
51
51
  throw new Error(`profile resource path is not an owned directory: ${path}`);
52
52
  return;
53
53
  }
54
- await mkdir(path, { mode: 0o700 });
54
+ await createOwnedDirectory(path, "profile resource path");
55
+ }
56
+ /**
57
+ * Creates a directory, treating "it already exists" as another launch having won
58
+ * the race rather than as a failure — but still refusing what it finds if that is
59
+ * not an owned directory.
60
+ */
61
+ async function createOwnedDirectory(path, subject) {
62
+ try {
63
+ await mkdir(path, { mode: 0o700 });
64
+ return;
65
+ }
66
+ catch (error) {
67
+ if (!isAlreadyThere(error))
68
+ throw error;
69
+ }
70
+ const metadata = await lstat(path);
71
+ if (!metadata.isDirectory() || metadata.isSymbolicLink())
72
+ throw new Error(`${subject} is not an owned directory: ${path}`);
73
+ }
74
+ function isAlreadyThere(error) {
75
+ return typeof error === "object" && error !== null && error.code === "EEXIST";
55
76
  }
56
77
  function isMissing(error) {
57
78
  return error instanceof Error && "code" in error && error.code === "ENOENT";
@@ -7,6 +7,6 @@ export interface LaunchProfileContract {
7
7
  readonly productSurface: "agent" | "vanilla-baseline" | "isolated-profile";
8
8
  readonly configurationRootPolicy: ConfigurationRootPolicy;
9
9
  readonly projectTrust: ProjectTrustPolicy;
10
- readonly terminalCapability: "owned-ui" | "transparent";
10
+ readonly terminalCapability: "owned-ui";
11
11
  }
12
12
  export declare function launchProfileContract(id: LaunchProfileId): LaunchProfileContract;
@@ -11,14 +11,14 @@ const contracts = Object.freeze({
11
11
  productSurface: "vanilla-baseline",
12
12
  configurationRootPolicy: "pi-default",
13
13
  projectTrust: "pi-default",
14
- terminalCapability: "transparent",
14
+ terminalCapability: "owned-ui",
15
15
  }),
16
16
  sandbox: Object.freeze({
17
17
  id: "sandbox",
18
18
  productSurface: "isolated-profile",
19
19
  configurationRootPolicy: "sandbox-profile",
20
20
  projectTrust: "ignore",
21
- terminalCapability: "transparent",
21
+ terminalCapability: "owned-ui",
22
22
  }),
23
23
  });
24
24
  export function launchProfileContract(id) {
@@ -1,14 +1,19 @@
1
1
  import { type LaunchProfileId } from "../../foundation/lifecycle/index.js";
2
- export type TransparentInteractiveProfileId = Exclude<LaunchProfileId, "a1">;
3
- export type InteractiveRuntimeSelection = {
2
+ /**
3
+ * Every interactive command is the same composition. What differs is the
4
+ * configuration root each one reads and whether A1's own screens are reachable:
5
+ * bare `a1` is the product, while `a1 pi` and `a1 sandbox` present pinned Pi's
6
+ * interface and nothing of A1's own, against Pi's own profile and against an
7
+ * isolated one.
8
+ */
9
+ export type OwnedUiProfileId = LaunchProfileId;
10
+ export interface InteractiveRuntimeSelection {
4
11
  readonly kind: "owned-ui";
5
- } | {
6
- readonly kind: "transparent";
7
- readonly profileId: TransparentInteractiveProfileId;
8
- };
12
+ readonly profileId: OwnedUiProfileId;
13
+ readonly ownedSurfaces: "on" | "off";
14
+ }
9
15
  export interface InteractiveRuntimeRunners {
10
- readonly ownedUi: () => Promise<number>;
11
- readonly transparent: (profileId: TransparentInteractiveProfileId) => Promise<number>;
16
+ readonly ownedUi: (profileId: OwnedUiProfileId, ownedSurfaces: "on" | "off") => Promise<number>;
12
17
  }
13
18
  export declare function selectInteractiveRuntime(profileId: string): InteractiveRuntimeSelection;
14
19
  export declare function runSelectedInteractiveRuntime(profileId: string, runners: InteractiveRuntimeRunners): Promise<number>;
@@ -1,13 +1,13 @@
1
1
  import { assertLaunchProfileId } from "../../foundation/lifecycle/index.js";
2
2
  export function selectInteractiveRuntime(profileId) {
3
3
  assertLaunchProfileId(profileId);
4
- return profileId === "a1"
5
- ? Object.freeze({ kind: "owned-ui" })
6
- : Object.freeze({ kind: "transparent", profileId });
4
+ return Object.freeze({
5
+ kind: "owned-ui",
6
+ profileId,
7
+ ownedSurfaces: profileId === "a1" ? "on" : "off",
8
+ });
7
9
  }
8
10
  export async function runSelectedInteractiveRuntime(profileId, runners) {
9
11
  const selection = selectInteractiveRuntime(profileId);
10
- return selection.kind === "owned-ui"
11
- ? await runners.ownedUi()
12
- : await runners.transparent(selection.profileId);
12
+ return await runners.ownedUi(selection.profileId, selection.ownedSurfaces);
13
13
  }
@@ -1,3 +1,4 @@
1
1
  export * from "./customization.js";
2
2
  export * from "./diagnostics.js";
3
3
  export * from "./run.js";
4
+ export * from "./settings-app.js";
@@ -1,3 +1,4 @@
1
1
  export * from "./customization.js";
2
2
  export * from "./diagnostics.js";
3
3
  export * from "./run.js";
4
+ export * from "./settings-app.js";
@@ -1,5 +1,11 @@
1
1
  import type { OwnedUiApplicationPort } from "../../foundation/presentation-contracts/index.js";
2
+ import type { OwnedUiSettingsSession } from "../../foundation/owned-ui-settings/index.js";
2
3
  export interface OwnedUiRunOptions {
3
4
  readonly application: OwnedUiApplicationPort;
5
+ /**
6
+ * Resolved once before the application starts, so every surface reads the same
7
+ * values for the life of the session. Omitted when the caller runs without settings.
8
+ */
9
+ readonly settings?: OwnedUiSettingsSession;
4
10
  }
5
11
  export declare function runOwnedUi(options: OwnedUiRunOptions): Promise<number>;
@@ -1,6 +1,8 @@
1
1
  export async function runOwnedUi(options) {
2
- const { application } = options;
2
+ const { application, settings } = options;
3
3
  try {
4
+ if (settings)
5
+ await settings.load();
4
6
  application.start();
5
7
  await application.flush();
6
8
  if (!application.disposed)
@@ -0,0 +1,22 @@
1
+ import type { AppHostServices, UiApp } from "../../foundation/ui-apps/index.js";
2
+ import { ShortcutRegistry, type PaneInputResult, type PaneMouseEvent, type PaneRect } from "../../foundation/ui-components/index.js";
3
+ import type { OwnedUiSettingsSession } from "../../foundation/owned-ui-settings/index.js";
4
+ export declare const SETTINGS_APP_ID = "settings";
5
+ export declare const SETTINGS_ROUTE = "settings";
6
+ type Action = "move-up" | "move-down" | "block-up" | "block-down" | "first" | "last" | "page-up" | "page-down" | "previous-value" | "next-value" | "activate" | "open-filter" | "close" | "part-previous" | "part-next" | "part-change";
7
+ export declare const SETTINGS_SHORTCUTS: ShortcutRegistry<Action>;
8
+ /**
9
+ * The settings screen. Rows, sticky headers, scrolling, and the keymap come from
10
+ * the shared component layer; what belongs to settings is which sections exist,
11
+ * how a value is shown, and where an accepted change is routed.
12
+ */
13
+ export declare class SettingsApp implements UiApp {
14
+ #private;
15
+ readonly id = "settings";
16
+ constructor(session: OwnedUiSettingsSession);
17
+ onActivate(host: AppHostServices): void;
18
+ render(rect: PaneRect, host: AppHostServices): readonly string[];
19
+ onInput(data: string, host: AppHostServices): PaneInputResult;
20
+ onMouse(event: PaneMouseEvent, _host: AppHostServices): PaneInputResult;
21
+ }
22
+ export {};