aamp-openclaw-plugin 0.1.35 → 0.1.37
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 +2 -0
- package/dist/chunk-ORTVVAMV.js +82 -0
- package/dist/chunk-ORTVVAMV.js.map +7 -0
- package/dist/chunk-W4C7IUCH.js +81 -0
- package/dist/chunk-W4C7IUCH.js.map +7 -0
- package/dist/index.js +156 -14
- package/dist/index.js.map +2 -2
- package/package.json +23 -1
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ npm run build
|
|
|
38
38
|
"enabled": true,
|
|
39
39
|
"config": {
|
|
40
40
|
"aampHost": "https://meshmail.ai",
|
|
41
|
+
"taskDispatchConcurrency": 10,
|
|
41
42
|
"slug": "openclaw-agent",
|
|
42
43
|
"credentialsFile": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json",
|
|
43
44
|
"senderPolicies": [
|
|
@@ -57,6 +58,7 @@ npm run build
|
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
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.
|
|
61
|
+
`taskDispatchConcurrency` is optional and defaults to `10`.
|
|
60
62
|
|
|
61
63
|
The plugin also understands:
|
|
62
64
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createRequire as __aampCreateRequire } from "module"; const require = __aampCreateRequire(import.meta.url);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined")
|
|
12
|
+
return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
15
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
16
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
27
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
28
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
29
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
30
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
31
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
32
|
+
mod
|
|
33
|
+
));
|
|
34
|
+
|
|
35
|
+
// src/file-store.ts
|
|
36
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
37
|
+
import { dirname, join } from "node:path";
|
|
38
|
+
import { homedir } from "node:os";
|
|
39
|
+
function defaultCredentialsPath() {
|
|
40
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
41
|
+
}
|
|
42
|
+
function loadCachedIdentity(file) {
|
|
43
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
44
|
+
if (!existsSync(resolved))
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
48
|
+
if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword)
|
|
49
|
+
return null;
|
|
50
|
+
return parsed;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function saveCachedIdentity(identity, file) {
|
|
56
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
57
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
58
|
+
writeFileSync(resolved, JSON.stringify(identity, null, 2), "utf-8");
|
|
59
|
+
}
|
|
60
|
+
function ensureDir(dir) {
|
|
61
|
+
mkdirSync(dir, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
function readBinaryFile(path) {
|
|
64
|
+
return readFileSync(path);
|
|
65
|
+
}
|
|
66
|
+
function writeBinaryFile(path, content) {
|
|
67
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
68
|
+
writeFileSync(path, content);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
__require,
|
|
73
|
+
__commonJS,
|
|
74
|
+
__toESM,
|
|
75
|
+
defaultCredentialsPath,
|
|
76
|
+
loadCachedIdentity,
|
|
77
|
+
saveCachedIdentity,
|
|
78
|
+
ensureDir,
|
|
79
|
+
readBinaryFile,
|
|
80
|
+
writeBinaryFile
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=chunk-ORTVVAMV.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/file-store.ts"],
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\n\nexport interface Identity {\n email: string\n jmapToken: string\n smtpPassword: string\n}\n\nexport function defaultCredentialsPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.credentials.json')\n}\n\nexport function loadCachedIdentity(file?: string): Identity | null {\n const resolved = file ?? defaultCredentialsPath()\n if (!existsSync(resolved)) return null\n try {\n const parsed = JSON.parse(readFileSync(resolved, 'utf-8')) as Partial<Identity>\n if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword) return null\n return parsed as Identity\n } catch {\n return null\n }\n}\n\nexport function saveCachedIdentity(identity: Identity, file?: string): void {\n const resolved = file ?? defaultCredentialsPath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify(identity, null, 2), 'utf-8')\n}\n\nexport function ensureDir(dir: string): void {\n mkdirSync(dir, { recursive: true })\n}\n\nexport function readBinaryFile(path: string): Buffer {\n return readFileSync(path)\n}\n\nexport function writeBinaryFile(path: string, content: Uint8Array | Buffer): void {\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, content)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY;AAC9B,SAAS,eAAe;AAQjB,SAAS,yBAAiC;AAC/C,SAAO,KAAK,QAAQ,GAAG,aAAa,cAAc,wBAAwB,mBAAmB;AAC/F;AAEO,SAAS,mBAAmB,MAAgC;AACjE,QAAM,WAAW,QAAQ,uBAAuB;AAChD,MAAI,CAAC,WAAW,QAAQ;AAAG,WAAO;AAClC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;AACzD,QAAI,CAAC,OAAO,SAAS,CAAC,OAAO,aAAa,CAAC,OAAO;AAAc,aAAO;AACvE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,mBAAmB,UAAoB,MAAqB;AAC1E,QAAM,WAAW,QAAQ,uBAAuB;AAChD,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,gBAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACpE;AAEO,SAAS,UAAU,KAAmB;AAC3C,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC;AAEO,SAAS,eAAe,MAAsB;AACnD,SAAO,aAAa,IAAI;AAC1B;AAEO,SAAS,gBAAgB,MAAc,SAAoC;AAChF,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,OAAO;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined")
|
|
11
|
+
return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
// src/file-store.ts
|
|
35
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
36
|
+
import { dirname, join } from "node:path";
|
|
37
|
+
import { homedir } from "node:os";
|
|
38
|
+
function defaultCredentialsPath() {
|
|
39
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
40
|
+
}
|
|
41
|
+
function loadCachedIdentity(file) {
|
|
42
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
43
|
+
if (!existsSync(resolved))
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
47
|
+
if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword)
|
|
48
|
+
return null;
|
|
49
|
+
return parsed;
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function saveCachedIdentity(identity, file) {
|
|
55
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
56
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
57
|
+
writeFileSync(resolved, JSON.stringify(identity, null, 2), "utf-8");
|
|
58
|
+
}
|
|
59
|
+
function ensureDir(dir) {
|
|
60
|
+
mkdirSync(dir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
function readBinaryFile(path) {
|
|
63
|
+
return readFileSync(path);
|
|
64
|
+
}
|
|
65
|
+
function writeBinaryFile(path, content) {
|
|
66
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
67
|
+
writeFileSync(path, content);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
__require,
|
|
72
|
+
__commonJS,
|
|
73
|
+
__toESM,
|
|
74
|
+
defaultCredentialsPath,
|
|
75
|
+
loadCachedIdentity,
|
|
76
|
+
saveCachedIdentity,
|
|
77
|
+
ensureDir,
|
|
78
|
+
readBinaryFile,
|
|
79
|
+
writeBinaryFile
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=chunk-W4C7IUCH.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/file-store.ts"],
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\n\nexport interface Identity {\n email: string\n jmapToken: string\n smtpPassword: string\n}\n\nexport function defaultCredentialsPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.credentials.json')\n}\n\nexport function loadCachedIdentity(file?: string): Identity | null {\n const resolved = file ?? defaultCredentialsPath()\n if (!existsSync(resolved)) return null\n try {\n const parsed = JSON.parse(readFileSync(resolved, 'utf-8')) as Partial<Identity>\n if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword) return null\n return parsed as Identity\n } catch {\n return null\n }\n}\n\nexport function saveCachedIdentity(identity: Identity, file?: string): void {\n const resolved = file ?? defaultCredentialsPath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify(identity, null, 2), 'utf-8')\n}\n\nexport function ensureDir(dir: string): void {\n mkdirSync(dir, { recursive: true })\n}\n\nexport function readBinaryFile(path: string): Buffer {\n return readFileSync(path)\n}\n\nexport function writeBinaryFile(path: string, content: Uint8Array | Buffer): void {\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, content)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY;AAC9B,SAAS,eAAe;AAQjB,SAAS,yBAAiC;AAC/C,SAAO,KAAK,QAAQ,GAAG,aAAa,cAAc,wBAAwB,mBAAmB;AAC/F;AAEO,SAAS,mBAAmB,MAAgC;AACjE,QAAM,WAAW,QAAQ,uBAAuB;AAChD,MAAI,CAAC,WAAW,QAAQ;AAAG,WAAO;AAClC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;AACzD,QAAI,CAAC,OAAO,SAAS,CAAC,OAAO,aAAa,CAAC,OAAO;AAAc,aAAO;AACvE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,mBAAmB,UAAoB,MAAqB;AAC1E,QAAM,WAAW,QAAQ,uBAAuB;AAChD,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,gBAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACpE;AAEO,SAAS,UAAU,KAAmB;AAC3C,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC;AAEO,SAAS,eAAe,MAAsB;AACnD,SAAO,aAAa,IAAI;AAC1B;AAEO,SAAS,gBAAgB,MAAc,SAAoC;AAChF,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,OAAO;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -421,6 +421,17 @@ var TinyEmitter = class {
|
|
|
421
421
|
}
|
|
422
422
|
return true;
|
|
423
423
|
}
|
|
424
|
+
async emitAsync(event, ...args) {
|
|
425
|
+
const bucket = this.listeners.get(event);
|
|
426
|
+
if (!bucket || bucket.size === 0)
|
|
427
|
+
return false;
|
|
428
|
+
const settled = await Promise.allSettled([...bucket].map((listener) => Promise.resolve(listener(...args))));
|
|
429
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
430
|
+
if (rejected) {
|
|
431
|
+
throw rejected.reason;
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
424
435
|
};
|
|
425
436
|
|
|
426
437
|
// ../sdk/src/jmap-push.js
|
|
@@ -1897,14 +1908,27 @@ function buildRegisteredCommandDispatchPayload(opts) {
|
|
|
1897
1908
|
stream: { mode: opts.streamMode ?? "full" }
|
|
1898
1909
|
};
|
|
1899
1910
|
}
|
|
1911
|
+
var DEFAULT_TASK_DISPATCH_CONCURRENCY = 10;
|
|
1912
|
+
function normalizeTaskDispatchConcurrency(value) {
|
|
1913
|
+
if (value == null)
|
|
1914
|
+
return DEFAULT_TASK_DISPATCH_CONCURRENCY;
|
|
1915
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 1) {
|
|
1916
|
+
throw new Error("taskDispatchConcurrency must be a positive integer");
|
|
1917
|
+
}
|
|
1918
|
+
return value;
|
|
1919
|
+
}
|
|
1900
1920
|
var AampClient = class _AampClient extends TinyEmitter {
|
|
1901
1921
|
jmapClient;
|
|
1902
1922
|
smtpSender;
|
|
1903
1923
|
config;
|
|
1924
|
+
taskDispatchConcurrency;
|
|
1925
|
+
pendingTaskDispatches = [];
|
|
1926
|
+
activeTaskDispatchCount = 0;
|
|
1904
1927
|
streamAppendQueues = /* @__PURE__ */ new Map();
|
|
1905
1928
|
constructor(config) {
|
|
1906
1929
|
super();
|
|
1907
1930
|
this.config = config;
|
|
1931
|
+
this.taskDispatchConcurrency = normalizeTaskDispatchConcurrency(config.taskDispatchConcurrency);
|
|
1908
1932
|
const mailboxToken = config.mailboxToken;
|
|
1909
1933
|
const resolvedBaseUrl = config.baseUrl;
|
|
1910
1934
|
const derived = deriveMailboxServiceDefaults(config.email, resolvedBaseUrl);
|
|
@@ -1940,7 +1964,7 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
1940
1964
|
rejectUnauthorized: config.rejectUnauthorized
|
|
1941
1965
|
});
|
|
1942
1966
|
this.jmapClient.on("task.dispatch", (task) => {
|
|
1943
|
-
this.
|
|
1967
|
+
this.enqueueTaskDispatch(task);
|
|
1944
1968
|
});
|
|
1945
1969
|
this.jmapClient.on("task.cancel", (task) => {
|
|
1946
1970
|
this.emit("task.cancel", task);
|
|
@@ -1993,6 +2017,7 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
1993
2017
|
smtpPort: config.smtpPort ?? 587,
|
|
1994
2018
|
smtpPassword: config.smtpPassword,
|
|
1995
2019
|
reconnectInterval: config.reconnectInterval,
|
|
2020
|
+
taskDispatchConcurrency: config.taskDispatchConcurrency,
|
|
1996
2021
|
rejectUnauthorized: config.rejectUnauthorized
|
|
1997
2022
|
});
|
|
1998
2023
|
}
|
|
@@ -2246,6 +2271,30 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2246
2271
|
}
|
|
2247
2272
|
return res.json();
|
|
2248
2273
|
}
|
|
2274
|
+
enqueueTaskDispatch(task) {
|
|
2275
|
+
this.pendingTaskDispatches.push(task);
|
|
2276
|
+
this.drainTaskDispatchQueue();
|
|
2277
|
+
}
|
|
2278
|
+
drainTaskDispatchQueue() {
|
|
2279
|
+
while (this.activeTaskDispatchCount < this.taskDispatchConcurrency && this.pendingTaskDispatches.length > 0) {
|
|
2280
|
+
const nextTask = this.pendingTaskDispatches.shift();
|
|
2281
|
+
if (!nextTask)
|
|
2282
|
+
return;
|
|
2283
|
+
this.activeTaskDispatchCount += 1;
|
|
2284
|
+
void this.runTaskDispatch(nextTask);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
async runTaskDispatch(task) {
|
|
2288
|
+
try {
|
|
2289
|
+
await this.emitAsync("task.dispatch", task);
|
|
2290
|
+
} catch (err) {
|
|
2291
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
2292
|
+
this.emit("error", error);
|
|
2293
|
+
} finally {
|
|
2294
|
+
this.activeTaskDispatchCount = Math.max(0, this.activeTaskDispatchCount - 1);
|
|
2295
|
+
this.drainTaskDispatchQueue();
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2249
2298
|
getStreamAppendQueue(streamId) {
|
|
2250
2299
|
let queue = this.streamAppendQueues.get(streamId);
|
|
2251
2300
|
if (!queue) {
|
|
@@ -2706,6 +2755,24 @@ function isSyntheticPendingKey(taskKey) {
|
|
|
2706
2755
|
function isTaskAwaitingHelpReply(task) {
|
|
2707
2756
|
return task.awaitingHelpReply === true;
|
|
2708
2757
|
}
|
|
2758
|
+
function isConversationalTask(task) {
|
|
2759
|
+
return task.dispatchContext?.source === "feishu";
|
|
2760
|
+
}
|
|
2761
|
+
function firstDispatchContextValue(context, keys) {
|
|
2762
|
+
if (!context)
|
|
2763
|
+
return void 0;
|
|
2764
|
+
for (const key of keys) {
|
|
2765
|
+
const value = context[key]?.trim();
|
|
2766
|
+
if (value)
|
|
2767
|
+
return value;
|
|
2768
|
+
}
|
|
2769
|
+
return void 0;
|
|
2770
|
+
}
|
|
2771
|
+
function threadAlreadyTerminal(events) {
|
|
2772
|
+
return (events ?? []).some(
|
|
2773
|
+
(event) => event.intent === "task.result" || event.intent === "task.cancel"
|
|
2774
|
+
);
|
|
2775
|
+
}
|
|
2709
2776
|
function isActionablePendingTask(taskKey, task) {
|
|
2710
2777
|
return !isSyntheticPendingKey(taskKey) && !isTaskAwaitingHelpReply(task);
|
|
2711
2778
|
}
|
|
@@ -2747,12 +2814,36 @@ function buildOpenClawMainSessionKey(mainKey, config) {
|
|
|
2747
2814
|
function buildAampConversationSessionKey(value, config) {
|
|
2748
2815
|
return buildOpenClawMainSessionKey(`${AAMP_SESSION_PREFIX}default:${value}`, config);
|
|
2749
2816
|
}
|
|
2817
|
+
function buildAampStickySessionKey(dispatchContext, config) {
|
|
2818
|
+
const stickyValue = firstDispatchContextValue(dispatchContext, ["session_key", "conversation_key", "thread_key"]);
|
|
2819
|
+
if (!stickyValue)
|
|
2820
|
+
return void 0;
|
|
2821
|
+
return buildAampConversationSessionKey(`session:${stickyValue}`, config);
|
|
2822
|
+
}
|
|
2750
2823
|
function buildAampTaskSessionKey(taskId, config) {
|
|
2751
2824
|
return buildAampConversationSessionKey(`task:${taskId}`, config);
|
|
2752
2825
|
}
|
|
2753
2826
|
function buildAampWakeSessionKey(kind, id) {
|
|
2754
2827
|
return `${AAMP_SESSION_PREFIX}wake:${kind}:${id}`;
|
|
2755
2828
|
}
|
|
2829
|
+
function buildSessionKeyForPendingTask(task, config) {
|
|
2830
|
+
return buildAampStickySessionKey(task.dispatchContext, config) ?? buildAampTaskSessionKey(task.taskId, config);
|
|
2831
|
+
}
|
|
2832
|
+
function buildWakeSessionKeyForPendingTask(task, config) {
|
|
2833
|
+
return buildAampStickySessionKey(task.dispatchContext, config) ?? buildAampWakeSessionKey("task", task.taskId);
|
|
2834
|
+
}
|
|
2835
|
+
function findPendingEntryForSession(sessionKey, config) {
|
|
2836
|
+
if (typeof sessionKey !== "string" || !isAampSessionKey(sessionKey))
|
|
2837
|
+
return void 0;
|
|
2838
|
+
const requested = buildOpenClawMainSessionKey(stripOpenClawAgentScope(sessionKey), config);
|
|
2839
|
+
const entries = [...pendingTasks.entries()].filter(([key, task]) => isActionablePendingTask(key, task)).filter(([, task]) => buildSessionKeyForPendingTask(task, config) === requested).sort((a, b) => {
|
|
2840
|
+
const rankDiff = priorityRank(a[1].priority) - priorityRank(b[1].priority);
|
|
2841
|
+
if (rankDiff !== 0)
|
|
2842
|
+
return rankDiff;
|
|
2843
|
+
return new Date(a[1].receivedAt).getTime() - new Date(b[1].receivedAt).getTime();
|
|
2844
|
+
});
|
|
2845
|
+
return entries[0];
|
|
2846
|
+
}
|
|
2756
2847
|
function resolvePendingKeyFromSessionKey(sessionKey) {
|
|
2757
2848
|
if (typeof sessionKey !== "string")
|
|
2758
2849
|
return void 0;
|
|
@@ -2843,6 +2934,7 @@ function queuePendingTask(task) {
|
|
|
2843
2934
|
from: task.from,
|
|
2844
2935
|
title: task.title,
|
|
2845
2936
|
bodyText: task.bodyText ?? "",
|
|
2937
|
+
dispatchContext: task.dispatchContext,
|
|
2846
2938
|
threadHistory: task.threadHistory ?? [],
|
|
2847
2939
|
threadContextText: task.threadContextText ?? "",
|
|
2848
2940
|
priority: task.priority ?? "normal",
|
|
@@ -3025,8 +3117,8 @@ var src_default = {
|
|
|
3025
3117
|
api.logger.info(`[AAMP] Directory profile synced${cardText ? " (card text registered)" : ""}`);
|
|
3026
3118
|
}
|
|
3027
3119
|
function wakeAgentForPendingTask(task) {
|
|
3028
|
-
const fallbackSessionKey =
|
|
3029
|
-
const openClawSessionKey =
|
|
3120
|
+
const fallbackSessionKey = buildWakeSessionKeyForPendingTask(task, api.config);
|
|
3121
|
+
const openClawSessionKey = buildSessionKeyForPendingTask(task, api.config);
|
|
3030
3122
|
const fallback = () => triggerHeartbeatWake(fallbackSessionKey, `task ${task.taskId}`);
|
|
3031
3123
|
const dispatcher = channelRuntime?.reply?.dispatchReplyWithBufferedBlockDispatcher;
|
|
3032
3124
|
api.logger.info(
|
|
@@ -3112,13 +3204,14 @@ var src_default = {
|
|
|
3112
3204
|
email: identity.email,
|
|
3113
3205
|
smtpPassword: identity.smtpPassword,
|
|
3114
3206
|
baseUrl: base,
|
|
3207
|
+
taskDispatchConcurrency: cfg.taskDispatchConcurrency,
|
|
3115
3208
|
// Local/dev: management-service proxy uses plain HTTP, no TLS cert to verify.
|
|
3116
3209
|
// Production: set to true when using wss:// with valid certs.
|
|
3117
3210
|
rejectUnauthorized: false
|
|
3118
3211
|
});
|
|
3119
3212
|
aampClient.on("task.dispatch", (task) => {
|
|
3120
3213
|
api.logger.info(`[AAMP] \u2190 task.dispatch ${task.taskId} "${task.title}" from=${task.from}`);
|
|
3121
|
-
|
|
3214
|
+
return (async () => {
|
|
3122
3215
|
try {
|
|
3123
3216
|
if (terminalTaskIds.has(task.taskId)) {
|
|
3124
3217
|
api.logger.info(`[AAMP] Skipping already-terminal task ${task.taskId}`);
|
|
@@ -3146,6 +3239,11 @@ var src_default = {
|
|
|
3146
3239
|
threadContextText: ""
|
|
3147
3240
|
};
|
|
3148
3241
|
});
|
|
3242
|
+
if (threadAlreadyTerminal(hydratedTask.threadHistory)) {
|
|
3243
|
+
rememberTerminalTask(task.taskId);
|
|
3244
|
+
api.logger.info(`[AAMP] Skipping historical task ${task.taskId} because the thread already reached a terminal state`);
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3149
3247
|
if (!queuePendingTask(hydratedTask)) {
|
|
3150
3248
|
api.logger.info(`[AAMP] Ignoring already-terminal or expired task ${task.taskId}`);
|
|
3151
3249
|
return;
|
|
@@ -3157,7 +3255,7 @@ var src_default = {
|
|
|
3157
3255
|
} catch (err) {
|
|
3158
3256
|
api.logger.error(`[AAMP] task.dispatch handler failed for ${task.taskId}: ${err.message}`);
|
|
3159
3257
|
if (pendingTasks.has(task.taskId)) {
|
|
3160
|
-
triggerHeartbeatWake(
|
|
3258
|
+
triggerHeartbeatWake(buildWakeSessionKeyForPendingTask(pendingTasks.get(task.taskId), api.config), `task ${task.taskId}`);
|
|
3161
3259
|
}
|
|
3162
3260
|
}
|
|
3163
3261
|
})();
|
|
@@ -3557,7 +3655,8 @@ ${notifyBody?.bodyText ?? help.question}`;
|
|
|
3557
3655
|
}
|
|
3558
3656
|
return [targetedPendingKey, targetedTask];
|
|
3559
3657
|
})() : void 0;
|
|
3560
|
-
const
|
|
3658
|
+
const sessionScopedEntry = targetedPendingKey ? void 0 : findPendingEntryForSession(ctx?.sessionKey, api.config);
|
|
3659
|
+
const nextEntry = targetedPendingKey ? targetedEntry : sessionScopedEntry ?? nextPendingEntry();
|
|
3561
3660
|
if (!nextEntry)
|
|
3562
3661
|
return {};
|
|
3563
3662
|
const [taskKey, task] = nextEntry;
|
|
@@ -3592,19 +3691,45 @@ ${notifyBody?.bodyText ?? help.question}`;
|
|
|
3592
3691
|
` Example: attachments: [{ filename: "file.html", path: "/tmp/aamp-files/file.html" }]`
|
|
3593
3692
|
] : []
|
|
3594
3693
|
].join("\n") : "";
|
|
3595
|
-
const
|
|
3596
|
-
|
|
3694
|
+
const dispatchContextLines = task.dispatchContext && Object.keys(task.dispatchContext).length > 0 ? `Dispatch Context:
|
|
3695
|
+
${Object.entries(task.dispatchContext).map(([key, value]) => ` - ${key}: ${value}`).join("\n")}` : "";
|
|
3696
|
+
const taskPromptLines = isConversationalTask(task) ? [
|
|
3697
|
+
`## Pending AAMP Conversation Turn`,
|
|
3597
3698
|
``,
|
|
3598
|
-
`
|
|
3599
|
-
`
|
|
3699
|
+
`This AAMP task came from a chat surface (${task.dispatchContext?.source ?? "unknown"}).`,
|
|
3700
|
+
`Treat it as an ongoing conversation turn, not a one-off work order.`,
|
|
3701
|
+
`Your job is to reply naturally to the user's latest message and keep the conversation moving.`,
|
|
3702
|
+
``,
|
|
3703
|
+
`### Tool selection rules for chat turns:`,
|
|
3704
|
+
``,
|
|
3705
|
+
`Use aamp_send_result for normal conversation replies, including:`,
|
|
3706
|
+
` - greetings, acknowledgements, and small talk ("hi", "hello", "thanks", "got it")`,
|
|
3707
|
+
` - short follow-up questions that help narrow the user's intent`,
|
|
3708
|
+
` - direct answers, suggestions, or next-step guidance`,
|
|
3709
|
+
``,
|
|
3710
|
+
`Use aamp_send_help ONLY when you are truly blocked and cannot produce a meaningful`,
|
|
3711
|
+
`reply without waiting for specific missing information from the human.`,
|
|
3712
|
+
`Do NOT use aamp_send_help just because the message is brief or casual.`,
|
|
3713
|
+
``,
|
|
3714
|
+
`IMPORTANT: For conversational traffic, replying to "hi" with a natural greeting and an`,
|
|
3715
|
+
`offer to help is CORRECT. Do not reject greetings as invalid tasks.`,
|
|
3716
|
+
``,
|
|
3717
|
+
`### Sub-task dispatch rules:`,
|
|
3718
|
+
`If you delegate work to another agent via aamp_dispatch_task, you MUST pass`,
|
|
3719
|
+
`parentTaskId: "${task.taskId}" to establish the parent-child relationship.`,
|
|
3720
|
+
`If you need to find a suitable agent first, call aamp_directory_search.`,
|
|
3600
3721
|
``,
|
|
3601
3722
|
`Task ID: ${task.taskId}`,
|
|
3602
|
-
`Priority: ${task.priority}`,
|
|
3603
3723
|
`From: ${task.from}`,
|
|
3604
3724
|
`Title: ${task.title}`,
|
|
3605
|
-
|
|
3725
|
+
dispatchContextLines,
|
|
3726
|
+
task.threadContextText ? `${task.threadContextText}` : "",
|
|
3727
|
+
task.bodyText ? `Latest user message:
|
|
3606
3728
|
${task.bodyText}` : "",
|
|
3607
|
-
|
|
3729
|
+
task.contextLinks.length ? `Context Links:
|
|
3730
|
+
${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
|
|
3731
|
+
task.expiresAt ? `Expires: ${task.expiresAt}` : `Expires: none`,
|
|
3732
|
+
`Received: ${task.receivedAt}`,
|
|
3608
3733
|
otherActionableTasks.length > 0 ? `
|
|
3609
3734
|
(+${otherActionableTasks.length} more tasks queued)` : ""
|
|
3610
3735
|
] : [
|
|
@@ -3639,6 +3764,7 @@ ${task.bodyText}` : "",
|
|
|
3639
3764
|
`Task ID: ${task.taskId}`,
|
|
3640
3765
|
`From: ${task.from}`,
|
|
3641
3766
|
`Title: ${task.title}`,
|
|
3767
|
+
dispatchContextLines,
|
|
3642
3768
|
task.threadContextText ? `${task.threadContextText}` : "",
|
|
3643
3769
|
task.bodyText ? `Description:
|
|
3644
3770
|
${task.bodyText}` : "",
|
|
@@ -3648,7 +3774,23 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
|
|
|
3648
3774
|
`Received: ${task.receivedAt}`,
|
|
3649
3775
|
otherActionableTasks.length > 0 ? `
|
|
3650
3776
|
(+${otherActionableTasks.length} more tasks queued)` : ""
|
|
3651
|
-
]
|
|
3777
|
+
];
|
|
3778
|
+
const lines = isNotification ? [
|
|
3779
|
+
`## Sub-task Update`,
|
|
3780
|
+
``,
|
|
3781
|
+
`A sub-task you dispatched has returned a result. Review the information below.`,
|
|
3782
|
+
`If the sub-task included attachments, use aamp_download_attachment to fetch them.`,
|
|
3783
|
+
``,
|
|
3784
|
+
`Task ID: ${task.taskId}`,
|
|
3785
|
+
`Priority: ${task.priority}`,
|
|
3786
|
+
`From: ${task.from}`,
|
|
3787
|
+
`Title: ${task.title}`,
|
|
3788
|
+
task.bodyText ? `
|
|
3789
|
+
${task.bodyText}` : "",
|
|
3790
|
+
actionRequiredSection,
|
|
3791
|
+
otherActionableTasks.length > 0 ? `
|
|
3792
|
+
(+${otherActionableTasks.length} more tasks queued)` : ""
|
|
3793
|
+
] : taskPromptLines.filter(Boolean).join("\n");
|
|
3652
3794
|
return { prependContext: lines };
|
|
3653
3795
|
},
|
|
3654
3796
|
{ priority: 5 }
|