@jc_stack/ez-agents 0.1.0-beta.32 → 0.1.0-beta.34

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 (70) hide show
  1. package/.env.example +4 -0
  2. package/AGENTS.md +46 -31
  3. package/CHANGELOG.md +33 -0
  4. package/CONTRIBUTING.md +29 -26
  5. package/Dockerfile +4 -0
  6. package/README.md +26 -18
  7. package/THIRD_PARTY_NOTICES.md +4 -4
  8. package/compose.yaml +2 -1
  9. package/docker/smoke.mjs +6 -3
  10. package/docs/application-channel.md +6 -0
  11. package/docs/architecture/ai-selection.md +3 -1
  12. package/docs/docker-runtime.md +27 -10
  13. package/docs/host-service.md +17 -6
  14. package/docs/plugin-contributions.md +4 -1
  15. package/docs/plugins.md +15 -0
  16. package/docs/repair.md +1 -1
  17. package/docs/setup.md +88 -14
  18. package/docs/standalone-cli.md +6 -5
  19. package/docs/upgrades.md +6 -4
  20. package/package.json +1 -1
  21. package/scripts/smoke-scheduler.ts +2 -1
  22. package/src/agent-install.ts +20 -6
  23. package/src/ai-cli.ts +9 -7
  24. package/src/ai.ts +7 -7
  25. package/src/application-channel.ts +10 -10
  26. package/src/codex-session.ts +5 -3
  27. package/src/config.ts +6 -1
  28. package/src/control-state.ts +12 -1
  29. package/src/executor.ts +91 -9
  30. package/src/host-executor.ts +25 -6
  31. package/src/index.ts +1 -0
  32. package/src/install-config.ts +2 -2
  33. package/src/install-tools.mjs +3 -1
  34. package/src/isolation.ts +25 -0
  35. package/src/menu.ts +9 -6
  36. package/src/plugins/manager.mjs +49 -6
  37. package/src/reply-context.ts +0 -1
  38. package/src/setup.ts +11 -7
  39. package/src/task-executor.ts +4 -1
  40. package/src/updates/control.mjs +11 -1
  41. package/src/updates/runtime.mjs +4 -2
  42. package/src/updates/supervisor.mjs +17 -3
  43. package/src/workspace-confine.ts +38 -0
  44. package/src/workspace.ts +23 -13
  45. package/templates/agent/AGENTS.md +4 -14
  46. package/templates/agent-guidance.md +13 -110
  47. package/templates/agent-purpose.md +1 -0
  48. package/test/agent-guidance.test.ts +8 -12
  49. package/test/agent-install.test.ts +34 -0
  50. package/test/ai-cli.test.ts +18 -0
  51. package/test/ai.test.ts +48 -0
  52. package/test/application-channel.test.ts +5 -2
  53. package/test/application-controls.test.ts +56 -3
  54. package/test/application-only.test.ts +2 -0
  55. package/test/codex-context.test.ts +61 -1
  56. package/test/codex-session.test.ts +6 -1
  57. package/test/config.test.ts +17 -2
  58. package/test/engine-handoff.test.ts +5 -2
  59. package/test/executor.test.ts +38 -1
  60. package/test/failure.test.ts +6 -0
  61. package/test/host-executor.test.ts +6 -0
  62. package/test/install-config.test.ts +17 -6
  63. package/test/install-tools.test.mjs +6 -0
  64. package/test/plugin-manager.test.mjs +24 -3
  65. package/test/scheduler.test.ts +2 -2
  66. package/test/updates.test.mjs +34 -2
  67. package/test/workspace-confine.test.ts +47 -0
  68. package/test/workspace.test.ts +23 -10
  69. package/templates/agent/SOUL.md +0 -11
  70. package/templates/agent/USER.md +0 -5
package/.env.example CHANGED
@@ -48,6 +48,10 @@ EZ_REPAIR_ENABLED=true
48
48
  # Optional native application-only runtime: no Telegram bot or token.
49
49
  # Requires EZ_APPLICATION_PORT; administrator bootstrap uses the real operator ID.
50
50
  # EZ_TELEGRAM_ENABLED=false
51
+ # Isolation class from agent creation (default isolated).
52
+ # EZ_ISOLATION=isolated
53
+ # EZ_EXECUTOR_TRANSPORT=local
54
+ # Host-capable: EZ_ISOLATION=host-capable and EZ_EXECUTOR_TRANSPORT=host
51
55
  # Private application-only local container isolation (optional; default workspace-write).
52
56
  # EZ_CODEX_SANDBOX=external
53
57
 
package/AGENTS.md CHANGED
@@ -73,6 +73,35 @@ can be stated briefly and deferred. Reviewers distinguish those blockers from
73
73
  optional follow-ups and do not hold a working increment for hypothetical polish.
74
74
  Measure progress by usable outcomes and feedback, not code or test volume.
75
75
 
76
+ ## Feasibility frame for non-trivial work
77
+
78
+ Before implementing a non-trivial coding, architecture or product change, state
79
+ the user-visible objective, no more than five task-relevant binding constraints,
80
+ the component that owns the failed boundary, the subtraction or simplification
81
+ option, the minimum evidence needed to prove the outcome, and the stop condition.
82
+ Do not restate the full prompt or add this ceremony to minor mechanical edits.
83
+
84
+ A result that violates a binding constraint is invalid even when tests pass or
85
+ the immediate symptom disappears. Check behavioral correctness, architecture and
86
+ ownership, real user-path evidence and authorized side effects separately. Before
87
+ completion, inspect whether the change added a runner, wrapper, state owner, queue,
88
+ retry loop, prompt layer, dependency or configuration format; retain it only when
89
+ the existing owner or contract cannot solve the demonstrated problem. Use one
90
+ bounded constraint review, then stop when sufficient evidence passes unless a
91
+ concrete material risk remains.
92
+
93
+ Frame that review as answers to these questions, not as an open-ended request to
94
+ find flaws:
95
+
96
+ 1. Did the solution cross an architecture, ownership or authority boundary?
97
+ 2. Did it bypass a binding constraint or preserve the symptom through a workaround?
98
+ 3. Did it add machinery where deletion or a simpler existing contract would work?
99
+ 4. Does the evidence prove the real user-visible outcome rather than only a proxy?
100
+ 5. Is there one concrete unresolved risk severe enough to block completion?
101
+
102
+ If no answer identifies a material blocker, finish. Do not start another review
103
+ round merely to seek more criticism, tests or hypothetical edge cases.
104
+
76
105
  ## 3. Crash-Safe Atomic Disk State
77
106
  - All persistent stores (`ControlStore`, `RunStore`, outbox queue) must be disk-backed JSON files.
78
107
  - **Atomic write pattern:** Never write directly to a state file. Always write to a temporary file (`${target}.${process.pid}.tmp`) with mode `0o600`, then atomically `rename` it over the destination.
@@ -97,37 +126,23 @@ Measure progress by usable outcomes and feedback, not code or test volume.
97
126
  - Unapproved group messages from other senders and unknown DMs fail silently unless an exact-contact or any-conversation owner-approved channel grant admits them. Both grant types use the restricted task runner. Paired-owner group text is discovery routed to the owner's private chat; it grants no group reply authority.
98
127
  - Stopping work (`/stop`) must terminate the active worker PID immediately (`SIGTERM`, escalating to `SIGKILL` if unclosed after 3s).
99
128
 
100
- ## 8. Mandatory Adversarial & Negative Tests
101
- Every pull request modifying authority, execution, or routing must include negative tests:
102
- 1. Unapproved Telegram ID $\rightarrow$ no process spawned.
103
- 2. Non-owner group message $\rightarrow$ silent ignore; owner group discovery $\rightarrow$ private destination only.
104
- 3. Executor environment check $\rightarrow$ asserts `TELEGRAM_BOT_TOKEN` is `undefined`.
105
- 4. Corrupt JSON in store $\rightarrow$ handled gracefully without process crash.
106
- 5. Injected path traversal in IDs $\rightarrow$ rejected.
129
+ ## 8. Risk-based negative validation
130
+
131
+ Changes to authority, execution, routing, secret isolation or durable state must
132
+ validate the affected failure boundary, not rerun or recreate an unrelated fixed
133
+ checklist. Examples include proving that an unapproved sender spawns no process,
134
+ executor secrets remain absent, invalid IDs cannot traverse paths, ambiguous
135
+ external writes are not retried, and corrupt state fails safely. Add or change a
136
+ test only when it protects the changed contract or a demonstrated regression.
137
+ Existing relevant checks plus focused real-path evidence may be sufficient.
107
138
 
108
139
  ## Public contribution workflow
109
140
 
110
- Read CONTRIBUTING.md before edits and docs/releasing.md before a release.
111
- Maintainers and external agents use the same PR, tests and documentation standard.
112
- Keep internal plans and private evidence outside this repository.
113
-
114
- Before edits, follow CONTRIBUTING.md's isolated-work rules: one task per dedicated
115
- worktree/branch/PR, starting from fetched origin/main. Do not switch or mix work in
116
- another task's checkout. Stage only this task's changes. Keep its worktree through
117
- review and QA; independent review and green CI precede an authorized merge.
118
- Never treat task completion as permission to merge or publish.
119
- Own the complete engineering/release handoff in CONTRIBUTING.md. Once ready,
120
- proactively request only missing merge/release authority, then ship and verify;
121
- do not leave the maintainer to discover ready drafts or operate the release.
122
-
123
- For every core or plugin PR you author, revise or review, apply CONTRIBUTING.md's
124
- architecture check and the README's engine/application boundary. A symptom fix
125
- and green tests do not justify a conflicting execution or context owner. Existing
126
- legacy channel-backend code is a documented limitation, not a pattern for new
127
- integrations; applications submit agent turns through the native Ez execution path.
128
-
129
- A coding-task handoff must include the pushed commit and draft PR URL, checks,
130
- independent-review status and remaining QA. If PR creation is blocked, state the
131
- blocker and preserved commit. The merging agent owns the post-merge worktree
132
- cleanup check and reports removal or the specific reason to retain it. Follow
133
- CONTRIBUTING.md for squash-merge evidence, ignored files and active-use checks.
141
+ Read CONTRIBUTING.md before edits and docs/releasing.md before a release. It owns
142
+ the worktree, PR, review, merge, release and cleanup process; do not duplicate that
143
+ process here or in feature documentation. For every core or plugin change, apply
144
+ its architecture check and the README's engine/application boundary. A symptom
145
+ fix and green tests do not justify a conflicting execution or context owner.
146
+ Existing legacy channel-backend code is a documented limitation, not a pattern
147
+ for new integrations. Keep internal plans and private evidence outside this
148
+ repository.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-beta.34
4
+
5
+ - Correct setup guidance for host-capable custom Codex providers and replace
6
+ stale pinned-beta installation guidance with current release records.
7
+
8
+ - Record an isolation class at agent creation. The default is `isolated`: the
9
+ native CLI runs in the relay (`EZ_EXECUTOR_TRANSPORT=local`). Pass
10
+ `--isolation host-capable` to reuse the installer UID and host CLI login.
11
+ Mismatched class and transport fail closed. Isolated Codex sets
12
+ `EZ_CODEX_SANDBOX=external`. Host transport is not started for isolated
13
+ agents. Unlabeled existing host transports stay host-capable.
14
+ - Install pinned Codex CLI `0.153.4` in the relay runtime image so isolated
15
+ agents resolve `codex` on PATH. Isolated jobs keep auth in
16
+ `control/cli/codex` and do not link the operator `~/.codex`.
17
+
18
+ - Confine macOS host jobs to their bound workspace and prevent cross-agent
19
+ plugin registry access.
20
+ - Support agent-scoped OpenRouter models through the native Codex engine and
21
+ start a new native session when the provider or model changes.
22
+ - Resolve native CLIs from the host PATH, expose captured presets in application
23
+ run receipts, and seed one purpose-scoped AGENTS.md for new agents.
24
+
25
+ ## 0.1.0-beta.33
26
+
27
+ - Allow compatible plugin updates to add commands to existing services, registering
28
+ their aliases atomically while retaining collision checks and rejecting changes
29
+ to existing command routes or deployment authority.
30
+ - Simplify contribution guidance and bound independent constraint reviews without
31
+ changing the native engine, transport or plugin responsibility boundaries.
32
+
3
33
  ## 0.1.0-beta.32
4
34
 
5
35
  - Add shared authenticated inbound attachment staging for application and
@@ -13,6 +43,9 @@
13
43
  host CLI configuration needed for restricted read-only plugin commands.
14
44
  - Clarify domain-agent positioning while preserving native engine ownership.
15
45
 
46
+ - Prevent accidental cross-agent plugin access by rejecting relay-bound registry
47
+ calls outside the owning workspace, and restrict its host-folder bindings to
48
+ workspace roots explicitly granted by the host.
16
49
  - Answer native app-server requests with their current protocol shapes. Scheduled
17
50
  `codex-gui` browser work now surfaces approval requests and grants only its
18
51
  active turn session-scoped HTTPS browser-origin and Chrome app elicitations,
package/CONTRIBUTING.md CHANGED
@@ -16,9 +16,11 @@ repository. Public docs describe shipped behavior and explicit limitations.
16
16
  Packaging/runtime changes also need the Docker checks in docs/releasing.md.
17
17
  5. Open a PR explaining the problem, resulting behavior, verification and limits.
18
18
  Include a short sanitized reproduction. State which tests were not run.
19
- 6. Obtain independent review and required CI, then complete the
20
- maintainer-authorized merge. Maintainers use PRs too. No CLA, ticket
21
- requirement, custom commit format or additional approval committee.
19
+ 6. Obtain one bounded independent final-head review and required CI, resolve
20
+ material findings, then complete the authorized merge. An owner or maintainer
21
+ request for a product change or fix authorizes this normal PR/merge loop;
22
+ unsolicited external contributions still require maintainer merge authority.
23
+ No CLA, ticket requirement, custom commit format or additional approval committee.
22
24
 
23
25
  You are responsible for understanding submitted code, including AI-generated
24
26
  code, and having the right to contribute it under this repository's license.
@@ -79,10 +81,10 @@ do not disguise them as supported architecture or start a duplicate repair.
79
81
  create one worktree/PR per repository and link dependencies and merge order.
80
82
  Branch from an unmerged feature only when the dependency is intentional and
81
83
  documented; do not quietly include it in an unrelated PR.
82
- - Push the task branch and open a draft PR as soon as the first coherent change
83
- is reviewable, before reporting implementation complete. Do not leave completed
84
- work only in a local branch. Continue through the release handoff below;
85
- implementation completion alone does not authorize publication.
84
+ - Push the task branch and open a PR when the coherent change is reviewable. Use
85
+ draft status only while implementation or a named gate remains incomplete; do
86
+ not leave completed mergeable work in a draft or only on a local branch.
87
+ Publication remains a separate authority boundary.
86
88
  - Every handoff names the repository, worktree, branch, exact commit, PR URL,
87
89
  checks, independent-review status and remaining QA with its next action. If
88
90
  pushing or PR creation is blocked, report the blocker and preserved local
@@ -98,9 +100,10 @@ do not disguise them as supported architecture or start a duplicate repair.
98
100
  - New substantive commits or conflict resolutions invalidate the affected review
99
101
  and test evidence. Refresh against current main, review the resulting diff and
100
102
  run the applicable checks. Do not bypass protected-branch requirements.
101
- - Merge only after review, required CI, applicable QA and maintainer authorization.
102
- Publish only under the separate release process; commits and merges are not
103
- releases. Record which source commit and artifact hash were tested.
103
+ - Merge only after review, required CI and applicable QA under the authority in
104
+ the contribution checklist above. Publish only under the separate release
105
+ process; commits and merges are not releases. Record which source commit and
106
+ artifact hash were tested.
104
107
  - Keep the worktree while its PR or QA is open. After merge or explicit abandonment,
105
108
  inspect it for uncommitted/untracked files and local-only commits. Remove only
106
109
  the clean task worktree after valuable work is preserved; never force cleanup.
@@ -124,22 +127,22 @@ and prepare the release. Reuse valid final-commit evidence; repeat checks when
124
127
  changes or failures invalidate it. Do not leave a ready feature silently in draft
125
128
  or ask the maintainer to run commands, coordinate reviewers, or operate CI.
126
129
 
127
- The maintainer's request is authorization for the requested work and its normal
128
- implementation steps. Do not ask them to approve the same work again. Once the
129
- applicable gates pass, execute the requested merge/release and report the packages,
130
- versions, channel, verification and material limits. Batch related packages in
131
- dependency order. Prepare the reviewed commits, artifacts and checksums before
132
- shipping. A request to merge does not silently expand to publication or changing
133
- a private package's visibility; a request to release already authorizes release.
134
- If shipping was not requested, report readiness and the next step without treating
135
- every completed feature as permission to publish.
136
-
137
- Complete the authorized merge, publication and rollout using
138
- docs/releasing.md, then verify registry metadata, downloaded artifact and the
139
- installed runtime. Report the outcome. Escalate only a product decision, missing
140
- credential/2FA, failed gate that cannot be repaired in scope, or material scope
141
- change. Human attention belongs on product intent;
142
- the agent operates the technical workflow.
130
+ The owner or maintainer request authorizes the requested work and its normal
131
+ PR/merge steps; do not ask them to approve the same work again. Release remains
132
+ separate authority: a request to merge does not authorize publication or changing
133
+ a private package's visibility, while a request to release authorizes the merge
134
+ and publication needed for that release. Prepare and verify the reviewed commits,
135
+ artifacts and checksums before shipping. If release was not requested, stop after
136
+ the merged contribution loop and report any distinct deployment or publication
137
+ step without performing it.
138
+
139
+ When release is authorized, complete publication using docs/releasing.md, then
140
+ verify registry metadata, the downloaded artifact and any explicitly requested
141
+ installed-runtime QA. Deployment is separate and requires a direct rollout
142
+ request or the target's saved update policy. Report the outcome. Escalate only a
143
+ product decision, missing credential/2FA, failed gate that cannot be repaired in
144
+ scope, or material scope change. Human attention belongs on product intent; the
145
+ agent operates the technical workflow.
143
146
 
144
147
  Example, substituting a unique task name and an absolute external directory:
145
148
 
package/Dockerfile CHANGED
@@ -9,6 +9,10 @@ COPY . .
9
9
  FROM dependencies AS test
10
10
  RUN pnpm verify
11
11
  FROM dependencies AS runtime
12
+ # Keep this pin aligned with TASK_CODEX_VERSION. Isolated agents run this CLI
13
+ # in the relay; do not bind-mount the operator's ~/.codex.
14
+ ARG CODEX_CLI_VERSION=0.153.4
15
+ RUN npm install -g @openai/codex@${CODEX_CLI_VERSION} && command -v codex
12
16
  RUN chmod +x docker/entrypoint.sh bin/ezenciel-agents* && mkdir -p /state/control /state/home /workspace && chown node:node /state/control /state/home /workspace
13
17
  RUN node -e 'for (const [name, target] of Object.entries(require("./package.json").bin)) require("node:fs").symlinkSync("/app/" + target, "/usr/local/bin/" + name)'
14
18
  ENV HOME=/state/home EZ_AGENT_WORKSPACE=/workspace EZ_CONTROL_DIR=/state/control EZ_EXECUTOR_CLI=grok PATH=/app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
package/README.md CHANGED
@@ -148,15 +148,19 @@ the user's request and preserve its scope.
148
148
 
149
149
  ## Package and runtime
150
150
 
151
- **Beta 0.1.0-beta.12 — for testing on a trusted machine.** Offline and Docker
152
- fixtures are verified. Live account onboarding and reboot acceptance for this
153
- release are deferred; this is not a production-readiness claim. The source branch
154
- may contain changes newer than the published beta; inspect the supplied artifact's
155
- documentation when installing a pinned release.
156
-
157
- The npm package is `@jc_stack/ez-agents` (channel `beta`). Download a pinned
158
- release with `npm pack @jc_stack/ez-agents@0.1.0-beta.12`, or get the tarball and SHA256SUMS from
159
- [GitHub prereleases](https://github.com/jdorado/ez-agents/releases/tag/v0.1.0-beta.12).
151
+ **Published beta releases are for testing on a trusted machine.** Offline and
152
+ Docker fixtures are verified for the selected release. Live account onboarding
153
+ and reboot acceptance remain beta gates; this is not a production-readiness
154
+ claim. The source branch may contain changes newer than the published beta, so
155
+ inspect the supplied artifact's documentation when installing a pinned release.
156
+
157
+ The npm package is `@jc_stack/ez-agents`. Approved beta publications use npm's
158
+ `latest` distribution tag while their versions and GitHub releases remain
159
+ prereleases. For a reproducible download, choose an exact version from
160
+ [GitHub prereleases](https://github.com/jdorado/ez-agents/releases) and substitute
161
+ it for `VERSION` in `npm pack @jc_stack/ez-agents@VERSION`.
162
+ You can also download the release's candidate tarball and verify its SHA-256
163
+ against the attached release receipt or release notes.
160
164
  GitHub remains under `jdorado`; npm uses `jc_stack`. Do not install the unrelated
161
165
  unscoped `ez-whatsapp` package.
162
166
 
@@ -242,28 +246,32 @@ credentials, startup, and verified pairing; never present this as human homework
242
246
  | `ezenciel-agents-install build` | Build once per artifact, with a private log and duplicate-build detection |
243
247
  | `ezenciel-agents-install status --deployment <path>` | Distinguish runtime/configuration, pairing and Telegram reply evidence |
244
248
  | `ezenciel-agents-create --register-cli <current-cli>` | Record the CLI that installs the initial package |
245
- | `ezenciel-agents-create --name <name> --purpose <purpose>` | Create an agent inheriting the installation CLI; token via stdin |
249
+ | `ezenciel-agents-create --name <name> --purpose <agent-brief>` | Create an agent from the installing LLM's compact brief; packaged Codex defaults isolated, other CLIs stay host-capable; token via stdin |
250
+ | `ezenciel-agents-create --name <name> --purpose <agent-brief> --isolation host-capable` | Explicitly reuse the host CLI and installer UID |
246
251
  | `ezenciel-agents-host` | Invoke the existing shared host CLI for this deployment |
247
- | `ezenciel-agents-setup configure <executor>` | Private configuration and missing starter files; preserves personal files |
248
- | `ezenciel-agents-setup configure <executor> --token-stdin` | Same, with the BotFather token supplied privately through stdin |
252
+ | `ezenciel-agents-setup configure <executor> --purpose-file <path>` | Private configuration and missing starter files; fresh workspaces require installer-scoped purpose; preserves personal files |
253
+ | `ezenciel-agents-setup configure <executor> --purpose-file <path> --token-stdin` | Same, with the BotFather token supplied privately through stdin |
249
254
  | `ezenciel-agents-setup service` | Start only the Docker relay bound by `docker.env` in the current directory; register the [host service](docs/host-service.md) separately |
250
255
  | `ezenciel-agents-owner status` | Inspect the owner and pending pairing requests |
251
256
  | `ezenciel-agents-owner approve <telegram-user-id>` | Approve the verified owner; never an arbitrary first sender |
252
257
  | `ezenciel-agents-setup status` | Inspect installed executor choices |
253
- | `ezenciel-agents-setup init` | Seed only missing workspace guidance |
258
+ | `ezenciel-agents-setup init --purpose-file <path>` | Seed missing workspace guidance; purpose is optional only when AGENTS.md already exists |
254
259
 
255
260
  Run runtime tools through `ezenciel-agents-docker run --rm relay <command>`
256
261
  as shown in the Docker guide. Keep the Docker engine available; Compose owns
257
- restart and shutdown. The installing agent registers the small host CLI transport with the native service manager.
262
+ restart and shutdown. Host-capable agents register the small host CLI transport with the native service manager. Isolated agents do not.
258
263
 
259
264
  ## Runtime and development reference
260
265
 
261
266
  The selected host CLI runs in this agent's persistent workspace and owns its
262
267
  Markdown/work files. New conversations and AI changes preserve those files.
263
- `AGENTS.md`, `SOUL.md`, and `USER.md` are seeded; `MEMORY.md` is
264
- optional. Installed plugin snippets and skill paths come from `ez tools list --details`; no tool inventory file needs maintenance. Received files go in `inbox/`, tasks and deliverables in `work/`.
265
- Credentials and control state stay outside the mind. File separation is not
266
- OS isolation against a process running as the same user.
268
+ One compact, purpose-scoped `AGENTS.md` is seeded; `MEMORY.md` is optional.
269
+ The installing LLM authors the initial brief from the owner's request and known
270
+ application context; Ez stores it without trying to infer the product itself.
271
+ Installed plugin snippets and skill paths come from `ez tools list --details`; no tool inventory file needs maintenance. Received files go in `inbox/`, tasks and deliverables in `work/`.
272
+ Credentials and control state stay outside the mind. Isolated agents use relay
273
+ mounts as the filesystem boundary. Host-capable file separation is not OS
274
+ isolation against a process running as the same user.
267
275
 
268
276
  In Telegram, **Conversations** (or `/chats`) lists this agent's saved Ez
269
277
  conversations by name. Tap a name to continue its native engine session and
@@ -8,7 +8,7 @@ The container is built locally; no bundled third-party binary image is promised.
8
8
 
9
9
  Direct runtime dependencies: grammy (MIT), tsx (MIT). Development dependencies
10
10
  include TypeScript (Apache-2.0) and Node type definitions (MIT). The runtime
11
- image installs ffmpeg and Debian packages: redistributing that image requires
12
- review of its exact package build, notices and corresponding-source obligations.
13
- Docker and the selected host AI CLI are separately installed products, not covered
14
- by this repository's MIT license.
11
+ image installs ffmpeg, Debian packages, and pinned `@openai/codex` (Apache-2.0):
12
+ redistributing that image requires review of its exact package build, notices and
13
+ corresponding-source obligations. Docker and a host-capable AI CLI remain
14
+ separately installed products, not covered by this repository's MIT license.
package/compose.yaml CHANGED
@@ -17,6 +17,7 @@ services:
17
17
  EZ_RELAY_UID: ${EZ_RELAY_UID:-1001}
18
18
  EZ_REPAIR_ENABLED: ${EZ_REPAIR_ENABLED:-true}
19
19
  EZ_AGENT_PURPOSE_FILE: /run/agent-purpose.md
20
+ EZ_ISOLATION: ${EZ_ISOLATION:-}
20
21
  EZ_EXECUTOR_TRANSPORT: ${EZ_EXECUTOR_TRANSPORT:-host}
21
22
  EZ_CHANNEL_BACKEND_URL: ${EZ_CHANNEL_BACKEND_URL:-}
22
23
  EZ_TELEGRAM_WEB_APP: ${EZ_TELEGRAM_WEB_APP:-}
@@ -50,7 +51,7 @@ services:
50
51
  retries: 3
51
52
  configs:
52
53
  agent_purpose:
53
- file: ${EZ_AGENT_PURPOSE_FILE:-./templates/agent/SOUL.md}
54
+ file: ${EZ_AGENT_PURPOSE_FILE:-./templates/agent-purpose.md}
54
55
  secrets:
55
56
  relay_env:
56
57
  file: ${EZ_RELAY_ENV_FILE:?Set EZ_RELAY_ENV_FILE to the private relay env file}
package/docker/smoke.mjs CHANGED
@@ -10,6 +10,7 @@ const volume = `${holder}-control`;
10
10
  const application = `${holder}-application`;
11
11
  const marker = 'qa-private-secret-never-in-executor';
12
12
  writeFileSync(join(dir, 'relay.env'), `TELEGRAM_BOT_TOKEN=${marker}\n`, { mode: 0o600 });
13
+ writeFileSync(join(dir, 'purpose.md'), 'Verify the packaged Ez runtime.\n', { mode: 0o644 });
13
14
  writeFileSync(join(dir, 'node'), `#!/bin/sh\nIFS= read -r value <&3\n[ "$value" = "TELEGRAM_BOT_TOKEN=${marker}" ] || exit 91\nexec /usr/local/bin/node "$@"\n`, { mode: 0o555 });
14
15
  const run = (args) => spawnSync('docker', args, { encoding: 'utf8', timeout: 60000 });
15
16
  try {
@@ -17,17 +18,19 @@ try {
17
18
  '--mount',`type=bind,src=${join(dir,'node')},dst=/qa/node,readonly`,'-e','PATH=/qa:/usr/local/bin:/usr/bin:/bin',image,'application','--help']);
18
19
  assert.equal(inherited.status,0,inherited.stderr);
19
20
  const nonroot = ['--user','20000:20000','--read-only','--cap-drop','ALL','--security-opt','no-new-privileges',
21
+ '--mount',`type=bind,src=${join(dir,'purpose.md')},dst=/run/agent-purpose.md,readonly`,'-e','EZ_AGENT_PURPOSE_FILE=/run/agent-purpose.md',
20
22
  '--tmpfs','/tmp:mode=1777','--tmpfs','/state/control:uid=20000,gid=20000,mode=700',
21
23
  '--tmpfs','/state/home:uid=20000,gid=20000,mode=700','--tmpfs','/workspace:uid=20000,gid=20000,mode=700',
22
- '-e','EZ_TELEGRAM_ENABLED=false','-e','EZ_APPLICATION_PORT=8110','-e','EZ_EXECUTOR_TRANSPORT=local','-e','EZ_EXECUTOR_CLI=codex'];
24
+ '-e','EZ_TELEGRAM_ENABLED=false','-e','EZ_APPLICATION_PORT=8110','-e','EZ_ISOLATION=isolated','-e','EZ_EXECUTOR_TRANSPORT=local','-e','EZ_EXECUTOR_CLI=codex'];
23
25
  const help = run(['run','--rm',...nonroot,image,'application','--help']);
24
26
  assert.equal(help.status,0,help.stderr);
25
27
  assert.match(help.stdout,/ezenciel-agents-application/);
26
28
  const login = run(['run','--rm',...nonroot,'--entrypoint','/bin/bash',image,'-lc',
27
- 'command -v ezenciel-agents-application && command -v ezenciel-agents-message && ezenciel-agents-message --help']);
29
+ 'command -v ezenciel-agents-application && command -v ezenciel-agents-message && command -v codex && ezenciel-agents-message --help && codex --version']);
28
30
  assert.equal(login.status,0,login.stderr);
29
31
  assert.match(login.stdout,/\/usr\/local\/bin\/ezenciel-agents-application/);
30
32
  assert.match(login.stdout,/\/usr\/local\/bin\/ezenciel-agents-message/);
33
+ assert.match(login.stdout,/codex-cli 0\.153\.4/);
31
34
  const started = run(['run','-d','--name',application,...nonroot,image,'start']);
32
35
  assert.equal(started.status,0,started.stderr);
33
36
  let ready = false;
@@ -82,5 +85,5 @@ try {
82
85
  assert.equal(run(['kill',holder]).status,0);
83
86
  const recovered=run(['run','--rm','-v',`${volume}:/state/control`,image,'exec','node','-e','process.exit(0)']);
84
87
  assert.equal(recovered.status,0,recovered.stderr);
85
- console.log('Docker smoke passed: direct non-root application help/start/health/stop, inherited private descriptor, non-root executor, private secret isolation, literal argv, exit code, no Docker socket, duplicate writer rejection and crash lock release.');
88
+ console.log('Docker smoke passed: direct non-root application help/start/health/stop, inherited private descriptor, non-root executor, isolated Codex on PATH, private secret isolation, literal argv, exit code, no Docker socket, duplicate writer rejection and crash lock release.');
86
89
  } finally { run(['rm','-f',holder,application]); run(['volume','rm',volume]); rmSync(dir, {recursive:true, force:true}); }
@@ -6,6 +6,10 @@ outbox. The application supplies domain tools and keeps its UI/content API; it
6
6
  can remove its own model runner, continuation loop and engine authentication.
7
7
  This is separate from the older outbound channel-backend integration.
8
8
 
9
+ Run admission, status and inbox receipts include the immutable `preset` captured
10
+ for that run so an application can label delivered messages with the engine choice
11
+ that actually produced them.
12
+
9
13
  ## Shared runtime controls
10
14
 
11
15
  Register with `--share-owner` (the existing `--share-telegram` spelling is an
@@ -267,6 +271,8 @@ separate CLI histories. A retry cannot change an admitted turn's AI choice.
267
271
 
268
272
  Register owner-chat channels with `--share-owner` and submit `followOwner:true`.
269
273
  Web, phone and Telegram then use the same selected native conversation and AI.
274
+ A later same-client provider or model change starts a new native conversation for the next
275
+ admitted turn; already-queued work keeps its original engine.
270
276
  This works without Telegram and requires no transcript replay or new runner.
271
277
  Scoped application conversations remain separate when the flag is omitted.
272
278
  Owner identity does not automatically merge histories or grant learner access.
@@ -8,7 +8,9 @@ At intake each journal entry receives an immutable preset and conversation ID.
8
8
  Batches cannot cross that boundary. The run copies that choice and invokes the
9
9
  native adapter with its exact model/effort. A later menu change cannot reroute it.
10
10
  Old conversation metadata is retained only so accepted work can finish; selecting
11
- a different CLI never restores that CLI's old history.
11
+ a different CLI never restores that CLI's old history. Selecting a different provider
12
+ or model on the same client does not resume the previous selection's native thread; the next
13
+ admitted turn starts a new native conversation.
12
14
 
13
15
  Grok/Claude accept caller-selected native UUIDs. Codex/OpenCode generate IDs, so
14
16
  the adapter reads only their typed JSONL session metadata; stdout never becomes a
@@ -1,9 +1,9 @@
1
1
  # Docker runtime and shared host CLI
2
2
 
3
- Docker Compose owns each relay and executable plugin. The CLI the user installs
4
- Ez from stays on the host, with its existing authentication. All agents in that
5
- installation use that CLI; there is no second CLI installation or agent-specific
6
- CLI login. The relay image contains Node, relay dependencies and ffmpeg.
3
+ Docker Compose owns each relay and executable plugin. Isolated agents run the
4
+ native CLI from the relay image (pinned Codex). Host-capable agents reuse the
5
+ CLI the user installed Ez from, with its existing authentication. The relay
6
+ image contains Node, relay dependencies, ffmpeg and that pinned Codex CLI.
7
7
  The runtime image exposes Ez commands through the wrappers in `/app/bin`, including
8
8
  `ezenciel-agents-application`; application images do not need to link Ez themselves.
9
9
  The image also installs package-manifest commands in `/usr/local/bin`, so native
@@ -13,26 +13,33 @@ belongs to Ez packaging, not an application image or an engine-specific prompt.
13
13
  ## Agent binding
14
14
 
15
15
  Use [agent-led setup](setup.md). `ezenciel-agents-create` accepts a name, purpose,
16
+ optional `--isolation isolated|host-capable` (default `isolated`),
16
17
  a bot token through stdin; the CLI defaults to the one recorded at package
17
18
  installation by `--register-cli <current-cli>`. It creates a private deployment
18
19
  with a unique Compose project, mind, control directory and secret file. Names
19
20
  cannot overwrite existing agents. `installation.json` keeps subsequent agents
20
21
  on the same CLI. Each agent gets its own purpose and native conversation IDs.
22
+ `isolated` sets `EZ_EXECUTOR_TRANSPORT=local` and runs the native CLI in the
23
+ relay. `host-capable` sets `host` and reuses the installer UID; unlabeled
24
+ existing host transports stay host-capable so they are not flipped.
21
25
 
22
26
  `docker.env` contains explicit absolute paths, never token values:
23
27
 
24
28
  ```dotenv
25
29
  COMPOSE_PROJECT_NAME=ez-agent-family
26
30
  EZ_EXECUTOR_CLI=grok
31
+ EZ_ISOLATION=isolated
32
+ EZ_EXECUTOR_TRANSPORT=local
27
33
  EZ_AGENT_WORKSPACE=/absolute/private/agents/family/mind
28
34
  EZ_CONTROL_DIR=/absolute/private/agents/family/control
29
35
  EZ_RELAY_ENV_FILE=/absolute/private/agents/family/relay.env
30
36
  EZ_AGENT_PURPOSE_FILE=/absolute/private/agents/family/purpose.md
31
37
  ```
32
38
 
33
- The installing agent runs `ezenciel-agents-host` under the host's service manager
39
+ Host-capable agents run `ezenciel-agents-host` under the host's service manager
34
40
  with `EZ_DEPLOYMENT_DIR` bound to that deployment. This small transport invokes
35
- the existing CLI; it is not a second relay or model loop. It reads requests from
41
+ the existing CLI; it is not a second relay or model loop. Isolated agents do not
42
+ start it. It reads requests from
36
43
  the agent's control directory, fixes cwd/control/tool paths from its installation
37
44
  binding, strips environment secrets, forwards native output and exit status, and
38
45
  propagates cancellation. Requests cannot select another executable. No network
@@ -41,9 +48,10 @@ is required. Host Node 22+ and the package dependencies run this transport.
41
48
 
42
49
  Mount mind and control at identical absolute paths in Docker and on the host,
43
50
  so incoming files, message attachments and CLI outputs need no path translation.
44
- Only that agent's directories are mounted into its relay. The host CLI reuses the existing login. Codex receives a private per-agent
51
+ Only that agent's directories are mounted into its relay. Host-capable CLI reuses the existing login. Codex receives a private per-agent
45
52
  state home with linked authentication; global memory/configuration is excluded.
46
- This is not an OS security boundary between agents running as the same user.
53
+ Host-capable is not an OS security boundary between agents running as the same user.
54
+ Isolated agents use the relay mounts; sibling host paths are not present.
47
55
  Plugin setup also binds `toolsHome` in the host configuration. Codex receives
48
56
  write access to that registry and network access for the Docker client; the host
49
57
  rejects registries belonging to a different workspace. Restart the host worker
@@ -146,13 +154,22 @@ default. Existing queued jobs retain their captured execution choice.
146
154
 
147
155
  ## Codex context isolation
148
156
 
149
- The host Codex binary and existing login are reused, but each agent has its own
150
- `control/cli/codex` state directory. Only authentication is linked to the host
157
+ Isolated agents run the image Codex with `EZ_CODEX_SANDBOX=external`. Auth is a
158
+ real file at that agent's `control/cli/codex/auth.json`. Do not bind-mount the
159
+ operator `~/.codex` or `$HOME` into an isolated relay. Host-capable agents reuse
160
+ the host Codex binary and login, with a
161
+ `control/cli/codex` state directory. The native CLI is resolved from the host
162
+ PATH, not the package `binDir`, so a shared runtime or one agent's wrapper cannot
163
+ retarget another agent's `CODEX_HOME`. Only authentication is linked to the host
151
164
  login; global configuration, sessions and memories are not imported. Global
152
165
  memory and host skill discovery are disabled for relay jobs. A conversation
153
166
  that already received unrelated global context must be replaced with a fresh
154
167
  native conversation; disabling injection does not remove prior turn content.
155
168
  This prevents automatic context sharing, not adversarial access by the host user.
169
+ On macOS host jobs, Seatbelt also denies sibling directories of the bound
170
+ workspace (a tenant farm), while allowing explicitly shared and additional
171
+ workspace grants and leaving `$HOME` readable for the host CLI.
172
+ Codex then uses `danger-full-access` so it does not apply a nested Seatbelt.
156
173
 
157
174
  ## Chat latency
158
175
 
@@ -1,7 +1,8 @@
1
1
  # Host transport startup
2
2
 
3
- Register one service per deployment after initializing its tools. Substitute the
4
- exact absolute paths below. Use the existing authenticated host user and include
3
+ Register one service per **host-capable** deployment after initializing its tools.
4
+ Isolated agents run the native CLI in the relay and must not start this service.
5
+ Substitute the exact absolute paths below. Use the existing authenticated host user and include
5
6
  its Node 22+, pnpm (or Corepack), Docker and CLI directories in PATH. Resolve the
6
7
  actual installed launchers first; shell aliases and interactive shell startup
7
8
  files are not available to services. No bot token goes in these service files.
@@ -21,6 +22,7 @@ Description=Ez family host CLI transport
21
22
  Type=simple
22
23
  Environment=EZ_DEPLOYMENT_DIR=/absolute/private/agents/family
23
24
  Environment=PATH=/absolute/node/bin:/absolute/package-manager/bin:/absolute/cli/bin:/usr/local/bin:/usr/bin:/bin
25
+ EnvironmentFile=-%h/.config/ez/family-provider.env
24
26
  ExecStart=/absolute/ez-package/package/bin/ezenciel-agents-host
25
27
  Restart=on-failure
26
28
  RestartSec=5
@@ -29,6 +31,13 @@ RestartSec=5
29
31
  WantedBy=default.target
30
32
  ```
31
33
 
34
+ When the installation declares a custom Codex provider, create that private
35
+ environment file with mode `0600` and only the referenced key, for example
36
+ `OPENROUTER_API_KEY=...`. Provider and model selection do not belong in the
37
+ environment; they remain agent runtime state. The supervisor forwards only keys
38
+ explicitly named by the agent's provider binding, and the Codex subprocess still
39
+ receives none of the relay or plugin secrets.
40
+
32
41
  ```sh
33
42
  systemctl --user daemon-reload
34
43
  systemctl --user enable --now ez-family.service
@@ -81,10 +90,12 @@ main/plugin replacement. See [upgrade setup, tools and recovery](upgrades.md). E
81
90
 
82
91
  ## Shared application workspace
83
92
 
84
- Codex tasks inherit the agent's `control/cli/codex/auth.json` binding. Its default
85
- is a link to the host user's existing login. An operator may provision a private
86
- agent credential there during migration; new task homes link to that binding,
87
- preserving it rather than reverting to a different or expired host login.
93
+ Codex tasks inherit the agent's `control/cli/codex/auth.json` binding. Host-capable
94
+ default is a link to the host user's existing login. Isolated agents use a real
95
+ file in that directory and the image Codex; they do not link `~/.codex`. An
96
+ operator may provision a private agent credential there during migration; new
97
+ task homes link to that binding, preserving it rather than reverting to a
98
+ different or expired host login.
88
99
 
89
100
  An agent binding may set `sharedWorkspace` to an absolute canonical application
90
101
  repository. The host resolves its canonical path, adds that directory to Codex
@@ -28,7 +28,10 @@ Before the first PR, provide:
28
28
 
29
29
  Copy CONTRIBUTING.md, SECURITY.md structure, PR template and docs/releasing.md
30
30
  from a released Ez repository; adapt commands and security facts to your plugin.
31
- Do not copy private QA, company policy, proposed features or unsupported claims.
31
+ Add a short AGENTS.md that states the plugin's ownership boundary and applies the
32
+ same compact feasibility frame and risk-based validation policy; do not copy
33
+ core-only Telegram, state or dependency invariants. Do not copy private QA,
34
+ company policy, proposed features or unsupported claims.
32
35
  Public instructions must work without the maintainer's parent workspace. Use
33
36
  reviewed local source and its inspected content hash; registry installation is
34
37
  inert. Installation is complete only after account onboarding and verified use.
package/docs/plugins.md CHANGED
@@ -291,6 +291,21 @@ already exist on the host. Keep indexes and writable metadata in the plugin's
291
291
  normal volume. This uses Docker bind mounts; it copies no source bytes and
292
292
  starts no provider sync. Read-only is the default.
293
293
 
294
+ For a relay-bound agent, the source must be inside its workspace/shared workspace
295
+ or under a plugin-specific root granted in the agent's external host configuration:
296
+
297
+ ```json
298
+ {
299
+ "pluginFolderRoots": {
300
+ "library": ["/absolute/existing/notes"]
301
+ }
302
+ }
303
+ ```
304
+
305
+ The agent cannot add this grant through its registry. A missing grant fails before
306
+ Compose or registry state changes. Standalone registries remain operator-owned and
307
+ do not use a relay host allowlist.
308
+
294
309
  Stop the plugin before changing a binding:
295
310
 
296
311
  ```sh
package/docs/repair.md CHANGED
@@ -30,7 +30,7 @@ True makes the capability available; it does not itself grant a repair mandate.
30
30
  The setting does not change filesystem/GitHub permissions and does
31
31
  not cancel an already running task. Explicitly stop active repair work when needed.
32
32
  An owner can also disable repairs globally or for a repository in the agent's
33
- saved USER.md preferences; carry those restrictions into background task context.
33
+ saved AGENTS.md preferences; carry those restrictions into background task context.
34
34
 
35
35
  ## Setup boundary
36
36