@zeph-to/cli 1.13.2 → 1.15.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/dist/cli.js CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const fs_1 = require("fs");
5
4
  const child_process_1 = require("child_process");
6
5
  const zeph_hook_js_1 = require("./zeph-hook.js");
7
6
  const errors_js_1 = require("./errors.js");
@@ -13,21 +12,10 @@ const check_update_js_1 = require("./check-update.js");
13
12
  const wrapper_js_1 = require("./wrapper.js");
14
13
  const listener_js_1 = require("./listener.js");
15
14
  const config_js_1 = require("./config.js");
16
- const PROJECT_DIR_VARS = ['CLAUDE_PROJECT_DIR', 'CURSOR_PROJECT_DIR', 'WINDSURF_PROJECT_DIR'];
17
- const detectProjectDir = () => PROJECT_DIR_VARS.reduce((found, key) => found || process.env[key], undefined) ?? process.cwd();
18
- const isMuted = () => {
19
- try {
20
- const dir = detectProjectDir();
21
- const raw = (0, child_process_1.execFileSync)('cksum', { input: dir, encoding: 'utf-8' });
22
- const hash = raw.split(' ')[0];
23
- return (0, fs_1.existsSync)(`/tmp/zeph-muted-${hash}`);
24
- }
25
- catch {
26
- return false;
27
- }
28
- };
15
+ const gate_js_1 = require("./gate.js");
16
+ const remote_agents_js_1 = require("./remote-agents.js");
29
17
  const detectBranchAndProject = () => {
30
- const dir = detectProjectDir();
18
+ const dir = (0, config_js_1.detectProjectDir)();
31
19
  const project = dir.split('/').filter(Boolean).pop() ?? 'project';
32
20
  let branch;
33
21
  try {
@@ -67,6 +55,8 @@ const parseArgs = (argv) => {
67
55
  return result;
68
56
  };
69
57
  // ── Output ──────────────────────────────────────────────────────
58
+ /** One usage line per registered remote agent — generated so help text can't drift from the table. */
59
+ const usageAgentLines = () => remote_agents_js_1.REMOTE_AGENTS.map((a) => ` ${`${a.subcommands[0]} [args…]`.padEnd(15)} Run '${a.binary}' in a named tmux session ('zeph-<project>')`).join('\n');
70
60
  const printUsage = () => {
71
61
  console.log(`Usage: zeph <command> [options]
72
62
 
@@ -80,9 +70,7 @@ Commands:
80
70
  list List recent push notifications
81
71
  dismiss <id> Dismiss a push notification (or --all)
82
72
  test Send a test notification to verify setup
83
- cc [args…] Run 'claude' in a named tmux session ('zeph-<project>')
84
- codex [args…] Run 'codex' in a named tmux session
85
- gemini [args…] Run 'gemini' in a named tmux session
73
+ ${usageAgentLines()}
86
74
  (auto-suffixed -2/-3/… when another zeph cc is already
87
75
  attached to the default name; any args after the
88
76
  subcommand are forwarded verbatim, e.g.
@@ -99,6 +87,11 @@ Notify options:
99
87
  --priority <p> Priority (low|normal|high|urgent) [default: normal]
100
88
  --device <id> Target device ID
101
89
  --session <id> AI session ID (or set ZEPH_SESSION_ID env)
90
+ --auto Apply the push gate before sending (honors the
91
+ /zeph-quiet | /zeph-loud dial; silent exit when gated)
92
+ --marker <m> Push Signal marker for --auto (skip|push|high)
93
+ --tools <n> Turn tool count for --auto [default: assume real work]
94
+ --nonreadonly <n> Non-read-only tool count for --auto
102
95
 
103
96
  List options:
104
97
  --limit <n> Number of pushes (1-20, default 5)
@@ -166,10 +159,33 @@ const createHook = (args) => {
166
159
  ...(baseUrl && { baseUrl }),
167
160
  });
168
161
  };
162
+ /** Parse a gate count flag; garbage input falls back to the default (never accidentally silences). */
163
+ const gateCount = (raw, fallback) => {
164
+ const n = typeof raw === 'string' ? Number(raw) : NaN;
165
+ return Number.isFinite(n) && n >= 0 ? n : fallback;
166
+ };
169
167
  const handleNotify = async (args) => {
170
168
  const isJson = args.json === true;
171
- if (isMuted())
169
+ const projectDir = (0, config_js_1.detectProjectDir)();
170
+ if ((0, gate_js_1.isMuted)(projectDir))
172
171
  return 0;
172
+ // --auto: apply the shared push-gate before sending. Inputs default to
173
+ // GATE_DEFAULTS ("assume real work") so dumb hooks keep their historical
174
+ // always-push behavior in normal mode, while the /zeph-quiet | /zeph-loud
175
+ // dial now works for every hook-driven agent. Gated-out → silent success.
176
+ if (args.auto === true) {
177
+ const verdict = (0, gate_js_1.decidePush)({
178
+ toolCount: gateCount(args.tools, gate_js_1.GATE_DEFAULTS.toolCount),
179
+ nonReadonlyCount: gateCount(args.nonreadonly, gate_js_1.GATE_DEFAULTS.nonReadonlyCount),
180
+ alreadyAsked: gate_js_1.GATE_DEFAULTS.alreadyAsked,
181
+ marker: (0, gate_js_1.normalizeMarker)(typeof args.marker === 'string' ? args.marker : undefined),
182
+ pushMode: (0, gate_js_1.readPushMode)(projectDir),
183
+ });
184
+ if (!verdict.push)
185
+ return 0;
186
+ if (verdict.priority === 'high' && !args.priority)
187
+ args.priority = 'high';
188
+ }
173
189
  const hook = createHook(args);
174
190
  if (!hook)
175
191
  return 3;
@@ -337,6 +353,12 @@ const main = async () => {
337
353
  printUsage();
338
354
  return 0;
339
355
  }
356
+ // Remote-control subcommands (`zeph cc` / `zeph codex` / …) come from the
357
+ // registry — one table row per agent, no hardcoded cases. Pass the typed
358
+ // command token to collectPassthrough (aliases map to the same agent).
359
+ const remote = (0, remote_agents_js_1.findAgentBySubcommand)(command);
360
+ if (remote)
361
+ return (0, wrapper_js_1.handleAgentSession)(remote, collectPassthrough(process.argv, command));
340
362
  switch (command) {
341
363
  case 'install':
342
364
  case 'setup':
@@ -357,12 +379,6 @@ const main = async () => {
357
379
  return handleDismiss(args);
358
380
  case 'test':
359
381
  return handleTest(args);
360
- case 'cc':
361
- return (0, wrapper_js_1.handleAgentSession)('claude', collectPassthrough(process.argv, 'cc'));
362
- case 'codex':
363
- return (0, wrapper_js_1.handleAgentSession)('codex', collectPassthrough(process.argv, 'codex'));
364
- case 'gemini':
365
- return (0, wrapper_js_1.handleAgentSession)('gemini', collectPassthrough(process.argv, 'gemini'));
366
382
  case 'listener':
367
383
  return (0, listener_js_1.handleListener)(args);
368
384
  default:
package/dist/config.d.ts CHANGED
@@ -8,6 +8,9 @@ export interface ZephConfig {
8
8
  deviceId?: string;
9
9
  }
10
10
  export declare const resolvedEnv: (key: string) => string | undefined;
11
+ export declare const PROJECT_DIR_ENV_VARS: readonly ["CLAUDE_PROJECT_DIR", "CURSOR_PROJECT_DIR", "WINDSURF_PROJECT_DIR"];
12
+ /** First set project-dir env (unresolved `${VAR}` placeholders ignored), else cwd. */
13
+ export declare const detectProjectDir: () => string;
11
14
  export declare const loadConfig: () => ZephConfig;
12
15
  export declare const saveConfig: (config: ZephConfig) => void;
13
16
  export declare const VERSION: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,QAA2B,CAAC;AACnD,eAAO,MAAM,WAAW,QAAkC,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGlD,CAAC;AAEF,eAAO,MAAM,UAAU,QAAO,UAM7B,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,QAAQ,UAAU,KAAG,IAG/C,CAAC;AAEF,eAAO,MAAM,OAAO,QAOhB,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,QAA2B,CAAC;AACnD,eAAO,MAAM,WAAW,QAAkC,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGlD,CAAC;AAMF,eAAO,MAAM,oBAAoB,+EAAgF,CAAC;AAElH,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,QAAO,MAMnC,CAAC;AAEF,eAAO,MAAM,UAAU,QAAO,UAM7B,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,QAAQ,UAAU,KAAG,IAG/C,CAAC;AAEF,eAAO,MAAM,OAAO,QAOhB,CAAC"}
package/dist/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VERSION = exports.saveConfig = exports.loadConfig = exports.resolvedEnv = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
3
+ exports.VERSION = exports.saveConfig = exports.loadConfig = exports.detectProjectDir = exports.PROJECT_DIR_ENV_VARS = exports.resolvedEnv = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const os_1 = require("os");
6
6
  const path_1 = require("path");
@@ -11,6 +11,21 @@ const resolvedEnv = (key) => {
11
11
  return val && !val.startsWith('${') ? val : undefined;
12
12
  };
13
13
  exports.resolvedEnv = resolvedEnv;
14
+ // Per-agent project-dir env vars, in precedence order. Deliberately NOT part
15
+ // of the remote-agent registry: Cursor/Windsurf carry project-dir envs but
16
+ // are not remote-controllable via tmux — the two tables have different
17
+ // membership.
18
+ exports.PROJECT_DIR_ENV_VARS = ['CLAUDE_PROJECT_DIR', 'CURSOR_PROJECT_DIR', 'WINDSURF_PROJECT_DIR'];
19
+ /** First set project-dir env (unresolved `${VAR}` placeholders ignored), else cwd. */
20
+ const detectProjectDir = () => {
21
+ for (const key of exports.PROJECT_DIR_ENV_VARS) {
22
+ const val = (0, exports.resolvedEnv)(key);
23
+ if (val)
24
+ return val;
25
+ }
26
+ return process.cwd();
27
+ };
28
+ exports.detectProjectDir = detectProjectDir;
14
29
  const loadConfig = () => {
15
30
  try {
16
31
  return JSON.parse((0, fs_1.readFileSync)(exports.CONFIG_FILE, 'utf-8'));
package/dist/gate.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ export type GateMarker = 'skip' | 'push' | 'high' | 'none';
2
+ export type GatePushMode = 'quiet' | 'loud' | 'normal';
3
+ export interface GateInput {
4
+ /** Total tool_use blocks this turn. */
5
+ toolCount: number;
6
+ /** Tools that are NOT read-only (Read/Grep/Glob). */
7
+ nonReadonlyCount: number;
8
+ /** A zeph_ask/zeph_prompt already notified this turn. */
9
+ alreadyAsked: boolean;
10
+ marker: GateMarker;
11
+ pushMode: GatePushMode;
12
+ }
13
+ export interface GateVerdict {
14
+ push: boolean;
15
+ priority: 'high' | 'normal';
16
+ }
17
+ /**
18
+ * "Assume real work" defaults for hooks that can't supply turn facts
19
+ * (most non-Claude agents pass no counts): in normal mode the push still
20
+ * fires — preserving the historical always-push behavior of the dumb
21
+ * hooks — while quiet/loud now work everywhere.
22
+ */
23
+ export declare const GATE_DEFAULTS: {
24
+ readonly toolCount: 2;
25
+ readonly nonReadonlyCount: 1;
26
+ readonly alreadyAsked: false;
27
+ };
28
+ export declare const normalizeMarker: (raw: string | undefined) => GateMarker;
29
+ export declare const normalizePushMode: (raw: string | undefined) => GatePushMode;
30
+ export declare const decidePush: (input: GateInput) => GateVerdict;
31
+ export declare const projectHash: (dir: string) => string | null;
32
+ /** True when the user ran /zeph-mute for this project. */
33
+ export declare const isMuted: (dir: string) => boolean;
34
+ /** The user's session push-mode dial (/zeph-quiet | /zeph-loud), default normal. */
35
+ export declare const readPushMode: (dir: string) => GatePushMode;
36
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAsBA,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;;;;;GAKG;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;AAQF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAOlD,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,OAGrC,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,YAQ1C,CAAC"}
package/dist/gate.js ADDED
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readPushMode = exports.isMuted = exports.projectHash = exports.decidePush = exports.normalizePushMode = exports.normalizeMarker = exports.GATE_DEFAULTS = void 0;
4
+ /**
5
+ * Push-gate decision — the portable half of the Zeph Stop-hook logic.
6
+ *
7
+ * `decidePush` is the TS twin of `plugin/hooks/gate.sh` (zeph-to/plugin).
8
+ * Both implementations are locked to the same semantics by the shared
9
+ * vector file `src/fixtures/gate-vectors.json` (vendored from the plugin
10
+ * repo's canonical copy via `npm run sync:plugin`): the bash side and this
11
+ * side run the exact same cases in their CIs, so a semantic change to one
12
+ * that isn't mirrored in the other fails a build. Edit them together.
13
+ *
14
+ * Ordering is contractual (encoded as named vectors):
15
+ * 1. alreadyAsked wins over EVERYTHING — even loud (dedup beats the dial).
16
+ * 2. priority is high iff marker === 'high', decided BEFORE the mode
17
+ * switch, so quiet+high and loud+high both push at high priority.
18
+ * 3. quiet → only a high marker pushes; loud → always push; normal →
19
+ * marker overrides the heuristic (skip → silent, push/high → push),
20
+ * no marker → push iff toolCount ≥ 2 AND nonReadonlyCount > 0
21
+ * (the B1 read-only floor).
22
+ */
23
+ const child_process_1 = require("child_process");
24
+ const fs_1 = require("fs");
25
+ /**
26
+ * "Assume real work" defaults for hooks that can't supply turn facts
27
+ * (most non-Claude agents pass no counts): in normal mode the push still
28
+ * fires — preserving the historical always-push behavior of the dumb
29
+ * hooks — while quiet/loud now work everywhere.
30
+ */
31
+ exports.GATE_DEFAULTS = {
32
+ toolCount: 2,
33
+ nonReadonlyCount: 1,
34
+ alreadyAsked: false,
35
+ };
36
+ const normalizeMarker = (raw) => raw === 'skip' || raw === 'push' || raw === 'high' ? raw : 'none';
37
+ exports.normalizeMarker = normalizeMarker;
38
+ const normalizePushMode = (raw) => raw === 'quiet' || raw === 'loud' ? raw : 'normal';
39
+ exports.normalizePushMode = normalizePushMode;
40
+ const decidePush = (input) => {
41
+ if (input.alreadyAsked)
42
+ return { push: false, priority: 'normal' };
43
+ const priority = input.marker === 'high' ? 'high' : 'normal';
44
+ if (input.pushMode === 'quiet')
45
+ return { push: input.marker === 'high', priority };
46
+ if (input.pushMode === 'loud')
47
+ return { push: true, priority };
48
+ if (input.marker === 'skip')
49
+ return { push: false, priority };
50
+ if (input.marker === 'push' || input.marker === 'high')
51
+ return { push: true, priority };
52
+ return { push: input.toolCount >= 2 && input.nonReadonlyCount > 0, priority };
53
+ };
54
+ exports.decidePush = decidePush;
55
+ // ── Per-project gate state (mute + push-mode dial) ───────────────
56
+ //
57
+ // The plugin's bash hooks key these tmp files off `cksum` of the project
58
+ // dir; shelling out to the same `cksum` here (instead of a pure-TS CRC)
59
+ // guarantees hash parity with every already-written file.
60
+ const projectHash = (dir) => {
61
+ try {
62
+ const raw = (0, child_process_1.execFileSync)('cksum', { input: dir, encoding: 'utf-8' });
63
+ return raw.split(' ')[0] || null;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ };
69
+ exports.projectHash = projectHash;
70
+ /** True when the user ran /zeph-mute for this project. */
71
+ const isMuted = (dir) => {
72
+ const hash = (0, exports.projectHash)(dir);
73
+ return hash !== null && (0, fs_1.existsSync)(`/tmp/zeph-muted-${hash}`);
74
+ };
75
+ exports.isMuted = isMuted;
76
+ /** The user's session push-mode dial (/zeph-quiet | /zeph-loud), default normal. */
77
+ const readPushMode = (dir) => {
78
+ const hash = (0, exports.projectHash)(dir);
79
+ if (!hash)
80
+ return 'normal';
81
+ try {
82
+ return (0, exports.normalizePushMode)((0, fs_1.readFileSync)(`/tmp/zeph-pushmode-${hash}`, 'utf-8').replace(/\s+/g, ''));
83
+ }
84
+ catch {
85
+ return 'normal';
86
+ }
87
+ };
88
+ exports.readPushMode = readPushMode;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { ZephHook } from './zeph-hook.js';
2
2
  export { ZephError, AuthenticationError, QuotaExceededError } from './errors.js';
3
3
  export type { ZephOptions, NotifyPayload, NotifyResult, ListParams, ListResult, DismissOneResult, DismissAllResult, PushItem } from './types.js';
4
+ export { decidePush, GATE_DEFAULTS, normalizeMarker, normalizePushMode } from './gate.js';
5
+ export type { GateInput, GateVerdict, GateMarker, GatePushMode } from './gate.js';
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjJ,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC1F,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.QuotaExceededError = exports.AuthenticationError = exports.ZephError = exports.ZephHook = void 0;
3
+ exports.normalizePushMode = exports.normalizeMarker = exports.GATE_DEFAULTS = exports.decidePush = exports.QuotaExceededError = exports.AuthenticationError = exports.ZephError = exports.ZephHook = void 0;
4
4
  var zeph_hook_js_1 = require("./zeph-hook.js");
5
5
  Object.defineProperty(exports, "ZephHook", { enumerable: true, get: function () { return zeph_hook_js_1.ZephHook; } });
6
6
  var errors_js_1 = require("./errors.js");
7
7
  Object.defineProperty(exports, "ZephError", { enumerable: true, get: function () { return errors_js_1.ZephError; } });
8
8
  Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_js_1.AuthenticationError; } });
9
9
  Object.defineProperty(exports, "QuotaExceededError", { enumerable: true, get: function () { return errors_js_1.QuotaExceededError; } });
10
+ var gate_js_1 = require("./gate.js");
11
+ Object.defineProperty(exports, "decidePush", { enumerable: true, get: function () { return gate_js_1.decidePush; } });
12
+ Object.defineProperty(exports, "GATE_DEFAULTS", { enumerable: true, get: function () { return gate_js_1.GATE_DEFAULTS; } });
13
+ Object.defineProperty(exports, "normalizeMarker", { enumerable: true, get: function () { return gate_js_1.normalizeMarker; } });
14
+ Object.defineProperty(exports, "normalizePushMode", { enumerable: true, get: function () { return gate_js_1.normalizePushMode; } });
@@ -20,7 +20,8 @@
20
20
  * messages as new pushes are created. Reconnects with exponential
21
21
  * backoff on transient failures; gives up on auth failures (4001/4002/4003).
22
22
  */
23
- type AgentKind = 'claude' | 'codex' | 'gemini';
23
+ import { type AgentKind, type RegisteredRemoteAgent } from './remote-agents.js';
24
+ import { type AgentState } from './agent-state.js';
24
25
  interface AgentSession {
25
26
  name: string;
26
27
  attached: boolean;
@@ -30,6 +31,15 @@ interface AgentSession {
30
31
  label?: string | null;
31
32
  createdAt?: string;
32
33
  lastActivityAt?: string;
34
+ /**
35
+ * Detected agent state (wire contract — flows to the server and the
36
+ * phone's Agents tab). Absent when the pane can't be captured or
37
+ * detection is unavailable. `done` never appears here: it's derived
38
+ * client-side (SPEC-AGENT-AWARENESS §S1/§S6).
39
+ */
40
+ state?: AgentState;
41
+ stateChangedAt?: string;
42
+ stateRuleId?: string;
33
43
  }
34
44
  export declare const AUTH_FAILURE_CODES: ReadonlySet<number>;
35
45
  export declare const checkRateLimit: (session: string, now?: number) => boolean;
@@ -55,15 +65,78 @@ export declare const parseSessionName: (name: string) => {
55
65
  project: string;
56
66
  label: string | null;
57
67
  } | null;
68
+ interface PaneInfo {
69
+ currentCommand: string | null;
70
+ startCommand: string | null;
71
+ currentPath: string | null;
72
+ }
73
+ /**
74
+ * Identify the agent from the tmux pane. Prefer `pane_start_command`
75
+ * because the foreground process is usually `node`/`python3` (the
76
+ * interpreter), which doesn't tell us *what* was launched. Fall back to
77
+ * `pane_current_command` when start_command is empty — tmux clears
78
+ * start_command in some re-attach cases, especially when a pre-existing
79
+ * session was joined via `tmux new -A` instead of being created fresh.
80
+ * That fallback is safe because only the literal binaries registered in
81
+ * remote-agents.ts are accepted as a match.
82
+ */
83
+ export declare const detectRemoteAgent: (info: PaneInfo) => RegisteredRemoteAgent | null;
84
+ /** Test hook. */
85
+ export declare const resetSessionStates: () => void;
58
86
  /**
59
- * Locate the most recent Claude Code session UUID for the working
60
- * directory of a tmux pane. Mirrors `mcp-server/config.ts`'s
61
- * detectClaudeSessionId: CC writes per-session jsonl files at
62
- * `~/.claude/projects/<projectHash>/<UUID>.jsonl` where the hash is
63
- * the cwd with `/` replaced by `-`. Cached for 60s — see
64
- * claudeSessionCache.
87
+ * Fold one pane capture into the per-session state machine and return
88
+ * the wire fields for this report cycle. Exported (with `paneText`
89
+ * injected) so tests can drive it without tmux.
90
+ *
91
+ * Hash short-circuit: an unchanged screen skips rule evaluation but
92
+ * still replays the previous observation through the debounce — cheap
93
+ * cycles stay cheap without wedging candidate promotion.
94
+ */
95
+ export declare const deriveSessionState: (name: string, agentKind: AgentKind, paneText: string | null, now?: number) => Pick<AgentSession, "state" | "stateChangedAt" | "stateRuleId">;
96
+ export interface PatternWatch {
97
+ sessionName: string;
98
+ pattern: string;
99
+ }
100
+ /** Ack payload → active watch list. Prunes fired-markers for dead watches. */
101
+ export declare const setPatternWatches: (raw: unknown) => void;
102
+ /** Test hook. */
103
+ export declare const resetPatternWatches: () => void;
104
+ export interface WatchHit {
105
+ sessionName: string;
106
+ pattern: string;
107
+ matchedLine: string;
108
+ }
109
+ /**
110
+ * Probe every un-fired watch whose session is live. `capture` is
111
+ * injectable for tests; production passes capturePaneText.
112
+ */
113
+ export declare const collectWatchHits: (liveNames: ReadonlySet<string>, capture?: (session: string) => string | null) => WatchHit[];
114
+ export interface ScreenRequest {
115
+ subtype?: string;
116
+ targetDeviceId?: string;
117
+ sessionName?: string;
118
+ requestId?: string;
119
+ }
120
+ export interface ScreenSnapshot {
121
+ subtype: 'agent.screen.snapshot';
122
+ requestId: string;
123
+ sessionName: string;
124
+ content?: string;
125
+ truncated?: boolean;
126
+ error?: string;
127
+ capturedAt: string;
128
+ }
129
+ /**
130
+ * Answer a phone's screen-peek request for one of OUR sessions.
131
+ * Returns null when the message isn't addressed to this machine (other
132
+ * ephemeral traffic — clipboard, mirrors — flows through constantly).
133
+ *
134
+ * Security posture: only sessions the inventory already exposes are
135
+ * readable — the phone can see exactly the panes it can already send
136
+ * commands into, nothing else. Rate-limited with the same per-session
137
+ * token bucket as command injection.
65
138
  */
66
- export declare const detectClaudeSessionId: (cwd: string) => string | null;
139
+ export declare const handleScreenRequest: (req: ScreenRequest) => ScreenSnapshot | null;
67
140
  export interface CollectResult {
68
141
  sessions: AgentSession[];
69
142
  /** Diagnostic notes per rejected session — surfaced under `--verbose`. */
@@ -81,11 +154,12 @@ export interface CollectResult {
81
154
  export declare const collectSessionsVerbose: () => CollectResult;
82
155
  /**
83
156
  * Snapshot the live `zeph-*` tmux sessions on this machine, enriched
84
- * with the running agent kind, CC session UUID (claude only), project,
85
- * and tmux activity timestamps. Returns [] when tmux is unreachable
86
- * or no agent sessions exist. Sessions whose pane is at a shell or
87
- * running something other than claude/codex/gemini are filtered out
88
- * the phone can't usefully address them.
157
+ * with the running agent kind, the agent's own session id (when the
158
+ * registry has a resolver currently Claude Code only), project, and
159
+ * tmux activity timestamps. Returns [] when tmux is unreachable or no
160
+ * agent sessions exist. Sessions whose pane is at a shell or running
161
+ * something not registered in remote-agents.ts are filtered out — the
162
+ * phone can't usefully address them.
89
163
  */
90
164
  export declare const collectSessions: () => AgentSession[];
91
165
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}