@timurproko/a1 0.1.1-dev.6 → 0.1.1-dev.8

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 (167) hide show
  1. package/README.md +32 -9
  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.js +30 -11
  87. package/dist/src/foundation/release/cohort-selection.js +3 -3
  88. package/dist/src/foundation/release/cohort-state.d.ts +3 -2
  89. package/dist/src/foundation/release/process-cleanup.d.ts +1 -1
  90. package/dist/src/foundation/release/process-cleanup.js +2 -2
  91. package/dist/src/foundation/release/update.js +2 -2
  92. package/dist/src/foundation/storage/control-store.d.ts +8 -7
  93. package/dist/src/foundation/storage/control-store.js +105 -55
  94. package/dist/src/foundation/supervision/server.d.ts +4 -2
  95. package/dist/src/foundation/supervision/server.js +254 -87
  96. package/dist/src/foundation/ui-apps/contracts.d.ts +42 -0
  97. package/dist/src/foundation/ui-apps/contracts.js +12 -0
  98. package/dist/src/foundation/ui-apps/host.d.ts +42 -0
  99. package/dist/src/foundation/ui-apps/host.js +152 -0
  100. package/dist/src/foundation/ui-apps/index.d.ts +3 -0
  101. package/dist/src/foundation/ui-apps/index.js +3 -0
  102. package/dist/src/foundation/ui-apps/registry.d.ts +11 -0
  103. package/dist/src/foundation/ui-apps/registry.js +32 -0
  104. package/dist/src/foundation/ui-components/dialog-panel.d.ts +36 -0
  105. package/dist/src/foundation/ui-components/dialog-panel.js +47 -0
  106. package/dist/src/foundation/ui-components/frame.d.ts +21 -0
  107. package/dist/src/foundation/ui-components/frame.js +54 -0
  108. package/dist/src/foundation/ui-components/index.d.ts +18 -0
  109. package/dist/src/foundation/ui-components/index.js +18 -0
  110. package/dist/src/foundation/ui-components/label.d.ts +8 -0
  111. package/dist/src/foundation/ui-components/label.js +36 -0
  112. package/dist/src/foundation/ui-components/line-input.d.ts +74 -0
  113. package/dist/src/foundation/ui-components/line-input.js +245 -0
  114. package/dist/src/foundation/ui-components/list-block.d.ts +66 -0
  115. package/dist/src/foundation/ui-components/list-block.js +176 -0
  116. package/dist/src/foundation/ui-components/list-view.d.ts +66 -0
  117. package/dist/src/foundation/ui-components/list-view.js +76 -0
  118. package/dist/src/foundation/ui-components/mouse.d.ts +15 -0
  119. package/dist/src/foundation/ui-components/mouse.js +46 -0
  120. package/dist/src/foundation/ui-components/pane.d.ts +33 -0
  121. package/dist/src/foundation/ui-components/pane.js +7 -0
  122. package/dist/src/foundation/ui-components/revision.d.ts +35 -0
  123. package/dist/src/foundation/ui-components/revision.js +79 -0
  124. package/dist/src/foundation/ui-components/scrollbar.d.ts +62 -0
  125. package/dist/src/foundation/ui-components/scrollbar.js +102 -0
  126. package/dist/src/foundation/ui-components/shortcuts.d.ts +57 -0
  127. package/dist/src/foundation/ui-components/shortcuts.js +81 -0
  128. package/dist/src/foundation/ui-components/spans.d.ts +7 -0
  129. package/dist/src/foundation/ui-components/spans.js +59 -0
  130. package/dist/src/foundation/ui-components/status-line.d.ts +16 -0
  131. package/dist/src/foundation/ui-components/status-line.js +14 -0
  132. package/dist/src/foundation/ui-components/stepper.d.ts +24 -0
  133. package/dist/src/foundation/ui-components/stepper.js +33 -0
  134. package/dist/src/foundation/ui-components/surface.d.ts +20 -0
  135. package/dist/src/foundation/ui-components/surface.js +38 -0
  136. package/dist/src/foundation/ui-components/text.d.ts +24 -0
  137. package/dist/src/foundation/ui-components/text.js +123 -0
  138. package/dist/src/foundation/ui-components/theme.d.ts +23 -0
  139. package/dist/src/foundation/ui-components/theme.js +13 -0
  140. package/dist/src/foundation/ui-components/value-menu.d.ts +41 -0
  141. package/dist/src/foundation/ui-components/value-menu.js +33 -0
  142. package/dist/src/product-identity.d.ts +2 -2
  143. package/dist/src/product-identity.js +2 -1
  144. package/dist/src/product-identity.json +8 -5
  145. package/docs/architecture/boundaries.md +31 -13
  146. package/docs/architecture/process-guardian-provenance.md +26 -0
  147. package/docs/architecture/toolchain.md +8 -2
  148. package/docs/architecture/ui-reference-provenance.md +50 -0
  149. package/docs/ci-release-runbook.md +38 -42
  150. package/docs/features/launch-profiles.md +7 -1
  151. package/docs/manual-launch-instance-acceptance.md +66 -0
  152. package/docs/manual-terminal-colour-check.md +55 -0
  153. package/package.json +14 -15
  154. package/dist/src/composition/transparent-runtime.d.ts +0 -6
  155. package/dist/src/composition/transparent-runtime.js +0 -19
  156. package/dist/src/foundation/transparent-terminal/command-resolution.d.ts +0 -13
  157. package/dist/src/foundation/transparent-terminal/command-resolution.js +0 -69
  158. package/dist/src/foundation/transparent-terminal/foreground-broker.d.ts +0 -32
  159. package/dist/src/foundation/transparent-terminal/foreground-broker.js +0 -87
  160. package/dist/src/foundation/transparent-terminal/index.d.ts +0 -4
  161. package/dist/src/foundation/transparent-terminal/index.js +0 -4
  162. package/dist/src/foundation/transparent-terminal/main.d.ts +0 -9
  163. package/dist/src/foundation/transparent-terminal/main.js +0 -94
  164. package/dist/src/foundation/transparent-terminal/native-launcher.d.ts +0 -25
  165. package/dist/src/foundation/transparent-terminal/native-launcher.js +0 -146
  166. /package/bin/{a1.js → cli.js} +0 -0
  167. /package/bin/{a1-supervisor.js → supervisor.js} +0 -0
package/README.md CHANGED
@@ -9,6 +9,8 @@ Requirements:
9
9
  - Node.js 22.19 through 24.x
10
10
  - npm 11
11
11
 
12
+ Published packages include the integrity-verified process guardian for supported platforms. Building A1 from source additionally requires Rust/Cargo 1.85 or newer.
13
+
12
14
  ```sh
13
15
  npm install --global @timurproko/a1@latest
14
16
  ```
@@ -32,6 +34,8 @@ Bare `a1` owns its Pi-compatible TUI composition and uses the public Pi engine,
32
34
 
33
35
  `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
36
 
37
+ Every interactive command creates an independent, non-detachable launch instance. Any number of `a1`, `a1 pi`, and `a1 sandbox` commands may run in separate terminals at the same time. Closing one command closes only its owned UI/Pi process and all agents, extensions, tools, daemons, and descendants created by that invocation; it never requires finding PIDs, deleting control state, or restarting the supervisor. The idle supervisor may remain available for release coordination after all interactive instances close, but it owns no surviving instance runtime.
38
+
35
39
  ## Develop
36
40
 
37
41
  ```sh
@@ -44,18 +48,37 @@ npm start
44
48
 
45
49
  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
50
 
47
- ### Branch lifecycle
51
+ ### Worktree lifecycle
52
+
53
+ The primary worktree stays on `develop` and is integration-only. Each task uses a detached worktree under the repository's ignored `.worktrees/` directory, based on current `origin/develop` unless another base is selected.
54
+
55
+ ```sh
56
+ git fetch origin --prune
57
+ git worktree add --detach .worktrees/<task-id> origin/develop
58
+ cd .worktrees/<task-id>
59
+ ```
60
+
61
+ Commit and validate coherent work there. Because `develop` is protected, push the detached commit to one temporary remote branch and merge one pull request for the requested change. After merge, update the primary worktree and remove the task worktree only when its commits are reachable from `develop`:
62
+
63
+ ```sh
64
+ git push origin HEAD:refs/heads/<task-id>
65
+ gh pr create --base develop --head <task-id>
66
+ # After the pull request is merged:
67
+ cd <repository-root>
68
+ git fetch origin --prune
69
+ git merge --ff-only origin/develop
70
+ git worktree remove .worktrees/<task-id>
71
+ git worktree prune
72
+ ```
48
73
 
49
- Create every topic branch and every `milestone/<name>` work branch from `develop`; never implement directly on `develop` or `master`. A source branch is closed only after this sequence completes:
74
+ Manual acceptance does not leave a permanent validation checkout. Once acceptance is recorded, archive the completed OpenSpec change, integrate the archive commit into `develop`, then remove and prune every task or acceptance worktree retained for that change. Never remove a worktree before all of its implementation, acceptance, and archive commits are reachable from `develop`.
50
75
 
51
- 1. Validate the source branch with its required gates.
52
- 2. Merge it into `develop` and push the merged `develop` commit.
53
- 3. Switch to `develop`.
54
- 4. Preview the exact cleanup set with `npm run branches:prune -- --branch <source>`.
55
- 5. Safely delete the reviewed local source with `npm run branches:prune -- --apply --branch <source>`.
56
- 6. When the non-protected remote source exists, delete it in the same cleanup with `npm run branches:prune -- --apply --branch <source> --remote origin`.
76
+ Local package archives and ad hoc test builds belong under `.builds/`, never in the repository root. For manual package tests, use:
57
77
 
58
- The command defaults to a dry run against `develop`. It always retains `develop`, `master`, the checked-out branch, explicitly protected branches, and branches whose tips are not ancestors of the integration target. Apply mode uses only Git safe deletion (`git branch -d`), never force deletion. Use `--base <branch>` for another explicit integration target, `--protect <branch>` for additional protection, and `--json` for machine-readable review.
78
+ ```sh
79
+ mkdir -p .builds
80
+ npm pack --ignore-scripts --pack-destination .builds
81
+ ```
59
82
 
60
83
  Run the non-desktop gates with:
61
84
 
@@ -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-22T17:52:01.473Z",
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-22T17:52:01.995Z",
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-22T17:52:47.054Z",
9
+ "artifact": {
10
+ "filename": "process-guardian.exe",
11
+ "sha256": "ed14b51aeb577ecd86df4a8a9ca3ea33c27885da8d3d196b26b0d705d70a0b71",
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 {};