@pushary/agent-hooks 0.76.0 → 0.77.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.
@@ -7,7 +7,7 @@ import {
7
7
  KILL_REASON,
8
8
  isDeferAnswer,
9
9
  resolveGate
10
- } from "../chunk-UPPHYOIZ.js";
10
+ } from "../chunk-FWWBEB6L.js";
11
11
  import {
12
12
  askUser,
13
13
  cancelQuestion,
@@ -19,7 +19,7 @@ import {
19
19
  reportEvent,
20
20
  scopePathFor,
21
21
  waitForAnswer
22
- } from "../chunk-7L57N7WN.js";
22
+ } from "../chunk-HPDLDQC5.js";
23
23
  import "../chunk-BSZYIAZL.js";
24
24
  import "../chunk-SAF6HGAA.js";
25
25
  import "../chunk-7QLSKOSU.js";
@@ -1047,33 +1047,21 @@ var flagValue = (args2, index) => {
1047
1047
  const next = args2[index + 1];
1048
1048
  return next && !next.startsWith("-") ? next : void 0;
1049
1049
  };
1050
- var VALUE_TAKING_FLAGS = /* @__PURE__ */ new Set(["--model", "-m", "--cwd", "--add-dir", "--settings"]);
1051
- var markValueConsumed = (args2, index, consumed, ownsNextToken) => {
1052
- if (args2[index]?.includes("=")) return;
1053
- const next = args2[index + 1];
1054
- if (next == null) return;
1055
- if (!ownsNextToken && next.startsWith("-")) return;
1056
- consumed.add(index + 1);
1057
- };
1050
+ var PASS_THROUGH_VALUE_FLAGS = /* @__PURE__ */ new Set(["--model", "-m", "--cwd", "--add-dir", "--settings"]);
1058
1051
  var parseRemoteArgs = (args2) => {
1059
1052
  let initialPrompt;
1060
1053
  let resume;
1061
1054
  let permissionMode;
1062
- const consumedAsValue = /* @__PURE__ */ new Set();
1063
1055
  for (let i = 0; i < args2.length; i++) {
1064
1056
  const arg = args2[i];
1065
- if (consumedAsValue.has(i)) continue;
1066
- if (arg === "-p" || arg === "--print" || arg?.startsWith("--print=")) {
1057
+ if (PASS_THROUGH_VALUE_FLAGS.has(arg ?? "")) {
1058
+ i++;
1059
+ } else if (arg === "-p" || arg === "--print" || arg?.startsWith("--print=")) {
1067
1060
  initialPrompt = flagValue(args2, i);
1068
- markValueConsumed(args2, i, consumedAsValue, false);
1069
1061
  } else if (arg === "-r" || arg === "--resume" || arg?.startsWith("--resume=")) {
1070
1062
  resume = flagValue(args2, i);
1071
- markValueConsumed(args2, i, consumedAsValue, false);
1072
1063
  } else if (arg === "--permission-mode" || arg?.startsWith("--permission-mode=")) {
1073
1064
  permissionMode = flagValue(args2, i);
1074
- markValueConsumed(args2, i, consumedAsValue, false);
1075
- } else if (VALUE_TAKING_FLAGS.has(arg ?? "")) {
1076
- markValueConsumed(args2, i, consumedAsValue, true);
1077
1065
  }
1078
1066
  }
1079
1067
  return { initialPrompt, resume, permissionMode };
@@ -4,7 +4,7 @@ import {
4
4
  denyReasonFrom,
5
5
  isDeferAnswer,
6
6
  resolveGate
7
- } from "../chunk-UPPHYOIZ.js";
7
+ } from "../chunk-FWWBEB6L.js";
8
8
  import {
9
9
  CODEX_AGENT,
10
10
  DEFAULT_SESSION,
@@ -35,7 +35,7 @@ import {
35
35
  toPolicyLookup,
36
36
  toPolicyLookups,
37
37
  waitForAnswer
38
- } from "../chunk-7L57N7WN.js";
38
+ } from "../chunk-HPDLDQC5.js";
39
39
  import {
40
40
  isGatingMoment,
41
41
  recordKeylessMoment
@@ -3,7 +3,7 @@ import {
3
3
  askUser,
4
4
  reportEvent,
5
5
  waitForAnswer
6
- } from "../chunk-7L57N7WN.js";
6
+ } from "../chunk-HPDLDQC5.js";
7
7
  import "../chunk-BSZYIAZL.js";
8
8
  import "../chunk-SAF6HGAA.js";
9
9
  import "../chunk-7QLSKOSU.js";
@@ -28,6 +28,27 @@ import {
28
28
  import { spawn } from "child_process";
29
29
  import { existsSync } from "fs";
30
30
  import { basename, dirname, join } from "path";
31
+
32
+ // src/stop-requests.ts
33
+ var STOP_CAPABILITY = "stop";
34
+ var isStopRequest = (value) => {
35
+ if (!value || typeof value !== "object") return false;
36
+ const candidate = value;
37
+ return typeof candidate.sessionId === "string" && candidate.sessionId.length > 0 && typeof candidate.pid === "number" && Number.isInteger(candidate.pid) && candidate.pid > 1;
38
+ };
39
+ var parseStopRequests = (value) => Array.isArray(value) ? value.filter(isStopRequest) : [];
40
+ var applyStop = (request, deps) => {
41
+ if (request.pid === deps.selfPid) return "skipped_self";
42
+ try {
43
+ deps.kill(request.pid, "SIGTERM");
44
+ return "stopped";
45
+ } catch (err) {
46
+ const code = err?.code;
47
+ return code === "ESRCH" ? "already_gone" : "refused";
48
+ }
49
+ };
50
+
51
+ // src/spawn-daemon.ts
31
52
  var DRAIN_PATH = "/api/agent/spawn/drain";
32
53
  var FAST_POLL_MS = 3e3;
33
54
  var SLOW_POLL_MS = 3e4;
@@ -51,7 +72,9 @@ var isAuthFailure = (err) => {
51
72
  return candidate.status === 401 || candidate.status === 403;
52
73
  };
53
74
  var NATIVE_AGENT_BINARIES = { codex: "codex", gemini: "gemini" };
75
+ var isFlagShapedPrompt = (prompt) => prompt.trimStart().startsWith("-");
54
76
  var buildSpawnLaunch = (req, opts) => {
77
+ if (isFlagShapedPrompt(req.prompt)) return null;
55
78
  const agent = req.agent === "codex" || req.agent === "gemini" ? req.agent : "claude";
56
79
  const model = typeof req.model === "string" && req.model.length > 0 ? ["--model", req.model] : [];
57
80
  if (agent === "claude") {
@@ -103,6 +126,10 @@ var runSpawnDaemon = async () => {
103
126
  pusharyBin,
104
127
  resolveNative: (name) => resolveGlobalBinary(name) ?? name
105
128
  });
129
+ if (!plan) {
130
+ stderr("[pushary] refused a phone-requested session: the prompt looks like a command-line flag\n");
131
+ return;
132
+ }
106
133
  try {
107
134
  const child = plan.direct ? spawn(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env }) : spawnClaude(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env });
108
135
  child.on("error", (err) => stderr(`[pushary] could not launch session: ${err.message}
@@ -115,6 +142,17 @@ var runSpawnDaemon = async () => {
115
142
  `);
116
143
  }
117
144
  };
145
+ const runStop = (stop) => {
146
+ const outcome = applyStop(stop, { kill: process.kill.bind(process), selfPid: process.pid });
147
+ const said = {
148
+ stopped: `stopped session ${stop.sessionId}`,
149
+ already_gone: `session ${stop.sessionId} had already exited`,
150
+ refused: `could not stop session ${stop.sessionId}, the system refused the signal`,
151
+ skipped_self: `refused to stop session ${stop.sessionId}: that pid is this daemon`
152
+ };
153
+ stderr(`[pushary] ${said[outcome]}
154
+ `);
155
+ };
118
156
  const drain = async (signal) => {
119
157
  const res = await fetch(`${baseUrl}${DRAIN_PATH}`, {
120
158
  method: "POST",
@@ -124,6 +162,7 @@ var runSpawnDaemon = async () => {
124
162
  });
125
163
  if (!res.ok) throw new DrainError(res.status);
126
164
  const data = await res.json();
165
+ for (const stop of parseStopRequests(data.stops)) runStop(stop);
127
166
  const spawn2 = data.spawn;
128
167
  return spawn2 && typeof spawn2.prompt === "string" && typeof spawn2.id === "string" ? spawn2 : null;
129
168
  };
@@ -176,7 +215,10 @@ var runSpawnDaemon = async () => {
176
215
  };
177
216
  const beat = async () => {
178
217
  if (stopped) return;
179
- const result = await sendHeartbeat(apiKey, { repoKey: basename(process.cwd()) });
218
+ const result = await sendHeartbeat(apiKey, {
219
+ repoKey: basename(process.cwd()),
220
+ capabilities: ["spawn", STOP_CAPABILITY]
221
+ });
180
222
  if (result.kind === "unauthorized" && !stopped) {
181
223
  fatalCode = EXIT.UNAUTHENTICATED;
182
224
  stderr("[pushary] daemon stopping: this machine's key was rejected. Run `pushary login` to sign in again.\n");
@@ -4,7 +4,7 @@ import {
4
4
  denyReasonFrom,
5
5
  isDeferAnswer,
6
6
  resolveGate
7
- } from "../chunk-UPPHYOIZ.js";
7
+ } from "../chunk-FWWBEB6L.js";
8
8
  import {
9
9
  DEFAULT_SESSION,
10
10
  askUser,
@@ -26,7 +26,7 @@ import {
26
26
  scopePathFor,
27
27
  sendNotification,
28
28
  waitForAnswer
29
- } from "../chunk-7L57N7WN.js";
29
+ } from "../chunk-HPDLDQC5.js";
30
30
  import {
31
31
  isGatingMoment,
32
32
  recordKeylessMoment
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePreToolUse
4
- } from "../chunk-DJHWBNM3.js";
4
+ } from "../chunk-MY4VJPWW.js";
5
5
  import "../chunk-3CBPY2G5.js";
6
6
  import {
7
7
  exitOnHelpFlag
8
8
  } from "../chunk-BBK3TTU2.js";
9
9
  import "../chunk-7OYGFJYZ.js";
10
- import "../chunk-UPPHYOIZ.js";
11
- import "../chunk-7L57N7WN.js";
10
+ import "../chunk-FWWBEB6L.js";
11
+ import "../chunk-HPDLDQC5.js";
12
12
  import "../chunk-IA5GBKNL.js";
13
13
  import "../chunk-BSZYIAZL.js";
14
14
  import "../chunk-SAF6HGAA.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleNotification
4
- } from "../chunk-7L57N7WN.js";
4
+ } from "../chunk-HPDLDQC5.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionDenied
4
- } from "../chunk-DJHWBNM3.js";
4
+ } from "../chunk-MY4VJPWW.js";
5
5
  import "../chunk-3CBPY2G5.js";
6
- import "../chunk-UPPHYOIZ.js";
7
- import "../chunk-7L57N7WN.js";
6
+ import "../chunk-FWWBEB6L.js";
7
+ import "../chunk-HPDLDQC5.js";
8
8
  import "../chunk-IA5GBKNL.js";
9
9
  import "../chunk-BSZYIAZL.js";
10
10
  import "../chunk-SAF6HGAA.js";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionRequest
4
- } from "../chunk-DJHWBNM3.js";
4
+ } from "../chunk-MY4VJPWW.js";
5
5
  import "../chunk-3CBPY2G5.js";
6
- import "../chunk-UPPHYOIZ.js";
7
- import "../chunk-7L57N7WN.js";
6
+ import "../chunk-FWWBEB6L.js";
7
+ import "../chunk-HPDLDQC5.js";
8
8
  import "../chunk-IA5GBKNL.js";
9
9
  import "../chunk-BSZYIAZL.js";
10
10
  import "../chunk-SAF6HGAA.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePostToolUse
4
- } from "../chunk-7L57N7WN.js";
4
+ } from "../chunk-HPDLDQC5.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleUserPrompt
4
- } from "../chunk-7L57N7WN.js";
4
+ } from "../chunk-HPDLDQC5.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionEnd
4
- } from "../chunk-7L57N7WN.js";
4
+ } from "../chunk-HPDLDQC5.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionStart
4
- } from "../chunk-7L57N7WN.js";
4
+ } from "../chunk-HPDLDQC5.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -94,7 +94,7 @@ import {
94
94
  } from "../chunk-7OYGFJYZ.js";
95
95
  import {
96
96
  reportEvent
97
- } from "../chunk-7L57N7WN.js";
97
+ } from "../chunk-HPDLDQC5.js";
98
98
  import "../chunk-BSZYIAZL.js";
99
99
  import "../chunk-SAF6HGAA.js";
100
100
  import {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStop
4
- } from "../chunk-7L57N7WN.js";
4
+ } from "../chunk-HPDLDQC5.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStopFailure
4
- } from "../chunk-7L57N7WN.js";
4
+ } from "../chunk-HPDLDQC5.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -4,7 +4,7 @@ import {
4
4
  isModeStateDegraded,
5
5
  resolveAutoResolveOrigin,
6
6
  resolvePolicyAcross
7
- } from "./chunk-7L57N7WN.js";
7
+ } from "./chunk-HPDLDQC5.js";
8
8
  import {
9
9
  evaluateScope,
10
10
  scopeChangeReason,
@@ -1028,6 +1028,9 @@ var reportEvent = async (event, options = {}) => {
1028
1028
  body: JSON.stringify({
1029
1029
  ...event,
1030
1030
  machineId: event.machineId ?? getMachineId(),
1031
+ // Lets the phone's Stop end this process instead of only denying its next
1032
+ // tool call. Ours alone: never another process, never a parent.
1033
+ pid: event.pid ?? process.pid,
1031
1034
  repoKey: event.repoKey ?? repoKeyFor(),
1032
1035
  installMode: detectInstallMode(),
1033
1036
  // Link-back for a phone-spawned session: forward the spawn id the daemon
@@ -5,7 +5,7 @@ import {
5
5
  denyReasonFrom,
6
6
  isDeferAnswer,
7
7
  resolveGate
8
- } from "./chunk-UPPHYOIZ.js";
8
+ } from "./chunk-FWWBEB6L.js";
9
9
  import {
10
10
  DEFAULT_SESSION,
11
11
  askUser,
@@ -25,7 +25,7 @@ import {
25
25
  sendNotification,
26
26
  throttlePass,
27
27
  waitForAnswer
28
- } from "./chunk-7L57N7WN.js";
28
+ } from "./chunk-HPDLDQC5.js";
29
29
  import {
30
30
  isGatingMoment,
31
31
  recordKeylessMoment
@@ -70,6 +70,7 @@ interface AgentEvent {
70
70
  agents?: readonly string[];
71
71
  meta?: ReceiptMeta;
72
72
  usage?: UsageReport;
73
+ pid?: number;
73
74
  }
74
75
  interface ReportEventOptions {
75
76
  maxAttempts?: number;
package/dist/src/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-DJHWBNM3.js";
3
+ } from "../chunk-MY4VJPWW.js";
4
4
  import "../chunk-3CBPY2G5.js";
5
- import "../chunk-UPPHYOIZ.js";
5
+ import "../chunk-FWWBEB6L.js";
6
6
  import {
7
7
  askUser,
8
8
  cancelQuestion,
@@ -15,7 +15,7 @@ import {
15
15
  reportEvent,
16
16
  resolvePolicy,
17
17
  waitForAnswer
18
- } from "../chunk-7L57N7WN.js";
18
+ } from "../chunk-HPDLDQC5.js";
19
19
  import "../chunk-IA5GBKNL.js";
20
20
  import "../chunk-BSZYIAZL.js";
21
21
  import "../chunk-SAF6HGAA.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.76.0",
3
+ "version": "0.77.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",