aamp-openclaw-plugin 0.1.10 → 0.1.12
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 +18 -5
- package/bin/aamp-openclaw-plugin.mjs +46 -15
- package/dist/index.js +57 -20
- package/dist/index.js.map +2 -2
- package/openclaw.plugin.json +16 -3
- package/package.json +18 -5
package/README.md
CHANGED
|
@@ -8,10 +8,17 @@ OpenClaw plugin that gives an OpenClaw agent an AAMP mailbox identity.
|
|
|
8
8
|
npm install aamp-openclaw-plugin
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
When installed
|
|
11
|
+
When installed via:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx aamp-openclaw-plugin init
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
the installer will prompt for:
|
|
12
18
|
|
|
13
19
|
- `AAMP Host`
|
|
14
|
-
- `
|
|
20
|
+
- `Primary trusted dispatch sender`
|
|
21
|
+
- optional `Dispatch context rules`
|
|
15
22
|
|
|
16
23
|
The answers are written into the OpenClaw plugin config automatically, so users do not need to hand-edit `openclaw.json`.
|
|
17
24
|
|
|
@@ -33,8 +40,14 @@ npm run build
|
|
|
33
40
|
"aampHost": "https://meshmail.ai",
|
|
34
41
|
"slug": "openclaw-agent",
|
|
35
42
|
"credentialsFile": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json",
|
|
36
|
-
"
|
|
37
|
-
|
|
43
|
+
"senderPolicies": [
|
|
44
|
+
{
|
|
45
|
+
"sender": "meegle-bot@meshmail.ai",
|
|
46
|
+
"dispatchContextRules": {
|
|
47
|
+
"project_key": ["proj_123"],
|
|
48
|
+
"user_key": ["alice"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
38
51
|
]
|
|
39
52
|
}
|
|
40
53
|
}
|
|
@@ -43,4 +56,4 @@ npm run build
|
|
|
43
56
|
}
|
|
44
57
|
```
|
|
45
58
|
|
|
46
|
-
If `
|
|
59
|
+
If `senderPolicies` is omitted, all senders are accepted. If set, the dispatch sender must match one policy and all configured dispatch-context rules for that sender must pass.
|
|
@@ -62,8 +62,8 @@ function ensurePluginConfig(config, pluginConfig) {
|
|
|
62
62
|
...(prevEntry?.config && typeof prevEntry.config === 'object' ? prevEntry.config : {}),
|
|
63
63
|
...pluginConfig,
|
|
64
64
|
}
|
|
65
|
-
if (!pluginConfig.
|
|
66
|
-
delete mergedConfig.
|
|
65
|
+
if (!pluginConfig.senderPolicies) {
|
|
66
|
+
delete mergedConfig.senderPolicies
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
next.plugins.entries[PLUGIN_ID] = {
|
|
@@ -79,13 +79,27 @@ function ensurePluginConfig(config, pluginConfig) {
|
|
|
79
79
|
return next
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function
|
|
82
|
+
function parseDispatchContextRules(raw) {
|
|
83
83
|
const trimmed = raw.trim()
|
|
84
84
|
if (!trimmed) return undefined
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
const rules = {}
|
|
86
|
+
for (const part of trimmed.split(';')) {
|
|
87
|
+
const segment = part.trim()
|
|
88
|
+
if (!segment) continue
|
|
89
|
+
const eqIdx = segment.indexOf('=')
|
|
90
|
+
if (eqIdx <= 0) continue
|
|
91
|
+
const key = segment.slice(0, eqIdx).trim().toLowerCase()
|
|
92
|
+
if (!/^[a-z0-9_-]+$/.test(key)) continue
|
|
93
|
+
const values = segment
|
|
94
|
+
.slice(eqIdx + 1)
|
|
95
|
+
.split(',')
|
|
96
|
+
.map((s) => s.trim())
|
|
97
|
+
.filter(Boolean)
|
|
98
|
+
if (values.length) {
|
|
99
|
+
rules[key] = values
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return Object.keys(rules).length ? rules : undefined
|
|
89
103
|
}
|
|
90
104
|
|
|
91
105
|
function packageRootFromEntry(entryPath) {
|
|
@@ -230,7 +244,7 @@ function printHelp() {
|
|
|
230
244
|
|
|
231
245
|
async function runInit() {
|
|
232
246
|
let aampHost = DEFAULT_AAMP_HOST
|
|
233
|
-
let
|
|
247
|
+
let senderPolicies
|
|
234
248
|
|
|
235
249
|
if (input.isTTY) {
|
|
236
250
|
const rl = createInterface({ input, output })
|
|
@@ -240,17 +254,34 @@ async function runInit() {
|
|
|
240
254
|
const aampHostAnswer = await rl.question(`AAMP Host (${DEFAULT_AAMP_HOST}): `)
|
|
241
255
|
aampHost = aampHostAnswer.trim() || DEFAULT_AAMP_HOST
|
|
242
256
|
|
|
243
|
-
const
|
|
244
|
-
'
|
|
257
|
+
const senderAnswer = await rl.question(
|
|
258
|
+
'Primary trusted dispatch sender (e.g. meegle-bot@meshmail.ai, leave blank to allow all): ',
|
|
245
259
|
)
|
|
246
|
-
|
|
260
|
+
const sender = senderAnswer.trim()
|
|
261
|
+
if (sender) {
|
|
262
|
+
const rulesAnswer = await rl.question(
|
|
263
|
+
'Dispatch context rules for that sender (optional, format: project_key=proj1,proj2; user_key=alice): ',
|
|
264
|
+
)
|
|
265
|
+
const dispatchContextRules = parseDispatchContextRules(rulesAnswer)
|
|
266
|
+
senderPolicies = [{
|
|
267
|
+
sender,
|
|
268
|
+
...(dispatchContextRules ? { dispatchContextRules } : {}),
|
|
269
|
+
}]
|
|
270
|
+
}
|
|
247
271
|
} finally {
|
|
248
272
|
rl.close()
|
|
249
273
|
}
|
|
250
274
|
} else {
|
|
251
|
-
const [hostLine = '',
|
|
275
|
+
const [hostLine = '', senderLine = '', rulesLine = ''] = readFileSync(0, 'utf-8').split(/\r?\n/)
|
|
252
276
|
aampHost = hostLine.trim() || DEFAULT_AAMP_HOST
|
|
253
|
-
|
|
277
|
+
const sender = senderLine.trim()
|
|
278
|
+
if (sender) {
|
|
279
|
+
const dispatchContextRules = parseDispatchContextRules(rulesLine)
|
|
280
|
+
senderPolicies = [{
|
|
281
|
+
sender,
|
|
282
|
+
...(dispatchContextRules ? { dispatchContextRules } : {}),
|
|
283
|
+
}]
|
|
284
|
+
}
|
|
254
285
|
}
|
|
255
286
|
|
|
256
287
|
const configPath = resolveOpenClawConfigPath()
|
|
@@ -265,7 +296,7 @@ async function runInit() {
|
|
|
265
296
|
aampHost,
|
|
266
297
|
slug: 'openclaw-agent',
|
|
267
298
|
credentialsFile: DEFAULT_CREDENTIALS_FILE,
|
|
268
|
-
...(
|
|
299
|
+
...(senderPolicies ? { senderPolicies } : {}),
|
|
269
300
|
})
|
|
270
301
|
|
|
271
302
|
writeJsonFile(configPath, next)
|
|
@@ -288,7 +319,7 @@ async function runInit() {
|
|
|
288
319
|
` plugins.entries["${PLUGIN_ID}"]`,
|
|
289
320
|
` aampHost: ${aampHost}`,
|
|
290
321
|
` credentialsFile: ${DEFAULT_CREDENTIALS_FILE}`,
|
|
291
|
-
`
|
|
322
|
+
` senderPolicies: ${senderPolicies ? JSON.stringify(senderPolicies) : '(allow all)'}`,
|
|
292
323
|
identityResult.created
|
|
293
324
|
? ` mailbox: ${identityResult.email} (registered and saved to ${identityResult.credentialsPath})`
|
|
294
325
|
: ` mailbox: existing credentials reused from ${identityResult.credentialsPath}`,
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,30 @@ import {
|
|
|
7
7
|
saveCachedIdentity,
|
|
8
8
|
writeBinaryFile
|
|
9
9
|
} from "./file-store.js";
|
|
10
|
+
function matchSenderPolicy(task, senderPolicies) {
|
|
11
|
+
if (!senderPolicies?.length)
|
|
12
|
+
return { allowed: true };
|
|
13
|
+
const sender = task.from.toLowerCase();
|
|
14
|
+
const policy = senderPolicies.find((item) => item.sender.trim().toLowerCase() === sender);
|
|
15
|
+
if (!policy) {
|
|
16
|
+
return { allowed: false, reason: `sender ${task.from} is not allowed by senderPolicies` };
|
|
17
|
+
}
|
|
18
|
+
const rules = policy.dispatchContextRules;
|
|
19
|
+
if (!rules || Object.keys(rules).length === 0) {
|
|
20
|
+
return { allowed: true };
|
|
21
|
+
}
|
|
22
|
+
const context = task.dispatchContext ?? {};
|
|
23
|
+
for (const [key, allowedValues] of Object.entries(rules)) {
|
|
24
|
+
const contextValue = context[key];
|
|
25
|
+
if (!contextValue) {
|
|
26
|
+
return { allowed: false, reason: `dispatchContext missing required key "${key}"` };
|
|
27
|
+
}
|
|
28
|
+
if (!allowedValues.includes(contextValue)) {
|
|
29
|
+
return { allowed: false, reason: `dispatchContext ${key}=${contextValue} is not allowed` };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { allowed: true };
|
|
33
|
+
}
|
|
10
34
|
function baseUrl(aampHost) {
|
|
11
35
|
if (aampHost.startsWith("http://") || aampHost.startsWith("https://")) {
|
|
12
36
|
return aampHost.replace(/\/$/, "");
|
|
@@ -95,10 +119,27 @@ var src_default = {
|
|
|
95
119
|
type: "string",
|
|
96
120
|
description: "Absolute path to cache AAMP credentials between gateway restarts. Default: ~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json. Delete this file to force re-registration with a new mailbox."
|
|
97
121
|
},
|
|
98
|
-
|
|
122
|
+
senderPolicies: {
|
|
99
123
|
type: "array",
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
description: "Per-sender authorization policies. Each sender can optionally require specific X-AAMP-Dispatch-Context key/value pairs before a task is accepted.",
|
|
125
|
+
items: {
|
|
126
|
+
type: "object",
|
|
127
|
+
required: ["sender"],
|
|
128
|
+
properties: {
|
|
129
|
+
sender: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "Dispatch sender email address (case-insensitive exact match)."
|
|
132
|
+
},
|
|
133
|
+
dispatchContextRules: {
|
|
134
|
+
type: "object",
|
|
135
|
+
description: "Optional exact-match rules over X-AAMP-Dispatch-Context. All listed keys must be present and their values must match one of the configured entries.",
|
|
136
|
+
additionalProperties: {
|
|
137
|
+
type: "array",
|
|
138
|
+
items: { type: "string" }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
102
143
|
}
|
|
103
144
|
}
|
|
104
145
|
},
|
|
@@ -161,23 +202,19 @@ var src_default = {
|
|
|
161
202
|
});
|
|
162
203
|
aampClient.on("task.dispatch", (task) => {
|
|
163
204
|
api.logger.info(`[AAMP] \u2190 task.dispatch ${task.taskId} "${task.title}" from=${task.from}`);
|
|
164
|
-
const
|
|
165
|
-
if (allowed
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
api.logger.error(`[AAMP] Failed to send rejection for task ${task.taskId}: ${err.message}`);
|
|
178
|
-
});
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
205
|
+
const decision = matchSenderPolicy(task, cfg.senderPolicies);
|
|
206
|
+
if (!decision.allowed) {
|
|
207
|
+
api.logger.warn(`[AAMP] \u2717 rejected by senderPolicies: ${task.from} task=${task.taskId} reason=${decision.reason}`);
|
|
208
|
+
void aampClient.sendResult({
|
|
209
|
+
to: task.from,
|
|
210
|
+
taskId: task.taskId,
|
|
211
|
+
status: "rejected",
|
|
212
|
+
output: "",
|
|
213
|
+
errorMsg: decision.reason ?? `Sender ${task.from} is not allowed.`
|
|
214
|
+
}).catch((err) => {
|
|
215
|
+
api.logger.error(`[AAMP] Failed to send rejection for task ${task.taskId}: ${err.message}`);
|
|
216
|
+
});
|
|
217
|
+
return;
|
|
181
218
|
}
|
|
182
219
|
pendingTasks.set(task.taskId, {
|
|
183
220
|
taskId: task.taskId,
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @aamp/openclaw-plugin\n *\n * OpenClaw plugin that gives the agent an AAMP mailbox identity and lets it\n * receive, process, and reply to AAMP tasks \u2014 entirely through standard email.\n *\n * How it works:\n * 1. Plugin resolves or auto-registers an AAMP mailbox identity on startup.\n * 2. Credentials are cached to a local file so the same mailbox is reused\n * across gateway restarts (no re-registration needed).\n * 3. Background JMAP WebSocket Push receives incoming task.dispatch emails.\n * 4. Incoming tasks are stored in an in-memory pending-task queue.\n * 5. before_prompt_build injects the oldest pending task into the LLM's\n * system context so the agent sees it and acts without user prompting.\n * 6. The agent calls aamp_send_result or aamp_send_help to reply.\n *\n * OpenClaw config (openclaw.json):\n *\n * \"plugins\": {\n * \"entries\": {\n * \"aamp\": {\n * \"enabled\": true,\n * \"config\": {\n * \"aampHost\": \"https://meshmail.ai\",\n * \"slug\": \"openclaw-agent\",\n * \"credentialsFile\": \"/absolute/path/to/.aamp-credentials.json\"\n * }\n * }\n * }\n * }\n *\n * Install:\n * openclaw plugins install ./packages/openclaw-plugin\n */\n\nimport { AampClient } from 'aamp-sdk'\nimport type { TaskDispatch, TaskResult, TaskHelp, AampAttachment, ReceivedAttachment } from 'aamp-sdk'\nimport {\n defaultCredentialsPath,\n ensureDir,\n loadCachedIdentity,\n readBinaryFile,\n saveCachedIdentity,\n writeBinaryFile,\n type Identity,\n} from './file-store.js'\n\n// \u2500\u2500\u2500 Shared runtime state (single instance per plugin lifetime) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface PendingTask {\n taskId: string\n from: string\n title: string\n bodyText: string\n contextLinks: string[]\n timeoutSecs: number\n messageId: string\n receivedAt: string // ISO-8601\n}\n\ninterface PluginConfig {\n /** e.g. \"meshmail.ai\" \u2014 all URLs are derived from this */\n aampHost: string\n slug?: string\n /** Absolute path to cache AAMP credentials. Default: ~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json */\n credentialsFile?: string\n /**\n * Sender whitelist. Only task.dispatch emails whose `from` address appears in\n * this list are enqueued and processed. All other senders are silently rejected\n * with an automatic task.result status=rejected email.\n *\n * Matching is case-insensitive and exact (no wildcards).\n * If omitted (undefined), all senders are accepted.\n * If set to an empty array [], all senders are rejected.\n *\n * Example: [\"meego-abc123@aamp.local\", \"ci-bot-ff09@aamp.local\"]\n */\n allowedSenders?: string[]\n}\n\ntype StructuredResultFieldInput = {\n fieldKey: string\n fieldTypeKey: string\n fieldAlias?: string\n value?: unknown\n index?: string\n attachmentFilenames?: string[]\n}\n\n/** Normalise aampHost to a base URL with scheme and no trailing slash */\nfunction baseUrl(aampHost: string): string {\n if (aampHost.startsWith('http://') || aampHost.startsWith('https://')) {\n return aampHost.replace(/\\/$/, '')\n }\n return `https://${aampHost}`\n}\n\nconst pendingTasks = new Map<string, PendingTask>()\n// Tracks sub-tasks dispatched TO other agents \u2014 waiting for their result/help replies\nconst dispatchedSubtasks = new Map<string, { to: string; title: string; dispatchedAt: string; parentTaskId?: string }>()\n// Tracks notification keys that have been shown to LLM (auto-cleaned on next prompt build)\nconst shownNotifications = new Set<string>()\n// Pending synchronous dispatch waiters \u2014 resolve callback keyed by sub-task ID.\n// When aamp_dispatch_task sends a sub-task, it parks a Promise here and waits.\n// When task.result/help arrives for that sub-task ID, the waiter is resolved\n// directly, keeping the LLM awake with full context (no heartbeat needed).\nconst waitingDispatches = new Map<string, (reply: { type: 'result' | 'help'; data: unknown }) => void>()\nlet aampClient: AampClient | null = null\nlet agentEmail = ''\nlet lastConnectionError = ''\nlet lastDisconnectReason = ''\nlet lastTransportMode: 'disconnected' | 'websocket' | 'polling' = 'disconnected'\nlet lastLoggedTransportMode: 'disconnected' | 'websocket' | 'polling' = 'disconnected'\nlet reconcileTimer: NodeJS.Timeout | null = null\nlet transportMonitorTimer: NodeJS.Timeout | null = null\n// Tracks the most recently seen session key so task.dispatch can wake the right session.\n// Default 'agent:main:main' is the standard OpenClaw single-agent session key.\nlet currentSessionKey = 'agent:main:main'\n// Channel runtime \u2014 captured from channel adapter's startAccount for instant dispatch.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet channelRuntime: any = null\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet channelCfg: any = null\n\nfunction logTransportState(\n api: { logger: { info: (msg: string) => void; warn: (msg: string) => void } },\n mode: 'websocket' | 'polling',\n email: string,\n previousMode: 'disconnected' | 'websocket' | 'polling',\n): void {\n if (mode === previousMode) return\n\n if (mode === 'polling') {\n api.logger.info(`[AAMP] Connected (polling fallback active) \u2014 listening as ${email}`)\n return\n }\n\n if (previousMode === 'polling') {\n api.logger.info(`[AAMP] WebSocket restored \u2014 listening as ${email}`)\n return\n }\n\n api.logger.info(`[AAMP] Connected \u2014 listening as ${email}`)\n}\n\n// \u2500\u2500\u2500 Identity helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface Identity {\n email: string\n jmapToken: string\n smtpPassword: string\n}\n\n/**\n * Register a new AAMP node via the management service.\n *\n * Always creates a NEW mailbox (always returns 201). The slug is just a\n * human-readable prefix; a random hex suffix makes the email unique.\n * Callers should only call this once and persist the returned credentials.\n */\nasync function registerNode(cfg: PluginConfig): Promise<Identity> {\n const slug = (cfg.slug ?? 'openclaw-agent')\n .toLowerCase()\n .replace(/[\\s_]+/g, '-')\n .replace(/[^a-z0-9-]/g, '')\n\n const base = baseUrl(cfg.aampHost)\n\n // Step 1: Self-register \u2192 get one-time registration code\n const res = await fetch(`${base}/api/nodes/self-register`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ slug, description: 'OpenClaw AAMP agent node' }),\n })\n\n if (!res.ok) {\n const err = (await res.json().catch(() => ({}))) as { error?: string }\n throw new Error(`AAMP registration failed (${res.status}): ${err.error ?? res.statusText}`)\n }\n\n const regData = (await res.json()) as {\n registrationCode: string\n email: string\n }\n\n // Step 2: Exchange registration code for credentials\n const credRes = await fetch(\n `${base}/api/nodes/credentials?code=${encodeURIComponent(regData.registrationCode)}`,\n )\n\n if (!credRes.ok) {\n const err = (await credRes.json().catch(() => ({}))) as { error?: string }\n throw new Error(`AAMP credential exchange failed (${credRes.status}): ${err.error ?? credRes.statusText}`)\n }\n\n const credData = (await credRes.json()) as {\n email: string\n jmap: { token: string }\n smtp: { password: string }\n }\n\n return {\n email: credData.email,\n jmapToken: credData.jmap.token,\n smtpPassword: credData.smtp.password,\n }\n}\n\n/**\n * Resolve this agent's identity:\n * 1. Return cached credentials from disk if available.\n * 2. Otherwise register a new node and cache the result.\n */\nasync function resolveIdentity(cfg: PluginConfig): Promise<Identity> {\n const cached = loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath())\n if (cached) return cached\n\n const identity = await registerNode(cfg)\n saveCachedIdentity(identity, cfg.credentialsFile ?? defaultCredentialsPath())\n return identity\n}\n\n// \u2500\u2500\u2500 Plugin definition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default {\n id: 'aamp-openclaw-plugin',\n name: 'AAMP Agent Mail Protocol',\n\n configSchema: {\n type: 'object',\n properties: {\n aampHost: {\n type: 'string',\n description: 'AAMP service host, e.g. https://meshmail.ai',\n },\n slug: {\n type: 'string',\n default: 'openclaw-agent',\n description: 'Agent name prefix used in the mailbox address',\n },\n credentialsFile: {\n type: 'string',\n description:\n 'Absolute path to cache AAMP credentials between gateway restarts. ' +\n 'Default: ~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json. ' +\n 'Delete this file to force re-registration with a new mailbox.',\n },\n allowedSenders: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Sender whitelist. Only task.dispatch emails from these addresses are accepted. ' +\n 'Matching is case-insensitive and exact. ' +\n 'If omitted, all senders are accepted. If set to [], all senders are rejected. ' +\n 'Example: [\"meego-abc123@aamp.local\", \"ci-bot-ff09@aamp.local\"]',\n },\n },\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n register(api: any) {\n // api.pluginConfig = the config block under plugins.entries[\"aamp-openclaw-plugin\"].config in openclaw.json\n // api.config = the full global OpenClaw config (NOT our plugin's config)\n const cfg = (api.pluginConfig ?? {}) as PluginConfig\n\n // \u2500\u2500 Register lightweight channel adapter to capture channelRuntime \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // We register as a channel SOLELY to get access to channelRuntime, which provides\n // dispatchReplyWithBufferedBlockDispatcher for instant LLM dispatch (bypassing\n // heartbeat's global running-mutex + requests-in-flight ~60s delay).\n // JMAP connection is managed by registerService, NOT startAccount.\n api.registerChannel({\n id: 'aamp',\n meta: { label: 'AAMP' },\n capabilities: { chatTypes: ['dm'] },\n config: {\n listAccountIds: () => cfg.aampHost ? ['default'] : [],\n resolveAccount: () => ({ aampHost: cfg.aampHost }),\n isEnabled: () => !!cfg.aampHost,\n isConfigured: () => !!loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath()),\n },\n gateway: {\n startAccount: async (ctx: { channelRuntime?: unknown; cfg?: unknown; abortSignal?: AbortSignal }) => {\n // Capture channelRuntime for use by sub-task notification dispatch\n channelRuntime = ctx.channelRuntime ?? null\n channelCfg = ctx.cfg ?? null\n api.logger.info(`[AAMP] Channel adapter started \u2014 channelRuntime ${channelRuntime ? 'available' : 'NOT available'}`)\n\n // Keep alive until abort \u2014 JMAP connection is managed by registerService\n await new Promise<void>((resolve) => {\n ctx.abortSignal?.addEventListener('abort', () => resolve())\n })\n channelRuntime = null\n channelCfg = null\n },\n stopAccount: async () => {\n channelRuntime = null\n channelCfg = null\n },\n },\n })\n\n // \u2500\u2500 Shared connect logic (used by service auto-connect and startup recovery) \u2500\u2500\u2500\u2500\u2500\u2500\n async function doConnect(identity: { email: string; jmapToken: string; smtpPassword: string }) {\n if (reconcileTimer) {\n clearInterval(reconcileTimer)\n reconcileTimer = null\n }\n if (transportMonitorTimer) {\n clearInterval(transportMonitorTimer)\n transportMonitorTimer = null\n }\n\n agentEmail = identity.email\n lastConnectionError = ''\n lastDisconnectReason = ''\n lastTransportMode = 'disconnected'\n lastLoggedTransportMode = 'disconnected'\n api.logger.info(`[AAMP] Mailbox identity ready \u2014 ${agentEmail}`)\n\n // All traffic goes through aampHost (port 3000).\n // The management service proxies /jmap/* and /.well-known/jmap \u2192 Stalwart:8080.\n const base = baseUrl(cfg.aampHost)\n\n aampClient = new AampClient({\n email: identity.email,\n jmapToken: identity.jmapToken,\n jmapUrl: base,\n smtpHost: new URL(base).hostname,\n smtpPort: 587,\n smtpPassword: identity.smtpPassword,\n // Local/dev: management-service proxy uses plain HTTP, no TLS cert to verify.\n // Production: set to true when using wss:// with valid certs.\n rejectUnauthorized: false,\n })\n\n aampClient.on('task.dispatch', (task: TaskDispatch) => {\n api.logger.info(`[AAMP] \u2190 task.dispatch ${task.taskId} \"${task.title}\" from=${task.from}`)\n\n // \u2500\u2500 Whitelist check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // undefined \u2192 allow all; [] \u2192 deny all; [...] \u2192 exact match only\n const allowed = cfg.allowedSenders\n if (allowed !== undefined) {\n const fromLower = task.from.toLowerCase()\n const permitted = allowed.some((e) => e.toLowerCase() === fromLower)\n if (!permitted) {\n api.logger.warn(`[AAMP] \u2717 rejected (not in allowedSenders): ${task.from} task=${task.taskId}`)\n void aampClient!.sendResult({\n to: task.from,\n taskId: task.taskId,\n status: 'rejected',\n output: '',\n errorMsg: `Sender ${task.from} is not in the allowed senders list.`,\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Failed to send rejection for task ${task.taskId}: ${err.message}`)\n })\n return\n }\n }\n\n pendingTasks.set(task.taskId, {\n taskId: task.taskId,\n from: task.from,\n title: task.title,\n bodyText: task.bodyText ?? '',\n contextLinks: task.contextLinks,\n timeoutSecs: task.timeoutSecs,\n messageId: task.messageId ?? '',\n receivedAt: new Date().toISOString(),\n })\n\n // Autonomously wake the agent so it processes the task without manual prompting.\n // reason:'wake' bypasses the HEARTBEAT.md file gate (which would skip if the file\n // is empty). before_prompt_build injects the task context into the LLM prompt.\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: currentSessionKey })\n api.logger.info(`[AAMP] Heartbeat triggered for session ${currentSessionKey}`)\n } catch (err) {\n api.logger.warn(`[AAMP] Could not trigger heartbeat: ${(err as Error).message}`)\n }\n })\n\n // \u2500\u2500 Sub-task result: another agent completed a task we dispatched \u2500\u2500\u2500\u2500\u2500\u2500\n aampClient.on('task.result', (result: TaskResult) => {\n api.logger.info(`[AAMP] \u2190 task.result ${result.taskId} status=${result.status} from=${result.from}`)\n\n const sub = dispatchedSubtasks.get(result.taskId)\n dispatchedSubtasks.delete(result.taskId)\n\n // \u2500\u2500 Synchronous dispatch: if aamp_dispatch_task is waiting, resolve it directly \u2500\u2500\n const waiter = waitingDispatches.get(result.taskId)\n if (waiter) {\n waitingDispatches.delete(result.taskId)\n api.logger.info(`[AAMP] Resolving sync waiter for sub-task ${result.taskId}`)\n waiter({ type: 'result', data: result })\n return // Don't go through heartbeat/channel \u2014 the LLM is already awake\n }\n\n // Pre-download attachments to local disk so the LLM can reference them\n // by local file path (instead of requiring a separate download tool call).\n const downloadedFiles: Array<{ filename: string; path: string; size: number }> = []\n const downloadPromise = (async () => {\n if (!result.attachments?.length) return\n const dir = '/tmp/aamp-files'\n ensureDir(dir)\n for (const att of result.attachments) {\n try {\n const buffer = await aampClient!.downloadBlob(att.blobId, att.filename)\n const filepath = `${dir}/${att.filename}`\n writeBinaryFile(filepath, buffer)\n downloadedFiles.push({ filename: att.filename, path: filepath, size: buffer.length })\n api.logger.info(`[AAMP] Pre-downloaded: ${att.filename} (${(buffer.length / 1024).toFixed(1)} KB) \u2192 ${filepath}`)\n } catch (dlErr) {\n api.logger.warn(`[AAMP] Pre-download failed for ${att.filename}: ${(dlErr as Error).message}`)\n }\n }\n })()\n\n downloadPromise.then(() => {\n // Build notification with pre-downloaded file paths\n const MAX_OUTPUT_CHARS = 800\n const label = result.status === 'completed' ? 'Sub-task completed' : 'Sub-task rejected'\n const rawOutput = result.output ?? ''\n const truncatedOutput = rawOutput.length > MAX_OUTPUT_CHARS\n ? rawOutput.slice(0, MAX_OUTPUT_CHARS) + `\\n\\n... [truncated, ${rawOutput.length} chars total]`\n : rawOutput\n\n let attachmentInfo = ''\n if (downloadedFiles.length > 0) {\n attachmentInfo = `\\n\\nAttachments (pre-downloaded to local disk):\\n${downloadedFiles.map(f =>\n `- ${f.filename} (${(f.size / 1024).toFixed(1)} KB) \u2192 ${f.path}`\n ).join('\\n')}\\nUse aamp_send_result with attachments: [${downloadedFiles.map(f => `{ filename: \"${f.filename}\", path: \"${f.path}\" }`).join(', ')}] to forward them.`\n } else if (result.attachments?.length) {\n const files = result.attachments.map((a: ReceivedAttachment) =>\n `${a.filename} (${(a.size / 1024).toFixed(1)} KB, blobId: ${a.blobId})`,\n )\n attachmentInfo = `\\n\\nAttachments (download failed \u2014 use aamp_download_attachment manually):\\n${files.join('\\n')}`\n }\n\n pendingTasks.set(`result:${result.taskId}`, {\n taskId: result.taskId,\n from: result.from,\n title: `${label}: ${sub?.title ?? result.taskId}`,\n bodyText: result.status === 'completed'\n ? `Agent ${result.from} completed the sub-task.\\n\\nOutput:\\n${truncatedOutput}${attachmentInfo}`\n : `Agent ${result.from} rejected the sub-task.\\n\\nReason: ${result.errorMsg ?? 'unknown'}`,\n contextLinks: [],\n timeoutSecs: 0,\n messageId: '',\n receivedAt: new Date().toISOString(),\n })\n\n // Wake LLM via channel dispatch (instant) or heartbeat (fallback)\n if (channelRuntime && channelCfg) {\n const notifyBody = pendingTasks.get(`result:${result.taskId}`)\n const actionableTasks = [...pendingTasks.entries()]\n .filter(([key]) => !key.startsWith('result:') && !key.startsWith('help:'))\n .map(([, t]) => t)\n const actionSection = actionableTasks.length > 0\n ? `\\n\\n### Action Required\\nYou MUST call aamp_send_result to complete the pending task(s):\\n${actionableTasks.map(t => `- Task ID: ${t.taskId} | From: ${t.from} | Title: \"${t.title}\"`).join('\\n')}`\n : ''\n const prompt = `## Sub-task Update\\n\\n${notifyBody?.bodyText ?? 'Sub-task completed.'}${actionSection}`\n\n channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({\n ctx: {\n Body: `Sub-task result: ${result.taskId}`,\n BodyForAgent: prompt,\n From: result.from,\n To: agentEmail,\n SessionKey: `aamp:default:${result.from}`,\n AccountId: 'default',\n ChatType: 'dm',\n Provider: 'aamp',\n Surface: 'aamp',\n OriginatingChannel: 'aamp',\n OriginatingTo: result.from,\n MessageSid: result.taskId,\n Timestamp: Date.now(),\n SenderName: result.from,\n SenderId: result.from,\n CommandAuthorized: true,\n },\n cfg: channelCfg,\n dispatcherOptions: {\n deliver: async () => {},\n onError: (err: unknown) => {\n api.logger.error(`[AAMP] Channel dispatch error: ${err instanceof Error ? err.message : String(err)}`)\n },\n },\n }).then(() => {\n api.logger.info(`[AAMP] Channel dispatch completed for sub-task result ${result.taskId}`)\n pendingTasks.delete(`result:${result.taskId}`)\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Channel dispatch failed: ${err.message}`)\n })\n } else {\n const notifySessionKey = `agent:main:aamp-notify-${Date.now()}`\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: notifySessionKey })\n api.logger.info(`[AAMP] Heartbeat for sub-task result ${result.taskId}`)\n } catch (err) {\n api.logger.warn(`[AAMP] Heartbeat for sub-task result failed: ${(err as Error).message}`)\n }\n }\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Sub-task result processing failed: ${err.message}`)\n })\n })\n\n // \u2500\u2500 Sub-task help: another agent asks for clarification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n aampClient.on('task.help', (help: TaskHelp) => {\n api.logger.info(`[AAMP] \u2190 task.help ${help.taskId} question=\"${help.question}\" from=${help.from}`)\n\n // \u2500\u2500 Synchronous dispatch: if aamp_dispatch_task is waiting, resolve it directly \u2500\u2500\n const waiter = waitingDispatches.get(help.taskId)\n if (waiter) {\n waitingDispatches.delete(help.taskId)\n api.logger.info(`[AAMP] Resolving sync waiter for sub-task help ${help.taskId}`)\n waiter({ type: 'help', data: help })\n return\n }\n\n const sub = dispatchedSubtasks.get(help.taskId)\n\n pendingTasks.set(`help:${help.taskId}`, {\n taskId: help.taskId,\n from: help.from,\n title: `Sub-task needs help: ${sub?.title ?? help.taskId}`,\n bodyText: `Agent ${help.from} is asking for help on the sub-task.\\n\\nQuestion: ${help.question}\\nBlocked reason: ${help.blockedReason}${help.suggestedOptions?.length ? `\\nSuggested options: ${help.suggestedOptions.join(', ')}` : ''}`,\n contextLinks: [],\n timeoutSecs: 0,\n messageId: '',\n receivedAt: new Date().toISOString(),\n })\n\n if (channelRuntime && channelCfg) {\n const notifyBody = pendingTasks.get(`help:${help.taskId}`)\n const prompt = `## Sub-task Help Request\\n\\n${notifyBody?.bodyText ?? help.question}`\n\n channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({\n ctx: {\n Body: `Sub-task help: ${help.taskId}`,\n BodyForAgent: prompt,\n From: help.from,\n To: agentEmail,\n SessionKey: `aamp:default:${help.from}`,\n AccountId: 'default',\n ChatType: 'dm',\n Provider: 'aamp',\n Surface: 'aamp',\n OriginatingChannel: 'aamp',\n OriginatingTo: help.from,\n MessageSid: help.taskId,\n Timestamp: Date.now(),\n SenderName: help.from,\n SenderId: help.from,\n CommandAuthorized: true,\n },\n cfg: channelCfg,\n dispatcherOptions: {\n deliver: async () => {},\n onError: (err: unknown) => {\n api.logger.error(`[AAMP] Channel dispatch error (help): ${err instanceof Error ? err.message : String(err)}`)\n },\n },\n }).then(() => {\n pendingTasks.delete(`help:${help.taskId}`)\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Channel dispatch failed for help: ${err.message}`)\n })\n } else {\n const helpSessionKey = `agent:main:aamp-notify-${Date.now()}`\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: helpSessionKey })\n api.logger.info(`[AAMP] Heartbeat fallback for sub-task help ${help.taskId}`)\n } catch (err) {\n api.logger.warn(`[AAMP] Heartbeat for sub-task help failed: ${(err as Error).message}`)\n }\n }\n })\n\n aampClient.on('connected', () => {\n lastConnectionError = ''\n lastDisconnectReason = ''\n const mode = aampClient?.isUsingPollingFallback() ? 'polling' : 'websocket'\n logTransportState(api, mode, agentEmail, lastLoggedTransportMode)\n lastTransportMode = mode\n lastLoggedTransportMode = mode\n })\n\n aampClient.on('disconnected', (reason: string) => {\n lastDisconnectReason = reason\n if (lastTransportMode !== 'disconnected') {\n api.logger.warn(`[AAMP] Disconnected: ${reason} (will auto-reconnect)`)\n lastTransportMode = 'disconnected'\n lastLoggedTransportMode = 'disconnected'\n }\n })\n\n aampClient.on('error', (err: Error) => {\n lastConnectionError = err.message\n if (err.message.startsWith('JMAP WebSocket unavailable, falling back to polling:')) {\n if (lastTransportMode !== 'polling') {\n logTransportState(api, 'polling', agentEmail, lastLoggedTransportMode)\n lastTransportMode = 'polling'\n lastLoggedTransportMode = 'polling'\n }\n return\n }\n api.logger.error(`[AAMP] ${err.message}`)\n })\n\n await aampClient.connect()\n\n api.logger.info(\n `[AAMP] Transport after connect \u2014 ${aampClient.isUsingPollingFallback() ? 'polling fallback' : 'websocket'} as ${agentEmail}`,\n )\n\n if (aampClient.isConnected() && lastTransportMode === 'disconnected') {\n if (aampClient.isUsingPollingFallback()) {\n logTransportState(api, 'polling', agentEmail, lastLoggedTransportMode)\n lastTransportMode = 'polling'\n lastLoggedTransportMode = 'polling'\n } else {\n logTransportState(api, 'websocket', agentEmail, lastLoggedTransportMode)\n lastTransportMode = 'websocket'\n lastLoggedTransportMode = 'websocket'\n }\n }\n\n setTimeout(() => {\n if (!aampClient?.isConnected()) return\n const mode = aampClient.isUsingPollingFallback() ? 'polling' : 'websocket'\n logTransportState(api, mode, agentEmail, lastLoggedTransportMode)\n lastTransportMode = mode\n lastLoggedTransportMode = mode\n }, 1000)\n\n transportMonitorTimer = setInterval(() => {\n if (!aampClient) return\n if (!aampClient.isConnected()) {\n if (lastTransportMode !== 'disconnected') {\n lastTransportMode = 'disconnected'\n }\n return\n }\n const mode = aampClient.isUsingPollingFallback() ? 'polling' : 'websocket'\n logTransportState(api, mode, agentEmail, lastLoggedTransportMode)\n lastTransportMode = mode\n lastLoggedTransportMode = mode\n }, 5000)\n\n reconcileTimer = setInterval(() => {\n if (!aampClient) return\n void aampClient.reconcileRecentEmails(20).catch((err: Error) => {\n lastConnectionError = err.message\n api.logger.warn(`[AAMP] Mailbox reconcile failed: ${err.message}`)\n })\n }, 15000)\n }\n\n // \u2500\u2500 Service: auto-connect at gateway startup, disconnect on shutdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // registerService causes this plugin to load eagerly (at gateway startup),\n // not lazily (on first agent run). start() is called once the gateway is up.\n api.registerService({\n id: 'aamp-service',\n start: async () => {\n if (!cfg.aampHost) {\n api.logger.info('[AAMP] aampHost not configured \u2014 skipping auto-connect')\n return\n }\n try {\n const identity = await resolveIdentity(cfg)\n await doConnect(identity)\n } catch (err) {\n api.logger.warn(`[AAMP] Service auto-connect failed: ${(err as Error).message}`)\n }\n },\n stop: () => {\n if (reconcileTimer) {\n clearInterval(reconcileTimer)\n reconcileTimer = null\n }\n if (transportMonitorTimer) {\n clearInterval(transportMonitorTimer)\n transportMonitorTimer = null\n }\n if (aampClient) {\n try {\n aampClient.disconnect()\n api.logger.info('[AAMP] Disconnected on gateway stop')\n } catch {\n // ignore disconnect errors on shutdown\n }\n }\n },\n })\n\n // \u2500\u2500 gateway_start hook: re-trigger heartbeat after runner is initialized \u2500\u2500\u2500\n // Service start() runs BEFORE the heartbeat runner is ready, so\n // requestHeartbeatNow() called during JMAP initial fetch is silently dropped\n // (handler == null). gateway_start fires AFTER the heartbeat runner starts,\n // so we re-trigger here to process any tasks queued during startup.\n api.on('gateway_start', () => {\n if (pendingTasks.size === 0) return\n api.logger.info(`[AAMP] gateway_start: re-triggering heartbeat for ${pendingTasks.size} pending task(s)`)\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: currentSessionKey })\n } catch (err) {\n api.logger.warn(`[AAMP] gateway_start heartbeat failed: ${(err as Error).message}`)\n }\n })\n\n // \u2500\u2500 2. Prompt injection: surface the oldest pending task to the LLM \u2500\u2500\u2500\u2500\u2500\u2500\n api.on(\n 'before_prompt_build',\n (_event, ctx) => {\n // Keep currentSessionKey fresh \u2014 used by task.dispatch to target the right session.\n // Skip channel dispatch sessions (aamp:*) to avoid polluting the heartbeat session key.\n if (ctx?.sessionKey && !String(ctx.sessionKey).startsWith('aamp:')) {\n currentSessionKey = ctx.sessionKey\n }\n\n // Expire tasks that have exceeded their timeout\n const now = Date.now()\n for (const [id, t] of pendingTasks) {\n if (t.timeoutSecs && now - new Date(t.receivedAt).getTime() > t.timeoutSecs * 1000) {\n api.logger.warn(`[AAMP] Task ${id} timed out \u2014 removing from queue`)\n pendingTasks.delete(id)\n }\n }\n\n if (pendingTasks.size === 0) return {}\n\n // Prioritize notifications (sub-task results/help) over actionable tasks.\n // Without this, the oldest actionable task blocks notification delivery,\n // preventing the LLM from seeing sub-task results and completing the parent task.\n const allEntries = [...pendingTasks.entries()]\n const notifications = allEntries.filter(([key]) => key.startsWith('result:') || key.startsWith('help:'))\n const actionable = allEntries.filter(([key]) => !key.startsWith('result:') && !key.startsWith('help:'))\n\n // Pick notification first if available, otherwise oldest actionable task\n const [taskKey, task] = notifications.length > 0\n ? notifications.sort((a, b) => new Date(a[1].receivedAt).getTime() - new Date(b[1].receivedAt).getTime())[0]\n : actionable.sort((a, b) => new Date(a[1].receivedAt).getTime() - new Date(b[1].receivedAt).getTime())[0]\n\n const isNotification = taskKey.startsWith('result:') || taskKey.startsWith('help:')\n\n // Notifications are one-shot: remove immediately after injecting into prompt\n if (isNotification && taskKey) {\n pendingTasks.delete(taskKey)\n }\n\n // Find remaining actionable tasks (non-notification) that still need a response\n const actionableTasks = [...pendingTasks.entries()]\n .filter(([key]) => !key.startsWith('result:') && !key.startsWith('help:'))\n .map(([, t]) => t)\n\n const hasAttachmentInfo = isNotification && (task.bodyText?.includes('aamp_download_attachment') ?? false)\n const actionRequiredSection = isNotification && actionableTasks.length > 0\n ? [\n ``,\n `### Action Required`,\n ``,\n `You still have ${actionableTasks.length} pending task(s) that need a response.`,\n `Use the sub-task result above to complete them by calling aamp_send_result.`,\n ``,\n ...actionableTasks.map((t) =>\n `- Task ID: ${t.taskId} | From: ${t.from} | Title: \"${t.title}\"`\n ),\n ...(hasAttachmentInfo ? [\n ``,\n `### Forwarding Attachments`,\n `The sub-task result includes file attachments. To forward them:`,\n `1. Call aamp_download_attachment for each blobId listed above`,\n `2. Include the downloaded files in aamp_send_result via the attachments parameter`,\n ` Example: attachments: [{ filename: \"file.html\", path: \"/tmp/aamp-files/file.html\" }]`,\n ] : []),\n ].join('\\n')\n : ''\n\n const lines = isNotification ? [\n `## Sub-task Update`,\n ``,\n `A sub-task you dispatched has returned a result. Review the information below.`,\n `If the sub-task included attachments, use aamp_download_attachment to fetch them.`,\n ``,\n `Task ID: ${task.taskId}`,\n `From: ${task.from}`,\n `Title: ${task.title}`,\n task.bodyText ? `\\n${task.bodyText}` : '',\n actionRequiredSection,\n pendingTasks.size > 1 ? `\\n(+${pendingTasks.size - 1} more items queued)` : '',\n ] : [\n `## Pending AAMP Task (action required)`,\n ``,\n `You have received a task via AAMP email. You MUST call one of the two tools below`,\n `BEFORE responding to the user \u2014 do not skip this step.`,\n ``,\n `### Tool selection rules (follow strictly):`,\n ``,\n `Use aamp_send_result ONLY when ALL of the following are true:`,\n ` 1. The title contains a clear, specific action verb (e.g. \"summarise\", \"review\",`,\n ` \"translate\", \"generate\", \"fix\", \"search\", \"compare\", \"list\")`,\n ` 2. You know exactly what input/resource to act on`,\n ` 3. No ambiguity remains \u2014 you could start work immediately without asking anything`,\n ``,\n `Use aamp_send_help in ALL other cases, including:`,\n ` - Title is a greeting or salutation (\"hello\", \"hi\", \"hey\", \"test\", \"ping\", etc.)`,\n ` - Title is fewer than 4 words and contains no actionable verb`,\n ` - Title is too vague to act on without guessing (e.g. \"help\", \"task\", \"question\")`,\n ` - Required context is missing (which file? which URL? which criteria?)`,\n ` - Multiple interpretations are equally plausible`,\n ``,\n `IMPORTANT: Responding to a greeting with a greeting is WRONG. \"hello\" is not a`,\n `valid task description \u2014 ask what specific task the dispatcher needs done.`,\n ``,\n `### Sub-task dispatch rules:`,\n `If you delegate work to another agent via aamp_dispatch_task, you MUST pass`,\n `parentTaskId: \"${task.taskId}\" to establish the parent-child relationship.`,\n ``,\n `Task ID: ${task.taskId}`,\n `From: ${task.from}`,\n `Title: ${task.title}`,\n task.bodyText ? `Description:\\n${task.bodyText}` : '',\n task.contextLinks.length\n ? `Context Links:\\n${task.contextLinks.map((l) => ` - ${l}`).join('\\n')}`\n : '',\n task.timeoutSecs ? `Deadline: ${task.timeoutSecs}s from dispatch` : `Deadline: none`,\n `Received: ${task.receivedAt}`,\n pendingTasks.size > 1 ? `\\n(+${pendingTasks.size - 1} more tasks queued)` : '',\n ]\n .filter(Boolean)\n .join('\\n')\n\n return { prependContext: lines }\n },\n { priority: 5 },\n )\n\n // \u2500\u2500 3. Tool: send task result \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_send_result',\n description:\n 'Send the result of an AAMP task back to the dispatcher. ' +\n 'Call this after you have finished processing the task.',\n parameters: {\n type: 'object',\n required: ['taskId', 'status', 'output'],\n properties: {\n taskId: {\n type: 'string',\n description: 'The AAMP task ID to reply to (from the system context)',\n },\n status: {\n type: 'string',\n enum: ['completed', 'rejected'],\n description: '\"completed\" on success, \"rejected\" if the task cannot be done',\n },\n output: {\n type: 'string',\n description: 'Your result or explanation',\n },\n errorMsg: {\n type: 'string',\n description: 'Optional error details (use only when status = rejected)',\n },\n attachments: {\n type: 'array',\n description: 'File attachments. Each item: { filename, contentType, path (local file path) }',\n items: {\n type: 'object',\n properties: {\n filename: { type: 'string' },\n contentType: { type: 'string' },\n path: { type: 'string', description: 'Absolute path to the file on disk' },\n },\n required: ['filename', 'path'],\n },\n },\n structuredResult: {\n type: 'array',\n description: 'Optional structured Meego field values.',\n items: {\n type: 'object',\n required: ['fieldKey', 'fieldTypeKey'],\n properties: {\n fieldKey: { type: 'string' },\n fieldTypeKey: { type: 'string' },\n fieldAlias: { type: 'string' },\n value: {\n description: 'Field value in the exact format required by Meego for this field type.',\n },\n index: { type: 'string' },\n attachmentFilenames: {\n type: 'array',\n items: { type: 'string' },\n description: 'For attachment fields, filenames from attachments[] that should be uploaded into this field.',\n },\n },\n },\n },\n },\n },\n execute: async (_id, params) => {\n const p = params as {\n taskId: string\n status: 'completed' | 'rejected'\n output: string\n errorMsg?: string\n attachments?: Array<{ filename: string; contentType?: string; path: string }>\n structuredResult?: StructuredResultFieldInput[]\n }\n\n const task = pendingTasks.get(p.taskId)\n if (!task) {\n return {\n content: [{ type: 'text', text: `Error: task ${p.taskId} not found in pending queue.` }],\n }\n }\n\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n api.logger.info(`[AAMP] aamp_send_result params ${JSON.stringify({\n taskId: p.taskId,\n status: p.status,\n output: p.output,\n errorMsg: p.errorMsg,\n attachments: p.attachments?.map((a) => ({\n filename: a.filename,\n contentType: a.contentType ?? 'application/octet-stream',\n path: a.path,\n })) ?? [],\n structuredResult: p.structuredResult?.map((field) => ({\n fieldKey: field.fieldKey,\n fieldTypeKey: field.fieldTypeKey,\n fieldAlias: field.fieldAlias,\n value: field.value,\n index: field.index,\n attachmentFilenames: field.attachmentFilenames ?? [],\n })) ?? [],\n })}`)\n\n // Build attachments from file paths\n let attachments: AampAttachment[] | undefined\n if (p.attachments?.length) {\n attachments = p.attachments.map((a: { filename: string; contentType?: string; path: string }) => ({\n filename: a.filename,\n contentType: a.contentType ?? 'application/octet-stream',\n content: readBinaryFile(a.path),\n }))\n }\n\n await aampClient.sendResult({\n to: task.from,\n taskId: task.taskId,\n status: p.status,\n output: p.output,\n errorMsg: p.errorMsg,\n structuredResult: p.structuredResult?.length ? p.structuredResult : undefined,\n inReplyTo: task.messageId || undefined,\n attachments,\n })\n\n pendingTasks.delete(task.taskId)\n api.logger.info(`[AAMP] \u2192 task.result ${task.taskId} ${p.status}`)\n\n // If more tasks remain, wake the agent to process them\n if (pendingTasks.size > 0) {\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: currentSessionKey })\n } catch { /* ignore */ }\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Result sent for task ${task.taskId} (status: ${p.status}).`,\n },\n ],\n }\n },\n }, { name: 'aamp_send_result' })\n\n // \u2500\u2500 4. Tool: ask for help \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_send_help',\n description:\n 'Send a help request for an AAMP task when you are blocked or need human clarification ' +\n 'before you can proceed.',\n parameters: {\n type: 'object',\n required: ['taskId', 'question', 'blockedReason'],\n properties: {\n taskId: {\n type: 'string',\n description: 'The AAMP task ID',\n },\n question: {\n type: 'string',\n description: 'Your question for the human dispatcher',\n },\n blockedReason: {\n type: 'string',\n description: 'Why you cannot proceed without their input',\n },\n suggestedOptions: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional list of choices for the dispatcher to pick from',\n },\n },\n },\n execute: async (_id, params) => {\n const p = params as {\n taskId: string\n question: string\n blockedReason: string\n suggestedOptions?: string[]\n }\n\n const task = pendingTasks.get(p.taskId)\n if (!task) {\n return {\n content: [{ type: 'text', text: `Error: task ${p.taskId} not found in pending queue.` }],\n }\n }\n\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n await aampClient.sendHelp({\n to: task.from,\n taskId: task.taskId,\n question: p.question,\n blockedReason: p.blockedReason,\n suggestedOptions: p.suggestedOptions ?? [],\n inReplyTo: task.messageId || undefined,\n })\n\n api.logger.info(`[AAMP] \u2192 task.help ${task.taskId}`)\n\n // Keep the task in pending \u2014 the help reply may arrive later\n return {\n content: [\n {\n type: 'text',\n text: `Help request sent for task ${task.taskId}. The task remains pending until the dispatcher replies.`,\n },\n ],\n }\n },\n }, { name: 'aamp_send_help' })\n\n // \u2500\u2500 5. Tool: inspect queue \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_pending_tasks',\n description: 'List all AAMP tasks currently waiting to be processed.',\n parameters: { type: 'object', properties: {} },\n execute: async () => {\n if (pendingTasks.size === 0) {\n return { content: [{ type: 'text', text: 'No pending AAMP tasks.' }] }\n }\n\n const lines = [...pendingTasks.values()]\n .sort((a, b) => new Date(a.receivedAt).getTime() - new Date(b.receivedAt).getTime())\n .map(\n (t, i) =>\n `${i + 1}. [${t.taskId}] \"${t.title}\"${t.bodyText ? `\\n Description: ${t.bodyText}` : ''} \u2014 from ${t.from} (received ${t.receivedAt})`,\n )\n\n return {\n content: [\n {\n type: 'text',\n text: `${pendingTasks.size} pending task(s):\\n${lines.join('\\n')}`,\n },\n ],\n }\n },\n }, { name: 'aamp_pending_tasks' })\n\n // \u2500\u2500 6. Tool: dispatch task to another agent (SYNCHRONOUS) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Sends the task and BLOCKS until the sub-agent replies with task.result or\n // task.help. The reply is returned directly as the tool result, keeping the\n // LLM awake with full context \u2014 no heartbeat/channel dispatch needed.\n api.registerTool({\n name: 'aamp_dispatch_task',\n description:\n 'Send a task to another AAMP agent and WAIT for the result. ' +\n 'This tool blocks until the sub-agent replies (typically 5-60s). ' +\n 'The sub-agent\\'s output and any attachment file paths are returned directly.',\n parameters: {\n type: 'object',\n required: ['to', 'title'],\n properties: {\n to: { type: 'string', description: 'Target agent AAMP email address' },\n title: { type: 'string', description: 'Task title (concise summary)' },\n bodyText: { type: 'string', description: 'Detailed task description' },\n parentTaskId: { type: 'string', description: 'If you are processing a pending AAMP task, pass its Task ID here to establish parent-child nesting. Omit for top-level tasks.' },\n timeoutSecs: { type: 'number', description: 'Timeout in seconds (optional)' },\n contextLinks: {\n type: 'array', items: { type: 'string' },\n description: 'URLs providing context (optional)',\n },\n attachments: {\n type: 'array',\n description: 'File attachments. Each item: { filename, contentType, path (local file path) }',\n items: {\n type: 'object',\n properties: {\n filename: { type: 'string' },\n contentType: { type: 'string' },\n path: { type: 'string', description: 'Absolute path to the file on disk' },\n },\n required: ['filename', 'path'],\n },\n },\n },\n },\n execute: async (_id: unknown, params: {\n to: string; title: string; bodyText?: string;\n parentTaskId?: string; timeoutSecs?: number; contextLinks?: string[];\n attachments?: Array<{ filename: string; contentType?: string; path: string }>\n }) => {\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n try {\n // Build attachments from file paths\n let attachments: AampAttachment[] | undefined\n if (params.attachments?.length) {\n attachments = params.attachments.map((a: { filename: string; contentType?: string; path: string }) => ({\n filename: a.filename,\n contentType: a.contentType ?? 'application/octet-stream',\n content: readBinaryFile(a.path),\n }))\n }\n\n const result = await aampClient.sendTask({\n to: params.to,\n title: params.title,\n parentTaskId: params.parentTaskId,\n timeoutSecs: params.timeoutSecs,\n contextLinks: params.contextLinks,\n attachments,\n })\n\n // Track as dispatched sub-task\n dispatchedSubtasks.set(result.taskId, {\n to: params.to,\n title: params.title,\n dispatchedAt: new Date().toISOString(),\n parentTaskId: params.parentTaskId,\n })\n\n api.logger.info(`[AAMP] \u2192 task.dispatch ${result.taskId} to=${params.to} parent=${params.parentTaskId ?? 'none'} (waiting for reply\u2026)`)\n\n // \u2500\u2500 SYNCHRONOUS WAIT: block until sub-agent replies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const timeoutMs = (params.timeoutSecs ?? 300) * 1000\n const reply = await new Promise<{ type: 'result' | 'help'; data: unknown }>((resolve, reject) => {\n waitingDispatches.set(result.taskId, resolve)\n setTimeout(() => {\n if (waitingDispatches.delete(result.taskId)) {\n reject(new Error(`Sub-task ${result.taskId} timed out after ${params.timeoutSecs ?? 300}s`))\n }\n }, timeoutMs)\n })\n\n api.logger.info(`[AAMP] \u2190 sync reply for ${result.taskId}: type=${reply.type} attachments=${JSON.stringify((reply.data as any)?.attachments?.length ?? 0)}`)\n\n if (reply.type === 'result') {\n const r = reply.data as TaskResult\n\n // Pre-download attachments \u2014 use direct JMAP blob download (bypass SDK's downloadBlob\n // which was returning 404 due to URL construction issues in the esbuild bundle).\n let attachmentLines = ''\n if (r.attachments?.length) {\n api.logger.info(`[AAMP] Downloading ${r.attachments.length} attachment(s) from sync reply...`)\n const dir = '/tmp/aamp-files'\n ensureDir(dir)\n const downloaded: string[] = []\n const base = baseUrl(cfg.aampHost)\n const identity = loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath())\n const authHeader = identity ? `Basic ${Buffer.from(identity.email + ':' + identity.smtpPassword).toString('base64')}` : ''\n for (const att of r.attachments) {\n try {\n // Direct JMAP blob download \u2014 construct URL manually\n const dlUrl = `${base}/jmap/download/n/${encodeURIComponent(att.blobId)}/${encodeURIComponent(att.filename)}?accept=application/octet-stream`\n api.logger.info(`[AAMP] Fetching ${dlUrl}`)\n const dlRes = await fetch(dlUrl, { headers: { Authorization: authHeader } })\n if (!dlRes.ok) throw new Error(`HTTP ${dlRes.status}`)\n const buffer = Buffer.from(await dlRes.arrayBuffer())\n const filepath = `${dir}/${att.filename}`\n writeBinaryFile(filepath, buffer)\n downloaded.push(`${att.filename} (${(buffer.length / 1024).toFixed(1)} KB) \u2192 ${filepath}`)\n api.logger.info(`[AAMP] Downloaded: ${att.filename} (${(buffer.length / 1024).toFixed(1)} KB)`)\n } catch (dlErr) {\n api.logger.error(`[AAMP] Download failed for ${att.filename}: ${(dlErr as Error).message}`)\n }\n }\n if (downloaded.length) {\n attachmentLines = `\\n\\nAttachments downloaded:\\n${downloaded.join('\\n')}`\n }\n }\n\n return {\n content: [{\n type: 'text',\n text: [\n `Sub-task ${r.status}: ${params.title}`,\n `Agent: ${r.from}`,\n `Task ID: ${result.taskId}`,\n r.status === 'completed' ? `\\nOutput:\\n${r.output}` : `\\nError: ${r.errorMsg ?? 'rejected'}`,\n attachmentLines,\n ].filter(Boolean).join('\\n'),\n }],\n }\n } else {\n const h = reply.data as TaskHelp\n return {\n content: [{\n type: 'text',\n text: [\n `Sub-task needs help: ${params.title}`,\n `Agent: ${h.from}`,\n `Task ID: ${result.taskId}`,\n `\\nQuestion: ${h.question}`,\n `Blocked reason: ${h.blockedReason}`,\n h.suggestedOptions?.length ? `Options: ${h.suggestedOptions.join(' | ')}` : '',\n ].filter(Boolean).join('\\n'),\n }],\n }\n }\n } catch (err) {\n return {\n content: [{ type: 'text', text: `Error dispatching task: ${(err as Error).message}` }],\n }\n }\n },\n }, { name: 'aamp_dispatch_task' })\n\n // \u2500\u2500 7. Tool: check AAMP protocol support \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_check_protocol',\n description:\n 'Check if an email address supports the AAMP protocol. ' +\n 'Returns { aamp: true/false } indicating whether the address is an AAMP agent.',\n parameters: {\n type: 'object',\n required: ['email'],\n properties: {\n email: { type: 'string', description: 'Email address to check' },\n },\n },\n execute: async (_id: unknown, params: { email: string }) => {\n const base = baseUrl(cfg.aampHost)\n const email = params?.email ?? ''\n if (!email) {\n return { content: [{ type: 'text', text: 'Error: email parameter is required' }] }\n }\n try {\n const res = await fetch(`${base}/api/aamp-check?email=${encodeURIComponent(email)}`)\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const data = await res.json() as { aamp: boolean; domain?: string }\n return {\n content: [{\n type: 'text',\n text: data.aamp\n ? `${params.email} supports AAMP protocol (domain: ${data.domain ?? 'unknown'})`\n : `${params.email} does not support AAMP protocol`,\n }],\n }\n } catch (err) {\n return {\n content: [{ type: 'text', text: `Could not check ${params.email}: ${(err as Error).message}` }],\n }\n }\n },\n }, { name: 'aamp_check_protocol' })\n\n // \u2500\u2500 8. Tool: download attachment blob \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_download_attachment',\n description:\n 'Download an AAMP email attachment to local disk by its blobId. ' +\n 'Use this to retrieve files received from sub-agent task results.',\n parameters: {\n type: 'object',\n required: ['blobId', 'filename'],\n properties: {\n blobId: { type: 'string', description: 'The JMAP blobId from the attachment metadata' },\n filename: { type: 'string', description: 'Filename to save as' },\n saveTo: { type: 'string', description: 'Directory to save to (default: /tmp/aamp-files)' },\n },\n },\n execute: async (_id: unknown, params: { blobId: string; filename: string; saveTo?: string }) => {\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n const dir = params.saveTo ?? '/tmp/aamp-files'\n ensureDir(dir)\n\n try {\n const buffer = await aampClient.downloadBlob(params.blobId, params.filename)\n const filepath = `${dir}/${params.filename}`\n writeBinaryFile(filepath, buffer)\n return {\n content: [{\n type: 'text',\n text: `Downloaded ${params.filename} (${(buffer.length / 1024).toFixed(1)} KB) to ${filepath}`,\n }],\n }\n } catch (err) {\n return {\n content: [{ type: 'text', text: `Download failed: ${(err as Error).message}` }],\n }\n }\n },\n }, { name: 'aamp_download_attachment' })\n\n // \u2500\u2500 9. Slash command: /aamp-status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerCommand({\n name: 'aamp-status',\n description: 'Show AAMP connection status and pending task queue',\n acceptsArgs: false,\n requireAuth: false,\n handler: () => {\n const isPollingFallback = aampClient?.isUsingPollingFallback?.() ?? false\n const connectionLine = aampClient?.isConnected()\n ? (isPollingFallback ? '\uD83D\uDFE1 connected (polling fallback)' : '\u2705 connected')\n : '\u274C disconnected'\n\n return {\n text: [\n `**AAMP Plugin Status**`,\n `Host: ${cfg.aampHost || '(not configured)'}`,\n `Identity: ${agentEmail || '(not yet registered)'}`,\n `Connection: ${connectionLine}`,\n `Cached: ${loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath()) ? 'yes' : 'no'}`,\n lastConnectionError ? `Last error: ${lastConnectionError}` : '',\n lastDisconnectReason ? `Last disconnect: ${lastDisconnectReason}` : '',\n `Pending: ${pendingTasks.size} task(s)`,\n ...[...pendingTasks.values()].map(\n (t) => ` \u2022 ${t.taskId.slice(0, 8)}\u2026 \"${t.title}\" from ${t.from}`,\n ),\n ]\n .filter(Boolean)\n .join('\\n'),\n }\n },\n })\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * @aamp/openclaw-plugin\n *\n * OpenClaw plugin that gives the agent an AAMP mailbox identity and lets it\n * receive, process, and reply to AAMP tasks \u2014 entirely through standard email.\n *\n * How it works:\n * 1. Plugin resolves or auto-registers an AAMP mailbox identity on startup.\n * 2. Credentials are cached to a local file so the same mailbox is reused\n * across gateway restarts (no re-registration needed).\n * 3. Background JMAP WebSocket Push receives incoming task.dispatch emails.\n * 4. Incoming tasks are stored in an in-memory pending-task queue.\n * 5. before_prompt_build injects the oldest pending task into the LLM's\n * system context so the agent sees it and acts without user prompting.\n * 6. The agent calls aamp_send_result or aamp_send_help to reply.\n *\n * OpenClaw config (openclaw.json):\n *\n * \"plugins\": {\n * \"entries\": {\n * \"aamp\": {\n * \"enabled\": true,\n * \"config\": {\n * \"aampHost\": \"https://meshmail.ai\",\n * \"slug\": \"openclaw-agent\",\n * \"credentialsFile\": \"/absolute/path/to/.aamp-credentials.json\"\n * }\n * }\n * }\n * }\n *\n * Install:\n * openclaw plugins install ./packages/openclaw-plugin\n */\n\nimport { AampClient } from 'aamp-sdk'\nimport type { TaskDispatch, TaskResult, TaskHelp, AampAttachment, ReceivedAttachment } from 'aamp-sdk'\nimport {\n defaultCredentialsPath,\n ensureDir,\n loadCachedIdentity,\n readBinaryFile,\n saveCachedIdentity,\n writeBinaryFile,\n type Identity,\n} from './file-store.js'\n\n// \u2500\u2500\u2500 Shared runtime state (single instance per plugin lifetime) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface PendingTask {\n taskId: string\n from: string\n title: string\n bodyText: string\n contextLinks: string[]\n timeoutSecs: number\n messageId: string\n receivedAt: string // ISO-8601\n}\n\ninterface PluginConfig {\n /** e.g. \"meshmail.ai\" \u2014 all URLs are derived from this */\n aampHost: string\n slug?: string\n /** Absolute path to cache AAMP credentials. Default: ~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json */\n credentialsFile?: string\n senderPolicies?: SenderPolicy[]\n}\n\ninterface SenderPolicy {\n sender: string\n dispatchContextRules?: Record<string, string[]>\n}\n\nfunction matchSenderPolicy(\n task: TaskDispatch,\n senderPolicies: SenderPolicy[] | undefined,\n): { allowed: boolean; reason?: string } {\n if (!senderPolicies?.length) return { allowed: true }\n\n const sender = task.from.toLowerCase()\n const policy = senderPolicies.find((item) => item.sender.trim().toLowerCase() === sender)\n if (!policy) {\n return { allowed: false, reason: `sender ${task.from} is not allowed by senderPolicies` }\n }\n\n const rules = policy.dispatchContextRules\n if (!rules || Object.keys(rules).length === 0) {\n return { allowed: true }\n }\n\n const context = task.dispatchContext ?? {}\n for (const [key, allowedValues] of Object.entries(rules)) {\n const contextValue = context[key]\n if (!contextValue) {\n return { allowed: false, reason: `dispatchContext missing required key \"${key}\"` }\n }\n if (!allowedValues.includes(contextValue)) {\n return { allowed: false, reason: `dispatchContext ${key}=${contextValue} is not allowed` }\n }\n }\n\n return { allowed: true }\n}\n\ntype StructuredResultFieldInput = {\n fieldKey: string\n fieldTypeKey: string\n fieldAlias?: string\n value?: unknown\n index?: string\n attachmentFilenames?: string[]\n}\n\n/** Normalise aampHost to a base URL with scheme and no trailing slash */\nfunction baseUrl(aampHost: string): string {\n if (aampHost.startsWith('http://') || aampHost.startsWith('https://')) {\n return aampHost.replace(/\\/$/, '')\n }\n return `https://${aampHost}`\n}\n\nconst pendingTasks = new Map<string, PendingTask>()\n// Tracks sub-tasks dispatched TO other agents \u2014 waiting for their result/help replies\nconst dispatchedSubtasks = new Map<string, { to: string; title: string; dispatchedAt: string; parentTaskId?: string }>()\n// Tracks notification keys that have been shown to LLM (auto-cleaned on next prompt build)\nconst shownNotifications = new Set<string>()\n// Pending synchronous dispatch waiters \u2014 resolve callback keyed by sub-task ID.\n// When aamp_dispatch_task sends a sub-task, it parks a Promise here and waits.\n// When task.result/help arrives for that sub-task ID, the waiter is resolved\n// directly, keeping the LLM awake with full context (no heartbeat needed).\nconst waitingDispatches = new Map<string, (reply: { type: 'result' | 'help'; data: unknown }) => void>()\nlet aampClient: AampClient | null = null\nlet agentEmail = ''\nlet lastConnectionError = ''\nlet lastDisconnectReason = ''\nlet lastTransportMode: 'disconnected' | 'websocket' | 'polling' = 'disconnected'\nlet lastLoggedTransportMode: 'disconnected' | 'websocket' | 'polling' = 'disconnected'\nlet reconcileTimer: NodeJS.Timeout | null = null\nlet transportMonitorTimer: NodeJS.Timeout | null = null\n// Tracks the most recently seen session key so task.dispatch can wake the right session.\n// Default 'agent:main:main' is the standard OpenClaw single-agent session key.\nlet currentSessionKey = 'agent:main:main'\n// Channel runtime \u2014 captured from channel adapter's startAccount for instant dispatch.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet channelRuntime: any = null\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet channelCfg: any = null\n\nfunction logTransportState(\n api: { logger: { info: (msg: string) => void; warn: (msg: string) => void } },\n mode: 'websocket' | 'polling',\n email: string,\n previousMode: 'disconnected' | 'websocket' | 'polling',\n): void {\n if (mode === previousMode) return\n\n if (mode === 'polling') {\n api.logger.info(`[AAMP] Connected (polling fallback active) \u2014 listening as ${email}`)\n return\n }\n\n if (previousMode === 'polling') {\n api.logger.info(`[AAMP] WebSocket restored \u2014 listening as ${email}`)\n return\n }\n\n api.logger.info(`[AAMP] Connected \u2014 listening as ${email}`)\n}\n\n// \u2500\u2500\u2500 Identity helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface Identity {\n email: string\n jmapToken: string\n smtpPassword: string\n}\n\n/**\n * Register a new AAMP node via the management service.\n *\n * Always creates a NEW mailbox (always returns 201). The slug is just a\n * human-readable prefix; a random hex suffix makes the email unique.\n * Callers should only call this once and persist the returned credentials.\n */\nasync function registerNode(cfg: PluginConfig): Promise<Identity> {\n const slug = (cfg.slug ?? 'openclaw-agent')\n .toLowerCase()\n .replace(/[\\s_]+/g, '-')\n .replace(/[^a-z0-9-]/g, '')\n\n const base = baseUrl(cfg.aampHost)\n\n // Step 1: Self-register \u2192 get one-time registration code\n const res = await fetch(`${base}/api/nodes/self-register`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ slug, description: 'OpenClaw AAMP agent node' }),\n })\n\n if (!res.ok) {\n const err = (await res.json().catch(() => ({}))) as { error?: string }\n throw new Error(`AAMP registration failed (${res.status}): ${err.error ?? res.statusText}`)\n }\n\n const regData = (await res.json()) as {\n registrationCode: string\n email: string\n }\n\n // Step 2: Exchange registration code for credentials\n const credRes = await fetch(\n `${base}/api/nodes/credentials?code=${encodeURIComponent(regData.registrationCode)}`,\n )\n\n if (!credRes.ok) {\n const err = (await credRes.json().catch(() => ({}))) as { error?: string }\n throw new Error(`AAMP credential exchange failed (${credRes.status}): ${err.error ?? credRes.statusText}`)\n }\n\n const credData = (await credRes.json()) as {\n email: string\n jmap: { token: string }\n smtp: { password: string }\n }\n\n return {\n email: credData.email,\n jmapToken: credData.jmap.token,\n smtpPassword: credData.smtp.password,\n }\n}\n\n/**\n * Resolve this agent's identity:\n * 1. Return cached credentials from disk if available.\n * 2. Otherwise register a new node and cache the result.\n */\nasync function resolveIdentity(cfg: PluginConfig): Promise<Identity> {\n const cached = loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath())\n if (cached) return cached\n\n const identity = await registerNode(cfg)\n saveCachedIdentity(identity, cfg.credentialsFile ?? defaultCredentialsPath())\n return identity\n}\n\n// \u2500\u2500\u2500 Plugin definition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default {\n id: 'aamp-openclaw-plugin',\n name: 'AAMP Agent Mail Protocol',\n\n configSchema: {\n type: 'object',\n properties: {\n aampHost: {\n type: 'string',\n description: 'AAMP service host, e.g. https://meshmail.ai',\n },\n slug: {\n type: 'string',\n default: 'openclaw-agent',\n description: 'Agent name prefix used in the mailbox address',\n },\n credentialsFile: {\n type: 'string',\n description:\n 'Absolute path to cache AAMP credentials between gateway restarts. ' +\n 'Default: ~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json. ' +\n 'Delete this file to force re-registration with a new mailbox.',\n },\n senderPolicies: {\n type: 'array',\n description:\n 'Per-sender authorization policies. Each sender can optionally require specific ' +\n 'X-AAMP-Dispatch-Context key/value pairs before a task is accepted.',\n items: {\n type: 'object',\n required: ['sender'],\n properties: {\n sender: {\n type: 'string',\n description: 'Dispatch sender email address (case-insensitive exact match).',\n },\n dispatchContextRules: {\n type: 'object',\n description:\n 'Optional exact-match rules over X-AAMP-Dispatch-Context. ' +\n 'All listed keys must be present and their values must match one of the configured entries.',\n additionalProperties: {\n type: 'array',\n items: { type: 'string' },\n },\n },\n },\n },\n },\n },\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n register(api: any) {\n // api.pluginConfig = the config block under plugins.entries[\"aamp-openclaw-plugin\"].config in openclaw.json\n // api.config = the full global OpenClaw config (NOT our plugin's config)\n const cfg = (api.pluginConfig ?? {}) as PluginConfig\n\n // \u2500\u2500 Register lightweight channel adapter to capture channelRuntime \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // We register as a channel SOLELY to get access to channelRuntime, which provides\n // dispatchReplyWithBufferedBlockDispatcher for instant LLM dispatch (bypassing\n // heartbeat's global running-mutex + requests-in-flight ~60s delay).\n // JMAP connection is managed by registerService, NOT startAccount.\n api.registerChannel({\n id: 'aamp',\n meta: { label: 'AAMP' },\n capabilities: { chatTypes: ['dm'] },\n config: {\n listAccountIds: () => cfg.aampHost ? ['default'] : [],\n resolveAccount: () => ({ aampHost: cfg.aampHost }),\n isEnabled: () => !!cfg.aampHost,\n isConfigured: () => !!loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath()),\n },\n gateway: {\n startAccount: async (ctx: { channelRuntime?: unknown; cfg?: unknown; abortSignal?: AbortSignal }) => {\n // Capture channelRuntime for use by sub-task notification dispatch\n channelRuntime = ctx.channelRuntime ?? null\n channelCfg = ctx.cfg ?? null\n api.logger.info(`[AAMP] Channel adapter started \u2014 channelRuntime ${channelRuntime ? 'available' : 'NOT available'}`)\n\n // Keep alive until abort \u2014 JMAP connection is managed by registerService\n await new Promise<void>((resolve) => {\n ctx.abortSignal?.addEventListener('abort', () => resolve())\n })\n channelRuntime = null\n channelCfg = null\n },\n stopAccount: async () => {\n channelRuntime = null\n channelCfg = null\n },\n },\n })\n\n // \u2500\u2500 Shared connect logic (used by service auto-connect and startup recovery) \u2500\u2500\u2500\u2500\u2500\u2500\n async function doConnect(identity: { email: string; jmapToken: string; smtpPassword: string }) {\n if (reconcileTimer) {\n clearInterval(reconcileTimer)\n reconcileTimer = null\n }\n if (transportMonitorTimer) {\n clearInterval(transportMonitorTimer)\n transportMonitorTimer = null\n }\n\n agentEmail = identity.email\n lastConnectionError = ''\n lastDisconnectReason = ''\n lastTransportMode = 'disconnected'\n lastLoggedTransportMode = 'disconnected'\n api.logger.info(`[AAMP] Mailbox identity ready \u2014 ${agentEmail}`)\n\n // All traffic goes through aampHost (port 3000).\n // The management service proxies /jmap/* and /.well-known/jmap \u2192 Stalwart:8080.\n const base = baseUrl(cfg.aampHost)\n\n aampClient = new AampClient({\n email: identity.email,\n jmapToken: identity.jmapToken,\n jmapUrl: base,\n smtpHost: new URL(base).hostname,\n smtpPort: 587,\n smtpPassword: identity.smtpPassword,\n // Local/dev: management-service proxy uses plain HTTP, no TLS cert to verify.\n // Production: set to true when using wss:// with valid certs.\n rejectUnauthorized: false,\n })\n\n aampClient.on('task.dispatch', (task: TaskDispatch) => {\n api.logger.info(`[AAMP] \u2190 task.dispatch ${task.taskId} \"${task.title}\" from=${task.from}`)\n\n // \u2500\u2500 Sender policy / dispatch-context authorization \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const decision = matchSenderPolicy(task, cfg.senderPolicies)\n if (!decision.allowed) {\n api.logger.warn(`[AAMP] \u2717 rejected by senderPolicies: ${task.from} task=${task.taskId} reason=${decision.reason}`)\n void aampClient!.sendResult({\n to: task.from,\n taskId: task.taskId,\n status: 'rejected',\n output: '',\n errorMsg: decision.reason ?? `Sender ${task.from} is not allowed.`,\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Failed to send rejection for task ${task.taskId}: ${err.message}`)\n })\n return\n }\n\n pendingTasks.set(task.taskId, {\n taskId: task.taskId,\n from: task.from,\n title: task.title,\n bodyText: task.bodyText ?? '',\n contextLinks: task.contextLinks,\n timeoutSecs: task.timeoutSecs,\n messageId: task.messageId ?? '',\n receivedAt: new Date().toISOString(),\n })\n\n // Autonomously wake the agent so it processes the task without manual prompting.\n // reason:'wake' bypasses the HEARTBEAT.md file gate (which would skip if the file\n // is empty). before_prompt_build injects the task context into the LLM prompt.\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: currentSessionKey })\n api.logger.info(`[AAMP] Heartbeat triggered for session ${currentSessionKey}`)\n } catch (err) {\n api.logger.warn(`[AAMP] Could not trigger heartbeat: ${(err as Error).message}`)\n }\n })\n\n // \u2500\u2500 Sub-task result: another agent completed a task we dispatched \u2500\u2500\u2500\u2500\u2500\u2500\n aampClient.on('task.result', (result: TaskResult) => {\n api.logger.info(`[AAMP] \u2190 task.result ${result.taskId} status=${result.status} from=${result.from}`)\n\n const sub = dispatchedSubtasks.get(result.taskId)\n dispatchedSubtasks.delete(result.taskId)\n\n // \u2500\u2500 Synchronous dispatch: if aamp_dispatch_task is waiting, resolve it directly \u2500\u2500\n const waiter = waitingDispatches.get(result.taskId)\n if (waiter) {\n waitingDispatches.delete(result.taskId)\n api.logger.info(`[AAMP] Resolving sync waiter for sub-task ${result.taskId}`)\n waiter({ type: 'result', data: result })\n return // Don't go through heartbeat/channel \u2014 the LLM is already awake\n }\n\n // Pre-download attachments to local disk so the LLM can reference them\n // by local file path (instead of requiring a separate download tool call).\n const downloadedFiles: Array<{ filename: string; path: string; size: number }> = []\n const downloadPromise = (async () => {\n if (!result.attachments?.length) return\n const dir = '/tmp/aamp-files'\n ensureDir(dir)\n for (const att of result.attachments) {\n try {\n const buffer = await aampClient!.downloadBlob(att.blobId, att.filename)\n const filepath = `${dir}/${att.filename}`\n writeBinaryFile(filepath, buffer)\n downloadedFiles.push({ filename: att.filename, path: filepath, size: buffer.length })\n api.logger.info(`[AAMP] Pre-downloaded: ${att.filename} (${(buffer.length / 1024).toFixed(1)} KB) \u2192 ${filepath}`)\n } catch (dlErr) {\n api.logger.warn(`[AAMP] Pre-download failed for ${att.filename}: ${(dlErr as Error).message}`)\n }\n }\n })()\n\n downloadPromise.then(() => {\n // Build notification with pre-downloaded file paths\n const MAX_OUTPUT_CHARS = 800\n const label = result.status === 'completed' ? 'Sub-task completed' : 'Sub-task rejected'\n const rawOutput = result.output ?? ''\n const truncatedOutput = rawOutput.length > MAX_OUTPUT_CHARS\n ? rawOutput.slice(0, MAX_OUTPUT_CHARS) + `\\n\\n... [truncated, ${rawOutput.length} chars total]`\n : rawOutput\n\n let attachmentInfo = ''\n if (downloadedFiles.length > 0) {\n attachmentInfo = `\\n\\nAttachments (pre-downloaded to local disk):\\n${downloadedFiles.map(f =>\n `- ${f.filename} (${(f.size / 1024).toFixed(1)} KB) \u2192 ${f.path}`\n ).join('\\n')}\\nUse aamp_send_result with attachments: [${downloadedFiles.map(f => `{ filename: \"${f.filename}\", path: \"${f.path}\" }`).join(', ')}] to forward them.`\n } else if (result.attachments?.length) {\n const files = result.attachments.map((a: ReceivedAttachment) =>\n `${a.filename} (${(a.size / 1024).toFixed(1)} KB, blobId: ${a.blobId})`,\n )\n attachmentInfo = `\\n\\nAttachments (download failed \u2014 use aamp_download_attachment manually):\\n${files.join('\\n')}`\n }\n\n pendingTasks.set(`result:${result.taskId}`, {\n taskId: result.taskId,\n from: result.from,\n title: `${label}: ${sub?.title ?? result.taskId}`,\n bodyText: result.status === 'completed'\n ? `Agent ${result.from} completed the sub-task.\\n\\nOutput:\\n${truncatedOutput}${attachmentInfo}`\n : `Agent ${result.from} rejected the sub-task.\\n\\nReason: ${result.errorMsg ?? 'unknown'}`,\n contextLinks: [],\n timeoutSecs: 0,\n messageId: '',\n receivedAt: new Date().toISOString(),\n })\n\n // Wake LLM via channel dispatch (instant) or heartbeat (fallback)\n if (channelRuntime && channelCfg) {\n const notifyBody = pendingTasks.get(`result:${result.taskId}`)\n const actionableTasks = [...pendingTasks.entries()]\n .filter(([key]) => !key.startsWith('result:') && !key.startsWith('help:'))\n .map(([, t]) => t)\n const actionSection = actionableTasks.length > 0\n ? `\\n\\n### Action Required\\nYou MUST call aamp_send_result to complete the pending task(s):\\n${actionableTasks.map(t => `- Task ID: ${t.taskId} | From: ${t.from} | Title: \"${t.title}\"`).join('\\n')}`\n : ''\n const prompt = `## Sub-task Update\\n\\n${notifyBody?.bodyText ?? 'Sub-task completed.'}${actionSection}`\n\n channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({\n ctx: {\n Body: `Sub-task result: ${result.taskId}`,\n BodyForAgent: prompt,\n From: result.from,\n To: agentEmail,\n SessionKey: `aamp:default:${result.from}`,\n AccountId: 'default',\n ChatType: 'dm',\n Provider: 'aamp',\n Surface: 'aamp',\n OriginatingChannel: 'aamp',\n OriginatingTo: result.from,\n MessageSid: result.taskId,\n Timestamp: Date.now(),\n SenderName: result.from,\n SenderId: result.from,\n CommandAuthorized: true,\n },\n cfg: channelCfg,\n dispatcherOptions: {\n deliver: async () => {},\n onError: (err: unknown) => {\n api.logger.error(`[AAMP] Channel dispatch error: ${err instanceof Error ? err.message : String(err)}`)\n },\n },\n }).then(() => {\n api.logger.info(`[AAMP] Channel dispatch completed for sub-task result ${result.taskId}`)\n pendingTasks.delete(`result:${result.taskId}`)\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Channel dispatch failed: ${err.message}`)\n })\n } else {\n const notifySessionKey = `agent:main:aamp-notify-${Date.now()}`\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: notifySessionKey })\n api.logger.info(`[AAMP] Heartbeat for sub-task result ${result.taskId}`)\n } catch (err) {\n api.logger.warn(`[AAMP] Heartbeat for sub-task result failed: ${(err as Error).message}`)\n }\n }\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Sub-task result processing failed: ${err.message}`)\n })\n })\n\n // \u2500\u2500 Sub-task help: another agent asks for clarification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n aampClient.on('task.help', (help: TaskHelp) => {\n api.logger.info(`[AAMP] \u2190 task.help ${help.taskId} question=\"${help.question}\" from=${help.from}`)\n\n // \u2500\u2500 Synchronous dispatch: if aamp_dispatch_task is waiting, resolve it directly \u2500\u2500\n const waiter = waitingDispatches.get(help.taskId)\n if (waiter) {\n waitingDispatches.delete(help.taskId)\n api.logger.info(`[AAMP] Resolving sync waiter for sub-task help ${help.taskId}`)\n waiter({ type: 'help', data: help })\n return\n }\n\n const sub = dispatchedSubtasks.get(help.taskId)\n\n pendingTasks.set(`help:${help.taskId}`, {\n taskId: help.taskId,\n from: help.from,\n title: `Sub-task needs help: ${sub?.title ?? help.taskId}`,\n bodyText: `Agent ${help.from} is asking for help on the sub-task.\\n\\nQuestion: ${help.question}\\nBlocked reason: ${help.blockedReason}${help.suggestedOptions?.length ? `\\nSuggested options: ${help.suggestedOptions.join(', ')}` : ''}`,\n contextLinks: [],\n timeoutSecs: 0,\n messageId: '',\n receivedAt: new Date().toISOString(),\n })\n\n if (channelRuntime && channelCfg) {\n const notifyBody = pendingTasks.get(`help:${help.taskId}`)\n const prompt = `## Sub-task Help Request\\n\\n${notifyBody?.bodyText ?? help.question}`\n\n channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({\n ctx: {\n Body: `Sub-task help: ${help.taskId}`,\n BodyForAgent: prompt,\n From: help.from,\n To: agentEmail,\n SessionKey: `aamp:default:${help.from}`,\n AccountId: 'default',\n ChatType: 'dm',\n Provider: 'aamp',\n Surface: 'aamp',\n OriginatingChannel: 'aamp',\n OriginatingTo: help.from,\n MessageSid: help.taskId,\n Timestamp: Date.now(),\n SenderName: help.from,\n SenderId: help.from,\n CommandAuthorized: true,\n },\n cfg: channelCfg,\n dispatcherOptions: {\n deliver: async () => {},\n onError: (err: unknown) => {\n api.logger.error(`[AAMP] Channel dispatch error (help): ${err instanceof Error ? err.message : String(err)}`)\n },\n },\n }).then(() => {\n pendingTasks.delete(`help:${help.taskId}`)\n }).catch((err: Error) => {\n api.logger.error(`[AAMP] Channel dispatch failed for help: ${err.message}`)\n })\n } else {\n const helpSessionKey = `agent:main:aamp-notify-${Date.now()}`\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: helpSessionKey })\n api.logger.info(`[AAMP] Heartbeat fallback for sub-task help ${help.taskId}`)\n } catch (err) {\n api.logger.warn(`[AAMP] Heartbeat for sub-task help failed: ${(err as Error).message}`)\n }\n }\n })\n\n aampClient.on('connected', () => {\n lastConnectionError = ''\n lastDisconnectReason = ''\n const mode = aampClient?.isUsingPollingFallback() ? 'polling' : 'websocket'\n logTransportState(api, mode, agentEmail, lastLoggedTransportMode)\n lastTransportMode = mode\n lastLoggedTransportMode = mode\n })\n\n aampClient.on('disconnected', (reason: string) => {\n lastDisconnectReason = reason\n if (lastTransportMode !== 'disconnected') {\n api.logger.warn(`[AAMP] Disconnected: ${reason} (will auto-reconnect)`)\n lastTransportMode = 'disconnected'\n lastLoggedTransportMode = 'disconnected'\n }\n })\n\n aampClient.on('error', (err: Error) => {\n lastConnectionError = err.message\n if (err.message.startsWith('JMAP WebSocket unavailable, falling back to polling:')) {\n if (lastTransportMode !== 'polling') {\n logTransportState(api, 'polling', agentEmail, lastLoggedTransportMode)\n lastTransportMode = 'polling'\n lastLoggedTransportMode = 'polling'\n }\n return\n }\n api.logger.error(`[AAMP] ${err.message}`)\n })\n\n await aampClient.connect()\n\n api.logger.info(\n `[AAMP] Transport after connect \u2014 ${aampClient.isUsingPollingFallback() ? 'polling fallback' : 'websocket'} as ${agentEmail}`,\n )\n\n if (aampClient.isConnected() && lastTransportMode === 'disconnected') {\n if (aampClient.isUsingPollingFallback()) {\n logTransportState(api, 'polling', agentEmail, lastLoggedTransportMode)\n lastTransportMode = 'polling'\n lastLoggedTransportMode = 'polling'\n } else {\n logTransportState(api, 'websocket', agentEmail, lastLoggedTransportMode)\n lastTransportMode = 'websocket'\n lastLoggedTransportMode = 'websocket'\n }\n }\n\n setTimeout(() => {\n if (!aampClient?.isConnected()) return\n const mode = aampClient.isUsingPollingFallback() ? 'polling' : 'websocket'\n logTransportState(api, mode, agentEmail, lastLoggedTransportMode)\n lastTransportMode = mode\n lastLoggedTransportMode = mode\n }, 1000)\n\n transportMonitorTimer = setInterval(() => {\n if (!aampClient) return\n if (!aampClient.isConnected()) {\n if (lastTransportMode !== 'disconnected') {\n lastTransportMode = 'disconnected'\n }\n return\n }\n const mode = aampClient.isUsingPollingFallback() ? 'polling' : 'websocket'\n logTransportState(api, mode, agentEmail, lastLoggedTransportMode)\n lastTransportMode = mode\n lastLoggedTransportMode = mode\n }, 5000)\n\n reconcileTimer = setInterval(() => {\n if (!aampClient) return\n void aampClient.reconcileRecentEmails(20).catch((err: Error) => {\n lastConnectionError = err.message\n api.logger.warn(`[AAMP] Mailbox reconcile failed: ${err.message}`)\n })\n }, 15000)\n }\n\n // \u2500\u2500 Service: auto-connect at gateway startup, disconnect on shutdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // registerService causes this plugin to load eagerly (at gateway startup),\n // not lazily (on first agent run). start() is called once the gateway is up.\n api.registerService({\n id: 'aamp-service',\n start: async () => {\n if (!cfg.aampHost) {\n api.logger.info('[AAMP] aampHost not configured \u2014 skipping auto-connect')\n return\n }\n try {\n const identity = await resolveIdentity(cfg)\n await doConnect(identity)\n } catch (err) {\n api.logger.warn(`[AAMP] Service auto-connect failed: ${(err as Error).message}`)\n }\n },\n stop: () => {\n if (reconcileTimer) {\n clearInterval(reconcileTimer)\n reconcileTimer = null\n }\n if (transportMonitorTimer) {\n clearInterval(transportMonitorTimer)\n transportMonitorTimer = null\n }\n if (aampClient) {\n try {\n aampClient.disconnect()\n api.logger.info('[AAMP] Disconnected on gateway stop')\n } catch {\n // ignore disconnect errors on shutdown\n }\n }\n },\n })\n\n // \u2500\u2500 gateway_start hook: re-trigger heartbeat after runner is initialized \u2500\u2500\u2500\n // Service start() runs BEFORE the heartbeat runner is ready, so\n // requestHeartbeatNow() called during JMAP initial fetch is silently dropped\n // (handler == null). gateway_start fires AFTER the heartbeat runner starts,\n // so we re-trigger here to process any tasks queued during startup.\n api.on('gateway_start', () => {\n if (pendingTasks.size === 0) return\n api.logger.info(`[AAMP] gateway_start: re-triggering heartbeat for ${pendingTasks.size} pending task(s)`)\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: currentSessionKey })\n } catch (err) {\n api.logger.warn(`[AAMP] gateway_start heartbeat failed: ${(err as Error).message}`)\n }\n })\n\n // \u2500\u2500 2. Prompt injection: surface the oldest pending task to the LLM \u2500\u2500\u2500\u2500\u2500\u2500\n api.on(\n 'before_prompt_build',\n (_event, ctx) => {\n // Keep currentSessionKey fresh \u2014 used by task.dispatch to target the right session.\n // Skip channel dispatch sessions (aamp:*) to avoid polluting the heartbeat session key.\n if (ctx?.sessionKey && !String(ctx.sessionKey).startsWith('aamp:')) {\n currentSessionKey = ctx.sessionKey\n }\n\n // Expire tasks that have exceeded their timeout\n const now = Date.now()\n for (const [id, t] of pendingTasks) {\n if (t.timeoutSecs && now - new Date(t.receivedAt).getTime() > t.timeoutSecs * 1000) {\n api.logger.warn(`[AAMP] Task ${id} timed out \u2014 removing from queue`)\n pendingTasks.delete(id)\n }\n }\n\n if (pendingTasks.size === 0) return {}\n\n // Prioritize notifications (sub-task results/help) over actionable tasks.\n // Without this, the oldest actionable task blocks notification delivery,\n // preventing the LLM from seeing sub-task results and completing the parent task.\n const allEntries = [...pendingTasks.entries()]\n const notifications = allEntries.filter(([key]) => key.startsWith('result:') || key.startsWith('help:'))\n const actionable = allEntries.filter(([key]) => !key.startsWith('result:') && !key.startsWith('help:'))\n\n // Pick notification first if available, otherwise oldest actionable task\n const [taskKey, task] = notifications.length > 0\n ? notifications.sort((a, b) => new Date(a[1].receivedAt).getTime() - new Date(b[1].receivedAt).getTime())[0]\n : actionable.sort((a, b) => new Date(a[1].receivedAt).getTime() - new Date(b[1].receivedAt).getTime())[0]\n\n const isNotification = taskKey.startsWith('result:') || taskKey.startsWith('help:')\n\n // Notifications are one-shot: remove immediately after injecting into prompt\n if (isNotification && taskKey) {\n pendingTasks.delete(taskKey)\n }\n\n // Find remaining actionable tasks (non-notification) that still need a response\n const actionableTasks = [...pendingTasks.entries()]\n .filter(([key]) => !key.startsWith('result:') && !key.startsWith('help:'))\n .map(([, t]) => t)\n\n const hasAttachmentInfo = isNotification && (task.bodyText?.includes('aamp_download_attachment') ?? false)\n const actionRequiredSection = isNotification && actionableTasks.length > 0\n ? [\n ``,\n `### Action Required`,\n ``,\n `You still have ${actionableTasks.length} pending task(s) that need a response.`,\n `Use the sub-task result above to complete them by calling aamp_send_result.`,\n ``,\n ...actionableTasks.map((t) =>\n `- Task ID: ${t.taskId} | From: ${t.from} | Title: \"${t.title}\"`\n ),\n ...(hasAttachmentInfo ? [\n ``,\n `### Forwarding Attachments`,\n `The sub-task result includes file attachments. To forward them:`,\n `1. Call aamp_download_attachment for each blobId listed above`,\n `2. Include the downloaded files in aamp_send_result via the attachments parameter`,\n ` Example: attachments: [{ filename: \"file.html\", path: \"/tmp/aamp-files/file.html\" }]`,\n ] : []),\n ].join('\\n')\n : ''\n\n const lines = isNotification ? [\n `## Sub-task Update`,\n ``,\n `A sub-task you dispatched has returned a result. Review the information below.`,\n `If the sub-task included attachments, use aamp_download_attachment to fetch them.`,\n ``,\n `Task ID: ${task.taskId}`,\n `From: ${task.from}`,\n `Title: ${task.title}`,\n task.bodyText ? `\\n${task.bodyText}` : '',\n actionRequiredSection,\n pendingTasks.size > 1 ? `\\n(+${pendingTasks.size - 1} more items queued)` : '',\n ] : [\n `## Pending AAMP Task (action required)`,\n ``,\n `You have received a task via AAMP email. You MUST call one of the two tools below`,\n `BEFORE responding to the user \u2014 do not skip this step.`,\n ``,\n `### Tool selection rules (follow strictly):`,\n ``,\n `Use aamp_send_result ONLY when ALL of the following are true:`,\n ` 1. The title contains a clear, specific action verb (e.g. \"summarise\", \"review\",`,\n ` \"translate\", \"generate\", \"fix\", \"search\", \"compare\", \"list\")`,\n ` 2. You know exactly what input/resource to act on`,\n ` 3. No ambiguity remains \u2014 you could start work immediately without asking anything`,\n ``,\n `Use aamp_send_help in ALL other cases, including:`,\n ` - Title is a greeting or salutation (\"hello\", \"hi\", \"hey\", \"test\", \"ping\", etc.)`,\n ` - Title is fewer than 4 words and contains no actionable verb`,\n ` - Title is too vague to act on without guessing (e.g. \"help\", \"task\", \"question\")`,\n ` - Required context is missing (which file? which URL? which criteria?)`,\n ` - Multiple interpretations are equally plausible`,\n ``,\n `IMPORTANT: Responding to a greeting with a greeting is WRONG. \"hello\" is not a`,\n `valid task description \u2014 ask what specific task the dispatcher needs done.`,\n ``,\n `### Sub-task dispatch rules:`,\n `If you delegate work to another agent via aamp_dispatch_task, you MUST pass`,\n `parentTaskId: \"${task.taskId}\" to establish the parent-child relationship.`,\n ``,\n `Task ID: ${task.taskId}`,\n `From: ${task.from}`,\n `Title: ${task.title}`,\n task.bodyText ? `Description:\\n${task.bodyText}` : '',\n task.contextLinks.length\n ? `Context Links:\\n${task.contextLinks.map((l) => ` - ${l}`).join('\\n')}`\n : '',\n task.timeoutSecs ? `Deadline: ${task.timeoutSecs}s from dispatch` : `Deadline: none`,\n `Received: ${task.receivedAt}`,\n pendingTasks.size > 1 ? `\\n(+${pendingTasks.size - 1} more tasks queued)` : '',\n ]\n .filter(Boolean)\n .join('\\n')\n\n return { prependContext: lines }\n },\n { priority: 5 },\n )\n\n // \u2500\u2500 3. Tool: send task result \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_send_result',\n description:\n 'Send the result of an AAMP task back to the dispatcher. ' +\n 'Call this after you have finished processing the task.',\n parameters: {\n type: 'object',\n required: ['taskId', 'status', 'output'],\n properties: {\n taskId: {\n type: 'string',\n description: 'The AAMP task ID to reply to (from the system context)',\n },\n status: {\n type: 'string',\n enum: ['completed', 'rejected'],\n description: '\"completed\" on success, \"rejected\" if the task cannot be done',\n },\n output: {\n type: 'string',\n description: 'Your result or explanation',\n },\n errorMsg: {\n type: 'string',\n description: 'Optional error details (use only when status = rejected)',\n },\n attachments: {\n type: 'array',\n description: 'File attachments. Each item: { filename, contentType, path (local file path) }',\n items: {\n type: 'object',\n properties: {\n filename: { type: 'string' },\n contentType: { type: 'string' },\n path: { type: 'string', description: 'Absolute path to the file on disk' },\n },\n required: ['filename', 'path'],\n },\n },\n structuredResult: {\n type: 'array',\n description: 'Optional structured Meego field values.',\n items: {\n type: 'object',\n required: ['fieldKey', 'fieldTypeKey'],\n properties: {\n fieldKey: { type: 'string' },\n fieldTypeKey: { type: 'string' },\n fieldAlias: { type: 'string' },\n value: {\n description: 'Field value in the exact format required by Meego for this field type.',\n },\n index: { type: 'string' },\n attachmentFilenames: {\n type: 'array',\n items: { type: 'string' },\n description: 'For attachment fields, filenames from attachments[] that should be uploaded into this field.',\n },\n },\n },\n },\n },\n },\n execute: async (_id, params) => {\n const p = params as {\n taskId: string\n status: 'completed' | 'rejected'\n output: string\n errorMsg?: string\n attachments?: Array<{ filename: string; contentType?: string; path: string }>\n structuredResult?: StructuredResultFieldInput[]\n }\n\n const task = pendingTasks.get(p.taskId)\n if (!task) {\n return {\n content: [{ type: 'text', text: `Error: task ${p.taskId} not found in pending queue.` }],\n }\n }\n\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n api.logger.info(`[AAMP] aamp_send_result params ${JSON.stringify({\n taskId: p.taskId,\n status: p.status,\n output: p.output,\n errorMsg: p.errorMsg,\n attachments: p.attachments?.map((a) => ({\n filename: a.filename,\n contentType: a.contentType ?? 'application/octet-stream',\n path: a.path,\n })) ?? [],\n structuredResult: p.structuredResult?.map((field) => ({\n fieldKey: field.fieldKey,\n fieldTypeKey: field.fieldTypeKey,\n fieldAlias: field.fieldAlias,\n value: field.value,\n index: field.index,\n attachmentFilenames: field.attachmentFilenames ?? [],\n })) ?? [],\n })}`)\n\n // Build attachments from file paths\n let attachments: AampAttachment[] | undefined\n if (p.attachments?.length) {\n attachments = p.attachments.map((a: { filename: string; contentType?: string; path: string }) => ({\n filename: a.filename,\n contentType: a.contentType ?? 'application/octet-stream',\n content: readBinaryFile(a.path),\n }))\n }\n\n await aampClient.sendResult({\n to: task.from,\n taskId: task.taskId,\n status: p.status,\n output: p.output,\n errorMsg: p.errorMsg,\n structuredResult: p.structuredResult?.length ? p.structuredResult : undefined,\n inReplyTo: task.messageId || undefined,\n attachments,\n })\n\n pendingTasks.delete(task.taskId)\n api.logger.info(`[AAMP] \u2192 task.result ${task.taskId} ${p.status}`)\n\n // If more tasks remain, wake the agent to process them\n if (pendingTasks.size > 0) {\n try {\n api.runtime.system.requestHeartbeatNow({ reason: 'wake', sessionKey: currentSessionKey })\n } catch { /* ignore */ }\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Result sent for task ${task.taskId} (status: ${p.status}).`,\n },\n ],\n }\n },\n }, { name: 'aamp_send_result' })\n\n // \u2500\u2500 4. Tool: ask for help \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_send_help',\n description:\n 'Send a help request for an AAMP task when you are blocked or need human clarification ' +\n 'before you can proceed.',\n parameters: {\n type: 'object',\n required: ['taskId', 'question', 'blockedReason'],\n properties: {\n taskId: {\n type: 'string',\n description: 'The AAMP task ID',\n },\n question: {\n type: 'string',\n description: 'Your question for the human dispatcher',\n },\n blockedReason: {\n type: 'string',\n description: 'Why you cannot proceed without their input',\n },\n suggestedOptions: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional list of choices for the dispatcher to pick from',\n },\n },\n },\n execute: async (_id, params) => {\n const p = params as {\n taskId: string\n question: string\n blockedReason: string\n suggestedOptions?: string[]\n }\n\n const task = pendingTasks.get(p.taskId)\n if (!task) {\n return {\n content: [{ type: 'text', text: `Error: task ${p.taskId} not found in pending queue.` }],\n }\n }\n\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n await aampClient.sendHelp({\n to: task.from,\n taskId: task.taskId,\n question: p.question,\n blockedReason: p.blockedReason,\n suggestedOptions: p.suggestedOptions ?? [],\n inReplyTo: task.messageId || undefined,\n })\n\n api.logger.info(`[AAMP] \u2192 task.help ${task.taskId}`)\n\n // Keep the task in pending \u2014 the help reply may arrive later\n return {\n content: [\n {\n type: 'text',\n text: `Help request sent for task ${task.taskId}. The task remains pending until the dispatcher replies.`,\n },\n ],\n }\n },\n }, { name: 'aamp_send_help' })\n\n // \u2500\u2500 5. Tool: inspect queue \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_pending_tasks',\n description: 'List all AAMP tasks currently waiting to be processed.',\n parameters: { type: 'object', properties: {} },\n execute: async () => {\n if (pendingTasks.size === 0) {\n return { content: [{ type: 'text', text: 'No pending AAMP tasks.' }] }\n }\n\n const lines = [...pendingTasks.values()]\n .sort((a, b) => new Date(a.receivedAt).getTime() - new Date(b.receivedAt).getTime())\n .map(\n (t, i) =>\n `${i + 1}. [${t.taskId}] \"${t.title}\"${t.bodyText ? `\\n Description: ${t.bodyText}` : ''} \u2014 from ${t.from} (received ${t.receivedAt})`,\n )\n\n return {\n content: [\n {\n type: 'text',\n text: `${pendingTasks.size} pending task(s):\\n${lines.join('\\n')}`,\n },\n ],\n }\n },\n }, { name: 'aamp_pending_tasks' })\n\n // \u2500\u2500 6. Tool: dispatch task to another agent (SYNCHRONOUS) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Sends the task and BLOCKS until the sub-agent replies with task.result or\n // task.help. The reply is returned directly as the tool result, keeping the\n // LLM awake with full context \u2014 no heartbeat/channel dispatch needed.\n api.registerTool({\n name: 'aamp_dispatch_task',\n description:\n 'Send a task to another AAMP agent and WAIT for the result. ' +\n 'This tool blocks until the sub-agent replies (typically 5-60s). ' +\n 'The sub-agent\\'s output and any attachment file paths are returned directly.',\n parameters: {\n type: 'object',\n required: ['to', 'title'],\n properties: {\n to: { type: 'string', description: 'Target agent AAMP email address' },\n title: { type: 'string', description: 'Task title (concise summary)' },\n bodyText: { type: 'string', description: 'Detailed task description' },\n parentTaskId: { type: 'string', description: 'If you are processing a pending AAMP task, pass its Task ID here to establish parent-child nesting. Omit for top-level tasks.' },\n timeoutSecs: { type: 'number', description: 'Timeout in seconds (optional)' },\n contextLinks: {\n type: 'array', items: { type: 'string' },\n description: 'URLs providing context (optional)',\n },\n attachments: {\n type: 'array',\n description: 'File attachments. Each item: { filename, contentType, path (local file path) }',\n items: {\n type: 'object',\n properties: {\n filename: { type: 'string' },\n contentType: { type: 'string' },\n path: { type: 'string', description: 'Absolute path to the file on disk' },\n },\n required: ['filename', 'path'],\n },\n },\n },\n },\n execute: async (_id: unknown, params: {\n to: string; title: string; bodyText?: string;\n parentTaskId?: string; timeoutSecs?: number; contextLinks?: string[];\n attachments?: Array<{ filename: string; contentType?: string; path: string }>\n }) => {\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n try {\n // Build attachments from file paths\n let attachments: AampAttachment[] | undefined\n if (params.attachments?.length) {\n attachments = params.attachments.map((a: { filename: string; contentType?: string; path: string }) => ({\n filename: a.filename,\n contentType: a.contentType ?? 'application/octet-stream',\n content: readBinaryFile(a.path),\n }))\n }\n\n const result = await aampClient.sendTask({\n to: params.to,\n title: params.title,\n parentTaskId: params.parentTaskId,\n timeoutSecs: params.timeoutSecs,\n contextLinks: params.contextLinks,\n attachments,\n })\n\n // Track as dispatched sub-task\n dispatchedSubtasks.set(result.taskId, {\n to: params.to,\n title: params.title,\n dispatchedAt: new Date().toISOString(),\n parentTaskId: params.parentTaskId,\n })\n\n api.logger.info(`[AAMP] \u2192 task.dispatch ${result.taskId} to=${params.to} parent=${params.parentTaskId ?? 'none'} (waiting for reply\u2026)`)\n\n // \u2500\u2500 SYNCHRONOUS WAIT: block until sub-agent replies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const timeoutMs = (params.timeoutSecs ?? 300) * 1000\n const reply = await new Promise<{ type: 'result' | 'help'; data: unknown }>((resolve, reject) => {\n waitingDispatches.set(result.taskId, resolve)\n setTimeout(() => {\n if (waitingDispatches.delete(result.taskId)) {\n reject(new Error(`Sub-task ${result.taskId} timed out after ${params.timeoutSecs ?? 300}s`))\n }\n }, timeoutMs)\n })\n\n api.logger.info(`[AAMP] \u2190 sync reply for ${result.taskId}: type=${reply.type} attachments=${JSON.stringify((reply.data as any)?.attachments?.length ?? 0)}`)\n\n if (reply.type === 'result') {\n const r = reply.data as TaskResult\n\n // Pre-download attachments \u2014 use direct JMAP blob download (bypass SDK's downloadBlob\n // which was returning 404 due to URL construction issues in the esbuild bundle).\n let attachmentLines = ''\n if (r.attachments?.length) {\n api.logger.info(`[AAMP] Downloading ${r.attachments.length} attachment(s) from sync reply...`)\n const dir = '/tmp/aamp-files'\n ensureDir(dir)\n const downloaded: string[] = []\n const base = baseUrl(cfg.aampHost)\n const identity = loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath())\n const authHeader = identity ? `Basic ${Buffer.from(identity.email + ':' + identity.smtpPassword).toString('base64')}` : ''\n for (const att of r.attachments) {\n try {\n // Direct JMAP blob download \u2014 construct URL manually\n const dlUrl = `${base}/jmap/download/n/${encodeURIComponent(att.blobId)}/${encodeURIComponent(att.filename)}?accept=application/octet-stream`\n api.logger.info(`[AAMP] Fetching ${dlUrl}`)\n const dlRes = await fetch(dlUrl, { headers: { Authorization: authHeader } })\n if (!dlRes.ok) throw new Error(`HTTP ${dlRes.status}`)\n const buffer = Buffer.from(await dlRes.arrayBuffer())\n const filepath = `${dir}/${att.filename}`\n writeBinaryFile(filepath, buffer)\n downloaded.push(`${att.filename} (${(buffer.length / 1024).toFixed(1)} KB) \u2192 ${filepath}`)\n api.logger.info(`[AAMP] Downloaded: ${att.filename} (${(buffer.length / 1024).toFixed(1)} KB)`)\n } catch (dlErr) {\n api.logger.error(`[AAMP] Download failed for ${att.filename}: ${(dlErr as Error).message}`)\n }\n }\n if (downloaded.length) {\n attachmentLines = `\\n\\nAttachments downloaded:\\n${downloaded.join('\\n')}`\n }\n }\n\n return {\n content: [{\n type: 'text',\n text: [\n `Sub-task ${r.status}: ${params.title}`,\n `Agent: ${r.from}`,\n `Task ID: ${result.taskId}`,\n r.status === 'completed' ? `\\nOutput:\\n${r.output}` : `\\nError: ${r.errorMsg ?? 'rejected'}`,\n attachmentLines,\n ].filter(Boolean).join('\\n'),\n }],\n }\n } else {\n const h = reply.data as TaskHelp\n return {\n content: [{\n type: 'text',\n text: [\n `Sub-task needs help: ${params.title}`,\n `Agent: ${h.from}`,\n `Task ID: ${result.taskId}`,\n `\\nQuestion: ${h.question}`,\n `Blocked reason: ${h.blockedReason}`,\n h.suggestedOptions?.length ? `Options: ${h.suggestedOptions.join(' | ')}` : '',\n ].filter(Boolean).join('\\n'),\n }],\n }\n }\n } catch (err) {\n return {\n content: [{ type: 'text', text: `Error dispatching task: ${(err as Error).message}` }],\n }\n }\n },\n }, { name: 'aamp_dispatch_task' })\n\n // \u2500\u2500 7. Tool: check AAMP protocol support \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_check_protocol',\n description:\n 'Check if an email address supports the AAMP protocol. ' +\n 'Returns { aamp: true/false } indicating whether the address is an AAMP agent.',\n parameters: {\n type: 'object',\n required: ['email'],\n properties: {\n email: { type: 'string', description: 'Email address to check' },\n },\n },\n execute: async (_id: unknown, params: { email: string }) => {\n const base = baseUrl(cfg.aampHost)\n const email = params?.email ?? ''\n if (!email) {\n return { content: [{ type: 'text', text: 'Error: email parameter is required' }] }\n }\n try {\n const res = await fetch(`${base}/api/aamp-check?email=${encodeURIComponent(email)}`)\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const data = await res.json() as { aamp: boolean; domain?: string }\n return {\n content: [{\n type: 'text',\n text: data.aamp\n ? `${params.email} supports AAMP protocol (domain: ${data.domain ?? 'unknown'})`\n : `${params.email} does not support AAMP protocol`,\n }],\n }\n } catch (err) {\n return {\n content: [{ type: 'text', text: `Could not check ${params.email}: ${(err as Error).message}` }],\n }\n }\n },\n }, { name: 'aamp_check_protocol' })\n\n // \u2500\u2500 8. Tool: download attachment blob \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerTool({\n name: 'aamp_download_attachment',\n description:\n 'Download an AAMP email attachment to local disk by its blobId. ' +\n 'Use this to retrieve files received from sub-agent task results.',\n parameters: {\n type: 'object',\n required: ['blobId', 'filename'],\n properties: {\n blobId: { type: 'string', description: 'The JMAP blobId from the attachment metadata' },\n filename: { type: 'string', description: 'Filename to save as' },\n saveTo: { type: 'string', description: 'Directory to save to (default: /tmp/aamp-files)' },\n },\n },\n execute: async (_id: unknown, params: { blobId: string; filename: string; saveTo?: string }) => {\n if (!aampClient?.isConnected()) {\n return { content: [{ type: 'text', text: 'Error: AAMP client is not connected.' }] }\n }\n\n const dir = params.saveTo ?? '/tmp/aamp-files'\n ensureDir(dir)\n\n try {\n const buffer = await aampClient.downloadBlob(params.blobId, params.filename)\n const filepath = `${dir}/${params.filename}`\n writeBinaryFile(filepath, buffer)\n return {\n content: [{\n type: 'text',\n text: `Downloaded ${params.filename} (${(buffer.length / 1024).toFixed(1)} KB) to ${filepath}`,\n }],\n }\n } catch (err) {\n return {\n content: [{ type: 'text', text: `Download failed: ${(err as Error).message}` }],\n }\n }\n },\n }, { name: 'aamp_download_attachment' })\n\n // \u2500\u2500 9. Slash command: /aamp-status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n api.registerCommand({\n name: 'aamp-status',\n description: 'Show AAMP connection status and pending task queue',\n acceptsArgs: false,\n requireAuth: false,\n handler: () => {\n const isPollingFallback = aampClient?.isUsingPollingFallback?.() ?? false\n const connectionLine = aampClient?.isConnected()\n ? (isPollingFallback ? '\uD83D\uDFE1 connected (polling fallback)' : '\u2705 connected')\n : '\u274C disconnected'\n\n return {\n text: [\n `**AAMP Plugin Status**`,\n `Host: ${cfg.aampHost || '(not configured)'}`,\n `Identity: ${agentEmail || '(not yet registered)'}`,\n `Connection: ${connectionLine}`,\n `Cached: ${loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath()) ? 'yes' : 'no'}`,\n lastConnectionError ? `Last error: ${lastConnectionError}` : '',\n lastDisconnectReason ? `Last disconnect: ${lastDisconnectReason}` : '',\n `Pending: ${pendingTasks.size} task(s)`,\n ...[...pendingTasks.values()].map(\n (t) => ` \u2022 ${t.taskId.slice(0, 8)}\u2026 \"${t.title}\" from ${t.from}`,\n ),\n ]\n .filter(Boolean)\n .join('\\n'),\n }\n },\n })\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -8,9 +8,22 @@
|
|
|
8
8
|
"aampHost": { "type": "string" },
|
|
9
9
|
"slug": { "type": "string", "default": "openclaw-agent" },
|
|
10
10
|
"credentialsFile": { "type": "string", "default": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json" },
|
|
11
|
-
"
|
|
11
|
+
"senderPolicies": {
|
|
12
12
|
"type": "array",
|
|
13
|
-
"items": {
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["sender"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"sender": { "type": "string" },
|
|
18
|
+
"dispatchContextRules": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"additionalProperties": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": { "type": "string" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
}
|
|
15
28
|
}
|
|
16
29
|
},
|
|
@@ -18,7 +31,7 @@
|
|
|
18
31
|
"aampHost": { "label": "AAMP Host", "placeholder": "https://meshmail.ai", "promptOnInstall": true },
|
|
19
32
|
"slug": { "label": "Agent Slug", "placeholder": "openclaw-agent", "promptOnInstall": false },
|
|
20
33
|
"credentialsFile": { "label": "Credentials File", "placeholder": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json", "promptOnInstall": false },
|
|
21
|
-
"
|
|
34
|
+
"senderPolicies": { "label": "Sender Policies", "placeholder": "[{\"sender\":\"meegle-bot@meshmail.ai\",\"dispatchContextRules\":{\"project_key\":[\"proj_123\"]}}]", "promptOnInstall": true }
|
|
22
35
|
},
|
|
23
36
|
"skills": ["./skills"]
|
|
24
37
|
}
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"skills"
|
|
8
8
|
],
|
|
9
9
|
"license": "MIT",
|
|
10
|
-
"version": "0.1.
|
|
10
|
+
"version": "0.1.12",
|
|
11
11
|
"description": "AAMP Agent Mail Protocol — OpenClaw plugin. Gives OpenClaw an AAMP mailbox identity and lets it receive, process and reply to AAMP tasks.",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"main": "dist/index.js",
|
|
@@ -34,10 +34,23 @@
|
|
|
34
34
|
"default": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json",
|
|
35
35
|
"description": "Path to persist AAMP credentials between restarts"
|
|
36
36
|
},
|
|
37
|
-
"
|
|
37
|
+
"senderPolicies": {
|
|
38
38
|
"type": "array",
|
|
39
|
-
"items": {
|
|
40
|
-
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"required": ["sender"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"sender": { "type": "string" },
|
|
44
|
+
"dispatchContextRules": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"items": { "type": "string" }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"description": "Per-sender authorization policies. Each sender can optionally require exact-match X-AAMP-Dispatch-Context key/value pairs."
|
|
41
54
|
}
|
|
42
55
|
}
|
|
43
56
|
},
|
|
@@ -45,7 +58,7 @@
|
|
|
45
58
|
"aampHost": { "label": "AAMP Host", "placeholder": "https://meshmail.ai", "promptOnInstall": true },
|
|
46
59
|
"slug": { "label": "Agent Slug", "placeholder": "openclaw-agent", "promptOnInstall": false },
|
|
47
60
|
"credentialsFile": { "label": "Credentials File", "placeholder": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json", "promptOnInstall": false },
|
|
48
|
-
"
|
|
61
|
+
"senderPolicies": { "label": "Sender Policies", "placeholder": "[{\"sender\":\"meegle-bot@meshmail.ai\",\"dispatchContextRules\":{\"project_key\":[\"proj_123\"]}}]", "promptOnInstall": true }
|
|
49
62
|
}
|
|
50
63
|
},
|
|
51
64
|
"scripts": {
|