@jc_stack/ez-agents 0.1.0-beta.49 → 0.1.0-beta.51
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/AGENTS.md +8 -6
- package/CHANGELOG.md +26 -0
- package/README.md +9 -5
- package/bin/ez +16 -7
- package/docs/architecture/ai-selection.md +4 -0
- package/docs/docker-runtime.md +28 -2
- package/docs/plugin-guide.md +3 -3
- package/docs/plugins.md +13 -10
- package/docs/scheduling.md +62 -22
- package/docs/setup.md +2 -2
- package/docs/standalone-cli.md +4 -4
- package/docs/upgrades.md +16 -15
- package/package.json +4 -2
- package/src/agent-guidance.ts +2 -2
- package/src/ai-cli.ts +4 -2
- package/src/ai.ts +17 -5
- package/src/application-channel.ts +12 -8
- package/src/control-state.ts +10 -4
- package/src/delivery-socket-client.mjs +15 -1
- package/src/delivery-socket.ts +18 -4
- package/src/executor.ts +63 -10
- package/src/host-executor-client.ts +23 -8
- package/src/host-executor.ts +63 -32
- package/src/index.ts +157 -35
- package/src/menu.ts +3 -2
- package/src/plugins/manager.mjs +43 -40
- package/src/plugins/workspace-lease.mjs +1 -1
- package/src/runs.ts +67 -3
- package/src/schedule-cli.ts +24 -15
- package/src/scheduled-tasks.ts +53 -17
- package/src/scheduler.ts +7 -8
- package/src/session-close.ts +17 -0
- package/src/setup-fallback.ts +62 -25
- package/src/tools-line.d.mts +1 -0
- package/src/tools-line.mjs +3 -0
- package/src/updates/binding.mjs +4 -10
- package/src/updates/control.mjs +3 -2
- package/src/updates/runtime.mjs +25 -9
- package/src/updates/supervisor.mjs +105 -16
- package/src/workspace.ts +12 -1
- package/test/ai-cli.test.ts +23 -1
- package/test/ai.test.ts +72 -4
- package/test/application-channel.test.ts +24 -0
- package/test/application-controls.test.ts +4 -1
- package/test/delivery-transport.test.ts +41 -2
- package/test/executor.test.ts +32 -4
- package/test/host-executor.test.ts +70 -3
- package/test/intake-relay.test.ts +71 -3
- package/test/model-policy.test.ts +1 -1
- package/test/plugin-broker.test.mjs +48 -1
- package/test/plugin-manager.test.mjs +52 -38
- package/test/run-handoff.test.ts +136 -0
- package/test/schedule-cli.test.ts +24 -1
- package/test/scheduled-tasks.test.ts +45 -15
- package/test/session-close.test.ts +17 -0
- package/test/setup-fallback.test.ts +194 -16
- package/test/updates.test.mjs +55 -12
- package/test/workspace.test.ts +6 -0
package/AGENTS.md
CHANGED
|
@@ -16,11 +16,13 @@ through one generic transport, with separate agent workspaces and sessions.
|
|
|
16
16
|
|
|
17
17
|
## Operating-agent instructions
|
|
18
18
|
|
|
19
|
-
Do not inject a coding handbook into production agent minds. The
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
Do not inject a coding handbook into production agent minds. The only
|
|
20
|
+
package-written `AGENTS.md` text is the initial workspace seed: purpose, a short
|
|
21
|
+
engine-neutral `memory/` note and one static line pointing at
|
|
22
|
+
`ez tools list --details`. After seeding the agent owns `AGENTS.md`; inventory is
|
|
23
|
+
generated on read, so do not hardcode command recipes. The one transport-level
|
|
24
|
+
lifecycle prompt is the silent conversation-close turn (`src/session-close.ts`),
|
|
25
|
+
which runs only for owner conversations. Coding defaults for this repository stay in this file and
|
|
24
26
|
CONTRIBUTING.md.
|
|
25
27
|
|
|
26
28
|
## Installing this package
|
|
@@ -105,7 +107,7 @@ If no answer identifies a material blocker, finish. Do not start another review
|
|
|
105
107
|
round merely to seek more criticism, tests or hypothetical edge cases.
|
|
106
108
|
|
|
107
109
|
## 3. Crash-Safe Disk State
|
|
108
|
-
- Persistent authority state (`ControlStore` owner/pairing/sessions/presets, application bindings, schedule definitions) stays in disk-backed JSON files. Run/outbox/inbox bookkeeping is relay memory reached through the delivery socket (`src/delivery-socket.ts`): a restart drops in-flight runs and queued deliveries by design, uncertain sends report unknown rather than success, and Telegram redelivery is the replay mechanism. The live delivery socket also replaces the relay flock as the single-relay guard.
|
|
110
|
+
- Persistent authority state (`ControlStore` owner/pairing/sessions/presets, application bindings, schedule definitions) stays in disk-backed JSON files. Run/outbox/inbox bookkeeping is relay memory reached through the delivery socket (`src/delivery-socket.ts`): a restart drops in-flight runs and queued deliveries by design, uncertain sends report unknown rather than success, and Telegram redelivery is the replay mechanism. The one exception: a graceful stop hands running host-transport runs, whose executors outlive the relay, to the next relay through private `control/run-handoff.json`; that relay adopts and watches them and never relaunches one. The live delivery socket also replaces the relay flock as the single-relay guard.
|
|
109
111
|
- **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.
|
|
110
112
|
- **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 (`..`).
|
|
111
113
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.51
|
|
4
|
+
|
|
5
|
+
- Keep native runs alive during handoff-compatible core upgrades. The retiring executor finishes existing work while its replacement accepts new requests; the relay adopts runs without replay and preserves schedule holds, cancellation and settled delivery receipts. Isolated agents migrate once while idle to a separate, unprivileged executor container; later compatible upgrades preserve its running generation. Older runtimes without the handoff contract retain the full native-work drain.
|
|
6
|
+
- Prepare compatible-upgrade workspace and native-client backups before pausing admission or stopping the relay, so large histories do not extend the swap outage. Rollback restores code and configuration without rewinding active native state.
|
|
7
|
+
- Make stop requests reach handed-off native work even while its observer is starting. A failed cancellation write leaves the observer active and reports the failed stop request.
|
|
8
|
+
- Show paused schedules in `/tasks` and task details with a paused label and no next run. Editing a paused schedule keeps it paused.
|
|
9
|
+
- Replace the oldest unused saved AI choice when the saved-choice limit is reached, preserving the selected, default and recently used choices.
|
|
10
|
+
- Use the isolated native client's own catalog for AI selection, so available models such as Sonnet appear without relying on a host catalog.
|
|
11
|
+
|
|
12
|
+
Beta limits: live fresh-host/reboot acceptance and live compatible upgrades during active native work on the VM and Mac remain pending. Synthetic container lifecycle checks and an idle EzStocks OVH private-candidate upgrade passed; these do not establish live active-run or provider acceptance. Publication does not establish an installed-agent upgrade or authorize provider/domain writes.
|
|
13
|
+
|
|
14
|
+
## 0.1.0-beta.50
|
|
15
|
+
|
|
16
|
+
- Share memory across engines through the workspace. New workspaces seed a short `## Memory` note (durable facts in `memory/`, one per file, indexed in `memory/MEMORY.md`); every engine loads `AGENTS.md`, so Codex and Claude read the same files. When a chat conversation is replaced (`/new`, the New button, web app new, or an AI/provider switch), its engine gets one silent turn in that native session to save durable facts; nothing is delivered and the engine decides what to write.
|
|
17
|
+
- Drop the managed `<!-- ez tools -->` AGENTS.md footer. The install seed writes one static line ("Tools: run `ez tools list --details`; each plugin's `--help` gives usage."); update binding replaces an existing footer with that line once and otherwise never writes instructions. `ez tools list --details` now prints per plugin `aliases`, `purpose`, `skills`, `skillReads` and one `example` call (optional manifest `example`, else `<alias> --help`).
|
|
18
|
+
- Stop reporting a live plugin call as a stale lease on isolated agents. Leases record their PID namespace; a lease written in the broker container is judged by its call container's running state, so `ez updates apply` no longer refuses during in-flight calls. Apply and recover only queue the job; main activation now drains in-flight calls before switching instead of failing.
|
|
19
|
+
- Keep host-capable native runs alive across a graceful relay restart. The stopping relay hands running host-transport runs to the next relay instead of cancelling them; it adopts and watches each run with its schedule hold, concurrency slot and message delivery, and records its exit status. Nothing is relaunched; a run that cannot be adopted or whose outcome is unknown is recorded failed and interrupted, holding its schedule, and a run that fails adoption is asked to stop. Core upgrades still wait for idle native work; isolated agents, crashes and queued work keep the previous behavior.
|
|
20
|
+
- Continue a scheduled occurrence on its next saved login when Claude stops at a usage/session limit after work began. The next setup starts a fresh native session with the saved prompt plus one factual line naming the run, stopped setup, its native session and reset time. Only a typed quota rejection the run ended on continues; work after a limit, access denial, other failures, cancellation and unknown outcomes still stop without replay. Attempts record `quota-after-work-began` and each Ez-assigned session ID.
|
|
21
|
+
- Stop isolated plugin calls hanging silently before reaching the broker when the caller leaves a socket stdin open (native engine shells, async Node spawns). Pipes and files are still read to EOF; a socket that sends nothing for one second is treated as no input. Remove a uniquely named call container whenever Compose exits non-zero, so a call that stops between create and start no longer strands it in `Created`.
|
|
22
|
+
- Advance scheduled work to its next saved login after a typed authentication rejection before work. Recognize Claude API-error metadata without marking analysis started; account/organization denials, hooks, model/tool work and uncertain outcomes still stop fallback. Preserve the saved chain for the next occurrence.
|
|
23
|
+
- Let scheduled fallback setups name an owner-provisioned native login profile per setup, with no fixed list length. A quota rejection skips later setups on the same login profile while other accounts stay eligible; model/tool/hook work, access denial, cancellation and uncertain failures still stop the chain.
|
|
24
|
+
- Show each scheduled task's frequency and full primary → fallback path (CLI, login profile, provider, model, effort) in Telegram `/tasks` and `/tasks N`.
|
|
25
|
+
- Report the exact AI selection error (missing client models, missing model, unsupported effort) instead of a generic invalid selection, without changing the saved choice.
|
|
26
|
+
|
|
27
|
+
Beta limits: live fresh-host/reboot acceptance, a live host-capable relay recreate during a running native run, two-real-account channel switching and real exhausted-account/provider rejection remain pending. An adopted scheduled occurrence does not advance to fallback setups, and messages a run sends while no relay is up still fail. The conversation close turn is engine-decided and runs only for owner conversations; existing workspaces do not receive the `## Memory` seed note automatically. Plugin manifests may declare `example`; a plugin that does requires core 0.1.0-beta.50 or newer, and older cores refuse that plugin update (manifest validation) and keep the installed version until core upgrades. Publication does not establish an installed-agent upgrade or authorize provider/domain writes.
|
|
28
|
+
|
|
3
29
|
## 0.1.0-beta.49
|
|
4
30
|
|
|
5
31
|
- Upgrade compatible plugins while native work continues, draining in-flight plugin calls for activation and preserving rollback. Core updates still wait for idle.
|
package/README.md
CHANGED
|
@@ -172,7 +172,8 @@ transport, with separate agent workspaces and sessions. See
|
|
|
172
172
|
|
|
173
173
|
Telegram `/status` shows the running relay and host versions plus installed
|
|
174
174
|
plugin versions. Its read-only **Scheduled tasks** control lists the paired
|
|
175
|
-
owner's active schedules in alphabetical order, with their
|
|
175
|
+
owner's active schedules in alphabetical order, with their frequency/timezone
|
|
176
|
+
and ordered CLI/login-profile/model/effort execution path (primary → fallbacks),
|
|
176
177
|
next occurrence or current queued/running state, and a short preview of the
|
|
177
178
|
saved invocation prompt; it never creates, changes or runs a task. The agent's `ez status` adds verified plugin
|
|
178
179
|
runtime states and upgrade job receipts. See [status and upgrades](docs/upgrades.md).
|
|
@@ -260,11 +261,14 @@ restart and shutdown. Host-capable agents register the small host CLI transport
|
|
|
260
261
|
|
|
261
262
|
The selected host CLI runs in this agent's persistent workspace and owns its
|
|
262
263
|
Markdown/work files. New conversations and AI changes preserve those files.
|
|
263
|
-
One compact `AGENTS.md` is seeded with the installer purpose
|
|
264
|
-
|
|
264
|
+
One compact `AGENTS.md` is seeded with the installer purpose, a short note to
|
|
265
|
+
keep durable facts in `memory/` (one fact per file, indexed in `memory/MEMORY.md`)
|
|
266
|
+
and one line pointing at `ez tools list --details`. Every engine loads `AGENTS.md`,
|
|
267
|
+
so they share that memory. When a chat conversation is replaced (new conversation
|
|
268
|
+
or AI/provider change), its engine gets one silent turn to save durable facts. The installing LLM authors the initial brief from the owner's request
|
|
265
269
|
and known application context; Ez stores it without trying to infer the product
|
|
266
|
-
itself. Installed plugin
|
|
267
|
-
`ez tools list --details
|
|
270
|
+
itself. Installed plugin aliases, purposes, skills and examples come from
|
|
271
|
+
`ez tools list --details`; no tool inventory file needs maintenance.
|
|
268
272
|
Credentials and control state stay outside the mind. Isolated agents use relay
|
|
269
273
|
mounts as the filesystem boundary. Host-capable file separation is not OS
|
|
270
274
|
isolation against a process running as the same user.
|
package/bin/ez
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createConnection } from 'node:net';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { fstatSync } from 'node:fs';
|
|
4
5
|
import { fileURLToPath } from 'node:url';
|
|
5
6
|
import { installedNativeHelp } from '../src/plugins/native-tasks.mjs';
|
|
6
7
|
|
|
7
8
|
const MAX_STDIN_BYTES = 1024 * 1024;
|
|
9
|
+
const SOCKET_STDIN_IDLE_MS = 1000;
|
|
8
10
|
const RUN_ID = /^[A-Za-z0-9_-]{1,160}$/;
|
|
9
11
|
const socketPath = process.env.EZ_PLUGIN_BROKER_SOCKET?.trim();
|
|
10
12
|
const runId = process.env.EZ_RUN_ID?.trim();
|
|
@@ -50,18 +52,25 @@ const request = (payload, signal = undefined) => new Promise((resolve, reject) =
|
|
|
50
52
|
socket.on('connect', () => socket.write(JSON.stringify(payload) + '\n'));
|
|
51
53
|
});
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
// Pipes and files always end, so read them to EOF. A socket is how Node-based
|
|
56
|
+
// parents (native engine shells, agent scripts) wire child stdio, and they
|
|
57
|
+
// often pass it on without ever closing it. Waiting for that EOF hung calls
|
|
58
|
+
// before the broker was contacted, so a socket that stays silent means no input.
|
|
59
|
+
const readStdin = () => new Promise((resolve, reject) => {
|
|
60
|
+
if (process.stdin.isTTY) return resolve('');
|
|
55
61
|
const chunks = [];
|
|
56
62
|
let bytes = 0;
|
|
57
|
-
|
|
63
|
+
const idle = fstatSync(0).isSocket() ? setTimeout(() => { process.stdin.destroy(); resolve(''); }, SOCKET_STDIN_IDLE_MS) : undefined;
|
|
64
|
+
process.stdin.on('data', chunk => {
|
|
65
|
+
clearTimeout(idle);
|
|
58
66
|
const value = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
59
67
|
bytes += value.length;
|
|
60
|
-
if (bytes > MAX_STDIN_BYTES)
|
|
68
|
+
if (bytes > MAX_STDIN_BYTES) { process.stdin.destroy(); return reject(Error('plugin stdin exceeds the broker limit')); }
|
|
61
69
|
chunks.push(value);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
};
|
|
70
|
+
});
|
|
71
|
+
process.stdin.once('end', () => { clearTimeout(idle); resolve(Buffer.concat(chunks).toString('utf8')); });
|
|
72
|
+
process.stdin.once('error', error => { clearTimeout(idle); reject(error); });
|
|
73
|
+
});
|
|
65
74
|
|
|
66
75
|
const invoke = async (args, stdin) => {
|
|
67
76
|
if (!runId || !RUN_ID.test(runId)) throw Error('EZ_RUN_ID is required for an isolated plugin command');
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
`menu.ts` owns Telegram controls; `ai.ts` projects installed-client metadata.
|
|
4
4
|
`ControlStore` stores saved presets, the next-conversation default and current choice.
|
|
5
5
|
No provider SDK, model fallback, history migration, or second execution loop.
|
|
6
|
+
A missing catalog entry rejects selection without changing the conversation.
|
|
7
|
+
An unsupported effort reports the exact installed model's advertised levels.
|
|
8
|
+
Missing selectable models do not establish that the active native session is
|
|
9
|
+
unavailable, and editing a scheduled task does not change the chat selection.
|
|
6
10
|
|
|
7
11
|
At intake each journal entry receives an immutable preset and conversation ID.
|
|
8
12
|
Batches cannot cross that boundary. The run copies that choice and invokes the
|
package/docs/docker-runtime.md
CHANGED
|
@@ -141,6 +141,17 @@ An interrupted Telegram poller keeps authorized work and in-flight deliveries
|
|
|
141
141
|
alive while it retries intake. Run and delivery records are relay memory: a
|
|
142
142
|
relay process restart drops in-flight work by design, Telegram redelivery is the
|
|
143
143
|
intake replay path, and uncertain sends report unknown instead of success.
|
|
144
|
+
Host-capable agents are the exception for running work: their native CLIs run under
|
|
145
|
+
the host transport, so a graceful relay stop (Compose recreate, Docker restart)
|
|
146
|
+
writes those running runs to private `control/run-handoff.json` instead of
|
|
147
|
+
cancelling them. The next relay adopts each one, keeps its concurrency slot and
|
|
148
|
+
schedule hold, accepts its messages and records its exit status; it never
|
|
149
|
+
relaunches a run. A run whose outcome is unknown (no request, claim or result
|
|
150
|
+
record, or an adoption failure) is recorded failed and interrupted, which holds
|
|
151
|
+
its schedule until an explicit edit; the host is asked to cancel it. A request
|
|
152
|
+
the host had not claimed before the relay stopped is failed by the host without
|
|
153
|
+
starting. Records older than 24 hours are ignored, and the file is removed on
|
|
154
|
+
every start. Queued runs are still dropped. Accepted deliveries are drained before listener shutdown completes; settled receipts for handed-off runs move with the handoff. A connection failure during a declared upgrade swap waits for the relay; an accepted timeout is never retried.
|
|
144
155
|
A `409 Conflict` still requires the operator to stop the competing poller; it
|
|
145
156
|
remains unhealthy after the startup grace. Pending and uncertain deliveries keep
|
|
146
157
|
their existing receipt semantics; executor stdout is not replayed as a reply.
|
|
@@ -196,8 +207,12 @@ The image pins modern Docker Compose, including its standalone executable, so
|
|
|
196
207
|
existing operator-created volumes retain their data and driver bindings.
|
|
197
208
|
|
|
198
209
|
The relay-side `ez` client sends only declared aliases, literal argument arrays,
|
|
199
|
-
bounded stdin, and the active run ID to the broker.
|
|
200
|
-
|
|
210
|
+
bounded stdin, and the active run ID to the broker. It reads a piped or
|
|
211
|
+
redirected stdin to EOF. A socket stdin, which native engine shells and Node
|
|
212
|
+
child processes often leave open, counts as no input unless data arrives within
|
|
213
|
+
one second; write and close it immediately when a command needs input. The
|
|
214
|
+
broker re-reads owner authority and the registry before every call, pins the
|
|
215
|
+
discovered revision, and
|
|
201
216
|
passes only the installed plugin's `run.application.context.plugins[plugin-id]`
|
|
202
217
|
object as `EZ_PLUGIN_CONTEXT`. Missing, revoked, cross-agent, expired, or reused
|
|
203
218
|
admissions fail closed. Timeouts, cancellation and bounded output produce a
|
|
@@ -376,3 +391,14 @@ The isolated executor image includes Node, jq and Python 3 (`python` and
|
|
|
376
391
|
remain behind installed plugins. Registered command admission waits up to five
|
|
377
392
|
seconds for the short registry lock; lifecycle changes still fail closed, and
|
|
378
393
|
no caller removes an existing lock. Serialize calls when a plugin requires it.
|
|
394
|
+
|
|
395
|
+
## Isolated executor generations
|
|
396
|
+
|
|
397
|
+
A handoff-capable supervisor runs the existing native file transport in a separate
|
|
398
|
+
unprivileged per-agent container. It derives workspace/control mounts and the image
|
|
399
|
+
from the owning relay deployment, adds only a read-only executor binding, and gives
|
|
400
|
+
the worker no relay secrets, provider secret environment or Docker socket. Native
|
|
401
|
+
credential homes stay in the existing control volume. Only the active generation
|
|
402
|
+
claims work; retired generations keep cancellation and per-run liveness until
|
|
403
|
+
their last process exits. The supervisor removes their temporary Compose resources.
|
|
404
|
+
The first upgrade from an embedded isolated executor drains before this migration.
|
package/docs/plugin-guide.md
CHANGED
|
@@ -57,14 +57,14 @@ At minimum, provide:
|
|
|
57
57
|
machine-readable commands, stable exit codes, and readback for writes.
|
|
58
58
|
|
|
59
59
|
The manager snapshots the package according to `package.json.files` plus the
|
|
60
|
-
required manifests and Docker inputs. `ez tools list --details`
|
|
60
|
+
required manifests and Docker inputs. `ez tools list --details` prints aliases, purpose, skill paths, one example call
|
|
61
|
+
(the optional manifest `example`, else `<alias> --help`) and
|
|
61
62
|
`skillReads`: literal command argument arrays for reading each installed skill.
|
|
62
63
|
Use `ez tools skill <plugin> <declared-path>` to return its text and pinned
|
|
63
64
|
revision. The isolated executor cannot open private host package paths directly;
|
|
64
65
|
the authorized broker reads only manifest-declared regular skill files, without
|
|
65
66
|
copying them into the agent workspace. Skill reads reject symlinks and files over
|
|
66
|
-
64 KiB.
|
|
67
|
-
usage snippet or per-turn instruction is needed for this read contract.
|
|
67
|
+
64 KiB. No AGENTS.md text or per-turn instruction is needed for this read contract.
|
|
68
68
|
|
|
69
69
|
Keep one canonical checkout; do not keep
|
|
70
70
|
permanent packaged copies or a second registry. See the full
|
package/docs/plugins.md
CHANGED
|
@@ -27,8 +27,7 @@ node /absolute/ezenciel_agents/bin/ezenciel-agents-tools.mjs init \
|
|
|
27
27
|
Without `--catalog`, init loads an empty packaged catalog. Keep it empty for
|
|
28
28
|
initial main onboarding; no sibling repository, broker or provider account is
|
|
29
29
|
needed. Finish owner pairing and verify an actual Telegram agent reply first.
|
|
30
|
-
Init creates a private registry and `tools/bin/ez
|
|
31
|
-
managed AGENTS.md registry locator, and binds the matching host executor to that bin
|
|
30
|
+
Init creates a private registry and `tools/bin/ez` and binds the matching host executor to that bin
|
|
32
31
|
folder. Native binaries are linked through; the package's own `bin/ez` is replaced
|
|
33
32
|
by that bound launcher, while an unrelated existing `ez` collision fails.
|
|
34
33
|
Both host-bound and isolated `ez --help` list installed native scheduling and delivery helpers
|
|
@@ -307,8 +306,10 @@ never a source of network attachment authority.
|
|
|
307
306
|
|
|
308
307
|
Commands run in one-shot client containers against their own service's volumes;
|
|
309
308
|
stdin, stdout, stderr, literal arguments and exit codes are preserved. SIGINT/
|
|
310
|
-
SIGTERM cancel the Docker call and remove its unique client container
|
|
311
|
-
|
|
309
|
+
SIGTERM cancel the Docker call and remove its unique client container; a Compose
|
|
310
|
+
exit with a non-zero status also removes it, so a call that stopped between
|
|
311
|
+
create and start never remains `Created`. The manager never retries a provider
|
|
312
|
+
operation. Service startup/restart is explicit; commands
|
|
312
313
|
never implicitly start a stopped provider service. Plugin data does not enter the
|
|
313
314
|
relay. The manager sends only a whitelist of Docker client environment variables.
|
|
314
315
|
|
|
@@ -412,10 +413,12 @@ QMD runtimes or edit installed Compose/package files to bypass missing support.
|
|
|
412
413
|
|
|
413
414
|
## Generated capability discovery
|
|
414
415
|
|
|
415
|
-
`ez tools list --details` generates a
|
|
416
|
-
`ez-plugin.json` manifests
|
|
417
|
-
|
|
418
|
-
|
|
416
|
+
`ez tools list --details` generates a self-sufficient index directly from
|
|
417
|
+
installed `ez-plugin.json` manifests. Per plugin it prints `aliases`, a one-line
|
|
418
|
+
`purpose` (the manifest `description`, at most 200 characters), absolute `skills`
|
|
419
|
+
paths and one `example` call. The optional manifest `example` is one line of at
|
|
420
|
+
most 160 characters starting with a declared command alias; without it the
|
|
421
|
+
example is `ez <first alias> --help`. Full instructions stay in the skill. Installation, replacement and removal are reflected
|
|
419
422
|
on the next read, without hooks, LLM calls or a cached inventory file.
|
|
420
423
|
|
|
421
424
|
The additive `skillReads` field supplies literal argument arrays for
|
|
@@ -424,8 +427,8 @@ installed plugin revision, declared path and skill content. Use it in isolated
|
|
|
424
427
|
agents, where host package paths are private. It reads only the installed
|
|
425
428
|
manifest's skills; arbitrary files, symlinks and files over 64 KiB are rejected.
|
|
426
429
|
|
|
427
|
-
`ez tools list` retains its alias mapping for existing clients.
|
|
428
|
-
|
|
430
|
+
`ez tools list` retains its alias mapping for existing clients. The workspace
|
|
431
|
+
seed writes one static line pointing here; nothing else is written. New workspaces do not seed
|
|
429
432
|
TOOLS.md, SOUL.md, USER.md, or an `inbox/`/`work/` layout; upgrades preserve
|
|
430
433
|
legacy notes without rewriting them. Keep owner/account policies in agent
|
|
431
434
|
instructions or linked policy files, separate from plugin metadata.
|
package/docs/scheduling.md
CHANGED
|
@@ -21,7 +21,8 @@ ezenciel-agents-schedule cancel RUN_ID
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Use `--text-file` for longer instructions. `edit ID` replaces the complete schedule
|
|
24
|
-
with a new revision; use the same trigger/name/text flags as `create`.
|
|
24
|
+
with a new revision; use the same trigger/name/text flags as `create`. It keeps
|
|
25
|
+
the paused/enabled state: an edited paused task stays paused until `resume`.
|
|
25
26
|
An edit keeps a future pending occurrence when its trigger is unchanged;
|
|
26
27
|
otherwise the edited rule begins at its next future occurrence. Editing does
|
|
27
28
|
not replay missed work from an old start date. Use `--now` to request a new
|
|
@@ -57,7 +58,7 @@ available through `ezenciel-agents-schedule context`.
|
|
|
57
58
|
|
|
58
59
|
## Ordered setup fallback
|
|
59
60
|
|
|
60
|
-
An agent task may list
|
|
61
|
+
An agent task may list ordered fallback setups after its saved one, for
|
|
61
62
|
example `claude · opus → claude · sonnet → codex · gpt-6-sol`:
|
|
62
63
|
|
|
63
64
|
```sh
|
|
@@ -68,30 +69,58 @@ ezenciel-agents-schedule create --name Daily --cli claude --model opus \
|
|
|
68
69
|
|
|
69
70
|
`edit` keeps the saved fallbacks unless `--fallback` replaces them or
|
|
70
71
|
`--clear-fallbacks` removes them. Setups must be distinct and each needs a model.
|
|
72
|
+
There is no fixed list-length limit. Each setup captures its own supported CLI,
|
|
73
|
+
model, effort and optional provider; the installed native client owns valid model
|
|
74
|
+
and effort values. The existing failure rules below apply to every setup.
|
|
75
|
+
Each fallback may include `auth-profile=NAME` to select its own owner-provisioned
|
|
76
|
+
Codex or Claude account. Omitting it selects the default login, independently of
|
|
77
|
+
the primary setup. For four separate Claude accounts, provision and log into
|
|
78
|
+
`claude2`, `claude3` and `claude4`, then use:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
ezenciel-agents-schedule create --name Daily --cli claude --model opus \
|
|
82
|
+
--cron '0 9 * * *' --timezone Asia/Dubai --text 'Prepare the daily report.' \
|
|
83
|
+
--fallback cli=claude,model=opus,auth-profile=claude2 \
|
|
84
|
+
--fallback cli=claude,model=opus,auth-profile=claude3 \
|
|
85
|
+
--fallback cli=claude,model=opus,auth-profile=claude4
|
|
86
|
+
```
|
|
71
87
|
|
|
72
88
|
Core advances to the next setup only with typed evidence that nothing ran: the
|
|
73
89
|
CLI is not installed or executable, or the provider rejected the turn before any
|
|
74
90
|
model output, tool or hook item (quota/rate limit, billing, overload, server
|
|
75
|
-
error, unavailable model or bad request)
|
|
76
|
-
|
|
91
|
+
error, unavailable model or bad request), including a missing, expired or revoked
|
|
92
|
+
login (typed authentication failure or HTTP401). Native API-error metadata does
|
|
93
|
+
not count as model/tool work. It stops, without replay, on access
|
|
94
|
+
denial (HTTP403, account or organization restriction), cancellation, relay shutdown,
|
|
77
95
|
a schedule edit, a policy or unknown error, an interrupted transport, or any
|
|
78
|
-
failure after work began. Evidence comes from structured events only: Codex
|
|
96
|
+
other failure after work began. Evidence comes from structured events only: Codex
|
|
79
97
|
native-session `codexErrorInfo` and Claude `stream-json` error fields (scheduled
|
|
80
98
|
Claude runs use `--output-format stream-json`; the host forwards only a compact
|
|
81
99
|
startup summary). Other engines advance only when their CLI is unavailable.
|
|
82
100
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
The one failure after work began that continues is a Claude usage limit the run
|
|
102
|
+
ended on (for example "You've hit your session limit"): a typed quota rejection
|
|
103
|
+
with no model, tool or hook activity after it. A native session cannot be resumed
|
|
104
|
+
under another login's configuration home, so the next eligible setup starts a fresh
|
|
105
|
+
session with the saved prompt plus one factual line naming the run, the stopped
|
|
106
|
+
setup, its native session and reset time, and that its work may be partly applied.
|
|
107
|
+
The agent reconciles from its own records; core replays nothing else.
|
|
108
|
+
|
|
109
|
+
A login or quota rejection skips the remaining setups in the same scope: the native
|
|
110
|
+
client and login profile (`claude`, `codex` including desktop and custom Codex
|
|
111
|
+
providers), `grok`, or the upstream provider for OpenCode/Pi models. A named
|
|
112
|
+
profile selects a separate owner-provisioned account and is eligible for its own
|
|
113
|
+
attempt; changing only the model in the same profile does not reset its scope.
|
|
114
|
+
Do not provision aliases of the same account to obtain more allowance. Ez does
|
|
115
|
+
not infer account independence from credentials or promise that a shared
|
|
116
|
+
organization's allowance is separate. Each setup runs at most once per occurrence.
|
|
88
117
|
|
|
89
118
|
Every attempt keeps the same run ID, occurrence, schedule revision, workspace,
|
|
90
119
|
delivery binding and scheduled concurrency slot, in a fresh native session.
|
|
91
120
|
`run RUN_ID` shows `attempts`: each setup, quota scope, outcome, failure category
|
|
92
|
-
(`cli-unavailable`, `quota`, `provider-rejected`, `access-denied`,
|
|
93
|
-
`after-work-began`, `uncertain`, or `same-quota-scope`
|
|
94
|
-
code
|
|
121
|
+
(`cli-unavailable`, `login-unavailable`, `quota`, `provider-rejected`, `access-denied`,
|
|
122
|
+
`quota-after-work-began`, `after-work-began`, `uncertain`, or `same-login-scope`/`same-quota-scope`
|
|
123
|
+
for a skipped setup), exit code, reported reset time and the session ID Ez assigned. If every setup is unavailable the run fails with
|
|
95
124
|
reason `setups-unavailable`, a summary of each setup, and an owner notice on
|
|
96
125
|
Telegram-bound tasks. Core never purchases credits, enables overage or consumes
|
|
97
126
|
reset credits; the next regular occurrence runs normally.
|
|
@@ -225,10 +254,13 @@ queued occurrence; already-running work continues until explicitly cancelled.
|
|
|
225
254
|
|
|
226
255
|
`/stop` stops all active work; `cancel RUN_ID` stops one background task. `/cancel`
|
|
227
256
|
clears queued work. Pause/remove a recurring schedule to prevent future runs.
|
|
228
|
-
Stopping the relay also stops its workers
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
257
|
+
Stopping the relay also stops its workers, except on host-capable agents: a
|
|
258
|
+
graceful relay stop hands running host-transport runs to the next relay, which
|
|
259
|
+
adopts them without relaunching (see [Docker runtime](docker-runtime.md)). A
|
|
260
|
+
request the host had not yet claimed when the relay stopped is failed by the host
|
|
261
|
+
without starting. Run and delivery records are otherwise relay memory: a process
|
|
262
|
+
restart drops in-flight runs and queued deliveries by design, so the next
|
|
263
|
+
eligible occurrence dispatches normally and nothing is replayed from disk. Status labels failed runs as history and shows recent reasons; new failures retain their exit code or interruption cause. Typing indicators stop after 30 seconds even when work continues. A failed run that cannot prove whether an external side effect happened stays visible until the agent inspects the evidence and explicitly edits the schedule. Inspect the task's files, native session and delivery
|
|
232
264
|
receipts before deciding whether to resume. A clock cannot reconstruct an
|
|
233
265
|
in-flight process or prove whether an external side effect happened.
|
|
234
266
|
|
|
@@ -307,10 +339,18 @@ messages, read private sessions, trigger work or certify owning results.
|
|
|
307
339
|
|
|
308
340
|
The prompt controls diagnosis, authorized recovery and quiet notification behavior. Inspect prior effects and receipts before retrying anything. A failed review run itself remains visible as a new failure for the next occurrence.
|
|
309
341
|
|
|
310
|
-
The Telegram Scheduled tasks menu lists
|
|
311
|
-
occurrence or a queued/running occurrence.
|
|
312
|
-
|
|
313
|
-
|
|
342
|
+
The Telegram Scheduled tasks menu lists paused schedules alongside enabled schedules
|
|
343
|
+
that still have a pending occurrence or a queued/running occurrence. Paused tasks
|
|
344
|
+
are labeled Paused and have no next run; an existing queued/running occurrence
|
|
345
|
+
keeps its execution state visible. Finished enabled one-time tasks and enabled
|
|
346
|
+
revisions stopped for review are hidden. Each entry shows the
|
|
347
|
+
frequency with the cron timezone, the saved primary and fallback setups in order
|
|
348
|
+
(CLI, login profile, model and effort), next occurrence in UTC (or queued/running state),
|
|
314
349
|
and the first sentence of its saved invocation
|
|
315
350
|
prompt, limited to 140 characters. This is a read-only view; history and full
|
|
316
|
-
prompts remain available through the scheduling CLI.
|
|
351
|
+
prompts remain available through the scheduling CLI. `/tasks N` shows the same
|
|
352
|
+
frequency and execution path with the saved instructions. Common minute/hourly,
|
|
353
|
+
daily and weekday rules have plain-language labels; other calendar rules show
|
|
354
|
+
the exact cron expression and timezone. Interval rules show their fixed period,
|
|
355
|
+
and one-time tasks show `Once`. This describes the schedule, not a guarantee of
|
|
356
|
+
work when admission/preflight conditions are unmet.
|
package/docs/setup.md
CHANGED
|
@@ -261,8 +261,8 @@ node /absolute/ezenciel_agents/bin/ezenciel-agents-tools.mjs init \
|
|
|
261
261
|
|
|
262
262
|
This binds a private `ez` and preserves native command access. The default catalog
|
|
263
263
|
is empty. Do not supply a plugin catalog during first-time main onboarding.
|
|
264
|
-
|
|
265
|
-
|
|
264
|
+
The seeded
|
|
265
|
+
AGENTS.md already points at `ez tools list --details`. Verify `tools/bin/ez plugins
|
|
266
266
|
available` before the first agent turn. Also execute the agent-bound
|
|
267
267
|
`ezenciel-agents-message --help` through the selected CLI sandbox and verify
|
|
268
268
|
its actual tool output, so absent launchers or blocked execution are detected
|
package/docs/standalone-cli.md
CHANGED
|
@@ -22,14 +22,14 @@ node /absolute/package/bin/ezenciel-agents-tools.mjs init --standalone \
|
|
|
22
22
|
/absolute/private/company-tools/bin/ez plugins list
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Init starts nothing, uses an empty catalog by default
|
|
26
|
-
workspace
|
|
25
|
+
Init starts nothing, uses an empty catalog by default and never writes
|
|
26
|
+
workspace instructions. A registry
|
|
27
27
|
cannot be replaced by rerunning init. Keep the package at its original path:
|
|
28
28
|
the launcher imports it. Status reports `main: null` without a relay binding;
|
|
29
29
|
automated software upgrades currently require a relay deployment.
|
|
30
30
|
|
|
31
|
-
Use `ez tools list --details` for
|
|
32
|
-
Add
|
|
31
|
+
Use `ez tools list --details` for each installed plugin's aliases, purpose, skill paths and one example call. Read only the relevant skill.
|
|
32
|
+
Add "Tools: run `ez tools list --details`; each plugin's `--help` gives usage." to the project's existing instructions, with `ez` resolving to the returned launcher.
|
|
33
33
|
Use the absolute launcher, or prepend its bin directory to that session's PATH.
|
|
34
34
|
Never overwrite another global `ez`; it may belong to a different installation.
|
|
35
35
|
A standalone company registry remains explicit even when invoked from a different
|
package/docs/upgrades.md
CHANGED
|
@@ -16,7 +16,12 @@ automatic channel for core and plugins without a saved policy. Existing explicit
|
|
|
16
16
|
stable or manual policies are preserved. The owner may select either per target.
|
|
17
17
|
The main target and installed plugins version independently.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Handoff-compatible core upgrades build while native work continues and pause admission only for activation. The current native executor retires from claiming new work; the replacement claims new requests while the retiring generation finishes its existing runs. The relay adopts those runs without replay, preserving their schedule holds, cancellation and settled delivery receipts. The supervisor reloads only after native work is idle, so OS service process-group cleanup cannot kill a retiring generation. Plugin upgrades build while native work continues. Activation closes the existing registry admission lock, drains in-flight plugin command leases, and replaces the plugin under that lock. New native plugin calls wait for admission; an outdated revision fails before provider execution and must be rediscovered. No admitted provider operation is replayed. The shared broker and native sessions remain running. Older runtimes without the handoff contract retain the full native-work drain. Isolated agents migrate once while idle to a separate, unprivileged executor container using the same file transport; later relay/runtime upgrades preserve that executor generation. No native CLI or provider credentials move to the host. A plugin update also retains that drain if an operator replaced the broker’s single-file host binding (for example to approve new network pins), because that case still requires broker recreation.
|
|
20
|
+
|
|
21
|
+
Compatible main upgrades also finish their advisory workspace/native-client
|
|
22
|
+
backup before closing admission or stopping the relay. Large native histories do
|
|
23
|
+
not extend the swap's delivery outage. These live-state copies are best effort;
|
|
24
|
+
rollback restores code and configuration, never the backup over active state.
|
|
20
25
|
|
|
21
26
|
The owner chooses the saved update policy. The host supervisor owns
|
|
22
27
|
interruption-safe replacement. There is one host service per agent, not a second
|
|
@@ -153,7 +158,7 @@ is required. Stable automation excludes prereleases, major changes and 0.x minor
|
|
|
153
158
|
changes. Beta policy permits compatible prereleases. Downgrades are rejected;
|
|
154
159
|
rollback is a recovery operation for the previous installation only.
|
|
155
160
|
|
|
156
|
-
`apply` records one durable queued job. **For a core upgrade, the agent must finish its turn after queuing it** so the supervisor can drain native work. A plugin upgrade can complete while that turn continues; only plugin-call admission is held during activation. Calls already executing finish first.
|
|
161
|
+
`apply` records one durable queued job. **For a core upgrade from a runtime without the handoff contract, the agent must finish its turn after queuing it** so the supervisor can drain native work. New compatible runtimes can swap while native work continues. A plugin upgrade can complete while that turn continues; only plugin-call admission is held during activation. Calls already executing finish first.
|
|
157
162
|
One job at a time; an unresolved recovery blocks new upgrades. The agent processes
|
|
158
163
|
multiple requested packages over successive update requests.
|
|
159
164
|
|
|
@@ -255,19 +260,15 @@ handbook from workspace `AGENTS.md` (and existing `AGENTS.override.md`).
|
|
|
255
260
|
Personal text outside that block is preserved byte-for-byte. Do not reinsert
|
|
256
261
|
coding, KISS, contribution or repair loops into operating minds.
|
|
257
262
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
Inventory and
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
Codex, Grok and OpenCode discover workspace instructions natively. Claude
|
|
269
|
-
receives the native `--append-system-prompt-file` binding to `AGENTS.md`, alongside
|
|
270
|
-
its own normal instructions. Fresh and resumed owner input is literal; ez does
|
|
263
|
+
Ez writes no managed guidance after the initial workspace seed. Update binding
|
|
264
|
+
(init with a host config, `enable-updates`, upgrade and rollback) replaces a former
|
|
265
|
+
`<!-- ez tools -->` footer once with the seeded static line
|
|
266
|
+
"Tools: run `ez tools list --details`; each plugin's `--help` gives usage." and
|
|
267
|
+
otherwise leaves `AGENTS.md` untouched. Malformed markers and symlinks fail visibly
|
|
268
|
+
rather than overwriting personal work. Inventory and usage are generated on read.
|
|
269
|
+
|
|
270
|
+
Codex, Grok, OpenCode and Claude Code discover workspace `AGENTS.md` natively
|
|
271
|
+
(Claude Code loads it when the workspace has no `CLAUDE.md`). Fresh and resumed owner input is literal; ez does
|
|
271
272
|
not surround it with policies, tool recipes, repair instructions or history.
|
|
272
273
|
The native engine owns instruction loading and its context/token overhead.
|
|
273
274
|
Already running sessions retain their current context until native reload.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jc_stack/ez-agents",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.51",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A lightweight foundation for persistent business AI assistants using existing AI harnesses, workspaces and plugins.",
|
|
@@ -112,7 +112,9 @@
|
|
|
112
112
|
"protocol": 1,
|
|
113
113
|
"kind": "main",
|
|
114
114
|
"stateSchema": 1,
|
|
115
|
-
"mainProtocol": 1
|
|
115
|
+
"mainProtocol": 1,
|
|
116
|
+
"runHandoff": 1,
|
|
117
|
+
"containerExecutor": 1
|
|
116
118
|
},
|
|
117
119
|
"exports": {
|
|
118
120
|
"./application-client": "./src/application-client.mjs",
|
package/src/agent-guidance.ts
CHANGED
|
@@ -5,8 +5,8 @@ import path from 'node:path'
|
|
|
5
5
|
const start = '<!-- ez shared guidance: begin -->'
|
|
6
6
|
const end = '<!-- ez shared guidance: end -->'
|
|
7
7
|
|
|
8
|
-
// Drop the obsolete package-owned handbook. Discovery
|
|
9
|
-
// ez tools
|
|
8
|
+
// Drop the obsolete package-owned handbook. Discovery is seeded once and
|
|
9
|
+
// generated by `ez tools list --details`. Personal text outside the markers stays byte-for-byte.
|
|
10
10
|
export async function installAgentGuidance(workspace: string): Promise<void> {
|
|
11
11
|
for (const name of ['AGENTS.md', 'AGENTS.override.md']) {
|
|
12
12
|
const file = path.join(workspace, name)
|
package/src/ai-cli.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { mkdir, readFile } from 'node:fs/promises'
|
|
|
5
5
|
import { authProfileStatus, readModels, presetProvider, sameChoice, sameEngine, validateSelection, type AiPreset, type ModelChoice } from './ai.js'
|
|
6
6
|
import { cliHome } from './auth-profile.js'
|
|
7
7
|
import { ControlStore } from './control-state.js'
|
|
8
|
+
import { resolveIsolation } from './isolation.js'
|
|
8
9
|
|
|
9
10
|
const usage='Usage: ezenciel-agents-ai list | select --cli <installed-cli> [--auth-profile <name>] [--provider <provider>] [--model <model>] [--effort <effort>] | default | profiles | profile add --cli codex|claude --auth-profile <name>'
|
|
10
11
|
const {values,positionals}=parseArgs({allowPositionals:true,options:{help:{type:'boolean'},cli:{type:'string'},'auth-profile':{type:'string'},provider:{type:'string'},model:{type:'string'},effort:{type:'string'}}})
|
|
@@ -15,8 +16,9 @@ if(values.help){
|
|
|
15
16
|
if (!process.env.EZ_CONTROL_DIR) throw new Error('Use this agent’s bound control directory')
|
|
16
17
|
const controlDir=process.env.EZ_CONTROL_DIR
|
|
17
18
|
const authProfile=values['auth-profile']
|
|
18
|
-
const catalog=():Promise<ModelChoice[]>=>
|
|
19
|
-
|
|
19
|
+
const catalog=():Promise<ModelChoice[]>=>(process.env.EZ_AI_CATALOG==='host'||(process.env.EZ_AI_CATALOG===undefined&&resolveIsolation(process.env)==='host-capable'))
|
|
20
|
+
? readFile(join(controlDir,'host-executor','models.json'),'utf8').then(text=>JSON.parse(text))
|
|
21
|
+
: readModels(undefined,undefined,join(controlDir,'cli','codex'),undefined,undefined,undefined,controlDir)
|
|
20
22
|
if(positionals[0]==='list')console.log(JSON.stringify(await catalog()))
|
|
21
23
|
else if(positionals[0]==='profiles')console.log(JSON.stringify(await authProfileStatus(controlDir)))
|
|
22
24
|
else if(positionals[0]==='profile' && positionals[1]==='add'){
|