@pushary/agent-hooks 0.87.4 → 0.88.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 (57) hide show
  1. package/dist/bin/pushary-bell-hook.js +4 -4
  2. package/dist/bin/pushary-bell.js +5 -5
  3. package/dist/bin/pushary-claude.js +8 -8
  4. package/dist/bin/pushary-clean.js +9 -8
  5. package/dist/bin/pushary-codex-bridge.js +5 -5
  6. package/dist/bin/pushary-codex-hook.js +15 -9
  7. package/dist/bin/pushary-codex.js +4 -4
  8. package/dist/bin/pushary-connect.js +6 -6
  9. package/dist/bin/pushary-daemon.js +4 -4
  10. package/dist/bin/pushary-disconnect.js +5 -5
  11. package/dist/bin/pushary-doctor.js +15 -14
  12. package/dist/bin/pushary-elicitation-hook.js +1 -1
  13. package/dist/bin/pushary-gemini-bridge.js +5 -5
  14. package/dist/bin/pushary-gemini-hook.js +15 -9
  15. package/dist/bin/pushary-hook.js +6 -6
  16. package/dist/bin/pushary-login.js +4 -4
  17. package/dist/bin/pushary-logout.js +4 -4
  18. package/dist/bin/pushary-mode.js +3 -3
  19. package/dist/bin/pushary-notification-hook.js +4 -4
  20. package/dist/bin/pushary-opencode-hook.js +5 -5
  21. package/dist/bin/pushary-permission-denied-hook.js +6 -6
  22. package/dist/bin/pushary-permission-hook.js +6 -6
  23. package/dist/bin/pushary-post-hook.js +4 -4
  24. package/dist/bin/pushary-prompt-hook.js +4 -4
  25. package/dist/bin/pushary-session-end-hook.js +4 -4
  26. package/dist/bin/pushary-session-start-hook.js +4 -4
  27. package/dist/bin/pushary-setup.js +16 -16
  28. package/dist/bin/pushary-stats.js +2 -2
  29. package/dist/bin/pushary-status.js +5 -5
  30. package/dist/bin/pushary-stop-hook.js +4 -4
  31. package/dist/bin/pushary-stopfailure-hook.js +4 -4
  32. package/dist/bin/pushary-upgrade.js +5 -5
  33. package/dist/bin/pushary-wait.js +3 -3
  34. package/dist/{chunk-BFBAHOGR.js → chunk-3B3PYTKH.js} +1 -1
  35. package/dist/{chunk-DMW54VWK.js → chunk-3JL64GVE.js} +93 -3
  36. package/dist/{chunk-TD3M232T.js → chunk-3PSEZOC4.js} +1 -1
  37. package/dist/{chunk-VIHRP25S.js → chunk-4VYELDAH.js} +1 -1
  38. package/dist/{chunk-YX6ZKCC5.js → chunk-67TNJVJW.js} +1 -1
  39. package/dist/{chunk-KFAH6KRE.js → chunk-6BOE5NJZ.js} +1 -1
  40. package/dist/{chunk-ZGC3W23N.js → chunk-DSTZ2RBU.js} +1 -1
  41. package/dist/{chunk-ICERLAE2.js → chunk-DXBUFXPL.js} +1 -1
  42. package/dist/{chunk-EJL3PJU7.js → chunk-ESARAYRI.js} +1 -1
  43. package/dist/{chunk-7J67NYK4.js → chunk-F5W3AY6L.js} +2 -1
  44. package/dist/{chunk-QLMX5LLV.js → chunk-LEYXXXRN.js} +1 -1
  45. package/dist/{chunk-VOE4SYPR.js → chunk-MNXG6IIM.js} +23 -7
  46. package/dist/{chunk-K42RXJPG.js → chunk-MY5A4EPQ.js} +1 -1
  47. package/dist/{chunk-H5PIPIKQ.js → chunk-OULRSHFB.js} +1 -1
  48. package/dist/{chunk-QXZ2CKRK.js → chunk-RSUBLTMP.js} +2 -2
  49. package/dist/{chunk-KULUNLRO.js → chunk-T2XZH7D5.js} +3 -7
  50. package/dist/{chunk-2JYQAW4B.js → chunk-U6HRYNHC.js} +2 -2
  51. package/dist/{chunk-OMA2OZRE.js → chunk-UTQCV46U.js} +27 -62
  52. package/dist/{chunk-NAPRGHZ3.js → chunk-VHHVGVFQ.js} +1 -1
  53. package/dist/{chunk-KBS53WOE.js → chunk-VSCSZ2LI.js} +1 -1
  54. package/dist/{chunk-YYTLTEGU.js → chunk-WXCHB6BO.js} +1 -1
  55. package/dist/{chunk-TV3UFGT7.js → chunk-XFHMGVMX.js} +1 -1
  56. package/dist/src/index.js +6 -6
  57. package/package.json +1 -1
@@ -1,6 +1,39 @@
1
1
  // ../contracts/src/index.ts
2
2
  var APPROVAL_MODES = ["push_only", "terminal_only", "push_first", "notify_only"];
3
3
  var isApprovalMode = (value) => typeof value === "string" && APPROVAL_MODES.includes(value);
4
+ var MAX_AGENT_QUESTIONS = 4;
5
+ var parseAgentQuestion = (raw) => {
6
+ const q = raw;
7
+ if (!q || typeof q.question !== "string") return void 0;
8
+ const options = Array.isArray(q.options) ? q.options.flatMap((option) => {
9
+ if (!option || typeof option !== "object") return [];
10
+ const value = option;
11
+ if (typeof value.label !== "string" || value.label.length === 0) return [];
12
+ return [{
13
+ label: value.label,
14
+ ...typeof value.description === "string" ? { description: value.description } : {}
15
+ }];
16
+ }) : [];
17
+ if (options.length < 2) return void 0;
18
+ return {
19
+ question: q.question,
20
+ ...typeof q.header === "string" ? { header: q.header } : {},
21
+ multiSelect: q.multiSelect === true,
22
+ options
23
+ };
24
+ };
25
+ var parseAgentQuestions = (toolInput) => {
26
+ const questions = toolInput.questions;
27
+ if (!Array.isArray(questions) || questions.length === 0) return void 0;
28
+ if (questions.length > MAX_AGENT_QUESTIONS) return void 0;
29
+ const parsed = [];
30
+ for (const raw of questions) {
31
+ const one = parseAgentQuestion(raw);
32
+ if (!one || parsed.some((question) => question.question === one.question)) return void 0;
33
+ parsed.push(one);
34
+ }
35
+ return parsed;
36
+ };
4
37
  var parseAgentQuestionAnswers = (questions, value) => {
5
38
  let parsed;
6
39
  try {
@@ -72,6 +105,7 @@ var hookWaitDeadline = (startMs, policyWaitSeconds, agent, nowMs) => {
72
105
  };
73
106
  var hookWaitClamped = (startMs, policyWaitSeconds, agent, nowMs) => startMs + hookMaxWaitSeconds(agent) * 1e3 < nowMs + Math.max(policyWaitSeconds, 0) * 1e3;
74
107
  var effectiveWaitSeconds = (timeoutAction, policySeconds, agent) => timeoutAction === "wait" ? hookMaxWaitSeconds(agent) : policySeconds;
108
+ var HOOK_SOURCES = ["claude", "codex", "gemini", "cursor", "vscode", "opencode"];
75
109
  var AGENT_LABELS_BY_TYPE = {
76
110
  claude_code: "Claude Code",
77
111
  claude_cowork: "Claude Cowork",
@@ -86,6 +120,7 @@ var AGENT_LABELS_BY_TYPE = {
86
120
  unknown: "Agent"
87
121
  };
88
122
  var OPENCODE_APPROVAL_EVENT = "permission.ask";
123
+ var CURSOR_GATE_EVENTS = ["beforeShellExecution", "beforeMCPExecution"];
89
124
  var HOOK_LOCATOR_BINARY = "pushary-bridge";
90
125
  var HOOK_BATCH_MAX_BYTES = 4 * 1024 * 1024;
91
126
  var REPO_KEY_MAX_LENGTH = 200;
@@ -960,8 +995,15 @@ var resolveAutoResolveOrigin = (config, toolName, toolInput, cwd, repoKey, sessi
960
995
  var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, sessionId) => {
961
996
  const args = toolInput ? policyArgForms(toolName, toolInput, cwd) : [];
962
997
  const arg = args[0];
963
- const match = selectGoverningRule(config.policies, toolName, args, repoKey, sessionId);
964
- let base = match?.policy ?? config.policies.find((p) => p.tool === "*") ?? {
998
+ const governing = (pool) => {
999
+ if (pool.length === 0) return void 0;
1000
+ const rule = selectGoverningRule(pool, toolName, args, repoKey, sessionId);
1001
+ if (rule) return rule;
1002
+ const wildcard = pool.find((p) => p.tool === "*");
1003
+ return wildcard ? { policy: wildcard, rank: "wildcard" } : void 0;
1004
+ };
1005
+ const match = governing(config.policies.filter((p) => !p.origin)) ?? governing(config.policies.filter((p) => p.origin === "preset")) ?? governing(config.policies.filter((p) => p.origin === "builtin"));
1006
+ let base = match?.policy ?? {
965
1007
  tool: toolName,
966
1008
  timeoutSeconds: config.defaultTimeoutSeconds,
967
1009
  timeoutAction: config.defaultTimeoutAction,
@@ -1013,6 +1055,21 @@ var strictestScopeVerdict = (scope, paths) => {
1013
1055
  return worst;
1014
1056
  };
1015
1057
  var survivesDegraded = (config, toolName, toolInputs, cwd, repoKey, sessionId) => toolInputs.length === 1 && resolveAutoResolveOrigin(config, toolName, toolInputs[0], cwd, repoKey, sessionId) === "safe_readonly";
1058
+ var FULL_DEFER_MODES = /* @__PURE__ */ new Set(["plan", "auto", "dontAsk"]);
1059
+ var ACCEPT_EDITS_DEFER_TOOLS = /* @__PURE__ */ new Set([
1060
+ "Write",
1061
+ "Edit",
1062
+ "MultiEdit",
1063
+ "NotebookEdit"
1064
+ ]);
1065
+ var shouldDeferToNativeMode = (permissionMode, toolName, options = {}) => {
1066
+ if (options.respectPermissionMode === false) return false;
1067
+ if (typeof permissionMode !== "string") return false;
1068
+ if (FULL_DEFER_MODES.has(permissionMode)) return true;
1069
+ if (permissionMode === "acceptEdits" && ACCEPT_EDITS_DEFER_TOOLS.has(toolName)) return true;
1070
+ if (permissionMode === "bypassPermissions") return options.deferBypass === true;
1071
+ return false;
1072
+ };
1016
1073
  var resolveGate = (input) => {
1017
1074
  const { modeState, config, toolName, toolInputs, cwd, repoKey, sessionId } = input;
1018
1075
  if (modeState.kill) return { kind: "kill", reason: KILL_REASON };
@@ -1068,6 +1125,33 @@ var claudePreToolUseOutput = (decision, reason, updatedInput) => ({
1068
1125
  ...updatedInput ? { updatedInput } : {}
1069
1126
  }
1070
1127
  });
1128
+ var PENDING_COMMAND_PREFIX = "The user sent a new instruction from their phone via Pushary:";
1129
+ var pendingCommandEvents = (source) => {
1130
+ switch (source) {
1131
+ case "claude":
1132
+ return ["PostToolUse", "UserPromptSubmit", "Stop"];
1133
+ // Codex carries context on its activity events and NOT on Stop, whose output
1134
+ // schema has no context field at all. Draining for it at the turn boundary
1135
+ // was therefore right to render nothing, and wrong to conclude that Codex
1136
+ // could not be sent a message.
1137
+ case "codex":
1138
+ return ["PostToolUse", "PreToolUse", "UserPromptSubmit"];
1139
+ case "gemini":
1140
+ return ["AfterTool", "BeforeTool", "BeforeAgent"];
1141
+ // Registered events only. Cursor's carriers are `postToolUse` and `stop`,
1142
+ // which this build does not write into the config; the ones it does write
1143
+ // (`afterShellExecution`, `afterFileEdit`, `afterAgentResponse`) are
1144
+ // observational and discard whatever they print.
1145
+ case "cursor":
1146
+ return [];
1147
+ case "vscode":
1148
+ return [];
1149
+ // The prompt is appended through the TUI rather than through a hook return,
1150
+ // so there is nothing for this to print.
1151
+ case "opencode":
1152
+ return [];
1153
+ }
1154
+ };
1071
1155
  var claudePermissionRequestOutput = (decision, reason, updatedInput) => ({
1072
1156
  hookSpecificOutput: {
1073
1157
  hookEventName: "PermissionRequest",
@@ -1103,7 +1187,7 @@ var gateOutputFor = (source, event, decision, reason) => {
1103
1187
  output = decision === "allow" ? { decision: "allow" } : { decision: "deny", reason: reason ?? "Denied from Pushary" };
1104
1188
  } else if (source === "opencode" && event === OPENCODE_APPROVAL_EVENT) {
1105
1189
  output = decision === "deny" ? { status: "deny", reason: reason ?? "Denied from Pushary" } : { status: decision };
1106
- } else if (source === "cursor" && event === "beforeShellExecution") {
1190
+ } else if (source === "cursor" && CURSOR_GATE_EVENTS.includes(event)) {
1107
1191
  output = decision === "allow" ? { permission: "allow" } : decision === "ask" ? { permission: "ask" } : {
1108
1192
  permission: "deny",
1109
1193
  user_message: "Command denied via Pushary.",
@@ -1125,14 +1209,17 @@ var claudeElicitationOutput = (event, action, content) => ({
1125
1209
  var renderElicitationOutput = (event, action, content) => ELICITATION_EVENTS.includes(event) ? JSON.stringify(claudeElicitationOutput(event, action, content)) : void 0;
1126
1210
 
1127
1211
  export {
1212
+ parseAgentQuestions,
1128
1213
  parseAgentQuestionAnswers,
1129
1214
  normalizeInstallSource,
1130
1215
  HOOK_BUDGETS,
1131
1216
  hookWaitDeadline,
1132
1217
  hookWaitClamped,
1133
1218
  effectiveWaitSeconds,
1219
+ HOOK_SOURCES,
1134
1220
  AGENT_LABELS_BY_TYPE,
1135
1221
  OPENCODE_APPROVAL_EVENT,
1222
+ CURSOR_GATE_EVENTS,
1136
1223
  HOOK_LOCATOR_BINARY,
1137
1224
  normalizeRepoRemote,
1138
1225
  localRepoKey,
@@ -1148,9 +1235,12 @@ export {
1148
1235
  resolveAutoResolveOrigin,
1149
1236
  resolvePolicy,
1150
1237
  KILL_REASON,
1238
+ shouldDeferToNativeMode,
1151
1239
  resolveGate,
1152
1240
  modeStateFromResponse,
1153
1241
  claudePreToolUseOutput,
1242
+ PENDING_COMMAND_PREFIX,
1243
+ pendingCommandEvents,
1154
1244
  claudePermissionRequestOutput,
1155
1245
  gateOutputFor,
1156
1246
  renderElicitationOutput
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-KFAH6KRE.js";
4
+ } from "./chunk-6BOE5NJZ.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-DMW54VWK.js";
3
+ } from "./chunk-3JL64GVE.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-6MTNS63X.js";
10
10
  import {
11
11
  resolveInstallSourceFor
12
- } from "./chunk-KBS53WOE.js";
12
+ } from "./chunk-VSCSZ2LI.js";
13
13
  import {
14
14
  configFilePath
15
15
  } from "./chunk-2UMNXADU.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-2UMNXADU.js";
13
13
  import {
14
14
  isValidApiKey
15
- } from "./chunk-DMW54VWK.js";
15
+ } from "./chunk-3JL64GVE.js";
16
16
 
17
17
  // src/session.ts
18
18
  var IDENTITY_PATH = "/api/v1/server/identity";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
3
  OPENCODE_APPROVAL_EVENT
4
- } from "./chunk-DMW54VWK.js";
4
+ } from "./chunk-3JL64GVE.js";
5
5
 
6
6
  // src/opencode-plugin.ts
7
7
  var OPENCODE_PLUGIN_MARKER = "pushary-opencode-plugin w1";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-K42RXJPG.js";
3
+ } from "./chunk-MY5A4EPQ.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-BSZYIAZL.js";
12
12
  import {
13
13
  reportedInstallSource
14
- } from "./chunk-KBS53WOE.js";
14
+ } from "./chunk-VSCSZ2LI.js";
15
15
 
16
16
  // src/reach.ts
17
17
  var reachVerdict = (channels) => {
@@ -5,7 +5,7 @@ import {
5
5
  FILE_TARGET_TOOLS,
6
6
  redactSecrets,
7
7
  redactSecretsDeep
8
- } from "./chunk-DMW54VWK.js";
8
+ } from "./chunk-3JL64GVE.js";
9
9
 
10
10
  // ../hook-mapping/src/describe.ts
11
11
  import { isAbsolute, relative, resolve } from "path";
@@ -280,6 +280,7 @@ export {
280
280
  deriveActionBody,
281
281
  deriveAction,
282
282
  deriveBlocker,
283
+ AGENT_IDENTITIES,
283
284
  summarizeStop,
284
285
  sessionStartAction,
285
286
  sessionEndAction,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  redactSecrets
6
- } from "./chunk-DMW54VWK.js";
6
+ } from "./chunk-3JL64GVE.js";
7
7
 
8
8
  // src/crypto.ts
9
9
  import nacl from "tweetnacl";
@@ -10,6 +10,7 @@ import {
10
10
  waitForAnswer
11
11
  } from "./chunk-J3YDT4HM.js";
12
12
  import {
13
+ AGENT_IDENTITIES,
13
14
  deriveReceiptMeta,
14
15
  deriveToolTarget,
15
16
  describeToolCall,
@@ -21,7 +22,7 @@ import {
21
22
  summarizeStop,
22
23
  taskTitleFrom,
23
24
  toolResultErrorText
24
- } from "./chunk-7J67NYK4.js";
25
+ } from "./chunk-F5W3AY6L.js";
25
26
  import {
26
27
  getMachineId
27
28
  } from "./chunk-RN3NOEJF.js";
@@ -30,20 +31,23 @@ import {
30
31
  } from "./chunk-BSZYIAZL.js";
31
32
  import {
32
33
  reportedInstallSource
33
- } from "./chunk-KBS53WOE.js";
34
+ } from "./chunk-VSCSZ2LI.js";
34
35
  import {
35
36
  getApiKey,
36
37
  getBaseUrl
37
38
  } from "./chunk-2UMNXADU.js";
38
39
  import {
40
+ HOOK_SOURCES,
41
+ PENDING_COMMAND_PREFIX,
39
42
  gateOutputFor,
40
43
  isAutoApprove,
41
44
  localRepoKey,
42
45
  modeStateFromResponse,
43
46
  normalizeRepoRemote,
47
+ pendingCommandEvents,
44
48
  resolveAutoResolveOrigin,
45
49
  resolvePolicy
46
- } from "./chunk-DMW54VWK.js";
50
+ } from "./chunk-3JL64GVE.js";
47
51
 
48
52
  // src/policy.ts
49
53
  import { createHash } from "crypto";
@@ -648,7 +652,6 @@ var deriveUsage = (transcriptPath, sessionId) => {
648
652
  return void 0;
649
653
  }
650
654
  };
651
- var PENDING_COMMAND_PREFIX = "The user sent a new instruction from their phone via Pushary:";
652
655
  var mergeAdditionalContext = (existing, reported) => {
653
656
  const pendingCommand = reported.status === "fulfilled" ? reported.value?.pendingCommand : void 0;
654
657
  if (typeof pendingCommand !== "string" || pendingCommand.trim().length === 0) return existing;
@@ -657,6 +660,11 @@ var mergeAdditionalContext = (existing, reported) => {
657
660
 
658
661
  ${injected}` : injected;
659
662
  };
663
+ var consumesActivityContext = (agentType) => {
664
+ const source = HOOK_SOURCES.find((candidate) => AGENT_IDENTITIES[candidate].type === agentType);
665
+ if (!source) return false;
666
+ return pendingCommandEvents(source).some((event) => event !== "Stop");
667
+ };
660
668
  var detectInstallMode = () => (process.argv[1] ?? "").includes("_npx") ? "npx" : "cli";
661
669
  var reportEvent = async (event, options = {}) => {
662
670
  const apiKey = options.apiKey ?? getApiKey();
@@ -691,7 +699,15 @@ var reportEvent = async (event, options = {}) => {
691
699
  // Only Claude Code consumes additionalContext from these hooks; Codex and
692
700
  // Gemini keep their existing session_end-only drain, so they must NOT
693
701
  // advertise it or the server would pop-and-drop their queued command.
694
- canDrainCommand: event.agentType === CLAUDE_CODE_AGENT.type
702
+ //
703
+ // And only when this caller kept the full budget. The pop is a destructive
704
+ // LPOP that happens before the response is written, so a caller that
705
+ // abandons the request destroys the message: `user_prompt` runs on a human
706
+ // keystroke and gave it 800ms, which a cold function misses routinely.
707
+ // Asking the server to consume something we may not wait to receive is the
708
+ // one shape that loses it outright, and the instruction reaches the agent
709
+ // on its next tool call instead.
710
+ canDrainCommand: consumesActivityContext(event.agentType) && options.timeoutMs === void 0
695
711
  }),
696
712
  signal: AbortSignal.timeout(options.timeoutMs ?? 1e4)
697
713
  });
@@ -750,7 +766,7 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
750
766
  );
751
767
  }
752
768
  const [reported] = await Promise.allSettled([toolReport, ...extraReports]);
753
- if (agent.type === CLAUDE_CODE_AGENT.type) {
769
+ if (consumesActivityContext(agent.type)) {
754
770
  additionalContext = mergeAdditionalContext(additionalContext, reported);
755
771
  }
756
772
  return additionalContext;
@@ -780,7 +796,7 @@ var handleUserPrompt = async (input, agent = CLAUDE_CODE_AGENT) => {
780
796
  taskTitle
781
797
  }, { maxAttempts: 1, timeoutMs: 800 })
782
798
  ]);
783
- if (agent.type === CLAUDE_CODE_AGENT.type) {
799
+ if (consumesActivityContext(agent.type)) {
784
800
  additionalContext = mergeAdditionalContext(additionalContext, reported);
785
801
  }
786
802
  return additionalContext;
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  HOOK_BUDGETS,
7
7
  HOOK_LOCATOR_BINARY
8
- } from "./chunk-DMW54VWK.js";
8
+ } from "./chunk-3JL64GVE.js";
9
9
 
10
10
  // src/gemini-config.ts
11
11
  var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VT4IVERX.js";
8
8
  import {
9
9
  pusharyDir
10
- } from "./chunk-NAPRGHZ3.js";
10
+ } from "./chunk-VHHVGVFQ.js";
11
11
  import {
12
12
  getMachineId
13
13
  } from "./chunk-RN3NOEJF.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  hasGeminiHooks
3
- } from "./chunk-K42RXJPG.js";
3
+ } from "./chunk-MY5A4EPQ.js";
4
4
  import {
5
5
  hasCodexHooks
6
- } from "./chunk-NAPRGHZ3.js";
6
+ } from "./chunk-VHHVGVFQ.js";
7
7
 
8
8
  // src/diagnostics/wiring.ts
9
9
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  openCodeConfigDirFrom
3
- } from "./chunk-ZGC3W23N.js";
3
+ } from "./chunk-DSTZ2RBU.js";
4
4
  import {
5
5
  codexHomeFrom
6
- } from "./chunk-NAPRGHZ3.js";
6
+ } from "./chunk-VHHVGVFQ.js";
7
7
 
8
8
  // src/vscode-config.ts
9
9
  import { homedir } from "os";
@@ -326,9 +326,6 @@ var hasInstructionBlock = (filePath) => {
326
326
  }
327
327
  };
328
328
 
329
- // src/hooks-spec.ts
330
- var CURSOR_GATE_EVENTS = ["beforeShellExecution", "beforeMCPExecution"];
331
-
332
329
  export {
333
330
  registerPluginLocation,
334
331
  unregisterPluginLocation,
@@ -345,6 +342,5 @@ export {
345
342
  renderProjectAgentInstructions,
346
343
  writeInstructionBlock,
347
344
  removeInstructionBlock,
348
- hasInstructionBlock,
349
- CURSOR_GATE_EVENTS
345
+ hasInstructionBlock
350
346
  };
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-K42RXJPG.js";
4
+ } from "./chunk-MY5A4EPQ.js";
5
5
  import {
6
6
  guardBinary
7
7
  } from "./chunk-B7ZBHWOW.js";
8
8
  import {
9
9
  HOOK_LOCATOR_BINARY
10
- } from "./chunk-DMW54VWK.js";
10
+ } from "./chunk-3JL64GVE.js";
11
11
 
12
12
  // src/claude-config.ts
13
13
  import { join } from "path";
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  isGatingMoment,
7
7
  recordKeylessMoment
8
- } from "./chunk-VIHRP25S.js";
8
+ } from "./chunk-4VYELDAH.js";
9
9
  import {
10
10
  denyReasonFrom,
11
11
  isDeferAnswer
@@ -17,7 +17,7 @@ import {
17
17
  readLastUserPrompt,
18
18
  repoKeyFor,
19
19
  throttlePass
20
- } from "./chunk-VOE4SYPR.js";
20
+ } from "./chunk-MNXG6IIM.js";
21
21
  import {
22
22
  DEFAULT_SESSION,
23
23
  askUser,
@@ -33,7 +33,7 @@ import {
33
33
  deriveToolTarget,
34
34
  describeToolCall,
35
35
  scopePathFor
36
- } from "./chunk-7J67NYK4.js";
36
+ } from "./chunk-F5W3AY6L.js";
37
37
  import {
38
38
  getMachineId
39
39
  } from "./chunk-RN3NOEJF.js";
@@ -49,8 +49,10 @@ import {
49
49
  hookWaitClamped,
50
50
  hookWaitDeadline,
51
51
  parseAgentQuestionAnswers,
52
- resolveGate
53
- } from "./chunk-DMW54VWK.js";
52
+ parseAgentQuestions,
53
+ resolveGate,
54
+ shouldDeferToNativeMode
55
+ } from "./chunk-3JL64GVE.js";
54
56
 
55
57
  // src/decision-episode.ts
56
58
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -107,10 +109,10 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
107
109
  return void 0;
108
110
  }
109
111
  }
110
- if (result.suppressed) {
112
+ if (result.suppressed || result.status === "terminal") {
111
113
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
112
114
  });
113
- return ask("You are at the keyboard, approve here.");
115
+ return ask(result.suppressed ? "You are at the keyboard, approve here." : "Delivery mode is Terminal, approve here.");
114
116
  }
115
117
  if (result.noDevices) {
116
118
  switch (timeoutAction) {
@@ -300,58 +302,21 @@ var keylessNoticeOnce = (sessionId) => {
300
302
  } catch {
301
303
  }
302
304
  };
303
- var FULL_DEFER_MODES = /* @__PURE__ */ new Set(["plan", "auto", "dontAsk"]);
304
- var ACCEPT_EDITS_DEFER_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
305
- var nativeModeGatingDisabled = () => {
306
- const flag = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
307
- return flag === "0" || flag === "false";
308
- };
309
- var shouldDeferToNativeMode = (permissionMode, toolName) => {
310
- if (nativeModeGatingDisabled()) return false;
311
- if (typeof permissionMode !== "string") return false;
312
- if (FULL_DEFER_MODES.has(permissionMode)) return true;
313
- if (permissionMode === "acceptEdits" && ACCEPT_EDITS_DEFER_TOOLS.has(toolName)) return true;
314
- if (permissionMode === "bypassPermissions") {
315
- const flag = process.env.PUSHARY_DEFER_BYPASS;
316
- return flag === "1" || flag === "true";
317
- }
318
- return false;
319
- };
320
- var MAX_PHONE_QUESTIONS = 4;
321
- var parseQuestion = (raw) => {
322
- const q = raw;
323
- if (!q || typeof q.question !== "string") return void 0;
324
- const options = Array.isArray(q.options) ? q.options.flatMap((option) => {
325
- if (!option || typeof option !== "object") return [];
326
- const value = option;
327
- if (typeof value.label !== "string" || value.label.length === 0) return [];
328
- return [{
329
- label: value.label,
330
- ...typeof value.description === "string" ? { description: value.description } : {}
331
- }];
332
- }) : [];
333
- const labels = options.map((option) => option.label);
334
- if (labels.length < 2) return void 0;
305
+ var nativeDeferOptions = () => {
306
+ const respect = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
307
+ const bypass = process.env.PUSHARY_DEFER_BYPASS;
335
308
  return {
336
- question: q.question,
337
- header: typeof q.header === "string" ? q.header : void 0,
338
- multiSelect: q.multiSelect === true,
339
- options,
340
- labels
309
+ respectPermissionMode: !(respect === "0" || respect === "false"),
310
+ deferBypass: bypass === "1" || bypass === "true"
341
311
  };
342
312
  };
343
- var parseQuestions = (toolInput) => {
344
- const questions = toolInput.questions;
345
- if (!Array.isArray(questions) || questions.length === 0) return void 0;
346
- if (questions.length > MAX_PHONE_QUESTIONS) return void 0;
347
- const parsed = [];
348
- for (const raw of questions) {
349
- const one = parseQuestion(raw);
350
- if (!one || parsed.some((question) => question.question === one.question)) return void 0;
351
- parsed.push(one);
352
- }
353
- return parsed;
354
- };
313
+ var shouldDeferToNativeMode2 = (permissionMode, toolName) => shouldDeferToNativeMode(permissionMode, toolName, nativeDeferOptions());
314
+ var handedToTerminal = (result) => result.suppressed === true || result.noDevices === true || result.status === "terminal";
315
+ var withLabels = (question) => ({
316
+ ...question,
317
+ labels: question.options.map((option) => option.label)
318
+ });
319
+ var parseQuestions = (toolInput) => parseAgentQuestions(toolInput)?.map(withLabels);
355
320
  var questionContext = (question, index, total, project) => {
356
321
  const subject = question.header ? `${question.header}: your agent is asking in ${project}` : `Your agent is asking in ${project}`;
357
322
  return total > 1 ? `${subject} (${index + 1} of ${total})` : subject;
@@ -376,7 +341,7 @@ var askAsOneCard = async (apiKey, input, parsed, projectName, deadline) => {
376
341
  } catch {
377
342
  return void 0;
378
343
  }
379
- if (result.suppressed || result.noDevices) {
344
+ if (handedToTerminal(result)) {
380
345
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
381
346
  });
382
347
  return void 0;
@@ -411,7 +376,7 @@ var askQuestionByQuestion = async (apiKey, input, parsed, projectName, deadline)
411
376
  } catch {
412
377
  return void 0;
413
378
  }
414
- if (result.suppressed || result.noDevices) {
379
+ if (handedToTerminal(result)) {
415
380
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
416
381
  });
417
382
  return void 0;
@@ -461,7 +426,7 @@ var handleExitPlanMode = async (apiKey, input) => {
461
426
  } catch {
462
427
  return void 0;
463
428
  }
464
- if (result.suppressed || result.noDevices) {
429
+ if (handedToTerminal(result)) {
465
430
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
466
431
  });
467
432
  return void 0;
@@ -524,7 +489,7 @@ var handlePreToolUse = async (input) => {
524
489
  if (input.tool_name === "ExitPlanMode") {
525
490
  return handleExitPlanMode(apiKey, input);
526
491
  }
527
- if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
492
+ if (shouldDeferToNativeMode2(input.permission_mode, input.tool_name)) {
528
493
  return void 0;
529
494
  }
530
495
  switch (verdict.kind) {
@@ -555,7 +520,7 @@ var toPermissionRequestOutput = (hookOutput) => {
555
520
  };
556
521
  var handlePermissionRequest = async (input) => {
557
522
  if (PRETOOLUSE_HANDLED_TOOLS.has(input.tool_name)) {
558
- const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode(input.permission_mode, input.tool_name);
523
+ const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode2(input.permission_mode, input.tool_name);
559
524
  if (!reclaimDeferredExec) return void 0;
560
525
  }
561
526
  if (input.tool_name.startsWith("mcp__pushary__")) return void 0;
@@ -636,7 +601,7 @@ var handlePermissionDenied = async (input) => {
636
601
  } catch {
637
602
  return void 0;
638
603
  }
639
- if (result.suppressed || result.noDevices) {
604
+ if (handedToTerminal(result)) {
640
605
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
641
606
  });
642
607
  return void 0;
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  HOOK_BUDGETS,
6
6
  HOOK_LOCATOR_BINARY
7
- } from "./chunk-DMW54VWK.js";
7
+ } from "./chunk-3JL64GVE.js";
8
8
 
9
9
  // src/codex-config.ts
10
10
  import { createHash } from "crypto";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-DMW54VWK.js";
6
+ } from "./chunk-3JL64GVE.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readConfigFileKey
3
- } from "./chunk-YX6ZKCC5.js";
3
+ } from "./chunk-67TNJVJW.js";
4
4
 
5
5
  // src/bell/fleet.ts
6
6
  import { closeSync, constants, futimesSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";
@@ -5,7 +5,7 @@ import {
5
5
  generateSessionKey,
6
6
  uploadTranscriptRecords,
7
7
  wrapSessionKey
8
- } from "./chunk-QLMX5LLV.js";
8
+ } from "./chunk-LEYXXXRN.js";
9
9
 
10
10
  // src/live-session.ts
11
11
  var MAX_TRANSCRIPT_QUEUE = 100;