@jc_stack/ez-agents 0.1.0-beta.12

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 (147) hide show
  1. package/.dockerignore +24 -0
  2. package/.env.example +26 -0
  3. package/AGENTS.md +84 -0
  4. package/CHANGELOG.md +39 -0
  5. package/CONTRIBUTING.md +73 -0
  6. package/Dockerfile +16 -0
  7. package/LICENSE +21 -0
  8. package/README.md +134 -0
  9. package/SECURITY.md +26 -0
  10. package/THIRD_PARTY_NOTICES.md +14 -0
  11. package/bin/ezenciel-agents +2 -0
  12. package/bin/ezenciel-agents-ai +2 -0
  13. package/bin/ezenciel-agents-ai.mjs +5 -0
  14. package/bin/ezenciel-agents-approval +2 -0
  15. package/bin/ezenciel-agents-approval.mjs +16 -0
  16. package/bin/ezenciel-agents-create +12 -0
  17. package/bin/ezenciel-agents-docker +6 -0
  18. package/bin/ezenciel-agents-host +5 -0
  19. package/bin/ezenciel-agents-install +2 -0
  20. package/bin/ezenciel-agents-message +2 -0
  21. package/bin/ezenciel-agents-message.mjs +16 -0
  22. package/bin/ezenciel-agents-owner +2 -0
  23. package/bin/ezenciel-agents-owner.mjs +18 -0
  24. package/bin/ezenciel-agents-react +2 -0
  25. package/bin/ezenciel-agents-react.mjs +16 -0
  26. package/bin/ezenciel-agents-setup.mjs +18 -0
  27. package/bin/ezenciel-agents-source +2 -0
  28. package/bin/ezenciel-agents-source.mjs +16 -0
  29. package/bin/ezenciel-agents-tools.mjs +3 -0
  30. package/bin/ezenciel-agents.mjs +37 -0
  31. package/compose.whatsapp.yaml +12 -0
  32. package/compose.yaml +40 -0
  33. package/default-plugins.json +1 -0
  34. package/docker/entrypoint.sh +15 -0
  35. package/docker/healthcheck.mjs +8 -0
  36. package/docker/plugin-smoke.mjs +48 -0
  37. package/docker/pnpm-lock.yaml +415 -0
  38. package/docker/recovery.ts +11 -0
  39. package/docker/run.ts +52 -0
  40. package/docker/smoke.mjs +47 -0
  41. package/docker/status-smoke.mjs +30 -0
  42. package/docker/upgrade-smoke.mjs +58 -0
  43. package/docs/architecture/ai-selection.md +37 -0
  44. package/docs/architecture/authority-boundaries.md +14 -0
  45. package/docs/architecture/event-sources.md +34 -0
  46. package/docs/architecture/telegram-intake.md +29 -0
  47. package/docs/development-and-testing.md +18 -0
  48. package/docs/docker-runtime.md +118 -0
  49. package/docs/host-service.md +80 -0
  50. package/docs/plugin-contributions.md +34 -0
  51. package/docs/plugins.md +181 -0
  52. package/docs/releasing.md +71 -0
  53. package/docs/setup.md +234 -0
  54. package/docs/upgrades.md +193 -0
  55. package/package.json +106 -0
  56. package/scripts/assert-local-registry.mjs +22 -0
  57. package/scripts/release-check.mjs +14 -0
  58. package/scripts/smoke.ts +102 -0
  59. package/src/agent-install.ts +96 -0
  60. package/src/ai-cli.ts +22 -0
  61. package/src/ai.ts +88 -0
  62. package/src/approval-cli.ts +59 -0
  63. package/src/approval.ts +119 -0
  64. package/src/audio.ts +184 -0
  65. package/src/client-defaults.ts +101 -0
  66. package/src/config.ts +48 -0
  67. package/src/control-state.ts +350 -0
  68. package/src/desktop-bridge.ts +284 -0
  69. package/src/event-sources.ts +112 -0
  70. package/src/executor.ts +335 -0
  71. package/src/files.ts +75 -0
  72. package/src/format.ts +57 -0
  73. package/src/host-executor-client.ts +46 -0
  74. package/src/host-executor-protocol.ts +2 -0
  75. package/src/host-executor.ts +129 -0
  76. package/src/identity.ts +17 -0
  77. package/src/inbox.ts +171 -0
  78. package/src/index.ts +812 -0
  79. package/src/install-config.ts +56 -0
  80. package/src/install-tools.mjs +98 -0
  81. package/src/menu.ts +123 -0
  82. package/src/message-send.ts +57 -0
  83. package/src/message.ts +68 -0
  84. package/src/owner-args.ts +4 -0
  85. package/src/owner.ts +30 -0
  86. package/src/plugins/manager.mjs +272 -0
  87. package/src/react.ts +33 -0
  88. package/src/reaction.ts +32 -0
  89. package/src/read-request.ts +72 -0
  90. package/src/reply.ts +13 -0
  91. package/src/runs.ts +445 -0
  92. package/src/service.ts +28 -0
  93. package/src/setup.ts +180 -0
  94. package/src/software-status.ts +23 -0
  95. package/src/source-cli.ts +18 -0
  96. package/src/update-attention.ts +18 -0
  97. package/src/updates/artifact.mjs +83 -0
  98. package/src/updates/binding.mjs +27 -0
  99. package/src/updates/control.mjs +131 -0
  100. package/src/updates/launch.mjs +13 -0
  101. package/src/updates/runtime.mjs +140 -0
  102. package/src/updates/status.mjs +49 -0
  103. package/src/updates/supervisor.mjs +102 -0
  104. package/src/version.ts +4 -0
  105. package/src/workspace.ts +32 -0
  106. package/templates/agent/AGENTS.md +49 -0
  107. package/templates/agent/SOUL.md +11 -0
  108. package/templates/agent/TOOLS.md +46 -0
  109. package/templates/agent/USER.md +5 -0
  110. package/templates/updates.md +45 -0
  111. package/test/agent-install.test.ts +48 -0
  112. package/test/ai-cli.test.ts +28 -0
  113. package/test/ai.test.ts +105 -0
  114. package/test/approval.test.ts +40 -0
  115. package/test/audio.test.ts +77 -0
  116. package/test/client-defaults.test.ts +64 -0
  117. package/test/codex-context.test.ts +33 -0
  118. package/test/config.test.ts +32 -0
  119. package/test/control-state.test.ts +58 -0
  120. package/test/desktop-bridge.test.ts +159 -0
  121. package/test/docker-runtime.test.ts +23 -0
  122. package/test/event-sources.test.ts +113 -0
  123. package/test/executor.test.ts +135 -0
  124. package/test/files.test.ts +50 -0
  125. package/test/format.test.ts +41 -0
  126. package/test/host-executor.test.ts +149 -0
  127. package/test/inbox-burst.test.ts +66 -0
  128. package/test/inbox.test.ts +102 -0
  129. package/test/install-config.test.ts +75 -0
  130. package/test/install-tools.test.mjs +59 -0
  131. package/test/intake-relay.test.ts +337 -0
  132. package/test/owner-help.test.mjs +10 -0
  133. package/test/plugin-manager.test.mjs +157 -0
  134. package/test/publish-guard.test.ts +21 -0
  135. package/test/reaction.test.ts +122 -0
  136. package/test/read-request.test.ts +134 -0
  137. package/test/relay.test.ts +254 -0
  138. package/test/release-entrypoints.test.mjs +26 -0
  139. package/test/runs.test.ts +116 -0
  140. package/test/security.test.ts +85 -0
  141. package/test/setup.test.ts +68 -0
  142. package/test/software-status.test.ts +31 -0
  143. package/test/update-attention.test.ts +21 -0
  144. package/test/updates.test.mjs +282 -0
  145. package/test/upgrade-pause.test.ts +70 -0
  146. package/test/workspace.test.ts +80 -0
  147. package/tsconfig.json +19 -0
package/.dockerignore ADDED
@@ -0,0 +1,24 @@
1
+ *
2
+ !package.json
3
+ !pnpm-lock.yaml
4
+ !src/**
5
+ !bin/**
6
+ !docker/**
7
+ !templates/**
8
+ !skills/**
9
+ !test/**
10
+ !tsconfig.json
11
+ !scripts/smoke.ts
12
+ !README.md
13
+ !AGENTS.md
14
+ !ez-plugin.json
15
+ !LICENSE
16
+ !SECURITY.md
17
+ !docs/**
18
+ !scripts/assert-local-registry.mjs
19
+
20
+ !THIRD_PARTY_NOTICES.md
21
+ !CONTRIBUTING.md
22
+ !CHANGELOG.md
23
+ !scripts/release-check.mjs
24
+ !default-plugins.json
package/.env.example ADDED
@@ -0,0 +1,26 @@
1
+ # The only credential required for the first smoke test. Keep the real value in .env.
2
+ TELEGRAM_BOT_TOKEN=
3
+
4
+ # Persistent mind, seeded by setup init or first relay start; existing files survive.
5
+ EZ_AGENT_WORKSPACE=./agent
6
+
7
+ # Local, protected authority state. Keep it outside the agent Markdown workspace.
8
+ # EZ_CONTROL_DIR=/absolute/path/outside/agent-workspace
9
+
10
+ # Owner-pairing request lifetime. Default is 900 seconds.
11
+ # EZ_PAIRING_TTL_SECONDS=900
12
+
13
+ # Optional. Default is 300.
14
+ # EZ_EXECUTOR_TIMEOUT_SECONDS=300
15
+
16
+ # CLI Executor adapter. Supported: agy (default), claude, grok, opencode
17
+ # Switch easily with: pnpm run setup <executor-name>
18
+ EZ_EXECUTOR_CLI=agy
19
+
20
+ # Optional: Audio transcription (STT) and voice synthesis (TTS)
21
+ # GEMINI_API_KEY=
22
+ # OPENAI_API_KEY=
23
+
24
+ # Optional: OpenRouter API key for OpenCode CLI (Nemotron 3.5 Lightning)
25
+ # OPENROUTER_API_KEY=
26
+ # OPENCODE_MODEL=openrouter/nvidia/nemotron-3.5-lightning
package/AGENTS.md ADDED
@@ -0,0 +1,84 @@
1
+ # ezenciel-agents — Coding Standards & Architectural Invariants
2
+
3
+ Installed runtime operation uses Docker Compose. Read
4
+ [Docker setup, state and QA](docs/docker-runtime.md). Docker owns relay/plugin services; the existing host CLI and login are shared
5
+ through one generic transport, with separate agent workspaces and sessions.
6
+
7
+ ## Installing this package
8
+
9
+ When the user asks to set up or install this repository or package, follow
10
+ [the agent-led setup instructions](docs/setup.md). Handle the technical work
11
+ and ask only for missing information. The user does not need to name or read
12
+ the documentation. Install means onboarding through a real Telegram reply,
13
+ unless the user explicitly asks for package files only. Do not finish with
14
+ developer commands or an uninitialized bot. At a required user step, give one
15
+ concrete action with its exact link; continue after their answer. The coding
16
+ standards below apply when developing the relay.
17
+
18
+ Main-first onboarding is mandatory: initialize an empty registry, complete owner
19
+ pairing and verify an agent-produced Telegram reply before plugin preparation or
20
+ QR onboarding. A supplied plugin archive is deferred input. After main setup,
21
+ plugin requests and onboarding run through the installed agent's Telegram chat,
22
+ not the original installer CLI. Read docs/setup.md for the handoff and diagnostics.
23
+
24
+ This package will be published as an open-source, lightweight Telegram-to-CLI relay. All code added to this package must adhere to these strict invariants to prevent bloat, credential leaks, and rewrites.
25
+
26
+ ---
27
+
28
+ ## 1. Radical Lean Footprint (Zero DB Dependencies)
29
+ - **Runtime dependencies:** Restricted to `grammy` and TypeScript tooling (`tsx`). Do not add databases (no MongoDB, SQLite, Redis, or ORMs) or web frameworks (no Express/Fastify).
30
+ - **Native Node 22+ APIs only:** Use `node:fs/promises`, `node:child_process`, `node:crypto`, `node:path`, and native global `fetch`, `FormData`, and `Blob`.
31
+ - Keep `node_modules` minimal and installable in seconds.
32
+
33
+ ## 2. Zero Deterministic Coding Across the Board (Tools, Flows & Dialog)
34
+ - **The relay is dumb plumbing:** The relay only ingests messages, batches them into runs, spawns the CLI executor, and drains outbox items. It is strictly minimal.
35
+ - **Nothing deterministic—not just dialog:** Do not hardcode multi-step tool pipelines, fallback state machines, parameter translation shims, procedural dialog flows, or canned conversational receipts ("Started run...", "I'll message as I go...").
36
+ - **The debugging reflex trap:** When an integration, tool, or flow doesn't work as expected, developers and LLMs have an overwhelming reflex to patch it by writing deterministic procedural code (`if error X -> hardcode Y -> do Z`). **Resist this completely.**
37
+ - **The real engineering work:** Our job is solely to:
38
+ 1. Build clean, standalone tools that work reliably with clear Unix interfaces (clear args, predictable stdout/stderr, clean exit codes).
39
+ 2. Ensure tools have simple setup and are clearly explained in the agent's workspace so the **agent understands them**.
40
+ 3. Let the agent own all flow orchestration, tool chaining, decision making, and error recovery. Question every line of code—if it can be agentic, keep code out of it.
41
+
42
+ ## 3. Crash-Safe Atomic Disk State
43
+ - All persistent stores (`ControlStore`, `RunStore`, outbox queue) must be disk-backed JSON files.
44
+ - **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.
45
+ - **Path sanitization:** Every ID parameter (`runId`, `outboxId`, `pairingId`) must be validated against `/^[a-zA-Z0-9_-]+$/` before being joined into paths. Never allow directory traversal (`..`).
46
+
47
+ ## 4. Strict Subprocess Isolation & Secret Whitelisting
48
+ - **Environment leak prevention:** `TELEGRAM_BOT_TOKEN`, relay internals, and parent daemon secrets must **never** leak into the executor subprocess.
49
+ - The executor environment is built from an explicit whitelist plus bound runtime identifiers. Inspect `src/executor.ts` for the current list; never spread the daemon environment into a child.
50
+ - Executor stdout is ignored for chat delivery. To send updates, the agent must invoke the native binary: `ezenciel-agents-message`.
51
+
52
+ ## 5. Telegram Formatting & Rate Limit Safety
53
+ - **Safe HTML formatting:** Never pass raw markdown directly into Telegram with `parse_mode: 'HTML'` or `'MarkdownV2'` without escaping. Use a dedicated sanitizer (`markdownToTelegramHtml`) that escapes `<, >, &` before wrapping tags.
54
+ - **Message chunking:** Chunk all outgoing messages to $\le 4,000$ characters on paragraph (`\n\n`), line (`\n`), or word boundaries.
55
+ - **Rate limits:** Avoid spamming bubbles to respect Telegram’s 1 message/sec per-chat rate limit.
56
+
57
+ ## 6. Concurrency & Workspace Invariants
58
+ - **1 Writer Job per Workspace:** The agent's Markdown folder (`./agent/`) is its mind. Never run concurrent background processes writing to the same workspace simultaneously.
59
+ - Executor jobs queue sequentially in `RunStore`; the poller remains available for cancellation and incoming messages. There is no separate planner or automatic chat ACK lane.
60
+
61
+ ## 7. Fail-Closed Authority (Channel Access ≠ Execution)
62
+ - Incoming messages from unapproved senders must **never** spawn the executor. First DM registers an unapproved pairing request, then stops.
63
+ - Group messages or unknown DMs fail silently.
64
+ - Stopping work (`/stop`) must terminate the active worker PID immediately (`SIGTERM`, escalating to `SIGKILL` if unclosed after 3s).
65
+
66
+ ## 8. Mandatory Adversarial & Negative Tests
67
+ Every pull request modifying authority, execution, or routing must include negative tests:
68
+ 1. Unapproved Telegram ID $\rightarrow$ no process spawned.
69
+ 2. Group message $\rightarrow$ silent ignore.
70
+ 3. Executor environment check $\rightarrow$ asserts `TELEGRAM_BOT_TOKEN` is `undefined`.
71
+ 4. Corrupt JSON in store $\rightarrow$ handled gracefully without process crash.
72
+ 5. Injected path traversal in IDs $\rightarrow$ rejected.
73
+
74
+ ## Public contribution workflow
75
+
76
+ Read CONTRIBUTING.md before edits and docs/releasing.md before a release.
77
+ Maintainers and external agents use the same PR, tests and documentation standard.
78
+ Keep internal plans and private evidence outside this repository.
79
+
80
+ Before edits, follow CONTRIBUTING.md's isolated-work rules: one task per dedicated
81
+ worktree/branch/PR, starting from fetched origin/main. Do not switch or mix work in
82
+ another task's checkout. Stage only this task's changes. Keep its worktree through
83
+ review and QA; independent review and green CI precede an authorized merge.
84
+ Never treat task completion as permission to merge or publish.
package/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0-beta.12 — self-upgrade beta
4
+
5
+ - Agent-owned main/plugin upgrades with stable-default policy, queued maintenance,
6
+ pinned package-manager preflight, private backups and code/config rollback.
7
+ - Main Telegram onboarding before plugin installation, reusable build/preflight tools,
8
+ executor and private-pause fixes, and installed/running version diagnostics.
9
+ - Isolated worktree/PR contribution process and explicit independent review.
10
+ - User-reported VM QA passed main beta.3 to beta.4 upgrade and deliberate beta.11
11
+ activation failure with rollback to beta.4 and a post-recovery Telegram reply.
12
+ - That VM evidence covers earlier candidates. Final beta.12 fresh-host/reboot and
13
+ live WhatsApp upgrade acceptance remain pending; this is a testing beta.
14
+
15
+ Private QA versions beta.3 through beta.11 were not public releases; some were
16
+ deliberately broken rollback fixtures and must never be published.
17
+
18
+ ## 0.1.0-beta.2 — distribution preparation (not published)
19
+
20
+ - Publish under `@jc_stack/ez-agents`; GitHub remains `jdorado/ez-agents`.
21
+ - Document pinned npm downloads and retain the beta-only distribution channel.
22
+ - Runtime behavior and dependencies are unchanged from beta.1. Live account and
23
+ reboot acceptance remain deferred.
24
+
25
+ ## 0.1.0-beta.1 — initial public beta
26
+
27
+ - Owner-paired Telegram relay using an existing host AI CLI and persistent
28
+ Markdown workspace, with queued execution and explicit messaging tools.
29
+ - Docker runtime, private deployment creation and a local hash-pinned plugin
30
+ manager with explicit installation/start/stop and provider-owned onboarding.
31
+ - Empty default catalog: no unpublished sibling package required.
32
+ - Public contribution, security, packaging and release instructions.
33
+
34
+ Limits: trusted host administration, not hostile-agent isolation. No hosted
35
+ service or remote plugin marketplace. Desktop executor acceptance and optional
36
+ voice providers need separate verification; CLI success is not GUI evidence.
37
+
38
+ Beta acceptance: automated tests, packed installs and Docker fixtures only. Live
39
+ provider onboarding and reboot verification are deferred, not marked passed.
@@ -0,0 +1,73 @@
1
+ # Contributing
2
+
3
+ Humans, LLM agents and maintainers use the same small-PR process. Read AGENTS.md
4
+ and the relevant CLI help first. Open an issue for substantial scope changes;
5
+ small fixes need no proposal. Internal plans and private QA belong outside this
6
+ repository. Public docs describe shipped behavior and explicit limitations.
7
+
8
+ 1. Fork/branch from main and make one coherent change. Preserve unrelated work.
9
+ 2. Install Node 22+ and pnpm 10.30.3. Run `pnpm install --frozen-lockfile`.
10
+ 3. Change the code, user instructions and focused tests together. Authority,
11
+ paths, credentials, cancellation and uncertain writes need negative tests.
12
+ 4. Run `pnpm verify`, `npm run release:check` and `git diff --check`.
13
+ Packaging/runtime changes also need the Docker checks in docs/releasing.md.
14
+ 5. Open a PR explaining the problem, resulting behavior, verification and limits.
15
+ Include a short sanitized reproduction. State which tests were not run.
16
+ 6. A maintainer reviews and merges after CI. Maintainers use PRs too. No CLA,
17
+ ticket requirement, custom commit format or additional approval committee.
18
+
19
+ You are responsible for understanding submitted code, including AI-generated
20
+ code, and having the right to contribute it under this repository's license.
21
+ Do not upload conversation dumps, credentials, QR codes or customer records.
22
+ Installation authority alone does not authorize messaging another person.
23
+ Use synthetic providers for routine tests; live tests need a dedicated account
24
+ and explicit recipient authority. A process exit is not provider delivery proof.
25
+
26
+ Report bugs through a GitHub issue with version, OS, Node/Docker versions,
27
+ minimal steps, expected/actual behavior and sanitized output. Feature requests
28
+ should explain the user problem and a small acceptance example. Security reports
29
+ follow SECURITY.md. Release and new-plugin requirements: docs/releasing.md and
30
+ docs/plugin-contributions.md.
31
+
32
+ ## Isolated work and review
33
+
34
+ - One coherent change, one branch, one dedicated Git worktree, one PR per
35
+ repository. Create the worktree from freshly fetched origin/main before edits.
36
+ Never develop on main or switch branches in another task's checkout.
37
+ - Inspect git status and git worktree list first. Reuse a worktree only for the
38
+ same task. Preserve other work; never stash, reset or commit another task's
39
+ files. Stage explicit paths or hunks and review the staged diff before committing.
40
+ - Keep worktrees outside the published package. Each task installs its own
41
+ dependencies and uses separate test state, ports and Docker project names.
42
+ Never share live bot tokens or provider profiles between test instances.
43
+ - Keep unrelated fixes/features in separate PRs. For work spanning repositories,
44
+ create one worktree/PR per repository and link dependencies and merge order.
45
+ Branch from an unmerged feature only when the dependency is intentional and
46
+ documented; do not quietly include it in an unrelated PR.
47
+ - Open a draft PR when the change is reviewable. Report its scope, exact commit,
48
+ validation and remaining QA. A completed coding task can remain a draft;
49
+ implementation completion does not authorize merge or npm publication.
50
+ - Before merge, obtain an independent human or agent review of the final diff.
51
+ The implementer's self-check and passing CI are not independent review.
52
+ Reviewers inspect correctness, architecture, state/permissions and negative
53
+ cases. Record reviewer identity/session, reviewed commit, findings and resolution
54
+ in the PR. If review is unavailable, leave it explicitly pending.
55
+ - New substantive commits or conflict resolutions invalidate the affected review
56
+ and test evidence. Refresh against current main, review the resulting diff and
57
+ run the applicable checks. Do not bypass protected-branch requirements.
58
+ - Merge only after review, required CI, applicable QA and maintainer authorization.
59
+ Publish only under the separate release process; commits and merges are not
60
+ releases. Record which source commit and artifact hash were tested.
61
+ - Keep the worktree while its PR or QA is open. After merge or explicit abandonment,
62
+ inspect it for uncommitted/untracked files and local-only commits. Remove only
63
+ the clean task worktree after valuable work is preserved; never force cleanup.
64
+ Delete its branch only after confirming merge or authorized abandonment.
65
+
66
+ Example, substituting a unique task name and an absolute external directory:
67
+
68
+ ```sh
69
+ git status --short
70
+ git worktree list
71
+ git fetch origin
72
+ git worktree add -b feat/task-name /absolute/worktrees/task-name origin/main
73
+ ```
package/Dockerfile ADDED
@@ -0,0 +1,16 @@
1
+ FROM node:22.22.0-bookworm-slim@sha256:dd9d21971ec4395903fa6143c2b9267d048ae01ca6d3ea96f16cb30df6187d94 AS dependencies
2
+ WORKDIR /app
3
+ RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates git util-linux ffmpeg && rm -rf /var/lib/apt/lists/*
4
+ RUN corepack enable && corepack prepare pnpm@10.30.3 --activate
5
+ COPY package.json ./
6
+ COPY docker/pnpm-lock.yaml ./pnpm-lock.yaml
7
+ RUN pnpm install --frozen-lockfile --ignore-scripts
8
+ COPY . .
9
+ FROM dependencies AS test
10
+ RUN pnpm verify
11
+ FROM dependencies AS runtime
12
+ 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
+ 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
14
+ WORKDIR /workspace
15
+ ENTRYPOINT ["/app/docker/entrypoint.sh"]
16
+ CMD ["start"]
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ezenciel-agents contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # ezenciel-agents
2
+
3
+ Telegram `/status` shows the running relay and host
4
+ versions plus installed plugin versions. The agent's `ez status` adds verified
5
+ plugin runtime states and upgrade job receipts. See [status and upgrades](docs/upgrades.md).
6
+
7
+ **Beta 0.1.0-beta.12 — for testing on a trusted machine.** Offline and Docker
8
+ fixtures are verified. Live account onboarding and reboot acceptance for this
9
+ release are deferred; this is not a production-readiness claim.
10
+
11
+ The npm package is `@jc_stack/ez-agents` (channel `beta`). Download a pinned
12
+ release with `npm pack @jc_stack/ez-agents@0.1.0-beta.12`, or get the tarball and SHA256SUMS from
13
+ [GitHub prereleases](https://github.com/jdorado/ez-agents/releases/tag/v0.1.0-beta.12).
14
+ GitHub remains under `jdorado`; npm uses `jc_stack`. Do not install the unrelated
15
+ unscoped `ez-whatsapp` package.
16
+
17
+
18
+ Installed runtime operation uses Docker Compose. Read
19
+ [Docker setup, state and QA](docs/docker-runtime.md). Docker owns relay/plugin services; the existing host CLI and login are shared
20
+ through one generic transport, with separate agent workspaces and sessions.
21
+
22
+ A minimal Telegram-to-AI-CLI relay. One owner, one persistent Markdown mind.
23
+
24
+ ## You are the installing agent
25
+
26
+ If the user says **install** or **set up** this repository/package, complete
27
+ [docs/setup.md](docs/setup.md) yourself. The instructions and commands in this
28
+ repository are addressed to you, the LLM with computer access, not the human.
29
+
30
+ **Your finish line is a working Telegram reply and automatic startup.** Adding
31
+ npm dependencies, printing `--help`, or creating files is not completion.
32
+ Install the main runtime alone first, with an empty plugin catalog. Even when
33
+ WhatsApp is supplied or requested alongside it, defer plugin setup and QR scanning
34
+ until owner pairing and a real agent reply are verified. Then the owner requests
35
+ the plugin in Telegram and the installed agent handles onboarding there. The
36
+ initial host CLI must not install the plugin on that agent's behalf.
37
+ Do not hand the human terminal commands, ask them to start the relay, leave a
38
+ terminal open, read logs, find their numeric Telegram ID, or approve pairing
39
+ from another terminal. You own those steps and their verification.
40
+
41
+ Only involve the human for missing account access, a bot token you cannot
42
+ obtain through authorized tools, confirmation of which Telegram account is
43
+ theirs, or an OS-level authorization prompt you cannot approve yourself. Give
44
+ one concrete action at a time, with the exact link. Resume setup when they
45
+ answer. Explain actual blockers in plain language.
46
+
47
+ ### Docker is installer work
48
+
49
+ Docker and Compose are required, but they are **not a prerequisite the human
50
+ must install**. First inspect the host. If Docker is absent, stopped, or lacks
51
+ Compose, install the official supported Docker distribution for that operating
52
+ system yourself, start it, and arrange the normal user access it requires.
53
+ Then verify the engine and Compose with a harmless real container before
54
+ building this package. Do not respond with “install Docker and retry,” a list
55
+ of commands for the human, or a request to open a terminal.
56
+
57
+ An administrator password, native system approval, or Docker Desktop sign-in
58
+ dialog is a human-only boundary. When one appears, explain the single action
59
+ needed and continue immediately after it. Everything else—package install,
60
+ image build, host transport, Compose startup, pairing, and a real Telegram
61
+ reply—is the installing LLM's job.
62
+
63
+ ### Recognize this artifact immediately
64
+
65
+ This is a **Node.js npm package**, not a Python package or a Codex skill.
66
+ For a supplied tarball, read `package/README.md` and `package/docs/setup.md`
67
+ from that archive. Install that artifact; do not search npm, PyPI, or skill
68
+ catalogs for its name. Build/run its Docker image; Docker and Compose are
69
+ required on the host and the installing LLM makes them ready if needed. The
70
+ image supplies relay dependencies; the existing host CLI supplies execution.
71
+
72
+ Use the exact scoped npm name and version above, or the supplied tarball/path.
73
+ A bare product name is not a package identity. Inspect the artifact before setup;
74
+ do not substitute a similarly named package.
75
+
76
+ ### Installer tools
77
+
78
+ Run these yourself from the installation directory. Read the setup guide for
79
+ credentials, startup, and verified pairing; never present this as human homework.
80
+
81
+ | Tool | Purpose |
82
+ |---|---|
83
+ | `ezenciel-agents-install preflight --executor <path-or-name>` | Check prerequisites, disk and executor identity hints before setup |
84
+ | `ezenciel-agents-install build` | Build once per artifact, with a private log and duplicate-build detection |
85
+ | `ezenciel-agents-install status --deployment <path>` | Distinguish runtime/configuration, pairing and Telegram reply evidence |
86
+ | `ezenciel-agents-create --register-cli <current-cli>` | Record the CLI that installs the initial package |
87
+ | `ezenciel-agents-create --name <name> --purpose <purpose>` | Create an agent inheriting the installation CLI; token via stdin |
88
+ | `ezenciel-agents-host` | Invoke the existing shared host CLI for this deployment |
89
+ | `ezenciel-agents-setup configure <executor>` | Private configuration and missing starter files; preserves personal files |
90
+ | `ezenciel-agents-setup configure <executor> --token-stdin` | Same, with the BotFather token supplied privately through stdin |
91
+ | `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 |
92
+ | `ezenciel-agents-owner status` | Inspect the owner and pending pairing requests |
93
+ | `ezenciel-agents-owner approve <telegram-user-id>` | Approve the verified owner; never an arbitrary first sender |
94
+ | `ezenciel-agents-setup status` | Inspect installed executor choices |
95
+ | `ezenciel-agents-setup init` | Seed only missing workspace guidance |
96
+
97
+ Run runtime tools through `ezenciel-agents-docker run --rm relay <command>`
98
+ as shown in the Docker guide. Keep the Docker engine available; Compose owns
99
+ restart and shutdown. The installing agent registers the small host CLI transport with the native service manager.
100
+
101
+ ## Runtime and development reference
102
+
103
+ The selected host CLI runs in this agent's persistent workspace and owns its
104
+ Markdown/work files. New conversations and AI changes preserve those files.
105
+ `AGENTS.md`, `SOUL.md`, `USER.md`, and `TOOLS.md` are seeded; `MEMORY.md` is
106
+ optional. Received files go in `inbox/`, tasks and deliverables in `work/`.
107
+ Credentials and control state stay outside the mind. File separation is not
108
+ OS isolation against a process running as the same user.
109
+
110
+ Telegram intake is owner-gated. The executor sends replies with the messaging
111
+ CLI; stdout is not a reply. A process exit code is not delivery proof.
112
+ Voice/media needs its additional providers and tools; defer optional setup
113
+ until the owner asks. Do not advertise unverified integrations.
114
+
115
+ For code development only: `pnpm install`, then `pnpm verify` for offline tests
116
+ and TypeScript. `pnpm smoke` uses real providers and a paired owner; it verifies
117
+ outbound delivery, not the full incoming Telegram path. Never publish publicly
118
+ without authorization. Local registry rehearsal uses `pnpm publish:local`.
119
+
120
+ - [Setup and troubleshooting](docs/setup.md)
121
+ - [Architecture and run semantics](docs/architecture/telegram-intake.md)
122
+ - [Saved AI choices](docs/architecture/ai-selection.md)
123
+ - [Development and verification](docs/development-and-testing.md)
124
+ - [Authority boundaries](docs/architecture/authority-boundaries.md)
125
+ - [Security scope](SECURITY.md)
126
+
127
+ - [Contributing](CONTRIBUTING.md) and [releasing](docs/releasing.md)
128
+ - [First plugin and extension contract](docs/plugins.md)
129
+ - [License and dependency notices](THIRD_PARTY_NOTICES.md)
130
+
131
+ ## Agent-owned upgrades
132
+
133
+ This beta includes owner-policy release checks and durable
134
+ main/plugin replacement. See [upgrade setup, tools and recovery](docs/upgrades.md). Earlier main upgrade/rollback VM QA passed; final-release fresh-host/reboot and live plugin upgrade acceptance remain pending.
package/SECURITY.md ADDED
@@ -0,0 +1,26 @@
1
+ # Security
2
+
3
+ Supported scope: the latest release, one paired Telegram owner per agent on a
4
+ trusted host. Unknown senders/groups cannot acquire execution authority.
5
+ The host CLI and plugin manager run with the host user's authority, including
6
+ Docker administration. Environment filtering and container profile separation
7
+ reduce accidental exposure; they do not isolate a hostile process from its own
8
+ host user. Markdown roles and approvals are instructions, not an OS sandbox.
9
+ Do not expose this as a public multi-tenant execution service.
10
+
11
+ Keep Telegram tokens, device profiles, QR images, control state and native CLI
12
+ sessions outside source and mind files. Treat incoming provider content as
13
+ untrusted data. Do not bypass owner checks or silently retry uncertain writes.
14
+
15
+ ## Reporting
16
+
17
+ On the public GitHub repository use Security → Report a vulnerability (private
18
+ vulnerability reporting). Before publication the maintainer must enable and
19
+ verify that channel. If it is unavailable, open an issue asking only for a private
20
+ contact route; do not include exploit details or sensitive attachments publicly.
21
+ Include affected version, sanitized reproduction, impact and suggested fix.
22
+ There is no paid response SLA. Only the latest release receives fixes; report
23
+ older-version findings with a reproduction on the latest version when possible.
24
+
25
+ Current support target: the latest beta on a trusted single-user host. Report
26
+ privately at https://github.com/jdorado/ez-agents/security/advisories/new.
@@ -0,0 +1,14 @@
1
+ # Third-party software
2
+
3
+ This repository's original source is MIT (LICENSE). Dependencies retain their
4
+ own copyright and licenses; our LICENSE does not relicense them. Preserve the
5
+ license files installed with every dependency and the frozen pnpm-lock.yaml.
6
+ Node/Debian container layers and optional external CLIs have separate terms.
7
+ The container is built locally; no bundled third-party binary image is promised.
8
+
9
+ Direct runtime dependencies: grammy (MIT), tsx (MIT). Development dependencies
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.
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec node "$(dirname "$0")/ezenciel-agents.mjs" "$@"
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec node "$(dirname -- "$0")/ezenciel-agents-ai.mjs" "$@"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+ import { fileURLToPath } from 'node:url'
4
+ const child=spawn(process.execPath,['--import',fileURLToPath(new URL('../node_modules/tsx/dist/loader.mjs',import.meta.url)),fileURLToPath(new URL('../src/ai-cli.ts',import.meta.url)),...process.argv.slice(2)],{stdio:'inherit'})
5
+ child.on('exit',code=>{process.exitCode=code??1})
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec node "$(dirname "$0")/ezenciel-agents-approval.mjs" "$@"
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+ import { createRequire } from 'node:module'
4
+ import { dirname, join } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const here = dirname(fileURLToPath(import.meta.url))
8
+ const require = createRequire(import.meta.url)
9
+ const tsx = require.resolve('tsx')
10
+ const entry = join(here, '..', 'src', 'approval-cli.ts')
11
+ const forwarded = process.argv.slice(2).filter((arg) => arg !== '--')
12
+ const child = spawn(process.execPath, ['--import', tsx, entry, ...forwarded], { stdio: 'inherit' })
13
+ child.on('exit', (code, signal) => {
14
+ if (signal) process.kill(process.pid, signal)
15
+ process.exit(code ?? 1)
16
+ })
@@ -0,0 +1,12 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ umask 077
4
+ root=$(node -e 'const fs=require("node:fs"),path=require("node:path");process.stdout.write(path.dirname(path.dirname(fs.realpathSync(process.argv[1]))))' "$0")
5
+ : "${EZ_AGENTS_HOME:?Set EZ_AGENTS_HOME to the absolute private installation registry}"
6
+ case "$EZ_AGENTS_HOME" in /*) ;; *) echo 'EZ_AGENTS_HOME must be absolute' >&2; exit 1;; esac
7
+ mkdir -p "$EZ_AGENTS_HOME"
8
+ exec docker run --rm -i --user "$(id -u):$(id -g)" --cap-drop ALL --security-opt no-new-privileges \
9
+ --mount "type=bind,src=$EZ_AGENTS_HOME,dst=/installations" \
10
+ --entrypoint node "${EZ_RELAY_IMAGE:-ezenciel-agents:local}" \
11
+ --import /app/node_modules/tsx/dist/loader.mjs /app/src/agent-install.ts \
12
+ --host-root "$EZ_AGENTS_HOME" --compose-file "$root/compose.yaml" --relay-image "${EZ_RELAY_IMAGE:-ezenciel-agents:local}" "$@"
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ root=$(node -e 'const fs=require("node:fs"),path=require("node:path");process.stdout.write(path.dirname(path.dirname(fs.realpathSync(process.argv[1]))))' "$0")
4
+ : "${EZ_DEPLOYMENT_DIR:?Set EZ_DEPLOYMENT_DIR to the absolute deployment directory containing docker.env}"
5
+ case "$EZ_DEPLOYMENT_DIR" in /*) ;; *) echo 'EZ_DEPLOYMENT_DIR must be absolute' >&2; exit 1;; esac
6
+ exec docker compose --project-directory "$root" --env-file "$EZ_DEPLOYMENT_DIR/docker.env" "$@"
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ root=$(node -e 'const fs=require("node:fs"),path=require("node:path");process.stdout.write(path.dirname(path.dirname(fs.realpathSync(process.argv[1]))))' "$0")
4
+ : "${EZ_DEPLOYMENT_DIR:?Set EZ_DEPLOYMENT_DIR to this agent deployment}"
5
+ exec node "$root/src/updates/launch.mjs" "$EZ_DEPLOYMENT_DIR"
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import('../src/install-tools.mjs').then(m=>m.main(process.argv.slice(2))).catch(e=>{console.error(JSON.stringify({ok:false,error:e.message}));process.exitCode=1;});
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec node "$(dirname "$0")/ezenciel-agents-message.mjs" "$@"
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+ import { createRequire } from 'node:module'
4
+ import { dirname, join } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const here = dirname(fileURLToPath(import.meta.url))
8
+ const require = createRequire(import.meta.url)
9
+ const tsx = require.resolve('tsx')
10
+ const entry = join(here, '..', 'src', 'message.ts')
11
+ const forwarded = process.argv.slice(2).filter((arg) => arg !== '--')
12
+ const child = spawn(process.execPath, ['--import', tsx, entry, ...forwarded], { stdio: 'inherit' })
13
+ child.on('exit', (code, signal) => {
14
+ if (signal) process.kill(process.pid, signal)
15
+ process.exit(code ?? 1)
16
+ })
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec node "$(dirname "$0")/ezenciel-agents-owner.mjs" "$@"
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+ import { existsSync } from 'node:fs'
4
+ import { createRequire } from 'node:module'
5
+ import { dirname, join } from 'node:path'
6
+ import { fileURLToPath } from 'node:url'
7
+
8
+ const here = dirname(fileURLToPath(import.meta.url))
9
+ const require = createRequire(import.meta.url)
10
+ const tsx = require.resolve('tsx')
11
+ const entry = join(here, '..', 'src', 'owner.ts')
12
+ const forwarded = process.argv.slice(2).filter((arg) => arg !== '--')
13
+ const args = existsSync('.env') ? ['--env-file=.env', entry] : [entry]
14
+ const child = spawn(process.execPath, ['--import', tsx, ...args, ...forwarded], { stdio: 'inherit' })
15
+ child.on('exit', (code, signal) => {
16
+ if (signal) process.kill(process.pid, signal)
17
+ process.exit(code ?? 1)
18
+ })
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec node "$(dirname "$0")/ezenciel-agents-react.mjs" "$@"
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+ import { createRequire } from 'node:module'
4
+ import { dirname, join } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const here = dirname(fileURLToPath(import.meta.url))
8
+ const require = createRequire(import.meta.url)
9
+ const tsx = require.resolve('tsx')
10
+ const entry = join(here, '..', 'src', 'react.ts')
11
+ const forwarded = process.argv.slice(2).filter((arg) => arg !== '--')
12
+ const child = spawn(process.execPath, ['--import', tsx, entry, ...forwarded], { stdio: 'inherit' })
13
+ child.on('exit', (code, signal) => {
14
+ if (signal) process.kill(process.pid, signal)
15
+ process.exit(code ?? 1)
16
+ })
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+ import { existsSync } from 'node:fs'
4
+ import { createRequire } from 'node:module'
5
+ import { dirname, join } from 'node:path'
6
+ import { fileURLToPath } from 'node:url'
7
+
8
+ const here = dirname(fileURLToPath(import.meta.url))
9
+ const require = createRequire(import.meta.url)
10
+ const tsx = require.resolve('tsx')
11
+ const entry = join(here, '..', 'src', 'setup.ts')
12
+ const forwarded = process.argv.slice(2).filter((arg) => arg !== '--')
13
+ const args = existsSync('.env') ? ['--env-file=.env', entry] : [entry]
14
+ const child = spawn(process.execPath, ['--import', tsx, ...args, ...forwarded], { stdio: 'inherit' })
15
+ child.on('exit', (code, signal) => {
16
+ if (signal) process.kill(process.pid, signal)
17
+ process.exit(code ?? 1)
18
+ })
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec node "$(dirname "$0")/ezenciel-agents-source.mjs" "$@"