@kafca/agentdock 0.1.23 → 0.1.24

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.
Files changed (70) hide show
  1. package/README.md +1 -0
  2. package/dist/renderer/assets/{Badge-C2K5apfV.js → Badge-CXcUMqb-.js} +1 -1
  3. package/dist/renderer/assets/{Chat-DI2YeVKb.js → Chat-BRxdPzi-.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-DMAr15Oi.js → ChatMarkdown-sWGBIfIb.js} +1 -1
  5. package/dist/renderer/assets/{Config-7rP3EuUf.js → Config-DqxuxIGg.js} +1 -1
  6. package/dist/renderer/assets/{CronList-81QslYE8.js → CronList-OgHxbti5.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-Db2T7rSZ.js → Dashboard-DmklXiH1.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-BBJ3cTvB.js → EmptyState-BvDmi8dq.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-Dz_6flG3.js → KnowledgeDetail-Bj59Hdpo.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-DIh046mN.js → KnowledgeHome-CigQ-nSo.js} +1 -1
  11. package/dist/renderer/assets/{Logs-BXBtHJLf.js → Logs-Bfc7gBpY.js} +1 -1
  12. package/dist/renderer/assets/{Modal-BB2Av_Xs.js → Modal-CfmiFknz.js} +1 -1
  13. package/dist/renderer/assets/{Page-DPSdvMgU.js → Page-BxtLEw_b.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-C6EWGvGg.js → ProjectDetail-CcUphxGm.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-BBo5ygnS.js → ProjectList-CVkvDIfy.js} +1 -1
  16. package/dist/renderer/assets/{Select-CNX-Tx_O.js → Select-DgQMZ0MD.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-BIn46jqZ.js → SessionChat-CRmGKfBx.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-CZt6f1Rd.js → SessionList-D0E8XbYw.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-DUgqZSBB.js +23 -0
  20. package/dist/renderer/assets/{Workspace-C_4tdpjX.js → Workspace-Q4g4rlRT.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-cHr5wkpq.js → arrow-left-BySN_zhF.js} +1 -1
  22. package/dist/renderer/assets/{book-open-4nx5C8T-.js → book-open-BGlrnrTc.js} +1 -1
  23. package/dist/renderer/assets/{check-exTS-R-x.js → check-BiJogEa5.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-DSvxXQa0.js → chevron-down-D2fHRJv6.js} +1 -1
  25. package/dist/renderer/assets/{circle-DyvEovdw.js → circle-CIcjixmI.js} +1 -1
  26. package/dist/renderer/assets/desktop-DAXY9Bxv.js +4 -0
  27. package/dist/renderer/assets/{desktop-B-KVT10D.js → desktop-DyA_NYew.js} +1 -1
  28. package/dist/renderer/assets/{funnel-DA9qVdTy.js → funnel-BUzwb-id.js} +1 -1
  29. package/dist/renderer/assets/{heart-DuEV0ZRJ.js → heart-DZEQ1z_V.js} +1 -1
  30. package/dist/renderer/assets/{index-Biz-aexG.js → index-CB_KyLOr.js} +2 -2
  31. package/dist/renderer/assets/{index-BgflqkCN.js → index-DgsIo4vJ.js} +1 -1
  32. package/dist/renderer/assets/{pencil-DBDxeNmO.js → pencil-DGL4agcl.js} +1 -1
  33. package/dist/renderer/assets/{play-IzlP83_7.js → play-DzasCLM4.js} +1 -1
  34. package/dist/renderer/assets/{plug-CuT-qFnW.js → plug-dubNMe_m.js} +1 -1
  35. package/dist/renderer/assets/{plus-DEqgkn63.js → plus-DN4kML4k.js} +1 -1
  36. package/dist/renderer/assets/projects-migPtfRe.js +1 -0
  37. package/dist/renderer/assets/{save-D4sRo07y.js → save-CNIc9tjl.js} +1 -1
  38. package/dist/renderer/assets/{search-DbPZLuV8.js → search-C0EGbF3I.js} +1 -1
  39. package/dist/renderer/assets/{server-D1jUQmwN.js → server-BMxQ4zmc.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-Aife8ep2.js → session-utils-Bf5O-iMi.js} +1 -1
  41. package/dist/renderer/assets/{sessions-DaH3H7gC.js → sessions-BMSs6rEI.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-BGwUFWNr.js → shield-check-i3Tf1TUz.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-CIFBioBa.js → trash-2-SKrcp18f.js} +1 -1
  44. package/dist/renderer/assets/{user-CnKcvHdo.js → user-cNUZddjZ.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-ClO53WGa.js → wifi-off-Dyb84ANR.js} +1 -1
  46. package/dist/renderer/index.html +1 -1
  47. package/dist-electron/electron/channel-content.test.js +29 -0
  48. package/dist-electron/electron/lac-cli.test.js +49 -4
  49. package/dist-electron/electron/local-core-acp-content.test.js +39 -0
  50. package/dist-electron/electron/local-core-contracts.test.js +67 -0
  51. package/dist-electron/electron/local-core-refactor.test.js +123 -0
  52. package/dist-electron/electron/workspace-task-store.test.js +124 -0
  53. package/dist-electron/packages/contracts/src/local-core.js +110 -0
  54. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +25 -4
  55. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +3 -2
  56. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +44 -28
  57. package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +5 -48
  58. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +68 -25
  59. package/dist-electron/services/local-ai-core/src/channel/shared/content.js +23 -0
  60. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +23 -28
  61. package/dist-electron/services/local-ai-core/src/cli/lac.js +19 -16
  62. package/dist-electron/services/local-ai-core/src/scheduler/job-id.js +15 -0
  63. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +30 -5
  64. package/dist-electron/shared/desktop.js +27 -5
  65. package/dist-electron/src/pages/Threads/thread-chat-model.js +57 -0
  66. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +70 -0
  67. package/package.json +1 -1
  68. package/dist/renderer/assets/ThreadChat-BotD0gKQ.js +0 -23
  69. package/dist/renderer/assets/desktop-CVibRyAw.js +0 -4
  70. package/dist/renderer/assets/projects-DDtDN1rc.js +0 -1
@@ -9,6 +9,7 @@ const node_fs_1 = require("node:fs");
9
9
  const node_path_1 = require("node:path");
10
10
  const node_os_1 = require("node:os");
11
11
  const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-store.js");
12
+ const scheduler_service_js_1 = require("../services/local-ai-core/src/scheduler/scheduler-service.js");
12
13
  (0, node_test_1.default)('workspace registry entries persist in LocalCoreAcpStore', () => {
13
14
  const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-registry-'));
14
15
  try {
@@ -34,6 +35,86 @@ const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/loc
34
35
  (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
35
36
  }
36
37
  });
38
+ (0, node_test_1.default)('scheduler uses short ids for new jobs and resolves legacy full ids by short id', () => {
39
+ const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-id-store-'));
40
+ try {
41
+ const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
42
+ const scheduler = new scheduler_service_js_1.SchedulerService({
43
+ store,
44
+ triggers: [],
45
+ executors: [],
46
+ eventBus: { emit: () => { }, on: () => () => { } },
47
+ });
48
+ const created = scheduler.createJob({
49
+ workspaceId: 'workspace-a',
50
+ platform: 'local',
51
+ route: { type: 'local.thread', channelId: 'workspace-a', threadId: 'thread-1' },
52
+ executionMode: 'side-thread',
53
+ triggerType: 'cron',
54
+ cronExpr: '30 18 * * *',
55
+ promptTemplate: 'ping',
56
+ description: 'daily ping',
57
+ enabled: true,
58
+ });
59
+ strict_1.default.match(created.id, /^[0-9a-f]{8}$/);
60
+ const legacy = store.createScheduledJob({
61
+ workspaceId: 'workspace-a',
62
+ platform: 'local',
63
+ route: { type: 'local.thread', channelId: 'workspace-a', threadId: 'thread-1' },
64
+ executionMode: 'side-thread',
65
+ triggerType: 'cron',
66
+ cronExpr: '0 9 * * *',
67
+ promptTemplate: 'legacy',
68
+ description: 'legacy job',
69
+ enabled: true,
70
+ });
71
+ store.db.prepare('UPDATE scheduled_jobs SET id = ? WHERE id = ?').run('job:826aff79-570b-4308-822e-18318e2c96ba', legacy.id);
72
+ strict_1.default.equal(scheduler.getJob('826aff79')?.id, 'job:826aff79-570b-4308-822e-18318e2c96ba');
73
+ scheduler.updateJob('826aff79', { description: 'updated legacy job' });
74
+ strict_1.default.equal(scheduler.getJob('826aff79')?.description, 'updated legacy job');
75
+ scheduler.deleteJob('826aff79');
76
+ strict_1.default.equal(scheduler.getJob('826aff79'), undefined);
77
+ strict_1.default.throws(() => scheduler.deleteJob('826aff79'), /Scheduled job not found: 826aff79/);
78
+ store.close();
79
+ }
80
+ finally {
81
+ (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
82
+ }
83
+ });
84
+ (0, node_test_1.default)('scheduled jobs normalize enum-like input before persistence', () => {
85
+ const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-enum-store-'));
86
+ try {
87
+ const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
88
+ const created = store.createScheduledJob({
89
+ workspaceId: 'workspace-a',
90
+ platform: ' Lark ',
91
+ route: { type: 'channel.chat', channelId: 'chat-1', participantId: 'user-1' },
92
+ executionMode: 'side_thread',
93
+ triggerType: 'CRON',
94
+ cronExpr: '30 18 * * *',
95
+ promptTemplate: 'ping',
96
+ description: 'daily ping',
97
+ enabled: true,
98
+ });
99
+ strict_1.default.equal(created.platform, 'lark');
100
+ strict_1.default.equal(created.executionMode, 'side-thread');
101
+ strict_1.default.equal(created.triggerType, 'cron');
102
+ const updated = store.updateScheduledJob(created.id, {
103
+ executionMode: 'same_thread',
104
+ triggerType: 'one time',
105
+ runAt: '2026-05-04T10:00:00.000Z',
106
+ cronExpr: '',
107
+ });
108
+ strict_1.default.equal(updated.executionMode, 'same-thread');
109
+ strict_1.default.equal(updated.triggerType, 'once');
110
+ strict_1.default.equal(updated.runAt, '2026-05-04T10:00:00.000Z');
111
+ strict_1.default.equal(updated.cronExpr, undefined);
112
+ store.close();
113
+ }
114
+ finally {
115
+ (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
116
+ }
117
+ });
37
118
  (0, node_test_1.default)('agent tasks persist, update status, and can be found by run id', () => {
38
119
  const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agent-task-store-'));
39
120
  try {
@@ -64,3 +145,46 @@ const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/loc
64
145
  (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
65
146
  }
66
147
  });
148
+ (0, node_test_1.default)('agent task and run statuses normalize before persistence', () => {
149
+ const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'task-run-status-store-'));
150
+ try {
151
+ const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
152
+ const task = store.createAgentTask({
153
+ workspaceId: 'workspace-a',
154
+ deviceId: 'device-a',
155
+ runtimeId: 'runtime-a',
156
+ title: 'Normalize states',
157
+ status: 'waiting for user',
158
+ });
159
+ strict_1.default.equal(task.status, 'waiting_for_user');
160
+ strict_1.default.equal(store.listAgentTasks({ status: 'waiting for user' }).tasks.length, 1);
161
+ const updated = store.updateAgentTask(task.taskId, { status: 'canceled' });
162
+ strict_1.default.equal(updated.status, 'cancelled');
163
+ strict_1.default.equal(updated.timeline.at(-1)?.status, 'cancelled');
164
+ const thread = store.createThread('workspace-a', 'Thread');
165
+ store.updateRun('run-1', thread.id, 'awaiting input');
166
+ strict_1.default.equal(store.getRun('run-1')?.status, 'awaiting_input');
167
+ store.updateRun('run-1', thread.id, 'canceled');
168
+ strict_1.default.equal(store.getRun('run-1')?.status, 'interrupted');
169
+ const job = store.createScheduledJob({
170
+ workspaceId: 'workspace-a',
171
+ platform: 'local',
172
+ route: { type: 'local.thread', channelId: 'workspace-a', threadId: 'thread-1' },
173
+ executionMode: 'same-thread',
174
+ triggerType: 'cron',
175
+ cronExpr: '0 9 * * *',
176
+ promptTemplate: 'ping',
177
+ description: 'daily ping',
178
+ enabled: true,
179
+ });
180
+ const run = store.createScheduledJobRun(job.id, 'complete');
181
+ strict_1.default.equal(run.status, 'succeeded');
182
+ const skipped = store.updateScheduledJobRun(run.id, { status: 'cancelled' });
183
+ strict_1.default.equal(skipped.status, 'skipped');
184
+ strict_1.default.equal(store.getScheduledJob(job.id)?.lastStatus, 'skipped');
185
+ store.close();
186
+ }
187
+ finally {
188
+ (0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
189
+ }
190
+ });
@@ -1,2 +1,112 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeApprovalRequestStatus = normalizeApprovalRequestStatus;
4
+ exports.normalizeAgentTaskStatus = normalizeAgentTaskStatus;
5
+ exports.normalizeChannelContentPartType = normalizeChannelContentPartType;
6
+ exports.normalizeScheduledJobExecutionMode = normalizeScheduledJobExecutionMode;
7
+ exports.normalizeScheduledJobTriggerType = normalizeScheduledJobTriggerType;
8
+ exports.normalizeChannelPlatform = normalizeChannelPlatform;
9
+ exports.normalizeRunStatus = normalizeRunStatus;
10
+ exports.normalizeScheduledJobRunStatus = normalizeScheduledJobRunStatus;
11
+ function normalizeApprovalRequestStatus(value, fallback = 'pending') {
12
+ const normalized = normalizeContractEnumValue(value || fallback).replace(/-/g, '_');
13
+ if (normalized === 'pending' ||
14
+ normalized === 'approved' ||
15
+ normalized === 'rejected' ||
16
+ normalized === 'cancelled' ||
17
+ normalized === 'expired') {
18
+ return normalized;
19
+ }
20
+ if (normalized === 'approve') {
21
+ return 'approved';
22
+ }
23
+ if (normalized === 'reject') {
24
+ return 'rejected';
25
+ }
26
+ if (normalized === 'canceled') {
27
+ return 'cancelled';
28
+ }
29
+ throw new Error('Approval request status must be pending, approved, rejected, cancelled, or expired.');
30
+ }
31
+ function normalizeAgentTaskStatus(value, fallback = 'created') {
32
+ const normalized = normalizeContractEnumValue(value || fallback).replace(/-/g, '_');
33
+ if (normalized === 'created' ||
34
+ normalized === 'queued' ||
35
+ normalized === 'running' ||
36
+ normalized === 'waiting_for_user' ||
37
+ normalized === 'completed' ||
38
+ normalized === 'failed' ||
39
+ normalized === 'cancelled') {
40
+ return normalized;
41
+ }
42
+ if (normalized === 'canceled') {
43
+ return 'cancelled';
44
+ }
45
+ throw new Error('Agent task status must be created, queued, running, waiting_for_user, completed, failed, or cancelled.');
46
+ }
47
+ function normalizeChannelContentPartType(value) {
48
+ const normalized = normalizeContractEnumValue(value).replace(/-/g, '_');
49
+ if (normalized === 'text' ||
50
+ normalized === 'image' ||
51
+ normalized === 'file' ||
52
+ normalized === 'permission_card') {
53
+ return normalized;
54
+ }
55
+ throw new Error('Channel content part type must be text, image, file, or permission_card.');
56
+ }
57
+ function normalizeContractEnumValue(value) {
58
+ return String(value || '').trim().toLowerCase().replace(/[\s_]+/g, '-');
59
+ }
60
+ function normalizeScheduledJobExecutionMode(value, fallback = 'same-thread') {
61
+ const normalized = normalizeContractEnumValue(value || fallback);
62
+ if (normalized === 'same-thread' || normalized === 'side-thread') {
63
+ return normalized;
64
+ }
65
+ throw new Error('Scheduled job execution mode must be same-thread or side-thread.');
66
+ }
67
+ function normalizeScheduledJobTriggerType(value, fallback = 'cron') {
68
+ const normalized = normalizeContractEnumValue(value || fallback);
69
+ if (normalized === 'cron' || normalized === 'once' || normalized === 'one-time') {
70
+ return normalized === 'one-time' ? 'once' : normalized;
71
+ }
72
+ throw new Error('Scheduled job trigger type must be cron or once.');
73
+ }
74
+ function normalizeChannelPlatform(value) {
75
+ const normalized = String(value || '').trim().toLowerCase();
76
+ if (!normalized) {
77
+ throw new Error('Channel platform is required.');
78
+ }
79
+ return normalized;
80
+ }
81
+ function normalizeRunStatus(value, fallback = 'queued') {
82
+ const normalized = normalizeContractEnumValue(value || fallback).replace(/-/g, '_');
83
+ if (normalized === 'queued' ||
84
+ normalized === 'running' ||
85
+ normalized === 'awaiting_input' ||
86
+ normalized === 'completed' ||
87
+ normalized === 'failed' ||
88
+ normalized === 'interrupted') {
89
+ return normalized;
90
+ }
91
+ if (normalized === 'cancelled' || normalized === 'canceled') {
92
+ return 'interrupted';
93
+ }
94
+ throw new Error('Run status must be queued, running, awaiting_input, completed, failed, or interrupted.');
95
+ }
96
+ function normalizeScheduledJobRunStatus(value, fallback = 'queued') {
97
+ const normalized = normalizeContractEnumValue(value || fallback).replace(/-/g, '_');
98
+ if (normalized === 'queued' ||
99
+ normalized === 'running' ||
100
+ normalized === 'succeeded' ||
101
+ normalized === 'failed' ||
102
+ normalized === 'skipped') {
103
+ return normalized;
104
+ }
105
+ if (normalized === 'complete' || normalized === 'completed' || normalized === 'success') {
106
+ return 'succeeded';
107
+ }
108
+ if (normalized === 'cancelled' || normalized === 'canceled') {
109
+ return 'skipped';
110
+ }
111
+ throw new Error('Scheduled job run status must be queued, running, succeeded, failed, or skipped.');
112
+ }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeThreadMessageInput = normalizeThreadMessageInput;
4
4
  exports.createTextMessageInput = createTextMessageInput;
5
+ const index_js_1 = require("../../../../packages/contracts/src/index.js");
5
6
  function normalizeThreadMessageInput(input) {
6
7
  if (typeof input === 'string') {
7
8
  return {
@@ -32,7 +33,13 @@ function isSupportedContentPart(part) {
32
33
  if (!part || typeof part !== 'object') {
33
34
  return false;
34
35
  }
35
- const type = String(part.type || '');
36
+ let type;
37
+ try {
38
+ type = (0, index_js_1.normalizeChannelContentPartType)(part.type);
39
+ }
40
+ catch {
41
+ return false;
42
+ }
36
43
  if (type === 'text') {
37
44
  return Boolean(String(part.text || ''));
38
45
  }
@@ -40,6 +47,10 @@ function isSupportedContentPart(part) {
40
47
  const image = part;
41
48
  return Boolean(String(image.data || image.uri || ''));
42
49
  }
50
+ if (type === 'file') {
51
+ const file = part;
52
+ return Boolean(String(file.data || file.uri || file.path || ''));
53
+ }
43
54
  return false;
44
55
  }
45
56
  function summarizeContentParts(parts) {
@@ -52,9 +63,19 @@ function summarizeContentParts(parts) {
52
63
  if (text) {
53
64
  return text;
54
65
  }
55
- const imageCount = parts.filter((part) => part.type === 'image').length;
56
- if (imageCount > 0) {
57
- return imageCount === 1 ? '[Image]' : `[${imageCount} images]`;
66
+ const summaries = parts
67
+ .map((part) => {
68
+ if (part.type === 'image') {
69
+ return part.fileName ? `[Image: ${part.fileName}]` : '[Image]';
70
+ }
71
+ if (part.type === 'file') {
72
+ return part.fileName ? `[File: ${part.fileName}]` : '[File]';
73
+ }
74
+ return '';
75
+ })
76
+ .filter(Boolean);
77
+ if (summaries.length > 0) {
78
+ return summaries.join('\n');
58
79
  }
59
80
  return '';
60
81
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalCoreAcpResponseProcessor = void 0;
4
4
  const cron_command_detector_js_1 = require("../scheduler/cron-command-detector.js");
5
+ const job_id_js_1 = require("../scheduler/job-id.js");
5
6
  class LocalCoreAcpResponseProcessor {
6
7
  options;
7
8
  constructor(options) {
@@ -82,7 +83,7 @@ class LocalCoreAcpResponseProcessor {
82
83
  scheduleDescription: command.scheduleDescription,
83
84
  message: command.message,
84
85
  });
85
- return `已创建定时任务:${job.description || command.name},计划 ${command.scheduleDescription}(${command.schedule}),ID: ${job.id}`;
86
+ return `已创建定时任务:${job.description || command.name},计划 ${command.scheduleDescription}(${command.schedule}),ID: ${(0, job_id_js_1.toPublicScheduledJobId)(job.id)}`;
86
87
  }
87
88
  case 'list': {
88
89
  const jobs = await this.options.scheduler.listJobsForThread(threadId);
@@ -91,7 +92,7 @@ class LocalCoreAcpResponseProcessor {
91
92
  }
92
93
  return [
93
94
  '当前对话定时任务:',
94
- ...jobs.map((job) => `- ${job.description || job.id} | ${job.triggerType === 'cron' ? job.cronExpr : job.runAt} | ${job.enabled ? 'enabled' : 'disabled'} | ${job.id}`),
95
+ ...jobs.map((job) => `- ${job.description || (0, job_id_js_1.toPublicScheduledJobId)(job.id)} | ${job.triggerType === 'cron' ? job.cronExpr : job.runAt} | ${job.enabled ? 'enabled' : 'disabled'} | ${(0, job_id_js_1.toPublicScheduledJobId)(job.id)}`),
95
96
  ].join('\n');
96
97
  }
97
98
  case 'delete': {
@@ -6,6 +6,8 @@ const node_crypto_1 = require("node:crypto");
6
6
  const node_fs_1 = require("node:fs");
7
7
  const node_sqlite_1 = require("node:sqlite");
8
8
  const node_path_1 = require("node:path");
9
+ const index_js_1 = require("../../../../packages/contracts/src/index.js");
10
+ const job_id_js_1 = require("../scheduler/job-id.js");
9
11
  const desktop_js_1 = require("../../../../shared/desktop.js");
10
12
  const workspace_thread_mappers_js_1 = require("../thread/workspace-thread-mappers.js");
11
13
  const workspace_thread_id_js_1 = require("../thread/workspace-thread-id.js");
@@ -378,31 +380,34 @@ class LocalCoreAcpStore {
378
380
  }
379
381
  updateRun(runId, threadId, status) {
380
382
  const now = new Date().toISOString();
383
+ const normalizedStatus = (0, index_js_1.normalizeRunStatus)(status);
381
384
  const existing = this.db.prepare('SELECT id FROM runs WHERE id = ?').get(runId);
382
385
  if (existing) {
383
- this.db.prepare('UPDATE runs SET status = ?, updated_at = ? WHERE id = ?').run(status, now, runId);
386
+ this.db.prepare('UPDATE runs SET status = ?, updated_at = ? WHERE id = ?').run(normalizedStatus, now, runId);
384
387
  return;
385
388
  }
386
389
  this.db.prepare(`
387
390
  INSERT INTO runs (id, thread_id, status, started_at, updated_at)
388
391
  VALUES (?, ?, ?, ?, ?)
389
- `).run(runId, threadId, status, now, now);
392
+ `).run(runId, threadId, normalizedStatus, now, now);
390
393
  }
391
394
  getLatestRunForThread(threadId) {
392
- return this.db.prepare(`
395
+ const row = this.db.prepare(`
393
396
  SELECT id, thread_id, status, started_at, updated_at
394
397
  FROM runs
395
398
  WHERE thread_id = ?
396
399
  ORDER BY updated_at DESC
397
400
  LIMIT 1
398
401
  `).get(threadId);
402
+ return row ? { ...row, status: (0, index_js_1.normalizeRunStatus)(row.status) } : undefined;
399
403
  }
400
404
  getRun(runId) {
401
- return this.db.prepare(`
405
+ const row = this.db.prepare(`
402
406
  SELECT id, thread_id, status, started_at, updated_at
403
407
  FROM runs
404
408
  WHERE id = ?
405
409
  `).get(runId);
410
+ return row ? { ...row, status: (0, index_js_1.normalizeRunStatus)(row.status) } : undefined;
406
411
  }
407
412
  listWorkspaceRegistry() {
408
413
  const rows = this.db.prepare(`
@@ -573,7 +578,7 @@ class LocalCoreAcpStore {
573
578
  params.push(query.taskId);
574
579
  }
575
580
  if (query.status) {
576
- const statuses = Array.isArray(query.status) ? query.status : [query.status];
581
+ const statuses = (Array.isArray(query.status) ? query.status : [query.status]).map((status) => (0, index_js_1.normalizeApprovalRequestStatus)(status));
577
582
  predicates.push(`status IN (${statuses.map(() => '?').join(', ')})`);
578
583
  params.push(...statuses);
579
584
  }
@@ -669,7 +674,7 @@ class LocalCoreAcpStore {
669
674
  createAgentTask(input) {
670
675
  const id = `task:${(0, node_crypto_1.randomUUID)()}`;
671
676
  const now = new Date().toISOString();
672
- const status = input.status || 'created';
677
+ const status = (0, index_js_1.normalizeAgentTaskStatus)(input.status);
673
678
  const timeline = [{
674
679
  id: `timeline:${(0, node_crypto_1.randomUUID)()}`,
675
680
  type: 'status_change',
@@ -706,7 +711,7 @@ class LocalCoreAcpStore {
706
711
  params.push(query.runtimeId);
707
712
  }
708
713
  if (query.status) {
709
- const statuses = Array.isArray(query.status) ? query.status : [query.status];
714
+ const statuses = (Array.isArray(query.status) ? query.status : [query.status]).map((status) => (0, index_js_1.normalizeAgentTaskStatus)(status));
710
715
  predicates.push(`status IN (${statuses.map(() => '?').join(', ')})`);
711
716
  params.push(...statuses);
712
717
  }
@@ -735,17 +740,17 @@ class LocalCoreAcpStore {
735
740
  throw new Error(`Task not found: ${taskId}`);
736
741
  }
737
742
  const now = new Date().toISOString();
738
- const nextStatus = input.status || existing.status;
743
+ const nextStatus = input.status ? (0, index_js_1.normalizeAgentTaskStatus)(input.status) : existing.status;
739
744
  const timeline = [...existing.timeline];
740
745
  const logs = [...existing.logs];
741
746
  const artifacts = [...existing.artifacts];
742
747
  const approvalIds = [...existing.approvalIds];
743
- if (input.status && input.status !== existing.status) {
748
+ if (input.status && nextStatus !== existing.status) {
744
749
  timeline.push({
745
750
  id: `timeline:${(0, node_crypto_1.randomUUID)()}`,
746
751
  type: 'status_change',
747
- title: `Task ${input.status}`,
748
- status: input.status,
752
+ title: `Task ${nextStatus}`,
753
+ status: nextStatus,
749
754
  timestamp: now,
750
755
  });
751
756
  }
@@ -780,14 +785,14 @@ class LocalCoreAcpStore {
780
785
  WHERE id = ?
781
786
  `).run(nextStatus, input.threadId || existing.threadId || null, input.runId || existing.runId || null, input.title || existing.title, now, existing.queuedAt || (nextStatus === 'queued' ? now : null), existing.startedAt || (nextStatus === 'running' ? now : null), existing.completedAt || (['completed', 'failed', 'cancelled'].includes(nextStatus) ? now : null), input.summary ?? existing.summary ?? null, input.error === null ? null : input.error ?? existing.error ?? null, JSON.stringify(timeline), JSON.stringify(logs), JSON.stringify(artifacts), JSON.stringify(approvalIds), JSON.stringify(input.metadata || existing.metadata || {}), taskId);
782
787
  const task = this.getAgentTask(taskId);
783
- if (input.status && input.status !== existing.status) {
788
+ if (input.status && nextStatus !== existing.status) {
784
789
  this.createAuditEvent({
785
790
  type: 'task.updated',
786
791
  workspaceId: task.workspaceId,
787
792
  taskId: task.taskId,
788
793
  actor: 'local',
789
- summary: `Task status changed to ${input.status}.`,
790
- metadata: { previousStatus: existing.status, status: input.status, runId: task.runId },
794
+ summary: `Task status changed to ${nextStatus}.`,
795
+ metadata: { previousStatus: existing.status, status: nextStatus, runId: task.runId },
791
796
  });
792
797
  }
793
798
  return task;
@@ -820,14 +825,20 @@ class LocalCoreAcpStore {
820
825
  return row ? this.toScheduledJob(row) : undefined;
821
826
  }
822
827
  createScheduledJob(input) {
823
- const id = `job:${(0, node_crypto_1.randomUUID)()}`;
828
+ let id = (0, job_id_js_1.createScheduledJobId)();
829
+ for (let attempt = 0; attempt < 5 && this.getScheduledJob(id); attempt += 1) {
830
+ id = (0, job_id_js_1.createScheduledJobId)();
831
+ }
832
+ if (this.getScheduledJob(id)) {
833
+ throw new Error('Unable to allocate a unique scheduled job id.');
834
+ }
824
835
  const now = new Date().toISOString();
825
836
  this.db.prepare(`
826
837
  INSERT INTO scheduled_jobs (
827
838
  id, workspace_id, platform, route_type, route_config, execution_mode, trigger_type, cron_expr, run_at,
828
839
  prompt_template, description, enabled, concurrency_policy, created_at, updated_at
829
840
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'skip_if_running', ?, ?)
830
- `).run(id, input.workspaceId, input.platform, input.route.type, JSON.stringify(input.route), input.executionMode || 'same-thread', input.triggerType, input.cronExpr || null, input.runAt || null, input.promptTemplate, input.description || '', input.enabled === false ? 0 : 1, now, now);
841
+ `).run(id, input.workspaceId, (0, index_js_1.normalizeChannelPlatform)(input.platform), input.route.type, JSON.stringify(input.route), (0, index_js_1.normalizeScheduledJobExecutionMode)(input.executionMode), (0, index_js_1.normalizeScheduledJobTriggerType)(input.triggerType), input.cronExpr || null, input.runAt || null, input.promptTemplate, input.description || '', input.enabled === false ? 0 : 1, now, now);
831
842
  return this.getScheduledJob(id);
832
843
  }
833
844
  updateScheduledJob(jobId, input) {
@@ -838,8 +849,8 @@ class LocalCoreAcpStore {
838
849
  const next = {
839
850
  ...existing,
840
851
  ...(input.route ? { route: input.route } : {}),
841
- ...(input.executionMode ? { executionMode: input.executionMode } : {}),
842
- ...(input.triggerType ? { triggerType: input.triggerType } : {}),
852
+ ...(input.executionMode ? { executionMode: (0, index_js_1.normalizeScheduledJobExecutionMode)(input.executionMode) } : {}),
853
+ ...(input.triggerType ? { triggerType: (0, index_js_1.normalizeScheduledJobTriggerType)(input.triggerType) } : {}),
843
854
  ...(Object.prototype.hasOwnProperty.call(input, 'cronExpr') ? { cronExpr: input.cronExpr || undefined } : {}),
844
855
  ...(Object.prototype.hasOwnProperty.call(input, 'runAt') ? { runAt: input.runAt || undefined } : {}),
845
856
  ...(typeof input.promptTemplate === 'string' ? { promptTemplate: input.promptTemplate } : {}),
@@ -856,7 +867,10 @@ class LocalCoreAcpStore {
856
867
  return this.getScheduledJob(jobId);
857
868
  }
858
869
  deleteScheduledJob(jobId) {
859
- this.db.prepare('DELETE FROM scheduled_jobs WHERE id = ?').run(jobId);
870
+ const result = this.db.prepare('DELETE FROM scheduled_jobs WHERE id = ?').run(jobId);
871
+ if (result.changes === 0) {
872
+ throw new Error(`Scheduled job not found: ${jobId}`);
873
+ }
860
874
  return { deleted: true };
861
875
  }
862
876
  listScheduledJobRuns(jobId) {
@@ -871,13 +885,14 @@ class LocalCoreAcpStore {
871
885
  createScheduledJobRun(jobId, status, input = {}) {
872
886
  const id = `jobrun:${(0, node_crypto_1.randomUUID)()}`;
873
887
  const triggeredAt = input.triggeredAt || new Date().toISOString();
888
+ const normalizedStatus = (0, index_js_1.normalizeScheduledJobRunStatus)(status);
874
889
  this.db.prepare(`
875
890
  INSERT INTO scheduled_job_runs (id, job_id, status, triggered_at, started_at, finished_at, error, thread_id, run_id, platform_message_id)
876
891
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
877
- `).run(id, jobId, status, triggeredAt, input.startedAt || null, input.finishedAt || null, input.error || null, input.threadId || null, input.runId || null, input.platformMessageId || null);
892
+ `).run(id, jobId, normalizedStatus, triggeredAt, input.startedAt || null, input.finishedAt || null, input.error || null, input.threadId || null, input.runId || null, input.platformMessageId || null);
878
893
  this.updateScheduledJobStatus(jobId, {
879
894
  lastRunAt: triggeredAt,
880
- lastStatus: status,
895
+ lastStatus: normalizedStatus,
881
896
  lastError: input.error || '',
882
897
  });
883
898
  return this.getScheduledJobRun(id);
@@ -898,6 +913,7 @@ class LocalCoreAcpStore {
898
913
  const next = {
899
914
  ...existing,
900
915
  ...input,
916
+ ...(input.status ? { status: (0, index_js_1.normalizeScheduledJobRunStatus)(input.status) } : {}),
901
917
  };
902
918
  this.db.prepare(`
903
919
  UPDATE scheduled_job_runs
@@ -1113,10 +1129,10 @@ class LocalCoreAcpStore {
1113
1129
  return {
1114
1130
  id: row.id,
1115
1131
  workspaceId: row.workspace_id,
1116
- platform: row.platform,
1132
+ platform: (0, index_js_1.normalizeChannelPlatform)(row.platform),
1117
1133
  route,
1118
- executionMode: row.execution_mode || 'same-thread',
1119
- triggerType: row.trigger_type,
1134
+ executionMode: (0, index_js_1.normalizeScheduledJobExecutionMode)(row.execution_mode),
1135
+ triggerType: (0, index_js_1.normalizeScheduledJobTriggerType)(row.trigger_type),
1120
1136
  cronExpr: row.cron_expr || undefined,
1121
1137
  runAt: row.run_at || undefined,
1122
1138
  promptTemplate: row.prompt_template,
@@ -1126,7 +1142,7 @@ class LocalCoreAcpStore {
1126
1142
  createdAt: row.created_at,
1127
1143
  updatedAt: row.updated_at,
1128
1144
  lastRunAt: row.last_run_at || undefined,
1129
- lastStatus: row.last_status || undefined,
1145
+ lastStatus: row.last_status ? (0, index_js_1.normalizeScheduledJobRunStatus)(row.last_status) : undefined,
1130
1146
  lastError: row.last_error || undefined,
1131
1147
  };
1132
1148
  }
@@ -1134,7 +1150,7 @@ class LocalCoreAcpStore {
1134
1150
  return {
1135
1151
  id: row.id,
1136
1152
  jobId: row.job_id,
1137
- status: row.status,
1153
+ status: (0, index_js_1.normalizeScheduledJobRunStatus)(row.status),
1138
1154
  triggeredAt: row.triggered_at,
1139
1155
  startedAt: row.started_at || undefined,
1140
1156
  finishedAt: row.finished_at || undefined,
@@ -1187,7 +1203,7 @@ class LocalCoreAcpStore {
1187
1203
  runId: row.run_id || undefined,
1188
1204
  title: row.title,
1189
1205
  prompt: row.prompt || undefined,
1190
- status: row.status,
1206
+ status: (0, index_js_1.normalizeAgentTaskStatus)(row.status),
1191
1207
  createdAt: row.created_at,
1192
1208
  updatedAt: row.updated_at,
1193
1209
  queuedAt: row.queued_at || undefined,
@@ -1224,7 +1240,7 @@ class LocalCoreAcpStore {
1224
1240
  runId: row.run_id || undefined,
1225
1241
  deviceId: row.device_id,
1226
1242
  kind: row.kind,
1227
- status: row.status,
1243
+ status: (0, index_js_1.normalizeApprovalRequestStatus)(row.status),
1228
1244
  riskLevel: row.risk_level,
1229
1245
  title: row.title,
1230
1246
  description: row.description,
@@ -4,18 +4,9 @@ exports.normalizePermissionAction = normalizePermissionAction;
4
4
  exports.normalizePermissionOptionAction = normalizePermissionOptionAction;
5
5
  exports.formatToolCallContent = formatToolCallContent;
6
6
  exports.toPermissionButtonRows = toPermissionButtonRows;
7
+ const desktop_js_1 = require("../../../../shared/desktop.js");
7
8
  function normalizePermissionAction(kind) {
8
- const normalized = String(kind || '').trim().toLowerCase();
9
- if (isAllowAllPermissionToken(normalized)) {
10
- return 'allow all';
11
- }
12
- if (isAllowPermissionToken(normalized)) {
13
- return 'allow';
14
- }
15
- if (isDenyPermissionToken(normalized)) {
16
- return 'deny';
17
- }
18
- return '';
9
+ return (0, desktop_js_1.normalizePermissionResponse)(kind) || '';
19
10
  }
20
11
  function normalizePermissionOptionAction(option) {
21
12
  const fields = [
@@ -23,51 +14,17 @@ function normalizePermissionOptionAction(option) {
23
14
  option?.name,
24
15
  option?.optionId,
25
16
  ].map((value) => String(value || '').trim().toLowerCase()).filter(Boolean);
26
- if (fields.some(isDenyPermissionToken)) {
17
+ if (fields.some((field) => normalizePermissionAction(field) === 'deny')) {
27
18
  return 'deny';
28
19
  }
29
- if (fields.some(isAllowAllPermissionToken)) {
20
+ if (fields.some((field) => normalizePermissionAction(field) === 'allow all')) {
30
21
  return 'allow all';
31
22
  }
32
- if (fields.some(isAllowPermissionToken)) {
23
+ if (fields.some((field) => normalizePermissionAction(field) === 'allow')) {
33
24
  return 'allow';
34
25
  }
35
26
  return '';
36
27
  }
37
- function isAllowAllPermissionToken(normalized) {
38
- if (normalized === 'allow_always' ||
39
- normalized === 'allow_all' ||
40
- normalized === 'allowall' ||
41
- normalized === 'always' ||
42
- normalized === 'always_allow' ||
43
- normalized === 'alwaysallow' ||
44
- normalized.includes('allow_all') ||
45
- normalized.includes('allow-always') ||
46
- normalized.includes('allow_always') ||
47
- normalized.includes('allow always') ||
48
- normalized.includes('always allow') ||
49
- normalized.includes('allow all') ||
50
- normalized.includes('始终允许') ||
51
- normalized.includes('永久允许')) {
52
- return true;
53
- }
54
- return false;
55
- }
56
- function isAllowPermissionToken(normalized) {
57
- if (normalized.startsWith('allow')) {
58
- return true;
59
- }
60
- return false;
61
- }
62
- function isDenyPermissionToken(normalized) {
63
- if (normalized.startsWith('reject')) {
64
- return true;
65
- }
66
- if (normalized.startsWith('deny')) {
67
- return true;
68
- }
69
- return false;
70
- }
71
28
  function formatToolCallContent(toolCall) {
72
29
  if (!toolCall || typeof toolCall !== 'object') {
73
30
  return 'Permission required before continuing.';