gentle-pi 4.1.0 → 4.3.0
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.
- package/README.md +352 -128
- package/assets/agents/gentle-ai-explore.md +1 -0
- package/assets/agents/gentle-ai-verify.md +6 -4
- package/assets/agents/gentle-ai-worker.md +7 -1
- package/assets/orchestrator-delegation-reference.md +163 -0
- package/assets/orchestrator-delegation.md +25 -176
- package/assets/orchestrator-tracking.md +1 -1
- package/assets/orchestrator-verification.md +14 -8
- package/assets/orchestrator-writer.md +5 -1
- package/assets/orchestrator.md +9 -9
- package/bin/engram-guard +1 -1
- package/bin/gentle-shell.mjs +188 -35
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/README.md +7 -0
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/manifest.json +11 -2
- package/contracts/review-provider-contract-mirror/v1.4.0/bundle/routing/pi.md +92 -0
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-capabilities.baseline.json +8 -1
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/assets/diagrams/agent-orchestration.svg +54 -15
- package/docs/assets/diagrams/gentleman-workflow.svg +69 -11
- package/docs/assets/diagrams/measured-routing.svg +49 -0
- package/docs/assets/diagrams/native-review.svg +52 -12
- package/docs/assets/diagrams/odd-workflow.svg +128 -66
- package/docs/assets/diagrams/permission-modes.svg +51 -0
- package/docs/assets/features/audio-notifications.png +0 -0
- package/docs/assets/features/command-palette.png +0 -0
- package/docs/auto-permission-answers.md +25 -0
- package/docs/auto-permission-evidence.md +39 -0
- package/docs/bundled-install.md +40 -8
- package/docs/delegated-verification.md +10 -8
- package/docs/gentle-agents-activity.md +11 -5
- package/docs/gentle-shell-uninstall.md +8 -1
- package/docs/gentle-shell.md +20 -12
- package/docs/install-wizard.md +17 -10
- package/docs/permission-modes.md +51 -6
- package/docs/readme-reference.md +155 -31
- package/docs/session-profile-format.md +39 -0
- package/docs/yolo-mode.md +21 -7
- package/extensions/ask-user-choice.ts +4 -0
- package/extensions/ask-user-question.ts +6 -1
- package/extensions/codegraph-tools.ts +68 -6
- package/extensions/gentle-agents.ts +146 -28
- package/extensions/gentle-ai.ts +380 -68
- package/extensions/gentle-herdr-activity.ts +2 -3
- package/extensions/gentle-shell.ts +53 -43
- package/extensions/gentle-todo.ts +62 -18
- package/extensions/quiet-tools.ts +1 -1
- package/extensions/session-scratch.ts +365 -0
- package/extensions/startup-banner.ts +2 -3
- package/lib/agent-home.ts +8 -3
- package/lib/agents-protocol.ts +3 -0
- package/lib/agents-runner.ts +55 -6
- package/lib/agents-view.ts +3 -1
- package/lib/auto-permission-answers.ts +133 -0
- package/lib/auto-permission-classifier.ts +215 -36
- package/lib/auto-permission-evidence.ts +88 -0
- package/lib/auto-permission-memory.ts +69 -0
- package/lib/auto-permission-runtime.ts +251 -35
- package/lib/auto-permission-tool-context.ts +61 -0
- package/lib/bounded-writer-admission.ts +51 -0
- package/lib/command-palette-catalog.ts +0 -4
- package/lib/commit-slice-signal.ts +100 -0
- package/lib/context-backstop.ts +84 -0
- package/lib/delivered-test-lint.ts +80 -0
- package/lib/destructive-command-guard.ts +215 -28
- package/lib/gauge-theme.ts +146 -0
- package/lib/gentle-ai-binary.ts +28 -7
- package/lib/gentle-shell-bundled.ts +62 -8
- package/lib/gentle-shell-import-pi.ts +233 -0
- package/lib/gentle-shell-launcher.ts +146 -12
- package/lib/gentle-shell-uninstall.ts +18 -1
- package/lib/herdr-activity.ts +24 -0
- package/lib/model-routing-authority.ts +6 -1
- package/lib/native-review-cli.ts +9 -1
- package/lib/orchestrator-discovery.ts +5 -1
- package/lib/orchestrator-presence.ts +9 -0
- package/lib/orchestrator-scope.ts +21 -3
- package/lib/provider-contract-bundle.ts +99 -53
- package/lib/review-candidate-view-owner.ts +29 -19
- package/lib/review-candidate-view.ts +36 -2
- package/lib/review-risk-assessment.ts +43 -38
- package/lib/review-session-standing-permission.ts +3 -1
- package/lib/selection-engine.ts +4 -1
- package/lib/session-profile-freeze.ts +40 -16
- package/lib/session-profile-persistence.ts +187 -0
- package/lib/session-scratch.ts +474 -0
- package/lib/shell-bar.ts +25 -25
- package/lib/shell-changes-view.ts +80 -13
- package/lib/shell-changes.ts +2 -0
- package/lib/shell-gauge.ts +49 -6
- package/lib/shell-prompt.ts +6 -4
- package/lib/shell-todo.ts +39 -8
- package/lib/shell-usage-view.ts +5 -1
- package/lib/shell-usage.ts +7 -7
- package/lib/verifier-admission.ts +29 -0
- package/lib/vim-editor-adapter.ts +22 -4
- package/lib/yolo-temp-scratch.ts +69 -0
- package/package.json +5 -6
- package/runtime/gentle-ai-binary.mjs +28 -7
- package/runtime/gentle-shell-bundled.mjs +61 -7
- package/runtime/gentle-shell-import-pi.mjs +234 -0
- package/runtime/gentle-shell-launcher.mjs +144 -10
- package/runtime/gentle-shell-uninstall.mjs +18 -1
- package/runtime/native-review-cli.mjs +8 -0
- package/runtime/review-risk-assessment.mjs +43 -38
- package/scripts/build-runtime-modules.mjs +1 -0
- package/scripts/bundled-engram-migration.mjs +125 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/install-wizard-preview.mjs +2 -2
- package/scripts/installer-downloads.mjs +7 -7
- package/scripts/installer-preflight.mjs +1 -1
- package/scripts/installer-server.mjs +14 -4
- package/scripts/main-channel.mjs +3 -2
- package/scripts/pi-update.mjs +223 -0
- package/scripts/run-test-suite.mjs +5 -0
- package/scripts/verify-package-files.mjs +19 -13
- package/skills/gentle-ai/SKILL.md +3 -3
- package/skills/work-unit-commits/SKILL.md +5 -3
- package/tests/agent-risk-escalation.test.ts +37 -10
- package/tests/agents-runner.test.ts +260 -3
- package/tests/append-system-prompt-route.test.ts +18 -1
- package/tests/auto-mode-human-answers.test.ts +68 -0
- package/tests/auto-mode-invariants.test.ts +23 -5
- package/tests/auto-mode-sdk.test.ts +29 -2
- package/tests/auto-permission-answers.test.ts +170 -0
- package/tests/auto-permission-classifier.test.ts +353 -2
- package/tests/auto-permission-compound.test.ts +24 -0
- package/tests/auto-permission-evidence.test.ts +88 -0
- package/tests/auto-permission-memory.test.ts +46 -0
- package/tests/auto-permission-public-regressions.test.ts +111 -0
- package/tests/auto-permission-runtime.test.ts +270 -12
- package/tests/autonomous-guard.test.ts +208 -16
- package/tests/bounded-writer-admission.test.ts +11 -3
- package/tests/bundled-install.test.ts +3 -3
- package/tests/card-content-policy.test.ts +19 -9
- package/tests/child-safety.test.ts +39 -0
- package/tests/codegraph-tools.test.ts +123 -2
- package/tests/command-palette.test.ts +14 -4
- package/tests/commit-slice-signal.test.ts +162 -0
- package/tests/context-backstop.test.ts +159 -0
- package/tests/delegate-for-reason-contract.test.ts +91 -24
- package/tests/delivered-test-lint.test.ts +105 -0
- package/tests/destructive-command-guard.test.ts +85 -1
- package/tests/engram-safety.test.ts +35 -7
- package/tests/fixtures/primary-session-prompt/gentleman.golden.md +143 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/README.md +29 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/manifest.json +83 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/orchestration/pi.md +70 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/routing/pi.md +92 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/targeted-validator.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/targeted-validator.json +1 -0
- package/tests/gauge-theme.test.ts +120 -0
- package/tests/gentle-agents.test.ts +443 -8
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-child-guards.test.ts +61 -0
- package/tests/gentle-ai-doctor-herdr.test.ts +94 -0
- package/tests/gentle-ai-installer.test.ts +51 -51
- package/tests/gentle-shell-bin.test.ts +180 -17
- package/tests/gentle-shell-bundled.test.ts +235 -38
- package/tests/gentle-shell-import-pi.test.ts +262 -0
- package/tests/gentle-shell-launcher.test.ts +97 -13
- package/tests/gentle-shell-uninstall.test.ts +30 -0
- package/tests/gentle-shell.test.ts +113 -14
- package/tests/gentle-todo.test.ts +258 -27
- package/tests/headless-delivery-question.test.ts +58 -0
- package/tests/herdr-activity.test.ts +34 -1
- package/tests/install-wizard.test.ts +2 -2
- package/tests/installer-posix-bootstrap.test.ts +8 -8
- package/tests/installer-preflight.test.ts +3 -3
- package/tests/installer-probes.test.ts +8 -8
- package/tests/installer-runner.test.ts +14 -13
- package/tests/installer-server.test.ts +13 -5
- package/tests/installer-windows-bootstrap.test.ts +33 -4
- package/tests/main-channel-upgrade.test.ts +7 -1
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-parity.test.ts +22 -1
- package/tests/no-self-pause-contract.test.ts +5 -2
- package/tests/odd-integration.test.ts +5 -1
- package/tests/odd-routing-canonical-ratchet.test.ts +127 -162
- package/tests/odd-routing-contract.test.ts +115 -30
- package/tests/odd-runtime-delegation-gate.test.ts +4 -1
- package/tests/orchestrator-budget.test.ts +19 -8
- package/tests/orchestrator-discovery.test.ts +29 -0
- package/tests/orchestrator-rdd-ownership.test.ts +3 -1
- package/tests/orchestrator-scope.test.ts +66 -0
- package/tests/orchestrator-status-publication-contract.test.ts +12 -8
- package/tests/package-manifest.test.ts +28 -24
- package/tests/pi-update-launcher.test.ts +106 -0
- package/tests/pi-update.test.ts +186 -0
- package/tests/primary-session-prompt.test.ts +101 -0
- package/tests/provider-contract-bundle.test.ts +118 -0
- package/tests/provider-contract-mirror.test.ts +24 -0
- package/tests/quiet-tool-rendering.test.ts +23 -0
- package/tests/rdd-aware-verification-contract.test.ts +29 -11
- package/tests/rdd-status-line.test.ts +131 -0
- package/tests/review-agent-end-preflight.test.ts +6 -0
- package/tests/review-candidate-view.test.ts +49 -0
- package/tests/review-contract-prompt.test.ts +5 -2
- package/tests/review-controller-native-routing.test.ts +14 -1
- package/tests/review-controller.test.ts +1 -1
- package/tests/review-host-relay-routing.test.ts +59 -2
- package/tests/review-loadout-rdd-off-sdk.test.ts +237 -0
- package/tests/review-owner-identity.test.ts +132 -0
- package/tests/review-risk-assessment.test.ts +163 -76
- package/tests/review-session-standing-permission-controller.test.ts +14 -1
- package/tests/review-session-standing-permission.test.ts +27 -1
- package/tests/run-test-suite.test.ts +15 -0
- package/tests/runtime-harness.mjs +16 -6
- package/tests/selection-engine.test.ts +35 -5
- package/tests/session-changes-shell.test.ts +1 -1
- package/tests/session-profile-freeze.test.ts +37 -1
- package/tests/session-profile-persistence.test.ts +334 -0
- package/tests/session-scratch.test.ts +945 -0
- package/tests/session-subject-routing-contract.test.ts +2 -1
- package/tests/shell-bar.test.ts +7 -0
- package/tests/shell-changes-view.test.ts +137 -1
- package/tests/shell-changes.test.ts +18 -0
- package/tests/shell-gauge.test.ts +42 -0
- package/tests/shell-todo.test.ts +15 -12
- package/tests/small-task-inline-contract.test.ts +37 -0
- package/tests/standalone-config-home.test.ts +324 -0
- package/tests/startup-banner.test.ts +5 -4
- package/tests/startup-home-repo.test.ts +34 -0
- package/tests/support/orchestrator-modules.ts +1 -0
- package/tests/task-size-routing-contract.test.ts +69 -9
- package/tests/test-scratch-contract.test.ts +102 -0
- package/tests/test-strength-contract.test.ts +36 -1
- package/tests/tool-batching-contract.test.ts +64 -0
- package/tests/verification-proportionality.test.ts +46 -0
- package/tests/verifier-admission.test.ts +137 -0
- package/tests/verify-agent-contract.test.ts +8 -8
- package/tests/vim-editor-adapter.test.ts +41 -15
- package/tests/windows-hidden-processes.test.ts +5 -0
- package/tests/writer-edit-surface-scope.test.ts +3 -0
- package/tests/writer-feature-document-admission.test.ts +156 -0
- package/tests/yolo-mode.test.ts +24 -1
- package/tests/yolo-temp-scratch.test.ts +71 -0
- package/scripts/mirror-odd-routing.mjs +0 -242
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/orchestration/pi.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/targeted-validator.json +0 -0
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Parent Pi session only. Load it when the Writer rule fires or an explicitly activated Judgment Day fix batch is dispatched; small tasks never need it. The Writer rule fires on a named reason (parallel units, the context backstop), never on size alone or a price ratio: a large task without a reason stays inline, following its logbook. Configured per-agent models (`subagents.json` `model_profiles`) still apply to every writer it launches. Each writer prompt requires the self-review of the Parallel review protocol in `orchestrator-verification.md`. A reason holds only when the launch delivers it:
|
|
4
4
|
|
|
5
|
-
- **
|
|
5
|
+
- **Invariants**: each writer prompt names the existing helpers its unit must reuse for shared state (for example the function that adds an entry other commands also add) and the invariants they keep; units built in parallel cannot see each other, so this is the only place they learn them.
|
|
6
|
+
- **Size threshold**: a subagent costs one to two minutes of reading and setup before it writes anything; delegate a unit only when it needs several minutes of work (several files or roughly 100+ changed lines). Several small independent changes are faster inline, one after another.
|
|
7
|
+
- **Parallelism**: a unit is one independent piece of behavior with its own code, tests, and docs; never split one behavior into a test writer, a docs writer, and inline code, and never delegate a unit that depends on another unit still in progress. With the Background subagent policy on, launch every independent unit in the same turn via `subagent_run` with `mode: "background"`, then wait for all completions before the Seam check; serial launches void the reason: if units cannot launch together, work inline.
|
|
6
8
|
|
|
7
9
|
#### Allowed edit surfaces (MANDATORY)
|
|
8
10
|
|
|
@@ -15,6 +17,8 @@ Before launching a bounded writer (`gentle-ai-worker`, a user-configured `worker
|
|
|
15
17
|
- pre-existing untracked targets the writer may write, listed explicitly;
|
|
16
18
|
- nothing beyond the delegated task — a surface wider than the task is the same defect as no surface at all.
|
|
17
19
|
|
|
20
|
+
Test scratch stays outside the worktree (`mktemp -d`), never in the repository (nor caches or `TMPDIR`); state needed inside uses an excluded path the test removes. The runtime owns session scratch; `/gentle:scratch` lists or cleans recorded clones and worktrees.
|
|
21
|
+
|
|
18
22
|
If the surface genuinely cannot be derived, do not launch the writer or ask the human to author paths. Derive a candidate set first — the exact paths this task would touch — and present that enumerated list as an approve/decline choice under the Lossless Blocking Prompts rules in `orchestrator-prompts.md`. A free-text question asking which paths or globs to authorize is never a valid escalation: computing it is the parent's job.
|
|
19
23
|
|
|
20
24
|
Relay a writer's `interaction_required` payload about edit surfaces the same way: present its derived candidate paths as the choice, and add or drop paths only on the human's explicit instruction.
|
package/assets/orchestrator.md
CHANGED
|
@@ -14,6 +14,8 @@ You are a COORDINATOR, not the default executor for substantial work. Maintain o
|
|
|
14
14
|
|
|
15
15
|
Keep synthesis short by default: decision, outcome, next action. Expand only when the user asks or the situation requires detail.
|
|
16
16
|
|
|
17
|
+
Batch independent tool calls in one turn (parallel calls or one multi-file command). Never spend a turn only on a `todo` or status update.
|
|
18
|
+
|
|
17
19
|
## Language Boundary
|
|
18
20
|
|
|
19
21
|
Reply-language style and the active persona's Spanish variant are defined once in the identity/harness section above (its `Current persona mode:` line). The rules below are delegation/artifact-scoped and not restated there:
|
|
@@ -32,7 +34,7 @@ A task is **small** when all three hold:
|
|
|
32
34
|
2. **Contained risk** — no high-risk item below.
|
|
33
35
|
3. **Resumable from the diff** — resume test: if the session stopped now, someone could finish from the original request and `git diff` alone.
|
|
34
36
|
|
|
35
|
-
The number of files, commands or tests, fixes, or a requested `todo` list never decides size. A task is **large** only when the resume test fails (several sessions, external waits,
|
|
37
|
+
The number of files, commands or tests, fixes, or a requested `todo` list never decides size. A task is **large** only when the resume test fails (several sessions, external waits, deliverables the user wants apart (never several features asked at once), requirements compaction could lose); large tasks get ODD tracking and workers only by the Writer rule, else inline.
|
|
36
38
|
|
|
37
39
|
Small path: inline (parent; `bash_background` policy); observe RED inline before the fix; run the focused test and the suite inline, once each. No explore, worker, or verifier; no feature document, mirror, or commits unless the user asks; `todo` optional. It needs no lazy asset.
|
|
38
40
|
|
|
@@ -40,8 +42,6 @@ Small path: inline (parent; `bash_background` policy); observe RED inline before
|
|
|
40
42
|
|
|
41
43
|
**Risk line**: close every code change, small path or delegated, with `Risk: item N (reason)` or `Risk: none` per the list; any item → Verification rule.
|
|
42
44
|
|
|
43
|
-
ODD (Default Workflow, harness section above) is mandatory on every request, with the harness's applicable test-first policy (RED, GREEN, refactor); detail: `orchestrator-delegation.md`, `orchestrator-memory.md`.
|
|
44
|
-
|
|
45
45
|
## Delegation Rules
|
|
46
46
|
|
|
47
47
|
Core question: does this inflate parent context without need?
|
|
@@ -50,19 +50,19 @@ Before launching bounded writer (`gentle-ai-worker` or `worker`), derive nonempt
|
|
|
50
50
|
|
|
51
51
|
## Mechanisms
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
Delegation Triggers — each mechanism turns on only when its own trigger condition truly holds, and only then is it mandatory; no trigger holds by default, and when in doubt work inline (prefer `subagent_run`; role missing/unusable: native `Agent`, same read-only constraints; report fallback). When it resolves, re-evaluate task size.
|
|
54
54
|
|
|
55
55
|
1. **Ask** — open product or design decision → one focused question; stop and wait.
|
|
56
56
|
2. **Evidence-budget rule** — understanding needs more than one read batch or >~5 sequential lookups → one `gentle-ai-explore`, handoff at most ~2k tokens with `path:line` evidence; one spot check only; never for reading before an inline write.
|
|
57
|
-
3. **Verification rule** — high
|
|
57
|
+
3. **Verification rule** — only a named high-risk item (assess `escalate`) → independent `gentle-ai-verify`; else inline; runtime admission, gates/overengineering: `orchestrator-verification.md`.
|
|
58
58
|
4. **Track** — large task → feature document, Engram mirror, `todo`, work-unit commits (`orchestrator-tracking.md`, `orchestrator-memory.md`).
|
|
59
|
-
5. **Writer rule** — never by file count or a large task alone; only for a reason (`orchestrator-writer.md`): 2+ independent units
|
|
59
|
+
5. **Writer rule** — never by file count or a large task alone; only for a reason (`orchestrator-writer.md`): 2+ independent units of behavior (separate features or fixes that do not depend on each other), each heavier than a subagent start (a subagent spends one to two minutes reading before it writes, so a unit qualifies only when it needs several minutes of work: several files or roughly 100+ changed lines; a small change to one module or one command is done inline even when several such changes are independent), launched together in background, else inline; Context backstop. Tests, docs, and code of the same behavior are one unit, never separate writers; a delivery that depends on an earlier one is not independent.
|
|
60
60
|
6. **Incident rule** — diagnose wrong cwd/worktree/git/tooling incidents separately before resuming.
|
|
61
|
-
7. **Context backstop** —
|
|
61
|
+
7. **Context backstop** — when the runtime signals it → delegate the next unit needing several steps. Bound command output (counts, `--stat`, `tail`).
|
|
62
62
|
|
|
63
63
|
{{GENTLE_PI_BACKGROUND_POLICY}}; rules: delegation background-subagents block.
|
|
64
64
|
|
|
65
|
-
Per-action table, Work Routing Ladder, Canonical Workflows: `orchestrator-delegation.md`; blocking-prompt relays and provider defects: `orchestrator-prompts.md`.
|
|
65
|
+
Per-action table, Work Routing Ladder, Canonical Workflows: `orchestrator-delegation-reference.md`; blocking-prompt relays and provider defects: `orchestrator-prompts.md`.
|
|
66
66
|
|
|
67
67
|
## Memory Contract
|
|
68
68
|
|
|
@@ -83,7 +83,7 @@ This package injects the mirrored provider-bundle review execution contract into
|
|
|
83
83
|
## Safety
|
|
84
84
|
|
|
85
85
|
- An eligible interactive Pi host may resolve `gentle-ai.review-integration.consent/v3` before the envelope reaches the model. Permission: host-owned. If `gentle_review` returns the envelope unresolved, it is still the original provider-owned two-choice contract. Use `ask_user_choice` exactly or relay losslessly and stop. Never add the host action to a decoded or relayed provider envelope.
|
|
86
|
-
- Never commit unless the user explicitly asks.
|
|
86
|
+
- Never commit unless the user explicitly asks, except the work-unit commits authorized large ODD implementation makes on its feature branch (ODD step 6).
|
|
87
87
|
- Ask before destructive git operations, publishing, or irreversible file changes.
|
|
88
88
|
- Parallel writers only with disjoint Allowed edit surfaces (runtime-enforced) or isolated worktrees.
|
|
89
89
|
- Keep session work inside the project root and registered same-clone worktrees; ask before any read or write outside it, naming the absolute path. Grants are per-target, per-session, never blanket: in-project scripts naming outside paths are not standing consent.
|
package/bin/engram-guard
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
# local Engram data only by running ENGRAM_BIN, so failing here stops them
|
|
6
6
|
# before any store is opened, created or migrated. It never runs Engram and
|
|
7
7
|
# never reads or writes Engram data; the notice is printed as data, not run.
|
|
8
|
-
printf '%s\n' "${ENGRAM_STARTUP_NOTICE:-Engram memory
|
|
8
|
+
printf '%s\n' "${ENGRAM_STARTUP_NOTICE:-Engram memory disabled: Shell could not verify safe store or server reuse. Matching versions or updating alone do not prove compatibility. Reuse requires a server with verified /compatibility evidence or matching Shell-provisioned store identity. Back up existing Engram data before manual changes; do not delete it or bypass the safety check. Shell rechecks on every launch and remains usable without memory.}" >&2
|
|
9
9
|
exit 78
|
package/bin/gentle-shell.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
chmodSync,
|
|
10
10
|
closeSync,
|
|
11
11
|
constants as fsConstants,
|
|
12
|
+
copyFileSync,
|
|
12
13
|
existsSync,
|
|
13
14
|
fstatSync,
|
|
14
15
|
lstatSync,
|
|
@@ -50,22 +51,31 @@ import {
|
|
|
50
51
|
parseRawLauncherConfig,
|
|
51
52
|
planSpawn,
|
|
52
53
|
POST_INSTALL_REMOVAL_SOURCES,
|
|
54
|
+
withLauncherPackagesExcluded,
|
|
53
55
|
postInstallRemovals,
|
|
54
56
|
provisionedEntry,
|
|
57
|
+
migrateStandaloneConfig,
|
|
55
58
|
recordProvisioned,
|
|
56
59
|
resolveHome,
|
|
57
60
|
resolvePiRuntime,
|
|
58
61
|
restoreJsonField,
|
|
62
|
+
separateHomeHint,
|
|
59
63
|
shellQuote,
|
|
64
|
+
standaloneConfigHome,
|
|
65
|
+
userPiHome,
|
|
60
66
|
} from "../runtime/gentle-shell-launcher.mjs";
|
|
67
|
+
import { formatImportPiReport, importPi } from "../runtime/gentle-shell-import-pi.mjs";
|
|
61
68
|
import {
|
|
62
69
|
BUNDLED_VERIFY_ENV,
|
|
63
70
|
bundledEngramSchema,
|
|
64
71
|
classifyEngramStartup,
|
|
65
72
|
ENGRAM_COMPANION_SOURCE,
|
|
73
|
+
ENGRAM_CORE_CHANGED_NOTICE,
|
|
74
|
+
ENGRAM_PRIVATE_REQUIRED_NOTICE,
|
|
66
75
|
ENGRAM_PROVISION_FAILED_NOTICE,
|
|
67
76
|
ENGRAM_RECOVERY_NOTICE,
|
|
68
77
|
engramHeaderAllows,
|
|
78
|
+
GENTLE_SHELL_CONFIG_HOME_ENV,
|
|
69
79
|
engramProbeAllowed,
|
|
70
80
|
engramProvenanceMatches,
|
|
71
81
|
engramProvenancePath,
|
|
@@ -74,6 +84,7 @@ import {
|
|
|
74
84
|
probeEngramEndpoint,
|
|
75
85
|
sqliteHeaderUserVersion,
|
|
76
86
|
supportsEngramStartupPolicy,
|
|
87
|
+
supportsEngramSessionOwned,
|
|
77
88
|
bundledChildEnv,
|
|
78
89
|
bundledPi,
|
|
79
90
|
bundledSetupEnv,
|
|
@@ -95,6 +106,7 @@ import {
|
|
|
95
106
|
import { bundledGentleAiPaths, GENTLE_AI_VERSION, gentleAiBinaryPath, gentleAiIsolationEnv, PackageLocalGentleAiBinaryMissingError } from "../runtime/gentle-ai-binary.mjs";
|
|
96
107
|
import { DEFAULT_THEME_NAME, installIsolatedTuiModeSetting } from "../scripts/install-tui-mode-setting.mjs";
|
|
97
108
|
import { artifactFor, engramPinVersion } from "../scripts/installer-downloads.mjs";
|
|
109
|
+
import { migrateBundledEngram, migrationPending, supportsBundledSessionOwned, MIGRATION_PENDING_NOTICE, MIGRATION_USAGE } from "../scripts/bundled-engram-migration.mjs";
|
|
98
110
|
import { shouldCheckStartupUpdate, findStartupUpdate } from "../runtime/gentle-shell-startup-update.mjs";
|
|
99
111
|
import { promptStartupUpdate } from "../runtime/gentle-shell-update-panel.mjs";
|
|
100
112
|
import { applyStartupChoice, omitStartupVersion, resolveStartupRelaunch } from "../runtime/gentle-shell-startup-flow.mjs";
|
|
@@ -268,24 +280,31 @@ function ownEngramStore(observed, core) {
|
|
|
268
280
|
// of the default endpoint. No Engram executable, SQLite, identity repair, or
|
|
269
281
|
// contact with an explicitly selected endpoint.
|
|
270
282
|
async function classifyBundledMemory() {
|
|
283
|
+
if (migrationPending(bundledInstall.root)) return { policy: "disabled", notice: MIGRATION_PENDING_NOTICE };
|
|
271
284
|
const core = verifiedBundledCore();
|
|
272
285
|
const decision = { env: process.env, core: core !== undefined, coreVersion: engramPinVersion };
|
|
273
286
|
if (!engramProbeAllowed(process.env)) return classifyEngramStartup({ ...decision, store: "unknown", endpoint: { kind: "unknown" } });
|
|
274
287
|
const observed = observeEngramStore();
|
|
275
288
|
const endpoint = await probeEngramEndpoint();
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
+
const result = classifyEngramStartup({ ...decision, store: observed.store, endpoint, provenance: ownEngramStore(observed, core) });
|
|
290
|
+
if (result.policy === "disabled" && endpoint.kind === "refused" && observed.store === "present" && core) {
|
|
291
|
+
// Wording only: an unreadable record or core keeps the generic notice and
|
|
292
|
+
// must never abort startup.
|
|
293
|
+
try {
|
|
294
|
+
const record = parseEngramProvenance(readSmallRegularFile(engramProvenancePath(bundledInstall.root, process.platform), 4096));
|
|
295
|
+
if (record?.dataDir === observed.dataDir && record.instanceId === observed.instanceId && record.coreSha256 !== sha256File(core)) {
|
|
296
|
+
return { ...result, notice: ENGRAM_CORE_CHANGED_NOTICE };
|
|
297
|
+
}
|
|
298
|
+
} catch { /* Keep the generic disabled notice. */ }
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Verified with the published Engram v3.4.0 darwin-arm64 binary in an isolated
|
|
304
|
+
// HOME: `instance-id` creates only `.instance-id.lock` and `.instance-id`,
|
|
305
|
+
// without a database. Keep update checks suppressed and GitHub tokens removed
|
|
306
|
+
// for this provisioning invocation. The timeout stays below the identity lock
|
|
307
|
+
// wait: a held lock means a concurrent creator, so memory stays off that launch.
|
|
289
308
|
const ENGRAM_PROVISION_TIMEOUT_MS = 15000;
|
|
290
309
|
|
|
291
310
|
// A fresh launch's store identity, provisioned by Shell itself with the verified
|
|
@@ -338,6 +357,7 @@ async function ensureEngramCompanion(home, runtime, stdio, timeoutMs) {
|
|
|
338
357
|
if (!Array.isArray(packages)) return { ok: false, exitCode: 1, message: "gentle-shell: cannot verify Engram startupPolicy from non-array packages" };
|
|
339
358
|
let pinned = false;
|
|
340
359
|
let local = false;
|
|
360
|
+
let localOwned = true;
|
|
341
361
|
let npmSelected = false;
|
|
342
362
|
for (const entry of packages) {
|
|
343
363
|
const source = typeof entry === "string" ? entry : entry?.source;
|
|
@@ -351,9 +371,10 @@ async function ensureEngramCompanion(home, runtime, stdio, timeoutMs) {
|
|
|
351
371
|
return { ok: false, exitCode: 1, message: `gentle-shell: selected companion ${source} lacks engram.startupPolicy=1; update it and recheck (selection preserved)` };
|
|
352
372
|
}
|
|
353
373
|
local = true;
|
|
374
|
+
localOwned &&= supportsEngramSessionOwned(readJsonIfExists(join(selected, "package.json")));
|
|
354
375
|
}
|
|
355
|
-
if (local && !npmSelected) return { ok: true };
|
|
356
|
-
if (pinned && supportsEngramStartupPolicy(readJsonIfExists(metadata))) return { ok: true };
|
|
376
|
+
if (local && !npmSelected) return { ok: true, sessionOwned: localOwned };
|
|
377
|
+
if (pinned && supportsEngramStartupPolicy(readJsonIfExists(metadata))) return { ok: true, sessionOwned: localOwned && supportsEngramSessionOwned(readJsonIfExists(metadata)) };
|
|
357
378
|
process.stderr.write(`gentle-shell: installing policy-capable companion ${ENGRAM_COMPANION_SOURCE} through Pi\n`);
|
|
358
379
|
const result = await spawnAndWait(runtime.command, [...runtime.args, "install", ENGRAM_COMPANION_SOURCE], buildSetupEnv(home, runtime), stdio, timeoutMs);
|
|
359
380
|
let declared = false;
|
|
@@ -364,7 +385,7 @@ async function ensureEngramCompanion(home, runtime, stdio, timeoutMs) {
|
|
|
364
385
|
if (!result.ok || result.timedOut || !declared || !supportsEngramStartupPolicy(readJsonIfExists(metadata))) {
|
|
365
386
|
return { ok: false, exitCode: 1, message: "gentle-shell: companion installation did not establish engram.startupPolicy=1; the Engram guard still keeps unverified stores closed. Repair gentle-engram, then restart Shell." };
|
|
366
387
|
}
|
|
367
|
-
return { ok: true };
|
|
388
|
+
return { ok: true, sessionOwned: localOwned && supportsEngramSessionOwned(readJsonIfExists(metadata)) };
|
|
368
389
|
}
|
|
369
390
|
|
|
370
391
|
// The bundled children's environment (bundledChildEnv), with the pinned Engram
|
|
@@ -632,6 +653,42 @@ function handleHomeCommand(commandArgs) {
|
|
|
632
653
|
process.exit(0);
|
|
633
654
|
}
|
|
634
655
|
|
|
656
|
+
// `gentle-shell [home selectors] home import-pi [--dry-run]`: copies the
|
|
657
|
+
// user's pi sign-ins, MCP servers and default model into the resolved
|
|
658
|
+
// separate home (lib/gentle-shell-import-pi.ts). A home that is new or empty
|
|
659
|
+
// is first bootstrapped exactly as a first launch would, so the files this
|
|
660
|
+
// adds never make it look foreign to auto-provisioning (homeIsForeign).
|
|
661
|
+
async function handleImportPiCommand(args) {
|
|
662
|
+
const options = args.commandArgs.slice(1);
|
|
663
|
+
if (options.some((option) => option !== "--dry-run")) fail("gentle-shell home import-pi accepts only --dry-run. Run 'gentle-shell --help'.", 2);
|
|
664
|
+
const dryRun = options.includes("--dry-run");
|
|
665
|
+
let home = resolveHome({ args, env: process.env, homedir: homedir(), config: loadConfig() });
|
|
666
|
+
if (home.mode === "path") home = { ...home, dir: resolvePath(home.dir) };
|
|
667
|
+
const source = resolvePath(userPiHome(process.env, homedir()));
|
|
668
|
+
const refusal = "Choose a separate home with --isolated or --home <path>.";
|
|
669
|
+
if (home.mode === "link") {
|
|
670
|
+
fail(`gentle-shell home import-pi: the selected home is your pi home itself (--link), so there is nothing to import. ${refusal}`, 1);
|
|
671
|
+
}
|
|
672
|
+
if ([source, join(homedir(), ".pi", "agent")].some((piHome) => safeRealpath(piHome) === safeRealpath(home.dir))) {
|
|
673
|
+
fail(`gentle-shell home import-pi: ${home.dir} is your pi home itself, so there is nothing to import. ${refusal}`, 1);
|
|
674
|
+
}
|
|
675
|
+
if (homeNeedsBootstrap(home.dir)) {
|
|
676
|
+
if (dryRun) {
|
|
677
|
+
process.stdout.write(`Would prepare ${home.dir} as a new Gentle Shell home\n`);
|
|
678
|
+
} else {
|
|
679
|
+
try {
|
|
680
|
+
await bootstrapOwnedHome(home);
|
|
681
|
+
} catch (error) {
|
|
682
|
+
fail(`gentle-shell home import-pi: could not prepare ${home.dir} (${error.message}); nothing was imported`, 1);
|
|
683
|
+
}
|
|
684
|
+
process.stdout.write(`Prepared ${home.dir} as a new Gentle Shell home\n`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const result = importPi({ source, target: home.dir, dryRun });
|
|
688
|
+
for (const line of formatImportPiReport(result, { source, target: home.dir, dryRun })) process.stdout.write(`${line}\n`);
|
|
689
|
+
process.exitCode = result.failed ? 1 : 0;
|
|
690
|
+
}
|
|
691
|
+
|
|
635
692
|
// Test/development-only override for the setup subcommand's gentle-ai
|
|
636
693
|
// executable path. Lets a test point at a stub script (or a deliberately
|
|
637
694
|
// missing path) without touching the real pinned .gentle-ai/v<version>/gentle-ai
|
|
@@ -804,12 +861,18 @@ function buildSetupEnv(home, runtime, piLauncherDir) {
|
|
|
804
861
|
const env = bundledSetupEnv({ ...bundledEnv, PI_CODING_AGENT_DIR: home.dir, GENTLE_PI_AGENT_HOME: home.dir }, { dirs: bundledSetupDirs, platform: process.platform });
|
|
805
862
|
return gentleAiIsolationEnv(env, { packageRoot });
|
|
806
863
|
}
|
|
807
|
-
|
|
864
|
+
const env = {
|
|
808
865
|
...process.env,
|
|
809
866
|
PI_CODING_AGENT_DIR: home.dir,
|
|
810
867
|
GENTLE_PI_AGENT_HOME: home.dir,
|
|
811
868
|
PATH: [...(piLauncherDir ? [piLauncherDir] : []), dirname(runtime.command), process.env.PATH ?? ""].join(delimiter),
|
|
812
869
|
};
|
|
870
|
+
// #2201: the isolated home's own config folder, given to Gentle AI the
|
|
871
|
+
// bundled way (gentleAiIsolationEnv); never an outer session's marker.
|
|
872
|
+
const configHome = standaloneConfigHome(home);
|
|
873
|
+
if (configHome === undefined) delete env[GENTLE_SHELL_CONFIG_HOME_ENV];
|
|
874
|
+
else env[GENTLE_SHELL_CONFIG_HOME_ENV] = configHome;
|
|
875
|
+
return gentleAiIsolationEnv(env, { packageRoot });
|
|
813
876
|
}
|
|
814
877
|
|
|
815
878
|
// Gentle AI requires a `pi` on its PATH and runs `pi install npm:gentle-pi`
|
|
@@ -852,15 +915,34 @@ function sharedPersonaPath() {
|
|
|
852
915
|
return join(homedir(), ".pi", "gentle-ai", "persona.json");
|
|
853
916
|
}
|
|
854
917
|
|
|
855
|
-
// The persona files a setup run may rewrite. Bundled (S9)
|
|
856
|
-
// which a Gentle AI with
|
|
857
|
-
// still the user's,
|
|
858
|
-
// unchanged file
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
return [join(bundledGentleAiPaths(bundledInstall.root, process.platform).piConfig, "persona.json"), sharedPersonaPath()];
|
|
918
|
+
// The persona files a setup run may rewrite. Bundled (S9) and the standalone
|
|
919
|
+
// isolated home (#2201): its own config folder's, which a Gentle AI with
|
|
920
|
+
// GENTLE_AI_PI_CONFIG_DIR (gentle-ai #5462) writes, and still the user's,
|
|
921
|
+
// which a pinned Gentle AI without it writes; restoring an unchanged file
|
|
922
|
+
// does nothing.
|
|
923
|
+
function setupPersonaPaths(home) {
|
|
924
|
+
if (bundledInstall) return [join(bundledGentleAiPaths(bundledInstall.root, process.platform).piConfig, "persona.json"), sharedPersonaPath()];
|
|
925
|
+
const configHome = standaloneConfigHome(home);
|
|
926
|
+
return configHome === undefined ? [sharedPersonaPath()] : [join(configHome, "persona.json"), sharedPersonaPath()];
|
|
862
927
|
}
|
|
863
928
|
|
|
929
|
+
// The real filesystem behind migrateStandaloneConfig (#2201).
|
|
930
|
+
const configMigrationFs = {
|
|
931
|
+
kind: (path) => {
|
|
932
|
+
try {
|
|
933
|
+
const stat = lstatSync(path);
|
|
934
|
+
return stat.isDirectory() ? "dir" : stat.isFile() ? "file" : "other";
|
|
935
|
+
} catch {
|
|
936
|
+
return "missing";
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
list: (path) => readdirSync(path),
|
|
940
|
+
mkdirPrivate: (path) => {
|
|
941
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
942
|
+
},
|
|
943
|
+
copyNew: (from, to) => copyFileSync(from, to, fsConstants.COPYFILE_EXCL),
|
|
944
|
+
};
|
|
945
|
+
|
|
864
946
|
// Bundled setup (S9): the pinned Engram GENTLE_AI_ENGRAM_BIN names, published
|
|
865
947
|
// once in runtime/ (verified, never brew or the user's PATH). A failure stops
|
|
866
948
|
// setup rather than let Gentle AI acquire another Engram.
|
|
@@ -1139,8 +1221,10 @@ async function runSetupFlow(home, runtime, { dryRun, stdio, timeoutMs }) {
|
|
|
1139
1221
|
|
|
1140
1222
|
const setupArgs = ["install", "--agent", "pi", "--scope", "global", ...(dryRun ? ["--dry-run"] : [])];
|
|
1141
1223
|
const piLauncherDir = writeSetupPiLauncher(runtime);
|
|
1142
|
-
|
|
1143
|
-
|
|
1224
|
+
// #2143: a Gentle AI with package exclusion never installs the launcher-owned
|
|
1225
|
+
// gentle-pi; an older one ignores this and runPostInstallCleanup removes it.
|
|
1226
|
+
const env = withLauncherPackagesExcluded(buildSetupEnv(home, runtime, piLauncherDir));
|
|
1227
|
+
const personaSnapshots = setupPersonaPaths(home).map((path) => [path, safely("snapshot your Pi persona file", path, undefined, () => snapshotFile(path))]);
|
|
1144
1228
|
const statePath = sharedGentleAiStatePath();
|
|
1145
1229
|
const originalStateText = safely("read your Gentle AI state file", statePath, undefined, () => readParsableJsonText(statePath));
|
|
1146
1230
|
// Theme restore (never for --link — that home is the user's own
|
|
@@ -1392,6 +1476,25 @@ function writeHomeOwnershipMarker(home) {
|
|
|
1392
1476
|
writeFileSync(homeOwnershipMarkerPath(home), `${content}\n`, "utf8");
|
|
1393
1477
|
}
|
|
1394
1478
|
|
|
1479
|
+
// The isolated/--home bootstrap: the first launch's own (main, below) and
|
|
1480
|
+
// `home import-pi`'s before it adds files to a new or empty home.
|
|
1481
|
+
async function bootstrapOwnedHome(home) {
|
|
1482
|
+
// 0700: under umask 002 a default mode would leave ~/.gentle-shell group-writable,
|
|
1483
|
+
// which the bundled install's prefix claim must tighten or refuse.
|
|
1484
|
+
mkdirSync(home.dir, { recursive: true, mode: 0o700 });
|
|
1485
|
+
await installIsolatedTuiModeSetting(home.dir);
|
|
1486
|
+
writeHomeOwnershipMarker(home); // R3-001: lets a failed first auto-provision attempt still be retried later
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function homeNeedsBootstrap(dir) {
|
|
1490
|
+
if (!existsSync(dir)) return true;
|
|
1491
|
+
try {
|
|
1492
|
+
return isDirectory(dir) && readdirSync(dir).length === 0;
|
|
1493
|
+
} catch {
|
|
1494
|
+
return false;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1395
1498
|
// `homeHadContentBeforeBootstrap` must be read by the caller (main, below)
|
|
1396
1499
|
// before the isolated/--home bootstrap runs: that bootstrap itself creates
|
|
1397
1500
|
// and seeds a brand-new directory (settings.json with tuiMode, and now the
|
|
@@ -1782,16 +1885,32 @@ async function main() {
|
|
|
1782
1885
|
const args = parseLauncherArgs(process.argv.slice(2));
|
|
1783
1886
|
if (args.error !== undefined) fail(`${args.error}\nRun 'gentle-shell --help' for usage.`, 2);
|
|
1784
1887
|
if (args.help) {
|
|
1785
|
-
process.stdout.write(
|
|
1888
|
+
process.stdout.write("Pi updates: update reinstalls the verified supplier of the Pi this launcher runs (a global Pi, or the\nShell package whose adjacent peer it is, at the same Shell version) and reads the resulting Pi version back.\nUnknown ownership refuses without installing; update --extensions-only stays Pi-only.\nPrefix-bundled Pi updates with gentle-shell upgrade.\n");
|
|
1889
|
+
process.stdout.write(`${helpText()}\nBundled Engram safety: memory opens only a fresh, verified or Shell-provisioned store; otherwise ENGRAM_BIN\nis Shell's guard and memory stays off. Matching versions or updating alone do not prove compatibility.\nReuse requires a server with verified /compatibility evidence or matching Shell-provisioned store identity.\nBack up existing data before manual changes; do not delete it or bypass the safety check.\nShell rechecks on every launch and remains usable without memory.\nAn explicit ENGRAM_BIN or ENGRAM_URL is kept and bypasses the guard.\n`);
|
|
1786
1890
|
process.exit(0);
|
|
1787
1891
|
}
|
|
1788
1892
|
if (args.command === "home") {
|
|
1789
|
-
|
|
1893
|
+
if (args.commandArgs[0] === "import-pi") await handleImportPiCommand(args);
|
|
1894
|
+
else handleHomeCommand(args.commandArgs);
|
|
1790
1895
|
return;
|
|
1791
1896
|
}
|
|
1792
1897
|
// The verification input applies to `--version` only (scripts/bundled-install.mjs upgradeBundled).
|
|
1793
1898
|
const verifying = args.version && args.command === undefined ? process.env[BUNDLED_VERIFY_ENV] : undefined;
|
|
1794
1899
|
const bundled = resolveBundledInstall({ packageRoot, platform: process.platform, fs: bundledFs, verifying });
|
|
1900
|
+
if (args.command === "memory") {
|
|
1901
|
+
if (args.commandArgs.length !== 2 || args.commandArgs[0] !== "migrate" || args.commandArgs[1] !== "--confirm-quiescent") {
|
|
1902
|
+
process.stderr.write(MIGRATION_USAGE);
|
|
1903
|
+
process.exitCode = 2;
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
if (!bundled) fail("gentle-shell: memory migrate requires a prefix-bundled installation", 1);
|
|
1907
|
+
bundledInstall = bundled;
|
|
1908
|
+
const result = migrateBundledEngram({ prefix: bundled.root, core: verifiedBundledCore(), home: homedir(), env: process.env });
|
|
1909
|
+
process.stdout.write(result.stdout);
|
|
1910
|
+
process.stderr.write(result.stderr);
|
|
1911
|
+
process.exitCode = result.code;
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1795
1914
|
if (args.command === "upgrade") {
|
|
1796
1915
|
if (bundled) process.exitCode = await handleBundledUpgrade(bundled, args.commandArgs);
|
|
1797
1916
|
else await handleUpgradeCommand(args.commandArgs);
|
|
@@ -1847,7 +1966,11 @@ async function main() {
|
|
|
1847
1966
|
});
|
|
1848
1967
|
if (versionProbe.error) fail(`Could not run the pi runtime at "${runtime.command}": ${versionProbe.error.message}`, 1);
|
|
1849
1968
|
const versionCheck = checkPiVersion(versionProbe.stdout ?? "");
|
|
1850
|
-
|
|
1969
|
+
// A managed self update is how an old Pi gets replaced, so the minimum-version
|
|
1970
|
+
// gate must not block it; everything else still requires the minimum.
|
|
1971
|
+
const managedSelfUpdate = !bundled && args.piSubcommand === "update" && versionCheck.version !== undefined
|
|
1972
|
+
&& (await import("../scripts/pi-update.mjs")).managedUpdatePlan(args.passthrough).self;
|
|
1973
|
+
if (!versionCheck.ok && !managedSelfUpdate) fail(versionCheck.message, 1);
|
|
1851
1974
|
|
|
1852
1975
|
if (args.version) {
|
|
1853
1976
|
process.stdout.write(`${describeVersion({ gentlePiVersion: ownPackageVersion(), piVersion: versionCheck.version, home })}\n`);
|
|
@@ -1869,6 +1992,15 @@ async function main() {
|
|
|
1869
1992
|
if (next.note) process.stderr.write(`${next.note}\n`);
|
|
1870
1993
|
}
|
|
1871
1994
|
passthrough = plan.piArgs;
|
|
1995
|
+
} else if (args.piSubcommand === "update") {
|
|
1996
|
+
const { managedUpdatePlan, updateSelectedPi } = await import("../scripts/pi-update.mjs");
|
|
1997
|
+
const plan = managedUpdatePlan(args.passthrough);
|
|
1998
|
+
if (plan.self) {
|
|
1999
|
+
process.exitCode = await updateSelectedPi({ runtime, packageRoot, platform: process.platform, currentVersion: versionCheck.version, cwd: tmpdir(),
|
|
2000
|
+
adapters: await packageManagerAdapters(), out: (line) => process.stdout.write(`${line}\n`) });
|
|
2001
|
+
if (plan.piArgs === undefined) return;
|
|
2002
|
+
}
|
|
2003
|
+
passthrough = plan.piArgs;
|
|
1872
2004
|
}
|
|
1873
2005
|
|
|
1874
2006
|
// Home-ownership signal for auto-provisioning (S9, homeIsForeign): must be
|
|
@@ -1888,12 +2020,22 @@ async function main() {
|
|
|
1888
2020
|
// Isolated-home bootstrap: only on a home gentle-shell has not seen before
|
|
1889
2021
|
// (link never bootstraps — it reuses the user's own pi agent home as-is).
|
|
1890
2022
|
if ((home.mode === "isolated" || home.mode === "path") && !existsSync(home.dir)) {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2023
|
+
await bootstrapOwnedHome(home);
|
|
2024
|
+
const piSignIns = existsSync(join(userPiHome(process.env, homedir()), "auth.json"));
|
|
2025
|
+
process.stderr.write(`${separateHomeHint(home, { piSignIns })}\n`);
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
// #2201: the standalone isolated home's own config folder, seeded once from
|
|
2029
|
+
// the shared ~/.pi/gentle-ai. Not when an explicit GENTLE_PI_CONFIG_HOME
|
|
2030
|
+
// decides the config home; never blocks the launch.
|
|
2031
|
+
const configHome = bundled || process.env.GENTLE_PI_CONFIG_HOME ? undefined : standaloneConfigHome(home);
|
|
2032
|
+
if (configHome !== undefined) {
|
|
2033
|
+
try {
|
|
2034
|
+
const migration = migrateStandaloneConfig({ source: join(homedir(), ".pi", "gentle-ai"), target: configHome, fs: configMigrationFs });
|
|
2035
|
+
for (const line of migration.lines) process.stderr.write(`${line}\n`);
|
|
2036
|
+
} catch (error) {
|
|
2037
|
+
process.stderr.write(`gentle-shell: could not prepare ${configHome} (${error.message}); continuing\n`);
|
|
2038
|
+
}
|
|
1897
2039
|
}
|
|
1898
2040
|
|
|
1899
2041
|
if (bundled) {
|
|
@@ -2043,6 +2185,16 @@ async function main() {
|
|
|
2043
2185
|
// could not be verified is reported, never a reason to stop Shell.
|
|
2044
2186
|
const companion = await ensureEngramCompanion(home, runtime, ["ignore", 2, 2], 10000);
|
|
2045
2187
|
if (!companion.ok) process.stderr.write(`${companion.message}\n`);
|
|
2188
|
+
if (bundledMemory.policy === "allow" && !bundledMemory.url) {
|
|
2189
|
+
const mode = process.env.ENGRAM_SERVER_MODE;
|
|
2190
|
+
if (mode !== undefined && mode !== "session-owned") {
|
|
2191
|
+
bundledMemory = { policy: "disabled", notice: "Engram memory disabled: explicit ENGRAM_SERVER_MODE is invalid for a local bundled server; unset it or select exactly session-owned. Your selection has not been overridden." };
|
|
2192
|
+
} else {
|
|
2193
|
+
bundledMemory = companion.ok && companion.sessionOwned && supportsBundledSessionOwned(verifiedBundledCore(), process.env)
|
|
2194
|
+
? { ...bundledMemory, owned: true }
|
|
2195
|
+
: { policy: "disabled", notice: ENGRAM_PRIVATE_REQUIRED_NOTICE };
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2046
2198
|
// A fresh store's identity and provenance exist before Pi starts, or
|
|
2047
2199
|
// memory stays off: Pi never runs with allow without a written record.
|
|
2048
2200
|
if (bundledMemory.fresh && !provisionEngramStore()) bundledMemory = { policy: "disabled", notice: ENGRAM_PROVISION_FAILED_NOTICE };
|
|
@@ -2061,6 +2213,7 @@ async function main() {
|
|
|
2061
2213
|
passthrough: [...managedHerdrExtensionArgs(home, args), ...passthrough],
|
|
2062
2214
|
piSubcommand: args.piSubcommand,
|
|
2063
2215
|
baseEnv: bundledEnv ?? process.env,
|
|
2216
|
+
bundled: Boolean(bundled),
|
|
2064
2217
|
homedir: homedir(),
|
|
2065
2218
|
// The spawn below sets no cwd, so pi runs in the launcher's own.
|
|
2066
2219
|
cwd: process.cwd(),
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "gentle-pi.review-provider-contract-mirror-lock/v1",
|
|
3
3
|
"acquisition": "field-test-local",
|
|
4
|
-
"contract_semver": "1.
|
|
4
|
+
"contract_semver": "1.4.0",
|
|
5
5
|
"source": {
|
|
6
6
|
"kind": "archive",
|
|
7
|
-
"archive_sha256": "
|
|
7
|
+
"archive_sha256": "9d2927a84ba9a596f69029e3f854b16f86ca4c8b32cc5d63683764ccf2fb394b"
|
|
8
8
|
},
|
|
9
|
-
"tree_sha256": "
|
|
9
|
+
"tree_sha256": "aab5ab7fe7ee6bb180d8bbce21c034cebb0c0d284afcc8664a9b3bb57323c35d",
|
|
10
10
|
"entries": {
|
|
11
|
-
"README.md": "
|
|
12
|
-
"manifest.json": "
|
|
11
|
+
"README.md": "3a655c2f19aee02636da062f0cae7a189b7e5f691c8ba8010f2ed50e5d8e8d70",
|
|
12
|
+
"manifest.json": "d2b5765e28df5b2459848379e91a27beb806916fabc96207060ad1cc5e911972",
|
|
13
13
|
"orchestration/pi.md": "2d7ddd99f1c739a97f4a6b3929e5e2bdddcbf69aed69356395038d90bca25b2f",
|
|
14
|
+
"routing/pi.md": "1c7466e2333c08e386b4b5afb081531146d181030692463431e7695df7f90690",
|
|
14
15
|
"schemas/lens.schema.json": "9b8a2354a570888486015434501b66b51441a3baf5bf858635e4fff56b2e8298",
|
|
15
16
|
"schemas/refuter.schema.json": "36f8267c6c3f04600b8f1cb77df70d90858611af822cc81922efd6b3502fb646",
|
|
16
17
|
"schemas/targeted-validator.schema.json": "40e397195bdfbc6782fc3090f20c5be082e4d14d2855314f99ecb8506133eddc",
|
|
@@ -19,8 +20,8 @@
|
|
|
19
20
|
"vectors/targeted-validator.json": "7b5a5165e3a913863fb98d2ba5387811860c21333b5c37e6ed3b4e0d121cf2fa"
|
|
20
21
|
},
|
|
21
22
|
"generated": {
|
|
22
|
-
"generated/provider-capabilities.baseline.json": "
|
|
23
|
-
"generated/provider-roles.baseline.json": "
|
|
23
|
+
"generated/provider-capabilities.baseline.json": "bca708f5f67f506b4acb2cfdcb775b02a51468579fc7d6b4d169785ff411a215",
|
|
24
|
+
"generated/provider-roles.baseline.json": "aa36ec6f7469ac6faab756eec681f68f234cbadc1e12b345a93c6c34aa01f7aa"
|
|
24
25
|
},
|
|
25
26
|
"runtimes": [
|
|
26
27
|
"claude-code",
|
|
@@ -19,4 +19,11 @@ It carries no executable content, same as every other file in this bundle: a
|
|
|
19
19
|
runtime mirrors and delivers the text as-is and still relies on Go for every
|
|
20
20
|
review decision, prompt, receipt, and delivery gate.
|
|
21
21
|
|
|
22
|
+
## Routing
|
|
23
|
+
|
|
24
|
+
manifest.json's routing array lists, for closed runtimes only, one
|
|
25
|
+
routing/<runtime>.md file: the exact ODD implementation-routing block Gentle
|
|
26
|
+
AI renders for that runtime, whose prompt Gentle AI does not install. It is
|
|
27
|
+
data only as well: a runtime mirrors and delivers the text as-is.
|
|
28
|
+
|
|
22
29
|
Go remains the admission authority for prompts, results, receipts, and delivery gates.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "gentle-ai.review-provider-contract-bundle/v1",
|
|
3
|
-
"contract_semver": "1.
|
|
3
|
+
"contract_semver": "1.4.0",
|
|
4
4
|
"transport_capability": "gentle-ai.provider-transport/v1",
|
|
5
5
|
"runtimes": [
|
|
6
6
|
"claude-code",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
],
|
|
11
11
|
"readme": {
|
|
12
12
|
"path": "README.md",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "3a655c2f19aee02636da062f0cae7a189b7e5f691c8ba8010f2ed50e5d8e8d70"
|
|
14
14
|
},
|
|
15
15
|
"roles": [
|
|
16
16
|
{
|
|
@@ -70,5 +70,14 @@
|
|
|
70
70
|
"sha256": "2d7ddd99f1c739a97f4a6b3929e5e2bdddcbf69aed69356395038d90bca25b2f"
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
],
|
|
74
|
+
"routing": [
|
|
75
|
+
{
|
|
76
|
+
"runtime": "pi",
|
|
77
|
+
"file": {
|
|
78
|
+
"path": "routing/pi.md",
|
|
79
|
+
"sha256": "1c7466e2333c08e386b4b5afb081531146d181030692463431e7695df7f90690"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
73
82
|
]
|
|
74
83
|
}
|