@pushary/agent-hooks 0.95.4 → 0.95.6

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 (91) 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 +5 -0
  4. package/data/cursor-plugin/README.md +1 -1
  5. package/data/cursor-plugin/scripts/pushary-gate.mjs +5 -2
  6. package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
  7. package/data/vscode-plugin/CHANGELOG.md +5 -0
  8. package/data/vscode-plugin/README.md +1 -1
  9. package/data/vscode-plugin/scripts/pushary-gate.mjs +14 -3
  10. package/dist/bin/pushary-bell-hook.js +20 -20
  11. package/dist/bin/pushary-bell.js +9 -9
  12. package/dist/bin/pushary-claude.js +29 -24
  13. package/dist/bin/pushary-clean.js +20 -20
  14. package/dist/bin/pushary-codex-bridge.js +10 -10
  15. package/dist/bin/pushary-codex-hook.js +24 -24
  16. package/dist/bin/pushary-codex.js +8 -8
  17. package/dist/bin/pushary-connect.js +8 -8
  18. package/dist/bin/pushary-cowork.js +5 -5
  19. package/dist/bin/pushary-daemon.js +26 -26
  20. package/dist/bin/pushary-disconnect.js +11 -10
  21. package/dist/bin/pushary-doctor.js +89 -35
  22. package/dist/bin/pushary-elicitation-hook.js +20 -20
  23. package/dist/bin/pushary-gemini-bridge.js +9 -9
  24. package/dist/bin/pushary-gemini-hook.js +23 -23
  25. package/dist/bin/pushary-hook.js +27 -27
  26. package/dist/bin/pushary-login.js +9 -9
  27. package/dist/bin/pushary-logout.js +6 -6
  28. package/dist/bin/pushary-mode.js +5 -5
  29. package/dist/bin/pushary-notification-hook.js +22 -22
  30. package/dist/bin/pushary-opencode-hook.js +23 -23
  31. package/dist/bin/pushary-permission-denied-hook.js +25 -25
  32. package/dist/bin/pushary-permission-hook.js +25 -25
  33. package/dist/bin/pushary-post-hook.js +22 -22
  34. package/dist/bin/pushary-prompt-hook.js +22 -22
  35. package/dist/bin/pushary-session-end-hook.js +22 -22
  36. package/dist/bin/pushary-session-start-hook.js +25 -25
  37. package/dist/bin/pushary-setup.js +62 -42
  38. package/dist/bin/pushary-stats.js +4 -4
  39. package/dist/bin/pushary-status.js +14 -10
  40. package/dist/bin/pushary-stop-hook.js +22 -22
  41. package/dist/bin/pushary-stopfailure-hook.js +22 -22
  42. package/dist/bin/pushary-suggestions.js +2 -2
  43. package/dist/bin/pushary-transcript-register.js +18 -18
  44. package/dist/bin/pushary-transcripts.js +7 -7
  45. package/dist/bin/pushary-upgrade.js +17 -17
  46. package/dist/bin/pushary-wait.js +5 -5
  47. package/dist/bin/pushary.js +1 -1
  48. package/dist/{chunk-EEJ6EQMI.js → chunk-32WFHA77.js} +1 -1
  49. package/dist/{chunk-YE37HYN3.js → chunk-32WWVRXN.js} +3 -14
  50. package/dist/{chunk-GGX5QTBG.js → chunk-3AJIK5AO.js} +4 -1
  51. package/dist/{chunk-PFRLYYF6.js → chunk-43TFQOAT.js} +1 -1
  52. package/dist/{chunk-MDDKA4QM.js → chunk-4XFBO3GW.js} +1 -1
  53. package/dist/{chunk-KT2CGUOE.js → chunk-6II5RBAS.js} +1 -1
  54. package/dist/{chunk-3N4FT4WQ.js → chunk-6MRPTYYH.js} +1 -1
  55. package/dist/{chunk-ND52YE7L.js → chunk-7N3D4GN7.js} +4 -4
  56. package/dist/{chunk-TMOCSYJK.js → chunk-A2M6FGUF.js} +3 -3
  57. package/dist/{chunk-HPM47TEO.js → chunk-AXECWTTZ.js} +2 -2
  58. package/dist/{chunk-MZOTKCNZ.js → chunk-BS5XR4J7.js} +4 -4
  59. package/dist/{chunk-O7ODXFII.js → chunk-C7BFOC35.js} +2 -2
  60. package/dist/{chunk-TKA2L2BU.js → chunk-CDBTCEKE.js} +5 -5
  61. package/dist/{chunk-JDNW6FTV.js → chunk-DH4INZW3.js} +1 -1
  62. package/dist/{chunk-6BNBW3M2.js → chunk-DSCGKZO5.js} +2 -2
  63. package/dist/{chunk-W5GH5GTX.js → chunk-FBO5WS3D.js} +1 -1
  64. package/dist/{chunk-U24ERDPE.js → chunk-FHIVWF4B.js} +13 -15
  65. package/dist/{chunk-HTWQH3VI.js → chunk-FVTFSYKB.js} +1 -1
  66. package/dist/{chunk-HJYGC7IR.js → chunk-GOPIMNVC.js} +1 -1
  67. package/dist/{chunk-DLLAWCQB.js → chunk-GOQHN2UH.js} +1 -1
  68. package/dist/{chunk-LVU3Q43J.js → chunk-HBBQO77U.js} +1 -1
  69. package/dist/{chunk-CVZP67ST.js → chunk-HHODMHTH.js} +4 -4
  70. package/dist/{chunk-3OXHYPZG.js → chunk-HKQ6CD32.js} +4 -1
  71. package/dist/{chunk-TGORRMOU.js → chunk-JZSWA3VT.js} +1 -1
  72. package/dist/{chunk-MNWDOXX6.js → chunk-KAWLZTRJ.js} +1 -1
  73. package/dist/{chunk-7W6E3C3I.js → chunk-KNIVXEW4.js} +1 -1
  74. package/dist/{chunk-D6PRC3YQ.js → chunk-LZQVAEWP.js} +2 -1
  75. package/dist/{chunk-JAKWHALW.js → chunk-O5RQ7RYJ.js} +90 -18
  76. package/dist/{chunk-TR6DBXO3.js → chunk-OIZVYUED.js} +1 -1
  77. package/dist/{chunk-V4G7BQZ6.js → chunk-R4MBUCPQ.js} +1 -1
  78. package/dist/{chunk-XDHJ4KZJ.js → chunk-RPHXG4HI.js} +2 -2
  79. package/dist/{chunk-J5KTLGO5.js → chunk-SLVXQOYU.js} +1 -1
  80. package/dist/{chunk-AGQA72IH.js → chunk-SRFA5BOO.js} +1 -1
  81. package/dist/{chunk-7VVX6HQG.js → chunk-VZADMT7R.js} +219 -17
  82. package/dist/{chunk-J6NAWJTQ.js → chunk-WRK6RAA5.js} +47 -4
  83. package/dist/{chunk-ZKDQ3QRP.js → chunk-WZ63WTAZ.js} +1 -1
  84. package/dist/{chunk-57NOPUBK.js → chunk-XJ6CXTSB.js} +8 -8
  85. package/dist/{chunk-4NJURLRP.js → chunk-XKIB2FQY.js} +8 -8
  86. package/dist/{chunk-2BE7ZVWP.js → chunk-XS33QBJA.js} +15 -3
  87. package/dist/{chunk-MQO7J2OJ.js → chunk-ZJ6QXXBC.js} +10 -7
  88. package/dist/{chunk-XIVPHGV4.js → chunk-ZQREURP6.js} +1 -1
  89. package/dist/src/index.d.ts +3 -0
  90. package/dist/src/index.js +11 -11
  91. package/package.json +1 -1
@@ -21,6 +21,168 @@ var COWORK_STANDING_INSTRUCTIONS = `Pushary is connected as a custom connector.
21
21
  - Cancel a question when it is no longer needed. Once this turn ends, reopen Cowork for follow-up work; this connector cannot restart it.
22
22
  - If Pushary tools are missing or disabled, report that once and continue within my authorization. Do not claim a notification or approval was delivered.`;
23
23
 
24
+ // ../contracts/src/agent-manifest.ts
25
+ var defaults = {
26
+ hasStoredIdentity: true,
27
+ hookSource: null,
28
+ policyAgent: null,
29
+ repoAware: false,
30
+ disconnectable: false,
31
+ managedProvider: false,
32
+ enforcement: "cooperative",
33
+ connectVia: "Plain MCP",
34
+ setupCommand: null,
35
+ setupChoice: null,
36
+ setupGuide: "",
37
+ activationGuide: null,
38
+ enforcementDetail: "Cooperative. The agent chooses when to ask; no native permission interception.",
39
+ capabilityNote: "",
40
+ documented: true
41
+ };
42
+ var AGENTS = {
43
+ claude_code: {
44
+ ...defaults,
45
+ label: "Claude Code",
46
+ activationGuide: "/docs/agents/guides/claude-code#verify-a-real-approval",
47
+ hookSource: "claude",
48
+ policyAgent: "claude_code",
49
+ repoAware: true,
50
+ disconnectable: true,
51
+ managedProvider: true,
52
+ enforcement: "enforced",
53
+ connectVia: "Native hooks + MCP",
54
+ setupCommand: "npx @pushary/agent-hooks setup",
55
+ enforcementDetail: "Shell, writes and edits, subject to Claude Code permission mode and explicit policy."
56
+ },
57
+ claude_chat: {
58
+ ...defaults,
59
+ label: "Claude Chat",
60
+ connectVia: "Custom MCP connector",
61
+ setupGuide: "[Claude.ai and Claude Desktop setup](/docs/agents/guides/claude-desktop)"
62
+ },
63
+ claude_cowork: {
64
+ ...defaults,
65
+ label: "Claude Cowork",
66
+ connectVia: "Custom MCP connector + optional Cowork skill",
67
+ setupGuide: "[Cowork setup](/docs/agents/guides/claude-desktop#use-it-in-cowork)",
68
+ enforcementDetail: COWORK_CONNECTOR_LIMIT
69
+ },
70
+ codex: {
71
+ ...defaults,
72
+ label: "Codex",
73
+ hookSource: "codex",
74
+ policyAgent: "codex",
75
+ repoAware: true,
76
+ disconnectable: true,
77
+ managedProvider: true,
78
+ enforcement: "enforced",
79
+ connectVia: "Native hooks + MCP",
80
+ setupCommand: "npx @pushary/agent-hooks setup",
81
+ enforcementDetail: "Shell and apply_patch edits through native permission hooks; requires a compatible Codex runtime."
82
+ },
83
+ chatgpt: {
84
+ ...defaults,
85
+ label: "ChatGPT",
86
+ connectVia: "Custom MCP connector",
87
+ setupGuide: "[ChatGPT setup](/docs/agents/guides/chatgpt)"
88
+ },
89
+ gemini_cli: {
90
+ ...defaults,
91
+ label: "Gemini CLI",
92
+ hookSource: "gemini",
93
+ policyAgent: "gemini_cli",
94
+ repoAware: true,
95
+ disconnectable: true,
96
+ managedProvider: true,
97
+ enforcement: "enforced",
98
+ connectVia: "Native hooks + MCP",
99
+ setupCommand: "npx @pushary/agent-hooks setup",
100
+ enforcementDetail: "Shell, writes and edits through native BeforeTool hooks."
101
+ },
102
+ hermes: {
103
+ ...defaults,
104
+ label: "Hermes",
105
+ policyAgent: "hermes",
106
+ enforcement: "enforced",
107
+ connectVia: "Native Python plugin + approval transport",
108
+ setupCommand: "npx @pushary/agent-hooks setup",
109
+ enforcementDetail: "Hermes dangerous-command approvals when the Pushary transport is selected; optional PUSHARY_GATE_TOOLS gates named tools.",
110
+ capabilityNote: "Uses the Python plugin, not a macOS bridge HookSource. CLI disconnect and managed-provider controls are not implemented for Hermes."
111
+ },
112
+ cursor: {
113
+ ...defaults,
114
+ label: "Cursor",
115
+ hookSource: "cursor",
116
+ policyAgent: "cursor",
117
+ disconnectable: true,
118
+ enforcement: "enforced",
119
+ connectVia: "Editor plugin hooks + MCP",
120
+ setupCommand: "npx @pushary/agent-hooks setup",
121
+ enforcementDetail: "With installed hooks: shell, MCP execution and Write/Delete/Edit tool calls. Plain MCP remains cooperative."
122
+ },
123
+ vscode: {
124
+ ...defaults,
125
+ label: "VS Code",
126
+ hookSource: "vscode",
127
+ policyAgent: "vscode",
128
+ enforcement: "enforced",
129
+ connectVia: "Agent plugin hooks + MCP",
130
+ setupCommand: "npx @pushary/agent-hooks setup",
131
+ enforcementDetail: "PreToolUse approval hooks from the installed agent plugin; the host decides which events it exposes.",
132
+ capabilityNote: "Has both a bridge HookSource and a per-agent policy profile. CLI disconnect and managed-provider controls are not implemented for VS Code."
133
+ },
134
+ opencode: {
135
+ ...defaults,
136
+ label: "OpenCode",
137
+ hookSource: "opencode",
138
+ policyAgent: "opencode",
139
+ repoAware: true,
140
+ disconnectable: true,
141
+ enforcement: "enforced",
142
+ connectVia: "Generated plugin + MCP",
143
+ setupCommand: "npx @pushary/agent-hooks setup",
144
+ enforcementDetail: "Shell, writes, edits and web fetches when OpenCode itself asks for permission."
145
+ },
146
+ windsurf: {
147
+ ...defaults,
148
+ label: "Windsurf",
149
+ setupCommand: "npx @pushary/agent-hooks setup",
150
+ setupChoice: "Other"
151
+ },
152
+ unknown: {
153
+ ...defaults,
154
+ label: "Agent",
155
+ documented: false
156
+ },
157
+ lovable: {
158
+ ...defaults,
159
+ label: "Lovable",
160
+ setupGuide: "[Lovable setup](/docs/agents/guides/lovable)"
161
+ },
162
+ mcp: {
163
+ ...defaults,
164
+ hasStoredIdentity: false,
165
+ label: "Any MCP client",
166
+ setupCommand: "npx @pushary/agent-hooks setup",
167
+ setupChoice: "Other"
168
+ },
169
+ http: {
170
+ ...defaults,
171
+ hasStoredIdentity: false,
172
+ label: "No-code and HTTP",
173
+ connectVia: "REST with a Bearer token",
174
+ setupGuide: "[Connect any agent](/docs/agents/connect-any-agent)"
175
+ }
176
+ };
177
+ var HOOK_SOURCES = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.hookSource === null ? [] : [agent.hookSource]));
178
+ var POLICY_AGENTS = [...Object.values(AGENTS).flatMap((agent) => agent.policyAgent === null ? [] : [agent.policyAgent]), "*"];
179
+ var AGENT_LABELS_BY_TYPE = Object.fromEntries(
180
+ Object.entries(AGENTS).flatMap(([id, agent]) => agent.hasStoredIdentity ? [[id, agent.label]] : [])
181
+ );
182
+ var DISCONNECTABLE_AGENTS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.disconnectable ? [agent.hookSource] : []));
183
+ var REPO_AWARE_AGENTS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.repoAware ? [agent.policyAgent] : []));
184
+ var MANAGED_PROVIDERS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.managedProvider ? [agent.hookSource] : []));
185
+
24
186
  // ../contracts/src/index.ts
25
187
  var KEY_SCOPES = ["full_access", "agent", "connector", "bound"];
26
188
  var normalizeKeyScope = (value) => typeof value === "string" && KEY_SCOPES.includes(value) ? value : null;
@@ -140,21 +302,6 @@ var hookWaitDeadline = (startMs, policyWaitSeconds, agent, nowMs) => {
140
302
  };
141
303
  var hookWaitClamped = (startMs, policyWaitSeconds, agent, nowMs) => startMs + hookMaxWaitSeconds(agent) * 1e3 < nowMs + Math.max(policyWaitSeconds, 0) * 1e3;
142
304
  var effectiveWaitSeconds = (timeoutAction, policySeconds, agent) => timeoutAction === "wait" ? hookMaxWaitSeconds(agent) : policySeconds;
143
- var HOOK_SOURCES = ["claude", "codex", "gemini", "cursor", "vscode", "opencode"];
144
- var AGENT_LABELS_BY_TYPE = {
145
- claude_code: "Claude Code",
146
- claude_chat: "Claude Chat",
147
- claude_cowork: "Claude Cowork",
148
- codex: "Codex",
149
- chatgpt: "ChatGPT",
150
- gemini_cli: "Gemini CLI",
151
- hermes: "Hermes",
152
- cursor: "Cursor",
153
- vscode: "VS Code",
154
- opencode: "OpenCode",
155
- windsurf: "Windsurf",
156
- unknown: "Agent"
157
- };
158
305
  var OPENCODE_APPROVAL_EVENT = "permission.ask";
159
306
  var CURSOR_GATE_EVENTS = ["beforeShellExecution", "beforeMCPExecution", "preToolUse"];
160
307
  var HOOK_LOCATOR_BINARY = "pushary-bridge";
@@ -813,6 +960,23 @@ p:12:small=n:1e-7`
813
960
  }
814
961
  ];
815
962
  var AUTHORIZATION_PERMIT_TTL_SECONDS = 60 * 60;
963
+ var ANSWER_SOURCES = [
964
+ "dashboard",
965
+ "mobile_app",
966
+ "mobile_background",
967
+ "decide_page",
968
+ "live_page",
969
+ "answer_link",
970
+ "inbox",
971
+ "slack",
972
+ "mac_app",
973
+ // Nobody. A decision opened on a workspace's sandbox site is settled by the
974
+ // simulator instead of being delivered, and the ledger has to say so: a
975
+ // sandbox row that looked like a human answer would be indistinguishable
976
+ // evidence of a person who was never asked.
977
+ "sandbox"
978
+ ];
979
+ var isAnswerSource = (value) => typeof value === "string" && ANSWER_SOURCES.includes(value);
816
980
  var classifyDecisionRisk = (toolName, toolTarget) => {
817
981
  const parts = [toolName, toolTarget ?? ""];
818
982
  const matchesAny = (patterns) => parts.some((part) => patterns.some((re) => re.test(part)));
@@ -1111,6 +1275,17 @@ var shouldDeferToNativeMode = (permissionMode, toolName, options = {}) => {
1111
1275
  var ASKED_IN_EVERY_MODE_TOOLS = ["AskUserQuestion", "ExitPlanMode"];
1112
1276
  var ASKED_IN_EVERY_MODE = new Set(ASKED_IN_EVERY_MODE_TOOLS);
1113
1277
  var isAskedInEveryMode = (toolName) => ASKED_IN_EVERY_MODE.has(toolName);
1278
+ var PRETOOLUSE_GATED_TOOLS = [
1279
+ "Bash",
1280
+ "Write",
1281
+ "Edit",
1282
+ "MultiEdit",
1283
+ "NotebookEdit",
1284
+ "PowerShell",
1285
+ "Monitor"
1286
+ ];
1287
+ var PRETOOLUSE_HANDLED_TOOLS = /* @__PURE__ */ new Set([...PRETOOLUSE_GATED_TOOLS, ...ASKED_IN_EVERY_MODE_TOOLS]);
1288
+ var isPermissionRequestOwnedByPreToolUse = (toolName, permissionMode, defers = shouldDeferToNativeMode) => PRETOOLUSE_HANDLED_TOOLS.has(toolName) && (isAskedInEveryMode(toolName) || !defers(permissionMode, toolName));
1114
1289
  var resolveGate = (input) => {
1115
1290
  const { modeState, config, toolName, toolInputs, cwd, repoKey, sessionId } = input;
1116
1291
  if (isPusharyOwnTool(toolName)) {
@@ -1368,6 +1543,23 @@ var elicitationContentFromAnswers = (ask, answers) => {
1368
1543
  return content;
1369
1544
  };
1370
1545
 
1546
+ // ../contracts/src/hook-activation.ts
1547
+ var hookWritePath = (toolName, input) => toolName === "Write" && typeof input.file_path === "string" && input.file_path.startsWith("/") && input.file_path.length <= 4096 ? input.file_path : void 0;
1548
+ var HOOK_ACTIVATION_RESULTS = ["unverified", "approved_executed", "denied_observed", "unexpected_execution", "tool_failed"];
1549
+ var isHookActivationReport = (value) => {
1550
+ if (!value || typeof value !== "object") return false;
1551
+ const report = value;
1552
+ return typeof report.agentType === "string" && typeof report.machineId === "string" && typeof report.target === "string" && report.target.length > 0 && report.target.length <= 4096 && typeof report.truncated === "boolean" && Array.isArray(report.evidence) && report.evidence.length <= 20 && (!report.truncated || report.evidence.length === 0) && report.evidence.every((item) => {
1553
+ if (!item || typeof item !== "object") return false;
1554
+ const row = item;
1555
+ return ["correlationId", "sessionId", "toolUseId", "toolName", "createdAt"].every((key) => typeof row[key] === "string" && row[key].length > 0 && row[key].length <= 256) && (row.answerSource === null || isAnswerSource(row.answerSource)) && HOOK_ACTIVATION_RESULTS.includes(row.result) && Number.isFinite(Date.parse(row.createdAt)) && (row.occurredAt === null || typeof row.occurredAt === "string" && Number.isFinite(Date.parse(row.occurredAt))) && (row.observedAt === null || typeof row.observedAt === "string" && Number.isFinite(Date.parse(row.observedAt))) && (row.result === "unverified" || row.occurredAt !== null && row.observedAt !== null && row.producer !== null && isAnswerSource(row.answerSource) && row.answerSource !== "sandbox") && (row.producer === null || row.producer === "hook" || row.producer === "uplink");
1556
+ });
1557
+ };
1558
+ var hookActivationResult = (report) => {
1559
+ if (report?.evidence.some((row) => row.result === "unexpected_execution")) return "unexpected_execution";
1560
+ return report && !report.truncated && report.evidence.length === 1 ? report.evidence[0].result : "unverified";
1561
+ };
1562
+
1371
1563
  export {
1372
1564
  CLAUDE_CONNECTOR_URL,
1373
1565
  CLAUDE_CONNECTOR_GUIDE,
@@ -1375,6 +1567,15 @@ export {
1375
1567
  CLAUDE_CONNECTOR_STEPS,
1376
1568
  CLAUDE_CONNECTOR_TEST,
1377
1569
  COWORK_STANDING_INSTRUCTIONS,
1570
+ AGENTS,
1571
+ HOOK_SOURCES,
1572
+ AGENT_LABELS_BY_TYPE,
1573
+ DISCONNECTABLE_AGENTS,
1574
+ REPO_AWARE_AGENTS,
1575
+ MANAGED_PROVIDERS,
1576
+ hookWritePath,
1577
+ isHookActivationReport,
1578
+ hookActivationResult,
1378
1579
  normalizeKeyScope,
1379
1580
  parseAgentQuestion,
1380
1581
  parseAgentQuestions,
@@ -1384,8 +1585,6 @@ export {
1384
1585
  hookWaitDeadline,
1385
1586
  hookWaitClamped,
1386
1587
  effectiveWaitSeconds,
1387
- HOOK_SOURCES,
1388
- AGENT_LABELS_BY_TYPE,
1389
1588
  OPENCODE_APPROVAL_EVENT,
1390
1589
  CURSOR_GATE_EVENTS,
1391
1590
  HOOK_LOCATOR_BINARY,
@@ -1399,6 +1598,7 @@ export {
1399
1598
  DECISION_LINE_MAX,
1400
1599
  redactSecrets,
1401
1600
  redactSecretsDeep,
1601
+ isAnswerSource,
1402
1602
  buildDecisionEpisodeFeatures,
1403
1603
  resolveAutoResolveOrigin,
1404
1604
  resolvePolicy,
@@ -1406,6 +1606,8 @@ export {
1406
1606
  isPusharyOwnTool,
1407
1607
  shouldDeferToNativeMode,
1408
1608
  ASKED_IN_EVERY_MODE_TOOLS,
1609
+ PRETOOLUSE_GATED_TOOLS,
1610
+ isPermissionRequestOwnedByPreToolUse,
1409
1611
  resolveGate,
1410
1612
  modeStateFromResponse,
1411
1613
  stoppedOutput,
@@ -1,9 +1,25 @@
1
+ import {
2
+ detectProviderLiveCapabilities,
3
+ hasSpawnProvider,
4
+ resolveControlPolicy
5
+ } from "./chunk-HKQ6CD32.js";
6
+ import {
7
+ daemonHasRemoteProof,
8
+ daemonServiceManagerAvailable,
9
+ hasDaemonServiceDefinition,
10
+ readDaemonNeedsLogin,
11
+ readDaemonStatus
12
+ } from "./chunk-7N3D4GN7.js";
1
13
  import {
2
14
  getMachineId
3
- } from "./chunk-O7ODXFII.js";
15
+ } from "./chunk-C7BFOC35.js";
4
16
  import {
5
17
  getPackageVersion
6
18
  } from "./chunk-DINDL2CF.js";
19
+ import {
20
+ readControlMode,
21
+ readKeySource
22
+ } from "./chunk-6MRPTYYH.js";
7
23
  import {
8
24
  getBaseUrl
9
25
  } from "./chunk-2UMNXADU.js";
@@ -50,7 +66,7 @@ var parseMachine = (value) => {
50
66
  online: raw.online === true
51
67
  };
52
68
  };
53
- var machineReadiness = (machines, machineId, expectedCapabilities) => {
69
+ var machineReadiness = (machines, machineId, expectedCapabilities, now = Date.now()) => {
54
70
  const machine = machines.find((item) => item.machineId === machineId);
55
71
  if (!machine) return { kind: "not-ready", detail: "this machine has not heartbeated to the server" };
56
72
  if (!machine.online) return { kind: "not-ready", detail: "the server reports this machine offline" };
@@ -60,12 +76,15 @@ var machineReadiness = (machines, machineId, expectedCapabilities) => {
60
76
  if (machine.lastControlAt === null || !Number.isFinite(Date.parse(machine.lastControlAt))) {
61
77
  return { kind: "not-ready", detail: "the server has not observed this machine polling the control queue" };
62
78
  }
79
+ if (!daemonHasRemoteProof({ kind: "running", lastSuccessfulHeartbeatAt: machine.lastSeenAt, lastSuccessfulControlAt: machine.lastControlAt }, now)) {
80
+ return { kind: "not-ready", detail: "the heartbeat or control-queue proof is stale; wake this machine and retry pushary doctor" };
81
+ }
63
82
  const missing = expectedCapabilities.filter((capability) => !machine.capabilities.includes(capability));
64
83
  return missing.length === 0 ? { kind: "ready", machine } : { kind: "not-ready", detail: `the server is missing capabilities: ${missing.join(", ")}` };
65
84
  };
66
85
  var readMachineReadiness = async (apiKey, expectedCapabilities, deps = {}) => {
67
86
  const listed = await listMachines(apiKey, deps);
68
- return listed.kind === "ok" ? machineReadiness(listed.machines, deps.machineId ?? getMachineId(), expectedCapabilities) : listed.kind === "unsupported" ? { kind: "unsupported", detail: "the server does not report machine readiness yet" } : listed;
87
+ return listed.kind === "ok" ? machineReadiness(listed.machines, deps.machineId ?? getMachineId(), expectedCapabilities, deps.now?.()) : listed.kind === "unsupported" ? { kind: "unsupported", detail: "the server does not report machine readiness yet" } : listed;
69
88
  };
70
89
  var waitForMachineReadiness = async (apiKey, expectedCapabilities, deps = {}) => {
71
90
  const attempts = deps.attempts ?? 21;
@@ -106,6 +125,29 @@ var describeLastSeen = (iso, now = Date.now()) => {
106
125
  if (hours < 48) return `${hours}h ago`;
107
126
  return `${Math.round(hours / 24)}d ago`;
108
127
  };
128
+ var readManagedControlEvidence = (apiKey, providers, serverProof) => {
129
+ const now = Date.now();
130
+ const daemon = readDaemonStatus();
131
+ const capabilities = detectProviderLiveCapabilities(providers);
132
+ const providerAvailable = hasSpawnProvider(capabilities);
133
+ const managerAvailable = daemonServiceManagerAvailable();
134
+ const serviceInstalled = hasDaemonServiceDefinition();
135
+ const keyCurrent = Boolean(apiKey) && readKeySource().key === apiKey && readDaemonNeedsLogin().kind !== "blocked";
136
+ const policy = resolveControlPolicy({ mode: readControlMode(), providerAvailable, managerAvailable, servicePresent: serviceInstalled || daemon.kind !== "absent" });
137
+ const localProof = daemonHasRemoteProof(daemon, now);
138
+ const remote = serverProof?.kind === "ready" ? machineReadiness([serverProof.machine], daemon.machineId ?? "", ["spawn", ...capabilities], now) : serverProof;
139
+ return {
140
+ ready: keyCurrent && policy === "enabled" && providerAvailable && managerAvailable && serviceInstalled && localProof && remote?.kind === "ready",
141
+ keyCurrent,
142
+ providerAvailable,
143
+ managerAvailable,
144
+ serviceInstalled,
145
+ policy,
146
+ localProof,
147
+ serverProof: remote?.kind ?? "unchecked",
148
+ checkedAt: new Date(now).toISOString()
149
+ };
150
+ };
109
151
 
110
152
  export {
111
153
  HEARTBEAT_INTERVAL_MS,
@@ -113,5 +155,6 @@ export {
113
155
  readMachineReadiness,
114
156
  waitForMachineReadiness,
115
157
  listMachines,
116
- describeLastSeen
158
+ describeLastSeen,
159
+ readManagedControlEvidence
117
160
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  writeJsonAtomicDurable
3
- } from "./chunk-MNWDOXX6.js";
3
+ } from "./chunk-KAWLZTRJ.js";
4
4
 
5
5
  // src/transcript-recipient-pin.ts
6
6
  import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-TR6DBXO3.js";
3
+ } from "./chunk-OIZVYUED.js";
4
4
  import {
5
5
  addClaudeMcpServer,
6
6
  addPusharyHooks,
7
7
  addPusharyToolPermissions
8
- } from "./chunk-CVZP67ST.js";
8
+ } from "./chunk-HHODMHTH.js";
9
9
  import {
10
10
  claudeWired,
11
11
  codexWired,
12
12
  geminiWired
13
- } from "./chunk-TMOCSYJK.js";
13
+ } from "./chunk-A2M6FGUF.js";
14
14
  import {
15
15
  CLAUDE_HOOK_EVENTS,
16
16
  GEMINI_HOOK_BINARY,
@@ -21,10 +21,10 @@ import {
21
21
  needsReregistration,
22
22
  registrationSignatures,
23
23
  supportedClaudeEvents
24
- } from "./chunk-TKA2L2BU.js";
24
+ } from "./chunk-CDBTCEKE.js";
25
25
  import {
26
26
  guardBinary
27
- } from "./chunk-GGX5QTBG.js";
27
+ } from "./chunk-3AJIK5AO.js";
28
28
  import {
29
29
  resolveGlobalBinDir,
30
30
  resolveGlobalBinary
@@ -40,14 +40,14 @@ import {
40
40
  codexHooksJson,
41
41
  hasCodexHooks,
42
42
  vscodePluginMcp
43
- } from "./chunk-W5GH5GTX.js";
43
+ } from "./chunk-FBO5WS3D.js";
44
44
  import {
45
45
  readJsonSafe,
46
46
  writeJsonAtomic
47
- } from "./chunk-MNWDOXX6.js";
47
+ } from "./chunk-KAWLZTRJ.js";
48
48
  import {
49
49
  HOOK_LOCATOR_BINARY
50
- } from "./chunk-7VVX6HQG.js";
50
+ } from "./chunk-VZADMT7R.js";
51
51
 
52
52
  // src/reapply.ts
53
53
  import { join } from "path";
@@ -3,19 +3,19 @@ import {
3
3
  loadPin,
4
4
  savePin,
5
5
  verifyRecipients
6
- } from "./chunk-ZKDQ3QRP.js";
6
+ } from "./chunk-WZ63WTAZ.js";
7
7
  import {
8
8
  hookOwnerOf
9
- } from "./chunk-MZOTKCNZ.js";
9
+ } from "./chunk-BS5XR4J7.js";
10
10
  import {
11
11
  ensureDaemonRunning
12
- } from "./chunk-ND52YE7L.js";
12
+ } from "./chunk-7N3D4GN7.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
16
  generateSessionKey,
17
17
  wrapSessionKey
18
- } from "./chunk-JDNW6FTV.js";
18
+ } from "./chunk-DH4INZW3.js";
19
19
  import {
20
20
  fetchTranscriptRecipients,
21
21
  uploadTranscriptRecords
@@ -23,17 +23,17 @@ import {
23
23
  import {
24
24
  transcriptEnvironmentPreference,
25
25
  transcriptsEnabled
26
- } from "./chunk-TGORRMOU.js";
26
+ } from "./chunk-JZSWA3VT.js";
27
27
  import {
28
28
  codexHome,
29
29
  pusharyDir
30
- } from "./chunk-W5GH5GTX.js";
30
+ } from "./chunk-FBO5WS3D.js";
31
31
  import {
32
32
  readTranscriptPreference
33
- } from "./chunk-3N4FT4WQ.js";
33
+ } from "./chunk-6MRPTYYH.js";
34
34
  import {
35
35
  writeJsonAtomic
36
- } from "./chunk-MNWDOXX6.js";
36
+ } from "./chunk-KAWLZTRJ.js";
37
37
  import {
38
38
  getApiKey,
39
39
  getBaseUrl
@@ -12,7 +12,7 @@ import { randomUUID } from "crypto";
12
12
  var isPolicyConfig = (data) => {
13
13
  if (!data || typeof data !== "object") return false;
14
14
  const d = data;
15
- return Array.isArray(d.policies) && typeof d.defaultTimeoutSeconds === "number" && typeof d.defaultTimeoutAction === "string";
15
+ return (d.resolvedAgent === void 0 || typeof d.resolvedAgent === "string") && Array.isArray(d.policies) && typeof d.defaultTimeoutSeconds === "number" && typeof d.defaultTimeoutAction === "string";
16
16
  };
17
17
  var isAskUserResponse = (data) => {
18
18
  if (!data || typeof data !== "object") return false;
@@ -45,13 +45,24 @@ var waitForAnswer = async (apiKey, correlationId, timeoutMs = 3e4, signal) => {
45
45
  if (!isWaitForAnswerResponse(result)) throw new Error("Invalid wait_for_answer response");
46
46
  return result;
47
47
  };
48
+ var WAIT_RESPONSE_GRACE_MS = 2e3;
49
+ var MIN_LONG_POLL_MS = 1e3;
50
+ var MAX_LONG_POLL_MS = 3e4;
51
+ var nextLongPollMs = (deadlineMs, nowMs) => {
52
+ const hold = Math.min(deadlineMs - nowMs - WAIT_RESPONSE_GRACE_MS, MAX_LONG_POLL_MS);
53
+ return hold >= MIN_LONG_POLL_MS ? hold : null;
54
+ };
48
55
  var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3) => {
49
56
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
50
57
  while (Date.now() < deadlineMs) {
51
- const remaining = Math.min(Math.max(deadlineMs - Date.now(), 1e3), 3e4);
58
+ const hold = nextLongPollMs(deadlineMs, Date.now());
59
+ if (hold === null) {
60
+ await sleep(Math.max(0, deadlineMs - Date.now()));
61
+ continue;
62
+ }
52
63
  let answer;
53
64
  try {
54
- answer = await waitForAnswer(apiKey, correlationId, remaining, AbortSignal.timeout(Math.max(1, deadlineMs - Date.now())));
65
+ answer = await waitForAnswer(apiKey, correlationId, hold, AbortSignal.timeout(Math.max(1, deadlineMs - Date.now())));
55
66
  } catch {
56
67
  const late = await withdrawQuestion(apiKey, correlationId);
57
68
  return late.answered ? late : STOPPED;
@@ -93,6 +104,7 @@ export {
93
104
  isLocalQuestionHandoff,
94
105
  askUser,
95
106
  waitForAnswer,
107
+ nextLongPollMs,
96
108
  cancelQuestion,
97
109
  withdrawQuestion,
98
110
  waitForQuestion,
@@ -3,7 +3,7 @@ import {
3
3
  isPolicyConfig,
4
4
  sendNotification,
5
5
  waitForAnswer
6
- } from "./chunk-2BE7ZVWP.js";
6
+ } from "./chunk-XS33QBJA.js";
7
7
  import {
8
8
  AGENT_IDENTITIES,
9
9
  TEAMMATE_IDLE_ACTION,
@@ -24,20 +24,20 @@ import {
24
24
  summarizeStop,
25
25
  taskTitleFrom,
26
26
  toolResultErrorText
27
- } from "./chunk-LVU3Q43J.js";
27
+ } from "./chunk-HBBQO77U.js";
28
28
  import {
29
29
  NOTIFICATION_PUSH_TYPES,
30
30
  POSTTOOLUSE_MIRROR_ONLY_TOOLS
31
- } from "./chunk-YE37HYN3.js";
31
+ } from "./chunk-32WWVRXN.js";
32
32
  import {
33
33
  getMachineId
34
- } from "./chunk-O7ODXFII.js";
34
+ } from "./chunk-C7BFOC35.js";
35
35
  import {
36
36
  withRetry
37
37
  } from "./chunk-EYRGBYZG.js";
38
38
  import {
39
39
  reportedInstallSource
40
- } from "./chunk-MNWDOXX6.js";
40
+ } from "./chunk-KAWLZTRJ.js";
41
41
  import {
42
42
  getApiKey,
43
43
  getBaseUrl
@@ -45,6 +45,7 @@ import {
45
45
  import {
46
46
  HOOK_SOURCES,
47
47
  PENDING_COMMAND_PREFIX,
48
+ REPO_AWARE_AGENTS,
48
49
  gateOutputFor,
49
50
  isAutoApprove,
50
51
  localRepoKey,
@@ -53,7 +54,7 @@ import {
53
54
  pendingCommandEvents,
54
55
  resolveAutoResolveOrigin,
55
56
  resolvePolicy
56
- } from "./chunk-7VVX6HQG.js";
57
+ } from "./chunk-VZADMT7R.js";
57
58
 
58
59
  // src/policy.ts
59
60
  import { createHash } from "crypto";
@@ -62,7 +63,6 @@ import { join } from "path";
62
63
  import { tmpdir } from "os";
63
64
  var CACHE_TTL_MS = 60 * 1e3;
64
65
  var STALE_CACHE_MAX_MS = 12 * 60 * 60 * 1e3;
65
- var REPO_AWARE_AGENTS = ["claude_code", "codex", "gemini_cli", "opencode"];
66
66
  var policyRequestFor = (agentType) => ({
67
67
  agent: agentType,
68
68
  repoAware: REPO_AWARE_AGENTS.includes(agentType)
@@ -1004,6 +1004,7 @@ var settleQuestionsForToolUse = async (sessionId, toolUseId) => {
1004
1004
  for (const answer of late) removePendingQuestion(sessionId, answer.correlationId);
1005
1005
  };
1006
1006
  var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1007
+ const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
1007
1008
  const deadline = Date.now() + HOOK_BUDGET_MS;
1008
1009
  const signal = AbortSignal.timeout(HOOK_BUDGET_MS);
1009
1010
  try {
@@ -1028,6 +1029,8 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1028
1029
  const beacon = !isMirrorOnly && decisionSource === "policy_auto" && throttlePass(`autodecision:${sessionKey}:${lookup.tool}`, AUTO_DECISION_WINDOW_MS) ? deriveAutoDecisionBeacon(lookup.tool, lookup.input, liveMode, agent.type, input.cwd, input.session_id) : void 0;
1029
1030
  const toolReport = reportEvent({
1030
1031
  event: isError ? "tool_error" : "tool_complete",
1032
+ occurredAt,
1033
+ toolUseId: input.tool_use_id,
1031
1034
  agentType: agent.type,
1032
1035
  agentName: `${agent.label} - ${projectName}`,
1033
1036
  action,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-KT2CGUOE.js";
4
+ } from "./chunk-6II5RBAS.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
@@ -34,6 +34,8 @@ interface AgentIdentity {
34
34
  readonly label: string;
35
35
  }
36
36
  interface AgentEvent {
37
+ occurredAt?: string;
38
+ toolUseId?: string;
37
39
  event: string;
38
40
  agentType: string;
39
41
  agentName?: string;
@@ -147,6 +149,7 @@ interface AskUserParams {
147
149
  machineId?: string;
148
150
  toolName?: string;
149
151
  toolTarget?: string;
152
+ toolPath?: string;
150
153
  repoKey?: string;
151
154
  intent?: string;
152
155
  action?: string;