@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
@@ -0,0 +1,67 @@
1
+ # CI and release operations
2
+
3
+ GitHub Actions is the only automation platform. Two required checks protect the branches:
4
+
5
+ | Branch | Required check | Produced by |
6
+ | --- | --- | --- |
7
+ | `develop` | `Development validation required` | `.github/workflows/ci.yml` |
8
+ | `master` | `Stable candidate required` | `.github/workflows/certify-stable.yml` |
9
+
10
+ These are job display names. If you rename one, update the ruleset definition, this runbook, and the live ruleset together.
11
+
12
+ ## How much validation runs when
13
+
14
+ | Change | Validation |
15
+ | --- | --- |
16
+ | Docs or specs only | OpenSpec strict lint, nothing else |
17
+ | Any code PR into `develop` | Fast tier: typecheck, architecture checks, unit/contract tests |
18
+ | Preview candidate (`next`) | Fast tier + package gates: content, clean install, dependency policy |
19
+ | Stable candidate (`latest`) | Complete suite on Windows, Linux, and macOS + physical evidence |
20
+
21
+ Need more coverage for a risky preview? Dispatch the candidate with `full: true`, or run the **Full regression** workflow on demand.
22
+
23
+ ## Publish a preview to npm `next`
24
+
25
+ One command from a clean `develop` checkout runs the whole chain — candidate build, publish, registry verification:
26
+
27
+ ```sh
28
+ npm run release:next
29
+ ```
30
+
31
+ It dispatches the same trusted workflows below and never publishes local bytes. The manual steps remain the reference path:
32
+
33
+ 1. Make sure `Development validation required` is green on the `develop` tip.
34
+ 2. Dispatch **Build npm next candidate** with the exact commit and `confirm_candidate=build-uncertified-next-candidate`.
35
+ 3. Check the resulting `candidate-evidence.json`: gates passed, package integrity matches.
36
+ 4. Approve the `npm-publish` environment and dispatch **Publish npm next** with the candidate run id.
37
+
38
+ The publisher uploads the exact validated tarball — it never rebuilds. Candidates expire after 14 days; an expired or mismatched artifact means building a new candidate, not patching the old one.
39
+
40
+ ## Publish a stable release to npm `latest`
41
+
42
+ 1. Commit the final version to `develop` (clean tree, version not yet on the registry).
43
+ 2. Dispatch **Build stable candidate** — it packs once and validates the same bytes on all three platforms.
44
+ 3. Dispatch **Certify stable physical platforms** on the dedicated isolated workers (they set `PHYSICAL_WORKER_ISOLATED=true` and run under the `stable-physical` environment). Never run physical host probes on a developer workstation.
45
+ 4. Dispatch **Certify stable candidate** with both run ids. `Stable candidate required` passes only when every verdict binds the same commit, version, and digest.
46
+ 5. Merge that exact commit to `master`, tag it `v<version>`, and dispatch **Publish npm stable**, then approve `npm-publish`.
47
+
48
+ Stable artifacts expire after 30 days. The same rule as previews applies: publication still requires exact certified bytes. Never upload locally rebuilt bytes, and never route around certification by rebuilding inside a publisher.
49
+
50
+ ## When something fails
51
+
52
+ - **PR validation fails:** fix the code and push. Do not mark a failed tier optional.
53
+ - **Candidate validation fails:** discard the candidate and build a new one after the fix.
54
+ - **Physical worker fails:** fix or replace the worker, then rerun the full physical evidence for the candidate.
55
+ - **Publisher fails before npm accepted the bytes:** diagnose and retry with the same candidate while it is unexpired and the version is still unpublished.
56
+ - **Publisher is uncertain after npm accepted the bytes:** stop. Check the registry for the version, tag, and digest. Repair a dist-tag only as a separate reviewed operation — never republish.
57
+
58
+ ## Why the rulesets look the way they do
59
+
60
+ One person maintains this repository, and GitHub does not let a PR author approve their own PR. Requiring even one approval would therefore deadlock the authorized solo-maintainer path — no PR could ever merge. So both rulesets require a pull request, a green required check, and resolved review threads, but set required approving reviews to zero.
61
+
62
+ - `develop` does not require the branch to be up to date: once a PR is approved and green, it merges even if unrelated work landed first — no re-validation loop.
63
+ - `master` does require it: a stable promotion always validates the exact final state.
64
+
65
+ If a second maintainer joins, raise the approval count. Do not add a direct-push bypass as a shortcut, and never weaken the force-push or deletion protection.
66
+
67
+ Ruleset mutation is a separate administrative operation: run `node scripts/check-github-rulesets.mjs` to see the diff, and apply only when a maintainer explicitly confirms with `--apply --confirm apply-a1-ci-rulesets`.
@@ -43,7 +43,13 @@ Run Pi’s normal `/login` independently in each profile that needs stored authe
43
43
 
44
44
  Bare `a1` runs the A1-owned full-viewport TUI over public Pi engine, component, and terminal APIs. A1 owns composition, input routing, modal focus, selection behavior, scrolling, and restoration without inserting a PTY or terminal-byte relay. Its default presentation is pinned to vanilla Pi parity; structured tabs and A1-specific visual customization are not enabled.
45
45
 
46
- `a1 pi` and `a1 sandbox` retain transparent direct attachment. In those profiles, one untouched Pi process and the physical terminal own rendering, input, selection, clipboard, scrollback, and terminal modes; A1 owns foreground lease and lifecycle only.
46
+ `a1 pi` and `a1 sandbox` retain transparent direct attachment. In those profiles, untouched Pi and the physical terminal own rendering, input, selection, clipboard, scrollback, and terminal modes; A1 owns only per-invocation process containment and lifecycle evidence.
47
+
48
+ ## Concurrent instance lifecycle
49
+
50
+ Every `a1`, `a1 pi`, and `a1 sandbox` invocation receives a unique non-detachable launch instance. There is no product-wide foreground slot: the same profile or any mixture of profiles may run concurrently in separate terminals. Profile files remain shared according to the selected profile root, while process ownership, lifecycle outcome, and cleanup remain scoped to the command that launched them.
51
+
52
+ Normal root exit, terminal closure, guardian failure, and verified update shutdown all close the instance's remaining process tree within bounded graceful and forced deadlines. Closing one instance never closes another. A control supervisor may remain idle afterward, but closed instances leave no UI, Pi, extension daemon, agent worker, tool, or descendant process. Ordinary recovery never asks users to kill a PID or delete the control database.
47
53
 
48
54
  ## Recovery and comparison
49
55
 
@@ -0,0 +1,66 @@
1
+ # Launch-instance exact-artifact acceptance
2
+
3
+ Run this checklist only against a packaged candidate on a user-controlled terminal or isolated disposable Windows worker. Do not automate launching, focusing, driving, or closing terminal windows on an active workstation.
4
+
5
+ ## Artifact identity
6
+
7
+ Record before testing:
8
+
9
+ ```text
10
+ A1 release ID:
11
+ A1 package version:
12
+ Source commit:
13
+ Candidate tarball SHA-256/integrity:
14
+ Process guardian target: win32-x64
15
+ Process guardian crate version:
16
+ Process guardian protocol version:
17
+ Process guardian SHA-256:
18
+ Process guardian signature/attestation status:
19
+ Machine/Windows/terminal versions:
20
+ Tester or worker attestation:
21
+ ```
22
+
23
+ The process guardian values must match `dist/native/win32-x64/manifest.json` inside the exact installed candidate. Stop if the file is missing, mismatched, altered, or reports an unsupported capability.
24
+
25
+ ## Concurrent launch matrix
26
+
27
+ Open four independent terminal tabs and launch:
28
+
29
+ ```text
30
+ Terminal 1: a1
31
+ Terminal 2: a1
32
+ Terminal 3: a1 pi
33
+ Terminal 4: a1 sandbox
34
+ ```
35
+
36
+ Confirm all four become usable without a global foreground-lease error. Record each launch-instance ID from supervisor metadata and verify all four IDs are distinct.
37
+
38
+ ## Isolated closure
39
+
40
+ 1. Exit Terminal 3 normally. Confirm only its vanilla Pi tree exits; Terminals 1, 2, and 4 remain usable.
41
+ 2. Close the Terminal 4 tab/window without exiting Pi first. Confirm its sandbox Pi, extension daemons, agent workers, tools, and descendants disappear within the bounded deadline while both bare-A1 terminals remain usable.
42
+ 3. Exit Terminal 1 normally after causing a fixture extension to create a child and grandchild. Confirm the complete tree disappears and Terminal 2 remains usable.
43
+ 4. Force the Terminal 2 launch guardian to fail on the isolated worker. Confirm Job Object kill-on-close removes its root and descendants and leaves unrelated fixture processes alive.
44
+
45
+ Record root, child, and grandchild native identities before each close and the observed terminal outcome afterward. Do not substitute manual PID termination for a failed cleanup.
46
+
47
+ ## Stale-state recovery
48
+
49
+ Using isolated control directories, install a version-4 fixture database containing one requested or active legacy foreground lease whose former PID is dead. Run `a1 sandbox` once. Confirm that the same invocation migrates the row to historical interrupted state and launches normally without restarting the supervisor, deleting state, or discovering a PID.
50
+
51
+ Repeat with an ownership-uncertain fixture process. Confirm a new independent launch still starts and the uncertain unrelated process is not terminated.
52
+
53
+ ## Update fan-out
54
+
55
+ With at least three mixed-profile instances active, run the candidate's update-shutdown fixture. Confirm every instance receives one stop intent, each complete process tree closes, and cohort replacement occurs only after all verified terminal outcomes. A delayed or uncertain instance must prevent unsafe replacement without causing another instance to be reported idle prematurely.
56
+
57
+ ## Verdict
58
+
59
+ Record one of:
60
+
61
+ ```text
62
+ ACCEPTED — exact package and guardian digests above passed every scenario
63
+ REJECTED — include failed step, observed process identities, outcomes, and diagnostics
64
+ ```
65
+
66
+ Acceptance applies only to the exact package and process-guardian bytes recorded above. It does not certify macOS, Linux, composed terminals, detached agents, or an operating-system security sandbox.
@@ -0,0 +1,55 @@
1
+ # Terminal colour check
2
+
3
+ ## What can go wrong
4
+
5
+ Every colour the interface draws is 24-bit: the pinned dark theme's accent is
6
+ `#8abeb7`, its border `#5f87ff`, its headings `#f0c674`. On Windows, Node decides
7
+ once at startup whether it may send those sequences to the terminal at all. It
8
+ asks the console to enable virtual terminal processing; when that fails it renders
9
+ ANSI itself, and its renderer knows only the sixteen SGR colours. Each 24-bit
10
+ colour is then replaced by the terminal's nearest palette entry, so the whole
11
+ interface shifts at once — accent to the terminal's cyan, headings to its yellow —
12
+ while the application still writes exactly the bytes it always wrote.
13
+
14
+ That decision is made per process, from the console handle the process was given,
15
+ and every process it launches inherits the result. It is a property of how the
16
+ session was started, not of what is drawn.
17
+
18
+ ## Launching so colour survives
19
+
20
+ The installed command is an npm shim — a shell script that `exec`s Node — and that
21
+ shape keeps 24-bit colour. So does `pi`, for the same reason.
22
+
23
+ - Installed: `a1`, `a1 pi`
24
+ - From a checkout: `scripts/dev` and `scripts/dev pi`
25
+
26
+ `npm start` and `npm run start:pi` build first and then launch through the same
27
+ entry, but a package manager on Windows hands the script to `cmd.exe`, which can
28
+ leave the launch without a terminal on its input; the launcher says so and stops
29
+ rather than opening a session that closes as it starts. Running `scripts/dev`
30
+ from the shell is the direct path.
31
+
32
+ A directly launched `node scripts/start-local.mjs` under Git Bash does not, and
33
+ neither does a directly launched `node …/pi/dist/cli.js`: the collapse belongs to
34
+ the launch, not to either application.
35
+
36
+ ## Checking a terminal
37
+
38
+ ```
39
+ node scripts/check-terminal-colour.mjs
40
+ ```
41
+
42
+ It prints the same colour as 24-bit, as a 256-colour index, and as the terminal's
43
+ own palette entry. When the first block is indistinguishable from the third, this
44
+ launch collapses colour and any comparison made in it is a comparison of two wrong
45
+ screens.
46
+
47
+ ## What the gates hold
48
+
49
+ - `test/features/launch/terminal-colour-fidelity.test.ts` holds the emitted bytes:
50
+ the pinned palette resolves to 24-bit sequences, a 256-colour terminal stays on
51
+ indices, and the launch chain keeps inheriting the terminal it was started from
52
+ rather than opening a console of its own.
53
+ - The reader compares `a1 pi` with pinned Pi in the same terminal. Run the colour
54
+ check first: a launch that collapses colour makes both sessions equally wrong, so
55
+ they would agree with each other and disagree with what a user sees.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.1.1-dev.1",
3
+ "version": "0.1.1-dev.10",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.13.0",
7
7
  "bin": {
8
- "a1": "bin/a1.js"
8
+ "a1": "bin/cli.js"
9
9
  },
10
10
  "files": [
11
11
  "bin",
@@ -19,30 +19,35 @@
19
19
  },
20
20
  "scripts": {
21
21
  "clean": "node scripts/clean.mjs",
22
- "build": "npm run clean && tsc -p tsconfig.build.json",
23
- "typecheck": "tsc -p tsconfig.json --noEmit",
22
+ "build": "npm run clean && tsc -p tsconfig.build.json && node scripts/build-process-guardian.mjs",
23
+ "build:process-guardian": "node scripts/build-process-guardian.mjs",
24
+ "typecheck": "tsgo -p tsconfig.json --noEmit",
24
25
  "check:architecture": "node scripts/check-architecture.mjs && node scripts/product-identifier-policy.mjs --check && node scripts/check-product-identity-boundaries.mjs && node scripts/check-package-identity.mjs && node scripts/check-pinned-pi-source-ledger.mjs && node scripts/check-terminal-host-provenance.mjs",
25
26
  "check:customization-ready": "node scripts/check-owned-ui-customization-prerequisites.mjs",
26
27
  "check:deprecated": "node scripts/check-deprecated-dependencies.mjs",
27
- "check": "npm run typecheck && npm run check:architecture && npm run check:customization-ready && npm run check:deprecated && npm test && npm run test:release",
28
- "validate:agent": "npm run check",
28
+ "branches:prune": "node scripts/prune-merged-branches.mjs",
29
+ "check": "npm run test:fast",
29
30
  "publish:next": "tsx scripts/publish-next.ts",
30
- "test": "vitest run",
31
- "test:unit": "vitest run test/cli test/features test/foundation test/repository-governance --exclude '**/*.integration.test.ts'",
32
- "test:integration": "vitest run test/foundation/release/update-transition.integration.test.ts test/features/launch/profile-isolation.integration.test.ts",
31
+ "release:next": "node scripts/release-next.mjs",
32
+ "test": "npm run test:fast",
33
+ "test:fast": "node scripts/run-validation-tier.mjs typecheck fast",
34
+ "test:scope": "node scripts/run-validation-tier.mjs",
35
+ "test:full": "node scripts/run-validation-tier.mjs full-release",
33
36
  "test:release": "node scripts/run-release-gates.mjs",
34
37
  "test:terminal-host": "node scripts/run-terminal-host-probe.mjs",
35
- "test:pi-terminal-parity": "npm run build --silent && node scripts/run-pi-terminal-parity.mjs",
36
38
  "test:pi-engine-conformance": "vitest run test/foundation/pi-engine-adapter/conformance.test.ts test/foundation/pi-engine-adapter/runtime-integration.test.ts test/foundation/pi-engine-adapter/session-integration.test.ts",
37
39
  "report:pi-engine-conformance": "npm run build --silent && node scripts/run-pi-engine-conformance.mjs",
38
- "sync:pi-ui": "npm run update:pi-component-parity && npm run update:pi-event-frame-parity",
40
+ "sync:pi-ui": "npm run update:pi-component-parity && npm run update:pi-event-frame-parity && npm run update:pi-settings-metadata",
39
41
  "update:pi-component-parity": "tsx scripts/update-pi-component-parity.ts",
40
42
  "update:pi-event-frame-parity": "tsx scripts/update-pi-event-frame-parity.ts",
41
43
  "proof:terminal-host": "node scripts/run-terminal-host.mjs",
42
- "start": "npm run build && node scripts/start-local.mjs",
44
+ "start": "npm run build && node scripts/dev-launch.mjs",
45
+ "start:pi": "npm run build && node scripts/dev-launch.mjs pi",
46
+ "start:sandbox": "npm run build && node scripts/dev-launch.mjs sandbox",
43
47
  "prepack": "node scripts/prepack-gate.mjs",
44
- "prepublishOnly": "npm run check",
45
- "prepare": "npm run build"
48
+ "prepublishOnly": "npm run test:release",
49
+ "prepare": "npm run build",
50
+ "update:pi-settings-metadata": "node scripts/update-pi-settings-metadata.mjs"
46
51
  },
47
52
  "dependencies": {
48
53
  "@earendil-works/pi-coding-agent": "0.84.2",
@@ -55,6 +60,7 @@
55
60
  "@types/cross-spawn": "6.0.6",
56
61
  "@types/node": "24.2.1",
57
62
  "@types/semver": "7.8.0",
63
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
58
64
  "@xterm/headless": "6.0.0",
59
65
  "node-pty": "1.1.0",
60
66
  "tsx": "4.20.3",
@@ -1,6 +0,0 @@
1
- import type { LaunchProfileId } from "../foundation/lifecycle/index.js";
2
- export declare function selectTransparentChild(environment?: NodeJS.ProcessEnv, executable?: string, compositionUrl?: string): {
3
- readonly executable: string;
4
- readonly arguments: readonly string[];
5
- };
6
- export declare function runSelectedTransparentRuntime(profileId: Exclude<LaunchProfileId, "a1">, environment?: NodeJS.ProcessEnv): Promise<number>;
@@ -1,19 +0,0 @@
1
- import { fileURLToPath } from "node:url";
2
- import { runTransparentForeground } from "../foundation/transparent-terminal/index.js";
3
- export function selectTransparentChild(environment = process.env, executable = process.execPath, compositionUrl = import.meta.url) {
4
- const entry = fileURLToPath(new URL("../foundation/pi-engine-adapter/public-main-entry.js", compositionUrl));
5
- return { executable, arguments: [entry, ...parseSelectedArguments(environment.A1_LAUNCH_ARGUMENTS_JSON)] };
6
- }
7
- export async function runSelectedTransparentRuntime(profileId, environment = process.env) {
8
- const child = selectTransparentChild(environment);
9
- return await runTransparentForeground({ profileId, environment, ...child });
10
- }
11
- function parseSelectedArguments(source) {
12
- if (!source)
13
- return [];
14
- const value = JSON.parse(source);
15
- if (!Array.isArray(value) || value.some(item => typeof item !== "string")) {
16
- throw new TypeError("A1_LAUNCH_ARGUMENTS_JSON must be a JSON array of strings");
17
- }
18
- return value;
19
- }
@@ -1,13 +0,0 @@
1
- export interface ResolvedTransparentCommand {
2
- readonly executable: string;
3
- readonly arguments: readonly string[];
4
- readonly source: "exact" | "path" | "npm-windows-shim";
5
- }
6
- export interface TransparentCommandResolutionOptions {
7
- readonly platform?: NodeJS.Platform;
8
- readonly cwd: string;
9
- readonly environment: Readonly<Record<string, string>>;
10
- readonly nodeExecutable?: string;
11
- }
12
- /** Resolve a command without invoking a command interpreter. */
13
- export declare function resolveTransparentCommand(executable: string, arguments_: readonly string[], options: TransparentCommandResolutionOptions): Promise<ResolvedTransparentCommand>;
@@ -1,69 +0,0 @@
1
- import { access, readFile } from "node:fs/promises";
2
- import { delimiter, extname, isAbsolute, resolve } from "node:path";
3
- /** Resolve a command without invoking a command interpreter. */
4
- export async function resolveTransparentCommand(executable, arguments_, options) {
5
- const platform = options.platform ?? process.platform;
6
- return platform === "win32"
7
- ? await resolveWindowsCommand(executable, arguments_, options)
8
- : { executable, arguments: [...arguments_], source: "exact" };
9
- }
10
- async function resolveWindowsCommand(command, commandArguments, options) {
11
- const resolved = await resolveWindowsPathCommand(command, options.cwd, options.environment);
12
- const selected = resolved ?? throwCommandNotFound(command);
13
- if (extname(selected).toLowerCase() !== ".cmd") {
14
- return { executable: selected, arguments: [...commandArguments], source: "path" };
15
- }
16
- return await unwrapNpmWindowsShim(selected, commandArguments, options.nodeExecutable ?? process.execPath);
17
- }
18
- async function resolveWindowsPathCommand(executable, cwd, environment) {
19
- const hasPath = isAbsolute(executable) || executable.includes("/") || executable.includes("\\");
20
- const roots = hasPath ? [cwd] : windowsPath(environment).split(delimiter).filter(Boolean);
21
- const extension = extname(executable);
22
- const extensions = extension ? [""] : windowsExecutableExtensions(environment);
23
- for (const root of roots) {
24
- const base = hasPath ? resolve(cwd, executable) : resolve(root.replace(/^"|"$/g, ""), executable);
25
- for (const suffix of extensions) {
26
- const candidate = `${base}${suffix}`;
27
- if (await isFile(candidate))
28
- return candidate;
29
- }
30
- }
31
- return null;
32
- }
33
- async function unwrapNpmWindowsShim(shimPath, arguments_, fallbackNodeExecutable) {
34
- const source = (await readFile(shimPath, "utf8")).replace(/^\uFEFF/, "");
35
- const targetMatch = source.match(/"%_prog%"\s+"%dp0%\\([^"\r\n]+)"\s+%\*/i);
36
- if (!targetMatch?.[1]) {
37
- throw Object.assign(new Error(`transparent launch refuses arbitrary Windows command script ${shimPath}; configure an executable binary instead`), { code: "UNSUPPORTED_WINDOWS_COMMAND_SHIM" });
38
- }
39
- const shimRoot = resolve(shimPath, "..");
40
- const target = resolve(shimRoot, targetMatch[1].replaceAll("\\", "/"));
41
- if (!await isFile(target))
42
- throw Object.assign(new Error(`npm command shim target is missing: ${target}`), { code: "ENOENT" });
43
- const localNode = resolve(shimRoot, "node.exe");
44
- const nodeExecutable = await isFile(localNode) ? localNode : fallbackNodeExecutable;
45
- return {
46
- executable: nodeExecutable,
47
- arguments: [target, ...arguments_],
48
- source: "npm-windows-shim",
49
- };
50
- }
51
- function throwCommandNotFound(command) {
52
- throw Object.assign(new Error(`spawn ${command} ENOENT`), { code: "ENOENT" });
53
- }
54
- function windowsPath(environment) {
55
- return environment.Path ?? environment.PATH ?? "";
56
- }
57
- function windowsExecutableExtensions(environment) {
58
- const source = environment.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
59
- return source.split(";").filter(Boolean).map(value => value.startsWith(".") ? value : `.${value}`);
60
- }
61
- async function isFile(path) {
62
- try {
63
- await access(path);
64
- return true;
65
- }
66
- catch {
67
- return false;
68
- }
69
- }
@@ -1,32 +0,0 @@
1
- import type { CommandResult, ForegroundTerminalLeaseId, GenerationId, NativeProcessIdentity, RequestId, SupervisorCommand, TransparentTerminalLaunchProfile, TransparentTerminalLifecycleOutcome } from "../lifecycle/index.js";
2
- export interface ForegroundLeaseControl {
3
- command(command: SupervisorCommand): Promise<CommandResult>;
4
- }
5
- export type TransparentStopReason = "owner-disconnect" | "user-request" | "update";
6
- export interface TransparentChildHandle {
7
- readonly processIdentity: NativeProcessIdentity;
8
- readonly outcome: Promise<TransparentTerminalLifecycleOutcome>;
9
- stop(reason: TransparentStopReason): Promise<TransparentTerminalLifecycleOutcome>;
10
- }
11
- export interface TransparentNativeLauncher {
12
- launch(profile: TransparentTerminalLaunchProfile): Promise<TransparentChildHandle>;
13
- }
14
- export interface ForegroundBrokerRequest {
15
- readonly leaseId: ForegroundTerminalLeaseId;
16
- readonly generationId: GenerationId;
17
- readonly ownerId: string;
18
- readonly profile: TransparentTerminalLaunchProfile;
19
- readonly stopRequested?: Promise<TransparentStopReason>;
20
- }
21
- export interface ForegroundBrokerResult {
22
- readonly leaseId: ForegroundTerminalLeaseId;
23
- readonly generationId: GenerationId;
24
- readonly processIdentity: NativeProcessIdentity | null;
25
- readonly outcome: TransparentTerminalLifecycleOutcome;
26
- }
27
- /**
28
- * Coordinates ownership around one natively attached child. Ordinary terminal
29
- * data never enters this object: the launcher owns native attachment and this
30
- * broker observes only process identity and the final lifecycle outcome.
31
- */
32
- export declare function runForegroundBroker(request: ForegroundBrokerRequest, control: ForegroundLeaseControl, launcher: TransparentNativeLauncher, createRequestId: () => RequestId): Promise<ForegroundBrokerResult>;
@@ -1,87 +0,0 @@
1
- import { assertNativeProcessIdentity, assertTransparentTerminalLaunchProfile } from "../lifecycle/index.js";
2
- /**
3
- * Coordinates ownership around one natively attached child. Ordinary terminal
4
- * data never enters this object: the launcher owns native attachment and this
5
- * broker observes only process identity and the final lifecycle outcome.
6
- */
7
- export async function runForegroundBroker(request, control, launcher, createRequestId) {
8
- assertRequest(request);
9
- await requireAccepted(control, {
10
- type: "acquire-foreground-terminal-lease",
11
- requestId: createRequestId(),
12
- leaseId: request.leaseId,
13
- ownerId: request.ownerId,
14
- profile: request.profile,
15
- });
16
- let handle;
17
- try {
18
- handle = await launcher.launch(request.profile);
19
- assertNativeProcessIdentity(handle.processIdentity);
20
- }
21
- catch (error) {
22
- const outcome = {
23
- kind: "spawn-error",
24
- message: errorMessage(error),
25
- code: errorCode(error),
26
- };
27
- await requireAccepted(control, {
28
- type: "release-foreground-terminal-lease",
29
- requestId: createRequestId(),
30
- leaseId: request.leaseId,
31
- processIdentity: null,
32
- outcome,
33
- });
34
- return { leaseId: request.leaseId, generationId: request.generationId, processIdentity: null, outcome };
35
- }
36
- try {
37
- await requireAccepted(control, {
38
- type: "activate-foreground-terminal-lease",
39
- requestId: createRequestId(),
40
- leaseId: request.leaseId,
41
- generationId: request.generationId,
42
- processIdentity: handle.processIdentity,
43
- });
44
- const outcome = request.stopRequested
45
- ? await Promise.race([handle.outcome, request.stopRequested.then(async (reason) => await handle.stop(reason))])
46
- : await handle.outcome;
47
- await requireAccepted(control, {
48
- type: "release-foreground-terminal-lease",
49
- requestId: createRequestId(),
50
- leaseId: request.leaseId,
51
- processIdentity: handle.processIdentity,
52
- outcome,
53
- });
54
- return { leaseId: request.leaseId, generationId: request.generationId, processIdentity: handle.processIdentity, outcome };
55
- }
56
- catch (error) {
57
- const outcome = {
58
- kind: "broker-error",
59
- message: errorMessage(error),
60
- code: errorCode(error),
61
- };
62
- await control.command({
63
- type: "release-foreground-terminal-lease",
64
- requestId: createRequestId(),
65
- leaseId: request.leaseId,
66
- processIdentity: handle.processIdentity,
67
- outcome,
68
- }).catch(() => undefined);
69
- throw error;
70
- }
71
- }
72
- function assertRequest(request) {
73
- if (!request.leaseId || !request.generationId || !request.ownerId)
74
- throw new TypeError("foreground broker identities are required");
75
- assertTransparentTerminalLaunchProfile(request.profile);
76
- }
77
- async function requireAccepted(control, command) {
78
- const result = await control.command(command);
79
- if (!result.ok)
80
- throw new Error(result.error?.message ?? `supervisor rejected ${command.type}`);
81
- }
82
- function errorMessage(error) {
83
- return error instanceof Error ? error.message : String(error);
84
- }
85
- function errorCode(error) {
86
- return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : null;
87
- }
@@ -1,4 +0,0 @@
1
- export * from "./command-resolution.js";
2
- export * from "./foreground-broker.js";
3
- export * from "./main.js";
4
- export * from "./native-launcher.js";
@@ -1,4 +0,0 @@
1
- export * from "./command-resolution.js";
2
- export * from "./foreground-broker.js";
3
- export * from "./main.js";
4
- export * from "./native-launcher.js";
@@ -1,9 +0,0 @@
1
- import { type LaunchProfileId } from "../lifecycle/index.js";
2
- export interface TransparentForegroundOptions {
3
- readonly environment?: NodeJS.ProcessEnv;
4
- readonly profileId?: LaunchProfileId;
5
- readonly cwd?: string;
6
- readonly executable: string;
7
- readonly arguments: readonly string[];
8
- }
9
- export declare function runTransparentForeground(options: TransparentForegroundOptions): Promise<number>;
@@ -1,94 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { realpath } from "node:fs/promises";
3
- import { resolve } from "node:path";
4
- import { assertLaunchProfileId } from "../lifecycle/index.js";
5
- import { SupervisorClient } from "../protocol/index.js";
6
- import { resolveProductPaths } from "../supervision/index.js";
7
- import { runForegroundBroker } from "./foreground-broker.js";
8
- import { createPlatformTransparentLauncher } from "./native-launcher.js";
9
- export async function runTransparentForeground(options) {
10
- const environment = { ...(options.environment ?? process.env) };
11
- const profileId = options.profileId ?? environment.A1_LAUNCH_PROFILE ?? "a1";
12
- assertLaunchProfileId(profileId);
13
- const paths = resolveProductPaths(environment);
14
- const client = new SupervisorClient(environment.A1_RELEASE_ID);
15
- await client.connect(paths.endpoint);
16
- const ownerId = `foreground-broker-${randomUUID()}`;
17
- const profile = await launchProfile(environment, options, profileId);
18
- const stop = stopSignal();
19
- try {
20
- const result = await runForegroundBroker({ leaseId: randomUUID(), generationId: randomUUID(), ownerId, profile, stopRequested: stop.requested }, client, createPlatformTransparentLauncher(), randomUUID);
21
- if (result.outcome.kind === "exited")
22
- return result.outcome.exitCode;
23
- if (result.outcome.kind === "signaled")
24
- return 1;
25
- if (result.outcome.kind === "stopped" || result.outcome.kind === "detached")
26
- return 0;
27
- throw Object.assign(new Error(result.outcome.message), { code: result.outcome.code ?? undefined });
28
- }
29
- finally {
30
- stop.dispose();
31
- client.close();
32
- }
33
- }
34
- async function launchProfile(environment, options, profileId) {
35
- const executable = options.executable;
36
- const arguments_ = options.arguments;
37
- const cwd = await realpath(options.cwd ?? process.cwd());
38
- const terminalType = environment.TERM?.trim() || "xterm-256color";
39
- return {
40
- id: `${profileId}-transparent-${randomUUID()}`,
41
- terminalCapability: "transparent",
42
- executable,
43
- arguments: arguments_,
44
- cwd,
45
- environment: selectedChildEnvironment(environment),
46
- terminalType,
47
- dimensions: terminalDimensions(environment),
48
- ownerDisconnect: "stop",
49
- recovery: "none",
50
- surface: "none",
51
- visualReconnection: "none",
52
- };
53
- }
54
- function selectedChildEnvironment(environment) {
55
- const selected = {};
56
- for (const [name, value] of Object.entries(environment)) {
57
- if (value !== undefined && !name.startsWith("A1_"))
58
- selected[name] = value;
59
- }
60
- return selected;
61
- }
62
- function terminalDimensions(environment) {
63
- const columns = positiveInteger(environment.COLUMNS) ?? 80;
64
- const rows = positiveInteger(environment.LINES) ?? 24;
65
- return { columns: Math.min(500, Math.max(2, columns)), rows: Math.min(300, Math.max(1, rows)) };
66
- }
67
- function positiveInteger(value) {
68
- const parsed = Number(value);
69
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
70
- }
71
- function stopSignal() {
72
- let resolveStop;
73
- let settled = false;
74
- const requested = new Promise(resolve => { resolveStop = resolve; });
75
- const request = (reason) => {
76
- if (settled)
77
- return;
78
- settled = true;
79
- resolveStop(reason);
80
- };
81
- const onSigterm = () => request("user-request");
82
- // The attached child shares foreground signal delivery. Keep the broker alive
83
- // without translating or forwarding Ctrl+C.
84
- const onSigint = () => undefined;
85
- process.once("SIGTERM", onSigterm);
86
- process.on("SIGINT", onSigint);
87
- return {
88
- requested,
89
- dispose() {
90
- process.off("SIGTERM", onSigterm);
91
- process.off("SIGINT", onSigint);
92
- },
93
- };
94
- }