tellrun 1.3.8 → 1.3.9
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 +43 -27
- package/README.zh-CN.md +37 -21
- package/dist/agents/codex-input.js +19 -0
- package/dist/agents/codex-input.js.map +1 -0
- package/dist/agents/codex-interaction.js +34 -27
- package/dist/agents/codex-interaction.js.map +1 -1
- package/dist/agents/codex.js +230 -50
- package/dist/agents/codex.js.map +1 -1
- package/dist/core/cron.js +226 -0
- package/dist/core/cron.js.map +1 -0
- package/dist/core/menus.js +1 -2
- package/dist/core/menus.js.map +1 -1
- package/dist/core/relay.js +163 -31
- package/dist/core/relay.js.map +1 -1
- package/dist/im/telegram-files.js +53 -0
- package/dist/im/telegram-files.js.map +1 -0
- package/dist/im/telegram.js +11 -3
- package/dist/im/telegram.js.map +1 -1
- package/dist/main.js +7 -2
- package/dist/main.js.map +1 -1
- package/dist/storage/attachments.js +129 -0
- package/dist/storage/attachments.js.map +1 -0
- package/dist/storage/schema.js +64 -50
- package/dist/storage/schema.js.map +1 -1
- package/dist/storage/store.js +136 -53
- package/dist/storage/store.js.map +1 -1
- package/docs/architecture.md +45 -19
- package/docs/prd.md +33 -9
- package/package.json +2 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { Cron } from "croner";
|
|
2
|
+
import { activeTaskStatuses } from "../storage/schema.js";
|
|
3
|
+
/** 只使用五段 Cron;Croner 负责校验和时区计算,不创建独立执行定时器。 */
|
|
4
|
+
function nextRun(expression, timezone, after) {
|
|
5
|
+
let cron;
|
|
6
|
+
try {
|
|
7
|
+
if (expression.trim().split(/\s+/).length !== 5)
|
|
8
|
+
return null;
|
|
9
|
+
cron = new Cron(expression, { timezone, mode: "5-part", paused: true });
|
|
10
|
+
return cron.nextRun(new Date(after))?.getTime() ?? null;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
cron?.stop();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** 时间按计划保存的时区展示,避免服务器时区变更后误读旧任务。 */
|
|
20
|
+
function formatTime(timestamp, timezone) {
|
|
21
|
+
if (timestamp === null)
|
|
22
|
+
return "—";
|
|
23
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
24
|
+
timeZone: timezone,
|
|
25
|
+
year: "numeric",
|
|
26
|
+
month: "2-digit",
|
|
27
|
+
day: "2-digit",
|
|
28
|
+
hour: "2-digit",
|
|
29
|
+
minute: "2-digit",
|
|
30
|
+
hourCycle: "h23",
|
|
31
|
+
}).format(timestamp);
|
|
32
|
+
}
|
|
33
|
+
/** 命令、触发记录和任务创建共用存储事务;计划本身不直接调用 Agent。 */
|
|
34
|
+
export function createCrons(storage) {
|
|
35
|
+
/** 调度只读取当前机器人和授权用户的计划,旧配置的计划不能误触发。 */
|
|
36
|
+
function channelsFor(source) {
|
|
37
|
+
return new Map(storage
|
|
38
|
+
.read()
|
|
39
|
+
.channels.filter((channel) => channel.platform === source.platform &&
|
|
40
|
+
channel.accountId === source.accountId &&
|
|
41
|
+
channel.userId === source.userId)
|
|
42
|
+
.map((channel) => [channel.id, channel]));
|
|
43
|
+
}
|
|
44
|
+
/** 到期时向未来推进;无效计划关闭并通知,避免反复触发或阻塞其他计划。 */
|
|
45
|
+
function advance(cron, now) {
|
|
46
|
+
cron.nextRunAt = nextRun(cron.expression, cron.timezone, now);
|
|
47
|
+
if (cron.nextRunAt !== null)
|
|
48
|
+
return;
|
|
49
|
+
cron.enabled = false;
|
|
50
|
+
cron.pendingAt = null;
|
|
51
|
+
storage.enqueueReply(cron.channelId, `Cron #${cron.id} disabled: invalid expression or timezone, or no future run. Delete and recreate the schedule.`);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
/** 已接收命令由外层事务去重;参数校验完成后才修改计划。 */
|
|
55
|
+
handleCron(channelId, args, now = Date.now()) {
|
|
56
|
+
const state = storage.read();
|
|
57
|
+
const channel = state.channels.find((item) => item.id === channelId);
|
|
58
|
+
if (!channel?.cwd)
|
|
59
|
+
return "Set a working directory with /cwd first.";
|
|
60
|
+
const cwd = channel.cwd;
|
|
61
|
+
/** 新计划读取创建时的服务器时区,已有计划始终使用保存的时区。 */
|
|
62
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
63
|
+
/** 帮助与参数错误共用命令格式,描述可以包含空格和换行。 */
|
|
64
|
+
const usage = [
|
|
65
|
+
"/cron — List all scheduled tasks",
|
|
66
|
+
"/cron add <minute> <hour> <day> <month> <weekday> <task>",
|
|
67
|
+
"/cron del <id> — Delete a schedule",
|
|
68
|
+
"/cron enable <id> — Enable a schedule",
|
|
69
|
+
"/cron disable <id> — Disable a schedule",
|
|
70
|
+
`New schedule timezone: ${timezone} (server timezone)`,
|
|
71
|
+
"Example: /cron add 0 9 * * 1-5 Check the project and report issues",
|
|
72
|
+
].join("\n");
|
|
73
|
+
const input = args.trim();
|
|
74
|
+
const paused = channel.paused
|
|
75
|
+
? "\nChat paused. Send /resume to allow future scheduled runs."
|
|
76
|
+
: "";
|
|
77
|
+
if (!input) {
|
|
78
|
+
const crons = state.crons.filter((cron) => cron.channelId === channelId);
|
|
79
|
+
if (!crons.length)
|
|
80
|
+
return `No scheduled tasks.${paused}\n\n${usage}`;
|
|
81
|
+
return [
|
|
82
|
+
`Scheduled tasks: ${crons.length}${paused}`,
|
|
83
|
+
...crons.map((cron) => {
|
|
84
|
+
const running = state.tasks.find((task) => task.cron?.id === cron.id && activeTaskStatuses.has(task.status));
|
|
85
|
+
const activity = [
|
|
86
|
+
running ? `Active task: #${running.id}` : "",
|
|
87
|
+
cron.pendingAt !== null ? "Pending" : "",
|
|
88
|
+
]
|
|
89
|
+
.filter(Boolean)
|
|
90
|
+
.join(" · ");
|
|
91
|
+
return `#${cron.id} · ${cron.enabled ? "Enabled" : "Disabled"}${activity ? ` · ${activity}` : ""}\n${cron.expression}\nTimezone: ${cron.timezone}\nDirectory: ${cron.cwd}\nTask: ${cron.prompt}\nNext: ${formatTime(cron.nextRunAt, cron.timezone)}`;
|
|
92
|
+
}),
|
|
93
|
+
].join("\n\n");
|
|
94
|
+
}
|
|
95
|
+
const add = input.match(/^add\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+([\s\S]+)$/);
|
|
96
|
+
if (add) {
|
|
97
|
+
const expression = add.slice(1, 6).join(" ");
|
|
98
|
+
const prompt = add[6]?.trim();
|
|
99
|
+
const nextRunAt = nextRun(expression, timezone, now);
|
|
100
|
+
if (!prompt || nextRunAt === null)
|
|
101
|
+
return `Invalid cron expression or no future run. Use five fields.\n\n${usage}`;
|
|
102
|
+
return storage.update(() => {
|
|
103
|
+
const state = storage.read();
|
|
104
|
+
const id = state.nextCronId++;
|
|
105
|
+
state.crons.push({
|
|
106
|
+
id,
|
|
107
|
+
channelId,
|
|
108
|
+
expression,
|
|
109
|
+
timezone,
|
|
110
|
+
prompt,
|
|
111
|
+
cwd,
|
|
112
|
+
model: channel.model,
|
|
113
|
+
reasoningEffort: channel.reasoningEffort,
|
|
114
|
+
permissionMode: channel.permissionMode,
|
|
115
|
+
enabled: true,
|
|
116
|
+
nextRunAt,
|
|
117
|
+
pendingAt: null,
|
|
118
|
+
});
|
|
119
|
+
return `Cron #${id} created (enabled).\n${expression}\nTimezone: ${timezone}\nDirectory: ${cwd}\nTask: ${prompt}\nNext: ${formatTime(nextRunAt, timezone)}${paused}`;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const action = input.match(/^(del|enable|disable)\s+([1-9]\d*)$/);
|
|
123
|
+
if (!action || !Number.isSafeInteger(Number(action[2])))
|
|
124
|
+
return usage;
|
|
125
|
+
const cron = state.crons.find((item) => item.id === Number(action[2]) && item.channelId === channelId);
|
|
126
|
+
if (!cron)
|
|
127
|
+
return "Schedule not found. Send /cron to see task IDs.";
|
|
128
|
+
if (action[1] === "enable" && cron.enabled)
|
|
129
|
+
return `Cron #${cron.id} is already enabled.\nNext: ${formatTime(cron.nextRunAt, cron.timezone)} (${cron.timezone})${paused}`;
|
|
130
|
+
const nextRunAt = action[1] === "enable" ? nextRun(cron.expression, cron.timezone, now) : null;
|
|
131
|
+
if (action[1] === "enable" && nextRunAt === null)
|
|
132
|
+
return "No future run could be calculated. Delete and recreate the schedule.";
|
|
133
|
+
return storage.update(() => {
|
|
134
|
+
const state = storage.read();
|
|
135
|
+
const current = state.crons.find((item) => item.id === cron.id);
|
|
136
|
+
if (!current)
|
|
137
|
+
return "Schedule not found.";
|
|
138
|
+
const active = state.tasks.some((task) => task.cron?.id === current.id && activeTaskStatuses.has(task.status));
|
|
139
|
+
if (action[1] === "del")
|
|
140
|
+
state.crons = state.crons.filter((item) => item.id !== current.id);
|
|
141
|
+
else {
|
|
142
|
+
current.enabled = action[1] === "enable";
|
|
143
|
+
current.nextRunAt = nextRunAt;
|
|
144
|
+
current.pendingAt = null;
|
|
145
|
+
}
|
|
146
|
+
return `Cron #${current.id} ${action[1] === "del" ? "deleted" : current.enabled ? "enabled" : "disabled"}.${action[1] === "enable" ? `\nNext: ${formatTime(nextRunAt, current.timezone)} (${current.timezone})${paused}` : active ? "\nThe active task will continue. Use /stop to stop it." : ""}`;
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
/** 暂停清空等待触发;恢复时跳过暂停期间到期的次数,不产生过期工作。 */
|
|
150
|
+
resetCronPending(channelId, now = Date.now()) {
|
|
151
|
+
storage.update(() => {
|
|
152
|
+
for (const cron of storage.read().crons) {
|
|
153
|
+
if (cron.channelId !== channelId)
|
|
154
|
+
continue;
|
|
155
|
+
cron.pendingAt = null;
|
|
156
|
+
if (cron.enabled && (cron.nextRunAt === null || cron.nextRunAt <= now))
|
|
157
|
+
advance(cron, now);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
/** 只跳过停机期间错过的触发;停机前已记录的待执行项继续保留。 */
|
|
162
|
+
recoverCrons(source, now = Date.now()) {
|
|
163
|
+
const channels = channelsFor(source);
|
|
164
|
+
const due = storage
|
|
165
|
+
.read()
|
|
166
|
+
.crons.filter((cron) => channels.has(cron.channelId) &&
|
|
167
|
+
((cron.enabled && (cron.nextRunAt === null || cron.nextRunAt <= now)) ||
|
|
168
|
+
(channels.get(cron.channelId)?.paused && cron.pendingAt !== null)));
|
|
169
|
+
if (!due.length)
|
|
170
|
+
return;
|
|
171
|
+
storage.update(() => {
|
|
172
|
+
for (const saved of due) {
|
|
173
|
+
const cron = storage.read().crons.find((item) => item.id === saved.id);
|
|
174
|
+
if (!cron)
|
|
175
|
+
continue;
|
|
176
|
+
if (channels.get(cron.channelId)?.paused)
|
|
177
|
+
cron.pendingAt = null;
|
|
178
|
+
if (cron.enabled && (cron.nextRunAt === null || cron.nextRunAt <= now))
|
|
179
|
+
advance(cron, now);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
/** 触发、下一时刻和执行快照一次落盘;忙时每个计划只保留一次,空闲按时间串行领取。 */
|
|
184
|
+
tickCrons(source, now = Date.now()) {
|
|
185
|
+
const channels = channelsFor(source);
|
|
186
|
+
const crons = storage
|
|
187
|
+
.read()
|
|
188
|
+
.crons.filter((cron) => cron.enabled && channels.has(cron.channelId));
|
|
189
|
+
const busy = storage.read().tasks.some((task) => activeTaskStatuses.has(task.status));
|
|
190
|
+
if (!crons.some((cron) => (cron.nextRunAt !== null && cron.nextRunAt <= now) ||
|
|
191
|
+
(cron.pendingAt !== null && (!busy || channels.get(cron.channelId)?.paused))))
|
|
192
|
+
return;
|
|
193
|
+
storage.update(() => {
|
|
194
|
+
const state = storage.read();
|
|
195
|
+
for (const cron of state.crons) {
|
|
196
|
+
const channel = channels.get(cron.channelId);
|
|
197
|
+
if (!cron.enabled || !channel)
|
|
198
|
+
continue;
|
|
199
|
+
if (channel.paused)
|
|
200
|
+
cron.pendingAt = null;
|
|
201
|
+
if (cron.nextRunAt === null || cron.nextRunAt > now)
|
|
202
|
+
continue;
|
|
203
|
+
if (!channel.paused)
|
|
204
|
+
cron.pendingAt ??= cron.nextRunAt;
|
|
205
|
+
advance(cron, now);
|
|
206
|
+
}
|
|
207
|
+
if (state.tasks.some((task) => activeTaskStatuses.has(task.status)))
|
|
208
|
+
return;
|
|
209
|
+
const cron = state.crons
|
|
210
|
+
.filter((item) => item.enabled &&
|
|
211
|
+
item.pendingAt !== null &&
|
|
212
|
+
channels.has(item.channelId) &&
|
|
213
|
+
!channels.get(item.channelId)?.paused)
|
|
214
|
+
.sort((a, b) => (a.pendingAt ?? 0) - (b.pendingAt ?? 0) || a.id - b.id)[0];
|
|
215
|
+
const channel = cron && state.channels.find((item) => item.id === cron.channelId);
|
|
216
|
+
if (!cron || !channel || cron.pendingAt === null)
|
|
217
|
+
return;
|
|
218
|
+
const scheduledAt = cron.pendingAt;
|
|
219
|
+
cron.pendingAt = null;
|
|
220
|
+
const task = storage.createTask(channel, cron, scheduledAt);
|
|
221
|
+
storage.enqueueReply(channel.id, `Cron #${cron.id} queued task #${task.id}.\nScheduled: ${formatTime(scheduledAt, cron.timezone)} (${cron.timezone})\nDirectory: ${cron.cwd}\nTask: ${cron.prompt}`);
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=cron.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/core/cron.ts"],"names":[],"mappings":"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"}
|
package/dist/core/menus.js
CHANGED
|
@@ -46,10 +46,9 @@ export function createMenus(storage) {
|
|
|
46
46
|
.channels.find((channel) => channel.platform === source.platform &&
|
|
47
47
|
channel.accountId === source.accountId &&
|
|
48
48
|
channel.userId === source.userId &&
|
|
49
|
-
channel.cwd &&
|
|
50
49
|
channel.modelMenu?.stage === "loading" &&
|
|
51
50
|
channel.modelMenu.expiresAt > Date.now());
|
|
52
|
-
if (channel?.modelMenu
|
|
51
|
+
if (channel?.modelMenu)
|
|
53
52
|
return { channelId: channel.id, cwd: channel.cwd, id: channel.modelMenu.id };
|
|
54
53
|
},
|
|
55
54
|
/** 新菜单、换目录和过期后丢弃旧查询;失败时保留原模型选择。 */
|
package/dist/core/menus.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menus.js","sourceRoot":"","sources":["../../src/core/menus.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"menus.js","sourceRoot":"","sources":["../../src/core/menus.ts"],"names":[],"mappings":"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"}
|
package/dist/core/relay.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { isAbsolute } from "node:path";
|
|
3
3
|
import { setTimeout } from "node:timers/promises";
|
|
4
|
+
import { AttachmentError, maxAttachmentBytes } from "../storage/attachments.js";
|
|
4
5
|
/** 接收消息只保存必要状态;任务执行独立串行运行,控制命令始终可用。 */
|
|
5
6
|
export function createRelayService(options) {
|
|
6
7
|
let stopping = false;
|
|
8
|
+
/** 当前任务的附件准备可独立取消,不能阻塞停止指令或拖住服务退出。 */
|
|
9
|
+
let inputPreparation;
|
|
7
10
|
/** 菜单查询独立于任务,关停时取消查询并等待退出后再关闭存储。 */
|
|
8
11
|
const modelLoading = new AbortController();
|
|
9
|
-
/**
|
|
10
|
-
const
|
|
12
|
+
/** 状态与会话命令复用独立工作循环,不阻塞入站审批和停止指令。 */
|
|
13
|
+
const controlQueries = [];
|
|
14
|
+
/** 会话变更确认前暂缓新任务,避免接口等待期间把消息发往旧会话。 */
|
|
15
|
+
let changingSession = false;
|
|
11
16
|
/** 状态查询与历史结果共用面向用户的状态名称。 */
|
|
12
17
|
const statuses = {
|
|
13
18
|
queued: "Queued",
|
|
19
|
+
preparing: "Downloading attachments",
|
|
14
20
|
dispatching: "Connecting",
|
|
15
21
|
running: "Running",
|
|
16
22
|
cancelling: "Stopping",
|
|
@@ -25,11 +31,8 @@ export function createRelayService(options) {
|
|
|
25
31
|
async handleMessage(message) {
|
|
26
32
|
if (stopping)
|
|
27
33
|
throw new Error("Service stopping. Message not accepted.");
|
|
28
|
-
let
|
|
34
|
+
let controlQuery;
|
|
29
35
|
const reply = options.store.accept(message, (channel) => {
|
|
30
|
-
/** 未指定目录时沿用服务启动目录,并随消息一同保存,后续可用 /cwd 切换。 */
|
|
31
|
-
if (!channel.cwd)
|
|
32
|
-
options.store.setSession(channel.id, realpathSync(process.cwd()));
|
|
33
36
|
const task = options.store.activeTask();
|
|
34
37
|
if (message.content.type === "permissions" || message.content.type === "model")
|
|
35
38
|
return options.store.chooseOption(channel.id, message.content);
|
|
@@ -39,11 +42,20 @@ export function createRelayService(options) {
|
|
|
39
42
|
if (answer !== undefined || message.content.type === "interaction")
|
|
40
43
|
return answer;
|
|
41
44
|
}
|
|
45
|
+
if (changingSession &&
|
|
46
|
+
(message.content.type === "text" ||
|
|
47
|
+
message.content.type === "attachments" ||
|
|
48
|
+
(message.content.type === "command" &&
|
|
49
|
+
(["new", "switch", "list", "update"].includes(message.content.name) ||
|
|
50
|
+
(message.content.name === "cwd" && message.content.args)))))
|
|
51
|
+
return "Session change in progress. Wait for confirmation, then resend your message.";
|
|
42
52
|
if (message.content.type === "command") {
|
|
43
53
|
switch (message.content.name) {
|
|
44
54
|
case "start":
|
|
45
55
|
case "help":
|
|
46
|
-
return "Tellrun ready.\nSend text to
|
|
56
|
+
return "Tellrun ready.\nSend text, photos, or files to Codex (up to 20 MB per attachment). Add a caption to describe your task. New messages add to the running task.\n/cwd <path> — Set directory; start a new session\n/new [name] — Create and select a new session\n/list — List sessions in the current project\n/switch <id> — Select a session by its full ID\n/model — Select model and reasoning effort\n/permissions — Select permission mode\n/cron — Manage scheduled tasks\n/status — Status and update check\n/update — Ask Codex to upgrade Tellrun\n/last — Last result\n/stop — Stop task and pause\n/resume — Accept new tasks\nWait for the task to finish before creating or switching sessions.";
|
|
57
|
+
case "cron":
|
|
58
|
+
return options.store.handleCron(channel.id, message.content.args);
|
|
47
59
|
case "model":
|
|
48
60
|
if (message.content.args)
|
|
49
61
|
return "Use /model to choose a model with the buttons.";
|
|
@@ -55,11 +67,11 @@ export function createRelayService(options) {
|
|
|
55
67
|
case "status": {
|
|
56
68
|
/** 查询期间保存请求时的选择,后续切换不会改写这次状态快照。 */
|
|
57
69
|
const snapshot = structuredClone(channel);
|
|
58
|
-
|
|
70
|
+
controlQuery = async () => {
|
|
59
71
|
/** 未选择时读取有效配置;读取失败仅回退到明确保存的模型与档位。 */
|
|
60
72
|
const describe = async (selection) => {
|
|
61
73
|
try {
|
|
62
|
-
return await options.agent.describeModel(selection.cwd
|
|
74
|
+
return await options.agent.describeModel(selection.cwd, selection, modelLoading.signal);
|
|
63
75
|
}
|
|
64
76
|
catch {
|
|
65
77
|
return selection.model && selection.reasoningEffort
|
|
@@ -100,7 +112,7 @@ export function createRelayService(options) {
|
|
|
100
112
|
const upgradeHint = update?.available
|
|
101
113
|
? `\n\nSend /update to ask ${options.agent.name} to upgrade Tellrun.`
|
|
102
114
|
: "";
|
|
103
|
-
options.store.reply(snapshot.id, `Service: connected\nTellrun: ${options.updates.version}${versionSuffix}\n${options.agent.name}: ${options.agent.version}\nDirectory: ${snapshot.cwd}\nModel: ${formatModel(model)}${showTaskModel ? `\nTask model: ${formatModel(taskModel)}` : ""}\nPermissions: ${options.agent.permissionModes.find((mode) => mode.id === snapshot.permissionMode)?.label ?? snapshot.permissionMode ?? options.agent.permissionModes[0]?.label ?? "Not set"}${task && task.permissionMode !== snapshot.permissionMode ? `\nTask permissions: ${options.agent.permissionModes.find((mode) => mode.id === task.permissionMode)?.label ?? task.permissionMode ?? options.agent.permissionModes[0]?.label ?? "Not set"}` : ""}\nTask: ${task ? `#${task.id} ${statuses[task.status]}` : "idle"}${task?.interactions.length ? `\nPending: ${task.interactions.map((item) => `${item.kind === "approval" ? "Approval" : "Question"}${item.blocking ? " (waiting)" : " (task running)"}`).join(", ")}` : ""}${upgradeHint}`);
|
|
115
|
+
options.store.reply(snapshot.id, `Service: connected\nTellrun: ${options.updates.version}${versionSuffix}\n${options.agent.name}: ${options.agent.version}\nDirectory: ${snapshot.cwd}${task && task.cwd !== snapshot.cwd ? `\nTask directory: ${task.cwd}` : ""}\nSession: ${snapshot.sessionId ?? "Not started"}\nModel: ${formatModel(model)}${showTaskModel ? `\nTask model: ${formatModel(taskModel)}` : ""}\nPermissions: ${options.agent.permissionModes.find((mode) => mode.id === snapshot.permissionMode)?.label ?? snapshot.permissionMode ?? options.agent.permissionModes[0]?.label ?? "Not set"}${task && task.permissionMode !== snapshot.permissionMode ? `\nTask permissions: ${options.agent.permissionModes.find((mode) => mode.id === task.permissionMode)?.label ?? task.permissionMode ?? options.agent.permissionModes[0]?.label ?? "Not set"}` : ""}\nTask: ${task ? `#${task.id} ${statuses[task.status]}${task.cron ? ` · Cron #${task.cron.id}` : ""}` : "idle"}${task?.interactions.length ? `\nPending: ${task.interactions.map((item) => `${item.kind === "approval" ? "Approval" : "Question"}${item.blocking ? " (waiting)" : " (task running)"}`).join(", ")}` : ""}${upgradeHint}`);
|
|
104
116
|
};
|
|
105
117
|
return;
|
|
106
118
|
}
|
|
@@ -131,14 +143,71 @@ export function createRelayService(options) {
|
|
|
131
143
|
return "Cannot access directory. Check the path and permissions.";
|
|
132
144
|
}
|
|
133
145
|
if (cwd !== channel.cwd)
|
|
134
|
-
options.store.setSession(channel.id, cwd);
|
|
146
|
+
options.store.setSession(channel.id, cwd, null);
|
|
135
147
|
return `Directory: ${cwd}\n${channel.paused ? "Paused. Send /resume to continue." : "Send a task to Codex."}`;
|
|
136
148
|
}
|
|
149
|
+
case "list": {
|
|
150
|
+
if (message.content.args)
|
|
151
|
+
return "Use /list without arguments.";
|
|
152
|
+
const snapshot = structuredClone(channel);
|
|
153
|
+
controlQuery = async () => {
|
|
154
|
+
let sessions;
|
|
155
|
+
try {
|
|
156
|
+
sessions = await options.agent.listSessions(snapshot.cwd, modelLoading.signal);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
if (!stopping)
|
|
160
|
+
options.store.reply(snapshot.id, "Cannot load sessions. Try /list again.");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (!stopping)
|
|
164
|
+
options.store.reply(snapshot.id, `Directory: ${snapshot.cwd}\n${sessions.length
|
|
165
|
+
? sessions
|
|
166
|
+
.map((session) => `\n${session.id === snapshot.sessionId ? "[current] " : ""}${session.label}\n${session.id}`)
|
|
167
|
+
.join("\n")
|
|
168
|
+
: "No sessions yet. Create one with /new [name]."}${sessions.length ? "\n\nUse /switch <full ID> to select a session." : ""}`);
|
|
169
|
+
};
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
137
172
|
case "new":
|
|
173
|
+
case "switch": {
|
|
138
174
|
if (task)
|
|
139
175
|
return "Wait for the task to finish, or use /stop first.";
|
|
140
|
-
|
|
141
|
-
|
|
176
|
+
const { name, args } = message.content;
|
|
177
|
+
if (name === "switch" && (!args || /\s/u.test(args)))
|
|
178
|
+
return "Use /switch <full session ID>. Find the ID with /list.";
|
|
179
|
+
const snapshot = structuredClone(channel);
|
|
180
|
+
changingSession = true;
|
|
181
|
+
controlQuery = async () => {
|
|
182
|
+
try {
|
|
183
|
+
let session;
|
|
184
|
+
try {
|
|
185
|
+
session =
|
|
186
|
+
name === "new"
|
|
187
|
+
? await options.agent.createSession({
|
|
188
|
+
cwd: snapshot.cwd,
|
|
189
|
+
name: args,
|
|
190
|
+
model: snapshot.model,
|
|
191
|
+
permissionMode: snapshot.permissionMode,
|
|
192
|
+
}, modelLoading.signal)
|
|
193
|
+
: await options.agent.readSession(snapshot.cwd, args, modelLoading.signal);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (!stopping)
|
|
197
|
+
options.store.reply(snapshot.id, error instanceof Error && error.message.startsWith("Codex ")
|
|
198
|
+
? error.message
|
|
199
|
+
: "Cannot change sessions. Current session unchanged. Check /list before retrying.");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (!stopping)
|
|
203
|
+
options.store.setSession(snapshot.id, snapshot.cwd, session.id, `${name === "new" ? "New session" : "Switched session"}: ${session.label}\nID: ${session.id}\nDirectory: ${snapshot.cwd}`);
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
changingSession = false;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
142
211
|
case "stop":
|
|
143
212
|
options.store.setPaused(channel.id, true);
|
|
144
213
|
if (!task || task.channelId !== channel.id)
|
|
@@ -153,43 +222,50 @@ export function createRelayService(options) {
|
|
|
153
222
|
case "last": {
|
|
154
223
|
const last = options.store.lastTask(channel.id);
|
|
155
224
|
return last
|
|
156
|
-
? `Task #${last.id} ${statuses[last.status]}\n${last.result ?? "No result yet."}`
|
|
225
|
+
? `Task #${last.id}${last.cron ? ` · Cron #${last.cron.id}` : ""} ${statuses[last.status]}\n${last.result ?? "No result yet."}`
|
|
157
226
|
: "No tasks yet.";
|
|
158
227
|
}
|
|
159
228
|
default:
|
|
160
229
|
return "Unknown command. See /help.";
|
|
161
230
|
}
|
|
162
231
|
}
|
|
163
|
-
if (message.content.type !== "text")
|
|
164
|
-
return "
|
|
232
|
+
if (message.content.type !== "text" && message.content.type !== "attachments")
|
|
233
|
+
return "Send text, a photo, or a file. See /help.";
|
|
165
234
|
if (channel.paused)
|
|
166
235
|
return "Paused. Check /last, then /resume to send a new task.";
|
|
167
|
-
|
|
236
|
+
const attachments = message.content.type === "attachments" ? message.content.attachments : [];
|
|
237
|
+
if (attachments.some((attachment) => attachment.size !== null && attachment.size > maxAttachmentBytes))
|
|
238
|
+
return "Attachment exceeds the 20 MB download limit. Send a smaller file.";
|
|
239
|
+
if (!message.content.text.trim() && !attachments.length)
|
|
168
240
|
return "Send a task description.";
|
|
169
241
|
if (task) {
|
|
170
242
|
if (task.channelId !== channel.id)
|
|
171
243
|
return `Task #${task.id} is running in another chat. Message not sent.`;
|
|
172
244
|
if (task.status === "cancelling")
|
|
173
245
|
return "Task stopping. Wait for its result.";
|
|
174
|
-
options.store.appendTaskInput(task.id, message.content.text);
|
|
246
|
+
options.store.appendTaskInput(task.id, message.content.text, attachments);
|
|
175
247
|
return;
|
|
176
248
|
}
|
|
177
|
-
options.store.createTask(channel, message.content.text);
|
|
249
|
+
options.store.createTask(channel, message.content.text, attachments);
|
|
178
250
|
});
|
|
179
|
-
/**
|
|
180
|
-
if (
|
|
181
|
-
|
|
251
|
+
/** 只有入站去重记录保存成功后才执行;重复事件不会重复创建或切换会话。 */
|
|
252
|
+
if (controlQuery)
|
|
253
|
+
controlQueries.push(controlQuery);
|
|
182
254
|
/** 只有选择和停止状态落盘后才影响运行时;回调答复不承担提交职责。 */
|
|
183
|
-
if ((
|
|
184
|
-
(message.content.type === "interaction" && message.content.action === "stop"))
|
|
185
|
-
options.store.activeTask()
|
|
186
|
-
|
|
255
|
+
if ((message.content.type === "command" && message.content.name === "stop") ||
|
|
256
|
+
(message.content.type === "interaction" && message.content.action === "stop")) {
|
|
257
|
+
const active = options.store.activeTask();
|
|
258
|
+
if (!active || active.status === "cancelling")
|
|
259
|
+
inputPreparation?.abort();
|
|
260
|
+
if (active?.status === "cancelling")
|
|
261
|
+
options.agent.stop();
|
|
262
|
+
}
|
|
187
263
|
return reply;
|
|
188
264
|
},
|
|
189
|
-
/**
|
|
265
|
+
/** 模型、状态和会话请求不占用入站或任务循环,关停后丢弃迟到结果。 */
|
|
190
266
|
async loadModels(source) {
|
|
191
267
|
while (!stopping) {
|
|
192
|
-
const query =
|
|
268
|
+
const query = controlQueries.shift();
|
|
193
269
|
if (query) {
|
|
194
270
|
await query();
|
|
195
271
|
continue;
|
|
@@ -210,6 +286,16 @@ export function createRelayService(options) {
|
|
|
210
286
|
options.store.fillModels(menu.channelId, menu.id, models);
|
|
211
287
|
}
|
|
212
288
|
},
|
|
289
|
+
/** 调度独立于执行和菜单查询;启动先跳过离线时段,再按持久化时间触发。 */
|
|
290
|
+
async schedule(source) {
|
|
291
|
+
if (stopping)
|
|
292
|
+
return;
|
|
293
|
+
options.store.recoverCrons(source);
|
|
294
|
+
while (!stopping) {
|
|
295
|
+
options.store.tickCrons(source);
|
|
296
|
+
await setTimeout(1_000);
|
|
297
|
+
}
|
|
298
|
+
},
|
|
213
299
|
/** 领取任务后再次检查目录;运行结果与最终回复在同一个快照中保存。 */
|
|
214
300
|
async run(source) {
|
|
215
301
|
while (!stopping) {
|
|
@@ -229,11 +315,42 @@ export function createRelayService(options) {
|
|
|
229
315
|
});
|
|
230
316
|
continue;
|
|
231
317
|
}
|
|
318
|
+
const preparing = new AbortController();
|
|
319
|
+
inputPreparation = preparing;
|
|
320
|
+
let attachments = [];
|
|
321
|
+
if (task.status === "preparing") {
|
|
322
|
+
try {
|
|
323
|
+
attachments = await options.prepareAttachments(task.attachments, preparing.signal);
|
|
324
|
+
if (stopping)
|
|
325
|
+
return;
|
|
326
|
+
if (preparing.signal.aborted || !options.store.markDispatching(task.id))
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
if (stopping)
|
|
331
|
+
return;
|
|
332
|
+
options.store.finishTask(task.id, {
|
|
333
|
+
status: preparing.signal.aborted ? "cancelled" : "failed",
|
|
334
|
+
text: preparing.signal.aborted
|
|
335
|
+
? "Stopped before starting."
|
|
336
|
+
: error instanceof AttachmentError
|
|
337
|
+
? error.message
|
|
338
|
+
: "Cannot prepare attachments. Resend them to try again.",
|
|
339
|
+
});
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
if (inputPreparation === preparing)
|
|
344
|
+
inputPreparation = undefined;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
inputPreparation = preparing;
|
|
232
348
|
let finished = false;
|
|
233
349
|
const running = options.agent
|
|
234
350
|
.run({
|
|
235
351
|
cwd: task.cwd,
|
|
236
352
|
prompt: task.prompt,
|
|
353
|
+
attachments,
|
|
237
354
|
sessionId: task.sessionId,
|
|
238
355
|
runId: task.runId,
|
|
239
356
|
permissionMode: task.permissionMode,
|
|
@@ -251,6 +368,7 @@ export function createRelayService(options) {
|
|
|
251
368
|
})
|
|
252
369
|
.finally(() => {
|
|
253
370
|
finished = true;
|
|
371
|
+
preparing.abort();
|
|
254
372
|
});
|
|
255
373
|
/** 补充输入独立于入站处理和任务完成等待,慢 RPC 不阻塞 /status 与 /stop。 */
|
|
256
374
|
const steering = (async () => {
|
|
@@ -262,11 +380,19 @@ export function createRelayService(options) {
|
|
|
262
380
|
}
|
|
263
381
|
let error;
|
|
264
382
|
try {
|
|
265
|
-
await options.
|
|
383
|
+
const attachments = await options.prepareAttachments(input.attachments, preparing.signal);
|
|
384
|
+
if (finished || stopping)
|
|
385
|
+
return;
|
|
386
|
+
if (!options.store.markTaskInputSending(task.id, input.index))
|
|
387
|
+
continue;
|
|
388
|
+
await options.agent.steer(input.text, attachments);
|
|
266
389
|
}
|
|
267
390
|
catch (cause) {
|
|
391
|
+
if (finished || stopping)
|
|
392
|
+
return;
|
|
268
393
|
error =
|
|
269
|
-
cause instanceof
|
|
394
|
+
cause instanceof AttachmentError ||
|
|
395
|
+
(cause instanceof Error && cause.message.startsWith("Codex "))
|
|
270
396
|
? cause.message
|
|
271
397
|
: "Submission unconfirmed. Not retried; check the session before resending.";
|
|
272
398
|
}
|
|
@@ -278,7 +404,7 @@ export function createRelayService(options) {
|
|
|
278
404
|
while (!finished && !stopping) {
|
|
279
405
|
const interaction = options.store.claimInteraction(task.id);
|
|
280
406
|
if (interaction) {
|
|
281
|
-
const submitted = options.agent.respond(interaction.id, interaction.answer);
|
|
407
|
+
const submitted = await options.agent.respond(interaction.id, interaction.answer);
|
|
282
408
|
options.store.closeInteraction(interaction.id, submitted
|
|
283
409
|
? "Submitted. Waiting for results."
|
|
284
410
|
: "Request closed or delivery unconfirmed. Check /status.");
|
|
@@ -300,12 +426,18 @@ export function createRelayService(options) {
|
|
|
300
426
|
});
|
|
301
427
|
throw error;
|
|
302
428
|
}
|
|
429
|
+
finally {
|
|
430
|
+
preparing.abort();
|
|
431
|
+
if (inputPreparation === preparing)
|
|
432
|
+
inputPreparation = undefined;
|
|
433
|
+
}
|
|
303
434
|
}
|
|
304
435
|
},
|
|
305
436
|
/** 服务退出只断开客户端;后台任务继续运行,重启后按已保存轮次接回。 */
|
|
306
437
|
stop() {
|
|
307
438
|
stopping = true;
|
|
308
439
|
modelLoading.abort();
|
|
440
|
+
inputPreparation?.abort();
|
|
309
441
|
options.agent.disconnect();
|
|
310
442
|
},
|
|
311
443
|
};
|