@jc_stack/ez-agents 0.1.0-beta.12 → 0.1.0-beta.13
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/.dockerignore +1 -0
- package/.env.example +1 -1
- package/AGENTS.md +10 -1
- package/CHANGELOG.md +22 -0
- package/CONTRIBUTING.md +3 -0
- package/README.md +112 -10
- package/SECURITY.md +7 -1
- package/bin/ezenciel-agents-schedule +2 -0
- package/bin/ezenciel-agents-schedule.mjs +16 -0
- package/bin/ezenciel-agents-task +2 -0
- package/bin/ezenciel-agents-task.mjs +16 -0
- package/compose.yaml +2 -1
- package/docker/recovery.ts +2 -2
- package/docker/run.ts +2 -2
- package/docs/architecture/authority-boundaries.md +114 -12
- package/docs/architecture/event-sources.md +12 -7
- package/docs/channel-backend.md +36 -0
- package/docs/local-qa.md +45 -0
- package/docs/plugin-catalog.md +54 -0
- package/docs/plugin-contributions.md +3 -0
- package/docs/plugins.md +49 -0
- package/docs/scheduling.md +127 -0
- package/docs/selective-monitoring.md +106 -0
- package/docs/setup.md +7 -0
- package/docs/standalone-cli.md +62 -0
- package/package.json +7 -2
- package/scripts/smoke-scheduler.ts +90 -0
- package/scripts/stage-qa.mjs +42 -0
- package/src/channel-backend.ts +46 -0
- package/src/codex-session.ts +96 -0
- package/src/config.ts +6 -1
- package/src/desktop-bridge.ts +29 -11
- package/src/execution-authority.ts +24 -0
- package/src/executor.ts +66 -15
- package/src/host-executor.ts +30 -10
- package/src/inbox.ts +4 -0
- package/src/index.ts +130 -34
- package/src/plugins/exposure.mjs +13 -0
- package/src/plugins/manager.mjs +27 -12
- package/src/process-tree.ts +33 -0
- package/src/runs.ts +50 -17
- package/src/schedule-cli.ts +69 -0
- package/src/schedule-time.ts +85 -0
- package/src/scheduler.ts +121 -0
- package/src/source-cli.ts +1 -1
- package/src/task-cli.ts +16 -0
- package/src/task-executor.ts +63 -0
- package/src/task-mcp.ts +36 -0
- package/src/task-rpc.ts +45 -0
- package/src/task-workspace.ts +22 -0
- package/src/tasks.ts +192 -0
- package/src/updates/binding.mjs +1 -0
- package/src/updates/status.mjs +7 -1
- package/templates/agent/TOOLS.md +54 -1
- package/templates/standalone-tools.md +20 -0
- package/test/channel-backend.test.ts +100 -0
- package/test/codex-context.test.ts +36 -1
- package/test/codex-session.test.ts +49 -0
- package/test/config.test.ts +2 -2
- package/test/desktop-bridge.test.ts +19 -0
- package/test/event-sources.test.ts +47 -11
- package/test/execution-authority.test.ts +42 -0
- package/test/executor.test.ts +42 -1
- package/test/helpers/owner-run.ts +13 -0
- package/test/host-executor.test.ts +9 -3
- package/test/local-qa.test.mjs +38 -0
- package/test/plugin-manager.test.mjs +70 -1
- package/test/schedule-cli.test.ts +49 -0
- package/test/scheduler-host.test.ts +55 -0
- package/test/scheduler-relay.test.ts +67 -0
- package/test/scheduler.test.ts +104 -0
- package/test/task-native.test.ts +87 -0
- package/test/tasks.test.ts +179 -0
package/.dockerignore
CHANGED
package/.env.example
CHANGED
|
@@ -11,7 +11,7 @@ EZ_AGENT_WORKSPACE=./agent
|
|
|
11
11
|
# EZ_PAIRING_TTL_SECONDS=900
|
|
12
12
|
|
|
13
13
|
# Optional. Default is 300.
|
|
14
|
-
#
|
|
14
|
+
# Agent execution has no wall-clock timeout. Use /stop or schedule cancel.
|
|
15
15
|
|
|
16
16
|
# CLI Executor adapter. Supported: agy (default), claude, grok, opencode
|
|
17
17
|
# Switch easily with: pnpm run setup <executor-name>
|
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# ezenciel-agents — Coding Standards & Architectural Invariants
|
|
2
2
|
|
|
3
|
+
Ez supports two independent, composable uses: an autonomous Telegram agent and
|
|
4
|
+
workspace-scoped plugins called by your existing local CLI/GUI executor.
|
|
5
|
+
For CLI-only requests, follow [standalone setup](docs/standalone-cli.md): no Telegram
|
|
6
|
+
pairing, relay or host executor is required. The main-first Telegram onboarding
|
|
7
|
+
rules below apply only when installing the autonomous relay. When both are
|
|
8
|
+
requested, keep each workspace's authority and registry explicit.
|
|
9
|
+
|
|
3
10
|
Installed runtime operation uses Docker Compose. Read
|
|
4
11
|
[Docker setup, state and QA](docs/docker-runtime.md). Docker owns relay/plugin services; the existing host CLI and login are shared
|
|
5
12
|
through one generic transport, with separate agent workspaces and sessions.
|
|
@@ -56,7 +63,7 @@ This package will be published as an open-source, lightweight Telegram-to-CLI re
|
|
|
56
63
|
|
|
57
64
|
## 6. Concurrency & Workspace Invariants
|
|
58
65
|
- **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
|
-
-
|
|
66
|
+
- Main-conversation jobs queue sequentially in `RunStore`. Scheduled/background work uses separate task directories and native CLI sessions (up to four alongside chat). Never share a mutable task directory. Delegation decisions and goal persistence belong to the agent/executor; there is no automatic planner or canned chat ACK. Production executor runs have no wall-clock timeout; cancellation is explicit.
|
|
60
67
|
|
|
61
68
|
## 7. Fail-Closed Authority (Channel Access ≠ Execution)
|
|
62
69
|
- Incoming messages from unapproved senders must **never** spawn the executor. First DM registers an unapproved pairing request, then stops.
|
|
@@ -82,3 +89,5 @@ worktree/branch/PR, starting from fetched origin/main. Do not switch or mix work
|
|
|
82
89
|
another task's checkout. Stage only this task's changes. Keep its worktree through
|
|
83
90
|
review and QA; independent review and green CI precede an authorized merge.
|
|
84
91
|
Never treat task completion as permission to merge or publish.
|
|
92
|
+
|
|
93
|
+
- In channel-backend mode, the application owns native sessions and actions. Forward normalized inputs with stable run IDs, recover only by idempotent backend submission, and deliver replies through the existing outbox. Never launch a fallback CLI or pass relay credentials into an executor.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.13
|
|
4
|
+
|
|
5
|
+
- Escalate cancelled plugin clients and report container cleanup failures.
|
|
6
|
+
- List the GitHub CLI plugin in the public catalog.
|
|
7
|
+
- Include standalone CLI, native task runtime and integration discovery updates.
|
|
8
|
+
|
|
9
|
+
- Support incoming-only reply tasks without an opening message; refresh installed
|
|
10
|
+
guidance for selective setup, implicit follow-up, and quiet account linking.
|
|
11
|
+
|
|
12
|
+
- Add owner-approved, single-contact messaging tasks, core-bound sends and notes,
|
|
13
|
+
fresh restricted Codex execution, revocation/expiry, and durable uncertain sends.
|
|
14
|
+
- Task execution requires audited Codex 0.153.4 and a message-v1 event source.
|
|
15
|
+
Other external events remain blocked; live provider acceptance is pending.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
- Add optional per-command exposure declarations and `ez tools exposure` with
|
|
19
|
+
conservative defaults. Declarations do not grant authority.
|
|
20
|
+
- Block registered external events before owner-runtime execution; keep durable
|
|
21
|
+
blocked records visible in work status. This disables prior external wakeups
|
|
22
|
+
until an isolated runner exists. Recheck active paired-owner provenance at
|
|
23
|
+
the local and host launch boundaries.
|
|
24
|
+
|
|
3
25
|
## 0.1.0-beta.12 — self-upgrade beta
|
|
4
26
|
|
|
5
27
|
- Agent-owned main/plugin upgrades with stable-default policy, queued maintenance,
|
package/CONTRIBUTING.md
CHANGED
|
@@ -18,6 +18,9 @@ repository. Public docs describe shipped behavior and explicit limitations.
|
|
|
18
18
|
|
|
19
19
|
You are responsible for understanding submitted code, including AI-generated
|
|
20
20
|
code, and having the right to contribute it under this repository's license.
|
|
21
|
+
For unreleased feature testing, follow [local QA](docs/local-qa.md): stage an
|
|
22
|
+
immutable beta candidate and provide a simple PA upgrade instruction and feature
|
|
23
|
+
QA flow. A public release is not required for this handoff.
|
|
21
24
|
Do not upload conversation dumps, credentials, QR codes or customer records.
|
|
22
25
|
Installation authority alone does not authorize messaging another person.
|
|
23
26
|
Use synthetic providers for routine tests; live tests need a dedicated account
|
package/README.md
CHANGED
|
@@ -1,12 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Ez — AI assistants for small businesses
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
**A lightweight, open-source foundation for persistent AI assistants, powered by
|
|
4
|
+
existing AI harnesses.** Give an assistant a purpose, a workspace and tools for
|
|
5
|
+
your business, then work with it through chat.
|
|
6
|
+
|
|
7
|
+
Ez is for owners of shops, studios and small teams who want help doing everyday
|
|
8
|
+
work: preparing quotes, maintaining business records, researching decisions or
|
|
9
|
+
following up on an agreed task. Each assistant has its own working context and
|
|
10
|
+
responsibilities. The tools and permissions you configure determine what it can
|
|
11
|
+
do.
|
|
12
|
+
|
|
13
|
+
An AI harness is the client that lets a model reason, use tools and carry out
|
|
14
|
+
work. Ez uses your chosen host CLI and its existing login. The harness does the
|
|
15
|
+
reasoning; Ez connects it to a persistent workspace, messaging, plugins and
|
|
16
|
+
explicit authority boundaries. Telegram is the current owner interface.
|
|
17
|
+
|
|
18
|
+
## What makes an Ez assistant
|
|
19
|
+
|
|
20
|
+
| Part | What it contributes |
|
|
21
|
+
|---|---|
|
|
22
|
+
| **Workspace** | Markdown instructions, business context and working files that persist across conversations and changes of AI. |
|
|
23
|
+
| **Harness** | The existing AI client that plans, chooses tools and executes work. See [AI selection](docs/architecture/ai-selection.md). |
|
|
24
|
+
| **Authority** | A verified owner and explicit limits on delegated work. Receiving a message or installing a tool does not grant permission to act. |
|
|
25
|
+
| **Plugins** | Separately maintained tools and connections, with instructions the agent can discover and use. Provider authentication and receipts belong to the plugin. |
|
|
26
|
+
| **Messaging and continuity** | A way to reach the assistant, return results and continue work in its own environment. |
|
|
27
|
+
|
|
28
|
+
The design starts with one assistant, one mind and clear responsibilities.
|
|
29
|
+
Business instructions live in the workspace; reusable capabilities live in tools
|
|
30
|
+
and plugins. The agent decides how to do the work using those instructions and
|
|
31
|
+
the capabilities available to it.
|
|
32
|
+
|
|
33
|
+
Use the harness's native capabilities when they meet the need. Add a standalone
|
|
34
|
+
plugin when a missing integration earns the maintenance. Ez keeps the surrounding
|
|
35
|
+
runtime small, so your files, tools and business knowledge remain useful as AI
|
|
36
|
+
clients improve.
|
|
37
|
+
|
|
38
|
+
## Plugin catalog
|
|
39
|
+
|
|
40
|
+
Browse the [available plugins](docs/plugin-catalog.md) for capabilities you can
|
|
41
|
+
add to your assistant. The first released plugin is
|
|
42
|
+
[WhatsApp](https://github.com/jdorado/ez-whatsapp), which connects an existing
|
|
43
|
+
WhatsApp account. Ask your working Ez assistant to set it up in Telegram.
|
|
44
|
+
|
|
45
|
+
## Start with one useful job
|
|
46
|
+
|
|
47
|
+
Ez supports two independent, composable uses: an autonomous Telegram agent and
|
|
48
|
+
workspace-scoped plugins called by your existing local CLI/GUI executor.
|
|
49
|
+
For CLI-only requests, follow [standalone setup](docs/standalone-cli.md): no Telegram
|
|
50
|
+
pairing, relay or host executor is required. The main-first Telegram onboarding
|
|
51
|
+
rules below apply only when installing the autonomous relay. When both are
|
|
52
|
+
requested, keep each workspace's authority and registry explicit.
|
|
53
|
+
|
|
54
|
+
Ask an AI agent with computer access to install Ez for a concrete purpose:
|
|
55
|
+
|
|
56
|
+
> Install Ez from https://github.com/jdorado/ez-agents for my business.
|
|
57
|
+
> I want an assistant that helps me prepare customer quotes from my own files.
|
|
58
|
+
> Set it up and verify that I can talk to it in Telegram.
|
|
59
|
+
|
|
60
|
+
The installing agent follows [the setup guide](docs/setup.md), handles technical
|
|
61
|
+
setup and asks you for the account access or approvals it needs. Once the
|
|
62
|
+
assistant replies in Telegram, explain your business, give it the relevant
|
|
63
|
+
materials and request any additional tools there. Integrations require their
|
|
64
|
+
own setup and authorized accounts.
|
|
65
|
+
|
|
66
|
+
## Available today and where this is going
|
|
67
|
+
|
|
68
|
+
The current source supports persistent agent workspaces, a choice of host AI
|
|
69
|
+
executors, owner-gated Telegram messaging, native scheduling, plugin lifecycle
|
|
70
|
+
management and bounded, owner-approved correspondence. A messaging task can
|
|
71
|
+
contact one approved person using only its explicitly shared task context;
|
|
72
|
+
restricted task execution currently requires the pinned Codex runtime. See
|
|
73
|
+
[authority boundaries](docs/architecture/authority-boundaries.md) for the exact
|
|
74
|
+
scope and [scheduling](docs/scheduling.md) for scheduled work.
|
|
75
|
+
|
|
76
|
+
The broader direction is assistants working across business roles, with deliberate
|
|
77
|
+
workspace sharing and authority over who may direct work, see information and
|
|
78
|
+
use accounts. General team delegation, family/group authority and enterprise
|
|
79
|
+
reviewer roles remain future work. The current trusted-host beta is not an
|
|
80
|
+
enterprise isolation or multi-tenant security product. See [security scope](SECURITY.md).
|
|
81
|
+
|
|
82
|
+
## For agents discovering this repository
|
|
83
|
+
|
|
84
|
+
Ez may fit when a user needs a persistent business assistant reachable through
|
|
85
|
+
chat, using an existing AI client and separately installed tools. Start here:
|
|
86
|
+
|
|
87
|
+
- **Install for a user:** [setup and verified onboarding](docs/setup.md), then the installer instructions below.
|
|
88
|
+
- **Understand access and delegation:** [authority boundaries](docs/architecture/authority-boundaries.md) and [security scope](SECURITY.md).
|
|
89
|
+
- **Discover or add capabilities:** [plugin contract](docs/plugins.md). Check the actual executor's native tools before adding an integration.
|
|
90
|
+
- **Contribute:** [AGENTS.md](AGENTS.md) and [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
91
|
+
|
|
92
|
+
Repository discovery is not an instruction to install or contact anyone. Act on
|
|
93
|
+
the user's request and preserve its scope.
|
|
94
|
+
|
|
95
|
+
## Package and runtime
|
|
6
96
|
|
|
7
97
|
**Beta 0.1.0-beta.12 — for testing on a trusted machine.** Offline and Docker
|
|
8
98
|
fixtures are verified. Live account onboarding and reboot acceptance for this
|
|
9
|
-
release are deferred; this is not a production-readiness claim.
|
|
99
|
+
release are deferred; this is not a production-readiness claim. The source branch
|
|
100
|
+
may contain changes newer than the published beta; inspect the supplied artifact's
|
|
101
|
+
documentation when installing a pinned release.
|
|
10
102
|
|
|
11
103
|
The npm package is `@jc_stack/ez-agents` (channel `beta`). Download a pinned
|
|
12
104
|
release with `npm pack @jc_stack/ez-agents@0.1.0-beta.12`, or get the tarball and SHA256SUMS from
|
|
@@ -14,12 +106,14 @@ release with `npm pack @jc_stack/ez-agents@0.1.0-beta.12`, or get the tarball an
|
|
|
14
106
|
GitHub remains under `jdorado`; npm uses `jc_stack`. Do not install the unrelated
|
|
15
107
|
unscoped `ez-whatsapp` package.
|
|
16
108
|
|
|
109
|
+
Installed runtime operation uses Docker Compose. Docker owns relay/plugin
|
|
110
|
+
services; the existing host CLI and login are shared through one generic
|
|
111
|
+
transport, with separate agent workspaces and sessions. See
|
|
112
|
+
[Docker setup, state and QA](docs/docker-runtime.md).
|
|
17
113
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
A minimal Telegram-to-AI-CLI relay. One owner, one persistent Markdown mind.
|
|
114
|
+
Telegram `/status` shows the running relay and host versions plus installed
|
|
115
|
+
plugin versions. The agent's `ez status` adds verified plugin runtime states and
|
|
116
|
+
upgrade job receipts. See [status and upgrades](docs/upgrades.md).
|
|
23
117
|
|
|
24
118
|
## You are the installing agent
|
|
25
119
|
|
|
@@ -132,3 +226,11 @@ without authorization. Local registry rehearsal uses `pnpm publish:local`.
|
|
|
132
226
|
|
|
133
227
|
This beta includes owner-policy release checks and durable
|
|
134
228
|
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.
|
|
229
|
+
|
|
230
|
+
### Scheduling and background work
|
|
231
|
+
|
|
232
|
+
The core `ezenciel-agents-schedule` CLI accepts instruction text for one-off dates,
|
|
233
|
+
intervals and timezone-aware cron schedules. `create --now` delegates a task to a
|
|
234
|
+
separate CLI session so the owner conversation remains available. Long work has
|
|
235
|
+
no production wall-clock timeout; goals and subagents remain native executor
|
|
236
|
+
features. See [scheduling, recovery and QA](docs/scheduling.md).
|
package/SECURITY.md
CHANGED
|
@@ -6,7 +6,13 @@ The host CLI and plugin manager run with the host user's authority, including
|
|
|
6
6
|
Docker administration. Environment filtering and container profile separation
|
|
7
7
|
reduce accidental exposure; they do not isolate a hostile process from its own
|
|
8
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.
|
|
9
|
+
Do not expose this as a public multi-tenant execution service. Registered external
|
|
10
|
+
events require an approved, account/contact-bound task and the restricted native
|
|
11
|
+
runner; unmatched events remain blocked. The task worker has only core message,
|
|
12
|
+
note and report tools. It has no owner workspace, shell or general network tool.
|
|
13
|
+
The native client, broker and local host user remain trusted. See
|
|
14
|
+
[authority boundaries](docs/architecture/authority-boundaries.md). Plugin exposure
|
|
15
|
+
declarations are metadata, not grants.
|
|
10
16
|
|
|
11
17
|
Keep Telegram tokens, device profiles, QR images, control state and native CLI
|
|
12
18
|
sessions outside source and mind files. Treat incoming provider content as
|
|
@@ -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', 'schedule-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,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', 'task-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
|
+
})
|
package/compose.yaml
CHANGED
|
@@ -11,7 +11,8 @@ services:
|
|
|
11
11
|
cap_add: [CHOWN, DAC_OVERRIDE, FOWNER, SETUID, SETGID, SETPCAP]
|
|
12
12
|
environment:
|
|
13
13
|
EZ_AGENT_PURPOSE_FILE: /run/agent-purpose.md
|
|
14
|
-
EZ_EXECUTOR_TRANSPORT: host
|
|
14
|
+
EZ_EXECUTOR_TRANSPORT: ${EZ_EXECUTOR_TRANSPORT:-host}
|
|
15
|
+
EZ_CHANNEL_BACKEND_URL: ${EZ_CHANNEL_BACKEND_URL:-}
|
|
15
16
|
EZ_AGENT_WORKSPACE: ${EZ_AGENT_WORKSPACE:?Set this agent workspace}
|
|
16
17
|
EZ_CONTROL_DIR: ${EZ_CONTROL_DIR:?Set this agent control directory}
|
|
17
18
|
EZ_EXECUTOR_CLI: ${EZ_EXECUTOR_CLI:?Set the host installation CLI}
|
package/docker/recovery.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { RunStore } from '../src/runs.js'
|
|
|
2
2
|
|
|
3
3
|
// Called only after the deployment's kernel writer lock is held. No worker
|
|
4
4
|
// from a previous container can still own this deployment; PIDs may be reused.
|
|
5
|
-
export const recoverInterruptedRuns = async (controlDir: string) => {
|
|
5
|
+
export const recoverInterruptedRuns = async (controlDir: string, channelBackend = false) => {
|
|
6
6
|
const store = new RunStore(controlDir)
|
|
7
7
|
for (const run of await store.list()) {
|
|
8
8
|
if (run.status === 'running')
|
|
9
|
-
await store.patch(run.id, { status: 'failed', endedAt: new Date().toISOString() })
|
|
9
|
+
await store.patch(run.id, { status: channelBackend && !run.pid ? 'queued' : 'failed', endedAt: new Date().toISOString() })
|
|
10
10
|
}
|
|
11
11
|
}
|
package/docker/run.ts
CHANGED
|
@@ -21,12 +21,12 @@ try { privateEnv = parseEnv(readFileSync(3, 'utf8')) } catch (error) {
|
|
|
21
21
|
if ((error as NodeJS.ErrnoException).code !== 'EINVAL' && (error as NodeJS.ErrnoException).code !== 'EBADF') throw error
|
|
22
22
|
} finally { try { closeSync(3) } catch {} }
|
|
23
23
|
for (const [key, value] of Object.entries(privateEnv)) {
|
|
24
|
-
if (value !== undefined && ['TELEGRAM_BOT_TOKEN', 'GEMINI_API_KEY', 'OPENAI_API_KEY'].includes(key)) process.env[key] = value
|
|
24
|
+
if (value !== undefined && ['TELEGRAM_BOT_TOKEN', 'GEMINI_API_KEY', 'OPENAI_API_KEY', 'EZ_CHANNEL_BACKEND_TOKEN'].includes(key)) process.env[key] = value
|
|
25
25
|
}
|
|
26
26
|
privateEnv = {}
|
|
27
27
|
const [command = 'start', ...args] = process.argv.slice(2)
|
|
28
28
|
process.argv = [process.argv[0], '', ...args]
|
|
29
|
-
if (['start', 'smoke'].includes(command)) await recoverInterruptedRuns(loadConfig().controlDir)
|
|
29
|
+
if (['start', 'smoke'].includes(command)) await recoverInterruptedRuns(loadConfig().controlDir, Boolean(loadConfig().channelBackendUrl))
|
|
30
30
|
if (command === 'start') {
|
|
31
31
|
const relay = createRelay(loadConfig())
|
|
32
32
|
const heartbeat = '/state/control/heartbeat.json'
|
|
@@ -1,14 +1,116 @@
|
|
|
1
1
|
# Authority boundaries
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
The core pairs one Telegram owner and owns authority across providers. Plugins
|
|
4
|
+
provide transport, authentication, capture, and receipts. Their exposure metadata
|
|
5
|
+
is discovery information, never a grant. A CRM can contain external text too;
|
|
6
|
+
marking it internal does not confer owner authority.
|
|
7
|
+
|
|
8
|
+
## Messaging v1
|
|
9
|
+
|
|
10
|
+
The owner asks the agent to contact one person for a bounded purpose. The owner
|
|
11
|
+
agent prepares a proposal with `ezenciel-agents-task propose`: registered source,
|
|
12
|
+
exact canonical contact, purpose, explicitly shareable context, and expiry (up to
|
|
13
|
+
72 hours). Telegram displays that exact proposal for approval. The core binds it
|
|
14
|
+
to the verified owner, current source registration, and connected account. The
|
|
15
|
+
owner does not edit JSON. The agent uses `list` and `revoke` when asked.
|
|
16
|
+
|
|
17
|
+
After approval the relay starts a restricted task, including the initial outgoing
|
|
18
|
+
message. Incoming-only tasks instead wait for new correspondence and never create
|
|
19
|
+
an opening run. Their task records use version 2 so older task readers fail closed. Matching new correspondence resumes that task in a fresh native session.
|
|
20
|
+
Other contacts remain blocked. Each task has at most 30 distinct text sends;
|
|
21
|
+
there are no payments, attachments, extra recipients, plugin installation,
|
|
22
|
+
settings changes, or access to owner memory. A contact can have one active or
|
|
23
|
+
pending task at a time. Completed, revoked, expired, replaced-source, and changed-
|
|
24
|
+
account grants cannot dispatch further messages.
|
|
25
|
+
|
|
26
|
+
The worker receives only the approved dossier, its notes, its operation receipts,
|
|
27
|
+
and rechecked correspondence for its contact. All dossier contents may be shared
|
|
28
|
+
with that contact. The agent judges how to pursue the purpose; code does not prove
|
|
29
|
+
that each sentence serves the booking or that a correspondent is truthful. A
|
|
30
|
+
prompt injection can still derail a task or elicit its shared context. It cannot
|
|
31
|
+
use the provided tools to read owner files or select another destination.
|
|
32
|
+
|
|
33
|
+
## Native execution and core tools
|
|
34
|
+
|
|
35
|
+
V1 uses audited Codex CLI **0.153.4** for task work, regardless of the owner's
|
|
36
|
+
selected executor. Missing or different versions fail closed; upgrading this pin
|
|
37
|
+
requires repeating the native tool inventory test. Owner work retains its normal
|
|
38
|
+
executor. The task runner creates a fresh ephemeral home/session, skips user
|
|
39
|
+
config, rules and ancestor project instructions, and disables shell, file/image,
|
|
40
|
+
browser, apps, hooks, memory and agent spawning tools. A native permissions
|
|
41
|
+
profile denies general filesystem access and tool network access. No owner
|
|
42
|
+
workspace or conversation is passed to this runner. The runner uses the pinned
|
|
43
|
+
CLI's bundled model catalog with task-specific tool defaults: direct MCP calls,
|
|
44
|
+
no model-added patch tools, experimental tools or collaboration, and no deferred
|
|
45
|
+
tool discovery. Model metadata can override feature flags, so flags alone are
|
|
46
|
+
insufficient. The native inventory test uses a real bundled model entry and must
|
|
47
|
+
prove the five bounded task tools work without additional action tools.
|
|
48
|
+
|
|
49
|
+
A core stdio MCP broker exposes `context`, `send`, `note`, `report`, and `complete`.
|
|
50
|
+
The native client also lists resource helpers, but the broker serves no resources.
|
|
51
|
+
Only these five tools have native approval bypass configured: the core rechecks
|
|
52
|
+
the grant on each call. Broker requests cross host/relay through private atomic
|
|
53
|
+
control files; only the relay dispatches provider writes. Model tool arguments
|
|
54
|
+
never choose a recipient, account, control path, shell command, or permission.
|
|
55
|
+
The native harness and broker are trusted processes; this is model-tool
|
|
56
|
+
containment, not isolation from a malicious native executable or local host user.
|
|
57
|
+
Native configuration reference: [Codex configuration](https://learn.chatgpt.com/docs/config-file/config-reference).
|
|
58
|
+
|
|
59
|
+
Task notes live under protected `control/tasks/`, separate from the owner mind.
|
|
60
|
+
Reports go to the owner's Telegram outbox, visibly labelled as task reports;
|
|
61
|
+
they are not inserted as owner instructions or trusted memory. The owner mind
|
|
62
|
+
keeps its existing `inbox/` and `work/` organization. No database or general memory
|
|
63
|
+
index is introduced.
|
|
64
|
+
|
|
65
|
+
## Provider protocol
|
|
66
|
+
|
|
67
|
+
A registered Unix event source advertises `taskProtocol: "message-v1"` and a
|
|
68
|
+
stable string `accountId` in `events-head`. The core calls `task-watch` with that
|
|
69
|
+
account, exact `conversationId`, and expiry to request bounded capture attention.
|
|
70
|
+
The existing events/events-check protocol supplies incoming messages. This
|
|
71
|
+
subscription grants attention only; execution still requires the core grant.
|
|
72
|
+
|
|
73
|
+
For sends the core supplies `task-send` with those same bound IDs, text, and a
|
|
74
|
+
core-prefixed idempotency key. The adapter checks account consistency immediately
|
|
75
|
+
before provider dispatch and returns `{accountId, conversationId, key, state:
|
|
76
|
+
"accepted", receiptId}`. Acceptance is not delivery or booking confirmation.
|
|
77
|
+
The WhatsApp adapter implements this protocol for individual contacts. Another
|
|
78
|
+
provider, including a Gmail/Composio adapter, can implement the same transport
|
|
79
|
+
contract without implementing authority policy; those adapters are not supplied
|
|
80
|
+
by this change.
|
|
81
|
+
|
|
82
|
+
Before dispatch the core durably records an uncertain operation. A validated
|
|
83
|
+
receipt changes it to accepted. Crashes, timeouts, malformed receipts and lost
|
|
84
|
+
responses remain uncertain; replaying the same key does not send again, and a
|
|
85
|
+
changed payload under the same key is rejected. The agent must report uncertainty
|
|
86
|
+
for owner inspection. V1 has no automatic uncertain-send reconciliation. Core
|
|
87
|
+
revocation and send acceptance are serialized; revocation cannot undo a message
|
|
88
|
+
already dispatched. Expired task watches may still leave captured provider
|
|
89
|
+
records, but cannot launch task work.
|
|
90
|
+
|
|
91
|
+
## Compatibility and limits
|
|
92
|
+
|
|
93
|
+
Unmatched events retain terminal `cancelled` plus
|
|
94
|
+
`external-execution-unavailable`. Task runs use record version 2; older readers
|
|
95
|
+
reject/skip them instead of executing them with owner access. Existing version-1
|
|
96
|
+
owner runs remain readable. Package state schema stays 1, but rollback suspends
|
|
97
|
+
task processing until a task-aware version returns; rollback does not replay
|
|
98
|
+
messages or erase task receipts. Update host and relay together. A missing or
|
|
99
|
+
outdated host fails task launch closed.
|
|
100
|
+
|
|
101
|
+
The installing host user remains trusted and can modify local control state.
|
|
102
|
+
Owner runs can use the plugin manager and Docker administration; content read
|
|
103
|
+
inside owner work still depends on native protections and agent judgment. This
|
|
104
|
+
is not a public multi-tenant execution service. Family delegation, payments,
|
|
105
|
+
enterprise reviewer agents, arbitrary file sharing, and other restricted native
|
|
106
|
+
executors are deferred.
|
|
107
|
+
|
|
108
|
+
Verify with `pnpm verify`, Docker test/runtime targets and smoke fixtures.
|
|
109
|
+
`EZ_TEST_NATIVE_TASKS=1 pnpm exec tsx --test test/task-native.test.ts` checks the
|
|
110
|
+
actual pinned native tool inventory and executes a synthetic model/broker/provider
|
|
111
|
+
conversation without real credentials or external sends. Real Telegram-owner to
|
|
112
|
+
WhatsApp-correspondent acceptance remains separate live QA requiring an
|
|
113
|
+
authorized account/contact.
|
|
114
|
+
|
|
115
|
+
For plain-language intent, onboarding defaults and source setup see
|
|
116
|
+
[selective monitoring](../selective-monitoring.md).
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Local event sources
|
|
2
2
|
|
|
3
|
-
A plugin owns capture, authentication and
|
|
3
|
+
A plugin owns capture, authentication and subscription filtering. The core owns
|
|
4
|
+
authority and execution; subscription filtering cannot grant permissions.
|
|
4
5
|
Register through `ezenciel-agents-source --name NAME --socket /absolute/service.sock`;
|
|
5
6
|
inspect with `--list`, remove with `--name NAME --remove`. Registration pins the
|
|
6
7
|
paired owner, assigns a new binding ID and starts at the provider's current head.
|
|
7
|
-
|
|
8
|
-
not
|
|
8
|
+
Registered plugins are trusted installed code. Private Unix sockets bind a local
|
|
9
|
+
transport, not an authorization claim from message content or an adversarial sandbox.
|
|
9
10
|
|
|
10
11
|
POST JSON `{ "command": "...", "args": {} }` to `/` over the Unix socket.
|
|
11
12
|
Return HTTP 200 with `{ "ok": true, "data": ... }`:
|
|
@@ -19,7 +20,8 @@ Return HTTP 200 with `{ "ok": true, "data": ... }`:
|
|
|
19
20
|
Each event is `{id, conversationId, receivedAt, text}`. IDs are at most 100 ASCII
|
|
20
21
|
letters/digits/underscore/hyphen; conversation IDs at most 200 characters;
|
|
21
22
|
receivedAt is epoch milliseconds; text at most 16000 characters. Responses are
|
|
22
|
-
bounded to 256 KiB and three seconds.
|
|
23
|
+
bounded to 256 KiB and three seconds. Provider capture/filtering stays in the plugin;
|
|
24
|
+
execution authority stays in the core.
|
|
23
25
|
|
|
24
26
|
The host polls each second and waits for two seconds of quiet, ten seconds of
|
|
25
27
|
age, or ten events. It groups by conversation and persists the batch before
|
|
@@ -29,6 +31,9 @@ creation; it does not promise exactly-once external actions after executor failu
|
|
|
29
31
|
|
|
30
32
|
Before starting queued work, recheck binding, owner and provider eligibility.
|
|
31
33
|
Unavailable sources keep work queued; removed subscriptions cancel empty runs.
|
|
32
|
-
No check can retract work already started.
|
|
33
|
-
|
|
34
|
-
the
|
|
34
|
+
No check can retract work already started. Eligible external runs are now recorded
|
|
35
|
+
as blocked (`external-execution-unavailable`), with no executor launch. They do
|
|
36
|
+
not borrow the owner workspace, session or tools. The existing source cursor and
|
|
37
|
+
deduplication remain intact; blocked work does not retry automatically. Work status
|
|
38
|
+
shows the blocked count. No provider SDK or provider-specific authority is imported.
|
|
39
|
+
See [authority boundaries](authority-boundaries.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Application channel backend
|
|
2
|
+
|
|
3
|
+
An optional backend receives owner-authorized Telegram turns instead of a CLI.
|
|
4
|
+
The existing private-chat gate, inbox batching, media downloads and outbox remain
|
|
5
|
+
in use. This is still one approved owner per bot, not shared-bot tenant routing.
|
|
6
|
+
|
|
7
|
+
Set `EZ_CHANNEL_BACKEND_URL` to an HTTPS endpoint and put
|
|
8
|
+
`EZ_CHANNEL_BACKEND_TOKEN` in the private relay env file. For Compose also set
|
|
9
|
+
`EZ_EXECUTOR_TRANSPORT=backend`; no host CLI heartbeat is required. The existing
|
|
10
|
+
workspace/control paths and pairing remain mandatory. Loopback HTTP is allowed
|
|
11
|
+
for isolated local tests. Redirects and URL credentials are rejected.
|
|
12
|
+
|
|
13
|
+
POST carries version 1, channel `telegram`, stable `event_id`, numeric strings
|
|
14
|
+
`sender_id` / `chat_id`, and `items` with text, message_id, sent_at, album_id and
|
|
15
|
+
optional attachment `{type,data}` (base64, at most 12 MB decoded per item).
|
|
16
|
+
The application must bound and validate the whole body, bind sender identity to
|
|
17
|
+
its authenticated account, and deduplicate the stable event ID before actions.
|
|
18
|
+
It must support up to ten normalized items, including a received album.
|
|
19
|
+
|
|
20
|
+
The response is `{status,reply}`. `queued` or `running` yields and resubmits the
|
|
21
|
+
same event after four seconds. `complete` or `failed` supplies a final string
|
|
22
|
+
reply, queued once with a deterministic outbox ID. Backend completion and
|
|
23
|
+
Telegram delivery are separate; an uncertain send never replays the operation.
|
|
24
|
+
Restart resubmits interrupted backend runs using the original ID. Permanent
|
|
25
|
+
4xx responses leave a failed relay run for operator inspection; transient
|
|
26
|
+
failures back off. No response body or credential is logged.
|
|
27
|
+
|
|
28
|
+
Native `/new` and AI settings belong to the application. `/stop` cannot cancel
|
|
29
|
+
an application job and explicitly reports that limitation. `/cancel` removes
|
|
30
|
+
pending relay work only; it does not undo work already accepted by a backend.
|
|
31
|
+
External plugin/maintenance wakes do not dispatch through this channel backend.
|
|
32
|
+
|
|
33
|
+
Backend mode handles owner Telegram intake only. Existing native schedules,
|
|
34
|
+
restricted messaging tasks, plugin events and maintenance prompts are not
|
|
35
|
+
forwarded to the application and cannot launch a fallback CLI. Their scheduling
|
|
36
|
+
and authority remain separate from the application's own job lifecycle.
|
package/docs/local-qa.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Local feature QA
|
|
2
|
+
|
|
3
|
+
Use the existing local-tarball updater for unreleased features. No npm publication,
|
|
4
|
+
registry server, new daemon or runtime upgrade flow is needed. A developer stages
|
|
5
|
+
an immutable candidate in the PA's local tools directory. The agent reads its
|
|
6
|
+
manifest and uses `ez updates prepare main --file ...` and `apply` on the owner's
|
|
7
|
+
request. Automatic public update policy stays unchanged.
|
|
8
|
+
|
|
9
|
+
Stage from a clean, reviewed feature checkout:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
node scripts/stage-qa.mjs --source /absolute/feature-checkout \
|
|
13
|
+
--catalog /absolute/deployment/tools/qa --label beta-12 \
|
|
14
|
+
--version 0.1.0-beta.12.qa.1 --flow /absolute/feature-QA.md
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`beta-12` is a local catalog label, distinct from public `0.1.0-beta.12`. The
|
|
18
|
+
manifest records the private version, source commit and archive SHA-256. Only
|
|
19
|
+
package version and `ezQa` provenance metadata differ from the source npm package.
|
|
20
|
+
Do not publish these private archives. Each new candidate gets a new label and a
|
|
21
|
+
version newer than the PA's installed version; labels are never overwritten.
|
|
22
|
+
|
|
23
|
+
Add the catalog path and the following instructions to the PA's local TOOLS.md:
|
|
24
|
+
|
|
25
|
+
> When the owner requests a beta number, first inspect the matching beta-N entry
|
|
26
|
+
> in the local QA catalog. Read manifest.json and QA.md. Check the archive SHA-256
|
|
27
|
+
> against the manifest, then prepare that local file using the existing updater.
|
|
28
|
+
> Verify the returned version and hash match. Apply only on the owner's explicit
|
|
29
|
+
> upgrade request, without --automatic, and finish the turn so the supervisor can
|
|
30
|
+
> replace the runtime. On completion, inspect the receipt and both loaded host and
|
|
31
|
+
> relay versions before reporting success. Provide the feature's short QA flow.
|
|
32
|
+
> If no local entry exists, report that; do not silently substitute a public beta.
|
|
33
|
+
> Follow an explicit request for a public npm release separately.
|
|
34
|
+
|
|
35
|
+
For every feature, the developer handoff includes the beta label, exact private
|
|
36
|
+
version, source commit, checks completed and a short user-facing QA flow with
|
|
37
|
+
expected results. Include any limitations. A prepared archive or healthy service
|
|
38
|
+
does not prove the feature works; read back its result through the PA.
|
|
39
|
+
|
|
40
|
+
Before handing off a candidate, extract it into a fresh directory, copy
|
|
41
|
+
docker/pnpm-lock.yaml to pnpm-lock.yaml, run frozen install and applicable tests,
|
|
42
|
+
and prepare it through the target PA's updater. Preparation validates admission
|
|
43
|
+
without stopping services. Preserve that receipt for the agent to inspect, and
|
|
44
|
+
leave application to the owner's chat request. If deployment/schema compatibility
|
|
45
|
+
fails, fix or review the migration; never bypass the updater's checks.
|