weacpx 0.5.0 → 0.5.2
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 +2 -2
- package/dist/channels/types.d.ts +12 -0
- package/dist/cli.js +873 -344
- 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";
|
|
@@ -14617,6 +14953,12 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
14617
14953
|
text: requestText,
|
|
14618
14954
|
...media.length > 0 ? { media } : {},
|
|
14619
14955
|
replyContextToken: contextToken,
|
|
14956
|
+
metadata: {
|
|
14957
|
+
channel: "weixin",
|
|
14958
|
+
chatType: full.group_id ? "group" : "direct",
|
|
14959
|
+
...full.from_user_id ? { senderId: full.from_user_id } : {},
|
|
14960
|
+
...full.group_id ? { groupId: full.group_id } : {}
|
|
14961
|
+
},
|
|
14620
14962
|
perfSpan
|
|
14621
14963
|
};
|
|
14622
14964
|
try {
|
|
@@ -15552,7 +15894,11 @@ async function executeScheduledTurn(input, deps) {
|
|
|
15552
15894
|
text: input.promptText,
|
|
15553
15895
|
...deliveryContextToken ? { replyContextToken: deliveryContextToken } : {},
|
|
15554
15896
|
...input.abortSignal ? { abortSignal: input.abortSignal } : {},
|
|
15555
|
-
metadata: {
|
|
15897
|
+
metadata: {
|
|
15898
|
+
channel: "weixin",
|
|
15899
|
+
scheduledSessionAlias: input.sessionAlias,
|
|
15900
|
+
...input.sessionDescriptor ? { scheduledSessionDescriptor: input.sessionDescriptor } : {}
|
|
15901
|
+
}
|
|
15556
15902
|
},
|
|
15557
15903
|
onReplySegment: sendReplySegment
|
|
15558
15904
|
});
|
|
@@ -15884,78 +16230,6 @@ var init_weixin_channel = __esm(() => {
|
|
|
15884
16230
|
init_consumer_lock();
|
|
15885
16231
|
});
|
|
15886
16232
|
|
|
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
16233
|
// src/plugins/known-plugins.ts
|
|
15960
16234
|
function listKnownPlugins() {
|
|
15961
16235
|
return KNOWN_PLUGINS.map((plugin) => ({ ...plugin, channels: [...plugin.channels] }));
|
|
@@ -18138,7 +18412,7 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
18138
18412
|
return { text: lines.join(`
|
|
18139
18413
|
`) };
|
|
18140
18414
|
}
|
|
18141
|
-
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
18415
|
+
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18142
18416
|
const effectiveReplyMode = session.replyMode ?? context.config?.channel.replyMode ?? "verbose";
|
|
18143
18417
|
if (!session.replyMode)
|
|
18144
18418
|
session.replyMode = effectiveReplyMode;
|
|
@@ -18148,8 +18422,10 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
18148
18422
|
await context.orchestration.recordCoordinatorRouteContext?.({
|
|
18149
18423
|
coordinatorSession: session.transportSession,
|
|
18150
18424
|
chatKey,
|
|
18425
|
+
sessionAlias: session.alias,
|
|
18151
18426
|
...replyContextToken ? { replyContextToken } : {},
|
|
18152
|
-
...accountId ? { accountId } : {}
|
|
18427
|
+
...accountId ? { accountId } : {},
|
|
18428
|
+
...toCoordinatorRouteChatMetadata(metadata)
|
|
18153
18429
|
});
|
|
18154
18430
|
} catch (error2) {
|
|
18155
18431
|
await context.logger.error("orchestration.coordinator_route_context.record_failed", "failed to record coordinator route context", {
|
|
@@ -18158,6 +18434,13 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
18158
18434
|
chatKey,
|
|
18159
18435
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
18160
18436
|
});
|
|
18437
|
+
return {
|
|
18438
|
+
text: [
|
|
18439
|
+
"无法记录当前会话路由,已取消本次发送。",
|
|
18440
|
+
"请稍后重试;如果问题持续存在,请检查 weacpx 运行日志和 state.json 写入权限。"
|
|
18441
|
+
].join(`
|
|
18442
|
+
`)
|
|
18443
|
+
};
|
|
18161
18444
|
}
|
|
18162
18445
|
}
|
|
18163
18446
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session, chatKey, text, replyContextToken, accountId);
|
|
@@ -18183,23 +18466,36 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
18183
18466
|
throw error2;
|
|
18184
18467
|
}
|
|
18185
18468
|
}
|
|
18186
|
-
async function handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
18469
|
+
async function handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18187
18470
|
try {
|
|
18188
|
-
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
18471
|
+
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
18189
18472
|
} catch (error2) {
|
|
18190
18473
|
const recovered = await context.recovery.tryRecoverMissingSession(session, error2);
|
|
18191
18474
|
if (recovered) {
|
|
18192
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
18475
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
18193
18476
|
}
|
|
18194
18477
|
return context.recovery.renderTransportError(session, error2);
|
|
18195
18478
|
}
|
|
18196
18479
|
}
|
|
18197
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
18480
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18198
18481
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
18199
18482
|
if (!session) {
|
|
18200
18483
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
18201
18484
|
}
|
|
18202
|
-
return await handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
18485
|
+
return await handlePromptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
18486
|
+
}
|
|
18487
|
+
function toCoordinatorRouteChatMetadata(metadata) {
|
|
18488
|
+
if (!metadata) {
|
|
18489
|
+
return {};
|
|
18490
|
+
}
|
|
18491
|
+
return {
|
|
18492
|
+
...metadata.channel ? { channel: metadata.channel } : {},
|
|
18493
|
+
...metadata.chatType ? { chatType: metadata.chatType } : {},
|
|
18494
|
+
...metadata.senderId ? { senderId: metadata.senderId } : {},
|
|
18495
|
+
...metadata.senderName ? { senderName: metadata.senderName } : {},
|
|
18496
|
+
...metadata.groupId ? { groupId: metadata.groupId } : {},
|
|
18497
|
+
...metadata.isOwner !== undefined ? { isOwner: metadata.isOwner } : {}
|
|
18498
|
+
};
|
|
18203
18499
|
}
|
|
18204
18500
|
async function preparePromptWithFallback(context, session, chatKey, text, replyContextToken, accountId) {
|
|
18205
18501
|
const orchestration = context.orchestration;
|
|
@@ -18962,12 +19258,6 @@ var init_workspace_handler = __esm(() => {
|
|
|
18962
19258
|
};
|
|
18963
19259
|
});
|
|
18964
19260
|
|
|
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
19261
|
// src/scheduled/parse-later-time.ts
|
|
18972
19262
|
function parseLaterTime(tokens, now = new Date) {
|
|
18973
19263
|
if (tokens.length === 0)
|
|
@@ -19105,83 +19395,24 @@ var init_parse_later_time = __esm(() => {
|
|
|
19105
19395
|
]);
|
|
19106
19396
|
});
|
|
19107
19397
|
|
|
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
19398
|
// src/commands/handlers/later-handler.ts
|
|
19180
19399
|
function handleLaterHelp() {
|
|
19181
19400
|
return { text: renderLaterHelp() };
|
|
19182
19401
|
}
|
|
19183
|
-
async function handleLaterCreate(tokens, scheduled, chatKey,
|
|
19184
|
-
|
|
19402
|
+
async function handleLaterCreate(tokens, scheduled, chatKey, currentSession, defaultMode, accountId, replyContextToken) {
|
|
19403
|
+
let rest = tokens;
|
|
19404
|
+
const seenFlags = new Set;
|
|
19405
|
+
let flagMode;
|
|
19406
|
+
while (rest.length > 0 && (rest[0] === "--bind" || rest[0] === "--temp")) {
|
|
19407
|
+
seenFlags.add(rest[0]);
|
|
19408
|
+
flagMode = rest[0] === "--bind" ? "bound" : "temp";
|
|
19409
|
+
rest = rest.slice(1);
|
|
19410
|
+
}
|
|
19411
|
+
if (seenFlags.size > 1) {
|
|
19412
|
+
return { text: "定时任务的 --bind 与 --temp 不能同时使用。" };
|
|
19413
|
+
}
|
|
19414
|
+
const mode = flagMode ?? defaultMode;
|
|
19415
|
+
if (!currentSession) {
|
|
19185
19416
|
return {
|
|
19186
19417
|
text: [
|
|
19187
19418
|
"当前没有会话,无法创建定时任务。",
|
|
@@ -19193,11 +19424,11 @@ async function handleLaterCreate(tokens, scheduled, chatKey, currentSessionAlias
|
|
|
19193
19424
|
`)
|
|
19194
19425
|
};
|
|
19195
19426
|
}
|
|
19196
|
-
const result = parseLaterTime(
|
|
19427
|
+
const result = parseLaterTime(rest);
|
|
19197
19428
|
if (!result.ok) {
|
|
19198
19429
|
return { text: renderTimeParseError(result.code, result.value) };
|
|
19199
19430
|
}
|
|
19200
|
-
const message =
|
|
19431
|
+
const message = rest.slice(result.messageStartIndex).join(" ").trim();
|
|
19201
19432
|
if (message.startsWith("/")) {
|
|
19202
19433
|
return {
|
|
19203
19434
|
text: [
|
|
@@ -19211,13 +19442,15 @@ async function handleLaterCreate(tokens, scheduled, chatKey, currentSessionAlias
|
|
|
19211
19442
|
}
|
|
19212
19443
|
const task = await scheduled.createTask({
|
|
19213
19444
|
chatKey,
|
|
19214
|
-
sessionAlias:
|
|
19445
|
+
sessionAlias: currentSession.alias,
|
|
19215
19446
|
executeAt: result.executeAt,
|
|
19216
19447
|
message,
|
|
19448
|
+
sessionMode: mode,
|
|
19449
|
+
...mode === "temp" ? { agent: currentSession.agent, workspace: currentSession.workspace } : {},
|
|
19217
19450
|
...accountId ? { accountId } : {},
|
|
19218
19451
|
...replyContextToken ? { replyContextToken } : {}
|
|
19219
19452
|
});
|
|
19220
|
-
return { text: renderTaskCreated(task, toDisplaySessionAlias(
|
|
19453
|
+
return { text: renderTaskCreated(task, toDisplaySessionAlias(currentSession.alias)) };
|
|
19221
19454
|
}
|
|
19222
19455
|
function handleLaterList(scheduled) {
|
|
19223
19456
|
const tasks = scheduled.listPending();
|
|
@@ -19265,9 +19498,11 @@ var init_later_handler = __esm(() => {
|
|
|
19265
19498
|
laterHelpMetadata = {
|
|
19266
19499
|
topic: "later",
|
|
19267
19500
|
aliases: ["lt"],
|
|
19268
|
-
summary: "
|
|
19501
|
+
summary: "定时任务:到点在临时会话执行(或 --bind 发到当前会话)",
|
|
19269
19502
|
commands: [
|
|
19270
19503
|
{ usage: "/lt <时间> <消息>", description: "创建定时任务" },
|
|
19504
|
+
{ usage: "/lt --bind <时间> <消息>", description: "改为发送到当前会话" },
|
|
19505
|
+
{ usage: "/lt --temp <时间> <消息>", description: "强制使用临时会话" },
|
|
19271
19506
|
{ usage: "/lt list", description: "查看待执行定时任务" },
|
|
19272
19507
|
{ usage: "/lt cancel <id>", description: "取消定时任务" }
|
|
19273
19508
|
],
|
|
@@ -19281,7 +19516,8 @@ var init_later_handler = __esm(() => {
|
|
|
19281
19516
|
notes: [
|
|
19282
19517
|
"只支持一次性任务,不支持重复执行",
|
|
19283
19518
|
"时间必须在 10 秒之后、7 天之内",
|
|
19284
|
-
"
|
|
19519
|
+
"默认在为本次任务新建的临时会话里执行,跑完即销毁",
|
|
19520
|
+
"加 --bind 改为发送到创建时绑定的当前会话(默认模式可用 later.defaultMode 配置)",
|
|
19285
19521
|
"/lt list 显示全局待执行任务;群聊中只有群主可取消",
|
|
19286
19522
|
"不支持延迟执行 / 开头的 weacpx 命令",
|
|
19287
19523
|
"完整时间格式与说明见 docs/later-command.md"
|
|
@@ -19566,6 +19802,15 @@ var init_session_shortcut_handler = __esm(() => {
|
|
|
19566
19802
|
function renderTransportError(session, error2) {
|
|
19567
19803
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
19568
19804
|
if (message.includes("No acpx session found")) {
|
|
19805
|
+
if (session.transient) {
|
|
19806
|
+
return {
|
|
19807
|
+
text: [
|
|
19808
|
+
"定时任务的临时会话启动失败,本次任务未能执行。",
|
|
19809
|
+
"临时会话由系统在执行时自动创建,无需手动操作;如需重排,请用 /lt 重新安排。"
|
|
19810
|
+
].join(`
|
|
19811
|
+
`)
|
|
19812
|
+
};
|
|
19813
|
+
}
|
|
19569
19814
|
return {
|
|
19570
19815
|
text: [
|
|
19571
19816
|
`当前会话「${session.alias}」暂时不可用。`,
|
|
@@ -19642,6 +19887,9 @@ async function tryRecoverMissingSession(ops, session, error2) {
|
|
|
19642
19887
|
if (!message.includes("No acpx session found")) {
|
|
19643
19888
|
return null;
|
|
19644
19889
|
}
|
|
19890
|
+
if (session.transient) {
|
|
19891
|
+
return null;
|
|
19892
|
+
}
|
|
19645
19893
|
const transportAgentCommand = await ops.resolveSessionAgentCommand(session);
|
|
19646
19894
|
if (!transportAgentCommand || transportAgentCommand === session.agentCommand) {
|
|
19647
19895
|
return null;
|
|
@@ -20171,7 +20419,7 @@ class CommandRouter {
|
|
|
20171
20419
|
return { text: renderLaterUnsupportedChannel() };
|
|
20172
20420
|
}
|
|
20173
20421
|
const currentSession = await this.sessions.getCurrentSession(chatKey);
|
|
20174
|
-
return await handleLaterCreate(command.tokens, this.scheduled, chatKey, currentSession
|
|
20422
|
+
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
20423
|
}
|
|
20176
20424
|
case "later.cancel":
|
|
20177
20425
|
if (!this.scheduled)
|
|
@@ -20179,14 +20427,22 @@ class CommandRouter {
|
|
|
20179
20427
|
return await handleLaterCancel(command.id, this.scheduled);
|
|
20180
20428
|
case "prompt": {
|
|
20181
20429
|
const sessionContext = this.createSessionHandlerContext(undefined, perfSpan);
|
|
20430
|
+
if (metadata?.scheduledSessionDescriptor) {
|
|
20431
|
+
const descriptor = metadata.scheduledSessionDescriptor;
|
|
20432
|
+
const transientSession = {
|
|
20433
|
+
...this.sessions.resolveSession(descriptor.alias, descriptor.agent, descriptor.workspace, descriptor.transportSession),
|
|
20434
|
+
transient: true
|
|
20435
|
+
};
|
|
20436
|
+
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
20437
|
+
}
|
|
20182
20438
|
if (metadata?.scheduledSessionAlias) {
|
|
20183
20439
|
const scheduledSession = await this.sessions.getSession(metadata.scheduledSessionAlias);
|
|
20184
20440
|
if (!scheduledSession) {
|
|
20185
20441
|
throw new Error(`session "${metadata.scheduledSessionAlias}" not found for scheduled prompt`);
|
|
20186
20442
|
}
|
|
20187
|
-
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
20443
|
+
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
20188
20444
|
}
|
|
20189
|
-
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
20445
|
+
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
20190
20446
|
}
|
|
20191
20447
|
}
|
|
20192
20448
|
});
|
|
@@ -20664,24 +20920,6 @@ var init_console_agent = __esm(() => {
|
|
|
20664
20920
|
init_command_list();
|
|
20665
20921
|
});
|
|
20666
20922
|
|
|
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
20923
|
// src/orchestration/orchestration-server.ts
|
|
20686
20924
|
import { rm as rm7 } from "node:fs/promises";
|
|
20687
20925
|
import { createConnection as createConnection2, createServer } from "node:net";
|
|
@@ -20837,6 +21075,12 @@ class OrchestrationServer {
|
|
|
20837
21075
|
reviewId: requireString(params, "reviewId"),
|
|
20838
21076
|
decision: requireEnum(params, "decision", ["accept", "discard"])
|
|
20839
21077
|
});
|
|
21078
|
+
case "scheduled.create":
|
|
21079
|
+
return await this.dispatchScheduledCreate(params);
|
|
21080
|
+
case "scheduled.list":
|
|
21081
|
+
return await this.dispatchScheduledList(params);
|
|
21082
|
+
case "scheduled.cancel":
|
|
21083
|
+
return await this.dispatchScheduledCancel(params);
|
|
20840
21084
|
case "group.new":
|
|
20841
21085
|
requireOnlyKeys(params, ["coordinatorSession", "title"], "params");
|
|
20842
21086
|
return await this.handlers.createGroup({
|
|
@@ -20847,6 +21091,47 @@ class OrchestrationServer {
|
|
|
20847
21091
|
throw new OrchestrationInvalidRequestError(`unsupported orchestration method: ${method}`);
|
|
20848
21092
|
}
|
|
20849
21093
|
}
|
|
21094
|
+
async dispatchScheduledCreate(params) {
|
|
21095
|
+
const input = this.parseScheduledCreateInput(params);
|
|
21096
|
+
const handler = this.deps.createScheduledTaskFromRoute ?? this.handlers.createScheduledTaskFromRoute;
|
|
21097
|
+
if (!handler) {
|
|
21098
|
+
throw new Error("scheduled task creation is not configured");
|
|
21099
|
+
}
|
|
21100
|
+
return await handler(input);
|
|
21101
|
+
}
|
|
21102
|
+
parseScheduledCreateInput(params) {
|
|
21103
|
+
requireOnlyKeys(params, ["coordinatorSession", "timeText", "message", "mode"], "params");
|
|
21104
|
+
const mode = requireOptionalEnum(params, "mode", ["temp", "bound"]);
|
|
21105
|
+
return {
|
|
21106
|
+
coordinatorSession: requireString(params, "coordinatorSession"),
|
|
21107
|
+
timeText: requireString(params, "timeText"),
|
|
21108
|
+
message: requireString(params, "message"),
|
|
21109
|
+
...mode !== undefined ? { mode } : {}
|
|
21110
|
+
};
|
|
21111
|
+
}
|
|
21112
|
+
async dispatchScheduledList(params) {
|
|
21113
|
+
requireOnlyKeys(params, ["coordinatorSession"], "params");
|
|
21114
|
+
const input = {
|
|
21115
|
+
coordinatorSession: requireString(params, "coordinatorSession")
|
|
21116
|
+
};
|
|
21117
|
+
const handler = this.deps.listScheduledTasksFromRoute ?? this.handlers.listScheduledTasksFromRoute;
|
|
21118
|
+
if (!handler) {
|
|
21119
|
+
throw new Error("scheduled task listing is not configured");
|
|
21120
|
+
}
|
|
21121
|
+
return await handler(input);
|
|
21122
|
+
}
|
|
21123
|
+
async dispatchScheduledCancel(params) {
|
|
21124
|
+
requireOnlyKeys(params, ["coordinatorSession", "id"], "params");
|
|
21125
|
+
const input = {
|
|
21126
|
+
coordinatorSession: requireString(params, "coordinatorSession"),
|
|
21127
|
+
id: requireString(params, "id")
|
|
21128
|
+
};
|
|
21129
|
+
const handler = this.deps.cancelScheduledTaskFromRoute ?? this.handlers.cancelScheduledTaskFromRoute;
|
|
21130
|
+
if (!handler) {
|
|
21131
|
+
throw new Error("scheduled task cancellation is not configured");
|
|
21132
|
+
}
|
|
21133
|
+
return await handler(input);
|
|
21134
|
+
}
|
|
20850
21135
|
parseRegisterExternalCoordinatorInput(params) {
|
|
20851
21136
|
requireOnlyKeys(params, ["coordinatorSession", "workspace", "defaultTargetAgent"], "params");
|
|
20852
21137
|
const workspace = requireOptionalString(params, "workspace");
|
|
@@ -21191,6 +21476,9 @@ var init_orchestration_server = __esm(() => {
|
|
|
21191
21476
|
"coordinator.retract_answer",
|
|
21192
21477
|
"coordinator.request_human_input",
|
|
21193
21478
|
"coordinator.review_contested_result",
|
|
21479
|
+
"scheduled.create",
|
|
21480
|
+
"scheduled.list",
|
|
21481
|
+
"scheduled.cancel",
|
|
21194
21482
|
"group.new"
|
|
21195
21483
|
]);
|
|
21196
21484
|
});
|
|
@@ -22184,10 +22472,11 @@ class OrchestrationService {
|
|
|
22184
22472
|
const state = await this.deps.loadState();
|
|
22185
22473
|
const now = this.deps.now().toISOString();
|
|
22186
22474
|
const existing = this.ensureCoordinatorRoutes(state)[input.coordinatorSession];
|
|
22475
|
+
const sameChat = existing?.chatKey === input.chatKey;
|
|
22187
22476
|
const hasAccountId = input.accountId !== undefined;
|
|
22188
22477
|
const hasReplyContextToken = input.replyContextToken !== undefined;
|
|
22189
22478
|
const hasCompleteReplyRoute = hasAccountId && hasReplyContextToken;
|
|
22190
|
-
const shouldPreserveExistingReplyRoute = !hasAccountId && !hasReplyContextToken &&
|
|
22479
|
+
const shouldPreserveExistingReplyRoute = !hasAccountId && !hasReplyContextToken && sameChat;
|
|
22191
22480
|
const replyRoute = hasCompleteReplyRoute ? {
|
|
22192
22481
|
accountId: input.accountId,
|
|
22193
22482
|
replyContextToken: input.replyContextToken
|
|
@@ -22198,7 +22487,9 @@ class OrchestrationService {
|
|
|
22198
22487
|
const route = {
|
|
22199
22488
|
coordinatorSession: input.coordinatorSession,
|
|
22200
22489
|
chatKey: input.chatKey,
|
|
22490
|
+
...input.sessionAlias ? { sessionAlias: input.sessionAlias } : {},
|
|
22201
22491
|
...replyRoute ? replyRoute : {},
|
|
22492
|
+
...buildCoordinatorRouteChatMetadata(input, sameChat ? existing : undefined),
|
|
22202
22493
|
updatedAt: now
|
|
22203
22494
|
};
|
|
22204
22495
|
this.ensureCoordinatorRoutes(state)[input.coordinatorSession] = route;
|
|
@@ -24525,6 +24816,22 @@ class OrchestrationService {
|
|
|
24525
24816
|
task.events = events.slice(-MAX_TASK_EVENTS_PER_TASK);
|
|
24526
24817
|
}
|
|
24527
24818
|
}
|
|
24819
|
+
function buildCoordinatorRouteChatMetadata(input, existing) {
|
|
24820
|
+
const channel = input.channel ?? existing?.channel;
|
|
24821
|
+
const chatType = input.chatType ?? existing?.chatType;
|
|
24822
|
+
const senderId = input.senderId ?? existing?.senderId;
|
|
24823
|
+
const senderName = input.senderName ?? existing?.senderName;
|
|
24824
|
+
const groupId = input.groupId ?? existing?.groupId;
|
|
24825
|
+
const isOwner = input.isOwner ?? existing?.isOwner;
|
|
24826
|
+
return {
|
|
24827
|
+
...channel !== undefined ? { channel } : {},
|
|
24828
|
+
...chatType !== undefined ? { chatType } : {},
|
|
24829
|
+
...senderId !== undefined ? { senderId } : {},
|
|
24830
|
+
...senderName !== undefined ? { senderName } : {},
|
|
24831
|
+
...groupId !== undefined ? { groupId } : {},
|
|
24832
|
+
...isOwner !== undefined ? { isOwner } : {}
|
|
24833
|
+
};
|
|
24834
|
+
}
|
|
24528
24835
|
function isTerminalTaskStatus2(status) {
|
|
24529
24836
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
24530
24837
|
}
|
|
@@ -24650,137 +24957,186 @@ var init_scheduled_scheduler = __esm(() => {
|
|
|
24650
24957
|
DEFAULT_DISPATCH_TIMEOUT_MS = 10 * 60 * 1000;
|
|
24651
24958
|
});
|
|
24652
24959
|
|
|
24653
|
-
// src/scheduled/scheduled-
|
|
24654
|
-
|
|
24655
|
-
|
|
24656
|
-
|
|
24657
|
-
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
|
|
24661
|
-
|
|
24662
|
-
|
|
24663
|
-
|
|
24664
|
-
|
|
24665
|
-
|
|
24666
|
-
|
|
24960
|
+
// src/scheduled/scheduled-dispatch.ts
|
|
24961
|
+
function buildScheduledDispatchTask(deps) {
|
|
24962
|
+
return async (task, abortSignal) => {
|
|
24963
|
+
if (task.session_mode === "temp") {
|
|
24964
|
+
await dispatchTemp(task, abortSignal, deps);
|
|
24965
|
+
return;
|
|
24966
|
+
}
|
|
24967
|
+
await dispatchBound(task, abortSignal, deps);
|
|
24968
|
+
};
|
|
24969
|
+
}
|
|
24970
|
+
async function dispatchBound(task, abortSignal, deps) {
|
|
24971
|
+
const session = await deps.getSession(task.session_alias);
|
|
24972
|
+
if (!session) {
|
|
24973
|
+
throw new Error(`session "${task.session_alias}" not found for scheduled task`);
|
|
24667
24974
|
}
|
|
24668
|
-
|
|
24669
|
-
|
|
24670
|
-
|
|
24671
|
-
|
|
24672
|
-
|
|
24673
|
-
|
|
24674
|
-
|
|
24675
|
-
|
|
24676
|
-
|
|
24677
|
-
|
|
24678
|
-
|
|
24679
|
-
|
|
24680
|
-
|
|
24681
|
-
|
|
24682
|
-
|
|
24683
|
-
|
|
24684
|
-
|
|
24685
|
-
|
|
24975
|
+
const noticeText = `执行定时任务 #${task.id}
|
|
24976
|
+
会话:${toDisplaySessionAlias(task.session_alias)}
|
|
24977
|
+
内容:${preview(task.message)}`;
|
|
24978
|
+
await deps.sendScheduledMessage({
|
|
24979
|
+
chatKey: task.chat_key,
|
|
24980
|
+
taskId: task.id,
|
|
24981
|
+
sessionAlias: task.session_alias,
|
|
24982
|
+
noticeText,
|
|
24983
|
+
promptText: task.message,
|
|
24984
|
+
abortSignal,
|
|
24985
|
+
...task.account_id ? { accountId: task.account_id } : {},
|
|
24986
|
+
...task.reply_context_token ? { replyContextToken: task.reply_context_token } : {}
|
|
24987
|
+
});
|
|
24988
|
+
}
|
|
24989
|
+
async function dispatchTemp(task, abortSignal, deps) {
|
|
24990
|
+
if (!task.agent || !task.workspace) {
|
|
24991
|
+
throw new Error(`temp scheduled task #${task.id} is missing its agent/workspace snapshot`);
|
|
24992
|
+
}
|
|
24993
|
+
const alias = `later-${task.id}`;
|
|
24994
|
+
const transportSession = `${task.workspace}:${alias}`;
|
|
24995
|
+
const session = deps.resolveSession(alias, task.agent, task.workspace, transportSession);
|
|
24996
|
+
const noticeText = `执行定时任务 #${task.id}
|
|
24997
|
+
会话:临时会话(${task.workspace} · ${task.agent})
|
|
24998
|
+
内容:${preview(task.message)}`;
|
|
24999
|
+
try {
|
|
25000
|
+
await deps.sendScheduledMessage({
|
|
25001
|
+
chatKey: task.chat_key,
|
|
25002
|
+
taskId: task.id,
|
|
25003
|
+
sessionAlias: task.session_alias,
|
|
25004
|
+
sessionDescriptor: { alias, agent: task.agent, workspace: task.workspace, transportSession },
|
|
25005
|
+
noticeText,
|
|
25006
|
+
promptText: task.message,
|
|
25007
|
+
abortSignal,
|
|
25008
|
+
...task.account_id ? { accountId: task.account_id } : {},
|
|
25009
|
+
...task.reply_context_token ? { replyContextToken: task.reply_context_token } : {}
|
|
24686
25010
|
});
|
|
25011
|
+
} finally {
|
|
25012
|
+
if (deps.removeSession) {
|
|
25013
|
+
try {
|
|
25014
|
+
await deps.removeSession(session);
|
|
25015
|
+
} catch (error2) {
|
|
25016
|
+
await deps.logger?.error("scheduled.temp_session_close_failed", "failed to close temp scheduled session", { taskId: task.id, transportSession, error: String(error2) });
|
|
25017
|
+
}
|
|
25018
|
+
}
|
|
24687
25019
|
}
|
|
24688
|
-
|
|
24689
|
-
|
|
25020
|
+
}
|
|
25021
|
+
var init_scheduled_dispatch = __esm(() => {
|
|
25022
|
+
init_channel_scope();
|
|
25023
|
+
init_scheduled_render();
|
|
25024
|
+
});
|
|
25025
|
+
|
|
25026
|
+
// src/scheduled/scheduled-route-create.ts
|
|
25027
|
+
async function createScheduledTaskFromRoute(input, deps) {
|
|
25028
|
+
const coordinatorSession = input.coordinatorSession.trim();
|
|
25029
|
+
if (coordinatorSession.length === 0) {
|
|
25030
|
+
throw new Error("coordinatorSession must be a non-empty string");
|
|
24690
25031
|
}
|
|
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
|
-
});
|
|
25032
|
+
const route = deps.state.orchestration.coordinatorRoutes[coordinatorSession];
|
|
25033
|
+
if (!route) {
|
|
25034
|
+
throw new Error(`no chat route is recorded for coordinator session "${coordinatorSession}"`);
|
|
24702
25035
|
}
|
|
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
|
-
});
|
|
25036
|
+
if (route.chatType !== "direct" && route.chatType !== "group") {
|
|
25037
|
+
throw new Error("scheduled_create requires current chat route metadata");
|
|
24723
25038
|
}
|
|
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
|
-
});
|
|
25039
|
+
if (route.chatType === "group" && route.isOwner !== true) {
|
|
25040
|
+
throw new Error("scheduled_create is owner-only in group chats");
|
|
24739
25041
|
}
|
|
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
|
-
});
|
|
25042
|
+
if (deps.supportsScheduledMessages && !deps.supportsScheduledMessages(route.chatKey)) {
|
|
25043
|
+
throw new Error("current channel does not support scheduled tasks");
|
|
24751
25044
|
}
|
|
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
|
-
});
|
|
25045
|
+
const message = input.message.trim();
|
|
25046
|
+
if (message.length === 0) {
|
|
25047
|
+
throw new Error("message must be a non-empty string");
|
|
24764
25048
|
}
|
|
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");
|
|
25049
|
+
if (message.startsWith("/")) {
|
|
25050
|
+
throw new Error("scheduled_create does not support slash-prefixed weacpx commands");
|
|
24772
25051
|
}
|
|
24773
|
-
|
|
24774
|
-
|
|
25052
|
+
if (!route.sessionAlias) {
|
|
25053
|
+
throw new Error("scheduled_create requires current session route metadata");
|
|
24775
25054
|
}
|
|
24776
|
-
|
|
24777
|
-
|
|
25055
|
+
const session = await deps.sessions.getSession(route.sessionAlias);
|
|
25056
|
+
if (!session) {
|
|
25057
|
+
throw new Error(`session "${route.sessionAlias}" recorded for coordinator session "${coordinatorSession}" was not found`);
|
|
24778
25058
|
}
|
|
25059
|
+
if (session.transportSession !== coordinatorSession) {
|
|
25060
|
+
throw new Error(`session "${route.sessionAlias}" is no longer attached to coordinator session "${coordinatorSession}"`);
|
|
25061
|
+
}
|
|
25062
|
+
const executeAt = parseRouteScheduledTime(input.timeText, deps.now?.() ?? new Date);
|
|
25063
|
+
const mode = input.mode ?? (deps.config.later?.defaultMode === "bind" ? "bound" : "temp");
|
|
25064
|
+
return await deps.scheduled.createTask({
|
|
25065
|
+
chatKey: route.chatKey,
|
|
25066
|
+
sessionAlias: session.alias,
|
|
25067
|
+
executeAt,
|
|
25068
|
+
message,
|
|
25069
|
+
sessionMode: mode,
|
|
25070
|
+
...mode === "temp" ? { agent: session.agent, workspace: session.workspace } : {},
|
|
25071
|
+
...route.accountId ? { accountId: route.accountId } : {},
|
|
25072
|
+
...route.replyContextToken ? { replyContextToken: route.replyContextToken } : {},
|
|
25073
|
+
sourceLabel: "mcp:scheduled_create"
|
|
25074
|
+
});
|
|
24779
25075
|
}
|
|
24780
|
-
function
|
|
24781
|
-
|
|
25076
|
+
function parseRouteScheduledTime(timeText, now) {
|
|
25077
|
+
const timeTokens = timeText.trim().split(/\s+/).filter((token) => token.length > 0);
|
|
25078
|
+
if (timeTokens.length === 0) {
|
|
25079
|
+
throw new Error(formatLaterTimeParseError("missing_time"));
|
|
25080
|
+
}
|
|
25081
|
+
const parsed = parseLaterTime([...timeTokens, "__scheduled_create_message__"], now);
|
|
25082
|
+
if (!parsed.ok) {
|
|
25083
|
+
throw new Error(formatLaterTimeParseError(parsed.code, parsed.value));
|
|
25084
|
+
}
|
|
25085
|
+
if (parsed.messageStartIndex !== timeTokens.length) {
|
|
25086
|
+
throw new Error("timeText must contain only the time expression; put the scheduled content in message");
|
|
25087
|
+
}
|
|
25088
|
+
return parsed.executeAt;
|
|
24782
25089
|
}
|
|
24783
|
-
|
|
25090
|
+
function formatLaterTimeParseError(code, value) {
|
|
25091
|
+
switch (code) {
|
|
25092
|
+
case "missing_message":
|
|
25093
|
+
return "message must be provided separately from timeText";
|
|
25094
|
+
case "too_soon":
|
|
25095
|
+
return "scheduled task time must be at least 10 seconds in the future";
|
|
25096
|
+
case "out_of_range":
|
|
25097
|
+
return "scheduled task time must be within 7 days";
|
|
25098
|
+
case "past_today_time":
|
|
25099
|
+
return `today at ${value} has already passed; choose a future time or use tomorrow`;
|
|
25100
|
+
case "unrecognized_time":
|
|
25101
|
+
case "missing_time":
|
|
25102
|
+
default:
|
|
25103
|
+
return "unrecognized timeText; supported examples: in 2h, 30分钟后, tomorrow 09:00, 周五 09:00";
|
|
25104
|
+
}
|
|
25105
|
+
}
|
|
25106
|
+
var init_scheduled_route_create = __esm(() => {
|
|
25107
|
+
init_parse_later_time();
|
|
25108
|
+
});
|
|
25109
|
+
|
|
25110
|
+
// src/scheduled/scheduled-route-manage.ts
|
|
25111
|
+
async function listScheduledTasksFromRoute(input, deps) {
|
|
25112
|
+
resolveOwnedCoordinatorRoute(input.coordinatorSession, deps.state, "scheduled_list");
|
|
25113
|
+
return deps.scheduled.listPending();
|
|
25114
|
+
}
|
|
25115
|
+
async function cancelScheduledTaskFromRoute(input, deps) {
|
|
25116
|
+
resolveOwnedCoordinatorRoute(input.coordinatorSession, deps.state, "scheduled_cancel");
|
|
25117
|
+
const cancelled = await deps.scheduled.cancelPending(input.id);
|
|
25118
|
+
return { id: normalizeId(input.id), cancelled };
|
|
25119
|
+
}
|
|
25120
|
+
function resolveOwnedCoordinatorRoute(coordinatorSession, state, label) {
|
|
25121
|
+
const session = coordinatorSession.trim();
|
|
25122
|
+
if (session.length === 0) {
|
|
25123
|
+
throw new Error("coordinatorSession must be a non-empty string");
|
|
25124
|
+
}
|
|
25125
|
+
const route = state.orchestration.coordinatorRoutes[session];
|
|
25126
|
+
if (!route) {
|
|
25127
|
+
throw new Error(`no chat route is recorded for coordinator session "${session}"`);
|
|
25128
|
+
}
|
|
25129
|
+
if (route.chatType !== "direct" && route.chatType !== "group") {
|
|
25130
|
+
throw new Error(`${label} requires current chat route metadata`);
|
|
25131
|
+
}
|
|
25132
|
+
if (route.chatType === "group" && route.isOwner !== true) {
|
|
25133
|
+
throw new Error(`${label} is owner-only in group chats`);
|
|
25134
|
+
}
|
|
25135
|
+
return route;
|
|
25136
|
+
}
|
|
25137
|
+
var init_scheduled_route_manage = __esm(() => {
|
|
25138
|
+
init_scheduled_service();
|
|
25139
|
+
});
|
|
24784
25140
|
|
|
24785
25141
|
// src/sessions/session-service.ts
|
|
24786
25142
|
class SessionService {
|
|
@@ -26409,7 +26765,7 @@ import { join as join13 } from "node:path";
|
|
|
26409
26765
|
function buildWeacpxMcpServerSpec(input) {
|
|
26410
26766
|
const { command, args } = splitCommandLine(input.weacpxCommand);
|
|
26411
26767
|
return {
|
|
26412
|
-
name: "weacpx
|
|
26768
|
+
name: "weacpx",
|
|
26413
26769
|
type: "stdio",
|
|
26414
26770
|
command,
|
|
26415
26771
|
args: [
|
|
@@ -26417,7 +26773,7 @@ function buildWeacpxMcpServerSpec(input) {
|
|
|
26417
26773
|
"mcp-stdio",
|
|
26418
26774
|
"--coordinator-session",
|
|
26419
26775
|
input.coordinatorSession,
|
|
26420
|
-
...input.sourceHandle ? ["--source-handle", input.sourceHandle] : []
|
|
26776
|
+
...input.sourceHandle ? ["--source-handle", input.sourceHandle] : ["--internal-session-tools"]
|
|
26421
26777
|
]
|
|
26422
26778
|
};
|
|
26423
26779
|
}
|
|
@@ -27867,32 +28223,32 @@ async function buildApp(paths, deps = {}) {
|
|
|
27867
28223
|
}
|
|
27868
28224
|
const progressHeartbeatInterval = startProgressHeartbeat(orchestration, config2, logger2, deps.channel ?? null);
|
|
27869
28225
|
const orchestrationEndpoint = createOrchestrationEndpoint(paths.orchestrationSocketPath ?? resolveOrchestrationSocketPathFromConfigPath(paths.configPath));
|
|
27870
|
-
const orchestrationServer = new OrchestrationServer(orchestrationEndpoint, orchestration
|
|
28226
|
+
const orchestrationServer = new OrchestrationServer(orchestrationEndpoint, orchestration, {
|
|
28227
|
+
createScheduledTaskFromRoute: async (input) => await createScheduledTaskFromRoute(input, {
|
|
28228
|
+
state,
|
|
28229
|
+
config: config2,
|
|
28230
|
+
sessions,
|
|
28231
|
+
scheduled: scheduledService,
|
|
28232
|
+
...deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : {}
|
|
28233
|
+
}),
|
|
28234
|
+
listScheduledTasksFromRoute: async (input) => await listScheduledTasksFromRoute(input, { state, scheduled: scheduledService }),
|
|
28235
|
+
cancelScheduledTaskFromRoute: async (input) => await cancelScheduledTaskFromRoute(input, { state, scheduled: scheduledService })
|
|
28236
|
+
});
|
|
27871
28237
|
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
28238
|
const agent = new ConsoleAgent(router, logger2);
|
|
27873
28239
|
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
|
-
},
|
|
28240
|
+
dispatchTask: buildScheduledDispatchTask({
|
|
28241
|
+
getSession: (alias) => sessions.getSession(alias),
|
|
28242
|
+
resolveSession: (alias, agent2, workspace, transportSession) => sessions.resolveSession(alias, agent2, workspace, transportSession),
|
|
28243
|
+
sendScheduledMessage: async (input) => {
|
|
28244
|
+
if (!deps.channel?.sendScheduledMessage) {
|
|
28245
|
+
throw new Error("no channel runtime available for scheduled task dispatch");
|
|
28246
|
+
}
|
|
28247
|
+
await deps.channel.sendScheduledMessage(input);
|
|
28248
|
+
},
|
|
28249
|
+
...transport.removeSession ? { removeSession: (session) => transport.removeSession(session) } : {},
|
|
28250
|
+
logger: logger2
|
|
28251
|
+
}),
|
|
27896
28252
|
logger: logger2
|
|
27897
28253
|
});
|
|
27898
28254
|
return {
|
|
@@ -28065,8 +28421,9 @@ var init_main = __esm(async () => {
|
|
|
28065
28421
|
init_build_coordinator_prompt();
|
|
28066
28422
|
init_scheduled_scheduler();
|
|
28067
28423
|
init_scheduled_service();
|
|
28068
|
-
|
|
28069
|
-
|
|
28424
|
+
init_scheduled_dispatch();
|
|
28425
|
+
init_scheduled_route_create();
|
|
28426
|
+
init_scheduled_route_manage();
|
|
28070
28427
|
init_session_service();
|
|
28071
28428
|
init_state_store();
|
|
28072
28429
|
init_run_console();
|
|
@@ -41598,12 +41955,13 @@ var sortSchema = exports_external.enum(["updatedAt", "createdAt"]);
|
|
|
41598
41955
|
var orderSchema = exports_external.enum(["asc", "desc"]);
|
|
41599
41956
|
var contestedDecisionSchema = exports_external.enum(["accept", "discard"]);
|
|
41600
41957
|
var taskWatchModeSchema = exports_external.enum(["next_event", "until_attention_or_terminal"]);
|
|
41958
|
+
var scheduledModeSchema = exports_external.enum(["temp", "bound"]);
|
|
41601
41959
|
var taskQuestionSchema = exports_external.object({
|
|
41602
41960
|
taskId: exports_external.string().min(1),
|
|
41603
41961
|
questionId: exports_external.string().min(1)
|
|
41604
41962
|
}).strict();
|
|
41605
41963
|
function buildWeacpxMcpToolRegistry(input) {
|
|
41606
|
-
const { transport, coordinatorSession, sourceHandle, isExternalCoordinator, availableAgents } = input;
|
|
41964
|
+
const { transport, coordinatorSession, sourceHandle, isExternalCoordinator, internalSessionTools, availableAgents } = input;
|
|
41607
41965
|
const tools = [
|
|
41608
41966
|
{
|
|
41609
41967
|
name: "delegate_request",
|
|
@@ -41821,6 +42179,63 @@ function buildWeacpxMcpToolRegistry(input) {
|
|
|
41821
42179
|
})
|
|
41822
42180
|
}
|
|
41823
42181
|
];
|
|
42182
|
+
if (internalSessionTools && !isExternalCoordinator && !sourceHandle) {
|
|
42183
|
+
tools.push({
|
|
42184
|
+
name: "scheduled_create",
|
|
42185
|
+
description: "Schedule a one-shot task to run a natural-language message at a future time, using the recorded chat route. By default — and like /later — the task runs in a FRESH TEMPORARY session (it snapshots the current agent and workspace but starts with brand-new history and is destroyed after running, so it does not pollute this conversation); the reply is still pushed back to this chat. Provide only timeText and message and OMIT mode to get this default. Routing, session, and account are resolved by weacpx.",
|
|
42186
|
+
inputSchema: exports_external.object({
|
|
42187
|
+
timeText: exports_external.string().min(1).describe("Time expression, e.g. 'in 2h', '30分钟后', 'tomorrow 09:00', or '周五 09:00'."),
|
|
42188
|
+
message: exports_external.string().min(1).describe("Natural-language message to run at the scheduled time."),
|
|
42189
|
+
mode: scheduledModeSchema.describe("Optional; leave UNSET for the default temporary session (recommended). Set 'bound' ONLY when the user explicitly asks for the task to run inside this conversation's current session and share its context. 'temp' forces the temporary session.").optional()
|
|
42190
|
+
}).strict(),
|
|
42191
|
+
handler: async (args) => await asToolResult(async () => {
|
|
42192
|
+
const input2 = args;
|
|
42193
|
+
const task = await transport.scheduledCreate({
|
|
42194
|
+
coordinatorSession,
|
|
42195
|
+
timeText: input2.timeText,
|
|
42196
|
+
message: input2.message,
|
|
42197
|
+
...input2.mode ? { mode: input2.mode } : {}
|
|
42198
|
+
});
|
|
42199
|
+
return createSuccessResult(`Scheduled task #${task.id} created for ${task.execute_at}.`, {
|
|
42200
|
+
id: task.id,
|
|
42201
|
+
status: task.status,
|
|
42202
|
+
executeAt: task.execute_at,
|
|
42203
|
+
sessionAlias: task.session_alias,
|
|
42204
|
+
sessionMode: task.session_mode ?? "bound"
|
|
42205
|
+
});
|
|
42206
|
+
})
|
|
42207
|
+
});
|
|
42208
|
+
tools.push({
|
|
42209
|
+
name: "scheduled_list",
|
|
42210
|
+
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.",
|
|
42211
|
+
inputSchema: exports_external.object({}).strict(),
|
|
42212
|
+
handler: async () => await asToolResult(async () => {
|
|
42213
|
+
const tasks = await transport.scheduledList({ coordinatorSession });
|
|
42214
|
+
return createSuccessResult(renderScheduledList(tasks), {
|
|
42215
|
+
tasks: tasks.map((task) => ({
|
|
42216
|
+
id: task.id,
|
|
42217
|
+
executeAt: task.execute_at,
|
|
42218
|
+
message: task.message,
|
|
42219
|
+
sessionAlias: task.session_alias,
|
|
42220
|
+
sessionMode: task.session_mode ?? "bound",
|
|
42221
|
+
chatKey: task.chat_key
|
|
42222
|
+
}))
|
|
42223
|
+
});
|
|
42224
|
+
})
|
|
42225
|
+
});
|
|
42226
|
+
tools.push({
|
|
42227
|
+
name: "scheduled_cancel",
|
|
42228
|
+
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.",
|
|
42229
|
+
inputSchema: exports_external.object({
|
|
42230
|
+
id: exports_external.string().min(1).describe("The scheduled task id, e.g. 'k8f2' (a leading # is allowed).")
|
|
42231
|
+
}).strict(),
|
|
42232
|
+
handler: async (args) => await asToolResult(async () => {
|
|
42233
|
+
const { id } = args;
|
|
42234
|
+
const result = await transport.scheduledCancel({ coordinatorSession, id });
|
|
42235
|
+
return createSuccessResult(renderScheduledCancel(result), { id: result.id, cancelled: result.cancelled });
|
|
42236
|
+
})
|
|
42237
|
+
});
|
|
42238
|
+
}
|
|
41824
42239
|
if (isExternalCoordinator) {
|
|
41825
42240
|
const externalCoordinatorIncompatibleTools = new Set([
|
|
41826
42241
|
"coordinator_request_human_input"
|
|
@@ -41900,6 +42315,19 @@ function renderTaskList(tasks) {
|
|
|
41900
42315
|
return ["Tasks for the current coordinator:", ...tasks.map((task) => renderTaskListItem(task))].join(`
|
|
41901
42316
|
`);
|
|
41902
42317
|
}
|
|
42318
|
+
function renderScheduledList(tasks) {
|
|
42319
|
+
if (tasks.length === 0) {
|
|
42320
|
+
return "There are no pending scheduled tasks.";
|
|
42321
|
+
}
|
|
42322
|
+
return [
|
|
42323
|
+
"Pending scheduled tasks:",
|
|
42324
|
+
...tasks.map((task) => `- #${task.id} at ${task.execute_at} [${task.session_mode ?? "bound"}] -> ${task.session_alias}: ${task.message}`)
|
|
42325
|
+
].join(`
|
|
42326
|
+
`);
|
|
42327
|
+
}
|
|
42328
|
+
function renderScheduledCancel(result) {
|
|
42329
|
+
return result.cancelled ? `Scheduled task #${result.id} cancelled.` : `No pending scheduled task #${result.id} found.`;
|
|
42330
|
+
}
|
|
41903
42331
|
function renderTaskListItem(task) {
|
|
41904
42332
|
const role = task.role ? ` / ${task.role}` : "";
|
|
41905
42333
|
const group = task.groupId ? `; group: ${task.groupId}` : "";
|
|
@@ -42070,6 +42498,15 @@ class OrchestrationClient {
|
|
|
42070
42498
|
async createGroup(input) {
|
|
42071
42499
|
return await this.request("group.new", input);
|
|
42072
42500
|
}
|
|
42501
|
+
async scheduledCreate(input) {
|
|
42502
|
+
return await this.request("scheduled.create", input);
|
|
42503
|
+
}
|
|
42504
|
+
async scheduledList(input) {
|
|
42505
|
+
return await this.request("scheduled.list", input);
|
|
42506
|
+
}
|
|
42507
|
+
async scheduledCancel(input) {
|
|
42508
|
+
return await this.request("scheduled.cancel", input);
|
|
42509
|
+
}
|
|
42073
42510
|
async request(method, params, timeoutMs = this.timeoutMs) {
|
|
42074
42511
|
const id = this.createId();
|
|
42075
42512
|
return await new Promise((resolve, reject) => {
|
|
@@ -42182,7 +42619,25 @@ function createOrchestrationTransport(endpoint, deps = {}) {
|
|
|
42182
42619
|
},
|
|
42183
42620
|
coordinatorAnswerQuestion: async (input) => await client.coordinatorAnswerQuestion(input),
|
|
42184
42621
|
coordinatorRequestHumanInput: async (input) => await client.coordinatorRequestHumanInput(input),
|
|
42185
|
-
coordinatorReviewContestedResult: async (input) => await client.coordinatorReviewContestedResult(input)
|
|
42622
|
+
coordinatorReviewContestedResult: async (input) => await client.coordinatorReviewContestedResult(input),
|
|
42623
|
+
scheduledCreate: async (input) => {
|
|
42624
|
+
if (!client.scheduledCreate) {
|
|
42625
|
+
throw new Error("orchestration client scheduledCreate is not configured");
|
|
42626
|
+
}
|
|
42627
|
+
return await client.scheduledCreate(input);
|
|
42628
|
+
},
|
|
42629
|
+
scheduledList: async (input) => {
|
|
42630
|
+
if (!client.scheduledList) {
|
|
42631
|
+
throw new Error("orchestration client scheduledList is not configured");
|
|
42632
|
+
}
|
|
42633
|
+
return await client.scheduledList(input);
|
|
42634
|
+
},
|
|
42635
|
+
scheduledCancel: async (input) => {
|
|
42636
|
+
if (!client.scheduledCancel) {
|
|
42637
|
+
throw new Error("orchestration client scheduledCancel is not configured");
|
|
42638
|
+
}
|
|
42639
|
+
return await client.scheduledCancel(input);
|
|
42640
|
+
}
|
|
42186
42641
|
};
|
|
42187
42642
|
}
|
|
42188
42643
|
|
|
@@ -42206,7 +42661,7 @@ function createWeacpxMcpServer(options) {
|
|
|
42206
42661
|
const taskOptionsById = new Map;
|
|
42207
42662
|
const watchTasksById = new Map;
|
|
42208
42663
|
const server = new Server({
|
|
42209
|
-
name: "weacpx
|
|
42664
|
+
name: "weacpx",
|
|
42210
42665
|
version: readVersion()
|
|
42211
42666
|
}, {
|
|
42212
42667
|
capabilities: {
|
|
@@ -42238,6 +42693,7 @@ function createWeacpxMcpServer(options) {
|
|
|
42238
42693
|
coordinatorSession: identity.coordinatorSession,
|
|
42239
42694
|
...identity.sourceHandle ? { sourceHandle: identity.sourceHandle } : {},
|
|
42240
42695
|
...identity.isExternalCoordinator ? { isExternalCoordinator: true } : {},
|
|
42696
|
+
...identity.internalSessionTools ? { internalSessionTools: true } : {},
|
|
42241
42697
|
...options.availableAgents ? { availableAgents: options.availableAgents } : {}
|
|
42242
42698
|
});
|
|
42243
42699
|
return toolState;
|
|
@@ -42673,7 +43129,8 @@ async function resolveMcpIdentity(server, options) {
|
|
|
42673
43129
|
return {
|
|
42674
43130
|
coordinatorSession: options.coordinatorSession,
|
|
42675
43131
|
...options.sourceHandle ? { sourceHandle: options.sourceHandle } : {},
|
|
42676
|
-
...options.isExternalCoordinator ? { isExternalCoordinator: true } : {}
|
|
43132
|
+
...options.isExternalCoordinator ? { isExternalCoordinator: true } : {},
|
|
43133
|
+
...options.internalSessionTools ? { internalSessionTools: true } : {}
|
|
42677
43134
|
};
|
|
42678
43135
|
}
|
|
42679
43136
|
throw new McpError(ErrorCode.InvalidRequest, "weacpx MCP identity is not configured; run through `weacpx mcp-stdio` or provide --coordinator-session");
|
|
@@ -42762,6 +43219,7 @@ async function runWeacpxMcpServer(options) {
|
|
|
42762
43219
|
transport,
|
|
42763
43220
|
...options.coordinatorSession ? { coordinatorSession: options.coordinatorSession } : {},
|
|
42764
43221
|
...options.sourceHandle ? { sourceHandle: options.sourceHandle } : {},
|
|
43222
|
+
...options.internalSessionTools ? { internalSessionTools: true } : {},
|
|
42765
43223
|
...options.resolveIdentity ? { resolveIdentity: options.resolveIdentity } : {},
|
|
42766
43224
|
...options.availableAgents ? { availableAgents: options.availableAgents } : {}
|
|
42767
43225
|
});
|
|
@@ -42856,6 +43314,11 @@ function parseCoordinatorSession(args, env = process.env) {
|
|
|
42856
43314
|
});
|
|
42857
43315
|
}
|
|
42858
43316
|
|
|
43317
|
+
// src/mcp/parse-internal-session-tools.ts
|
|
43318
|
+
function parseInternalSessionToolsFlag(args, _env = process.env) {
|
|
43319
|
+
return args.includes("--internal-session-tools");
|
|
43320
|
+
}
|
|
43321
|
+
|
|
42859
43322
|
// src/mcp/parse-source-handle.ts
|
|
42860
43323
|
function parseSourceHandle(args, env = process.env) {
|
|
42861
43324
|
return parseStringFlag(args, env, {
|
|
@@ -42868,13 +43331,19 @@ function parseSourceHandle(args, env = process.env) {
|
|
|
42868
43331
|
init_workspace_path();
|
|
42869
43332
|
init_workspace_name();
|
|
42870
43333
|
init_state_store();
|
|
43334
|
+
init_channel_scope();
|
|
43335
|
+
init_scheduled_render();
|
|
43336
|
+
init_scheduled_service();
|
|
42871
43337
|
|
|
42872
43338
|
// src/onboarding.ts
|
|
42873
43339
|
init_workspace_path();
|
|
42874
43340
|
init_workspace_name();
|
|
43341
|
+
init_default_workspace();
|
|
42875
43342
|
init_agent_templates();
|
|
42876
43343
|
function isFirstUse(config2, state) {
|
|
42877
|
-
|
|
43344
|
+
const workspaceNames = Object.keys(config2.workspaces ?? {});
|
|
43345
|
+
const onlyDefaultOrEmpty = workspaceNames.length === 0 || workspaceNames.length === 1 && workspaceNames[0] === DEFAULT_HOME_WORKSPACE_NAME;
|
|
43346
|
+
return Object.keys(state.sessions ?? {}).length === 0 && onlyDefaultOrEmpty && (config2.plugins ?? []).length === 0;
|
|
42878
43347
|
}
|
|
42879
43348
|
async function maybeRunFirstUseOnboarding(input) {
|
|
42880
43349
|
if (!isFirstUse(input.config, input.state))
|
|
@@ -44542,7 +45011,8 @@ function createMcpStdioIdentityResolver(input) {
|
|
|
44542
45011
|
return {
|
|
44543
45012
|
coordinatorSession: resolvedCoordinatorSession,
|
|
44544
45013
|
...sourceHandle ? { sourceHandle } : {},
|
|
44545
|
-
...startup.kind === "external-coordinator" ? { isExternalCoordinator: true } : {}
|
|
45014
|
+
...startup.kind === "external-coordinator" ? { isExternalCoordinator: true } : {},
|
|
45015
|
+
...input.internalSessionTools && startup.kind === "existing-session" && !sourceHandle ? { internalSessionTools: true } : {}
|
|
44546
45016
|
};
|
|
44547
45017
|
};
|
|
44548
45018
|
}
|
|
@@ -44587,6 +45057,7 @@ var HELP_LINES = [
|
|
|
44587
45057
|
"weacpx version - 查看版本",
|
|
44588
45058
|
"weacpx agent|agents list|add|rm|templates - 管理本机 Agent",
|
|
44589
45059
|
"weacpx workspace list|add [name] [--raw]|rm <name> - 管理本机工作区(别名:ws)",
|
|
45060
|
+
"weacpx later|lt list|cancel <id> - 管理本机待执行定时任务",
|
|
44590
45061
|
"weacpx mcp-stdio [--coordinator-session <session>] [--source-handle <handle>] [--workspace <name>] - 启动 MCP stdio 服务"
|
|
44591
45062
|
];
|
|
44592
45063
|
function getUsageText() {
|
|
@@ -44674,6 +45145,17 @@ async function runCli(args, deps = {}) {
|
|
|
44674
45145
|
}
|
|
44675
45146
|
return result;
|
|
44676
45147
|
}
|
|
45148
|
+
case "later":
|
|
45149
|
+
case "lt": {
|
|
45150
|
+
const result = await handleLaterCli(args.slice(1), { print });
|
|
45151
|
+
if (result === null) {
|
|
45152
|
+
for (const line of HELP_LINES) {
|
|
45153
|
+
print(line);
|
|
45154
|
+
}
|
|
45155
|
+
return 1;
|
|
45156
|
+
}
|
|
45157
|
+
return result;
|
|
45158
|
+
}
|
|
44677
45159
|
case "plugin": {
|
|
44678
45160
|
const result = await handlePluginCli(args.slice(1), await createPluginCliDeps({
|
|
44679
45161
|
print,
|
|
@@ -45022,6 +45504,48 @@ async function agentRemove(rawName, print) {
|
|
|
45022
45504
|
print(`Agent「${name}」已删除`);
|
|
45023
45505
|
return 0;
|
|
45024
45506
|
}
|
|
45507
|
+
async function handleLaterCli(args, deps) {
|
|
45508
|
+
const subcommand = args[0];
|
|
45509
|
+
switch (subcommand) {
|
|
45510
|
+
case "list":
|
|
45511
|
+
if (args.length !== 1)
|
|
45512
|
+
return null;
|
|
45513
|
+
return await laterList(deps.print);
|
|
45514
|
+
case "cancel":
|
|
45515
|
+
if (args.length !== 2 || !args[1])
|
|
45516
|
+
return null;
|
|
45517
|
+
return await laterCancel(args[1], deps.print);
|
|
45518
|
+
default:
|
|
45519
|
+
return null;
|
|
45520
|
+
}
|
|
45521
|
+
}
|
|
45522
|
+
async function laterList(print) {
|
|
45523
|
+
const scheduled = await createCliScheduledTaskService();
|
|
45524
|
+
print(renderLaterList(scheduled.listPending(), (alias) => toDisplaySessionAlias(alias)));
|
|
45525
|
+
return 0;
|
|
45526
|
+
}
|
|
45527
|
+
async function laterCancel(rawId, print) {
|
|
45528
|
+
const id = normalizeId(rawId);
|
|
45529
|
+
if (id.length === 0) {
|
|
45530
|
+
print("定时任务 ID 不能为空。");
|
|
45531
|
+
return 1;
|
|
45532
|
+
}
|
|
45533
|
+
const scheduled = await createCliScheduledTaskService();
|
|
45534
|
+
const ok = await scheduled.cancelPending(id);
|
|
45535
|
+
if (!ok) {
|
|
45536
|
+
print(`未找到待执行的定时任务 #${id}。`);
|
|
45537
|
+
print("可以用 weacpx later list 查看当前待执行任务。");
|
|
45538
|
+
return 1;
|
|
45539
|
+
}
|
|
45540
|
+
print(`已取消定时任务 #${id}`);
|
|
45541
|
+
return 0;
|
|
45542
|
+
}
|
|
45543
|
+
async function createCliScheduledTaskService() {
|
|
45544
|
+
const runtimePaths = (await init_main().then(() => exports_main)).resolveRuntimePaths();
|
|
45545
|
+
const stateStore = new StateStore(runtimePaths.statePath);
|
|
45546
|
+
const state = await stateStore.load();
|
|
45547
|
+
return new ScheduledTaskService(state, stateStore);
|
|
45548
|
+
}
|
|
45025
45549
|
function resolveConfigPathForCurrentEnv() {
|
|
45026
45550
|
return process.env.WEACPX_CONFIG ?? `${requireHome2()}/.weacpx/config.json`;
|
|
45027
45551
|
}
|
|
@@ -45148,10 +45672,12 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
45148
45672
|
let transport;
|
|
45149
45673
|
let identityResolver;
|
|
45150
45674
|
let availableAgents;
|
|
45675
|
+
let internalSessionTools = false;
|
|
45151
45676
|
try {
|
|
45152
45677
|
const parsedCoordinatorSession = parseCoordinatorSession(args, process.env);
|
|
45153
45678
|
sourceHandle = parseSourceHandle(args, process.env);
|
|
45154
45679
|
const workspace = parseCoordinatorWorkspace(args, process.env);
|
|
45680
|
+
const requestedInternalSessionTools = parseInternalSessionToolsFlag(args, process.env);
|
|
45155
45681
|
endpoint = resolveDefaultOrchestrationEndpoint(process.env, process.platform);
|
|
45156
45682
|
const client = new OrchestrationClient(endpoint);
|
|
45157
45683
|
transport = createOrchestrationTransport(endpoint, { client });
|
|
@@ -45166,10 +45692,12 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
45166
45692
|
workspace,
|
|
45167
45693
|
config: config2,
|
|
45168
45694
|
state,
|
|
45169
|
-
client
|
|
45695
|
+
client,
|
|
45696
|
+
internalSessionTools: requestedInternalSessionTools
|
|
45170
45697
|
});
|
|
45171
45698
|
const eagerIdentity = parsedCoordinatorSession ? await resolveIdentity({ clientName: undefined, listRoots: async () => [] }) : null;
|
|
45172
45699
|
coordinatorSession = eagerIdentity?.coordinatorSession ?? "";
|
|
45700
|
+
internalSessionTools = eagerIdentity?.internalSessionTools ?? false;
|
|
45173
45701
|
identityResolver = eagerIdentity ? undefined : resolveIdentity;
|
|
45174
45702
|
} catch (error2) {
|
|
45175
45703
|
(deps.stderr ?? ((text) => process.stderr.write(text)))(`${error2 instanceof Error ? error2.message : String(error2)}
|
|
@@ -45180,6 +45708,7 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
45180
45708
|
transport,
|
|
45181
45709
|
...coordinatorSession ? { coordinatorSession } : {},
|
|
45182
45710
|
...sourceHandle ? { sourceHandle } : {},
|
|
45711
|
+
...internalSessionTools ? { internalSessionTools: true } : {},
|
|
45183
45712
|
...identityResolver ? { resolveIdentity: identityResolver } : {},
|
|
45184
45713
|
...availableAgents ? { availableAgents } : {},
|
|
45185
45714
|
onDiagnostic: (event, context) => {
|