@zeph-to/cli 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/dist/cli.js +7 -6
- package/dist/gate.d.ts +9 -0
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +10 -1
- package/dist/inventory-offload.d.ts +15 -1
- package/dist/inventory-offload.d.ts.map +1 -1
- package/dist/inventory-offload.js +68 -16
- package/dist/listener.d.ts +1 -0
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +61 -1
- package/dist/remote-hook.js +4 -4
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +155 -31
- package/dist/wrapper.d.ts +18 -8
- package/dist/wrapper.d.ts.map +1 -1
- package/dist/wrapper.js +57 -27
- package/dist/zeph-core.generated.d.ts +4 -4
- package/dist/zeph-core.generated.d.ts.map +1 -1
- package/dist/zeph-core.generated.js +4 -4
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -265,10 +265,18 @@ block here.
|
|
|
265
265
|
same folder, run `zeph cc` again, and the wrapper auto-suffixes:
|
|
266
266
|
first session is `zeph-encl`, the next attached one becomes
|
|
267
267
|
`zeph-encl-2`, then `zeph-encl-3`, etc. The phone picker shows them
|
|
268
|
-
as `encl · Claude`, `encl · Claude #2`, `encl · Claude #3`.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
as `encl · Claude`, `encl · Claude #2`, `encl · Claude #3`.
|
|
269
|
+
|
|
270
|
+
**Reattaching wins over spawning.** If any session of that project is
|
|
271
|
+
**detached** (no one attached), the wrapper reattaches instead of
|
|
272
|
+
spawning a new one — close the terminal, come back later, pick up
|
|
273
|
+
where you left off. When several are detached it takes the
|
|
274
|
+
highest-numbered one first, then works down on later runs, so no
|
|
275
|
+
session can end up out of reach. That matters because a session you
|
|
276
|
+
cannot reach still holds a live agent: an agent CLI plus its MCP
|
|
277
|
+
servers is a few hundred MB you can neither see nor reclaim. A
|
|
278
|
+
detached session is never killed for you — nothing here can tell
|
|
279
|
+
"done with this" from "closed the laptop".
|
|
272
280
|
|
|
273
281
|
If you're already inside a tmux session (`$TMUX` set) the wrapper
|
|
274
282
|
skips the outer tmux and runs the agent in the current pane — the
|
|
@@ -514,7 +522,7 @@ zeph notify --title "Hello" --json
|
|
|
514
522
|
| `dismiss <id>` | Dismiss a push (or `--all`) |
|
|
515
523
|
| `rename <name>` | Set the current agent session's display name in the app — run inside a `zeph cc` session (`--clear` resets). Auto-detects the tmux session + this machine's listener device id, so the alias lands on the right device |
|
|
516
524
|
| `test` | Verify connection and API key |
|
|
517
|
-
| `cc` · `codex` · `gemini` | Run the agent in a `zeph-<project>` tmux session (auto-
|
|
525
|
+
| `cc` · `codex` · `gemini` | Run the agent in a `zeph-<project>` tmux session — reattaches a detached session of that project (newest suffix first) when there is one, else auto-suffixes `-2`, `-3`, …. Auto-spawns the background listener on first invocation so the phone picker just works. Trailing args pass through to the agent (`zeph cc --resume "..."`) |
|
|
518
526
|
| `listener` | (Usually unnecessary — `zeph cc` autospawns it.) Resident daemon: subscribes via WebSocket, reports tmux session inventory every 5 s, injects `agent.command` pushes into the matching session. Run in the foreground for SDK development; otherwise let `zeph cc` manage it |
|
|
519
527
|
|
|
520
528
|
### Notify Options
|
|
@@ -605,10 +613,17 @@ this order — first hit wins:
|
|
|
605
613
|
It is now `quiet`, so upgrading turns the routine per-turn push off until
|
|
606
614
|
you run `/zeph-normal`. Row 4 is why the hooks this CLI installs are
|
|
607
615
|
unaffected: they name `normal` themselves, since a hook that supplies no
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
616
|
+
`high` marker would be permanently silent under `quiet` rather than
|
|
617
|
+
merely quieter. Row 4 sits *below* the state files on purpose — the flag
|
|
618
|
+
names a default, it does not override a dial the user set.
|
|
619
|
+
|
|
620
|
+
Under `normal` the JSON hook configs push on every turn, because they see
|
|
621
|
+
no per-tool events and so pass no counts — the gate then assumes real
|
|
622
|
+
work. The Pi extension and the OpenCode plugin do see those events, so
|
|
623
|
+
they pass real `--tools` / `--nonreadonly` counts and go quiet where the
|
|
624
|
+
others cannot: a turn with no tool calls, a turn with exactly one (a
|
|
625
|
+
lone edit included — the gate wants two), and a turn whose calls were
|
|
626
|
+
all reads. `/zeph-loud` still pushes on all of them.
|
|
612
627
|
|
|
613
628
|
A dial file that exists but reads empty resolves to `normal`, not to row
|
|
614
629
|
5: an empty file is a failed write, and resolving breakage to silence
|
package/dist/cli.js
CHANGED
|
@@ -79,10 +79,11 @@ Commands:
|
|
|
79
79
|
(run inside a zeph cc session; --clear resets it)
|
|
80
80
|
test Send a test notification to verify setup
|
|
81
81
|
${usageAgentLines()}
|
|
82
|
-
(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
(reattaches a detached session of that project when
|
|
83
|
+
there is one, newest suffix first; auto-suffixes
|
|
84
|
+
-2/-3/… only when every existing one has a client
|
|
85
|
+
attached. Any args after the subcommand are forwarded
|
|
86
|
+
verbatim, e.g. 'zeph cc --resume')
|
|
86
87
|
listener Resident daemon — receives 'agent.command' pushes from
|
|
87
88
|
the phone picker and injects them into the matching
|
|
88
89
|
tmux session.
|
|
@@ -279,8 +280,8 @@ const handleNotify = async (args) => {
|
|
|
279
280
|
// With no dial the mode falls back to --pushmode-default, then to quiet.
|
|
280
281
|
if (args.auto === true) {
|
|
281
282
|
const verdict = (0, gate_js_1.decidePush)({
|
|
282
|
-
toolCount: gateCount(args.
|
|
283
|
-
nonReadonlyCount: gateCount(args.
|
|
283
|
+
toolCount: gateCount(args[gate_js_1.TOOL_COUNT_FLAG], gate_js_1.GATE_DEFAULTS.toolCount),
|
|
284
|
+
nonReadonlyCount: gateCount(args[gate_js_1.NONREADONLY_COUNT_FLAG], gate_js_1.GATE_DEFAULTS.nonReadonlyCount),
|
|
284
285
|
alreadyAsked: gate_js_1.GATE_DEFAULTS.alreadyAsked,
|
|
285
286
|
marker: (0, gate_js_1.normalizeMarker)(typeof args.marker === 'string' ? args.marker : undefined),
|
|
286
287
|
pushMode: (0, gate_js_1.autoPushMode)(projectDir, args[gate_js_1.PUSHMODE_DEFAULT_FLAG]),
|
package/dist/gate.d.ts
CHANGED
|
@@ -88,6 +88,15 @@ export declare const PUSHMODE_DEFAULT: GatePushMode;
|
|
|
88
88
|
* read back in cli.ts — shared so the two can never drift apart.
|
|
89
89
|
*/
|
|
90
90
|
export declare const PUSHMODE_DEFAULT_FLAG = "pushmode-default";
|
|
91
|
+
/**
|
|
92
|
+
* `notify` flags carrying the turn's tool counts. Only the drop-in artifacts
|
|
93
|
+
* (pi extension, opencode plugin) can supply them — they are the only installed
|
|
94
|
+
* hooks that see per-tool events. Shared for the same reason as the flag above:
|
|
95
|
+
* templates.ts writes them, cli.ts reads them, and a rename on one side alone
|
|
96
|
+
* would send `gateCount` back to GATE_DEFAULTS with nothing to show for it.
|
|
97
|
+
*/
|
|
98
|
+
export declare const TOOL_COUNT_FLAG = "tools";
|
|
99
|
+
export declare const NONREADONLY_COUNT_FLAG = "nonreadonly";
|
|
91
100
|
/**
|
|
92
101
|
* The user's session push-mode dial (/zeph-quiet | /zeph-loud | /zeph-normal).
|
|
93
102
|
*
|
package/dist/gate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AAEX,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,UACS,CAAC;AAEpE,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,YACR,CAAC;AAErD,eAAO,MAAM,UAAU,GAAI,OAAO,SAAS,KAAG,WAW7C,CAAC;AAeF,eAAO,MAAM,QAAQ,QAAO,MACoD,CAAC;AA2BjF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAOlD,CAAC;AAWF,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,MAA4C,CAAC;AAE7F;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MAG1B,CAAC;AAiBnB;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,QAAQ,CAAC;AAEpC,8EAA8E;AAC9E,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MACJ,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,EAAE,MAAK,MAAM,MAAiB,KAAG,OAsB1E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,EAAE,MAAK,MAAM,MAAiB,KAAG,IAS7E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,IAQ/C,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,OAGrC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAAsB,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,MAAM,EACX,WAAU,YAA+B,KACxC,YAUF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,EAAE,MAAM,MAAM,GAAG,OAAO,GAAG,SAAS,KAAG,YACW,CAAC"}
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AAEX,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,UACS,CAAC;AAEpE,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,YACR,CAAC;AAErD,eAAO,MAAM,UAAU,GAAI,OAAO,SAAS,KAAG,WAW7C,CAAC;AAeF,eAAO,MAAM,QAAQ,QAAO,MACoD,CAAC;AA2BjF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAOlD,CAAC;AAWF,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,MAA4C,CAAC;AAE7F;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MAG1B,CAAC;AAiBnB;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,QAAQ,CAAC;AAEpC,8EAA8E;AAC9E,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MACJ,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,EAAE,MAAK,MAAM,MAAiB,KAAG,OAsB1E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,EAAE,MAAK,MAAM,MAAiB,KAAG,IAS7E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,IAQ/C,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,OAGrC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAAsB,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,MAAM,EACX,WAAU,YAA+B,KACxC,YAUF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,EAAE,MAAM,MAAM,GAAG,OAAO,GAAG,SAAS,KAAG,YACW,CAAC"}
|
package/dist/gate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.autoPushMode = exports.readPushMode = exports.PUSHMODE_DEFAULT_FLAG = exports.PUSHMODE_DEFAULT = exports.isMuted = exports.clearRemoteActive = exports.touchRemoteActive = exports.isRemoteActive = exports.remoteStatePath = exports.REMOTE_TTL_SEC = exports.remoteDigest = exports.remoteMarkerPath = exports.projectHash = exports.stateDir = exports.decidePush = exports.normalizePushMode = exports.normalizeMarker = exports.GATE_DEFAULTS = void 0;
|
|
3
|
+
exports.autoPushMode = exports.readPushMode = exports.NONREADONLY_COUNT_FLAG = exports.TOOL_COUNT_FLAG = exports.PUSHMODE_DEFAULT_FLAG = exports.PUSHMODE_DEFAULT = exports.isMuted = exports.clearRemoteActive = exports.touchRemoteActive = exports.isRemoteActive = exports.remoteStatePath = exports.REMOTE_TTL_SEC = exports.remoteDigest = exports.remoteMarkerPath = exports.projectHash = exports.stateDir = exports.decidePush = exports.normalizePushMode = exports.normalizeMarker = exports.GATE_DEFAULTS = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Push-gate decision — the portable half of the Zeph Stop-hook logic.
|
|
6
6
|
*
|
|
@@ -244,6 +244,15 @@ exports.PUSHMODE_DEFAULT = 'quiet';
|
|
|
244
244
|
* read back in cli.ts — shared so the two can never drift apart.
|
|
245
245
|
*/
|
|
246
246
|
exports.PUSHMODE_DEFAULT_FLAG = 'pushmode-default';
|
|
247
|
+
/**
|
|
248
|
+
* `notify` flags carrying the turn's tool counts. Only the drop-in artifacts
|
|
249
|
+
* (pi extension, opencode plugin) can supply them — they are the only installed
|
|
250
|
+
* hooks that see per-tool events. Shared for the same reason as the flag above:
|
|
251
|
+
* templates.ts writes them, cli.ts reads them, and a rename on one side alone
|
|
252
|
+
* would send `gateCount` back to GATE_DEFAULTS with nothing to show for it.
|
|
253
|
+
*/
|
|
254
|
+
exports.TOOL_COUNT_FLAG = 'tools';
|
|
255
|
+
exports.NONREADONLY_COUNT_FLAG = 'nonreadonly';
|
|
247
256
|
/**
|
|
248
257
|
* The user's session push-mode dial (/zeph-quiet | /zeph-loud | /zeph-normal).
|
|
249
258
|
*
|
|
@@ -14,5 +14,19 @@ export interface InventoryOffload {
|
|
|
14
14
|
* run of cycles a stuck worker would otherwise cost: the caller gates sweeps
|
|
15
15
|
* one-at-a-time, so this bounds how long a hang can hold the inventory. */
|
|
16
16
|
export declare const COLLECT_TIMEOUT_MS = 20000;
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Consecutive timed-out sweeps before the offload gives up on workers.
|
|
19
|
+
*
|
|
20
|
+
* A sweep that overruns is terminated, and the next one starts a fresh worker
|
|
21
|
+
* from nothing — so on a host where a cold sweep cannot finish inside the
|
|
22
|
+
* timeout, retrying forever means the daemon never reports at all. Reporting
|
|
23
|
+
* slowly beats not reporting, and in-thread is exactly "slowly". Three in a row
|
|
24
|
+
* rather than one, so a single slow moment (a load spike, a machine waking) does
|
|
25
|
+
* not cost the offload for the rest of the daemon's life; the streak resets on
|
|
26
|
+
* any answered sweep.
|
|
27
|
+
*/
|
|
28
|
+
export declare const MAX_CONSECUTIVE_TIMEOUTS = 3;
|
|
29
|
+
export declare const startInventoryOffload: (inThread: () => CollectResult, log: (msg: string) => void, workerPath?: string,
|
|
30
|
+
/** Overridable so a test can exercise the timeout path without waiting 20s. */
|
|
31
|
+
timeoutMs?: number) => InventoryOffload;
|
|
18
32
|
//# sourceMappingURL=inventory-offload.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inventory-offload.d.ts","sourceRoot":"","sources":["../src/inventory-offload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"inventory-offload.d.ts","sourceRoot":"","sources":["../src/inventory-offload.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,kFAAkF;IAClF,OAAO,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IACtC,oDAAoD;IACpD,KAAK,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;4EAE4E;AAC5E,eAAO,MAAM,kBAAkB,QAAS,CAAC;AAEzC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAoB1C,eAAO,MAAM,qBAAqB,GAC9B,UAAU,MAAM,aAAa,EAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAC1B,aAAY,MAA+C;AAC3D,+EAA+E;AAC/E,YAAW,MAA2B,KACvC,gBAoHF,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.startInventoryOffload = exports.COLLECT_TIMEOUT_MS = void 0;
|
|
3
|
+
exports.startInventoryOffload = exports.MAX_CONSECUTIVE_TIMEOUTS = exports.COLLECT_TIMEOUT_MS = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Runs the tmux session inventory on a worker thread so the daemon's event
|
|
6
6
|
* loop stays free while `tmux` child processes are waited on.
|
|
@@ -21,6 +21,14 @@ exports.startInventoryOffload = exports.COLLECT_TIMEOUT_MS = void 0;
|
|
|
21
21
|
* behaviour instead of an empty session list. A worker that dies after having
|
|
22
22
|
* worked is treated as transient — that sweep fails (the caller skips the
|
|
23
23
|
* cycle) and the next `collect()` starts a fresh one.
|
|
24
|
+
*
|
|
25
|
+
* A worker we killed ourselves is neither. `terminate()` (on timeout, on close)
|
|
26
|
+
* exits with code 1, indistinguishable from a crash, so the exit code cannot be
|
|
27
|
+
* read — what separates the two is whether that worker still owed an answer,
|
|
28
|
+
* which is why `Live` carries its own `owed` ids rather than sharing `pending`.
|
|
29
|
+
* Getting this wrong is expensive in one direction only: a timeout misread as
|
|
30
|
+
* "died before ever answering" is permanent, and it fires exactly when the host
|
|
31
|
+
* is slow enough for a sweep to overrun, which is when the offload matters most.
|
|
24
32
|
*/
|
|
25
33
|
const path_1 = require("path");
|
|
26
34
|
const worker_threads_1 = require("worker_threads");
|
|
@@ -28,7 +36,21 @@ const worker_threads_1 = require("worker_threads");
|
|
|
28
36
|
* run of cycles a stuck worker would otherwise cost: the caller gates sweeps
|
|
29
37
|
* one-at-a-time, so this bounds how long a hang can hold the inventory. */
|
|
30
38
|
exports.COLLECT_TIMEOUT_MS = 20_000;
|
|
31
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Consecutive timed-out sweeps before the offload gives up on workers.
|
|
41
|
+
*
|
|
42
|
+
* A sweep that overruns is terminated, and the next one starts a fresh worker
|
|
43
|
+
* from nothing — so on a host where a cold sweep cannot finish inside the
|
|
44
|
+
* timeout, retrying forever means the daemon never reports at all. Reporting
|
|
45
|
+
* slowly beats not reporting, and in-thread is exactly "slowly". Three in a row
|
|
46
|
+
* rather than one, so a single slow moment (a load spike, a machine waking) does
|
|
47
|
+
* not cost the offload for the rest of the daemon's life; the streak resets on
|
|
48
|
+
* any answered sweep.
|
|
49
|
+
*/
|
|
50
|
+
exports.MAX_CONSECUTIVE_TIMEOUTS = 3;
|
|
51
|
+
const startInventoryOffload = (inThread, log, workerPath = (0, path_1.join)(__dirname, 'inventory-worker.js'),
|
|
52
|
+
/** Overridable so a test can exercise the timeout path without waiting 20s. */
|
|
53
|
+
timeoutMs = exports.COLLECT_TIMEOUT_MS) => {
|
|
32
54
|
let worker = null;
|
|
33
55
|
let closed = false;
|
|
34
56
|
/** Set once a worker has answered; a failure before that is not transient. */
|
|
@@ -36,6 +58,8 @@ const startInventoryOffload = (inThread, log, workerPath = (0, path_1.join)(__di
|
|
|
36
58
|
/** Set when the worker is given up on — sweeps run in-thread from then on. */
|
|
37
59
|
let inThreadOnly = false;
|
|
38
60
|
let nextId = 0;
|
|
61
|
+
/** Timed-out sweeps since the last answered one. */
|
|
62
|
+
let consecutiveTimeouts = 0;
|
|
39
63
|
const pending = new Map();
|
|
40
64
|
const settleAll = (outcome) => {
|
|
41
65
|
for (const [id, p] of pending) {
|
|
@@ -65,59 +89,87 @@ const startInventoryOffload = (inThread, log, workerPath = (0, path_1.join)(__di
|
|
|
65
89
|
// Never keep the process alive on the worker's account: the daemon's
|
|
66
90
|
// own socket and timers decide when it exits.
|
|
67
91
|
w.unref();
|
|
92
|
+
const live = { w, owed: new Set() };
|
|
68
93
|
w.on('message', (msg) => {
|
|
94
|
+
live.owed.delete(msg.id);
|
|
69
95
|
const p = pending.get(msg.id);
|
|
70
96
|
if (!p)
|
|
71
97
|
return;
|
|
72
98
|
pending.delete(msg.id);
|
|
73
99
|
clearTimeout(p.timer);
|
|
74
100
|
everReplied = true;
|
|
101
|
+
consecutiveTimeouts = 0;
|
|
75
102
|
if (msg.error !== undefined)
|
|
76
103
|
p.reject(new Error(msg.error));
|
|
77
104
|
else
|
|
78
105
|
p.resolve(msg.result);
|
|
79
106
|
});
|
|
80
107
|
w.on('error', (err) => {
|
|
81
|
-
if (worker ===
|
|
108
|
+
if (worker === live)
|
|
82
109
|
worker = null;
|
|
110
|
+
// Reported here, so the exit that follows is this same death and not
|
|
111
|
+
// a second one. An id leaves `owed` the moment its sweep is settled,
|
|
112
|
+
// whatever settled it — that is the invariant the exit handler reads.
|
|
113
|
+
live.owed.clear();
|
|
83
114
|
onFailure(`inventory worker error (${err instanceof Error ? err.message : String(err)})`);
|
|
84
115
|
});
|
|
85
116
|
w.on('exit', (code) => {
|
|
86
|
-
if (worker ===
|
|
117
|
+
if (worker === live)
|
|
87
118
|
worker = null;
|
|
88
|
-
//
|
|
89
|
-
|
|
119
|
+
// Did THIS worker still owe an answer? `terminate()` exits with code
|
|
120
|
+
// 1 exactly like a crash, so the code says nothing; what separates a
|
|
121
|
+
// death we caused from one we did not is whether the sweep it was
|
|
122
|
+
// holding had already been settled. Asking the shared `pending` map
|
|
123
|
+
// instead judged a terminated worker against the NEXT worker's sweep
|
|
124
|
+
// — the timeout path nulls `worker`, so the following poll spawns a
|
|
125
|
+
// replacement and registers its id a second before this event lands.
|
|
126
|
+
// A routine timeout then read as "died before ever answering", which
|
|
127
|
+
// is permanent: `everReplied` is false, so the offload never
|
|
128
|
+
// recovers for the life of the daemon.
|
|
129
|
+
if (live.owed.size > 0)
|
|
90
130
|
onFailure(`inventory worker exited (${code})`);
|
|
91
131
|
});
|
|
92
|
-
return
|
|
132
|
+
return live;
|
|
93
133
|
};
|
|
94
134
|
const collect = () => {
|
|
95
135
|
if (closed || inThreadOnly)
|
|
96
136
|
return Promise.resolve(inThread());
|
|
97
137
|
worker ??= spawn();
|
|
98
|
-
const
|
|
138
|
+
const live = worker;
|
|
99
139
|
const id = ++nextId;
|
|
100
140
|
return new Promise((resolve, reject) => {
|
|
101
141
|
const timer = setTimeout(() => {
|
|
102
142
|
pending.delete(id);
|
|
103
|
-
|
|
143
|
+
// Settled here, so the terminate below is a death we caused and
|
|
144
|
+
// this worker's exit must not be reported as a failure.
|
|
145
|
+
live.owed.delete(id);
|
|
146
|
+
consecutiveTimeouts += 1;
|
|
147
|
+
if (consecutiveTimeouts >= exports.MAX_CONSECUTIVE_TIMEOUTS && !inThreadOnly) {
|
|
148
|
+
inThreadOnly = true;
|
|
149
|
+
log(`! inventory sweep timed out ${consecutiveTimeouts}× in a row — running the inventory in-thread from now on`);
|
|
150
|
+
}
|
|
151
|
+
reject(new Error(`inventory sweep exceeded ${timeoutMs / 1000}s`));
|
|
104
152
|
// A stuck worker is not coming back; the next call spawns a fresh one.
|
|
105
|
-
if (worker ===
|
|
153
|
+
if (worker === live)
|
|
106
154
|
worker = null;
|
|
107
|
-
void w.terminate();
|
|
108
|
-
},
|
|
155
|
+
void live.w.terminate();
|
|
156
|
+
}, timeoutMs);
|
|
109
157
|
timer.unref();
|
|
110
158
|
pending.set(id, { resolve, reject, timer });
|
|
111
|
-
|
|
159
|
+
live.owed.add(id);
|
|
160
|
+
live.w.postMessage(id);
|
|
112
161
|
});
|
|
113
162
|
};
|
|
114
163
|
const close = () => {
|
|
115
164
|
closed = true;
|
|
116
165
|
settleAll((p) => p.reject(new Error('inventory offload closed')));
|
|
117
|
-
const
|
|
166
|
+
const live = worker;
|
|
118
167
|
worker = null;
|
|
119
|
-
if (
|
|
120
|
-
|
|
168
|
+
if (!live)
|
|
169
|
+
return;
|
|
170
|
+
// Settled above; clear what it owed for the same reason the timeout does.
|
|
171
|
+
live.owed.clear();
|
|
172
|
+
void live.w.terminate();
|
|
121
173
|
};
|
|
122
174
|
return { collect, close };
|
|
123
175
|
};
|
package/dist/listener.d.ts
CHANGED
|
@@ -694,6 +694,7 @@ export declare const collectSessionsVerbose: () => CollectResult;
|
|
|
694
694
|
export declare const recordInventory: (sessions: ReadonlyArray<{
|
|
695
695
|
name: string;
|
|
696
696
|
}> | null) => void;
|
|
697
|
+
export declare const syncTmuxAgentNames: (sessions: ReadonlyArray<AgentSession>) => void;
|
|
697
698
|
/**
|
|
698
699
|
* Is this session in the inventory? Answered from the last sweep when it can
|
|
699
700
|
* be: a full sweep is two tmux spawns per session on this thread, and it used
|
package/dist/listener.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA4BH,OAAO,EAA0C,KAAK,SAAS,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAUxH,OAAO,EAAiD,KAAK,UAAU,EAA4C,MAAM,kBAAkB,CAAC;AAG5I,OAAO,EAA4E,KAAK,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACvI,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAmCtG,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,UAAU,YAAY;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,GAC9B,MAAM,SAAS,YAAY,EAAE,EAC7B,MAAK,MAAmB,KACzB,oBAAoB,EAYtB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,OAAO,oBAAoB,EAAE,KAAG,MACP,CAAC;AAEnE,eAAO,MAAM,mBAAmB,GAAI,UAAU,YAAY,EAAE,KAAG,MAS7C,CAAC;AAEnB;6BAC6B;AAC7B,eAAO,MAAM,iBAAiB,GAC1B,aAAa,MAAM,EACnB,qBAAqB,MAAM,GAAG,IAAI,EAClC,cAAc,MAAM,EACpB,OAAO,MAAM,KACd,OAEoD,CAAC;AAqBxD,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAA+B,CAAC;AAenF,eAAO,MAAM,cAAc,GACvB,SAAS,MAAM,EACf,MAAK,MAAmB,EACxB,OAAM,MAAU,KACjB,OAgBF,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,IAO7D,CAAC;AAgGF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,KAAG,MAAM,EAAE,GAAG,IAQvD,CAAC;AA0CF;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAG5C,CAAC;AA8NF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAK3F,CAAC;AAEF,UAAU,QAAQ;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAiDD;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,QAAQ,KAAG,qBAAqB,GAAG,IAGhE,CAAC;AA0BZ,iBAAiB;AACjB,eAAO,MAAM,kBAAkB,QAAO,IAErC,CAAC;AAWF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC3B,MAAM,MAAM,EACZ,WAAW,SAAS,EACpB,UAAU,MAAM,GAAG,IAAI,EACvB,MAAK,MAAmB,KACzB,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,gBAAgB,GAAG,aAAa,CAmB/D,CAAC;AAWF,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACnB;AASD,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,GAAI,KAAK,OAAO,KAAG,IAWhD,CAAC;AAEF,iBAAiB;AACjB,eAAO,MAAM,mBAAmB,QAAO,IAGtC,CAAC;AAEF,MAAM,WAAW,QAAQ;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GACzB,WAAW,WAAW,CAAC,MAAM,CAAC,EAC9B,UAAS,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAsB,KAC9D,QAAQ,EAgBV,CAAC;AAWF,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,uBAAuB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,cAAc,GAAG,IAmBzE,CAAC;AAgBF;;kDAEkD;AAClD,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAK7C,MAAM,WAAW,gBAAgB;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;4EACwE;IACxE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACrD;wEACoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;sCACkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AAsGD;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAC/B,KAAK,gBAAgB,EACrB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA2CF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAC9B,KAAK,eAAe,EACpB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA+CF,CAAC;AAWF,MAAM,WAAW,oBAAoB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,6BAA6B,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GACnC,KAAK,oBAAoB,EACzB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OAoDF,CAAC;AAEF,MAAM,WAAW,kBAAkB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,2BAA2B,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACd;AA0BD,8DAA8D;AAC9D,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAI1C;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GACjC,KAAK,kBAAkB,EACvB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA0DF,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,6BAA6B,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACnC,KAAK,oBAAoB,EACzB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA6BF,CAAC;AAiBF;+EAC+E;AAC/E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAG5C,MAAM,WAAW,oBAAoB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IAClC,OAAO,EAAE,+BAA+B,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;sCACkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;oEACgE;IAChE,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AA0CD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACnC,KAAK,oBAAoB,EACzB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA8DF,CAAC;AAsEF,MAAM,WAAW,YAAY;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf;;;oDAGgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,YAAY,GAAG,IAY9D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACtB,SAAS,MAAM,EACf,QAAQ;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAKlC,CAAC;AASF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,MAClB,CAAC;AAUpD;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAChC,aAAa,MAAM,EACnB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EAC7C,SAAQ,MAAM,EAA8B,KAC7C,IAuBF,CAAC;AAcF,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAQD,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAKtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,eAAe,OAAQ,CAAC;AAOrC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,GAAG,IAAI,EAAE,KAAK,MAAM,KAAG,MACgC,CAAC;AAEzG,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH;;gEAEgE;AAChE,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,EAAE,KAAK,MAAM,KAAG,OAGxB,CAAC;AAE3C,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,EAAE,KAAK,MAAM,KAAG,OAYjE,CAAC;AAqBF,eAAO,MAAM,eAAe,QAAS,CAAC;AAStC,eAAO,MAAM,qBAAqB,OAAQ,CAAC;AAM3C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAgHxC,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,KAAG,IAuBhD,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,IAGjC,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;mEAC+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;4EAMwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;0CAMsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;0CAOsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,yBAAyB,CAAC;CACzC,CAAC;AAEF;;;gDAGgD;AAChD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB,CAAC;IACrH;;+EAE2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;sEAEkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,aAAa,MAAM,KAAG,OAChB,CAAC;AAsC1C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GACzB,UAAU;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,EACjD,aAAa,MAAM,EACnB,sBAAsB,MAAM,EAC5B,OAAO;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,KACF,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAmBnC,CAAC;AAEF;;;;GAIG;AASH,eAAO,MAAM,mBAAmB,GAC5B,KAAK,aAAa,EAClB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OAwQF,CAAC;AAWF;;;uBAGuB;AACvB,MAAM,WAAW,iBAAiB;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;+EAI2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;kFAK8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;8DAE8D;AAC9D,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC;;uDAEuD;AACvD,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,kFAAkF;AAClF,KAAK,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAE7D,+BAA+B;AAC/B,KAAK,cAAc,GAAG,cAAc,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB;kEAC8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;wEACoE;IACpE,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC;AAOF,KAAK,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAuCtF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,iBAAiB,KAAG,UAsC7D,CAAC;AAoBF;;;6DAG6D;AAC7D,eAAO,MAAM,eAAe,KAAK,CAAC;AA6ClC;;;;sDAIsD;AACtD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,QAAO,OAAO,CAAC,IAAI,CAAsB,CAAC;AA6G3E;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC3B,KAAK,iBAAiB,EACtB,MAAM,aAAa,KACpB,OAqCF,CAAC;AAEF,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAAO,aAuFzC,CAAC;AAiBF,eAAO,MAAM,eAAe,GAAI,UAAU,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,KAAG,IAElF,CAAC;AAKF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,OAQnD,CAAC;AAIF;;;;;GAKG;AACH,UAAU,kBAAkB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,QAAQ;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;sEACkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;sEACkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED,UAAU,cAAc;IACpB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACpD,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACxD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAC1D,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACtE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzF,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC7C;sEACkE;IAClE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AA2BD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC1B,SAAS,MAAM,EACf,MAAM,MAAM,EACZ,MAAK,MAAM,MAAiB,KAC7B,OAUF,CAAC;AA0DF,eAAO,MAAM,oBAAoB,GAAI,KAAK;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KAAG,IAE/E,CAAC;AA2FF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACtB,MAAK,MAAmB,EACxB,MAAK,MAAwB,EAC7B,MAAK,MAA0B,KAChC,MAaF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GACnB,MAAM,QAAQ,EACd,OAAM,cAAmB,KAC1B,OAAO,CAAC,OAAO,CAgDjB,CAAC;AAcF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAIhD,CAAC;AA4DF,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,KAAG,MA2BvD,CAAC;AA4RF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GACrB,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACtC,QAAQ;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1B,SAAS,MAAM,KAChB,MAAM,GAAG,IAIX,CAAC;AA6HF,eAAO,MAAM,cAAc,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CA2J3F,CAAC"}
|
|
1
|
+
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
|
package/dist/listener.js
CHANGED
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
};
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
28
|
exports.MAX_INPUT_BODY_CHARS = exports.MAX_INPUT_KEYS = exports.handleStreamControl = exports.buildStreamFrame = exports.isStreamCapReached = exports.stopAllStreams = exports.stopStream = exports.MAX_CONCURRENT_STREAMS = exports.STREAM_RENEW_FRESH_MS = exports.STREAM_LEASE_MS = exports.claimFrameSend = exports.hasFrameBudget = exports.streamCadence = exports.MAX_FRAMES_PER_SEC = exports.BURST_WINDOW_MS = exports.BURST_INTERVAL_MS = exports.STREAM_INTERVAL_MS = exports.STREAM_CAPTURE_LINES = exports.scheduleInjectSnapshots = exports.historyLinesIn = exports.cursorLineFor = exports.parsePaneGeometry = exports.handleScreenHistoryRequest = exports.SCREEN_HISTORY_MAX_LINES = exports.handleSessionForgetRequest = exports.handleSessionExitRequest = exports.SESSION_EXIT_STEP_MS = exports.handleSessionResumeRequest = exports.handleDiffFileRequest = exports.handleDiffFilesRequest = exports.DIFF_PAGE_MAX_BYTES = exports.handleScreenRequest = exports.collectWatchHits = exports.resetPatternWatches = exports.setPatternWatches = exports.deriveSessionState = exports.resetSessionStates = exports.detectRemoteAgent = exports.parseSessionName = exports.invalidateTmuxSocketCache = exports.resolveKeys = exports.paneCurrentCommand = exports.checkRateLimit = exports.AUTH_FAILURE_CODES = exports.sessionsReportDue = exports.sessionsFingerprint = exports.knownSessionsFingerprint = exports.knownSessionsToReport = exports.KNOWN_SESSIONS_REPORTED = exports.SESSION_REPORT_HEARTBEAT_MS = void 0;
|
|
29
|
-
exports.handleListener = exports.resolveWsUrl = exports.computeListenerDeviceId = exports.computeBackoff = exports.handlePush = exports.gcAttachments = exports.setAttachmentContext = exports.writeRemoteMarker = exports.isInventoried = exports.recordInventory = exports.collectSessionsVerbose = exports.handleCommandInput = exports.pendingInputDecrypts = exports.MAX_PENDING_DECRYPTS = exports.MAX_INPUT_LANES = exports.validateInputMessage = void 0;
|
|
29
|
+
exports.handleListener = exports.resolveWsUrl = exports.computeListenerDeviceId = exports.computeBackoff = exports.handlePush = exports.gcAttachments = exports.setAttachmentContext = exports.writeRemoteMarker = exports.isInventoried = exports.syncTmuxAgentNames = exports.recordInventory = exports.collectSessionsVerbose = exports.handleCommandInput = exports.pendingInputDecrypts = exports.MAX_PENDING_DECRYPTS = exports.MAX_INPUT_LANES = exports.validateInputMessage = void 0;
|
|
30
30
|
const child_process_1 = require("child_process");
|
|
31
31
|
const crypto_1 = require("crypto");
|
|
32
32
|
const fs_1 = require("fs");
|
|
@@ -2495,6 +2495,63 @@ exports.recordInventory = recordInventory;
|
|
|
2495
2495
|
/** The daemon's sweep runner, set for the lifetime of `handleListener`. Null
|
|
2496
2496
|
* outside the daemon (tests, one-shot commands) — sweeps then run in-thread. */
|
|
2497
2497
|
let inventoryOffload = null;
|
|
2498
|
+
/**
|
|
2499
|
+
* Mirror each session's provider name onto the tmux session itself, as the
|
|
2500
|
+
* user option `@zeph_agent_name`.
|
|
2501
|
+
*
|
|
2502
|
+
* Why: the phone shows what the agent calls this session (`zeph-to-d0`) while
|
|
2503
|
+
* the user's own status bar shows the tmux name (`zeph-zeph-to-3`), and the
|
|
2504
|
+
* two never agree. The phone's render precedence is already decided
|
|
2505
|
+
* (`alias ?? "<providerSessionName> · <Agent>" ?? computed label`, see the
|
|
2506
|
+
* `agentSessionAliases` comment in zeph `libs/shared/src/types/device.ts`);
|
|
2507
|
+
* this puts the same middle tier where a tmux config can read it:
|
|
2508
|
+
*
|
|
2509
|
+
* #{?@zeph_agent_name,#{@zeph_agent_name},#S}
|
|
2510
|
+
*
|
|
2511
|
+
* A tmux option rather than a `#()` in the status line on purpose. The status
|
|
2512
|
+
* line re-renders per attached client on every status-interval, so a shell
|
|
2513
|
+
* there would walk a process tree several times a second across every session;
|
|
2514
|
+
* an option is written only when the name actually changes and costs the
|
|
2515
|
+
* renderer nothing.
|
|
2516
|
+
*
|
|
2517
|
+
* The tmux name is NOT renamed and must not be: it is the addressing key the
|
|
2518
|
+
* phone injects against, it has to exist before the agent it names does, and
|
|
2519
|
+
* the agent's own name rotates on resume. Display and address stay separate —
|
|
2520
|
+
* this only makes the display agree with the phone.
|
|
2521
|
+
*
|
|
2522
|
+
* Keyed by name AND `createdAt`, mirroring the alias anchor in zeph
|
|
2523
|
+
* `apps/server/src/lib/agent-session-alias.ts`: a tmux name is a slot, so the
|
|
2524
|
+
* cache must not let a new occupant inherit the previous one's write. tmux
|
|
2525
|
+
* drops session options with the session, so this is the belt to that braces —
|
|
2526
|
+
* what it actually prevents is the *skip*, where an unchanged key would leave
|
|
2527
|
+
* the new session with no write at all.
|
|
2528
|
+
*/
|
|
2529
|
+
const publishedTmuxNames = new Map();
|
|
2530
|
+
const syncTmuxAgentNames = (sessions) => {
|
|
2531
|
+
const liveKeys = new Set();
|
|
2532
|
+
for (const s of sessions) {
|
|
2533
|
+
const key = `${s.name}\u241f${s.createdAt ?? ''}`;
|
|
2534
|
+
liveKeys.add(key);
|
|
2535
|
+
const value = s.providerSessionName ?? '';
|
|
2536
|
+
if (publishedTmuxNames.get(key) === value)
|
|
2537
|
+
continue;
|
|
2538
|
+
// No name to show → unset, so the format falls through to `#S` and no
|
|
2539
|
+
// stale value is left on a session whose agent stopped reporting one.
|
|
2540
|
+
const args = value
|
|
2541
|
+
? ['set-option', '-t', s.name, '@zeph_agent_name', value]
|
|
2542
|
+
: ['set-option', '-u', '-t', s.name, '@zeph_agent_name'];
|
|
2543
|
+
const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(args), { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
2544
|
+
// Only remember a write that landed; a failed one must be retried next
|
|
2545
|
+
// sweep rather than cached as done.
|
|
2546
|
+
if (r.status === 0)
|
|
2547
|
+
publishedTmuxNames.set(key, value);
|
|
2548
|
+
}
|
|
2549
|
+
for (const key of [...publishedTmuxNames.keys()]) {
|
|
2550
|
+
if (!liveKeys.has(key))
|
|
2551
|
+
publishedTmuxNames.delete(key);
|
|
2552
|
+
}
|
|
2553
|
+
};
|
|
2554
|
+
exports.syncTmuxAgentNames = syncTmuxAgentNames;
|
|
2498
2555
|
/**
|
|
2499
2556
|
* Is this session in the inventory? Answered from the last sweep when it can
|
|
2500
2557
|
* be: a full sweep is two tmux spawns per session on this thread, and it used
|
|
@@ -2971,6 +3028,9 @@ const streamSession = (wsUrl, apiKey) => {
|
|
|
2971
3028
|
};
|
|
2972
3029
|
const publishSessions = ({ sessions, rejected }) => {
|
|
2973
3030
|
(0, exports.recordInventory)(sessions);
|
|
3031
|
+
// Let the user's own status bar say what the phone says. Change-gated
|
|
3032
|
+
// internally, so an unchanged sweep spawns nothing.
|
|
3033
|
+
(0, exports.syncTmuxAgentNames)(sessions);
|
|
2974
3034
|
// Watch hits are one-shot events — they go out on every poll,
|
|
2975
3035
|
// independent of the sessions-report gate below.
|
|
2976
3036
|
// (§S5 v2): probe watched panes and report hits; the server
|
package/dist/remote-hook.js
CHANGED
|
@@ -14,7 +14,7 @@ exports.runRemoteHook = exports.isRemoteHookAgent = exports.REMOTE_HOOK_AGENTS =
|
|
|
14
14
|
* record — same project (cksum of the cwd), fresh (≤15 min), and
|
|
15
15
|
* byte-identical trimmed text — the hook emits additionalContext telling
|
|
16
16
|
* the model the user is driving the session from their phone, which enters
|
|
17
|
-
* sticky REMOTE mode
|
|
17
|
+
* sticky REMOTE mode.
|
|
18
18
|
*
|
|
19
19
|
* Detection is exact: a terminal keystroke racing a phone message can
|
|
20
20
|
* never false-match. Parity with the writer (listener.ts writeRemoteMarker)
|
|
@@ -54,7 +54,7 @@ const HOOK_EVENT_NAME = {
|
|
|
54
54
|
const FRESH_WINDOW_SEC = 900;
|
|
55
55
|
const TWO_WAY_CONTEXT = `# System note (Zeph remote-origin detect)
|
|
56
56
|
|
|
57
|
-
This user message arrived from the user's phone via Zeph agent chat (verified by the listener — exact text match). The user is driving this session remotely and is NOT at the terminal. Enter sticky REMOTE mode now (
|
|
57
|
+
This user message arrived from the user's phone via Zeph agent chat (verified by the listener — exact text match). The user is driving this session remotely and is NOT at the terminal. Enter sticky REMOTE mode now (the "Sticky REMOTE mode" rule in your Zeph rules): end EVERY response with \`zeph_ask\` (buttons + free-text) until the user exits — an exit signal (done/stop/exit), or a prompt they type at the terminal, which this hook will tell you about. Plain-text questions are invisible to them.`;
|
|
58
58
|
const ONE_WAY_CONTEXT = `# System note (Zeph remote-origin detect)
|
|
59
59
|
|
|
60
60
|
This user message arrived from the user's phone via Zeph agent chat (verified by the listener — exact text match), but no hook id is configured (neither \`ZEPH_HOOK_ID\` nor \`hookId\` in ~/.zeph/config.json), so two-way tools (zeph_ask/zeph_prompt/zeph_input) are unavailable. Make your final message self-contained — the completion push is the user's only feedback channel. If you have not already mentioned it this session, tell the user once that running \`npx @zeph-to/cli setup\` upgrades this into a two-way remote session (buttons + text replies from the phone).`;
|
|
@@ -66,7 +66,7 @@ This user message arrived from the user's phone via Zeph agent chat (verified by
|
|
|
66
66
|
*/
|
|
67
67
|
const EXIT_CONTEXT = `# System note (Zeph)
|
|
68
68
|
|
|
69
|
-
The user typed this prompt at the terminal, so this session has LEFT sticky REMOTE mode — answer normally (
|
|
69
|
+
The user typed this prompt at the terminal, so this session has LEFT sticky REMOTE mode — answer normally (the NORMAL branch of your Zeph rules) and do not end this response with \`zeph_ask\` just to keep the loop alive. One rule still holds: if you actually ask the user something, ask it with \`zeph_ask\`. Re-entry is automatic the moment they send another message from their phone.`;
|
|
70
70
|
const isRemoteHookAgent = (raw) => exports.REMOTE_HOOK_AGENTS.includes(raw);
|
|
71
71
|
exports.isRemoteHookAgent = isRemoteHookAgent;
|
|
72
72
|
/**
|
|
@@ -87,7 +87,7 @@ const runRemoteHook = (agent, stdin, env = process.env, now = Date.now) => {
|
|
|
87
87
|
// `cwd` keys every state file, so without it there is nothing to look up.
|
|
88
88
|
if (!cwd)
|
|
89
89
|
return null;
|
|
90
|
-
// Mute outranks everything (
|
|
90
|
+
// Mute outranks everything (the mute rule) — stay silent and leave both the marker
|
|
91
91
|
// and the state untouched (the next inject overwrites the marker anyway).
|
|
92
92
|
if ((0, gate_js_1.isMuted)(cwd))
|
|
93
93
|
return null;
|
package/dist/templates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AA4KA,6FAA6F;AAC7F,eAAO,MAAM,WAAW,QAKtB,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,aAAa,QAIxB,CAAC;AAEH,0FAA0F;AAC1F,eAAO,MAAM,WAAW,QAAyE,CAAC;AAElG,uFAAuF;AACvF,eAAO,MAAM,UAAU,QAAyE,CAAC;AAEjG,oGAAoG;AACpG,eAAO,MAAM,YAAY,QAIvB,CAAC;AAEH,gFAAgF;AAChF,eAAO,MAAM,UAAU,QAIrB,CAAC;AAEH,sGAAsG;AACtG,eAAO,MAAM,UAAU,QAIrB,CAAC;AAEH,6GAA6G;AAC7G,eAAO,MAAM,OAAO,QAIlB,CAAC;AAEH,2GAA2G;AAC3G,eAAO,MAAM,aAAa,QAIxB,CAAC;AAIH,eAAO,MAAM,YAAY,QAKd,CAAC;AAEZ,eAAO,MAAM,cAAc,QAOhB,CAAC;AAEZ,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;CAuBxB,CAAC;AAYF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;CASvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,OAUhD,CAAC;AAEF,eAAO,MAAM,aAAa,QASf,CAAC;AAQZ,2FAA2F;AAC3F,eAAO,MAAM,YAAY,QAsDxB,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,eAAe,QAwG3B,CAAC;AASF,eAAO,MAAM,eAAe,oFAA+E,CAAC;AAC5G,eAAO,MAAM,aAAa,sBAAsB,CAAC;AAQjD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,KAAG,MAWnE,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,MAOrD,CAAC"}
|
package/dist/templates.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
// generated core (src/zeph-core.generated.ts) plus a per-agent
|
|
7
7
|
// notification preamble:
|
|
8
8
|
//
|
|
9
|
-
// - Hook-driven agents (Cursor, Windsurf, Gemini, Codex, Copilot
|
|
9
|
+
// - Hook-driven agents (Cursor, Windsurf, Gemini, Codex, Copilot, Pi,
|
|
10
|
+
// OpenCode) have
|
|
10
11
|
// a Stop-equivalent hook installed that auto-pushes on completion, so
|
|
11
12
|
// they must NOT manually call zeph_notify for "done".
|
|
12
13
|
// - Rule-only agents (Cline, Aider) have no Stop hook, so they DO call
|
|
@@ -16,9 +17,9 @@
|
|
|
16
17
|
// all of them — that is the whole point of the shared generated core.
|
|
17
18
|
//
|
|
18
19
|
// One more axis, and it decides who can enter REMOTE at all: the core's
|
|
19
|
-
// Rules
|
|
20
|
-
// hook note (a phone message) or by a `zeph_ask` answer. Only Gemini
|
|
21
|
-
// have that hook (remoteHookCmd below). Every other agent's only door is the
|
|
20
|
+
// Rules 1/2/8/9 are REMOTE-scoped, and REMOTE is entered by a prompt-submit
|
|
21
|
+
// hook note (a phone message) or by a `zeph_ask` answer. Only Gemini, Codex
|
|
22
|
+
// and Pi have that hook (remoteHookCmd below). Every other agent's only door is the
|
|
22
23
|
// ask itself — so for them the NORMAL branch must still send one after real
|
|
23
24
|
// work, or the phone loop can never begin. That is REMOTE_ENTRY_NO_HOOK.
|
|
24
25
|
//
|
|
@@ -35,8 +36,9 @@ const zeph_core_generated_js_1 = require("./zeph-core.generated.js");
|
|
|
35
36
|
// pattern in plugin/hooks/zeph-{stop,ask}.sh.
|
|
36
37
|
//
|
|
37
38
|
// `--auto` applies the shared push-gate before sending (see src/gate.ts):
|
|
38
|
-
//
|
|
39
|
-
// /zeph-quiet | /zeph-loud dial works for every hook-driven
|
|
39
|
+
// a caller that names no turn counts still pushes (gate defaults assume real
|
|
40
|
+
// work), and the /zeph-quiet | /zeph-loud dial works for every hook-driven
|
|
41
|
+
// agent. The two drop-in artifacts do name counts — see `turnFacts` below.
|
|
40
42
|
//
|
|
41
43
|
// `--pushmode-default normal` is what keeps the first half of that true. The
|
|
42
44
|
// built-in default for a project with no dial is quiet, and quiet only lets a
|
|
@@ -47,8 +49,23 @@ const zeph_core_generated_js_1 = require("./zeph-core.generated.js");
|
|
|
47
49
|
// Older installed `zeph` versions parse both flags as unknown booleans and
|
|
48
50
|
// ignore them — graceful backward compatibility, and for `--pushmode-default`
|
|
49
51
|
// specifically the old build's behavior was already "normal when no dial".
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
//
|
|
53
|
+
// `turnFacts` carries the `--tools` / `--nonreadonly` counts, which only the
|
|
54
|
+
// two drop-in artifacts can supply — a JSON hook config sees no per-tool
|
|
55
|
+
// events, so it passes nothing and keeps riding GATE_DEFAULTS.
|
|
56
|
+
const notifyCmd = (turnFacts = '') => '$(command -v zeph || echo "npx -y @zeph-to/cli") notify --title "Task done" --auto'
|
|
57
|
+
+ turnFacts
|
|
58
|
+
+ ` --${gate_js_1.PUSHMODE_DEFAULT_FLAG} normal 2>/dev/null || true`;
|
|
59
|
+
// The same command as a JS template literal, so an artifact's runtime counters
|
|
60
|
+
// interpolate into it. JSON.stringify would emit a double-quoted string and
|
|
61
|
+
// ship the placeholders verbatim. Safe as long as the command holds no
|
|
62
|
+
// backtick and no `${` of its own — `$(command -v zeph …)` is shell, not JS.
|
|
63
|
+
const notifyCmdLiteral = (turnFacts) => '`' + notifyCmd(turnFacts) + '`';
|
|
64
|
+
// The turn-fact fragment, as JS template-literal placeholders. Both artifacts
|
|
65
|
+
// name their counters `tools` / `nonReadonly`, so one fragment serves both —
|
|
66
|
+
// templates.test.ts pins those identifiers, since a rename on the artifact side
|
|
67
|
+
// would leave `${tools}` unresolved and kill the push with a ReferenceError.
|
|
68
|
+
const TURN_FACT_FLAGS = ` --${gate_js_1.TOOL_COUNT_FLAG} \${tools} --${gate_js_1.NONREADONLY_COUNT_FLAG} \${nonReadonly}`;
|
|
52
69
|
// Prompt-submit hook command — remote-origin detection (ADR-0002). Reads
|
|
53
70
|
// the hook JSON on stdin and prints additionalContext JSON on a marker
|
|
54
71
|
// match (see src/remote-hook.ts). stdout IS the hook response, so only
|
|
@@ -81,7 +98,7 @@ refactor, multi-file changes) call zeph_notify. Skip it for trivial
|
|
|
81
98
|
operations (file reads, simple searches). Set priority "high" for
|
|
82
99
|
errors/blockers.`;
|
|
83
100
|
// REMOTE-entry preamble — agents with NO prompt-submit hook (Cursor, Windsurf,
|
|
84
|
-
// Copilot, Cline, Aider). The shared core scopes Rules
|
|
101
|
+
// Copilot, Cline, Aider, OpenCode). The shared core scopes Rules 1/2/8/9 to REMOTE
|
|
85
102
|
// and tells a NORMAL session it owes no `zeph_ask`. That is right where a
|
|
86
103
|
// hook can announce the phone message that starts REMOTE; here nothing can,
|
|
87
104
|
// and the only remaining entry is a `zeph_ask` answer that is not Done-like.
|
|
@@ -91,7 +108,7 @@ errors/blockers.`;
|
|
|
91
108
|
// says about NORMAL still holds (no ask on trivial turns, questions may go
|
|
92
109
|
// to the local picker, no `zeph_ask` just to mark a turn finished).
|
|
93
110
|
//
|
|
94
|
-
// This overrides the core's Rule
|
|
111
|
+
// This overrides the core's Rule 2 NORMAL clause for these agents. It is a
|
|
95
112
|
// per-agent preamble, not a fork of the core, for the reason the core's
|
|
96
113
|
// header gives: the rule text must stay one thing.
|
|
97
114
|
const REMOTE_ENTRY_NO_HOOK = `## Entering REMOTE without a prompt hook
|
|
@@ -99,14 +116,14 @@ const REMOTE_ENTRY_NO_HOOK = `## Entering REMOTE without a prompt hook
|
|
|
99
116
|
This agent has no prompt-submit hook, so nothing can tell you when a
|
|
100
117
|
message arrived from the user's phone. The ONLY way this session enters
|
|
101
118
|
REMOTE is a \`zeph_ask\` answer that is not a Done-like button. So — and
|
|
102
|
-
this overrides the "In NORMAL, end with nothing" clause
|
|
119
|
+
this overrides the "In NORMAL, end with nothing" clause below —
|
|
103
120
|
**after substantial work in NORMAL, end the response with \`zeph_ask\`**:
|
|
104
121
|
2–4 \`actions\` carrying the next-step candidates plus a Done-like
|
|
105
122
|
\`fallback\`, \`timeout\` 300–600 s. "Substantial" = file changes, commits,
|
|
106
123
|
builds, tests, deploys, destructive ops, milestone completions. Skip it on
|
|
107
124
|
trivial turns (read-only exploration, a mid-step in an approved plan, a
|
|
108
|
-
typo-sized fix). Once the answer reports \`zephState: "REMOTE"\`,
|
|
109
|
-
takes over.`;
|
|
125
|
+
typo-sized fix). Once the answer reports \`zephState: "REMOTE"\`, sticky REMOTE
|
|
126
|
+
mode takes over.`;
|
|
110
127
|
// Tool-access preamble — pi only.
|
|
111
128
|
const PI_TOOL_ACCESS = `## Zeph tools via the CLI (no MCP)
|
|
112
129
|
|
|
@@ -191,13 +208,13 @@ exports.OPENCODE_RULE = buildRule({
|
|
|
191
208
|
exports.CURSOR_HOOKS = JSON.stringify({
|
|
192
209
|
version: 1,
|
|
193
210
|
hooks: {
|
|
194
|
-
stop: [{ command:
|
|
211
|
+
stop: [{ command: notifyCmd() }],
|
|
195
212
|
},
|
|
196
213
|
}, null, 2);
|
|
197
214
|
exports.WINDSURF_HOOKS = JSON.stringify({
|
|
198
215
|
hooks: {
|
|
199
216
|
post_cascade_response: [{
|
|
200
|
-
command:
|
|
217
|
+
command: notifyCmd(),
|
|
201
218
|
show_output: false,
|
|
202
219
|
}],
|
|
203
220
|
},
|
|
@@ -220,7 +237,7 @@ exports.GEMINI_HOOKS = {
|
|
|
220
237
|
hooks: [{
|
|
221
238
|
name: 'zeph-notify',
|
|
222
239
|
type: 'command',
|
|
223
|
-
command:
|
|
240
|
+
command: notifyCmd(),
|
|
224
241
|
}],
|
|
225
242
|
}],
|
|
226
243
|
},
|
|
@@ -242,7 +259,7 @@ exports.CODEX_HOOKS = {
|
|
|
242
259
|
hooks: [{ type: 'command', command: remoteHookCmd('codex'), timeout: 5 }],
|
|
243
260
|
}],
|
|
244
261
|
Stop: [{
|
|
245
|
-
hooks: [{ type: 'command', command:
|
|
262
|
+
hooks: [{ type: 'command', command: notifyCmd() }],
|
|
246
263
|
}],
|
|
247
264
|
},
|
|
248
265
|
};
|
|
@@ -270,7 +287,7 @@ exports.COPILOT_HOOKS = JSON.stringify({
|
|
|
270
287
|
hooks: {
|
|
271
288
|
sessionEnd: [{
|
|
272
289
|
type: 'command',
|
|
273
|
-
bash:
|
|
290
|
+
bash: notifyCmd(),
|
|
274
291
|
timeoutSec: 10,
|
|
275
292
|
}],
|
|
276
293
|
},
|
|
@@ -295,17 +312,36 @@ const sh = (cmd: string, cwd: string, stdin?: string): Promise<string> =>
|
|
|
295
312
|
child.stdin.end(stdin ?? "");
|
|
296
313
|
});
|
|
297
314
|
|
|
315
|
+
// Read-only tools, mirroring what the Claude Code Stop hook excludes
|
|
316
|
+
// (Read/Grep/Glob). Names read from the installed package's dist/core/tools on
|
|
317
|
+
// 2026-08-28: bash, edit, find, grep, ls, powershell, read, write — \`find\` is
|
|
318
|
+
// pi's Glob. Anything absent here, MCP tools included, counts as real work, so
|
|
319
|
+
// an unknown tool errs toward pushing rather than toward silence.
|
|
320
|
+
const READ_ONLY = new Set(["read", "grep", "find", "ls"]);
|
|
321
|
+
|
|
298
322
|
export default function (pi: ExtensionAPI) {
|
|
323
|
+
// Turn facts for the push gate. One agent per process, so plain counters
|
|
324
|
+
// suffice — no session keying.
|
|
325
|
+
let tools = 0;
|
|
326
|
+
let nonReadonly = 0;
|
|
327
|
+
|
|
328
|
+
pi.on("tool_execution_end", (event) => {
|
|
329
|
+
tools += 1;
|
|
330
|
+
if (!READ_ONLY.has(event.toolName)) nonReadonly += 1;
|
|
331
|
+
});
|
|
299
332
|
// Stop-equivalent: agent_settled fires once per user turn, after retries/compaction.
|
|
300
333
|
// Fire-and-forget: never block pi's turn-end on the notify network call.
|
|
301
334
|
pi.on("agent_settled", (_event, ctx) => {
|
|
302
|
-
const child = spawn("sh", ["-c", ${
|
|
335
|
+
const child = spawn("sh", ["-c", ${notifyCmdLiteral(TURN_FACT_FLAGS)}], { cwd: ctx.cwd, stdio: "ignore", detached: true });
|
|
303
336
|
child.on("error", () => {});
|
|
304
337
|
child.unref();
|
|
305
338
|
});
|
|
306
339
|
// Prompt-submit: remote-origin detection (ADR-0002) — additionalContext comes
|
|
307
340
|
// back as a persistent injected message (this extension is the consumer).
|
|
341
|
+
// Doubles as the turn boundary: this is where the counters start over.
|
|
308
342
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
343
|
+
tools = 0;
|
|
344
|
+
nonReadonly = 0;
|
|
309
345
|
const out = await sh(${JSON.stringify(remoteHookCmd('pi'))}, ctx.cwd,
|
|
310
346
|
JSON.stringify({ prompt: event.prompt, cwd: ctx.cwd }));
|
|
311
347
|
try {
|
|
@@ -320,20 +356,108 @@ export default function (pi: ExtensionAPI) {
|
|
|
320
356
|
/** OpenCode plugin source — written to ~/.config/opencode/plugins/zeph.ts (drop-in auto-load). */
|
|
321
357
|
exports.OPENCODE_PLUGIN = `// Generated by @zeph-to/cli — reinstall overwrites; \`zeph uninstall\` removes.
|
|
322
358
|
import { spawn } from "node:child_process";
|
|
359
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
323
360
|
|
|
324
|
-
|
|
361
|
+
// Read-only tools, mirroring what the Claude Code Stop hook excludes
|
|
362
|
+
// (Read/Grep/Glob). Names read from the installed opencode binary's tool
|
|
363
|
+
// registry on 2026-08-28: read, list, glob, grep, webfetch, websearch, task,
|
|
364
|
+
// shell, bash, edit, write, patch, skill, lsp, todowrite. Anything absent
|
|
365
|
+
// here, MCP tools included, counts as real work — an unknown tool errs toward
|
|
366
|
+
// pushing rather than toward silence.
|
|
367
|
+
const READ_ONLY = new Set(["read", "grep", "glob", "list"]);
|
|
325
368
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
369
|
+
type TurnFacts = { tools: number; nonReadonly: number };
|
|
370
|
+
const zeroFacts = (): TurnFacts => ({ tools: 0, nonReadonly: 0 });
|
|
371
|
+
|
|
372
|
+
// Typed against the real plugin contract, the way the pi extension is: the
|
|
373
|
+
// hook names and payload shapes this file asserts by hand are then checked by
|
|
374
|
+
// the compiler, so an upstream rename fails loudly instead of silently
|
|
375
|
+
// handing every handler an \`any\`.
|
|
376
|
+
export const ZephPlugin: Plugin = async ({ client, directory }) => {
|
|
377
|
+
// Turn facts, keyed by session. The plugin instance is per-project, not
|
|
378
|
+
// per-session, and the \`task\` tool runs subagents in their own child
|
|
379
|
+
// sessions with their own full lifecycle — one shared counter would bill
|
|
380
|
+
// their tool calls to whatever else the project had running.
|
|
381
|
+
const facts = new Map<string, TurnFacts>();
|
|
382
|
+
|
|
383
|
+
// A child session's id is all \`session.idle\` carries; the parent link lives
|
|
384
|
+
// on the session record. This is the one blocking call in the idle handler —
|
|
385
|
+
// the notify spawn below stays fire-and-forget, but the push decision cannot
|
|
386
|
+
// be made without knowing whether this session is somebody's subagent. The
|
|
387
|
+
// generated client defaults to \`throwOnError: false\`, so a non-2xx arrives as
|
|
388
|
+
// a result with no \`data\` rather than as a throw; both paths land on
|
|
389
|
+
// undefined, which pushes — the same direction every other unknown in this
|
|
390
|
+
// file leans.
|
|
391
|
+
const parentOf = async (id: string): Promise<string | undefined> => {
|
|
392
|
+
try {
|
|
393
|
+
return (await client.session.get({ path: { id } })).data?.parentID;
|
|
394
|
+
} catch {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
// Turn start. Create-if-absent, never reset: this fires per *message*, and
|
|
401
|
+
// a second message queued while the agent is still working would otherwise
|
|
402
|
+
// zero the counts already earned. session.idle deletes the entry, so the
|
|
403
|
+
// next turn always starts from zero anyway.
|
|
404
|
+
"chat.message": async ({ sessionID }) => {
|
|
405
|
+
if (!facts.has(sessionID)) facts.set(sessionID, zeroFacts());
|
|
406
|
+
},
|
|
407
|
+
"tool.execute.after": async ({ tool, sessionID }) => {
|
|
408
|
+
const entry = facts.get(sessionID) ?? zeroFacts();
|
|
409
|
+
entry.tools += 1;
|
|
410
|
+
if (!READ_ONLY.has(tool)) entry.nonReadonly += 1;
|
|
411
|
+
facts.set(sessionID, entry);
|
|
412
|
+
},
|
|
413
|
+
// Stop-equivalent. Unlike the named hooks above, session.idle arrives only
|
|
414
|
+
// through the generic \`event\` hook — there is no per-event key for it, so
|
|
415
|
+
// filter by type.
|
|
416
|
+
event: async ({ event }) => {
|
|
417
|
+
// A deleted session never idles again, so nothing would ever consume its
|
|
418
|
+
// entry. Drop it here or it outlives the turn for the life of the
|
|
419
|
+
// process. An *aborted* turn needs no such handling: the entry survives
|
|
420
|
+
// to the session's next chat.message, which adds to it rather than
|
|
421
|
+
// resetting, so the interrupted work is reported with the retry.
|
|
422
|
+
if (event.type === "session.deleted") {
|
|
423
|
+
facts.delete(event.properties.info.id);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (event.type !== "session.idle") return;
|
|
427
|
+
const { sessionID } = event.properties;
|
|
428
|
+
// No entry means no user message was seen for this session, so there is
|
|
429
|
+
// no turn to report — the zeroes gate the push out, which is correct.
|
|
430
|
+
const settled = facts.get(sessionID) ?? zeroFacts();
|
|
431
|
+
facts.delete(sessionID);
|
|
432
|
+
|
|
433
|
+
// A \`task\` subagent settles first, in its own session. Pushing there
|
|
434
|
+
// would announce "Task done" for a slice of a turn still in flight, and
|
|
435
|
+
// leave the parent crediting the whole delegation as the single
|
|
436
|
+
// \`task\` call it saw — one tool, below the gate, so the real
|
|
437
|
+
// completion would go silent. Roll the counts up and stay quiet.
|
|
438
|
+
const parentID = await parentOf(sessionID);
|
|
439
|
+
const parent = parentID === undefined ? undefined : facts.get(parentID);
|
|
440
|
+
if (parent) {
|
|
441
|
+
parent.tools += settled.tools;
|
|
442
|
+
parent.nonReadonly += settled.nonReadonly;
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
// Falling through means no live parent entry to roll into — either this
|
|
446
|
+
// is a top-level session, or a \`background\` task outlived the turn that
|
|
447
|
+
// spawned it and the parent settled and pushed already, or the lookup
|
|
448
|
+
// above failed. The three are indistinguishable from here, and all three
|
|
449
|
+
// want the same thing: report this session's own work rather than write
|
|
450
|
+
// it into an entry nobody will ever read. Never conjure that entry — that
|
|
451
|
+
// would leak one per background task.
|
|
452
|
+
|
|
453
|
+
const { tools, nonReadonly } = settled;
|
|
454
|
+
// Fire-and-forget: never block opencode's event pipeline on the notify call.
|
|
455
|
+
const child = spawn("sh", ["-c", ${notifyCmdLiteral(TURN_FACT_FLAGS)}], { cwd: directory, stdio: "ignore", detached: true });
|
|
456
|
+
child.on("error", () => {});
|
|
457
|
+
child.unref();
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
};
|
|
337
461
|
`;
|
|
338
462
|
// ── Marker-section helpers for shared global rule files ──────────
|
|
339
463
|
//
|
package/dist/wrapper.d.ts
CHANGED
|
@@ -6,15 +6,25 @@ export declare const tmuxSessionName: (project: string) => string;
|
|
|
6
6
|
/**
|
|
7
7
|
* Pick a tmux session name that won't steal focus from another live
|
|
8
8
|
* `zeph cc`. Strategy:
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
* `<base>-3`, … so the new `zeph cc` gets an independent session
|
|
13
|
-
* instead of joining the existing one.
|
|
14
|
-
* Falls back to `<base>` after 20 attempts (shouldn't realistically hit).
|
|
9
|
+
* - Reuse the **highest-numbered detached** session in the family.
|
|
10
|
+
* - Otherwise take the lowest name that doesn't exist yet (create new),
|
|
11
|
+
* so an all-attached family gets an independent `<base>-N`.
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* Highest-detached rather than lowest is what keeps the family reachable.
|
|
14
|
+
* The old scan walked the family in order and returned the first session that
|
|
15
|
+
* was missing *or* detached, which meant a detached `<base>` ended the scan
|
|
16
|
+
* every time: `<base>-2` could not be reached by any later `zeph cc`, not even
|
|
17
|
+
* after `<base>` was used and exited (a missing `<base>` is taken fresh). A
|
|
18
|
+
* session nobody can reach still holds a live agent — a Claude Code CLI plus
|
|
19
|
+
* its MCP fleet, 250-550MB measured — so stranding one leaks memory the user
|
|
20
|
+
* can neither see nor reclaim. Draining from the tail makes every suffix
|
|
21
|
+
* reachable again: the tail goes first, and `<base>` is picked on the run
|
|
22
|
+
* after that. Reuse also wins over a gap in the numbering, for the same
|
|
23
|
+
* reason — a free `<base>-2` must not hide a detached `<base>-3`.
|
|
24
|
+
*
|
|
25
|
+
* Reclaiming is deliberately all this does. Killing a detached session would
|
|
26
|
+
* take a live agent with it, and nothing here can tell "the user is done with
|
|
27
|
+
* this" from "the user closed the terminal and will come back".
|
|
18
28
|
*/
|
|
19
29
|
export declare const findAvailableSession: (base: string) => string;
|
|
20
30
|
/**
|
package/dist/wrapper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,MAapC,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,MAapC,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAA2B,CAAC;AAkC9E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,MAOnD,CAAC;AAmCF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,GAAG,IAAI,EAAE,WAAW,MAAM,KAAG,OAChC,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,IAAI,CAgC1D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAU,OAAO,WAAW,EAAE,QAAO,MAAM,EAAO,KAAG,OAAO,CAAC,MAAM,CAmCjG,CAAC"}
|
package/dist/wrapper.js
CHANGED
|
@@ -43,37 +43,67 @@ exports.detectProjectName = detectProjectName;
|
|
|
43
43
|
/** `zeph-<project>` — the canonical tmux session base name. */
|
|
44
44
|
const tmuxSessionName = (project) => `zeph-${project}`;
|
|
45
45
|
exports.tmuxSessionName = tmuxSessionName;
|
|
46
|
+
/**
|
|
47
|
+
* Every live tmux session as `name → attached`, in one spawn.
|
|
48
|
+
*
|
|
49
|
+
* `tmux list-sessions` exits non-zero when no server is running; that is the
|
|
50
|
+
* ordinary "first `zeph cc` after a reboot" case, not an error, and it means
|
|
51
|
+
* an empty family.
|
|
52
|
+
*
|
|
53
|
+
* The format puts the 0/1 flag first so the name — which carries whatever the
|
|
54
|
+
* project directory was called, spaces included — is everything after the
|
|
55
|
+
* first space and needs no quoting.
|
|
56
|
+
*/
|
|
57
|
+
const liveSessions = () => {
|
|
58
|
+
const r = (0, child_process_1.spawnSync)('tmux', ['list-sessions', '-F', '#{session_attached} #{session_name}'], {
|
|
59
|
+
encoding: 'utf-8',
|
|
60
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
61
|
+
});
|
|
62
|
+
const out = new Map();
|
|
63
|
+
if (r.status !== 0)
|
|
64
|
+
return out;
|
|
65
|
+
for (const line of (r.stdout ?? '').split('\n')) {
|
|
66
|
+
const sep = line.indexOf(' ');
|
|
67
|
+
if (sep < 0)
|
|
68
|
+
continue;
|
|
69
|
+
out.set(line.slice(sep + 1), line.slice(0, sep) === '1');
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
};
|
|
46
73
|
const MAX_SUFFIX_ATTEMPTS = 20;
|
|
74
|
+
/** `<base>`, `<base>-2`, `<base>-3`, … — the session family for one project. */
|
|
75
|
+
const familyNames = (base) => Array.from({ length: MAX_SUFFIX_ATTEMPTS }, (_, i) => (i === 0 ? base : `${base}-${i + 1}`));
|
|
47
76
|
/**
|
|
48
77
|
* Pick a tmux session name that won't steal focus from another live
|
|
49
78
|
* `zeph cc`. Strategy:
|
|
50
|
-
* -
|
|
51
|
-
* -
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
79
|
+
* - Reuse the **highest-numbered detached** session in the family.
|
|
80
|
+
* - Otherwise take the lowest name that doesn't exist yet (create new),
|
|
81
|
+
* so an all-attached family gets an independent `<base>-N`.
|
|
82
|
+
*
|
|
83
|
+
* Highest-detached rather than lowest is what keeps the family reachable.
|
|
84
|
+
* The old scan walked the family in order and returned the first session that
|
|
85
|
+
* was missing *or* detached, which meant a detached `<base>` ended the scan
|
|
86
|
+
* every time: `<base>-2` could not be reached by any later `zeph cc`, not even
|
|
87
|
+
* after `<base>` was used and exited (a missing `<base>` is taken fresh). A
|
|
88
|
+
* session nobody can reach still holds a live agent — a Claude Code CLI plus
|
|
89
|
+
* its MCP fleet, 250-550MB measured — so stranding one leaks memory the user
|
|
90
|
+
* can neither see nor reclaim. Draining from the tail makes every suffix
|
|
91
|
+
* reachable again: the tail goes first, and `<base>` is picked on the run
|
|
92
|
+
* after that. Reuse also wins over a gap in the numbering, for the same
|
|
93
|
+
* reason — a free `<base>-2` must not hide a detached `<base>-3`.
|
|
56
94
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
95
|
+
* Reclaiming is deliberately all this does. Killing a detached session would
|
|
96
|
+
* take a live agent with it, and nothing here can tell "the user is done with
|
|
97
|
+
* this" from "the user closed the terminal and will come back".
|
|
59
98
|
*/
|
|
60
99
|
const findAvailableSession = (base) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (has.status !== 0)
|
|
67
|
-
return name; // doesn't exist — fresh session
|
|
68
|
-
const clients = (0, child_process_1.spawnSync)('tmux', ['list-clients', '-t', name, '-F', '#{client_tty}'], {
|
|
69
|
-
encoding: 'utf-8',
|
|
70
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
71
|
-
});
|
|
72
|
-
const attached = (clients.stdout ?? '').trim().length > 0;
|
|
73
|
-
if (!attached)
|
|
74
|
-
return name; // exists but detached — reattach
|
|
100
|
+
const live = liveSessions();
|
|
101
|
+
const family = familyNames(base);
|
|
102
|
+
for (let i = family.length - 1; i >= 0; i--) {
|
|
103
|
+
if (live.get(family[i]) === false)
|
|
104
|
+
return family[i];
|
|
75
105
|
}
|
|
76
|
-
return base;
|
|
106
|
+
return family.find((name) => !live.has(name)) ?? base;
|
|
77
107
|
};
|
|
78
108
|
exports.findAvailableSession = findAvailableSession;
|
|
79
109
|
/** POSIX shell-quote so passthrough args survive being joined into a tmux shell-command string. */
|
|
@@ -87,10 +117,10 @@ const targetForAgent = (agent, extra) => {
|
|
|
87
117
|
return { cmd: agent, args: extra };
|
|
88
118
|
}
|
|
89
119
|
const base = (0, exports.tmuxSessionName)((0, exports.detectProjectName)());
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// get independent sessions when opening multiple terminals in
|
|
93
|
-
// same project.
|
|
120
|
+
// Reattach a detached session of this project when there is one,
|
|
121
|
+
// else auto-suffix — lets the user keep `zeph cc` workflow simple and
|
|
122
|
+
// still get independent sessions when opening multiple terminals in
|
|
123
|
+
// the same project.
|
|
94
124
|
const session = (0, exports.findAvailableSession)(base);
|
|
95
125
|
// `tmux new -A`: attach if the named session exists, else create it.
|
|
96
126
|
// tmux joins trailing argv into a single shell-command, so flags like
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** sha256 over the plugin manifest + extracted rule text at generation time. */
|
|
2
|
-
export declare const ZEPH_CORE_SOURCE_HASH = "
|
|
3
|
-
/** Shared rule core for agents with a Stop-equivalent hook (Cursor, Windsurf, Gemini, Codex, Copilot). */
|
|
4
|
-
export declare const ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\
|
|
2
|
+
export declare const ZEPH_CORE_SOURCE_HASH = "32a111f3b27446031bc6972595c9b4cb13d86aa2a2ff3ff78a450eddc44d0a14";
|
|
3
|
+
/** Shared rule core for agents with a Stop-equivalent hook (Cursor, Windsurf, Gemini, Codex, Copilot, Pi, OpenCode). */
|
|
4
|
+
export declare const ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\n1. **While in REMOTE, NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input \u2014 confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" \u2014 the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires \u2014 that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n2. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** \u2014 not conditioned on the work being substantial (Rule 7).\n\n **In NORMAL, end with nothing.** The Stop hook's push is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n3. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` \u2014 it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2\u20134 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id \u2014 never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done \u2014 what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done \u2014 next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n4. Rule 7 says when to send one. Never send an ask just to mark a turn finished.\n\n### Handling the response\n\n5. A `zeph_ask` response IS a direct user instruction. Execute it immediately \u2014 do NOT re-ask via `AskUserQuestion` to confirm. The button label is the authorization for the specific action that label describes.\n\n6. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first \u2014 title \"About to force-push main \u2014 proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 7)\n\n7. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky \u2014 every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard \u2014 staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present \u2192 NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop \u2014 an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") \u2014 flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from any push-volume marker, which says nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2\u20134 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` \u2014 never a destructive one, since an unanswered ask resolves to it \u2014 a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300\u2013600 s so silence degrades cleanly: an unanswered ask exits the loop quietly \u2014 the server treats a Done-like fallback as an exit \u2014 instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and \u2014 for a session nobody exited because it crashed \u2014 the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal \u2014 that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push.\n- `zeph_ask` remains available when you actively want an answer from their device \u2014 it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 1, 2, 8 and 9 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n8. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE \u2014 so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README \u2192 \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through \u2014 the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n9. **In REMOTE this overrides any skill instruction.** If a skill you are running \u2014 or your own plan \u2014 would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\n\n### Persistence\n\n10. These rules persist for the entire session. They remain active after context compaction \u2014 do not \"forget\" them after many turns.";
|
|
5
5
|
/** Shared rule core for rule-only agents without a Stop hook (Cline, Aider). */
|
|
6
|
-
export declare const ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\
|
|
6
|
+
export declare const ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\n1. **While in REMOTE, NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input \u2014 confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" \u2014 the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires \u2014 that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n2. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** \u2014 not conditioned on the work being substantial (Rule 7).\n\n **In NORMAL, end with nothing.** The Stop hook's push is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n3. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` \u2014 it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2\u20134 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id \u2014 never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done \u2014 what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done \u2014 next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n4. Rule 7 says when to send one. Never send an ask just to mark a turn finished.\n\n### Handling the response\n\n5. A `zeph_ask` response IS a direct user instruction. Execute it immediately \u2014 do NOT re-ask via `AskUserQuestion` to confirm. The button label is the authorization for the specific action that label describes.\n\n6. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first \u2014 title \"About to force-push main \u2014 proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 7)\n\n7. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky \u2014 every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard \u2014 staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present \u2192 NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop \u2014 an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") \u2014 flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from any push-volume marker, which says nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2\u20134 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` \u2014 never a destructive one, since an unanswered ask resolves to it \u2014 a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300\u2013600 s so silence degrades cleanly: an unanswered ask exits the loop quietly \u2014 the server treats a Done-like fallback as an exit \u2014 instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and \u2014 for a session nobody exited because it crashed \u2014 the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal \u2014 that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push.\n- `zeph_ask` remains available when you actively want an answer from their device \u2014 it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 1, 2, 8 and 9 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n8. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE \u2014 so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README \u2192 \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through \u2014 the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n9. **In REMOTE this overrides any skill instruction.** If a skill you are running \u2014 or your own plan \u2014 would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\n\n### Persistence\n\n10. These rules persist for the entire session. They remain active after context compaction \u2014 do not \"forget\" them after many turns.";
|
|
7
7
|
//# sourceMappingURL=zeph-core.generated.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zeph-core.generated.d.ts","sourceRoot":"","sources":["../src/zeph-core.generated.ts"],"names":[],"mappings":"AAMA,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,qEAAqE,CAAC;AAExG,
|
|
1
|
+
{"version":3,"file":"zeph-core.generated.d.ts","sourceRoot":"","sources":["../src/zeph-core.generated.ts"],"names":[],"mappings":"AAMA,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,qEAAqE,CAAC;AAExG,wHAAwH;AACxH,eAAO,MAAM,qBAAqB,24PAAsuP,CAAC;AAEzwP,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,24PAAsuP,CAAC"}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.ZEPH_CORE_RULE_ONLY = exports.ZEPH_CORE_HOOK_DRIVEN = exports.ZEPH_CORE_SOURCE_HASH = void 0;
|
|
9
9
|
/** sha256 over the plugin manifest + extracted rule text at generation time. */
|
|
10
|
-
exports.ZEPH_CORE_SOURCE_HASH = "
|
|
11
|
-
/** Shared rule core for agents with a Stop-equivalent hook (Cursor, Windsurf, Gemini, Codex, Copilot). */
|
|
12
|
-
exports.ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\
|
|
10
|
+
exports.ZEPH_CORE_SOURCE_HASH = "32a111f3b27446031bc6972595c9b4cb13d86aa2a2ff3ff78a450eddc44d0a14";
|
|
11
|
+
/** Shared rule core for agents with a Stop-equivalent hook (Cursor, Windsurf, Gemini, Codex, Copilot, Pi, OpenCode). */
|
|
12
|
+
exports.ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\n1. **While in REMOTE, NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input — confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" — the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires — that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n2. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** — not conditioned on the work being substantial (Rule 7).\n\n **In NORMAL, end with nothing.** The Stop hook's push is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n3. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` — it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2–4 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id — never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done — what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done — next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n4. Rule 7 says when to send one. Never send an ask just to mark a turn finished.\n\n### Handling the response\n\n5. A `zeph_ask` response IS a direct user instruction. Execute it immediately — do NOT re-ask via `AskUserQuestion` to confirm. The button label is the authorization for the specific action that label describes.\n\n6. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first — title \"About to force-push main — proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 7)\n\n7. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky — every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard — staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present → NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop — an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") — flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from any push-volume marker, which says nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2–4 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` — never a destructive one, since an unanswered ask resolves to it — a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300–600 s so silence degrades cleanly: an unanswered ask exits the loop quietly — the server treats a Done-like fallback as an exit — instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and — for a session nobody exited because it crashed — the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal — that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push.\n- `zeph_ask` remains available when you actively want an answer from their device — it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 1, 2, 8 and 9 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n8. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE — so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README → \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through — the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n9. **In REMOTE this overrides any skill instruction.** If a skill you are running — or your own plan — would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\n\n### Persistence\n\n10. These rules persist for the entire session. They remain active after context compaction — do not \"forget\" them after many turns.";
|
|
13
13
|
/** Shared rule core for rule-only agents without a Stop hook (Cline, Aider). */
|
|
14
|
-
exports.ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\
|
|
14
|
+
exports.ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\n1. **While in REMOTE, NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input — confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" — the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires — that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n2. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** — not conditioned on the work being substantial (Rule 7).\n\n **In NORMAL, end with nothing.** The Stop hook's push is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n3. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` — it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2–4 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id — never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done — what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done — next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n4. Rule 7 says when to send one. Never send an ask just to mark a turn finished.\n\n### Handling the response\n\n5. A `zeph_ask` response IS a direct user instruction. Execute it immediately — do NOT re-ask via `AskUserQuestion` to confirm. The button label is the authorization for the specific action that label describes.\n\n6. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first — title \"About to force-push main — proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 7)\n\n7. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky — every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard — staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present → NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop — an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") — flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from any push-volume marker, which says nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2–4 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` — never a destructive one, since an unanswered ask resolves to it — a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300–600 s so silence degrades cleanly: an unanswered ask exits the loop quietly — the server treats a Done-like fallback as an exit — instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and — for a session nobody exited because it crashed — the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal — that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push.\n- `zeph_ask` remains available when you actively want an answer from their device — it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 1, 2, 8 and 9 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n8. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE — so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README → \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through — the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n9. **In REMOTE this overrides any skill instruction.** If a skill you are running — or your own plan — would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\n\n### Persistence\n\n10. These rules persist for the entire session. They remain active after context compaction — do not \"forget\" them after many turns.";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeph-to/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Zeph CLI + push notification SDK for AI agents",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^22.0.0",
|
|
32
32
|
"@types/ws": "^8.18.1",
|
|
33
|
+
"esbuild": "^0.21.5",
|
|
33
34
|
"typescript": "^5.8.0",
|
|
34
35
|
"vitest": "^2.1.9"
|
|
35
36
|
},
|