@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,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  describeWaitLadder
4
- } from "../chunk-IIENZE2J.js";
4
+ } from "../chunk-CVWG2N3Y.js";
5
5
  import {
6
6
  readServiceExecPath,
7
7
  versionManagerOf
8
8
  } from "../chunk-GU3EJCVV.js";
9
9
  import {
10
- readMachineReadiness
11
- } from "../chunk-32RMQO5Z.js";
10
+ readMachineReadiness,
11
+ readManagedControlEvidence
12
+ } from "../chunk-WAUAYRUQ.js";
12
13
  import {
13
14
  CURSOR_GATE_SCRIPT
14
15
  } from "../chunk-KBITFQST.js";
@@ -20,28 +21,28 @@ import {
20
21
  isDetected,
21
22
  isPluginRegistered,
22
23
  vscodeSettingsTargets
23
- } from "../chunk-6VANQN5Q.js";
24
+ } from "../chunk-NKUE5SJB.js";
24
25
  import {
25
26
  OPENCODE_PLUGIN_PLACEHOLDER,
26
27
  openCodePluginDir,
27
28
  openCodePluginFile,
28
29
  openCodeWired
29
- } from "../chunk-HQIVR7MM.js";
30
- import "../chunk-HGUZOUB2.js";
30
+ } from "../chunk-U5RIXR7I.js";
31
+ import "../chunk-CNUDZS4V.js";
31
32
  import {
32
33
  detectProviderLiveCapabilities,
33
34
  hasSpawnProvider,
34
35
  resolveControlPolicy
35
- } from "../chunk-IM2A5H33.js";
36
+ } from "../chunk-54W7ELDQ.js";
36
37
  import {
37
38
  readLedgerSummary
38
- } from "../chunk-UCS65VVR.js";
39
+ } from "../chunk-5GKNAQTG.js";
39
40
  import {
40
41
  describeBridgeResolution,
41
42
  isBridgeLocator,
42
43
  liveBridgeProbe,
43
44
  resolveBridgeStub
44
- } from "../chunk-CUEZMPLJ.js";
45
+ } from "../chunk-QJPYJVL3.js";
45
46
  import {
46
47
  claudeOtherHolders,
47
48
  claudeWired,
@@ -56,7 +57,7 @@ import {
56
57
  geminiWired,
57
58
  holdsLong,
58
59
  pusharyHookExecutable
59
- } from "../chunk-HBHEX3JQ.js";
60
+ } from "../chunk-GQRU4KJF.js";
60
61
  import {
61
62
  daemonHasRemoteProof,
62
63
  daemonServiceManagerAvailable,
@@ -65,22 +66,22 @@ import {
65
66
  readDaemonNeedsLogin,
66
67
  readDaemonStatus,
67
68
  readInstalledDaemonService
68
- } from "../chunk-XLOELLAS.js";
69
+ } from "../chunk-VMZ4SWQD.js";
69
70
  import {
70
71
  GEMINI_HOOK_BINARY,
71
72
  hasGeminiHooks,
72
73
  missingGeminiHookEvents
73
- } from "../chunk-2D7BAOMY.js";
74
+ } from "../chunk-7RNY3UHJ.js";
74
75
  import "../chunk-3AJIK5AO.js";
75
76
  import "../chunk-XHKBHWLX.js";
76
77
  import {
77
78
  execNpm
78
79
  } from "../chunk-L4ZCFMWW.js";
79
- import "../chunk-BDI2MXTT.js";
80
- import "../chunk-IVQMNLSA.js";
80
+ import "../chunk-Z4JVEL7R.js";
81
+ import "../chunk-XUTSZYYJ.js";
81
82
  import {
82
83
  getMachineId
83
- } from "../chunk-N4VNLISN.js";
84
+ } from "../chunk-4OSQZ5TB.js";
84
85
  import {
85
86
  CODEX_HOOK_EVENTS,
86
87
  claudeJson,
@@ -102,27 +103,28 @@ import {
102
103
  pusharyConfigFile,
103
104
  readCodexMcpAuth,
104
105
  vscodePluginDir
105
- } from "../chunk-OW6AZDY2.js";
106
+ } from "../chunk-BGX4QUQF.js";
106
107
  import {
107
108
  describeReach,
108
109
  describeRoundTrip,
109
110
  fetchChannels,
110
111
  reachVerdict,
112
+ roundTripEvidence,
111
113
  verifyRoundTrip
112
- } from "../chunk-KI4PA7MQ.js";
114
+ } from "../chunk-XO3JR3OP.js";
113
115
  import "../chunk-3EGEA4KH.js";
114
116
  import {
115
117
  checkApiKey,
116
118
  describeKeyCheck
117
- } from "../chunk-I6GZX3FT.js";
119
+ } from "../chunk-BQL7UDIB.js";
118
120
  import "../chunk-QCGNGEGE.js";
119
121
  import {
120
122
  createIo
121
123
  } from "../chunk-5RUIFDTP.js";
122
124
  import {
123
125
  exitOnHelpFlag
124
- } from "../chunk-DLLAWCQB.js";
125
- import "../chunk-D6PRC3YQ.js";
126
+ } from "../chunk-GOQHN2UH.js";
127
+ import "../chunk-LZQVAEWP.js";
126
128
  import {
127
129
  parseFlags
128
130
  } from "../chunk-GMXKITVA.js";
@@ -133,7 +135,7 @@ import {
133
135
  keyPrefix,
134
136
  readControlMode,
135
137
  readKeySource
136
- } from "../chunk-S5CMLR35.js";
138
+ } from "../chunk-VMQU7YEJ.js";
137
139
  import {
138
140
  callMcpTool,
139
141
  sendMcpRequest
@@ -145,18 +147,21 @@ import {
145
147
  import {
146
148
  EXIT
147
149
  } from "../chunk-KZERVKTD.js";
148
- import "../chunk-KAVDAZ7A.js";
150
+ import "../chunk-L7NOG547.js";
149
151
  import {
150
152
  getBaseUrl
151
153
  } from "../chunk-2UMNXADU.js";
152
154
  import {
155
+ AGENTS,
153
156
  CURSOR_GATE_EVENTS,
154
- HOOK_LOCATOR_BINARY
155
- } from "../chunk-WDKHZRAS.js";
157
+ HOOK_LOCATOR_BINARY,
158
+ hookActivationResult,
159
+ isHookActivationReport
160
+ } from "../chunk-MC5WUOJJ.js";
156
161
 
157
162
  // bin/pushary-doctor.ts
158
163
  import { existsSync, readFileSync, writeFileSync } from "fs";
159
- import { join } from "path";
164
+ import { join, resolve } from "path";
160
165
  import { homedir as homedir2 } from "os";
161
166
  import { execFileSync } from "child_process";
162
167
  import { confirm } from "@inquirer/prompts";
@@ -215,6 +220,29 @@ var resolveDoctorExit = ({ keyExit, failures }) => {
215
220
  return EXIT.PROBLEMS_FOUND;
216
221
  };
217
222
 
223
+ // src/diagnostics/activation.ts
224
+ var readHookActivation = async (apiKey, agentType, target, deps = {}) => {
225
+ const machineId = deps.machineId ?? getMachineId();
226
+ const url = new URL("/api/agent/onboarding/activation", deps.baseUrl ?? getBaseUrl());
227
+ url.searchParams.set("machineId", machineId);
228
+ url.searchParams.set("agent", agentType);
229
+ url.searchParams.set("target", target);
230
+ try {
231
+ const response = await (deps.fetchImpl ?? fetch)(url, {
232
+ headers: { Authorization: `Bearer ${apiKey}` },
233
+ signal: AbortSignal.timeout(1e4)
234
+ });
235
+ if (!response.ok) return { kind: "unavailable", detail: response.status === 404 ? "This server does not report hook activation yet." : `Evidence request returned HTTP ${response.status}.` };
236
+ const body = await response.json();
237
+ if (!isHookActivationReport(body) || body.machineId !== machineId || body.agentType !== agentType || body.target !== target) {
238
+ return { kind: "unavailable", detail: "The server returned invalid or mismatched activation evidence." };
239
+ }
240
+ return { kind: "ok", report: body };
241
+ } catch {
242
+ return { kind: "unavailable", detail: "Activation evidence could not be read. Retry when the connection is available." };
243
+ }
244
+ };
245
+
218
246
  // src/diagnostics/roundtrip-plan.ts
219
247
  var planRoundTrip = (context) => {
220
248
  if (context.roundtrip) return { kind: "always" };
@@ -238,17 +266,25 @@ var firstToken = (command) => {
238
266
  // bin/pushary-doctor.ts
239
267
  exitOnHelpFlag("doctor");
240
268
  var parseDoctorOptions = (argv) => {
241
- const flags = parseFlags(argv, { bools: ["json", "roundtrip", "no-push", "bundle", "help", "h"], values: [] });
269
+ const flags = parseFlags(argv, { bools: ["json", "roundtrip", "no-push", "bundle", "help", "h"], values: ["activation", "activation-target"] });
242
270
  if (flags.command !== void 0 && flags.command !== "doctor") {
243
271
  throw new UsageError(`doctor takes no positional arguments (got ${flags.command})`);
244
272
  }
245
273
  const roundtrip = flags.bools.has("roundtrip");
246
- const noPush = flags.bools.has("no-push");
274
+ const activation2 = flags.values.get("activation") ?? null;
275
+ if (activation2 && (!Object.hasOwn(AGENTS, activation2) || !AGENTS[activation2].activationGuide)) throw new UsageError("This build supports --activation claude_code. Other integrations remain unverified.");
276
+ const rawTarget = flags.values.get("activation-target");
277
+ if (Boolean(activation2) !== Boolean(rawTarget)) throw new UsageError("--activation requires --activation-target with the unique scratch-file path used for this check.");
278
+ const activationTarget = rawTarget ? resolve(rawTarget) : null;
279
+ const noPush = flags.bools.has("no-push") || activation2 !== null && !roundtrip;
247
280
  if (roundtrip && noPush) {
248
281
  throw new UsageError("--roundtrip needs to send a push, so it cannot be combined with --no-push");
249
282
  }
250
- return { json: flags.bools.has("json"), roundtrip, noPush, bundle: flags.bools.has("bundle") };
283
+ return { activationTarget, activation: activation2, json: flags.bools.has("json"), roundtrip, noPush, bundle: flags.bools.has("bundle") };
251
284
  };
285
+ var activation = null;
286
+ var hookActivation = "unverified";
287
+ var roundTrip = null;
252
288
  var options;
253
289
  try {
254
290
  options = parseDoctorOptions(process.argv.slice(2));
@@ -642,8 +678,8 @@ var main = async () => {
642
678
  const controlUnsupported = controlMode === "on" && controlPolicy !== "enabled";
643
679
  const daemonOk = controlUnsupported ? false : controlPolicy === "disabled" ? daemon.kind === "absent" && !serviceDefined : controlPolicy === "enabled" ? daemonHasRemoteProof(daemon) : controlPolicy === "legacy" ? false : daemon.kind === "absent" && !serviceDefined;
644
680
  const needsLogin = loginBlock.kind === "blocked" ? `needs login: ${describeDaemonLoginBlock(loginBlock.record)}` : null;
645
- const daemonFailure = daemonOk ? null : needsLogin ?? (controlUnsupported ? "control is required but no usable provider/service-manager pair is available" : controlPolicy === "enabled" && daemon.kind === "running" ? "the daemon has not completed an authenticated heartbeat and control poll" : controlPolicy === "disabled" ? `control is disabled but ${serviceDefined ? "the service definition remains" : `local state is ${daemon.kind}`}` : controlPolicy === "legacy" ? "a legacy service exists without a usable provider/service-manager pair" : `local state is ${daemon.kind}`);
646
- const daemonDetail = daemonFailure ? needsLogin ? daemonFailure : controlUnsupported ? `${daemonFailure}; re-run setup with \`--control off\`, or restore both requirements` : controlPolicy === "disabled" ? `${daemonFailure}; re-run setup with \`--control off\`` : controlPolicy === "legacy" ? `${daemonFailure}; run setup with \`--control off\`, or restore both requirements` : `${daemonFailure}; run \`pushary daemon install\` to install or repair the background service` : controlPolicy === "disabled" ? "disabled by setup" : controlPolicy === "preserve-absent" ? "legacy absence preserved" : !controlRequired && daemon.kind === "absent" ? "managed control is unsupported on this provider or operating-system session" : null;
681
+ const daemonFailure = daemonOk ? null : needsLogin ?? (controlUnsupported ? !controlSupported ? "install and configure a supported Claude Code, Codex or Gemini provider" : "no native background-service manager is available in this OS session" : controlPolicy === "enabled" && daemon.kind === "running" ? "the daemon has not completed an authenticated heartbeat and control poll" : controlPolicy === "disabled" ? `control is disabled but ${serviceDefined ? "the service definition remains" : `local state is ${daemon.kind}`}` : controlPolicy === "legacy" ? "a legacy service exists without a usable provider/service-manager pair" : `local state is ${daemon.kind}`);
682
+ const daemonDetail = daemonFailure ? needsLogin ? daemonFailure : controlUnsupported ? `${daemonFailure}; re-run setup with \`--control off\`, or repair the missing prerequisite` : controlPolicy === "disabled" ? `${daemonFailure}; re-run setup with \`--control off\`` : controlPolicy === "legacy" ? `${daemonFailure}; run setup with \`--control off\`, or repair the missing prerequisite` : `${daemonFailure}; run \`pushary daemon install\` to install or repair the background service` : controlPolicy === "disabled" ? "disabled by setup" : controlPolicy === "preserve-absent" ? "legacy absence preserved" : !controlRequired && daemon.kind === "absent" ? "managed control is unsupported on this provider or operating-system session" : null;
647
683
  check(
648
684
  daemonOk,
649
685
  "Remote-control daemon",
@@ -672,8 +708,9 @@ var main = async () => {
672
708
  } else if (serviceFile) {
673
709
  console.log(` ${warn} Remote-control service runtime ${dim(`(could not read the node path out of ${serviceFile.path})`)}`);
674
710
  }
711
+ let serverProof = null;
675
712
  if (controlPolicy === "enabled" && apiKey && daemon.machineId) {
676
- const serverProof = await readMachineReadiness(apiKey, ["spawn", ...controlCapabilities], {
713
+ serverProof = await readMachineReadiness(apiKey, ["spawn", ...controlCapabilities], {
677
714
  machineId: daemon.machineId
678
715
  });
679
716
  if (serverProof.kind === "unsupported") {
@@ -975,7 +1012,7 @@ var main = async () => {
975
1012
  stdinIsTty: process.stdin.isTTY === true
976
1013
  });
977
1014
  const testQuestion = roundTripPlan.kind === "always" ? true : roundTripPlan.kind === "never" ? false : await confirm({
978
- message: "Test question roundtrip? (sends a push notification; the only check that proves you can answer one)",
1015
+ message: "Test a synthetic question and answer? (sends a notification; does not test agent hooks)",
979
1016
  default: roundTripPlan.defaultAnswer
980
1017
  });
981
1018
  if (testQuestion) {
@@ -983,8 +1020,9 @@ var main = async () => {
983
1020
  console.log(` ${dim("Question Roundtrip")}`);
984
1021
  console.log(` ${dim("\u2192")} Question sent, waiting for your answer...`);
985
1022
  const outcome = await verifyRoundTrip(apiKey, "Pushary Doctor");
1023
+ roundTrip = outcome;
986
1024
  const report = describeRoundTrip(outcome);
987
- check(report.ok, "Answer received", outcome.kind === "answered" ? `"${outcome.value}" (${(outcome.latencyMs / 1e3).toFixed(1)}s roundtrip)` : report.message);
1025
+ check(report.ok, "Synthetic answer test", report.message);
988
1026
  for (const line of report.next) console.log(` ${dim(line)}`);
989
1027
  }
990
1028
  }
@@ -1000,7 +1038,8 @@ var main = async () => {
1000
1038
  console.log(` ${dim("Wait Ladder")} ${dim("(what happens when you don't answer)")}`);
1001
1039
  const wildcard = policy.policies?.find((p) => p.tool === "*");
1002
1040
  const defaultWindow = policy.defaultPushFirstSeconds ?? 20;
1003
- console.log(` ${dim("\xB7")} default: ${cyan(`${defaultWindow}s`)} ${dim("\u2014")} ${wildcard ? describeWaitLadder(wildcard) : `ping your phone ${defaultWindow}s, then ask in the terminal`}`);
1041
+ const defaultLadder = describeWaitLadder(wildcard ?? { tool: "*", mode: "push_first", pushFirstSeconds: defaultWindow, timeoutSeconds: 0, timeoutAction: "escalate" });
1042
+ console.log(` ${dim("\xB7")} default: ${cyan(`${defaultWindow}s`)} ${dim("\xB7")} ${defaultLadder}`);
1004
1043
  if (policy.modeOverride) {
1005
1044
  console.log(` ${warn} mode override active: ${cyan(policy.modeOverride)} ${dim("(pushary mode status)")}`);
1006
1045
  }
@@ -1013,7 +1052,19 @@ var main = async () => {
1013
1052
  } catch {
1014
1053
  }
1015
1054
  }
1055
+ if (options.activation) {
1056
+ activation = apiKey ? await readHookActivation(apiKey, options.activation, options.activationTarget) : { kind: "unavailable", detail: "Sign in before reading this workspace\u2019s hook evidence." };
1057
+ const latest = activation.kind === "ok" ? activation.report.evidence[0] : void 0;
1058
+ hookActivation = hookActivationResult(activation.kind === "ok" ? activation.report : null);
1059
+ const observed = hookActivation === "approved_executed" || hookActivation === "denied_observed";
1060
+ check(observed, "Approval for the selected scratch file (last 10 minutes)", activation.kind !== "ok" ? activation.detail : activation.report.truncated ? "Too many recent decisions to establish a unique correlation. Start a quiet test session and retry." : activation.report.evidence.length > 1 ? `${hookActivation}: multiple decisions for this target. Use a new scratch file for each attempt; inspect hookActivationEvidence for all results.` : latest ? `${latest.result}; answer source: ${latest.answerSource ?? "unknown"}; decision ${latest.correlationId}; session ${latest.sessionId}; tool call ${latest.toolUseId}` : "Unverified: no recent runtime-tagged Write approval for this scratch file on this machine.");
1061
+ const guide = AGENTS[options.activation].activationGuide;
1062
+ console.log(` ${dim(`Real-agent check: https://pushary.com${guide}`)}`);
1063
+ }
1064
+ const managedControl = readManagedControlEvidence(apiKey, configuredManagedProviders, serverProof);
1065
+ console.log(` ${dim(`Phone-start and steering: ${managedControl.ready ? "local and server checks passed for this run" : "unverified; hook approvals are a separate capability"}.`)}`);
1016
1066
  console.log();
1067
+ if (!options.activation) console.log(` ${dim("Agent hook activation is unverified by these checks. Use --activation claude_code --activation-target <unique scratch file> after one real approval.")}`);
1017
1068
  const failed = results.filter((r) => !r.passed);
1018
1069
  if (failed.length === 0) {
1019
1070
  console.log(` ${green(bold("All checks passed."))}`);
@@ -1050,18 +1101,22 @@ var main = async () => {
1050
1101
  }
1051
1102
  }
1052
1103
  if (options.json) {
1053
- process.stdout.write(`${JSON.stringify({
1104
+ await new Promise((resolve2, reject) => process.stdout.write(`${JSON.stringify({
1054
1105
  ok: exitCode === EXIT.OK,
1055
1106
  exitCode,
1056
1107
  keyCheck: keyVerdict?.kind ?? null,
1108
+ roundTripEvidence: roundTripEvidence(roundTrip),
1109
+ hookActivation,
1110
+ hookActivationEvidence: activation,
1057
1111
  daemon,
1112
+ managedControl,
1058
1113
  daemonNeedsLogin: loginBlock.kind === "blocked" ? loginBlock.record : null,
1059
1114
  passed: results.filter((r) => r.passed).length,
1060
1115
  failed: failed.length,
1061
1116
  checks: results.map((r) => ({ label: r.label, passed: r.passed, detail: r.detail ?? null })),
1062
1117
  otherHolders: otherHoldersByAgent
1063
1118
  }, null, 2)}
1064
- `);
1119
+ `, (error) => error ? reject(error) : resolve2()));
1065
1120
  }
1066
1121
  process.exit(exitCode);
1067
1122
  };
@@ -1,43 +1,43 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readHookInput
4
- } from "../chunk-OEKFD5BC.js";
4
+ } from "../chunk-IZVK6FKD.js";
5
5
  import {
6
6
  isDeferAnswer
7
7
  } from "../chunk-YHG74UFF.js";
8
- import "../chunk-4RF52LSU.js";
9
- import "../chunk-BMANRGJQ.js";
10
- import "../chunk-VOSGZ6PH.js";
11
- import "../chunk-2ZTL3PGN.js";
12
- import "../chunk-CUEZMPLJ.js";
13
- import "../chunk-HBHEX3JQ.js";
14
- import "../chunk-XLOELLAS.js";
15
- import "../chunk-2D7BAOMY.js";
8
+ import "../chunk-HAXZ6CBF.js";
9
+ import "../chunk-EMGGUO2Q.js";
10
+ import "../chunk-WPS5IM7B.js";
11
+ import "../chunk-XZWUOCJZ.js";
12
+ import "../chunk-QJPYJVL3.js";
13
+ import "../chunk-GQRU4KJF.js";
14
+ import "../chunk-VMZ4SWQD.js";
15
+ import "../chunk-7RNY3UHJ.js";
16
16
  import "../chunk-GOPIMNVC.js";
17
17
  import "../chunk-3AJIK5AO.js";
18
- import "../chunk-4PX4QK2Z.js";
18
+ import "../chunk-SKPXQRKN.js";
19
19
  import "../chunk-EQRS3SGM.js";
20
- import "../chunk-OI3DJA7N.js";
20
+ import "../chunk-VERXHIQF.js";
21
21
  import "../chunk-XHKBHWLX.js";
22
22
  import "../chunk-L4ZCFMWW.js";
23
+ import "../chunk-XUTSZYYJ.js";
23
24
  import {
24
25
  askUser,
25
26
  isDecisionStopped,
26
27
  waitForQuestion
27
28
  } from "../chunk-XS33QBJA.js";
28
- import "../chunk-IVQMNLSA.js";
29
29
  import {
30
30
  getMachineId
31
- } from "../chunk-N4VNLISN.js";
32
- import "../chunk-OW6AZDY2.js";
31
+ } from "../chunk-4OSQZ5TB.js";
32
+ import "../chunk-BGX4QUQF.js";
33
33
  import "../chunk-GMXKITVA.js";
34
34
  import "../chunk-DINDL2CF.js";
35
- import "../chunk-S5CMLR35.js";
35
+ import "../chunk-VMQU7YEJ.js";
36
36
  import "../chunk-EYRGBYZG.js";
37
37
  import "../chunk-SAF6HGAA.js";
38
38
  import "../chunk-7QLSKOSU.js";
39
39
  import "../chunk-KZERVKTD.js";
40
- import "../chunk-KAVDAZ7A.js";
40
+ import "../chunk-L7NOG547.js";
41
41
  import {
42
42
  getApiKey
43
43
  } from "../chunk-2UMNXADU.js";
@@ -51,7 +51,7 @@ import {
51
51
  planElicitationAsk,
52
52
  readElicitationFields,
53
53
  renderElicitationOutput
54
- } from "../chunk-WDKHZRAS.js";
54
+ } from "../chunk-MC5WUOJJ.js";
55
55
 
56
56
  // bin/pushary-elicitation-hook.ts
57
57
  import { basename } from "path";
@@ -5,19 +5,19 @@ import {
5
5
  postLiveSession,
6
6
  postLiveSessionUntilAccepted,
7
7
  startLiveSessionHeartbeat
8
- } from "../chunk-QHWXUNMN.js";
8
+ } from "../chunk-FRXZMCVA.js";
9
9
  import {
10
10
  SESSION_IDLE_TIMEOUT_MS,
11
11
  describeIdleWindow,
12
12
  idleDeadlinePassed,
13
13
  startProcessOwnership,
14
14
  withSpawnProcessOwnership
15
- } from "../chunk-QQGNFHIK.js";
16
- import "../chunk-4PX4QK2Z.js";
15
+ } from "../chunk-PQGLDN4V.js";
16
+ import "../chunk-SKPXQRKN.js";
17
17
  import "../chunk-EQRS3SGM.js";
18
18
  import {
19
19
  transcriptsEnabled
20
- } from "../chunk-OI3DJA7N.js";
20
+ } from "../chunk-VERXHIQF.js";
21
21
  import {
22
22
  spawnClaude
23
23
  } from "../chunk-XHKBHWLX.js";
@@ -26,15 +26,15 @@ import {
26
26
  } from "../chunk-L4ZCFMWW.js";
27
27
  import {
28
28
  getMachineId
29
- } from "../chunk-N4VNLISN.js";
30
- import "../chunk-OW6AZDY2.js";
31
- import "../chunk-S5CMLR35.js";
32
- import "../chunk-KAVDAZ7A.js";
29
+ } from "../chunk-4OSQZ5TB.js";
30
+ import "../chunk-BGX4QUQF.js";
31
+ import "../chunk-VMQU7YEJ.js";
32
+ import "../chunk-L7NOG547.js";
33
33
  import {
34
34
  getApiKey,
35
35
  getBaseUrl
36
36
  } from "../chunk-2UMNXADU.js";
37
- import "../chunk-WDKHZRAS.js";
37
+ import "../chunk-MC5WUOJJ.js";
38
38
 
39
39
  // bin/pushary-gemini-bridge.ts
40
40
  import { existsSync } from "fs";
@@ -2,27 +2,27 @@
2
2
  import {
3
3
  isGatingMoment,
4
4
  recordKeylessMoment
5
- } from "../chunk-UCS65VVR.js";
5
+ } from "../chunk-5GKNAQTG.js";
6
6
  import {
7
7
  readHookInput
8
- } from "../chunk-OEKFD5BC.js";
8
+ } from "../chunk-IZVK6FKD.js";
9
9
  import {
10
10
  denyReasonFrom,
11
11
  isDeferAnswer
12
12
  } from "../chunk-YHG74UFF.js";
13
- import "../chunk-4RF52LSU.js";
14
- import "../chunk-BMANRGJQ.js";
15
- import "../chunk-VOSGZ6PH.js";
16
- import "../chunk-2ZTL3PGN.js";
17
- import "../chunk-CUEZMPLJ.js";
18
- import "../chunk-HBHEX3JQ.js";
19
- import "../chunk-XLOELLAS.js";
20
- import "../chunk-2D7BAOMY.js";
13
+ import "../chunk-HAXZ6CBF.js";
14
+ import "../chunk-EMGGUO2Q.js";
15
+ import "../chunk-WPS5IM7B.js";
16
+ import "../chunk-XZWUOCJZ.js";
17
+ import "../chunk-QJPYJVL3.js";
18
+ import "../chunk-GQRU4KJF.js";
19
+ import "../chunk-VMZ4SWQD.js";
20
+ import "../chunk-7RNY3UHJ.js";
21
21
  import "../chunk-GOPIMNVC.js";
22
22
  import "../chunk-3AJIK5AO.js";
23
- import "../chunk-4PX4QK2Z.js";
23
+ import "../chunk-SKPXQRKN.js";
24
24
  import "../chunk-EQRS3SGM.js";
25
- import "../chunk-OI3DJA7N.js";
25
+ import "../chunk-VERXHIQF.js";
26
26
  import "../chunk-XHKBHWLX.js";
27
27
  import "../chunk-L4ZCFMWW.js";
28
28
  import {
@@ -38,7 +38,16 @@ import {
38
38
  readLastUserPrompt,
39
39
  repoKeyFor,
40
40
  reportEvent
41
- } from "../chunk-HCMM3BOW.js";
41
+ } from "../chunk-XJQOVWBQ.js";
42
+ import {
43
+ UNNAMED_ACTION,
44
+ deriveActionBody,
45
+ deriveBlocker,
46
+ deriveToolTarget,
47
+ describeToolCall,
48
+ scopePathFor
49
+ } from "../chunk-Z4JVEL7R.js";
50
+ import "../chunk-XUTSZYYJ.js";
42
51
  import {
43
52
  DECISION_STOP_REASON,
44
53
  askUser,
@@ -47,27 +56,18 @@ import {
47
56
  sendNotification,
48
57
  waitForQuestion
49
58
  } from "../chunk-XS33QBJA.js";
50
- import {
51
- UNNAMED_ACTION,
52
- deriveActionBody,
53
- deriveBlocker,
54
- deriveToolTarget,
55
- describeToolCall,
56
- scopePathFor
57
- } from "../chunk-BDI2MXTT.js";
58
- import "../chunk-IVQMNLSA.js";
59
59
  import {
60
60
  getMachineId
61
- } from "../chunk-N4VNLISN.js";
62
- import "../chunk-OW6AZDY2.js";
61
+ } from "../chunk-4OSQZ5TB.js";
62
+ import "../chunk-BGX4QUQF.js";
63
63
  import "../chunk-GMXKITVA.js";
64
64
  import "../chunk-DINDL2CF.js";
65
- import "../chunk-S5CMLR35.js";
65
+ import "../chunk-VMQU7YEJ.js";
66
66
  import "../chunk-EYRGBYZG.js";
67
67
  import "../chunk-SAF6HGAA.js";
68
68
  import "../chunk-7QLSKOSU.js";
69
69
  import "../chunk-KZERVKTD.js";
70
- import "../chunk-KAVDAZ7A.js";
70
+ import "../chunk-L7NOG547.js";
71
71
  import {
72
72
  getApiKey
73
73
  } from "../chunk-2UMNXADU.js";
@@ -79,7 +79,7 @@ import {
79
79
  hookWaitClamped,
80
80
  hookWaitDeadline,
81
81
  resolveGate
82
- } from "../chunk-WDKHZRAS.js";
82
+ } from "../chunk-MC5WUOJJ.js";
83
83
 
84
84
  // bin/pushary-gemini-hook.ts
85
85
  import { basename } from "path";
@@ -115,6 +115,30 @@ var toGeminiPolicyLookup = (toolName, toolInput, cwd, mcpContext) => {
115
115
  var geminiAllow = () => ({ kind: "allow" });
116
116
  var geminiDeny = (reason) => ({ kind: "deny", reason });
117
117
  var geminiPass = () => ({ kind: "pass" });
118
+ var NO_LOCAL_INPUT = "This unattended Gemini session has no local approval input. Use an interactive session or Every time.";
119
+ var NO_ANSWER_IN_TIME = "No answer from your phone in time, and this unattended Gemini session has no local approval input. Retry when you can answer on your phone.";
120
+ var HANDLED_ON_MACHINE = "You chose to handle this on your machine, but this unattended Gemini session has no local approval input. Use an interactive session.";
121
+ var NO_DEVICE = "No phone or other notification channel is connected to Pushary, and this unattended Gemini session has no local approval input. Connect one in Pushary, then retry.";
122
+ var geminiLocalHandoff = (bridge, reason = NO_LOCAL_INPUT) => geminiOwnsLifecycle(bridge) ? geminiPass() : geminiDeny(reason);
123
+ var geminiPushOnlyHandoff = (askStatus, bridge) => askStatus === "notified" || askStatus === "terminal" ? geminiLocalHandoff(bridge) : geminiPass();
124
+ var fromPhoneAnswer = (answer) => answer.value === "yes" ? geminiAllow() : geminiDeny(denyReasonFrom(answer.value, answer.note));
125
+ var geminiPushFirstDecision = (pushed, bridge) => {
126
+ if (!pushed) return geminiPass();
127
+ const { answer, localHandoff } = pushed;
128
+ if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
129
+ if (localHandoff) return geminiLocalHandoff(bridge, pushed.noDevices ? NO_DEVICE : NO_LOCAL_INPUT);
130
+ if (answer.answered) return isDeferAnswer(answer.value) ? geminiLocalHandoff(bridge, HANDLED_ON_MACHINE) : fromPhoneAnswer(answer);
131
+ return geminiLocalHandoff(bridge, NO_ANSWER_IN_TIME);
132
+ };
133
+ var geminiPushOnlyDecision = (pushed, timeoutAction, bridge) => {
134
+ if (!pushed) return geminiDeny("Pushary could not create a verifiable approval. Retry the action.");
135
+ const { answer, localHandoff, clampCut, askStatus } = pushed;
136
+ if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
137
+ if (localHandoff) return geminiPushOnlyHandoff(askStatus, bridge);
138
+ if (answer.answered) return isDeferAnswer(answer.value) ? geminiPass() : fromPhoneAnswer(answer);
139
+ if (clampCut) return geminiPass();
140
+ return geminiTimeoutDecision(timeoutAction);
141
+ };
118
142
  var toGeminiWire = (decision) => {
119
143
  if (decision.kind === "pass") return null;
120
144
  return gateOutputFor(
@@ -150,12 +174,18 @@ var pushQuestion = async (apiKey, input, lookup, waitSeconds, mode, pollInterval
150
174
  blocker: deriveBlocker(mode),
151
175
  actionBody: deriveActionBody(input.tool_name ?? "", input.tool_input ?? {}),
152
176
  repoKey: repoKeyFor(input.cwd)
153
- }).catch(() => ({ correlationId: "", status: "unavailable" }));
177
+ });
154
178
  const now = Date.now();
155
179
  const deadline = hookWaitDeadline(START_MS, waitSeconds, "gemini", now);
156
180
  const clampCut = hookWaitClamped(START_MS, waitSeconds, "gemini", now);
157
181
  const answer = await waitForQuestion(apiKey, result, deadline, pollInterval);
158
- return { answer, localHandoff: !answer.answered && isLocalQuestionHandoff(result), clampCut };
182
+ return {
183
+ answer,
184
+ localHandoff: !answer.answered && isLocalQuestionHandoff(result),
185
+ clampCut,
186
+ askStatus: result.status,
187
+ noDevices: result.noDevices === true
188
+ };
159
189
  };
160
190
  var notifyApprovalNeeded = async (apiKey, input, lookup) => {
161
191
  try {
@@ -174,33 +204,18 @@ var handlePushOnly = async (apiKey, input, lookup, timeoutSeconds, timeoutAction
174
204
  try {
175
205
  pushed = await pushQuestion(apiKey, input, lookup, effectiveWaitSeconds(timeoutAction, timeoutSeconds, "gemini"), "push_only");
176
206
  } catch {
177
- return geminiDeny("Pushary could not create a verifiable approval. Retry the action.");
178
- }
179
- const { answer, localHandoff, clampCut } = pushed;
180
- if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
181
- if (localHandoff) return geminiPass();
182
- if (answer.answered) {
183
- if (isDeferAnswer(answer.value)) return geminiPass();
184
- return answer.value === "yes" ? geminiAllow() : geminiDeny(denyReasonFrom(answer.value, answer.note));
207
+ pushed = void 0;
185
208
  }
186
- if (clampCut) return geminiPass();
187
- return geminiTimeoutDecision(timeoutAction);
209
+ return geminiPushOnlyDecision(pushed, timeoutAction, process.env.PUSHARY_GEMINI_BRIDGE);
188
210
  };
189
211
  var handlePushFirst = async (apiKey, input, lookup, pushFirstSeconds) => {
190
212
  let pushed;
191
213
  try {
192
214
  pushed = await pushQuestion(apiKey, input, lookup, pushFirstSeconds, "push_first", 1500);
193
215
  } catch {
194
- return geminiPass();
216
+ pushed = void 0;
195
217
  }
196
- const { answer, localHandoff } = pushed;
197
- if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
198
- if (localHandoff) return geminiPass();
199
- if (answer.answered) {
200
- if (isDeferAnswer(answer.value)) return geminiPass();
201
- return answer.value === "yes" ? geminiAllow() : geminiDeny(denyReasonFrom(answer.value, answer.note));
202
- }
203
- return geminiPass();
218
+ return geminiPushFirstDecision(pushed, process.env.PUSHARY_GEMINI_BRIDGE);
204
219
  };
205
220
  var decideBeforeTool = async (input) => {
206
221
  let apiKey;
@@ -246,12 +261,12 @@ var decideBeforeTool = async (input) => {
246
261
  case "push_only":
247
262
  return handlePushOnly(apiKey, input, lookup, toolPolicy.timeoutSeconds, toolPolicy.timeoutAction);
248
263
  case "terminal_only":
249
- return geminiPass();
264
+ return geminiLocalHandoff(process.env.PUSHARY_GEMINI_BRIDGE);
250
265
  case "push_first":
251
266
  return handlePushFirst(apiKey, input, lookup, toolPolicy.pushFirstSeconds);
252
267
  case "notify_only":
253
268
  await notifyApprovalNeeded(apiKey, input, lookup);
254
- return geminiPass();
269
+ return geminiLocalHandoff(process.env.PUSHARY_GEMINI_BRIDGE);
255
270
  default:
256
271
  return handlePushFirst(apiKey, input, lookup, toolPolicy.pushFirstSeconds);
257
272
  }