@agenticmail/claudecode 0.1.12 → 0.1.14
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/README.md +7 -1
- package/dist/{chunk-6SCQVJBL.js → chunk-2GPBHK2M.js} +1 -1
- package/dist/{chunk-3ZIHOOA4.js → chunk-DKTAW2N5.js} +25 -17
- package/dist/{chunk-DI3DIMUQ.js → chunk-GAD64LKZ.js} +8 -2
- package/dist/{chunk-5H46YKCX.js → chunk-RGLMLG7Y.js} +2 -2
- package/dist/{chunk-YWUZKKQ5.js → chunk-RNKJRBEF.js} +90 -3
- package/dist/cli.js +3 -3
- package/dist/dispatcher-bin.js +1 -1
- package/dist/dispatcher.d.ts +15 -1
- package/dist/dispatcher.js +1 -1
- package/dist/http-routes.js +4 -4
- package/dist/index.js +5 -5
- package/dist/install.js +2 -2
- package/dist/mail-hook.js +16 -9
- package/dist/uninstall.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,13 @@ Agent { subagent_type: "agenticmail-fola", prompt: "draft a reply to my last ema
|
|
|
16
16
|
|
|
17
17
|
This package is to Claude Code what `@agenticmail/openclaw` is to OpenClaw: an integration package that wires AgenticMail into the host AI runtime. It mirrors that package's layout 1:1, so if you know one, you know the other.
|
|
18
18
|
|
|
19
|
-
## ✨ What's new in 0.1.
|
|
19
|
+
## ✨ What's new in 0.1.14
|
|
20
|
+
|
|
21
|
+
- **Workers run for hours** — dropped the 30-min hard timeout. Per-worker log file at `~/.agenticmail/worker-logs/<id>.log` capturing every SDK tool call + result + assistant chunk as a one-liner. Heartbeats POSTed to the API every 30 s so `check_activity` sees real progress. Per-worker scratch cwd at `~/.agenticmail/worker-cwds/<id>/` prevents parallel agents from clobbering each other's output. Tail via the new MCP tool `tail_worker`.
|
|
22
|
+
- **Autonomous-mode awareness via Stop hook** — the mail hook now registers on the **Stop** Claude Code event too. Long headless runs (no user prompts) finally see teammate replies: when bridge mail is unread at a turn boundary, the hook returns `{decision: 'block', reason: '...'}`, forcing Claude to continue with the new-mail summary in context. This is the schema-correct supported way to inject context at Stop, unlike the 0.8.22 PreToolUse attempt.
|
|
23
|
+
- **Hook bin resolved with absolute path** — previously the hook was registered as the bare bin name `agenticmail-mail-hook`, which produced `command not found` errors when the npm global bin dir wasn't on `$PATH`. Now resolved via `import.meta.url` at install time and registered as `node "/abs/.../mail-hook.js"`. Old bare-name installs auto-heal on the next `agenticmail claudecode` run.
|
|
24
|
+
|
|
25
|
+
## ✨ Earlier — 0.1.11
|
|
20
26
|
|
|
21
27
|
- **Selective wake** — when the sender sets `wake: ["alice", "bob"]` on `send_email` / `reply_email`, the dispatcher gives a Claude turn only to listed agents. CC'd-but-not-listed agents still receive the mail in their inbox but stay asleep. Single biggest token saver on multi-agent threads.
|
|
22
28
|
- **Thread-close markers** — `[FINAL]`, `[DONE]`, `[CLOSED]`, `[WRAP]` in a subject. The dispatcher stops waking workers on any further reply to that thread. Closes the "no native done signal" gap from the 5-agent stress test.
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// src/claude-hooks-config.ts
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs";
|
|
3
3
|
import { dirname } from "path";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
function isAgenticMailHookCommand(command) {
|
|
5
|
+
if (typeof command !== "string") return false;
|
|
6
|
+
return command.includes("agenticmail-mail-hook") || command.includes("mail-hook.js");
|
|
7
|
+
}
|
|
8
|
+
var HOOK_EVENTS_TO_REGISTER = ["UserPromptSubmit", "Stop"];
|
|
9
|
+
var HOOK_EVENTS_TO_REMOVE = ["UserPromptSubmit", "Stop", "PreToolUse"];
|
|
6
10
|
function readSettings(path) {
|
|
7
11
|
if (!existsSync(path)) return {};
|
|
8
12
|
const raw = readFileSync(path, "utf-8");
|
|
@@ -29,15 +33,30 @@ function upsertMailHook(path, command) {
|
|
|
29
33
|
const settings = readSettings(path);
|
|
30
34
|
if (!settings.hooks) settings.hooks = {};
|
|
31
35
|
let changed = false;
|
|
32
|
-
for (const event of
|
|
36
|
+
for (const event of HOOK_EVENTS_TO_REGISTER) {
|
|
33
37
|
if (upsertOneEvent(settings.hooks, event, command)) changed = true;
|
|
34
38
|
}
|
|
39
|
+
for (const event of HOOK_EVENTS_TO_REMOVE) {
|
|
40
|
+
if (HOOK_EVENTS_TO_REGISTER.includes(event)) continue;
|
|
41
|
+
if (removeOneEvent(settings.hooks, event)) changed = true;
|
|
42
|
+
}
|
|
35
43
|
if (changed) writeSettings(path, settings);
|
|
36
44
|
return changed;
|
|
37
45
|
}
|
|
46
|
+
function removeOneEvent(hooks, event) {
|
|
47
|
+
const list = hooks[event] ?? [];
|
|
48
|
+
if (list.length === 0) return false;
|
|
49
|
+
const filtered = list.filter(
|
|
50
|
+
(rule) => !rule.hooks?.some((h) => isAgenticMailHookCommand(h.command))
|
|
51
|
+
);
|
|
52
|
+
if (filtered.length === list.length) return false;
|
|
53
|
+
if (filtered.length === 0) delete hooks[event];
|
|
54
|
+
else hooks[event] = filtered;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
38
57
|
function upsertOneEvent(hooks, event, command) {
|
|
39
58
|
const list = hooks[event] ?? [];
|
|
40
|
-
const isOurs = (rule) => rule.hooks?.some((h) =>
|
|
59
|
+
const isOurs = (rule) => rule.hooks?.some((h) => isAgenticMailHookCommand(h.command)) ?? false;
|
|
41
60
|
const desired = {
|
|
42
61
|
matcher: "",
|
|
43
62
|
// empty = match every fire of this event
|
|
@@ -61,19 +80,8 @@ function removeMailHook(path) {
|
|
|
61
80
|
const settings = readSettings(path);
|
|
62
81
|
if (!settings.hooks) return false;
|
|
63
82
|
let changed = false;
|
|
64
|
-
for (const event of
|
|
65
|
-
|
|
66
|
-
if (list.length === 0) continue;
|
|
67
|
-
const filtered = list.filter(
|
|
68
|
-
(rule) => !rule.hooks?.some((h) => typeof h.command === "string" && h.command.includes(AGENTICMAIL_HOOK_MARKER))
|
|
69
|
-
);
|
|
70
|
-
if (filtered.length === list.length) continue;
|
|
71
|
-
if (filtered.length === 0) {
|
|
72
|
-
delete settings.hooks[event];
|
|
73
|
-
} else {
|
|
74
|
-
settings.hooks[event] = filtered;
|
|
75
|
-
}
|
|
76
|
-
changed = true;
|
|
83
|
+
for (const event of HOOK_EVENTS_TO_REMOVE) {
|
|
84
|
+
if (removeOneEvent(settings.hooks, event)) changed = true;
|
|
77
85
|
}
|
|
78
86
|
if (settings.hooks && Object.keys(settings.hooks).length === 0) {
|
|
79
87
|
delete settings.hooks;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
upsertUserPromptSubmitHook
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DKTAW2N5.js";
|
|
4
4
|
import {
|
|
5
5
|
startDispatcher,
|
|
6
6
|
upsertMcpServer
|
|
@@ -111,7 +111,7 @@ async function install(opts = {}) {
|
|
|
111
111
|
const pruned = pruneStaleSubagentFiles(cfg.agentsDir, cfg, liveNames);
|
|
112
112
|
let hookChanged = false;
|
|
113
113
|
try {
|
|
114
|
-
hookChanged = upsertUserPromptSubmitHook(cfg.claudeSettingsPath,
|
|
114
|
+
hookChanged = upsertUserPromptSubmitHook(cfg.claudeSettingsPath, resolveMailHookCommand());
|
|
115
115
|
} catch {
|
|
116
116
|
}
|
|
117
117
|
const dispatcherStatus = await startDispatcherForInstall(cfg);
|
|
@@ -129,6 +129,12 @@ function resolveDispatcherBinPath() {
|
|
|
129
129
|
const dir = thisFile.slice(0, thisFile.lastIndexOf("/"));
|
|
130
130
|
return `${dir}/dispatcher-bin.js`;
|
|
131
131
|
}
|
|
132
|
+
function resolveMailHookCommand() {
|
|
133
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
134
|
+
const dir = thisFile.slice(0, thisFile.lastIndexOf("/"));
|
|
135
|
+
const hookPath = `${dir}/mail-hook.js`;
|
|
136
|
+
return `node "${hookPath}"`;
|
|
137
|
+
}
|
|
132
138
|
async function startDispatcherForInstall(cfg) {
|
|
133
139
|
const binPath = resolveDispatcherBinPath();
|
|
134
140
|
return startDispatcher({
|
|
@@ -36,6 +36,9 @@ function loadPersonaForAgent(opts) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// src/dispatcher.ts
|
|
39
|
+
import { mkdirSync, createWriteStream, rmSync } from "fs";
|
|
40
|
+
import { join as join2 } from "path";
|
|
41
|
+
import { homedir } from "os";
|
|
39
42
|
function extractSubject(event) {
|
|
40
43
|
if (typeof event.subject === "string") return event.subject;
|
|
41
44
|
if (event.message && typeof event.message.subject === "string") return event.message.subject;
|
|
@@ -101,7 +104,7 @@ function threadIdFromSubject(subject) {
|
|
|
101
104
|
}
|
|
102
105
|
var DEFAULT_MAX_WAKES_PER_THREAD = 10;
|
|
103
106
|
var DEFAULT_WAKE_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
104
|
-
async function runWorker(query, persona, userPrompt, agent, mcpServerName, mcpCommand, mcpArgs, mcpEnv, log, abortSignal) {
|
|
107
|
+
async function runWorker(query, persona, userPrompt, agent, mcpServerName, mcpCommand, mcpArgs, mcpEnv, log, abortSignal, observer, cwd) {
|
|
105
108
|
const opts = {
|
|
106
109
|
systemPrompt: persona,
|
|
107
110
|
mcpServers: {
|
|
@@ -133,6 +136,7 @@ async function runWorker(query, persona, userPrompt, agent, mcpServerName, mcpCo
|
|
|
133
136
|
permissionMode: "bypassPermissions",
|
|
134
137
|
abortController: abortSignal ? wrapSignal(abortSignal) : void 0
|
|
135
138
|
};
|
|
139
|
+
if (cwd) opts.cwd = cwd;
|
|
136
140
|
const collectedText = [];
|
|
137
141
|
try {
|
|
138
142
|
for await (const msg of query({ prompt: userPrompt, options: opts })) {
|
|
@@ -140,11 +144,32 @@ async function runWorker(query, persona, userPrompt, agent, mcpServerName, mcpCo
|
|
|
140
144
|
if (m.type === "assistant" && Array.isArray(m.message && m.message.content)) {
|
|
141
145
|
for (const block of m.message.content) {
|
|
142
146
|
const b = block;
|
|
143
|
-
if (b.type === "text" && typeof b.text === "string")
|
|
147
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
148
|
+
collectedText.push(b.text);
|
|
149
|
+
if (observer) observer.onMessage("assistant", b.text.slice(0, 240).replace(/\s+/g, " ").trim());
|
|
150
|
+
} else if (b.type === "tool_use" && typeof b.name === "string") {
|
|
151
|
+
const inputSummary = (() => {
|
|
152
|
+
try {
|
|
153
|
+
return JSON.stringify(b.input).slice(0, 200);
|
|
154
|
+
} catch {
|
|
155
|
+
return "(uninspectable input)";
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
if (observer) observer.onMessage("tool_use", `${b.name} ${inputSummary}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else if (m.type === "user" && Array.isArray(m.message && m.message.content)) {
|
|
162
|
+
for (const block of m.message.content) {
|
|
163
|
+
const b = block;
|
|
164
|
+
if (b.type === "tool_result") {
|
|
165
|
+
const bodyStr = typeof b.content === "string" ? b.content : Array.isArray(b.content) ? b.content.map((c) => c.text ?? "").join(" ") : "";
|
|
166
|
+
if (observer) observer.onMessage("tool_result", bodyStr.slice(0, 240).replace(/\s+/g, " ").trim());
|
|
167
|
+
}
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
170
|
if (m.type === "result" && typeof m.result === "string") {
|
|
147
171
|
collectedText.push(m.result);
|
|
172
|
+
if (observer) observer.onMessage("result", m.result.slice(0, 240).replace(/\s+/g, " ").trim());
|
|
148
173
|
}
|
|
149
174
|
}
|
|
150
175
|
const text = collectedText.join("\n").trim();
|
|
@@ -153,6 +178,7 @@ async function runWorker(query, persona, userPrompt, agent, mcpServerName, mcpCo
|
|
|
153
178
|
} catch (err) {
|
|
154
179
|
const msg = err instanceof Error ? err.message : String(err);
|
|
155
180
|
log("error", `[dispatcher] worker for "${agent.name}" failed: ${msg}`);
|
|
181
|
+
if (observer) observer.onMessage("error", msg);
|
|
156
182
|
return { ok: false, error: msg };
|
|
157
183
|
}
|
|
158
184
|
}
|
|
@@ -699,6 +725,50 @@ var Dispatcher = class {
|
|
|
699
725
|
kind: ctx.kind,
|
|
700
726
|
trigger: { uid: ctx.uid, taskId: ctx.taskId, subject: ctx.subject, from: ctx.from }
|
|
701
727
|
});
|
|
728
|
+
const logsDir = join2(homedir(), ".agenticmail", "worker-logs");
|
|
729
|
+
try {
|
|
730
|
+
mkdirSync(logsDir, { recursive: true });
|
|
731
|
+
} catch {
|
|
732
|
+
}
|
|
733
|
+
const logPath = join2(logsDir, `${sanitizeId(workerId)}.log`);
|
|
734
|
+
let logStream = null;
|
|
735
|
+
try {
|
|
736
|
+
logStream = createWriteStream(logPath, { flags: "a" });
|
|
737
|
+
} catch {
|
|
738
|
+
}
|
|
739
|
+
const writeLog = (line) => {
|
|
740
|
+
try {
|
|
741
|
+
logStream?.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] ${line}
|
|
742
|
+
`);
|
|
743
|
+
} catch {
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
writeLog(`worker_started agent=${account.name} kind=${ctx.kind}${ctx.uid ? " uid=" + ctx.uid : ""}${ctx.taskId ? " task=" + ctx.taskId : ""}`);
|
|
747
|
+
const cwdDir = join2(homedir(), ".agenticmail", "worker-cwds", sanitizeId(workerId));
|
|
748
|
+
try {
|
|
749
|
+
mkdirSync(cwdDir, { recursive: true });
|
|
750
|
+
} catch {
|
|
751
|
+
}
|
|
752
|
+
let turnCount = 0;
|
|
753
|
+
let lastTool = "";
|
|
754
|
+
const observer = {
|
|
755
|
+
onMessage: (tag, summary) => {
|
|
756
|
+
writeLog(`${tag} ${summary}`);
|
|
757
|
+
if (tag === "tool_use") {
|
|
758
|
+
lastTool = summary.split(" ")[0];
|
|
759
|
+
turnCount++;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
const heartbeatHandle = setInterval(() => {
|
|
764
|
+
this.postActivity("/dispatcher/worker-heartbeat", {
|
|
765
|
+
workerId,
|
|
766
|
+
agentName: account.name,
|
|
767
|
+
lastTool: lastTool || void 0,
|
|
768
|
+
turnCount
|
|
769
|
+
});
|
|
770
|
+
}, 3e4);
|
|
771
|
+
heartbeatHandle.unref?.();
|
|
702
772
|
try {
|
|
703
773
|
const { body } = loadPersonaForAgent({
|
|
704
774
|
agent: account,
|
|
@@ -717,16 +787,30 @@ var Dispatcher = class {
|
|
|
717
787
|
this.cfg.mcpCommand,
|
|
718
788
|
this.cfg.mcpArgs,
|
|
719
789
|
mcpEnv,
|
|
720
|
-
this.log
|
|
790
|
+
this.log,
|
|
791
|
+
void 0,
|
|
792
|
+
observer,
|
|
793
|
+
cwdDir
|
|
721
794
|
);
|
|
722
795
|
} finally {
|
|
796
|
+
clearInterval(heartbeatHandle);
|
|
723
797
|
this.releaseSlot();
|
|
724
798
|
const ok = workerResult?.ok === true;
|
|
725
799
|
const preview = workerResult?.ok ? workerResult.text : workerResult ? workerResult.error : "worker did not start";
|
|
800
|
+
writeLog(`worker_finished ok=${ok} chars=${preview.length}`);
|
|
801
|
+
try {
|
|
802
|
+
logStream?.end();
|
|
803
|
+
} catch {
|
|
804
|
+
}
|
|
805
|
+
try {
|
|
806
|
+
rmSync(cwdDir, { recursive: true, force: true });
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
726
809
|
this.postActivity("/dispatcher/worker-finished", {
|
|
727
810
|
workerId,
|
|
728
811
|
agentName: account.name,
|
|
729
812
|
ok,
|
|
813
|
+
turnCount,
|
|
730
814
|
resultPreview: typeof preview === "string" ? preview.slice(0, 240) : void 0
|
|
731
815
|
});
|
|
732
816
|
}
|
|
@@ -789,6 +873,9 @@ var Dispatcher = class {
|
|
|
789
873
|
function sleep(ms) {
|
|
790
874
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
791
875
|
}
|
|
876
|
+
function sanitizeId(id) {
|
|
877
|
+
return id.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
878
|
+
}
|
|
792
879
|
function defaultLog(level, msg) {
|
|
793
880
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
794
881
|
stream.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${msg}
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
uninstall
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2GPBHK2M.js";
|
|
5
5
|
import {
|
|
6
6
|
install
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-GAD64LKZ.js";
|
|
8
|
+
import "./chunk-DKTAW2N5.js";
|
|
9
9
|
import {
|
|
10
10
|
status
|
|
11
11
|
} from "./chunk-O4H76K3B.js";
|
package/dist/dispatcher-bin.js
CHANGED
package/dist/dispatcher.d.ts
CHANGED
|
@@ -110,6 +110,20 @@ interface QueryFn {
|
|
|
110
110
|
options?: Record<string, unknown>;
|
|
111
111
|
}): AsyncIterable<unknown>;
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Per-worker observation channel. `runWorker` calls `onMessage` for every
|
|
115
|
+
* SDK message — assistant text, tool calls, tool results, result frames.
|
|
116
|
+
* The caller (spawnWorker) wires this to:
|
|
117
|
+
* - a per-worker log file at `~/.agenticmail/worker-logs/<id>.log`
|
|
118
|
+
* - a heartbeat ticker that POSTs progress to /dispatcher/worker-heartbeat
|
|
119
|
+
*
|
|
120
|
+
* Kept generic so tests don't need to mock disk + network to verify the
|
|
121
|
+
* observation path.
|
|
122
|
+
*/
|
|
123
|
+
interface WorkerObserver {
|
|
124
|
+
/** Called once per SDK message. Tag is a short event name. */
|
|
125
|
+
onMessage(tag: string, summary: string): void;
|
|
126
|
+
}
|
|
113
127
|
/**
|
|
114
128
|
* The dispatcher itself. Construct once, call .start() to begin watching,
|
|
115
129
|
* .stop() to tear down. Returns when stop() has finished cleaning up.
|
|
@@ -224,4 +238,4 @@ declare class Dispatcher {
|
|
|
224
238
|
private releaseSlot;
|
|
225
239
|
}
|
|
226
240
|
|
|
227
|
-
export { Dispatcher, type DispatcherOptions, type QueryFn };
|
|
241
|
+
export { Dispatcher, type DispatcherOptions, type QueryFn, type WorkerObserver };
|
package/dist/dispatcher.js
CHANGED
package/dist/http-routes.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createIntegrationRoutes
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-RGLMLG7Y.js";
|
|
4
|
+
import "./chunk-2GPBHK2M.js";
|
|
5
|
+
import "./chunk-GAD64LKZ.js";
|
|
6
|
+
import "./chunk-DKTAW2N5.js";
|
|
7
7
|
import "./chunk-O4H76K3B.js";
|
|
8
8
|
import "./chunk-US5FT2UB.js";
|
|
9
9
|
import "./chunk-SBP7MJP2.js";
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Dispatcher,
|
|
3
3
|
loadPersonaForAgent
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RNKJRBEF.js";
|
|
5
5
|
import {
|
|
6
6
|
createIntegrationRoutes
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RGLMLG7Y.js";
|
|
8
8
|
import {
|
|
9
9
|
uninstall
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2GPBHK2M.js";
|
|
11
11
|
import {
|
|
12
12
|
install
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-GAD64LKZ.js";
|
|
14
|
+
import "./chunk-DKTAW2N5.js";
|
|
15
15
|
import {
|
|
16
16
|
status
|
|
17
17
|
} from "./chunk-O4H76K3B.js";
|
package/dist/install.js
CHANGED
package/dist/mail-hook.js
CHANGED
|
@@ -9,7 +9,7 @@ var CONFIG_PATH = join(AGENTICMAIL_DIR, "config.json");
|
|
|
9
9
|
var CURSOR_PATH = join(AGENTICMAIL_DIR, "claudecode-hook-cursor.json");
|
|
10
10
|
var HOOK_VERSION = "1";
|
|
11
11
|
var HTTP_TIMEOUT_MS = 2e3;
|
|
12
|
-
var
|
|
12
|
+
var STOP_THROTTLE_MS = 15e3;
|
|
13
13
|
async function readStdinJson() {
|
|
14
14
|
if (process.stdin.isTTY) return null;
|
|
15
15
|
return new Promise((resolve) => {
|
|
@@ -73,7 +73,7 @@ async function main() {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
const now = Date.now();
|
|
76
|
-
if (eventName === "
|
|
76
|
+
if (eventName === "Stop" && now - lastCheckedMs < STOP_THROTTLE_MS) {
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
79
|
let messages = [];
|
|
@@ -94,7 +94,7 @@ async function main() {
|
|
|
94
94
|
return Number.isFinite(t) && t > cursorMs;
|
|
95
95
|
});
|
|
96
96
|
if (newOnes.length === 0) {
|
|
97
|
-
if (eventName === "
|
|
97
|
+
if (eventName === "Stop") {
|
|
98
98
|
try {
|
|
99
99
|
if (!existsSync(dirname(CURSOR_PATH))) mkdirSync(dirname(CURSOR_PATH), { recursive: true });
|
|
100
100
|
writeFileSync(
|
|
@@ -135,12 +135,19 @@ async function main() {
|
|
|
135
135
|
);
|
|
136
136
|
} catch {
|
|
137
137
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
}
|
|
138
|
+
if (eventName === "Stop") {
|
|
139
|
+
process.stdout.write(JSON.stringify({
|
|
140
|
+
decision: "block",
|
|
141
|
+
reason: lines.join("\n")
|
|
142
|
+
}));
|
|
143
|
+
} else {
|
|
144
|
+
process.stdout.write(JSON.stringify({
|
|
145
|
+
hookSpecificOutput: {
|
|
146
|
+
hookEventName: eventName,
|
|
147
|
+
additionalContext: lines.join("\n")
|
|
148
|
+
}
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
144
151
|
}
|
|
145
152
|
main().catch(() => {
|
|
146
153
|
process.exit(0);
|
package/dist/uninstall.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agenticmail/claudecode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Claude Code integration for AgenticMail — surfaces every AgenticMail agent as a native Claude Code subagent so any Claude Code session can delegate to them with the Agent tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|