@agenticmail/claudecode 0.1.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/LICENSE +21 -0
- package/README.md +299 -0
- package/claudecode.plugin.json +73 -0
- package/dist/chunk-563BZ447.js +408 -0
- package/dist/chunk-P2DXF7DO.js +139 -0
- package/dist/chunk-RI4USTMC.js +89 -0
- package/dist/chunk-ULKJ773Y.js +65 -0
- package/dist/chunk-UPA2YLSM.js +91 -0
- package/dist/chunk-US5FT2UB.js +151 -0
- package/dist/chunk-XAW5NUNU.js +269 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +174 -0
- package/dist/config-BegnlyPD.d.ts +122 -0
- package/dist/dispatcher-bin.d.ts +1 -0
- package/dist/dispatcher-bin.js +40 -0
- package/dist/dispatcher.d.ts +116 -0
- package/dist/dispatcher.js +7 -0
- package/dist/http-routes.d.ts +36 -0
- package/dist/http-routes.js +11 -0
- package/dist/index.d.ts +176 -0
- package/dist/index.js +47 -0
- package/dist/install.d.ts +47 -0
- package/dist/install.js +10 -0
- package/dist/status.d.ts +18 -0
- package/dist/status.js +8 -0
- package/dist/uninstall.d.ts +25 -0
- package/dist/uninstall.js +8 -0
- package/package.json +97 -0
- package/scripts/uninstall.mjs +78 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the @agenticmail/claudecode package.
|
|
3
|
+
*
|
|
4
|
+
* Kept in one file so the install / uninstall / status / discovery modules can
|
|
5
|
+
* import a single source of truth without circular deps.
|
|
6
|
+
*/
|
|
7
|
+
/** An AgenticMail account as returned by `GET /api/agenticmail/accounts`. */
|
|
8
|
+
interface AgenticMailAccount {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
email: string;
|
|
12
|
+
apiKey: string;
|
|
13
|
+
role?: string;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** Resolved configuration for everything the package does. */
|
|
17
|
+
interface ClaudeCodeIntegrationConfig {
|
|
18
|
+
/** AgenticMail master API URL. */
|
|
19
|
+
apiUrl: string;
|
|
20
|
+
/** AgenticMail master key (mk_…). */
|
|
21
|
+
masterKey: string;
|
|
22
|
+
/** Path to Claude Code's user-level config (typically ~/.claude.json). */
|
|
23
|
+
claudeConfigPath: string;
|
|
24
|
+
/** Directory where per-agent Claude Code subagent .md files live (typically ~/.claude/agents). */
|
|
25
|
+
agentsDir: string;
|
|
26
|
+
/** Key under mcpServers in Claude Code's config. */
|
|
27
|
+
mcpServerName: string;
|
|
28
|
+
/** Name of the dedicated AgenticMail agent that represents Claude Code. */
|
|
29
|
+
bridgeAgentName: string;
|
|
30
|
+
/** Prefix for generated subagent names — produces e.g. `agenticmail-fola`. */
|
|
31
|
+
subagentPrefix: string;
|
|
32
|
+
/**
|
|
33
|
+
* Command used to invoke the AgenticMail MCP server.
|
|
34
|
+
* Defaults to the globally-installed `agenticmail-mcp` bin; falls back to
|
|
35
|
+
* `npx -y @agenticmail/mcp` for portability.
|
|
36
|
+
*/
|
|
37
|
+
mcpCommand: string;
|
|
38
|
+
mcpArgs: string[];
|
|
39
|
+
}
|
|
40
|
+
/** Snapshot of the installation state — used by `status`. */
|
|
41
|
+
interface InstallStatus {
|
|
42
|
+
state: 'installed' | 'not_installed' | 'partial';
|
|
43
|
+
/** Whether the MCP server block is present in Claude Code config. */
|
|
44
|
+
mcpInstalled: boolean;
|
|
45
|
+
/** Bridge agent (Claude Code's identity inside AgenticMail) exists. */
|
|
46
|
+
bridgeAgentExists: boolean;
|
|
47
|
+
/** Subagent .md files currently present, keyed by AgenticMail agent name. */
|
|
48
|
+
subagents: string[];
|
|
49
|
+
/** Path to Claude Code config (so the user knows what we touched). */
|
|
50
|
+
claudeConfigPath: string;
|
|
51
|
+
/** Directory used for subagent .md files. */
|
|
52
|
+
agentsDir: string;
|
|
53
|
+
/** Free-form notes for the user (e.g. "API unreachable"). */
|
|
54
|
+
notes: string[];
|
|
55
|
+
/** Dispatcher PM2 status (null when PM2 isn't installed or entry absent). */
|
|
56
|
+
dispatcher: {
|
|
57
|
+
running: boolean;
|
|
58
|
+
pid?: number;
|
|
59
|
+
restartCount?: number;
|
|
60
|
+
uptimeMs?: number;
|
|
61
|
+
} | null;
|
|
62
|
+
}
|
|
63
|
+
/** Result returned by `install`. */
|
|
64
|
+
interface InstallResult {
|
|
65
|
+
/** AgenticMail agents that were turned into Claude Code subagents. */
|
|
66
|
+
registeredAgents: AgenticMailAccount[];
|
|
67
|
+
/** Where the MCP server block was written. */
|
|
68
|
+
claudeConfigPath: string;
|
|
69
|
+
/** Where the subagent .md files were written. */
|
|
70
|
+
agentsDir: string;
|
|
71
|
+
/** The bridge agent (Claude Code's identity inside AgenticMail). */
|
|
72
|
+
bridgeAgent: AgenticMailAccount;
|
|
73
|
+
/** True if the install changed any files (false on no-op re-runs). */
|
|
74
|
+
changed: boolean;
|
|
75
|
+
/** Dispatcher daemon launch status (best-effort; reason populated on failure). */
|
|
76
|
+
dispatcher?: {
|
|
77
|
+
started: boolean;
|
|
78
|
+
reason?: string;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Result returned by `uninstall`. */
|
|
82
|
+
interface UninstallResult {
|
|
83
|
+
/** Whether anything was actually removed. */
|
|
84
|
+
changed: boolean;
|
|
85
|
+
/** Removed subagent .md files. */
|
|
86
|
+
removedSubagents: string[];
|
|
87
|
+
/** Whether the MCP server block was removed from Claude Code config. */
|
|
88
|
+
mcpBlockRemoved: boolean;
|
|
89
|
+
/** Whether the bridge agent was deleted (only if `--purge-bridge` was set). */
|
|
90
|
+
bridgeAgentDeleted: boolean;
|
|
91
|
+
/** Whether the dispatcher PM2 entry was stopped. */
|
|
92
|
+
dispatcherStopped: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resolves a fully-populated ClaudeCodeIntegrationConfig from defaults +
|
|
97
|
+
* overrides + the on-disk AgenticMail config (~/.agenticmail/config.json).
|
|
98
|
+
*
|
|
99
|
+
* Reading the master key from disk lives here (not in install.ts) so tests
|
|
100
|
+
* can supply config inline without touching the filesystem.
|
|
101
|
+
*/
|
|
102
|
+
|
|
103
|
+
/** Public options for resolveConfig — everything is optional and overrides defaults. */
|
|
104
|
+
interface ResolveConfigOptions {
|
|
105
|
+
apiUrl?: string;
|
|
106
|
+
masterKey?: string;
|
|
107
|
+
claudeConfigPath?: string;
|
|
108
|
+
agentsDir?: string;
|
|
109
|
+
mcpServerName?: string;
|
|
110
|
+
bridgeAgentName?: string;
|
|
111
|
+
subagentPrefix?: string;
|
|
112
|
+
mcpCommand?: string;
|
|
113
|
+
mcpArgs?: string[];
|
|
114
|
+
/**
|
|
115
|
+
* Override path to AgenticMail's config.json (defaults to
|
|
116
|
+
* ~/.agenticmail/config.json). Mainly useful in tests.
|
|
117
|
+
*/
|
|
118
|
+
agenticmailConfigPath?: string;
|
|
119
|
+
}
|
|
120
|
+
declare function resolveConfig(opts?: ResolveConfigOptions): ClaudeCodeIntegrationConfig;
|
|
121
|
+
|
|
122
|
+
export { type AgenticMailAccount as A, type ClaudeCodeIntegrationConfig as C, type InstallResult as I, type ResolveConfigOptions as R, type UninstallResult as U, type InstallStatus as a, resolveConfig as r };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
Dispatcher
|
|
4
|
+
} from "./chunk-563BZ447.js";
|
|
5
|
+
import "./chunk-XAW5NUNU.js";
|
|
6
|
+
|
|
7
|
+
// src/dispatcher-bin.ts
|
|
8
|
+
async function main() {
|
|
9
|
+
const dispatcher = new Dispatcher({
|
|
10
|
+
apiUrl: process.env.AGENTICMAIL_API_URL,
|
|
11
|
+
masterKey: process.env.AGENTICMAIL_MASTER_KEY,
|
|
12
|
+
agentsDir: process.env.CLAUDE_CODE_AGENTS_DIR,
|
|
13
|
+
maxConcurrentWorkers: positiveInt(process.env.AGENTICMAIL_DISPATCHER_MAX),
|
|
14
|
+
accountSyncIntervalMs: positiveInt(process.env.AGENTICMAIL_DISPATCHER_SYNC)
|
|
15
|
+
});
|
|
16
|
+
const shutdown = async (sig) => {
|
|
17
|
+
console.error(`[dispatcher-bin] received ${sig} \u2014 shutting down`);
|
|
18
|
+
try {
|
|
19
|
+
await dispatcher.stop();
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error(`[dispatcher-bin] error during shutdown: ${err.message}`);
|
|
22
|
+
}
|
|
23
|
+
process.exit(0);
|
|
24
|
+
};
|
|
25
|
+
process.once("SIGINT", shutdown);
|
|
26
|
+
process.once("SIGTERM", shutdown);
|
|
27
|
+
process.on("unhandledRejection", (reason) => {
|
|
28
|
+
console.error("[dispatcher-bin] unhandledRejection:", reason);
|
|
29
|
+
});
|
|
30
|
+
await dispatcher.start();
|
|
31
|
+
}
|
|
32
|
+
function positiveInt(s) {
|
|
33
|
+
if (!s) return void 0;
|
|
34
|
+
const n = parseInt(s, 10);
|
|
35
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
36
|
+
}
|
|
37
|
+
main().catch((err) => {
|
|
38
|
+
console.error(`[dispatcher-bin] fatal: ${err.message}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { R as ResolveConfigOptions, A as AgenticMailAccount } from './config-BegnlyPD.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AgenticMail → Claude Code event dispatcher.
|
|
5
|
+
*
|
|
6
|
+
* Long-lived daemon that bridges AgenticMail's event stream to the
|
|
7
|
+
* Claude Agent SDK. Concretely: subscribes to the master API's SSE
|
|
8
|
+
* stream for every AgenticMail account, and when an event arrives —
|
|
9
|
+
* either a new mail in some agent's inbox, or a task assigned to some
|
|
10
|
+
* agent — it spawns a Claude-powered worker that *is* that agent (same
|
|
11
|
+
* persona, same `_account`-scoped MCP toolbelt) and lets it handle the
|
|
12
|
+
* trigger.
|
|
13
|
+
*
|
|
14
|
+
* This is what makes "send an email to fola@localhost and she wakes up
|
|
15
|
+
* and replies" work — without any always-on enterprise runtime, and
|
|
16
|
+
* without an interactive Claude Code session having to be open.
|
|
17
|
+
*
|
|
18
|
+
* # Design notes
|
|
19
|
+
*
|
|
20
|
+
* - One SSE connection per account (the master API does not currently
|
|
21
|
+
* expose a master-key "watch everything" endpoint). The dispatcher
|
|
22
|
+
* polls `GET /accounts` every `accountSyncIntervalMs` to discover
|
|
23
|
+
* newly-created accounts and tear down ones that disappeared, so
|
|
24
|
+
* `create_account` is wake-able within ~one sync interval with zero
|
|
25
|
+
* manual steps.
|
|
26
|
+
*
|
|
27
|
+
* - Workers are spawned via `@anthropic-ai/claude-agent-sdk`'s
|
|
28
|
+
* `query()` — same OAuth as the user's `claude`, same MCP server,
|
|
29
|
+
* same persona prompt as the on-disk `.md`. Each worker drains its
|
|
30
|
+
* query stream to completion, then exits.
|
|
31
|
+
*
|
|
32
|
+
* - Concurrency is capped via a small semaphore (default 10). Beyond
|
|
33
|
+
* that, wakes queue. This is a hard floor on Anthropic-side cost:
|
|
34
|
+
* 50 simultaneous wakes = 50 simultaneous Claude calls, which the
|
|
35
|
+
* user is unlikely to want by default.
|
|
36
|
+
*
|
|
37
|
+
* - Task events get an explicit "claim + submit_result" instruction
|
|
38
|
+
* in the wake prompt so the call_agent long-poll on the master API
|
|
39
|
+
* resolves cleanly. Mail events just say "you've got new mail" and
|
|
40
|
+
* trust the persona to do the right thing (read / reply / archive).
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/** Event shape we accept off the SSE stream. */
|
|
44
|
+
interface SSEEvent {
|
|
45
|
+
type?: string;
|
|
46
|
+
uid?: number;
|
|
47
|
+
from?: string;
|
|
48
|
+
subject?: string;
|
|
49
|
+
taskId?: string;
|
|
50
|
+
taskType?: string;
|
|
51
|
+
task?: string;
|
|
52
|
+
assignee?: string;
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
}
|
|
55
|
+
interface DispatcherOptions extends ResolveConfigOptions {
|
|
56
|
+
/** Max concurrent workers. Default 10. Hard floor on Anthropic cost. */
|
|
57
|
+
maxConcurrentWorkers?: number;
|
|
58
|
+
/** How often to re-poll /accounts for new agents. Default 60s. */
|
|
59
|
+
accountSyncIntervalMs?: number;
|
|
60
|
+
/** How long to wait between SSE reconnect attempts (start). Default 2s. */
|
|
61
|
+
sseReconnectBaseMs?: number;
|
|
62
|
+
/** Max backoff between SSE reconnect attempts. Default 60s. */
|
|
63
|
+
sseReconnectMaxMs?: number;
|
|
64
|
+
/** Override the Claude Agent SDK `query` function. Used by tests. */
|
|
65
|
+
querySdk?: QueryFn;
|
|
66
|
+
/** Override the global `fetch`. Used by tests. */
|
|
67
|
+
fetchImpl?: typeof fetch;
|
|
68
|
+
/** Override the global EventSource. Optional — we don't use EventSource
|
|
69
|
+
* by default (fetch + reader is simpler). */
|
|
70
|
+
log?: (level: 'info' | 'warn' | 'error', msg: string) => void;
|
|
71
|
+
}
|
|
72
|
+
/** Minimal Claude Agent SDK query signature we use. */
|
|
73
|
+
interface QueryFn {
|
|
74
|
+
(params: {
|
|
75
|
+
prompt: string;
|
|
76
|
+
options?: Record<string, unknown>;
|
|
77
|
+
}): AsyncIterable<unknown>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The dispatcher itself. Construct once, call .start() to begin watching,
|
|
81
|
+
* .stop() to tear down. Returns when stop() has finished cleaning up.
|
|
82
|
+
*/
|
|
83
|
+
declare class Dispatcher {
|
|
84
|
+
private cfg;
|
|
85
|
+
private maxConcurrent;
|
|
86
|
+
private syncIntervalMs;
|
|
87
|
+
private reconnectBaseMs;
|
|
88
|
+
private reconnectMaxMs;
|
|
89
|
+
private query;
|
|
90
|
+
private fetchImpl;
|
|
91
|
+
private log;
|
|
92
|
+
private channels;
|
|
93
|
+
private accountSyncTimer;
|
|
94
|
+
private running;
|
|
95
|
+
private waiters;
|
|
96
|
+
private stopped;
|
|
97
|
+
constructor(opts?: DispatcherOptions);
|
|
98
|
+
start(): Promise<void>;
|
|
99
|
+
stop(): Promise<void>;
|
|
100
|
+
/** Public for tests — directly hand an event to the routing path. */
|
|
101
|
+
handleEvent(account: AgenticMailAccount, event: SSEEvent): Promise<void>;
|
|
102
|
+
/** Re-fetch /accounts; open SSE for new ones, close for vanished ones. */
|
|
103
|
+
private syncAccounts;
|
|
104
|
+
/** Watch one account's SSE stream forever; reconnect with backoff on drop. */
|
|
105
|
+
private runChannel;
|
|
106
|
+
/** Single SSE attach. Returns when the stream closes for any reason. */
|
|
107
|
+
private streamOne;
|
|
108
|
+
/** Acquire a concurrency slot, run a worker, release the slot. */
|
|
109
|
+
private spawnWorker;
|
|
110
|
+
/** Build the env block we pass to the worker's MCP server child process. */
|
|
111
|
+
private buildMcpEnv;
|
|
112
|
+
private acquireSlot;
|
|
113
|
+
private releaseSlot;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { Dispatcher, type DispatcherOptions, type QueryFn };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HTTP routes for the Claude Code integration.
|
|
5
|
+
*
|
|
6
|
+
* Mounted by `@agenticmail/api` (see app.ts) under
|
|
7
|
+
* /api/agenticmail/integrations/claudecode
|
|
8
|
+
*
|
|
9
|
+
* Purpose: let an agent (Claude Code itself, a shell script, a CI job, …)
|
|
10
|
+
* install the integration with a single POST request, no terminal interaction
|
|
11
|
+
* required. The endpoints are deliberately registered BEFORE the master-key
|
|
12
|
+
* auth middleware because they ARE the bootstrap — a Claude Code session that
|
|
13
|
+
* doesn't yet have AgenticMail wired up has no way to know the master key,
|
|
14
|
+
* so requiring it would defeat the purpose.
|
|
15
|
+
*
|
|
16
|
+
* SECURITY MODEL
|
|
17
|
+
* --------------
|
|
18
|
+
* The AgenticMail master API binds to 127.0.0.1 by default. Any process that
|
|
19
|
+
* can reach this endpoint can already read ~/.agenticmail/config.json (same
|
|
20
|
+
* file ownership), so leaving these endpoints unauthenticated does not widen
|
|
21
|
+
* the attack surface. If the operator binds the API to a non-loopback
|
|
22
|
+
* interface they MUST put auth or a firewall in front of it — same as every
|
|
23
|
+
* other unauthenticated route on this server (e.g. /health).
|
|
24
|
+
*
|
|
25
|
+
* The install handler still reads the master key from disk before touching
|
|
26
|
+
* AgenticMail, so the routes don't grant *new* powers — they just save the
|
|
27
|
+
* agent the trouble of plumbing one in.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build the Express router. Caller mounts at the prefix of its choosing
|
|
32
|
+
* (typically `/api/agenticmail/integrations/claudecode`).
|
|
33
|
+
*/
|
|
34
|
+
declare function createIntegrationRoutes(): Router;
|
|
35
|
+
|
|
36
|
+
export { createIntegrationRoutes };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createIntegrationRoutes
|
|
3
|
+
} from "./chunk-UPA2YLSM.js";
|
|
4
|
+
import "./chunk-P2DXF7DO.js";
|
|
5
|
+
import "./chunk-RI4USTMC.js";
|
|
6
|
+
import "./chunk-ULKJ773Y.js";
|
|
7
|
+
import "./chunk-US5FT2UB.js";
|
|
8
|
+
import "./chunk-XAW5NUNU.js";
|
|
9
|
+
export {
|
|
10
|
+
createIntegrationRoutes
|
|
11
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
export { install } from './install.js';
|
|
2
|
+
export { UninstallOptions, uninstall } from './uninstall.js';
|
|
3
|
+
export { status } from './status.js';
|
|
4
|
+
import { A as AgenticMailAccount } from './config-BegnlyPD.js';
|
|
5
|
+
export { C as ClaudeCodeIntegrationConfig, I as InstallResult, a as InstallStatus, R as ResolveConfigOptions, U as UninstallResult, r as resolveConfig } from './config-BegnlyPD.js';
|
|
6
|
+
export { createIntegrationRoutes } from './http-routes.js';
|
|
7
|
+
export { Dispatcher, DispatcherOptions, QueryFn } from './dispatcher.js';
|
|
8
|
+
import 'express';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Thin client for the AgenticMail master API.
|
|
12
|
+
*
|
|
13
|
+
* We talk to ONE endpoint family — `/api/agenticmail/accounts` — to discover
|
|
14
|
+
* and provision agents. The MCP server itself handles every other call once
|
|
15
|
+
* Claude Code is wired up, so this client deliberately stays tiny.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare class AgenticMailApiError extends Error {
|
|
19
|
+
status: number;
|
|
20
|
+
constructor(status: number, message: string);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Confirm the master API is reachable.
|
|
24
|
+
*
|
|
25
|
+
* The `/health` route is intentionally unauthenticated upstream — we use it
|
|
26
|
+
* here precisely *because* it doesn't require the master key, so an early
|
|
27
|
+
* misconfiguration shows the user "API is down" rather than "key is wrong".
|
|
28
|
+
* Returns the version string from the response.
|
|
29
|
+
*/
|
|
30
|
+
declare function checkApiHealth(apiUrl: string): Promise<{
|
|
31
|
+
ok: true;
|
|
32
|
+
version?: string;
|
|
33
|
+
}>;
|
|
34
|
+
/** List every AgenticMail account (agents). Requires the master key. */
|
|
35
|
+
declare function listAccounts(apiUrl: string, masterKey: string): Promise<AgenticMailAccount[]>;
|
|
36
|
+
/** Look up a single account by name. Returns null if not found. */
|
|
37
|
+
declare function getAccountByName(apiUrl: string, masterKey: string, name: string): Promise<AgenticMailAccount | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Create an AgenticMail account. Idempotent at the call site: if the name
|
|
40
|
+
* already exists, returns the existing record instead of throwing.
|
|
41
|
+
*/
|
|
42
|
+
declare function ensureAccount(apiUrl: string, masterKey: string, name: string, role?: string): Promise<AgenticMailAccount>;
|
|
43
|
+
/** Delete an account by id. */
|
|
44
|
+
declare function deleteAccount(apiUrl: string, masterKey: string, id: string): Promise<void>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generates the markdown content for one Claude Code subagent file.
|
|
48
|
+
*
|
|
49
|
+
* A Claude Code subagent is a `.md` file in `~/.claude/agents/` with YAML
|
|
50
|
+
* frontmatter. When the host Claude Code session calls
|
|
51
|
+
* Agent { subagent_type: "<name>", prompt: "..." }
|
|
52
|
+
* Claude Code spawns a fresh session whose system prompt is the body of the
|
|
53
|
+
* `.md` file and whose `tools` are restricted to those listed in frontmatter.
|
|
54
|
+
*
|
|
55
|
+
* # Design: Claude Code is the brain
|
|
56
|
+
*
|
|
57
|
+
* Each AgenticMail "agent" (Fola, John, …) is a mailbox + persistent state
|
|
58
|
+
* inside AgenticMail: an email address, a folder of past mail, a task
|
|
59
|
+
* queue, and an API key. This package supplies the missing piece — the
|
|
60
|
+
* *thinking* — by making the user's Claude Code session itself drive each
|
|
61
|
+
* agent's behaviour.
|
|
62
|
+
*
|
|
63
|
+
* When the host session calls `Agent { subagent_type: "agenticmail-fola",
|
|
64
|
+
* ... }`, Claude Code spawns a fresh subagent whose system prompt is the
|
|
65
|
+
* persona below. That subagent uses Claude Code's own Claude OAuth
|
|
66
|
+
* credentials (no separate Anthropic key needed) and operates Fola's
|
|
67
|
+
* mailbox via the MCP server with `_account: "Fola"` on every call. From
|
|
68
|
+
* the outside, Fola behaves as you'd expect: she reads her email, sends
|
|
69
|
+
* mail from fola@localhost, manages her tasks. Internally she is powered
|
|
70
|
+
* by the same Claude that powers the host session.
|
|
71
|
+
*
|
|
72
|
+
* This is the "AgenticMail rides on Claude Code" architecture.
|
|
73
|
+
*
|
|
74
|
+
* # Tiered tool loading (token budget)
|
|
75
|
+
*
|
|
76
|
+
* Loading all 62 AgenticMail tool schemas into a fresh subagent's context
|
|
77
|
+
* costs ~10K tokens per spawn — most of it never used. We mirror the
|
|
78
|
+
* three-tier lazy-loading design from the AgenticMail enterprise
|
|
79
|
+
* tool-resolver:
|
|
80
|
+
*
|
|
81
|
+
* - Tier 1 — ESSENTIAL: a curated whitelist of ~9 common tools, plus
|
|
82
|
+
* the two meta-tools `request_tools` and `invoke`. These are listed
|
|
83
|
+
* in the subagent's `tools:` frontmatter so they're available
|
|
84
|
+
* immediately at spawn.
|
|
85
|
+
*
|
|
86
|
+
* - Tier 2/3 — ON-DEMAND: the other ~50 tools (signatures, drafts,
|
|
87
|
+
* bulk ops, SMS voice, setup wizards, account admin, …). The agent
|
|
88
|
+
* discovers them via `request_tools` (returns a text catalogue) and
|
|
89
|
+
* calls them via `invoke({ tool, args, _account })`.
|
|
90
|
+
*
|
|
91
|
+
* Net effect: spawn cost drops from ~15K tokens to ~3-4K, while the full
|
|
92
|
+
* tool surface remains reachable. The trade-off is one extra round trip
|
|
93
|
+
* for uncommon operations.
|
|
94
|
+
*
|
|
95
|
+
* Generic Claude Code tools (Read/Edit/Bash/Glob/Grep/WebFetch/…) are
|
|
96
|
+
* NOT in the `tools:` whitelist — Claude Code will refuse to call them
|
|
97
|
+
* from inside this subagent, which mechanically enforces the
|
|
98
|
+
* "you operate an email account, not a developer environment" rule.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/** Configuration shape used when building one subagent's .md content. */
|
|
102
|
+
interface SubagentTemplateInput {
|
|
103
|
+
/** Subagent name (already includes the prefix, e.g. "agenticmail-fola"). */
|
|
104
|
+
name: string;
|
|
105
|
+
/** The AgenticMail agent this subagent embodies. */
|
|
106
|
+
agent: AgenticMailAccount;
|
|
107
|
+
/** MCP server key as configured in ~/.claude.json (e.g. "agenticmail"). */
|
|
108
|
+
mcpServerName: string;
|
|
109
|
+
}
|
|
110
|
+
/** Marker we embed in frontmatter so uninstall can be sure a file is ours. */
|
|
111
|
+
declare const MANAGED_BY_MARKER = "@agenticmail/claudecode";
|
|
112
|
+
/**
|
|
113
|
+
* Render JUST the persona body (no frontmatter, no .md wrapper).
|
|
114
|
+
*
|
|
115
|
+
* This is what the dispatcher feeds to the Claude Agent SDK as the
|
|
116
|
+
* `systemPrompt` when waking an agent — the SDK doesn't read `.md`
|
|
117
|
+
* frontmatter, only the prose. Splitting the body out also lets us
|
|
118
|
+
* generate a persona for a never-seen-before account (e.g. one that
|
|
119
|
+
* was just `create_account`ed by another worker) without needing a
|
|
120
|
+
* pre-written `.md` file on disk.
|
|
121
|
+
*
|
|
122
|
+
* `renderSubagentMarkdown` builds on top of this by adding the YAML
|
|
123
|
+
* frontmatter Claude Code's Agent tool needs.
|
|
124
|
+
*/
|
|
125
|
+
declare function renderPersonaBody(input: SubagentTemplateInput): string;
|
|
126
|
+
/**
|
|
127
|
+
* Produce the full text (frontmatter + body) for one subagent .md file.
|
|
128
|
+
*
|
|
129
|
+
* The body is a "you are <Agent>" persona that drives the subagent to do
|
|
130
|
+
* real work using MCP tools scoped to its own account.
|
|
131
|
+
*/
|
|
132
|
+
declare function renderSubagentMarkdown(input: SubagentTemplateInput): string;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Resolves a persona prompt for an AgenticMail agent, with two sources
|
|
136
|
+
* tried in order:
|
|
137
|
+
*
|
|
138
|
+
* 1. `~/.claude/agents/agenticmail-<name>.md` on disk (if present).
|
|
139
|
+
* Lets the operator customise an agent's behaviour by hand-editing
|
|
140
|
+
* its file. Owned-by-us files (frontmatter marker) AND user-owned
|
|
141
|
+
* files are both honoured — the dispatcher trusts whatever is on
|
|
142
|
+
* disk for that agent.
|
|
143
|
+
*
|
|
144
|
+
* 2. In-memory render from live AgenticMail account metadata via
|
|
145
|
+
* `renderPersonaBody`. This is the path for agents that were just
|
|
146
|
+
* `create_account`-ed and have no file yet — they become wake-able
|
|
147
|
+
* immediately, no install step required.
|
|
148
|
+
*
|
|
149
|
+
* Returns the persona BODY (no YAML frontmatter). That's what the
|
|
150
|
+
* Claude Agent SDK consumes as a system prompt.
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
interface LoadPersonaOptions {
|
|
154
|
+
agent: AgenticMailAccount;
|
|
155
|
+
/** Directory holding per-agent .md files. Default: ~/.claude/agents. */
|
|
156
|
+
agentsDir: string;
|
|
157
|
+
/** Prefix for filenames. Default: "agenticmail-". */
|
|
158
|
+
subagentPrefix: string;
|
|
159
|
+
/** MCP server name used inside tool examples in the prose. */
|
|
160
|
+
mcpServerName: string;
|
|
161
|
+
}
|
|
162
|
+
interface LoadedPersona {
|
|
163
|
+
/** The persona body — system prompt for the worker. */
|
|
164
|
+
body: string;
|
|
165
|
+
/** Where the body came from (for logs / debugging). */
|
|
166
|
+
source: 'file' | 'generated';
|
|
167
|
+
/** Resolved file path if source === 'file'. */
|
|
168
|
+
filePath?: string;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Try the disk file; fall back to live generation. Pure function — no
|
|
172
|
+
* side effects, no API calls (the account metadata is passed in).
|
|
173
|
+
*/
|
|
174
|
+
declare function loadPersonaForAgent(opts: LoadPersonaOptions): LoadedPersona;
|
|
175
|
+
|
|
176
|
+
export { AgenticMailAccount, AgenticMailApiError, type LoadPersonaOptions, type LoadedPersona, MANAGED_BY_MARKER, checkApiHealth, deleteAccount, ensureAccount, getAccountByName, listAccounts, loadPersonaForAgent, renderPersonaBody, renderSubagentMarkdown };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Dispatcher,
|
|
3
|
+
loadPersonaForAgent
|
|
4
|
+
} from "./chunk-563BZ447.js";
|
|
5
|
+
import {
|
|
6
|
+
createIntegrationRoutes
|
|
7
|
+
} from "./chunk-UPA2YLSM.js";
|
|
8
|
+
import {
|
|
9
|
+
install
|
|
10
|
+
} from "./chunk-P2DXF7DO.js";
|
|
11
|
+
import {
|
|
12
|
+
status
|
|
13
|
+
} from "./chunk-RI4USTMC.js";
|
|
14
|
+
import {
|
|
15
|
+
uninstall
|
|
16
|
+
} from "./chunk-ULKJ773Y.js";
|
|
17
|
+
import "./chunk-US5FT2UB.js";
|
|
18
|
+
import {
|
|
19
|
+
AgenticMailApiError,
|
|
20
|
+
MANAGED_BY_MARKER,
|
|
21
|
+
checkApiHealth,
|
|
22
|
+
deleteAccount,
|
|
23
|
+
ensureAccount,
|
|
24
|
+
getAccountByName,
|
|
25
|
+
listAccounts,
|
|
26
|
+
renderPersonaBody,
|
|
27
|
+
renderSubagentMarkdown,
|
|
28
|
+
resolveConfig
|
|
29
|
+
} from "./chunk-XAW5NUNU.js";
|
|
30
|
+
export {
|
|
31
|
+
AgenticMailApiError,
|
|
32
|
+
Dispatcher,
|
|
33
|
+
MANAGED_BY_MARKER,
|
|
34
|
+
checkApiHealth,
|
|
35
|
+
createIntegrationRoutes,
|
|
36
|
+
deleteAccount,
|
|
37
|
+
ensureAccount,
|
|
38
|
+
getAccountByName,
|
|
39
|
+
install,
|
|
40
|
+
listAccounts,
|
|
41
|
+
loadPersonaForAgent,
|
|
42
|
+
renderPersonaBody,
|
|
43
|
+
renderSubagentMarkdown,
|
|
44
|
+
resolveConfig,
|
|
45
|
+
status,
|
|
46
|
+
uninstall
|
|
47
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { R as ResolveConfigOptions, I as InstallResult, A as AgenticMailAccount, C as ClaudeCodeIntegrationConfig } from './config-BegnlyPD.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Install AgenticMail into Claude Code.
|
|
5
|
+
*
|
|
6
|
+
* Pure-function-ish: takes config, writes files, returns a result object.
|
|
7
|
+
* The chalk-painted CLI wizard (in agenticmail/src/cli.ts → cmdClaudeCode)
|
|
8
|
+
* is responsible for narrating progress. This module knows nothing about
|
|
9
|
+
* the terminal.
|
|
10
|
+
*
|
|
11
|
+
* What we write:
|
|
12
|
+
* 1. ~/.claude.json → adds mcpServers.<name> entry that runs the AgenticMail MCP server
|
|
13
|
+
* 2. ~/.claude/agents/<prefix><agent>.md → one Claude Code subagent per AgenticMail agent
|
|
14
|
+
*
|
|
15
|
+
* What we provision in AgenticMail:
|
|
16
|
+
* - A single "claudecode" agent whose API key the MCP server uses as its identity.
|
|
17
|
+
* This is the same model OpenClaw's plugin uses: every external host gets
|
|
18
|
+
* its own AgenticMail identity so call traces are attributable.
|
|
19
|
+
*
|
|
20
|
+
* What we do NOT do:
|
|
21
|
+
* - We do NOT touch ~/.claude/.credentials.json (the host Claude OAuth token).
|
|
22
|
+
* Claude Code manages those credentials and uses them when spawning each
|
|
23
|
+
* subagent — we never need to read or modify them. See README "How auth
|
|
24
|
+
* works" for the full story.
|
|
25
|
+
* - We do NOT touch any per-agent runtime artefacts that may exist outside
|
|
26
|
+
* AgenticMail itself. Agent discovery uses the master API alone — that
|
|
27
|
+
* is the only contract this package depends on.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Decide which AgenticMail agents to surface as Claude Code subagents.
|
|
32
|
+
*
|
|
33
|
+
* Filters out:
|
|
34
|
+
* - the bridge agent itself (Claude Code's own identity — calling yourself
|
|
35
|
+
* is silly and would also create a duplicate-name collision in the
|
|
36
|
+
* subagent_type namespace)
|
|
37
|
+
* - any account with role="bridge" (reserved for hosts like this one)
|
|
38
|
+
*/
|
|
39
|
+
declare function selectExposableAgents(accounts: AgenticMailAccount[], cfg: ClaudeCodeIntegrationConfig): AgenticMailAccount[];
|
|
40
|
+
/**
|
|
41
|
+
* Top-level install. Throws if the AgenticMail master API is unreachable —
|
|
42
|
+
* we refuse to write a half-broken config that Claude Code would silently
|
|
43
|
+
* load but never connect.
|
|
44
|
+
*/
|
|
45
|
+
declare function install(opts?: ResolveConfigOptions): Promise<InstallResult>;
|
|
46
|
+
|
|
47
|
+
export { install, selectExposableAgents };
|
package/dist/install.js
ADDED
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { R as ResolveConfigOptions, a as InstallStatus } from './config-BegnlyPD.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Inspect the current install state of @agenticmail/claudecode.
|
|
5
|
+
*
|
|
6
|
+
* Used by:
|
|
7
|
+
* - `agenticmail status` (top-level command, prints a one-line summary)
|
|
8
|
+
* - `agenticmail claudecode --status` (prints a detailed report)
|
|
9
|
+
* - tests
|
|
10
|
+
*
|
|
11
|
+
* The function is forgiving: anything that can't be checked (e.g. API down)
|
|
12
|
+
* is recorded as a note rather than a thrown error. The point of `status` is
|
|
13
|
+
* to help the user fix whatever's wrong, not to surface stack traces.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
declare function status(opts?: ResolveConfigOptions): Promise<InstallStatus>;
|
|
17
|
+
|
|
18
|
+
export { status };
|
package/dist/status.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { R as ResolveConfigOptions, U as UninstallResult } from './config-BegnlyPD.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Uninstall AgenticMail from Claude Code.
|
|
5
|
+
*
|
|
6
|
+
* Reverses everything install() did, in the opposite order, with one
|
|
7
|
+
* deliberate exception: we keep the bridge agent in AgenticMail by default.
|
|
8
|
+
*
|
|
9
|
+
* Why? The bridge agent owns an inbox, may have ongoing conversations, and
|
|
10
|
+
* may be referenced from other agents' contact lists. Silently deleting it
|
|
11
|
+
* during a Claude Code uninstall is destructive in a way users would not
|
|
12
|
+
* expect. Pass { purgeBridgeAgent: true } if you really want it gone.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface UninstallOptions extends ResolveConfigOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Also delete the bridge agent from AgenticMail. Default false — see header
|
|
18
|
+
* comment. Setting this to true is irreversible (the agent's API key, inbox
|
|
19
|
+
* folder layout, and contact references will all be invalidated).
|
|
20
|
+
*/
|
|
21
|
+
purgeBridgeAgent?: boolean;
|
|
22
|
+
}
|
|
23
|
+
declare function uninstall(opts?: UninstallOptions): Promise<UninstallResult>;
|
|
24
|
+
|
|
25
|
+
export { type UninstallOptions, uninstall };
|