@pushary/agent-hooks 0.95.5 → 0.95.7

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.
Files changed (88) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
  3. package/data/cursor-plugin/CHANGELOG.md +7 -0
  4. package/data/cursor-plugin/scripts/pushary-gate.mjs +72 -17
  5. package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
  6. package/data/vscode-plugin/CHANGELOG.md +6 -0
  7. package/data/vscode-plugin/scripts/pushary-gate.mjs +76 -24
  8. package/dist/bin/pushary-bell-hook.js +18 -18
  9. package/dist/bin/pushary-bell.js +7 -7
  10. package/dist/bin/pushary-claude.js +24 -24
  11. package/dist/bin/pushary-clean.js +18 -18
  12. package/dist/bin/pushary-codex-bridge.js +29 -12
  13. package/dist/bin/pushary-codex-hook.js +29 -29
  14. package/dist/bin/pushary-codex.js +7 -7
  15. package/dist/bin/pushary-connect.js +8 -8
  16. package/dist/bin/pushary-cowork.js +5 -5
  17. package/dist/bin/pushary-daemon.js +24 -24
  18. package/dist/bin/pushary-disconnect.js +8 -8
  19. package/dist/bin/pushary-doctor.js +91 -36
  20. package/dist/bin/pushary-elicitation-hook.js +17 -17
  21. package/dist/bin/pushary-gemini-bridge.js +9 -9
  22. package/dist/bin/pushary-gemini-hook.js +65 -50
  23. package/dist/bin/pushary-hook.js +24 -24
  24. package/dist/bin/pushary-login.js +9 -9
  25. package/dist/bin/pushary-logout.js +6 -6
  26. package/dist/bin/pushary-mode.js +5 -5
  27. package/dist/bin/pushary-notification-hook.js +19 -19
  28. package/dist/bin/pushary-opencode-hook.js +29 -29
  29. package/dist/bin/pushary-permission-denied-hook.js +22 -22
  30. package/dist/bin/pushary-permission-hook.js +22 -22
  31. package/dist/bin/pushary-post-hook.js +19 -19
  32. package/dist/bin/pushary-prompt-hook.js +19 -19
  33. package/dist/bin/pushary-session-end-hook.js +19 -19
  34. package/dist/bin/pushary-session-start-hook.js +22 -22
  35. package/dist/bin/pushary-setup.js +45 -38
  36. package/dist/bin/pushary-stats.js +4 -4
  37. package/dist/bin/pushary-status.js +14 -10
  38. package/dist/bin/pushary-stop-hook.js +19 -19
  39. package/dist/bin/pushary-stopfailure-hook.js +19 -19
  40. package/dist/bin/pushary-suggestions.js +2 -2
  41. package/dist/bin/pushary-transcript-register.js +16 -16
  42. package/dist/bin/pushary-transcripts.js +7 -7
  43. package/dist/bin/pushary-upgrade.js +16 -16
  44. package/dist/bin/pushary-wait.js +8 -7
  45. package/dist/bin/pushary.js +1 -1
  46. package/dist/{chunk-N4VNLISN.js → chunk-4OSQZ5TB.js} +2 -2
  47. package/dist/{chunk-IM2A5H33.js → chunk-54W7ELDQ.js} +1 -1
  48. package/dist/{chunk-UCS65VVR.js → chunk-5GKNAQTG.js} +1 -1
  49. package/dist/{chunk-Z2UWFEJW.js → chunk-6W2WGNO5.js} +7 -7
  50. package/dist/{chunk-2D7BAOMY.js → chunk-7RNY3UHJ.js} +3 -3
  51. package/dist/{chunk-OW6AZDY2.js → chunk-BGX4QUQF.js} +1 -1
  52. package/dist/{chunk-I6GZX3FT.js → chunk-BQL7UDIB.js} +1 -1
  53. package/dist/{chunk-HGUZOUB2.js → chunk-CNUDZS4V.js} +3 -3
  54. package/dist/chunk-CVWG2N3Y.js +16 -0
  55. package/dist/{chunk-BMANRGJQ.js → chunk-EMGGUO2Q.js} +1 -1
  56. package/dist/{chunk-QHWXUNMN.js → chunk-FRXZMCVA.js} +1 -1
  57. package/dist/{chunk-DLLAWCQB.js → chunk-GOQHN2UH.js} +1 -1
  58. package/dist/{chunk-HBHEX3JQ.js → chunk-GQRU4KJF.js} +3 -3
  59. package/dist/{chunk-4RF52LSU.js → chunk-HAXZ6CBF.js} +8 -8
  60. package/dist/{chunk-OEKFD5BC.js → chunk-IZVK6FKD.js} +1 -1
  61. package/dist/{chunk-KAVDAZ7A.js → chunk-L7NOG547.js} +1 -1
  62. package/dist/{chunk-D6PRC3YQ.js → chunk-LZQVAEWP.js} +2 -1
  63. package/dist/{chunk-WDKHZRAS.js → chunk-MC5WUOJJ.js} +41 -0
  64. package/dist/{chunk-6VANQN5Q.js → chunk-NKUE5SJB.js} +2 -2
  65. package/dist/{chunk-QQGNFHIK.js → chunk-PQGLDN4V.js} +2 -2
  66. package/dist/{chunk-CUEZMPLJ.js → chunk-QJPYJVL3.js} +1 -1
  67. package/dist/{chunk-RLXVW4AM.js → chunk-SDNTRS76.js} +1 -1
  68. package/dist/{chunk-4PX4QK2Z.js → chunk-SKPXQRKN.js} +1 -1
  69. package/dist/{chunk-VTO3TNIB.js → chunk-TY6IJY6I.js} +1 -1
  70. package/dist/{chunk-HQIVR7MM.js → chunk-U5RIXR7I.js} +1 -1
  71. package/dist/{chunk-WRDLHR2F.js → chunk-UKMLSQXA.js} +2 -2
  72. package/dist/{chunk-6AEM5RCS.js → chunk-USWT4PTR.js} +1 -1
  73. package/dist/{chunk-D62EONG3.js → chunk-VDI645ZX.js} +1 -1
  74. package/dist/{chunk-OI3DJA7N.js → chunk-VERXHIQF.js} +1 -1
  75. package/dist/{chunk-67YNQ3QA.js → chunk-VIUOGMT5.js} +18 -14
  76. package/dist/{chunk-S5CMLR35.js → chunk-VMQU7YEJ.js} +1 -1
  77. package/dist/{chunk-XLOELLAS.js → chunk-VMZ4SWQD.js} +4 -4
  78. package/dist/{chunk-32RMQO5Z.js → chunk-WAUAYRUQ.js} +47 -4
  79. package/dist/{chunk-VOSGZ6PH.js → chunk-WPS5IM7B.js} +3 -3
  80. package/dist/{chunk-HCMM3BOW.js → chunk-XJQOVWBQ.js} +14 -11
  81. package/dist/{chunk-KI4PA7MQ.js → chunk-XO3JR3OP.js} +90 -18
  82. package/dist/{chunk-IVQMNLSA.js → chunk-XUTSZYYJ.js} +1 -1
  83. package/dist/{chunk-2ZTL3PGN.js → chunk-XZWUOCJZ.js} +1 -1
  84. package/dist/{chunk-BDI2MXTT.js → chunk-Z4JVEL7R.js} +1 -1
  85. package/dist/src/index.d.ts +3 -0
  86. package/dist/src/index.js +10 -10
  87. package/package.json +1 -1
  88. package/dist/chunk-IIENZE2J.js +0 -15
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SETUP_FLAGS
3
- } from "./chunk-2ZTL3PGN.js";
3
+ } from "./chunk-XZWUOCJZ.js";
4
4
  import {
5
5
  parseFlags
6
6
  } from "./chunk-GMXKITVA.js";
@@ -14,7 +14,7 @@ import {
14
14
  CLAUDE_CONNECTOR_URL,
15
15
  COWORK_CONNECTOR_LIMIT,
16
16
  COWORK_STANDING_INSTRUCTIONS
17
- } from "./chunk-WDKHZRAS.js";
17
+ } from "./chunk-MC5WUOJJ.js";
18
18
 
19
19
  // src/cli/cowork.ts
20
20
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-2D7BAOMY.js";
3
+ } from "./chunk-7RNY3UHJ.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  shouldDeferToNativeMode
3
- } from "./chunk-WDKHZRAS.js";
3
+ } from "./chunk-MC5WUOJJ.js";
4
4
 
5
5
  // src/native-defer.ts
6
6
  var nativeDeferOptions = () => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-S5CMLR35.js";
3
+ } from "./chunk-VMQU7YEJ.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var transcriptEnvironmentPreference = () => {
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  defersToNativeMode
3
- } from "./chunk-D62EONG3.js";
3
+ } from "./chunk-VDI645ZX.js";
4
4
  import {
5
5
  isGatingMoment,
6
6
  recordKeylessMoment
7
- } from "./chunk-UCS65VVR.js";
7
+ } from "./chunk-5GKNAQTG.js";
8
8
  import {
9
9
  denyReasonFrom,
10
10
  isDeferAnswer
@@ -20,15 +20,7 @@ import {
20
20
  reportEvent,
21
21
  settleQuestionsForToolUse,
22
22
  throttlePass
23
- } from "./chunk-HCMM3BOW.js";
24
- import {
25
- DECISION_STOP_REASON,
26
- askUser,
27
- isDecisionStopped,
28
- isLocalQuestionHandoff,
29
- sendNotification,
30
- waitForQuestion
31
- } from "./chunk-XS33QBJA.js";
23
+ } from "./chunk-XJQOVWBQ.js";
32
24
  import {
33
25
  UNNAMED_ACTION,
34
26
  deriveAction,
@@ -38,10 +30,18 @@ import {
38
30
  describeToolCall,
39
31
  permissionDeniedAction,
40
32
  scopePathFor
41
- } from "./chunk-BDI2MXTT.js";
33
+ } from "./chunk-Z4JVEL7R.js";
34
+ import {
35
+ DECISION_STOP_REASON,
36
+ askUser,
37
+ isDecisionStopped,
38
+ isLocalQuestionHandoff,
39
+ sendNotification,
40
+ waitForQuestion
41
+ } from "./chunk-XS33QBJA.js";
42
42
  import {
43
43
  getMachineId
44
- } from "./chunk-N4VNLISN.js";
44
+ } from "./chunk-4OSQZ5TB.js";
45
45
  import {
46
46
  getApiKey,
47
47
  getBaseUrl
@@ -53,13 +53,14 @@ import {
53
53
  effectiveWaitSeconds,
54
54
  hookWaitClamped,
55
55
  hookWaitDeadline,
56
+ hookWritePath,
56
57
  isPermissionRequestOwnedByPreToolUse,
57
58
  isPusharyOwnTool,
58
59
  parseAgentQuestionAnswers,
59
60
  parseAgentQuestions,
60
61
  resolveGate,
61
62
  stoppedOutput
62
- } from "./chunk-WDKHZRAS.js";
63
+ } from "./chunk-MC5WUOJJ.js";
63
64
 
64
65
  // src/decision-episode.ts
65
66
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -225,6 +226,7 @@ var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingC
225
226
  const toolTarget = deriveToolTarget(input.tool_name, input.tool_input);
226
227
  const intent = readLastPrompt(sessionId ?? DEFAULT_SESSION) ?? (input.transcript_path ? readLastUserPrompt(input.transcript_path) : void 0);
227
228
  const decision = {
229
+ toolPath: hookWritePath(input.tool_name, input.tool_input),
228
230
  intent,
229
231
  action: deriveAction(input.tool_name, input.tool_input),
230
232
  blocker: scopeReason ?? deriveBlocker(toolPolicy.mode),
@@ -537,6 +539,8 @@ var automodeRetryEnabled = () => {
537
539
  };
538
540
  var reportPermissionDenied = (input) => reportEvent({
539
541
  event: "permission_denied",
542
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
543
+ toolUseId: input.tool_use_id,
540
544
  agentType: CLAUDE_CODE_AGENT.type,
541
545
  agentName: `${CLAUDE_CODE_AGENT.label} - ${basename(input.cwd ?? process.cwd())}`,
542
546
  action: permissionDeniedAction(input.tool_name),
@@ -4,7 +4,7 @@ import {
4
4
  resolveInstallSourceFor,
5
5
  writeFileAtomic,
6
6
  writeJsonAtomic
7
- } from "./chunk-KAVDAZ7A.js";
7
+ } from "./chunk-L7NOG547.js";
8
8
  import {
9
9
  configFilePath
10
10
  } from "./chunk-2UMNXADU.js";
@@ -7,22 +7,22 @@ import {
7
7
  } from "./chunk-L4ZCFMWW.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-N4VNLISN.js";
10
+ } from "./chunk-4OSQZ5TB.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-OW6AZDY2.js";
13
+ } from "./chunk-BGX4QUQF.js";
14
14
  import {
15
15
  getPackageVersion
16
16
  } from "./chunk-DINDL2CF.js";
17
17
  import {
18
18
  readControlMode
19
- } from "./chunk-S5CMLR35.js";
19
+ } from "./chunk-VMQU7YEJ.js";
20
20
  import {
21
21
  readJsonSafe,
22
22
  writeFileAtomic,
23
23
  writeJsonAtomic,
24
24
  writeJsonAtomicDurable
25
- } from "./chunk-KAVDAZ7A.js";
25
+ } from "./chunk-L7NOG547.js";
26
26
  import {
27
27
  getApiKey
28
28
  } from "./chunk-2UMNXADU.js";
@@ -1,9 +1,25 @@
1
+ import {
2
+ detectProviderLiveCapabilities,
3
+ hasSpawnProvider,
4
+ resolveControlPolicy
5
+ } from "./chunk-54W7ELDQ.js";
6
+ import {
7
+ daemonHasRemoteProof,
8
+ daemonServiceManagerAvailable,
9
+ hasDaemonServiceDefinition,
10
+ readDaemonNeedsLogin,
11
+ readDaemonStatus
12
+ } from "./chunk-VMZ4SWQD.js";
1
13
  import {
2
14
  getMachineId
3
- } from "./chunk-N4VNLISN.js";
15
+ } from "./chunk-4OSQZ5TB.js";
4
16
  import {
5
17
  getPackageVersion
6
18
  } from "./chunk-DINDL2CF.js";
19
+ import {
20
+ readControlMode,
21
+ readKeySource
22
+ } from "./chunk-VMQU7YEJ.js";
7
23
  import {
8
24
  getBaseUrl
9
25
  } from "./chunk-2UMNXADU.js";
@@ -50,7 +66,7 @@ var parseMachine = (value) => {
50
66
  online: raw.online === true
51
67
  };
52
68
  };
53
- var machineReadiness = (machines, machineId, expectedCapabilities) => {
69
+ var machineReadiness = (machines, machineId, expectedCapabilities, now = Date.now()) => {
54
70
  const machine = machines.find((item) => item.machineId === machineId);
55
71
  if (!machine) return { kind: "not-ready", detail: "this machine has not heartbeated to the server" };
56
72
  if (!machine.online) return { kind: "not-ready", detail: "the server reports this machine offline" };
@@ -60,12 +76,15 @@ var machineReadiness = (machines, machineId, expectedCapabilities) => {
60
76
  if (machine.lastControlAt === null || !Number.isFinite(Date.parse(machine.lastControlAt))) {
61
77
  return { kind: "not-ready", detail: "the server has not observed this machine polling the control queue" };
62
78
  }
79
+ if (!daemonHasRemoteProof({ kind: "running", lastSuccessfulHeartbeatAt: machine.lastSeenAt, lastSuccessfulControlAt: machine.lastControlAt }, now)) {
80
+ return { kind: "not-ready", detail: "the heartbeat or control-queue proof is stale; wake this machine and retry pushary doctor" };
81
+ }
63
82
  const missing = expectedCapabilities.filter((capability) => !machine.capabilities.includes(capability));
64
83
  return missing.length === 0 ? { kind: "ready", machine } : { kind: "not-ready", detail: `the server is missing capabilities: ${missing.join(", ")}` };
65
84
  };
66
85
  var readMachineReadiness = async (apiKey, expectedCapabilities, deps = {}) => {
67
86
  const listed = await listMachines(apiKey, deps);
68
- return listed.kind === "ok" ? machineReadiness(listed.machines, deps.machineId ?? getMachineId(), expectedCapabilities) : listed.kind === "unsupported" ? { kind: "unsupported", detail: "the server does not report machine readiness yet" } : listed;
87
+ return listed.kind === "ok" ? machineReadiness(listed.machines, deps.machineId ?? getMachineId(), expectedCapabilities, deps.now?.()) : listed.kind === "unsupported" ? { kind: "unsupported", detail: "the server does not report machine readiness yet" } : listed;
69
88
  };
70
89
  var waitForMachineReadiness = async (apiKey, expectedCapabilities, deps = {}) => {
71
90
  const attempts = deps.attempts ?? 21;
@@ -106,6 +125,29 @@ var describeLastSeen = (iso, now = Date.now()) => {
106
125
  if (hours < 48) return `${hours}h ago`;
107
126
  return `${Math.round(hours / 24)}d ago`;
108
127
  };
128
+ var readManagedControlEvidence = (apiKey, providers, serverProof) => {
129
+ const now = Date.now();
130
+ const daemon = readDaemonStatus();
131
+ const capabilities = detectProviderLiveCapabilities(providers);
132
+ const providerAvailable = hasSpawnProvider(capabilities);
133
+ const managerAvailable = daemonServiceManagerAvailable();
134
+ const serviceInstalled = hasDaemonServiceDefinition();
135
+ const keyCurrent = Boolean(apiKey) && readKeySource().key === apiKey && readDaemonNeedsLogin().kind !== "blocked";
136
+ const policy = resolveControlPolicy({ mode: readControlMode(), providerAvailable, managerAvailable, servicePresent: serviceInstalled || daemon.kind !== "absent" });
137
+ const localProof = daemonHasRemoteProof(daemon, now);
138
+ const remote = serverProof?.kind === "ready" ? machineReadiness([serverProof.machine], daemon.machineId ?? "", ["spawn", ...capabilities], now) : serverProof;
139
+ return {
140
+ ready: keyCurrent && policy === "enabled" && providerAvailable && managerAvailable && serviceInstalled && localProof && remote?.kind === "ready",
141
+ keyCurrent,
142
+ providerAvailable,
143
+ managerAvailable,
144
+ serviceInstalled,
145
+ policy,
146
+ localProof,
147
+ serverProof: remote?.kind ?? "unchecked",
148
+ checkedAt: new Date(now).toISOString()
149
+ };
150
+ };
109
151
 
110
152
  export {
111
153
  HEARTBEAT_INTERVAL_MS,
@@ -113,5 +155,6 @@ export {
113
155
  readMachineReadiness,
114
156
  waitForMachineReadiness,
115
157
  listMachines,
116
- describeLastSeen
158
+ describeLastSeen,
159
+ readManagedControlEvidence
117
160
  };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  AGENT_CHOICES
3
- } from "./chunk-2ZTL3PGN.js";
3
+ } from "./chunk-XZWUOCJZ.js";
4
4
  import {
5
5
  isBridgeLocator
6
- } from "./chunk-CUEZMPLJ.js";
6
+ } from "./chunk-QJPYJVL3.js";
7
7
  import {
8
8
  pusharyHookExecutable
9
- } from "./chunk-HBHEX3JQ.js";
9
+ } from "./chunk-GQRU4KJF.js";
10
10
  import {
11
11
  BELL_HOOK_BINARY
12
12
  } from "./chunk-GOPIMNVC.js";
@@ -1,9 +1,3 @@
1
- import {
2
- cancelQuestion,
3
- isPolicyConfig,
4
- sendNotification,
5
- waitForAnswer
6
- } from "./chunk-XS33QBJA.js";
7
1
  import {
8
2
  AGENT_IDENTITIES,
9
3
  TEAMMATE_IDLE_ACTION,
@@ -24,20 +18,26 @@ import {
24
18
  summarizeStop,
25
19
  taskTitleFrom,
26
20
  toolResultErrorText
27
- } from "./chunk-BDI2MXTT.js";
21
+ } from "./chunk-Z4JVEL7R.js";
28
22
  import {
29
23
  NOTIFICATION_PUSH_TYPES,
30
24
  POSTTOOLUSE_MIRROR_ONLY_TOOLS
31
- } from "./chunk-IVQMNLSA.js";
25
+ } from "./chunk-XUTSZYYJ.js";
26
+ import {
27
+ cancelQuestion,
28
+ isPolicyConfig,
29
+ sendNotification,
30
+ waitForAnswer
31
+ } from "./chunk-XS33QBJA.js";
32
32
  import {
33
33
  getMachineId
34
- } from "./chunk-N4VNLISN.js";
34
+ } from "./chunk-4OSQZ5TB.js";
35
35
  import {
36
36
  withRetry
37
37
  } from "./chunk-EYRGBYZG.js";
38
38
  import {
39
39
  reportedInstallSource
40
- } from "./chunk-KAVDAZ7A.js";
40
+ } from "./chunk-L7NOG547.js";
41
41
  import {
42
42
  getApiKey,
43
43
  getBaseUrl
@@ -54,7 +54,7 @@ import {
54
54
  pendingCommandEvents,
55
55
  resolveAutoResolveOrigin,
56
56
  resolvePolicy
57
- } from "./chunk-WDKHZRAS.js";
57
+ } from "./chunk-MC5WUOJJ.js";
58
58
 
59
59
  // src/policy.ts
60
60
  import { createHash } from "crypto";
@@ -1004,6 +1004,7 @@ var settleQuestionsForToolUse = async (sessionId, toolUseId) => {
1004
1004
  for (const answer of late) removePendingQuestion(sessionId, answer.correlationId);
1005
1005
  };
1006
1006
  var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1007
+ const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
1007
1008
  const deadline = Date.now() + HOOK_BUDGET_MS;
1008
1009
  const signal = AbortSignal.timeout(HOOK_BUDGET_MS);
1009
1010
  try {
@@ -1028,6 +1029,8 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1028
1029
  const beacon = !isMirrorOnly && decisionSource === "policy_auto" && throttlePass(`autodecision:${sessionKey}:${lookup.tool}`, AUTO_DECISION_WINDOW_MS) ? deriveAutoDecisionBeacon(lookup.tool, lookup.input, liveMode, agent.type, input.cwd, input.session_id) : void 0;
1029
1030
  const toolReport = reportEvent({
1030
1031
  event: isError ? "tool_error" : "tool_complete",
1032
+ occurredAt,
1033
+ toolUseId: input.tool_use_id,
1031
1034
  agentType: agent.type,
1032
1035
  agentName: `${agent.label} - ${projectName}`,
1033
1036
  action,
@@ -15,7 +15,10 @@ import {
15
15
  } from "./chunk-EYRGBYZG.js";
16
16
  import {
17
17
  reportedInstallSource
18
- } from "./chunk-KAVDAZ7A.js";
18
+ } from "./chunk-L7NOG547.js";
19
+ import {
20
+ isAnswerSource
21
+ } from "./chunk-MC5WUOJJ.js";
19
22
 
20
23
  // src/reach.ts
21
24
  var reachVerdict = (channels) => {
@@ -119,7 +122,7 @@ var classifyDelivery = (channels, push) => {
119
122
  var sendTestNotification = async (apiKey) => {
120
123
  const result = await callMcpTool(apiKey, "send_notification", {
121
124
  title: "Pushary is connected",
122
- body: "Your AI agent can now reach you right here.",
125
+ body: "This setup notification reached you. Agent hooks still need a real-agent check.",
123
126
  agentName: "Pushary setup"
124
127
  }, { timeoutMs: 2e4 });
125
128
  const toCount = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -145,7 +148,7 @@ var describeVerifyOutcome = (outcome) => {
145
148
  message: "Nothing to deliver to. Agents are configured; approvals will fall back to the terminal.",
146
149
  next: [
147
150
  "Run pushary connect to add your phone.",
148
- "Then pushary doctor --roundtrip to test a real approval."
151
+ "Then pushary doctor --roundtrip to test a synthetic question and answer."
149
152
  ]
150
153
  };
151
154
  case "others-only":
@@ -174,7 +177,7 @@ var describeVerifyOutcome = (outcome) => {
174
177
  };
175
178
  }
176
179
  };
177
- var ROUND_TRIP_QUESTION = "Setup is nearly done. Tap Yes and this machine is connected.";
180
+ var ROUND_TRIP_QUESTION = "Pushary delivery test. Tap Yes or No to test returning an answer. This does not test your agent hooks.";
178
181
  var ROUND_TRIP_TIMEOUT_MS = 55e3;
179
182
  var defaultAsk = (apiKey, agentName) => (
180
183
  // wait:false so the poll is ours: the blocking form returns on the SITE's
@@ -195,6 +198,18 @@ var defaultCancel = async (apiKey, correlationId) => {
195
198
  await callMcpTool(apiKey, "cancel_question", { correlationId }, { timeoutMs: 1e4 }).catch(() => {
196
199
  });
197
200
  };
201
+ var answeredRoundTrip = (answer, correlationId, latencyMs) => {
202
+ if (answer.value !== "yes" && answer.value !== "no") {
203
+ return { kind: "error", detail: "the server reported an answer without a valid Yes or No value" };
204
+ }
205
+ return {
206
+ kind: "answered",
207
+ value: answer.value,
208
+ latencyMs,
209
+ correlationId,
210
+ ...isAnswerSource(answer.answerSource) ? { answerSource: answer.answerSource } : {}
211
+ };
212
+ };
198
213
  var verifyRoundTrip = async (apiKey, agentName = "Pushary setup", deps = {}) => {
199
214
  const ask = deps.ask ?? defaultAsk;
200
215
  const wait = deps.wait ?? defaultWait;
@@ -207,31 +222,73 @@ var verifyRoundTrip = async (apiKey, agentName = "Pushary setup", deps = {}) =>
207
222
  } catch (err) {
208
223
  return { kind: "error", detail: err instanceof Error ? err.message : "ask failed" };
209
224
  }
210
- if (asked.noDevices || asked.warning) return { kind: "no-recipients" };
211
- if (asked.mode === "notify_only" || asked.status === "notified") return { kind: "notify-only" };
212
- if (!asked.correlationId) return { kind: "error", detail: "no correlationId returned" };
213
- if (asked.answered === true) {
214
- return { kind: "answered", value: asked.value ?? "yes", latencyMs: now() - started };
225
+ let shutdown;
226
+ const stop = (code) => {
227
+ if (shutdown) return;
228
+ shutdown = (async () => {
229
+ let timer;
230
+ try {
231
+ await Promise.race([
232
+ cancel(apiKey, asked.correlationId).catch(() => {
233
+ }),
234
+ new Promise((resolve) => {
235
+ timer = setTimeout(resolve, 4e3);
236
+ })
237
+ ]);
238
+ } finally {
239
+ clearTimeout(timer);
240
+ process.stderr.write("Delivery test interrupted. Cleanup was attempted; an unresolved question expires automatically.\n");
241
+ process.exit(code);
242
+ }
243
+ })();
244
+ };
245
+ const onInterrupt = () => stop(130);
246
+ const onTerminate = () => stop(143);
247
+ if (asked.correlationId) {
248
+ process.on("SIGINT", onInterrupt);
249
+ process.on("SIGTERM", onTerminate);
215
250
  }
216
- let answer;
251
+ let answerObserved = false;
217
252
  try {
218
- answer = await wait(apiKey, asked.correlationId);
253
+ if (asked.noDevices || asked.warning) return { kind: "no-recipients" };
254
+ if (asked.mode === "notify_only" || asked.status === "notified") return { kind: "notify-only" };
255
+ if (!asked.correlationId) return { kind: "error", detail: "no correlationId returned" };
256
+ const answer = asked.answered === true ? asked : await wait(apiKey, asked.correlationId);
257
+ if (answer.answered === true) {
258
+ const outcome = answeredRoundTrip(answer, asked.correlationId, now() - started);
259
+ answerObserved = outcome.kind === "answered";
260
+ return outcome;
261
+ }
262
+ return { kind: "unanswered" };
219
263
  } catch (err) {
220
- await cancel(apiKey, asked.correlationId);
221
264
  return { kind: "error", detail: err instanceof Error ? err.message : "wait failed" };
265
+ } finally {
266
+ if (shutdown) await shutdown;
267
+ process.off("SIGINT", onInterrupt);
268
+ process.off("SIGTERM", onTerminate);
269
+ if (!answerObserved && asked.correlationId) await cancel(apiKey, asked.correlationId).catch(() => {
270
+ });
222
271
  }
223
- if (answer.answered === true) {
224
- return { kind: "answered", value: answer.value ?? "yes", latencyMs: now() - started };
272
+ };
273
+ var describeAnswerSource = (source) => {
274
+ switch (source) {
275
+ case "mobile_app":
276
+ case "mobile_background":
277
+ return "A phone answered this synthetic test.";
278
+ case "mac_app":
279
+ return "A Mac answered this synthetic test.";
280
+ case "sandbox":
281
+ return "This was a simulated sandbox answer.";
282
+ default:
283
+ return source ? `Answer source: ${source}. This was a synthetic test.` : "The answering surface was not reported. This was a synthetic test.";
225
284
  }
226
- await cancel(apiKey, asked.correlationId);
227
- return { kind: "unanswered" };
228
285
  };
229
- var describeRoundTrip = (outcome) => {
286
+ var describeRoundTripOutcome = (outcome) => {
230
287
  switch (outcome.kind) {
231
288
  case "answered":
232
289
  return {
233
290
  ok: true,
234
- message: `Answered "${outcome.value}" in ${(outcome.latencyMs / 1e3).toFixed(1)}s. Your agent can reach you and you can answer it.`,
291
+ message: `Answered "${outcome.value}" in ${(outcome.latencyMs / 1e3).toFixed(1)}s. ${describeAnswerSource(outcome.answerSource)}`,
235
292
  next: []
236
293
  };
237
294
  case "unanswered":
@@ -266,6 +323,20 @@ var describeRoundTrip = (outcome) => {
266
323
  };
267
324
  }
268
325
  };
326
+ var describeRoundTrip = (outcome) => {
327
+ const report = describeRoundTripOutcome(outcome);
328
+ return { ...report, next: [...report.next, "Agent hook activation remains unverified. Restart your agent and check one real approval."] };
329
+ };
330
+ var roundTripEvidence = (outcome) => ({
331
+ kind: outcome?.kind ?? "skipped",
332
+ ...outcome?.kind === "answered" ? {
333
+ correlationId: outcome.correlationId ?? null,
334
+ answerSource: outcome.answerSource ?? null,
335
+ value: outcome.value,
336
+ latencyMs: outcome.latencyMs
337
+ } : {},
338
+ hookActivation: "unverified"
339
+ });
269
340
 
270
341
  // src/onboarding.ts
271
342
  import qrcodeTerminal from "qrcode-terminal";
@@ -863,6 +934,7 @@ export {
863
934
  describeReach,
864
935
  verifyRoundTrip,
865
936
  describeRoundTrip,
937
+ roundTripEvidence,
866
938
  fetchChannels,
867
939
  connectDevice,
868
940
  confirmAppConnection,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ASKED_IN_EVERY_MODE_TOOLS,
3
3
  PRETOOLUSE_GATED_TOOLS
4
- } from "./chunk-WDKHZRAS.js";
4
+ } from "./chunk-MC5WUOJJ.js";
5
5
 
6
6
  // src/claude-tools.ts
7
7
  var PRETOOLUSE_PHONE_ANSWERED_TOOLS = ASKED_IN_EVERY_MODE_TOOLS;
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-7QLSKOSU.js";
7
7
  import {
8
8
  isValidApiKey
9
- } from "./chunk-WDKHZRAS.js";
9
+ } from "./chunk-MC5WUOJJ.js";
10
10
 
11
11
  // src/setup/options.ts
12
12
  var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "opencode", "custom"];
@@ -5,7 +5,7 @@ import {
5
5
  FILE_TARGET_TOOLS,
6
6
  redactSecrets,
7
7
  redactSecretsDeep
8
- } from "./chunk-WDKHZRAS.js";
8
+ } from "./chunk-MC5WUOJJ.js";
9
9
 
10
10
  // ../hook-mapping/src/describe.ts
11
11
  import { isAbsolute, relative, resolve } from "path";
@@ -34,6 +34,8 @@ interface AgentIdentity {
34
34
  readonly label: string;
35
35
  }
36
36
  interface AgentEvent {
37
+ occurredAt?: string;
38
+ toolUseId?: string;
37
39
  event: string;
38
40
  agentType: string;
39
41
  agentName?: string;
@@ -147,6 +149,7 @@ interface AskUserParams {
147
149
  machineId?: string;
148
150
  toolName?: string;
149
151
  toolTarget?: string;
152
+ toolPath?: string;
150
153
  repoKey?: string;
151
154
  intent?: string;
152
155
  action?: string;
package/dist/src/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-67YNQ3QA.js";
4
- import "../chunk-D62EONG3.js";
5
- import "../chunk-UCS65VVR.js";
3
+ } from "../chunk-VIUOGMT5.js";
4
+ import "../chunk-VDI645ZX.js";
5
+ import "../chunk-5GKNAQTG.js";
6
6
  import "../chunk-YHG74UFF.js";
7
7
  import {
8
8
  fetchAccountPublicKey
@@ -15,28 +15,28 @@ import {
15
15
  handlePostToolUse,
16
16
  handleStop,
17
17
  reportEvent
18
- } from "../chunk-HCMM3BOW.js";
18
+ } from "../chunk-XJQOVWBQ.js";
19
+ import "../chunk-Z4JVEL7R.js";
20
+ import "../chunk-XUTSZYYJ.js";
19
21
  import {
20
22
  askUser,
21
23
  cancelQuestion,
22
24
  waitForAnswer
23
25
  } from "../chunk-XS33QBJA.js";
24
- import "../chunk-BDI2MXTT.js";
25
- import "../chunk-IVQMNLSA.js";
26
- import "../chunk-N4VNLISN.js";
27
- import "../chunk-OW6AZDY2.js";
26
+ import "../chunk-4OSQZ5TB.js";
27
+ import "../chunk-BGX4QUQF.js";
28
28
  import "../chunk-EYRGBYZG.js";
29
29
  import "../chunk-SAF6HGAA.js";
30
30
  import "../chunk-7QLSKOSU.js";
31
31
  import "../chunk-KZERVKTD.js";
32
- import "../chunk-KAVDAZ7A.js";
32
+ import "../chunk-L7NOG547.js";
33
33
  import {
34
34
  getApiKey,
35
35
  getBaseUrl
36
36
  } from "../chunk-2UMNXADU.js";
37
37
  import {
38
38
  resolvePolicy
39
- } from "../chunk-WDKHZRAS.js";
39
+ } from "../chunk-MC5WUOJJ.js";
40
40
  export {
41
41
  askUser,
42
42
  cancelQuestion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.95.5",
3
+ "version": "0.95.7",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",
@@ -1,15 +0,0 @@
1
- // src/wait-ladder.ts
2
- var describeWaitLadder = (p) => {
3
- if (p.mode === "terminal_only") return "decide in the terminal, no push";
4
- if (p.mode === "notify_only") return "send a heads-up, the terminal decides";
5
- if (p.mode === "push_only") {
6
- if (p.timeoutAction === "wait") return "wait for your phone, then ask in the terminal";
7
- const tail = p.timeoutAction === "approve" ? "then auto-approve" : p.timeoutAction === "deny" ? "then auto-deny" : "then ask in the terminal";
8
- return `wait for your phone up to ${p.timeoutSeconds}s, ${tail}`;
9
- }
10
- return `ping your phone ${p.pushFirstSeconds}s, then ask in the terminal`;
11
- };
12
-
13
- export {
14
- describeWaitLadder
15
- };