shipmyagent 1.0.217 → 1.0.229
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/bin/main/agent/Agent.d.ts.map +1 -1
- package/bin/main/agent/Agent.js +237 -114
- package/bin/main/agent/Agent.js.map +1 -1
- package/bin/main/model/CreateModel.d.ts.map +1 -1
- package/bin/main/model/CreateModel.js +11 -1
- package/bin/main/model/CreateModel.js.map +1 -1
- package/bin/main/tools/shell/Tool.d.ts.map +1 -1
- package/bin/main/tools/shell/Tool.js +49 -3
- package/bin/main/tools/shell/Tool.js.map +1 -1
- package/bin/services/chat/Action.d.ts.map +1 -1
- package/bin/services/chat/Action.js +51 -5
- package/bin/services/chat/Action.js.map +1 -1
- package/bin/services/chat/Index.d.ts.map +1 -1
- package/bin/services/chat/Index.js +12 -0
- package/bin/services/chat/Index.js.map +1 -1
- package/bin/services/chat/PROMPT.direct.txt +29 -17
- package/bin/services/chat/PROMPT.txt +1 -1
- package/bin/services/chat/channels/feishu/PROMPT.direct.txt +1 -1
- package/bin/services/chat/channels/qq/PROMPT.direct.txt +1 -1
- package/bin/services/chat/channels/telegram/PROMPT.direct.txt +2 -2
- package/bin/services/chat/runtime/ChatQueueWorker.d.ts.map +1 -1
- package/bin/services/chat/runtime/ChatQueueWorker.js +110 -7
- package/bin/services/chat/runtime/ChatQueueWorker.js.map +1 -1
- package/bin/services/chat/runtime/ChatSendRegistry.d.ts.map +1 -1
- package/bin/services/chat/runtime/ChatSendRegistry.js +9 -0
- package/bin/services/chat/runtime/ChatSendRegistry.js.map +1 -1
- package/bin/services/chat/runtime/ChatkeySend.d.ts.map +1 -1
- package/bin/services/chat/runtime/ChatkeySend.js +2 -1
- package/bin/services/chat/runtime/ChatkeySend.js.map +1 -1
- package/bin/services/chat/runtime/DirectDispatchParser.d.ts.map +1 -1
- package/bin/services/chat/runtime/DirectDispatchParser.js +136 -133
- package/bin/services/chat/runtime/DirectDispatchParser.js.map +1 -1
- package/bin/services/chat/types/DirectDispatch.d.ts.map +1 -1
- package/bin/services/memory/runtime/Extractor.d.ts.map +1 -1
- package/bin/services/memory/runtime/Extractor.js +19 -1
- package/bin/services/memory/runtime/Extractor.js.map +1 -1
- package/bin/services/memory/runtime/Manager.d.ts.map +1 -1
- package/bin/services/memory/runtime/Manager.js +14 -2
- package/bin/services/memory/runtime/Manager.js.map +1 -1
- package/bin/services/memory/runtime/Service.d.ts.map +1 -1
- package/bin/services/memory/runtime/Service.js +11 -7
- package/bin/services/memory/runtime/Service.js.map +1 -1
- package/bin/services/task/Action.d.ts.map +1 -1
- package/bin/services/task/Action.js +37 -14
- package/bin/services/task/Action.js.map +1 -1
- package/bin/services/task/Index.d.ts.map +1 -1
- package/bin/services/task/Index.js +121 -16
- package/bin/services/task/Index.js.map +1 -1
- package/bin/services/task/PROMPT.txt +9 -4
- package/bin/services/task/Scheduler.d.ts.map +1 -1
- package/bin/services/task/Scheduler.js +14 -19
- package/bin/services/task/Scheduler.js.map +1 -1
- package/bin/services/task/runtime/Model.d.ts.map +1 -1
- package/bin/services/task/runtime/Model.js +140 -9
- package/bin/services/task/runtime/Model.js.map +1 -1
- package/bin/services/task/runtime/Paths.d.ts.map +1 -1
- package/bin/services/task/runtime/Paths.js +8 -7
- package/bin/services/task/runtime/Paths.js.map +1 -1
- package/bin/services/task/types/Task.d.ts.map +1 -1
- package/bin/utils/logger/Fetch.d.ts.map +1 -1
- package/bin/utils/logger/Fetch.js +1 -5
- package/bin/utils/logger/Fetch.js.map +1 -1
- package/bin/utils/logger/Format.d.ts.map +1 -1
- package/bin/utils/logger/Format.js +221 -105
- package/bin/utils/logger/Format.js.map +1 -1
- package/bin/utils/logger/Logger.d.ts.map +1 -1
- package/bin/utils/logger/Logger.js +106 -24
- package/bin/utils/logger/Logger.js.map +1 -1
- package/package.json +1 -1
- package/src/services/chat/PROMPT.direct.txt +29 -17
- package/src/services/chat/PROMPT.txt +1 -1
- package/src/services/chat/channels/feishu/PROMPT.direct.txt +1 -1
- package/src/services/chat/channels/qq/PROMPT.direct.txt +1 -1
- package/src/services/chat/channels/telegram/PROMPT.direct.txt +2 -2
- package/src/services/task/PROMPT.txt +9 -4
- package/test/chat/direct-delay-nonblocking.test.mjs +112 -0
- package/test/chat/direct-dispatch-parser.test.mjs +43 -0
- package/test/chat/send-time-timezone-parse.test.mjs +47 -0
- package/test/task/schedule-timezone-validation.test.mjs +76 -0
|
@@ -11,8 +11,30 @@ import { getLogsDirPath } from "../../main/server/env/Paths.js";
|
|
|
11
11
|
import { getTimestamp } from "../../main/utils/Time.js";
|
|
12
12
|
const ANSI_RESET = "\x1b[0m";
|
|
13
13
|
const ANSI_BOLD = "\x1b[1m";
|
|
14
|
-
const ANSI_DIM = "\x1b[2m";
|
|
15
14
|
const TASK_BRACKET_COLOR = "\x1b[95m"; // bright magenta
|
|
15
|
+
const ALLOWED_MESSAGE_LABELS = new Set([
|
|
16
|
+
"main",
|
|
17
|
+
"system",
|
|
18
|
+
"user",
|
|
19
|
+
"assistant",
|
|
20
|
+
"tool",
|
|
21
|
+
"tool_result",
|
|
22
|
+
"agent",
|
|
23
|
+
]);
|
|
24
|
+
const FIXED_BRACKET_COLORS = {
|
|
25
|
+
INFO: "\x1b[36m", // cyan
|
|
26
|
+
WARN: "\x1b[33m", // yellow
|
|
27
|
+
ERROR: "\x1b[31m", // red
|
|
28
|
+
DEBUG: "\x1b[90m", // gray
|
|
29
|
+
ACTION: "\x1b[96m", // bright cyan
|
|
30
|
+
SYSTEM: "\x1b[90m", // gray
|
|
31
|
+
USER: "\x1b[33m", // yellow
|
|
32
|
+
ASSISTANT: "\x1b[32m", // green
|
|
33
|
+
TOOL: "\x1b[95m", // bright magenta
|
|
34
|
+
TOOL_RESULT: "\x1b[34m", // blue
|
|
35
|
+
AGENT: "\x1b[36m", // cyan
|
|
36
|
+
MAIN: "\x1b[90m", // gray
|
|
37
|
+
};
|
|
16
38
|
const BRACKET_COLOR_PALETTE = [
|
|
17
39
|
"\x1b[36m", // cyan
|
|
18
40
|
"\x1b[32m", // green
|
|
@@ -41,26 +63,87 @@ function hashText(input) {
|
|
|
41
63
|
}
|
|
42
64
|
function colorizeBracketedPrefix(label) {
|
|
43
65
|
const normalized = String(label || "").trim().toUpperCase();
|
|
66
|
+
const baseLabel = normalized.split(/[.\s]+/)[0].replace(/[^A-Z0-9_]/g, "");
|
|
44
67
|
// 关键点(中文):task 日志标签使用固定高可见色,避免随机色导致辨识度不稳定。
|
|
45
68
|
if (normalized === "TASK") {
|
|
46
69
|
return `${TASK_BRACKET_COLOR}[${label}]${ANSI_RESET}`;
|
|
47
70
|
}
|
|
71
|
+
const fixedColor = FIXED_BRACKET_COLORS[normalized] || FIXED_BRACKET_COLORS[baseLabel];
|
|
72
|
+
if (fixedColor) {
|
|
73
|
+
return `${fixedColor}[${label}]${ANSI_RESET}`;
|
|
74
|
+
}
|
|
48
75
|
const color = BRACKET_COLOR_PALETTE[hashText(label) % BRACKET_COLOR_PALETTE.length];
|
|
49
76
|
return `${color}[${label}]${ANSI_RESET}`;
|
|
50
77
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
function colorizeMessageBracketPrefixes(message) {
|
|
79
|
+
// 关键点(中文):紧凑单行展示时,给消息中的标签 token 着色,正文保持原样。
|
|
80
|
+
return String(message || "").replace(/\[([^\]\n]+)\]/g, (full, label) => {
|
|
81
|
+
const normalized = String(label || "").trim();
|
|
82
|
+
const base = normalized
|
|
83
|
+
.toUpperCase()
|
|
84
|
+
.split(/[.\s:]+/)[0]
|
|
85
|
+
.replace(/[^A-Z0-9_]/g, "");
|
|
86
|
+
if (!base || !/[A-Z]/.test(base))
|
|
87
|
+
return full;
|
|
88
|
+
return colorizeBracketedPrefix(normalized);
|
|
56
89
|
});
|
|
57
90
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
91
|
+
/**
|
|
92
|
+
* 压缩日志为“可分段的紧凑展示”。
|
|
93
|
+
*
|
|
94
|
+
* 关键点(中文)
|
|
95
|
+
* - 段内换行转义为字面量 `\n`,避免一条消息占多行。
|
|
96
|
+
* - 段与段之间保留真实换行,确保不同 message 可清晰区分。
|
|
97
|
+
*/
|
|
98
|
+
function compactMessageForConsole(message) {
|
|
99
|
+
const normalized = String(message || "").replace(/\r\n/g, "\n");
|
|
100
|
+
const blocks = normalized
|
|
101
|
+
.split(/\n{2,}/)
|
|
102
|
+
.map((block) => String(block || "").replace(/\n/g, "\\n").trim())
|
|
103
|
+
.filter(Boolean);
|
|
104
|
+
if (blocks.length === 0)
|
|
105
|
+
return "";
|
|
106
|
+
return blocks.join("\n");
|
|
107
|
+
}
|
|
108
|
+
function normalizeToAllowedMessageLabels(message) {
|
|
109
|
+
const normalizedLines = [];
|
|
110
|
+
let activeLabel = null;
|
|
111
|
+
for (const rawLine of String(message || "").split("\n")) {
|
|
112
|
+
const line = String(rawLine || "");
|
|
113
|
+
const trimmed = line.trim();
|
|
114
|
+
if (!trimmed) {
|
|
115
|
+
normalizedLines.push("");
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const matched = trimmed.match(/^\[([^\]]+)\]\s*:?\s*(.*)$/);
|
|
119
|
+
if (matched) {
|
|
120
|
+
const rawLabel = String(matched[1] || "").trim();
|
|
121
|
+
const lowerLabel = rawLabel.toLowerCase();
|
|
122
|
+
const labelToken = lowerLabel.split(/\s+/)[0] || "";
|
|
123
|
+
const baseLabel = labelToken.split(".")[0];
|
|
124
|
+
const content = String(matched[2] || "").trim();
|
|
125
|
+
if (ALLOWED_MESSAGE_LABELS.has(baseLabel)) {
|
|
126
|
+
activeLabel = baseLabel;
|
|
127
|
+
const attrsRaw = rawLabel.slice(labelToken.length).trim();
|
|
128
|
+
const normalizedLabel = attrsRaw ? `${baseLabel} ${attrsRaw}` : baseLabel;
|
|
129
|
+
normalizedLines.push(`[${normalizedLabel}]${content ? ` ${content}` : ""}`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
activeLabel = "main";
|
|
133
|
+
normalizedLines.push(`[main] ${content || trimmed}`);
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (activeLabel) {
|
|
138
|
+
// 关键点(中文):同一消息块的续行不重复打标签。
|
|
139
|
+
normalizedLines.push(line);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
normalizedLines.push(`[main] ${trimmed}`);
|
|
143
|
+
}
|
|
144
|
+
if (normalizedLines.length === 0)
|
|
145
|
+
return "[main] -";
|
|
146
|
+
return normalizedLines.join("\n");
|
|
64
147
|
}
|
|
65
148
|
/**
|
|
66
149
|
* Logger:项目级日志器。
|
|
@@ -136,7 +219,7 @@ export class Logger {
|
|
|
136
219
|
id: this.generateId(),
|
|
137
220
|
timestamp: getTimestamp(),
|
|
138
221
|
type,
|
|
139
|
-
message,
|
|
222
|
+
message: normalizeToAllowedMessageLabels(message),
|
|
140
223
|
details: normalizeLogDetails(details),
|
|
141
224
|
level: type,
|
|
142
225
|
};
|
|
@@ -152,26 +235,25 @@ export class Logger {
|
|
|
152
235
|
await this.writeChain;
|
|
153
236
|
}
|
|
154
237
|
printLog(entry) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
const message = `${timestampToken} ${levelToken} ${body}`;
|
|
238
|
+
// 关键点(中文):控制台输出不展示前置时间戳,减少聊天链路日志噪音。
|
|
239
|
+
// 时间信息仍保存在 JSONL 落盘字段 `timestamp` 中。
|
|
240
|
+
const compactBody = compactMessageForConsole(entry.message);
|
|
241
|
+
const body = colorizeMessageBracketPrefixes(compactBody);
|
|
242
|
+
const message = body;
|
|
161
243
|
switch (entry.type) {
|
|
162
244
|
case "error":
|
|
163
|
-
console.error(
|
|
245
|
+
console.error(message);
|
|
164
246
|
break;
|
|
165
247
|
case "warn":
|
|
166
|
-
console.warn(
|
|
248
|
+
console.warn(message);
|
|
167
249
|
break;
|
|
168
250
|
case "debug":
|
|
169
251
|
if (this.logLevel === "debug") {
|
|
170
|
-
console.log(
|
|
252
|
+
console.log(message);
|
|
171
253
|
}
|
|
172
254
|
break;
|
|
173
255
|
case "action":
|
|
174
|
-
console.log(
|
|
256
|
+
console.log(message);
|
|
175
257
|
break;
|
|
176
258
|
default:
|
|
177
259
|
console.log(message);
|
|
@@ -186,7 +268,7 @@ export class Logger {
|
|
|
186
268
|
if (!this.projectRoot)
|
|
187
269
|
return;
|
|
188
270
|
const logsDir = getLogsDirPath(this.projectRoot);
|
|
189
|
-
const date = new Date().toISOString().
|
|
271
|
+
const date = String(entry.timestamp || "").slice(0, 10) || new Date().toISOString().slice(0, 10);
|
|
190
272
|
const logFile = path.join(logsDir, `${date}.jsonl`);
|
|
191
273
|
const logLine = JSON.stringify(entry) + "\n";
|
|
192
274
|
await fs.ensureDir(logsDir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../../src/utils/logger/Logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOnD,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,SAAS,GAAG,SAAS,CAAC;AAC5B,MAAM,
|
|
1
|
+
{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../../src/utils/logger/Logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -9,36 +9,48 @@
|
|
|
9
9
|
- 当前会话会自动投递 assistant 文本(按 step 分条发送),不需要通过 CLI 发送。
|
|
10
10
|
- 保持简洁清晰,避免连续空行(`\n\n`)。
|
|
11
11
|
|
|
12
|
-
##
|
|
13
|
-
|
|
12
|
+
## 参数传递协议(frontmatter metadata,可选)
|
|
13
|
+
控制参数统一放在文本顶部 frontmatter;正文继续写自然语言。
|
|
14
|
+
除附件外,不要再使用尖括号控制标签。
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
16
|
+
可用 metadata 字段(可选):
|
|
17
|
+
- `chatKey`:覆盖主文本目标会话(跨会话发送)。
|
|
18
|
+
- `reply`:目标 `message_id`(推荐)。设置后按该消息 ID reply。
|
|
19
|
+
- `message_id` / `messageId`:目标 `message_id`(与 `reply` 等价,二选一)。
|
|
20
|
+
- `react` / `reactions`:发送表情反应。
|
|
21
|
+
- 单个字符串:`react: "👍"`
|
|
22
|
+
- 或对象/数组:`emoji/chatKey/messageId/big`
|
|
23
|
+
- 若未提供 `messageId`,会默认复用 `reply/message_id`(同一 chatKey)。
|
|
24
|
+
- 附件仍使用 `<file type="...">path</file>`(支持 `document/photo/voice/audio`)。
|
|
21
25
|
|
|
22
|
-
示例(跨对话 +
|
|
26
|
+
示例(跨对话 + reply + 附件 + react):
|
|
23
27
|
```text
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
---
|
|
29
|
+
chatKey: telegram-chat-123
|
|
30
|
+
reply: "128"
|
|
31
|
+
react:
|
|
32
|
+
- emoji: "✅"
|
|
33
|
+
messageId: "128"
|
|
34
|
+
---
|
|
27
35
|
这是给用户的正文。
|
|
28
36
|
<file type="document">reports/daily.md</file>
|
|
29
|
-
<react messageId="128">✅</react>
|
|
30
37
|
```
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
示例(普通当前会话回复,无 metadata):
|
|
33
40
|
```text
|
|
34
41
|
已完成修复,核心原因是缓存键冲突,现已改为按会话隔离。
|
|
35
42
|
```
|
|
36
43
|
|
|
37
44
|
协议约束:
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
45
|
+
- 只有需要控制参数时才写 frontmatter;普通回复只写正文。
|
|
46
|
+
- frontmatter 必须位于文本最开头(`---` 包裹)。
|
|
47
|
+
- `reply` 的值必须是目标 `message_id`(字符串或数字),不要写 `reply: true/false`。
|
|
48
|
+
- 除 `<file>` 附件标签外,不要使用尖括号格式做控制参数。
|
|
41
49
|
- 不要输出 JSON 或 CLI 命令作为用户可见内容。
|
|
50
|
+
- direct metadata 不支持 `delay/time/sendAt`。
|
|
51
|
+
- 需要延迟或定时发送时,使用 shell 执行 `sma chat send`:
|
|
52
|
+
- `sma chat send --text '...' --delay <ms>`
|
|
53
|
+
- `sma chat send --text '...' --time '<ISO|timestamp>'`
|
|
42
54
|
|
|
43
55
|
## 跨协议/跨平台操作
|
|
44
56
|
- 当任务需要跨会话、跨平台或复杂路由,允许使用 `sma chat`。
|
|
@@ -67,7 +67,7 @@ User-facing output rules:
|
|
|
67
67
|
- 不要滥用 `--reply`:仅在消息密集、需要把回复明确挂到某条消息时再使用。
|
|
68
68
|
- 默认使用单引号包裹 `--text`,例如:`sma chat send --text '你好'`。
|
|
69
69
|
- `send` 支持 `--delay <ms>`(非负整数,毫秒)用于延迟发送。
|
|
70
|
-
- `send` 支持 `--time <time>`(Unix 时间戳秒/毫秒或 ISO
|
|
70
|
+
- `send` 支持 `--time <time>`(Unix 时间戳秒/毫秒或 ISO 时间;ISO 必须显式时区)用于定时发送。
|
|
71
71
|
- `--delay` 与 `--time` 互斥,不能同时传。
|
|
72
72
|
- 多行正文使用 heredoc 管道到 stdin,例如:
|
|
73
73
|
`cat <<'EOF' | sma chat send --stdin --chat-key <chatKey>`
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
## 使用约束
|
|
8
8
|
- 在 direct 模式下,你输出的 assistant 文本会自动发送到当前飞书会话。
|
|
9
9
|
- 群聊可配置管理员白名单(`adminUserIds`);若群内无法触发,优先检查发起人/管理员权限策略。
|
|
10
|
-
-
|
|
10
|
+
- 如需跨会话发送,请在 frontmatter metadata 中设置 `chatKey` 覆盖目标会话。
|
|
11
11
|
- 自动发送时由适配器按 chat 类型选择回复模式(群聊优先 reply、私聊直发)。
|
|
12
12
|
- 平台事件可能重复投递,适配器会做去重;业务层无需重复实现去重逻辑。
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
## 使用约束
|
|
8
8
|
- 在 direct 模式下,你输出的 assistant 文本会自动发送到当前 QQ 会话。
|
|
9
|
-
- QQ 出站回复依赖入站消息上下文(如 `chatType` 与 `messageId`);跨 context
|
|
9
|
+
- QQ 出站回复依赖入站消息上下文(如 `chatType` 与 `messageId`);跨 context 发送时,frontmatter metadata 的 `chatKey` 必须可解析到 QQ 元信息。
|
|
10
10
|
- 群聊权限支持 `anyone` 与 `initiator_or_admin` 两种门禁策略;群内无响应时优先检查该配置。
|
|
11
11
|
- 适配器会过滤机器人自回环消息并做入站去重,避免“自己回复自己”的循环。
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
## 使用约束
|
|
8
8
|
- 在 direct 模式下,你输出的 assistant 文本会自动发送到当前 Telegram 会话。
|
|
9
9
|
- Telegram 会在入站 `<info>` 中注入 `is_master` 字段(yes/no/unknown);涉及执行权限时应优先遵循 master 指令。
|
|
10
|
-
-
|
|
11
|
-
-
|
|
10
|
+
- 自动发送默认是普通消息;仅当 frontmatter metadata 中提供目标 `message_id`(`reply` 或 `message_id/messageId`,值为消息 ID)时,才会按该消息 reply。
|
|
11
|
+
- 仅在必要场景设置 `reply/message_id`(例如群里消息很多,需要精确指向某条消息);常规回复不要开启。
|
|
12
12
|
- 群聊场景存在权限与 follow-up 窗口策略;如果用户反馈“机器人没响应”,优先检查是否触发群聊权限限制。
|
|
13
13
|
- 旧消息会在启动时执行 pending drain 入队审计,避免重启后补回复历史消息。
|
|
14
14
|
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
|
|
54
54
|
- 常用 action(CLI):
|
|
55
55
|
- `sma task list [--status enabled|paused|disabled]`
|
|
56
|
-
- `sma task create --title '...' --description '...' --context-id <contextId> [--cron '@manual'] [--kind agent|script]`
|
|
56
|
+
- `sma task create --title '...' --description '...' --context-id <contextId> [--cron '@manual'] [--kind agent|script] [--status ...|--activate]`
|
|
57
57
|
- `sma task run <taskId> [--reason '...']`
|
|
58
58
|
- `sma task delete <taskId>`
|
|
59
|
-
- `sma task update <taskId> [--title ...] [--description ...] [--cron ...] [--status
|
|
59
|
+
- `sma task update <taskId> [--title ...] [--description ...] [--cron ...] [--status ...|--activate] [--context-id ...]`
|
|
60
60
|
- `sma task status <taskId> <enabled|paused|disabled>`
|
|
61
61
|
- `sma task enable <taskId>`
|
|
62
62
|
- `sma task disable <taskId>`
|
|
@@ -66,6 +66,10 @@
|
|
|
66
66
|
- 调度重载命令(task lifecycle command):
|
|
67
67
|
- `sma service command task reschedule`
|
|
68
68
|
|
|
69
|
+
关键行为(中文):
|
|
70
|
+
- `create/update/delete/status/enable/disable` 成功后,task scheduler 会自动重载一次(等价于内部执行 `reschedule`)。
|
|
71
|
+
- 仍可手动执行 `sma service command task reschedule` 做显式重载。
|
|
72
|
+
|
|
69
73
|
## 状态语义
|
|
70
74
|
|
|
71
75
|
- `enabled`:可被 cron 调度执行
|
|
@@ -85,7 +89,7 @@
|
|
|
85
89
|
- `contextId`:结果回发目标(必填)
|
|
86
90
|
- `status`:`enabled|paused|disabled`(必填)
|
|
87
91
|
- `kind`:`agent|script`(可选,默认 `agent`)
|
|
88
|
-
- `time`:单次计划时间(可选,ISO8601
|
|
92
|
+
- `time`:单次计划时间(可选,ISO8601 且必须显式时区,如 `2026-03-05T20:00:00+08:00`;到点执行一次)
|
|
89
93
|
|
|
90
94
|
### 可选字段
|
|
91
95
|
- `timezone`:IANA 时区(如 `Asia/Shanghai`)
|
|
@@ -95,7 +99,8 @@
|
|
|
95
99
|
|
|
96
100
|
### 冲突与校验
|
|
97
101
|
- 更新时所有 `set` 与 `clear` 选项必须互斥。
|
|
98
|
-
- `time`
|
|
102
|
+
- `time` 适合“单次计划”;请与 `cron=@manual` 搭配使用。
|
|
103
|
+
- `time` 触发后系统会自动将任务置为 `paused` 并清空 `time`。
|
|
99
104
|
- `kind=script` 时,正文不能为空且应为脚本;不要配置成空 body。
|
|
100
105
|
- `requiredArtifacts` 应是 run 目录内相对路径。
|
|
101
106
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* direct delay 非阻塞调度测试(node:test)。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - `nonBlockingDelay=true` 时应立即返回,实际发送由后台延迟调度。
|
|
6
|
+
* - 默认行为仍应阻塞等待,避免破坏 `chat send` 既有语义。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import { sendChatTextByChatKey } from "../../bin/services/chat/Action.js";
|
|
12
|
+
import {
|
|
13
|
+
getChatSender,
|
|
14
|
+
registerChatSender,
|
|
15
|
+
unregisterChatSender,
|
|
16
|
+
} from "../../bin/services/chat/runtime/ChatSendRegistry.js";
|
|
17
|
+
|
|
18
|
+
const TELEGRAM_CHANNEL = "telegram";
|
|
19
|
+
const CHAT_KEY = "telegram-chat-10001";
|
|
20
|
+
|
|
21
|
+
function buildRuntime() {
|
|
22
|
+
return {
|
|
23
|
+
rootPath: "",
|
|
24
|
+
logger: {
|
|
25
|
+
warn() {},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
test("sendChatTextByChatKey schedules delayed send without blocking when nonBlockingDelay=true", async () => {
|
|
31
|
+
const previous = getChatSender(TELEGRAM_CHANNEL);
|
|
32
|
+
const calls = [];
|
|
33
|
+
registerChatSender(TELEGRAM_CHANNEL, {
|
|
34
|
+
async sendText(payload) {
|
|
35
|
+
calls.push({
|
|
36
|
+
ts: Date.now(),
|
|
37
|
+
payload,
|
|
38
|
+
});
|
|
39
|
+
return { success: true };
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const start = Date.now();
|
|
45
|
+
const result = await sendChatTextByChatKey({
|
|
46
|
+
context: buildRuntime(),
|
|
47
|
+
chatKey: CHAT_KEY,
|
|
48
|
+
text: "delayed-message",
|
|
49
|
+
delayMs: 120,
|
|
50
|
+
nonBlockingDelay: true,
|
|
51
|
+
});
|
|
52
|
+
const elapsedMs = Date.now() - start;
|
|
53
|
+
|
|
54
|
+
assert.equal(result.success, true);
|
|
55
|
+
assert.equal(result.chatKey, CHAT_KEY);
|
|
56
|
+
assert.equal(calls.length, 0);
|
|
57
|
+
assert.ok(
|
|
58
|
+
elapsedMs < 80,
|
|
59
|
+
`expected non-blocking return (<80ms), got ${elapsedMs}ms`,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 180));
|
|
63
|
+
assert.equal(calls.length, 1);
|
|
64
|
+
assert.equal(calls[0].payload.chatId, "10001");
|
|
65
|
+
assert.equal(calls[0].payload.text, "delayed-message");
|
|
66
|
+
} finally {
|
|
67
|
+
if (previous) {
|
|
68
|
+
registerChatSender(TELEGRAM_CHANNEL, previous);
|
|
69
|
+
} else {
|
|
70
|
+
unregisterChatSender(TELEGRAM_CHANNEL);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("sendChatTextByChatKey keeps blocking behavior by default", async () => {
|
|
76
|
+
const previous = getChatSender(TELEGRAM_CHANNEL);
|
|
77
|
+
const calls = [];
|
|
78
|
+
registerChatSender(TELEGRAM_CHANNEL, {
|
|
79
|
+
async sendText(payload) {
|
|
80
|
+
calls.push({
|
|
81
|
+
ts: Date.now(),
|
|
82
|
+
payload,
|
|
83
|
+
});
|
|
84
|
+
return { success: true };
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const start = Date.now();
|
|
90
|
+
const result = await sendChatTextByChatKey({
|
|
91
|
+
context: buildRuntime(),
|
|
92
|
+
chatKey: CHAT_KEY,
|
|
93
|
+
text: "blocking-message",
|
|
94
|
+
delayMs: 120,
|
|
95
|
+
});
|
|
96
|
+
const elapsedMs = Date.now() - start;
|
|
97
|
+
|
|
98
|
+
assert.equal(result.success, true);
|
|
99
|
+
assert.equal(result.chatKey, CHAT_KEY);
|
|
100
|
+
assert.equal(calls.length, 1);
|
|
101
|
+
assert.ok(
|
|
102
|
+
elapsedMs >= 90,
|
|
103
|
+
`expected blocking return (>=90ms), got ${elapsedMs}ms`,
|
|
104
|
+
);
|
|
105
|
+
} finally {
|
|
106
|
+
if (previous) {
|
|
107
|
+
registerChatSender(TELEGRAM_CHANNEL, previous);
|
|
108
|
+
} else {
|
|
109
|
+
unregisterChatSender(TELEGRAM_CHANNEL);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* direct dispatch parser 行为测试(node:test)。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - direct frontmatter 仅支持 `chatKey/reply/message_id/react` 等字段。
|
|
6
|
+
* - `delay/time/sendAt/sendAtMs` 在 direct 模式下应被忽略,不能进入发送参数。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import { parseDirectDispatchAssistantText } from "../../bin/services/chat/runtime/DirectDispatchParser.js";
|
|
12
|
+
|
|
13
|
+
test("parseDirectDispatchAssistantText ignores delay/time metadata in direct mode", () => {
|
|
14
|
+
const plan = parseDirectDispatchAssistantText({
|
|
15
|
+
fallbackChatKey: "telegram-chat-999",
|
|
16
|
+
assistantText: `---
|
|
17
|
+
chatKey: telegram-chat-123
|
|
18
|
+
reply: "556"
|
|
19
|
+
delay: 3000
|
|
20
|
+
time: 2026-03-08T20:00:00+08:00
|
|
21
|
+
sendAt: 2026-03-08T21:00:00+08:00
|
|
22
|
+
sendAtMs: 1767225600000
|
|
23
|
+
react: "👍"
|
|
24
|
+
---
|
|
25
|
+
hello direct`,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
assert.ok(plan, "expected non-null direct dispatch plan");
|
|
29
|
+
assert.ok(plan.text, "expected text plan");
|
|
30
|
+
assert.equal(plan.text.chatKey, "telegram-chat-123");
|
|
31
|
+
assert.equal(plan.text.replyToMessage, true);
|
|
32
|
+
assert.equal(plan.text.messageId, "556");
|
|
33
|
+
assert.equal(plan.text.text, "hello direct");
|
|
34
|
+
|
|
35
|
+
// 关键点(中文):delay/time 字段必须被忽略,避免 direct 自动调度。
|
|
36
|
+
assert.equal(Object.hasOwn(plan.text, "delayMs"), false);
|
|
37
|
+
assert.equal(Object.hasOwn(plan.text, "sendAtMs"), false);
|
|
38
|
+
|
|
39
|
+
assert.equal(plan.reactions.length, 1);
|
|
40
|
+
assert.equal(plan.reactions[0].emoji, "👍");
|
|
41
|
+
assert.equal(plan.reactions[0].chatKey, "telegram-chat-123");
|
|
42
|
+
assert.equal(plan.reactions[0].messageId, "556");
|
|
43
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat send 时间参数解析测试(node:test)。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - `sendAt/sendAtMs/time` 若传 ISO 日期时间,必须带显式时区(`Z` 或 `+08:00`)。
|
|
6
|
+
* - 秒级时间戳会自动归一化为毫秒。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import { chatService } from "../../bin/services/chat/Index.js";
|
|
12
|
+
|
|
13
|
+
async function mapSendApiInput(payload) {
|
|
14
|
+
return chatService.actions.send.api.mapInput({
|
|
15
|
+
req: {
|
|
16
|
+
async json() {
|
|
17
|
+
return payload;
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test("chat send api rejects ISO datetime without timezone", async () => {
|
|
24
|
+
await assert.rejects(
|
|
25
|
+
mapSendApiInput({
|
|
26
|
+
text: "hello",
|
|
27
|
+
sendAt: "2026-03-08T10:30:00",
|
|
28
|
+
}),
|
|
29
|
+
/ISO datetime must include timezone offset/i,
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("chat send api accepts timezone-aware ISO datetime", async () => {
|
|
34
|
+
const result = await mapSendApiInput({
|
|
35
|
+
text: "hello",
|
|
36
|
+
sendAt: "2026-03-08T10:30:00+08:00",
|
|
37
|
+
});
|
|
38
|
+
assert.equal(result.sendAtMs, Date.parse("2026-03-08T10:30:00+08:00"));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("chat send api normalizes second timestamp to milliseconds", async () => {
|
|
42
|
+
const result = await mapSendApiInput({
|
|
43
|
+
text: "hello",
|
|
44
|
+
time: "1767225600",
|
|
45
|
+
});
|
|
46
|
+
assert.equal(result.sendAtMs, 1767225600 * 1000);
|
|
47
|
+
});
|