@pushary/agent-hooks 0.89.13 → 0.90.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/data/SKILL.md +1 -1
  3. package/data/cursor-plugin/skills/pushary/SKILL.md +1 -1
  4. package/data/vscode-plugin/skills/pushary/SKILL.md +1 -1
  5. package/dist/bin/pushary-bell-hook.js +4 -4
  6. package/dist/bin/pushary-bell.js +8 -8
  7. package/dist/bin/pushary-claude.js +72 -21
  8. package/dist/bin/pushary-clean.js +16 -42
  9. package/dist/bin/pushary-codex-bridge.js +37 -12
  10. package/dist/bin/pushary-codex-hook.js +45 -12
  11. package/dist/bin/pushary-codex.js +7 -7
  12. package/dist/bin/pushary-connect.js +11 -10
  13. package/dist/bin/pushary-daemon.js +186 -62
  14. package/dist/bin/pushary-disconnect.js +8 -8
  15. package/dist/bin/pushary-doctor.js +108 -25
  16. package/dist/bin/pushary-elicitation-hook.js +4 -4
  17. package/dist/bin/pushary-gemini-bridge.js +33 -11
  18. package/dist/bin/pushary-gemini-hook.js +21 -10
  19. package/dist/bin/pushary-hook.js +13 -13
  20. package/dist/bin/pushary-login.js +18 -7
  21. package/dist/bin/pushary-logout.js +6 -6
  22. package/dist/bin/pushary-mode.js +28 -21
  23. package/dist/bin/pushary-notification-hook.js +7 -7
  24. package/dist/bin/pushary-opencode-hook.js +8 -8
  25. package/dist/bin/pushary-permission-denied-hook.js +11 -11
  26. package/dist/bin/pushary-permission-hook.js +11 -11
  27. package/dist/bin/pushary-post-hook.js +14 -8
  28. package/dist/bin/pushary-prompt-hook.js +7 -7
  29. package/dist/bin/pushary-session-end-hook.js +7 -7
  30. package/dist/bin/pushary-session-start-hook.js +7 -7
  31. package/dist/bin/pushary-setup.js +168 -45
  32. package/dist/bin/pushary-stats.js +4 -4
  33. package/dist/bin/pushary-status.js +11 -10
  34. package/dist/bin/pushary-stop-hook.js +14 -8
  35. package/dist/bin/pushary-stopfailure-hook.js +7 -7
  36. package/dist/bin/pushary-suggestions.js +2 -2
  37. package/dist/bin/pushary-transcripts.js +6 -6
  38. package/dist/bin/pushary-upgrade.js +15 -15
  39. package/dist/bin/pushary-wait.js +5 -5
  40. package/dist/bin/pushary.js +1 -1
  41. package/dist/{chunk-B2HND22S.js → chunk-2LAHXHBR.js} +1 -1
  42. package/dist/{chunk-GMJDQDXU.js → chunk-43GMYSS4.js} +1 -0
  43. package/dist/{chunk-JTBDBKHU.js → chunk-54ELC7D7.js} +1 -1
  44. package/dist/{chunk-C56VHSXU.js → chunk-5NAZ7SFS.js} +1 -1
  45. package/dist/{chunk-4IPSMEGT.js → chunk-7FE2KABY.js} +12 -2
  46. package/dist/{chunk-RAEPAUNJ.js → chunk-7TPQEWUZ.js} +1 -1
  47. package/dist/{chunk-ZO43JTT3.js → chunk-AJYV3VGU.js} +1 -1
  48. package/dist/{chunk-QFJZHVVL.js → chunk-AZPIQG4G.js} +35 -1
  49. package/dist/{chunk-5MNTWAAJ.js → chunk-B7UZHEBL.js} +29 -22
  50. package/dist/{chunk-MNTZKFZI.js → chunk-BQBEH6TV.js} +5 -5
  51. package/dist/{chunk-WHDSJ6PD.js → chunk-BVBEURWJ.js} +1 -1
  52. package/dist/{chunk-ZWEKONU6.js → chunk-CELX75MW.js} +1 -1
  53. package/dist/{chunk-ZP3SNUP7.js → chunk-CNPAT3HL.js} +3 -2
  54. package/dist/{chunk-YXT55PYF.js → chunk-DYP6SMOR.js} +1 -1
  55. package/dist/chunk-DZDI62LY.js +145 -0
  56. package/dist/{chunk-VF3RTVWY.js → chunk-EZIEXHYM.js} +15 -7
  57. package/dist/{chunk-7JZCADBR.js → chunk-F2ICEWZL.js} +101 -19
  58. package/dist/{chunk-VASZ6TWW.js → chunk-FQL5E6NG.js} +2 -2
  59. package/dist/{chunk-JHJHCMAF.js → chunk-FXEVRLHC.js} +20 -2
  60. package/dist/{chunk-JVLB5CAJ.js → chunk-GL3FFM7A.js} +7 -4
  61. package/dist/{chunk-5YEF55AX.js → chunk-H2QZRQR2.js} +1 -1
  62. package/dist/{chunk-S4BLNILH.js → chunk-I463AEG3.js} +1 -1
  63. package/dist/{chunk-ANMHIZZY.js → chunk-JW4KO5AF.js} +1 -1
  64. package/dist/{chunk-3YGGEDJK.js → chunk-KUZWXPUS.js} +3 -2
  65. package/dist/{chunk-N4QJ3CLX.js → chunk-L4E2BIJ2.js} +80 -7
  66. package/dist/{chunk-NBDU3R3G.js → chunk-OSALSIUF.js} +1 -1
  67. package/dist/{chunk-KNLKHJZR.js → chunk-PYA4FLKI.js} +32 -21
  68. package/dist/chunk-QCGNGEGE.js +15 -0
  69. package/dist/chunk-S6LYP7QA.js +127 -0
  70. package/dist/{chunk-OX35BSPV.js → chunk-TDZFPMSZ.js} +70 -11
  71. package/dist/{chunk-M3UABYRT.js → chunk-UZNKVRLE.js} +1 -1
  72. package/dist/{chunk-FOGW6O2Y.js → chunk-VHWCVD42.js} +1 -1
  73. package/dist/{chunk-QINYXLA5.js → chunk-VNMKIJZB.js} +15 -2
  74. package/dist/{chunk-CBGGUEKE.js → chunk-Y6BWPERV.js} +90 -7
  75. package/dist/src/index.d.ts +3 -0
  76. package/dist/src/index.js +11 -11
  77. package/package.json +1 -1
  78. package/dist/chunk-MSTHT2M7.js +0 -72
@@ -3,6 +3,10 @@ import {
3
3
  openSealedKey,
4
4
  publicKeyFingerprint
5
5
  } from "./chunk-3EGEA4KH.js";
6
+ import {
7
+ humanIsTty,
8
+ writeHuman
9
+ } from "./chunk-QCGNGEGE.js";
6
10
  import {
7
11
  useColor
8
12
  } from "./chunk-5RUIFDTP.js";
@@ -11,7 +15,7 @@ import {
11
15
  } from "./chunk-BSZYIAZL.js";
12
16
  import {
13
17
  reportedInstallSource
14
- } from "./chunk-ANMHIZZY.js";
18
+ } from "./chunk-JW4KO5AF.js";
15
19
 
16
20
  // src/reach.ts
17
21
  var reachVerdict = (channels) => {
@@ -263,16 +267,6 @@ var describeRoundTrip = (outcome) => {
263
267
  }
264
268
  };
265
269
 
266
- // src/cli/human.ts
267
- var stream = process.stdout;
268
- var setHumanStream = (target) => {
269
- stream = target;
270
- };
271
- var writeHuman = (text) => {
272
- stream.write(text);
273
- };
274
- var humanIsTty = () => stream === process.stdout && process.stdout.isTTY === true;
275
-
276
270
  // src/onboarding.ts
277
271
  import qrcodeTerminal from "qrcode-terminal";
278
272
  var paint = (open) => (s) => useColor() ? `${open}${s}\x1B[0m` : s;
@@ -650,6 +644,20 @@ var confirmAppConnection = async (apiKey) => {
650
644
  }
651
645
  return sendAndConfirm(apiKey, channels);
652
646
  };
647
+ var planAppConnect = (channels) => channels.precise && ownedApp(channels) === 0 ? { kind: "unpaired", repair: describeAppRepair(reachVerdict(channels)) ?? [] } : { kind: "send" };
648
+ var connectExistingApp = async (apiKey) => {
649
+ const probe = await fetchChannels(apiKey);
650
+ if (probe.kind !== "ok") return describeProbeFailure(probe);
651
+ const plan = planAppConnect(probe.status);
652
+ if (plan.kind === "send") {
653
+ console.log(` ${check} ${describeExistingConnection(probe.status)}`);
654
+ return sendAndConfirm(apiKey, probe.status);
655
+ }
656
+ console.log(` ${yellow("!")} No Pushary app of yours is connected to this site, and nothing was paired just now.`);
657
+ for (const line of plan.repair) console.log(` ${dim(line)}`);
658
+ console.log(` ${dim("No app yet? Get it at")} ${cyan("https://pushary.com/download")}`);
659
+ return phoneFailure("app_not_paired", plan.repair);
660
+ };
653
661
  var printConnectInstructions = async (apiKey) => {
654
662
  const probe = await fetchChannels(apiKey);
655
663
  if (probe.kind !== "ok") return describeProbeFailure(probe);
@@ -737,6 +745,8 @@ var ackPairing = async (pairId) => {
737
745
  } catch {
738
746
  }
739
747
  };
748
+ var pairFailed = (reason) => ({ kind: "failed", reason });
749
+ var pairingEscapeApplies = (outcome) => outcome.reason === "timeout" || outcome.reason === "expired" || outcome.reason === "cancelled";
740
750
  var buildPairLinks = (pairId, publicKeyB64, baseUrl = apiBase(), code = null) => {
741
751
  const id = encodeURIComponent(pairId);
742
752
  const pk = encodeURIComponent(publicKeyB64);
@@ -760,7 +770,7 @@ var connectViaAppPairing = async (options = {}) => {
760
770
  const started = await startPairing(keypair.publicKeyB64);
761
771
  if (!started) {
762
772
  console.log(` ${yellow("!")} Couldn't start pairing. Check your connection and try again.`);
763
- return null;
773
+ return pairFailed("unreachable");
764
774
  }
765
775
  const { pairId, code } = started;
766
776
  const { qrLink, universalLink, deepLink } = buildPairLinks(pairId, keypair.publicKeyB64, apiBase(), code);
@@ -800,14 +810,14 @@ var connectViaAppPairing = async (options = {}) => {
800
810
  let consecutiveFailures = 0;
801
811
  try {
802
812
  while (Date.now() < deadline) {
803
- if (cancelled) return null;
813
+ if (cancelled) return pairFailed("cancelled");
804
814
  const claim = await claimPairing(pairId);
805
815
  if (claim === null) {
806
816
  consecutiveFailures += 1;
807
817
  if (consecutiveFailures >= PAIR_MAX_CONSECUTIVE_FAILURES) {
808
818
  stop(yellow("!"), "Can't reach pushary.com to finish pairing. Check your connection, then re-run setup.");
809
819
  await cancelPairing(pairId);
810
- return null;
820
+ return pairFailed("unreachable");
811
821
  }
812
822
  await sleep(PAIR_POLL_INTERVAL_MS);
813
823
  continue;
@@ -820,29 +830,29 @@ var connectViaAppPairing = async (options = {}) => {
820
830
  } catch {
821
831
  stop(yellow("!"), "Pairing failed to decrypt \u2014 re-run setup to try again");
822
832
  await cancelPairing(pairId);
823
- return null;
833
+ return pairFailed("decrypt");
824
834
  }
825
835
  await ackPairing(pairId);
826
836
  stop(check, "App connected");
827
- return { apiKey, handle: claim.siteSlug };
837
+ return { kind: "paired", apiKey, handle: claim.siteSlug };
828
838
  }
829
839
  if (claim?.status === "expired") {
830
840
  stop(yellow("!"), "Pairing expired \u2014 re-run setup to try again");
831
- return null;
841
+ return pairFailed("expired");
832
842
  }
833
843
  if (claim?.status === "failed") {
834
844
  stop(yellow("!"), describePairFailure(claim.reason));
835
- return null;
845
+ return pairFailed("rejected");
836
846
  }
837
847
  await sleep(PAIR_POLL_INTERVAL_MS);
838
848
  }
839
849
  stop(yellow("!"), "Timed out waiting for the app");
840
850
  await cancelPairing(pairId);
841
- return null;
851
+ return pairFailed("timeout");
842
852
  } catch (err) {
843
853
  stop(yellow("!"), `Pairing error ${dim(`(${err instanceof Error ? err.message : "network"})`)}`);
844
854
  await cancelPairing(pairId);
845
- return null;
855
+ return pairFailed("unreachable");
846
856
  } finally {
847
857
  process.off("SIGINT", onInterrupt);
848
858
  }
@@ -853,10 +863,11 @@ export {
853
863
  describeReach,
854
864
  verifyRoundTrip,
855
865
  describeRoundTrip,
856
- setHumanStream,
857
866
  fetchChannels,
858
867
  connectDevice,
859
868
  confirmAppConnection,
869
+ connectExistingApp,
860
870
  printConnectInstructions,
871
+ pairingEscapeApplies,
861
872
  connectViaAppPairing
862
873
  };
@@ -0,0 +1,15 @@
1
+ // src/cli/human.ts
2
+ var stream = process.stdout;
3
+ var setHumanStream = (target) => {
4
+ stream = target;
5
+ };
6
+ var writeHuman = (text) => {
7
+ stream.write(text);
8
+ };
9
+ var humanIsTty = () => stream === process.stdout && process.stdout.isTTY === true;
10
+
11
+ export {
12
+ setHumanStream,
13
+ writeHuman,
14
+ humanIsTty
15
+ };
@@ -0,0 +1,127 @@
1
+ import {
2
+ HOOK_LOCATOR_BINARY
3
+ } from "./chunk-VNMKIJZB.js";
4
+
5
+ // src/diagnostics/bridge.ts
6
+ import { execFileSync } from "child_process";
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { homedir } from "os";
9
+ import { dirname, join } from "path";
10
+ var BRIDGE_BUNDLE_IDENTIFIER = "com.pushary.app";
11
+ var BRIDGE_HELPER_SUFFIX = "/Contents/Helpers/pushary-bridge";
12
+ var BRIDGE_BUNDLE_ROOTS = ["/Applications/Pushary.app", "$HOME/Applications/Pushary.app"];
13
+ var expandHome = (path, home) => path.replaceAll("$HOME", home);
14
+ var parseBridgeStub = (contents, stubPath) => {
15
+ const helperSuffix = contents.match(/^H=(\S+)$/m)?.[1] ?? BRIDGE_HELPER_SUFFIX;
16
+ const cachePath = contents.match(/^C="([^"\n]+)"$/m)?.[1] ?? join(dirname(stubPath), ".bridge-cache");
17
+ const rootsLine = contents.match(/^for P in ((?:"[^"\n]*"\s*)+); do$/m)?.[1];
18
+ const bundleRoots = rootsLine ? [...rootsLine.matchAll(/"([^"]*)"/g)].map((match) => match[1]) : BRIDGE_BUNDLE_ROOTS;
19
+ const bundleIdentifier = contents.match(/kMDItemCFBundleIdentifier == "([^"]+)"/)?.[1] ?? BRIDGE_BUNDLE_IDENTIFIER;
20
+ return { helperSuffix, cachePath, bundleRoots, bundleIdentifier };
21
+ };
22
+ var resolveBridgeStub = (stubPath, probe) => {
23
+ const contents = probe.readFile(stubPath);
24
+ if (contents === null) return { kind: "no-stub", stub: stubPath };
25
+ const shape = parseBridgeStub(contents, stubPath);
26
+ const helperIn = (bundle) => `${bundle}${shape.helperSuffix}`;
27
+ const looked = [];
28
+ for (const root of shape.bundleRoots.map((root2) => expandHome(root2, probe.home))) {
29
+ looked.push(root);
30
+ if (probe.exists(helperIn(root))) return { kind: "bundle", helper: helperIn(root), via: "root" };
31
+ }
32
+ const cached = shape.cachePath ? probe.readFile(shape.cachePath)?.split("\n")[0]?.trim() : void 0;
33
+ if (cached) {
34
+ looked.push(cached);
35
+ if (probe.exists(helperIn(cached))) return { kind: "bundle", helper: helperIn(cached), via: "cache" };
36
+ }
37
+ for (const found of probe.mdfind(shape.bundleIdentifier)) {
38
+ looked.push(found);
39
+ if (probe.exists(helperIn(found))) return { kind: "bundle", helper: helperIn(found), via: "spotlight" };
40
+ }
41
+ return { kind: "missing", stub: stubPath, looked };
42
+ };
43
+ var BRIDGE_GONE_MESSAGE = "the Mac app is gone but its hooks remain; run pushary setup or reinstall the app";
44
+ var describeBridgeResolution = (resolution) => {
45
+ switch (resolution.kind) {
46
+ case "bundle":
47
+ return `${resolution.helper} (${resolution.via === "root" ? "installed" : resolution.via === "cache" ? "cached path" : "found by Spotlight"})`;
48
+ case "missing":
49
+ return BRIDGE_GONE_MESSAGE;
50
+ case "no-stub":
51
+ return `${resolution.stub} is not there; ${BRIDGE_GONE_MESSAGE}`;
52
+ }
53
+ };
54
+ var isBridgeLocator = (executable) => executable !== null && executable.split(/[/\\]/).pop() === HOOK_LOCATOR_BINARY;
55
+ var spotlight = (bundleIdentifier) => {
56
+ if (process.platform !== "darwin") return [];
57
+ try {
58
+ return execFileSync("/usr/bin/mdfind", [`kMDItemCFBundleIdentifier == "${bundleIdentifier}"`], {
59
+ encoding: "utf-8",
60
+ timeout: 5e3,
61
+ stdio: ["ignore", "pipe", "ignore"]
62
+ }).split("\n").map((line) => line.trim()).filter((line) => line !== "");
63
+ } catch {
64
+ return [];
65
+ }
66
+ };
67
+ var readOrNull = (path) => {
68
+ try {
69
+ return readFileSync(path, "utf-8");
70
+ } catch {
71
+ return null;
72
+ }
73
+ };
74
+ var liveBridgeProbe = () => ({
75
+ exists: existsSync,
76
+ readFile: readOrNull,
77
+ mdfind: spotlight,
78
+ home: homedir()
79
+ });
80
+
81
+ // src/diagnostics/service-runtime.ts
82
+ var unescapeXml = (value) => value.replaceAll("&quot;", '"').replaceAll("&apos;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
83
+ var unquoteSystemd = (value) => value.replaceAll('\\"', '"').replaceAll("\\\\", "\\").replaceAll("%%", "%");
84
+ var readServiceExecPath = (contents, platform) => {
85
+ if (platform === "darwin") {
86
+ const program = contents.match(/<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]*)<\/string>/)?.[1];
87
+ return program ? unescapeXml(program) : null;
88
+ }
89
+ if (platform === "linux") {
90
+ const exec = contents.match(/^ExecStart="((?:[^"\\]|\\.)*)"/m)?.[1];
91
+ return exec ? unquoteSystemd(exec) : null;
92
+ }
93
+ if (platform === "win32") {
94
+ const command = contents.match(/<Command>([^<]*)<\/Command>/)?.[1];
95
+ return command ? unescapeXml(command) : null;
96
+ }
97
+ return null;
98
+ };
99
+ var MANAGER_SEGMENTS = [
100
+ [".nvm", "nvm"],
101
+ ["nvm", "nvm"],
102
+ ["fnm", "fnm"],
103
+ [".fnm", "fnm"],
104
+ [".volta", "volta"],
105
+ ["volta", "volta"],
106
+ [".asdf", "asdf"],
107
+ ["asdf", "asdf"]
108
+ ];
109
+ var versionManagerOf = (execPath) => {
110
+ const segments = execPath.split(/[/\\]/);
111
+ for (const segment of segments) {
112
+ const hit = MANAGER_SEGMENTS.find(([name]) => name === segment);
113
+ if (hit) return hit[1];
114
+ }
115
+ return null;
116
+ };
117
+ var describeVersionManagerPin = (execPath, manager) => `The background service is pinned to ${execPath}, which ${manager} manages. Switching Node versions strands it; run pushary daemon install afterwards.`;
118
+
119
+ export {
120
+ resolveBridgeStub,
121
+ describeBridgeResolution,
122
+ isBridgeLocator,
123
+ liveBridgeProbe,
124
+ readServiceExecPath,
125
+ versionManagerOf,
126
+ describeVersionManagerPin
127
+ };
@@ -4,6 +4,7 @@ import {
4
4
  isDefaultSession,
5
5
  isPolicyConfig,
6
6
  listPendingQuestions,
7
+ listPendingQuestionsForToolUse,
7
8
  markPendingCommandDelivered,
8
9
  readPendingCommand,
9
10
  removePendingCommand,
@@ -12,9 +13,10 @@ import {
12
13
  savePendingCommand,
13
14
  sendNotification,
14
15
  waitForAnswer
15
- } from "./chunk-JHJHCMAF.js";
16
+ } from "./chunk-FXEVRLHC.js";
16
17
  import {
17
18
  AGENT_IDENTITIES,
19
+ compactionAction,
18
20
  deriveReceiptMeta,
19
21
  deriveToolTarget,
20
22
  describeToolCall,
@@ -22,20 +24,22 @@ import {
22
24
  parseApplyPatchFiles,
23
25
  parseApplyPatchOps,
24
26
  sessionEndAction,
27
+ sessionIdentityOf,
25
28
  sessionStartAction,
29
+ subagentStopSummary,
26
30
  summarizeStop,
27
31
  taskTitleFrom,
28
32
  toolResultErrorText
29
- } from "./chunk-N4QJ3CLX.js";
33
+ } from "./chunk-L4E2BIJ2.js";
30
34
  import {
31
35
  getMachineId
32
- } from "./chunk-JTBDBKHU.js";
36
+ } from "./chunk-54ELC7D7.js";
33
37
  import {
34
38
  withRetry
35
39
  } from "./chunk-BSZYIAZL.js";
36
40
  import {
37
41
  reportedInstallSource
38
- } from "./chunk-ANMHIZZY.js";
42
+ } from "./chunk-JW4KO5AF.js";
39
43
  import {
40
44
  getApiKey,
41
45
  getBaseUrl
@@ -51,7 +55,7 @@ import {
51
55
  pendingCommandEvents,
52
56
  resolveAutoResolveOrigin,
53
57
  resolvePolicy
54
- } from "./chunk-QINYXLA5.js";
58
+ } from "./chunk-VNMKIJZB.js";
55
59
 
56
60
  // src/policy.ts
57
61
  import { createHash } from "crypto";
@@ -505,6 +509,15 @@ var permissionTimeoutDecision = (timeoutAction) => {
505
509
  };
506
510
  var preToolUseTimeoutDecision = (timeoutAction, denyReason = "No response within timeout") => timeoutAction === "deny" ? codexDeny(denyReason) : codexPass();
507
511
 
512
+ // src/driver.ts
513
+ var DRIVER_ENV = "PUSHARY_DRIVER";
514
+ var PHONE_DRIVER = "phone";
515
+ var driverFromEnv = (env) => env[DRIVER_ENV] === PHONE_DRIVER ? PHONE_DRIVER : void 0;
516
+ var phoneDrivenEnv = (base) => ({
517
+ ...base,
518
+ [DRIVER_ENV]: PHONE_DRIVER
519
+ });
520
+
508
521
  // src/throttle.ts
509
522
  import { join as join4 } from "path";
510
523
  import { tmpdir as tmpdir3 } from "os";
@@ -552,10 +565,13 @@ var readLastPrompt = (sessionId) => {
552
565
  return void 0;
553
566
  }
554
567
  };
555
- var reconcilePendingQuestions = async (sessionId) => {
568
+ var pendingQuestionsToSettle = (sessionId, toolUseId) => {
569
+ const settledElsewhere = toolUseId ? listPendingQuestionsForToolUse(sessionId, toolUseId) : [];
570
+ return [.../* @__PURE__ */ new Set([...settledElsewhere, ...listPendingQuestions(sessionId)])];
571
+ };
572
+ var reconcileQuestions = async (sessionId, files) => {
556
573
  const late = [];
557
574
  try {
558
- const files = listPendingQuestions(sessionId);
559
575
  if (files.length === 0) return late;
560
576
  const apiKey = getApiKey();
561
577
  for (const correlationId of files) {
@@ -576,6 +592,7 @@ var reconcilePendingQuestions = async (sessionId) => {
576
592
  }
577
593
  return late;
578
594
  };
595
+ var reconcilePendingQuestions = (sessionId, toolUseId) => reconcileQuestions(sessionId, pendingQuestionsToSettle(sessionId, toolUseId));
579
596
  var describeLateAnswer = (value) => {
580
597
  const v = (value ?? "").trim();
581
598
  if (v === "yes") return "approved it";
@@ -724,6 +741,7 @@ var reportEvent = async (event, options = {}) => {
724
741
  // Link-back for a phone-spawned session: forward the spawn id the daemon
725
742
  // set in the environment so the server can map spawnId -> sessionId.
726
743
  ...process.env.PUSHARY_SPAWN_ID ? { spawnId: process.env.PUSHARY_SPAWN_ID } : {},
744
+ ...driverFromEnv(process.env) ? { driver: driverFromEnv(process.env) } : {},
727
745
  canDrainCommand: clientCanDrain && !storedCommand,
728
746
  ackMode: clientCanDrain && !storedCommand,
729
747
  skipCommandDrain: storedCommand != null
@@ -780,6 +798,11 @@ var reportEvent = async (event, options = {}) => {
780
798
  }, { maxAttempts: options.maxAttempts ?? 2, baseDelayMs: 300 });
781
799
  };
782
800
  var NON_GATED_MIRROR_TOOLS = /* @__PURE__ */ new Set(["TodoWrite"]);
801
+ var settleQuestionsForToolUse = async (sessionId, toolUseId) => {
802
+ if (!toolUseId) return;
803
+ const late = await reconcileQuestions(sessionId, listPendingQuestionsForToolUse(sessionId, toolUseId));
804
+ for (const answer of late) removePendingQuestion(sessionId, answer.correlationId);
805
+ };
783
806
  var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
784
807
  try {
785
808
  const projectName = basename2(input.cwd ?? process.cwd());
@@ -790,7 +813,7 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
790
813
  const receiptsEnabled = process.env.PUSHARY_RECEIPTS !== "off";
791
814
  const sessionKey = input.session_id || DEFAULT_SESSION;
792
815
  const liveMode = await fetchModeState(getApiKey(), input.session_id, MODE_REUSE_MS);
793
- const late = await reconcilePendingQuestions(sessionKey);
816
+ const late = await reconcilePendingQuestions(sessionKey, input.tool_use_id);
794
817
  let additionalContext;
795
818
  if (late.length > 0 && agent.type === CLAUDE_CODE_AGENT.type) {
796
819
  additionalContext = buildLateAnswerContext(late);
@@ -875,7 +898,7 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
875
898
  agentType: agent.type,
876
899
  agentName: `${agent.label} - ${projectName}`,
877
900
  action: summarizeStop(input),
878
- sessionId: input.session_id,
901
+ ...sessionIdentityOf(input),
879
902
  usage: deriveUsage(input.transcript_path, input.session_id)
880
903
  }, { maxAttempts: 1 })
881
904
  ];
@@ -897,16 +920,46 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
897
920
  return void 0;
898
921
  }
899
922
  };
923
+ var isSubagentStop = (input) => input.hook_event_name === "SubagentStop" || typeof input.agent_id === "string" && input.agent_id.length > 0;
924
+ var SUBAGENT_STOP_REPORT_TIMEOUT_MS = 4e3;
925
+ var handleSubagentStop = async (input, agent = CLAUDE_CODE_AGENT) => {
926
+ try {
927
+ const projectName = basename2(input.cwd ?? process.cwd());
928
+ await reportEvent({
929
+ event: "session_end",
930
+ agentType: agent.type,
931
+ agentName: `${agent.label} - ${projectName}`,
932
+ action: subagentStopSummary(input),
933
+ ...sessionIdentityOf(input)
934
+ }, { maxAttempts: 1, timeoutMs: SUBAGENT_STOP_REPORT_TIMEOUT_MS });
935
+ } catch {
936
+ }
937
+ };
938
+ var isCompactionEvent = (hookEventName) => hookEventName === "PreCompact" || hookEventName === "PostCompact";
939
+ var COMPACTION_REPORT_TIMEOUT_MS = 4e3;
940
+ var handleCompaction = async (input, agent = CLAUDE_CODE_AGENT) => {
941
+ try {
942
+ const projectName = basename2(input.cwd ?? process.cwd());
943
+ await reportEvent({
944
+ event: "compaction",
945
+ agentType: agent.type,
946
+ agentName: `${agent.label} - ${projectName}`,
947
+ action: compactionAction(input.hook_event_name, input.trigger),
948
+ sessionId: input.session_id
949
+ }, { maxAttempts: 1, timeoutMs: COMPACTION_REPORT_TIMEOUT_MS });
950
+ } catch {
951
+ }
952
+ };
900
953
  var handleSessionStart = async (input, agent = CLAUDE_CODE_AGENT) => {
901
954
  try {
902
955
  const projectName = basename2(input.cwd ?? process.cwd());
903
- const action = sessionStartAction(input.source);
956
+ const action = input.agent_id ? "Subagent started" : sessionStartAction(input.source);
904
957
  await reportEvent({
905
958
  event: "session_start",
906
959
  agentType: agent.type,
907
960
  agentName: `${agent.label} - ${projectName}`,
908
961
  action,
909
- sessionId: input.session_id
962
+ ...sessionIdentityOf(input)
910
963
  }, { maxAttempts: 2, timeoutMs: 4e3 });
911
964
  } catch {
912
965
  }
@@ -1033,6 +1086,7 @@ export {
1033
1086
  getPolicy,
1034
1087
  fetchModeState,
1035
1088
  fetchModeOverride,
1089
+ phoneDrivenEnv,
1036
1090
  repoKeyFor,
1037
1091
  throttlePass,
1038
1092
  readLastUserPrompt,
@@ -1048,9 +1102,14 @@ export {
1048
1102
  preToolUseTimeoutDecision,
1049
1103
  readLastPrompt,
1050
1104
  reportEvent,
1105
+ settleQuestionsForToolUse,
1051
1106
  handlePostToolUse,
1052
1107
  handleUserPrompt,
1053
1108
  handleStop,
1109
+ isSubagentStop,
1110
+ handleSubagentStop,
1111
+ isCompactionEvent,
1112
+ handleCompaction,
1054
1113
  handleSessionStart,
1055
1114
  handleSessionEnd,
1056
1115
  handleNotification,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-NBDU3R3G.js";
3
+ } from "./chunk-OSALSIUF.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var fromEnv = () => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-QINYXLA5.js";
3
+ } from "./chunk-VNMKIJZB.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -91,7 +91,7 @@ var INSTALL_SOURCES = [
91
91
  var isInstallSource = (value) => typeof value === "string" && INSTALL_SOURCES.includes(value);
92
92
  var normalizeInstallSource = (value) => isInstallSource(value) ? value : "unknown";
93
93
  var HOOK_BUDGETS = {
94
- claude: { budgetSeconds: 120, guardSeconds: 10 },
94
+ claude: { budgetSeconds: 600, guardSeconds: 10 },
95
95
  codex: { budgetSeconds: 180, guardSeconds: 10 },
96
96
  gemini: { budgetSeconds: 180, guardSeconds: 10 },
97
97
  // The Cursor gate is a dependency-free .mjs that cannot import this module; it
@@ -1054,6 +1054,8 @@ var resolvePolicyAcross = (config, toolName, modeOverride, toolInputs, cwd, repo
1054
1054
  };
1055
1055
  var isModeStateDegraded = (state) => state?.degraded === true;
1056
1056
  var KILL_REASON = "Stopped by user, this agent was halted from Pushary";
1057
+ var PUSHARY_TOOL_PREFIX = "mcp__pushary__";
1058
+ var isPusharyOwnTool = (toolName) => toolName.startsWith(PUSHARY_TOOL_PREFIX);
1057
1059
  var strictestScopeVerdict = (scope, paths) => {
1058
1060
  if (paths.length === 0) return evaluateScope(scope, void 0);
1059
1061
  let worst = { outcome: "in_scope" };
@@ -1085,6 +1087,13 @@ var ASKED_IN_EVERY_MODE = new Set(ASKED_IN_EVERY_MODE_TOOLS);
1085
1087
  var isAskedInEveryMode = (toolName) => ASKED_IN_EVERY_MODE.has(toolName);
1086
1088
  var resolveGate = (input) => {
1087
1089
  const { modeState, config, toolName, toolInputs, cwd, repoKey, sessionId } = input;
1090
+ if (isPusharyOwnTool(toolName)) {
1091
+ return {
1092
+ kind: "defer",
1093
+ policy: resolvePolicyAcross(config, toolName, modeState.mode, toolInputs, cwd, repoKey, sessionId),
1094
+ reason: "own_tool"
1095
+ };
1096
+ }
1088
1097
  if (modeState.kill) return { kind: "kill", reason: KILL_REASON };
1089
1098
  const policy = resolvePolicyAcross(config, toolName, modeState.mode, toolInputs, cwd, repoKey, sessionId);
1090
1099
  const verdict = verdictForPolicy(policy);
@@ -1128,9 +1137,11 @@ var modeStateFromResponse = (data) => {
1128
1137
  // Validated rather than cast: a malformed contract must read as no contract,
1129
1138
  // never as one that silently matches nothing.
1130
1139
  scope: isScopeContract(data.scope) ? data.scope : null,
1131
- degraded: false
1140
+ degraded: false,
1141
+ askHandoff: data.askHandoff === true
1132
1142
  };
1133
1143
  };
1144
+ var stoppedOutput = (output, stopReason) => ({ ...output, continue: false, stopReason });
1134
1145
  var claudePreToolUseOutput = (decision, reason, updatedInput) => ({
1135
1146
  hookSpecificOutput: {
1136
1147
  hookEventName: "PreToolUse",
@@ -1362,10 +1373,12 @@ export {
1362
1373
  resolveAutoResolveOrigin,
1363
1374
  resolvePolicy,
1364
1375
  KILL_REASON,
1376
+ isPusharyOwnTool,
1365
1377
  shouldDeferToNativeMode,
1366
1378
  ASKED_IN_EVERY_MODE_TOOLS,
1367
1379
  resolveGate,
1368
1380
  modeStateFromResponse,
1381
+ stoppedOutput,
1369
1382
  claudePreToolUseOutput,
1370
1383
  PENDING_COMMAND_PREFIX,
1371
1384
  pendingCommandEvents,