@sinm/kai 1.9.0 → 1.9.2

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 (132) hide show
  1. package/dist-cli/kai-cli.js +1593 -532
  2. package/dist-electron/renderer/assets/{_baseUniq-wFb12gB-.js → _baseUniq-CsQsIo3h.js} +1 -1
  3. package/dist-electron/renderer/assets/_baseUniq-CsQsIo3h.js.gz +0 -0
  4. package/dist-electron/renderer/assets/{arc-Bjp2aBSU.js → arc-CnDy2qlp.js} +1 -1
  5. package/dist-electron/renderer/assets/arc-CnDy2qlp.js.gz +0 -0
  6. package/dist-electron/renderer/assets/{architectureDiagram-Q4EWVU46-AEAF6BMT.js → architectureDiagram-Q4EWVU46-7xdcupg1.js} +5 -5
  7. package/dist-electron/renderer/assets/architectureDiagram-Q4EWVU46-7xdcupg1.js.gz +0 -0
  8. package/dist-electron/renderer/assets/{blockDiagram-DXYQGD6D-CZGyJqzN.js → blockDiagram-DXYQGD6D-NHzIf6KM.js} +6 -6
  9. package/dist-electron/renderer/assets/blockDiagram-DXYQGD6D-NHzIf6KM.js.gz +0 -0
  10. package/dist-electron/renderer/assets/{c4Diagram-AHTNJAMY-QR6T5UK-.js → c4Diagram-AHTNJAMY-C805hJap.js} +2 -2
  11. package/dist-electron/renderer/assets/c4Diagram-AHTNJAMY-C805hJap.js.gz +0 -0
  12. package/dist-electron/renderer/assets/{channel-Bqk4dDiH.js → channel-yHIQhPj2.js} +1 -1
  13. package/dist-electron/renderer/assets/{chunk-4BX2VUAB-Ql_0mvKl.js → chunk-4BX2VUAB-BW86W7DE.js} +1 -1
  14. package/dist-electron/renderer/assets/{chunk-4TB4RGXK-DcpxCTnt.js → chunk-4TB4RGXK-CiAgH5vp.js} +5 -5
  15. package/dist-electron/renderer/assets/chunk-4TB4RGXK-CiAgH5vp.js.gz +0 -0
  16. package/dist-electron/renderer/assets/{chunk-55IACEB6-5i5ku6Mp.js → chunk-55IACEB6-CLYAwM0u.js} +1 -1
  17. package/dist-electron/renderer/assets/{chunk-EDXVE4YY-DWU8RPW7.js → chunk-EDXVE4YY-SvJyEgqT.js} +1 -1
  18. package/dist-electron/renderer/assets/chunk-EDXVE4YY-SvJyEgqT.js.gz +0 -0
  19. package/dist-electron/renderer/assets/{chunk-FMBD7UC4-CGgyRi9w.js → chunk-FMBD7UC4-CTX3SpFe.js} +1 -1
  20. package/dist-electron/renderer/assets/{chunk-OYMX7WX6-DsuO_QrB.js → chunk-OYMX7WX6-yuicy5fD.js} +3 -3
  21. package/dist-electron/renderer/assets/chunk-OYMX7WX6-yuicy5fD.js.gz +0 -0
  22. package/dist-electron/renderer/assets/{chunk-QZHKN3VN-Bbii2TV9.js → chunk-QZHKN3VN-8uwLkZ2k.js} +1 -1
  23. package/dist-electron/renderer/assets/{chunk-YZCP3GAM-DBLeyzP3.js → chunk-YZCP3GAM-BOYtvFmP.js} +1 -1
  24. package/dist-electron/renderer/assets/chunk-YZCP3GAM-BOYtvFmP.js.gz +0 -0
  25. package/dist-electron/renderer/assets/{classDiagram-6PBFFD2Q-CTppFDM-.js → classDiagram-6PBFFD2Q-DdqJHrBR.js} +6 -6
  26. package/dist-electron/renderer/assets/{classDiagram-v2-HSJHXN6E-CTppFDM-.js → classDiagram-v2-HSJHXN6E-DdqJHrBR.js} +6 -6
  27. package/dist-electron/renderer/assets/{clone-DJcYmsl6.js → clone-CohPIibk.js} +1 -1
  28. package/dist-electron/renderer/assets/{cose-bilkent-S5V4N54A-lOBC_Swy.js → cose-bilkent-S5V4N54A-B1jjJbx2.js} +1 -1
  29. package/dist-electron/renderer/assets/cose-bilkent-S5V4N54A-B1jjJbx2.js.gz +0 -0
  30. package/dist-electron/renderer/assets/{dagre-KV5264BT-CFc-IlN0.js → dagre-KV5264BT-Cr1UGppV.js} +6 -6
  31. package/dist-electron/renderer/assets/dagre-KV5264BT-Cr1UGppV.js.gz +0 -0
  32. package/dist-electron/renderer/assets/{diagram-5BDNPKRD-CMjGq3Qd.js → diagram-5BDNPKRD-OQB6O17M.js} +6 -6
  33. package/dist-electron/renderer/assets/diagram-5BDNPKRD-OQB6O17M.js.gz +0 -0
  34. package/dist-electron/renderer/assets/{diagram-G4DWMVQ6-BZ_RCuPz.js → diagram-G4DWMVQ6-B992OGq3.js} +6 -6
  35. package/dist-electron/renderer/assets/diagram-G4DWMVQ6-B992OGq3.js.gz +0 -0
  36. package/dist-electron/renderer/assets/{diagram-MMDJMWI5-CAbPKB-M.js → diagram-MMDJMWI5-sfDZyGEc.js} +5 -5
  37. package/dist-electron/renderer/assets/diagram-MMDJMWI5-sfDZyGEc.js.gz +0 -0
  38. package/dist-electron/renderer/assets/{diagram-TYMM5635-CUDsp3Va.js → diagram-TYMM5635-SAOHwHvV.js} +5 -5
  39. package/dist-electron/renderer/assets/diagram-TYMM5635-SAOHwHvV.js.gz +0 -0
  40. package/dist-electron/renderer/assets/{erDiagram-SMLLAGMA-DoaAVQ3k.js → erDiagram-SMLLAGMA-B3lOLLG4.js} +4 -4
  41. package/dist-electron/renderer/assets/erDiagram-SMLLAGMA-B3lOLLG4.js.gz +0 -0
  42. package/dist-electron/renderer/assets/{flowDiagram-DWJPFMVM-BPPswghk.js → flowDiagram-DWJPFMVM-DM8NWY2I.js} +6 -6
  43. package/dist-electron/renderer/assets/flowDiagram-DWJPFMVM-DM8NWY2I.js.gz +0 -0
  44. package/dist-electron/renderer/assets/{ganttDiagram-T4ZO3ILL-D9dDRsnt.js → ganttDiagram-T4ZO3ILL-Ck9cM-4F.js} +2 -2
  45. package/dist-electron/renderer/assets/ganttDiagram-T4ZO3ILL-Ck9cM-4F.js.gz +0 -0
  46. package/dist-electron/renderer/assets/{gitGraphDiagram-UUTBAWPF-BqSxB4JC.js → gitGraphDiagram-UUTBAWPF-BbTlLKCx.js} +6 -6
  47. package/dist-electron/renderer/assets/gitGraphDiagram-UUTBAWPF-BbTlLKCx.js.gz +0 -0
  48. package/dist-electron/renderer/assets/{graph-Cdcjro9E.js → graph-SD4HIKJw.js} +2 -2
  49. package/dist-electron/renderer/assets/graph-SD4HIKJw.js.gz +0 -0
  50. package/dist-electron/renderer/assets/{index-DR-b90MW.js → index-CHaHfSLU.js} +3004 -1996
  51. package/dist-electron/renderer/assets/index-CHaHfSLU.js.gz +0 -0
  52. package/dist-electron/renderer/assets/{index-DIbFt7rO.css → index-CP1VBw7a.css} +1545 -1057
  53. package/dist-electron/renderer/assets/index-CP1VBw7a.css.gz +0 -0
  54. package/dist-electron/renderer/assets/{infoDiagram-42DDH7IO-BNgkgl2w.js → infoDiagram-42DDH7IO-DyxtiwFL.js} +4 -4
  55. package/dist-electron/renderer/assets/{ishikawaDiagram-UXIWVN3A-B2smtRtE.js → ishikawaDiagram-UXIWVN3A-BUrLWa0T.js} +1 -1
  56. package/dist-electron/renderer/assets/ishikawaDiagram-UXIWVN3A-BUrLWa0T.js.gz +0 -0
  57. package/dist-electron/renderer/assets/{journeyDiagram-VCZTEJTY-5i0hgzSw.js → journeyDiagram-VCZTEJTY-BMmtCdj3.js} +4 -4
  58. package/dist-electron/renderer/assets/journeyDiagram-VCZTEJTY-BMmtCdj3.js.gz +0 -0
  59. package/dist-electron/renderer/assets/{kanban-definition-6JOO6SKY-DZ82Qc3W.js → kanban-definition-6JOO6SKY-OQBC48Se.js} +2 -2
  60. package/dist-electron/renderer/assets/kanban-definition-6JOO6SKY-OQBC48Se.js.gz +0 -0
  61. package/dist-electron/renderer/assets/{layout-DCDaRhDD.js → layout-D2Rz390M.js} +4 -4
  62. package/dist-electron/renderer/assets/layout-D2Rz390M.js.gz +0 -0
  63. package/dist-electron/renderer/assets/{linear-BhTANRPL.js → linear-CQh0Mysb.js} +1 -1
  64. package/dist-electron/renderer/assets/linear-CQh0Mysb.js.gz +0 -0
  65. package/dist-electron/renderer/assets/{min-fI6oHIzD.js → min-DlS-oIeB.js} +2 -2
  66. package/dist-electron/renderer/assets/min-DlS-oIeB.js.gz +0 -0
  67. package/dist-electron/renderer/assets/{mindmap-definition-QFDTVHPH-CFwqVKXp.js → mindmap-definition-QFDTVHPH-aTTTq6Nc.js} +3 -3
  68. package/dist-electron/renderer/assets/mindmap-definition-QFDTVHPH-aTTTq6Nc.js.gz +0 -0
  69. package/dist-electron/renderer/assets/{pieDiagram-DEJITSTG-B9qNWsik.js → pieDiagram-DEJITSTG-n1e04sg0.js} +6 -6
  70. package/dist-electron/renderer/assets/pieDiagram-DEJITSTG-n1e04sg0.js.gz +0 -0
  71. package/dist-electron/renderer/assets/{quadrantDiagram-34T5L4WZ-BH52I9Mz.js → quadrantDiagram-34T5L4WZ-C-xW2Cqp.js} +2 -2
  72. package/dist-electron/renderer/assets/quadrantDiagram-34T5L4WZ-C-xW2Cqp.js.gz +0 -0
  73. package/dist-electron/renderer/assets/{requirementDiagram-MS252O5E-zdr-UweE.js → requirementDiagram-MS252O5E-pnNGwrTw.js} +3 -3
  74. package/dist-electron/renderer/assets/requirementDiagram-MS252O5E-pnNGwrTw.js.gz +0 -0
  75. package/dist-electron/renderer/assets/{sankeyDiagram-XADWPNL6-fOSi4otN.js → sankeyDiagram-XADWPNL6-CbT_Xb_y.js} +1 -1
  76. package/dist-electron/renderer/assets/{sankeyDiagram-XADWPNL6-fOSi4otN.js.gz → sankeyDiagram-XADWPNL6-CbT_Xb_y.js.gz} +0 -0
  77. package/dist-electron/renderer/assets/{sequenceDiagram-FGHM5R23-BD9MRuC5.js → sequenceDiagram-FGHM5R23-a-9UfTJq.js} +3 -3
  78. package/dist-electron/renderer/assets/sequenceDiagram-FGHM5R23-a-9UfTJq.js.gz +0 -0
  79. package/dist-electron/renderer/assets/{stateDiagram-FHFEXIEX-CMQH2Bdw.js → stateDiagram-FHFEXIEX-LL3JhZd4.js} +8 -8
  80. package/dist-electron/renderer/assets/stateDiagram-FHFEXIEX-LL3JhZd4.js.gz +0 -0
  81. package/dist-electron/renderer/assets/{stateDiagram-v2-QKLJ7IA2-B4Kz8yNI.js → stateDiagram-v2-QKLJ7IA2-BQfhbm-p.js} +4 -4
  82. package/dist-electron/renderer/assets/{timeline-definition-GMOUNBTQ-eOylFnoH.js → timeline-definition-GMOUNBTQ-DeBW6Or5.js} +2 -2
  83. package/dist-electron/renderer/assets/timeline-definition-GMOUNBTQ-DeBW6Or5.js.gz +0 -0
  84. package/dist-electron/renderer/assets/{vennDiagram-DHZGUBPP-CYDW0VBV.js → vennDiagram-DHZGUBPP-DEq9JF1i.js} +1 -1
  85. package/dist-electron/renderer/assets/vennDiagram-DHZGUBPP-DEq9JF1i.js.gz +0 -0
  86. package/dist-electron/renderer/assets/{wardley-RL74JXVD-CYRSMbSd.js → wardley-RL74JXVD-ChJWklYY.js} +3 -3
  87. package/dist-electron/renderer/assets/wardley-RL74JXVD-ChJWklYY.js.gz +0 -0
  88. package/dist-electron/renderer/assets/{wardleyDiagram-NUSXRM2D-jjE8DGWF.js → wardleyDiagram-NUSXRM2D-CIROAFHI.js} +5 -5
  89. package/dist-electron/renderer/assets/wardleyDiagram-NUSXRM2D-CIROAFHI.js.gz +0 -0
  90. package/dist-electron/renderer/assets/{xychartDiagram-5P7HB3ND-BJCCYED3.js → xychartDiagram-5P7HB3ND-BedH4Z65.js} +2 -2
  91. package/dist-electron/renderer/assets/xychartDiagram-5P7HB3ND-BedH4Z65.js.gz +0 -0
  92. package/dist-electron/renderer/index.html +2 -2
  93. package/package.json +1 -1
  94. package/dist-electron/renderer/assets/_baseUniq-wFb12gB-.js.gz +0 -0
  95. package/dist-electron/renderer/assets/arc-Bjp2aBSU.js.gz +0 -0
  96. package/dist-electron/renderer/assets/architectureDiagram-Q4EWVU46-AEAF6BMT.js.gz +0 -0
  97. package/dist-electron/renderer/assets/blockDiagram-DXYQGD6D-CZGyJqzN.js.gz +0 -0
  98. package/dist-electron/renderer/assets/c4Diagram-AHTNJAMY-QR6T5UK-.js.gz +0 -0
  99. package/dist-electron/renderer/assets/chunk-4TB4RGXK-DcpxCTnt.js.gz +0 -0
  100. package/dist-electron/renderer/assets/chunk-EDXVE4YY-DWU8RPW7.js.gz +0 -0
  101. package/dist-electron/renderer/assets/chunk-OYMX7WX6-DsuO_QrB.js.gz +0 -0
  102. package/dist-electron/renderer/assets/chunk-YZCP3GAM-DBLeyzP3.js.gz +0 -0
  103. package/dist-electron/renderer/assets/cose-bilkent-S5V4N54A-lOBC_Swy.js.gz +0 -0
  104. package/dist-electron/renderer/assets/dagre-KV5264BT-CFc-IlN0.js.gz +0 -0
  105. package/dist-electron/renderer/assets/diagram-5BDNPKRD-CMjGq3Qd.js.gz +0 -0
  106. package/dist-electron/renderer/assets/diagram-G4DWMVQ6-BZ_RCuPz.js.gz +0 -0
  107. package/dist-electron/renderer/assets/diagram-MMDJMWI5-CAbPKB-M.js.gz +0 -0
  108. package/dist-electron/renderer/assets/diagram-TYMM5635-CUDsp3Va.js.gz +0 -0
  109. package/dist-electron/renderer/assets/erDiagram-SMLLAGMA-DoaAVQ3k.js.gz +0 -0
  110. package/dist-electron/renderer/assets/flowDiagram-DWJPFMVM-BPPswghk.js.gz +0 -0
  111. package/dist-electron/renderer/assets/ganttDiagram-T4ZO3ILL-D9dDRsnt.js.gz +0 -0
  112. package/dist-electron/renderer/assets/gitGraphDiagram-UUTBAWPF-BqSxB4JC.js.gz +0 -0
  113. package/dist-electron/renderer/assets/graph-Cdcjro9E.js.gz +0 -0
  114. package/dist-electron/renderer/assets/index-DIbFt7rO.css.gz +0 -0
  115. package/dist-electron/renderer/assets/index-DR-b90MW.js.gz +0 -0
  116. package/dist-electron/renderer/assets/ishikawaDiagram-UXIWVN3A-B2smtRtE.js.gz +0 -0
  117. package/dist-electron/renderer/assets/journeyDiagram-VCZTEJTY-5i0hgzSw.js.gz +0 -0
  118. package/dist-electron/renderer/assets/kanban-definition-6JOO6SKY-DZ82Qc3W.js.gz +0 -0
  119. package/dist-electron/renderer/assets/layout-DCDaRhDD.js.gz +0 -0
  120. package/dist-electron/renderer/assets/linear-BhTANRPL.js.gz +0 -0
  121. package/dist-electron/renderer/assets/min-fI6oHIzD.js.gz +0 -0
  122. package/dist-electron/renderer/assets/mindmap-definition-QFDTVHPH-CFwqVKXp.js.gz +0 -0
  123. package/dist-electron/renderer/assets/pieDiagram-DEJITSTG-B9qNWsik.js.gz +0 -0
  124. package/dist-electron/renderer/assets/quadrantDiagram-34T5L4WZ-BH52I9Mz.js.gz +0 -0
  125. package/dist-electron/renderer/assets/requirementDiagram-MS252O5E-zdr-UweE.js.gz +0 -0
  126. package/dist-electron/renderer/assets/sequenceDiagram-FGHM5R23-BD9MRuC5.js.gz +0 -0
  127. package/dist-electron/renderer/assets/stateDiagram-FHFEXIEX-CMQH2Bdw.js.gz +0 -0
  128. package/dist-electron/renderer/assets/timeline-definition-GMOUNBTQ-eOylFnoH.js.gz +0 -0
  129. package/dist-electron/renderer/assets/vennDiagram-DHZGUBPP-CYDW0VBV.js.gz +0 -0
  130. package/dist-electron/renderer/assets/wardley-RL74JXVD-CYRSMbSd.js.gz +0 -0
  131. package/dist-electron/renderer/assets/wardleyDiagram-NUSXRM2D-jjE8DGWF.js.gz +0 -0
  132. package/dist-electron/renderer/assets/xychartDiagram-5P7HB3ND-BJCCYED3.js.gz +0 -0
@@ -34,7 +34,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
35
 
36
36
  // src/shared/ipc-events.ts
37
- var IPC_SEND_USER_MESSAGE, IPC_STOP_RUN, IPC_CREATE_NEW_SESSION, IPC_COMPACT_CONVERSATION, IPC_CANCEL_QUEUED_MESSAGES, IPC_REQUEST_CONVERSATION_SNAPSHOT, IPC_CONVERSATION_EVENT, IPC_GET_SETTINGS, IPC_SAVE_SETTINGS, IPC_FETCH_MODELS, IPC_GET_MCP_SERVERS, IPC_SAVE_MCP_SERVERS, IPC_TEST_MCP_SERVER, IPC_GET_SKILLS, IPC_GET_SKILL, IPC_TOGGLE_SKILL, IPC_CREATE_AGENT, IPC_GET_AGENT, IPC_GET_AGENT_CONFIG, IPC_LIST_AGENTS, IPC_UPDATE_AGENT, IPC_DELETE_AGENT, IPC_OPTIMIZE_AGENT_DESCRIPTION, IPC_GET_AGENT_CHANNELS, IPC_UPDATE_AGENT_CHANNELS, IPC_SWITCH_CHANNEL, IPC_CREATE_CONVERSATION, IPC_GET_CONVERSATION, IPC_LIST_CONVERSATIONS, IPC_GET_CONVERSATION_AGENTS, IPC_UPDATE_CONVERSATION, IPC_DELETE_CONVERSATION, IPC_CREATE_GROUP, IPC_GET_GROUP, IPC_UPDATE_GROUP, IPC_DELETE_GROUP, IPC_GET_GROUP_MEMBERS, IPC_GET_TEAM_MEMORY, IPC_SAVE_GROUP_FILE, IPC_GET_GROUP_SETTINGS, IPC_GET_GROUP_MEMORY, IPC_LIST_GROUP_THREADS, IPC_GET_GROUP_THREAD, IPC_CREATE_GROUP_THREAD, IPC_UPDATE_GROUP_THREAD, IPC_DELETE_GROUP_THREAD, IPC_ARCHIVE_GROUP_THREAD, IPC_GET_MESSAGES, IPC_GET_MESSAGE, IPC_CREATE_LLM_CHANNEL, IPC_GET_LLM_CHANNEL, IPC_LIST_LLM_CHANNELS, IPC_UPDATE_LLM_CHANNEL, IPC_DELETE_LLM_CHANNEL, IPC_SET_DEFAULT_LLM_CHANNEL, IPC_GET_WORKSPACE_FILE, IPC_UPDATE_WORKSPACE_FILE, IPC_LIST_WORKSPACE_FILES, IPC_OPEN_WORKSPACE_DIR, IPC_OPEN_CHAT_DATA_DIR, IPC_GET_SEARCH_ENGINES, IPC_ADD_CUSTOM_ENGINE, IPC_UPDATE_CUSTOM_ENGINE, IPC_DELETE_CUSTOM_ENGINE, IPC_SET_DEFAULT_ENGINE, IPC_CREATE_SCHEDULED_TASK, IPC_GET_SCHEDULED_TASK, IPC_LIST_SCHEDULED_TASKS, IPC_UPDATE_SCHEDULED_TASK, IPC_DELETE_SCHEDULED_TASK, IPC_TOGGLE_SCHEDULED_TASK, IPC_LIST_TASK_EXECUTIONS, IPC_TASK_EXECUTION_EVENT, IPC_TRIGGER_MEMORY_UPDATE, IPC_GET_NIGHTLY_MEMORY_CONFIG, IPC_UPDATE_NIGHTLY_MEMORY_CONFIG, IPC_RUN_NIGHTLY_MEMORY_NOW, IPC_LIST_NIGHTLY_MEMORY_EXECUTIONS, IPC_OPEN_EXTERNAL_URL, IPC_OPEN_LOCAL_PATH, IPC_READ_FILE_CONTENT, IPC_SHOW_LOCAL_ITEM_IN_FOLDER, IPC_UPLOAD_ATTACHMENT, IPC_OPEN_ATTACHMENT, IPC_MEMORY_UPDATE_PROGRESS, IPC_HEALTH_GET_ALL, IPC_HEALTH_GET, IPC_HEALTH_UPDATE, IPC_REMOTE_ACCESS_START, IPC_REMOTE_ACCESS_STOP, IPC_REMOTE_ACCESS_ROTATE, IPC_REMOTE_ACCESS_STATUS, IPC_GET_APP_INFO, IPC_GET_RELEASE_NOTES, IPC_UPDATE_CHECK, IPC_UPDATE_DOWNLOAD, IPC_UPDATE_INSTALL, IPC_UPDATE_STATUS, IPC_UPDATE_STATUS_EVENT, IPC_UPDATE_AVAILABLE_EVENT, IPC_UPDATE_ERROR_EVENT, IPC_GET_PINNED, IPC_TOGGLE_PIN, IPC_CHANNEL_LIST, IPC_CHANNEL_BIND, IPC_CHANNEL_UPDATE_TARGET, IPC_CHANNEL_UNBIND, IPC_CHANNEL_TEST, IPC_CHANNEL_RECONNECT, IPC_CHANNEL_STATUS, IPC_CHANNEL_QRCODE, IPC_CHANNEL_QRCODE_POLL, IPC_CHANNEL_WECHAT_PERSONAL_QRCODE, IPC_CHANNEL_WECHAT_PERSONAL_QRCODE_POLL, IPC_TELEMETRY_TRACK, IPC_TELEMETRY_FEEDBACK, IPC_TELEMETRY_SET_ENABLED, IPC_TELEMETRY_IS_ENABLED;
37
+ var IPC_SEND_USER_MESSAGE, IPC_STOP_RUN, IPC_CREATE_NEW_SESSION, IPC_COMPACT_CONVERSATION, IPC_CANCEL_QUEUED_MESSAGES, IPC_REQUEST_CONVERSATION_SNAPSHOT, IPC_CONVERSATION_EVENT, IPC_GET_SETTINGS, IPC_SAVE_SETTINGS, IPC_FETCH_MODELS, IPC_GET_MCP_SERVERS, IPC_SAVE_MCP_SERVERS, IPC_TEST_MCP_SERVER, IPC_GET_SKILLS, IPC_GET_SKILL, IPC_TOGGLE_SKILL, IPC_CREATE_AGENT, IPC_GET_AGENT, IPC_GET_AGENT_CONFIG, IPC_LIST_AGENTS, IPC_UPDATE_AGENT, IPC_DELETE_AGENT, IPC_SET_AGENT_AVATAR, IPC_REMOVE_AGENT_AVATAR, IPC_OPTIMIZE_AGENT_DESCRIPTION, IPC_GET_AGENT_CHANNELS, IPC_UPDATE_AGENT_CHANNELS, IPC_SWITCH_CHANNEL, IPC_CREATE_CONVERSATION, IPC_GET_CONVERSATION, IPC_LIST_CONVERSATIONS, IPC_GET_CONVERSATION_AGENTS, IPC_UPDATE_CONVERSATION, IPC_DELETE_CONVERSATION, IPC_CREATE_GROUP, IPC_GET_GROUP, IPC_UPDATE_GROUP, IPC_DELETE_GROUP, IPC_SET_GROUP_AVATAR, IPC_REMOVE_GROUP_AVATAR, IPC_GET_GROUP_MEMBERS, IPC_GET_TEAM_MEMORY, IPC_SAVE_GROUP_FILE, IPC_GET_GROUP_SETTINGS, IPC_GET_GROUP_MEMORY, IPC_LIST_GROUP_THREADS, IPC_GET_GROUP_THREAD, IPC_CREATE_GROUP_THREAD, IPC_UPDATE_GROUP_THREAD, IPC_DELETE_GROUP_THREAD, IPC_ARCHIVE_GROUP_THREAD, IPC_GET_MESSAGES, IPC_GET_MESSAGE, IPC_CREATE_LLM_CHANNEL, IPC_GET_LLM_CHANNEL, IPC_LIST_LLM_CHANNELS, IPC_UPDATE_LLM_CHANNEL, IPC_DELETE_LLM_CHANNEL, IPC_SET_DEFAULT_LLM_CHANNEL, IPC_GET_WORKSPACE_FILE, IPC_UPDATE_WORKSPACE_FILE, IPC_LIST_WORKSPACE_FILES, IPC_OPEN_WORKSPACE_DIR, IPC_OPEN_CHAT_DATA_DIR, IPC_GET_SEARCH_ENGINES, IPC_ADD_CUSTOM_ENGINE, IPC_UPDATE_CUSTOM_ENGINE, IPC_DELETE_CUSTOM_ENGINE, IPC_SET_DEFAULT_ENGINE, IPC_CREATE_SCHEDULED_TASK, IPC_GET_SCHEDULED_TASK, IPC_LIST_SCHEDULED_TASKS, IPC_UPDATE_SCHEDULED_TASK, IPC_DELETE_SCHEDULED_TASK, IPC_TOGGLE_SCHEDULED_TASK, IPC_LIST_TASK_EXECUTIONS, IPC_TASK_EXECUTION_EVENT, IPC_TRIGGER_MEMORY_UPDATE, IPC_GET_NIGHTLY_MEMORY_CONFIG, IPC_UPDATE_NIGHTLY_MEMORY_CONFIG, IPC_RUN_NIGHTLY_MEMORY_NOW, IPC_LIST_NIGHTLY_MEMORY_EXECUTIONS, IPC_OPEN_EXTERNAL_URL, IPC_OPEN_LOCAL_PATH, IPC_READ_FILE_CONTENT, IPC_SHOW_LOCAL_ITEM_IN_FOLDER, IPC_COPY_IMAGE_TO_CLIPBOARD, IPC_UPLOAD_ATTACHMENT, IPC_OPEN_ATTACHMENT, IPC_MEMORY_UPDATE_PROGRESS, IPC_HEALTH_GET_ALL, IPC_HEALTH_GET, IPC_HEALTH_UPDATE, IPC_REMOTE_ACCESS_START, IPC_REMOTE_ACCESS_STOP, IPC_REMOTE_ACCESS_ROTATE, IPC_REMOTE_ACCESS_STATUS, IPC_GET_APP_INFO, IPC_GET_RELEASE_NOTES, IPC_UPDATE_CHECK, IPC_UPDATE_DOWNLOAD, IPC_UPDATE_INSTALL, IPC_UPDATE_STATUS, IPC_UPDATE_STATUS_EVENT, IPC_UPDATE_AVAILABLE_EVENT, IPC_UPDATE_ERROR_EVENT, IPC_GET_PINNED, IPC_TOGGLE_PIN, IPC_CHANNEL_LIST, IPC_CHANNEL_BIND, IPC_CHANNEL_UPDATE_TARGET, IPC_CHANNEL_UNBIND, IPC_CHANNEL_TEST, IPC_CHANNEL_RECONNECT, IPC_CHANNEL_STATUS, IPC_CHANNEL_QRCODE, IPC_CHANNEL_QRCODE_POLL, IPC_CHANNEL_WECHAT_PERSONAL_QRCODE, IPC_CHANNEL_WECHAT_PERSONAL_QRCODE_POLL, IPC_TELEMETRY_TRACK, IPC_TELEMETRY_FEEDBACK, IPC_TELEMETRY_SET_ENABLED, IPC_TELEMETRY_IS_ENABLED;
38
38
  var init_ipc_events = __esm({
39
39
  "src/shared/ipc-events.ts"() {
40
40
  "use strict";
@@ -60,6 +60,8 @@ var init_ipc_events = __esm({
60
60
  IPC_LIST_AGENTS = "LIST_AGENTS";
61
61
  IPC_UPDATE_AGENT = "UPDATE_AGENT";
62
62
  IPC_DELETE_AGENT = "DELETE_AGENT";
63
+ IPC_SET_AGENT_AVATAR = "SET_AGENT_AVATAR";
64
+ IPC_REMOVE_AGENT_AVATAR = "REMOVE_AGENT_AVATAR";
63
65
  IPC_OPTIMIZE_AGENT_DESCRIPTION = "OPTIMIZE_AGENT_DESCRIPTION";
64
66
  IPC_GET_AGENT_CHANNELS = "GET_AGENT_CHANNELS";
65
67
  IPC_UPDATE_AGENT_CHANNELS = "UPDATE_AGENT_CHANNELS";
@@ -74,6 +76,8 @@ var init_ipc_events = __esm({
74
76
  IPC_GET_GROUP = "GET_GROUP";
75
77
  IPC_UPDATE_GROUP = "UPDATE_GROUP";
76
78
  IPC_DELETE_GROUP = "DELETE_GROUP";
79
+ IPC_SET_GROUP_AVATAR = "SET_GROUP_AVATAR";
80
+ IPC_REMOVE_GROUP_AVATAR = "REMOVE_GROUP_AVATAR";
77
81
  IPC_GET_GROUP_MEMBERS = "GET_GROUP_MEMBERS";
78
82
  IPC_GET_TEAM_MEMORY = "GET_TEAM_MEMORY";
79
83
  IPC_SAVE_GROUP_FILE = "SAVE_GROUP_FILE";
@@ -120,6 +124,7 @@ var init_ipc_events = __esm({
120
124
  IPC_OPEN_LOCAL_PATH = "OPEN_LOCAL_PATH";
121
125
  IPC_READ_FILE_CONTENT = "READ_FILE_CONTENT";
122
126
  IPC_SHOW_LOCAL_ITEM_IN_FOLDER = "SHOW_LOCAL_ITEM_IN_FOLDER";
127
+ IPC_COPY_IMAGE_TO_CLIPBOARD = "COPY_IMAGE_TO_CLIPBOARD";
123
128
  IPC_UPLOAD_ATTACHMENT = "UPLOAD_ATTACHMENT";
124
129
  IPC_OPEN_ATTACHMENT = "OPEN_ATTACHMENT";
125
130
  IPC_MEMORY_UPDATE_PROGRESS = "MEMORY_UPDATE_PROGRESS";
@@ -219,7 +224,7 @@ var require_lodash = __commonJS({
219
224
  var HOT_COUNT = 800, HOT_SPAN = 16;
220
225
  var LAZY_FILTER_FLAG = 1, LAZY_MAP_FLAG = 2, LAZY_WHILE_FLAG = 3;
221
226
  var INFINITY = 1 / 0, MAX_SAFE_INTEGER = 9007199254740991, MAX_INTEGER = 17976931348623157e292, NAN = 0 / 0;
222
- var MAX_ARRAY_LENGTH = 4294967295, MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
227
+ var MAX_ARRAY_LENGTH2 = 4294967295, MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH2 - 1, HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH2 >>> 1;
223
228
  var wrapFlags = [
224
229
  ["ary", WRAP_ARY_FLAG],
225
230
  ["bind", WRAP_BIND_FLAG],
@@ -1015,7 +1020,7 @@ var require_lodash = __commonJS({
1015
1020
  this.__dir__ = 1;
1016
1021
  this.__filtered__ = false;
1017
1022
  this.__iteratees__ = [];
1018
- this.__takeCount__ = MAX_ARRAY_LENGTH;
1023
+ this.__takeCount__ = MAX_ARRAY_LENGTH2;
1019
1024
  this.__views__ = [];
1020
1025
  }
1021
1026
  function lazyClone() {
@@ -3518,7 +3523,7 @@ var require_lodash = __commonJS({
3518
3523
  }
3519
3524
  return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined2, comparator) : [];
3520
3525
  });
3521
- function join20(array3, separator) {
3526
+ function join21(array3, separator) {
3522
3527
  return array3 == null ? "" : nativeJoin.call(array3, separator);
3523
3528
  }
3524
3529
  function last2(array3) {
@@ -4116,7 +4121,7 @@ var require_lodash = __commonJS({
4116
4121
  var defer = baseRest(function(func, args2) {
4117
4122
  return baseDelay(func, 1, args2);
4118
4123
  });
4119
- var delay2 = baseRest(function(func, wait, args2) {
4124
+ var delay4 = baseRest(function(func, wait, args2) {
4120
4125
  return baseDelay(func, toNumber(wait) || 0, args2);
4121
4126
  });
4122
4127
  function flip(func) {
@@ -4426,7 +4431,7 @@ var require_lodash = __commonJS({
4426
4431
  return result2 === result2 ? remainder ? result2 - remainder : result2 : 0;
4427
4432
  }
4428
4433
  function toLength(value) {
4429
- return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;
4434
+ return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH2) : 0;
4430
4435
  }
4431
4436
  function toNumber(value) {
4432
4437
  if (typeof value == "number") {
@@ -4831,7 +4836,7 @@ var require_lodash = __commonJS({
4831
4836
  if (limit && typeof limit != "number" && isIterateeCall(string4, separator, limit)) {
4832
4837
  separator = limit = undefined2;
4833
4838
  }
4834
- limit = limit === undefined2 ? MAX_ARRAY_LENGTH : limit >>> 0;
4839
+ limit = limit === undefined2 ? MAX_ARRAY_LENGTH2 : limit >>> 0;
4835
4840
  if (!limit) {
4836
4841
  return [];
4837
4842
  }
@@ -4946,7 +4951,7 @@ var require_lodash = __commonJS({
4946
4951
  var strSymbols = stringToArray(string4), start = charsStartIndex(strSymbols, stringToArray(chars));
4947
4952
  return castSlice(strSymbols, start).join("");
4948
4953
  }
4949
- function truncate(string4, options2) {
4954
+ function truncate2(string4, options2) {
4950
4955
  var length = DEFAULT_TRUNC_LENGTH, omission = DEFAULT_TRUNC_OMISSION;
4951
4956
  if (isObject2(options2)) {
4952
4957
  var separator = "separator" in options2 ? options2.separator : separator;
@@ -5150,9 +5155,9 @@ var require_lodash = __commonJS({
5150
5155
  if (n < 1 || n > MAX_SAFE_INTEGER) {
5151
5156
  return [];
5152
5157
  }
5153
- var index2 = MAX_ARRAY_LENGTH, length = nativeMin(n, MAX_ARRAY_LENGTH);
5158
+ var index2 = MAX_ARRAY_LENGTH2, length = nativeMin(n, MAX_ARRAY_LENGTH2);
5154
5159
  iteratee2 = getIteratee(iteratee2);
5155
- n -= MAX_ARRAY_LENGTH;
5160
+ n -= MAX_ARRAY_LENGTH2;
5156
5161
  var result2 = baseTimes(length, iteratee2);
5157
5162
  while (++index2 < n) {
5158
5163
  iteratee2(index2);
@@ -5235,7 +5240,7 @@ var require_lodash = __commonJS({
5235
5240
  lodash.defaults = defaults2;
5236
5241
  lodash.defaultsDeep = defaultsDeep;
5237
5242
  lodash.defer = defer;
5238
- lodash.delay = delay2;
5243
+ lodash.delay = delay4;
5239
5244
  lodash.difference = difference;
5240
5245
  lodash.differenceBy = differenceBy;
5241
5246
  lodash.differenceWith = differenceWith;
@@ -5442,7 +5447,7 @@ var require_lodash = __commonJS({
5442
5447
  lodash.isUndefined = isUndefined;
5443
5448
  lodash.isWeakMap = isWeakMap;
5444
5449
  lodash.isWeakSet = isWeakSet;
5445
- lodash.join = join20;
5450
+ lodash.join = join21;
5446
5451
  lodash.kebabCase = kebabCase;
5447
5452
  lodash.last = last2;
5448
5453
  lodash.lastIndexOf = lastIndexOf;
@@ -5506,7 +5511,7 @@ var require_lodash = __commonJS({
5506
5511
  lodash.trim = trim;
5507
5512
  lodash.trimEnd = trimEnd;
5508
5513
  lodash.trimStart = trimStart;
5509
- lodash.truncate = truncate;
5514
+ lodash.truncate = truncate2;
5510
5515
  lodash.unescape = unescape2;
5511
5516
  lodash.uniqueId = uniqueId;
5512
5517
  lodash.upperCase = upperCase;
@@ -5535,7 +5540,7 @@ var require_lodash = __commonJS({
5535
5540
  result2.__takeCount__ = nativeMin(n, result2.__takeCount__);
5536
5541
  } else {
5537
5542
  result2.__views__.push({
5538
- "size": nativeMin(n, MAX_ARRAY_LENGTH),
5543
+ "size": nativeMin(n, MAX_ARRAY_LENGTH2),
5539
5544
  "type": methodName + (result2.__dir__ < 0 ? "Right" : "")
5540
5545
  });
5541
5546
  }
@@ -5610,7 +5615,7 @@ var require_lodash = __commonJS({
5610
5615
  return this.reverse().takeWhile(predicate).reverse();
5611
5616
  };
5612
5617
  LazyWrapper.prototype.toArray = function() {
5613
- return this.take(MAX_ARRAY_LENGTH);
5618
+ return this.take(MAX_ARRAY_LENGTH2);
5614
5619
  };
5615
5620
  baseForOwn(LazyWrapper.prototype, function(func, methodName) {
5616
5621
  var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName), isTaker = /^(?:head|last)$/.test(methodName), lodashFunc = lodash[isTaker ? "take" + (methodName == "last" ? "Right" : "") : methodName], retUnwrapped = isTaker || /^find/.test(methodName);
@@ -31570,11 +31575,11 @@ var require_core = __commonJS({
31570
31575
  Ajv2.ValidationError = validation_error_1.default;
31571
31576
  Ajv2.MissingRefError = ref_error_1.default;
31572
31577
  exports2.default = Ajv2;
31573
- function checkOptions(checkOpts, options2, msg, log21 = "error") {
31578
+ function checkOptions(checkOpts, options2, msg, log23 = "error") {
31574
31579
  for (const key in checkOpts) {
31575
31580
  const opt = key;
31576
31581
  if (opt in options2)
31577
- this.logger[log21](`${msg}: option ${key}. ${checkOpts[opt]}`);
31582
+ this.logger[log23](`${msg}: option ${key}. ${checkOpts[opt]}`);
31578
31583
  }
31579
31584
  }
31580
31585
  function getSchEnv(keyRef) {
@@ -34881,15 +34886,15 @@ var require_windows = __commonJS({
34881
34886
  }
34882
34887
  return false;
34883
34888
  }
34884
- function checkStat(stat4, path10, options2) {
34885
- if (!stat4.isSymbolicLink() && !stat4.isFile()) {
34889
+ function checkStat(stat5, path10, options2) {
34890
+ if (!stat5.isSymbolicLink() && !stat5.isFile()) {
34886
34891
  return false;
34887
34892
  }
34888
34893
  return checkPathExt(path10, options2);
34889
34894
  }
34890
34895
  function isexe(path10, options2, cb) {
34891
- fs6.stat(path10, function(er, stat4) {
34892
- cb(er, er ? false : checkStat(stat4, path10, options2));
34896
+ fs6.stat(path10, function(er, stat5) {
34897
+ cb(er, er ? false : checkStat(stat5, path10, options2));
34893
34898
  });
34894
34899
  }
34895
34900
  function sync(path10, options2) {
@@ -34905,20 +34910,20 @@ var require_mode = __commonJS({
34905
34910
  isexe.sync = sync;
34906
34911
  var fs6 = require("fs");
34907
34912
  function isexe(path10, options2, cb) {
34908
- fs6.stat(path10, function(er, stat4) {
34909
- cb(er, er ? false : checkStat(stat4, options2));
34913
+ fs6.stat(path10, function(er, stat5) {
34914
+ cb(er, er ? false : checkStat(stat5, options2));
34910
34915
  });
34911
34916
  }
34912
34917
  function sync(path10, options2) {
34913
34918
  return checkStat(fs6.statSync(path10), options2);
34914
34919
  }
34915
- function checkStat(stat4, options2) {
34916
- return stat4.isFile() && checkMode(stat4, options2);
34920
+ function checkStat(stat5, options2) {
34921
+ return stat5.isFile() && checkMode(stat5, options2);
34917
34922
  }
34918
- function checkMode(stat4, options2) {
34919
- var mod = stat4.mode;
34920
- var uid = stat4.uid;
34921
- var gid = stat4.gid;
34923
+ function checkMode(stat5, options2) {
34924
+ var mod = stat5.mode;
34925
+ var uid = stat5.uid;
34926
+ var gid = stat5.gid;
34922
34927
  var myUid = options2.uid !== void 0 ? options2.uid : process.getuid && process.getuid();
34923
34928
  var myGid = options2.gid !== void 0 ? options2.gid : process.getgid && process.getgid();
34924
34929
  var u = parseInt("100", 8);
@@ -36735,13 +36740,13 @@ var init_streamableHttp = __esm({
36735
36740
  this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));
36736
36741
  return;
36737
36742
  }
36738
- const delay2 = this._getNextReconnectionDelay(attemptCount);
36743
+ const delay4 = this._getNextReconnectionDelay(attemptCount);
36739
36744
  this._reconnectionTimeout = setTimeout(() => {
36740
36745
  this._startOrAuthSse(options2).catch((error48) => {
36741
36746
  this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error48 instanceof Error ? error48.message : String(error48)}`));
36742
36747
  this._scheduleReconnection(options2, attemptCount + 1);
36743
36748
  });
36744
- }, delay2);
36749
+ }, delay4);
36745
36750
  }
36746
36751
  _handleSseStream(stream, options2, isReconnectable) {
36747
36752
  if (!stream) {
@@ -40066,43 +40071,337 @@ var init_client3 = __esm({
40066
40071
  }
40067
40072
  });
40068
40073
 
40074
+ // src/shared/logging/structured-logger.ts
40075
+ function createStructuredLogger(options2) {
40076
+ return new DefaultStructuredLogger(
40077
+ normalizeScope(options2.scope),
40078
+ options2.context ?? {},
40079
+ options2.sinks ?? [new ConsoleLogSink()],
40080
+ options2.minLevel ?? resolveDefaultLevel()
40081
+ );
40082
+ }
40083
+ function normalizeLogError(error48) {
40084
+ if (error48 instanceof Error) {
40085
+ const code = typeof error48.code === "string" ? error48.code : void 0;
40086
+ return {
40087
+ name: error48.name,
40088
+ message: sanitizeErrorMessage(error48.message),
40089
+ ...code ? { code } : {},
40090
+ ...error48.stack ? { stack: sanitizeErrorStack(error48.stack) } : {},
40091
+ fingerprint: fingerprintError(error48.name, code, error48.message)
40092
+ };
40093
+ }
40094
+ if (typeof error48 === "object" && error48 !== null) {
40095
+ const obj = error48;
40096
+ const name21 = typeof obj.name === "string" ? obj.name : void 0;
40097
+ const code = typeof obj.code === "string" ? obj.code : void 0;
40098
+ const message2 = typeof obj.message === "string" ? obj.message : safeString(error48);
40099
+ const stack = typeof obj.stack === "string" ? sanitizeErrorStack(obj.stack) : void 0;
40100
+ return {
40101
+ ...name21 ? { name: name21 } : {},
40102
+ ...code ? { code } : {},
40103
+ message: sanitizeErrorMessage(message2),
40104
+ ...stack ? { stack } : {},
40105
+ fingerprint: fingerprintError(name21, code, message2)
40106
+ };
40107
+ }
40108
+ const message = safeString(error48);
40109
+ return {
40110
+ message: sanitizeErrorMessage(message),
40111
+ fingerprint: fingerprintError(void 0, void 0, message)
40112
+ };
40113
+ }
40114
+ function writeLogEventToSinks(event, sinks2) {
40115
+ const safeEvent = sanitizeLogEvent(event);
40116
+ for (const sink of sinks2) {
40117
+ try {
40118
+ sink.write(safeEvent);
40119
+ } catch {
40120
+ }
40121
+ }
40122
+ }
40123
+ function sanitizeLogEvent(event) {
40124
+ const sanitized = {
40125
+ ...event,
40126
+ scope: normalizeScope(event.scope),
40127
+ event: normalizeEventName(event.event)
40128
+ };
40129
+ if (event.message) sanitized.message = truncate(event.message, MAX_STRING_LENGTH);
40130
+ if (event.context) sanitized.context = sanitizeContext(event.context);
40131
+ if (event.data) sanitized.data = sanitizeValue(event.data, 0);
40132
+ if (event.error) sanitized.error = normalizeLogError(event.error);
40133
+ return sanitized;
40134
+ }
40135
+ function compactConsolePayload(event) {
40136
+ const payload = {};
40137
+ if (event.context) payload.context = event.context;
40138
+ if (event.message) payload.message = event.message;
40139
+ if (event.data) payload.data = event.data;
40140
+ if (event.error) payload.error = event.error;
40141
+ return payload;
40142
+ }
40143
+ function shouldLog(level, minLevel) {
40144
+ return levelRank(level) >= levelRank(minLevel);
40145
+ }
40146
+ function levelRank(level) {
40147
+ switch (level) {
40148
+ case "debug":
40149
+ return 10;
40150
+ case "info":
40151
+ return 20;
40152
+ case "warn":
40153
+ return 30;
40154
+ case "error":
40155
+ return 40;
40156
+ case "fatal":
40157
+ return 50;
40158
+ }
40159
+ }
40160
+ function resolveDefaultLevel() {
40161
+ return process.env.NODE_ENV === "development" || process.env.KAI_DEBUG ? "debug" : "info";
40162
+ }
40163
+ function sanitizeContext(context2) {
40164
+ const out = {};
40165
+ for (const [key, value] of Object.entries(context2)) {
40166
+ if (typeof value === "string" && value.trim()) out[key] = truncate(value, 200);
40167
+ }
40168
+ return out;
40169
+ }
40170
+ function sanitizeValue(value, depth) {
40171
+ if (depth > MAX_DEPTH) return "[MaxDepth]";
40172
+ if (value instanceof Error) return normalizeLogError(value);
40173
+ if (typeof value === "string") return redactString(truncate(value, MAX_STRING_LENGTH));
40174
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return value;
40175
+ if (typeof value === "undefined") return void 0;
40176
+ if (typeof value === "bigint") return value.toString();
40177
+ if (typeof value === "function") return `[Function ${value.name || "anonymous"}]`;
40178
+ if (typeof value === "symbol") return String(value);
40179
+ if (Array.isArray(value)) {
40180
+ return value.slice(0, MAX_ARRAY_LENGTH).map((item) => sanitizeValue(item, depth + 1));
40181
+ }
40182
+ if (typeof value === "object") {
40183
+ const result = {};
40184
+ let count = 0;
40185
+ for (const [key, child] of Object.entries(value)) {
40186
+ if (count >= MAX_OBJECT_KEYS) {
40187
+ result.__truncatedKeys = true;
40188
+ break;
40189
+ }
40190
+ if (SENSITIVE_KEY_PATTERN.test(key)) {
40191
+ result[key] = "[REDACTED]";
40192
+ } else {
40193
+ const sanitized = sanitizeValue(child, depth + 1);
40194
+ if (sanitized !== void 0) result[key] = sanitized;
40195
+ }
40196
+ count++;
40197
+ }
40198
+ return result;
40199
+ }
40200
+ return redactString(truncate(String(value), MAX_STRING_LENGTH));
40201
+ }
40202
+ function safeString(value) {
40203
+ try {
40204
+ return typeof value === "string" ? value : JSON.stringify(sanitizeValue(value, 0));
40205
+ } catch {
40206
+ return String(value);
40207
+ }
40208
+ }
40209
+ function sanitizeErrorMessage(message) {
40210
+ return redactString(truncate(message, 500));
40211
+ }
40212
+ function sanitizeErrorStack(stack) {
40213
+ return redactString(truncate(stack, 4e3));
40214
+ }
40215
+ function redactString(value) {
40216
+ return value.replace(/(sk-[A-Za-z0-9_-]{12,})/g, "[REDACTED_API_KEY]").replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]{12,}/gi, "$1[REDACTED]").replace(/(api[_-]?key\s*[:=]\s*)[^\s,;&]+/gi, "$1[REDACTED]").replace(/(token\s*[:=]\s*)[^\s,;&]+/gi, "$1[REDACTED]");
40217
+ }
40218
+ function truncate(value, max) {
40219
+ return value.length > max ? `${value.slice(0, max)}\u2026` : value;
40220
+ }
40221
+ function normalizeScope(scope) {
40222
+ return scope.trim().replace(/[^a-zA-Z0-9_.-]/g, ".").replace(/\.+/g, ".").slice(0, 120) || "app";
40223
+ }
40224
+ function normalizeEventName(event) {
40225
+ return event.trim().replace(/[^a-zA-Z0-9_.:-]/g, ".").replace(/\.+/g, ".").slice(0, 160) || "log.message";
40226
+ }
40227
+ function fingerprintError(name21, code, message) {
40228
+ const base = `${name21 ?? "Error"}:${code ?? "none"}:${message.slice(0, 160)}`;
40229
+ let hash2 = 0;
40230
+ for (let i = 0; i < base.length; i++) {
40231
+ hash2 = (hash2 << 5) - hash2 + base.charCodeAt(i) | 0;
40232
+ }
40233
+ return Math.abs(hash2).toString(36);
40234
+ }
40235
+ var SENSITIVE_KEY_PATTERN, MAX_STRING_LENGTH, MAX_ARRAY_LENGTH, MAX_OBJECT_KEYS, MAX_DEPTH, DefaultStructuredLogger, ConsoleLogSink;
40236
+ var init_structured_logger = __esm({
40237
+ "src/shared/logging/structured-logger.ts"() {
40238
+ "use strict";
40239
+ SENSITIVE_KEY_PATTERN = /(api[-_]?key|token|secret|password|authorization|cookie|set-cookie|access[-_]?token|refresh[-_]?token)/i;
40240
+ MAX_STRING_LENGTH = 2e3;
40241
+ MAX_ARRAY_LENGTH = 20;
40242
+ MAX_OBJECT_KEYS = 50;
40243
+ MAX_DEPTH = 4;
40244
+ DefaultStructuredLogger = class _DefaultStructuredLogger {
40245
+ constructor(scope, context2, sinks2, minLevel) {
40246
+ this.scope = scope;
40247
+ this.context = context2;
40248
+ this.sinks = sinks2;
40249
+ this.minLevel = minLevel;
40250
+ }
40251
+ scope;
40252
+ context;
40253
+ sinks;
40254
+ minLevel;
40255
+ child(context2) {
40256
+ const { scope, ...childContext } = context2;
40257
+ return new _DefaultStructuredLogger(
40258
+ scope ? normalizeScope(scope) : this.scope,
40259
+ { ...this.context, ...childContext },
40260
+ this.sinks,
40261
+ this.minLevel
40262
+ );
40263
+ }
40264
+ debug(event, data2) {
40265
+ this.write("debug", event, data2);
40266
+ }
40267
+ info(event, data2) {
40268
+ this.write("info", event, data2);
40269
+ }
40270
+ warn(event, data2) {
40271
+ this.write("warn", event, data2);
40272
+ }
40273
+ error(event, data2) {
40274
+ this.write("error", event, data2);
40275
+ }
40276
+ fatal(event, data2) {
40277
+ this.write("fatal", event, data2);
40278
+ }
40279
+ write(level, event, data2) {
40280
+ if (!shouldLog(level, this.minLevel)) return;
40281
+ const { error: error48, ...rest } = data2 ?? {};
40282
+ const logEvent = {
40283
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
40284
+ level,
40285
+ event,
40286
+ scope: this.scope,
40287
+ ...Object.keys(this.context).length > 0 ? { context: this.context } : {},
40288
+ ...Object.keys(rest).length > 0 ? { data: rest } : {},
40289
+ ...error48 !== void 0 ? { error: normalizeLogError(error48) } : {}
40290
+ };
40291
+ writeLogEventToSinks(logEvent, this.sinks);
40292
+ }
40293
+ };
40294
+ ConsoleLogSink = class {
40295
+ write(event) {
40296
+ const prefix = `[${event.scope}] ${event.event}`;
40297
+ const payload = compactConsolePayload(event);
40298
+ if (event.level === "error" || event.level === "fatal") {
40299
+ console.error(prefix, payload);
40300
+ } else if (event.level === "warn") {
40301
+ console.warn(prefix, payload);
40302
+ } else if (event.level === "info") {
40303
+ console.info(prefix, payload);
40304
+ } else {
40305
+ console.log(prefix, payload);
40306
+ }
40307
+ }
40308
+ };
40309
+ }
40310
+ });
40311
+
40312
+ // src/shared/logging/index.ts
40313
+ var init_logging = __esm({
40314
+ "src/shared/logging/index.ts"() {
40315
+ "use strict";
40316
+ init_structured_logger();
40317
+ }
40318
+ });
40319
+
40069
40320
  // src/shared/utils/logger.ts
40070
40321
  function createLogger(prefix) {
40322
+ const scope = legacyScope(prefix);
40323
+ const logger40 = createStructuredLogger({
40324
+ scope,
40325
+ sinks,
40326
+ minLevel: currentLevel <= LOG_LEVELS.noise ? "debug" : "info"
40327
+ });
40071
40328
  return {
40072
40329
  noise: (...args2) => {
40073
40330
  if (currentLevel <= LOG_LEVELS.noise) {
40074
- console.log(`[${prefix}]`, ...args2);
40331
+ writeLegacy(logger40, "debug", "legacy.noise", args2);
40075
40332
  }
40076
40333
  },
40077
40334
  debug: (...args2) => {
40078
40335
  if (currentLevel <= LOG_LEVELS.debug) {
40079
- console.log(`[${prefix}]`, ...args2);
40336
+ writeLegacy(logger40, "debug", "legacy.debug", args2);
40080
40337
  }
40081
40338
  },
40082
40339
  info: (...args2) => {
40083
- console.info(`[${prefix}]`, ...args2);
40340
+ writeLegacy(logger40, "info", "legacy.info", args2);
40084
40341
  },
40085
40342
  warn: (...args2) => {
40086
- console.warn(`[${prefix}]`, ...args2);
40343
+ writeLegacy(logger40, "warn", "legacy.warn", args2);
40087
40344
  },
40088
40345
  error: (...args2) => {
40089
- console.error(`[${prefix}]`, ...args2);
40346
+ writeLegacy(logger40, "error", "legacy.error", args2);
40090
40347
  }
40091
40348
  };
40092
40349
  }
40093
- var LOG_LEVELS, currentLevel, logger;
40350
+ function writeLegacy(logger40, level, event, args2) {
40351
+ const { message, data: data2, error: error48 } = normalizeLegacyArgs(args2);
40352
+ const payload = {
40353
+ ...message ? { message } : {},
40354
+ ...data2.length > 0 ? { args: data2 } : {},
40355
+ ...error48 ? { error: error48 } : {}
40356
+ };
40357
+ if (level === "debug") logger40.debug(event, payload);
40358
+ else if (level === "info") logger40.info(event, payload);
40359
+ else if (level === "warn") logger40.warn(event, payload);
40360
+ else if (level === "error") logger40.error(event, payload);
40361
+ else logger40.fatal(event, payload);
40362
+ }
40363
+ function normalizeLegacyArgs(args2) {
40364
+ const [first2, ...rest] = args2;
40365
+ const message = typeof first2 === "string" ? first2 : void 0;
40366
+ const values = message ? rest : args2;
40367
+ const errorIndex = values.findIndex((value) => value instanceof Error);
40368
+ const error48 = errorIndex >= 0 ? values[errorIndex] : void 0;
40369
+ const data2 = errorIndex >= 0 ? values.filter((_4, index2) => index2 !== errorIndex) : values;
40370
+ return { message, data: data2, error: error48 };
40371
+ }
40372
+ function createDefaultSinks() {
40373
+ const defaults2 = [new ConsoleLogSink()];
40374
+ return defaults2;
40375
+ }
40376
+ function resolveLegacyLogLevel() {
40377
+ const env2 = typeof process !== "undefined" ? process.env : void 0;
40378
+ return env2?.NODE_ENV === "development" || env2?.KAI_DEBUG ? LOG_LEVELS.noise : LOG_LEVELS.info;
40379
+ }
40380
+ function legacyScope(prefix) {
40381
+ return prefix.trim().replace(/([a-z0-9])([A-Z])/g, "$1.$2").replace(/[^a-zA-Z0-9_.-]/g, ".").replace(/\.+/g, ".").toLowerCase().slice(0, 120) || "legacy";
40382
+ }
40383
+ var LOG_LEVELS, currentLevel, globalLogState, sinks, structuredLogger, logger;
40094
40384
  var init_logger = __esm({
40095
40385
  "src/shared/utils/logger.ts"() {
40096
40386
  "use strict";
40387
+ init_logging();
40097
40388
  LOG_LEVELS = {
40098
40389
  noise: 0,
40099
40390
  debug: 1,
40100
- info: 2
40101
- };
40102
- currentLevel = LOG_LEVELS.info;
40103
- if (process.env.NODE_ENV === "development" || process.env.KAI_DEBUG) {
40104
- currentLevel = LOG_LEVELS.noise;
40105
- }
40391
+ info: 2,
40392
+ warn: 3,
40393
+ error: 4,
40394
+ fatal: 5
40395
+ };
40396
+ currentLevel = resolveLegacyLogLevel();
40397
+ globalLogState = globalThis;
40398
+ sinks = globalLogState.__KAI_LOG_SINKS__ ?? createDefaultSinks();
40399
+ globalLogState.__KAI_LOG_SINKS__ = sinks;
40400
+ structuredLogger = createStructuredLogger({
40401
+ scope: "global",
40402
+ sinks,
40403
+ minLevel: currentLevel <= LOG_LEVELS.noise ? "debug" : "info"
40404
+ });
40106
40405
  logger = createLogger("global");
40107
40406
  }
40108
40407
  });
@@ -56280,7 +56579,7 @@ var require_node = __commonJS({
56280
56579
  var tty = require("tty");
56281
56580
  var util2 = require("util");
56282
56581
  exports2.init = init;
56283
- exports2.log = log21;
56582
+ exports2.log = log23;
56284
56583
  exports2.formatArgs = formatArgs;
56285
56584
  exports2.save = save;
56286
56585
  exports2.load = load2;
@@ -56415,7 +56714,7 @@ var require_node = __commonJS({
56415
56714
  }
56416
56715
  return (/* @__PURE__ */ new Date()).toISOString() + " ";
56417
56716
  }
56418
- function log21(...args2) {
56717
+ function log23(...args2) {
56419
56718
  return process.stderr.write(util2.formatWithOptions(exports2.inspectOpts, ...args2) + "\n");
56420
56719
  }
56421
56720
  function save(namespaces) {
@@ -72158,9 +72457,9 @@ var require_query_generator = __commonJS({
72158
72457
  }
72159
72458
  return `json_unquote(json_extract(${quotedColumn},${pathStr}))`;
72160
72459
  case "postgres":
72161
- const join20 = isJson ? "#>" : "#>>";
72460
+ const join21 = isJson ? "#>" : "#>>";
72162
72461
  pathStr = this.escape(`{${paths.join(",")}}`);
72163
- return `(${quotedColumn}${join20}${pathStr})`;
72462
+ return `(${quotedColumn}${join21}${pathStr})`;
72164
72463
  default:
72165
72464
  throw new Error(`Unsupported ${this.dialect} for JSON operations`);
72166
72465
  }
@@ -72848,7 +73147,7 @@ https://github.com/sequelize/sequelize/discussions/15694`);
72848
73147
  const isBelongsTo = topAssociation.associationType === "BelongsTo";
72849
73148
  const sourceField = isBelongsTo ? topAssociation.identifierField : topAssociation.sourceKeyField || topParent.model.primaryKeyField;
72850
73149
  const targetField = isBelongsTo ? topAssociation.sourceKeyField || topInclude.model.primaryKeyField : topAssociation.identifierField;
72851
- const join20 = [
73150
+ const join21 = [
72852
73151
  `${this.quoteIdentifier(topInclude.as)}.${this.quoteIdentifier(targetField)}`,
72853
73152
  `${this.quoteTable(topParent.as || topParent.model.name)}.${this.quoteIdentifier(sourceField)}`
72854
73153
  ].join(" = ");
@@ -72859,7 +73158,7 @@ https://github.com/sequelize/sequelize/discussions/15694`);
72859
73158
  where: {
72860
73159
  [Op2.and]: [
72861
73160
  topInclude.where,
72862
- { [Op2.join]: this.sequelize.literal(join20) }
73161
+ { [Op2.join]: this.sequelize.literal(join21) }
72863
73162
  ]
72864
73163
  },
72865
73164
  limit: 1,
@@ -95700,12 +95999,12 @@ var require_form_data = __commonJS({
95700
95999
  if (value.end != void 0 && value.end != Infinity && value.start != void 0) {
95701
96000
  callback(null, value.end + 1 - (value.start ? value.start : 0));
95702
96001
  } else {
95703
- fs6.stat(value.path, function(err, stat4) {
96002
+ fs6.stat(value.path, function(err, stat5) {
95704
96003
  if (err) {
95705
96004
  callback(err);
95706
96005
  return;
95707
96006
  }
95708
- var fileSize = stat4.size - (value.start ? value.start : 0);
96007
+ var fileSize = stat5.size - (value.start ? value.start : 0);
95709
96008
  callback(null, fileSize);
95710
96009
  });
95711
96010
  }
@@ -104289,15 +104588,15 @@ var require_index_cjs = __commonJS({
104289
104588
  return;
104290
104589
  }
104291
104590
  this.authFailureAttempts++;
104292
- const delay2 = Math.min(this.reconnectBaseDelay * Math.pow(2, this.authFailureAttempts - 1), this.reconnectMaxDelay);
104293
- this.logger.info(`Auth failed, reconnecting in ${delay2}ms (auth attempt ${this.authFailureAttempts}/${this.maxAuthFailureAttempts})...`);
104591
+ const delay4 = Math.min(this.reconnectBaseDelay * Math.pow(2, this.authFailureAttempts - 1), this.reconnectMaxDelay);
104592
+ this.logger.info(`Auth failed, reconnecting in ${delay4}ms (auth attempt ${this.authFailureAttempts}/${this.maxAuthFailureAttempts})...`);
104294
104593
  this.onReconnecting?.(this.authFailureAttempts);
104295
104594
  this.reconnectTimer = setTimeout(() => {
104296
104595
  this.reconnectTimer = null;
104297
104596
  if (this.isManualClose)
104298
104597
  return;
104299
104598
  this.connect();
104300
- }, delay2);
104599
+ }, delay4);
104301
104600
  } else {
104302
104601
  if (this.maxReconnectAttempts !== -1 && this.reconnectAttempts >= this.maxReconnectAttempts) {
104303
104602
  this.logger.error(`Max reconnect attempts reached (${this.maxReconnectAttempts}), giving up`);
@@ -104305,15 +104604,15 @@ var require_index_cjs = __commonJS({
104305
104604
  return;
104306
104605
  }
104307
104606
  this.reconnectAttempts++;
104308
- const delay2 = Math.min(this.reconnectBaseDelay * Math.pow(2, this.reconnectAttempts - 1), this.reconnectMaxDelay);
104309
- this.logger.info(`Connection lost, reconnecting in ${delay2}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
104607
+ const delay4 = Math.min(this.reconnectBaseDelay * Math.pow(2, this.reconnectAttempts - 1), this.reconnectMaxDelay);
104608
+ this.logger.info(`Connection lost, reconnecting in ${delay4}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
104310
104609
  this.onReconnecting?.(this.reconnectAttempts);
104311
104610
  this.reconnectTimer = setTimeout(() => {
104312
104611
  this.reconnectTimer = null;
104313
104612
  if (this.isManualClose)
104314
104613
  return;
104315
104614
  this.connect();
104316
- }, delay2);
104615
+ }, delay4);
104317
104616
  }
104318
104617
  }
104319
104618
  /**
@@ -104900,9 +105199,9 @@ var require_index_cjs = __commonJS({
104900
105199
  } catch (err) {
104901
105200
  lastError = err;
104902
105201
  if (attempt < MAX_CHUNK_RETRIES) {
104903
- const delay2 = 500 * (attempt + 1);
104904
- this.logger.warn(`Chunk ${chunkIndex} upload failed (attempt ${attempt + 1}/${MAX_CHUNK_RETRIES + 1}), retrying in ${delay2}ms... error: ${err instanceof Error ? err.message : JSON.stringify(err)}`);
104905
- await new Promise((r) => setTimeout(r, delay2));
105202
+ const delay4 = 500 * (attempt + 1);
105203
+ this.logger.warn(`Chunk ${chunkIndex} upload failed (attempt ${attempt + 1}/${MAX_CHUNK_RETRIES + 1}), retrying in ${delay4}ms... error: ${err instanceof Error ? err.message : JSON.stringify(err)}`);
105204
+ await new Promise((r) => setTimeout(r, delay4));
104906
105205
  }
104907
105206
  }
104908
105207
  }
@@ -106736,19 +107035,19 @@ var require_token_io = __commonJS({
106736
107035
  getUserDataDir: () => getUserDataDir
106737
107036
  });
106738
107037
  module2.exports = __toCommonJS2(token_io_exports);
106739
- var import_path30 = __toESM2(require("path"));
107038
+ var import_path31 = __toESM2(require("path"));
106740
107039
  var import_fs22 = __toESM2(require("fs"));
106741
107040
  var import_os4 = __toESM2(require("os"));
106742
107041
  var import_token_error = require_token_error();
106743
107042
  function findRootDir() {
106744
107043
  try {
106745
107044
  let dir = process.cwd();
106746
- while (dir !== import_path30.default.dirname(dir)) {
106747
- const pkgPath = import_path30.default.join(dir, ".vercel");
107045
+ while (dir !== import_path31.default.dirname(dir)) {
107046
+ const pkgPath = import_path31.default.join(dir, ".vercel");
106748
107047
  if (import_fs22.default.existsSync(pkgPath)) {
106749
107048
  return dir;
106750
107049
  }
106751
- dir = import_path30.default.dirname(dir);
107050
+ dir = import_path31.default.dirname(dir);
106752
107051
  }
106753
107052
  } catch (e) {
106754
107053
  throw new import_token_error.VercelOidcTokenError(
@@ -106763,9 +107062,9 @@ var require_token_io = __commonJS({
106763
107062
  }
106764
107063
  switch (import_os4.default.platform()) {
106765
107064
  case "darwin":
106766
- return import_path30.default.join(import_os4.default.homedir(), "Library/Application Support");
107065
+ return import_path31.default.join(import_os4.default.homedir(), "Library/Application Support");
106767
107066
  case "linux":
106768
- return import_path30.default.join(import_os4.default.homedir(), ".local/share");
107067
+ return import_path31.default.join(import_os4.default.homedir(), ".local/share");
106769
107068
  case "win32":
106770
107069
  if (process.env.LOCALAPPDATA) {
106771
107070
  return process.env.LOCALAPPDATA;
@@ -116300,12 +116599,12 @@ function simulateReadableStream({
116300
116599
  _internal
116301
116600
  }) {
116302
116601
  var _a212;
116303
- const delay2 = (_a212 = _internal == null ? void 0 : _internal.delay) != null ? _a212 : delay;
116602
+ const delay22 = (_a212 = _internal == null ? void 0 : _internal.delay) != null ? _a212 : delay;
116304
116603
  let index2 = 0;
116305
116604
  return new ReadableStream({
116306
116605
  async pull(controller) {
116307
116606
  if (index2 < chunks.length) {
116308
- await delay2(index2 === 0 ? initialDelayInMs : chunkDelayInMs);
116607
+ await delay22(index2 === 0 ? initialDelayInMs : chunkDelayInMs);
116309
116608
  controller.enqueue(chunks[index2++]);
116310
116609
  } else {
116311
116610
  controller.close();
@@ -116521,7 +116820,7 @@ function pruneMessages({
116521
116820
  function smoothStream({
116522
116821
  delayInMs = 10,
116523
116822
  chunking = "word",
116524
- _internal: { delay: delay2 = delay } = {}
116823
+ _internal: { delay: delay22 = delay } = {}
116525
116824
  } = {}) {
116526
116825
  let detectChunk;
116527
116826
  if (chunking != null && typeof chunking === "object" && "segment" in chunking && typeof chunking.segment === "function") {
@@ -116602,7 +116901,7 @@ function smoothStream({
116602
116901
  while ((match = detectChunk(buffer)) != null) {
116603
116902
  controller.enqueue({ type, text: match, id });
116604
116903
  buffer = buffer.slice(match.length);
116605
- await delay2(delayInMs);
116904
+ await delay22(delayInMs);
116606
116905
  }
116607
116906
  }
116608
116907
  });
@@ -124030,7 +124329,7 @@ var init_message_summarizer = __esm({
124030
124329
  SUMMARIZER_MAX_TOOL_RESULT_CHARS = 2e3;
124031
124330
  SUMMARIZER_MAX_TOOL_CALL_ARGS_CHARS_WRITE = 1500;
124032
124331
  SUMMARIZER_MAX_TOOL_CALL_ARGS_CHARS_DEFAULT = 500;
124033
- WRITE_TOOL_NAMES = /* @__PURE__ */ new Set(["write", "edit", "createFile"]);
124332
+ WRITE_TOOL_NAMES = /* @__PURE__ */ new Set(["writeFile", "editFile", "write", "edit", "createFile"]);
124034
124333
  SUMMARIZER_SYSTEM_PROMPT = [
124035
124334
  "You are a continuation summarizer for an AI agent's working session.",
124036
124335
  "Your summary will replace the original conversation history \u2014 the agent can only see your summary to continue the work.",
@@ -124575,8 +124874,8 @@ function getMaxImageDimension() {
124575
124874
  return configured && configured > 0 ? configured : DEFAULT_MAX_IMAGE_DIMENSION;
124576
124875
  }
124577
124876
  async function resizeImageBuffer(buffer, maxDimension) {
124578
- const sharp = (await import("sharp")).default;
124579
- const image = sharp(buffer);
124877
+ const sharp2 = (await import("sharp")).default;
124878
+ const image = sharp2(buffer);
124580
124879
  const metadata = await image.metadata();
124581
124880
  const width = metadata.width || 0;
124582
124881
  const height = metadata.height || 0;
@@ -124589,7 +124888,7 @@ async function resizeImageBuffer(buffer, maxDimension) {
124589
124888
  };
124590
124889
  }
124591
124890
  const resizedBuffer = await image.resize(maxDimension, maxDimension, { fit: "inside", withoutEnlargement: true }).toBuffer();
124592
- const resizedMetadata = await sharp(resizedBuffer).metadata();
124891
+ const resizedMetadata = await sharp2(resizedBuffer).metadata();
124593
124892
  return {
124594
124893
  buffer: resizedBuffer,
124595
124894
  mediaType: `image/${resizedMetadata.format || metadata.format || "png"}`,
@@ -124846,21 +125145,30 @@ var FILE_PATH_TOOLS;
124846
125145
  var init_tool_args = __esm({
124847
125146
  "src/core/tools/shared/tool-args.ts"() {
124848
125147
  "use strict";
124849
- FILE_PATH_TOOLS = /* @__PURE__ */ new Set(["read", "write", "edit", "patch"]);
125148
+ FILE_PATH_TOOLS = /* @__PURE__ */ new Set(["readFile", "writeFile", "editFile", "applyPatch", "read", "write", "edit", "patch"]);
124850
125149
  }
124851
125150
  });
124852
125151
 
124853
125152
  // src/core/tools/shared/display.ts
124854
- function resolveFilePath(filePath, agentId) {
125153
+ function getWorkdirArg(args2) {
125154
+ if (!args2 || typeof args2 !== "object") return null;
125155
+ const workdir = args2.workdir;
125156
+ return typeof workdir === "string" && workdir.length > 0 ? workdir : null;
125157
+ }
125158
+ function resolveFilePath(filePath, scope) {
124855
125159
  if ((0, import_path14.isAbsolute)(filePath)) return filePath;
124856
- const baseDir = agentId ? getAgentWorkspaceDir(agentId) : process.cwd();
125160
+ const baseDir = scope?.groupId ? getGroupDir(scope.groupId) : scope?.agentId ? getAgentWorkspaceDir(scope.agentId) : process.cwd();
124857
125161
  return (0, import_path14.resolve)(baseDir, filePath);
124858
125162
  }
124859
- function normalizeToolArgsForDisplay(toolName, args2, agentId) {
125163
+ function normalizeToolArgsForDisplay(toolName, args2, agentId, groupId) {
124860
125164
  if (!isFilePathTool(toolName)) return args2;
124861
125165
  const path10 = getToolPathArg(args2);
124862
125166
  if (!path10) return args2;
124863
- return replaceToolPathArg(args2, resolveFilePath(path10, agentId));
125167
+ const workdir = getWorkdirArg(args2);
125168
+ if (workdir) {
125169
+ return replaceToolPathArg(args2, (0, import_path14.isAbsolute)(path10) ? path10 : (0, import_path14.resolve)(workdir, path10));
125170
+ }
125171
+ return replaceToolPathArg(args2, resolveFilePath(path10, { agentId, groupId }));
124864
125172
  }
124865
125173
  var import_path14;
124866
125174
  var init_display = __esm({
@@ -124868,6 +125176,7 @@ var init_display = __esm({
124868
125176
  "use strict";
124869
125177
  import_path14 = require("path");
124870
125178
  init_workspace();
125179
+ init_group_file_store();
124871
125180
  init_tool_args();
124872
125181
  }
124873
125182
  });
@@ -124948,7 +125257,7 @@ function buildSystemInfo(agentId) {
124948
125257
  `- \u4E3B\u673A\u540D\uFF1A${(0, import_os2.hostname)()}`,
124949
125258
  `- \u7528\u6237\u540D\uFF1A${(0, import_os2.userInfo)().username}`,
124950
125259
  `- \u9ED8\u8BA4Shell\uFF1A${process.env.SHELL || "/bin/sh"}`,
124951
- `- Agent\u5DE5\u4F5C\u76EE\u5F55\uFF1A${getAgentWorkspaceDir(agentId)}`,
125260
+ `- Agent\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55\uFF1A${getAgentWorkspaceDir(agentId)}`,
124952
125261
  `- \u9644\u4EF6\u76EE\u5F55\uFF1A${config.getAttachmentsPath()}`
124953
125262
  ];
124954
125263
  return `${sysInfoLines.join("\n")}
@@ -125008,10 +125317,7 @@ var init_PromptBuilder = __esm({
125008
125317
  if (workspaceContent) {
125009
125318
  sections.push(workspaceContent);
125010
125319
  }
125011
- sections.push(
125012
- buildSystemInfo(this.agent.id),
125013
- SYSTEM_RUNTIME_INSTRUCTIONS
125014
- );
125320
+ sections.push(buildSystemInfo(this.agent.id), SYSTEM_RUNTIME_INSTRUCTIONS);
125015
125321
  if (this.additionalInstructions) {
125016
125322
  sections.push(this.additionalInstructions);
125017
125323
  }
@@ -125037,7 +125343,7 @@ function buildGroupWorkspaceInstructions(groupDir) {
125037
125343
  - \`memory/YYYY-MM-DD.md\` \u2014 \u6BCF\u65E5\u8BA8\u8BBA\u8BB0\u5F55
125038
125344
  - \`docs/\` \u2014 \u5171\u4EAB\u6587\u6863
125039
125345
 
125040
- \u4F7F\u7528 \`read\`\u3001\`write\`\u3001\`edit\` \u67E5\u770B\u6216\u4FEE\u6539\u8FD9\u4E9B\u6587\u4EF6\u3002
125346
+ \u4F7F\u7528 \`readFile\`\u3001\`writeFile\`\u3001\`editFile\`\u3001\`applyPatch\` \u67E5\u770B\u6216\u4FEE\u6539\u8FD9\u4E9B\u6587\u4EF6\u3002\u8C03\u7528\u65F6 \`workdir\` \u4F7F\u7528\u7FA4\u76EE\u5F55\uFF0C\`path\` \u4F7F\u7528\u76F8\u5BF9\u7FA4\u76EE\u5F55\u7684\u8DEF\u5F84\u3002
125041
125347
 
125042
125348
  \u53D7\u4FDD\u62A4\u6587\u4EF6\uFF08\u7981\u6B62\u4FEE\u6539\uFF09\uFF1A
125043
125349
  - \`GROUP_SETTINGS.md\`
@@ -125178,7 +125484,67 @@ var init_prompts = __esm({
125178
125484
  }
125179
125485
  });
125180
125486
 
125487
+ // src/shared/crash-breadcrumb-port.ts
125488
+ function recordAppCrashBreadcrumb(event, data2, level = "info") {
125489
+ try {
125490
+ recorder?.(event, data2, level);
125491
+ } catch {
125492
+ }
125493
+ }
125494
+ var recorder;
125495
+ var init_crash_breadcrumb_port = __esm({
125496
+ "src/shared/crash-breadcrumb-port.ts"() {
125497
+ "use strict";
125498
+ }
125499
+ });
125500
+
125181
125501
  // src/core/agent/kernel/AgentExecutor.ts
125502
+ function isGlm51Thinking(ctx) {
125503
+ return ctx.model.toLowerCase().includes("glm-5.1") && Boolean(ctx.reasoningEffort && ctx.reasoningEffort !== "off");
125504
+ }
125505
+ function buildLlmRequestSettings(ctx) {
125506
+ const omitSampling = isGlm51Thinking(ctx);
125507
+ return {
125508
+ temperature: omitSampling ? void 0 : ctx.temperature,
125509
+ topP: omitSampling ? void 0 : ctx.topP,
125510
+ maxOutputTokens: resolveMaxOutputTokens(
125511
+ ctx.maxTokens,
125512
+ modelCapability.getMaxOutputTokens(ctx.model)
125513
+ ),
125514
+ providerOptions: buildProviderOptions(ctx.apiType, ctx.reasoningEffort),
125515
+ filtered: {
125516
+ temperature: omitSampling && ctx.temperature !== void 0,
125517
+ topP: omitSampling && ctx.topP !== void 0,
125518
+ reasoningMetadata: omitSampling
125519
+ }
125520
+ };
125521
+ }
125522
+ function stripUnsupportedReasoningMetadataForModel(messages, ctx) {
125523
+ if (!isGlm51Thinking(ctx)) return messages;
125524
+ let changed = false;
125525
+ const sanitized = messages.map((message) => {
125526
+ if (!Array.isArray(message.content)) return message;
125527
+ let contentChanged = false;
125528
+ const content = message.content.map((part) => {
125529
+ if (part.type !== "reasoning") return part;
125530
+ const record2 = part;
125531
+ if (!("providerOptions" in record2) && !("providerMetadata" in record2) && !("experimental_providerMetadata" in record2)) {
125532
+ return part;
125533
+ }
125534
+ const {
125535
+ providerOptions: _providerOptions,
125536
+ providerMetadata: _providerMetadata,
125537
+ experimental_providerMetadata: _experimentalProviderMetadata,
125538
+ ...rest
125539
+ } = record2;
125540
+ contentChanged = true;
125541
+ changed = true;
125542
+ return rest;
125543
+ });
125544
+ return contentChanged ? { ...message, content } : message;
125545
+ });
125546
+ return changed ? sanitized : messages;
125547
+ }
125182
125548
  function buildProviderOptions(apiType, reasoningEffort) {
125183
125549
  if (!reasoningEffort || reasoningEffort === "off") return void 0;
125184
125550
  const budgetMap = {
@@ -125232,7 +125598,18 @@ function isReasoningOnlyAssistantMessage(message) {
125232
125598
  }
125233
125599
  return hasReasoning;
125234
125600
  }
125235
- var logger17, FallbackExhaustedError, AgentExecutor, StreamContentState, TextStreamState, ReasoningStreamState;
125601
+ function getMissingToolFields(toolName, parsed) {
125602
+ const policy = TOOL_REPAIR_POLICIES[toolName];
125603
+ if (!policy || !parsed || typeof parsed !== "object") return [];
125604
+ const args2 = parsed;
125605
+ return policy.fields.filter((field) => {
125606
+ if (toolName === "applyPatch" && field === "patch") {
125607
+ return (typeof args2.patch !== "string" || args2.patch.length === 0) && (typeof args2.input !== "string" || args2.input.length === 0);
125608
+ }
125609
+ return typeof args2[field] !== "string" || args2[field].length === 0;
125610
+ });
125611
+ }
125612
+ var logger17, FallbackExhaustedError, TOOL_REPAIR_POLICIES, AgentExecutor, StreamContentState, TextStreamState, ReasoningStreamState;
125236
125613
  var init_AgentExecutor = __esm({
125237
125614
  "src/core/agent/kernel/AgentExecutor.ts"() {
125238
125615
  "use strict";
@@ -125251,6 +125628,7 @@ var init_AgentExecutor = __esm({
125251
125628
  init_execution();
125252
125629
  init_execution_deps();
125253
125630
  init_prompts();
125631
+ init_crash_breadcrumb_port();
125254
125632
  logger17 = createLogger("AgentExecutor");
125255
125633
  FallbackExhaustedError = class extends Error {
125256
125634
  constructor(attempts) {
@@ -125262,6 +125640,24 @@ var init_AgentExecutor = __esm({
125262
125640
  }
125263
125641
  attempts;
125264
125642
  };
125643
+ TOOL_REPAIR_POLICIES = {
125644
+ readFile: {
125645
+ fields: ["workdir", "path"],
125646
+ guidance: '\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177\uFF0C\u52A1\u5FC5\u5148\u8F93\u51FA "workdir" \u5B57\u6BB5\uFF0C\u518D\u8F93\u51FA "path" \u5B57\u6BB5\u3002'
125647
+ },
125648
+ writeFile: {
125649
+ fields: ["workdir", "path", "content"],
125650
+ guidance: '\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177\uFF0C\u52A1\u5FC5\u5148\u8F93\u51FA "workdir" \u5B57\u6BB5\uFF0C\u518D\u8F93\u51FA "path" \u5B57\u6BB5\uFF0C\u6700\u540E\u8F93\u51FA "content" \u5927\u6587\u672C\u5B57\u6BB5\u3002'
125651
+ },
125652
+ editFile: {
125653
+ fields: ["workdir", "path", "oldString", "newString"],
125654
+ guidance: '\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177\uFF0C\u52A1\u5FC5\u5148\u8F93\u51FA "workdir" \u5B57\u6BB5\uFF0C\u518D\u8F93\u51FA "path" \u5B57\u6BB5\uFF0C\u6700\u540E\u8F93\u51FA "oldString"/"newString" \u5927\u6587\u672C\u5B57\u6BB5\u3002'
125655
+ },
125656
+ applyPatch: {
125657
+ fields: ["workdir", "patch"],
125658
+ guidance: '\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177\uFF0C\u52A1\u5FC5\u5148\u8F93\u51FA "workdir" \u5B57\u6BB5\uFF0C\u6700\u540E\u8F93\u51FA "patch" \u5927\u6587\u672C\u5B57\u6BB5\u3002'
125659
+ }
125660
+ };
125265
125661
  AgentExecutor = class _AgentExecutor {
125266
125662
  streamCallback;
125267
125663
  lastCompactedMessages = [];
@@ -125283,6 +125679,7 @@ var init_AgentExecutor = __esm({
125283
125679
  tools;
125284
125680
  deps;
125285
125681
  runtime;
125682
+ groupId;
125286
125683
  customStopWhen;
125287
125684
  _ctx;
125288
125685
  /** 上下文压缩重试计数(独立于瞬时错误重试) */
@@ -125302,6 +125699,7 @@ var init_AgentExecutor = __esm({
125302
125699
  this.tools = options2.tools;
125303
125700
  this.deps = resolveAgentExecutionDeps(options2.deps);
125304
125701
  this.runtime = options2.runtime ?? createExecutionRuntime();
125702
+ this.groupId = options2.groupId;
125305
125703
  this.contextId = options2.contextId;
125306
125704
  this.saveContext = options2.saveContext;
125307
125705
  this.saveSeq = options2.saveSeqMessages;
@@ -125505,6 +125903,15 @@ var init_AgentExecutor = __esm({
125505
125903
  `Fallback: ${ctx.channelId}:${ctx.model} \u2192 ${targetChannel.id}:${fallbackModel}`
125506
125904
  );
125507
125905
  messages = this.getMessagesForRetry(messages);
125906
+ this.runInfoCallback?.({
125907
+ model: fallbackModel,
125908
+ fallback: true,
125909
+ fallbackTransition: {
125910
+ fromModel: ctx.model,
125911
+ toModel: fallbackModel,
125912
+ reason: this.classifyFallbackReason(error48)
125913
+ }
125914
+ });
125508
125915
  this.switchContext(targetChannel, fallbackModel);
125509
125916
  this.contextWindowOverride = void 0;
125510
125917
  this.compressionRetryCount = 0;
@@ -125560,6 +125967,19 @@ var init_AgentExecutor = __esm({
125560
125967
  if (/terminated/.test(msg)) return true;
125561
125968
  return false;
125562
125969
  }
125970
+ /**
125971
+ * 前端只需要稳定、可翻译的原因枚举;原始 provider 错误留在日志里。
125972
+ */
125973
+ classifyFallbackReason(error48) {
125974
+ const msg = error48 instanceof Error ? error48.message?.toLowerCase() ?? "" : String(error48).toLowerCase();
125975
+ if (/\b(rate\s*limit|too\s+many\s+requests|429)\b/.test(msg)) return "rate_limit";
125976
+ if (/\b(timeout|timed\s*out|etimedout|deadline)\b/.test(msg)) return "timeout";
125977
+ if (/context|token|maximum|too\s+long|length/.test(msg)) return "context_overflow";
125978
+ if (/api|provider|server|5\d\d|overloaded|unavailable|connection|econnreset|terminated/.test(msg)) {
125979
+ return "api_error";
125980
+ }
125981
+ return "unknown";
125982
+ }
125563
125983
  /**
125564
125984
  * 查找下一个可用的 fallback 条目
125565
125985
  * 排除:已尝试的、锁定的
@@ -125589,6 +126009,7 @@ var init_AgentExecutor = __esm({
125589
126009
  let latency = 0;
125590
126010
  this.runInfoCallback?.({ model: ctx.model, fallback: this.triedKeys.size > 0 });
125591
126011
  messages = this.projectMessagesForProvider(messages, ctx.apiType);
126012
+ messages = stripUnsupportedReasoningMetadataForModel(messages, ctx);
125592
126013
  messages = await multimodalTransformer.transform(messages, ctx.model);
125593
126014
  const model = this.deps.languageModelFactory.createLanguageModel(
125594
126015
  {
@@ -125605,6 +126026,25 @@ var init_AgentExecutor = __esm({
125605
126026
  if (!lastStep) return true;
125606
126027
  return (lastStep.toolCalls || []).length === 0;
125607
126028
  };
126029
+ const requestSettings = buildLlmRequestSettings(ctx);
126030
+ const streamId = genId(ID_TYPE.MSG);
126031
+ const breadcrumbBase = {
126032
+ streamId,
126033
+ contextId: this.contextId,
126034
+ threadId: this.groupId ? this.contextId : void 0,
126035
+ agentId: ctx.agentId,
126036
+ channelId: ctx.channelId,
126037
+ provider: ctx.apiType,
126038
+ model: ctx.model,
126039
+ groupId: this.groupId
126040
+ };
126041
+ recordAppCrashBreadcrumb("llm.stream.start", {
126042
+ ...breadcrumbBase,
126043
+ messageCount: messages.length,
126044
+ hasTools: Object.keys(this.tools).length > 0,
126045
+ reasoningEffort: ctx.reasoningEffort,
126046
+ filtered: requestSettings.filtered
126047
+ });
125608
126048
  const agent = new ToolLoopAgent({
125609
126049
  ...this.settings,
125610
126050
  model,
@@ -125613,15 +126053,16 @@ var init_AgentExecutor = __esm({
125613
126053
  try {
125614
126054
  const repaired = jsonrepair(toolCall.input);
125615
126055
  const parsed = JSON.parse(repaired);
125616
- if ((toolCall.toolName === "write" || toolCall.toolName === "edit") && !parsed.path) {
125617
- throw new Error('\u7F3A\u5C11 path \u5B57\u6BB5\u3002\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177\uFF0C\u52A1\u5FC5\u5148\u8F93\u51FA "path" \u5B57\u6BB5\u3002');
126056
+ const missingFields = getMissingToolFields(toolCall.toolName, parsed);
126057
+ if (missingFields.length > 0) {
126058
+ const policy = TOOL_REPAIR_POLICIES[toolCall.toolName];
126059
+ throw new Error(`\u7F3A\u5C11 ${missingFields.join("/")} \u5B57\u6BB5\u3002${policy.guidance}`);
125618
126060
  }
125619
126061
  return { ...toolCall, input: repaired };
125620
126062
  } catch (err) {
125621
- if (toolCall.toolName === "write" || toolCall.toolName === "edit") {
125622
- throw new Error(
125623
- 'JSON \u89E3\u6790\u5931\u8D25\u3002\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177\uFF0C\u52A1\u5FC5\u5148\u8F93\u51FA "path" \u5B57\u6BB5\uFF0C\u518D\u8F93\u51FA "content"/"oldString" \u7B49\u5927\u6587\u672C\u5B57\u6BB5\u3002'
125624
- );
126063
+ const policy = TOOL_REPAIR_POLICIES[toolCall.toolName];
126064
+ if (policy) {
126065
+ throw new Error(`JSON \u89E3\u6790\u5931\u8D25\u3002${policy.guidance}`);
125625
126066
  }
125626
126067
  throw new Error(`${toolCall.toolName}:\u53C2\u6570\u89E3\u6790\u51FA\u9519\uFF0C\u8BF7\u7B80\u5316\u8F93\u51FA\u6216\u4F7F\u7528\u5176\u4ED6\u5DE5\u5177\u518D\u8BD5`);
125627
126068
  }
@@ -125630,13 +126071,10 @@ var init_AgentExecutor = __esm({
125630
126071
  },
125631
126072
  stopWhen: this.customStopWhen || defaultStopWhen,
125632
126073
  tools: this.tools,
125633
- temperature: ctx.temperature,
125634
- topP: ctx.topP,
125635
- maxOutputTokens: resolveMaxOutputTokens(
125636
- ctx.maxTokens,
125637
- modelCapability.getMaxOutputTokens(ctx.model)
125638
- ),
125639
- providerOptions: buildProviderOptions(ctx.apiType, ctx.reasoningEffort)
126074
+ temperature: requestSettings.temperature,
126075
+ topP: requestSettings.topP,
126076
+ maxOutputTokens: requestSettings.maxOutputTokens,
126077
+ providerOptions: requestSettings.providerOptions
125640
126078
  });
125641
126079
  try {
125642
126080
  const result = await agent.stream({
@@ -125647,6 +126085,12 @@ var init_AgentExecutor = __esm({
125647
126085
  const steps = await result.steps;
125648
126086
  const response = await result.response;
125649
126087
  const responseUsage = await result.totalUsage;
126088
+ recordAppCrashBreadcrumb("llm.stream.end", {
126089
+ ...breadcrumbBase,
126090
+ latencyMs: Date.now() - startTime,
126091
+ stepCount: steps.length,
126092
+ usage: responseUsage
126093
+ });
125650
126094
  const allMessages = this.lastCompactedMessages;
125651
126095
  logger17.debug(
125652
126096
  `executeInternal: lastCompactedMessages=${this.lastCompactedMessages.length}, response.messages=${response.messages.length}, allMessages=${allMessages.length}`
@@ -125675,6 +126119,11 @@ var init_AgentExecutor = __esm({
125675
126119
  steps
125676
126120
  };
125677
126121
  } catch (error48) {
126122
+ recordAppCrashBreadcrumb("llm.stream.error", {
126123
+ ...breadcrumbBase,
126124
+ latencyMs: Date.now() - startTime,
126125
+ error: error48
126126
+ }, "error");
125678
126127
  if (error48 instanceof DOMException && error48.name === "AbortError" || abortSignal?.aborted) {
125679
126128
  throw error48;
125680
126129
  }
@@ -125993,7 +126442,7 @@ var init_AgentExecutor = __esm({
125993
126442
  toolCall: {
125994
126443
  id: chunk.toolCallId,
125995
126444
  name: chunk.toolName,
125996
- args: normalizeToolArgsForDisplay(chunk.toolName, chunk.input ?? {}, this.agent.id),
126445
+ args: normalizeToolArgsForDisplay(chunk.toolName, chunk.input ?? {}, this.agent.id, this.groupId),
125997
126446
  status: "pending",
125998
126447
  startedAt: meta3?.startedAt ?? Date.now()
125999
126448
  }
@@ -126031,7 +126480,8 @@ var init_AgentExecutor = __esm({
126031
126480
  args: normalizeToolArgsForDisplay(
126032
126481
  chunk.toolName,
126033
126482
  chunk.input ?? void 0,
126034
- this.agent.id
126483
+ this.agent.id,
126484
+ this.groupId
126035
126485
  ),
126036
126486
  status: "success",
126037
126487
  result: (() => {
@@ -126069,7 +126519,8 @@ var init_AgentExecutor = __esm({
126069
126519
  args: normalizeToolArgsForDisplay(
126070
126520
  chunk.toolName,
126071
126521
  chunk.input ?? void 0,
126072
- this.agent.id
126522
+ this.agent.id,
126523
+ this.groupId
126073
126524
  ),
126074
126525
  status: "error",
126075
126526
  error: String(chunk.error),
@@ -130193,7 +130644,9 @@ function mergeUsage(...usages) {
130193
130644
  return {
130194
130645
  inputTokens: validUsages.reduce((sum, u) => sum + (u.inputTokens || 0), 0),
130195
130646
  outputTokens: validUsages.reduce((sum, u) => sum + (u.outputTokens || 0), 0),
130196
- totalTokens: validUsages.reduce((sum, u) => sum + (u.totalTokens || 0), 0)
130647
+ totalTokens: validUsages.reduce((sum, u) => sum + (u.totalTokens || 0), 0),
130648
+ cachedInputTokens: validUsages.reduce((sum, u) => sum + (u.cachedInputTokens || 0), 0),
130649
+ reasoningTokens: validUsages.reduce((sum, u) => sum + (u.reasoningTokens || 0), 0)
130197
130650
  };
130198
130651
  }
130199
130652
  var init_message = __esm({
@@ -130585,6 +131038,9 @@ var init_PlanCoordinator = __esm({
130585
131038
  const result = await this.deps.taskOrchestrator.runPlanStepInternal(abortSignal, retryTodoIds);
130586
131039
  return this.buildCheckpoint(plan, result, checkpointIndex);
130587
131040
  }
131041
+ buildProgressSnapshot() {
131042
+ return this.buildTodoProgress(this.requirePlan().todos);
131043
+ }
130588
131044
  buildCheckpoint(plan, step, checkpointIndex) {
130589
131045
  const todoProgress = this.buildTodoProgress(plan.todos);
130590
131046
  const failure = this.findFailure(step.results, plan.todos);
@@ -130969,7 +131425,8 @@ var init_TaskOrchestrator = __esm({
130969
131425
  tools: allTools,
130970
131426
  stopWhen: createTaskStopWhen(),
130971
131427
  deps: this.agentDeps,
130972
- runtime
131428
+ runtime,
131429
+ groupId: this.deps.thread.groupId
130973
131430
  });
130974
131431
  return [taskExecutor, input];
130975
131432
  }
@@ -131132,7 +131589,9 @@ var init_TaskOrchestrator = __esm({
131132
131589
  };
131133
131590
  },
131134
131591
  broadcast: async (options2) => {
131135
- return this.executeBroadcast(options2.question, options2.targetAgentIds, abortSignal);
131592
+ return this.executeBroadcast(options2.question, options2.targetAgentIds, abortSignal, {
131593
+ contextMessages: this.deps.getInheritedMessages()
131594
+ });
131136
131595
  },
131137
131596
  listTasks: async () => {
131138
131597
  return {
@@ -131328,12 +131787,13 @@ var init_TaskOrchestrator = __esm({
131328
131787
  *
131329
131788
  * @returns 各 Agent 的回答内容(供调用方 — 通常是 LLM — 汇总使用)
131330
131789
  */
131331
- async executeBroadcast(question, targetAgentIds, abortSignal) {
131790
+ async executeBroadcast(question, targetAgentIds, abortSignal, options2 = {}) {
131332
131791
  const uniqueTargetAgentIds = Array.from(new Set(targetAgentIds));
131333
131792
  const { pass, reason } = this.deps.context.canBroadcast(uniqueTargetAgentIds.length);
131334
131793
  if (!pass) {
131335
131794
  return {
131336
- responses: [{ agentId: "", agentName: "", content: `\u5E7F\u64AD\u5931\u8D25\uFF1A${reason}` }]
131795
+ responses: [{ agentId: "", agentName: "", content: `\u5E7F\u64AD\u5931\u8D25\uFF1A${reason}`, status: "failed", error: reason }],
131796
+ error: reason
131337
131797
  };
131338
131798
  }
131339
131799
  const broadcastId = genId(ID_TYPE.BROADCAST);
@@ -131344,6 +131804,17 @@ var init_TaskOrchestrator = __esm({
131344
131804
  const agent = await this.deps.thread.getGroup().findMember(agentId);
131345
131805
  if (!agent) {
131346
131806
  invalidIds.push(agentId);
131807
+ responses.push({
131808
+ agentId,
131809
+ agentName: agentId,
131810
+ status: "failed",
131811
+ blocks: [{
131812
+ type: "text",
131813
+ text: "\u76EE\u6807\u6210\u5458\u4E0D\u5B58\u5728\uFF0C\u5DF2\u8DF3\u8FC7\u8BE5\u76EE\u6807\uFF0C\u8BF7\u4ECE\u76EE\u6807\u5217\u8868\u4E2D\u79FB\u9664\u65E0\u6548 agentId \u540E\u518D\u8BD5\u3002",
131814
+ id: `broadcast-error-${agentId}`
131815
+ }],
131816
+ error: `Agent ${agentId} \u4E0D\u5728\u5F53\u524D\u7FA4\u804A\u6210\u5458\u4E2D\u3002\u5F53\u524D\u6210\u5458 ID\uFF1A${memberIds.join(", ")}\u3002`
131817
+ });
131347
131818
  continue;
131348
131819
  }
131349
131820
  responses.push({
@@ -131354,15 +131825,6 @@ var init_TaskOrchestrator = __esm({
131354
131825
  blocks: []
131355
131826
  });
131356
131827
  }
131357
- if (invalidIds.length > 0) {
131358
- return {
131359
- responses: [{
131360
- agentId: "",
131361
- agentName: "",
131362
- content: `\u5E7F\u64AD\u5931\u8D25\uFF1AAgent ${invalidIds.join(", ")} \u4E0D\u5728\u5F53\u524D\u7FA4\u804A\u6210\u5458\u4E2D\u3002\u5F53\u524D\u6210\u5458 ID\uFF1A${memberIds.join(", ")}\u3002\u8BF7\u68C0\u67E5 agentId \u662F\u5426\u6B63\u786E\u3002`
131363
- }]
131364
- };
131365
- }
131366
131828
  const emitBroadcastBlock = (status) => {
131367
131829
  this.deps.onStream({
131368
131830
  type: "broadcast",
@@ -131377,11 +131839,13 @@ var init_TaskOrchestrator = __esm({
131377
131839
  });
131378
131840
  };
131379
131841
  emitBroadcastBlock("pending");
131380
- const validResponses = new Map(responses.map((response) => [response.agentId, response]));
131842
+ const validResponses = new Map(
131843
+ responses.filter((response) => response.status !== "failed").map((response) => [response.agentId, response])
131844
+ );
131381
131845
  const executors = uniqueTargetAgentIds.map(async (agentId) => {
131382
131846
  const response = validResponses.get(agentId);
131383
131847
  if (!response) {
131384
- return { agentId, agentName: "", content: "\u6267\u884C\u5931\u8D25\uFF1AAgent \u4E0D\u5728\u7FA4\u804A\u6210\u5458\u4E2D" };
131848
+ return { agentId, agentName: "", content: "\u76EE\u6807\u6210\u5458\u4E0D\u5B58\u5728\uFF0C\u5DF2\u8DF3\u8FC7\u8BE5\u76EE\u6807\u3002", status: "failed", error: "Agent \u4E0D\u5728\u5F53\u524D\u7FA4\u804A\u6210\u5458\u4E2D" };
131385
131849
  }
131386
131850
  response.status = "thinking";
131387
131851
  emitBroadcastBlock("running");
@@ -131391,7 +131855,7 @@ var init_TaskOrchestrator = __esm({
131391
131855
  response.status = "failed";
131392
131856
  response.blocks = [{ type: "text", text: `Agent ${agentId} \u4E0D\u5728\u5F53\u524D\u7FA4\u804A\u6210\u5458\u4E2D`, id: `broadcast-error-${agentId}` }];
131393
131857
  emitBroadcastBlock("running");
131394
- return { agentId, agentName: response.agentName, content: "\u6267\u884C\u5931\u8D25\uFF1AAgent \u4E0D\u5728\u7FA4\u804A\u6210\u5458\u4E2D" };
131858
+ return { agentId, agentName: response.agentName, content: "\u6267\u884C\u5931\u8D25\uFF1AAgent \u4E0D\u5728\u5F53\u524D\u7FA4\u804A\u6210\u5458\u4E2D", status: "failed", error: "Agent \u4E0D\u5728\u5F53\u524D\u7FA4\u804A\u6210\u5458\u4E2D" };
131395
131859
  }
131396
131860
  const agentConfig = await agent.getConfig();
131397
131861
  const runtime = this.deps.getRuntime?.() ?? createExecutionRuntime();
@@ -131405,11 +131869,12 @@ var init_TaskOrchestrator = __esm({
131405
131869
  runtime
131406
131870
  );
131407
131871
  const skills = await this.deps.getSkills(agent);
131872
+ const contextMessages = this.deps.removeSystem(options2.contextMessages ?? []);
131408
131873
  const broadcastPrompt = [
131409
131874
  `\u4F60\u662F @${agent.name}\u3002${agent.description || ""}`,
131410
131875
  "",
131411
131876
  `# \u5E7F\u64AD\u95EE\u9898`,
131412
- `\u4EE5\u4E0B\u662F\u6765\u81EA\u56E2\u961F\u7684\u5E7F\u64AD\u95EE\u9898\uFF0C\u8BF7\u57FA\u4E8E\u4F60\u7684\u4E13\u4E1A\u89D2\u8272\u548C\u89C6\u89D2\u72EC\u7ACB\u56DE\u7B54\u3002`,
131877
+ `\u4EE5\u4E0B\u662F\u6765\u81EA\u56E2\u961F\u7684\u5E7F\u64AD\u95EE\u9898\u3002\u8BF7\u5148\u7406\u89E3\u4E0A\u9762\u7684\u5F53\u524D\u8BA8\u8BBA\u4E0A\u4E0B\u6587\uFF0C\u518D\u57FA\u4E8E\u4F60\u7684\u4E13\u4E1A\u89D2\u8272\u548C\u89C6\u89D2\u72EC\u7ACB\u56DE\u7B54\u3002`,
131413
131878
  "",
131414
131879
  `## \u95EE\u9898`,
131415
131880
  question
@@ -131424,6 +131889,7 @@ var init_TaskOrchestrator = __esm({
131424
131889
  mode: "broadcast"
131425
131890
  })
131426
131891
  },
131892
+ ...contextMessages,
131427
131893
  {
131428
131894
  role: "user",
131429
131895
  content: broadcastPrompt
@@ -131439,7 +131905,8 @@ var init_TaskOrchestrator = __esm({
131439
131905
  tools,
131440
131906
  stopWhen: createTaskStopWhen(),
131441
131907
  deps: this.agentDeps,
131442
- runtime
131908
+ runtime,
131909
+ groupId: this.deps.thread.groupId
131443
131910
  });
131444
131911
  executor.onStream((block) => {
131445
131912
  const existingIdx = response.blocks.findIndex(
@@ -131464,23 +131931,29 @@ var init_TaskOrchestrator = __esm({
131464
131931
  (msg) => msg.role === "assistant"
131465
131932
  );
131466
131933
  const content = extractAssistantText(lastAssistant?.content) || "\uFF08\u65E0\u6587\u672C\u56DE\u590D\uFF09";
131467
- return { agentId, agentName: agent.name, content };
131934
+ return { agentId, agentName: agent.name, content, status: "done" };
131468
131935
  } catch (err) {
131469
131936
  response.status = "failed";
131937
+ const errorText = `\u6267\u884C\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`;
131470
131938
  response.blocks = [
131471
131939
  {
131472
131940
  type: "text",
131473
- text: `\u6267\u884C\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`,
131941
+ text: errorText,
131474
131942
  id: `broadcast-error-${agentId}`
131475
131943
  }
131476
131944
  ];
131945
+ response.error = errorText;
131477
131946
  emitBroadcastBlock("running");
131478
- return { agentId, agentName: response.agentName, content: "\u6267\u884C\u5931\u8D25" };
131947
+ return { agentId, agentName: response.agentName, content: "\u6267\u884C\u5931\u8D25", status: "failed", error: errorText };
131479
131948
  }
131480
131949
  });
131481
131950
  const results = await Promise.all(executors);
131482
131951
  emitBroadcastBlock("done");
131483
- return { responses: results };
131952
+ const failedCount = results.filter((result) => result.status === "failed").length;
131953
+ return {
131954
+ responses: results,
131955
+ ...failedCount === results.length && results.length > 0 ? { error: "\u5E7F\u64AD\u5168\u90E8\u5931\u8D25" } : {}
131956
+ };
131484
131957
  }
131485
131958
  };
131486
131959
  }
@@ -131936,7 +132409,8 @@ var init_SwarmExecutor = __esm({
131936
132409
  const { responses } = await this.taskOrchestrator.executeBroadcast(
131937
132410
  question,
131938
132411
  targetIds,
131939
- abortSignal
132412
+ abortSignal,
132413
+ { contextMessages: resultMessages }
131940
132414
  );
131941
132415
  const broadcastResultMessage = {
131942
132416
  role: "assistant",
@@ -132170,7 +132644,8 @@ var init_SwarmExecutor = __esm({
132170
132644
  tools: { ...tools, ...swarmTools },
132171
132645
  stopWhen: createSwarmStopWhen(),
132172
132646
  deps: this.agentDeps,
132173
- runtime
132647
+ runtime,
132648
+ groupId: this.thread.groupId
132174
132649
  });
132175
132650
  if (options2?.suppressStream) {
132176
132651
  executor.onStream((block) => options2?.onBlock?.(block));
@@ -132207,10 +132682,8 @@ var init_SwarmExecutor = __esm({
132207
132682
  * 监督者模式执行流程
132208
132683
  *
132209
132684
  * 1. 阶段 1: Planning — supervisor 调用 plan 工具制定计划(不执行任务)
132210
- * 2. 阶段 2+3: Execute & Review 循环
132211
- * - 每轮注入 plan + 进度上下文
132212
- * - supervisor 执行任务或调用 evaluate_result 评审
132213
- * - achieved=true → 结束;否则继续下一轮
132685
+ * 2. 阶段 2: Runtime 自动调度 ready todos 并等待本批收口
132686
+ * 3. 阶段 3: Supervisor 只在 checkpoint 调用 verdict 做语义裁决
132214
132687
  */
132215
132688
  async executeSupervised(messages, supervisor, abortSignal) {
132216
132689
  let totalUsage;
@@ -132462,7 +132935,8 @@ var init_SwarmExecutor = __esm({
132462
132935
  tools: { ...tools, ...swarmTools },
132463
132936
  stopWhen,
132464
132937
  deps: this.agentDeps,
132465
- runtime
132938
+ runtime,
132939
+ groupId: this.thread.groupId
132466
132940
  });
132467
132941
  if (this.streamCallback) {
132468
132942
  executor.onStream(this.streamCallback);
@@ -133441,8 +133915,139 @@ var init_error_classifier = __esm({
133441
133915
  }
133442
133916
  });
133443
133917
 
133918
+ // src/core/message/queue/StreamingPersistScheduler.ts
133919
+ var log15, DEFAULT_PERSIST_INTERVAL_MS, StreamingPersistScheduler;
133920
+ var init_StreamingPersistScheduler = __esm({
133921
+ "src/core/message/queue/StreamingPersistScheduler.ts"() {
133922
+ "use strict";
133923
+ init_logger();
133924
+ log15 = createLogger("StreamingPersistScheduler");
133925
+ DEFAULT_PERSIST_INTERVAL_MS = 500;
133926
+ StreamingPersistScheduler = class {
133927
+ constructor(options2) {
133928
+ this.options = options2;
133929
+ }
133930
+ options;
133931
+ skeletonPersisted = false;
133932
+ skeletonPromise = null;
133933
+ inflightUpdate = null;
133934
+ dirty = false;
133935
+ timer = null;
133936
+ flushed = false;
133937
+ flushPromise = null;
133938
+ notify() {
133939
+ if (this.flushed) return;
133940
+ if (!this.skeletonPersisted && !this.skeletonPromise) {
133941
+ this.startSkeletonInsert();
133942
+ return;
133943
+ }
133944
+ this.dirty = true;
133945
+ if (this.skeletonPersisted) {
133946
+ this.scheduleUpdate();
133947
+ }
133948
+ }
133949
+ flush() {
133950
+ if (!this.flushPromise) {
133951
+ this.flushPromise = this.doFlush();
133952
+ }
133953
+ return this.flushPromise;
133954
+ }
133955
+ async doFlush() {
133956
+ this.flushed = true;
133957
+ this.clearTimer();
133958
+ await this.skeletonPromise;
133959
+ await this.inflightUpdate;
133960
+ if (this.dirty && this.skeletonPersisted) {
133961
+ this.dirty = false;
133962
+ await this.runFinalUpdate();
133963
+ }
133964
+ }
133965
+ get hasPersistedSkeleton() {
133966
+ return this.skeletonPersisted;
133967
+ }
133968
+ cancel() {
133969
+ this.flushed = true;
133970
+ this.flushPromise ??= Promise.resolve();
133971
+ this.clearTimer();
133972
+ }
133973
+ startSkeletonInsert() {
133974
+ const snapshot = this.cloneBlocks();
133975
+ const promise2 = this.options.insertSkeleton(snapshot).then(() => {
133976
+ this.skeletonPersisted = true;
133977
+ log15.noise(`streaming skeleton persisted, aiMessageId=${this.options.messageId}`);
133978
+ }).catch((error48) => {
133979
+ log15.warn(`streaming skeleton insert failed, aiMessageId=${this.options.messageId}`, error48);
133980
+ }).finally(() => {
133981
+ this.skeletonPromise = null;
133982
+ if (this.dirty && this.skeletonPersisted && !this.flushed) {
133983
+ this.scheduleUpdate();
133984
+ }
133985
+ });
133986
+ this.skeletonPromise = promise2;
133987
+ this.track(promise2, `streaming-skeleton:${this.options.messageId}`);
133988
+ }
133989
+ scheduleUpdate() {
133990
+ if (this.flushed || this.timer || this.inflightUpdate) return;
133991
+ this.timer = setTimeout(() => {
133992
+ this.timer = null;
133993
+ this.startUpdate();
133994
+ }, this.options.persistIntervalMs ?? DEFAULT_PERSIST_INTERVAL_MS);
133995
+ }
133996
+ startUpdate() {
133997
+ if (this.flushed || !this.skeletonPersisted || !this.dirty || this.inflightUpdate) return;
133998
+ const snapshot = this.cloneBlocks();
133999
+ if (snapshot.length === 0) return;
134000
+ this.dirty = false;
134001
+ const promise2 = this.options.updateBlocks(snapshot).catch((error48) => {
134002
+ this.dirty = true;
134003
+ log15.noise(`streaming update failed, aiMessageId=${this.options.messageId}`, error48);
134004
+ }).finally(() => {
134005
+ this.inflightUpdate = null;
134006
+ if (this.dirty && !this.flushed) {
134007
+ this.scheduleUpdate();
134008
+ }
134009
+ });
134010
+ this.inflightUpdate = promise2;
134011
+ this.track(promise2, `streaming-update:${this.options.messageId}`);
134012
+ }
134013
+ async runFinalUpdate() {
134014
+ const snapshot = this.cloneBlocks();
134015
+ if (snapshot.length === 0) return;
134016
+ const promise2 = this.options.updateBlocks(snapshot);
134017
+ this.track(promise2, `streaming-final-update:${this.options.messageId}`);
134018
+ try {
134019
+ await promise2;
134020
+ } catch (error48) {
134021
+ log15.warn(`streaming final flush failed, aiMessageId=${this.options.messageId}`, error48);
134022
+ }
134023
+ }
134024
+ cloneBlocks() {
134025
+ return structuredClone(this.options.getBlocks());
134026
+ }
134027
+ clearTimer() {
134028
+ if (!this.timer) return;
134029
+ clearTimeout(this.timer);
134030
+ this.timer = null;
134031
+ }
134032
+ track(promise2, label) {
134033
+ this.options.pendingDbWrites?.track(promise2, label);
134034
+ }
134035
+ };
134036
+ }
134037
+ });
134038
+
133444
134039
  // src/core/message/queue/MessageProcessor.ts
133445
- var log15, MAX_RETRIES, RETRY_DELAY_BASE, MessageProcessor;
134040
+ function normalizeUsage(usage) {
134041
+ const normalized = {};
134042
+ for (const key of TOKEN_USAGE_KEYS) {
134043
+ const value = usage[key];
134044
+ if (typeof value === "number") {
134045
+ normalized[key] = value;
134046
+ }
134047
+ }
134048
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
134049
+ }
134050
+ var log16, TOKEN_USAGE_KEYS, MAX_RETRIES, RETRY_DELAY_BASE, MessageProcessor;
133446
134051
  var init_MessageProcessor = __esm({
133447
134052
  "src/core/message/queue/MessageProcessor.ts"() {
133448
134053
  "use strict";
@@ -133454,16 +134059,26 @@ var init_MessageProcessor = __esm({
133454
134059
  init_logger();
133455
134060
  init_error_classifier();
133456
134061
  init_message_deps();
133457
- log15 = createLogger("MessageProcessor");
134062
+ init_StreamingPersistScheduler();
134063
+ log16 = createLogger("MessageProcessor");
134064
+ TOKEN_USAGE_KEYS = [
134065
+ "inputTokens",
134066
+ "outputTokens",
134067
+ "totalTokens",
134068
+ "cachedInputTokens",
134069
+ "reasoningTokens"
134070
+ ];
133458
134071
  MAX_RETRIES = 3;
133459
134072
  RETRY_DELAY_BASE = 1e3;
133460
134073
  MessageProcessor = class {
133461
- constructor(callbacks, messageQueue2) {
134074
+ constructor(callbacks, messageQueue2, pendingDbWrites) {
133462
134075
  this.callbacks = callbacks;
133463
134076
  this.messageQueue = messageQueue2;
134077
+ this.pendingDbWrites = pendingDbWrites;
133464
134078
  }
133465
134079
  callbacks;
133466
134080
  messageQueue;
134081
+ pendingDbWrites;
133467
134082
  /**
133468
134083
  * 处理消息。
133469
134084
  *
@@ -133481,10 +134096,12 @@ var init_MessageProcessor = __esm({
133481
134096
  agentId = conversationId;
133482
134097
  }
133483
134098
  if (!agentId) {
133484
- log15.error(`no agentId found for conversation ${conversationId}`);
134099
+ log16.error(`no agentId found for conversation ${conversationId}`);
133485
134100
  return { success: false };
133486
134101
  }
133487
134102
  const aiMessageId = aiMessageIdOverride || genId(ID_TYPE.MSG);
134103
+ const createdAt = Date.now();
134104
+ const startTime = Date.now();
133488
134105
  const ctx = {
133489
134106
  conversationId,
133490
134107
  thread,
@@ -133492,20 +134109,38 @@ var init_MessageProcessor = __esm({
133492
134109
  metadata,
133493
134110
  agentId,
133494
134111
  runId: genId(ID_TYPE.RUN),
133495
- aiMessageId: genId(ID_TYPE.MSG),
133496
- createdAt: Date.now(),
133497
- startTime: Date.now(),
134112
+ aiMessageId,
134113
+ createdAt,
134114
+ startTime,
133498
134115
  streamingBlocks: [],
133499
134116
  retryCount,
133500
134117
  model: void 0,
133501
134118
  fallback: void 0,
133502
134119
  streamingUsage: {},
133503
- streamingPersisted: false,
133504
- blockCountSincePersist: 0,
134120
+ persistScheduler: new StreamingPersistScheduler({
134121
+ messageId: aiMessageId,
134122
+ getBlocks: () => ctx.streamingBlocks,
134123
+ insertSkeleton: async (blocks) => {
134124
+ await messageRepository2.insertWithId(
134125
+ {
134126
+ conversationId,
134127
+ senderId: agentId,
134128
+ senderRole: "agent",
134129
+ blocks
134130
+ },
134131
+ { id: aiMessageId, createdAt, updatedAt: Date.now() }
134132
+ );
134133
+ },
134134
+ updateBlocks: (blocks) => messageRepository2.update(aiMessageId, {
134135
+ blocks,
134136
+ updatedAt: Date.now()
134137
+ }),
134138
+ pendingDbWrites: this.pendingDbWrites
134139
+ }),
133505
134140
  isThread
133506
134141
  };
133507
134142
  this.messageQueue?.registerRunId(ctx.runId, conversationId);
133508
- log15.noise(`runId=${ctx.runId}, agentId=${agentId}, isThread=${isThread}`);
134143
+ log16.noise(`runId=${ctx.runId}, agentId=${agentId}, isThread=${isThread}`);
133509
134144
  try {
133510
134145
  return await this.executeAgent(ctx);
133511
134146
  } catch (err) {
@@ -133538,7 +134173,7 @@ var init_MessageProcessor = __esm({
133538
134173
  if (isThread) {
133539
134174
  const mgr = await this.callbacks.getThreadManager(conversationId);
133540
134175
  if (!mgr) {
133541
- log15.error(`thread manager not found, threadId=${conversationId}`);
134176
+ log16.error(`thread manager not found, threadId=${conversationId}`);
133542
134177
  this.callbacks.unregisterAbortController(conversationId);
133543
134178
  return { success: false };
133544
134179
  }
@@ -133563,18 +134198,20 @@ var init_MessageProcessor = __esm({
133563
134198
  streamingBlocks.push(block);
133564
134199
  }
133565
134200
  messageCache.set(aiMessageId, conversationId, streamingBlocks);
133566
- this.persistStreamingBlock(ctx);
134201
+ ctx.persistScheduler.notify();
133567
134202
  });
133568
134203
  chat.onRunInfo((info) => {
133569
134204
  ctx.model = info.model;
133570
134205
  ctx.fallback = info.fallback;
134206
+ ctx.fallbackTransition = info.fallbackTransition ?? ctx.fallbackTransition;
133571
134207
  eventBus.emit({
133572
134208
  type: "run.progress",
133573
134209
  conversationId,
133574
134210
  runId,
133575
134211
  messageId: aiMessageId,
133576
134212
  model: info.model,
133577
- fallback: info.fallback
134213
+ fallback: info.fallback,
134214
+ fallbackTransition: info.fallbackTransition
133578
134215
  });
133579
134216
  });
133580
134217
  chat.onUsageUpdate((usage2) => {
@@ -133583,13 +134220,6 @@ var init_MessageProcessor = __esm({
133583
134220
  const PROGRESS_INTERVAL_MS = 3e3;
133584
134221
  ctx.progressTimer = setInterval(() => {
133585
134222
  if (abortController.signal.aborted) return;
133586
- const u = ctx.streamingUsage;
133587
- const hasUsage = (u.inputTokens ?? 0) > 0 || (u.outputTokens ?? 0) > 0;
133588
- const usage2 = hasUsage ? {
133589
- inputTokens: u.inputTokens ?? void 0,
133590
- outputTokens: u.outputTokens ?? void 0,
133591
- totalTokens: u.totalTokens ?? void 0
133592
- } : void 0;
133593
134223
  eventBus.emit({
133594
134224
  type: "run.progress",
133595
134225
  conversationId,
@@ -133597,21 +134227,23 @@ var init_MessageProcessor = __esm({
133597
134227
  messageId: aiMessageId,
133598
134228
  model: ctx.model,
133599
134229
  fallback: ctx.fallback,
133600
- usage: usage2
134230
+ fallbackTransition: ctx.fallbackTransition,
134231
+ usage: normalizeUsage(ctx.streamingUsage)
133601
134232
  });
133602
134233
  }, PROGRESS_INTERVAL_MS);
133603
- log15.noise(`calling chat.reply, conversationId=${conversationId}`);
134234
+ log16.noise(`calling chat.reply, conversationId=${conversationId}`);
133604
134235
  const { usage, model, fallback, finalAgentId } = await chat.reply(message, abortController.signal, metadata);
133605
134236
  if (finalAgentId) {
133606
134237
  ctx.agentId = finalAgentId;
133607
134238
  }
133608
134239
  const durationMs = Date.now() - startTime;
133609
- log15.noise(`chat.reply completed, conversationId=${conversationId}, usage=`, usage, "model=", model, "fallback=", fallback, "durationMs=", durationMs);
134240
+ log16.noise(`chat.reply completed, conversationId=${conversationId}, usage=`, usage, "model=", model, "fallback=", fallback, "durationMs=", durationMs);
133610
134241
  if (ctx.progressTimer) {
133611
134242
  clearInterval(ctx.progressTimer);
133612
134243
  ctx.progressTimer = void 0;
133613
134244
  }
133614
- if (ctx.streamingPersisted) {
134245
+ await ctx.persistScheduler.flush();
134246
+ if (ctx.persistScheduler.hasPersistedSkeleton) {
133615
134247
  await messageRepository2.update(aiMessageId, {
133616
134248
  blocks: streamingBlocks,
133617
134249
  senderId: ctx.agentId,
@@ -133649,7 +134281,7 @@ var init_MessageProcessor = __esm({
133649
134281
  model,
133650
134282
  fallback
133651
134283
  });
133652
- log15.noise(`run.completed, conversationId=${conversationId}, runId=${runId}`);
134284
+ log16.noise(`run.completed, conversationId=${conversationId}, runId=${runId}`);
133653
134285
  this.callbacks.unregisterAbortController(conversationId);
133654
134286
  return { success: true };
133655
134287
  }
@@ -133662,7 +134294,7 @@ var init_MessageProcessor = __esm({
133662
134294
  clearInterval(ctx.progressTimer);
133663
134295
  ctx.progressTimer = void 0;
133664
134296
  }
133665
- log15.error(`error occurred, conversationId=${conversationId}, runId=${runId}, error=`, err);
134297
+ log16.error(`error occurred, conversationId=${conversationId}, runId=${runId}, error=`, err);
133666
134298
  this.callbacks.unregisterAbortController(conversationId);
133667
134299
  try {
133668
134300
  if (err instanceof DOMException ? err.name === "AbortError" : err instanceof Error && err.name === "AbortError") {
@@ -133679,9 +134311,9 @@ var init_MessageProcessor = __esm({
133679
134311
  }
133680
134312
  if (isRetryableError(err) && retryCount < MAX_RETRIES) {
133681
134313
  const attempt = retryCount + 1;
133682
- const delay2 = RETRY_DELAY_BASE * Math.pow(2, retryCount);
133683
- log15.warn(
133684
- `[Retry] conversationId=${conversationId}, runId=${runId}, messageId=${aiMessageId}, attempt=${attempt}/${MAX_RETRIES}, delay=${delay2}ms, error=${getErrorMessage3(err)}`
134314
+ const delay4 = RETRY_DELAY_BASE * Math.pow(2, retryCount);
134315
+ log16.warn(
134316
+ `[Retry] conversationId=${conversationId}, runId=${runId}, messageId=${aiMessageId}, attempt=${attempt}/${MAX_RETRIES}, delay=${delay4}ms, error=${getErrorMessage3(err)}`
133685
134317
  );
133686
134318
  eventBus.emit({
133687
134319
  type: "run.retrying",
@@ -133690,12 +134322,12 @@ var init_MessageProcessor = __esm({
133690
134322
  messageId: aiMessageId,
133691
134323
  attempt,
133692
134324
  maxAttempts: MAX_RETRIES,
133693
- delay: delay2
134325
+ delay: delay4
133694
134326
  });
133695
134327
  return {
133696
134328
  success: false,
133697
134329
  shouldRetry: true,
133698
- retryDelay: delay2,
134330
+ retryDelay: delay4,
133699
134331
  aiMessageId
133700
134332
  };
133701
134333
  }
@@ -133719,53 +134351,17 @@ var init_MessageProcessor = __esm({
133719
134351
  messageCache.delete(aiMessageId);
133720
134352
  }
133721
134353
  }
133722
- /**
133723
- * 流式持久化:第一个 block 时 insert 骨架,之后每 2 个 block update 一次。
133724
- * 防止中途崩溃导致消息丢失。
133725
- */
133726
- persistStreamingBlock(ctx) {
133727
- const { streamingBlocks, streamingPersisted, aiMessageId, conversationId, agentId, createdAt } = ctx;
133728
- const { messageRepository: messageRepository2 } = resolveMessageDeps();
133729
- if (!streamingPersisted) {
133730
- ctx.streamingPersisted = true;
133731
- ctx.blockCountSincePersist = 0;
133732
- messageRepository2.insertWithId(
133733
- {
133734
- conversationId,
133735
- senderId: agentId,
133736
- senderRole: "agent",
133737
- blocks: streamingBlocks
133738
- },
133739
- { id: aiMessageId, createdAt, updatedAt: Date.now() }
133740
- ).then(() => {
133741
- log15.noise(`streaming skeleton persisted, aiMessageId=${aiMessageId}`);
133742
- }).catch((err) => {
133743
- log15.warn(`streaming skeleton insert failed, aiMessageId=${aiMessageId}`, err);
133744
- });
133745
- return;
133746
- }
133747
- ctx.blockCountSincePersist++;
133748
- if (ctx.blockCountSincePersist >= 2) {
133749
- ctx.blockCountSincePersist = 0;
133750
- if (streamingBlocks.length === 0) return;
133751
- messageRepository2.update(aiMessageId, {
133752
- blocks: streamingBlocks,
133753
- updatedAt: Date.now()
133754
- }).catch((err) => {
133755
- log15.noise(`streaming update failed, aiMessageId=${aiMessageId}`, err);
133756
- });
133757
- }
133758
- }
133759
134354
  /**
133760
134355
  * 持久化错误消息到数据库
133761
134356
  */
133762
134357
  async persistErrorMessage(ctx, error48) {
133763
- const { conversationId, aiMessageId, createdAt, startTime, streamingBlocks, agentId, streamingPersisted, model, fallback, metadata, streamingUsage } = ctx;
134358
+ const { conversationId, aiMessageId, createdAt, startTime, streamingBlocks, agentId, model, fallback, metadata, streamingUsage } = ctx;
133764
134359
  const durationMs = Date.now() - startTime;
133765
134360
  const { messageRepository: messageRepository2 } = resolveMessageDeps();
133766
134361
  const usage = Object.keys(streamingUsage).length > 0 ? streamingUsage : void 0;
133767
134362
  try {
133768
- if (streamingPersisted) {
134363
+ await ctx.persistScheduler.flush();
134364
+ if (ctx.persistScheduler.hasPersistedSkeleton) {
133769
134365
  await messageRepository2.update(aiMessageId, {
133770
134366
  blocks: streamingBlocks,
133771
134367
  ...error48 ? { error: error48 } : {},
@@ -133794,8 +134390,50 @@ var init_MessageProcessor = __esm({
133794
134390
  );
133795
134391
  }
133796
134392
  } catch (persistErr) {
133797
- log15.error(`failed to persist error message, aiMessageId=${aiMessageId}`, persistErr);
134393
+ log16.error(`failed to persist error message, aiMessageId=${aiMessageId}`, persistErr);
134394
+ }
134395
+ }
134396
+ };
134397
+ }
134398
+ });
134399
+
134400
+ // src/core/message/queue/PendingDbWrites.ts
134401
+ function delay2(ms) {
134402
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
134403
+ }
134404
+ var log17, PendingDbWrites;
134405
+ var init_PendingDbWrites = __esm({
134406
+ "src/core/message/queue/PendingDbWrites.ts"() {
134407
+ "use strict";
134408
+ init_logger();
134409
+ log17 = createLogger("PendingDbWrites");
134410
+ PendingDbWrites = class {
134411
+ pending = /* @__PURE__ */ new Set();
134412
+ track(promise2, label) {
134413
+ const tracked = promise2.then(() => void 0).catch((error48) => {
134414
+ log17.warn(`pending DB write failed: ${label}`, error48);
134415
+ }).finally(() => {
134416
+ this.pending.delete(tracked);
134417
+ });
134418
+ this.pending.add(tracked);
134419
+ }
134420
+ snapshot() {
134421
+ return { pending: this.pending.size };
134422
+ }
134423
+ async drain(timeoutMs) {
134424
+ if (this.pending.size === 0) return "drained";
134425
+ const deadline = Date.now() + timeoutMs;
134426
+ while (this.pending.size > 0) {
134427
+ const remaining = deadline - Date.now();
134428
+ if (remaining <= 0) return "timeout";
134429
+ const batch = [...this.pending];
134430
+ const result = await Promise.race([
134431
+ Promise.allSettled(batch).then(() => "settled"),
134432
+ delay2(remaining).then(() => "timeout")
134433
+ ]);
134434
+ if (result === "timeout") return "timeout";
133798
134435
  }
134436
+ return "drained";
133799
134437
  }
133800
134438
  };
133801
134439
  }
@@ -133817,7 +134455,10 @@ var init_time = __esm({
133817
134455
  function stripSystemTag(content) {
133818
134456
  return content.replace(/\n<current_time>[^<]*<\/current_time>/g, "");
133819
134457
  }
133820
- var log16, MessageQueue, messageQueue;
134458
+ function delay3(ms) {
134459
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
134460
+ }
134461
+ var log18, MessageQueue, messageQueue;
133821
134462
  var init_MessageQueue = __esm({
133822
134463
  "src/core/message/queue/MessageQueue.ts"() {
133823
134464
  "use strict";
@@ -133825,9 +134466,10 @@ var init_MessageQueue = __esm({
133825
134466
  init_message_deps();
133826
134467
  init_ThreadManagerPool();
133827
134468
  init_MessageProcessor();
134469
+ init_PendingDbWrites();
133828
134470
  init_logger();
133829
134471
  init_time();
133830
- log16 = createLogger("MessageQueue");
134472
+ log18 = createLogger("MessageQueue");
133831
134473
  MessageQueue = class {
133832
134474
  constructor(threadManagerProvider) {
133833
134475
  this.threadManagerProvider = threadManagerProvider;
@@ -133848,7 +134490,7 @@ var init_MessageQueue = __esm({
133848
134490
  this.abortMap.delete(conversationId);
133849
134491
  },
133850
134492
  getThreadManager: (threadId) => this.threadManagerProvider.getManager(threadId)
133851
- }, this);
134493
+ }, this, this.pendingDbWrites);
133852
134494
  }
133853
134495
  threadManagerProvider;
133854
134496
  queueMap = /* @__PURE__ */ new Map();
@@ -133861,8 +134503,14 @@ var init_MessageQueue = __esm({
133861
134503
  isDestroyed = false;
133862
134504
  processingSet = /* @__PURE__ */ new Set();
133863
134505
  // 通道 ID(单聊=agentId,群线程=threadId)。同线程串行,不同线程可并行
134506
+ processingPromises = /* @__PURE__ */ new Set();
134507
+ pendingDbWrites = new PendingDbWrites();
133864
134508
  processor;
133865
134509
  push(conversationId, message, metadata, queueContext) {
134510
+ if (this.isDestroyed) {
134511
+ log18.warn(`push ignored after destroy, conversationId=${conversationId}`);
134512
+ return;
134513
+ }
133866
134514
  const prev2 = this.queueMap.get(conversationId) || [];
133867
134515
  this.queueMap.set(conversationId, [...prev2, {
133868
134516
  message,
@@ -133871,9 +134519,9 @@ var init_MessageQueue = __esm({
133871
134519
  originalContent: queueContext?.originalContent ?? "",
133872
134520
  hasAttachments: queueContext?.hasAttachments ?? false
133873
134521
  }]);
133874
- log16.noise(`push: conversationId=${conversationId}, queueLength=${prev2.length + 1}`);
134522
+ log18.noise(`push: conversationId=${conversationId}, queueLength=${prev2.length + 1}`);
133875
134523
  this.emitQueueUpdated(conversationId);
133876
- this.consume(conversationId);
134524
+ this.startConsume(conversationId);
133877
134525
  }
133878
134526
  getQueuedMessages(conversationId) {
133879
134527
  return this.queueMap.get(conversationId) || [];
@@ -133882,7 +134530,7 @@ var init_MessageQueue = __esm({
133882
134530
  return this.processingSet.has(conversationId);
133883
134531
  }
133884
134532
  stop(conversationId) {
133885
- log16.noise(`stop: conversationId=${conversationId}`);
134533
+ log18.noise(`stop: conversationId=${conversationId}`);
133886
134534
  this.abortMap.get(conversationId)?.abort();
133887
134535
  }
133888
134536
  /**
@@ -133894,7 +134542,7 @@ var init_MessageQueue = __esm({
133894
134542
  if (!items || items.length === 0) return [];
133895
134543
  const cancelled = items.splice(0);
133896
134544
  this.queueMap.set(conversationId, []);
133897
- log16.noise(`cancelQueuedMessages: conversationId=${conversationId}, cancelled=${cancelled.length}`);
134545
+ log18.noise(`cancelQueuedMessages: conversationId=${conversationId}, cancelled=${cancelled.length}`);
133898
134546
  this.emitQueueUpdated(conversationId);
133899
134547
  return cancelled;
133900
134548
  }
@@ -133902,7 +134550,7 @@ var init_MessageQueue = __esm({
133902
134550
  return this.runIdMap.get(runId);
133903
134551
  }
133904
134552
  registerRunId(runId, conversationId) {
133905
- log16.noise(`registerRunId: runId=${runId}, conversationId=${conversationId}`);
134553
+ log18.noise(`registerRunId: runId=${runId}, conversationId=${conversationId}`);
133906
134554
  this.runIdMap.set(runId, conversationId);
133907
134555
  this.conversationRunIdMap.set(conversationId, runId);
133908
134556
  }
@@ -133917,7 +134565,7 @@ var init_MessageQueue = __esm({
133917
134565
  emitQueueUpdated(conversationId) {
133918
134566
  const pending = this.queueMap.get(conversationId)?.length || 0;
133919
134567
  const running = this.processingSet.has(conversationId);
133920
- log16.noise(
134568
+ log18.noise(
133921
134569
  `emitQueueUpdated: conversationId=${conversationId}, pending=${pending}, running=${running}, processingSet=${this.processingSet.size}`
133922
134570
  );
133923
134571
  eventBus.emit({
@@ -133926,21 +134574,30 @@ var init_MessageQueue = __esm({
133926
134574
  queue: { pending, running }
133927
134575
  });
133928
134576
  }
134577
+ startConsume(conversationId, retryCount = 0) {
134578
+ if (this.isDestroyed) return;
134579
+ const promise2 = this.consume(conversationId, retryCount).catch((error48) => {
134580
+ log18.error(`consume failed, conversationId=${conversationId}`, error48);
134581
+ }).finally(() => {
134582
+ this.processingPromises.delete(promise2);
134583
+ });
134584
+ this.processingPromises.add(promise2);
134585
+ }
133929
134586
  async consume(conversationId, retryCount = 0) {
133930
134587
  if (this.isDestroyed) return;
133931
134588
  if (this.processingSet.has(conversationId)) {
133932
- log16.noise(`consume: already processing, conversationId=${conversationId}`);
134589
+ log18.noise(`consume: already processing, conversationId=${conversationId}`);
133933
134590
  return;
133934
134591
  }
133935
134592
  const queue = this.queueMap.get(conversationId);
133936
134593
  if (!queue || queue.length === 0) {
133937
- log16.noise(`consume: no message in queue, conversationId=${conversationId}`);
134594
+ log18.noise(`consume: no message in queue, conversationId=${conversationId}`);
133938
134595
  return;
133939
134596
  }
133940
134597
  const items = queue.splice(0);
133941
134598
  const hasAttachments = items.some((item) => item.hasAttachments);
133942
134599
  this.processingSet.add(conversationId);
133943
- log16.noise(`consume: start processing, conversationId=${conversationId}, merged=${items.length}, hasAttachments=${hasAttachments}`);
134600
+ log18.noise(`consume: start processing, conversationId=${conversationId}, merged=${items.length}, hasAttachments=${hasAttachments}`);
133944
134601
  this.emitQueueUpdated(conversationId);
133945
134602
  let shouldContinue = false;
133946
134603
  let willRetry = false;
@@ -133954,7 +134611,7 @@ var init_MessageQueue = __esm({
133954
134611
  if (skippedIds.length > 0) {
133955
134612
  await resolveMessageDeps().messageRepository.delete(skippedIds);
133956
134613
  eventBus.emit({ type: "messages.revoked", conversationId, messageIds: skippedIds });
133957
- log16.noise(`consume: cleaned up ${skippedIds.length} skipped DB messages (attachment conflict)`);
134614
+ log18.noise(`consume: cleaned up ${skippedIds.length} skipped DB messages (attachment conflict)`);
133958
134615
  }
133959
134616
  const result = await this.processor.processMessage(
133960
134617
  conversationId,
@@ -133970,9 +134627,10 @@ var init_MessageQueue = __esm({
133970
134627
  this.queueMap.set(conversationId, [current, ...this.queueMap.get(conversationId) || []]);
133971
134628
  const nextRetryCount = retryCount + 1;
133972
134629
  const timerId = setTimeout(() => {
134630
+ if (this.isDestroyed) return;
133973
134631
  this.timers.delete(conversationId);
133974
134632
  this.processingSet.delete(conversationId);
133975
- this.consume(conversationId, nextRetryCount);
134633
+ this.startConsume(conversationId, nextRetryCount);
133976
134634
  }, result.retryDelay);
133977
134635
  this.timers.set(conversationId, timerId);
133978
134636
  willRetry = true;
@@ -134011,15 +134669,16 @@ ${stripped}`;
134011
134669
  this.queueMap.set(conversationId, [...items, ...this.queueMap.get(conversationId) || []]);
134012
134670
  const nextRetryCount = retryCount + 1;
134013
134671
  const timerId = setTimeout(() => {
134672
+ if (this.isDestroyed) return;
134014
134673
  this.timers.delete(conversationId);
134015
134674
  this.processingSet.delete(conversationId);
134016
- this.consume(conversationId, nextRetryCount);
134675
+ this.startConsume(conversationId, nextRetryCount);
134017
134676
  }, result.retryDelay);
134018
134677
  this.timers.set(conversationId, timerId);
134019
134678
  willRetry = true;
134020
134679
  return;
134021
134680
  }
134022
- log16.noise(`consume: completed, conversationId=${conversationId}`);
134681
+ log18.noise(`consume: completed, conversationId=${conversationId}`);
134023
134682
  const mergedIds = items.slice(0, -1).map((item) => item.dbMessageId).filter((id) => Boolean(id));
134024
134683
  if (mergedIds.length > 0) {
134025
134684
  await resolveMessageDeps().messageRepository.delete(mergedIds);
@@ -134028,7 +134687,7 @@ ${stripped}`;
134028
134687
  conversationId,
134029
134688
  messageIds: mergedIds
134030
134689
  });
134031
- log16.noise(`consume: cleaned up ${mergedIds.length} merged DB messages`);
134690
+ log18.noise(`consume: cleaned up ${mergedIds.length} merged DB messages`);
134032
134691
  }
134033
134692
  shouldContinue = (this.queueMap.get(conversationId)?.length ?? 0) > 0;
134034
134693
  }
@@ -134039,11 +134698,37 @@ ${stripped}`;
134039
134698
  }
134040
134699
  }
134041
134700
  if (shouldContinue) {
134042
- log16.noise(`consume: continuing with next message, conversationId=${conversationId}`);
134043
- await this.consume(conversationId);
134701
+ log18.noise(`consume: continuing with next message, conversationId=${conversationId}`);
134702
+ this.startConsume(conversationId);
134703
+ }
134704
+ }
134705
+ async shutdown(options2 = {}) {
134706
+ const timeoutMs = options2.timeoutMs ?? 3e3;
134707
+ const deadline = Date.now() + timeoutMs;
134708
+ this.isDestroyed = true;
134709
+ for (const timer of this.timers.values()) {
134710
+ clearTimeout(timer);
134711
+ }
134712
+ this.timers.clear();
134713
+ for (const controller of this.abortMap.values()) {
134714
+ controller.abort();
134715
+ }
134716
+ const processingResult = await this.drainProcessing(deadline);
134717
+ if (processingResult === "timeout") {
134718
+ log18.warn(`shutdown timed out waiting for active processing, remaining=${this.processingPromises.size}`);
134719
+ }
134720
+ const dbWritesResult = await this.pendingDbWrites.drain(Math.max(0, deadline - Date.now()));
134721
+ if (dbWritesResult === "timeout") {
134722
+ log18.warn(`shutdown timed out waiting for pending DB writes, remaining=${this.pendingDbWrites.snapshot().pending}`);
134723
+ }
134724
+ this.abortMap.clear();
134725
+ this.processingSet.clear();
134726
+ for (const conversationId of this.queueMap.keys()) {
134727
+ this.emitQueueUpdated(conversationId);
134044
134728
  }
134045
134729
  }
134046
134730
  destroy() {
134731
+ if (this.isDestroyed) return;
134047
134732
  this.isDestroyed = true;
134048
134733
  for (const timer of this.timers.values()) {
134049
134734
  clearTimeout(timer);
@@ -134055,6 +134740,23 @@ ${stripped}`;
134055
134740
  this.abortMap.clear();
134056
134741
  this.processingSet.clear();
134057
134742
  }
134743
+ getPendingDbWritesCount() {
134744
+ return this.pendingDbWrites.snapshot().pending;
134745
+ }
134746
+ async drainProcessing(deadline) {
134747
+ if (this.processingPromises.size === 0) return "drained";
134748
+ while (this.processingPromises.size > 0) {
134749
+ const remaining = deadline - Date.now();
134750
+ if (remaining <= 0) return "timeout";
134751
+ const batch = [...this.processingPromises];
134752
+ const result = await Promise.race([
134753
+ Promise.allSettled(batch).then(() => "settled"),
134754
+ delay3(remaining).then(() => "timeout")
134755
+ ]);
134756
+ if (result === "timeout") return "timeout";
134757
+ }
134758
+ return "drained";
134759
+ }
134058
134760
  };
134059
134761
  messageQueue = new MessageQueue(threadManagerPool);
134060
134762
  }
@@ -134077,7 +134779,7 @@ var init_message2 = __esm({
134077
134779
  });
134078
134780
 
134079
134781
  // src/core/message/MessagePublisher.ts
134080
- var log17, MessagePublisherImpl, messagePublisher;
134782
+ var log19, MessagePublisherImpl, messagePublisher;
134081
134783
  var init_MessagePublisher = __esm({
134082
134784
  "src/core/message/MessagePublisher.ts"() {
134083
134785
  "use strict";
@@ -134087,7 +134789,7 @@ var init_MessagePublisher = __esm({
134087
134789
  init_message_deps();
134088
134790
  init_logger();
134089
134791
  init_time();
134090
- log17 = createLogger("MessagePublisher");
134792
+ log19 = createLogger("MessagePublisher");
134091
134793
  MessagePublisherImpl = class {
134092
134794
  /**
134093
134795
  * 发送用户消息
@@ -134122,7 +134824,7 @@ var init_MessagePublisher = __esm({
134122
134824
  }
134123
134825
  );
134124
134826
  }
134125
- log17.noise(`published user message, id=${message.id}, conversationId=${conversationId}`);
134827
+ log19.noise(`published user message, id=${message.id}, conversationId=${conversationId}`);
134126
134828
  return message;
134127
134829
  }
134128
134830
  /**
@@ -134144,7 +134846,7 @@ var init_MessagePublisher = __esm({
134144
134846
  });
134145
134847
  await sessionContextStore.saveAgentContext(conversationId, []);
134146
134848
  this.emitAccepted(message);
134147
- log17.noise(`reset context (single), conversationId=${conversationId}`);
134849
+ log19.noise(`reset context (single), conversationId=${conversationId}`);
134148
134850
  return conversationId;
134149
134851
  }
134150
134852
  /**
@@ -134172,7 +134874,7 @@ var init_MessagePublisher = __esm({
134172
134874
  // 保持 QueueItem 不变量
134173
134875
  );
134174
134876
  }
134175
- log17.noise(`published system message, id=${message.id}, conversationId=${conversationId}`);
134877
+ log19.noise(`published system message, id=${message.id}, conversationId=${conversationId}`);
134176
134878
  return message;
134177
134879
  }
134178
134880
  /**
@@ -134275,7 +134977,7 @@ var require_logger2 = __commonJS({
134275
134977
  };
134276
134978
  var GREEN = "\x1B[32m";
134277
134979
  var RESET = "\x1B[0m";
134278
- function log21(level, message, extra) {
134980
+ function log23(level, message, extra) {
134279
134981
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
134280
134982
  const color = levelColors[level] ?? "";
134281
134983
  const prefix = `[${timestamp}] [PID: ${process.pid}] ${GREEN}[NODE-CRON]${GREEN} ${color}[${level}]${RESET}`;
@@ -134298,23 +135000,23 @@ var require_logger2 = __commonJS({
134298
135000
  }
134299
135001
  var logger40 = {
134300
135002
  info(message) {
134301
- log21("INFO", message);
135003
+ log23("INFO", message);
134302
135004
  },
134303
135005
  warn(message) {
134304
- log21("WARN", message);
135006
+ log23("WARN", message);
134305
135007
  },
134306
135008
  error(message, err) {
134307
135009
  if (message instanceof Error) {
134308
- log21("ERROR", message.message, message);
135010
+ log23("ERROR", message.message, message);
134309
135011
  } else {
134310
- log21("ERROR", message, err);
135012
+ log23("ERROR", message, err);
134311
135013
  }
134312
135014
  },
134313
135015
  debug(message, err) {
134314
135016
  if (message instanceof Error) {
134315
- log21("DEBUG", message.message, message);
135017
+ log23("DEBUG", message.message, message);
134316
135018
  } else {
134317
- log21("DEBUG", message, err);
135019
+ log23("DEBUG", message, err);
134318
135020
  }
134319
135021
  }
134320
135022
  };
@@ -134562,11 +135264,11 @@ var require_runner = __commonJS({
134562
135264
  const maxDelay = 864e5;
134563
135265
  const nextRun = timeMatcher.getNextMatch(currentDate);
134564
135266
  const now2 = /* @__PURE__ */ new Date();
134565
- const delay2 = nextRun.getTime() - now2.getTime();
134566
- if (delay2 > maxDelay) {
135267
+ const delay4 = nextRun.getTime() - now2.getTime();
135268
+ if (delay4 > maxDelay) {
134567
135269
  return maxDelay;
134568
135270
  }
134569
- return Math.max(0, delay2);
135271
+ return Math.max(0, delay4);
134570
135272
  }
134571
135273
  function nowWithoutMs() {
134572
135274
  const date5 = /* @__PURE__ */ new Date();
@@ -135992,7 +136694,7 @@ var init_MemoryUpdateExecutor = __esm({
135992
136694
  for (const block of toolBlocks) {
135993
136695
  const toolCall = block.toolCall;
135994
136696
  if (toolCall.status !== "success") continue;
135995
- if (toolCall.name !== "write" && toolCall.name !== "edit") continue;
136697
+ if (!["writeFile", "editFile", "write", "edit"].includes(toolCall.name)) continue;
135996
136698
  const output = this.readToolOutput(toolCall.result);
135997
136699
  if (!output?.success || typeof output.path !== "string") continue;
135998
136700
  const relativePath = this.toRelativeWorkspacePath(workspaceDir, output.path);
@@ -140001,23 +140703,23 @@ function getKaiRoot() {
140001
140703
  return config.getAppDataPath();
140002
140704
  }
140003
140705
  function getDbPath() {
140004
- return (0, import_path19.join)(config.getAppDataPath(), "kai.db");
140706
+ return (0, import_path20.join)(config.getAppDataPath(), "kai.db");
140005
140707
  }
140006
140708
  function getThreadsRoot(groupId) {
140007
- return (0, import_path19.join)(getGroupDir(groupId), "threads");
140709
+ return (0, import_path20.join)(getGroupDir(groupId), "threads");
140008
140710
  }
140009
140711
  function getThreadMetaPath(groupId, threadId) {
140010
- return (0, import_path19.join)(getThreadsRoot(groupId), threadId, "meta.json");
140712
+ return (0, import_path20.join)(getThreadsRoot(groupId), threadId, "meta.json");
140011
140713
  }
140012
140714
  async function atomicWriteJson5(filePath, data2) {
140013
- await (0, import_promises20.mkdir)((0, import_path19.dirname)(filePath), { recursive: true });
140715
+ await (0, import_promises21.mkdir)((0, import_path20.dirname)(filePath), { recursive: true });
140014
140716
  const tmp = `${filePath}.tmp`;
140015
- await (0, import_promises20.writeFile)(tmp, `${JSON.stringify(data2, null, 2)}
140717
+ await (0, import_promises21.writeFile)(tmp, `${JSON.stringify(data2, null, 2)}
140016
140718
  `, "utf-8");
140017
- await (0, import_promises20.rename)(tmp, filePath);
140719
+ await (0, import_promises21.rename)(tmp, filePath);
140018
140720
  }
140019
140721
  async function isMigrationNeeded() {
140020
- const groupsRoot = (0, import_path19.join)(getKaiRoot(), "groups");
140722
+ const groupsRoot = (0, import_path20.join)(getKaiRoot(), "groups");
140021
140723
  if ((0, import_fs13.existsSync)(groupsRoot)) {
140022
140724
  try {
140023
140725
  const { readdir: readdir9 } = await import("fs/promises");
@@ -140025,7 +140727,7 @@ async function isMigrationNeeded() {
140025
140727
  const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
140026
140728
  if (dirs.length > 0) {
140027
140729
  const allMigrated = dirs.every(
140028
- (g) => (0, import_fs13.existsSync)((0, import_path19.join)(getGroupDir(g), "threads"))
140730
+ (g) => (0, import_fs13.existsSync)((0, import_path20.join)(getGroupDir(g), "threads"))
140029
140731
  );
140030
140732
  if (allMigrated) return false;
140031
140733
  }
@@ -140070,7 +140772,7 @@ async function migrateGroupsToThreads() {
140070
140772
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
140071
140773
  const backupPath = `${dbPath}.bak.${timestamp}`;
140072
140774
  logger33.debug(`Backing up database to ${backupPath}`);
140073
- await (0, import_promises20.copyFile)(dbPath, backupPath);
140775
+ await (0, import_promises21.copyFile)(dbPath, backupPath);
140074
140776
  try {
140075
140777
  logger33.debug("Rebuilding messages table (removing FK)");
140076
140778
  await sequelize.transaction(async (t) => {
@@ -140202,13 +140904,13 @@ async function migrateGroupsToThreads() {
140202
140904
  for (const conv of groupConversations) {
140203
140905
  const row = conv;
140204
140906
  const groupId = row.target_id;
140205
- const threadsDir = (0, import_path19.join)(getGroupDir(groupId), "threads");
140907
+ const threadsDir = (0, import_path20.join)(getGroupDir(groupId), "threads");
140206
140908
  if (!(0, import_fs13.existsSync)(threadsDir)) continue;
140207
140909
  try {
140208
140910
  const { readdir: readdirAsync } = await import("fs/promises");
140209
140911
  const threadDirs = (await readdirAsync(threadsDir, { withFileTypes: true })).filter((e) => e.isDirectory());
140210
140912
  const hasMeta = threadDirs.some(
140211
- (td) => (0, import_fs13.existsSync)((0, import_path19.join)(threadsDir, td.name, "meta.json"))
140913
+ (td) => (0, import_fs13.existsSync)((0, import_path20.join)(threadsDir, td.name, "meta.json"))
140212
140914
  );
140213
140915
  if (hasMeta) groupsWithThread++;
140214
140916
  } catch {
@@ -140244,7 +140946,7 @@ async function migrateGroupsToThreads() {
140244
140946
  if ((0, import_fs13.existsSync)(backupPath)) {
140245
140947
  logger33.debug("Restoring database from backup...");
140246
140948
  try {
140247
- await (0, import_promises20.copyFile)(backupPath, dbPath);
140949
+ await (0, import_promises21.copyFile)(backupPath, dbPath);
140248
140950
  logger33.debug("Database restored from backup");
140249
140951
  } catch (restoreError) {
140250
140952
  logger33.error(
@@ -140259,7 +140961,7 @@ async function migrateGroupsToThreads() {
140259
140961
  async function rollbackThreads() {
140260
140962
  const sequelize = getSequelize();
140261
140963
  const dbPath = getDbPath();
140262
- const groupsRoot = (0, import_path19.join)(getKaiRoot(), "groups");
140964
+ const groupsRoot = (0, import_path20.join)(getKaiRoot(), "groups");
140263
140965
  if (!(0, import_fs13.existsSync)(groupsRoot)) {
140264
140966
  logger33.debug("No groups directory, nothing to rollback.");
140265
140967
  return;
@@ -140267,7 +140969,7 @@ async function rollbackThreads() {
140267
140969
  const { readdir: readdir9 } = await import("fs/promises");
140268
140970
  const dirs = (await readdir9(groupsRoot, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
140269
140971
  const hasThreads = dirs.some(
140270
- (g) => (0, import_fs13.existsSync)((0, import_path19.join)(getGroupDir(g), "threads"))
140972
+ (g) => (0, import_fs13.existsSync)((0, import_path20.join)(getGroupDir(g), "threads"))
140271
140973
  );
140272
140974
  if (!hasThreads) {
140273
140975
  logger33.debug("No threads directories found, nothing to rollback.");
@@ -140282,12 +140984,12 @@ async function rollbackThreads() {
140282
140984
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
140283
140985
  const backupPath = `${dbPath}.bak.rollback.${timestamp}`;
140284
140986
  logger33.debug(`Backing up database to ${backupPath}`);
140285
- await (0, import_promises20.copyFile)(dbPath, backupPath);
140987
+ await (0, import_promises21.copyFile)(dbPath, backupPath);
140286
140988
  try {
140287
140989
  logger33.debug("Restoring group conversations from thread files");
140288
140990
  let restored = 0;
140289
140991
  for (const groupId of dirs) {
140290
- const threadsDir = (0, import_path19.join)(getGroupDir(groupId), "threads");
140992
+ const threadsDir = (0, import_path20.join)(getGroupDir(groupId), "threads");
140291
140993
  if (!(0, import_fs13.existsSync)(threadsDir)) continue;
140292
140994
  let threadDirs;
140293
140995
  try {
@@ -140298,7 +141000,7 @@ async function rollbackThreads() {
140298
141000
  for (const threadId of threadDirs) {
140299
141001
  const metaPath = getThreadMetaPath(groupId, threadId);
140300
141002
  if (!(0, import_fs13.existsSync)(metaPath)) continue;
140301
- const raw = await (0, import_promises20.readFile)(metaPath, "utf-8");
141003
+ const raw = await (0, import_promises21.readFile)(metaPath, "utf-8");
140302
141004
  const meta3 = JSON.parse(raw);
140303
141005
  if (!meta3.isDefault) {
140304
141006
  const orphanCount = await sequelize.query(
@@ -140425,9 +141127,9 @@ async function rollbackThreads() {
140425
141127
  logger33.debug("Messages table rebuilt with FK restored");
140426
141128
  logger33.debug("Removing threads directories");
140427
141129
  for (const groupId of dirs) {
140428
- const threadsDir = (0, import_path19.join)(getGroupDir(groupId), "threads");
141130
+ const threadsDir = (0, import_path20.join)(getGroupDir(groupId), "threads");
140429
141131
  if ((0, import_fs13.existsSync)(threadsDir)) {
140430
- await (0, import_promises20.rm)(threadsDir, { recursive: true, force: true });
141132
+ await (0, import_promises21.rm)(threadsDir, { recursive: true, force: true });
140431
141133
  }
140432
141134
  }
140433
141135
  const finalRows = await sequelize.query(
@@ -140448,7 +141150,7 @@ async function rollbackThreads() {
140448
141150
  if ((0, import_fs13.existsSync)(backupPath)) {
140449
141151
  logger33.debug("Restoring database from backup...");
140450
141152
  try {
140451
- await (0, import_promises20.copyFile)(backupPath, dbPath);
141153
+ await (0, import_promises21.copyFile)(backupPath, dbPath);
140452
141154
  logger33.debug("Database restored from backup");
140453
141155
  } catch (restoreError) {
140454
141156
  logger33.error(
@@ -140460,13 +141162,13 @@ async function rollbackThreads() {
140460
141162
  throw error48;
140461
141163
  }
140462
141164
  }
140463
- var import_path19, import_fs13, import_promises20, logger33;
141165
+ var import_path20, import_fs13, import_promises21, logger33;
140464
141166
  var init_threads_migration = __esm({
140465
141167
  "src/adapters/db/migration/threads-migration.ts"() {
140466
141168
  "use strict";
140467
- import_path19 = require("path");
141169
+ import_path20 = require("path");
140468
141170
  import_fs13 = require("fs");
140469
- import_promises20 = require("fs/promises");
141171
+ import_promises21 = require("fs/promises");
140470
141172
  init_lib();
140471
141173
  init_sequelize();
140472
141174
  init_config();
@@ -140484,13 +141186,13 @@ __export(cron_sqlite_migration_exports, {
140484
141186
  migrateCronSqliteToFileStore: () => migrateCronSqliteToFileStore
140485
141187
  });
140486
141188
  function getDataCronDir2() {
140487
- return (0, import_path20.join)(config.getAppDataPath(), "data", CRON_DIR2);
141189
+ return (0, import_path21.join)(config.getAppDataPath(), "data", CRON_DIR2);
140488
141190
  }
140489
141191
  function getAgentsDir2() {
140490
- return (0, import_path20.join)(config.getAppDataPath(), "agents");
141192
+ return (0, import_path21.join)(config.getAppDataPath(), "agents");
140491
141193
  }
140492
141194
  function getGroupsDir2() {
140493
- return (0, import_path20.join)(config.getAppDataPath(), "groups");
141195
+ return (0, import_path21.join)(config.getAppDataPath(), "groups");
140494
141196
  }
140495
141197
  function parseDatetime(value) {
140496
141198
  if (!value) return 0;
@@ -140508,17 +141210,17 @@ function parseScriptArgs(raw) {
140508
141210
  }
140509
141211
  async function writeJsonAtomic(filePath, data2) {
140510
141212
  const dir = filePath.substring(0, filePath.lastIndexOf("/"));
140511
- await (0, import_promises21.mkdir)(dir, { recursive: true });
141213
+ await (0, import_promises22.mkdir)(dir, { recursive: true });
140512
141214
  const tmp = `${filePath}.tmp`;
140513
- await (0, import_promises21.writeFile)(tmp, JSON.stringify(data2, null, 2), "utf-8");
140514
- await (0, import_promises21.rename)(tmp, filePath);
141215
+ await (0, import_promises22.writeFile)(tmp, JSON.stringify(data2, null, 2), "utf-8");
141216
+ await (0, import_promises22.rename)(tmp, filePath);
140515
141217
  }
140516
141218
  function getTaskDir2(task) {
140517
141219
  if (task.type === "agent" && task.agentId) {
140518
- return (0, import_path20.join)(getAgentsDir2(), task.agentId, CRON_DIR2);
141220
+ return (0, import_path21.join)(getAgentsDir2(), task.agentId, CRON_DIR2);
140519
141221
  }
140520
141222
  if (task.type === "group" && task.groupId) {
140521
- return (0, import_path20.join)(getGroupsDir2(), task.groupId, CRON_DIR2);
141223
+ return (0, import_path21.join)(getGroupsDir2(), task.groupId, CRON_DIR2);
140522
141224
  }
140523
141225
  return getDataCronDir2();
140524
141226
  }
@@ -140555,7 +141257,7 @@ async function migrateTasks(sequelize) {
140555
141257
  continue;
140556
141258
  }
140557
141259
  const dir = getTaskDir2(task);
140558
- const filePath = (0, import_path20.join)(dir, `${task.id}.json`);
141260
+ const filePath = (0, import_path21.join)(dir, `${task.id}.json`);
140559
141261
  if ((0, import_fs14.existsSync)(filePath)) {
140560
141262
  logger34.debug(`Task ${task.id} already exists in filesystem, skipping`);
140561
141263
  continue;
@@ -140588,9 +141290,9 @@ async function migrateExecutions(sequelize) {
140588
141290
  for (const [taskId, execs] of byTask) {
140589
141291
  try {
140590
141292
  const taskDir = await findTaskBaseDir(taskId);
140591
- const execDir = (0, import_path20.join)(taskDir, taskId);
141293
+ const execDir = (0, import_path21.join)(taskDir, taskId);
140592
141294
  for (const row of execs) {
140593
- const filePath = (0, import_path20.join)(execDir, `${row.id}.json`);
141295
+ const filePath = (0, import_path21.join)(execDir, `${row.id}.json`);
140594
141296
  if ((0, import_fs14.existsSync)(filePath)) continue;
140595
141297
  const execution = {
140596
141298
  id: row.id,
@@ -140683,15 +141385,15 @@ function rowToTask(row, conversationMap) {
140683
141385
  };
140684
141386
  }
140685
141387
  async function findTaskBaseDir(taskId) {
140686
- const directGlobalPath = (0, import_path20.join)(getDataCronDir2(), `${taskId}.json`);
141388
+ const directGlobalPath = (0, import_path21.join)(getDataCronDir2(), `${taskId}.json`);
140687
141389
  if ((0, import_fs14.existsSync)(directGlobalPath)) return getDataCronDir2();
140688
141390
  const agentsDir = getAgentsDir2();
140689
141391
  if ((0, import_fs14.existsSync)(agentsDir)) {
140690
141392
  try {
140691
- const owners = await (0, import_promises21.readdir)(agentsDir);
141393
+ const owners = await (0, import_promises22.readdir)(agentsDir);
140692
141394
  for (const ownerId of owners) {
140693
- const cronDir = (0, import_path20.join)(agentsDir, ownerId, CRON_DIR2);
140694
- const filePath = (0, import_path20.join)(cronDir, `${taskId}.json`);
141395
+ const cronDir = (0, import_path21.join)(agentsDir, ownerId, CRON_DIR2);
141396
+ const filePath = (0, import_path21.join)(cronDir, `${taskId}.json`);
140695
141397
  if ((0, import_fs14.existsSync)(filePath)) return cronDir;
140696
141398
  }
140697
141399
  } catch {
@@ -140700,10 +141402,10 @@ async function findTaskBaseDir(taskId) {
140700
141402
  const groupsDir = getGroupsDir2();
140701
141403
  if ((0, import_fs14.existsSync)(groupsDir)) {
140702
141404
  try {
140703
- const owners = await (0, import_promises21.readdir)(groupsDir);
141405
+ const owners = await (0, import_promises22.readdir)(groupsDir);
140704
141406
  for (const ownerId of owners) {
140705
- const cronDir = (0, import_path20.join)(groupsDir, ownerId, CRON_DIR2);
140706
- const filePath = (0, import_path20.join)(cronDir, `${taskId}.json`);
141407
+ const cronDir = (0, import_path21.join)(groupsDir, ownerId, CRON_DIR2);
141408
+ const filePath = (0, import_path21.join)(cronDir, `${taskId}.json`);
140707
141409
  if ((0, import_fs14.existsSync)(filePath)) return cronDir;
140708
141410
  }
140709
141411
  } catch {
@@ -140737,13 +141439,13 @@ async function migrateCronSqliteToFileStore() {
140737
141439
  logger34.debug(`Migrated ${tasksMigrated} tasks and ${execsMigrated} executions to filesystem`);
140738
141440
  await dropLegacyTables(sequelize);
140739
141441
  }
140740
- var import_path20, import_promises21, import_fs14, logger34, CRON_DIR2;
141442
+ var import_path21, import_promises22, import_fs14, logger34, CRON_DIR2;
140741
141443
  var init_cron_sqlite_migration = __esm({
140742
141444
  "src/adapters/db/migration/cron-sqlite-migration.ts"() {
140743
141445
  "use strict";
140744
141446
  init_lib();
140745
- import_path20 = require("path");
140746
- import_promises21 = require("fs/promises");
141447
+ import_path21 = require("path");
141448
+ import_promises22 = require("fs/promises");
140747
141449
  import_fs14 = require("fs");
140748
141450
  init_config();
140749
141451
  init_logger();
@@ -144409,24 +145111,24 @@ var require_dbcs_codec = __commonJS({
144409
145111
  return this.encodeTable[high];
144410
145112
  };
144411
145113
  DBCSCodec.prototype._setEncodeChar = function(uCode, dbcsCode) {
144412
- var bucket = this._getEncodeBucket(uCode);
145114
+ var bucket2 = this._getEncodeBucket(uCode);
144413
145115
  var low = uCode & 255;
144414
- if (bucket[low] <= SEQ_START)
144415
- this.encodeTableSeq[SEQ_START - bucket[low]][DEF_CHAR] = dbcsCode;
144416
- else if (bucket[low] == UNASSIGNED)
144417
- bucket[low] = dbcsCode;
145116
+ if (bucket2[low] <= SEQ_START)
145117
+ this.encodeTableSeq[SEQ_START - bucket2[low]][DEF_CHAR] = dbcsCode;
145118
+ else if (bucket2[low] == UNASSIGNED)
145119
+ bucket2[low] = dbcsCode;
144418
145120
  };
144419
145121
  DBCSCodec.prototype._setEncodeSequence = function(seq, dbcsCode) {
144420
145122
  var uCode = seq[0];
144421
- var bucket = this._getEncodeBucket(uCode);
145123
+ var bucket2 = this._getEncodeBucket(uCode);
144422
145124
  var low = uCode & 255;
144423
145125
  var node;
144424
- if (bucket[low] <= SEQ_START) {
144425
- node = this.encodeTableSeq[SEQ_START - bucket[low]];
145126
+ if (bucket2[low] <= SEQ_START) {
145127
+ node = this.encodeTableSeq[SEQ_START - bucket2[low]];
144426
145128
  } else {
144427
145129
  node = {};
144428
- if (bucket[low] !== UNASSIGNED) node[DEF_CHAR] = bucket[low];
144429
- bucket[low] = SEQ_START - this.encodeTableSeq.length;
145130
+ if (bucket2[low] !== UNASSIGNED) node[DEF_CHAR] = bucket2[low];
145131
+ bucket2[low] = SEQ_START - this.encodeTableSeq.length;
144430
145132
  this.encodeTableSeq.push(node);
144431
145133
  }
144432
145134
  for (var j = 1; j < seq.length - 1; j++) {
@@ -146915,9 +147617,9 @@ var require_timers = __commonJS({
146915
147617
  * before the specified function or code is executed.
146916
147618
  * @param {*} arg
146917
147619
  */
146918
- constructor(callback, delay2, arg) {
147620
+ constructor(callback, delay4, arg) {
146919
147621
  this._onTimeout = callback;
146920
- this._idleTimeout = delay2;
147622
+ this._idleTimeout = delay4;
146921
147623
  this._timerArg = arg;
146922
147624
  this.refresh();
146923
147625
  }
@@ -146962,8 +147664,8 @@ var require_timers = __commonJS({
146962
147664
  * when the timer expires.
146963
147665
  * @returns {NodeJS.Timeout|FastTimer}
146964
147666
  */
146965
- setTimeout(callback, delay2, arg) {
146966
- return delay2 <= RESOLUTION_MS ? setTimeout(callback, delay2, arg) : new FastTimer(callback, delay2, arg);
147667
+ setTimeout(callback, delay4, arg) {
147668
+ return delay4 <= RESOLUTION_MS ? setTimeout(callback, delay4, arg) : new FastTimer(callback, delay4, arg);
146967
147669
  },
146968
147670
  /**
146969
147671
  * The clearTimeout method cancels an instantiated Timer previously created
@@ -146989,8 +147691,8 @@ var require_timers = __commonJS({
146989
147691
  * when the timer expires.
146990
147692
  * @returns {FastTimer}
146991
147693
  */
146992
- setFastTimeout(callback, delay2, arg) {
146993
- return new FastTimer(callback, delay2, arg);
147694
+ setFastTimeout(callback, delay4, arg) {
147695
+ return new FastTimer(callback, delay4, arg);
146994
147696
  },
146995
147697
  /**
146996
147698
  * The clearTimeout method cancels an instantiated FastTimer previously
@@ -147016,8 +147718,8 @@ var require_timers = __commonJS({
147016
147718
  * @deprecated
147017
147719
  * @param {number} [delay=0] The delay in milliseconds to add to the now value.
147018
147720
  */
147019
- tick(delay2 = 0) {
147020
- fastNow += delay2 - RESOLUTION_MS + 1;
147721
+ tick(delay4 = 0) {
147722
+ fastNow += delay4 - RESOLUTION_MS + 1;
147021
147723
  onTick();
147022
147724
  onTick();
147023
147725
  },
@@ -153410,21 +154112,21 @@ var require_client_h1 = __commonJS({
153410
154112
  this.connection = "";
153411
154113
  this.maxResponseSize = client[kMaxResponseSize];
153412
154114
  }
153413
- setTimeout(delay2, type) {
153414
- if (delay2 !== this.timeoutValue || type & USE_FAST_TIMER ^ this.timeoutType & USE_FAST_TIMER) {
154115
+ setTimeout(delay4, type) {
154116
+ if (delay4 !== this.timeoutValue || type & USE_FAST_TIMER ^ this.timeoutType & USE_FAST_TIMER) {
153415
154117
  if (this.timeout) {
153416
154118
  timers.clearTimeout(this.timeout);
153417
154119
  this.timeout = null;
153418
154120
  }
153419
- if (delay2) {
154121
+ if (delay4) {
153420
154122
  if (type & USE_FAST_TIMER) {
153421
- this.timeout = timers.setFastTimeout(onParserTimeout, delay2, new WeakRef(this));
154123
+ this.timeout = timers.setFastTimeout(onParserTimeout, delay4, new WeakRef(this));
153422
154124
  } else {
153423
- this.timeout = setTimeout(onParserTimeout, delay2, new WeakRef(this));
154125
+ this.timeout = setTimeout(onParserTimeout, delay4, new WeakRef(this));
153424
154126
  this.timeout?.unref();
153425
154127
  }
153426
154128
  }
153427
- this.timeoutValue = delay2;
154129
+ this.timeoutValue = delay4;
153428
154130
  } else if (this.timeout) {
153429
154131
  if (this.timeout.refresh) {
153430
154132
  this.timeout.refresh();
@@ -159290,7 +159992,7 @@ var require_mock_utils = __commonJS({
159290
159992
  if (mockDispatch2.data.callback) {
159291
159993
  mockDispatch2.data = { ...mockDispatch2.data, ...mockDispatch2.data.callback(opts) };
159292
159994
  }
159293
- const { data: { statusCode, data: data2, headers, trailers, error: error48 }, delay: delay2, persist } = mockDispatch2;
159995
+ const { data: { statusCode, data: data2, headers, trailers, error: error48 }, delay: delay4, persist } = mockDispatch2;
159294
159996
  const { timesInvoked, times } = mockDispatch2;
159295
159997
  mockDispatch2.consumed = !persist && timesInvoked >= times;
159296
159998
  mockDispatch2.pending = timesInvoked < times;
@@ -159313,11 +160015,11 @@ var require_mock_utils = __commonJS({
159313
160015
  handler.onError(err);
159314
160016
  }
159315
160017
  handler.onConnect?.(abort, null);
159316
- if (typeof delay2 === "number" && delay2 > 0) {
160018
+ if (typeof delay4 === "number" && delay4 > 0) {
159317
160019
  timer = setTimeout(() => {
159318
160020
  timer = null;
159319
160021
  handleReply(this[kDispatches]);
159320
- }, delay2);
160022
+ }, delay4);
159321
160023
  } else {
159322
160024
  handleReply(this[kDispatches]);
159323
160025
  }
@@ -159687,13 +160389,13 @@ var require_mock_call_history = __commonJS({
159687
160389
  function makeFilterCalls(parameterName) {
159688
160390
  return (parameterValue) => {
159689
160391
  if (typeof parameterValue === "string" || parameterValue == null) {
159690
- return this.logs.filter((log21) => {
159691
- return log21[parameterName] === parameterValue;
160392
+ return this.logs.filter((log23) => {
160393
+ return log23[parameterName] === parameterValue;
159692
160394
  });
159693
160395
  }
159694
160396
  if (parameterValue instanceof RegExp) {
159695
- return this.logs.filter((log21) => {
159696
- return parameterValue.test(log21[parameterName]);
160397
+ return this.logs.filter((log23) => {
160398
+ return parameterValue.test(log23[parameterName]);
159697
160399
  });
159698
160400
  }
159699
160401
  throw new InvalidArgumentError3(`${parameterName} parameter should be one of string, regexp, undefined or null`);
@@ -159788,8 +160490,8 @@ var require_mock_call_history = __commonJS({
159788
160490
  return this.logs.filter(criteria);
159789
160491
  }
159790
160492
  if (criteria instanceof RegExp) {
159791
- return this.logs.filter((log21) => {
159792
- return criteria.test(log21.toString());
160493
+ return this.logs.filter((log23) => {
160494
+ return criteria.test(log23.toString());
159793
160495
  });
159794
160496
  }
159795
160497
  if (typeof criteria === "object" && criteria !== null) {
@@ -159839,13 +160541,13 @@ var require_mock_call_history = __commonJS({
159839
160541
  this.logs = [];
159840
160542
  }
159841
160543
  [kMockCallHistoryAddLog](requestInit) {
159842
- const log21 = new MockCallHistoryLog(requestInit);
159843
- this.logs.push(log21);
159844
- return log21;
160544
+ const log23 = new MockCallHistoryLog(requestInit);
160545
+ this.logs.push(log23);
160546
+ return log23;
159845
160547
  }
159846
160548
  *[Symbol.iterator]() {
159847
- for (const log21 of this.calls()) {
159848
- yield log21;
160549
+ for (const log23 of this.calls()) {
160550
+ yield log23;
159849
160551
  }
159850
160552
  }
159851
160553
  };
@@ -160230,7 +160932,7 @@ var require_snapshot_utils = __commonJS({
160230
160932
  var require_snapshot_recorder = __commonJS({
160231
160933
  "node_modules/undici/lib/mock/snapshot-recorder.js"(exports2, module2) {
160232
160934
  "use strict";
160233
- var { writeFile: writeFile15, readFile: readFile18, mkdir: mkdir14 } = require("node:fs/promises");
160935
+ var { writeFile: writeFile15, readFile: readFile18, mkdir: mkdir15 } = require("node:fs/promises");
160234
160936
  var { dirname: dirname9, resolve: resolve9 } = require("node:path");
160235
160937
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = require("node:timers");
160236
160938
  var { InvalidArgumentError: InvalidArgumentError3, UndiciError } = require_errors3();
@@ -160462,7 +161164,7 @@ var require_snapshot_recorder = __commonJS({
160462
161164
  throw new InvalidArgumentError3("Snapshot path is required");
160463
161165
  }
160464
161166
  const resolvedPath = resolve9(path10);
160465
- await mkdir14(dirname9(resolvedPath), { recursive: true });
161167
+ await mkdir15(dirname9(resolvedPath), { recursive: true });
160466
161168
  const data2 = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
160467
161169
  hash: hash2,
160468
161170
  snapshot
@@ -189145,14 +189847,14 @@ var require_turndown_cjs = __commonJS({
189145
189847
  } else if (node.nodeType === 1) {
189146
189848
  replacement = replacementForNode.call(self2, node);
189147
189849
  }
189148
- return join20(output, replacement);
189850
+ return join21(output, replacement);
189149
189851
  }, "");
189150
189852
  }
189151
189853
  function postProcess(output) {
189152
189854
  var self2 = this;
189153
189855
  this.rules.forEach(function(rule) {
189154
189856
  if (typeof rule.append === "function") {
189155
- output = join20(output, rule.append(self2.options));
189857
+ output = join21(output, rule.append(self2.options));
189156
189858
  }
189157
189859
  });
189158
189860
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -189164,7 +189866,7 @@ var require_turndown_cjs = __commonJS({
189164
189866
  if (whitespace2.leading || whitespace2.trailing) content = content.trim();
189165
189867
  return whitespace2.leading + rule.replacement(content, node, this.options) + whitespace2.trailing;
189166
189868
  }
189167
- function join20(output, replacement) {
189869
+ function join21(output, replacement) {
189168
189870
  var s1 = trimTrailingNewlines(output);
189169
189871
  var s2 = trimLeadingNewlines(replacement);
189170
189872
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -189263,9 +189965,9 @@ var McpManager = class {
189263
189965
  if (attempt === maxRetries - 1) {
189264
189966
  break;
189265
189967
  }
189266
- const delay2 = Math.pow(2, attempt) * 100;
189267
- logger2.debug(`Tool ${name21} failed (attempt ${attempt + 1}/${maxRetries}), retrying in ${delay2}ms...`);
189268
- await new Promise((resolve9) => setTimeout(resolve9, delay2));
189968
+ const delay4 = Math.pow(2, attempt) * 100;
189969
+ logger2.debug(`Tool ${name21} failed (attempt ${attempt + 1}/${maxRetries}), retrying in ${delay4}ms...`);
189970
+ await new Promise((resolve9) => setTimeout(resolve9, delay4));
189269
189971
  }
189270
189972
  }
189271
189973
  throw lastError || new Error(`Tool ${name21} execution failed after ${maxRetries} attempts`);
@@ -189618,8 +190320,8 @@ var SessionCleaner = class {
189618
190320
  for (const dir of dirs) {
189619
190321
  const fullPath = import_path6.default.join(sessionsDir, dir);
189620
190322
  try {
189621
- const stat4 = await import_promises4.default.stat(fullPath);
189622
- if (stat4.isDirectory() && stat4.mtimeMs < cutoffTime) {
190323
+ const stat5 = await import_promises4.default.stat(fullPath);
190324
+ if (stat5.isDirectory() && stat5.mtimeMs < cutoffTime) {
189623
190325
  await import_promises4.default.rm(fullPath, { recursive: true, force: true });
189624
190326
  cleaned++;
189625
190327
  logger7.debug("Removed old session:", dir);
@@ -189893,11 +190595,11 @@ var WeComBotClient = class {
189893
190595
  scheduleReconnect() {
189894
190596
  if (this.reconnectTimer || this.disposed || this.intentionalDisconnect) return;
189895
190597
  this.reconnectAttempts += 1;
189896
- const delay2 = Math.min(
190598
+ const delay4 = Math.min(
189897
190599
  INITIAL_RECONNECT_DELAY_MS * 2 ** (this.reconnectAttempts - 1),
189898
190600
  MAX_RECONNECT_DELAY_MS
189899
190601
  );
189900
- log2.info(`schedule reconnect in ${delay2}ms, attempt=${this.reconnectAttempts}`);
190602
+ log2.info(`schedule reconnect in ${delay4}ms, attempt=${this.reconnectAttempts}`);
189901
190603
  this.setState("reconnecting");
189902
190604
  this.reconnectTimer = setTimeout(() => {
189903
190605
  this.reconnectTimer = null;
@@ -189907,7 +190609,7 @@ var WeComBotClient = class {
189907
190609
  this.scheduleReconnect();
189908
190610
  }
189909
190611
  });
189910
- }, delay2);
190612
+ }, delay4);
189911
190613
  }
189912
190614
  clearReconnectTimer() {
189913
190615
  if (!this.reconnectTimer) return;
@@ -191620,7 +192322,7 @@ function checkSendFilePolicy(resolvedPath) {
191620
192322
  // src/adapters/channel/shared/ChannelManager.ts
191621
192323
  var import_promises13 = require("node:fs/promises");
191622
192324
  init_config();
191623
- var log18 = createLogger("ChannelManager");
192325
+ var log20 = createLogger("ChannelManager");
191624
192326
  var ChannelManager = class {
191625
192327
  adapters = /* @__PURE__ */ new Map();
191626
192328
  configStore;
@@ -191656,7 +192358,7 @@ var ChannelManager = class {
191656
192358
  this.adapters.set(config3.connectionId, adapter2);
191657
192359
  this.subscribeAdapter(config3.connectionId, adapter2);
191658
192360
  await adapter2.connect();
191659
- log18.info(`registered channel: ${config3.connectionId}`);
192361
+ log20.info(`registered channel: ${config3.connectionId}`);
191660
192362
  }
191661
192363
  /** 更新已注册接入的绑定目标 */
191662
192364
  async updateTarget(connectionId, target) {
@@ -191669,7 +192371,7 @@ var ChannelManager = class {
191669
192371
  this.adapters.set(connectionId, adapter2);
191670
192372
  this.subscribeAdapter(connectionId, adapter2);
191671
192373
  await adapter2.connect();
191672
- log18.info(`updated target for channel: ${connectionId}`);
192374
+ log20.info(`updated target for channel: ${connectionId}`);
191673
192375
  }
191674
192376
  /** 连接已注册的接入 */
191675
192377
  async connect(connectionId) {
@@ -191704,7 +192406,7 @@ var ChannelManager = class {
191704
192406
  await this.secretStore.delete(config3.secretRef);
191705
192407
  await this.configStore.delete(connectionId);
191706
192408
  }
191707
- log18.info(`unregistered channel: ${connectionId}`);
192409
+ log20.info(`unregistered channel: ${connectionId}`);
191708
192410
  }
191709
192411
  /** 发送消息到外部 */
191710
192412
  async send(reply) {
@@ -191767,11 +192469,11 @@ var ChannelManager = class {
191767
192469
  this.subscribeAdapter(config3.connectionId, adapter2);
191768
192470
  await adapter2.connect();
191769
192471
  } catch (error48) {
191770
- log18.error(`failed to init channel ${config3.connectionId}:`, error48);
192472
+ log20.error(`failed to init channel ${config3.connectionId}:`, error48);
191771
192473
  }
191772
192474
  }
191773
192475
  this.initialized = true;
191774
- log18.info(`channel manager initialized, adapters=${this.adapters.size}`);
192476
+ log20.info(`channel manager initialized, adapters=${this.adapters.size}`);
191775
192477
  }
191776
192478
  /** 清理所有资源(app quit 时调用) */
191777
192479
  async dispose() {
@@ -191783,9 +192485,9 @@ var ChannelManager = class {
191783
192485
  if (adapter2?.stream) {
191784
192486
  try {
191785
192487
  await adapter2.stream.finish(pending.streamFrame, pending.streamId, "\u26A0\uFE0F \u7CFB\u7EDF\u5173\u95ED");
191786
- log18.info(`stream finished (dispose): runId=${runId}`);
192488
+ log20.info(`stream finished (dispose): runId=${runId}`);
191787
192489
  } catch (err) {
191788
- log18.warn(`stream finish failed during dispose: runId=${runId}`, err);
192490
+ log20.warn(`stream finish failed during dispose: runId=${runId}`, err);
191789
192491
  }
191790
192492
  }
191791
192493
  }
@@ -191794,7 +192496,7 @@ var ChannelManager = class {
191794
192496
  try {
191795
192497
  await adapter2.disconnect();
191796
192498
  } catch (error48) {
191797
- log18.warn(`disconnect failed: ${connectionId}`, error48);
192499
+ log20.warn(`disconnect failed: ${connectionId}`, error48);
191798
192500
  }
191799
192501
  adapter2.dispose();
191800
192502
  }
@@ -191868,7 +192570,7 @@ var ChannelManager = class {
191868
192570
  switch (event.type) {
191869
192571
  case "message.received":
191870
192572
  if (this.dedupe.isProcessed(event.message.channelType, event.message.connectionId, event.message.id)) {
191871
- log18.info(`skip duplicated inbound message: ${event.message.id}`);
192573
+ log20.info(`skip duplicated inbound message: ${event.message.id}`);
191872
192574
  return;
191873
192575
  }
191874
192576
  if (this.aggregator.enabled) {
@@ -191878,10 +192580,10 @@ var ChannelManager = class {
191878
192580
  }
191879
192581
  return;
191880
192582
  case "status.changed":
191881
- log18.info(`channel status changed: ${connectionId} -> ${event.status.state}`);
192583
+ log20.info(`channel status changed: ${connectionId} -> ${event.status.state}`);
191882
192584
  return;
191883
192585
  case "error":
191884
- log18.warn(`channel error: ${connectionId} ${event.error.code} ${event.error.message}`);
192586
+ log20.warn(`channel error: ${connectionId} ${event.error.code} ${event.error.message}`);
191885
192587
  return;
191886
192588
  default: {
191887
192589
  const exhaustive = event;
@@ -191897,12 +192599,12 @@ var ChannelManager = class {
191897
192599
  }
191898
192600
  async routeInboundMessage(message) {
191899
192601
  if (this.dedupe.isProcessed(message.channelType, message.connectionId, message.id)) {
191900
- log18.info(`skip duplicated inbound message: ${message.id}`);
192602
+ log20.info(`skip duplicated inbound message: ${message.id}`);
191901
192603
  return;
191902
192604
  }
191903
192605
  const config3 = await this.configStore.get(message.connectionId);
191904
192606
  if (!config3) {
191905
- log18.warn(`channel config not found for inbound message: ${message.connectionId}`);
192607
+ log20.warn(`channel config not found for inbound message: ${message.connectionId}`);
191906
192608
  return;
191907
192609
  }
191908
192610
  try {
@@ -191913,7 +192615,7 @@ var ChannelManager = class {
191913
192615
  }
191914
192616
  this.dedupe.markProcessed(message.channelType, message.connectionId, message.id);
191915
192617
  } catch (error48) {
191916
- log18.error(`failed to route inbound message ${message.id}:`, error48);
192618
+ log20.error(`failed to route inbound message ${message.id}:`, error48);
191917
192619
  }
191918
192620
  }
191919
192621
  async deliverToAgent(config3, message) {
@@ -192035,9 +192737,9 @@ var ChannelManager = class {
192035
192737
  if (adapter2.sendTyping) {
192036
192738
  try {
192037
192739
  await adapter2.sendTyping(pending.chatId, pending.chatType);
192038
- log18.noise(`typing sent: runId=${runId}, chatId=${pending.chatId}`);
192740
+ log20.noise(`typing sent: runId=${runId}, chatId=${pending.chatId}`);
192039
192741
  } catch (err) {
192040
- log18.noise(`typing send failed: ${err instanceof Error ? err.message : String(err)}`);
192742
+ log20.noise(`typing send failed: ${err instanceof Error ? err.message : String(err)}`);
192041
192743
  }
192042
192744
  }
192043
192745
  return;
@@ -192045,10 +192747,10 @@ var ChannelManager = class {
192045
192747
  try {
192046
192748
  await adapter2.stream.start(pending.streamFrame, pending.streamId);
192047
192749
  pending.streamStarted = true;
192048
- log18.noise(`stream started: runId=${runId}, streamId=${pending.streamId}`);
192750
+ log20.noise(`stream started: runId=${runId}, streamId=${pending.streamId}`);
192049
192751
  } catch (err) {
192050
192752
  pending.streamStarted = false;
192051
- log18.warn(`stream start failed, will fallback to send on completed: ${err instanceof Error ? err.message : String(err)}`);
192753
+ log20.warn(`stream start failed, will fallback to send on completed: ${err instanceof Error ? err.message : String(err)}`);
192052
192754
  }
192053
192755
  }
192054
192756
  /** message.block.updated → 刷新流式内容 */
@@ -192063,14 +192765,14 @@ var ChannelManager = class {
192063
192765
  try {
192064
192766
  await adapter2.stream.update(pending.streamFrame, pending.streamId, block.text);
192065
192767
  } catch (err) {
192066
- log18.noise(`stream update failed: ${err instanceof Error ? err.message : String(err)}`);
192768
+ log20.noise(`stream update failed: ${err instanceof Error ? err.message : String(err)}`);
192067
192769
  }
192068
192770
  }
192069
192771
  /** run.completed → 结束流式回复(或降级为一次性 send) */
192070
192772
  async handleRunCompleted(conversationId, runId, messageId) {
192071
192773
  const pending = this.pendingReplies.get(runId);
192072
192774
  if (!pending) {
192073
- log18.warn(`handleRunCompleted: no pending reply for runId=${runId}, conversationId=${conversationId}`);
192775
+ log20.warn(`handleRunCompleted: no pending reply for runId=${runId}, conversationId=${conversationId}`);
192074
192776
  return;
192075
192777
  }
192076
192778
  this.pendingReplies.delete(runId);
@@ -192078,20 +192780,20 @@ var ChannelManager = class {
192078
192780
  try {
192079
192781
  const assistantMsg = await messageRepository.findById(messageId);
192080
192782
  if (!assistantMsg || assistantMsg.senderRole !== "agent") {
192081
- log18.warn(`no assistant reply found for channel outbound: ${conversationId}, messageId=${messageId}`);
192783
+ log20.warn(`no assistant reply found for channel outbound: ${conversationId}, messageId=${messageId}`);
192082
192784
  return;
192083
192785
  }
192084
192786
  await this.sendFilesFromBlocks(pending, assistantMsg.blocks);
192085
192787
  const text4 = assistantMsg.blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
192086
192788
  if (!text4) {
192087
- log18.info(`assistant reply has no text, skip text outbound: ${conversationId}`);
192789
+ log20.info(`assistant reply has no text, skip text outbound: ${conversationId}`);
192088
192790
  return;
192089
192791
  }
192090
192792
  if (pending.streamStarted) {
192091
192793
  const adapter2 = this.adapters.get(pending.connectionId);
192092
192794
  if (adapter2?.stream) {
192093
192795
  await adapter2.stream.finish(pending.streamFrame, pending.streamId, text4);
192094
- log18.info(`stream finished: conversationId=${conversationId}`);
192796
+ log20.info(`stream finished: conversationId=${conversationId}`);
192095
192797
  return;
192096
192798
  }
192097
192799
  }
@@ -192103,12 +192805,12 @@ var ChannelManager = class {
192103
192805
  };
192104
192806
  const result = await this.send(reply);
192105
192807
  if (result.ok) {
192106
- log18.info(`channel reply sent: conversationId=${conversationId}`);
192808
+ log20.info(`channel reply sent: conversationId=${conversationId}`);
192107
192809
  } else {
192108
- log18.error(`channel reply failed: ${result.code} ${result.message}`);
192810
+ log20.error(`channel reply failed: ${result.code} ${result.message}`);
192109
192811
  }
192110
192812
  } catch (error48) {
192111
- log18.error(`channel reply error:`, error48);
192813
+ log20.error(`channel reply error:`, error48);
192112
192814
  }
192113
192815
  }
192114
192816
  /** 清理 run 失败/停止时的残留上下文(含流式会话收尾) */
@@ -192119,9 +192821,9 @@ var ChannelManager = class {
192119
192821
  if (adapter2?.stream) {
192120
192822
  try {
192121
192823
  await adapter2.stream.finish(pending.streamFrame, pending.streamId, "\u26A0\uFE0F \u56DE\u590D\u51FA\u9519");
192122
- log18.info(`stream finished (run cleanup): conversationId=${conversationId}, runId=${runId}`);
192824
+ log20.info(`stream finished (run cleanup): conversationId=${conversationId}, runId=${runId}`);
192123
192825
  } catch (err) {
192124
- log18.warn(`stream finish failed during cleanup: ${err instanceof Error ? err.message : String(err)}`);
192826
+ log20.warn(`stream finish failed during cleanup: ${err instanceof Error ? err.message : String(err)}`);
192125
192827
  }
192126
192828
  }
192127
192829
  }
@@ -192132,28 +192834,28 @@ var ChannelManager = class {
192132
192834
  async sendFilesFromBlocks(pending, blocks) {
192133
192835
  const adapter2 = this.adapters.get(pending.connectionId);
192134
192836
  if (!adapter2) {
192135
- log18.error(`sendFilesFromBlocks: adapter not found for connectionId=${pending.connectionId}`);
192837
+ log20.error(`sendFilesFromBlocks: adapter not found for connectionId=${pending.connectionId}`);
192136
192838
  return;
192137
192839
  }
192138
192840
  const failures = [];
192139
192841
  let total = 0;
192140
- log18.info(`sendFilesFromBlocks: scanning ${blocks.length} blocks, channelType=${pending.channelType}`);
192842
+ log20.info(`sendFilesFromBlocks: scanning ${blocks.length} blocks, channelType=${pending.channelType}`);
192141
192843
  for (const block of blocks) {
192142
192844
  if (block.type === "tool") {
192143
192845
  const toolCall = block.toolCall;
192144
192846
  if (toolCall.name !== "sendFile" || toolCall.status !== "success") continue;
192145
192847
  const filePath = this.extractSendFilePath(toolCall.args);
192146
- log18.info(`sendFilesFromBlocks: found sendFile toolCall, filePath=${filePath}, args=${JSON.stringify(toolCall.args)}`);
192848
+ log20.info(`sendFilesFromBlocks: found sendFile toolCall, filePath=${filePath}, args=${JSON.stringify(toolCall.args)}`);
192147
192849
  if (!filePath) continue;
192148
192850
  total++;
192149
192851
  try {
192150
192852
  await this.assertSendableFilePath(filePath);
192151
192853
  await adapter2.sendFile(pending.chatId, filePath, pending.chatType);
192152
- log18.info(`channel file sent: ${pending.channelType} -> ${filePath}`);
192854
+ log20.info(`channel file sent: ${pending.channelType} -> ${filePath}`);
192153
192855
  } catch (err) {
192154
192856
  const msg = err instanceof Error ? err.message : String(err);
192155
192857
  failures.push(`${filePath}: ${msg}`);
192156
- log18.error(`channel file send failed: ${pending.channelType} -> ${msg}`);
192858
+ log20.error(`channel file send failed: ${pending.channelType} -> ${msg}`);
192157
192859
  }
192158
192860
  continue;
192159
192861
  }
@@ -192166,17 +192868,17 @@ var ChannelManager = class {
192166
192868
  const absolutePath = await attachmentStorage.getAttachmentPath(att.path);
192167
192869
  await this.assertSendableFilePath(absolutePath);
192168
192870
  await adapter2.sendFile(pending.chatId, absolutePath, pending.chatType);
192169
- log18.info(`channel image sent: ${pending.channelType} -> ${absolutePath}`);
192871
+ log20.info(`channel image sent: ${pending.channelType} -> ${absolutePath}`);
192170
192872
  } catch (err) {
192171
192873
  const msg = err instanceof Error ? err.message : String(err);
192172
192874
  failures.push(`${att.path}: ${msg}`);
192173
- log18.error(`channel image send failed: ${pending.channelType} -> ${msg}`);
192875
+ log20.error(`channel image send failed: ${pending.channelType} -> ${msg}`);
192174
192876
  }
192175
192877
  }
192176
192878
  }
192177
192879
  }
192178
192880
  if (failures.length > 0) {
192179
- log18.error(`sendFiles: ${failures.length}/${total} files failed: ${failures.join("; ")}`);
192881
+ log20.error(`sendFiles: ${failures.length}/${total} files failed: ${failures.join("; ")}`);
192180
192882
  }
192181
192883
  }
192182
192884
  extractSendFilePath(args2) {
@@ -211498,6 +212200,7 @@ var DESKTOP_ONLY_CHANNELS = /* @__PURE__ */ new Set([
211498
212200
  IPC_OPEN_EXTERNAL_URL,
211499
212201
  IPC_OPEN_LOCAL_PATH,
211500
212202
  IPC_SHOW_LOCAL_ITEM_IN_FOLDER,
212203
+ IPC_COPY_IMAGE_TO_CLIPBOARD,
211501
212204
  IPC_OPEN_ATTACHMENT
211502
212205
  ]);
211503
212206
  var RpcRouter = class {
@@ -211553,12 +212256,83 @@ function registerRpc(channel, handler) {
211553
212256
 
211554
212257
  // src/electron/main/telemetry/MainTelemetryService.ts
211555
212258
  init_logger();
212259
+
212260
+ // src/electron/main/telemetry/TelemetryPolicy.ts
212261
+ var DEFAULT_UPLOAD_ALLOWLIST = /* @__PURE__ */ new Set([
212262
+ "app.started",
212263
+ "conversation.created",
212264
+ "conversation.deleted",
212265
+ "session.created",
212266
+ "session.deleted",
212267
+ "message.sent",
212268
+ "message.stopped",
212269
+ "group.thread.created",
212270
+ "update.check",
212271
+ "update.available",
212272
+ "feedback.sent",
212273
+ "run_completed",
212274
+ "llm_call_finished",
212275
+ "crash.summary",
212276
+ "memory.alert"
212277
+ ]);
212278
+ var BLOCKED_PROPERTY_PATTERN = /(prompt|content|messageText|response|stack|path|file|directory|command|stdout|stderr|url|query|apikey|api_key|secret|password|authorization|cookie)/i;
212279
+ var MAX_PROPERTIES = 50;
212280
+ var MAX_STRING_LENGTH2 = 200;
212281
+ var DefaultTelemetryPolicy = class {
212282
+ constructor(allowlist = DEFAULT_UPLOAD_ALLOWLIST) {
212283
+ this.allowlist = allowlist;
212284
+ }
212285
+ allowlist;
212286
+ shouldUploadEventName(name21) {
212287
+ return this.allowlist.has(name21);
212288
+ }
212289
+ sanitizeProperties(name21, properties) {
212290
+ if (!properties) return void 0;
212291
+ const out = {};
212292
+ let count = 0;
212293
+ for (const [rawKey, value] of Object.entries(properties)) {
212294
+ if (count >= MAX_PROPERTIES) break;
212295
+ const key = rawKey.trim();
212296
+ if (!key || key.length > 100 || BLOCKED_PROPERTY_PATTERN.test(key)) continue;
212297
+ if (typeof value === "string") {
212298
+ out[key] = normalizeStringProperty(key, value);
212299
+ count++;
212300
+ } else if (typeof value === "number" && Number.isFinite(value)) {
212301
+ out[key] = bucketNumberIfNeeded(key, value);
212302
+ count++;
212303
+ } else if (typeof value === "boolean") {
212304
+ out[key] = value;
212305
+ count++;
212306
+ }
212307
+ }
212308
+ return Object.keys(out).length > 0 ? out : void 0;
212309
+ }
212310
+ };
212311
+ var defaultTelemetryPolicy = new DefaultTelemetryPolicy();
212312
+ function normalizeStringProperty(key, value) {
212313
+ return value.slice(0, MAX_STRING_LENGTH2);
212314
+ }
212315
+ function bucketNumberIfNeeded(key, value) {
212316
+ if (/duration|latency|elapsed/i.test(key)) return bucket(value, [1e3, 3e3, 1e4, 3e4, 6e4], "ms");
212317
+ if (/token/i.test(key)) return bucket(value, [1e3, 4e3, 16e3, 64e3, 128e3], "tokens");
212318
+ if (/length|size|bytes|count/i.test(key)) return bucket(value, [1, 10, 100, 1e3, 1e4], "count");
212319
+ if (/rss|heap|memory/i.test(key)) return bucket(value, [256, 512, 1024, 1536, 2200, 4096], "mb");
212320
+ return value;
212321
+ }
212322
+ function bucket(value, limits, suffix) {
212323
+ for (const limit of limits) {
212324
+ if (value <= limit) return `<=${limit}${suffix}`;
212325
+ }
212326
+ return `>${limits[limits.length - 1]}${suffix}`;
212327
+ }
212328
+
212329
+ // src/electron/main/telemetry/MainTelemetryService.ts
211556
212330
  var logger22 = createLogger("telemetry");
211557
212331
  function sanitizeEventName(value) {
211558
212332
  if (typeof value !== "string") return "";
211559
212333
  return value.trim().slice(0, 200);
211560
212334
  }
211561
- function sanitizeProperties(input) {
212335
+ function sanitizeProperties(name21, input) {
211562
212336
  if (!input || typeof input !== "object" || Array.isArray(input)) return void 0;
211563
212337
  const out = {};
211564
212338
  let count = 0;
@@ -211577,7 +212351,7 @@ function sanitizeProperties(input) {
211577
212351
  count++;
211578
212352
  }
211579
212353
  }
211580
- return Object.keys(out).length > 0 ? out : void 0;
212354
+ return defaultTelemetryPolicy.sanitizeProperties(name21, Object.keys(out).length > 0 ? out : void 0);
211581
212355
  }
211582
212356
  function sanitizeFeedback(input) {
211583
212357
  if (!input || typeof input !== "object") return null;
@@ -211611,7 +212385,8 @@ var MainTelemetryService = class {
211611
212385
  }
211612
212386
  track(name21, properties) {
211613
212387
  try {
211614
- this.client.track(name21, properties);
212388
+ if (!defaultTelemetryPolicy.shouldUploadEventName(name21)) return;
212389
+ this.client.track(name21, defaultTelemetryPolicy.sanitizeProperties(name21, properties));
211615
212390
  } catch (err) {
211616
212391
  logger22.debug("telemetry track ignored", err);
211617
212392
  }
@@ -211669,7 +212444,7 @@ var MainTelemetryService = class {
211669
212444
  registerRpc(IPC_TELEMETRY_TRACK, (name21, properties) => {
211670
212445
  const safeName = sanitizeEventName(name21);
211671
212446
  if (!safeName) return;
211672
- this.track(safeName, sanitizeProperties(properties));
212447
+ this.track(safeName, sanitizeProperties(safeName, properties));
211673
212448
  });
211674
212449
  registerRpc(IPC_TELEMETRY_FEEDBACK, async (feedback) => {
211675
212450
  const safe = sanitizeFeedback(feedback);
@@ -211781,7 +212556,8 @@ function withLlmLanguageTelemetry(model, context2) {
211781
212556
  ok: true,
211782
212557
  streaming: false,
211783
212558
  durationMs: Date.now() - startedAt,
211784
- finishReason: result.finishReason?.unified
212559
+ finishReason: result.finishReason?.unified,
212560
+ ...summarizeUsage(result.usage)
211785
212561
  });
211786
212562
  return result;
211787
212563
  } catch (error48) {
@@ -211913,6 +212689,11 @@ function sanitizeLlmTelemetry(event) {
211913
212689
  addBoolean(properties, "hasFiles", event.hasFiles);
211914
212690
  addBoolean(properties, "hasTools", event.hasTools);
211915
212691
  addString(properties, "finishReason", event.finishReason);
212692
+ addNumber(properties, "inputTokens", event.inputTokens);
212693
+ addNumber(properties, "outputTokens", event.outputTokens);
212694
+ addNumber(properties, "totalTokens", event.totalTokens);
212695
+ addNumber(properties, "cachedInputTokens", event.cachedInputTokens);
212696
+ addNumber(properties, "reasoningTokens", event.reasoningTokens);
211916
212697
  return properties;
211917
212698
  }
211918
212699
  function summarizeLanguageInput(params) {
@@ -211955,12 +212736,14 @@ function summarizeImageInput(params) {
211955
212736
  function instrumentStream(stream, context2, input, startedAt) {
211956
212737
  let captured = false;
211957
212738
  let finishReason;
212739
+ let usage;
211958
212740
  const captureOnce = (event) => {
211959
212741
  if (captured) return;
211960
212742
  captured = true;
211961
212743
  captureLlmCallFinished({
211962
212744
  ...context2,
211963
212745
  ...input,
212746
+ ...usage,
211964
212747
  ...event,
211965
212748
  streaming: true,
211966
212749
  durationMs: Date.now() - startedAt
@@ -211970,6 +212753,7 @@ function instrumentStream(stream, context2, input, startedAt) {
211970
212753
  transform(chunk, controller) {
211971
212754
  if (isStreamFinishChunk(chunk)) {
211972
212755
  finishReason = chunk.finishReason?.unified;
212756
+ usage = summarizeUsage(chunk.usage);
211973
212757
  } else if (isStreamErrorChunk(chunk)) {
211974
212758
  captureOnce({ ok: false, ...classifyLlmError(chunk.error) });
211975
212759
  }
@@ -211986,6 +212770,28 @@ function isStreamFinishChunk(chunk) {
211986
212770
  function isStreamErrorChunk(chunk) {
211987
212771
  return !!chunk && typeof chunk === "object" && chunk.type === "error";
211988
212772
  }
212773
+ function summarizeUsage(usage) {
212774
+ if (!usage || typeof usage !== "object") return void 0;
212775
+ const record2 = usage;
212776
+ const inputTokens = getFiniteNumber(record2.inputTokens);
212777
+ const outputTokens = getFiniteNumber(record2.outputTokens);
212778
+ const totalTokens = getFiniteNumber(record2.totalTokens) ?? (typeof inputTokens === "number" || typeof outputTokens === "number" ? (inputTokens ?? 0) + (outputTokens ?? 0) : void 0);
212779
+ const cachedInputTokens = getFiniteNumber(record2.cachedInputTokens);
212780
+ const reasoningTokens = getFiniteNumber(record2.reasoningTokens);
212781
+ if (inputTokens === void 0 && outputTokens === void 0 && totalTokens === void 0 && cachedInputTokens === void 0 && reasoningTokens === void 0) {
212782
+ return void 0;
212783
+ }
212784
+ const summarizedUsage = {};
212785
+ setUsageField(summarizedUsage, "inputTokens", inputTokens);
212786
+ setUsageField(summarizedUsage, "outputTokens", outputTokens);
212787
+ setUsageField(summarizedUsage, "totalTokens", totalTokens);
212788
+ setUsageField(summarizedUsage, "cachedInputTokens", cachedInputTokens);
212789
+ setUsageField(summarizedUsage, "reasoningTokens", reasoningTokens);
212790
+ return summarizedUsage;
212791
+ }
212792
+ function getFiniteNumber(value) {
212793
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
212794
+ }
211989
212795
  function isLanguageModelV3(model) {
211990
212796
  return typeof model === "object" && model !== null && model.specificationVersion === "v3";
211991
212797
  }
@@ -212036,6 +212842,11 @@ function addNumber(properties, key, value) {
212036
212842
  function addBoolean(properties, key, value) {
212037
212843
  if (typeof value === "boolean") properties[key] = value;
212038
212844
  }
212845
+ function setUsageField(usage, key, value) {
212846
+ if (typeof value === "number") {
212847
+ usage[key] = value;
212848
+ }
212849
+ }
212039
212850
  function hasOwnInputImages(value) {
212040
212851
  if (!value || typeof value !== "object") return false;
212041
212852
  return Object.keys(value).some((key) => key.toLowerCase().includes("image"));
@@ -212217,6 +213028,7 @@ var AutoUpdaterManager = class {
212217
213028
  }
212218
213029
  /** 用户每日首次发送消息后静默检查(延迟 3s) */
212219
213030
  triggerDailyAutoCheck() {
213031
+ if (process.env.KAI_RUNTIME === "cli") return;
212220
213032
  const { app } = getElectron();
212221
213033
  if (!app.isPackaged) return;
212222
213034
  const enabled = config.get("autoCheckForUpdates");
@@ -212326,7 +213138,7 @@ async function resolveIdType(id) {
212326
213138
  }
212327
213139
  return { type: "single", conversationId: id };
212328
213140
  }
212329
- async function resolveTargetAgent(threadId, groupId, mentionDirective) {
213141
+ async function resolveTargetAgent(threadId, groupId, mentionDirective, supervisorId) {
212330
213142
  if (!groupId) {
212331
213143
  const targetAgentId2 = mentionDirective.kind === "single" ? mentionDirective.agentId : threadId;
212332
213144
  if (!targetAgentId2) {
@@ -212334,6 +213146,10 @@ async function resolveTargetAgent(threadId, groupId, mentionDirective) {
212334
213146
  }
212335
213147
  return targetAgentId2;
212336
213148
  }
213149
+ if (supervisorId) {
213150
+ await threadService.updateSpeaker(threadId, supervisorId);
213151
+ return supervisorId;
213152
+ }
212337
213153
  if (mentionDirective.kind === "broadcast") {
212338
213154
  const thread2 = await threadFileStore.findById(threadId);
212339
213155
  if (thread2?.speakerId) return thread2.speakerId;
@@ -212423,7 +213239,12 @@ async function handleSendUserMessage(request) {
212423
213239
  throw new Error("Conversation is being compacted, please wait");
212424
213240
  }
212425
213241
  const blocks = buildUserBlocks(content, attachments);
212426
- const targetAgentId = await resolveTargetAgent(conversationId, groupId, mentionDirective);
213242
+ const targetAgentId = await resolveTargetAgent(
213243
+ conversationId,
213244
+ groupId,
213245
+ mentionDirective,
213246
+ normalizedMetadata?.supervisorId
213247
+ );
212427
213248
  try {
212428
213249
  const message = await messagePublisher.publishUserMessage(conversationId, blocks, {
212429
213250
  metadata: normalizedMetadata,
@@ -212542,7 +213363,7 @@ function registerChatHandlers() {
212542
213363
  }
212543
213364
 
212544
213365
  // src/electron/main/handlers/conversation-handlers.ts
212545
- var import_promises14 = require("fs/promises");
213366
+ var import_promises15 = require("fs/promises");
212546
213367
  init_conversation_event_bus();
212547
213368
  init_message2();
212548
213369
  init_agent_file_store();
@@ -212557,6 +213378,40 @@ init_thread_file_store();
212557
213378
  init_agent_file_store();
212558
213379
  init_ThreadManagerPool();
212559
213380
  init_logger();
213381
+
213382
+ // src/electron/main/handlers/avatar-storage.ts
213383
+ var import_promises14 = require("fs/promises");
213384
+ var import_path18 = require("path");
213385
+ var import_sharp = __toESM(require("sharp"));
213386
+ var AVATAR_DIR = "avatars";
213387
+ var AVATAR_FILE = "avatar.png";
213388
+ var AVATAR_RELATIVE_PATH = `${AVATAR_DIR}/${AVATAR_FILE}`;
213389
+ var MAX_AVATAR_SOURCE_BYTES = 10 * 1024 * 1024;
213390
+ var AVATAR_SIZE = 256;
213391
+ async function writeOwnerAvatar(ownerDir, sourcePath) {
213392
+ const source = sourcePath.trim();
213393
+ if (!source) throw new Error("AVATAR_SOURCE_REQUIRED");
213394
+ const sourceStats = await (0, import_promises14.stat)(source).catch(() => null);
213395
+ if (!sourceStats?.isFile()) throw new Error("AVATAR_SOURCE_NOT_FILE");
213396
+ if (sourceStats.size > MAX_AVATAR_SOURCE_BYTES) throw new Error("AVATAR_IMAGE_TOO_LARGE");
213397
+ const avatarDir = (0, import_path18.join)(ownerDir, AVATAR_DIR);
213398
+ const avatarPath = (0, import_path18.join)(avatarDir, AVATAR_FILE);
213399
+ await (0, import_promises14.mkdir)(avatarDir, { recursive: true });
213400
+ try {
213401
+ await (0, import_sharp.default)(source, { failOn: "error" }).rotate().resize(AVATAR_SIZE, AVATAR_SIZE, { fit: "cover", position: "centre" }).png().toFile(avatarPath);
213402
+ } catch (error48) {
213403
+ if (error48 instanceof Error && /Input file contains unsupported image format|corrupt|invalid/i.test(error48.message)) {
213404
+ throw new Error("AVATAR_IMAGE_UNREADABLE");
213405
+ }
213406
+ throw new Error("AVATAR_SAVE_FAILED");
213407
+ }
213408
+ return AVATAR_RELATIVE_PATH;
213409
+ }
213410
+ async function removeOwnerAvatar(ownerDir) {
213411
+ await (0, import_promises14.rm)((0, import_path18.join)(ownerDir, AVATAR_DIR), { force: true, recursive: true }).catch(() => void 0);
213412
+ }
213413
+
213414
+ // src/electron/main/handlers/conversation-handlers.ts
212560
213415
  var logger25 = createLogger("conversation-handlers");
212561
213416
  var filterVisibleMessages = (messages) => messages.filter((message) => !message.compressionMetadata?.isSummary);
212562
213417
  async function removeAgentFromAllGroups(agentId) {
@@ -212592,6 +213447,7 @@ async function resolveConversationView(id) {
212592
213447
  title: group.name,
212593
213448
  type: "group",
212594
213449
  groupId: group.id,
213450
+ avatar: group.avatar,
212595
213451
  createdAt: thread.createdAt,
212596
213452
  updatedAt: thread.updatedAt,
212597
213453
  messageCount: 0
@@ -212678,6 +213534,7 @@ async function handleListConversations() {
212678
213534
  title: group.name,
212679
213535
  type: "group",
212680
213536
  groupId: group.id,
213537
+ avatar: group.avatar,
212681
213538
  createdAt: defaultThreadWithMsg.createdAt,
212682
213539
  updatedAt: defaultThreadWithMsg.updatedAt,
212683
213540
  messageCount: 0,
@@ -212739,6 +213596,17 @@ async function handleUpdateGroup(id, updates) {
212739
213596
  await emitConversationSnapshot(defaultThread.id);
212740
213597
  }
212741
213598
  }
213599
+ async function handleSetGroupAvatar(groupId, sourcePath) {
213600
+ if (!await groupFileStore.exists(groupId)) throw new Error("GROUP_NOT_FOUND");
213601
+ const avatar = await writeOwnerAvatar(getGroupDir(groupId), sourcePath);
213602
+ await handleUpdateGroup(groupId, { avatar });
213603
+ return { avatar };
213604
+ }
213605
+ async function handleRemoveGroupAvatar(groupId) {
213606
+ if (!await groupFileStore.exists(groupId)) throw new Error("GROUP_NOT_FOUND");
213607
+ await removeOwnerAvatar(getGroupDir(groupId));
213608
+ await handleUpdateGroup(groupId, { avatar: null });
213609
+ }
212742
213610
  async function handleDeleteGroup(id) {
212743
213611
  threadManagerPool.removeManagersByGroup(id);
212744
213612
  const threads = await threadFileStore.listByGroupId(id);
@@ -212807,7 +213675,7 @@ async function handleOpenAttachment(relativePath) {
212807
213675
  await shell2.openPath(absolutePath);
212808
213676
  }
212809
213677
  async function openDir(dir) {
212810
- await (0, import_promises14.mkdir)(dir, { recursive: true });
213678
+ await (0, import_promises15.mkdir)(dir, { recursive: true });
212811
213679
  const { shell: shell2 } = require("electron");
212812
213680
  const error48 = await shell2.openPath(dir);
212813
213681
  if (error48) throw new Error(error48);
@@ -212836,6 +213704,8 @@ function registerConversationHandlers() {
212836
213704
  registerRpc(IPC_GET_GROUP, handleGetGroup);
212837
213705
  registerRpc(IPC_UPDATE_GROUP, handleUpdateGroup);
212838
213706
  registerRpc(IPC_DELETE_GROUP, handleDeleteGroup);
213707
+ registerRpc(IPC_SET_GROUP_AVATAR, handleSetGroupAvatar);
213708
+ registerRpc(IPC_REMOVE_GROUP_AVATAR, handleRemoveGroupAvatar);
212839
213709
  registerRpc(IPC_GET_GROUP_MEMBERS, handleGetGroupMembers);
212840
213710
  registerRpc(IPC_LIST_GROUP_THREADS, handleListGroupThreads);
212841
213711
  registerRpc(IPC_GET_GROUP_THREAD, handleGetGroupThread);
@@ -213032,6 +213902,19 @@ async function handleDeleteAgent(id) {
213032
213902
  );
213033
213903
  await agentFileStore.delete(id);
213034
213904
  }
213905
+ async function handleSetAgentAvatar(agentId, sourcePath) {
213906
+ const agent = await loadAgent(agentId, true);
213907
+ if (!agent) throw new Error("AGENT_NOT_FOUND");
213908
+ const avatar = await writeOwnerAvatar(getAgentRootDir(agentId), sourcePath);
213909
+ await agentFileStore.writeMeta({ ...agent, avatar, updatedAt: Date.now() });
213910
+ return { avatar };
213911
+ }
213912
+ async function handleRemoveAgentAvatar(agentId) {
213913
+ const agent = await loadAgent(agentId, true);
213914
+ if (!agent) throw new Error("AGENT_NOT_FOUND");
213915
+ await removeOwnerAvatar(getAgentRootDir(agentId));
213916
+ await agentFileStore.writeMeta({ ...agent, avatar: void 0, updatedAt: Date.now() });
213917
+ }
213035
213918
  async function handleGetWorkspaceFile(agentId, filename) {
213036
213919
  return getWorkspaceFile(agentId, filename);
213037
213920
  }
@@ -213179,6 +214062,8 @@ function registerAgentHandlers() {
213179
214062
  registerRpc(IPC_LIST_AGENTS, handleListAgents);
213180
214063
  registerRpc(IPC_UPDATE_AGENT, handleUpdateAgent);
213181
214064
  registerRpc(IPC_DELETE_AGENT, handleDeleteAgent);
214065
+ registerRpc(IPC_SET_AGENT_AVATAR, handleSetAgentAvatar);
214066
+ registerRpc(IPC_REMOVE_AGENT_AVATAR, handleRemoveAgentAvatar);
213182
214067
  registerRpc(IPC_GET_WORKSPACE_FILE, handleGetWorkspaceFile);
213183
214068
  registerRpc(IPC_UPDATE_WORKSPACE_FILE, handleUpdateWorkspaceFile);
213184
214069
  registerRpc(IPC_LIST_WORKSPACE_FILES, handleListWorkspaceFiles);
@@ -213628,13 +214513,17 @@ function registerSearchHandlers() {
213628
214513
  }
213629
214514
 
213630
214515
  // src/electron/main/handlers/url-handlers.ts
213631
- var import_promises15 = __toESM(require("node:fs/promises"));
214516
+ var import_promises16 = __toESM(require("node:fs/promises"));
213632
214517
  var import_node_path4 = __toESM(require("node:path"));
213633
214518
  init_ipc_events();
213634
214519
  init_config();
213635
214520
  init_workspace();
214521
+ init_agent_file_store();
214522
+ init_group_file_store();
213636
214523
  var SAFE_PROTOCOLS = ["http:", "https:"];
213637
214524
  var INVALID_PATH_CHARS = /[\0\r\n`$<>|;&]/;
214525
+ var WINDOWS_DRIVE_ABSOLUTE_PATH = /^[A-Za-z]:[\\/]/;
214526
+ var WINDOWS_UNC_ABSOLUTE_PATH = /^\\\\[^\\/]+[\\/][^\\/]+/;
213638
214527
  function isUrlSafe(urlString) {
213639
214528
  try {
213640
214529
  const url2 = new URL(urlString);
@@ -213647,6 +214536,9 @@ function isSubPath(parentDir, targetPath) {
213647
214536
  const relative4 = import_node_path4.default.relative(parentDir, targetPath);
213648
214537
  return relative4 === "" || !relative4.startsWith("..") && !import_node_path4.default.isAbsolute(relative4);
213649
214538
  }
214539
+ function isAvatarPath(parts) {
214540
+ return parts.length === 2 && parts[0] === "avatars" && parts[1] === "avatar.png";
214541
+ }
213650
214542
  function resolveKaiLocalPath(urlString) {
213651
214543
  let pathname;
213652
214544
  try {
@@ -213683,6 +214575,18 @@ function resolveKaiLocalPath(urlString) {
213683
214575
  }
213684
214576
  return resolvedPath;
213685
214577
  }
214578
+ if (pathname.startsWith("/agent/") || pathname.startsWith("/group/")) {
214579
+ const [kind, ownerId, ...relativeParts] = pathname.slice(1).split("/");
214580
+ if (!ownerId || !isAvatarPath(relativeParts)) {
214581
+ throw new Error("ERROR_INVALID_LOCAL_PATH");
214582
+ }
214583
+ const ownerDir = kind === "agent" ? getAgentRootDir(ownerId) : getGroupDir(ownerId);
214584
+ const resolvedPath = import_node_path4.default.resolve(ownerDir, relativeParts.join("/"));
214585
+ if (!isSubPath(ownerDir, resolvedPath)) {
214586
+ throw new Error("ERROR_INVALID_LOCAL_PATH");
214587
+ }
214588
+ return resolvedPath;
214589
+ }
213686
214590
  throw new Error("ERROR_INVALID_LOCAL_PATH");
213687
214591
  }
213688
214592
  function resolveLocalPath(inputPath) {
@@ -213697,12 +214601,14 @@ function resolveLocalPath(inputPath) {
213697
214601
  resolvedPath = import_node_path4.default.join(homeDir, trimmedPath.slice(2));
213698
214602
  } else if (trimmedPath.startsWith("./")) {
213699
214603
  resolvedPath = import_node_path4.default.resolve(process.cwd(), trimmedPath);
214604
+ } else if (WINDOWS_DRIVE_ABSOLUTE_PATH.test(trimmedPath) || WINDOWS_UNC_ABSOLUTE_PATH.test(trimmedPath)) {
214605
+ resolvedPath = import_node_path4.default.win32.normalize(trimmedPath);
213700
214606
  } else if (trimmedPath.startsWith("/")) {
213701
214607
  resolvedPath = import_node_path4.default.normalize(trimmedPath);
213702
214608
  } else {
213703
214609
  throw new Error("ERROR_INVALID_LOCAL_PATH");
213704
214610
  }
213705
- if (!import_node_path4.default.isAbsolute(resolvedPath)) {
214611
+ if (!import_node_path4.default.isAbsolute(resolvedPath) && !import_node_path4.default.win32.isAbsolute(resolvedPath)) {
213706
214612
  throw new Error("ERROR_INVALID_LOCAL_PATH");
213707
214613
  }
213708
214614
  return resolvedPath;
@@ -213714,7 +214620,7 @@ async function handleOpenExternalUrl(url2) {
213714
214620
  }
213715
214621
  async function handleOpenLocalPath(inputPath) {
213716
214622
  const resolvedPath = resolveLocalPath(inputPath);
213717
- const stats = await import_promises15.default.stat(resolvedPath).catch(() => null);
214623
+ const stats = await import_promises16.default.stat(resolvedPath).catch(() => null);
213718
214624
  if (!stats?.isFile()) {
213719
214625
  throw new Error("ERROR_LOCAL_PATH_NOT_FILE");
213720
214626
  }
@@ -213724,7 +214630,7 @@ async function handleOpenLocalPath(inputPath) {
213724
214630
  }
213725
214631
  async function handleReadFileContent(inputPath) {
213726
214632
  const resolvedPath = resolveLocalPath(inputPath);
213727
- const stats = await import_promises15.default.stat(resolvedPath).catch(() => null);
214633
+ const stats = await import_promises16.default.stat(resolvedPath).catch(() => null);
213728
214634
  if (!stats?.isFile()) {
213729
214635
  throw new Error("ERROR_LOCAL_PATH_NOT_FILE");
213730
214636
  }
@@ -213738,7 +214644,7 @@ async function handleReadFileContent(inputPath) {
213738
214644
  mimeType
213739
214645
  };
213740
214646
  }
213741
- const buffer = await import_promises15.default.readFile(resolvedPath);
214647
+ const buffer = await import_promises16.default.readFile(resolvedPath);
213742
214648
  const isText2 = !isBufferBinary(buffer);
213743
214649
  const content = isText2 ? buffer.toString("utf-8") : "";
213744
214650
  return {
@@ -213753,11 +214659,29 @@ async function handleShowLocalItemInFolder(localUrlOrPath) {
213753
214659
  const { shell: shell2 } = require("electron");
213754
214660
  shell2.showItemInFolder(resolvedPath);
213755
214661
  }
214662
+ async function handleCopyImageToClipboard(localUrlOrPath) {
214663
+ const resolvedPath = localUrlOrPath.startsWith("kai://local/") ? resolveKaiLocalPath(localUrlOrPath) : resolveLocalPath(localUrlOrPath);
214664
+ const stats = await import_promises16.default.stat(resolvedPath).catch(() => null);
214665
+ if (!stats?.isFile()) {
214666
+ throw new Error("ERROR_LOCAL_PATH_NOT_FILE");
214667
+ }
214668
+ const mimeType = getMimeType2(import_node_path4.default.extname(resolvedPath).toLowerCase());
214669
+ if (!mimeType.startsWith("image/")) {
214670
+ throw new Error("ERROR_LOCAL_PATH_NOT_IMAGE");
214671
+ }
214672
+ const { clipboard, nativeImage } = require("electron");
214673
+ const image = nativeImage.createFromPath(resolvedPath);
214674
+ if (image.isEmpty()) {
214675
+ throw new Error("ERROR_IMAGE_CLIPBOARD_UNSUPPORTED");
214676
+ }
214677
+ clipboard.writeImage(image);
214678
+ }
213756
214679
  function registerUrlHandlers() {
213757
214680
  registerRpc(IPC_OPEN_EXTERNAL_URL, handleOpenExternalUrl);
213758
214681
  registerRpc(IPC_OPEN_LOCAL_PATH, handleOpenLocalPath);
213759
214682
  registerRpc(IPC_READ_FILE_CONTENT, handleReadFileContent);
213760
214683
  registerRpc(IPC_SHOW_LOCAL_ITEM_IN_FOLDER, handleShowLocalItemInFolder);
214684
+ registerRpc(IPC_COPY_IMAGE_TO_CLIPBOARD, handleCopyImageToClipboard);
213761
214685
  }
213762
214686
  function getMimeType2(ext) {
213763
214687
  const map3 = {
@@ -214248,7 +215172,7 @@ function setupHealthIPC() {
214248
215172
 
214249
215173
  // src/electron/main/handlers/app-info-handlers.ts
214250
215174
  init_ipc_events();
214251
- var import_promises16 = __toESM(require("node:fs/promises"));
215175
+ var import_promises17 = __toESM(require("node:fs/promises"));
214252
215176
  var import_node_path5 = __toESM(require("node:path"));
214253
215177
  function getReleaseNotesPath() {
214254
215178
  const { app } = require("electron");
@@ -214267,7 +215191,7 @@ async function handleGetAppInfo() {
214267
215191
  }
214268
215192
  async function handleGetReleaseNotes() {
214269
215193
  try {
214270
- return await import_promises16.default.readFile(getReleaseNotesPath(), "utf-8");
215194
+ return await import_promises17.default.readFile(getReleaseNotesPath(), "utf-8");
214271
215195
  } catch {
214272
215196
  return "";
214273
215197
  }
@@ -214285,12 +215209,12 @@ init_config();
214285
215209
 
214286
215210
  // src/adapters/transport/http-server.ts
214287
215211
  var import_node_http = __toESM(require("node:http"));
214288
- var import_promises17 = __toESM(require("node:fs/promises"));
215212
+ var import_promises18 = __toESM(require("node:fs/promises"));
214289
215213
  var import_node_path6 = __toESM(require("node:path"));
214290
215214
  var import_node_os2 = __toESM(require("node:os"));
214291
215215
  var import_node_zlib = __toESM(require("node:zlib"));
214292
215216
  var import_node_fs = require("node:fs");
214293
- var import_promises18 = require("node:stream/promises");
215217
+ var import_promises19 = require("node:stream/promises");
214294
215218
 
214295
215219
  // node_modules/ws/wrapper.mjs
214296
215220
  var import_stream5 = __toESM(require_stream(), 1);
@@ -214444,8 +215368,17 @@ var WsConnectionManager = class {
214444
215368
  // src/adapters/transport/http-server.ts
214445
215369
  init_workspace();
214446
215370
  init_config();
215371
+ init_agent_file_store();
215372
+ init_group_file_store();
214447
215373
  var DEFAULT_PORT = 9527;
214448
215374
  var MAX_JSON_BODY_SIZE = 5 * 1024 * 1024;
215375
+ function isSubPath2(parent2, child) {
215376
+ const rel = import_node_path6.default.relative(parent2, child);
215377
+ return rel === "" || !!rel && !rel.startsWith("..") && !import_node_path6.default.isAbsolute(rel);
215378
+ }
215379
+ function isAvatarPath2(parts) {
215380
+ return parts.length === 2 && parts[0] === "avatars" && parts[1] === "avatar.png";
215381
+ }
214449
215382
  var MAX_UPLOAD_SIZE = ATTACHMENT.MAX_SIZE;
214450
215383
  var RENDERER_DIST_DIR = (() => {
214451
215384
  const electronPath = import_node_path6.default.resolve(__dirname, "../renderer");
@@ -214490,8 +215423,8 @@ function getCacheControl2(filePath) {
214490
215423
  }
214491
215424
  async function fileExists(filePath) {
214492
215425
  try {
214493
- const stat4 = await import_promises17.default.stat(filePath);
214494
- return stat4.isFile();
215426
+ const stat5 = await import_promises18.default.stat(filePath);
215427
+ return stat5.isFile();
214495
215428
  } catch {
214496
215429
  return false;
214497
215430
  }
@@ -214520,7 +215453,7 @@ async function sendStaticAsset(filePath, response, requestHeaders) {
214520
215453
  }
214521
215454
  response.writeHead(200, headers);
214522
215455
  try {
214523
- await (0, import_promises18.pipeline)((0, import_node_fs.createReadStream)(actualPath), response);
215456
+ await (0, import_promises19.pipeline)((0, import_node_fs.createReadStream)(actualPath), response);
214524
215457
  } catch (error48) {
214525
215458
  if (!response.headersSent) {
214526
215459
  response.writeHead(500);
@@ -214541,8 +215474,8 @@ async function serveStaticFile(urlPath, response, requestHeaders) {
214541
215474
  return;
214542
215475
  }
214543
215476
  try {
214544
- const stat4 = await import_promises17.default.stat(resolvedPath);
214545
- const filePath = stat4.isDirectory() ? import_node_path6.default.join(resolvedPath, "index.html") : resolvedPath;
215477
+ const stat5 = await import_promises18.default.stat(resolvedPath);
215478
+ const filePath = stat5.isDirectory() ? import_node_path6.default.join(resolvedPath, "index.html") : resolvedPath;
214546
215479
  if (!await fileExists(filePath)) throw new Error("Not a file");
214547
215480
  await sendStaticAsset(filePath, response, requestHeaders);
214548
215481
  } catch {
@@ -214661,7 +215594,7 @@ var HttpTransportServer = class {
214661
215594
  }
214662
215595
  /**
214663
215596
  * 提供本地文件访问(与 kai:// protocol handler 同构)
214664
- * 路径格式:/full/absolute/path、/workspace/{agentId}/relative/path、/attachment/{relativePath}
215597
+ * 路径格式:/full/absolute/path、/workspace/{agentId}/relative/path、/attachment/{relativePath}、/agent/{agentId}/avatars/avatar.png、/group/{groupId}/avatars/avatar.png
214665
215598
  */
214666
215599
  async serveLocalFile(pathname, response, isDownload = false) {
214667
215600
  let decodedPathname;
@@ -214685,7 +215618,21 @@ var HttpTransportServer = class {
214685
215618
  const relativePath = decodedPathname.slice(12);
214686
215619
  const attachmentsDir = config.getAttachmentsPath();
214687
215620
  resolvedPath = import_node_path6.default.resolve(attachmentsDir, relativePath);
214688
- if (!resolvedPath.startsWith(attachmentsDir)) {
215621
+ if (!isSubPath2(attachmentsDir, resolvedPath)) {
215622
+ response.writeHead(403);
215623
+ response.end("Forbidden");
215624
+ return;
215625
+ }
215626
+ } else if (decodedPathname.startsWith("/agent/") || decodedPathname.startsWith("/group/")) {
215627
+ const [kind, ownerId, ...relativeParts] = decodedPathname.slice(1).split("/");
215628
+ if (!ownerId || !isAvatarPath2(relativeParts)) {
215629
+ response.writeHead(400);
215630
+ response.end("Bad Request");
215631
+ return;
215632
+ }
215633
+ const ownerDir = kind === "agent" ? getAgentRootDir(ownerId) : getGroupDir(ownerId);
215634
+ resolvedPath = import_node_path6.default.resolve(ownerDir, relativeParts.join("/"));
215635
+ if (!isSubPath2(ownerDir, resolvedPath)) {
214689
215636
  response.writeHead(403);
214690
215637
  response.end("Forbidden");
214691
215638
  return;
@@ -214702,7 +215649,7 @@ var HttpTransportServer = class {
214702
215649
  const imagePath = parts.slice(1).join("/");
214703
215650
  const workspaceDir = getAgentWorkspaceDir(agentId);
214704
215651
  resolvedPath = import_node_path6.default.resolve(workspaceDir, imagePath);
214705
- if (!resolvedPath.startsWith(workspaceDir)) {
215652
+ if (!isSubPath2(workspaceDir, resolvedPath)) {
214706
215653
  response.writeHead(403);
214707
215654
  response.end("Forbidden");
214708
215655
  return;
@@ -214713,10 +215660,13 @@ var HttpTransportServer = class {
214713
215660
  return;
214714
215661
  }
214715
215662
  try {
214716
- await import_promises17.default.stat(resolvedPath);
215663
+ await import_promises18.default.stat(resolvedPath);
214717
215664
  const ext = import_node_path6.default.extname(resolvedPath).toLowerCase();
214718
215665
  const mimeType = getMimeType(ext);
214719
215666
  const headers = { "Content-Type": mimeType };
215667
+ if (decodedPathname.startsWith("/agent/") || decodedPathname.startsWith("/group/")) {
215668
+ headers["Cache-Control"] = "no-store";
215669
+ }
214720
215670
  if (isDownload) {
214721
215671
  const filename = import_node_path6.default.basename(resolvedPath);
214722
215672
  const asciiSafe = filename.replace(/[^\x20-\x7E]/g, "");
@@ -215150,14 +216100,14 @@ function registerUpdateHandlers() {
215150
216100
  }
215151
216101
 
215152
216102
  // src/adapters/storage/PinnedStore.ts
215153
- var import_path18 = require("path");
215154
- var import_promises19 = require("fs/promises");
216103
+ var import_path19 = require("path");
216104
+ var import_promises20 = require("fs/promises");
215155
216105
  var import_fs12 = require("fs");
215156
216106
  init_config();
215157
216107
  var FILE_NAME = "pinned.json";
215158
216108
  var SCHEMA_VERSION4 = 1;
215159
216109
  function getPinnedPath() {
215160
- return (0, import_path18.join)(config.getAppDataPath(), "config", FILE_NAME);
216110
+ return (0, import_path19.join)(config.getAppDataPath(), "config", FILE_NAME);
215161
216111
  }
215162
216112
  function createDefaultPinnedFile() {
215163
216113
  return { version: SCHEMA_VERSION4, agents: {}, groups: {} };
@@ -215192,17 +216142,17 @@ function clonePinnedFile(data2) {
215192
216142
  };
215193
216143
  }
215194
216144
  async function atomicWriteJson4(path10, data2) {
215195
- const dir = (0, import_path18.dirname)(path10);
215196
- await (0, import_promises19.mkdir)(dir, { recursive: true });
216145
+ const dir = (0, import_path19.dirname)(path10);
216146
+ await (0, import_promises20.mkdir)(dir, { recursive: true });
215197
216147
  const tmp = `${path10}.tmp`;
215198
- await (0, import_promises19.writeFile)(tmp, `${JSON.stringify(data2, null, 2)}
216148
+ await (0, import_promises20.writeFile)(tmp, `${JSON.stringify(data2, null, 2)}
215199
216149
  `, "utf-8");
215200
- await (0, import_promises19.rename)(tmp, path10);
216150
+ await (0, import_promises20.rename)(tmp, path10);
215201
216151
  }
215202
216152
  async function readJsonFile5(path10) {
215203
216153
  if (!(0, import_fs12.existsSync)(path10)) return null;
215204
216154
  try {
215205
- const raw = await (0, import_promises19.readFile)(path10, "utf-8");
216155
+ const raw = await (0, import_promises20.readFile)(path10, "utf-8");
215206
216156
  return JSON.parse(raw);
215207
216157
  } catch {
215208
216158
  return null;
@@ -215226,14 +216176,14 @@ var PinnedStore = class {
215226
216176
  }
215227
216177
  async pin(type, id) {
215228
216178
  const data2 = await this.load();
215229
- const bucket = type === "agent" ? data2.agents : data2.groups;
215230
- bucket[id] = Date.now();
216179
+ const bucket2 = type === "agent" ? data2.agents : data2.groups;
216180
+ bucket2[id] = Date.now();
215231
216181
  await this.flush();
215232
216182
  }
215233
216183
  async unpin(type, id) {
215234
216184
  const data2 = await this.load();
215235
- const bucket = type === "agent" ? data2.agents : data2.groups;
215236
- delete bucket[id];
216185
+ const bucket2 = type === "agent" ? data2.agents : data2.groups;
216186
+ delete bucket2[id];
215237
216187
  await this.flush();
215238
216188
  }
215239
216189
  };
@@ -215261,7 +216211,7 @@ var import_node_https = __toESM(require("node:https"));
215261
216211
  init_id2();
215262
216212
  init_ipc_events();
215263
216213
  init_logger();
215264
- var log19 = createLogger("ChannelHandlers");
216214
+ var log21 = createLogger("ChannelHandlers");
215265
216215
  var VALID_CHANNEL_TYPES = ["wecom-bot", "wechat-personal"];
215266
216216
  function httpsGetJson(url2, timeoutMs = 1e4) {
215267
216217
  return new Promise((resolve9, reject) => {
@@ -215464,9 +216414,9 @@ function registerHandlers() {
215464
216414
  }
215465
216415
 
215466
216416
  // src/adapters/db/migration/kai-migration.ts
215467
- var import_path21 = require("path");
216417
+ var import_path22 = require("path");
215468
216418
  var import_fs15 = require("fs");
215469
- var import_promises22 = require("fs/promises");
216419
+ var import_promises23 = require("fs/promises");
215470
216420
  var import_lt2 = __toESM(require_lt());
215471
216421
  init_lib();
215472
216422
  init_agent_file_store();
@@ -215493,7 +216443,7 @@ function getKaiRootDir() {
215493
216443
  return config.getAppDataPath();
215494
216444
  }
215495
216445
  function getKaiConfigPath() {
215496
- return (0, import_path21.join)(getKaiRootDir(), KAI_CONFIG_FILE);
216446
+ return (0, import_path22.join)(getKaiRootDir(), KAI_CONFIG_FILE);
215497
216447
  }
215498
216448
  function ensureKaiRootDir() {
215499
216449
  const root2 = getKaiRootDir();
@@ -215518,9 +216468,9 @@ async function writeKaiDataFile(version3) {
215518
216468
  ensureKaiRootDir();
215519
216469
  const path10 = getKaiConfigPath();
215520
216470
  const tmp = `${path10}.tmp`;
215521
- await (0, import_promises22.writeFile)(tmp, `${JSON.stringify({ version: version3 }, null, 2)}
216471
+ await (0, import_promises23.writeFile)(tmp, `${JSON.stringify({ version: version3 }, null, 2)}
215522
216472
  `, "utf-8");
215523
- await (0, import_promises22.rename)(tmp, path10);
216473
+ await (0, import_promises23.rename)(tmp, path10);
215524
216474
  }
215525
216475
  function currentVersion() {
215526
216476
  return readKaiDataFile()?.version ?? "0.0.0";
@@ -215697,16 +216647,16 @@ async function migrateConversationIds() {
215697
216647
  });
215698
216648
  await sequelize.query("PRAGMA foreign_keys = ON");
215699
216649
  }
215700
- const conversationsDir = (0, import_path21.resolve)(config.getAppDataPath(), "conversations");
216650
+ const conversationsDir = (0, import_path22.resolve)(config.getAppDataPath(), "conversations");
215701
216651
  if ((0, import_fs15.existsSync)(conversationsDir)) {
215702
216652
  try {
215703
216653
  const entries = (0, import_fs15.readdirSync)(conversationsDir, { withFileTypes: true });
215704
216654
  for (const entry of entries) {
215705
216655
  if (!entry.isDirectory()) continue;
215706
- const oldDir = (0, import_path21.join)(conversationsDir, entry.name);
216656
+ const oldDir = (0, import_path22.join)(conversationsDir, entry.name);
215707
216657
  const targetId = idMap.get(entry.name);
215708
216658
  if (targetId) {
215709
- const newDir = (0, import_path21.join)(conversationsDir, targetId);
216659
+ const newDir = (0, import_path22.join)(conversationsDir, targetId);
215710
216660
  if (!(0, import_fs15.existsSync)(newDir)) {
215711
216661
  (0, import_fs15.renameSync)(oldDir, newDir);
215712
216662
  logger35.debug(`Renamed dir ${entry.name} \u2192 ${targetId}`);
@@ -215956,7 +216906,7 @@ init_normalize_tool_schema();
215956
216906
  var import_child_process3 = require("child_process");
215957
216907
  init_dist6();
215958
216908
  init_zod();
215959
- var import_path22 = require("path");
216909
+ var import_path23 = require("path");
215960
216910
  init_workspace();
215961
216911
 
215962
216912
  // node_modules/shell-env/index.js
@@ -216073,10 +217023,10 @@ function getUserShellEnv() {
216073
217023
  // src/adapters/tools/tool-names.ts
216074
217024
  var BUILT_IN_TOOL_NAMES = Object.freeze({
216075
217025
  SHELL: "shell",
216076
- READ: "read",
216077
- WRITE: "write",
216078
- EDIT: "edit",
216079
- PATCH: "patch",
217026
+ READ: "readFile",
217027
+ WRITE: "writeFile",
217028
+ EDIT: "editFile",
217029
+ PATCH: "applyPatch",
216080
217030
  WEB_FETCH: "webFetch",
216081
217031
  WEB_SEARCH: "webSearch"
216082
217032
  });
@@ -216111,7 +217061,7 @@ function createShellTool(agentId) {
216111
217061
  };
216112
217062
  if (workdir) {
216113
217063
  const baseDir = agentId ? getAgentWorkspaceDir(agentId) : process.cwd();
216114
- options2.cwd = (0, import_path22.isAbsolute)(workdir) ? workdir : (0, import_path22.resolve)(baseDir, workdir);
217064
+ options2.cwd = (0, import_path23.isAbsolute)(workdir) ? workdir : (0, import_path23.resolve)(baseDir, workdir);
216115
217065
  } else if (agentId) {
216116
217066
  options2.cwd = getAgentWorkspaceDir(agentId);
216117
217067
  }
@@ -216236,11 +217186,11 @@ function spawnWithAbort(shell2, command, options2, timeoutMs, abortSignal) {
216236
217186
  // src/adapters/tools/filesystem/read-file.ts
216237
217187
  init_dist6();
216238
217188
  init_zod();
216239
- var import_promises23 = require("fs/promises");
217189
+ var import_promises24 = require("fs/promises");
216240
217190
  var import_fs16 = require("fs");
216241
217191
 
216242
217192
  // src/adapters/tools/filesystem/resolve-file-path.ts
216243
- var import_path23 = require("path");
217193
+ var import_path24 = require("path");
216244
217194
  init_workspace();
216245
217195
  init_group_file_store();
216246
217196
  var GROUP_PROTECTED_JSON_FILES = /* @__PURE__ */ new Set([
@@ -216249,38 +217199,38 @@ var GROUP_PROTECTED_JSON_FILES = /* @__PURE__ */ new Set([
216249
217199
  "members.json"
216250
217200
  ]);
216251
217201
  function containsPathTraversal(p) {
216252
- return (0, import_path23.normalize)(p).split(/[/\\]+/).some((part) => part === "..");
217202
+ return (0, import_path24.normalize)(p).split(/[/\\]+/).some((part) => part === "..");
216253
217203
  }
216254
217204
  function isWithinBase(resolvedPath, baseDir) {
216255
- const rel = (0, import_path23.relative)(baseDir, resolvedPath);
216256
- return !rel.startsWith("..") && !(0, import_path23.isAbsolute)(rel);
217205
+ const rel = (0, import_path24.relative)(baseDir, resolvedPath);
217206
+ return !rel.startsWith("..") && !(0, import_path24.isAbsolute)(rel);
216257
217207
  }
216258
217208
  function isGroupProtectedFile(resolvedPath, groupDir) {
216259
- const name21 = (0, import_path23.basename)(resolvedPath);
217209
+ if (!isWithinBase((0, import_path24.normalize)(resolvedPath), (0, import_path24.normalize)(groupDir))) {
217210
+ return false;
217211
+ }
217212
+ const name21 = (0, import_path24.basename)(resolvedPath);
216260
217213
  return name21.endsWith(".json") && GROUP_PROTECTED_JSON_FILES.has(name21);
216261
217214
  }
216262
- function resolveFilePath2(filePath, scope) {
216263
- const agentId = scope?.agentId;
216264
- const groupId = scope?.groupId;
216265
- if (!agentId && !groupId) {
216266
- if ((0, import_path23.isAbsolute)(filePath)) return filePath;
216267
- return (0, import_path23.resolve)(process.cwd(), filePath);
217215
+ function resolveToolFilePath(workdir, filePath) {
217216
+ if (!workdir || !(0, import_path24.isAbsolute)(workdir)) {
217217
+ throw new Error("workdir is required and must be an absolute path");
217218
+ }
217219
+ if (!filePath) {
217220
+ throw new Error("path is required and must be relative to workdir");
217221
+ }
217222
+ if ((0, import_path24.isAbsolute)(filePath)) {
217223
+ throw new Error("path must be relative to workdir, not absolute");
216268
217224
  }
216269
217225
  if (containsPathTraversal(filePath)) {
216270
217226
  throw new Error(`Path traversal not allowed: ${filePath}`);
216271
217227
  }
216272
- if (groupId) {
216273
- const groupDir = getGroupDir(groupId);
216274
- if ((0, import_path23.isAbsolute)(filePath)) return (0, import_path23.normalize)(filePath);
216275
- const resolved = (0, import_path23.resolve)(groupDir, filePath);
216276
- if (!isWithinBase(resolved, groupDir)) {
216277
- throw new Error(`Path escapes group directory: ${filePath}`);
216278
- }
216279
- return resolved;
217228
+ const normalizedWorkdir = (0, import_path24.normalize)(workdir);
217229
+ const resolvedPath = (0, import_path24.resolve)(normalizedWorkdir, filePath);
217230
+ if (!isWithinBase(resolvedPath, normalizedWorkdir)) {
217231
+ throw new Error(`Path escapes workdir: ${filePath}`);
216280
217232
  }
216281
- if ((0, import_path23.isAbsolute)(filePath)) return filePath;
216282
- const baseDir = agentId ? getAgentWorkspaceDir(agentId) : process.cwd();
216283
- return (0, import_path23.resolve)(baseDir, filePath);
217233
+ return resolvedPath;
216284
217234
  }
216285
217235
 
216286
217236
  // src/adapters/tools/filesystem/read-file.ts
@@ -216337,28 +217287,29 @@ var BINARY_EXTENSIONS2 = /* @__PURE__ */ new Set([
216337
217287
  ".pyc",
216338
217288
  ".wasm"
216339
217289
  ]);
216340
- function createReadFileTool(scope = {}, tracker) {
217290
+ function createReadFileTool(_scope = {}, tracker) {
216341
217291
  return {
216342
- read: tool({
216343
- description: "Read file content from the local filesystem.\n- Returns content with line numbers.\n- If the path is a directory, lists its contents.\n- For large files, use `offset` and `limit` to read in chunks.",
217292
+ readFile: tool({
217293
+ description: "Read file content from the local filesystem.\n- `workdir` is REQUIRED and must be an absolute path.\n- `path` must be relative to `workdir`; absolute paths and `..` are rejected.\n- Returns content with line numbers.\n- If the path is a directory, lists its contents.\n- For large files, use `offset` and `limit` to read in chunks.",
216344
217294
  inputSchema: zodSchema(
216345
217295
  external_exports3.object({
216346
- path: external_exports3.string().describe("The file or directory path (absolute or relative to workspace)"),
217296
+ workdir: external_exports3.string().describe("REQUIRED FIRST FIELD. Absolute root directory for this file operation."),
217297
+ path: external_exports3.string().describe("The file or directory path, relative to workdir. Do not use absolute paths or .."),
216347
217298
  offset: external_exports3.number().optional().describe("Line number to start reading from (1-indexed, default: 1)"),
216348
217299
  limit: external_exports3.number().optional().describe("Maximum number of lines to read (default: 2000)")
216349
217300
  })
216350
217301
  ),
216351
- execute: async ({ path: filePath, offset = 1, limit = DEFAULT_LIMIT }, { abortSignal }) => {
217302
+ execute: async ({ workdir, path: filePath, offset = 1, limit = DEFAULT_LIMIT }, { abortSignal }) => {
216352
217303
  if (abortSignal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
216353
217304
  try {
216354
- const resolvedPath = resolveFilePath2(filePath, scope);
217305
+ const resolvedPath = resolveToolFilePath(workdir, filePath);
216355
217306
  if (!(0, import_fs16.existsSync)(resolvedPath)) {
216356
217307
  return { error: `Path does not exist: ${filePath}`, content: "" };
216357
217308
  }
216358
- const fileStat = await (0, import_promises23.stat)(resolvedPath);
217309
+ const fileStat = await (0, import_promises24.stat)(resolvedPath);
216359
217310
  if (fileStat.isDirectory()) {
216360
217311
  tracker?.markKnown(resolvedPath);
216361
- const entries = await (0, import_promises23.readdir)(resolvedPath, { withFileTypes: true });
217312
+ const entries = await (0, import_promises24.readdir)(resolvedPath, { withFileTypes: true });
216362
217313
  const lines = entries.sort((a, b) => {
216363
217314
  if (a.isDirectory() && !b.isDirectory()) return -1;
216364
217315
  if (!a.isDirectory() && b.isDirectory()) return 1;
@@ -216381,7 +217332,7 @@ function createReadFileTool(scope = {}, tracker) {
216381
217332
  content: ""
216382
217333
  };
216383
217334
  }
216384
- const content = await (0, import_promises23.readFile)(resolvedPath, "utf-8");
217335
+ const content = await (0, import_promises24.readFile)(resolvedPath, "utf-8");
216385
217336
  const allLines = content.split("\n");
216386
217337
  if (allLines.length > 0 && allLines[allLines.length - 1] === "") {
216387
217338
  allLines.pop();
@@ -216415,8 +217366,8 @@ function createReadFileTool(scope = {}, tracker) {
216415
217366
  // src/adapters/tools/filesystem/write-file.ts
216416
217367
  init_dist6();
216417
217368
  init_zod();
216418
- var import_promises24 = require("fs/promises");
216419
- var import_path24 = require("path");
217369
+ var import_promises25 = require("fs/promises");
217370
+ var import_path25 = require("path");
216420
217371
  var import_fs17 = require("fs");
216421
217372
  init_group_file_store();
216422
217373
 
@@ -217189,18 +218140,20 @@ function createUnifiedFileDiff(displayPath, previousContent, nextContent) {
217189
218140
  // src/adapters/tools/filesystem/write-file.ts
217190
218141
  function createWriteFileTool(scope = {}, tracker) {
217191
218142
  return {
217192
- write: tool({
217193
- description: "Write content to a file on the local filesystem. Creates parent directories if they do not exist. Overwrites existing files. IMPORTANT: For editing specific parts of an existing file, prefer `edit` \u2014 it is more token-efficient and less error-prone than rewriting the entire file.",
218143
+ writeFile: tool({
218144
+ description: "Write content to a file on the local filesystem. Creates parent directories if they do not exist. `workdir` is REQUIRED and must be an absolute path. `path` must be relative to `workdir`; absolute paths and `..` are rejected. mode defaults to `overwrite`; use `append` only for append-only files such as logs or memory notes. IMPORTANT: For editing specific parts of an existing file, prefer `editFile` \u2014 it is more token-efficient and less error-prone than rewriting the entire file.",
217194
218145
  inputSchema: zodSchema(
217195
218146
  external_exports3.object({
217196
- path: external_exports3.string().describe("REQUIRED FIRST FIELD. The file path (absolute or relative to workspace)"),
217197
- content: external_exports3.string().describe("The content to write to the file")
218147
+ workdir: external_exports3.string().describe("REQUIRED FIRST FIELD. Absolute root directory for this file operation."),
218148
+ path: external_exports3.string().describe("The file path, relative to workdir. Do not use absolute paths or .."),
218149
+ content: external_exports3.string().describe("The content to write to the file"),
218150
+ mode: external_exports3.enum(["overwrite", "append"]).optional().describe("Write mode. Defaults to overwrite. Use append only for append-only text files.")
217198
218151
  })
217199
218152
  ),
217200
- execute: async ({ path: filePath, content }, { abortSignal }) => {
218153
+ execute: async ({ workdir, path: filePath, content, mode = "overwrite" }, { abortSignal }) => {
217201
218154
  if (abortSignal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
217202
218155
  try {
217203
- const resolvedPath = resolveFilePath2(filePath, scope);
218156
+ const resolvedPath = resolveToolFilePath(workdir, filePath);
217204
218157
  if (scope.groupId) {
217205
218158
  const groupDir = getGroupDir(scope.groupId);
217206
218159
  if (isGroupProtectedFile(resolvedPath, groupDir)) {
@@ -217211,27 +218164,35 @@ function createWriteFileTool(scope = {}, tracker) {
217211
218164
  }
217212
218165
  }
217213
218166
  const isNewFile = !(0, import_fs17.existsSync)(resolvedPath);
217214
- if (!isNewFile && tracker && !tracker.isKnown(resolvedPath)) {
218167
+ if (mode === "overwrite" && !isNewFile && tracker && !tracker.isKnown(resolvedPath)) {
217215
218168
  return {
217216
218169
  success: false,
217217
- error: `Cannot overwrite existing file without reading it first. Call read on "${filePath}" before using write to overwrite it.`
218170
+ error: `Cannot modify existing file without reading it first. Call readFile on "${filePath}" before using writeFile to overwrite it.`
217218
218171
  };
217219
218172
  }
217220
218173
  let oldContent = "";
217221
218174
  if (!isNewFile) {
217222
218175
  try {
217223
- oldContent = await (0, import_promises24.readFile)(resolvedPath, "utf-8");
218176
+ oldContent = await (0, import_promises25.readFile)(resolvedPath, "utf-8");
217224
218177
  } catch {
217225
218178
  }
217226
218179
  }
217227
- const diff = createUnifiedFileDiff(filePath, oldContent, content);
217228
- await (0, import_promises24.mkdir)((0, import_path24.dirname)(resolvedPath), { recursive: true });
217229
- await (0, import_promises24.writeFile)(resolvedPath, content, "utf-8");
217230
- tracker?.markKnown(resolvedPath);
218180
+ const nextContent = mode === "append" ? oldContent + content : content;
218181
+ const diff = createUnifiedFileDiff(filePath, oldContent, nextContent);
218182
+ await (0, import_promises25.mkdir)((0, import_path25.dirname)(resolvedPath), { recursive: true });
218183
+ if (mode === "append") {
218184
+ await (0, import_promises25.appendFile)(resolvedPath, content, "utf-8");
218185
+ } else {
218186
+ await (0, import_promises25.writeFile)(resolvedPath, content, "utf-8");
218187
+ }
218188
+ if (isNewFile) {
218189
+ tracker?.markKnown(resolvedPath);
218190
+ }
217231
218191
  return {
217232
218192
  success: true,
217233
218193
  path: resolvedPath,
217234
218194
  bytesWritten: Buffer.byteLength(content, "utf-8"),
218195
+ mode,
217235
218196
  isNewFile,
217236
218197
  diff
217237
218198
  };
@@ -217249,25 +218210,26 @@ function createWriteFileTool(scope = {}, tracker) {
217249
218210
  // src/adapters/tools/filesystem/edit-file.ts
217250
218211
  init_dist6();
217251
218212
  init_zod();
217252
- var import_promises25 = require("fs/promises");
218213
+ var import_promises26 = require("fs/promises");
217253
218214
  var import_fs18 = require("fs");
217254
218215
  init_group_file_store();
217255
- function createEditFileTool(scope = {}) {
218216
+ function createEditFileTool(scope = {}, tracker) {
217256
218217
  return {
217257
- edit: tool({
217258
- description: "Perform exact string replacement in a file. The oldString must match exactly (including whitespace and indentation). Fails if oldString is not found or found multiple times (unless replaceAll is true).",
218218
+ editFile: tool({
218219
+ description: "Perform exact string replacement in a file. The oldString must match exactly (including whitespace and indentation). Fails if oldString is not found or found multiple times (unless replaceAll is true). `workdir` is REQUIRED and must be an absolute path. `path` must be relative to `workdir`; absolute paths and `..` are rejected.",
217259
218220
  inputSchema: zodSchema(
217260
218221
  external_exports3.object({
217261
- path: external_exports3.string().describe("REQUIRED FIRST FIELD. The file path (absolute or relative to workspace)"),
218222
+ workdir: external_exports3.string().describe("REQUIRED FIRST FIELD. Absolute root directory for this file operation."),
218223
+ path: external_exports3.string().describe("The file path, relative to workdir. Do not use absolute paths or .."),
217262
218224
  oldString: external_exports3.string().describe("The exact text to be replaced"),
217263
218225
  newString: external_exports3.string().describe("The text to replace it with"),
217264
218226
  replaceAll: external_exports3.boolean().optional().describe("Replace all occurrences of oldString (default: false)")
217265
218227
  })
217266
218228
  ),
217267
- execute: async ({ path: filePath, oldString, newString, replaceAll = false }, { abortSignal }) => {
218229
+ execute: async ({ workdir, path: filePath, oldString, newString, replaceAll = false }, { abortSignal }) => {
217268
218230
  if (abortSignal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
217269
218231
  try {
217270
- const resolvedPath = resolveFilePath2(filePath, scope);
218232
+ const resolvedPath = resolveToolFilePath(workdir, filePath);
217271
218233
  if (scope.groupId) {
217272
218234
  const groupDir = getGroupDir(scope.groupId);
217273
218235
  if (isGroupProtectedFile(resolvedPath, groupDir)) {
@@ -217280,7 +218242,11 @@ function createEditFileTool(scope = {}) {
217280
218242
  if (!(0, import_fs18.existsSync)(resolvedPath)) {
217281
218243
  return { success: false, error: `File does not exist: ${filePath}` };
217282
218244
  }
217283
- const content = await (0, import_promises25.readFile)(resolvedPath, "utf-8");
218245
+ const accessError = tracker?.assertKnownForMutation(resolvedPath, "edit", "editFile", filePath);
218246
+ if (accessError) {
218247
+ return { success: false, error: accessError };
218248
+ }
218249
+ const content = await (0, import_promises26.readFile)(resolvedPath, "utf-8");
217284
218250
  const matchCount = content.split(oldString).length - 1;
217285
218251
  if (matchCount === 0) {
217286
218252
  return { success: false, error: "oldString not found in file" };
@@ -217293,7 +218259,8 @@ function createEditFileTool(scope = {}) {
217293
218259
  }
217294
218260
  const updatedContent = replaceAll ? content.split(oldString).join(newString) : content.replace(oldString, newString);
217295
218261
  const diff = createUnifiedFileDiff(filePath, content, updatedContent);
217296
- await (0, import_promises25.writeFile)(resolvedPath, updatedContent, "utf-8");
218262
+ await (0, import_promises26.writeFile)(resolvedPath, updatedContent, "utf-8");
218263
+ tracker?.markKnown(resolvedPath);
217297
218264
  return {
217298
218265
  success: true,
217299
218266
  path: resolvedPath,
@@ -217314,39 +218281,47 @@ function createEditFileTool(scope = {}) {
217314
218281
  // src/adapters/tools/filesystem/apply-patch.ts
217315
218282
  init_dist6();
217316
218283
  init_zod();
217317
- var import_promises26 = require("fs/promises");
218284
+ var import_promises27 = require("fs/promises");
217318
218285
  var import_fs19 = require("fs");
217319
- var import_path25 = require("path");
218286
+ var import_path26 = require("path");
217320
218287
  init_group_file_store();
217321
218288
  function containsPathTraversal2(p) {
217322
- return (0, import_path25.normalize)(p).split(/[/\\]+/).some((part) => part === "..");
218289
+ return (0, import_path26.normalize)(p).split(/[/\\]+/).some((part) => part === "..");
217323
218290
  }
217324
218291
  function isWithinBase2(resolvedPath, baseDir) {
217325
- const rel = (0, import_path25.relative)(baseDir, resolvedPath);
217326
- return !rel.startsWith("..") && !(0, import_path25.isAbsolute)(rel);
218292
+ const rel = (0, import_path26.relative)(baseDir, resolvedPath);
218293
+ return !rel.startsWith("..") && !(0, import_path26.isAbsolute)(rel);
217327
218294
  }
217328
218295
  function resolvePatchWorkdir(workdir) {
217329
218296
  if (typeof workdir !== "string" || workdir.length === 0) {
217330
218297
  throw new Error("patch workdir is required and must be an absolute path");
217331
218298
  }
217332
- if (!(0, import_path25.isAbsolute)(workdir)) {
218299
+ if (!(0, import_path26.isAbsolute)(workdir)) {
217333
218300
  throw new Error("patch workdir must be an absolute path");
217334
218301
  }
217335
- return (0, import_path25.normalize)(workdir);
218302
+ return (0, import_path26.normalize)(workdir);
217336
218303
  }
217337
218304
  function resolvePatchPath(filePath, workdir) {
217338
- if ((0, import_path25.isAbsolute)(filePath)) {
218305
+ if ((0, import_path26.isAbsolute)(filePath)) {
217339
218306
  throw new Error(`Patch file paths must be relative to workdir; pass workdir instead of absolute paths: ${filePath}`);
217340
218307
  }
217341
218308
  if (containsPathTraversal2(filePath)) {
217342
218309
  throw new Error(`Path traversal not allowed in patch file path: ${filePath}`);
217343
218310
  }
217344
- const resolved = (0, import_path25.resolve)(workdir, filePath);
218311
+ const resolved = (0, import_path26.resolve)(workdir, filePath);
217345
218312
  if (!isWithinBase2(resolved, workdir)) {
217346
218313
  throw new Error(`Patch file path escapes workdir: ${filePath}`);
217347
218314
  }
217348
218315
  return resolved;
217349
218316
  }
218317
+ function getPatchMutationAccessError(filePatch, resolvedPath, displayPath, tracker) {
218318
+ if (filePatch.action === "add" && !(0, import_fs19.existsSync)(resolvedPath)) return null;
218319
+ if (filePatch.action !== "add" && !(0, import_fs19.existsSync)(resolvedPath)) return null;
218320
+ if (filePatch.action === "add") {
218321
+ return tracker.assertKnownForMutation(resolvedPath, "replace", "applyPatch", displayPath);
218322
+ }
218323
+ return tracker.assertKnownForMutation(resolvedPath, filePatch.action, "applyPatch", displayPath);
218324
+ }
217350
218325
  function parsePatch2(input) {
217351
218326
  const lines = input.split("\n");
217352
218327
  const patches = [];
@@ -217437,19 +218412,26 @@ function applyHunks(content, filePath, hunks) {
217437
218412
  }
217438
218413
  return { result };
217439
218414
  }
217440
- function createApplyPatchTool(scope = {}) {
218415
+ function createApplyPatchTool(scope = {}, tracker) {
217441
218416
  return {
217442
- patch: tool({
217443
- description: 'Apply a multi-file patch using a structured diff format. This is ideal for multi-file or multi-location edits where calling `edit` multiple times would be brittle.\n\nThe patch is a stripped-down, file-oriented diff format with no line numbers. It uses a high-level envelope:\n\n```\n*** Begin Patch\n[ one or more file operations ]\n*** End Patch\n```\n\nEach operation starts with one of three headers:\n- `*** Add File: <path>` \u2014 create a new file. Every following line must start with `+`.\n- `*** Delete File: <path>` \u2014 remove an existing file.\n- `*** Update File: <path>` \u2014 modify an existing file in place (optionally with a rename).\n\nFor Update operations, you may add `*** Move to: <new path>` on the next line to rename the file.\nThen include one or more "hunks", each introduced by `@@` (optionally followed by a context header).\n\nWithin a hunk, each line starts with:\n- ` ` (space) \u2014 context line (unchanged, used for matching)\n- `-` \u2014 line to remove\n- `+` \u2014 line to add\n\n**Important guidelines for Update hunks:**\n- Always include 3 lines of context before and after each change. Context lines help locate the correct position in the file.\n- If 3 lines of context is insufficient to uniquely identify the code, use `@@` with a class or function name to narrow down the location. Example: `@@ class UserService`\n- For deeply nested or repeated code, use multiple `@@` markers to jump to the right context. Example:\n ```\n @@ class UserService\n @@ async login():\n - return false\n + return await authenticate()\n ```\n- Use `*** End of File` to mark a pure insertion at the end of a file.\n- `workdir` is REQUIRED and must be an absolute path. It is the root directory where the patch is applied.\n- File paths inside the patch are relative to `workdir`, never absolute.\n- Do not use `..` in patch file paths; paths may not escape `workdir`.\n\nFull example combining multiple operations:\n```\n*** Begin Patch\n*** Add File: hello.txt\n+Hello world\n*** Update File: src/app.py\n*** Move to: src/main.py\n@@ def greet():\n- print("Hi")\n+ print("Hello, world!")\n*** Delete File: obsolete.txt\n*** End Patch\n```',
218417
+ applyPatch: tool({
218418
+ description: 'Apply a multi-file patch using a structured diff format. This is ideal for multi-file or multi-location edits where calling `editFile` multiple times would be brittle.\n\nThe patch is a stripped-down, file-oriented diff format with no line numbers. It uses a high-level envelope:\n\n```\n*** Begin Patch\n[ one or more file operations ]\n*** End Patch\n```\n\nEach operation starts with one of three headers:\n- `*** Add File: <path>` \u2014 create a new file. Every following line must start with `+`.\n- `*** Delete File: <path>` \u2014 remove an existing file.\n- `*** Update File: <path>` \u2014 modify an existing file in place (optionally with a rename).\n\nFor Update operations, you may add `*** Move to: <new path>` on the next line to rename the file.\nThen include one or more "hunks", each introduced by `@@` (optionally followed by a context header).\n\nWithin a hunk, each line starts with:\n- ` ` (space) \u2014 context line (unchanged, used for matching)\n- `-` \u2014 line to remove\n- `+` \u2014 line to add\n\n**Important guidelines for Update hunks:**\n- Always include 3 lines of context before and after each change. Context lines help locate the correct position in the file.\n- If 3 lines of context is insufficient to uniquely identify the code, use `@@` with a class or function name to narrow down the location. Example: `@@ class UserService`\n- For deeply nested or repeated code, use multiple `@@` markers to jump to the right context. Example:\n ```\n @@ class UserService\n @@ async login():\n - return false\n + return await authenticate()\n ```\n- Use `*** End of File` to mark a pure insertion at the end of a file.\n- `workdir` is REQUIRED and must be an absolute path. It is the root directory where the patch is applied.\n- File paths inside the patch are relative to `workdir`, never absolute.\n- Do not use `..` in patch file paths; paths may not escape `workdir`.\n\nFull example combining multiple operations:\n```\n*** Begin Patch\n*** Add File: hello.txt\n+Hello world\n*** Update File: src/app.py\n*** Move to: src/main.py\n@@ def greet():\n- print("Hi")\n+ print("Hello, world!")\n*** Delete File: obsolete.txt\n*** End Patch\n```',
217444
218419
  inputSchema: zodSchema(
217445
218420
  external_exports3.object({
217446
218421
  workdir: external_exports3.string().describe("REQUIRED FIRST FIELD. Absolute root directory where the patch is applied. Patch file paths are relative to this directory."),
217447
- input: external_exports3.string().describe(
218422
+ patch: external_exports3.string().optional().describe(
217448
218423
  "The patch content. Must start with *** Begin Patch and end with *** End Patch."
217449
218424
  )
218425
+ }).passthrough().superRefine((args2, ctx) => {
218426
+ const legacyInput = args2.input;
218427
+ if (typeof args2.patch !== "string" && typeof legacyInput !== "string") {
218428
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, message: "patch is required" });
218429
+ }
217450
218430
  })
217451
218431
  ),
217452
- execute: async ({ workdir, input }, { abortSignal }) => {
218432
+ execute: async (args2, { abortSignal }) => {
218433
+ const { workdir, patch } = args2;
218434
+ const input = args2.input;
217453
218435
  if (abortSignal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
217454
218436
  let patchWorkdir;
217455
218437
  try {
@@ -217457,7 +218439,11 @@ function createApplyPatchTool(scope = {}) {
217457
218439
  } catch (error48) {
217458
218440
  return { success: false, error: error48 instanceof Error ? error48.message : String(error48) };
217459
218441
  }
217460
- const patches = parsePatch2(input);
218442
+ const patchContent = patch ?? input;
218443
+ if (typeof patchContent !== "string") {
218444
+ return { success: false, error: "patch is required" };
218445
+ }
218446
+ const patches = parsePatch2(patchContent);
217461
218447
  if (patches.length === 0) {
217462
218448
  return { success: false, error: "No file operations found in patch" };
217463
218449
  }
@@ -217487,6 +218473,18 @@ function createApplyPatchTool(scope = {}) {
217487
218473
  continue;
217488
218474
  }
217489
218475
  }
218476
+ if (tracker) {
218477
+ const mutationError = getPatchMutationAccessError(filePatch, resolvedPath, filePatch.path, tracker);
218478
+ if (mutationError) {
218479
+ results.push({
218480
+ action: filePatch.action,
218481
+ path: filePatch.path,
218482
+ success: false,
218483
+ error: mutationError
218484
+ });
218485
+ continue;
218486
+ }
218487
+ }
217490
218488
  try {
217491
218489
  switch (filePatch.action) {
217492
218490
  case "add": {
@@ -217500,8 +218498,9 @@ function createApplyPatchTool(scope = {}) {
217500
218498
  continue;
217501
218499
  }
217502
218500
  const content = (filePatch.content || "").replace(/\n$/, "");
217503
- await (0, import_promises26.mkdir)((0, import_path25.dirname)(resolvedPath), { recursive: true });
217504
- await (0, import_promises26.writeFile)(resolvedPath, content, "utf-8");
218501
+ await (0, import_promises27.mkdir)((0, import_path26.dirname)(resolvedPath), { recursive: true });
218502
+ await (0, import_promises27.writeFile)(resolvedPath, content, "utf-8");
218503
+ tracker?.markKnown(resolvedPath);
217505
218504
  results.push({
217506
218505
  action: "add",
217507
218506
  path: filePatch.path,
@@ -217519,7 +218518,7 @@ function createApplyPatchTool(scope = {}) {
217519
218518
  });
217520
218519
  continue;
217521
218520
  }
217522
- const oldContent = await (0, import_promises26.readFile)(resolvedPath, "utf-8");
218521
+ const oldContent = await (0, import_promises27.readFile)(resolvedPath, "utf-8");
217523
218522
  let newContent;
217524
218523
  if (filePatch.hunks && filePatch.hunks.length > 0) {
217525
218524
  const { result, error: error48 } = applyHunks(oldContent, filePatch.path, filePatch.hunks);
@@ -217538,9 +218537,46 @@ function createApplyPatchTool(scope = {}) {
217538
218537
  }
217539
218538
  if (filePatch.moveTo) {
217540
218539
  const newResolvedPath = resolvePatchPath(filePatch.moveTo, patchWorkdir);
217541
- await (0, import_promises26.mkdir)((0, import_path25.dirname)(newResolvedPath), { recursive: true });
217542
- await (0, import_promises26.writeFile)(newResolvedPath, newContent, "utf-8");
217543
- await (0, import_promises26.unlink)(resolvedPath);
218540
+ if (newResolvedPath === resolvedPath) {
218541
+ results.push({
218542
+ action: "rename",
218543
+ path: `${filePatch.path} \u2192 ${filePatch.moveTo}`,
218544
+ success: false,
218545
+ error: "Move target must differ from source path"
218546
+ });
218547
+ continue;
218548
+ }
218549
+ if (scope.groupId) {
218550
+ const groupDir = getGroupDir(scope.groupId);
218551
+ if (isGroupProtectedFile(newResolvedPath, groupDir)) {
218552
+ results.push({
218553
+ action: "rename",
218554
+ path: `${filePatch.path} \u2192 ${filePatch.moveTo}`,
218555
+ success: false,
218556
+ error: `Cannot modify protected file: ${filePatch.moveTo}`
218557
+ });
218558
+ continue;
218559
+ }
218560
+ }
218561
+ await (0, import_promises27.mkdir)((0, import_path26.dirname)(newResolvedPath), { recursive: true });
218562
+ const moveTargetError = tracker?.assertKnownForMutation(
218563
+ newResolvedPath,
218564
+ "overwrite rename target",
218565
+ "applyPatch",
218566
+ filePatch.moveTo
218567
+ );
218568
+ if ((0, import_fs19.existsSync)(newResolvedPath) && moveTargetError) {
218569
+ results.push({
218570
+ action: "rename",
218571
+ path: `${filePatch.path} \u2192 ${filePatch.moveTo}`,
218572
+ success: false,
218573
+ error: moveTargetError
218574
+ });
218575
+ continue;
218576
+ }
218577
+ await (0, import_promises27.writeFile)(newResolvedPath, newContent, "utf-8");
218578
+ await (0, import_promises27.unlink)(resolvedPath);
218579
+ tracker?.markKnown(newResolvedPath);
217544
218580
  const diff = createUnifiedFileDiff(filePatch.path, oldContent, newContent);
217545
218581
  results.push({
217546
218582
  action: "rename",
@@ -217550,7 +218586,8 @@ function createApplyPatchTool(scope = {}) {
217550
218586
  });
217551
218587
  } else {
217552
218588
  const diff = createUnifiedFileDiff(filePatch.path, oldContent, newContent);
217553
- await (0, import_promises26.writeFile)(resolvedPath, newContent, "utf-8");
218589
+ await (0, import_promises27.writeFile)(resolvedPath, newContent, "utf-8");
218590
+ tracker?.markKnown(resolvedPath);
217554
218591
  results.push({
217555
218592
  action: "update",
217556
218593
  path: filePatch.path,
@@ -217570,7 +218607,7 @@ function createApplyPatchTool(scope = {}) {
217570
218607
  });
217571
218608
  continue;
217572
218609
  }
217573
- await (0, import_promises26.unlink)(resolvedPath);
218610
+ await (0, import_promises27.unlink)(resolvedPath);
217574
218611
  results.push({
217575
218612
  action: "delete",
217576
218613
  path: filePatch.path,
@@ -232278,12 +233315,12 @@ function createWebSearchTool(executor) {
232278
233315
  }
232279
233316
 
232280
233317
  // src/electron/main/utils/icon.ts
232281
- var import_path26 = require("path");
233318
+ var import_path27 = require("path");
232282
233319
  function getIconPath() {
232283
233320
  if (process.env.NODE_ENV === "development") {
232284
- return (0, import_path26.join)(__dirname, "../../resources/icon.png");
233321
+ return (0, import_path27.join)(__dirname, "../../resources/icon.png");
232285
233322
  }
232286
- return (0, import_path26.join)(process.resourcesPath, "icon.png");
233323
+ return (0, import_path27.join)(process.resourcesPath, "icon.png");
232287
233324
  }
232288
233325
 
232289
233326
  // src/adapters/tools/web/search/electron/search-with-window.ts
@@ -232727,16 +233764,16 @@ function createSchedulerTools(scope) {
232727
233764
  // src/adapters/tools/image/analyze.ts
232728
233765
  init_dist6();
232729
233766
  init_zod();
232730
- var import_promises27 = require("fs/promises");
233767
+ var import_promises28 = require("fs/promises");
232731
233768
  var import_fs20 = require("fs");
232732
- var import_path27 = require("path");
233769
+ var import_path28 = require("path");
232733
233770
  init_config();
232734
233771
  init_multimodal();
232735
233772
  init_logger();
232736
233773
  var logger37 = createLogger("AnalyzeImageTool");
232737
233774
  function resolveImagePath(imagePath) {
232738
- if ((0, import_path27.isAbsolute)(imagePath)) return imagePath;
232739
- return (0, import_path27.resolve)(process.cwd(), imagePath);
233775
+ if ((0, import_path28.isAbsolute)(imagePath)) return imagePath;
233776
+ return (0, import_path28.resolve)(process.cwd(), imagePath);
232740
233777
  }
232741
233778
  function createModelFromCandidate(candidate) {
232742
233779
  const channels = config.get("llmChannels") ?? [];
@@ -232787,8 +233824,8 @@ async function loadImageAsDataUrl(imagePathOrUrl, abortSignal) {
232787
233824
  } else {
232788
233825
  const absPath = resolveImagePath(imagePathOrUrl);
232789
233826
  if (!(0, import_fs20.existsSync)(absPath)) throw new Error(`\u56FE\u7247\u6587\u4EF6\u4E0D\u5B58\u5728: ${absPath}`);
232790
- buffer = await (0, import_promises27.readFile)(absPath);
232791
- mimeType = extToMime((0, import_path27.extname)(absPath));
233827
+ buffer = await (0, import_promises28.readFile)(absPath);
233828
+ mimeType = extToMime((0, import_path28.extname)(absPath));
232792
233829
  }
232793
233830
  const maxDim = getMaxImageDimension();
232794
233831
  const resized = await resizeImageBuffer(buffer, maxDim);
@@ -232855,9 +233892,9 @@ function createAnalyzeImageTool() {
232855
233892
  // src/adapters/tools/image/generate.ts
232856
233893
  init_dist6();
232857
233894
  init_zod();
232858
- var import_promises28 = require("fs/promises");
233895
+ var import_promises29 = require("fs/promises");
232859
233896
  var import_fs21 = require("fs");
232860
- var import_path28 = require("path");
233897
+ var import_path29 = require("path");
232861
233898
  init_config();
232862
233899
  init_logger();
232863
233900
  var logger38 = createLogger("GenerateImageTool");
@@ -232894,9 +233931,9 @@ async function loadImageBuffer(imagePathOrUrl, abortSignal) {
232894
233931
  if (!response.ok) throw new Error(`\u83B7\u53D6\u56FE\u7247\u5931\u8D25: ${response.status} ${response.statusText}`);
232895
233932
  return Buffer.from(await response.arrayBuffer());
232896
233933
  }
232897
- const absPath = (0, import_path28.isAbsolute)(imagePathOrUrl) ? imagePathOrUrl : (0, import_path28.resolve)(process.cwd(), imagePathOrUrl);
233934
+ const absPath = (0, import_path29.isAbsolute)(imagePathOrUrl) ? imagePathOrUrl : (0, import_path29.resolve)(process.cwd(), imagePathOrUrl);
232898
233935
  if (!(0, import_fs21.existsSync)(absPath)) throw new Error(`\u56FE\u7247\u6587\u4EF6\u4E0D\u5B58\u5728: ${absPath}`);
232899
- return (0, import_promises28.readFile)(absPath);
233936
+ return (0, import_promises29.readFile)(absPath);
232900
233937
  }
232901
233938
  function createGenerateImageTool() {
232902
233939
  return {
@@ -233000,6 +234037,16 @@ var FileAccessTracker = class {
233000
234037
  isKnown(resolvedPath) {
233001
234038
  return this.knownPaths.has(resolvedPath);
233002
234039
  }
234040
+ /**
234041
+ * Enforce the mutation policy for an existing path.
234042
+ *
234043
+ * New files are allowed by the tools themselves. Existing files must have been
234044
+ * read, or created by a previous write in this tool set, before any mutation.
234045
+ */
234046
+ assertKnownForMutation(resolvedPath, action, toolName, filePath) {
234047
+ if (this.isKnown(resolvedPath)) return null;
234048
+ return `Cannot ${action} existing file without reading it first. Call readFile on "${filePath}" before using ${toolName}.`;
234049
+ }
233003
234050
  /** 重置(新会话 / 新工具实例时调用) */
233004
234051
  clear() {
233005
234052
  this.knownPaths.clear();
@@ -233041,8 +234088,8 @@ function getBuiltInTools(context2 = {}) {
233041
234088
  ...createShellTool(agentId),
233042
234089
  ...createReadFileTool(scope, tracker),
233043
234090
  ...createWriteFileTool(scope, tracker),
233044
- ...createEditFileTool(scope),
233045
- ...createApplyPatchTool(scope),
234091
+ ...createEditFileTool(scope, tracker),
234092
+ ...createApplyPatchTool(scope, tracker),
233046
234093
  ...createWebFetchTool(),
233047
234094
  ...createWebSearchTool2(),
233048
234095
  ...createDatabaseTool()
@@ -233075,7 +234122,7 @@ init_zod();
233075
234122
  var import_node_fs2 = require("node:fs");
233076
234123
  var import_node_path7 = require("node:path");
233077
234124
  init_logger();
233078
- var log20 = createLogger("SendFileTool");
234125
+ var log22 = createLogger("SendFileTool");
233079
234126
  function createSendFileTool(attachmentPort) {
233080
234127
  return {
233081
234128
  sendFile: tool({
@@ -233113,7 +234160,7 @@ function createSendFileTool(attachmentPort) {
233113
234160
  const fileBuffer = (0, import_node_fs2.readFileSync)(filePath);
233114
234161
  const metadata = await attachmentPort.saveAttachment(fileBuffer, filename, mimeType);
233115
234162
  const type = isImageExtension(ext) ? "\u56FE\u7247" : "\u6587\u4EF6";
233116
- log20.info(`sendFile: ${filePath} \u2192 attachment ${metadata.id} (${type}, ${(fileStat.size / 1024).toFixed(1)}KB)`);
234163
+ log22.info(`sendFile: ${filePath} \u2192 attachment ${metadata.id} (${type}, ${(fileStat.size / 1024).toFixed(1)}KB)`);
233117
234164
  return {
233118
234165
  text: `${type}\u5DF2\u53D1\u9001: ${filename}`,
233119
234166
  attachment: {
@@ -233126,7 +234173,7 @@ function createSendFileTool(attachmentPort) {
233126
234173
  };
233127
234174
  } catch (error48) {
233128
234175
  const message = error48 instanceof Error ? error48.message : String(error48);
233129
- log20.error("sendFile error:", message);
234176
+ log22.error("sendFile error:", message);
233130
234177
  return `\u53D1\u9001\u6587\u4EF6\u5931\u8D25: ${message}`;
233131
234178
  }
233132
234179
  }
@@ -233328,8 +234375,8 @@ var ToolOutputArtifactStateSlot = defineExecutionSlot({
233328
234375
  });
233329
234376
 
233330
234377
  // src/adapters/tools/middleware/save-tool-output-artifact.ts
233331
- var import_promises29 = require("fs/promises");
233332
- var import_path29 = require("path");
234378
+ var import_promises30 = require("fs/promises");
234379
+ var import_path30 = require("path");
233333
234380
  init_workspace();
233334
234381
  init_thread_file_store();
233335
234382
 
@@ -233520,7 +234567,7 @@ async function handleWebFetchResult(ctx, result) {
233520
234567
 
233521
234568
  ---
233522
234569
  [\u5185\u5BB9\u8D85\u8FC7 ${WEB_MAX_CHARS} \u5B57\u7B26\uFF0C\u5DF2\u622A\u65AD\u3002\u5B8C\u6574\u5185\u5BB9\u5DF2\u4FDD\u5B58\u5230: ${artifact.absolutePath}]
233523
- [\u53EF\u7528 read \u8BFB\u53D6\u5B8C\u6574\u5185\u5BB9]`;
234570
+ [\u53EF\u7528 readFile \u8BFB\u53D6\u5B8C\u6574\u5185\u5BB9]`;
233524
234571
  recordArtifact(ctx, "content", artifact);
233525
234572
  return {
233526
234573
  kind: "success",
@@ -233570,7 +234617,7 @@ async function handleWebSearchResult(ctx, result) {
233570
234617
 
233571
234618
  ---
233572
234619
  [\u5185\u5BB9\u8D85\u8FC7 ${WEB_MAX_CHARS} \u5B57\u7B26\uFF0C\u5DF2\u622A\u65AD\u3002\u5B8C\u6574\u5185\u5BB9\u5DF2\u4FDD\u5B58\u5230: ${artifact.absolutePath}]
233573
- [\u53EF\u7528 read \u8BFB\u53D6\u5B8C\u6574\u5185\u5BB9]`;
234620
+ [\u53EF\u7528 readFile \u8BFB\u53D6\u5B8C\u6574\u5185\u5BB9]`;
233574
234621
  recordArtifact(ctx, "content", artifact);
233575
234622
  return {
233576
234623
  kind: "success",
@@ -233601,11 +234648,11 @@ async function saveArtifact(ctx, filename, text4) {
233601
234648
  const resultsRoot = resolveToolResultsRoot(ctx);
233602
234649
  const now2 = ctx.runtime.now();
233603
234650
  const today = now2.toISOString().split("T")[0];
233604
- const dir = (0, import_path29.join)(resultsRoot, today);
233605
- await (0, import_promises29.mkdir)(dir, { recursive: true });
233606
- const absolutePath = (0, import_path29.join)(dir, filename);
234651
+ const dir = (0, import_path30.join)(resultsRoot, today);
234652
+ await (0, import_promises30.mkdir)(dir, { recursive: true });
234653
+ const absolutePath = (0, import_path30.join)(dir, filename);
233607
234654
  const content = limitArtifactBytes(text4);
233608
- await (0, import_promises29.writeFile)(absolutePath, content, "utf-8");
234655
+ await (0, import_promises30.writeFile)(absolutePath, content, "utf-8");
233609
234656
  return {
233610
234657
  absolutePath,
233611
234658
  relativePath: `tool_results/${today}/${filename}`,
@@ -233616,9 +234663,9 @@ async function saveArtifact(ctx, filename, text4) {
233616
234663
  }
233617
234664
  function resolveToolResultsRoot(ctx) {
233618
234665
  if (ctx.groupId && ctx.threadId) {
233619
- return (0, import_path29.join)(getThreadDir(ctx.groupId, ctx.threadId), "tool_results");
234666
+ return (0, import_path30.join)(getThreadDir(ctx.groupId, ctx.threadId), "tool_results");
233620
234667
  }
233621
- return (0, import_path29.join)(getAgentWorkspaceDir(ctx.agentId), "tool_results");
234668
+ return (0, import_path30.join)(getAgentWorkspaceDir(ctx.agentId), "tool_results");
233622
234669
  }
233623
234670
  function recordArtifact(ctx, stream, artifact) {
233624
234671
  const state = ctx.runtime.runState.getOrCreate(ToolOutputArtifactStateSlot, () => ({ artifacts: [] }));
@@ -233672,6 +234719,16 @@ registry2.seal();
233672
234719
  var toolExecutionRegistry = registry2;
233673
234720
 
233674
234721
  // src/adapters/tools/agent-tool-provider.ts
234722
+ var LEGACY_TOOL_NAME_ALIASES = {
234723
+ read: "readFile",
234724
+ write: "writeFile",
234725
+ edit: "editFile",
234726
+ patch: "applyPatch"
234727
+ };
234728
+ function getDisabledToolNames(toolName) {
234729
+ const canonical = LEGACY_TOOL_NAME_ALIASES[toolName];
234730
+ return canonical ? [toolName, canonical] : [toolName];
234731
+ }
233675
234732
  var DefaultAgentToolProvider = class {
233676
234733
  async getTools(agent, conversationIdOrContext, runtime) {
233677
234734
  const toolContext = normalizeToolProviderContext(conversationIdOrContext);
@@ -233686,7 +234743,9 @@ var DefaultAgentToolProvider = class {
233686
234743
  const tools = { ...mcpTools, ...skillTool, ...builtInTools };
233687
234744
  if (disabledTools?.length) {
233688
234745
  for (const toolName of disabledTools) {
233689
- delete tools[toolName];
234746
+ for (const disabledToolName of getDisabledToolNames(toolName)) {
234747
+ delete tools[disabledToolName];
234748
+ }
233690
234749
  }
233691
234750
  }
233692
234751
  const wrappedTools = wrapToolsWithExecutionPipeline(tools, {
@@ -233794,6 +234853,7 @@ function bootstrapAgentExecutionDeps() {
233794
234853
 
233795
234854
  // src/app/bootstrap.ts
233796
234855
  init_logger();
234856
+ init_sequelize();
233797
234857
  var logger39 = createLogger("bootstrap");
233798
234858
  var unsubscribeConversationEvents = null;
233799
234859
  async function bootstrapApplication() {
@@ -233848,6 +234908,7 @@ async function cleanup() {
233848
234908
  threadManagerPool.stopCleanup();
233849
234909
  await channelManager.dispose();
233850
234910
  await mcpManager?.dispose();
234911
+ await closeSequelize();
233851
234912
  }
233852
234913
 
233853
234914
  // src/cli/index.ts