@zeph-to/cli 2.17.1 → 2.19.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 CHANGED
@@ -146,6 +146,14 @@ an answerable `zeph_ask`).
146
146
  | Pi | `before_agent_start` → `zeph remote-hook pi` (via extension) | `zeph setup` |
147
147
  | Cursor CLI | — none yet | — |
148
148
 
149
+ **Subagents (view-only).** A pi subagent spawned into an extra pane of the
150
+ same tmux window shows up on your phone as a child of its parent card, named
151
+ `<session>.<pane>` — its terminal mirror streams live, but input, keys, exit,
152
+ resume and forget are all refused: you watch from the phone and answer at the
153
+ terminal, where the parent owns the conversation. The pane is labelled from
154
+ `@zeph_pane_label` (the pi extension sets it from `PI_SUBAGENT_NAME`), and a
155
+ prompt push from a subagent names it and opens its stream directly.
156
+
149
157
  Detection is exact-match: a terminal keystroke racing a phone message
150
158
  can never false-flag. Muted projects are never flagged.
151
159
 
@@ -29,6 +29,11 @@ interface AgentSession {
29
29
  name: string;
30
30
  attached: boolean;
31
31
  agentKind: AgentKind;
32
+ /** Set when this session is a SUBAGENT — an extra pane of the parent
33
+ * session `parentName` (`zeph-zeph.48` belongs to `zeph-zeph`). Subagent
34
+ * sessions are view-only: input, keys, exit, resume and forget are
35
+ * refused, and the server never pushes agent.state transitions for them. */
36
+ parentName?: string;
32
37
  agentSessionId?: string | null;
33
38
  project: string;
34
39
  label?: string | null;
@@ -105,7 +110,15 @@ export declare const sessionsFingerprint: (sessions: AgentSession[]) => string;
105
110
  export declare const sessionsReportDue: (fingerprint: string, lastSentFingerprint: string | null, lastSentAtMs: number, nowMs: number) => boolean;
106
111
  export declare const AUTH_FAILURE_CODES: ReadonlySet<number>;
107
112
  export declare const checkRateLimit: (session: string, now?: number, cost?: number) => boolean;
108
- /** Read the foreground command in the named tmux session's active pane. */
113
+ /**
114
+ * Read the foreground command on the named session's PINNED pane, verifying
115
+ * the pane still belongs to that session. A pane id is stable across restarts
116
+ * only within one tmux server: after a restart the same `%N` can name a pane
117
+ * in a different session, and typing into that pane would be RCE into someone
118
+ * else's shell. The owning session rides along in the same display-message —
119
+ * one spawn, no second probe — and a mismatch answers null ("no such
120
+ * session"), which refuses the inject until the next sweep re-pins.
121
+ */
109
122
  export declare const paneCurrentCommand: (session: string) => string | null;
110
123
  /**
111
124
  * Translate a phone-supplied key list to tmux tokens. Returns null if ANY
@@ -140,6 +153,27 @@ interface PaneInfo {
140
153
  currentPath: string | null;
141
154
  panePid: number | null;
142
155
  }
156
+ /**
157
+ * A subagent session name: `<session>.<pane id digits>` (`zeph-zeph.48`).
158
+ * tmux forbids `.` in session names, so the shape cannot collide; `#` would
159
+ * break the server's stream-sub keys and `:` is the web key separator.
160
+ */
161
+ export declare const isSubagentSessionName: (name: string) => boolean;
162
+ /** Store the latest sweep's name → pane-id map (see `CollectResult.targets`). */
163
+ export declare const recordTargets: (targets: Record<string, string> | null | undefined) => void;
164
+ /**
165
+ * The tmux target for a wire session name: its pinned pane id when the last
166
+ * sweep saw the session, else the name itself (a session younger than one
167
+ * sweep, or tmux unreachable at startup — today's resolution, active pane).
168
+ *
169
+ * A SUBAGENT name is the one exception: it resolves ONLY through the map, and
170
+ * an unknown one is null — because tmux parses `zeph-zeph.48` as session
171
+ * `zeph-zeph`, pane INDEX 48, and silently falls back to the active pane when
172
+ * that index is missing (measured: `display -t zeph-zeph.99` → main pane,
173
+ * exit 0). Every caller treats null as "gone": no spawn, refusal, or capture
174
+ * failure — never a write to whichever pane happens to be focused.
175
+ */
176
+ export declare const tmuxTargetFor: (name: string) => string | null;
143
177
  /**
144
178
  * Identify the agent from the tmux pane. Prefer `pane_start_command`
145
179
  * because the foreground process is usually `node`/`python3` (the
@@ -690,6 +724,10 @@ export interface CollectResult {
690
724
  name: string;
691
725
  reason: string;
692
726
  }>;
727
+ /** Local knowledge, NOT on the wire: every live session/subagent name →
728
+ * the pane id every `-t` call for it must use (`tmuxTargetFor`). The main
729
+ * thread stores it beside the snapshot (`recordTargets`). */
730
+ targets: Record<string, string>;
693
731
  }
694
732
  /**
695
733
  * Inventory pass that also records *why* each `zeph-*` session was
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA6BH,OAAO,EAA8D,KAAK,SAAS,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAU5I,OAAO,EAAiD,KAAK,UAAU,EAA4C,MAAM,kBAAkB,CAAC;AAG5I,OAAO,EAA4E,KAAK,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAGvI,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAoCtG,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,UAAU,YAAY;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB;;;iFAG6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,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;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,IAY7D,CAAC;AAuGF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,KAAG,MAAM,EAAE,GAAG,IAQvD,CAAC;AA4CF;;;;;;;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;AAsBD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,MAAM,KAAG,OAAsC,CAAC;AAE5F,iFAAiF;AACjF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,IAElF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,IAIrD,CAAC;AA0CF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,QAAQ,KAAG,qBAAqB,GAAG,IAGhE,CAAC;AA0BZ,iBAAiB;AACjB,eAAO,MAAM,kBAAkB,QAAO,IAErC,CAAC;AAeF;;;;;;;;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,OAwDF,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,OA8DF,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;;;;;OAKG;IACH,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,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,OAyCF,CAAC;AAyBF;+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;AA8CD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACnC,KAAK,oBAAoB,EACzB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA8DF,CAAC;AA0EF,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;AA6IxC,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;IAClD;;kEAE8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAAO,aAkLzC,CAAC;AAwBF,eAAO,MAAM,eAAe,GAAI,UAAU,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,KAAG,IAElF,CAAC;AAsCF,eAAO,MAAM,kBAAkB,GAAI,UAAU,aAAa,CAAC,YAAY,CAAC,KAAG,IAyB1E,CAAC;AAEF;;;;;;;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;AAoCD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC1B,SAAS,MAAM,EACf,MAAM,MAAM,EACZ,MAAK,MAAM,MAAiB,KAC7B,OAUF,CAAC;AAyEF,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;AA0SF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA+H3C,eAAO,MAAM,cAAc,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAoK3F,CAAC"}
package/dist/listener.js CHANGED
@@ -25,8 +25,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
25
25
  return (mod && mod.__esModule) ? mod : { "default": mod };
26
26
  };
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
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.syncTmuxAgentNames = exports.recordInventory = exports.collectSessionsVerbose = exports.handleCommandInput = exports.pendingInputDecrypts = exports.MAX_PENDING_DECRYPTS = exports.MAX_INPUT_LANES = exports.validateInputMessage = void 0;
28
+ 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.tmuxTargetFor = exports.recordTargets = exports.isSubagentSessionName = 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.syncTmuxAgentNames = exports.recordInventory = exports.collectSessionsVerbose = exports.handleCommandInput = exports.pendingInputDecrypts = exports.MAX_PENDING_DECRYPTS = exports.MAX_INPUT_LANES = exports.validateInputMessage = exports.MAX_INPUT_BODY_CHARS = exports.MAX_INPUT_KEYS = exports.handleStreamControl = 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");
@@ -185,15 +185,30 @@ const checkRateLimit = (session, now = Date.now(), cost = 1) => {
185
185
  return true;
186
186
  };
187
187
  exports.checkRateLimit = checkRateLimit;
188
- /** Read the foreground command in the named tmux session's active pane. */
188
+ /**
189
+ * Read the foreground command on the named session's PINNED pane, verifying
190
+ * the pane still belongs to that session. A pane id is stable across restarts
191
+ * only within one tmux server: after a restart the same `%N` can name a pane
192
+ * in a different session, and typing into that pane would be RCE into someone
193
+ * else's shell. The owning session rides along in the same display-message —
194
+ * one spawn, no second probe — and a mismatch answers null ("no such
195
+ * session"), which refuses the inject until the next sweep re-pins.
196
+ */
189
197
  const paneCurrentCommand = (session) => {
190
- const result = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['display-message', '-p', '-t', session, '#{pane_current_command}']), {
198
+ const target = (0, exports.tmuxTargetFor)(session);
199
+ if (target === null)
200
+ return null; // unresolvable subagent — never spawn
201
+ const result = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['display-message', '-p', '-t', target,
202
+ `#{pane_current_command}${FIELD_SEP}#{session_name}`]), {
191
203
  encoding: 'utf-8',
192
204
  stdio: ['ignore', 'pipe', 'ignore'],
193
205
  });
194
206
  if (result.status !== 0)
195
207
  return null;
196
- return (result.stdout ?? '').trim() || null;
208
+ const parts = (result.stdout ?? '').trim().split(FIELD_SEP);
209
+ if (parts.length !== 2 || parts[1] !== session)
210
+ return null;
211
+ return parts[0] || null;
197
212
  };
198
213
  exports.paneCurrentCommand = paneCurrentCommand;
199
214
  const isShellPane = (command) => {
@@ -205,8 +220,13 @@ const isShellPane = (command) => {
205
220
  * so the user's own paste stack is never pushed onto, and `-d` drops it
206
221
  * again the moment it has been delivered. */
207
222
  const INJECT_BUFFER = 'zeph-inject';
208
- const sendLiteral = (session, text) => (0, child_process_1.spawnSync)('tmux', tmuxArgs(['send-keys', '-l', '-t', session, text]), { stdio: ['ignore', 'ignore', 'pipe'] })
209
- .status === 0;
223
+ const sendLiteral = (session, text) => {
224
+ const target = (0, exports.tmuxTargetFor)(session);
225
+ if (target === null)
226
+ return false; // unresolvable subagent — never spawn
227
+ return (0, child_process_1.spawnSync)('tmux', tmuxArgs(['send-keys', '-l', '-t', target, text]), { stdio: ['ignore', 'ignore', 'pipe'] })
228
+ .status === 0;
229
+ };
210
230
  /**
211
231
  * Put the message in the pane as a PASTE rather than as typing.
212
232
  *
@@ -233,7 +253,10 @@ const pasteText = (session, text) => {
233
253
  const set = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['set-buffer', '-b', INJECT_BUFFER, '--', text]), { stdio: ['ignore', 'ignore', 'pipe'] });
234
254
  if (set.status !== 0)
235
255
  return sendLiteral(session, text);
236
- const paste = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['paste-buffer', '-d', '-p', '-b', INJECT_BUFFER, '-t', session]), { stdio: ['ignore', 'ignore', 'pipe'] });
256
+ const pasteTarget = (0, exports.tmuxTargetFor)(session);
257
+ if (pasteTarget === null)
258
+ return false; // unresolvable subagent — never spawn
259
+ const paste = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['paste-buffer', '-d', '-p', '-b', INJECT_BUFFER, '-t', pasteTarget]), { stdio: ['ignore', 'ignore', 'pipe'] });
237
260
  if (paste.status === 0)
238
261
  return true;
239
262
  // `-d` never ran, so the buffer is still there holding the message.
@@ -252,7 +275,10 @@ const pasteText = (session, text) => {
252
275
  const injectKeys = (session, text) => {
253
276
  if (!pasteText(session, text))
254
277
  return false;
255
- const b = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['send-keys', '-t', session, 'Enter']), { stdio: ['ignore', 'ignore', 'pipe'] });
278
+ const enterTarget = (0, exports.tmuxTargetFor)(session);
279
+ if (enterTarget === null)
280
+ return false; // unresolvable subagent — never spawn
281
+ const b = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['send-keys', '-t', enterTarget, 'Enter']), { stdio: ['ignore', 'ignore', 'pipe'] });
256
282
  return b.status === 0;
257
283
  };
258
284
  // Named keys the phone may inject to drive a full-screen Claude Code pane
@@ -310,7 +336,10 @@ exports.resolveKeys = resolveKeys;
310
336
  * whole sequence in order.
311
337
  */
312
338
  const injectNamedKeys = (session, tokens) => {
313
- const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['send-keys', '-t', session, ...tokens]), { stdio: ['ignore', 'ignore', 'pipe'] });
339
+ const target = (0, exports.tmuxTargetFor)(session);
340
+ if (target === null)
341
+ return false; // unresolvable subagent — never spawn
342
+ const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['send-keys', '-t', target, ...tokens]), { stdio: ['ignore', 'ignore', 'pipe'] });
314
343
  return r.status === 0;
315
344
  };
316
345
  const stamp = () => new Date().toISOString().slice(11, 19);
@@ -603,8 +632,51 @@ exports.parseSessionName = parseSessionName;
603
632
  // arrived at the consumer end. A printable Unicode char passes through
604
633
  // verbatim and won't appear in any real session name or filesystem path.
605
634
  const FIELD_SEP = '␟';
635
+ // ─── Pane targets ───────────────────────────────────────────────────
636
+ // A bare `-t <session>` resolves to whichever pane has FOCUS, so the moment a
637
+ // second pane exists in a `zeph-*` session (pi-interactive-subagents splits
638
+ // one off for every subagent), capture and input silently follow the user's
639
+ // focus instead of the agent. The inventory sweep pins each session to its
640
+ // main pane id, and every `-t` call site goes through `tmuxTargetFor` so the
641
+ // pinning cannot be bypassed by one call site forgetting it.
642
+ /** Session name → pinned pane id, replaced wholesale by each inventory sweep. */
643
+ let targetsBySession = new Map();
644
+ /**
645
+ * A subagent session name: `<session>.<pane id digits>` (`zeph-zeph.48`).
646
+ * tmux forbids `.` in session names, so the shape cannot collide; `#` would
647
+ * break the server's stream-sub keys and `:` is the web key separator.
648
+ */
649
+ const isSubagentSessionName = (name) => /^zeph-.+\.\d+$/.test(name);
650
+ exports.isSubagentSessionName = isSubagentSessionName;
651
+ /** Store the latest sweep's name → pane-id map (see `CollectResult.targets`). */
652
+ const recordTargets = (targets) => {
653
+ targetsBySession = new Map(Object.entries(targets ?? {}));
654
+ };
655
+ exports.recordTargets = recordTargets;
656
+ /**
657
+ * The tmux target for a wire session name: its pinned pane id when the last
658
+ * sweep saw the session, else the name itself (a session younger than one
659
+ * sweep, or tmux unreachable at startup — today's resolution, active pane).
660
+ *
661
+ * A SUBAGENT name is the one exception: it resolves ONLY through the map, and
662
+ * an unknown one is null — because tmux parses `zeph-zeph.48` as session
663
+ * `zeph-zeph`, pane INDEX 48, and silently falls back to the active pane when
664
+ * that index is missing (measured: `display -t zeph-zeph.99` → main pane,
665
+ * exit 0). Every caller treats null as "gone": no spawn, refusal, or capture
666
+ * failure — never a write to whichever pane happens to be focused.
667
+ */
668
+ const tmuxTargetFor = (name) => {
669
+ const hit = targetsBySession.get(name);
670
+ if (hit !== undefined)
671
+ return hit;
672
+ return (0, exports.isSubagentSessionName)(name) ? null : name;
673
+ };
674
+ exports.tmuxTargetFor = tmuxTargetFor;
606
675
  const readPaneInfo = (session) => {
607
- const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['display-message', '-p', '-t', session,
676
+ const target = (0, exports.tmuxTargetFor)(session);
677
+ if (target === null)
678
+ return { currentCommand: null, startCommand: null, currentPath: null, panePid: null };
679
+ const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['display-message', '-p', '-t', target,
608
680
  `#{pane_current_command}${FIELD_SEP}#{pane_start_command}${FIELD_SEP}#{pane_current_path}${FIELD_SEP}#{pane_pid}`]), {
609
681
  encoding: 'utf-8',
610
682
  stdio: ['ignore', 'pipe', 'ignore'],
@@ -674,8 +746,13 @@ const resetSessionStates = () => {
674
746
  sessionStates.clear();
675
747
  };
676
748
  exports.resetSessionStates = resetSessionStates;
677
- const capturePaneText = (session) => {
678
- const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['capture-pane', '-p', '-t', session, '-S', `-${STATE_CAPTURE_LINES}`]), {
749
+ const capturePaneText = (session, target) => {
750
+ // Explicit target for the sweep (subagent rows aren't in the recorded
751
+ // target map yet); otherwise resolve through it.
752
+ const resolved = target ?? (0, exports.tmuxTargetFor)(session);
753
+ if (resolved === null)
754
+ return null; // unresolvable subagent — never spawn
755
+ const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['capture-pane', '-p', '-t', resolved, '-S', `-${STATE_CAPTURE_LINES}`]), {
679
756
  encoding: 'utf-8',
680
757
  stdio: ['ignore', 'pipe', 'pipe'],
681
758
  });
@@ -1063,6 +1140,10 @@ const handleSessionResumeRequest = (req, send) => {
1063
1140
  send({ ...base, ...fields });
1064
1141
  return true;
1065
1142
  };
1143
+ // A subagent is a pane OF a live session, not a session to start — and
1144
+ // before the rate bucket, so refused subagent traffic costs nothing.
1145
+ if ((0, exports.isSubagentSessionName)(sessionName))
1146
+ return reply({ error: 'subagent_view_only' });
1066
1147
  // Charged like a submitted command, not like a keystroke: this starts a
1067
1148
  // process, and the two share one budget so a flood cannot be laundered
1068
1149
  // through whichever path is cheaper.
@@ -1154,6 +1235,11 @@ const handleSessionExitRequest = (req, send) => {
1154
1235
  // Same budget as resume and as a submitted command: ending a session is a
1155
1236
  // session-lifecycle action, and pricing it like a keystroke would let a
1156
1237
  // flood through the cheaper door.
1238
+ // Subagent refusal first, though — outside the bucket, like resume's:
1239
+ // ending a subagent pane is a kill(2) on the parent's child, not a
1240
+ // lifecycle action the phone is ever offered.
1241
+ if ((0, exports.isSubagentSessionName)(sessionName))
1242
+ return answer({ error: 'subagent_view_only' });
1157
1243
  if (!(0, exports.checkRateLimit)(sessionName, undefined, SUBMIT_COST))
1158
1244
  return answer({ error: 'rate_limited' });
1159
1245
  if (!sessionExists(sessionName))
@@ -1228,6 +1314,11 @@ const handleSessionForgetRequest = (req, send) => {
1228
1314
  };
1229
1315
  // Charged like a submitted command: it writes to disk and is irreversible,
1230
1316
  // so it shares the budget with the paths that start and stop processes.
1317
+ // Subagent refusal before the bucket: a subagent has no registry record
1318
+ // to forget (the sweep never remembers panes), so the honest answer is
1319
+ // the view-only one, not unknown_session.
1320
+ if ((0, exports.isSubagentSessionName)(sessionName))
1321
+ return reply({ error: 'subagent_view_only' });
1231
1322
  if (!(0, exports.checkRateLimit)(sessionName, undefined, SUBMIT_COST))
1232
1323
  return reply({ error: 'rate_limited' });
1233
1324
  if (sessionExists(sessionName))
@@ -1248,10 +1339,20 @@ const handleSessionForgetRequest = (req, send) => {
1248
1339
  return reply({ forgotten: true });
1249
1340
  };
1250
1341
  exports.handleSessionForgetRequest = handleSessionForgetRequest;
1251
- /** Whether tmux already holds a session by this name. */
1252
- const sessionExists = (name) => (0, child_process_1.spawnSync)('tmux', tmuxArgs(['has-session', '-t', name]), {
1253
- stdio: ['ignore', 'ignore', 'ignore'],
1254
- }).status === 0;
1342
+ /**
1343
+ * Whether this machine currently holds a session by this name. A subagent
1344
+ * pane cannot be asked with has-session: tmux would parse `zeph-x.48` as
1345
+ * session `zeph-x`, pane index 48 and answer for the WRONG thing (or exit 1
1346
+ * for a missing index) — so subs are answered from the sweep's target map,
1347
+ * which is exactly what "a pane we can address without ambiguity" means.
1348
+ */
1349
+ const sessionExists = (name) => {
1350
+ if ((0, exports.isSubagentSessionName)(name))
1351
+ return targetsBySession.has(name);
1352
+ return (0, child_process_1.spawnSync)('tmux', tmuxArgs(['has-session', '-t', name]), {
1353
+ stdio: ['ignore', 'ignore', 'ignore'],
1354
+ }).status === 0;
1355
+ };
1255
1356
  // ─── Deep pull: scrollback above the live window ───────────────────
1256
1357
  //
1257
1358
  // The live stream captures a fixed window and every frame is capped at
@@ -1270,7 +1371,10 @@ const isPageOffset = (v) => typeof v === 'number' && Number.isInteger(v) && v >=
1270
1371
  /** How many lines of scrollback the pane is holding, or null when tmux won't
1271
1372
  * say — which is also how a dead pane answers. */
1272
1373
  const paneHistorySize = (sessionName) => {
1273
- const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['display-message', '-p', '-t', sessionName, '#{history_size}']), { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
1374
+ const target = (0, exports.tmuxTargetFor)(sessionName);
1375
+ if (target === null)
1376
+ return null;
1377
+ const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['display-message', '-p', '-t', target, '#{history_size}']), { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
1274
1378
  if (r.status !== 0)
1275
1379
  return null;
1276
1380
  const size = Number((r.stdout ?? '').trim());
@@ -1279,8 +1383,11 @@ const paneHistorySize = (sessionName) => {
1279
1383
  /** Capture one explicit range. tmux line 0 is the top of the visible pane and
1280
1384
  * negative numbers reach into the history, so both bounds are negative here. */
1281
1385
  const capturePaneRange = (sessionName, start, end) => {
1386
+ const target = (0, exports.tmuxTargetFor)(sessionName);
1387
+ if (target === null)
1388
+ return null;
1282
1389
  const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs([
1283
- 'capture-pane', '-p', '-e', '-t', sessionName,
1390
+ 'capture-pane', '-p', '-e', '-t', target,
1284
1391
  '-S', String(start), '-E', String(end),
1285
1392
  ]), { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
1286
1393
  if (r.status !== 0)
@@ -1392,9 +1499,12 @@ const capturePane = (sessionName, escapes = false, lines = SCREEN_PEEK_LINES) =>
1392
1499
  // `lines` sets how far back the capture reaches — the live stream grabs
1393
1500
  // more history so the mirror has room to scroll; SCREEN_PEEK_MAX_BYTES
1394
1501
  // still caps the payload below.
1502
+ const target = (0, exports.tmuxTargetFor)(sessionName);
1503
+ if (target === null)
1504
+ return null;
1395
1505
  const captureArgs = escapes
1396
- ? ['capture-pane', '-p', '-e', '-t', sessionName, '-S', `-${lines}`]
1397
- : ['capture-pane', '-p', '-t', sessionName, '-S', `-${lines}`];
1506
+ ? ['capture-pane', '-p', '-e', '-t', target, '-S', `-${lines}`]
1507
+ : ['capture-pane', '-p', '-t', target, '-S', `-${lines}`];
1398
1508
  const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs(captureArgs), {
1399
1509
  encoding: 'utf-8',
1400
1510
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -1419,8 +1529,11 @@ const capturePane = (sessionName, escapes = false, lines = SCREEN_PEEK_LINES) =>
1419
1529
  * spawn per SENT frame rather than per tick.
1420
1530
  */
1421
1531
  const capturePaneCursor = (sessionName) => {
1532
+ const target = (0, exports.tmuxTargetFor)(sessionName);
1533
+ if (target === null)
1534
+ return null;
1422
1535
  const r = (0, child_process_1.spawnSync)('tmux', tmuxArgs([
1423
- 'display-message', '-p', '-t', sessionName,
1536
+ 'display-message', '-p', '-t', target,
1424
1537
  '#{cursor_x},#{cursor_y},#{pane_height},#{history_size}',
1425
1538
  ]), { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
1426
1539
  if (r.status !== 0)
@@ -2430,20 +2543,25 @@ exports.handleCommandInput = handleCommandInput;
2430
2543
  * re-attach, and the current command is `node` rather than `claude`).
2431
2544
  */
2432
2545
  const collectSessionsVerbose = () => {
2433
- const list = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['list-sessions', '-F',
2434
- `#{session_name}${FIELD_SEP}#{session_attached}${FIELD_SEP}#{session_created}${FIELD_SEP}#{session_activity}`]), {
2546
+ // One sweep for the whole server: every pane of every session, so a
2547
+ // session's main pane is known regardless of which pane has focus, and
2548
+ // extra panes are visible to later slices. Per-pane spawns (the old
2549
+ // per-session display-message) would multiply with split panes; the worker
2550
+ // thread keeps the one blocking sweep off the event loop.
2551
+ const list = (0, child_process_1.spawnSync)('tmux', tmuxArgs(['list-panes', '-a', '-F',
2552
+ `#{session_name}${FIELD_SEP}#{session_attached}${FIELD_SEP}#{session_created}${FIELD_SEP}#{session_activity}${FIELD_SEP}#{window_index}${FIELD_SEP}#{pane_index}${FIELD_SEP}#{pane_id}${FIELD_SEP}#{pane_current_command}${FIELD_SEP}#{pane_start_command}${FIELD_SEP}#{pane_current_path}${FIELD_SEP}#{pane_pid}${FIELD_SEP}#{@zeph_pane_label}`]), {
2435
2553
  encoding: 'utf-8',
2436
2554
  stdio: ['ignore', 'pipe', 'pipe'],
2437
2555
  });
2438
2556
  if (list.status !== 0) {
2439
2557
  const stderr = (list.stderr ?? '').toString().trim();
2440
- log(` tmux list-sessions failed: status=${list.status}${stderr ? ', stderr=' + stderr : ''}`);
2558
+ log(` tmux list-panes failed: status=${list.status}${stderr ? ', stderr=' + stderr : ''}`);
2441
2559
  // Tmux call failed against the cached socket — the server it
2442
2560
  // pointed at is gone (died, restarted at a different path, etc).
2443
2561
  // Invalidate so the next cycle re-runs full discovery instead of
2444
2562
  // wedging the listener at "reported 0 session(s)" forever.
2445
2563
  (0, exports.invalidateTmuxSocketCache)();
2446
- return { sessions: [], rejected: [] };
2564
+ return { sessions: [], rejected: [], targets: {} };
2447
2565
  }
2448
2566
  const rawLines = (list.stdout ?? '').split('\n').filter(Boolean);
2449
2567
  // Sanity-check that the format separator actually survived. tmux is
@@ -2453,66 +2571,159 @@ const collectSessionsVerbose = () => {
2453
2571
  // it as "not zeph-*". Detect that explicitly so the user isn't left
2454
2572
  // guessing.
2455
2573
  if (rawLines.length > 0 && !rawLines[0].includes(FIELD_SEP)) {
2456
- log(` tmux output missing FIELD_SEP — likely encoding issue. Raw line: ${JSON.stringify(rawLines[0])}`);
2574
+ log(` tmux list-panes output missing FIELD_SEP — likely encoding issue. Raw line: ${JSON.stringify(rawLines[0])}`);
2457
2575
  }
2458
2576
  const sessions = [];
2459
2577
  const rejected = [];
2460
- // Pane cwd per session, read in the same sweep. Kept beside the wire shape
2461
- // rather than in it: AgentSession is a server/phone contract and the
2462
- // directory is local knowledge the registry needs, nothing the phone gets.
2463
- const paneCwdOf = new Map();
2578
+ /** 12 fields per row; a mismatch is the mangled-separator failure the
2579
+ * sanity log above exists for skip the row loudly, never misparse it. */
2580
+ const PANE_FIELDS = 12;
2581
+ const groups = new Map();
2582
+ let malformed = 0;
2464
2583
  for (const line of rawLines) {
2465
- const [name, attached, created, activity] = line.split(FIELD_SEP);
2466
- const parsed = (0, exports.parseSessionName)(name);
2467
- if (!parsed) {
2584
+ const f = line.split(FIELD_SEP);
2585
+ if (f.length !== PANE_FIELDS) {
2586
+ malformed += 1;
2587
+ continue;
2588
+ }
2589
+ const [name, attached, created, activity, win, idx, paneId, current, start, path, pid, label] = f;
2590
+ if (!(0, exports.parseSessionName)(name)) {
2468
2591
  // Not noisy enough to log every plain tmux session here —
2469
2592
  // would clutter the verbose output on machines with many
2470
2593
  // non-zeph sessions.
2471
2594
  continue;
2472
2595
  }
2473
- const info = readPaneInfo(name);
2474
- const agent = (0, exports.detectRemoteAgent)(info);
2475
- if (!agent) {
2596
+ const numeric = (v) => {
2597
+ const n = Number(v);
2598
+ return Number.isInteger(n) && n >= 0 ? n : Number.MAX_SAFE_INTEGER;
2599
+ };
2600
+ const row = {
2601
+ paneId,
2602
+ win: numeric(win),
2603
+ idx: numeric(idx),
2604
+ label: label ?? '',
2605
+ info: {
2606
+ currentCommand: current || null,
2607
+ startCommand: start || null,
2608
+ currentPath: path || null,
2609
+ panePid: numeric(pid) === Number.MAX_SAFE_INTEGER ? null : numeric(pid),
2610
+ },
2611
+ };
2612
+ const group = groups.get(name) ?? { attached: attached === '1', created, activity, panes: [] };
2613
+ group.panes.push(row);
2614
+ groups.set(name, group);
2615
+ }
2616
+ if (malformed > 0) {
2617
+ log(` tmux list-panes: skipped ${malformed} malformed row(s) — format separator may be mangled`);
2618
+ }
2619
+ // Pane cwd per session, read in the same sweep. Kept beside the wire shape
2620
+ // rather than in it: AgentSession is a server/phone contract and the
2621
+ // directory is local knowledge the registry needs, nothing the phone gets.
2622
+ const paneCwdOf = new Map();
2623
+ const targets = {};
2624
+ for (const [name, group] of groups) {
2625
+ const parsed = (0, exports.parseSessionName)(name);
2626
+ if (!parsed)
2627
+ continue; // unreachable — groups only holds parsed names
2628
+ // Main pane = the lowest (window, pane) index that runs an agent.
2629
+ // pi-interactive-subagents splits `-d` to the RIGHT of the parent, so
2630
+ // the parent keeps the lower index; a manual left split would take the
2631
+ // main role — accepted. Focus decides nothing here, which is the point:
2632
+ // focusing a subagent pane must not move the phone's view of the main.
2633
+ group.panes.sort((a, b) => (a.win - b.win) || (a.idx - b.idx));
2634
+ let main = null;
2635
+ for (const row of group.panes) {
2636
+ const agent = (0, exports.detectRemoteAgent)(row.info);
2637
+ if (agent) {
2638
+ main = { row, agent };
2639
+ break;
2640
+ }
2641
+ }
2642
+ if (!main) {
2643
+ const first = group.panes[0]?.info;
2476
2644
  rejected.push({
2477
2645
  name,
2478
- reason: `no agent in pane (start=${info.startCommand ?? 'null'}, current=${info.currentCommand ?? 'null'})`,
2646
+ reason: `no agent in pane (start=${first?.startCommand ?? 'null'}, current=${first?.currentCommand ?? 'null'})`,
2479
2647
  });
2480
2648
  continue;
2481
2649
  }
2650
+ const info = main.row.info;
2482
2651
  const agentSessionId = info.currentPath
2483
- ? (agent.resolveSessionId?.(info.currentPath, info.panePid ?? undefined) ?? null)
2652
+ ? (main.agent.resolveSessionId?.(info.currentPath, info.panePid ?? undefined) ?? null)
2484
2653
  : null;
2485
2654
  const providerSessionName = info.currentPath
2486
- ? (agent.resolveSessionName?.(info.currentPath, info.panePid ?? undefined) ?? null)
2655
+ ? (main.agent.resolveSessionName?.(info.currentPath, info.panePid ?? undefined) ?? null)
2487
2656
  : null;
2488
2657
  if (info.currentPath)
2489
2658
  paneCwdOf.set(name, info.currentPath);
2659
+ targets[name] = main.row.paneId;
2490
2660
  sessions.push({
2491
2661
  name,
2492
- attached: attached === '1',
2493
- agentKind: agent.kind,
2662
+ attached: group.attached,
2663
+ agentKind: main.agent.kind,
2494
2664
  agentSessionId,
2495
2665
  project: parsed.project,
2496
2666
  label: parsed.label,
2497
2667
  providerSessionName,
2498
- createdAt: epochToIso(created),
2499
- lastActivityAt: epochToIso(activity),
2500
- ...(0, exports.deriveSessionState)(name, agent.kind, capturePaneText(name)),
2668
+ createdAt: epochToIso(group.created),
2669
+ lastActivityAt: epochToIso(group.activity),
2670
+ ...(0, exports.deriveSessionState)(name, main.agent.kind, capturePaneText(name, main.row.paneId)),
2501
2671
  });
2672
+ // Subagent panes: every OTHER pane of this session whose tty's
2673
+ // foreground process group runs a registered agent (measured:
2674
+ // pane_current_command alone says `bash` for a pi subagent — the
2675
+ // fg group's `comm` is what names it). View-only wire entities
2676
+ // named `<session>.<pane id digits>`; ordered by pane id so the
2677
+ // phone's switcher is stable across sweeps (tmux pane ids are
2678
+ // monotonic server-wide).
2679
+ const paneNum = (p) => Number(p.paneId.slice(1)) || 0;
2680
+ const extras = group.panes
2681
+ .filter((p) => p !== main.row)
2682
+ .sort((a, b) => paneNum(a) - paneNum(b));
2683
+ const fallbackCounts = new Map();
2684
+ for (const extra of extras) {
2685
+ const subAgent = (0, remote_agents_js_1.foregroundAgentFor)(extra.info.panePid ?? 0);
2686
+ if (!subAgent)
2687
+ continue; // a shell / non-agent pane — not reportable
2688
+ const subName = `${name}.${paneNum(extra)}`;
2689
+ targets[subName] = extra.paneId;
2690
+ const n = (fallbackCounts.get(subAgent.kind) ?? 0) + 1;
2691
+ fallbackCounts.set(subAgent.kind, n);
2692
+ sessions.push({
2693
+ name: subName,
2694
+ parentName: name,
2695
+ attached: group.attached,
2696
+ agentKind: subAgent.kind,
2697
+ agentSessionId: null,
2698
+ project: parsed.project,
2699
+ // The subagent names itself by setting @zeph_pane_label
2700
+ // (PI_EXTENSION); a manual `tmux set-option -p` works too.
2701
+ // Fallback names per agent kind so two subagents of the
2702
+ // same kind stay distinguishable.
2703
+ label: extra.label.trim() || `${subAgent.displayName} ${n}`,
2704
+ createdAt: epochToIso(group.created),
2705
+ lastActivityAt: epochToIso(group.activity),
2706
+ ...(0, exports.deriveSessionState)(subName, subAgent.kind, capturePaneText(subName, extra.paneId)),
2707
+ });
2708
+ }
2502
2709
  }
2503
2710
  pruneSessionStates(new Set(sessions.map((s) => s.name)));
2504
2711
  // Write down what each live session IS, while it still exists to be read.
2505
2712
  // tmux forgets a session the moment it ends, which is exactly when the
2506
2713
  // phone wants it back — and a resume must take its directory and its binary
2507
2714
  // from what this machine observed, never from the wire.
2508
- (0, session_registry_js_1.rememberSessions)(sessions.map((s) => ({
2715
+ (0, session_registry_js_1.rememberSessions)(
2716
+ // Subagents are view-only panes OF a session, not resumable ones:
2717
+ // the registry records what a session IS so exit/resume can act on
2718
+ // it, and neither is ever offered for a subagent.
2719
+ sessions.filter((s) => !s.parentName).map((s) => ({
2509
2720
  name: s.name,
2510
2721
  cwd: paneCwdOf.get(s.name) ?? null,
2511
2722
  agentKind: s.agentKind,
2512
2723
  project: s.project,
2513
2724
  label: s.label,
2514
2725
  })));
2515
- return { sessions, rejected };
2726
+ return { sessions, rejected, targets };
2516
2727
  };
2517
2728
  exports.collectSessionsVerbose = collectSessionsVerbose;
2518
2729
  /**
@@ -2524,7 +2735,14 @@ exports.collectSessionsVerbose = collectSessionsVerbose;
2524
2735
  * something not registered in remote-agents.ts are filtered out — the
2525
2736
  * phone can't usefully address them.
2526
2737
  */
2527
- const collectSessions = () => (0, exports.collectSessionsVerbose)().sessions;
2738
+ const collectSessions = () => {
2739
+ const result = (0, exports.collectSessionsVerbose)();
2740
+ // The membership path also refreshes the pane targets: a session younger
2741
+ // than one report cycle gets its pane id pinned here rather than falling
2742
+ // back to the (focus-following) bare session name.
2743
+ (0, exports.recordTargets)(result.targets);
2744
+ return result.sessions;
2745
+ };
2528
2746
  /** Names from the most recent inventory sweep; null until the first one lands —
2529
2747
  * a membership check in that window (the connect burst) still sweeps in-thread. */
2530
2748
  let inventorySnapshot = null;
@@ -2571,6 +2789,12 @@ const publishedTmuxNames = new Map();
2571
2789
  const syncTmuxAgentNames = (sessions) => {
2572
2790
  const liveKeys = new Set();
2573
2791
  for (const s of sessions) {
2792
+ // Subagents are view-only panes of the parent; publishing a name
2793
+ // for them would run set-option against a `<session>.<pane>` name
2794
+ // that tmux parses as the PARENT session (pane index) — clobbering
2795
+ // the main pane's published name.
2796
+ if (s.parentName)
2797
+ continue;
2574
2798
  const key = `${s.name}\u241f${s.createdAt ?? ''}`;
2575
2799
  liveKeys.add(key);
2576
2800
  const value = s.providerSessionName ?? '';
@@ -2618,6 +2842,15 @@ exports.isInventoried = isInventoried;
2618
2842
  * prefix path route through here so the defense layers can't diverge.
2619
2843
  */
2620
2844
  const passesInjectGuards = (session, deps, cost = 1) => {
2845
+ // Subagent panes are view-only: the phone reads them but the agent
2846
+ // itself answers at the terminal. This sits above the rate bucket so a
2847
+ // flood of refused subagent writes costs nothing and spawns nothing —
2848
+ // with an unresolvable name every downstream tmux call is a no-op, but
2849
+ // the refusal must not depend on the pane still being mapped.
2850
+ if ((0, exports.isSubagentSessionName)(session)) {
2851
+ log(`! ${session}: subagent pane is view-only — drop`);
2852
+ return false;
2853
+ }
2621
2854
  // Rate bucket first: the pane probe below is a blocking tmux spawnSync,
2622
2855
  // and the sequencer can flush several held messages back-to-back — an
2623
2856
  // empty bucket must refuse before paying that probe N times, not after.
@@ -3084,8 +3317,9 @@ const streamSession = (wsUrl, apiKey) => {
3084
3317
  sweepInFlight = false;
3085
3318
  }
3086
3319
  };
3087
- const publishSessions = ({ sessions, rejected }) => {
3320
+ const publishSessions = ({ sessions, rejected, targets }) => {
3088
3321
  (0, exports.recordInventory)(sessions);
3322
+ (0, exports.recordTargets)(targets);
3089
3323
  // Let the user's own status bar say what the phone says. Change-gated
3090
3324
  // internally, so an unchanged sweep spawns nothing.
3091
3325
  (0, exports.syncTmuxAgentNames)(sessions);
@@ -76,6 +76,15 @@ export interface ProcTable {
76
76
  children: Map<number, number[]>;
77
77
  /** pid → process start time in epoch ms. Only pids whose `lstart` parsed. */
78
78
  startTimes: Map<number, number>;
79
+ /** pid → its process group id. Absent when the row carried no pgid. */
80
+ pgidOf: Map<number, number>;
81
+ /** pid → the foreground process group of its controlling tty. A pane
82
+ * shell whose tpgid equals its own pgid IS the foreground — a plain
83
+ * prompt; anything else is running a foreground command. */
84
+ tpgidOf: Map<number, number>;
85
+ /** pid → its command name (`comm`), last column because it may contain
86
+ * spaces (`tmux: client`). Basename-matched against the agent registry. */
87
+ commOf: Map<number, string>;
79
88
  }
80
89
  /**
81
90
  * Parse `ps -axo pid=,ppid=,lstart=`. A row is `<pid> <ppid> [<lstart>]`, e.g.
@@ -92,7 +101,30 @@ export interface ProcTable {
92
101
  * allowed to go missing, and every consumer of `startTimes` treats an absent
93
102
  * entry as "no match" rather than guessing.
94
103
  */
104
+ /**
105
+ * Parse `ps -axo pid=,ppid=,pgid=,tpgid=,lstart=,comm=`. A row is
106
+ * `<pid> <ppid> <pgid> <tpgid> [<lstart>] [<comm>]`, e.g.
107
+ * ` 902 1 902 902 Fri Aug 7 11:12:59 2026 zsh` — note the two spaces
108
+ * before a single-digit day and the trailing padding.
109
+ *
110
+ * Token positions, not one regex: `lstart` is a fixed 5 tokens when present
111
+ * and `comm` is LAST because it may contain spaces. A row with fewer tokens
112
+ * simply contributes fewer maps — the shape is load-bearing rather than
113
+ * defensive: parent links must survive an unreadable timestamp (the reason
114
+ * `lstart` is optional), and the pgid/tpgid/comm columns are absent only when
115
+ * a caller fed the old narrower argv, which nothing in production does.
116
+ */
95
117
  export declare const parseProcTable: (table: string) => ProcTable;
118
+ /**
119
+ * The agent running in the FOREGROUND process group of the tty owned by a
120
+ * pane shell, or null. This is what separates a subagent pane from a plain
121
+ * prompt: the subagent pane reports `pane_current_command=bash` (a script
122
+ * execs pi), so the pane-level command identifies nothing — but the tty's
123
+ * foreground group is pi's, and `comm` names it. A pane sitting at its login
124
+ * shell has the shell's own group in the foreground (tpgid === pgid) and is
125
+ * not a subagent, no matter what the registry would match.
126
+ */
127
+ export declare const foregroundAgentFor: (panePid: number, table?: ProcTable | null) => RegisteredRemoteAgent | null;
96
128
  /** Start times for this cycle's snapshot. Empty when `ps` is unavailable. */
97
129
  export declare const psStartTimes: () => Map<number, number>;
98
130
  /** pid set of `rootPid` + all descendants, from one `ps` snapshot. */
@@ -1 +1 @@
1
- {"version":3,"file":"remote-agents.d.ts","sourceRoot":"","sources":["../src/remote-agents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remote-agents.d.ts","sourceRoot":"","sources":["../src/remote-agents.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.matchAgentByPaneCommand = exports.findAgentBySubcommand = exports.REMOTE_AGENTS = exports.detectCodexSessionName = exports.newestVersionedDb = exports.detectHermesSessionName = exports.sqliteJson = exports.pickRowByProcStart = exports.claudeTranscriptPath = exports.detectClaudeSessionNameByPid = exports.detectClaudeSessionIdByPid = exports.detectClaudeSessionByPid = exports.pidSessionRecordsExist = exports.collectDescendantPids = exports.psStartTimes = exports.parseProcTable = exports.detectClaudeSessionId = void 0;
3
+ exports.matchAgentByPaneCommand = exports.findAgentBySubcommand = exports.REMOTE_AGENTS = exports.detectCodexSessionName = exports.newestVersionedDb = exports.detectHermesSessionName = exports.sqliteJson = exports.pickRowByProcStart = exports.claudeTranscriptPath = exports.detectClaudeSessionNameByPid = exports.detectClaudeSessionIdByPid = exports.detectClaudeSessionByPid = exports.pidSessionRecordsExist = exports.collectDescendantPids = exports.psStartTimes = exports.foregroundAgentFor = exports.parseProcTable = exports.detectClaudeSessionId = void 0;
4
4
  /**
5
5
  * Remote-control agent registry — the single table behind every
6
6
  * `zeph <agent>` subcommand, the listener's pane matching, and the
@@ -178,24 +178,50 @@ const ttlMemo = (ttlMs, compute) => {
178
178
  * allowed to go missing, and every consumer of `startTimes` treats an absent
179
179
  * entry as "no match" rather than guessing.
180
180
  */
181
+ /**
182
+ * Parse `ps -axo pid=,ppid=,pgid=,tpgid=,lstart=,comm=`. A row is
183
+ * `<pid> <ppid> <pgid> <tpgid> [<lstart>] [<comm>]`, e.g.
184
+ * ` 902 1 902 902 Fri Aug 7 11:12:59 2026 zsh` — note the two spaces
185
+ * before a single-digit day and the trailing padding.
186
+ *
187
+ * Token positions, not one regex: `lstart` is a fixed 5 tokens when present
188
+ * and `comm` is LAST because it may contain spaces. A row with fewer tokens
189
+ * simply contributes fewer maps — the shape is load-bearing rather than
190
+ * defensive: parent links must survive an unreadable timestamp (the reason
191
+ * `lstart` is optional), and the pgid/tpgid/comm columns are absent only when
192
+ * a caller fed the old narrower argv, which nothing in production does.
193
+ */
181
194
  const parseProcTable = (table) => {
182
195
  const children = new Map();
183
196
  const startTimes = new Map();
197
+ const pgidOf = new Map();
198
+ const tpgidOf = new Map();
199
+ const commOf = new Map();
184
200
  for (const line of table.split('\n')) {
185
- const m = line.trim().match(/^(\d+)\s+(\d+)(?:\s+(.*))?$/);
186
- if (!m)
201
+ const tok = line.trim().split(/\s+/);
202
+ const pid = Number(tok[0]);
203
+ const ppid = Number(tok[1]);
204
+ if (!Number.isInteger(pid) || !Number.isInteger(ppid))
187
205
  continue;
188
- const [pid, ppid] = [Number(m[1]), Number(m[2])];
189
206
  const list = children.get(ppid) ?? [];
190
207
  list.push(pid);
191
208
  children.set(ppid, list);
192
- if (m[3] === undefined)
193
- continue;
194
- const started = Date.parse(m[3].trim());
195
- if (Number.isFinite(started))
196
- startTimes.set(pid, started);
209
+ let rest = tok.slice(2);
210
+ if (rest.length >= 2 && Number.isInteger(Number(rest[0]))) {
211
+ pgidOf.set(pid, Number(rest[0]));
212
+ tpgidOf.set(pid, Number(rest[1]));
213
+ rest = rest.slice(2);
214
+ }
215
+ if (rest.length >= 5) {
216
+ const started = Date.parse(rest.slice(0, 5).join(' '));
217
+ if (Number.isFinite(started))
218
+ startTimes.set(pid, started);
219
+ rest = rest.slice(5);
220
+ }
221
+ if (rest.length > 0)
222
+ commOf.set(pid, rest.join(' '));
197
223
  }
198
- return { children, startTimes };
224
+ return { children, startTimes, pgidOf, tpgidOf, commOf };
199
225
  };
200
226
  exports.parseProcTable = parseProcTable;
201
227
  /**
@@ -205,9 +231,38 @@ exports.parseProcTable = parseProcTable;
205
231
  * second `ps` spawn to learn that would undo what this memo is for.
206
232
  */
207
233
  const readPsSnapshot = ttlMemo(SNAPSHOT_TTL_MS, () => {
208
- const r = (0, child_process_1.spawnSync)('ps', ['-axo', 'pid=,ppid=,lstart='], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
234
+ // pgid/tpgid/comm ride along for the subagent detection (foreground
235
+ // process group), so the extra panes cost zero new spawns — one `ps`
236
+ // per report cycle is still the whole table (KB precedent).
237
+ const r = (0, child_process_1.spawnSync)('ps', ['-axo', 'pid=,ppid=,pgid=,tpgid=,lstart=,comm='], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
209
238
  return r.status === 0 ? (0, exports.parseProcTable)(r.stdout ?? '') : null;
210
239
  });
240
+ /**
241
+ * The agent running in the FOREGROUND process group of the tty owned by a
242
+ * pane shell, or null. This is what separates a subagent pane from a plain
243
+ * prompt: the subagent pane reports `pane_current_command=bash` (a script
244
+ * execs pi), so the pane-level command identifies nothing — but the tty's
245
+ * foreground group is pi's, and `comm` names it. A pane sitting at its login
246
+ * shell has the shell's own group in the foreground (tpgid === pgid) and is
247
+ * not a subagent, no matter what the registry would match.
248
+ */
249
+ const foregroundAgentFor = (panePid, table = readPsSnapshot()) => {
250
+ if (!Number.isInteger(panePid) || panePid <= 0 || !table)
251
+ return null;
252
+ const pgid = table.pgidOf.get(panePid);
253
+ const tpgid = table.tpgidOf.get(panePid);
254
+ if (pgid === undefined || tpgid === undefined || tpgid === pgid)
255
+ return null;
256
+ for (const [pid, pgrp] of table.pgidOf) {
257
+ if (pgrp !== tpgid)
258
+ continue;
259
+ const agent = (0, exports.matchAgentByPaneCommand)((0, path_1.basename)(table.commOf.get(pid) ?? ''));
260
+ if (agent)
261
+ return agent;
262
+ }
263
+ return null;
264
+ };
265
+ exports.foregroundAgentFor = foregroundAgentFor;
211
266
  /** Start times for this cycle's snapshot. Empty when `ps` is unavailable. */
212
267
  const psStartTimes = () => readPsSnapshot()?.startTimes ?? new Map();
213
268
  exports.psStartTimes = psStartTimes;
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAwGA,oGAAoG;AACpG,eAAO,MAAM,kBAAkB,QAAS,CAAC;AA8IzC,6FAA6F;AAC7F,eAAO,MAAM,WAAW,QAKtB,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,aAAa,QAIxB,CAAC;AAEH,+GAA+G;AAC/G,eAAO,MAAM,WAAW,QAAoE,CAAC;AAE7F,4GAA4G;AAC5G,eAAO,MAAM,UAAU,QAAoE,CAAC;AAE5F,oGAAoG;AACpG,eAAO,MAAM,YAAY,QAIvB,CAAC;AAEH,gFAAgF;AAChF,eAAO,MAAM,UAAU,QAIrB,CAAC;AAEH,sGAAsG;AACtG,eAAO,MAAM,UAAU,QAIrB,CAAC;AAEH,wJAAwJ;AACxJ,eAAO,MAAM,OAAO,QAKlB,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,QAgJxB,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"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAwGA,oGAAoG;AACpG,eAAO,MAAM,kBAAkB,QAAS,CAAC;AA8IzC,6FAA6F;AAC7F,eAAO,MAAM,WAAW,QAKtB,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,aAAa,QAIxB,CAAC;AAEH,+GAA+G;AAC/G,eAAO,MAAM,WAAW,QAAoE,CAAC;AAE7F,4GAA4G;AAC5G,eAAO,MAAM,UAAU,QAAoE,CAAC;AAE5F,oGAAoG;AACpG,eAAO,MAAM,YAAY,QAIvB,CAAC;AAEH,gFAAgF;AAChF,eAAO,MAAM,UAAU,QAIrB,CAAC;AAEH,sGAAsG;AACtG,eAAO,MAAM,UAAU,QAIrB,CAAC;AAEH,wJAAwJ;AACxJ,eAAO,MAAM,OAAO,QAKlB,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,QAyKxB,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
@@ -368,7 +368,7 @@ exports.COPILOT_HOOKS = JSON.stringify({
368
368
  // *_PLUGIN for the same quiet-default guard, at the string level.
369
369
  /** Pi extension source — written to ~/.pi/agent/extensions/zeph.ts (drop-in auto-load). */
370
370
  exports.PI_EXTENSION = `// Generated by @zeph-to/cli — reinstall overwrites; \`zeph uninstall\` removes.
371
- import { spawn } from "node:child_process";
371
+ import { spawn, execFileSync } from "node:child_process";
372
372
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
373
373
 
374
374
  const sh = (cmd: string, cwd: string, stdin?: string): Promise<string> =>
@@ -406,12 +406,32 @@ const pushHigh = (cwd: string, title: string, body: string): void => {
406
406
  ...process.env,
407
407
  ZEPH_PUSH_TITLE: title,
408
408
  ZEPH_PUSH_BODY: body.length > 200 ? body.slice(0, 199) + "…" : body,
409
+ ...(SUBAGENT_SESSION ? { ZEPH_AGENT_SESSION_NAME: SUBAGENT_SESSION } : {}),
409
410
  };
410
411
  const child = spawn("sh", ["-c", ${JSON.stringify(highNotifyCmd)}], { cwd, env, stdio: "ignore", detached: true });
411
412
  child.on("error", () => {});
412
413
  child.unref();
413
414
  };
414
415
 
416
+ // Subagent mode: pi spawned this process as a subagent (PI_SUBAGENT_NAME set
417
+ // by the spawner). Label this tmux pane so the listener's sweep picks the name
418
+ // up, and file pushes under the subagent's own session key
419
+ // (<parent session>.<pane id>) — the listener's naming — instead of the
420
+ // parent's, so a phone push opens the subagent's stream, not the parent's.
421
+ const SUBAGENT_NAME = process.env.PI_SUBAGENT_NAME || "";
422
+ const SUBAGENT_SESSION = (() => {
423
+ if (!SUBAGENT_NAME || !process.env.TMUX_PANE) return "";
424
+ try {
425
+ const pane = spawn("tmux", ["set-option", "-p", "-t", process.env.TMUX_PANE, "@zeph_pane_label", SUBAGENT_NAME], { stdio: "ignore", detached: true });
426
+ pane.on("error", () => {});
427
+ pane.unref();
428
+ // Targeted at this pane and bounded: this runs while pi loads, so a
429
+ // wedged tmux server must cost at most 2s, never the whole startup.
430
+ const s = execFileSync("tmux", ["display-message", "-p", "-t", process.env.TMUX_PANE, "#S"], { encoding: "utf8", timeout: 2000 }).trim();
431
+ return s ? s + "." + process.env.TMUX_PANE.slice(1) : "";
432
+ } catch { return ""; }
433
+ })();
434
+
415
435
  export default function (pi: ExtensionAPI) {
416
436
  // Turn facts for the push gate. One agent per process, so plain counters
417
437
  // suffice — no session keying.
@@ -467,7 +487,7 @@ export default function (pi: ExtensionAPI) {
467
487
  ?? (running ? running.toolName + " is waiting for your answer" : "Waiting for your input");
468
488
  promptTimer = setTimeout(() => {
469
489
  promptTimer = undefined;
470
- pushHigh(ctx.cwd, "pi asks: " + projectOf(ctx.cwd), body);
490
+ pushHigh(ctx.cwd, "pi asks: " + projectOf(ctx.cwd) + (SUBAGENT_NAME ? " · " + SUBAGENT_NAME : ""), body);
471
491
  }, PROMPT_GRACE_MS);
472
492
  promptTimer.unref?.();
473
493
  });
@@ -481,6 +501,11 @@ export default function (pi: ExtensionAPI) {
481
501
  agentRunning = false;
482
502
  clearTimeout(promptTimer);
483
503
  promptTimer = undefined;
504
+ // A subagent never pushes — not its completion, not its error. The parent
505
+ // settles on their results (4 parallel subagents would otherwise mean 4
506
+ // simultaneous pushes), and an error stays live in the subagent's own
507
+ // stream, which the phone is already mirroring.
508
+ if (SUBAGENT_NAME) return;
484
509
  // Esc (at the terminal, or a key sent from the phone): whoever stopped the
485
510
  // turn is already looking at it.
486
511
  if (stop?.reason === "aborted") return;
@@ -1 +1 @@
1
- {"version":3,"file":"zeph-hook.d.ts","sourceRoot":"","sources":["../src/zeph-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAY,gBAAgB,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAoCxL,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,OAAO,EAAE,WAAW;IAShC;;;;;;;;OAQG;YACW,YAAY;IAyB1B;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;YAgB7C,UAAU;YAyCV,cAAc;IA6DtB,aAAa,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAK7G,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrF,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAuB9C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKlD,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAK7C;;;;OAIG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;YASxF,OAAO;IAiCrB,OAAO,CAAC,UAAU;CASnB"}
1
+ {"version":3,"file":"zeph-hook.d.ts","sourceRoot":"","sources":["../src/zeph-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAY,gBAAgB,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAyCxL,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,OAAO,EAAE,WAAW;IAShC;;;;;;;;OAQG;YACW,YAAY;IAyB1B;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;YAgB7C,UAAU;YAyCV,cAAc;IA6DtB,aAAa,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAK7G,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrF,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAuB9C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKlD,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAK7C;;;;OAIG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;YASxF,OAAO;IAiCrB,OAAO,CAAC,UAAU;CASnB"}
package/dist/zeph-hook.js CHANGED
@@ -21,6 +21,12 @@ const PREVIEW_LENGTH = 200;
21
21
  const agentSessionContext = () => {
22
22
  if (!process.env.TMUX)
23
23
  return null;
24
+ // A pi subagent pane carries its own wire name (<#S>.<pane> — the
25
+ // listener's naming) in this env, set by the extension for its notify
26
+ // children. It wins over #S, which would file the push under the parent.
27
+ const fromEnv = process.env.ZEPH_AGENT_SESSION_NAME?.trim();
28
+ if (fromEnv)
29
+ return { agentDeviceId: (0, listener_device_id_js_1.listenerDeviceId)(), agentSessionName: fromEnv };
24
30
  let name;
25
31
  try {
26
32
  name = (0, node_child_process_1.execFileSync)('tmux', ['display-message', '-p', '#S'], { encoding: 'utf-8' }).trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeph-to/cli",
3
- "version": "2.17.1",
3
+ "version": "2.19.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",