@kafca/agentdock 0.1.23 → 0.1.25

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 (93) hide show
  1. package/README.md +3 -0
  2. package/dist/renderer/assets/{Badge-C2K5apfV.js → Badge-Bn2h-Qkf.js} +1 -1
  3. package/dist/renderer/assets/{Chat-DI2YeVKb.js → Chat-Cgo9aneg.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-DMAr15Oi.js → ChatMarkdown-CYzHfSiR.js} +1 -1
  5. package/dist/renderer/assets/{Config-7rP3EuUf.js → Config-DljB2-eP.js} +1 -1
  6. package/dist/renderer/assets/{CronList-81QslYE8.js → CronList-DeVbiY9a.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-Db2T7rSZ.js → Dashboard-fSqx5sm8.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-BBJ3cTvB.js → EmptyState-DgqKDwlq.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-Dz_6flG3.js → KnowledgeDetail-u8s2SmPV.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-DIh046mN.js → KnowledgeHome-BdQw8nbY.js} +1 -1
  11. package/dist/renderer/assets/{Logs-BXBtHJLf.js → Logs-DSbndMpy.js} +1 -1
  12. package/dist/renderer/assets/{Modal-BB2Av_Xs.js → Modal-0d0EmAuK.js} +1 -1
  13. package/dist/renderer/assets/{Page-DPSdvMgU.js → Page-CVbOH_Kh.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-C6EWGvGg.js → ProjectDetail-C91tF7uA.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-BBo5ygnS.js → ProjectList-D78Pc-BQ.js} +1 -1
  16. package/dist/renderer/assets/{Select-CNX-Tx_O.js → Select-BbKzAIuO.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-BIn46jqZ.js → SessionChat-BERvL5e4.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-CZt6f1Rd.js → SessionList-Bomz335M.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-CLr3amU2.js +23 -0
  20. package/dist/renderer/assets/{Workspace-C_4tdpjX.js → Workspace-In91z4W2.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-cHr5wkpq.js → arrow-left-FBnVKKBl.js} +1 -1
  22. package/dist/renderer/assets/{book-open-4nx5C8T-.js → book-open-4_CuP6Sa.js} +1 -1
  23. package/dist/renderer/assets/{check-exTS-R-x.js → check-vsoVjvac.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-DSvxXQa0.js → chevron-down-DFpmFTJW.js} +1 -1
  25. package/dist/renderer/assets/{circle-DyvEovdw.js → circle-vRULegER.js} +1 -1
  26. package/dist/renderer/assets/desktop-BkMW-h5p.js +4 -0
  27. package/dist/renderer/assets/{desktop-B-KVT10D.js → desktop-ul0Tpzyc.js} +1 -1
  28. package/dist/renderer/assets/{funnel-DA9qVdTy.js → funnel-Gu0E_hJ_.js} +1 -1
  29. package/dist/renderer/assets/{heart-DuEV0ZRJ.js → heart-DsdofOHf.js} +1 -1
  30. package/dist/renderer/assets/{index-Biz-aexG.js → index-BD3aCyBa.js} +3 -3
  31. package/dist/renderer/assets/{index-BgflqkCN.js → index-pJUFobU7.js} +1 -1
  32. package/dist/renderer/assets/{pencil-DBDxeNmO.js → pencil-DbKmV8nD.js} +1 -1
  33. package/dist/renderer/assets/{play-IzlP83_7.js → play-BDMsRxSm.js} +1 -1
  34. package/dist/renderer/assets/{plug-CuT-qFnW.js → plug-t2dMl78-.js} +1 -1
  35. package/dist/renderer/assets/{plus-DEqgkn63.js → plus-BUb6zOAe.js} +1 -1
  36. package/dist/renderer/assets/projects-4F6D_Q8t.js +1 -0
  37. package/dist/renderer/assets/{save-D4sRo07y.js → save-C-_5rQdv.js} +1 -1
  38. package/dist/renderer/assets/{search-DbPZLuV8.js → search-R3ekHFPM.js} +1 -1
  39. package/dist/renderer/assets/{server-D1jUQmwN.js → server-BTuvHf5n.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-Aife8ep2.js → session-utils-ClX6VPFG.js} +1 -1
  41. package/dist/renderer/assets/{sessions-DaH3H7gC.js → sessions-AFKIC-ub.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-BGwUFWNr.js → shield-check-CqMW8CHe.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-CIFBioBa.js → trash-2-BNLcHyUw.js} +1 -1
  44. package/dist/renderer/assets/{user-CnKcvHdo.js → user-BCCN8FYK.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-ClO53WGa.js → wifi-off-BsNsx3oX.js} +1 -1
  46. package/dist/renderer/index.html +1 -1
  47. package/dist-electron/electron/agent-runtime-detector.test.js +64 -0
  48. package/dist-electron/electron/architecture-docs.test.js +56 -0
  49. package/dist-electron/electron/channel-content.test.js +37 -0
  50. package/dist-electron/electron/lac-cli.test.js +297 -4
  51. package/dist-electron/electron/local-core-acp-content.test.js +39 -0
  52. package/dist-electron/electron/local-core-contracts.test.js +67 -0
  53. package/dist-electron/electron/local-core-refactor.test.js +789 -3
  54. package/dist-electron/electron/plugin-kernel.test.js +108 -0
  55. package/dist-electron/electron/renderer-packaging.test.js +77 -0
  56. package/dist-electron/electron/workspace-task-store.test.js +181 -0
  57. package/dist-electron/packages/contracts/src/local-core.js +110 -0
  58. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +67 -6
  59. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +25 -4
  60. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-permission-lifecycle.js +76 -0
  61. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +201 -0
  62. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +3 -2
  63. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +29 -0
  64. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +74 -46
  65. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +112 -210
  66. package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +60 -0
  67. package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +5 -48
  68. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +83 -28
  69. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +11 -0
  70. package/dist-electron/services/local-ai-core/src/channel/shared/content.js +23 -0
  71. package/dist-electron/services/local-ai-core/src/channel/shared/file-utils.js +13 -1
  72. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +31 -29
  73. package/dist-electron/services/local-ai-core/src/cli/lac.js +22 -17
  74. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +1 -0
  75. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
  76. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +221 -10
  77. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +39 -9
  78. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +1 -0
  79. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +1 -0
  80. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +439 -0
  81. package/dist-electron/services/local-ai-core/src/runtime/server.js +208 -303
  82. package/dist-electron/services/local-ai-core/src/scheduler/job-id.js +15 -0
  83. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +30 -5
  84. package/dist-electron/shared/desktop.js +34 -8
  85. package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +187 -0
  86. package/dist-electron/src/pages/Threads/thread-chat-model.js +58 -0
  87. package/dist-electron/src/pages/Threads/thread-chat-page-state.js +96 -0
  88. package/dist-electron/src/pages/Threads/thread-chat-permission.js +18 -0
  89. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +347 -0
  90. package/package.json +4 -2
  91. package/dist/renderer/assets/ThreadChat-BotD0gKQ.js +0 -23
  92. package/dist/renderer/assets/desktop-CVibRyAw.js +0 -4
  93. package/dist/renderer/assets/projects-DDtDN1rc.js +0 -1
@@ -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");
@@ -29,7 +31,8 @@ class LocalCoreAcpStore {
29
31
  history_count INTEGER NOT NULL DEFAULT 0,
30
32
  excerpt TEXT NOT NULL DEFAULT '',
31
33
  acp_session_id TEXT,
32
- acp_supports_load INTEGER NOT NULL DEFAULT 0
34
+ acp_supports_load INTEGER NOT NULL DEFAULT 0,
35
+ agent_mode TEXT NOT NULL DEFAULT 'default'
33
36
  );
34
37
  CREATE INDEX IF NOT EXISTS idx_threads_workspace_updated ON threads (workspace_id, updated_at DESC);
35
38
  CREATE TABLE IF NOT EXISTS messages (
@@ -37,6 +40,7 @@ class LocalCoreAcpStore {
37
40
  thread_id TEXT NOT NULL,
38
41
  role TEXT NOT NULL,
39
42
  content TEXT NOT NULL,
43
+ tool_call_json TEXT,
40
44
  timestamp TEXT NOT NULL,
41
45
  kind TEXT NOT NULL,
42
46
  seq INTEGER NOT NULL,
@@ -217,14 +221,16 @@ class LocalCoreAcpStore {
217
221
  CREATE INDEX IF NOT EXISTS idx_audit_events_task_created ON audit_events (task_id, created_at DESC);
218
222
  CREATE INDEX IF NOT EXISTS idx_audit_events_type_created ON audit_events (type, created_at DESC);
219
223
  `);
224
+ this.ensureColumn('messages', 'tool_call_json', 'TEXT');
220
225
  this.ensureColumn('scheduled_jobs', 'execution_mode', "TEXT NOT NULL DEFAULT 'same-thread'");
226
+ this.ensureColumn('threads', 'agent_mode', "TEXT NOT NULL DEFAULT 'default'");
221
227
  }
222
228
  close() {
223
229
  this.db.close();
224
230
  }
225
231
  listThreadSummaries(workspaceId) {
226
232
  const rows = this.db.prepare(`
227
- SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt
233
+ SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, acp_session_id, acp_supports_load, agent_mode
228
234
  FROM threads
229
235
  WHERE workspace_id = ?
230
236
  ORDER BY updated_at DESC
@@ -247,15 +253,15 @@ class LocalCoreAcpStore {
247
253
  const row = this.db.prepare('SELECT COUNT(*) AS total FROM threads WHERE workspace_id = ?').get(workspaceId);
248
254
  return Number(row?.total || 0);
249
255
  }
250
- createThread(workspaceId, title, agentType = desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
256
+ createThread(workspaceId, title, agentType = desktop_js_1.LOCALCORE_ACP_AGENT_TYPE, agentMode = 'default') {
251
257
  const sessionId = (0, node_crypto_1.randomUUID)();
252
258
  const threadId = (0, workspace_thread_id_js_1.encodeThreadId)(workspaceId, sessionId);
253
259
  const now = new Date().toISOString();
254
260
  const bridgeSessionKey = `${desktop_js_1.LOCALCORE_ACP_AGENT_TYPE}:${workspaceId}:${sessionId}`;
255
261
  this.db.prepare(`
256
- INSERT INTO threads (id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt)
257
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, '')
258
- `).run(threadId, workspaceId, sessionId, bridgeSessionKey, title, agentType, now, now);
262
+ INSERT INTO threads (id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, agent_mode)
263
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, '', ?)
264
+ `).run(threadId, workspaceId, sessionId, bridgeSessionKey, title, agentType, now, now, agentMode || 'default');
259
265
  return {
260
266
  id: threadId,
261
267
  workspaceId,
@@ -278,7 +284,7 @@ class LocalCoreAcpStore {
278
284
  throw new Error(`Thread not found: ${threadId}`);
279
285
  }
280
286
  const messages = this.db.prepare(`
281
- SELECT id, thread_id, role, content, timestamp, kind, seq
287
+ SELECT id, thread_id, role, content, tool_call_json, timestamp, kind, seq
282
288
  FROM messages
283
289
  WHERE thread_id = ?
284
290
  ORDER BY seq ASC
@@ -298,6 +304,7 @@ class LocalCoreAcpStore {
298
304
  id: message.id,
299
305
  role: message.role,
300
306
  content: message.content,
307
+ toolCall: parseJson(message.tool_call_json || 'null', null) || undefined,
301
308
  timestamp: message.timestamp,
302
309
  kind: message.kind,
303
310
  })),
@@ -312,7 +319,7 @@ class LocalCoreAcpStore {
312
319
  deleteThread(threadId) {
313
320
  this.db.prepare('DELETE FROM threads WHERE id = ?').run(threadId);
314
321
  }
315
- appendMessage(threadId, role, content, kind) {
322
+ appendMessage(threadId, role, content, kind, toolCall) {
316
323
  const timestamp = new Date().toISOString();
317
324
  const nextSequenceRow = this.db.prepare('SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM messages WHERE thread_id = ?').get(threadId);
318
325
  const nextSeq = Number(nextSequenceRow?.next_seq || 0);
@@ -321,9 +328,9 @@ class LocalCoreAcpStore {
321
328
  this.db.exec('BEGIN IMMEDIATE');
322
329
  try {
323
330
  this.db.prepare(`
324
- INSERT INTO messages (id, thread_id, role, content, timestamp, kind, seq)
325
- VALUES (?, ?, ?, ?, ?, ?, ?)
326
- `).run(id, threadId, role, content, timestamp, kind, nextSeq);
331
+ INSERT INTO messages (id, thread_id, role, content, tool_call_json, timestamp, kind, seq)
332
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
333
+ `).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, nextSeq);
327
334
  this.db.prepare(`
328
335
  UPDATE threads
329
336
  SET updated_at = ?, history_count = history_count + 1, excerpt = ?
@@ -337,7 +344,7 @@ class LocalCoreAcpStore {
337
344
  }
338
345
  return { id, timestamp };
339
346
  }
340
- upsertMessage(threadId, id, role, content, kind) {
347
+ upsertMessage(threadId, id, role, content, kind, toolCall) {
341
348
  const timestamp = new Date().toISOString();
342
349
  const excerpt = (0, workspace_thread_mappers_js_1.normalizeMessageContent)(content);
343
350
  const existing = this.db.prepare('SELECT id FROM messages WHERE id = ? AND thread_id = ?').get(id, threadId);
@@ -346,17 +353,17 @@ class LocalCoreAcpStore {
346
353
  if (existing) {
347
354
  this.db.prepare(`
348
355
  UPDATE messages
349
- SET content = ?, timestamp = ?, kind = ?
356
+ SET content = ?, tool_call_json = ?, timestamp = ?, kind = ?
350
357
  WHERE id = ? AND thread_id = ?
351
- `).run(content, timestamp, kind, id, threadId);
358
+ `).run(content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, id, threadId);
352
359
  }
353
360
  else {
354
361
  const nextSequenceRow = this.db.prepare('SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM messages WHERE thread_id = ?').get(threadId);
355
362
  const nextSeq = Number(nextSequenceRow?.next_seq || 0);
356
363
  this.db.prepare(`
357
- INSERT INTO messages (id, thread_id, role, content, timestamp, kind, seq)
358
- VALUES (?, ?, ?, ?, ?, ?, ?)
359
- `).run(id, threadId, role, content, timestamp, kind, nextSeq);
364
+ INSERT INTO messages (id, thread_id, role, content, tool_call_json, timestamp, kind, seq)
365
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
366
+ `).run(id, threadId, role, content, toolCall ? JSON.stringify(toolCall) : null, timestamp, kind, nextSeq);
360
367
  this.db.prepare(`
361
368
  UPDATE threads
362
369
  SET history_count = history_count + 1
@@ -378,31 +385,34 @@ class LocalCoreAcpStore {
378
385
  }
379
386
  updateRun(runId, threadId, status) {
380
387
  const now = new Date().toISOString();
388
+ const normalizedStatus = (0, index_js_1.normalizeRunStatus)(status);
381
389
  const existing = this.db.prepare('SELECT id FROM runs WHERE id = ?').get(runId);
382
390
  if (existing) {
383
- this.db.prepare('UPDATE runs SET status = ?, updated_at = ? WHERE id = ?').run(status, now, runId);
391
+ this.db.prepare('UPDATE runs SET status = ?, updated_at = ? WHERE id = ?').run(normalizedStatus, now, runId);
384
392
  return;
385
393
  }
386
394
  this.db.prepare(`
387
395
  INSERT INTO runs (id, thread_id, status, started_at, updated_at)
388
396
  VALUES (?, ?, ?, ?, ?)
389
- `).run(runId, threadId, status, now, now);
397
+ `).run(runId, threadId, normalizedStatus, now, now);
390
398
  }
391
399
  getLatestRunForThread(threadId) {
392
- return this.db.prepare(`
400
+ const row = this.db.prepare(`
393
401
  SELECT id, thread_id, status, started_at, updated_at
394
402
  FROM runs
395
403
  WHERE thread_id = ?
396
404
  ORDER BY updated_at DESC
397
405
  LIMIT 1
398
406
  `).get(threadId);
407
+ return row ? { ...row, status: (0, index_js_1.normalizeRunStatus)(row.status) } : undefined;
399
408
  }
400
409
  getRun(runId) {
401
- return this.db.prepare(`
410
+ const row = this.db.prepare(`
402
411
  SELECT id, thread_id, status, started_at, updated_at
403
412
  FROM runs
404
413
  WHERE id = ?
405
414
  `).get(runId);
415
+ return row ? { ...row, status: (0, index_js_1.normalizeRunStatus)(row.status) } : undefined;
406
416
  }
407
417
  listWorkspaceRegistry() {
408
418
  const rows = this.db.prepare(`
@@ -573,7 +583,7 @@ class LocalCoreAcpStore {
573
583
  params.push(query.taskId);
574
584
  }
575
585
  if (query.status) {
576
- const statuses = Array.isArray(query.status) ? query.status : [query.status];
586
+ const statuses = (Array.isArray(query.status) ? query.status : [query.status]).map((status) => (0, index_js_1.normalizeApprovalRequestStatus)(status));
577
587
  predicates.push(`status IN (${statuses.map(() => '?').join(', ')})`);
578
588
  params.push(...statuses);
579
589
  }
@@ -669,7 +679,7 @@ class LocalCoreAcpStore {
669
679
  createAgentTask(input) {
670
680
  const id = `task:${(0, node_crypto_1.randomUUID)()}`;
671
681
  const now = new Date().toISOString();
672
- const status = input.status || 'created';
682
+ const status = (0, index_js_1.normalizeAgentTaskStatus)(input.status);
673
683
  const timeline = [{
674
684
  id: `timeline:${(0, node_crypto_1.randomUUID)()}`,
675
685
  type: 'status_change',
@@ -706,7 +716,7 @@ class LocalCoreAcpStore {
706
716
  params.push(query.runtimeId);
707
717
  }
708
718
  if (query.status) {
709
- const statuses = Array.isArray(query.status) ? query.status : [query.status];
719
+ const statuses = (Array.isArray(query.status) ? query.status : [query.status]).map((status) => (0, index_js_1.normalizeAgentTaskStatus)(status));
710
720
  predicates.push(`status IN (${statuses.map(() => '?').join(', ')})`);
711
721
  params.push(...statuses);
712
722
  }
@@ -735,17 +745,17 @@ class LocalCoreAcpStore {
735
745
  throw new Error(`Task not found: ${taskId}`);
736
746
  }
737
747
  const now = new Date().toISOString();
738
- const nextStatus = input.status || existing.status;
748
+ const nextStatus = input.status ? (0, index_js_1.normalizeAgentTaskStatus)(input.status) : existing.status;
739
749
  const timeline = [...existing.timeline];
740
750
  const logs = [...existing.logs];
741
751
  const artifacts = [...existing.artifacts];
742
752
  const approvalIds = [...existing.approvalIds];
743
- if (input.status && input.status !== existing.status) {
753
+ if (input.status && nextStatus !== existing.status) {
744
754
  timeline.push({
745
755
  id: `timeline:${(0, node_crypto_1.randomUUID)()}`,
746
756
  type: 'status_change',
747
- title: `Task ${input.status}`,
748
- status: input.status,
757
+ title: `Task ${nextStatus}`,
758
+ status: nextStatus,
749
759
  timestamp: now,
750
760
  });
751
761
  }
@@ -780,14 +790,14 @@ class LocalCoreAcpStore {
780
790
  WHERE id = ?
781
791
  `).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
792
  const task = this.getAgentTask(taskId);
783
- if (input.status && input.status !== existing.status) {
793
+ if (input.status && nextStatus !== existing.status) {
784
794
  this.createAuditEvent({
785
795
  type: 'task.updated',
786
796
  workspaceId: task.workspaceId,
787
797
  taskId: task.taskId,
788
798
  actor: 'local',
789
- summary: `Task status changed to ${input.status}.`,
790
- metadata: { previousStatus: existing.status, status: input.status, runId: task.runId },
799
+ summary: `Task status changed to ${nextStatus}.`,
800
+ metadata: { previousStatus: existing.status, status: nextStatus, runId: task.runId },
791
801
  });
792
802
  }
793
803
  return task;
@@ -820,14 +830,20 @@ class LocalCoreAcpStore {
820
830
  return row ? this.toScheduledJob(row) : undefined;
821
831
  }
822
832
  createScheduledJob(input) {
823
- const id = `job:${(0, node_crypto_1.randomUUID)()}`;
833
+ let id = (0, job_id_js_1.createScheduledJobId)();
834
+ for (let attempt = 0; attempt < 5 && this.getScheduledJob(id); attempt += 1) {
835
+ id = (0, job_id_js_1.createScheduledJobId)();
836
+ }
837
+ if (this.getScheduledJob(id)) {
838
+ throw new Error('Unable to allocate a unique scheduled job id.');
839
+ }
824
840
  const now = new Date().toISOString();
825
841
  this.db.prepare(`
826
842
  INSERT INTO scheduled_jobs (
827
843
  id, workspace_id, platform, route_type, route_config, execution_mode, trigger_type, cron_expr, run_at,
828
844
  prompt_template, description, enabled, concurrency_policy, created_at, updated_at
829
845
  ) 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);
846
+ `).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
847
  return this.getScheduledJob(id);
832
848
  }
833
849
  updateScheduledJob(jobId, input) {
@@ -838,8 +854,8 @@ class LocalCoreAcpStore {
838
854
  const next = {
839
855
  ...existing,
840
856
  ...(input.route ? { route: input.route } : {}),
841
- ...(input.executionMode ? { executionMode: input.executionMode } : {}),
842
- ...(input.triggerType ? { triggerType: input.triggerType } : {}),
857
+ ...(input.executionMode ? { executionMode: (0, index_js_1.normalizeScheduledJobExecutionMode)(input.executionMode) } : {}),
858
+ ...(input.triggerType ? { triggerType: (0, index_js_1.normalizeScheduledJobTriggerType)(input.triggerType) } : {}),
843
859
  ...(Object.prototype.hasOwnProperty.call(input, 'cronExpr') ? { cronExpr: input.cronExpr || undefined } : {}),
844
860
  ...(Object.prototype.hasOwnProperty.call(input, 'runAt') ? { runAt: input.runAt || undefined } : {}),
845
861
  ...(typeof input.promptTemplate === 'string' ? { promptTemplate: input.promptTemplate } : {}),
@@ -856,7 +872,10 @@ class LocalCoreAcpStore {
856
872
  return this.getScheduledJob(jobId);
857
873
  }
858
874
  deleteScheduledJob(jobId) {
859
- this.db.prepare('DELETE FROM scheduled_jobs WHERE id = ?').run(jobId);
875
+ const result = this.db.prepare('DELETE FROM scheduled_jobs WHERE id = ?').run(jobId);
876
+ if (result.changes === 0) {
877
+ throw new Error(`Scheduled job not found: ${jobId}`);
878
+ }
860
879
  return { deleted: true };
861
880
  }
862
881
  listScheduledJobRuns(jobId) {
@@ -871,13 +890,14 @@ class LocalCoreAcpStore {
871
890
  createScheduledJobRun(jobId, status, input = {}) {
872
891
  const id = `jobrun:${(0, node_crypto_1.randomUUID)()}`;
873
892
  const triggeredAt = input.triggeredAt || new Date().toISOString();
893
+ const normalizedStatus = (0, index_js_1.normalizeScheduledJobRunStatus)(status);
874
894
  this.db.prepare(`
875
895
  INSERT INTO scheduled_job_runs (id, job_id, status, triggered_at, started_at, finished_at, error, thread_id, run_id, platform_message_id)
876
896
  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);
897
+ `).run(id, jobId, normalizedStatus, triggeredAt, input.startedAt || null, input.finishedAt || null, input.error || null, input.threadId || null, input.runId || null, input.platformMessageId || null);
878
898
  this.updateScheduledJobStatus(jobId, {
879
899
  lastRunAt: triggeredAt,
880
- lastStatus: status,
900
+ lastStatus: normalizedStatus,
881
901
  lastError: input.error || '',
882
902
  });
883
903
  return this.getScheduledJobRun(id);
@@ -898,6 +918,7 @@ class LocalCoreAcpStore {
898
918
  const next = {
899
919
  ...existing,
900
920
  ...input,
921
+ ...(input.status ? { status: (0, index_js_1.normalizeScheduledJobRunStatus)(input.status) } : {}),
901
922
  };
902
923
  this.db.prepare(`
903
924
  UPDATE scheduled_job_runs
@@ -926,11 +947,18 @@ class LocalCoreAcpStore {
926
947
  }
927
948
  getThreadRow(threadId) {
928
949
  return this.db.prepare(`
929
- SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, acp_session_id, acp_supports_load
950
+ SELECT id, workspace_id, session_id, bridge_session_key, title, agent_type, created_at, updated_at, history_count, excerpt, acp_session_id, acp_supports_load, agent_mode
930
951
  FROM threads
931
952
  WHERE id = ?
932
953
  `).get(threadId);
933
954
  }
955
+ updateThreadAgentMode(threadId, mode) {
956
+ this.db.prepare(`
957
+ UPDATE threads
958
+ SET agent_mode = ?, updated_at = ?
959
+ WHERE id = ?
960
+ `).run(mode, new Date().toISOString(), threadId);
961
+ }
934
962
  updateThreadSession(threadId, sessionId, supportsLoad) {
935
963
  this.db.prepare(`
936
964
  UPDATE threads
@@ -1113,10 +1141,10 @@ class LocalCoreAcpStore {
1113
1141
  return {
1114
1142
  id: row.id,
1115
1143
  workspaceId: row.workspace_id,
1116
- platform: row.platform,
1144
+ platform: (0, index_js_1.normalizeChannelPlatform)(row.platform),
1117
1145
  route,
1118
- executionMode: row.execution_mode || 'same-thread',
1119
- triggerType: row.trigger_type,
1146
+ executionMode: (0, index_js_1.normalizeScheduledJobExecutionMode)(row.execution_mode),
1147
+ triggerType: (0, index_js_1.normalizeScheduledJobTriggerType)(row.trigger_type),
1120
1148
  cronExpr: row.cron_expr || undefined,
1121
1149
  runAt: row.run_at || undefined,
1122
1150
  promptTemplate: row.prompt_template,
@@ -1126,7 +1154,7 @@ class LocalCoreAcpStore {
1126
1154
  createdAt: row.created_at,
1127
1155
  updatedAt: row.updated_at,
1128
1156
  lastRunAt: row.last_run_at || undefined,
1129
- lastStatus: row.last_status || undefined,
1157
+ lastStatus: row.last_status ? (0, index_js_1.normalizeScheduledJobRunStatus)(row.last_status) : undefined,
1130
1158
  lastError: row.last_error || undefined,
1131
1159
  };
1132
1160
  }
@@ -1134,7 +1162,7 @@ class LocalCoreAcpStore {
1134
1162
  return {
1135
1163
  id: row.id,
1136
1164
  jobId: row.job_id,
1137
- status: row.status,
1165
+ status: (0, index_js_1.normalizeScheduledJobRunStatus)(row.status),
1138
1166
  triggeredAt: row.triggered_at,
1139
1167
  startedAt: row.started_at || undefined,
1140
1168
  finishedAt: row.finished_at || undefined,
@@ -1187,7 +1215,7 @@ class LocalCoreAcpStore {
1187
1215
  runId: row.run_id || undefined,
1188
1216
  title: row.title,
1189
1217
  prompt: row.prompt || undefined,
1190
- status: row.status,
1218
+ status: (0, index_js_1.normalizeAgentTaskStatus)(row.status),
1191
1219
  createdAt: row.created_at,
1192
1220
  updatedAt: row.updated_at,
1193
1221
  queuedAt: row.queued_at || undefined,
@@ -1224,7 +1252,7 @@ class LocalCoreAcpStore {
1224
1252
  runId: row.run_id || undefined,
1225
1253
  deviceId: row.device_id,
1226
1254
  kind: row.kind,
1227
- status: row.status,
1255
+ status: (0, index_js_1.normalizeApprovalRequestStatus)(row.status),
1228
1256
  riskLevel: row.risk_level,
1229
1257
  title: row.title,
1230
1258
  description: row.description,