weacpx 0.5.0 → 0.5.1
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 +13 -5
- package/config.example.json +3 -7
- package/dist/bridge/bridge-main.js +1 -1
- package/dist/channels/types.d.ts +12 -0
- package/dist/cli.js +865 -342
- package/dist/config/types.d.ts +5 -0
- package/dist/orchestration/orchestration-types.d.ts +7 -0
- package/dist/weixin/agent/interface.d.ts +3 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -2317,6 +2317,7 @@ function parseConfig(raw, options = {}) {
|
|
|
2317
2317
|
const channelConfig = parseChannelConfig(channel, legacyWechat);
|
|
2318
2318
|
const channelsConfig = parseRuntimeChannels(raw.channels, channelConfig);
|
|
2319
2319
|
const orchestrationConfig = parseOrchestrationConfig(orchestration);
|
|
2320
|
+
const laterConfig = parseLaterConfig(raw.later);
|
|
2320
2321
|
const plugins = parsePlugins(raw.plugins);
|
|
2321
2322
|
return {
|
|
2322
2323
|
transport: {
|
|
@@ -2348,7 +2349,8 @@ function parseConfig(raw, options = {}) {
|
|
|
2348
2349
|
plugins,
|
|
2349
2350
|
agents,
|
|
2350
2351
|
workspaces,
|
|
2351
|
-
orchestration: orchestrationConfig
|
|
2352
|
+
orchestration: orchestrationConfig,
|
|
2353
|
+
later: laterConfig
|
|
2352
2354
|
};
|
|
2353
2355
|
}
|
|
2354
2356
|
function parsePluginConfig(raw, index) {
|
|
@@ -2437,6 +2439,11 @@ function parseRuntimeChannels(rawChannels, channel) {
|
|
|
2437
2439
|
}
|
|
2438
2440
|
];
|
|
2439
2441
|
}
|
|
2442
|
+
function parseLaterConfig(raw) {
|
|
2443
|
+
if (!isRecord(raw))
|
|
2444
|
+
return { ...DEFAULT_LATER_CONFIG };
|
|
2445
|
+
return raw.defaultMode === "bind" ? { defaultMode: "bind" } : { ...DEFAULT_LATER_CONFIG };
|
|
2446
|
+
}
|
|
2440
2447
|
function parseOrchestrationConfig(raw) {
|
|
2441
2448
|
if (!isRecord(raw)) {
|
|
2442
2449
|
return {
|
|
@@ -2452,7 +2459,7 @@ function parseOrchestrationConfig(raw) {
|
|
|
2452
2459
|
maxParallelTasksPerAgent: typeof raw.maxParallelTasksPerAgent === "number" && Number.isFinite(raw.maxParallelTasksPerAgent) && raw.maxParallelTasksPerAgent >= 1 ? Math.floor(raw.maxParallelTasksPerAgent) : DEFAULT_ORCHESTRATION_CONFIG.maxParallelTasksPerAgent
|
|
2453
2460
|
};
|
|
2454
2461
|
}
|
|
2455
|
-
var DEFAULT_PERF_LOG_CONFIG, DEFAULT_LOGGING_CONFIG, DEFAULT_PERMISSION_MODE = "approve-all", DEFAULT_NON_INTERACTIVE_PERMISSIONS = "deny", DEFAULT_QUEUE_OWNER_TTL_SECONDS = 1800, DEFAULT_CHANNEL_CONFIG, DEFAULT_ORCHESTRATION_CONFIG;
|
|
2462
|
+
var DEFAULT_PERF_LOG_CONFIG, DEFAULT_LOGGING_CONFIG, DEFAULT_PERMISSION_MODE = "approve-all", DEFAULT_NON_INTERACTIVE_PERMISSIONS = "deny", DEFAULT_QUEUE_OWNER_TTL_SECONDS = 1800, DEFAULT_CHANNEL_CONFIG, DEFAULT_ORCHESTRATION_CONFIG, DEFAULT_LATER_CONFIG;
|
|
2456
2463
|
var init_load_config = __esm(() => {
|
|
2457
2464
|
init_workspace_path();
|
|
2458
2465
|
DEFAULT_PERF_LOG_CONFIG = {
|
|
@@ -2480,6 +2487,9 @@ var init_load_config = __esm(() => {
|
|
|
2480
2487
|
progressHeartbeatSeconds: 300,
|
|
2481
2488
|
maxParallelTasksPerAgent: 3
|
|
2482
2489
|
};
|
|
2490
|
+
DEFAULT_LATER_CONFIG = {
|
|
2491
|
+
defaultMode: "temp"
|
|
2492
|
+
};
|
|
2483
2493
|
});
|
|
2484
2494
|
|
|
2485
2495
|
// src/config/config-store.ts
|
|
@@ -2547,6 +2557,15 @@ var init_config_store = __esm(() => {
|
|
|
2547
2557
|
init_load_config();
|
|
2548
2558
|
});
|
|
2549
2559
|
|
|
2560
|
+
// src/config/default-workspace.ts
|
|
2561
|
+
var DEFAULT_HOME_WORKSPACE_NAME = "home", DEFAULT_HOME_WORKSPACE;
|
|
2562
|
+
var init_default_workspace = __esm(() => {
|
|
2563
|
+
DEFAULT_HOME_WORKSPACE = {
|
|
2564
|
+
cwd: "~",
|
|
2565
|
+
description: "用户主目录"
|
|
2566
|
+
};
|
|
2567
|
+
});
|
|
2568
|
+
|
|
2550
2569
|
// src/config/ensure-config.ts
|
|
2551
2570
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
2552
2571
|
async function ensureConfigExists(path2, options = {}) {
|
|
@@ -2601,7 +2620,9 @@ function normalizeDefaultConfigTemplate(raw) {
|
|
|
2601
2620
|
...resolveAgentCommand(agent.driver, agent.command) ? { command: resolveAgentCommand(agent.driver, agent.command) } : {}
|
|
2602
2621
|
}
|
|
2603
2622
|
])),
|
|
2604
|
-
workspaces: {
|
|
2623
|
+
workspaces: {
|
|
2624
|
+
[DEFAULT_HOME_WORKSPACE_NAME]: { ...DEFAULT_HOME_WORKSPACE }
|
|
2625
|
+
}
|
|
2605
2626
|
};
|
|
2606
2627
|
}
|
|
2607
2628
|
function isMissingFileError(error) {
|
|
@@ -2610,6 +2631,7 @@ function isMissingFileError(error) {
|
|
|
2610
2631
|
var BUILTIN_DEFAULT_CONFIG_TEMPLATE;
|
|
2611
2632
|
var init_ensure_config = __esm(() => {
|
|
2612
2633
|
init_config_store();
|
|
2634
|
+
init_default_workspace();
|
|
2613
2635
|
init_load_config();
|
|
2614
2636
|
BUILTIN_DEFAULT_CONFIG_TEMPLATE = {
|
|
2615
2637
|
transport: {
|
|
@@ -2638,7 +2660,9 @@ var init_ensure_config = __esm(() => {
|
|
|
2638
2660
|
codex: { driver: "codex" },
|
|
2639
2661
|
claude: { driver: "claude" }
|
|
2640
2662
|
},
|
|
2641
|
-
workspaces: {
|
|
2663
|
+
workspaces: {
|
|
2664
|
+
[DEFAULT_HOME_WORKSPACE_NAME]: { ...DEFAULT_HOME_WORKSPACE }
|
|
2665
|
+
},
|
|
2642
2666
|
plugins: []
|
|
2643
2667
|
};
|
|
2644
2668
|
});
|
|
@@ -9832,7 +9856,7 @@ function isCoordinatorRouteContextRecord(value) {
|
|
|
9832
9856
|
if (!isRecord2(value)) {
|
|
9833
9857
|
return false;
|
|
9834
9858
|
}
|
|
9835
|
-
return isString(value.coordinatorSession) && isString(value.chatKey) && isOptionalString(value.accountId) && isOptionalString(value.replyContextToken) && isString(value.updatedAt);
|
|
9859
|
+
return isString(value.coordinatorSession) && isString(value.chatKey) && isOptionalString(value.sessionAlias) && isOptionalString(value.accountId) && isOptionalString(value.replyContextToken) && isOptionalString(value.channel) && (value.chatType === undefined || value.chatType === "direct" || value.chatType === "group") && isOptionalString(value.senderId) && isOptionalString(value.senderName) && isOptionalString(value.groupId) && isOptionalBoolean(value.isOwner) && isString(value.updatedAt);
|
|
9836
9860
|
}
|
|
9837
9861
|
function isHumanQuestionPackageMessageRecord(value) {
|
|
9838
9862
|
if (!isRecord2(value)) {
|
|
@@ -9985,10 +10009,13 @@ function parseChatContexts(raw, path3) {
|
|
|
9985
10009
|
function isScheduledTaskStatus(value) {
|
|
9986
10010
|
return value === "pending" || value === "triggering" || value === "executed" || value === "cancelled" || value === "missed" || value === "failed";
|
|
9987
10011
|
}
|
|
10012
|
+
function isOptionalScheduledSessionMode(value) {
|
|
10013
|
+
return value === undefined || value === "temp" || value === "bound";
|
|
10014
|
+
}
|
|
9988
10015
|
function isScheduledTaskRecord(value) {
|
|
9989
10016
|
if (!isRecord2(value))
|
|
9990
10017
|
return false;
|
|
9991
|
-
return isString(value.id) && isString(value.chat_key) && isString(value.session_alias) && isString(value.execute_at) && isString(value.message) && isScheduledTaskStatus(value.status) && isString(value.created_at) && isOptionalString(value.account_id) && isOptionalString(value.reply_context_token) && isOptionalString(value.source_label) && isOptionalString(value.triggered_at) && isOptionalString(value.executed_at) && isOptionalString(value.cancelled_at) && isOptionalString(value.missed_at) && isOptionalString(value.failed_at) && isOptionalString(value.last_error);
|
|
10018
|
+
return isString(value.id) && isString(value.chat_key) && isString(value.session_alias) && isString(value.execute_at) && isString(value.message) && isScheduledTaskStatus(value.status) && isString(value.created_at) && isOptionalString(value.account_id) && isOptionalString(value.reply_context_token) && isOptionalString(value.source_label) && isOptionalString(value.triggered_at) && isOptionalString(value.executed_at) && isOptionalString(value.cancelled_at) && isOptionalString(value.missed_at) && isOptionalString(value.failed_at) && isOptionalString(value.last_error) && isOptionalScheduledSessionMode(value.session_mode) && isOptionalString(value.agent) && isOptionalString(value.workspace);
|
|
9992
10019
|
}
|
|
9993
10020
|
function parseScheduledTasks(raw, path3) {
|
|
9994
10021
|
if (raw === undefined)
|
|
@@ -10080,6 +10107,315 @@ var init_state_store = __esm(() => {
|
|
|
10080
10107
|
init_types();
|
|
10081
10108
|
});
|
|
10082
10109
|
|
|
10110
|
+
// src/channels/channel-scope.ts
|
|
10111
|
+
function registerKnownChannelId(channelId) {
|
|
10112
|
+
const normalized = channelId.trim();
|
|
10113
|
+
if (!normalized || normalized.includes(":")) {
|
|
10114
|
+
throw new Error("channel id must be non-empty and must not contain ':'");
|
|
10115
|
+
}
|
|
10116
|
+
KNOWN_CHANNEL_IDS.add(normalized);
|
|
10117
|
+
}
|
|
10118
|
+
function listKnownChannelIds() {
|
|
10119
|
+
return Array.from(KNOWN_CHANNEL_IDS);
|
|
10120
|
+
}
|
|
10121
|
+
function getChannelIdFromChatKey(chatKey) {
|
|
10122
|
+
const first = chatKey.split(":", 1)[0];
|
|
10123
|
+
return first && KNOWN_CHANNEL_IDS.has(first) ? first : "weixin";
|
|
10124
|
+
}
|
|
10125
|
+
function toInternalSessionAlias(channelId, displayAlias) {
|
|
10126
|
+
const normalized = displayAlias.trim();
|
|
10127
|
+
if (normalized.length === 0) {
|
|
10128
|
+
throw new Error("display session alias must be non-empty");
|
|
10129
|
+
}
|
|
10130
|
+
if (normalized.startsWith(`${channelId}:`)) {
|
|
10131
|
+
return normalized;
|
|
10132
|
+
}
|
|
10133
|
+
return `${channelId}:${normalized}`;
|
|
10134
|
+
}
|
|
10135
|
+
function toDisplaySessionAlias(internalAlias) {
|
|
10136
|
+
const [first, ...rest] = internalAlias.split(":");
|
|
10137
|
+
if (first && KNOWN_CHANNEL_IDS.has(first) && rest.length > 0) {
|
|
10138
|
+
return rest.join(":");
|
|
10139
|
+
}
|
|
10140
|
+
return internalAlias;
|
|
10141
|
+
}
|
|
10142
|
+
function isSessionAliasVisibleInChannel(alias, channelId) {
|
|
10143
|
+
const [first] = alias.split(":", 1);
|
|
10144
|
+
if (first && KNOWN_CHANNEL_IDS.has(first)) {
|
|
10145
|
+
return first === channelId;
|
|
10146
|
+
}
|
|
10147
|
+
return channelId === "weixin";
|
|
10148
|
+
}
|
|
10149
|
+
function resolveSessionAliasForInput(channelId, displayAlias, existingAliases) {
|
|
10150
|
+
const normalized = displayAlias.trim();
|
|
10151
|
+
if (normalized.length === 0) {
|
|
10152
|
+
throw new Error("display session alias must be non-empty");
|
|
10153
|
+
}
|
|
10154
|
+
if (normalized.startsWith(`${channelId}:`)) {
|
|
10155
|
+
return normalized;
|
|
10156
|
+
}
|
|
10157
|
+
const scopedAlias = toInternalSessionAlias(channelId, normalized);
|
|
10158
|
+
for (const alias of existingAliases) {
|
|
10159
|
+
if (alias === scopedAlias)
|
|
10160
|
+
return scopedAlias;
|
|
10161
|
+
}
|
|
10162
|
+
if (channelId === "weixin") {
|
|
10163
|
+
for (const alias of existingAliases) {
|
|
10164
|
+
if (alias === normalized)
|
|
10165
|
+
return alias;
|
|
10166
|
+
}
|
|
10167
|
+
}
|
|
10168
|
+
return scopedAlias;
|
|
10169
|
+
}
|
|
10170
|
+
function buildDefaultTransportSession(channelId, displayAlias) {
|
|
10171
|
+
const normalized = displayAlias.trim();
|
|
10172
|
+
if (normalized.length === 0) {
|
|
10173
|
+
throw new Error("display session alias must be non-empty");
|
|
10174
|
+
}
|
|
10175
|
+
return channelId === "weixin" ? normalized : toInternalSessionAlias(channelId, normalized);
|
|
10176
|
+
}
|
|
10177
|
+
var KNOWN_CHANNEL_IDS;
|
|
10178
|
+
var init_channel_scope = __esm(() => {
|
|
10179
|
+
KNOWN_CHANNEL_IDS = new Set(["weixin"]);
|
|
10180
|
+
});
|
|
10181
|
+
|
|
10182
|
+
// src/scheduled/scheduled-types.ts
|
|
10183
|
+
var LATER_MIN_DELAY_MS = 1e4, LATER_MAX_DELAY_MS, LATER_MESSAGE_PREVIEW_CHARS = 120;
|
|
10184
|
+
var init_scheduled_types = __esm(() => {
|
|
10185
|
+
LATER_MAX_DELAY_MS = 7 * 24 * 60 * 60 * 1000;
|
|
10186
|
+
});
|
|
10187
|
+
|
|
10188
|
+
// src/scheduled/scheduled-render.ts
|
|
10189
|
+
function sessionLabel(task, displaySession) {
|
|
10190
|
+
if (task.session_mode === "temp") {
|
|
10191
|
+
return `临时会话(${task.workspace ?? "?"} · ${task.agent ?? "?"})`;
|
|
10192
|
+
}
|
|
10193
|
+
return `会话:${displaySession}`;
|
|
10194
|
+
}
|
|
10195
|
+
function renderLaterHelp() {
|
|
10196
|
+
return [
|
|
10197
|
+
"定时任务用法:",
|
|
10198
|
+
"",
|
|
10199
|
+
"创建:",
|
|
10200
|
+
"/lt in 2h 检查 CI",
|
|
10201
|
+
"/lt 30分钟后 总结进展",
|
|
10202
|
+
"/lt tomorrow 09:00 看 PR",
|
|
10203
|
+
"/lt 周五 09:00 继续处理",
|
|
10204
|
+
"",
|
|
10205
|
+
"查看:",
|
|
10206
|
+
"/lt list",
|
|
10207
|
+
"",
|
|
10208
|
+
"取消:",
|
|
10209
|
+
"/lt cancel <id>",
|
|
10210
|
+
"",
|
|
10211
|
+
"说明:",
|
|
10212
|
+
"- 只支持一次性任务",
|
|
10213
|
+
"- 时间必须在 10 秒之后、7 天之内",
|
|
10214
|
+
"- 默认在为本次任务新建的临时会话里执行(跑完即销毁)",
|
|
10215
|
+
"- 加 --bind 改为发送到创建时绑定的当前会话",
|
|
10216
|
+
"- 触发通知和 agent 回复复用现有频道路由;微信回复额度由现有路由控制",
|
|
10217
|
+
"- 不支持延迟执行 / 开头的 weacpx 命令",
|
|
10218
|
+
"- 完整时间格式与说明见 docs/later-command.md"
|
|
10219
|
+
].join(`
|
|
10220
|
+
`);
|
|
10221
|
+
}
|
|
10222
|
+
function renderLaterUnsupportedChannel() {
|
|
10223
|
+
return [
|
|
10224
|
+
"当前频道暂不支持定时任务,未创建任务。",
|
|
10225
|
+
"",
|
|
10226
|
+
"原因:这个频道还没有实现定时消息投递能力,任务到点后无法把结果发回原聊天。",
|
|
10227
|
+
"请切换到支持定时任务的频道后再使用 /lt。"
|
|
10228
|
+
].join(`
|
|
10229
|
+
`);
|
|
10230
|
+
}
|
|
10231
|
+
function renderTaskCreated(task, displaySession) {
|
|
10232
|
+
return [
|
|
10233
|
+
`已创建定时任务 #${task.id}`,
|
|
10234
|
+
`执行时间:${formatLocalDateTime(new Date(task.execute_at))}`,
|
|
10235
|
+
sessionLabel(task, displaySession),
|
|
10236
|
+
`内容:${preview(task.message)}`
|
|
10237
|
+
].join(`
|
|
10238
|
+
`);
|
|
10239
|
+
}
|
|
10240
|
+
function renderLaterList(tasks, displaySession) {
|
|
10241
|
+
if (tasks.length === 0)
|
|
10242
|
+
return "当前没有待执行定时任务。";
|
|
10243
|
+
return [
|
|
10244
|
+
"待执行定时任务:",
|
|
10245
|
+
"",
|
|
10246
|
+
...tasks.flatMap((task) => [
|
|
10247
|
+
`#${task.id} ${formatLocalDateTime(new Date(task.execute_at))} ${sessionLabel(task, displaySession(task.session_alias))}`,
|
|
10248
|
+
preview(task.message),
|
|
10249
|
+
""
|
|
10250
|
+
])
|
|
10251
|
+
].join(`
|
|
10252
|
+
`).trimEnd();
|
|
10253
|
+
}
|
|
10254
|
+
function preview(text) {
|
|
10255
|
+
return text.length <= LATER_MESSAGE_PREVIEW_CHARS ? text : `${text.slice(0, LATER_MESSAGE_PREVIEW_CHARS - 1)}…`;
|
|
10256
|
+
}
|
|
10257
|
+
function formatLocalDateTime(date4) {
|
|
10258
|
+
const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
|
10259
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
10260
|
+
return `${date4.getFullYear()}-${pad(date4.getMonth() + 1)}-${pad(date4.getDate())} ${weekdays[date4.getDay()]} ${pad(date4.getHours())}:${pad(date4.getMinutes())}`;
|
|
10261
|
+
}
|
|
10262
|
+
var init_scheduled_render = __esm(() => {
|
|
10263
|
+
init_scheduled_types();
|
|
10264
|
+
});
|
|
10265
|
+
|
|
10266
|
+
// src/orchestration/async-mutex.ts
|
|
10267
|
+
class AsyncMutex {
|
|
10268
|
+
tail = Promise.resolve();
|
|
10269
|
+
async run(critical) {
|
|
10270
|
+
const previous = this.tail;
|
|
10271
|
+
let release;
|
|
10272
|
+
this.tail = new Promise((resolve) => {
|
|
10273
|
+
release = resolve;
|
|
10274
|
+
});
|
|
10275
|
+
await previous;
|
|
10276
|
+
try {
|
|
10277
|
+
return await critical();
|
|
10278
|
+
} finally {
|
|
10279
|
+
release();
|
|
10280
|
+
}
|
|
10281
|
+
}
|
|
10282
|
+
}
|
|
10283
|
+
|
|
10284
|
+
// src/scheduled/scheduled-service.ts
|
|
10285
|
+
class ScheduledTaskService {
|
|
10286
|
+
state;
|
|
10287
|
+
stateStore;
|
|
10288
|
+
now;
|
|
10289
|
+
generateId;
|
|
10290
|
+
stateMutex;
|
|
10291
|
+
claimedInThisSession = new Set;
|
|
10292
|
+
constructor(state, stateStore, options) {
|
|
10293
|
+
this.state = state;
|
|
10294
|
+
this.stateStore = stateStore;
|
|
10295
|
+
this.now = options?.now ?? (() => new Date);
|
|
10296
|
+
this.generateId = options?.generateId ?? (() => Math.random().toString(36).slice(2, 6));
|
|
10297
|
+
this.stateMutex = options?.stateMutex ?? new AsyncMutex;
|
|
10298
|
+
}
|
|
10299
|
+
async createTask(input) {
|
|
10300
|
+
return await this.mutate(async () => {
|
|
10301
|
+
const id = this.nextId();
|
|
10302
|
+
const task = {
|
|
10303
|
+
id,
|
|
10304
|
+
chat_key: input.chatKey,
|
|
10305
|
+
session_alias: input.sessionAlias,
|
|
10306
|
+
execute_at: input.executeAt.toISOString(),
|
|
10307
|
+
message: input.message,
|
|
10308
|
+
status: "pending",
|
|
10309
|
+
created_at: this.now().toISOString(),
|
|
10310
|
+
...input.sessionMode ? { session_mode: input.sessionMode } : {},
|
|
10311
|
+
...input.agent ? { agent: input.agent } : {},
|
|
10312
|
+
...input.workspace ? { workspace: input.workspace } : {},
|
|
10313
|
+
...input.accountId ? { account_id: input.accountId } : {},
|
|
10314
|
+
...input.replyContextToken ? { reply_context_token: input.replyContextToken } : {},
|
|
10315
|
+
...input.sourceLabel ? { source_label: input.sourceLabel } : {}
|
|
10316
|
+
};
|
|
10317
|
+
this.state.scheduled_tasks[id] = task;
|
|
10318
|
+
await this.save();
|
|
10319
|
+
return task;
|
|
10320
|
+
});
|
|
10321
|
+
}
|
|
10322
|
+
listPending() {
|
|
10323
|
+
return Object.values(this.state.scheduled_tasks).filter((task) => task.status === "pending").sort((left, right) => left.execute_at.localeCompare(right.execute_at));
|
|
10324
|
+
}
|
|
10325
|
+
async cancelPending(inputId) {
|
|
10326
|
+
return await this.mutate(async () => {
|
|
10327
|
+
const id = normalizeId(inputId);
|
|
10328
|
+
const task = this.state.scheduled_tasks[id];
|
|
10329
|
+
if (!task || task.status !== "pending")
|
|
10330
|
+
return false;
|
|
10331
|
+
task.status = "cancelled";
|
|
10332
|
+
task.cancelled_at = this.now().toISOString();
|
|
10333
|
+
await this.save();
|
|
10334
|
+
return true;
|
|
10335
|
+
});
|
|
10336
|
+
}
|
|
10337
|
+
async markStartupMissed() {
|
|
10338
|
+
await this.mutate(async () => {
|
|
10339
|
+
const nowMs = this.now().getTime();
|
|
10340
|
+
let changed = false;
|
|
10341
|
+
for (const task of Object.values(this.state.scheduled_tasks)) {
|
|
10342
|
+
if (task.status === "pending" && Date.parse(task.execute_at) < nowMs) {
|
|
10343
|
+
task.status = "missed";
|
|
10344
|
+
task.missed_at = this.now().toISOString();
|
|
10345
|
+
changed = true;
|
|
10346
|
+
}
|
|
10347
|
+
if (task.status === "triggering" && !this.claimedInThisSession.has(task.id)) {
|
|
10348
|
+
task.status = "failed";
|
|
10349
|
+
task.failed_at = this.now().toISOString();
|
|
10350
|
+
task.last_error = "process stopped while task was triggering";
|
|
10351
|
+
changed = true;
|
|
10352
|
+
}
|
|
10353
|
+
}
|
|
10354
|
+
if (changed)
|
|
10355
|
+
await this.save();
|
|
10356
|
+
});
|
|
10357
|
+
}
|
|
10358
|
+
async claimDueTasks() {
|
|
10359
|
+
return await this.mutate(async () => {
|
|
10360
|
+
const nowMs = this.now().getTime();
|
|
10361
|
+
const due = this.listPending().filter((task) => Date.parse(task.execute_at) <= nowMs);
|
|
10362
|
+
if (due.length === 0)
|
|
10363
|
+
return [];
|
|
10364
|
+
const at = this.now().toISOString();
|
|
10365
|
+
for (const task of due) {
|
|
10366
|
+
task.status = "triggering";
|
|
10367
|
+
task.triggered_at = at;
|
|
10368
|
+
this.claimedInThisSession.add(task.id);
|
|
10369
|
+
}
|
|
10370
|
+
await this.save();
|
|
10371
|
+
return due.map((task) => ({ ...task }));
|
|
10372
|
+
});
|
|
10373
|
+
}
|
|
10374
|
+
async markExecuted(id) {
|
|
10375
|
+
await this.mutate(async () => {
|
|
10376
|
+
const taskId = normalizeId(id);
|
|
10377
|
+
const task = this.state.scheduled_tasks[taskId];
|
|
10378
|
+
if (!task)
|
|
10379
|
+
return;
|
|
10380
|
+
task.status = "executed";
|
|
10381
|
+
task.executed_at = this.now().toISOString();
|
|
10382
|
+
this.claimedInThisSession.delete(taskId);
|
|
10383
|
+
await this.save();
|
|
10384
|
+
});
|
|
10385
|
+
}
|
|
10386
|
+
async markFailed(id, error2) {
|
|
10387
|
+
await this.mutate(async () => {
|
|
10388
|
+
const taskId = normalizeId(id);
|
|
10389
|
+
const task = this.state.scheduled_tasks[taskId];
|
|
10390
|
+
if (!task)
|
|
10391
|
+
return;
|
|
10392
|
+
task.status = "failed";
|
|
10393
|
+
task.failed_at = this.now().toISOString();
|
|
10394
|
+
task.last_error = error2 instanceof Error ? error2.message : String(error2);
|
|
10395
|
+
this.claimedInThisSession.delete(taskId);
|
|
10396
|
+
await this.save();
|
|
10397
|
+
});
|
|
10398
|
+
}
|
|
10399
|
+
nextId() {
|
|
10400
|
+
for (let attempt = 0;attempt < 20; attempt += 1) {
|
|
10401
|
+
const id = normalizeId(this.generateId()).replace(/[^0-9a-z]/g, "").slice(0, 6);
|
|
10402
|
+
if (id.length >= 4 && !this.state.scheduled_tasks[id])
|
|
10403
|
+
return id;
|
|
10404
|
+
}
|
|
10405
|
+
throw new Error("failed to generate unique scheduled task id");
|
|
10406
|
+
}
|
|
10407
|
+
async mutate(critical) {
|
|
10408
|
+
return await this.stateMutex.run(critical);
|
|
10409
|
+
}
|
|
10410
|
+
async save() {
|
|
10411
|
+
await this.stateStore.save(this.state);
|
|
10412
|
+
}
|
|
10413
|
+
}
|
|
10414
|
+
function normalizeId(input) {
|
|
10415
|
+
return input.trim().replace(/^#/, "").toLowerCase();
|
|
10416
|
+
}
|
|
10417
|
+
var init_scheduled_service = () => {};
|
|
10418
|
+
|
|
10083
10419
|
// src/plugins/plugin-home.ts
|
|
10084
10420
|
import { mkdir as mkdir6, writeFile as writeFile4 } from "node:fs/promises";
|
|
10085
10421
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -15552,7 +15888,11 @@ async function executeScheduledTurn(input, deps) {
|
|
|
15552
15888
|
text: input.promptText,
|
|
15553
15889
|
...deliveryContextToken ? { replyContextToken: deliveryContextToken } : {},
|
|
15554
15890
|
...input.abortSignal ? { abortSignal: input.abortSignal } : {},
|
|
15555
|
-
metadata: {
|
|
15891
|
+
metadata: {
|
|
15892
|
+
channel: "weixin",
|
|
15893
|
+
scheduledSessionAlias: input.sessionAlias,
|
|
15894
|
+
...input.sessionDescriptor ? { scheduledSessionDescriptor: input.sessionDescriptor } : {}
|
|
15895
|
+
}
|
|
15556
15896
|
},
|
|
15557
15897
|
onReplySegment: sendReplySegment
|
|
15558
15898
|
});
|
|
@@ -15884,78 +16224,6 @@ var init_weixin_channel = __esm(() => {
|
|
|
15884
16224
|
init_consumer_lock();
|
|
15885
16225
|
});
|
|
15886
16226
|
|
|
15887
|
-
// src/channels/channel-scope.ts
|
|
15888
|
-
function registerKnownChannelId(channelId) {
|
|
15889
|
-
const normalized = channelId.trim();
|
|
15890
|
-
if (!normalized || normalized.includes(":")) {
|
|
15891
|
-
throw new Error("channel id must be non-empty and must not contain ':'");
|
|
15892
|
-
}
|
|
15893
|
-
KNOWN_CHANNEL_IDS.add(normalized);
|
|
15894
|
-
}
|
|
15895
|
-
function listKnownChannelIds() {
|
|
15896
|
-
return Array.from(KNOWN_CHANNEL_IDS);
|
|
15897
|
-
}
|
|
15898
|
-
function getChannelIdFromChatKey(chatKey) {
|
|
15899
|
-
const first = chatKey.split(":", 1)[0];
|
|
15900
|
-
return first && KNOWN_CHANNEL_IDS.has(first) ? first : "weixin";
|
|
15901
|
-
}
|
|
15902
|
-
function toInternalSessionAlias(channelId, displayAlias) {
|
|
15903
|
-
const normalized = displayAlias.trim();
|
|
15904
|
-
if (normalized.length === 0) {
|
|
15905
|
-
throw new Error("display session alias must be non-empty");
|
|
15906
|
-
}
|
|
15907
|
-
if (normalized.startsWith(`${channelId}:`)) {
|
|
15908
|
-
return normalized;
|
|
15909
|
-
}
|
|
15910
|
-
return `${channelId}:${normalized}`;
|
|
15911
|
-
}
|
|
15912
|
-
function toDisplaySessionAlias(internalAlias) {
|
|
15913
|
-
const [first, ...rest] = internalAlias.split(":");
|
|
15914
|
-
if (first && KNOWN_CHANNEL_IDS.has(first) && rest.length > 0) {
|
|
15915
|
-
return rest.join(":");
|
|
15916
|
-
}
|
|
15917
|
-
return internalAlias;
|
|
15918
|
-
}
|
|
15919
|
-
function isSessionAliasVisibleInChannel(alias, channelId) {
|
|
15920
|
-
const [first] = alias.split(":", 1);
|
|
15921
|
-
if (first && KNOWN_CHANNEL_IDS.has(first)) {
|
|
15922
|
-
return first === channelId;
|
|
15923
|
-
}
|
|
15924
|
-
return channelId === "weixin";
|
|
15925
|
-
}
|
|
15926
|
-
function resolveSessionAliasForInput(channelId, displayAlias, existingAliases) {
|
|
15927
|
-
const normalized = displayAlias.trim();
|
|
15928
|
-
if (normalized.length === 0) {
|
|
15929
|
-
throw new Error("display session alias must be non-empty");
|
|
15930
|
-
}
|
|
15931
|
-
if (normalized.startsWith(`${channelId}:`)) {
|
|
15932
|
-
return normalized;
|
|
15933
|
-
}
|
|
15934
|
-
const scopedAlias = toInternalSessionAlias(channelId, normalized);
|
|
15935
|
-
for (const alias of existingAliases) {
|
|
15936
|
-
if (alias === scopedAlias)
|
|
15937
|
-
return scopedAlias;
|
|
15938
|
-
}
|
|
15939
|
-
if (channelId === "weixin") {
|
|
15940
|
-
for (const alias of existingAliases) {
|
|
15941
|
-
if (alias === normalized)
|
|
15942
|
-
return alias;
|
|
15943
|
-
}
|
|
15944
|
-
}
|
|
15945
|
-
return scopedAlias;
|
|
15946
|
-
}
|
|
15947
|
-
function buildDefaultTransportSession(channelId, displayAlias) {
|
|
15948
|
-
const normalized = displayAlias.trim();
|
|
15949
|
-
if (normalized.length === 0) {
|
|
15950
|
-
throw new Error("display session alias must be non-empty");
|
|
15951
|
-
}
|
|
15952
|
-
return channelId === "weixin" ? normalized : toInternalSessionAlias(channelId, normalized);
|
|
15953
|
-
}
|
|
15954
|
-
var KNOWN_CHANNEL_IDS;
|
|
15955
|
-
var init_channel_scope = __esm(() => {
|
|
15956
|
-
KNOWN_CHANNEL_IDS = new Set(["weixin"]);
|
|
15957
|
-
});
|
|
15958
|
-
|
|
15959
16227
|
// src/plugins/known-plugins.ts
|
|
15960
16228
|
function listKnownPlugins() {
|
|
15961
16229
|
return KNOWN_PLUGINS.map((plugin) => ({ ...plugin, channels: [...plugin.channels] }));
|
|
@@ -18138,7 +18406,7 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
18138
18406
|
return { text: lines.join(`
|
|
18139
18407
|
`) };
|
|
18140
18408
|
}
|
|
18141
|
-
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
18409
|
+
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18142
18410
|
const effectiveReplyMode = session.replyMode ?? context.config?.channel.replyMode ?? "verbose";
|
|
18143
18411
|
if (!session.replyMode)
|
|
18144
18412
|
session.replyMode = effectiveReplyMode;
|
|
@@ -18148,8 +18416,10 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
18148
18416
|
await context.orchestration.recordCoordinatorRouteContext?.({
|
|
18149
18417
|
coordinatorSession: session.transportSession,
|
|
18150
18418
|
chatKey,
|
|
18419
|
+
sessionAlias: session.alias,
|
|
18151
18420
|
...replyContextToken ? { replyContextToken } : {},
|
|
18152
|
-
...accountId ? { accountId } : {}
|
|
18421
|
+
...accountId ? { accountId } : {},
|
|
18422
|
+
...toCoordinatorRouteChatMetadata(metadata)
|
|
18153
18423
|
});
|
|
18154
18424
|
} catch (error2) {
|
|
18155
18425
|
await context.logger.error("orchestration.coordinator_route_context.record_failed", "failed to record coordinator route context", {
|
|
@@ -18158,6 +18428,13 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
18158
18428
|
chatKey,
|
|
18159
18429
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
18160
18430
|
});
|
|
18431
|
+
return {
|
|
18432
|
+
text: [
|
|
18433
|
+
"无法记录当前会话路由,已取消本次发送。",
|
|
18434
|
+
"请稍后重试;如果问题持续存在,请检查 weacpx 运行日志和 state.json 写入权限。"
|
|
18435
|
+
].join(`
|
|
18436
|
+
`)
|
|
18437
|
+
};
|
|
18161
18438
|
}
|
|
18162
18439
|
}
|
|
18163
18440
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session, chatKey, text, replyContextToken, accountId);
|
|
@@ -18183,23 +18460,36 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
18183
18460
|
throw error2;
|
|
18184
18461
|
}
|
|
18185
18462
|
}
|
|
18186
|
-
async function handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
18463
|
+
async function handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18187
18464
|
try {
|
|
18188
|
-
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
18465
|
+
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
18189
18466
|
} catch (error2) {
|
|
18190
18467
|
const recovered = await context.recovery.tryRecoverMissingSession(session, error2);
|
|
18191
18468
|
if (recovered) {
|
|
18192
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
18469
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
18193
18470
|
}
|
|
18194
18471
|
return context.recovery.renderTransportError(session, error2);
|
|
18195
18472
|
}
|
|
18196
18473
|
}
|
|
18197
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
18474
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18198
18475
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
18199
18476
|
if (!session) {
|
|
18200
18477
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
18201
18478
|
}
|
|
18202
|
-
return await handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
18479
|
+
return await handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
18480
|
+
}
|
|
18481
|
+
function toCoordinatorRouteChatMetadata(metadata) {
|
|
18482
|
+
if (!metadata) {
|
|
18483
|
+
return {};
|
|
18484
|
+
}
|
|
18485
|
+
return {
|
|
18486
|
+
...metadata.channel ? { channel: metadata.channel } : {},
|
|
18487
|
+
...metadata.chatType ? { chatType: metadata.chatType } : {},
|
|
18488
|
+
...metadata.senderId ? { senderId: metadata.senderId } : {},
|
|
18489
|
+
...metadata.senderName ? { senderName: metadata.senderName } : {},
|
|
18490
|
+
...metadata.groupId ? { groupId: metadata.groupId } : {},
|
|
18491
|
+
...metadata.isOwner !== undefined ? { isOwner: metadata.isOwner } : {}
|
|
18492
|
+
};
|
|
18203
18493
|
}
|
|
18204
18494
|
async function preparePromptWithFallback(context, session, chatKey, text, replyContextToken, accountId) {
|
|
18205
18495
|
const orchestration = context.orchestration;
|
|
@@ -18962,12 +19252,6 @@ var init_workspace_handler = __esm(() => {
|
|
|
18962
19252
|
};
|
|
18963
19253
|
});
|
|
18964
19254
|
|
|
18965
|
-
// src/scheduled/scheduled-types.ts
|
|
18966
|
-
var LATER_MIN_DELAY_MS = 1e4, LATER_MAX_DELAY_MS, LATER_MESSAGE_PREVIEW_CHARS = 120;
|
|
18967
|
-
var init_scheduled_types = __esm(() => {
|
|
18968
|
-
LATER_MAX_DELAY_MS = 7 * 24 * 60 * 60 * 1000;
|
|
18969
|
-
});
|
|
18970
|
-
|
|
18971
19255
|
// src/scheduled/parse-later-time.ts
|
|
18972
19256
|
function parseLaterTime(tokens, now = new Date) {
|
|
18973
19257
|
if (tokens.length === 0)
|
|
@@ -19105,83 +19389,24 @@ var init_parse_later_time = __esm(() => {
|
|
|
19105
19389
|
]);
|
|
19106
19390
|
});
|
|
19107
19391
|
|
|
19108
|
-
// src/scheduled/scheduled-render.ts
|
|
19109
|
-
function renderLaterHelp() {
|
|
19110
|
-
return [
|
|
19111
|
-
"定时任务用法:",
|
|
19112
|
-
"",
|
|
19113
|
-
"创建:",
|
|
19114
|
-
"/lt in 2h 检查 CI",
|
|
19115
|
-
"/lt 30分钟后 总结进展",
|
|
19116
|
-
"/lt tomorrow 09:00 看 PR",
|
|
19117
|
-
"/lt 周五 09:00 继续处理",
|
|
19118
|
-
"",
|
|
19119
|
-
"查看:",
|
|
19120
|
-
"/lt list",
|
|
19121
|
-
"",
|
|
19122
|
-
"取消:",
|
|
19123
|
-
"/lt cancel <id>",
|
|
19124
|
-
"",
|
|
19125
|
-
"说明:",
|
|
19126
|
-
"- 只支持一次性任务",
|
|
19127
|
-
"- 时间必须在 10 秒之后、7 天之内",
|
|
19128
|
-
"- 到点后会把消息发送到创建时绑定的会话",
|
|
19129
|
-
"- 触发通知和 agent 回复复用现有频道路由;微信回复额度由现有路由控制",
|
|
19130
|
-
"- 不支持延迟执行 / 开头的 weacpx 命令",
|
|
19131
|
-
"- 完整时间格式与说明见 docs/later-command.md"
|
|
19132
|
-
].join(`
|
|
19133
|
-
`);
|
|
19134
|
-
}
|
|
19135
|
-
function renderLaterUnsupportedChannel() {
|
|
19136
|
-
return [
|
|
19137
|
-
"当前频道暂不支持定时任务,未创建任务。",
|
|
19138
|
-
"",
|
|
19139
|
-
"原因:这个频道还没有实现定时消息投递能力,任务到点后无法把结果发回原聊天。",
|
|
19140
|
-
"请切换到支持定时任务的频道后再使用 /lt。"
|
|
19141
|
-
].join(`
|
|
19142
|
-
`);
|
|
19143
|
-
}
|
|
19144
|
-
function renderTaskCreated(task, displaySession) {
|
|
19145
|
-
return [
|
|
19146
|
-
`已创建定时任务 #${task.id}`,
|
|
19147
|
-
`执行时间:${formatLocalDateTime(new Date(task.execute_at))}`,
|
|
19148
|
-
`会话:${displaySession}`,
|
|
19149
|
-
`内容:${preview(task.message)}`
|
|
19150
|
-
].join(`
|
|
19151
|
-
`);
|
|
19152
|
-
}
|
|
19153
|
-
function renderLaterList(tasks, displaySession) {
|
|
19154
|
-
if (tasks.length === 0)
|
|
19155
|
-
return "当前没有待执行定时任务。";
|
|
19156
|
-
return [
|
|
19157
|
-
"待执行定时任务:",
|
|
19158
|
-
"",
|
|
19159
|
-
...tasks.flatMap((task) => [
|
|
19160
|
-
`#${task.id} ${formatLocalDateTime(new Date(task.execute_at))} 会话:${displaySession(task.session_alias)}`,
|
|
19161
|
-
preview(task.message),
|
|
19162
|
-
""
|
|
19163
|
-
])
|
|
19164
|
-
].join(`
|
|
19165
|
-
`).trimEnd();
|
|
19166
|
-
}
|
|
19167
|
-
function preview(text) {
|
|
19168
|
-
return text.length <= LATER_MESSAGE_PREVIEW_CHARS ? text : `${text.slice(0, LATER_MESSAGE_PREVIEW_CHARS - 1)}…`;
|
|
19169
|
-
}
|
|
19170
|
-
function formatLocalDateTime(date4) {
|
|
19171
|
-
const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
|
19172
|
-
const pad = (value) => String(value).padStart(2, "0");
|
|
19173
|
-
return `${date4.getFullYear()}-${pad(date4.getMonth() + 1)}-${pad(date4.getDate())} ${weekdays[date4.getDay()]} ${pad(date4.getHours())}:${pad(date4.getMinutes())}`;
|
|
19174
|
-
}
|
|
19175
|
-
var init_scheduled_render = __esm(() => {
|
|
19176
|
-
init_scheduled_types();
|
|
19177
|
-
});
|
|
19178
|
-
|
|
19179
19392
|
// src/commands/handlers/later-handler.ts
|
|
19180
19393
|
function handleLaterHelp() {
|
|
19181
19394
|
return { text: renderLaterHelp() };
|
|
19182
19395
|
}
|
|
19183
|
-
async function handleLaterCreate(tokens, scheduled, chatKey,
|
|
19184
|
-
|
|
19396
|
+
async function handleLaterCreate(tokens, scheduled, chatKey, currentSession, defaultMode, accountId, replyContextToken) {
|
|
19397
|
+
let rest = tokens;
|
|
19398
|
+
const seenFlags = new Set;
|
|
19399
|
+
let flagMode;
|
|
19400
|
+
while (rest.length > 0 && (rest[0] === "--bind" || rest[0] === "--temp")) {
|
|
19401
|
+
seenFlags.add(rest[0]);
|
|
19402
|
+
flagMode = rest[0] === "--bind" ? "bound" : "temp";
|
|
19403
|
+
rest = rest.slice(1);
|
|
19404
|
+
}
|
|
19405
|
+
if (seenFlags.size > 1) {
|
|
19406
|
+
return { text: "定时任务的 --bind 与 --temp 不能同时使用。" };
|
|
19407
|
+
}
|
|
19408
|
+
const mode = flagMode ?? defaultMode;
|
|
19409
|
+
if (!currentSession) {
|
|
19185
19410
|
return {
|
|
19186
19411
|
text: [
|
|
19187
19412
|
"当前没有会话,无法创建定时任务。",
|
|
@@ -19193,11 +19418,11 @@ async function handleLaterCreate(tokens, scheduled, chatKey, currentSessionAlias
|
|
|
19193
19418
|
`)
|
|
19194
19419
|
};
|
|
19195
19420
|
}
|
|
19196
|
-
const result = parseLaterTime(
|
|
19421
|
+
const result = parseLaterTime(rest);
|
|
19197
19422
|
if (!result.ok) {
|
|
19198
19423
|
return { text: renderTimeParseError(result.code, result.value) };
|
|
19199
19424
|
}
|
|
19200
|
-
const message =
|
|
19425
|
+
const message = rest.slice(result.messageStartIndex).join(" ").trim();
|
|
19201
19426
|
if (message.startsWith("/")) {
|
|
19202
19427
|
return {
|
|
19203
19428
|
text: [
|
|
@@ -19211,13 +19436,15 @@ async function handleLaterCreate(tokens, scheduled, chatKey, currentSessionAlias
|
|
|
19211
19436
|
}
|
|
19212
19437
|
const task = await scheduled.createTask({
|
|
19213
19438
|
chatKey,
|
|
19214
|
-
sessionAlias:
|
|
19439
|
+
sessionAlias: currentSession.alias,
|
|
19215
19440
|
executeAt: result.executeAt,
|
|
19216
19441
|
message,
|
|
19442
|
+
sessionMode: mode,
|
|
19443
|
+
...mode === "temp" ? { agent: currentSession.agent, workspace: currentSession.workspace } : {},
|
|
19217
19444
|
...accountId ? { accountId } : {},
|
|
19218
19445
|
...replyContextToken ? { replyContextToken } : {}
|
|
19219
19446
|
});
|
|
19220
|
-
return { text: renderTaskCreated(task, toDisplaySessionAlias(
|
|
19447
|
+
return { text: renderTaskCreated(task, toDisplaySessionAlias(currentSession.alias)) };
|
|
19221
19448
|
}
|
|
19222
19449
|
function handleLaterList(scheduled) {
|
|
19223
19450
|
const tasks = scheduled.listPending();
|
|
@@ -19265,9 +19492,11 @@ var init_later_handler = __esm(() => {
|
|
|
19265
19492
|
laterHelpMetadata = {
|
|
19266
19493
|
topic: "later",
|
|
19267
19494
|
aliases: ["lt"],
|
|
19268
|
-
summary: "
|
|
19495
|
+
summary: "定时任务:到点在临时会话执行(或 --bind 发到当前会话)",
|
|
19269
19496
|
commands: [
|
|
19270
19497
|
{ usage: "/lt <时间> <消息>", description: "创建定时任务" },
|
|
19498
|
+
{ usage: "/lt --bind <时间> <消息>", description: "改为发送到当前会话" },
|
|
19499
|
+
{ usage: "/lt --temp <时间> <消息>", description: "强制使用临时会话" },
|
|
19271
19500
|
{ usage: "/lt list", description: "查看待执行定时任务" },
|
|
19272
19501
|
{ usage: "/lt cancel <id>", description: "取消定时任务" }
|
|
19273
19502
|
],
|
|
@@ -19281,7 +19510,8 @@ var init_later_handler = __esm(() => {
|
|
|
19281
19510
|
notes: [
|
|
19282
19511
|
"只支持一次性任务,不支持重复执行",
|
|
19283
19512
|
"时间必须在 10 秒之后、7 天之内",
|
|
19284
|
-
"
|
|
19513
|
+
"默认在为本次任务新建的临时会话里执行,跑完即销毁",
|
|
19514
|
+
"加 --bind 改为发送到创建时绑定的当前会话(默认模式可用 later.defaultMode 配置)",
|
|
19285
19515
|
"/lt list 显示全局待执行任务;群聊中只有群主可取消",
|
|
19286
19516
|
"不支持延迟执行 / 开头的 weacpx 命令",
|
|
19287
19517
|
"完整时间格式与说明见 docs/later-command.md"
|
|
@@ -19566,6 +19796,15 @@ var init_session_shortcut_handler = __esm(() => {
|
|
|
19566
19796
|
function renderTransportError(session, error2) {
|
|
19567
19797
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
19568
19798
|
if (message.includes("No acpx session found")) {
|
|
19799
|
+
if (session.transient) {
|
|
19800
|
+
return {
|
|
19801
|
+
text: [
|
|
19802
|
+
"定时任务的临时会话启动失败,本次任务未能执行。",
|
|
19803
|
+
"临时会话由系统在执行时自动创建,无需手动操作;如需重排,请用 /lt 重新安排。"
|
|
19804
|
+
].join(`
|
|
19805
|
+
`)
|
|
19806
|
+
};
|
|
19807
|
+
}
|
|
19569
19808
|
return {
|
|
19570
19809
|
text: [
|
|
19571
19810
|
`当前会话「${session.alias}」暂时不可用。`,
|
|
@@ -19642,6 +19881,9 @@ async function tryRecoverMissingSession(ops, session, error2) {
|
|
|
19642
19881
|
if (!message.includes("No acpx session found")) {
|
|
19643
19882
|
return null;
|
|
19644
19883
|
}
|
|
19884
|
+
if (session.transient) {
|
|
19885
|
+
return null;
|
|
19886
|
+
}
|
|
19645
19887
|
const transportAgentCommand = await ops.resolveSessionAgentCommand(session);
|
|
19646
19888
|
if (!transportAgentCommand || transportAgentCommand === session.agentCommand) {
|
|
19647
19889
|
return null;
|
|
@@ -20171,7 +20413,7 @@ class CommandRouter {
|
|
|
20171
20413
|
return { text: renderLaterUnsupportedChannel() };
|
|
20172
20414
|
}
|
|
20173
20415
|
const currentSession = await this.sessions.getCurrentSession(chatKey);
|
|
20174
|
-
return await handleLaterCreate(command.tokens, this.scheduled, chatKey, currentSession
|
|
20416
|
+
return await handleLaterCreate(command.tokens, this.scheduled, chatKey, currentSession ? { alias: currentSession.alias, agent: currentSession.agent, workspace: currentSession.workspace } : null, this.config?.later?.defaultMode === "bind" ? "bound" : "temp", accountId, replyContextToken);
|
|
20175
20417
|
}
|
|
20176
20418
|
case "later.cancel":
|
|
20177
20419
|
if (!this.scheduled)
|
|
@@ -20179,14 +20421,22 @@ class CommandRouter {
|
|
|
20179
20421
|
return await handleLaterCancel(command.id, this.scheduled);
|
|
20180
20422
|
case "prompt": {
|
|
20181
20423
|
const sessionContext = this.createSessionHandlerContext(undefined, perfSpan);
|
|
20424
|
+
if (metadata?.scheduledSessionDescriptor) {
|
|
20425
|
+
const descriptor = metadata.scheduledSessionDescriptor;
|
|
20426
|
+
const transientSession = {
|
|
20427
|
+
...this.sessions.resolveSession(descriptor.alias, descriptor.agent, descriptor.workspace, descriptor.transportSession),
|
|
20428
|
+
transient: true
|
|
20429
|
+
};
|
|
20430
|
+
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
20431
|
+
}
|
|
20182
20432
|
if (metadata?.scheduledSessionAlias) {
|
|
20183
20433
|
const scheduledSession = await this.sessions.getSession(metadata.scheduledSessionAlias);
|
|
20184
20434
|
if (!scheduledSession) {
|
|
20185
20435
|
throw new Error(`session "${metadata.scheduledSessionAlias}" not found for scheduled prompt`);
|
|
20186
20436
|
}
|
|
20187
|
-
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
20437
|
+
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
20188
20438
|
}
|
|
20189
|
-
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
20439
|
+
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
20190
20440
|
}
|
|
20191
20441
|
}
|
|
20192
20442
|
});
|
|
@@ -20664,24 +20914,6 @@ var init_console_agent = __esm(() => {
|
|
|
20664
20914
|
init_command_list();
|
|
20665
20915
|
});
|
|
20666
20916
|
|
|
20667
|
-
// src/orchestration/async-mutex.ts
|
|
20668
|
-
class AsyncMutex {
|
|
20669
|
-
tail = Promise.resolve();
|
|
20670
|
-
async run(critical) {
|
|
20671
|
-
const previous = this.tail;
|
|
20672
|
-
let release;
|
|
20673
|
-
this.tail = new Promise((resolve3) => {
|
|
20674
|
-
release = resolve3;
|
|
20675
|
-
});
|
|
20676
|
-
await previous;
|
|
20677
|
-
try {
|
|
20678
|
-
return await critical();
|
|
20679
|
-
} finally {
|
|
20680
|
-
release();
|
|
20681
|
-
}
|
|
20682
|
-
}
|
|
20683
|
-
}
|
|
20684
|
-
|
|
20685
20917
|
// src/orchestration/orchestration-server.ts
|
|
20686
20918
|
import { rm as rm7 } from "node:fs/promises";
|
|
20687
20919
|
import { createConnection as createConnection2, createServer } from "node:net";
|
|
@@ -20837,6 +21069,12 @@ class OrchestrationServer {
|
|
|
20837
21069
|
reviewId: requireString(params, "reviewId"),
|
|
20838
21070
|
decision: requireEnum(params, "decision", ["accept", "discard"])
|
|
20839
21071
|
});
|
|
21072
|
+
case "scheduled.create":
|
|
21073
|
+
return await this.dispatchScheduledCreate(params);
|
|
21074
|
+
case "scheduled.list":
|
|
21075
|
+
return await this.dispatchScheduledList(params);
|
|
21076
|
+
case "scheduled.cancel":
|
|
21077
|
+
return await this.dispatchScheduledCancel(params);
|
|
20840
21078
|
case "group.new":
|
|
20841
21079
|
requireOnlyKeys(params, ["coordinatorSession", "title"], "params");
|
|
20842
21080
|
return await this.handlers.createGroup({
|
|
@@ -20847,6 +21085,47 @@ class OrchestrationServer {
|
|
|
20847
21085
|
throw new OrchestrationInvalidRequestError(`unsupported orchestration method: ${method}`);
|
|
20848
21086
|
}
|
|
20849
21087
|
}
|
|
21088
|
+
async dispatchScheduledCreate(params) {
|
|
21089
|
+
const input = this.parseScheduledCreateInput(params);
|
|
21090
|
+
const handler = this.deps.createScheduledTaskFromRoute ?? this.handlers.createScheduledTaskFromRoute;
|
|
21091
|
+
if (!handler) {
|
|
21092
|
+
throw new Error("scheduled task creation is not configured");
|
|
21093
|
+
}
|
|
21094
|
+
return await handler(input);
|
|
21095
|
+
}
|
|
21096
|
+
parseScheduledCreateInput(params) {
|
|
21097
|
+
requireOnlyKeys(params, ["coordinatorSession", "timeText", "message", "mode"], "params");
|
|
21098
|
+
const mode = requireOptionalEnum(params, "mode", ["temp", "bound"]);
|
|
21099
|
+
return {
|
|
21100
|
+
coordinatorSession: requireString(params, "coordinatorSession"),
|
|
21101
|
+
timeText: requireString(params, "timeText"),
|
|
21102
|
+
message: requireString(params, "message"),
|
|
21103
|
+
...mode !== undefined ? { mode } : {}
|
|
21104
|
+
};
|
|
21105
|
+
}
|
|
21106
|
+
async dispatchScheduledList(params) {
|
|
21107
|
+
requireOnlyKeys(params, ["coordinatorSession"], "params");
|
|
21108
|
+
const input = {
|
|
21109
|
+
coordinatorSession: requireString(params, "coordinatorSession")
|
|
21110
|
+
};
|
|
21111
|
+
const handler = this.deps.listScheduledTasksFromRoute ?? this.handlers.listScheduledTasksFromRoute;
|
|
21112
|
+
if (!handler) {
|
|
21113
|
+
throw new Error("scheduled task listing is not configured");
|
|
21114
|
+
}
|
|
21115
|
+
return await handler(input);
|
|
21116
|
+
}
|
|
21117
|
+
async dispatchScheduledCancel(params) {
|
|
21118
|
+
requireOnlyKeys(params, ["coordinatorSession", "id"], "params");
|
|
21119
|
+
const input = {
|
|
21120
|
+
coordinatorSession: requireString(params, "coordinatorSession"),
|
|
21121
|
+
id: requireString(params, "id")
|
|
21122
|
+
};
|
|
21123
|
+
const handler = this.deps.cancelScheduledTaskFromRoute ?? this.handlers.cancelScheduledTaskFromRoute;
|
|
21124
|
+
if (!handler) {
|
|
21125
|
+
throw new Error("scheduled task cancellation is not configured");
|
|
21126
|
+
}
|
|
21127
|
+
return await handler(input);
|
|
21128
|
+
}
|
|
20850
21129
|
parseRegisterExternalCoordinatorInput(params) {
|
|
20851
21130
|
requireOnlyKeys(params, ["coordinatorSession", "workspace", "defaultTargetAgent"], "params");
|
|
20852
21131
|
const workspace = requireOptionalString(params, "workspace");
|
|
@@ -21191,6 +21470,9 @@ var init_orchestration_server = __esm(() => {
|
|
|
21191
21470
|
"coordinator.retract_answer",
|
|
21192
21471
|
"coordinator.request_human_input",
|
|
21193
21472
|
"coordinator.review_contested_result",
|
|
21473
|
+
"scheduled.create",
|
|
21474
|
+
"scheduled.list",
|
|
21475
|
+
"scheduled.cancel",
|
|
21194
21476
|
"group.new"
|
|
21195
21477
|
]);
|
|
21196
21478
|
});
|
|
@@ -22184,10 +22466,11 @@ class OrchestrationService {
|
|
|
22184
22466
|
const state = await this.deps.loadState();
|
|
22185
22467
|
const now = this.deps.now().toISOString();
|
|
22186
22468
|
const existing = this.ensureCoordinatorRoutes(state)[input.coordinatorSession];
|
|
22469
|
+
const sameChat = existing?.chatKey === input.chatKey;
|
|
22187
22470
|
const hasAccountId = input.accountId !== undefined;
|
|
22188
22471
|
const hasReplyContextToken = input.replyContextToken !== undefined;
|
|
22189
22472
|
const hasCompleteReplyRoute = hasAccountId && hasReplyContextToken;
|
|
22190
|
-
const shouldPreserveExistingReplyRoute = !hasAccountId && !hasReplyContextToken &&
|
|
22473
|
+
const shouldPreserveExistingReplyRoute = !hasAccountId && !hasReplyContextToken && sameChat;
|
|
22191
22474
|
const replyRoute = hasCompleteReplyRoute ? {
|
|
22192
22475
|
accountId: input.accountId,
|
|
22193
22476
|
replyContextToken: input.replyContextToken
|
|
@@ -22198,7 +22481,9 @@ class OrchestrationService {
|
|
|
22198
22481
|
const route = {
|
|
22199
22482
|
coordinatorSession: input.coordinatorSession,
|
|
22200
22483
|
chatKey: input.chatKey,
|
|
22484
|
+
...input.sessionAlias ? { sessionAlias: input.sessionAlias } : {},
|
|
22201
22485
|
...replyRoute ? replyRoute : {},
|
|
22486
|
+
...buildCoordinatorRouteChatMetadata(input, sameChat ? existing : undefined),
|
|
22202
22487
|
updatedAt: now
|
|
22203
22488
|
};
|
|
22204
22489
|
this.ensureCoordinatorRoutes(state)[input.coordinatorSession] = route;
|
|
@@ -24525,6 +24810,22 @@ class OrchestrationService {
|
|
|
24525
24810
|
task.events = events.slice(-MAX_TASK_EVENTS_PER_TASK);
|
|
24526
24811
|
}
|
|
24527
24812
|
}
|
|
24813
|
+
function buildCoordinatorRouteChatMetadata(input, existing) {
|
|
24814
|
+
const channel = input.channel ?? existing?.channel;
|
|
24815
|
+
const chatType = input.chatType ?? existing?.chatType;
|
|
24816
|
+
const senderId = input.senderId ?? existing?.senderId;
|
|
24817
|
+
const senderName = input.senderName ?? existing?.senderName;
|
|
24818
|
+
const groupId = input.groupId ?? existing?.groupId;
|
|
24819
|
+
const isOwner = input.isOwner ?? existing?.isOwner;
|
|
24820
|
+
return {
|
|
24821
|
+
...channel !== undefined ? { channel } : {},
|
|
24822
|
+
...chatType !== undefined ? { chatType } : {},
|
|
24823
|
+
...senderId !== undefined ? { senderId } : {},
|
|
24824
|
+
...senderName !== undefined ? { senderName } : {},
|
|
24825
|
+
...groupId !== undefined ? { groupId } : {},
|
|
24826
|
+
...isOwner !== undefined ? { isOwner } : {}
|
|
24827
|
+
};
|
|
24828
|
+
}
|
|
24528
24829
|
function isTerminalTaskStatus2(status) {
|
|
24529
24830
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
24530
24831
|
}
|
|
@@ -24650,137 +24951,186 @@ var init_scheduled_scheduler = __esm(() => {
|
|
|
24650
24951
|
DEFAULT_DISPATCH_TIMEOUT_MS = 10 * 60 * 1000;
|
|
24651
24952
|
});
|
|
24652
24953
|
|
|
24653
|
-
// src/scheduled/scheduled-
|
|
24654
|
-
|
|
24655
|
-
|
|
24656
|
-
|
|
24657
|
-
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
|
|
24661
|
-
|
|
24662
|
-
|
|
24663
|
-
|
|
24664
|
-
|
|
24665
|
-
|
|
24666
|
-
|
|
24954
|
+
// src/scheduled/scheduled-dispatch.ts
|
|
24955
|
+
function buildScheduledDispatchTask(deps) {
|
|
24956
|
+
return async (task, abortSignal) => {
|
|
24957
|
+
if (task.session_mode === "temp") {
|
|
24958
|
+
await dispatchTemp(task, abortSignal, deps);
|
|
24959
|
+
return;
|
|
24960
|
+
}
|
|
24961
|
+
await dispatchBound(task, abortSignal, deps);
|
|
24962
|
+
};
|
|
24963
|
+
}
|
|
24964
|
+
async function dispatchBound(task, abortSignal, deps) {
|
|
24965
|
+
const session = await deps.getSession(task.session_alias);
|
|
24966
|
+
if (!session) {
|
|
24967
|
+
throw new Error(`session "${task.session_alias}" not found for scheduled task`);
|
|
24667
24968
|
}
|
|
24668
|
-
|
|
24669
|
-
|
|
24670
|
-
|
|
24671
|
-
|
|
24672
|
-
|
|
24673
|
-
|
|
24674
|
-
|
|
24675
|
-
|
|
24676
|
-
|
|
24677
|
-
|
|
24678
|
-
|
|
24679
|
-
|
|
24680
|
-
|
|
24681
|
-
|
|
24682
|
-
|
|
24683
|
-
|
|
24684
|
-
|
|
24685
|
-
|
|
24969
|
+
const noticeText = `执行定时任务 #${task.id}
|
|
24970
|
+
会话:${toDisplaySessionAlias(task.session_alias)}
|
|
24971
|
+
内容:${preview(task.message)}`;
|
|
24972
|
+
await deps.sendScheduledMessage({
|
|
24973
|
+
chatKey: task.chat_key,
|
|
24974
|
+
taskId: task.id,
|
|
24975
|
+
sessionAlias: task.session_alias,
|
|
24976
|
+
noticeText,
|
|
24977
|
+
promptText: task.message,
|
|
24978
|
+
abortSignal,
|
|
24979
|
+
...task.account_id ? { accountId: task.account_id } : {},
|
|
24980
|
+
...task.reply_context_token ? { replyContextToken: task.reply_context_token } : {}
|
|
24981
|
+
});
|
|
24982
|
+
}
|
|
24983
|
+
async function dispatchTemp(task, abortSignal, deps) {
|
|
24984
|
+
if (!task.agent || !task.workspace) {
|
|
24985
|
+
throw new Error(`temp scheduled task #${task.id} is missing its agent/workspace snapshot`);
|
|
24986
|
+
}
|
|
24987
|
+
const alias = `later-${task.id}`;
|
|
24988
|
+
const transportSession = `${task.workspace}:${alias}`;
|
|
24989
|
+
const session = deps.resolveSession(alias, task.agent, task.workspace, transportSession);
|
|
24990
|
+
const noticeText = `执行定时任务 #${task.id}
|
|
24991
|
+
会话:临时会话(${task.workspace} · ${task.agent})
|
|
24992
|
+
内容:${preview(task.message)}`;
|
|
24993
|
+
try {
|
|
24994
|
+
await deps.sendScheduledMessage({
|
|
24995
|
+
chatKey: task.chat_key,
|
|
24996
|
+
taskId: task.id,
|
|
24997
|
+
sessionAlias: task.session_alias,
|
|
24998
|
+
sessionDescriptor: { alias, agent: task.agent, workspace: task.workspace, transportSession },
|
|
24999
|
+
noticeText,
|
|
25000
|
+
promptText: task.message,
|
|
25001
|
+
abortSignal,
|
|
25002
|
+
...task.account_id ? { accountId: task.account_id } : {},
|
|
25003
|
+
...task.reply_context_token ? { replyContextToken: task.reply_context_token } : {}
|
|
24686
25004
|
});
|
|
25005
|
+
} finally {
|
|
25006
|
+
if (deps.removeSession) {
|
|
25007
|
+
try {
|
|
25008
|
+
await deps.removeSession(session);
|
|
25009
|
+
} catch (error2) {
|
|
25010
|
+
await deps.logger?.error("scheduled.temp_session_close_failed", "failed to close temp scheduled session", { taskId: task.id, transportSession, error: String(error2) });
|
|
25011
|
+
}
|
|
25012
|
+
}
|
|
24687
25013
|
}
|
|
24688
|
-
|
|
24689
|
-
|
|
25014
|
+
}
|
|
25015
|
+
var init_scheduled_dispatch = __esm(() => {
|
|
25016
|
+
init_channel_scope();
|
|
25017
|
+
init_scheduled_render();
|
|
25018
|
+
});
|
|
25019
|
+
|
|
25020
|
+
// src/scheduled/scheduled-route-create.ts
|
|
25021
|
+
async function createScheduledTaskFromRoute(input, deps) {
|
|
25022
|
+
const coordinatorSession = input.coordinatorSession.trim();
|
|
25023
|
+
if (coordinatorSession.length === 0) {
|
|
25024
|
+
throw new Error("coordinatorSession must be a non-empty string");
|
|
24690
25025
|
}
|
|
24691
|
-
|
|
24692
|
-
|
|
24693
|
-
|
|
24694
|
-
const task = this.state.scheduled_tasks[id];
|
|
24695
|
-
if (!task || task.status !== "pending")
|
|
24696
|
-
return false;
|
|
24697
|
-
task.status = "cancelled";
|
|
24698
|
-
task.cancelled_at = this.now().toISOString();
|
|
24699
|
-
await this.save();
|
|
24700
|
-
return true;
|
|
24701
|
-
});
|
|
25026
|
+
const route = deps.state.orchestration.coordinatorRoutes[coordinatorSession];
|
|
25027
|
+
if (!route) {
|
|
25028
|
+
throw new Error(`no chat route is recorded for coordinator session "${coordinatorSession}"`);
|
|
24702
25029
|
}
|
|
24703
|
-
|
|
24704
|
-
|
|
24705
|
-
const nowMs = this.now().getTime();
|
|
24706
|
-
let changed = false;
|
|
24707
|
-
for (const task of Object.values(this.state.scheduled_tasks)) {
|
|
24708
|
-
if (task.status === "pending" && Date.parse(task.execute_at) < nowMs) {
|
|
24709
|
-
task.status = "missed";
|
|
24710
|
-
task.missed_at = this.now().toISOString();
|
|
24711
|
-
changed = true;
|
|
24712
|
-
}
|
|
24713
|
-
if (task.status === "triggering" && !this.claimedInThisSession.has(task.id)) {
|
|
24714
|
-
task.status = "failed";
|
|
24715
|
-
task.failed_at = this.now().toISOString();
|
|
24716
|
-
task.last_error = "process stopped while task was triggering";
|
|
24717
|
-
changed = true;
|
|
24718
|
-
}
|
|
24719
|
-
}
|
|
24720
|
-
if (changed)
|
|
24721
|
-
await this.save();
|
|
24722
|
-
});
|
|
25030
|
+
if (route.chatType !== "direct" && route.chatType !== "group") {
|
|
25031
|
+
throw new Error("scheduled_create requires current chat route metadata");
|
|
24723
25032
|
}
|
|
24724
|
-
|
|
24725
|
-
|
|
24726
|
-
const nowMs = this.now().getTime();
|
|
24727
|
-
const due = this.listPending().filter((task) => Date.parse(task.execute_at) <= nowMs);
|
|
24728
|
-
if (due.length === 0)
|
|
24729
|
-
return [];
|
|
24730
|
-
const at = this.now().toISOString();
|
|
24731
|
-
for (const task of due) {
|
|
24732
|
-
task.status = "triggering";
|
|
24733
|
-
task.triggered_at = at;
|
|
24734
|
-
this.claimedInThisSession.add(task.id);
|
|
24735
|
-
}
|
|
24736
|
-
await this.save();
|
|
24737
|
-
return due.map((task) => ({ ...task }));
|
|
24738
|
-
});
|
|
25033
|
+
if (route.chatType === "group" && route.isOwner !== true) {
|
|
25034
|
+
throw new Error("scheduled_create is owner-only in group chats");
|
|
24739
25035
|
}
|
|
24740
|
-
|
|
24741
|
-
|
|
24742
|
-
const taskId = normalizeId(id);
|
|
24743
|
-
const task = this.state.scheduled_tasks[taskId];
|
|
24744
|
-
if (!task)
|
|
24745
|
-
return;
|
|
24746
|
-
task.status = "executed";
|
|
24747
|
-
task.executed_at = this.now().toISOString();
|
|
24748
|
-
this.claimedInThisSession.delete(taskId);
|
|
24749
|
-
await this.save();
|
|
24750
|
-
});
|
|
25036
|
+
if (deps.supportsScheduledMessages && !deps.supportsScheduledMessages(route.chatKey)) {
|
|
25037
|
+
throw new Error("current channel does not support scheduled tasks");
|
|
24751
25038
|
}
|
|
24752
|
-
|
|
24753
|
-
|
|
24754
|
-
|
|
24755
|
-
const task = this.state.scheduled_tasks[taskId];
|
|
24756
|
-
if (!task)
|
|
24757
|
-
return;
|
|
24758
|
-
task.status = "failed";
|
|
24759
|
-
task.failed_at = this.now().toISOString();
|
|
24760
|
-
task.last_error = error2 instanceof Error ? error2.message : String(error2);
|
|
24761
|
-
this.claimedInThisSession.delete(taskId);
|
|
24762
|
-
await this.save();
|
|
24763
|
-
});
|
|
25039
|
+
const message = input.message.trim();
|
|
25040
|
+
if (message.length === 0) {
|
|
25041
|
+
throw new Error("message must be a non-empty string");
|
|
24764
25042
|
}
|
|
24765
|
-
|
|
24766
|
-
|
|
24767
|
-
const id = normalizeId(this.generateId()).replace(/[^0-9a-z]/g, "").slice(0, 6);
|
|
24768
|
-
if (id.length >= 4 && !this.state.scheduled_tasks[id])
|
|
24769
|
-
return id;
|
|
24770
|
-
}
|
|
24771
|
-
throw new Error("failed to generate unique scheduled task id");
|
|
25043
|
+
if (message.startsWith("/")) {
|
|
25044
|
+
throw new Error("scheduled_create does not support slash-prefixed weacpx commands");
|
|
24772
25045
|
}
|
|
24773
|
-
|
|
24774
|
-
|
|
25046
|
+
if (!route.sessionAlias) {
|
|
25047
|
+
throw new Error("scheduled_create requires current session route metadata");
|
|
24775
25048
|
}
|
|
24776
|
-
|
|
24777
|
-
|
|
25049
|
+
const session = await deps.sessions.getSession(route.sessionAlias);
|
|
25050
|
+
if (!session) {
|
|
25051
|
+
throw new Error(`session "${route.sessionAlias}" recorded for coordinator session "${coordinatorSession}" was not found`);
|
|
24778
25052
|
}
|
|
25053
|
+
if (session.transportSession !== coordinatorSession) {
|
|
25054
|
+
throw new Error(`session "${route.sessionAlias}" is no longer attached to coordinator session "${coordinatorSession}"`);
|
|
25055
|
+
}
|
|
25056
|
+
const executeAt = parseRouteScheduledTime(input.timeText, deps.now?.() ?? new Date);
|
|
25057
|
+
const mode = input.mode ?? (deps.config.later?.defaultMode === "bind" ? "bound" : "temp");
|
|
25058
|
+
return await deps.scheduled.createTask({
|
|
25059
|
+
chatKey: route.chatKey,
|
|
25060
|
+
sessionAlias: session.alias,
|
|
25061
|
+
executeAt,
|
|
25062
|
+
message,
|
|
25063
|
+
sessionMode: mode,
|
|
25064
|
+
...mode === "temp" ? { agent: session.agent, workspace: session.workspace } : {},
|
|
25065
|
+
...route.accountId ? { accountId: route.accountId } : {},
|
|
25066
|
+
...route.replyContextToken ? { replyContextToken: route.replyContextToken } : {},
|
|
25067
|
+
sourceLabel: "mcp:scheduled_create"
|
|
25068
|
+
});
|
|
24779
25069
|
}
|
|
24780
|
-
function
|
|
24781
|
-
|
|
25070
|
+
function parseRouteScheduledTime(timeText, now) {
|
|
25071
|
+
const timeTokens = timeText.trim().split(/\s+/).filter((token) => token.length > 0);
|
|
25072
|
+
if (timeTokens.length === 0) {
|
|
25073
|
+
throw new Error(formatLaterTimeParseError("missing_time"));
|
|
25074
|
+
}
|
|
25075
|
+
const parsed = parseLaterTime([...timeTokens, "__scheduled_create_message__"], now);
|
|
25076
|
+
if (!parsed.ok) {
|
|
25077
|
+
throw new Error(formatLaterTimeParseError(parsed.code, parsed.value));
|
|
25078
|
+
}
|
|
25079
|
+
if (parsed.messageStartIndex !== timeTokens.length) {
|
|
25080
|
+
throw new Error("timeText must contain only the time expression; put the scheduled content in message");
|
|
25081
|
+
}
|
|
25082
|
+
return parsed.executeAt;
|
|
24782
25083
|
}
|
|
24783
|
-
|
|
25084
|
+
function formatLaterTimeParseError(code, value) {
|
|
25085
|
+
switch (code) {
|
|
25086
|
+
case "missing_message":
|
|
25087
|
+
return "message must be provided separately from timeText";
|
|
25088
|
+
case "too_soon":
|
|
25089
|
+
return "scheduled task time must be at least 10 seconds in the future";
|
|
25090
|
+
case "out_of_range":
|
|
25091
|
+
return "scheduled task time must be within 7 days";
|
|
25092
|
+
case "past_today_time":
|
|
25093
|
+
return `today at ${value} has already passed; choose a future time or use tomorrow`;
|
|
25094
|
+
case "unrecognized_time":
|
|
25095
|
+
case "missing_time":
|
|
25096
|
+
default:
|
|
25097
|
+
return "unrecognized timeText; supported examples: in 2h, 30分钟后, tomorrow 09:00, 周五 09:00";
|
|
25098
|
+
}
|
|
25099
|
+
}
|
|
25100
|
+
var init_scheduled_route_create = __esm(() => {
|
|
25101
|
+
init_parse_later_time();
|
|
25102
|
+
});
|
|
25103
|
+
|
|
25104
|
+
// src/scheduled/scheduled-route-manage.ts
|
|
25105
|
+
async function listScheduledTasksFromRoute(input, deps) {
|
|
25106
|
+
resolveOwnedCoordinatorRoute(input.coordinatorSession, deps.state, "scheduled_list");
|
|
25107
|
+
return deps.scheduled.listPending();
|
|
25108
|
+
}
|
|
25109
|
+
async function cancelScheduledTaskFromRoute(input, deps) {
|
|
25110
|
+
resolveOwnedCoordinatorRoute(input.coordinatorSession, deps.state, "scheduled_cancel");
|
|
25111
|
+
const cancelled = await deps.scheduled.cancelPending(input.id);
|
|
25112
|
+
return { id: normalizeId(input.id), cancelled };
|
|
25113
|
+
}
|
|
25114
|
+
function resolveOwnedCoordinatorRoute(coordinatorSession, state, label) {
|
|
25115
|
+
const session = coordinatorSession.trim();
|
|
25116
|
+
if (session.length === 0) {
|
|
25117
|
+
throw new Error("coordinatorSession must be a non-empty string");
|
|
25118
|
+
}
|
|
25119
|
+
const route = state.orchestration.coordinatorRoutes[session];
|
|
25120
|
+
if (!route) {
|
|
25121
|
+
throw new Error(`no chat route is recorded for coordinator session "${session}"`);
|
|
25122
|
+
}
|
|
25123
|
+
if (route.chatType !== "direct" && route.chatType !== "group") {
|
|
25124
|
+
throw new Error(`${label} requires current chat route metadata`);
|
|
25125
|
+
}
|
|
25126
|
+
if (route.chatType === "group" && route.isOwner !== true) {
|
|
25127
|
+
throw new Error(`${label} is owner-only in group chats`);
|
|
25128
|
+
}
|
|
25129
|
+
return route;
|
|
25130
|
+
}
|
|
25131
|
+
var init_scheduled_route_manage = __esm(() => {
|
|
25132
|
+
init_scheduled_service();
|
|
25133
|
+
});
|
|
24784
25134
|
|
|
24785
25135
|
// src/sessions/session-service.ts
|
|
24786
25136
|
class SessionService {
|
|
@@ -26417,7 +26767,7 @@ function buildWeacpxMcpServerSpec(input) {
|
|
|
26417
26767
|
"mcp-stdio",
|
|
26418
26768
|
"--coordinator-session",
|
|
26419
26769
|
input.coordinatorSession,
|
|
26420
|
-
...input.sourceHandle ? ["--source-handle", input.sourceHandle] : []
|
|
26770
|
+
...input.sourceHandle ? ["--source-handle", input.sourceHandle] : ["--internal-session-tools"]
|
|
26421
26771
|
]
|
|
26422
26772
|
};
|
|
26423
26773
|
}
|
|
@@ -27867,32 +28217,32 @@ async function buildApp(paths, deps = {}) {
|
|
|
27867
28217
|
}
|
|
27868
28218
|
const progressHeartbeatInterval = startProgressHeartbeat(orchestration, config2, logger2, deps.channel ?? null);
|
|
27869
28219
|
const orchestrationEndpoint = createOrchestrationEndpoint(paths.orchestrationSocketPath ?? resolveOrchestrationSocketPathFromConfigPath(paths.configPath));
|
|
27870
|
-
const orchestrationServer = new OrchestrationServer(orchestrationEndpoint, orchestration
|
|
28220
|
+
const orchestrationServer = new OrchestrationServer(orchestrationEndpoint, orchestration, {
|
|
28221
|
+
createScheduledTaskFromRoute: async (input) => await createScheduledTaskFromRoute(input, {
|
|
28222
|
+
state,
|
|
28223
|
+
config: config2,
|
|
28224
|
+
sessions,
|
|
28225
|
+
scheduled: scheduledService,
|
|
28226
|
+
...deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : {}
|
|
28227
|
+
}),
|
|
28228
|
+
listScheduledTasksFromRoute: async (input) => await listScheduledTasksFromRoute(input, { state, scheduled: scheduledService }),
|
|
28229
|
+
cancelScheduledTaskFromRoute: async (input) => await cancelScheduledTaskFromRoute(input, { state, scheduled: scheduledService })
|
|
28230
|
+
});
|
|
27871
28231
|
const router = new CommandRouter(sessions, transport, config2, configStore, logger2, undefined, orchestration, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined);
|
|
27872
28232
|
const agent = new ConsoleAgent(router, logger2);
|
|
27873
28233
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
27874
|
-
dispatchTask:
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27883
|
-
|
|
27884
|
-
|
|
27885
|
-
|
|
27886
|
-
chatKey: task.chat_key,
|
|
27887
|
-
taskId: task.id,
|
|
27888
|
-
sessionAlias: task.session_alias,
|
|
27889
|
-
noticeText,
|
|
27890
|
-
promptText: task.message,
|
|
27891
|
-
abortSignal,
|
|
27892
|
-
...task.account_id ? { accountId: task.account_id } : {},
|
|
27893
|
-
...task.reply_context_token ? { replyContextToken: task.reply_context_token } : {}
|
|
27894
|
-
});
|
|
27895
|
-
},
|
|
28234
|
+
dispatchTask: buildScheduledDispatchTask({
|
|
28235
|
+
getSession: (alias) => sessions.getSession(alias),
|
|
28236
|
+
resolveSession: (alias, agent2, workspace, transportSession) => sessions.resolveSession(alias, agent2, workspace, transportSession),
|
|
28237
|
+
sendScheduledMessage: async (input) => {
|
|
28238
|
+
if (!deps.channel?.sendScheduledMessage) {
|
|
28239
|
+
throw new Error("no channel runtime available for scheduled task dispatch");
|
|
28240
|
+
}
|
|
28241
|
+
await deps.channel.sendScheduledMessage(input);
|
|
28242
|
+
},
|
|
28243
|
+
...transport.removeSession ? { removeSession: (session) => transport.removeSession(session) } : {},
|
|
28244
|
+
logger: logger2
|
|
28245
|
+
}),
|
|
27896
28246
|
logger: logger2
|
|
27897
28247
|
});
|
|
27898
28248
|
return {
|
|
@@ -28065,8 +28415,9 @@ var init_main = __esm(async () => {
|
|
|
28065
28415
|
init_build_coordinator_prompt();
|
|
28066
28416
|
init_scheduled_scheduler();
|
|
28067
28417
|
init_scheduled_service();
|
|
28068
|
-
|
|
28069
|
-
|
|
28418
|
+
init_scheduled_dispatch();
|
|
28419
|
+
init_scheduled_route_create();
|
|
28420
|
+
init_scheduled_route_manage();
|
|
28070
28421
|
init_session_service();
|
|
28071
28422
|
init_state_store();
|
|
28072
28423
|
init_run_console();
|
|
@@ -41598,12 +41949,13 @@ var sortSchema = exports_external.enum(["updatedAt", "createdAt"]);
|
|
|
41598
41949
|
var orderSchema = exports_external.enum(["asc", "desc"]);
|
|
41599
41950
|
var contestedDecisionSchema = exports_external.enum(["accept", "discard"]);
|
|
41600
41951
|
var taskWatchModeSchema = exports_external.enum(["next_event", "until_attention_or_terminal"]);
|
|
41952
|
+
var scheduledModeSchema = exports_external.enum(["temp", "bound"]);
|
|
41601
41953
|
var taskQuestionSchema = exports_external.object({
|
|
41602
41954
|
taskId: exports_external.string().min(1),
|
|
41603
41955
|
questionId: exports_external.string().min(1)
|
|
41604
41956
|
}).strict();
|
|
41605
41957
|
function buildWeacpxMcpToolRegistry(input) {
|
|
41606
|
-
const { transport, coordinatorSession, sourceHandle, isExternalCoordinator, availableAgents } = input;
|
|
41958
|
+
const { transport, coordinatorSession, sourceHandle, isExternalCoordinator, internalSessionTools, availableAgents } = input;
|
|
41607
41959
|
const tools = [
|
|
41608
41960
|
{
|
|
41609
41961
|
name: "delegate_request",
|
|
@@ -41821,6 +42173,63 @@ function buildWeacpxMcpToolRegistry(input) {
|
|
|
41821
42173
|
})
|
|
41822
42174
|
}
|
|
41823
42175
|
];
|
|
42176
|
+
if (internalSessionTools && !isExternalCoordinator && !sourceHandle) {
|
|
42177
|
+
tools.push({
|
|
42178
|
+
name: "scheduled_create",
|
|
42179
|
+
description: "Create a one-shot scheduled task for the current conversation session using the recorded chat route. Provide only the time expression and message; routing/session/account details are resolved by weacpx.",
|
|
42180
|
+
inputSchema: exports_external.object({
|
|
42181
|
+
timeText: exports_external.string().min(1).describe("Time expression, e.g. 'in 2h', '30分钟后', 'tomorrow 09:00', or '周五 09:00'."),
|
|
42182
|
+
message: exports_external.string().min(1).describe("Natural-language message to send to the current session at the scheduled time."),
|
|
42183
|
+
mode: scheduledModeSchema.describe("'temp' creates a temporary one-shot session; 'bound' sends to the current bound session.").optional()
|
|
42184
|
+
}).strict(),
|
|
42185
|
+
handler: async (args) => await asToolResult(async () => {
|
|
42186
|
+
const input2 = args;
|
|
42187
|
+
const task = await transport.scheduledCreate({
|
|
42188
|
+
coordinatorSession,
|
|
42189
|
+
timeText: input2.timeText,
|
|
42190
|
+
message: input2.message,
|
|
42191
|
+
...input2.mode ? { mode: input2.mode } : {}
|
|
42192
|
+
});
|
|
42193
|
+
return createSuccessResult(`Scheduled task #${task.id} created for ${task.execute_at}.`, {
|
|
42194
|
+
id: task.id,
|
|
42195
|
+
status: task.status,
|
|
42196
|
+
executeAt: task.execute_at,
|
|
42197
|
+
sessionAlias: task.session_alias,
|
|
42198
|
+
sessionMode: task.session_mode ?? "bound"
|
|
42199
|
+
});
|
|
42200
|
+
})
|
|
42201
|
+
});
|
|
42202
|
+
tools.push({
|
|
42203
|
+
name: "scheduled_list",
|
|
42204
|
+
description: "List pending one-shot scheduled tasks (global). Use to recover task ids before cancelling, or to see what is scheduled. Owner-only in group chats. Routing and account are resolved from the current session; pass no other arguments.",
|
|
42205
|
+
inputSchema: exports_external.object({}).strict(),
|
|
42206
|
+
handler: async () => await asToolResult(async () => {
|
|
42207
|
+
const tasks = await transport.scheduledList({ coordinatorSession });
|
|
42208
|
+
return createSuccessResult(renderScheduledList(tasks), {
|
|
42209
|
+
tasks: tasks.map((task) => ({
|
|
42210
|
+
id: task.id,
|
|
42211
|
+
executeAt: task.execute_at,
|
|
42212
|
+
message: task.message,
|
|
42213
|
+
sessionAlias: task.session_alias,
|
|
42214
|
+
sessionMode: task.session_mode ?? "bound",
|
|
42215
|
+
chatKey: task.chat_key
|
|
42216
|
+
}))
|
|
42217
|
+
});
|
|
42218
|
+
})
|
|
42219
|
+
});
|
|
42220
|
+
tools.push({
|
|
42221
|
+
name: "scheduled_cancel",
|
|
42222
|
+
description: "Cancel a pending scheduled task by id. Owner-only in group chats. Returns whether a pending task with that id was found and cancelled. Routing is resolved from the current session.",
|
|
42223
|
+
inputSchema: exports_external.object({
|
|
42224
|
+
id: exports_external.string().min(1).describe("The scheduled task id, e.g. 'k8f2' (a leading # is allowed).")
|
|
42225
|
+
}).strict(),
|
|
42226
|
+
handler: async (args) => await asToolResult(async () => {
|
|
42227
|
+
const { id } = args;
|
|
42228
|
+
const result = await transport.scheduledCancel({ coordinatorSession, id });
|
|
42229
|
+
return createSuccessResult(renderScheduledCancel(result), { id: result.id, cancelled: result.cancelled });
|
|
42230
|
+
})
|
|
42231
|
+
});
|
|
42232
|
+
}
|
|
41824
42233
|
if (isExternalCoordinator) {
|
|
41825
42234
|
const externalCoordinatorIncompatibleTools = new Set([
|
|
41826
42235
|
"coordinator_request_human_input"
|
|
@@ -41900,6 +42309,19 @@ function renderTaskList(tasks) {
|
|
|
41900
42309
|
return ["Tasks for the current coordinator:", ...tasks.map((task) => renderTaskListItem(task))].join(`
|
|
41901
42310
|
`);
|
|
41902
42311
|
}
|
|
42312
|
+
function renderScheduledList(tasks) {
|
|
42313
|
+
if (tasks.length === 0) {
|
|
42314
|
+
return "There are no pending scheduled tasks.";
|
|
42315
|
+
}
|
|
42316
|
+
return [
|
|
42317
|
+
"Pending scheduled tasks:",
|
|
42318
|
+
...tasks.map((task) => `- #${task.id} at ${task.execute_at} [${task.session_mode ?? "bound"}] -> ${task.session_alias}: ${task.message}`)
|
|
42319
|
+
].join(`
|
|
42320
|
+
`);
|
|
42321
|
+
}
|
|
42322
|
+
function renderScheduledCancel(result) {
|
|
42323
|
+
return result.cancelled ? `Scheduled task #${result.id} cancelled.` : `No pending scheduled task #${result.id} found.`;
|
|
42324
|
+
}
|
|
41903
42325
|
function renderTaskListItem(task) {
|
|
41904
42326
|
const role = task.role ? ` / ${task.role}` : "";
|
|
41905
42327
|
const group = task.groupId ? `; group: ${task.groupId}` : "";
|
|
@@ -42070,6 +42492,15 @@ class OrchestrationClient {
|
|
|
42070
42492
|
async createGroup(input) {
|
|
42071
42493
|
return await this.request("group.new", input);
|
|
42072
42494
|
}
|
|
42495
|
+
async scheduledCreate(input) {
|
|
42496
|
+
return await this.request("scheduled.create", input);
|
|
42497
|
+
}
|
|
42498
|
+
async scheduledList(input) {
|
|
42499
|
+
return await this.request("scheduled.list", input);
|
|
42500
|
+
}
|
|
42501
|
+
async scheduledCancel(input) {
|
|
42502
|
+
return await this.request("scheduled.cancel", input);
|
|
42503
|
+
}
|
|
42073
42504
|
async request(method, params, timeoutMs = this.timeoutMs) {
|
|
42074
42505
|
const id = this.createId();
|
|
42075
42506
|
return await new Promise((resolve, reject) => {
|
|
@@ -42182,7 +42613,25 @@ function createOrchestrationTransport(endpoint, deps = {}) {
|
|
|
42182
42613
|
},
|
|
42183
42614
|
coordinatorAnswerQuestion: async (input) => await client.coordinatorAnswerQuestion(input),
|
|
42184
42615
|
coordinatorRequestHumanInput: async (input) => await client.coordinatorRequestHumanInput(input),
|
|
42185
|
-
coordinatorReviewContestedResult: async (input) => await client.coordinatorReviewContestedResult(input)
|
|
42616
|
+
coordinatorReviewContestedResult: async (input) => await client.coordinatorReviewContestedResult(input),
|
|
42617
|
+
scheduledCreate: async (input) => {
|
|
42618
|
+
if (!client.scheduledCreate) {
|
|
42619
|
+
throw new Error("orchestration client scheduledCreate is not configured");
|
|
42620
|
+
}
|
|
42621
|
+
return await client.scheduledCreate(input);
|
|
42622
|
+
},
|
|
42623
|
+
scheduledList: async (input) => {
|
|
42624
|
+
if (!client.scheduledList) {
|
|
42625
|
+
throw new Error("orchestration client scheduledList is not configured");
|
|
42626
|
+
}
|
|
42627
|
+
return await client.scheduledList(input);
|
|
42628
|
+
},
|
|
42629
|
+
scheduledCancel: async (input) => {
|
|
42630
|
+
if (!client.scheduledCancel) {
|
|
42631
|
+
throw new Error("orchestration client scheduledCancel is not configured");
|
|
42632
|
+
}
|
|
42633
|
+
return await client.scheduledCancel(input);
|
|
42634
|
+
}
|
|
42186
42635
|
};
|
|
42187
42636
|
}
|
|
42188
42637
|
|
|
@@ -42238,6 +42687,7 @@ function createWeacpxMcpServer(options) {
|
|
|
42238
42687
|
coordinatorSession: identity.coordinatorSession,
|
|
42239
42688
|
...identity.sourceHandle ? { sourceHandle: identity.sourceHandle } : {},
|
|
42240
42689
|
...identity.isExternalCoordinator ? { isExternalCoordinator: true } : {},
|
|
42690
|
+
...identity.internalSessionTools ? { internalSessionTools: true } : {},
|
|
42241
42691
|
...options.availableAgents ? { availableAgents: options.availableAgents } : {}
|
|
42242
42692
|
});
|
|
42243
42693
|
return toolState;
|
|
@@ -42673,7 +43123,8 @@ async function resolveMcpIdentity(server, options) {
|
|
|
42673
43123
|
return {
|
|
42674
43124
|
coordinatorSession: options.coordinatorSession,
|
|
42675
43125
|
...options.sourceHandle ? { sourceHandle: options.sourceHandle } : {},
|
|
42676
|
-
...options.isExternalCoordinator ? { isExternalCoordinator: true } : {}
|
|
43126
|
+
...options.isExternalCoordinator ? { isExternalCoordinator: true } : {},
|
|
43127
|
+
...options.internalSessionTools ? { internalSessionTools: true } : {}
|
|
42677
43128
|
};
|
|
42678
43129
|
}
|
|
42679
43130
|
throw new McpError(ErrorCode.InvalidRequest, "weacpx MCP identity is not configured; run through `weacpx mcp-stdio` or provide --coordinator-session");
|
|
@@ -42762,6 +43213,7 @@ async function runWeacpxMcpServer(options) {
|
|
|
42762
43213
|
transport,
|
|
42763
43214
|
...options.coordinatorSession ? { coordinatorSession: options.coordinatorSession } : {},
|
|
42764
43215
|
...options.sourceHandle ? { sourceHandle: options.sourceHandle } : {},
|
|
43216
|
+
...options.internalSessionTools ? { internalSessionTools: true } : {},
|
|
42765
43217
|
...options.resolveIdentity ? { resolveIdentity: options.resolveIdentity } : {},
|
|
42766
43218
|
...options.availableAgents ? { availableAgents: options.availableAgents } : {}
|
|
42767
43219
|
});
|
|
@@ -42856,6 +43308,11 @@ function parseCoordinatorSession(args, env = process.env) {
|
|
|
42856
43308
|
});
|
|
42857
43309
|
}
|
|
42858
43310
|
|
|
43311
|
+
// src/mcp/parse-internal-session-tools.ts
|
|
43312
|
+
function parseInternalSessionToolsFlag(args, _env = process.env) {
|
|
43313
|
+
return args.includes("--internal-session-tools");
|
|
43314
|
+
}
|
|
43315
|
+
|
|
42859
43316
|
// src/mcp/parse-source-handle.ts
|
|
42860
43317
|
function parseSourceHandle(args, env = process.env) {
|
|
42861
43318
|
return parseStringFlag(args, env, {
|
|
@@ -42868,13 +43325,19 @@ function parseSourceHandle(args, env = process.env) {
|
|
|
42868
43325
|
init_workspace_path();
|
|
42869
43326
|
init_workspace_name();
|
|
42870
43327
|
init_state_store();
|
|
43328
|
+
init_channel_scope();
|
|
43329
|
+
init_scheduled_render();
|
|
43330
|
+
init_scheduled_service();
|
|
42871
43331
|
|
|
42872
43332
|
// src/onboarding.ts
|
|
42873
43333
|
init_workspace_path();
|
|
42874
43334
|
init_workspace_name();
|
|
43335
|
+
init_default_workspace();
|
|
42875
43336
|
init_agent_templates();
|
|
42876
43337
|
function isFirstUse(config2, state) {
|
|
42877
|
-
|
|
43338
|
+
const workspaceNames = Object.keys(config2.workspaces ?? {});
|
|
43339
|
+
const onlyDefaultOrEmpty = workspaceNames.length === 0 || workspaceNames.length === 1 && workspaceNames[0] === DEFAULT_HOME_WORKSPACE_NAME;
|
|
43340
|
+
return Object.keys(state.sessions ?? {}).length === 0 && onlyDefaultOrEmpty && (config2.plugins ?? []).length === 0;
|
|
42878
43341
|
}
|
|
42879
43342
|
async function maybeRunFirstUseOnboarding(input) {
|
|
42880
43343
|
if (!isFirstUse(input.config, input.state))
|
|
@@ -44542,7 +45005,8 @@ function createMcpStdioIdentityResolver(input) {
|
|
|
44542
45005
|
return {
|
|
44543
45006
|
coordinatorSession: resolvedCoordinatorSession,
|
|
44544
45007
|
...sourceHandle ? { sourceHandle } : {},
|
|
44545
|
-
...startup.kind === "external-coordinator" ? { isExternalCoordinator: true } : {}
|
|
45008
|
+
...startup.kind === "external-coordinator" ? { isExternalCoordinator: true } : {},
|
|
45009
|
+
...input.internalSessionTools && startup.kind === "existing-session" && !sourceHandle ? { internalSessionTools: true } : {}
|
|
44546
45010
|
};
|
|
44547
45011
|
};
|
|
44548
45012
|
}
|
|
@@ -44587,6 +45051,7 @@ var HELP_LINES = [
|
|
|
44587
45051
|
"weacpx version - 查看版本",
|
|
44588
45052
|
"weacpx agent|agents list|add|rm|templates - 管理本机 Agent",
|
|
44589
45053
|
"weacpx workspace list|add [name] [--raw]|rm <name> - 管理本机工作区(别名:ws)",
|
|
45054
|
+
"weacpx later|lt list|cancel <id> - 管理本机待执行定时任务",
|
|
44590
45055
|
"weacpx mcp-stdio [--coordinator-session <session>] [--source-handle <handle>] [--workspace <name>] - 启动 MCP stdio 服务"
|
|
44591
45056
|
];
|
|
44592
45057
|
function getUsageText() {
|
|
@@ -44674,6 +45139,17 @@ async function runCli(args, deps = {}) {
|
|
|
44674
45139
|
}
|
|
44675
45140
|
return result;
|
|
44676
45141
|
}
|
|
45142
|
+
case "later":
|
|
45143
|
+
case "lt": {
|
|
45144
|
+
const result = await handleLaterCli(args.slice(1), { print });
|
|
45145
|
+
if (result === null) {
|
|
45146
|
+
for (const line of HELP_LINES) {
|
|
45147
|
+
print(line);
|
|
45148
|
+
}
|
|
45149
|
+
return 1;
|
|
45150
|
+
}
|
|
45151
|
+
return result;
|
|
45152
|
+
}
|
|
44677
45153
|
case "plugin": {
|
|
44678
45154
|
const result = await handlePluginCli(args.slice(1), await createPluginCliDeps({
|
|
44679
45155
|
print,
|
|
@@ -45022,6 +45498,48 @@ async function agentRemove(rawName, print) {
|
|
|
45022
45498
|
print(`Agent「${name}」已删除`);
|
|
45023
45499
|
return 0;
|
|
45024
45500
|
}
|
|
45501
|
+
async function handleLaterCli(args, deps) {
|
|
45502
|
+
const subcommand = args[0];
|
|
45503
|
+
switch (subcommand) {
|
|
45504
|
+
case "list":
|
|
45505
|
+
if (args.length !== 1)
|
|
45506
|
+
return null;
|
|
45507
|
+
return await laterList(deps.print);
|
|
45508
|
+
case "cancel":
|
|
45509
|
+
if (args.length !== 2 || !args[1])
|
|
45510
|
+
return null;
|
|
45511
|
+
return await laterCancel(args[1], deps.print);
|
|
45512
|
+
default:
|
|
45513
|
+
return null;
|
|
45514
|
+
}
|
|
45515
|
+
}
|
|
45516
|
+
async function laterList(print) {
|
|
45517
|
+
const scheduled = await createCliScheduledTaskService();
|
|
45518
|
+
print(renderLaterList(scheduled.listPending(), (alias) => toDisplaySessionAlias(alias)));
|
|
45519
|
+
return 0;
|
|
45520
|
+
}
|
|
45521
|
+
async function laterCancel(rawId, print) {
|
|
45522
|
+
const id = normalizeId(rawId);
|
|
45523
|
+
if (id.length === 0) {
|
|
45524
|
+
print("定时任务 ID 不能为空。");
|
|
45525
|
+
return 1;
|
|
45526
|
+
}
|
|
45527
|
+
const scheduled = await createCliScheduledTaskService();
|
|
45528
|
+
const ok = await scheduled.cancelPending(id);
|
|
45529
|
+
if (!ok) {
|
|
45530
|
+
print(`未找到待执行的定时任务 #${id}。`);
|
|
45531
|
+
print("可以用 weacpx later list 查看当前待执行任务。");
|
|
45532
|
+
return 1;
|
|
45533
|
+
}
|
|
45534
|
+
print(`已取消定时任务 #${id}`);
|
|
45535
|
+
return 0;
|
|
45536
|
+
}
|
|
45537
|
+
async function createCliScheduledTaskService() {
|
|
45538
|
+
const runtimePaths = (await init_main().then(() => exports_main)).resolveRuntimePaths();
|
|
45539
|
+
const stateStore = new StateStore(runtimePaths.statePath);
|
|
45540
|
+
const state = await stateStore.load();
|
|
45541
|
+
return new ScheduledTaskService(state, stateStore);
|
|
45542
|
+
}
|
|
45025
45543
|
function resolveConfigPathForCurrentEnv() {
|
|
45026
45544
|
return process.env.WEACPX_CONFIG ?? `${requireHome2()}/.weacpx/config.json`;
|
|
45027
45545
|
}
|
|
@@ -45148,10 +45666,12 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
45148
45666
|
let transport;
|
|
45149
45667
|
let identityResolver;
|
|
45150
45668
|
let availableAgents;
|
|
45669
|
+
let internalSessionTools = false;
|
|
45151
45670
|
try {
|
|
45152
45671
|
const parsedCoordinatorSession = parseCoordinatorSession(args, process.env);
|
|
45153
45672
|
sourceHandle = parseSourceHandle(args, process.env);
|
|
45154
45673
|
const workspace = parseCoordinatorWorkspace(args, process.env);
|
|
45674
|
+
const requestedInternalSessionTools = parseInternalSessionToolsFlag(args, process.env);
|
|
45155
45675
|
endpoint = resolveDefaultOrchestrationEndpoint(process.env, process.platform);
|
|
45156
45676
|
const client = new OrchestrationClient(endpoint);
|
|
45157
45677
|
transport = createOrchestrationTransport(endpoint, { client });
|
|
@@ -45166,10 +45686,12 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
45166
45686
|
workspace,
|
|
45167
45687
|
config: config2,
|
|
45168
45688
|
state,
|
|
45169
|
-
client
|
|
45689
|
+
client,
|
|
45690
|
+
internalSessionTools: requestedInternalSessionTools
|
|
45170
45691
|
});
|
|
45171
45692
|
const eagerIdentity = parsedCoordinatorSession ? await resolveIdentity({ clientName: undefined, listRoots: async () => [] }) : null;
|
|
45172
45693
|
coordinatorSession = eagerIdentity?.coordinatorSession ?? "";
|
|
45694
|
+
internalSessionTools = eagerIdentity?.internalSessionTools ?? false;
|
|
45173
45695
|
identityResolver = eagerIdentity ? undefined : resolveIdentity;
|
|
45174
45696
|
} catch (error2) {
|
|
45175
45697
|
(deps.stderr ?? ((text) => process.stderr.write(text)))(`${error2 instanceof Error ? error2.message : String(error2)}
|
|
@@ -45180,6 +45702,7 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
45180
45702
|
transport,
|
|
45181
45703
|
...coordinatorSession ? { coordinatorSession } : {},
|
|
45182
45704
|
...sourceHandle ? { sourceHandle } : {},
|
|
45705
|
+
...internalSessionTools ? { internalSessionTools: true } : {},
|
|
45183
45706
|
...identityResolver ? { resolveIdentity: identityResolver } : {},
|
|
45184
45707
|
...availableAgents ? { availableAgents } : {},
|
|
45185
45708
|
onDiagnostic: (event, context) => {
|