@pushary/agent-hooks 0.88.0 → 0.89.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.
- package/CHANGELOG.md +44 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +63 -7
- package/data/vscode-plugin/scripts/pushary-gate.mjs +56 -18
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +7 -7
- package/dist/bin/pushary-claude.js +9 -9
- package/dist/bin/pushary-clean.js +11 -13
- package/dist/bin/pushary-codex-bridge.js +6 -6
- package/dist/bin/pushary-codex-hook.js +5 -5
- package/dist/bin/pushary-codex.js +4 -4
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +5 -5
- package/dist/bin/pushary-disconnect.js +9 -11
- package/dist/bin/pushary-doctor.js +15 -15
- package/dist/bin/pushary-elicitation-hook.js +7 -120
- package/dist/bin/pushary-gemini-bridge.js +6 -6
- package/dist/bin/pushary-gemini-hook.js +5 -5
- package/dist/bin/pushary-hook.js +6 -6
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +4 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +4 -4
- package/dist/bin/pushary-opencode-hook.js +5 -5
- package/dist/bin/pushary-permission-denied-hook.js +6 -6
- package/dist/bin/pushary-permission-hook.js +6 -6
- package/dist/bin/pushary-post-hook.js +4 -4
- package/dist/bin/pushary-prompt-hook.js +4 -4
- package/dist/bin/pushary-session-end-hook.js +4 -4
- package/dist/bin/pushary-session-start-hook.js +4 -4
- package/dist/bin/pushary-setup.js +32 -29
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +5 -5
- package/dist/bin/pushary-stop-hook.js +4 -4
- package/dist/bin/pushary-stopfailure-hook.js +4 -4
- package/dist/bin/pushary-upgrade.js +10 -12
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-OULRSHFB.js → chunk-4FANZF2L.js} +2 -2
- package/dist/{chunk-DSTZ2RBU.js → chunk-4KMWTOBK.js} +4 -1
- package/dist/{chunk-LEYXXXRN.js → chunk-6GBQDZ7K.js} +1 -1
- package/dist/{chunk-B7ZBHWOW.js → chunk-AIGDBRIJ.js} +1 -1
- package/dist/{chunk-VSCSZ2LI.js → chunk-AJ4EMW5D.js} +1 -1
- package/dist/{chunk-DXBUFXPL.js → chunk-DLI5IXSD.js} +1 -1
- package/dist/{chunk-67TNJVJW.js → chunk-EKNFTIKO.js} +1 -1
- package/dist/{chunk-MY5A4EPQ.js → chunk-F3ZDPL6O.js} +187 -60
- package/dist/{chunk-VHHVGVFQ.js → chunk-FRH2QIMY.js} +1 -1
- package/dist/{chunk-XFHMGVMX.js → chunk-LNAQWWBP.js} +1 -1
- package/dist/{chunk-F5W3AY6L.js → chunk-LXTX5CT7.js} +1 -1
- package/dist/{chunk-T2XZH7D5.js → chunk-O7P7O4VY.js} +2 -2
- package/dist/{chunk-MNXG6IIM.js → chunk-OSE7X44J.js} +3 -3
- package/dist/{chunk-4VYELDAH.js → chunk-PXEFENML.js} +1 -1
- package/dist/{chunk-3JL64GVE.js → chunk-Q6MK2YKV.js} +120 -1
- package/dist/{chunk-ESARAYRI.js → chunk-SHVW5KFR.js} +1 -1
- package/dist/{chunk-RSUBLTMP.js → chunk-SUZF64M4.js} +2 -2
- package/dist/{chunk-3B3PYTKH.js → chunk-U44C3ULB.js} +1 -1
- package/dist/{chunk-UTQCV46U.js → chunk-U65EIMUJ.js} +4 -4
- package/dist/{chunk-6BOE5NJZ.js → chunk-WFFPWJN6.js} +1 -1
- package/dist/{chunk-WXCHB6BO.js → chunk-WPDMGNQR.js} +1 -1
- package/dist/{chunk-3PSEZOC4.js → chunk-ZPRF4YLA.js} +1 -1
- package/dist/src/index.js +6 -6
- package/package.json +1 -1
- package/dist/chunk-U6HRYNHC.js +0 -137
- /package/dist/{chunk-VT4IVERX.js → chunk-PZA7G4CN.js} +0 -0
|
@@ -2,10 +2,177 @@ import {
|
|
|
2
2
|
PRETOOLUSE_GATED_TOOLS,
|
|
3
3
|
PRETOOLUSE_PHONE_ANSWERED_TOOLS
|
|
4
4
|
} from "./chunk-ATBKJNWS.js";
|
|
5
|
+
import {
|
|
6
|
+
guardBinary
|
|
7
|
+
} from "./chunk-AIGDBRIJ.js";
|
|
5
8
|
import {
|
|
6
9
|
HOOK_BUDGETS,
|
|
7
10
|
HOOK_LOCATOR_BINARY
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-Q6MK2YKV.js";
|
|
12
|
+
|
|
13
|
+
// src/claude-config.ts
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
|
|
16
|
+
// src/agent-version.ts
|
|
17
|
+
var parseVersion = (raw) => {
|
|
18
|
+
const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
19
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
20
|
+
};
|
|
21
|
+
var compareVersion = (a, b) => {
|
|
22
|
+
for (let index = 0; index < 3; index++) {
|
|
23
|
+
if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
};
|
|
27
|
+
var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
|
|
28
|
+
|
|
29
|
+
// src/claude-events.ts
|
|
30
|
+
var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
|
|
31
|
+
var MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"].join("|");
|
|
32
|
+
var ESTABLISHED = [1, 0, 0];
|
|
33
|
+
var OBSERVED = [2, 0, 0];
|
|
34
|
+
var CLAUDE_HOOK_EVENTS = [
|
|
35
|
+
{ event: "PreToolUse", binary: "pushary-hook", matcher: GATED_TOOLS, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
36
|
+
{ event: "PostToolUse", binary: "pushary-post-hook", matcher: MIRRORED_TOOLS, timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
37
|
+
{ event: "Stop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
38
|
+
{ event: "UserPromptSubmit", binary: "pushary-prompt-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
39
|
+
{ event: "Notification", binary: "pushary-notification-hook", matcher: "permission_prompt|idle_prompt|agent_needs_input|agent_completed", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
40
|
+
{ event: "SessionStart", binary: "pushary-session-start-hook", matcher: "startup|resume|clear", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
41
|
+
{ event: "SessionEnd", binary: "pushary-session-end-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
42
|
+
{ event: "StopFailure", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
43
|
+
{ event: "PermissionRequest", binary: "pushary-permission-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
44
|
+
{ event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: 60, delivery: "telemetry", since: ESTABLISHED },
|
|
45
|
+
// Observability only. Each carries session shape the island cannot get any other
|
|
46
|
+
// way, and none of them decides anything.
|
|
47
|
+
{ event: "SubagentStart", binary: "pushary-session-start-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
48
|
+
{ event: "SubagentStop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
49
|
+
{ event: "PreCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
50
|
+
{ event: "PostCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
51
|
+
{ event: "TeammateIdle", binary: "pushary-notification-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
52
|
+
// An MCP server asking the person for a value, answered from the phone rather
|
|
53
|
+
// than the terminal. No matcher: Claude Code matches this event on the MCP
|
|
54
|
+
// server's name, and we have no business preferring one server over another.
|
|
55
|
+
{ event: "Elicitation", binary: "pushary-elicitation-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: OBSERVED }
|
|
56
|
+
];
|
|
57
|
+
var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
|
|
58
|
+
|
|
59
|
+
// src/claude-config.ts
|
|
60
|
+
var PUSHARY_MCP_URL = "https://pushary.com/api/mcp/mcp";
|
|
61
|
+
var PUSHARY_PERMISSION_RULE = "mcp__pushary__*";
|
|
62
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
63
|
+
var ensureRecord = (target, key) => {
|
|
64
|
+
const existing = asRecord(target[key]);
|
|
65
|
+
if (existing) return existing;
|
|
66
|
+
const created = {};
|
|
67
|
+
target[key] = created;
|
|
68
|
+
return created;
|
|
69
|
+
};
|
|
70
|
+
var isPusharyPermission = (rule) => typeof rule === "string" && (rule.includes("pushary") || rule.includes("MCP(pushary"));
|
|
71
|
+
var HOOK_BINARIES = /* @__PURE__ */ new Set([
|
|
72
|
+
...CLAUDE_HOOK_EVENTS.map((hook) => hook.binary),
|
|
73
|
+
HOOK_LOCATOR_BINARY
|
|
74
|
+
]);
|
|
75
|
+
var namesPusharyBinary = (command) => command.split(/[\s/\\"']+/).some((segment) => HOOK_BINARIES.has(segment.replace(/\.(cmd|exe)$/i, "")));
|
|
76
|
+
var isPusharyHook = (entry2) => {
|
|
77
|
+
const hooks = asRecord(entry2)?.hooks;
|
|
78
|
+
if (!Array.isArray(hooks)) return false;
|
|
79
|
+
return hooks.some((hook) => namesPusharyBinary(String(asRecord(hook)?.command ?? "")));
|
|
80
|
+
};
|
|
81
|
+
var addClaudeMcpServer = (config, apiKey) => {
|
|
82
|
+
const mcpServers = ensureRecord(config, "mcpServers");
|
|
83
|
+
mcpServers.pushary = {
|
|
84
|
+
type: "http",
|
|
85
|
+
url: PUSHARY_MCP_URL,
|
|
86
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
var removeClaudeMcpServers = (config) => {
|
|
90
|
+
let changed = false;
|
|
91
|
+
const removeFrom = (target) => {
|
|
92
|
+
const mcpServers = asRecord(target.mcpServers);
|
|
93
|
+
if (!mcpServers?.pushary) return;
|
|
94
|
+
delete mcpServers.pushary;
|
|
95
|
+
if (Object.keys(mcpServers).length === 0) delete target.mcpServers;
|
|
96
|
+
changed = true;
|
|
97
|
+
};
|
|
98
|
+
removeFrom(config);
|
|
99
|
+
const projects = asRecord(config.projects);
|
|
100
|
+
if (projects) {
|
|
101
|
+
for (const project of Object.values(projects)) {
|
|
102
|
+
const projectConfig = asRecord(project);
|
|
103
|
+
if (projectConfig) removeFrom(projectConfig);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return changed;
|
|
107
|
+
};
|
|
108
|
+
var addPusharyToolPermissions = (settings) => {
|
|
109
|
+
const permissions = ensureRecord(settings, "permissions");
|
|
110
|
+
const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
111
|
+
const filtered = allow.filter((rule) => !isPusharyPermission(rule));
|
|
112
|
+
if (!filtered.includes(PUSHARY_PERMISSION_RULE)) {
|
|
113
|
+
filtered.push(PUSHARY_PERMISSION_RULE);
|
|
114
|
+
}
|
|
115
|
+
permissions.allow = filtered;
|
|
116
|
+
};
|
|
117
|
+
var addPusharyHooks = (settings, binDir, version = null) => {
|
|
118
|
+
const resolve = (name) => guardBinary(binDir ? join(binDir, name) : name);
|
|
119
|
+
const hooks = ensureRecord(settings, "hooks");
|
|
120
|
+
const supported = new Map(supportedClaudeEvents(version).map((hook) => [hook.event, hook]));
|
|
121
|
+
for (const known of CLAUDE_HOOK_EVENTS) {
|
|
122
|
+
const current = hooks[known.event];
|
|
123
|
+
const others = (Array.isArray(current) ? current : []).filter((item) => !isPusharyHook(item));
|
|
124
|
+
const definition = supported.get(known.event);
|
|
125
|
+
if (definition) {
|
|
126
|
+
hooks[known.event] = [...others, entry(definition, resolve)];
|
|
127
|
+
} else if (others.length > 0) {
|
|
128
|
+
hooks[known.event] = others;
|
|
129
|
+
} else {
|
|
130
|
+
delete hooks[known.event];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
var entry = (definition, resolve) => ({
|
|
135
|
+
...definition.matcher ? { matcher: definition.matcher } : {},
|
|
136
|
+
hooks: [{
|
|
137
|
+
type: "command",
|
|
138
|
+
command: resolve(definition.binary),
|
|
139
|
+
timeout: definition.timeout
|
|
140
|
+
}]
|
|
141
|
+
});
|
|
142
|
+
var removePusharySettings = (settings) => {
|
|
143
|
+
let changed = removeClaudeMcpServers(settings);
|
|
144
|
+
const permissions = asRecord(settings.permissions);
|
|
145
|
+
if (permissions && Array.isArray(permissions.allow)) {
|
|
146
|
+
const filtered = permissions.allow.filter((rule) => !isPusharyPermission(rule));
|
|
147
|
+
if (filtered.length !== permissions.allow.length) {
|
|
148
|
+
if (filtered.length === 0) {
|
|
149
|
+
delete permissions.allow;
|
|
150
|
+
} else {
|
|
151
|
+
permissions.allow = filtered;
|
|
152
|
+
}
|
|
153
|
+
if (Object.keys(permissions).length === 0) delete settings.permissions;
|
|
154
|
+
changed = true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const hooks = asRecord(settings.hooks);
|
|
158
|
+
if (hooks) {
|
|
159
|
+
for (const { event: key } of CLAUDE_HOOK_EVENTS) {
|
|
160
|
+
const entries = hooks[key];
|
|
161
|
+
if (!Array.isArray(entries)) continue;
|
|
162
|
+
const filtered = entries.filter((entry2) => !isPusharyHook(entry2));
|
|
163
|
+
if (filtered.length !== entries.length) {
|
|
164
|
+
if (filtered.length === 0) {
|
|
165
|
+
delete hooks[key];
|
|
166
|
+
} else {
|
|
167
|
+
hooks[key] = filtered;
|
|
168
|
+
}
|
|
169
|
+
changed = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
173
|
+
}
|
|
174
|
+
return changed;
|
|
175
|
+
};
|
|
9
176
|
|
|
10
177
|
// src/gemini-config.ts
|
|
11
178
|
var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
|
|
@@ -17,24 +184,24 @@ var GEMINI_HOOK_EVENTS = [
|
|
|
17
184
|
{ event: "SessionStart", delivery: "telemetry", timeoutMs: 1e4 },
|
|
18
185
|
{ event: "SessionEnd", delivery: "telemetry", timeoutMs: 1e4 }
|
|
19
186
|
];
|
|
20
|
-
var
|
|
21
|
-
var
|
|
22
|
-
const existing =
|
|
187
|
+
var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
188
|
+
var ensureRecord2 = (target, key) => {
|
|
189
|
+
const existing = asRecord2(target[key]);
|
|
23
190
|
if (existing) return existing;
|
|
24
191
|
const created = {};
|
|
25
192
|
target[key] = created;
|
|
26
193
|
return created;
|
|
27
194
|
};
|
|
28
|
-
var isPusharyGeminiHook = (
|
|
29
|
-
const hooks =
|
|
195
|
+
var isPusharyGeminiHook = (entry2) => {
|
|
196
|
+
const hooks = asRecord2(entry2)?.hooks;
|
|
30
197
|
if (!Array.isArray(hooks)) return false;
|
|
31
198
|
return hooks.some((hook) => {
|
|
32
|
-
const command = String(
|
|
199
|
+
const command = String(asRecord2(hook)?.command ?? "");
|
|
33
200
|
return command.includes(GEMINI_HOOK_BINARY) || command.includes(HOOK_LOCATOR_BINARY);
|
|
34
201
|
});
|
|
35
202
|
};
|
|
36
203
|
var addGeminiMcpServer = (settings, apiKey) => {
|
|
37
|
-
const mcpServers =
|
|
204
|
+
const mcpServers = ensureRecord2(settings, "mcpServers");
|
|
38
205
|
mcpServers.pushary = {
|
|
39
206
|
httpUrl: GEMINI_MCP_URL,
|
|
40
207
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
@@ -42,10 +209,10 @@ var addGeminiMcpServer = (settings, apiKey) => {
|
|
|
42
209
|
};
|
|
43
210
|
};
|
|
44
211
|
var addGeminiHooks = (settings, command) => {
|
|
45
|
-
const hooks =
|
|
212
|
+
const hooks = ensureRecord2(settings, "hooks");
|
|
46
213
|
for (const definition of GEMINI_HOOK_EVENTS) {
|
|
47
214
|
const existing = Array.isArray(hooks[definition.event]) ? hooks[definition.event] : [];
|
|
48
|
-
const entries = existing.filter((
|
|
215
|
+
const entries = existing.filter((entry2) => !isPusharyGeminiHook(entry2));
|
|
49
216
|
entries.push({
|
|
50
217
|
...definition.matcher ? { matcher: definition.matcher } : {},
|
|
51
218
|
hooks: [{
|
|
@@ -59,13 +226,13 @@ var addGeminiHooks = (settings, command) => {
|
|
|
59
226
|
}
|
|
60
227
|
};
|
|
61
228
|
var removeGeminiHooks = (settings) => {
|
|
62
|
-
const hooks =
|
|
229
|
+
const hooks = asRecord2(settings.hooks);
|
|
63
230
|
if (!hooks) return false;
|
|
64
231
|
let changed = false;
|
|
65
232
|
for (const definition of GEMINI_HOOK_EVENTS) {
|
|
66
233
|
const entries = hooks[definition.event];
|
|
67
234
|
if (!Array.isArray(entries)) continue;
|
|
68
|
-
const filtered = entries.filter((
|
|
235
|
+
const filtered = entries.filter((entry2) => !isPusharyGeminiHook(entry2));
|
|
69
236
|
if (filtered.length !== entries.length) {
|
|
70
237
|
if (filtered.length === 0) {
|
|
71
238
|
delete hooks[definition.event];
|
|
@@ -79,7 +246,7 @@ var removeGeminiHooks = (settings) => {
|
|
|
79
246
|
return changed;
|
|
80
247
|
};
|
|
81
248
|
var removeGeminiMcpServer = (settings) => {
|
|
82
|
-
const mcpServers =
|
|
249
|
+
const mcpServers = asRecord2(settings.mcpServers);
|
|
83
250
|
if (!mcpServers?.pushary) return false;
|
|
84
251
|
delete mcpServers.pushary;
|
|
85
252
|
if (Object.keys(mcpServers).length === 0) delete settings.mcpServers;
|
|
@@ -91,7 +258,7 @@ var removeGeminiSettings = (settings) => {
|
|
|
91
258
|
return mcpRemoved || hooksRemoved;
|
|
92
259
|
};
|
|
93
260
|
var hasGeminiHooks = (settings) => {
|
|
94
|
-
const hooks =
|
|
261
|
+
const hooks = asRecord2(settings.hooks);
|
|
95
262
|
if (!hooks) return false;
|
|
96
263
|
return GEMINI_HOOK_EVENTS.some((definition) => {
|
|
97
264
|
const entries = hooks[definition.event];
|
|
@@ -99,61 +266,21 @@ var hasGeminiHooks = (settings) => {
|
|
|
99
266
|
});
|
|
100
267
|
};
|
|
101
268
|
var missingGeminiHookEvents = (settings) => {
|
|
102
|
-
const hooks =
|
|
269
|
+
const hooks = asRecord2(settings.hooks);
|
|
103
270
|
return GEMINI_HOOK_EVENTS.filter((definition) => {
|
|
104
271
|
const entries = hooks?.[definition.event];
|
|
105
272
|
return !Array.isArray(entries) || !entries.some(isPusharyGeminiHook);
|
|
106
273
|
}).map((definition) => definition.event);
|
|
107
274
|
};
|
|
108
275
|
|
|
109
|
-
// src/agent-version.ts
|
|
110
|
-
var parseVersion = (raw) => {
|
|
111
|
-
const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
112
|
-
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
113
|
-
};
|
|
114
|
-
var compareVersion = (a, b) => {
|
|
115
|
-
for (let index = 0; index < 3; index++) {
|
|
116
|
-
if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
|
|
117
|
-
}
|
|
118
|
-
return 0;
|
|
119
|
-
};
|
|
120
|
-
var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
|
|
121
|
-
|
|
122
|
-
// src/claude-events.ts
|
|
123
|
-
var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
|
|
124
|
-
var MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"].join("|");
|
|
125
|
-
var ESTABLISHED = [1, 0, 0];
|
|
126
|
-
var OBSERVED = [2, 0, 0];
|
|
127
|
-
var CLAUDE_HOOK_EVENTS = [
|
|
128
|
-
{ event: "PreToolUse", binary: "pushary-hook", matcher: GATED_TOOLS, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
129
|
-
{ event: "PostToolUse", binary: "pushary-post-hook", matcher: MIRRORED_TOOLS, timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
130
|
-
{ event: "Stop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
131
|
-
{ event: "UserPromptSubmit", binary: "pushary-prompt-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
132
|
-
{ event: "Notification", binary: "pushary-notification-hook", matcher: "permission_prompt|idle_prompt|agent_needs_input|agent_completed", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
133
|
-
{ event: "SessionStart", binary: "pushary-session-start-hook", matcher: "startup|resume|clear", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
134
|
-
{ event: "SessionEnd", binary: "pushary-session-end-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
135
|
-
{ event: "StopFailure", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
136
|
-
{ event: "PermissionRequest", binary: "pushary-permission-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
137
|
-
{ event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: 60, delivery: "telemetry", since: ESTABLISHED },
|
|
138
|
-
// Observability only. Each carries session shape the island cannot get any other
|
|
139
|
-
// way, and none of them decides anything.
|
|
140
|
-
{ event: "SubagentStart", binary: "pushary-session-start-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
141
|
-
{ event: "SubagentStop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
142
|
-
{ event: "PreCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
143
|
-
{ event: "PostCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
144
|
-
{ event: "TeammateIdle", binary: "pushary-notification-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
145
|
-
// An MCP server asking the person for a value, answered from the phone rather
|
|
146
|
-
// than the terminal. No matcher: Claude Code matches this event on the MCP
|
|
147
|
-
// server's name, and we have no business preferring one server over another.
|
|
148
|
-
{ event: "Elicitation", binary: "pushary-elicitation-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: OBSERVED, cliOnly: true }
|
|
149
|
-
];
|
|
150
|
-
var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
|
|
151
|
-
|
|
152
276
|
export {
|
|
153
277
|
parseVersion,
|
|
154
278
|
compareVersion,
|
|
155
|
-
|
|
156
|
-
|
|
279
|
+
addClaudeMcpServer,
|
|
280
|
+
removeClaudeMcpServers,
|
|
281
|
+
addPusharyToolPermissions,
|
|
282
|
+
addPusharyHooks,
|
|
283
|
+
removePusharySettings,
|
|
157
284
|
GEMINI_HOOK_BINARY,
|
|
158
285
|
addGeminiMcpServer,
|
|
159
286
|
addGeminiHooks,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
summarizeStop,
|
|
23
23
|
taskTitleFrom,
|
|
24
24
|
toolResultErrorText
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-LXTX5CT7.js";
|
|
26
26
|
import {
|
|
27
27
|
getMachineId
|
|
28
28
|
} from "./chunk-RN3NOEJF.js";
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
} from "./chunk-BSZYIAZL.js";
|
|
32
32
|
import {
|
|
33
33
|
reportedInstallSource
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-AJ4EMW5D.js";
|
|
35
35
|
import {
|
|
36
36
|
getApiKey,
|
|
37
37
|
getBaseUrl
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
pendingCommandEvents,
|
|
48
48
|
resolveAutoResolveOrigin,
|
|
49
49
|
resolvePolicy
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-Q6MK2YKV.js";
|
|
51
51
|
|
|
52
52
|
// src/policy.ts
|
|
53
53
|
import { createHash } from "crypto";
|
|
@@ -76,6 +76,8 @@ var INSTALL_SOURCES = [
|
|
|
76
76
|
/** The browser onboarding wizard. */
|
|
77
77
|
"dashboard",
|
|
78
78
|
/** Reported nothing, or reported something we do not recognise. */
|
|
79
|
+
/** The macOS app pairing a phone from its own onboarding. */
|
|
80
|
+
"mac_app",
|
|
79
81
|
"unknown"
|
|
80
82
|
];
|
|
81
83
|
var isInstallSource = (value) => typeof value === "string" && INSTALL_SOURCES.includes(value);
|
|
@@ -1207,6 +1209,118 @@ var claudeElicitationOutput = (event, action, content) => ({
|
|
|
1207
1209
|
}
|
|
1208
1210
|
});
|
|
1209
1211
|
var renderElicitationOutput = (event, action, content) => ELICITATION_EVENTS.includes(event) ? JSON.stringify(claudeElicitationOutput(event, action, content)) : void 0;
|
|
1212
|
+
var ELICITATION_ANSWERABLE_MODE = "form";
|
|
1213
|
+
var ELICITATION_MIN_OPTIONS = 2;
|
|
1214
|
+
var ELICITATION_MAX_MULTI_QUESTIONS = 4;
|
|
1215
|
+
var ELICITATION_MAX_MULTI_OPTIONS = 4;
|
|
1216
|
+
var elicitationRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1217
|
+
var elicitationLabel = (name, property) => typeof property.title === "string" && property.title.trim() ? property.title.trim() : name;
|
|
1218
|
+
var elicitationEnum = (property) => {
|
|
1219
|
+
if (!Array.isArray(property.enum)) return null;
|
|
1220
|
+
const members = property.enum.filter((member) => typeof member === "string");
|
|
1221
|
+
return members.length === property.enum.length && members.length >= ELICITATION_MIN_OPTIONS ? members : null;
|
|
1222
|
+
};
|
|
1223
|
+
var readElicitationFields = (schema) => {
|
|
1224
|
+
const record = elicitationRecord(schema);
|
|
1225
|
+
const properties = elicitationRecord(record?.properties);
|
|
1226
|
+
if (!properties) return null;
|
|
1227
|
+
const entries = Object.entries(properties);
|
|
1228
|
+
if (entries.length === 0) return null;
|
|
1229
|
+
const fields = [];
|
|
1230
|
+
for (const [name, raw] of entries) {
|
|
1231
|
+
const property = elicitationRecord(raw);
|
|
1232
|
+
if (!property) return null;
|
|
1233
|
+
const label = elicitationLabel(name, property);
|
|
1234
|
+
if ("enum" in property) {
|
|
1235
|
+
const options = elicitationEnum(property);
|
|
1236
|
+
if (!options) return null;
|
|
1237
|
+
fields.push({ kind: "choice", name, label, options });
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
switch (property.type) {
|
|
1241
|
+
case "string":
|
|
1242
|
+
fields.push({ kind: "text", name, label });
|
|
1243
|
+
break;
|
|
1244
|
+
case "number":
|
|
1245
|
+
fields.push({ kind: "number", name, label, integer: false });
|
|
1246
|
+
break;
|
|
1247
|
+
case "integer":
|
|
1248
|
+
fields.push({ kind: "number", name, label, integer: true });
|
|
1249
|
+
break;
|
|
1250
|
+
case "boolean":
|
|
1251
|
+
fields.push({ kind: "boolean", name, label });
|
|
1252
|
+
break;
|
|
1253
|
+
default:
|
|
1254
|
+
return null;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return fields;
|
|
1258
|
+
};
|
|
1259
|
+
var elicitationQuestionText = (message, field, alone) => alone ? message : `${message} \u2014 ${field.label}`;
|
|
1260
|
+
var planElicitationAsk = (message, fields) => {
|
|
1261
|
+
if (fields.length === 1) {
|
|
1262
|
+
const field = fields[0];
|
|
1263
|
+
const question = elicitationQuestionText(message, field, true);
|
|
1264
|
+
switch (field.kind) {
|
|
1265
|
+
case "choice":
|
|
1266
|
+
return { kind: "select", field, question, options: field.options };
|
|
1267
|
+
case "boolean":
|
|
1268
|
+
return { kind: "confirm", field, question };
|
|
1269
|
+
case "text":
|
|
1270
|
+
case "number":
|
|
1271
|
+
return { kind: "input", field, question };
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
if (fields.length > ELICITATION_MAX_MULTI_QUESTIONS) return null;
|
|
1275
|
+
if (!fields.every((field) => field.kind === "choice" && field.options.length <= ELICITATION_MAX_MULTI_OPTIONS)) return null;
|
|
1276
|
+
const questions = fields.map((field) => ({
|
|
1277
|
+
question: elicitationQuestionText(message, field, false),
|
|
1278
|
+
header: field.label.slice(0, 12),
|
|
1279
|
+
multiSelect: false,
|
|
1280
|
+
options: field.options.map((label) => ({ label }))
|
|
1281
|
+
}));
|
|
1282
|
+
return new Set(questions.map((question) => question.question)).size === questions.length ? { kind: "questions", fields, questions } : null;
|
|
1283
|
+
};
|
|
1284
|
+
var ELICITATION_CONFIRMED = /* @__PURE__ */ new Set(["yes", "true", "approve", "approved", "allow"]);
|
|
1285
|
+
var ELICITATION_REFUSED = /* @__PURE__ */ new Set(["no", "false", "deny", "denied", "reject"]);
|
|
1286
|
+
var elicitationNumber = (raw, integer) => {
|
|
1287
|
+
const trimmed = raw.trim();
|
|
1288
|
+
if (!/^-?\d+(\.\d+)?$/.test(trimmed)) return null;
|
|
1289
|
+
const value = Number(trimmed);
|
|
1290
|
+
if (!Number.isFinite(value)) return null;
|
|
1291
|
+
return integer && !Number.isInteger(value) ? null : value;
|
|
1292
|
+
};
|
|
1293
|
+
var elicitationContent = (ask, answer) => {
|
|
1294
|
+
const value = answer.trim();
|
|
1295
|
+
if (!value) return null;
|
|
1296
|
+
if (ask.kind === "input") {
|
|
1297
|
+
if (ask.field.kind === "number") {
|
|
1298
|
+
const parsed = elicitationNumber(value, ask.field.integer);
|
|
1299
|
+
return parsed === null ? null : { [ask.field.name]: parsed };
|
|
1300
|
+
}
|
|
1301
|
+
return { [ask.field.name]: value };
|
|
1302
|
+
}
|
|
1303
|
+
if (ask.kind === "confirm") {
|
|
1304
|
+
const lowered = value.toLowerCase();
|
|
1305
|
+
if (ELICITATION_CONFIRMED.has(lowered)) return { [ask.field.name]: true };
|
|
1306
|
+
if (ELICITATION_REFUSED.has(lowered)) return { [ask.field.name]: false };
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
if (ask.kind === "select") {
|
|
1310
|
+
return ask.options.includes(value) ? { [ask.field.name]: value } : null;
|
|
1311
|
+
}
|
|
1312
|
+
return null;
|
|
1313
|
+
};
|
|
1314
|
+
var elicitationContentFromAnswers = (ask, answers) => {
|
|
1315
|
+
const content = {};
|
|
1316
|
+
for (const [index, field] of ask.fields.entries()) {
|
|
1317
|
+
if (field.kind !== "choice") return null;
|
|
1318
|
+
const chosen = answers[ask.questions[index].question];
|
|
1319
|
+
if (typeof chosen !== "string" || !field.options.includes(chosen)) return null;
|
|
1320
|
+
content[field.name] = chosen;
|
|
1321
|
+
}
|
|
1322
|
+
return content;
|
|
1323
|
+
};
|
|
1210
1324
|
|
|
1211
1325
|
export {
|
|
1212
1326
|
parseAgentQuestions,
|
|
@@ -1243,5 +1357,10 @@ export {
|
|
|
1243
1357
|
pendingCommandEvents,
|
|
1244
1358
|
claudePermissionRequestOutput,
|
|
1245
1359
|
gateOutputFor,
|
|
1246
|
-
renderElicitationOutput
|
|
1360
|
+
renderElicitationOutput,
|
|
1361
|
+
ELICITATION_ANSWERABLE_MODE,
|
|
1362
|
+
readElicitationFields,
|
|
1363
|
+
planElicitationAsk,
|
|
1364
|
+
elicitationContent,
|
|
1365
|
+
elicitationContentFromAnswers
|
|
1247
1366
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hasGeminiHooks
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-F3ZDPL6O.js";
|
|
4
4
|
import {
|
|
5
5
|
hasCodexHooks
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FRH2QIMY.js";
|
|
7
7
|
|
|
8
8
|
// src/diagnostics/wiring.ts
|
|
9
9
|
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
isGatingMoment,
|
|
7
7
|
recordKeylessMoment
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PXEFENML.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-
|
|
20
|
+
} from "./chunk-OSE7X44J.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-
|
|
36
|
+
} from "./chunk-LXTX5CT7.js";
|
|
37
37
|
import {
|
|
38
38
|
getMachineId
|
|
39
39
|
} from "./chunk-RN3NOEJF.js";
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
parseAgentQuestions,
|
|
53
53
|
resolveGate,
|
|
54
54
|
shouldDeferToNativeMode
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-Q6MK2YKV.js";
|
|
56
56
|
|
|
57
57
|
// src/decision-episode.ts
|
|
58
58
|
var EPISODE_PATH = "/api/agent/decision-episode";
|