eminiar 0.13.0 → 0.13.1
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/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +43 -15
- package/bin/_resolve.sh +7 -4
- package/bin/em-hook +2 -1
- package/bin/em-server +2 -1
- package/bin/eminiar +2 -1
- package/dist/cli.js +1502 -224
- package/dist/{hook-async-4MIP3VHX.js → hook-async-J3UKDO62.js} +3 -3
- package/dist/{hook-chunk-IYHVLMJ4.js → hook-chunk-BECSRJFD.js} +26 -11
- package/dist/{hook-chunk-MVOBVFQL.js → hook-chunk-DSLVDGOB.js} +57 -12
- package/dist/{hook-chunk-77I6QQ43.js → hook-chunk-RYZAFUTT.js} +2 -2
- package/dist/{hook-hooks-U6GDOQSY.js → hook-hooks-IDTGXCFD.js} +8 -6
- package/dist/hook.js +234 -57
- package/dist/server.js +80 -43
- package/dist/ui/app.global.js +78 -72
- package/dist/ui/styles.css +4724 -1508
- package/package.json +1 -1
package/dist/hook.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
recordShown,
|
|
24
24
|
renderProjectBrief,
|
|
25
25
|
resolvePrice,
|
|
26
|
+
spawnType,
|
|
26
27
|
subagentStart,
|
|
27
28
|
swarmActivity,
|
|
28
29
|
swarmAgentReturned,
|
|
@@ -32,13 +33,16 @@ import {
|
|
|
32
33
|
swarmSessionStart,
|
|
33
34
|
swarmStampCaller,
|
|
34
35
|
swarmSubagentStop,
|
|
35
|
-
swarmTouch
|
|
36
|
-
|
|
36
|
+
swarmTouch,
|
|
37
|
+
workerLacksTool
|
|
38
|
+
} from "./hook-chunk-DSLVDGOB.js";
|
|
37
39
|
import {
|
|
38
40
|
GUIDE_ORDER,
|
|
39
41
|
classifyAssistant,
|
|
40
42
|
classifyPrompt,
|
|
41
43
|
classifyToolCall,
|
|
44
|
+
claudeConfigDir,
|
|
45
|
+
claudePluginsDir,
|
|
42
46
|
clip,
|
|
43
47
|
ensureDirs,
|
|
44
48
|
ev,
|
|
@@ -46,13 +50,14 @@ import {
|
|
|
46
50
|
getStorage,
|
|
47
51
|
init_esm_shims,
|
|
48
52
|
projectDir,
|
|
53
|
+
setStorageBusyTimeout,
|
|
49
54
|
withPriority
|
|
50
|
-
} from "./hook-chunk-
|
|
55
|
+
} from "./hook-chunk-BECSRJFD.js";
|
|
51
56
|
|
|
52
57
|
// src/hook.ts
|
|
53
58
|
init_esm_shims();
|
|
54
59
|
|
|
55
|
-
// src/hooks/
|
|
60
|
+
// src/hooks/budget.ts
|
|
56
61
|
init_esm_shims();
|
|
57
62
|
|
|
58
63
|
// src/hooks/cursor.ts
|
|
@@ -117,6 +122,18 @@ function toolResponseOf(v) {
|
|
|
117
122
|
return v;
|
|
118
123
|
}
|
|
119
124
|
}
|
|
125
|
+
function handlerEvent(parsed, argv) {
|
|
126
|
+
const forced = argv.indexOf("--event");
|
|
127
|
+
const p = forced !== -1 && argv[forced + 1] ? { ...parsed, hook_event_name: argv[forced + 1] } : parsed;
|
|
128
|
+
const event = str(p.hook_event_name);
|
|
129
|
+
const tool = str(p.tool_name);
|
|
130
|
+
if (detectClient(p, argv) !== "cursor") return { event, tool };
|
|
131
|
+
if (event === "afterFileEdit") return { event: CURSOR_EVENTS[event], tool: "Edit" };
|
|
132
|
+
return {
|
|
133
|
+
event: event === void 0 ? void 0 : CURSOR_EVENTS[event] ?? event,
|
|
134
|
+
tool: tool === void 0 ? void 0 : mapToolName(tool)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
120
137
|
function fromCursor(raw) {
|
|
121
138
|
const event = str(raw.hook_event_name) ?? "";
|
|
122
139
|
const roots = Array.isArray(raw.workspace_roots) ? raw.workspace_roots.filter((r) => typeof r === "string") : [];
|
|
@@ -249,6 +266,63 @@ function toCursor(event, out, scope) {
|
|
|
249
266
|
}
|
|
250
267
|
}
|
|
251
268
|
|
|
269
|
+
// src/hooks/budget.ts
|
|
270
|
+
var HOOK_BUSY_TIMEOUT_MS = 250;
|
|
271
|
+
var WRITE_WAIT = {
|
|
272
|
+
SessionStart: 2e3,
|
|
273
|
+
PostToolUse: 2e3,
|
|
274
|
+
SubagentStart: 2e3,
|
|
275
|
+
PreCompact: 2e3,
|
|
276
|
+
SubagentStop: 1500,
|
|
277
|
+
SessionEnd: 1500,
|
|
278
|
+
Stop: 1500
|
|
279
|
+
};
|
|
280
|
+
function busyTimeoutFor(event, tool) {
|
|
281
|
+
if (event && WRITE_WAIT[event]) return WRITE_WAIT[event];
|
|
282
|
+
if ((event === "PreToolUse" || event === "PostToolUseFailure") && (tool === "Agent" || tool === "Task"))
|
|
283
|
+
return 1500;
|
|
284
|
+
return HOOK_BUSY_TIMEOUT_MS;
|
|
285
|
+
}
|
|
286
|
+
function lockWaitFor(raw, argv) {
|
|
287
|
+
let parsed = {};
|
|
288
|
+
try {
|
|
289
|
+
const p = raw.trim() ? JSON.parse(raw) : {};
|
|
290
|
+
if (p && typeof p === "object" && !Array.isArray(p)) parsed = p;
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
const { event, tool } = handlerEvent(parsed, argv);
|
|
294
|
+
return busyTimeoutFor(event, tool);
|
|
295
|
+
}
|
|
296
|
+
var HOOK_BUDGET_MS = 3500;
|
|
297
|
+
var LONGER = {
|
|
298
|
+
SessionStart: 8e3,
|
|
299
|
+
PostToolUse: 8e3,
|
|
300
|
+
SubagentStart: 8e3,
|
|
301
|
+
PreCompact: 12e3
|
|
302
|
+
};
|
|
303
|
+
function budgetFor(event) {
|
|
304
|
+
return event && LONGER[event] || HOOK_BUDGET_MS;
|
|
305
|
+
}
|
|
306
|
+
var startedAt;
|
|
307
|
+
var budgetMs = HOOK_BUDGET_MS;
|
|
308
|
+
function startBudget(ms = HOOK_BUDGET_MS, now = Date.now()) {
|
|
309
|
+
startedAt = now;
|
|
310
|
+
budgetMs = ms;
|
|
311
|
+
}
|
|
312
|
+
function clearBudget() {
|
|
313
|
+
startedAt = void 0;
|
|
314
|
+
}
|
|
315
|
+
function budgetLeft(now = Date.now()) {
|
|
316
|
+
if (startedAt === void 0) return Number.POSITIVE_INFINITY;
|
|
317
|
+
return Math.max(0, budgetMs - (now - startedAt));
|
|
318
|
+
}
|
|
319
|
+
function hasBudget(needMs, now = Date.now()) {
|
|
320
|
+
return budgetLeft(now) >= needMs;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/hooks/dispatch.ts
|
|
324
|
+
init_esm_shims();
|
|
325
|
+
|
|
252
326
|
// src/hooks/index.ts
|
|
253
327
|
init_esm_shims();
|
|
254
328
|
|
|
@@ -1246,7 +1320,6 @@ function budgetWarnings(db, ctx, sessionId, now = Date.now()) {
|
|
|
1246
1320
|
init_esm_shims();
|
|
1247
1321
|
import { createHash } from "crypto";
|
|
1248
1322
|
import { closeSync, existsSync as existsSync2, openSync, readdirSync, readSync, statSync } from "fs";
|
|
1249
|
-
import { homedir as homedir2 } from "os";
|
|
1250
1323
|
import { basename, dirname, join as join2, resolve, sep } from "path";
|
|
1251
1324
|
|
|
1252
1325
|
// src/usage/transcript.ts
|
|
@@ -1475,7 +1548,7 @@ function isValidSessionId(id) {
|
|
|
1475
1548
|
return SESSION_ID_RE.test(id);
|
|
1476
1549
|
}
|
|
1477
1550
|
function claudeDir() {
|
|
1478
|
-
return process.env.EMINIAR_CLAUDE_DIR ||
|
|
1551
|
+
return process.env.EMINIAR_CLAUDE_DIR || claudeConfigDir();
|
|
1479
1552
|
}
|
|
1480
1553
|
function insideDir(path, dir) {
|
|
1481
1554
|
const d = resolve(dir);
|
|
@@ -1727,6 +1800,9 @@ function ingestSession(db, root, sessionId, transcriptPath, opts = {}) {
|
|
|
1727
1800
|
}
|
|
1728
1801
|
|
|
1729
1802
|
// src/hooks/capture.ts
|
|
1803
|
+
var RECALL_NEEDS_MS = 600;
|
|
1804
|
+
var ADJUDICATOR_NEEDS_MS = 1500;
|
|
1805
|
+
var BEST_EFFORT_NEEDS_MS = 1e3;
|
|
1730
1806
|
var INDEXED = /* @__PURE__ */ new Set([
|
|
1731
1807
|
"decision",
|
|
1732
1808
|
"constraint",
|
|
@@ -1788,13 +1864,23 @@ function ingestUsage(db, input, sessionId) {
|
|
|
1788
1864
|
}
|
|
1789
1865
|
var userPromptSubmit = async (input) => {
|
|
1790
1866
|
if (!input.prompt?.trim()) return void 0;
|
|
1791
|
-
const { db, sessionId } = open(input);
|
|
1792
|
-
db.sessionUpsert({ id: sessionId, last_prompt: clip(input.prompt, 600) });
|
|
1793
|
-
persist(db, sessionId, withPriority(sessionId, classifyPrompt(input.prompt)));
|
|
1794
1867
|
const ctx = getContext(input.cwd);
|
|
1868
|
+
const db = getStorage(ctx, { vectors: false });
|
|
1869
|
+
const sessionId = input.session_id || "unknown";
|
|
1870
|
+
try {
|
|
1871
|
+
db.sessionUpsert({
|
|
1872
|
+
id: sessionId,
|
|
1873
|
+
project_root: ctx.root,
|
|
1874
|
+
transcript_path: input.transcript_path,
|
|
1875
|
+
last_prompt: clip(input.prompt, 600)
|
|
1876
|
+
});
|
|
1877
|
+
persist(db, sessionId, withPriority(sessionId, classifyPrompt(input.prompt)));
|
|
1878
|
+
} catch (err) {
|
|
1879
|
+
hookLog("info", `UserPromptSubmit: prompt not recorded: ${err.message}`);
|
|
1880
|
+
}
|
|
1795
1881
|
const { recall } = ctx.config;
|
|
1796
1882
|
const parts = [];
|
|
1797
|
-
if (recall.onPrompt) {
|
|
1883
|
+
if (recall.onPrompt && hasBudget(RECALL_NEEDS_MS)) {
|
|
1798
1884
|
try {
|
|
1799
1885
|
const r = await recallForText(db, input.prompt, {
|
|
1800
1886
|
maxMemories: recall.maxMemories,
|
|
@@ -1804,15 +1890,18 @@ var userPromptSubmit = async (input) => {
|
|
|
1804
1890
|
trace: true,
|
|
1805
1891
|
path: "prompt"
|
|
1806
1892
|
});
|
|
1807
|
-
recordShown(db, sessionId, r.keys, { path: "prompt", trace: r.trace });
|
|
1808
1893
|
if (r.text) parts.push(r.text);
|
|
1894
|
+
try {
|
|
1895
|
+
recordShown(db, sessionId, r.keys, { path: "prompt", trace: r.trace });
|
|
1896
|
+
} catch {
|
|
1897
|
+
}
|
|
1809
1898
|
} catch {
|
|
1810
1899
|
}
|
|
1811
1900
|
}
|
|
1812
1901
|
let systemMessage;
|
|
1813
|
-
if (ctx.config.adjudicator.enabled && input.client !== "cursor") {
|
|
1902
|
+
if (ctx.config.adjudicator.enabled && input.client !== "cursor" && hasBudget(ADJUDICATOR_NEEDS_MS)) {
|
|
1814
1903
|
try {
|
|
1815
|
-
const { onPrompt } = await import("./hook-hooks-
|
|
1904
|
+
const { onPrompt } = await import("./hook-hooks-IDTGXCFD.js");
|
|
1816
1905
|
const a = await onPrompt(ctx, db, input, sessionId);
|
|
1817
1906
|
if (a?.context) parts.push(a.context);
|
|
1818
1907
|
systemMessage = a?.systemMessage;
|
|
@@ -1870,7 +1959,7 @@ var postToolUse = async (input) => {
|
|
|
1870
1959
|
const ctx = getContext(input.cwd);
|
|
1871
1960
|
if (ctx.config.adjudicator.enabled) {
|
|
1872
1961
|
try {
|
|
1873
|
-
const { onPostAgent } = await import("./hook-hooks-
|
|
1962
|
+
const { onPostAgent } = await import("./hook-hooks-IDTGXCFD.js");
|
|
1874
1963
|
onPostAgent(ctx, db, input);
|
|
1875
1964
|
} catch {
|
|
1876
1965
|
}
|
|
@@ -1878,14 +1967,43 @@ var postToolUse = async (input) => {
|
|
|
1878
1967
|
}
|
|
1879
1968
|
return void 0;
|
|
1880
1969
|
};
|
|
1970
|
+
function bestEffortWrites(db) {
|
|
1971
|
+
let done = false;
|
|
1972
|
+
return (what, fn) => {
|
|
1973
|
+
if (done) return;
|
|
1974
|
+
if (!hasBudget(BEST_EFFORT_NEEDS_MS)) {
|
|
1975
|
+
done = true;
|
|
1976
|
+
hookLog("info", `PostToolUseFailure: ${what} skipped: out of time`);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
try {
|
|
1980
|
+
const short = db.adjudicator.withLockWait?.bind(db.adjudicator);
|
|
1981
|
+
if (short) short(HOOK_BUSY_TIMEOUT_MS, fn);
|
|
1982
|
+
else fn();
|
|
1983
|
+
} catch (err) {
|
|
1984
|
+
done = true;
|
|
1985
|
+
hookLog("info", `PostToolUseFailure: ${what} not recorded: ${err.message}`);
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1881
1989
|
var postToolUseFailure = async (input) => {
|
|
1882
1990
|
if (!input.tool_name) return void 0;
|
|
1883
|
-
|
|
1884
|
-
if (input.tool_name === "Agent" || input.tool_name === "Task")
|
|
1885
|
-
|
|
1886
|
-
const
|
|
1991
|
+
const ctx = getContext(input.cwd);
|
|
1992
|
+
if (input.tool_name === "Agent" || input.tool_name === "Task") swarmAgentReturned(ctx, input);
|
|
1993
|
+
const db = getStorage(ctx, { vectors: false });
|
|
1994
|
+
const sessionId = input.session_id || "unknown";
|
|
1995
|
+
const write = bestEffortWrites(db);
|
|
1996
|
+
if (input.agent_id) write("swarm activity", () => swarmActivity(ctx, input, true));
|
|
1997
|
+
write(
|
|
1998
|
+
"session",
|
|
1999
|
+
() => db.sessionUpsert({
|
|
2000
|
+
id: sessionId,
|
|
2001
|
+
project_root: ctx.root,
|
|
2002
|
+
transcript_path: input.transcript_path
|
|
2003
|
+
})
|
|
2004
|
+
);
|
|
1887
2005
|
let recalled;
|
|
1888
|
-
const { recall } =
|
|
2006
|
+
const { recall } = ctx.config;
|
|
1889
2007
|
if (recall.onError && input.error) {
|
|
1890
2008
|
try {
|
|
1891
2009
|
const r = await recallForError(db, input.error, {
|
|
@@ -1895,7 +2013,7 @@ var postToolUseFailure = async (input) => {
|
|
|
1895
2013
|
});
|
|
1896
2014
|
if (r.text) {
|
|
1897
2015
|
recalled = r.text;
|
|
1898
|
-
recordShown(db, sessionId, r.keys, { path: "error" });
|
|
2016
|
+
write("recall", () => recordShown(db, sessionId, r.keys, { path: "error" }));
|
|
1899
2017
|
}
|
|
1900
2018
|
} catch {
|
|
1901
2019
|
}
|
|
@@ -1904,9 +2022,9 @@ var postToolUseFailure = async (input) => {
|
|
|
1904
2022
|
toolName: input.tool_name,
|
|
1905
2023
|
toolInput: input.tool_input ?? {},
|
|
1906
2024
|
error: input.error ?? "tool failed",
|
|
1907
|
-
root
|
|
2025
|
+
root: ctx.root
|
|
1908
2026
|
}).filter((d) => d.category !== "tool_call");
|
|
1909
|
-
persist(db, sessionId, withPriority(sessionId, drafts));
|
|
2027
|
+
write("failure", () => persist(db, sessionId, withPriority(sessionId, drafts)));
|
|
1910
2028
|
return recalled ? { hookSpecificOutput: { hookEventName: "PostToolUseFailure", additionalContext: recalled } } : void 0;
|
|
1911
2029
|
};
|
|
1912
2030
|
var subagentStop = (input) => {
|
|
@@ -1946,7 +2064,7 @@ var sessionEnd = async (input) => {
|
|
|
1946
2064
|
ingestUsage(db, input, sessionId);
|
|
1947
2065
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor") {
|
|
1948
2066
|
try {
|
|
1949
|
-
const { onSessionEnd } = await import("./hook-hooks-
|
|
2067
|
+
const { onSessionEnd } = await import("./hook-hooks-IDTGXCFD.js");
|
|
1950
2068
|
onSessionEnd(ctx, db, input);
|
|
1951
2069
|
} catch {
|
|
1952
2070
|
}
|
|
@@ -2080,13 +2198,12 @@ var preCompact = (input) => {
|
|
|
2080
2198
|
// src/hooks/routing.ts
|
|
2081
2199
|
init_esm_shims();
|
|
2082
2200
|
import { realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
2083
|
-
import { homedir as
|
|
2201
|
+
import { homedir as homedir2 } from "os";
|
|
2084
2202
|
import { extname, join as join7, relative, resolve as resolve2 } from "path";
|
|
2085
2203
|
|
|
2086
2204
|
// src/permissions/settings.ts
|
|
2087
2205
|
init_esm_shims();
|
|
2088
2206
|
import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync, statSync as statSync2 } from "fs";
|
|
2089
|
-
import { homedir as homedir3 } from "os";
|
|
2090
2207
|
import { join as join3 } from "path";
|
|
2091
2208
|
|
|
2092
2209
|
// src/permissions/rules.ts
|
|
@@ -2142,11 +2259,11 @@ function stamp(file2) {
|
|
|
2142
2259
|
var cache = /* @__PURE__ */ new Map();
|
|
2143
2260
|
function loadPermissionRules(root, extraDeny = []) {
|
|
2144
2261
|
const files = [
|
|
2145
|
-
join3(
|
|
2262
|
+
join3(claudeConfigDir(), "settings.json"),
|
|
2146
2263
|
join3(root, ".claude", "settings.json"),
|
|
2147
2264
|
join3(root, ".claude", "settings.local.json")
|
|
2148
2265
|
];
|
|
2149
|
-
const key =
|
|
2266
|
+
const key = [...files, ...extraDeny].join("\0");
|
|
2150
2267
|
const stamps = files.map(stamp);
|
|
2151
2268
|
const hit = cache.get(key);
|
|
2152
2269
|
if (hit?.stamps.every((s, i) => s === stamps[i])) return hit.rules;
|
|
@@ -2246,10 +2363,8 @@ import {
|
|
|
2246
2363
|
readFileSync as readFileSync3,
|
|
2247
2364
|
writeFileSync as writeFileSync3
|
|
2248
2365
|
} from "fs";
|
|
2249
|
-
import { homedir as homedir4 } from "os";
|
|
2250
2366
|
import { dirname as dirname2, join as join5 } from "path";
|
|
2251
|
-
var
|
|
2252
|
-
var pluginsDir = () => join5(claudeDir2(), "plugins");
|
|
2367
|
+
var pluginsDir = () => claudePluginsDir();
|
|
2253
2368
|
|
|
2254
2369
|
// src/hooks/coexist.ts
|
|
2255
2370
|
var cached;
|
|
@@ -2488,7 +2603,7 @@ function fileOperands(stage, cwd) {
|
|
|
2488
2603
|
for (let f of files) {
|
|
2489
2604
|
if (/[*?[\]{}$`\\]/.test(f)) return void 0;
|
|
2490
2605
|
f = f.replace(/^['"]|['"]$/g, "");
|
|
2491
|
-
if (f.startsWith("~/")) f = join7(
|
|
2606
|
+
if (f.startsWith("~/")) f = join7(homedir2(), f.slice(2));
|
|
2492
2607
|
out.push(resolve2(cwd, f));
|
|
2493
2608
|
}
|
|
2494
2609
|
return out;
|
|
@@ -2639,12 +2754,18 @@ function suggest(toolName, input, root, extra = {}) {
|
|
|
2639
2754
|
return {
|
|
2640
2755
|
tool: "em_*",
|
|
2641
2756
|
reason: "subagents should prefer em_execute / em_batch_execute / em_search over Bash, Read and Grep",
|
|
2642
|
-
prefixPrompt:
|
|
2757
|
+
prefixPrompt: subagentPrefix(true)
|
|
2643
2758
|
};
|
|
2644
2759
|
default:
|
|
2645
2760
|
return void 0;
|
|
2646
2761
|
}
|
|
2647
2762
|
}
|
|
2763
|
+
function subagentPrefix(shell) {
|
|
2764
|
+
const runners = shell ? "em_execute, em_batch_execute, em_execute_file, " : "";
|
|
2765
|
+
return `Use the eminiar tools (${runners}em_fetch_and_index, em_search, em_symbols, em_history) instead of ${shell ? "Bash/" : ""}Read/Grep/WebFetch whenever output may be large; return only derived answers. Check em_recall {query} for project decisions and constraints before proposing changes, em_history {action:"file", path} before editing a file, em_symbols / em_search {source:"code"} before grepping for an identifier, and em_docs {action:"lookup", query} before WebFetch/WebSearch for anything the project depends on.
|
|
2766
|
+
|
|
2767
|
+
`;
|
|
2768
|
+
}
|
|
2648
2769
|
function projectRel(ctx, fp) {
|
|
2649
2770
|
if (!fp) return void 0;
|
|
2650
2771
|
let abs = resolve2(ctx.root, fp);
|
|
@@ -2773,7 +2894,7 @@ var preToolUse = async (input) => {
|
|
|
2773
2894
|
const out = await route(ctx, input, spawn?.kind === "worker");
|
|
2774
2895
|
if (!ctx.config.adjudicator.enabled || input.client === "cursor") return out;
|
|
2775
2896
|
try {
|
|
2776
|
-
const { afterRouting } = await import("./hook-hooks-
|
|
2897
|
+
const { afterRouting } = await import("./hook-hooks-IDTGXCFD.js");
|
|
2777
2898
|
return await afterRouting(ctx, input, spawn, out);
|
|
2778
2899
|
} catch {
|
|
2779
2900
|
return out;
|
|
@@ -2805,15 +2926,16 @@ async function route(ctx, input, swarmWorker) {
|
|
|
2805
2926
|
}
|
|
2806
2927
|
if (mode === "enforce") {
|
|
2807
2928
|
if (s.prefixPrompt) {
|
|
2929
|
+
const prefix = swarmWorker && workerLacksTool(input, spawnType(input), "em_execute") ? subagentPrefix(false) : s.prefixPrompt;
|
|
2808
2930
|
const prompt = String(input.tool_input.prompt ?? "");
|
|
2809
|
-
if (prompt.startsWith(
|
|
2931
|
+
if (prompt.startsWith(prefix)) return void 0;
|
|
2810
2932
|
return {
|
|
2811
2933
|
hookSpecificOutput: {
|
|
2812
2934
|
hookEventName: "PreToolUse",
|
|
2813
2935
|
permissionDecision: "allow",
|
|
2814
2936
|
updatedInput: {
|
|
2815
2937
|
...input.tool_input,
|
|
2816
|
-
prompt:
|
|
2938
|
+
prompt: prefix + (swarmWorker ? "" : await briefFor(ctx, prompt)) + prompt
|
|
2817
2939
|
}
|
|
2818
2940
|
}
|
|
2819
2941
|
};
|
|
@@ -2832,6 +2954,10 @@ async function route(ctx, input, swarmWorker) {
|
|
|
2832
2954
|
hint
|
|
2833
2955
|
);
|
|
2834
2956
|
}
|
|
2957
|
+
if (input.agent_id && workerLacksTool(input, input.agent_type, s.tool)) {
|
|
2958
|
+
record("allow", "no-shell");
|
|
2959
|
+
return withContext(void 0, hint);
|
|
2960
|
+
}
|
|
2835
2961
|
record("deny");
|
|
2836
2962
|
hookLog("info", `deny ${input.tool_name} \u2192 ${s.tool} [${s.rule ?? "?"}]: ${callHead(input)}`);
|
|
2837
2963
|
return {
|
|
@@ -2954,18 +3080,22 @@ var sessionStart = async (input) => {
|
|
|
2954
3080
|
const sessionId = input.session_id || "unknown";
|
|
2955
3081
|
setCurrentSession(ctx, sessionId);
|
|
2956
3082
|
const db = getStorage(ctx, { vectors: false });
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3083
|
+
try {
|
|
3084
|
+
db.sessionUpsert({
|
|
3085
|
+
id: sessionId,
|
|
3086
|
+
project_root: ctx.root,
|
|
3087
|
+
source: input.source,
|
|
3088
|
+
transcript_path: input.transcript_path
|
|
3089
|
+
});
|
|
3090
|
+
db.eventInsert({
|
|
3091
|
+
sessionId,
|
|
3092
|
+
category: "session_start",
|
|
3093
|
+
priority: 4,
|
|
3094
|
+
payload: { source: input.source }
|
|
3095
|
+
});
|
|
3096
|
+
} catch (err) {
|
|
3097
|
+
hookLog("info", `SessionStart: session not recorded: ${err.message}`);
|
|
3098
|
+
}
|
|
2969
3099
|
if (process.env.CLAUDE_ENV_FILE) {
|
|
2970
3100
|
try {
|
|
2971
3101
|
appendFileSync(
|
|
@@ -2984,7 +3114,11 @@ export EMINIAR_PROJECT_ROOT=${JSON.stringify(ctx.root)}
|
|
|
2984
3114
|
if (snap) {
|
|
2985
3115
|
parts.push(`## Session Guide (eminiar)
|
|
2986
3116
|
${snap.body}`);
|
|
2987
|
-
|
|
3117
|
+
try {
|
|
3118
|
+
db.snapshotMarkConsumed(snap.id);
|
|
3119
|
+
} catch (err) {
|
|
3120
|
+
hookLog("info", `SessionStart: snapshot not marked consumed: ${err.message}`);
|
|
3121
|
+
}
|
|
2988
3122
|
} else {
|
|
2989
3123
|
const prev = input.source === "resume" ? db.sessionLatest() : void 0;
|
|
2990
3124
|
const guide = renderGuide(db, prev && prev.id !== sessionId ? prev.id : sessionId, 2048);
|
|
@@ -2997,7 +3131,7 @@ ${guide}`);
|
|
|
2997
3131
|
if (swarm) parts.push(swarm);
|
|
2998
3132
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor") {
|
|
2999
3133
|
try {
|
|
3000
|
-
const { onSessionStart } = await import("./hook-hooks-
|
|
3134
|
+
const { onSessionStart } = await import("./hook-hooks-IDTGXCFD.js");
|
|
3001
3135
|
const line2 = onSessionStart(ctx, db, input);
|
|
3002
3136
|
if (line2) parts.push(line2);
|
|
3003
3137
|
} catch {
|
|
@@ -3072,7 +3206,29 @@ var handlers = {
|
|
|
3072
3206
|
};
|
|
3073
3207
|
|
|
3074
3208
|
// src/hooks/dispatch.ts
|
|
3075
|
-
|
|
3209
|
+
var late = false;
|
|
3210
|
+
function ranLate() {
|
|
3211
|
+
return late;
|
|
3212
|
+
}
|
|
3213
|
+
async function runWithin(handler, input, ms) {
|
|
3214
|
+
let timer;
|
|
3215
|
+
const deadline = new Promise((resolve3) => {
|
|
3216
|
+
timer = setTimeout(() => resolve3("late"), ms);
|
|
3217
|
+
});
|
|
3218
|
+
try {
|
|
3219
|
+
const r = await Promise.race([handler(input), deadline]);
|
|
3220
|
+
if (r === "late") {
|
|
3221
|
+
late = true;
|
|
3222
|
+
return { out: void 0, timedOut: true };
|
|
3223
|
+
}
|
|
3224
|
+
return { out: r, timedOut: false };
|
|
3225
|
+
} finally {
|
|
3226
|
+
clearTimeout(timer);
|
|
3227
|
+
if (!late) clearBudget();
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
async function dispatch(raw, argv, opts = {}) {
|
|
3231
|
+
late = false;
|
|
3076
3232
|
let parsed;
|
|
3077
3233
|
try {
|
|
3078
3234
|
parsed = raw.trim() ? JSON.parse(raw) : {};
|
|
@@ -3083,13 +3239,20 @@ async function dispatch(raw, argv) {
|
|
|
3083
3239
|
const forced = argv.indexOf("--event");
|
|
3084
3240
|
if (forced !== -1 && argv[forced + 1]) parsed.hook_event_name = argv[forced + 1];
|
|
3085
3241
|
if (argv.includes("--selftest")) return JSON.stringify({ systemMessage: "eminiar hook ok" });
|
|
3086
|
-
if (detectClient(parsed, argv) === "cursor") return runCursor(parsed);
|
|
3242
|
+
if (detectClient(parsed, argv) === "cursor") return runCursor(parsed, opts.budgetMs);
|
|
3087
3243
|
const input = parsed;
|
|
3088
3244
|
const handler = handlers[input.hook_event_name];
|
|
3089
3245
|
if (!handler) return "{}";
|
|
3246
|
+
const ms = opts.budgetMs ?? budgetFor(input.hook_event_name);
|
|
3247
|
+
startBudget(ms);
|
|
3090
3248
|
try {
|
|
3091
|
-
const
|
|
3092
|
-
|
|
3249
|
+
const r = await runWithin(handler, input, ms);
|
|
3250
|
+
if (r.timedOut) {
|
|
3251
|
+
const err = new Error(`hook budget of ${ms} ms exceeded`);
|
|
3252
|
+
hookLog("error", `${input.hook_event_name}: ${err.message}; answered without it`);
|
|
3253
|
+
return failClosed(input, err) ?? "{}";
|
|
3254
|
+
}
|
|
3255
|
+
return JSON.stringify(r.out ?? {});
|
|
3093
3256
|
} catch (err) {
|
|
3094
3257
|
hookLog("error", `${input.hook_event_name}: ${err.stack ?? String(err)}`);
|
|
3095
3258
|
return failClosed(input, err) ?? "{}";
|
|
@@ -3104,6 +3267,10 @@ function failClosed(input, err) {
|
|
|
3104
3267
|
try {
|
|
3105
3268
|
if (input.hook_event_name !== "PreToolUse" || input.client === "cursor" || input.tool_name !== "Agent" && input.tool_name !== "Task" || !rawEnforced({ cwd: input.cwd }))
|
|
3106
3269
|
return void 0;
|
|
3270
|
+
try {
|
|
3271
|
+
swarmAgentReturned(getContext(input.cwd), input);
|
|
3272
|
+
} catch {
|
|
3273
|
+
}
|
|
3107
3274
|
return JSON.stringify({
|
|
3108
3275
|
hookSpecificOutput: {
|
|
3109
3276
|
hookEventName: "PreToolUse",
|
|
@@ -3115,14 +3282,18 @@ function failClosed(input, err) {
|
|
|
3115
3282
|
return void 0;
|
|
3116
3283
|
}
|
|
3117
3284
|
}
|
|
3118
|
-
async function runCursor(parsed) {
|
|
3285
|
+
async function runCursor(parsed, budgetMs2) {
|
|
3119
3286
|
const input = fromCursor(parsed);
|
|
3120
3287
|
const event = input.hook_event_name;
|
|
3121
3288
|
let out;
|
|
3122
3289
|
const handler = handlers[event];
|
|
3123
3290
|
if (handler) {
|
|
3291
|
+
const ms = budgetMs2 ?? budgetFor(event);
|
|
3292
|
+
startBudget(ms);
|
|
3124
3293
|
try {
|
|
3125
|
-
|
|
3294
|
+
const r = await runWithin(handler, input, ms);
|
|
3295
|
+
if (r.timedOut) hookLog("error", `cursor ${event}: hook budget of ${ms} ms exceeded`);
|
|
3296
|
+
out = r.out;
|
|
3126
3297
|
} catch (err) {
|
|
3127
3298
|
hookLog("error", `cursor ${event}: ${err.stack ?? String(err)}`);
|
|
3128
3299
|
}
|
|
@@ -3153,12 +3324,18 @@ async function main() {
|
|
|
3153
3324
|
return;
|
|
3154
3325
|
}
|
|
3155
3326
|
if (process.argv.includes("--adjudicate")) {
|
|
3156
|
-
const { ASYNC_JOB_MAX_MS, runAsyncJob } = await import("./hook-async-
|
|
3327
|
+
const { ASYNC_JOB_MAX_MS, runAsyncJob } = await import("./hook-async-J3UKDO62.js");
|
|
3157
3328
|
setTimeout(() => process.exit(0), ASYNC_JOB_MAX_MS).unref();
|
|
3158
3329
|
await runAsyncJob(raw, { log: (line2) => hookLog("info", line2) });
|
|
3159
3330
|
return;
|
|
3160
3331
|
}
|
|
3161
|
-
|
|
3332
|
+
setStorageBusyTimeout(lockWaitFor(raw, process.argv.slice(2)));
|
|
3333
|
+
const out = await dispatch(raw, process.argv.slice(2));
|
|
3334
|
+
if (!ranLate()) {
|
|
3335
|
+
process.stdout.write(out);
|
|
3336
|
+
return;
|
|
3337
|
+
}
|
|
3338
|
+
process.stdout.write(out, () => process.exit(0));
|
|
3162
3339
|
}
|
|
3163
3340
|
main().catch((err) => {
|
|
3164
3341
|
hookLog("error", `fatal: ${err.stack ?? String(err)}`);
|