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
package/dist/storage/schema.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
/** 存储操作与状态校验共用的未结束任务状态。 */
|
|
3
3
|
export const activeTaskStatuses = new Set([
|
|
4
4
|
"queued",
|
|
5
|
+
"preparing",
|
|
5
6
|
"dispatching",
|
|
6
7
|
"running",
|
|
7
8
|
"cancelling",
|
|
@@ -10,6 +11,14 @@ export const activeTaskStatuses = new Set([
|
|
|
10
11
|
const id = z.number().int().positive();
|
|
11
12
|
/** 重试次数和时间均为非负整数。 */
|
|
12
13
|
const nonnegativeInteger = z.number().int().nonnegative();
|
|
14
|
+
/** 附件引用不含凭据;没有附件时显式保存空数组。 */
|
|
15
|
+
const attachmentsSchema = z.array(z.strictObject({
|
|
16
|
+
id: z.uuid(),
|
|
17
|
+
fileId: z.string().min(1),
|
|
18
|
+
name: z.string(),
|
|
19
|
+
kind: z.enum(["image", "file"]),
|
|
20
|
+
size: nonnegativeInteger.nullable(),
|
|
21
|
+
}));
|
|
13
22
|
/** 审批、权限、模型和推理档位共用展示选项。 */
|
|
14
23
|
const optionSchema = z.strictObject({ id: z.string().min(1), label: z.string() });
|
|
15
24
|
/** 菜单共用有效期和消息清理记录,模型菜单扩展第二步所需的信息。 */
|
|
@@ -19,7 +28,7 @@ const menuSchema = z.strictObject({
|
|
|
19
28
|
options: z.array(optionSchema),
|
|
20
29
|
messages: z.array(z.strictObject({ messageId: id, text: z.string() })),
|
|
21
30
|
});
|
|
22
|
-
/**
|
|
31
|
+
/** 仅接受当前状态结构;字段缺失或格式无效时拒绝启动,不迁移或清空文件。 */
|
|
23
32
|
export const stateSchema = z
|
|
24
33
|
.strictObject({
|
|
25
34
|
channels: z.array(z.strictObject({
|
|
@@ -28,35 +37,24 @@ export const stateSchema = z
|
|
|
28
37
|
accountId: z.string(),
|
|
29
38
|
chatId: z.string(),
|
|
30
39
|
userId: z.string(),
|
|
31
|
-
cwd: z.string().
|
|
40
|
+
cwd: z.string().min(1),
|
|
32
41
|
sessionId: z.string().nullable(),
|
|
33
42
|
paused: z.boolean(),
|
|
34
|
-
/** 模式标识属于 Agent
|
|
35
|
-
permissionMode: z.string().min(1).nullable()
|
|
36
|
-
permissionMenu: menuSchema.nullable()
|
|
37
|
-
/**
|
|
38
|
-
model: z.string().min(1).nullable()
|
|
39
|
-
reasoningEffort: z.string().min(1).nullable()
|
|
40
|
-
modelMenu: z
|
|
41
|
-
/** 旧菜单没有阶段标记;读取时补齐,原选择与消息清理记录保持不变。 */
|
|
42
|
-
(menu) => menu && typeof menu === "object" && !("stage" in menu)
|
|
43
|
-
? {
|
|
44
|
-
...menu,
|
|
45
|
-
stage: "model" in menu
|
|
46
|
-
? "reasoning"
|
|
47
|
-
: "options" in menu && Array.isArray(menu.options) && menu.options.length
|
|
48
|
-
? "model"
|
|
49
|
-
: "loading",
|
|
50
|
-
}
|
|
51
|
-
: menu, z
|
|
43
|
+
/** 模式标识属于 Agent;空值沿用默认模式。 */
|
|
44
|
+
permissionMode: z.string().min(1).nullable(),
|
|
45
|
+
permissionMenu: menuSchema.nullable(),
|
|
46
|
+
/** 显式空值表示沿用 Codex 的模型配置。 */
|
|
47
|
+
model: z.string().min(1).nullable(),
|
|
48
|
+
reasoningEffort: z.string().min(1).nullable(),
|
|
49
|
+
modelMenu: z
|
|
52
50
|
.discriminatedUnion("stage", [
|
|
53
51
|
menuSchema.extend({ stage: z.literal("loading"), options: z.tuple([]) }),
|
|
54
52
|
menuSchema.extend({
|
|
55
53
|
stage: z.literal("model"),
|
|
56
54
|
options: z
|
|
57
55
|
.array(optionSchema.extend({
|
|
58
|
-
reasoningEfforts: z.array(optionSchema)
|
|
59
|
-
defaultReasoningEffort: z.string()
|
|
56
|
+
reasoningEfforts: z.array(optionSchema),
|
|
57
|
+
defaultReasoningEffort: z.string(),
|
|
60
58
|
}))
|
|
61
59
|
.min(1),
|
|
62
60
|
}),
|
|
@@ -67,33 +65,54 @@ export const stateSchema = z
|
|
|
67
65
|
options: z.array(optionSchema).min(1),
|
|
68
66
|
}),
|
|
69
67
|
])
|
|
70
|
-
.nullable()
|
|
71
|
-
.default(null)),
|
|
68
|
+
.nullable(),
|
|
72
69
|
})),
|
|
73
70
|
inboundUpdates: z.array(z.strictObject({
|
|
74
71
|
channelId: id,
|
|
75
72
|
eventId: z.string(),
|
|
76
73
|
})),
|
|
74
|
+
/** 定时任务 ID 持续递增,删除后不复用;旧状态默认没有定时任务。 */
|
|
75
|
+
nextCronId: id.default(1),
|
|
76
|
+
crons: z
|
|
77
|
+
.array(z.strictObject({
|
|
78
|
+
id,
|
|
79
|
+
channelId: id,
|
|
80
|
+
expression: z.string().min(1),
|
|
81
|
+
timezone: z.string().min(1),
|
|
82
|
+
prompt: z.string().min(1),
|
|
83
|
+
cwd: z.string().min(1),
|
|
84
|
+
model: z.string().min(1).nullable(),
|
|
85
|
+
reasoningEffort: z.string().min(1).nullable(),
|
|
86
|
+
permissionMode: z.string().min(1).nullable(),
|
|
87
|
+
enabled: z.boolean(),
|
|
88
|
+
nextRunAt: nonnegativeInteger.nullable(),
|
|
89
|
+
/** 忙碌时同一计划最多保留一次待执行,不积累每次触发记录。 */
|
|
90
|
+
pendingAt: nonnegativeInteger.nullable(),
|
|
91
|
+
}))
|
|
92
|
+
.default([]),
|
|
77
93
|
tasks: z.array(z.strictObject({
|
|
78
94
|
id,
|
|
79
95
|
channelId: id,
|
|
96
|
+
/** 执行快照独立于计划,删除计划后仍能跟踪已开始的任务。 */
|
|
97
|
+
cron: z.strictObject({ id, scheduledAt: nonnegativeInteger }).nullable().default(null),
|
|
80
98
|
cwd: z.string(),
|
|
81
99
|
sessionId: z.string().nullable(),
|
|
82
|
-
/**
|
|
83
|
-
runId: z.string().min(1).nullable()
|
|
100
|
+
/** 派发前显式保存空值,取得轮次 ID 后用于重启恢复。 */
|
|
101
|
+
runId: z.string().min(1).nullable(),
|
|
84
102
|
prompt: z.string(),
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
attachments: attachmentsSchema,
|
|
104
|
+
permissionMode: z.string().min(1).nullable(),
|
|
105
|
+
model: z.string().min(1).nullable(),
|
|
106
|
+
reasoningEffort: z.string().min(1).nullable(),
|
|
107
|
+
/** 发送中记录用于重启后避免不确定重放;没有补充输入时保存空数组。 */
|
|
108
|
+
inputs: z.array(z.strictObject({
|
|
91
109
|
text: z.string(),
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
110
|
+
attachments: attachmentsSchema,
|
|
111
|
+
status: z.enum(["pending", "preparing", "sending", "accepted", "failed"]),
|
|
112
|
+
})),
|
|
95
113
|
status: z.enum([
|
|
96
114
|
"queued",
|
|
115
|
+
"preparing",
|
|
97
116
|
"dispatching",
|
|
98
117
|
"running",
|
|
99
118
|
"cancelling",
|
|
@@ -104,24 +123,16 @@ export const stateSchema = z
|
|
|
104
123
|
]),
|
|
105
124
|
result: z.string().nullable(),
|
|
106
125
|
})),
|
|
107
|
-
/**
|
|
108
|
-
interactions: z
|
|
109
|
-
.array(z.strictObject({
|
|
126
|
+
/** 请求编号和等待对象只保存在 Agent 内存中。 */
|
|
127
|
+
interactions: z.array(z.strictObject({
|
|
110
128
|
id: z.uuid(),
|
|
111
129
|
taskId: id,
|
|
112
130
|
kind: z.enum(["approval", "question"]),
|
|
113
131
|
text: z.string(),
|
|
114
|
-
/**
|
|
132
|
+
/** 审批没有本地期限,问题保存到期时间。 */
|
|
115
133
|
expiresAt: nonnegativeInteger.nullable(),
|
|
116
134
|
blocking: z.boolean(),
|
|
117
|
-
|
|
118
|
-
choices: z.array(z.union([
|
|
119
|
-
optionSchema,
|
|
120
|
-
z.enum(["accept", "decline"]).transform((id) => ({
|
|
121
|
-
id,
|
|
122
|
-
label: id === "accept" ? "Allow once" : "Decline",
|
|
123
|
-
})),
|
|
124
|
-
])),
|
|
135
|
+
choices: z.array(optionSchema),
|
|
125
136
|
questions: z.array(z.strictObject({
|
|
126
137
|
id: z.string(),
|
|
127
138
|
header: z.string(),
|
|
@@ -138,8 +149,7 @@ export const stateSchema = z
|
|
|
138
149
|
questionIndex: nonnegativeInteger.optional(),
|
|
139
150
|
text: z.string(),
|
|
140
151
|
})),
|
|
141
|
-
}))
|
|
142
|
-
.default([]),
|
|
152
|
+
})),
|
|
143
153
|
outbox: z.array(z.strictObject({
|
|
144
154
|
id,
|
|
145
155
|
channelId: id,
|
|
@@ -157,12 +167,16 @@ export const stateSchema = z
|
|
|
157
167
|
})
|
|
158
168
|
.refine((state) => {
|
|
159
169
|
const channels = new Set(state.channels.map((row) => row.id));
|
|
160
|
-
return ([state.channels, state.tasks, state.outbox].every((rows) => new Set(rows.map((row) => row.id)).size === rows.length) &&
|
|
170
|
+
return ([state.channels, state.tasks, state.outbox, state.crons].every((rows) => new Set(rows.map((row) => row.id)).size === rows.length) &&
|
|
161
171
|
new Set(state.channels.map((row) => JSON.stringify([row.platform, row.accountId, row.chatId]))).size === state.channels.length &&
|
|
162
172
|
new Set(state.inboundUpdates.map((row) => JSON.stringify([row.channelId, row.eventId])))
|
|
163
173
|
.size === state.inboundUpdates.length &&
|
|
164
174
|
state.inboundUpdates.every((row) => channels.has(row.channelId)) &&
|
|
165
175
|
state.tasks.every((row) => channels.has(row.channelId)) &&
|
|
176
|
+
state.crons.every((row) => channels.has(row.channelId) &&
|
|
177
|
+
row.id < state.nextCronId &&
|
|
178
|
+
(row.enabled ? row.nextRunAt !== null : row.nextRunAt === null && row.pendingAt === null)) &&
|
|
179
|
+
state.tasks.every((row) => !row.cron || row.cron.id < state.nextCronId) &&
|
|
166
180
|
state.outbox.every((row) => channels.has(row.channelId)) &&
|
|
167
181
|
new Set(state.interactions.map((row) => row.id)).size === state.interactions.length &&
|
|
168
182
|
state.interactions.every((row) => state.tasks.some((task) => task.id === row.taskId) &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/storage/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,2BAA2B;AAC3B,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAS;IAChD,QAAQ;IACR,aAAa;IACb,SAAS;IACT,YAAY;CACb,CAAC,CAAC;AAEH,mBAAmB;AACnB,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;AACvC,qBAAqB;AACrB,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;AAE1D,2BAA2B;AAC3B,MAAM,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAElF,qCAAqC;AACrC,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC;IAChC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;IACZ,SAAS,EAAE,kBAAkB;IAC7B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;CACvE,CAAC,CAAC;AAEH,
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/storage/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/store.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { chmodSync, mkdirSync, readFileSync } from "node:fs";
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { appDirectory } from "../config.js";
|
|
4
|
+
import { createCrons } from "../core/cron.js";
|
|
4
5
|
import { createMenus } from "../core/menus.js";
|
|
5
6
|
import { writeJson } from "./json.js";
|
|
6
7
|
import { acquireLock } from "./lock.js";
|
|
@@ -32,7 +33,15 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
32
33
|
catch (error) {
|
|
33
34
|
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
34
35
|
throw error;
|
|
35
|
-
state = {
|
|
36
|
+
state = {
|
|
37
|
+
channels: [],
|
|
38
|
+
inboundUpdates: [],
|
|
39
|
+
nextCronId: 1,
|
|
40
|
+
crons: [],
|
|
41
|
+
tasks: [],
|
|
42
|
+
interactions: [],
|
|
43
|
+
outbox: [],
|
|
44
|
+
};
|
|
36
45
|
writeJson(path, state);
|
|
37
46
|
}
|
|
38
47
|
/** 交互撤回可能清空发送队列;实例内保持递增,避免迟到发送回执删除新消息。 */
|
|
@@ -96,6 +105,45 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
96
105
|
});
|
|
97
106
|
}
|
|
98
107
|
}
|
|
108
|
+
/** 手动和定时输入共用任务快照,始终只允许一个未结束的编码任务。 */
|
|
109
|
+
function createTask(channel, prompt, attachments = [], cron = null) {
|
|
110
|
+
return update(() => {
|
|
111
|
+
if (state.tasks.some((task) => activeTaskStatuses.has(task.status)))
|
|
112
|
+
throw new Error("A task is already running.");
|
|
113
|
+
const task = {
|
|
114
|
+
id: state.tasks.reduce((max, item) => Math.max(max, item.id), 0) + 1,
|
|
115
|
+
channelId: channel.id,
|
|
116
|
+
cron,
|
|
117
|
+
cwd: channel.cwd,
|
|
118
|
+
sessionId: channel.sessionId,
|
|
119
|
+
runId: null,
|
|
120
|
+
prompt,
|
|
121
|
+
attachments,
|
|
122
|
+
permissionMode: channel.permissionMode,
|
|
123
|
+
model: channel.model,
|
|
124
|
+
reasoningEffort: channel.reasoningEffort,
|
|
125
|
+
inputs: [],
|
|
126
|
+
status: "queued",
|
|
127
|
+
result: null,
|
|
128
|
+
};
|
|
129
|
+
state.tasks.push(task);
|
|
130
|
+
return task;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/** 定时执行固定创建时的选择,每次新建会话,不改变聊天当前会话。 */
|
|
134
|
+
const crons = createCrons({
|
|
135
|
+
read: () => state,
|
|
136
|
+
update,
|
|
137
|
+
enqueueReply,
|
|
138
|
+
createTask: (channel, cron, scheduledAt) => createTask({
|
|
139
|
+
...channel,
|
|
140
|
+
cwd: cron.cwd,
|
|
141
|
+
sessionId: null,
|
|
142
|
+
model: cron.model,
|
|
143
|
+
reasoningEffort: cron.reasoningEffort,
|
|
144
|
+
permissionMode: cron.permissionMode,
|
|
145
|
+
}, cron.prompt, [], { id: cron.id, scheduledAt }),
|
|
146
|
+
});
|
|
99
147
|
/** 菜单始终读取当前事务快照,避免捕获 update 替换前的状态。 */
|
|
100
148
|
const { closeMenu, ...menus } = createMenus({ read: () => state, update, enqueueReply });
|
|
101
149
|
/** 关闭请求或单个已答问题;撤回未发送提示,已发送消息保留正文并移除按钮。 */
|
|
@@ -127,7 +175,7 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
127
175
|
}
|
|
128
176
|
});
|
|
129
177
|
}
|
|
130
|
-
/**
|
|
178
|
+
/** 终态与最终回复一起保存;定时结果标注来源,迟到事件不能覆盖已完成结果。 */
|
|
131
179
|
function finishTask(id, result) {
|
|
132
180
|
if (!state.tasks.some((task) => task.id === id && activeTaskStatuses.has(task.status)))
|
|
133
181
|
return;
|
|
@@ -140,20 +188,22 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
140
188
|
closeInteraction(interaction.id, "Task ended. Request closed.");
|
|
141
189
|
if (result.status === "interrupted") {
|
|
142
190
|
const channel = state.channels.find((channel) => channel.id === task.channelId);
|
|
143
|
-
if (channel)
|
|
191
|
+
if (channel) {
|
|
144
192
|
channel.paused = true;
|
|
193
|
+
crons.resetCronPending(channel.id);
|
|
194
|
+
}
|
|
145
195
|
}
|
|
146
|
-
enqueueReply(task.channelId, result.status === "completed"
|
|
196
|
+
enqueueReply(task.channelId, `${task.cron ? `Cron #${task.cron.id}\n` : ""}${result.status === "completed"
|
|
147
197
|
? result.text
|
|
148
198
|
: `Task #${id} ${{
|
|
149
199
|
failed: "Failed",
|
|
150
200
|
cancelled: "Stopped",
|
|
151
201
|
interrupted: "Unconfirmed",
|
|
152
|
-
}[result.status]}\n${result.text}`);
|
|
202
|
+
}[result.status]}\n${result.text}`}`);
|
|
153
203
|
for (const [index, input] of task.inputs.entries()) {
|
|
154
|
-
if (
|
|
204
|
+
if (!["pending", "preparing", "sending"].includes(input.status))
|
|
155
205
|
continue;
|
|
156
|
-
enqueueReply(task.channelId, `Task #${id}, message ${index + 1}: ${input.status === "
|
|
206
|
+
enqueueReply(task.channelId, `Task #${id}, message ${index + 1}: ${input.status === "sending" ? "delivery unconfirmed" : "not sent"}. Task ended; not retried. Check the session before resending.`);
|
|
157
207
|
input.status = "failed";
|
|
158
208
|
}
|
|
159
209
|
});
|
|
@@ -162,7 +212,7 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
162
212
|
}
|
|
163
213
|
/** 命令与交互按钮共用停止逻辑;先使交互失效,再由核心层请求中断。 */
|
|
164
214
|
function cancelTask(id) {
|
|
165
|
-
if (state.tasks.find((task) => task.id === id)?.status
|
|
215
|
+
if (["queued", "preparing"].includes(state.tasks.find((task) => task.id === id)?.status ?? "")) {
|
|
166
216
|
finishTask(id, { status: "cancelled", text: "Cancelled before starting." });
|
|
167
217
|
}
|
|
168
218
|
else
|
|
@@ -191,7 +241,8 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
191
241
|
channel = {
|
|
192
242
|
...message.channel,
|
|
193
243
|
id: state.channels.reduce((max, item) => Math.max(max, item.id), 0) + 1,
|
|
194
|
-
|
|
244
|
+
/** 创建聊天时固定服务启动目录,后续命令直接使用已保存的路径。 */
|
|
245
|
+
cwd: realpathSync(process.cwd()),
|
|
195
246
|
sessionId: null,
|
|
196
247
|
paused: false,
|
|
197
248
|
permissionMode: null,
|
|
@@ -234,7 +285,7 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
234
285
|
});
|
|
235
286
|
},
|
|
236
287
|
/** 新会话或目录变化只影响后续任务,已保存任务仍使用自己的快照。 */
|
|
237
|
-
setSession(channelId, cwd) {
|
|
288
|
+
setSession(channelId, cwd, sessionId, reply) {
|
|
238
289
|
update(() => {
|
|
239
290
|
const channel = state.channels.find((channel) => channel.id === channelId);
|
|
240
291
|
if (channel) {
|
|
@@ -245,17 +296,25 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
245
296
|
channel.model = null;
|
|
246
297
|
channel.reasoningEffort = null;
|
|
247
298
|
}
|
|
248
|
-
Object.assign(channel, { cwd, sessionId
|
|
299
|
+
Object.assign(channel, { cwd, sessionId });
|
|
300
|
+
/** 会话选择与成功回复一同落盘;暂停状态以接口返回时的值为准。 */
|
|
301
|
+
if (reply)
|
|
302
|
+
enqueueReply(channelId, `${reply}${channel.paused ? "\nPaused. Send /resume to continue." : ""}`);
|
|
249
303
|
}
|
|
250
304
|
});
|
|
251
305
|
},
|
|
252
306
|
...menus,
|
|
307
|
+
...crons,
|
|
253
308
|
/** 暂停状态跨重启保留,恢复须由用户明确触发。 */
|
|
254
309
|
setPaused(channelId, paused) {
|
|
255
310
|
update(() => {
|
|
256
311
|
const channel = state.channels.find((channel) => channel.id === channelId);
|
|
257
|
-
if (channel)
|
|
312
|
+
if (channel) {
|
|
313
|
+
const wasPaused = channel.paused;
|
|
258
314
|
channel.paused = paused;
|
|
315
|
+
if (paused || wasPaused)
|
|
316
|
+
crons.resetCronPending(channelId);
|
|
317
|
+
}
|
|
259
318
|
});
|
|
260
319
|
},
|
|
261
320
|
/** 返回快照,避免调用方直接修改内存中的持久状态。 */
|
|
@@ -290,44 +349,22 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
290
349
|
lastTask(channelId) {
|
|
291
350
|
return structuredClone(state.tasks.findLast((task) => task.channelId === channelId && !activeTaskStatuses.has(task.status)) ?? state.tasks.findLast((task) => task.channelId === channelId));
|
|
292
351
|
},
|
|
293
|
-
|
|
294
|
-
createTask(channel, prompt) {
|
|
295
|
-
return update(() => {
|
|
296
|
-
if (!channel.cwd)
|
|
297
|
-
throw new Error("Set a working directory first.");
|
|
298
|
-
if (state.tasks.some((task) => activeTaskStatuses.has(task.status)))
|
|
299
|
-
throw new Error("A task is already running.");
|
|
300
|
-
const task = {
|
|
301
|
-
id: state.tasks.reduce((max, item) => Math.max(max, item.id), 0) + 1,
|
|
302
|
-
channelId: channel.id,
|
|
303
|
-
cwd: channel.cwd,
|
|
304
|
-
sessionId: channel.sessionId,
|
|
305
|
-
runId: null,
|
|
306
|
-
prompt,
|
|
307
|
-
permissionMode: channel.permissionMode,
|
|
308
|
-
model: channel.model,
|
|
309
|
-
reasoningEffort: channel.reasoningEffort,
|
|
310
|
-
inputs: [],
|
|
311
|
-
status: "queued",
|
|
312
|
-
result: null,
|
|
313
|
-
};
|
|
314
|
-
state.tasks.push(task);
|
|
315
|
-
return task;
|
|
316
|
-
});
|
|
317
|
-
},
|
|
352
|
+
createTask,
|
|
318
353
|
/** 尚未派发时合并首条输入;执行中的补充输入与入站去重标识一起保存。 */
|
|
319
|
-
appendTaskInput(id, text) {
|
|
354
|
+
appendTaskInput(id, text, attachments = []) {
|
|
320
355
|
update(() => {
|
|
321
356
|
const task = state.tasks.find((task) => task.id === id);
|
|
322
|
-
if (!task || !["queued", "dispatching", "running"].includes(task.status))
|
|
357
|
+
if (!task || !["queued", "preparing", "dispatching", "running"].includes(task.status))
|
|
323
358
|
throw new Error("Task cannot accept more input.");
|
|
324
|
-
if (task.status === "queued")
|
|
359
|
+
if (task.status === "queued") {
|
|
325
360
|
task.prompt += `\n\n${text}`;
|
|
361
|
+
task.attachments.push(...attachments);
|
|
362
|
+
}
|
|
326
363
|
else
|
|
327
|
-
task.inputs.push({ text, status: "pending" });
|
|
364
|
+
task.inputs.push({ text, attachments, status: "pending" });
|
|
328
365
|
});
|
|
329
366
|
},
|
|
330
|
-
/**
|
|
367
|
+
/** 按接收顺序准备附件;下载阶段尚未提交给 Agent,可以在重启后继续。 */
|
|
331
368
|
claimTaskInput(id) {
|
|
332
369
|
const task = state.tasks.find((task) => task.id === id && task.status === "running");
|
|
333
370
|
const index = task?.inputs.findIndex((input) => input.status === "pending") ?? -1;
|
|
@@ -337,8 +374,21 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
337
374
|
const input = state.tasks.find((task) => task.id === id)?.inputs[index];
|
|
338
375
|
if (!input)
|
|
339
376
|
throw new Error("Input not found.");
|
|
377
|
+
input.status = "preparing";
|
|
378
|
+
return { index, text: input.text, attachments: input.attachments };
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
/** 附件准备完成后再次核实任务仍在执行,先保存 sending 再提交,避免误入其他轮次。 */
|
|
382
|
+
markTaskInputSending(id, index) {
|
|
383
|
+
const task = state.tasks.find((task) => task.id === id && task.status === "running");
|
|
384
|
+
if (task?.inputs[index]?.status !== "preparing")
|
|
385
|
+
return false;
|
|
386
|
+
return update(() => {
|
|
387
|
+
const input = state.tasks.find((task) => task.id === id)?.inputs[index];
|
|
388
|
+
if (!input)
|
|
389
|
+
return false;
|
|
340
390
|
input.status = "sending";
|
|
341
|
-
return
|
|
391
|
+
return true;
|
|
342
392
|
});
|
|
343
393
|
},
|
|
344
394
|
/** 成功补充不产生固定回执,失败提示和输入状态原子保存。 */
|
|
@@ -346,7 +396,7 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
346
396
|
update(() => {
|
|
347
397
|
const task = state.tasks.find((task) => task.id === id);
|
|
348
398
|
const input = task?.inputs[index];
|
|
349
|
-
if (!task || input
|
|
399
|
+
if (!task || !input || !["preparing", "sending"].includes(input.status))
|
|
350
400
|
return;
|
|
351
401
|
input.status = error === undefined ? "accepted" : "failed";
|
|
352
402
|
if (error !== undefined)
|
|
@@ -368,13 +418,29 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
368
418
|
return update(() => {
|
|
369
419
|
const claimed = {
|
|
370
420
|
...task,
|
|
371
|
-
status: task.status === "cancelling"
|
|
421
|
+
status: task.status === "cancelling"
|
|
422
|
+
? "cancelling"
|
|
423
|
+
: !task.runId && task.attachments.length
|
|
424
|
+
? "preparing"
|
|
425
|
+
: "dispatching",
|
|
372
426
|
};
|
|
373
427
|
state.tasks[index] = claimed;
|
|
374
428
|
return claimed;
|
|
375
429
|
});
|
|
376
430
|
},
|
|
377
|
-
/**
|
|
431
|
+
/** 下载完成后才进入可能派发的阶段;已停止的任务不能因下载迟到而启动。 */
|
|
432
|
+
markDispatching(id) {
|
|
433
|
+
if (!state.tasks.some((task) => task.id === id && task.status === "preparing"))
|
|
434
|
+
return false;
|
|
435
|
+
return update(() => {
|
|
436
|
+
const task = state.tasks.find((task) => task.id === id);
|
|
437
|
+
if (!task)
|
|
438
|
+
return false;
|
|
439
|
+
task.status = "dispatching";
|
|
440
|
+
return true;
|
|
441
|
+
});
|
|
442
|
+
},
|
|
443
|
+
/** 新建会话后立即保存关联;定时任务只更新执行快照,保留手动聊天的上下文。 */
|
|
378
444
|
recordSession(id, sessionId) {
|
|
379
445
|
update(() => {
|
|
380
446
|
const task = state.tasks.find((task) => task.id === id && activeTaskStatuses.has(task.status));
|
|
@@ -382,7 +448,7 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
382
448
|
return;
|
|
383
449
|
task.sessionId = sessionId;
|
|
384
450
|
const channel = state.channels.find((channel) => channel.id === task.channelId);
|
|
385
|
-
if (channel)
|
|
451
|
+
if (channel && !task.cron)
|
|
386
452
|
channel.sessionId = sessionId;
|
|
387
453
|
});
|
|
388
454
|
},
|
|
@@ -445,14 +511,16 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
445
511
|
interaction.status !== "pending" ||
|
|
446
512
|
(interaction.expiresAt !== null && interaction.expiresAt <= Date.now()))
|
|
447
513
|
return "Request closed. Check /status.";
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
514
|
+
/** 只有问题卡片提供停止操作,审批卡片仅接受审批选项。 */
|
|
515
|
+
if (interaction.kind === "question" &&
|
|
516
|
+
content.type === "interaction" &&
|
|
517
|
+
content.action === "stop") {
|
|
452
518
|
update(() => {
|
|
453
519
|
const channel = state.channels.find((channel) => channel.id === channelId);
|
|
454
|
-
if (channel)
|
|
520
|
+
if (channel) {
|
|
455
521
|
channel.paused = true;
|
|
522
|
+
crons.resetCronPending(channel.id);
|
|
523
|
+
}
|
|
456
524
|
cancelTask(task.id);
|
|
457
525
|
});
|
|
458
526
|
return "Stopping. Wait for the result.";
|
|
@@ -541,6 +609,21 @@ export function openStore(path = join(appDirectory, "state.json")) {
|
|
|
541
609
|
}
|
|
542
610
|
});
|
|
543
611
|
for (const task of state.tasks.filter((task) => activeTaskStatuses.has(task.status) && task.status !== "queued")) {
|
|
612
|
+
/** 下载阶段从未提交给 Codex;主输入和补充输入都可以安全重新准备。 */
|
|
613
|
+
if (task.status === "preparing" ||
|
|
614
|
+
task.inputs.some((input) => input.status === "preparing"))
|
|
615
|
+
update(() => {
|
|
616
|
+
const current = state.tasks.find((item) => item.id === task.id);
|
|
617
|
+
if (!current)
|
|
618
|
+
return;
|
|
619
|
+
if (current.status === "preparing")
|
|
620
|
+
current.status = "queued";
|
|
621
|
+
for (const input of current.inputs)
|
|
622
|
+
if (input.status === "preparing")
|
|
623
|
+
input.status = "pending";
|
|
624
|
+
});
|
|
625
|
+
if (task.status === "preparing")
|
|
626
|
+
continue;
|
|
544
627
|
if (task.sessionId && task.runId) {
|
|
545
628
|
for (const [index, input] of task.inputs.entries()) {
|
|
546
629
|
if (input.status !== "sending")
|