@foxden-app/foxclaw 0.5.8 → 0.5.10
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/CHANGELOG.md +26 -0
- package/dist/controller/controller.d.ts +14 -2
- package/dist/controller/controller.js +462 -47
- package/dist/i18n.d.ts +26 -2
- package/dist/i18n.js +26 -2
- package/dist/main.js +75 -0
- package/dist/store/database.d.ts +25 -1
- package/dist/store/database.js +321 -0
- package/dist/telegram/gateway.d.ts +1 -0
- package/dist/telegram/gateway.js +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/update.d.ts +3 -0
- package/dist/update.js +72 -3
- package/docs/devlogs//346/212/200/346/234/257/351/200/232/350/256/257/01./346/212/212 Codex /345/270/246/345/210/260/346/234/200/345/220/216/344/270/200/345/205/254/351/207/214/357/274/232FoxClaw /347/232/204 auth /347/224/237/345/221/275/345/221/250/346/234/237/344/270/216/350/267/250/350/212/202/347/202/271/345/220/214/346/255/245.md" +341 -0
- package/docs/install-for-beginners.md +3 -3
- package/docs/troubleshooting.md +2 -2
- package/docs/zh/install-for-beginners.md +3 -3
- package/docs/zh/troubleshooting.md +2 -2
- package/package.json +1 -1
package/dist/i18n.d.ts
CHANGED
|
@@ -103,6 +103,7 @@ declare const MESSAGES: {
|
|
|
103
103
|
readonly status_sync_on_turn_complete: "Sync on turn complete: {value}";
|
|
104
104
|
readonly status_pending_approvals: "Pending approvals: {value}";
|
|
105
105
|
readonly status_pending_user_inputs: "Pending questions: {value}";
|
|
106
|
+
readonly status_queued_turns: "Queued turns: {value}";
|
|
106
107
|
readonly status_active_turns: "Active turns: {value}";
|
|
107
108
|
readonly status_codex_account: "Codex account: {value}";
|
|
108
109
|
readonly status_codex_plan: "Codex plan: {value}";
|
|
@@ -133,6 +134,7 @@ declare const MESSAGES: {
|
|
|
133
134
|
readonly status_last_codex_update: "Last Codex update: {value}";
|
|
134
135
|
readonly update_started: "FoxClaw update started. I will report here after installation, checks, and service restart complete.";
|
|
135
136
|
readonly update_succeeded: "FoxClaw updated and restarted: {from} -> {to}.";
|
|
137
|
+
readonly update_changes_title: "What changed:";
|
|
136
138
|
readonly update_codex_result: "Codex CLI: {from} -> {to}.";
|
|
137
139
|
readonly update_failed: "FoxClaw update failed: {error}\nRun foxclaw update in a terminal for details.";
|
|
138
140
|
readonly update_unavailable: "Self-update is unavailable in this runtime. Run foxclaw update in a terminal.";
|
|
@@ -254,8 +256,18 @@ declare const MESSAGES: {
|
|
|
254
256
|
readonly watch_read_only_active: "This chat is watching that turn read-only. Use approval commands if needed, or /unwatch before sending a new prompt.";
|
|
255
257
|
readonly watch_not_enabled: "This chat is not watching any thread.";
|
|
256
258
|
readonly watch_stopped: "Stopped watching thread {threadId}.";
|
|
257
|
-
readonly queued_prompt_set: "Queued. I will send it after the current turn finishes.";
|
|
259
|
+
readonly queued_prompt_set: "Queued #{position} ({id}). I will send it after the current turn finishes.";
|
|
258
260
|
readonly queued_prompt_replaced: "Replaced the queued prompt. I will send the new one after the current turn finishes.";
|
|
261
|
+
readonly attachment_batch_staged: "Attachments staged: {count}. Send the prompt text to use them, or choose an action below.";
|
|
262
|
+
readonly attachment_batch_caption: "Caption: {value}";
|
|
263
|
+
readonly attachment_batch_consumed: "Attachments attached to the next Codex turn.";
|
|
264
|
+
readonly attachment_batch_queued: "Attachments queued as turn #{position} ({id}). I will send it after the current turn finishes.";
|
|
265
|
+
readonly attachment_batch_cleared: "Attachment batch cleared.";
|
|
266
|
+
readonly attachment_batch_missing: "This attachment batch is no longer active.";
|
|
267
|
+
readonly attachment_batch_mismatch: "This attachment batch does not match this message.";
|
|
268
|
+
readonly attachment_batch_default_prompt: "Analyze these attachments.";
|
|
269
|
+
readonly attachment_batch_button_analyze: "Analyze";
|
|
270
|
+
readonly attachment_batch_button_clear: "Clear";
|
|
259
271
|
readonly bound_to_thread: "Bound to thread {threadId}";
|
|
260
272
|
readonly line_title: "Title: {value}";
|
|
261
273
|
readonly line_cwd: "CWD: {value}";
|
|
@@ -744,6 +756,7 @@ declare const MESSAGES: {
|
|
|
744
756
|
readonly status_sync_on_turn_complete: "回复完成后同步:{value}";
|
|
745
757
|
readonly status_pending_approvals: "待审批:{value}";
|
|
746
758
|
readonly status_pending_user_inputs: "待确认问题:{value}";
|
|
759
|
+
readonly status_queued_turns: "排队中的回复:{value}";
|
|
747
760
|
readonly status_active_turns: "进行中的回复:{value}";
|
|
748
761
|
readonly status_codex_account: "Codex 账号:{value}";
|
|
749
762
|
readonly status_codex_plan: "Codex 套餐:{value}";
|
|
@@ -774,6 +787,7 @@ declare const MESSAGES: {
|
|
|
774
787
|
readonly status_last_codex_update: "最近 Codex 升级:{value}";
|
|
775
788
|
readonly update_started: "已开始升级 FoxClaw。安装、自检和服务重启完成后,我会在这里回报结果。";
|
|
776
789
|
readonly update_succeeded: "FoxClaw 已升级并重启:{from} -> {to}。";
|
|
790
|
+
readonly update_changes_title: "更新内容:";
|
|
777
791
|
readonly update_codex_result: "Codex CLI:{from} -> {to}。";
|
|
778
792
|
readonly update_failed: "FoxClaw 升级失败:{error}\n请在终端运行 foxclaw update 查看详情。";
|
|
779
793
|
readonly update_unavailable: "当前运行方式不支持自升级,请在终端运行 foxclaw update。";
|
|
@@ -895,8 +909,18 @@ declare const MESSAGES: {
|
|
|
895
909
|
readonly watch_read_only_active: "当前聊天正在只读观察这轮。需要审批时可用审批命令;要发送新 prompt 请先 /unwatch。";
|
|
896
910
|
readonly watch_not_enabled: "当前没有正在观察的线程。";
|
|
897
911
|
readonly watch_stopped: "已停止观察线程 {threadId}。";
|
|
898
|
-
readonly queued_prompt_set: "
|
|
912
|
+
readonly queued_prompt_set: "已排队 #{position}({id})。当前回复结束后我会自动发送。";
|
|
899
913
|
readonly queued_prompt_replaced: "已替换排队消息。当前回复结束后我会自动发送新的内容。";
|
|
914
|
+
readonly attachment_batch_staged: "附件已暂存:{count} 个。发送下一条文字即可带上这些附件,或使用下面的按钮。";
|
|
915
|
+
readonly attachment_batch_caption: "说明:{value}";
|
|
916
|
+
readonly attachment_batch_consumed: "附件已带入下一轮 Codex。";
|
|
917
|
+
readonly attachment_batch_queued: "附件已作为第 #{position} 个回复排队({id})。当前回复结束后我会自动发送。";
|
|
918
|
+
readonly attachment_batch_cleared: "已清除这组附件。";
|
|
919
|
+
readonly attachment_batch_missing: "这组附件已经不在待处理状态。";
|
|
920
|
+
readonly attachment_batch_mismatch: "这个附件按钮不属于当前消息。";
|
|
921
|
+
readonly attachment_batch_default_prompt: "分析这些附件。";
|
|
922
|
+
readonly attachment_batch_button_analyze: "分析";
|
|
923
|
+
readonly attachment_batch_button_clear: "清除";
|
|
900
924
|
readonly bound_to_thread: "已绑定到线程 {threadId}";
|
|
901
925
|
readonly line_title: "标题:{value}";
|
|
902
926
|
readonly line_cwd: "工作目录:{value}";
|
package/dist/i18n.js
CHANGED
|
@@ -101,6 +101,7 @@ const MESSAGES = {
|
|
|
101
101
|
status_sync_on_turn_complete: 'Sync on turn complete: {value}',
|
|
102
102
|
status_pending_approvals: 'Pending approvals: {value}',
|
|
103
103
|
status_pending_user_inputs: 'Pending questions: {value}',
|
|
104
|
+
status_queued_turns: 'Queued turns: {value}',
|
|
104
105
|
status_active_turns: 'Active turns: {value}',
|
|
105
106
|
status_codex_account: 'Codex account: {value}',
|
|
106
107
|
status_codex_plan: 'Codex plan: {value}',
|
|
@@ -131,6 +132,7 @@ const MESSAGES = {
|
|
|
131
132
|
status_last_codex_update: 'Last Codex update: {value}',
|
|
132
133
|
update_started: 'FoxClaw update started. I will report here after installation, checks, and service restart complete.',
|
|
133
134
|
update_succeeded: 'FoxClaw updated and restarted: {from} -> {to}.',
|
|
135
|
+
update_changes_title: 'What changed:',
|
|
134
136
|
update_codex_result: 'Codex CLI: {from} -> {to}.',
|
|
135
137
|
update_failed: 'FoxClaw update failed: {error}\nRun foxclaw update in a terminal for details.',
|
|
136
138
|
update_unavailable: 'Self-update is unavailable in this runtime. Run foxclaw update in a terminal.',
|
|
@@ -252,8 +254,18 @@ const MESSAGES = {
|
|
|
252
254
|
watch_read_only_active: 'This chat is watching that turn read-only. Use approval commands if needed, or /unwatch before sending a new prompt.',
|
|
253
255
|
watch_not_enabled: 'This chat is not watching any thread.',
|
|
254
256
|
watch_stopped: 'Stopped watching thread {threadId}.',
|
|
255
|
-
queued_prompt_set: 'Queued. I will send it after the current turn finishes.',
|
|
257
|
+
queued_prompt_set: 'Queued #{position} ({id}). I will send it after the current turn finishes.',
|
|
256
258
|
queued_prompt_replaced: 'Replaced the queued prompt. I will send the new one after the current turn finishes.',
|
|
259
|
+
attachment_batch_staged: 'Attachments staged: {count}. Send the prompt text to use them, or choose an action below.',
|
|
260
|
+
attachment_batch_caption: 'Caption: {value}',
|
|
261
|
+
attachment_batch_consumed: 'Attachments attached to the next Codex turn.',
|
|
262
|
+
attachment_batch_queued: 'Attachments queued as turn #{position} ({id}). I will send it after the current turn finishes.',
|
|
263
|
+
attachment_batch_cleared: 'Attachment batch cleared.',
|
|
264
|
+
attachment_batch_missing: 'This attachment batch is no longer active.',
|
|
265
|
+
attachment_batch_mismatch: 'This attachment batch does not match this message.',
|
|
266
|
+
attachment_batch_default_prompt: 'Analyze these attachments.',
|
|
267
|
+
attachment_batch_button_analyze: 'Analyze',
|
|
268
|
+
attachment_batch_button_clear: 'Clear',
|
|
257
269
|
bound_to_thread: 'Bound to thread {threadId}',
|
|
258
270
|
line_title: 'Title: {value}',
|
|
259
271
|
line_cwd: 'CWD: {value}',
|
|
@@ -742,6 +754,7 @@ const MESSAGES = {
|
|
|
742
754
|
status_sync_on_turn_complete: '回复完成后同步:{value}',
|
|
743
755
|
status_pending_approvals: '待审批:{value}',
|
|
744
756
|
status_pending_user_inputs: '待确认问题:{value}',
|
|
757
|
+
status_queued_turns: '排队中的回复:{value}',
|
|
745
758
|
status_active_turns: '进行中的回复:{value}',
|
|
746
759
|
status_codex_account: 'Codex 账号:{value}',
|
|
747
760
|
status_codex_plan: 'Codex 套餐:{value}',
|
|
@@ -772,6 +785,7 @@ const MESSAGES = {
|
|
|
772
785
|
status_last_codex_update: '最近 Codex 升级:{value}',
|
|
773
786
|
update_started: '已开始升级 FoxClaw。安装、自检和服务重启完成后,我会在这里回报结果。',
|
|
774
787
|
update_succeeded: 'FoxClaw 已升级并重启:{from} -> {to}。',
|
|
788
|
+
update_changes_title: '更新内容:',
|
|
775
789
|
update_codex_result: 'Codex CLI:{from} -> {to}。',
|
|
776
790
|
update_failed: 'FoxClaw 升级失败:{error}\n请在终端运行 foxclaw update 查看详情。',
|
|
777
791
|
update_unavailable: '当前运行方式不支持自升级,请在终端运行 foxclaw update。',
|
|
@@ -893,8 +907,18 @@ const MESSAGES = {
|
|
|
893
907
|
watch_read_only_active: '当前聊天正在只读观察这轮。需要审批时可用审批命令;要发送新 prompt 请先 /unwatch。',
|
|
894
908
|
watch_not_enabled: '当前没有正在观察的线程。',
|
|
895
909
|
watch_stopped: '已停止观察线程 {threadId}。',
|
|
896
|
-
queued_prompt_set: '
|
|
910
|
+
queued_prompt_set: '已排队 #{position}({id})。当前回复结束后我会自动发送。',
|
|
897
911
|
queued_prompt_replaced: '已替换排队消息。当前回复结束后我会自动发送新的内容。',
|
|
912
|
+
attachment_batch_staged: '附件已暂存:{count} 个。发送下一条文字即可带上这些附件,或使用下面的按钮。',
|
|
913
|
+
attachment_batch_caption: '说明:{value}',
|
|
914
|
+
attachment_batch_consumed: '附件已带入下一轮 Codex。',
|
|
915
|
+
attachment_batch_queued: '附件已作为第 #{position} 个回复排队({id})。当前回复结束后我会自动发送。',
|
|
916
|
+
attachment_batch_cleared: '已清除这组附件。',
|
|
917
|
+
attachment_batch_missing: '这组附件已经不在待处理状态。',
|
|
918
|
+
attachment_batch_mismatch: '这个附件按钮不属于当前消息。',
|
|
919
|
+
attachment_batch_default_prompt: '分析这些附件。',
|
|
920
|
+
attachment_batch_button_analyze: '分析',
|
|
921
|
+
attachment_batch_button_clear: '清除',
|
|
898
922
|
bound_to_thread: '已绑定到线程 {threadId}',
|
|
899
923
|
line_title: '标题:{value}',
|
|
900
924
|
line_cwd: '工作目录:{value}',
|
package/dist/main.js
CHANGED
|
@@ -289,6 +289,7 @@ async function runServeCli() {
|
|
|
289
289
|
currentBindings: store.countBindings(),
|
|
290
290
|
pendingApprovals: store.countPendingApprovals(),
|
|
291
291
|
pendingUserInputs: store.countPendingUserInputs(),
|
|
292
|
+
queuedTurns: store.countQueuedTurnInputs(),
|
|
292
293
|
activeTurns: statuses.reduce((sum, status) => sum + status.activeTurns, 0)
|
|
293
294
|
+ (weixinStatus?.activeTurns ?? 0),
|
|
294
295
|
lastError: statuses.find((status) => status.lastError)?.lastError
|
|
@@ -585,6 +586,7 @@ async function runServeCli() {
|
|
|
585
586
|
currentBindings: 0,
|
|
586
587
|
pendingApprovals: 0,
|
|
587
588
|
pendingUserInputs: 0,
|
|
589
|
+
queuedTurns: 0,
|
|
588
590
|
activeTurns: 0,
|
|
589
591
|
lastError: null,
|
|
590
592
|
updatedAt: new Date().toISOString(),
|
|
@@ -1132,6 +1134,7 @@ function runDoctorChecks() {
|
|
|
1132
1134
|
warnIfProxyEnvMissingFromLoadedEnv();
|
|
1133
1135
|
warnIfProxyConfigNeedsAttention();
|
|
1134
1136
|
warnIfInstalledServiceNodeLooksWrong();
|
|
1137
|
+
warnIfSystemdUserLingerDisabled();
|
|
1135
1138
|
return passed;
|
|
1136
1139
|
}
|
|
1137
1140
|
function warnIfProxyConfigNeedsAttention() {
|
|
@@ -1204,6 +1207,21 @@ function warnIfInstalledServiceNodeLooksWrong() {
|
|
|
1204
1207
|
console.log(`[WARN] installed service node is older than 24: ${nodePath}${version ? ` (${version})` : ''}`);
|
|
1205
1208
|
console.log('[WARN] Run foxclaw start from a Node 24 shell to refresh the service unit.');
|
|
1206
1209
|
}
|
|
1210
|
+
function warnIfSystemdUserLingerDisabled() {
|
|
1211
|
+
if (process.platform !== 'linux' || !hasCommand('loginctl')) {
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
const user = currentServiceUser();
|
|
1215
|
+
const linger = readSystemdUserLinger(user);
|
|
1216
|
+
if (linger === 'enabled') {
|
|
1217
|
+
console.log(`[OK] systemd user linger enabled: ${user}`);
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
if (linger === 'disabled') {
|
|
1221
|
+
console.log(`[WARN] systemd user linger is disabled for ${user}; user services may stop after logout.`);
|
|
1222
|
+
console.log('[WARN] Run foxclaw start to let FoxClaw enable it, or run: sudo loginctl enable-linger "$USER"');
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1207
1225
|
function extractNodePathFromExecStart(execStart) {
|
|
1208
1226
|
const tokens = execStart.split(/\s+/).map(systemdUnescape).filter(Boolean);
|
|
1209
1227
|
const directNode = tokens[0] || '';
|
|
@@ -1262,6 +1280,7 @@ function installSystemd() {
|
|
|
1262
1280
|
console.log(`[OK] updated FoxClaw ExecStart override: ${update.path}`);
|
|
1263
1281
|
}
|
|
1264
1282
|
}
|
|
1283
|
+
ensureSystemdUserLingerEnabled();
|
|
1265
1284
|
spawnChecked('systemctl', ['--user', 'daemon-reload']);
|
|
1266
1285
|
spawnChecked('systemctl', ['--user', 'enable', unitName]);
|
|
1267
1286
|
const restarted = spawnSync('systemctl', ['--user', 'restart', unitName], { stdio: 'inherit' });
|
|
@@ -1272,6 +1291,32 @@ function installSystemd() {
|
|
|
1272
1291
|
console.log(`Status: systemctl --user status ${unitName}`);
|
|
1273
1292
|
console.log(`Logs: journalctl --user -u ${unitName} -f`);
|
|
1274
1293
|
}
|
|
1294
|
+
function ensureSystemdUserLingerEnabled() {
|
|
1295
|
+
if (process.platform !== 'linux') {
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
if (!hasCommand('loginctl')) {
|
|
1299
|
+
console.log('[WARN] loginctl not found; cannot enable systemd user linger automatically.');
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const user = currentServiceUser();
|
|
1303
|
+
const before = readSystemdUserLinger(user);
|
|
1304
|
+
if (before === 'enabled') {
|
|
1305
|
+
console.log(`[OK] systemd user linger enabled: ${user}`);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
console.log(`[INFO] Enabling systemd user linger for ${user} so FoxClaw keeps running after logout.`);
|
|
1309
|
+
let result = spawnSync('loginctl', ['enable-linger', user], { stdio: 'inherit' });
|
|
1310
|
+
if (result.status !== 0 && hasCommand('sudo')) {
|
|
1311
|
+
result = spawnSync('sudo', ['-n', 'loginctl', 'enable-linger', user], { stdio: 'inherit' });
|
|
1312
|
+
}
|
|
1313
|
+
if (result.status === 0 && readSystemdUserLinger(user) === 'enabled') {
|
|
1314
|
+
console.log(`[OK] systemd user linger enabled: ${user}`);
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
console.log(`[WARN] Could not enable systemd user linger automatically for ${user}.`);
|
|
1318
|
+
console.log(`[WARN] FoxClaw is installed, but it may stop after logout until you run: sudo loginctl enable-linger ${shellQuote(user)}`);
|
|
1319
|
+
}
|
|
1275
1320
|
function uninstallSystemd() {
|
|
1276
1321
|
if (!hasCommand('systemctl')) {
|
|
1277
1322
|
console.error('systemctl not found');
|
|
@@ -1392,6 +1437,36 @@ function buildServicePath(nodeDir) {
|
|
|
1392
1437
|
function serviceEnvPath() {
|
|
1393
1438
|
return path.resolve(process.env.FOXCLAW_ENV?.trim() || getLoadedEnvPath() || DEFAULT_ENV_PATH);
|
|
1394
1439
|
}
|
|
1440
|
+
function currentServiceUser() {
|
|
1441
|
+
return process.env.USER?.trim()
|
|
1442
|
+
|| process.env.LOGNAME?.trim()
|
|
1443
|
+
|| os.userInfo().username;
|
|
1444
|
+
}
|
|
1445
|
+
function readSystemdUserLinger(user) {
|
|
1446
|
+
const valueResult = spawnSync('loginctl', ['show-user', user, '-p', 'Linger', '--value'], { encoding: 'utf8' });
|
|
1447
|
+
if (valueResult.status === 0) {
|
|
1448
|
+
return parseSystemdUserLinger(valueResult.stdout);
|
|
1449
|
+
}
|
|
1450
|
+
const propertyResult = spawnSync('loginctl', ['show-user', user, '-p', 'Linger'], { encoding: 'utf8' });
|
|
1451
|
+
if (propertyResult.status === 0) {
|
|
1452
|
+
return parseSystemdUserLinger(propertyResult.stdout);
|
|
1453
|
+
}
|
|
1454
|
+
return 'unknown';
|
|
1455
|
+
}
|
|
1456
|
+
function parseSystemdUserLinger(output) {
|
|
1457
|
+
const value = output.trim().replace(/^Linger=/, '').toLowerCase();
|
|
1458
|
+
if (value === 'yes')
|
|
1459
|
+
return 'enabled';
|
|
1460
|
+
if (value === 'no')
|
|
1461
|
+
return 'disabled';
|
|
1462
|
+
return 'unknown';
|
|
1463
|
+
}
|
|
1464
|
+
function shellQuote(value) {
|
|
1465
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(value)) {
|
|
1466
|
+
return value;
|
|
1467
|
+
}
|
|
1468
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1469
|
+
}
|
|
1395
1470
|
function hasStandardNodeProxyEnv() {
|
|
1396
1471
|
return STANDARD_NODE_PROXY_ENV_KEYS.some((key) => Boolean(proxyEnvValue(key)));
|
|
1397
1472
|
}
|
package/dist/store/database.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccessPresetValue, ActiveTurnMessageMode, AppLocale, CachedThread, ChatSessionSettings, CollaborationModeValue, PendingApprovalRecord, ReasoningEffortValue, ThreadBinding } from '../types.js';
|
|
1
|
+
import type { AccessPresetValue, ActiveTurnMessageMode, AppLocale, CachedThread, ChatSessionSettings, CollaborationModeValue, GuidedPlanSessionRecord, GuidedPlanSessionState, PendingAttachmentBatchRecord, PendingAttachmentBatchStatus, PendingApprovalRecord, QueuedTurnInputRecord, QueuedTurnInputStatus, ReasoningEffortValue, ThreadBinding } from '../types.js';
|
|
2
2
|
export interface ActiveTurnPreviewRecord {
|
|
3
3
|
turnId: string;
|
|
4
4
|
scopeId: string;
|
|
@@ -84,10 +84,34 @@ export declare class BridgeStore {
|
|
|
84
84
|
markPendingUserInputInterrupted(localId: string): void;
|
|
85
85
|
listPendingUserInputs(): PendingUserInputStoredRecord[];
|
|
86
86
|
countPendingUserInputs(): number;
|
|
87
|
+
saveQueuedTurnInput(record: QueuedTurnInputRecord): void;
|
|
88
|
+
getQueuedTurnInput(queueId: string): QueuedTurnInputRecord | null;
|
|
89
|
+
peekQueuedTurnInput(scopeId: string): QueuedTurnInputRecord | null;
|
|
90
|
+
listQueuedTurnInputs(scopeId?: string): QueuedTurnInputRecord[];
|
|
91
|
+
countQueuedTurnInputs(scopeId?: string): number;
|
|
92
|
+
updateQueuedTurnInputStatus(queueId: string, status: QueuedTurnInputStatus, error?: string | null): void;
|
|
93
|
+
cancelQueuedTurnInputs(scopeId: string): number;
|
|
94
|
+
requeueInterruptedQueuedTurnInputs(): number;
|
|
95
|
+
savePendingAttachmentBatch(record: PendingAttachmentBatchRecord): void;
|
|
96
|
+
getPendingAttachmentBatch(batchId: string): PendingAttachmentBatchRecord | null;
|
|
97
|
+
findPendingAttachmentBatchByMediaGroup(scopeId: string, mediaGroupId: string): PendingAttachmentBatchRecord | null;
|
|
98
|
+
getLatestPendingAttachmentBatch(scopeId: string): PendingAttachmentBatchRecord | null;
|
|
99
|
+
updatePendingAttachmentBatchMessage(batchId: string, messageId: number): void;
|
|
100
|
+
resolvePendingAttachmentBatch(batchId: string, status: PendingAttachmentBatchStatus): void;
|
|
101
|
+
clearPendingAttachmentBatches(scopeId: string): number;
|
|
102
|
+
saveGuidedPlanSession(record: GuidedPlanSessionRecord): void;
|
|
103
|
+
getGuidedPlanSession(sessionId: string): GuidedPlanSessionRecord | null;
|
|
104
|
+
findOpenGuidedPlanSession(scopeId: string, turnId?: string): GuidedPlanSessionRecord | null;
|
|
105
|
+
listOpenGuidedPlanSessions(): GuidedPlanSessionRecord[];
|
|
106
|
+
updateGuidedPlanSessionMessage(sessionId: string, messageId: number): void;
|
|
107
|
+
updateGuidedPlanSessionState(sessionId: string, state: GuidedPlanSessionState): void;
|
|
87
108
|
insertAudit(direction: 'inbound' | 'outbound', chatId: string, eventType: string, summary: string): void;
|
|
88
109
|
close(): void;
|
|
89
110
|
private mapApproval;
|
|
90
111
|
private mapPendingUserInput;
|
|
112
|
+
private mapQueuedTurnInput;
|
|
113
|
+
private mapPendingAttachmentBatch;
|
|
114
|
+
private mapGuidedPlanSession;
|
|
91
115
|
private writeChatSettings;
|
|
92
116
|
getWeixinContextToken(scopeId: string): string | null;
|
|
93
117
|
setWeixinContextToken(scopeId: string, contextToken: string): void;
|
package/dist/store/database.js
CHANGED
|
@@ -98,6 +98,61 @@ export class BridgeStore {
|
|
|
98
98
|
created_at INTEGER NOT NULL,
|
|
99
99
|
PRIMARY KEY (input_local_id, question_index, message_kind)
|
|
100
100
|
);
|
|
101
|
+
CREATE TABLE IF NOT EXISTS queued_turn_inputs (
|
|
102
|
+
queue_id TEXT PRIMARY KEY,
|
|
103
|
+
scope_id TEXT NOT NULL,
|
|
104
|
+
chat_id TEXT NOT NULL,
|
|
105
|
+
chat_type TEXT NOT NULL,
|
|
106
|
+
topic_id INTEGER,
|
|
107
|
+
thread_id TEXT NOT NULL,
|
|
108
|
+
input_json TEXT NOT NULL,
|
|
109
|
+
source_summary TEXT NOT NULL,
|
|
110
|
+
message_id INTEGER,
|
|
111
|
+
status TEXT NOT NULL,
|
|
112
|
+
error TEXT,
|
|
113
|
+
created_at INTEGER NOT NULL,
|
|
114
|
+
updated_at INTEGER NOT NULL,
|
|
115
|
+
resolved_at INTEGER
|
|
116
|
+
);
|
|
117
|
+
CREATE INDEX IF NOT EXISTS queued_turn_inputs_scope_status_idx
|
|
118
|
+
ON queued_turn_inputs(scope_id, status, created_at);
|
|
119
|
+
CREATE TABLE IF NOT EXISTS pending_attachment_batches (
|
|
120
|
+
batch_id TEXT PRIMARY KEY,
|
|
121
|
+
scope_id TEXT NOT NULL,
|
|
122
|
+
chat_id TEXT NOT NULL,
|
|
123
|
+
chat_type TEXT NOT NULL,
|
|
124
|
+
topic_id INTEGER,
|
|
125
|
+
thread_id TEXT NOT NULL,
|
|
126
|
+
cwd TEXT,
|
|
127
|
+
media_group_id TEXT,
|
|
128
|
+
attachments_json TEXT NOT NULL,
|
|
129
|
+
caption TEXT NOT NULL,
|
|
130
|
+
message_id INTEGER,
|
|
131
|
+
status TEXT NOT NULL,
|
|
132
|
+
created_at INTEGER NOT NULL,
|
|
133
|
+
updated_at INTEGER NOT NULL,
|
|
134
|
+
resolved_at INTEGER
|
|
135
|
+
);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS pending_attachment_batches_scope_status_idx
|
|
137
|
+
ON pending_attachment_batches(scope_id, status, updated_at);
|
|
138
|
+
CREATE TABLE IF NOT EXISTS guided_plan_sessions (
|
|
139
|
+
session_id TEXT PRIMARY KEY,
|
|
140
|
+
scope_id TEXT NOT NULL,
|
|
141
|
+
chat_id TEXT NOT NULL,
|
|
142
|
+
chat_type TEXT NOT NULL,
|
|
143
|
+
topic_id INTEGER,
|
|
144
|
+
thread_id TEXT NOT NULL,
|
|
145
|
+
turn_id TEXT NOT NULL,
|
|
146
|
+
cwd TEXT,
|
|
147
|
+
plan_markdown TEXT NOT NULL,
|
|
148
|
+
message_id INTEGER,
|
|
149
|
+
state TEXT NOT NULL,
|
|
150
|
+
created_at INTEGER NOT NULL,
|
|
151
|
+
updated_at INTEGER NOT NULL,
|
|
152
|
+
resolved_at INTEGER
|
|
153
|
+
);
|
|
154
|
+
CREATE INDEX IF NOT EXISTS guided_plan_sessions_scope_state_idx
|
|
155
|
+
ON guided_plan_sessions(scope_id, state, updated_at);
|
|
101
156
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
102
157
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
103
158
|
direction TEXT NOT NULL,
|
|
@@ -438,6 +493,204 @@ export class BridgeStore {
|
|
|
438
493
|
const row = this.db.prepare('SELECT COUNT(*) AS count FROM pending_user_inputs WHERE resolved_at IS NULL').get();
|
|
439
494
|
return Number(row?.count ?? 0);
|
|
440
495
|
}
|
|
496
|
+
saveQueuedTurnInput(record) {
|
|
497
|
+
this.db.prepare(`
|
|
498
|
+
INSERT INTO queued_turn_inputs (
|
|
499
|
+
queue_id, scope_id, chat_id, chat_type, topic_id, thread_id, input_json, source_summary,
|
|
500
|
+
message_id, status, error, created_at, updated_at, resolved_at
|
|
501
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
502
|
+
ON CONFLICT(queue_id) DO UPDATE SET
|
|
503
|
+
scope_id = excluded.scope_id,
|
|
504
|
+
chat_id = excluded.chat_id,
|
|
505
|
+
chat_type = excluded.chat_type,
|
|
506
|
+
topic_id = excluded.topic_id,
|
|
507
|
+
thread_id = excluded.thread_id,
|
|
508
|
+
input_json = excluded.input_json,
|
|
509
|
+
source_summary = excluded.source_summary,
|
|
510
|
+
message_id = excluded.message_id,
|
|
511
|
+
status = excluded.status,
|
|
512
|
+
error = excluded.error,
|
|
513
|
+
created_at = excluded.created_at,
|
|
514
|
+
updated_at = excluded.updated_at,
|
|
515
|
+
resolved_at = excluded.resolved_at
|
|
516
|
+
`).run(record.queueId, record.scopeId, record.chatId, record.chatType, record.topicId, record.threadId, record.inputJson, record.sourceSummary, record.messageId, record.status, record.error, record.createdAt, record.updatedAt, record.resolvedAt);
|
|
517
|
+
}
|
|
518
|
+
getQueuedTurnInput(queueId) {
|
|
519
|
+
const row = this.db.prepare('SELECT * FROM queued_turn_inputs WHERE queue_id = ?').get(queueId);
|
|
520
|
+
return row ? this.mapQueuedTurnInput(row) : null;
|
|
521
|
+
}
|
|
522
|
+
peekQueuedTurnInput(scopeId) {
|
|
523
|
+
const row = this.db.prepare(`
|
|
524
|
+
SELECT * FROM queued_turn_inputs
|
|
525
|
+
WHERE scope_id = ? AND status = 'queued'
|
|
526
|
+
ORDER BY created_at ASC
|
|
527
|
+
LIMIT 1
|
|
528
|
+
`).get(scopeId);
|
|
529
|
+
return row ? this.mapQueuedTurnInput(row) : null;
|
|
530
|
+
}
|
|
531
|
+
listQueuedTurnInputs(scopeId) {
|
|
532
|
+
const sql = scopeId
|
|
533
|
+
? `SELECT * FROM queued_turn_inputs WHERE scope_id = ? AND status IN ('queued', 'processing') ORDER BY created_at ASC`
|
|
534
|
+
: `SELECT * FROM queued_turn_inputs WHERE status IN ('queued', 'processing') ORDER BY created_at ASC`;
|
|
535
|
+
const rows = scopeId
|
|
536
|
+
? this.db.prepare(sql).all(scopeId)
|
|
537
|
+
: this.db.prepare(sql).all();
|
|
538
|
+
return rows.map((row) => this.mapQueuedTurnInput(row));
|
|
539
|
+
}
|
|
540
|
+
countQueuedTurnInputs(scopeId) {
|
|
541
|
+
const row = scopeId
|
|
542
|
+
? this.db.prepare(`SELECT COUNT(*) AS count FROM queued_turn_inputs WHERE scope_id = ? AND status IN ('queued', 'processing')`).get(scopeId)
|
|
543
|
+
: this.db.prepare(`SELECT COUNT(*) AS count FROM queued_turn_inputs WHERE status IN ('queued', 'processing')`).get();
|
|
544
|
+
return Number(row?.count ?? 0);
|
|
545
|
+
}
|
|
546
|
+
updateQueuedTurnInputStatus(queueId, status, error = null) {
|
|
547
|
+
const resolvedAt = status === 'queued' || status === 'processing' ? null : Date.now();
|
|
548
|
+
this.db.prepare(`
|
|
549
|
+
UPDATE queued_turn_inputs
|
|
550
|
+
SET status = ?, error = ?, updated_at = ?, resolved_at = ?
|
|
551
|
+
WHERE queue_id = ?
|
|
552
|
+
`).run(status, error, Date.now(), resolvedAt, queueId);
|
|
553
|
+
}
|
|
554
|
+
cancelQueuedTurnInputs(scopeId) {
|
|
555
|
+
const result = this.db.prepare(`
|
|
556
|
+
UPDATE queued_turn_inputs
|
|
557
|
+
SET status = 'cancelled', updated_at = ?, resolved_at = ?
|
|
558
|
+
WHERE scope_id = ? AND status = 'queued'
|
|
559
|
+
`).run(Date.now(), Date.now(), scopeId);
|
|
560
|
+
return Number(result.changes ?? 0);
|
|
561
|
+
}
|
|
562
|
+
requeueInterruptedQueuedTurnInputs() {
|
|
563
|
+
const result = this.db.prepare(`
|
|
564
|
+
UPDATE queued_turn_inputs
|
|
565
|
+
SET status = 'queued', error = NULL, updated_at = ?, resolved_at = NULL
|
|
566
|
+
WHERE status = 'processing'
|
|
567
|
+
`).run(Date.now());
|
|
568
|
+
return Number(result.changes ?? 0);
|
|
569
|
+
}
|
|
570
|
+
savePendingAttachmentBatch(record) {
|
|
571
|
+
this.db.prepare(`
|
|
572
|
+
INSERT INTO pending_attachment_batches (
|
|
573
|
+
batch_id, scope_id, chat_id, chat_type, topic_id, thread_id, cwd, media_group_id,
|
|
574
|
+
attachments_json, caption, message_id, status, created_at, updated_at, resolved_at
|
|
575
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
576
|
+
ON CONFLICT(batch_id) DO UPDATE SET
|
|
577
|
+
scope_id = excluded.scope_id,
|
|
578
|
+
chat_id = excluded.chat_id,
|
|
579
|
+
chat_type = excluded.chat_type,
|
|
580
|
+
topic_id = excluded.topic_id,
|
|
581
|
+
thread_id = excluded.thread_id,
|
|
582
|
+
cwd = excluded.cwd,
|
|
583
|
+
media_group_id = excluded.media_group_id,
|
|
584
|
+
attachments_json = excluded.attachments_json,
|
|
585
|
+
caption = excluded.caption,
|
|
586
|
+
message_id = excluded.message_id,
|
|
587
|
+
status = excluded.status,
|
|
588
|
+
created_at = excluded.created_at,
|
|
589
|
+
updated_at = excluded.updated_at,
|
|
590
|
+
resolved_at = excluded.resolved_at
|
|
591
|
+
`).run(record.batchId, record.scopeId, record.chatId, record.chatType, record.topicId, record.threadId, record.cwd, record.mediaGroupId, record.attachmentsJson, record.caption, record.messageId, record.status, record.createdAt, record.updatedAt, record.resolvedAt);
|
|
592
|
+
}
|
|
593
|
+
getPendingAttachmentBatch(batchId) {
|
|
594
|
+
const row = this.db.prepare('SELECT * FROM pending_attachment_batches WHERE batch_id = ?').get(batchId);
|
|
595
|
+
return row ? this.mapPendingAttachmentBatch(row) : null;
|
|
596
|
+
}
|
|
597
|
+
findPendingAttachmentBatchByMediaGroup(scopeId, mediaGroupId) {
|
|
598
|
+
const row = this.db.prepare(`
|
|
599
|
+
SELECT * FROM pending_attachment_batches
|
|
600
|
+
WHERE scope_id = ? AND media_group_id = ? AND status = 'pending'
|
|
601
|
+
ORDER BY updated_at DESC
|
|
602
|
+
LIMIT 1
|
|
603
|
+
`).get(scopeId, mediaGroupId);
|
|
604
|
+
return row ? this.mapPendingAttachmentBatch(row) : null;
|
|
605
|
+
}
|
|
606
|
+
getLatestPendingAttachmentBatch(scopeId) {
|
|
607
|
+
const row = this.db.prepare(`
|
|
608
|
+
SELECT * FROM pending_attachment_batches
|
|
609
|
+
WHERE scope_id = ? AND status = 'pending'
|
|
610
|
+
ORDER BY updated_at DESC
|
|
611
|
+
LIMIT 1
|
|
612
|
+
`).get(scopeId);
|
|
613
|
+
return row ? this.mapPendingAttachmentBatch(row) : null;
|
|
614
|
+
}
|
|
615
|
+
updatePendingAttachmentBatchMessage(batchId, messageId) {
|
|
616
|
+
this.db.prepare('UPDATE pending_attachment_batches SET message_id = ?, updated_at = ? WHERE batch_id = ?').run(messageId, Date.now(), batchId);
|
|
617
|
+
}
|
|
618
|
+
resolvePendingAttachmentBatch(batchId, status) {
|
|
619
|
+
this.db.prepare(`
|
|
620
|
+
UPDATE pending_attachment_batches
|
|
621
|
+
SET status = ?, updated_at = ?, resolved_at = ?
|
|
622
|
+
WHERE batch_id = ?
|
|
623
|
+
`).run(status, Date.now(), Date.now(), batchId);
|
|
624
|
+
}
|
|
625
|
+
clearPendingAttachmentBatches(scopeId) {
|
|
626
|
+
const result = this.db.prepare(`
|
|
627
|
+
UPDATE pending_attachment_batches
|
|
628
|
+
SET status = 'cleared', updated_at = ?, resolved_at = ?
|
|
629
|
+
WHERE scope_id = ? AND status = 'pending'
|
|
630
|
+
`).run(Date.now(), Date.now(), scopeId);
|
|
631
|
+
return Number(result.changes ?? 0);
|
|
632
|
+
}
|
|
633
|
+
saveGuidedPlanSession(record) {
|
|
634
|
+
this.db.prepare(`
|
|
635
|
+
INSERT INTO guided_plan_sessions (
|
|
636
|
+
session_id, scope_id, chat_id, chat_type, topic_id, thread_id, turn_id, cwd,
|
|
637
|
+
plan_markdown, message_id, state, created_at, updated_at, resolved_at
|
|
638
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
639
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
640
|
+
scope_id = excluded.scope_id,
|
|
641
|
+
chat_id = excluded.chat_id,
|
|
642
|
+
chat_type = excluded.chat_type,
|
|
643
|
+
topic_id = excluded.topic_id,
|
|
644
|
+
thread_id = excluded.thread_id,
|
|
645
|
+
turn_id = excluded.turn_id,
|
|
646
|
+
cwd = excluded.cwd,
|
|
647
|
+
plan_markdown = excluded.plan_markdown,
|
|
648
|
+
message_id = excluded.message_id,
|
|
649
|
+
state = excluded.state,
|
|
650
|
+
created_at = excluded.created_at,
|
|
651
|
+
updated_at = excluded.updated_at,
|
|
652
|
+
resolved_at = excluded.resolved_at
|
|
653
|
+
`).run(record.sessionId, record.scopeId, record.chatId, record.chatType, record.topicId, record.threadId, record.turnId, record.cwd, record.planMarkdown, record.messageId, record.state, record.createdAt, record.updatedAt, record.resolvedAt);
|
|
654
|
+
}
|
|
655
|
+
getGuidedPlanSession(sessionId) {
|
|
656
|
+
const row = this.db.prepare('SELECT * FROM guided_plan_sessions WHERE session_id = ?').get(sessionId);
|
|
657
|
+
return row ? this.mapGuidedPlanSession(row) : null;
|
|
658
|
+
}
|
|
659
|
+
findOpenGuidedPlanSession(scopeId, turnId) {
|
|
660
|
+
const row = turnId
|
|
661
|
+
? this.db.prepare(`
|
|
662
|
+
SELECT * FROM guided_plan_sessions
|
|
663
|
+
WHERE scope_id = ? AND turn_id = ? AND state = 'awaiting_confirmation'
|
|
664
|
+
ORDER BY updated_at DESC
|
|
665
|
+
LIMIT 1
|
|
666
|
+
`).get(scopeId, turnId)
|
|
667
|
+
: this.db.prepare(`
|
|
668
|
+
SELECT * FROM guided_plan_sessions
|
|
669
|
+
WHERE scope_id = ? AND state = 'awaiting_confirmation'
|
|
670
|
+
ORDER BY updated_at DESC
|
|
671
|
+
LIMIT 1
|
|
672
|
+
`).get(scopeId);
|
|
673
|
+
return row ? this.mapGuidedPlanSession(row) : null;
|
|
674
|
+
}
|
|
675
|
+
listOpenGuidedPlanSessions() {
|
|
676
|
+
const rows = this.db.prepare(`
|
|
677
|
+
SELECT * FROM guided_plan_sessions
|
|
678
|
+
WHERE state = 'awaiting_confirmation'
|
|
679
|
+
ORDER BY updated_at ASC
|
|
680
|
+
`).all();
|
|
681
|
+
return rows.map((row) => this.mapGuidedPlanSession(row));
|
|
682
|
+
}
|
|
683
|
+
updateGuidedPlanSessionMessage(sessionId, messageId) {
|
|
684
|
+
this.db.prepare('UPDATE guided_plan_sessions SET message_id = ?, updated_at = ? WHERE session_id = ?').run(messageId, Date.now(), sessionId);
|
|
685
|
+
}
|
|
686
|
+
updateGuidedPlanSessionState(sessionId, state) {
|
|
687
|
+
const resolvedAt = state === 'awaiting_confirmation' || state === 'executing' ? null : Date.now();
|
|
688
|
+
this.db.prepare(`
|
|
689
|
+
UPDATE guided_plan_sessions
|
|
690
|
+
SET state = ?, updated_at = ?, resolved_at = ?
|
|
691
|
+
WHERE session_id = ?
|
|
692
|
+
`).run(state, Date.now(), resolvedAt, sessionId);
|
|
693
|
+
}
|
|
441
694
|
insertAudit(direction, chatId, eventType, summary) {
|
|
442
695
|
this.db.prepare('INSERT INTO audit_logs (direction, chat_id, event_type, summary, created_at) VALUES (?, ?, ?, ?, ?)').run(direction, chatId, eventType, summary, Date.now());
|
|
443
696
|
}
|
|
@@ -482,6 +735,61 @@ export class BridgeStore {
|
|
|
482
735
|
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
483
736
|
};
|
|
484
737
|
}
|
|
738
|
+
mapQueuedTurnInput(row) {
|
|
739
|
+
return {
|
|
740
|
+
queueId: String(row.queue_id),
|
|
741
|
+
scopeId: String(row.scope_id),
|
|
742
|
+
chatId: String(row.chat_id),
|
|
743
|
+
chatType: String(row.chat_type),
|
|
744
|
+
topicId: row.topic_id === null ? null : Number(row.topic_id),
|
|
745
|
+
threadId: String(row.thread_id),
|
|
746
|
+
inputJson: String(row.input_json),
|
|
747
|
+
sourceSummary: String(row.source_summary),
|
|
748
|
+
messageId: row.message_id === null ? null : Number(row.message_id),
|
|
749
|
+
status: normalizeQueuedTurnInputStatus(row.status),
|
|
750
|
+
error: row.error === null ? null : String(row.error),
|
|
751
|
+
createdAt: Number(row.created_at),
|
|
752
|
+
updatedAt: Number(row.updated_at),
|
|
753
|
+
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
mapPendingAttachmentBatch(row) {
|
|
757
|
+
return {
|
|
758
|
+
batchId: String(row.batch_id),
|
|
759
|
+
scopeId: String(row.scope_id),
|
|
760
|
+
chatId: String(row.chat_id),
|
|
761
|
+
chatType: String(row.chat_type),
|
|
762
|
+
topicId: row.topic_id === null ? null : Number(row.topic_id),
|
|
763
|
+
threadId: String(row.thread_id),
|
|
764
|
+
cwd: row.cwd === null ? null : String(row.cwd),
|
|
765
|
+
mediaGroupId: row.media_group_id === null ? null : String(row.media_group_id),
|
|
766
|
+
attachmentsJson: String(row.attachments_json),
|
|
767
|
+
caption: String(row.caption ?? ''),
|
|
768
|
+
messageId: row.message_id === null ? null : Number(row.message_id),
|
|
769
|
+
status: normalizePendingAttachmentBatchStatus(row.status),
|
|
770
|
+
createdAt: Number(row.created_at),
|
|
771
|
+
updatedAt: Number(row.updated_at),
|
|
772
|
+
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
mapGuidedPlanSession(row) {
|
|
776
|
+
return {
|
|
777
|
+
sessionId: String(row.session_id),
|
|
778
|
+
scopeId: String(row.scope_id),
|
|
779
|
+
chatId: String(row.chat_id),
|
|
780
|
+
chatType: String(row.chat_type),
|
|
781
|
+
topicId: row.topic_id === null ? null : Number(row.topic_id),
|
|
782
|
+
threadId: String(row.thread_id),
|
|
783
|
+
turnId: String(row.turn_id),
|
|
784
|
+
cwd: row.cwd === null ? null : String(row.cwd),
|
|
785
|
+
planMarkdown: String(row.plan_markdown),
|
|
786
|
+
messageId: row.message_id === null ? null : Number(row.message_id),
|
|
787
|
+
state: normalizeGuidedPlanSessionState(row.state),
|
|
788
|
+
createdAt: Number(row.created_at),
|
|
789
|
+
updatedAt: Number(row.updated_at),
|
|
790
|
+
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
791
|
+
};
|
|
792
|
+
}
|
|
485
793
|
writeChatSettings(chatId, model, reasoningEffort, locale, accessPreset, collaborationMode, serviceTier, activeTurnMessageMode) {
|
|
486
794
|
this.db.prepare(`
|
|
487
795
|
INSERT INTO chat_settings (chat_id, model, reasoning_effort, locale, access_preset, collaboration_mode, service_tier, active_turn_message_mode, updated_at)
|
|
@@ -615,3 +923,16 @@ function normalizeCollaborationMode(value) {
|
|
|
615
923
|
function normalizeActiveTurnMessageMode(value) {
|
|
616
924
|
return value === 'steer' || value === 'queue' ? value : null;
|
|
617
925
|
}
|
|
926
|
+
function normalizeQueuedTurnInputStatus(value) {
|
|
927
|
+
return value === 'processing' || value === 'completed' || value === 'cancelled' || value === 'failed'
|
|
928
|
+
? value
|
|
929
|
+
: 'queued';
|
|
930
|
+
}
|
|
931
|
+
function normalizePendingAttachmentBatchStatus(value) {
|
|
932
|
+
return value === 'consumed' || value === 'cleared' ? value : 'pending';
|
|
933
|
+
}
|
|
934
|
+
function normalizeGuidedPlanSessionState(value) {
|
|
935
|
+
return value === 'executing' || value === 'cancelled' || value === 'completed'
|
|
936
|
+
? value
|
|
937
|
+
: 'awaiting_confirmation';
|
|
938
|
+
}
|
package/dist/telegram/gateway.js
CHANGED