@xopcai/xopc 0.0.89 → 0.0.91

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 (267) hide show
  1. package/README.md +36 -12
  2. package/README.zh-CN.md +36 -12
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +41 -0
  6. package/dist/gateway/static/root/assets/agents-bVWUlrlD.js +222 -0
  7. package/dist/gateway/static/root/assets/apps-page-CIC8bmvZ.js +1 -0
  8. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CpyoFbs4.js → attachment-preview-renderer-DBAxQXb-.js} +2 -2
  9. package/dist/gateway/static/root/assets/{attachment-process-heavy-CqVriadb.js → attachment-process-heavy-Csq3TrrP.js} +4 -4
  10. package/dist/gateway/static/root/assets/channels-settings-C8G8RAAP.js +1 -0
  11. package/dist/gateway/static/root/assets/{channels-status-swr-DaHGkRF1.js → channels-status-swr-CYWL5DLD.js} +1 -1
  12. package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +1 -0
  13. package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
  14. package/dist/gateway/static/root/assets/cron-api-TVqLlGAC.js +1 -0
  15. package/dist/gateway/static/root/assets/cron-dreaming-jobs-Ip703-qM.js +2 -0
  16. package/dist/gateway/static/root/assets/cron-page-BtcFYlvv.js +1 -0
  17. package/dist/gateway/static/root/assets/dist-CUV1uY5f.js +1 -0
  18. package/dist/gateway/static/root/assets/{extension-debug-page-CtuKJ9tE.js → extension-debug-page-mTLHRDp1.js} +1 -1
  19. package/dist/gateway/static/root/assets/{extension-page-ykzjOkR5.js → extension-page-iI8BI7WK.js} +1 -1
  20. package/dist/gateway/static/root/assets/{extension-settings-page-Ce2qrdpO.js → extension-settings-page-ByXcdubM.js} +1 -1
  21. package/dist/gateway/static/root/assets/{fetch-C9FFJjuH.js → fetch-BWtQq_Ys.js} +1 -1
  22. package/dist/gateway/static/root/assets/{field-primitives-BFcrNeTU.js → field-primitives-BsZ-4VT5.js} +1 -1
  23. package/dist/gateway/static/root/assets/{heartbeat-config-api-CEg4Vr9R.js → heartbeat-config-api-WjTsRLCU.js} +1 -1
  24. package/dist/gateway/static/root/assets/{index-CZfy9oxs.js → index-CKkR-v9U.js} +101 -97
  25. package/dist/gateway/static/root/assets/index-VlELBY99.css +1 -0
  26. package/dist/gateway/static/root/assets/logs-page-ClnIpxfd.js +1 -0
  27. package/dist/gateway/static/root/assets/note-detail-page-B91pLkEI.css +1 -0
  28. package/dist/gateway/static/root/assets/note-detail-page-DJ2Mb4x7.js +179 -0
  29. package/dist/gateway/static/root/assets/note-time-JLBPSLzK.js +1 -0
  30. package/dist/gateway/static/root/assets/notes-page-BE-75qz9.js +1 -0
  31. package/dist/gateway/static/root/assets/{pdf-BnEvgIXZ.js → pdf-epILhEOn.js} +1 -1
  32. package/dist/gateway/static/root/assets/preload-helper-zJ_50EbN.js +1 -0
  33. package/dist/gateway/static/root/assets/sessions-page-bJJkWtTl.js +1 -0
  34. package/dist/gateway/static/root/assets/{settings-form-section-BqdzA28u.js → settings-form-section-DSYCknxM.js} +1 -1
  35. package/dist/gateway/static/root/assets/settings-page-WcMXLq2U.js +3 -0
  36. package/dist/gateway/static/root/assets/share-preview-page-awRqs4hV.js +2 -0
  37. package/dist/gateway/static/root/assets/skills-page-Lu-i1JG7.js +2 -0
  38. package/dist/gateway/static/root/assets/{theme-store-CNqbmTNV.js → theme-store-BC-42BoZ.js} +1 -1
  39. package/dist/gateway/static/root/assets/toast-z0toXu32.js +1 -0
  40. package/dist/gateway/static/root/assets/url-CY1RQKTU.js +3 -0
  41. package/dist/gateway/static/root/assets/{utils-BWm2tG2w.js → utils-DX3TQuap.js} +1 -1
  42. package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +45 -0
  43. package/dist/gateway/static/root/assets/voice-api-key-field-B5uKlDqA.js +1 -0
  44. package/dist/gateway/static/root/assets/workflow-page.utils-ClC37yEp.js +1 -0
  45. package/dist/gateway/static/root/assets/workflows-page-C7VhIXtR.js +27 -0
  46. package/dist/gateway/static/root/index.html +11 -7
  47. package/dist/package.js +1 -1
  48. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +20 -18
  49. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -1
  50. package/dist/src/agent/tools/cronjob-tool.d.ts +6 -0
  51. package/dist/src/agent/tools/cronjob-tool.js +74 -9
  52. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  53. package/dist/src/agent/tools/edit.d.ts +5 -1
  54. package/dist/src/agent/tools/edit.js +7 -5
  55. package/dist/src/agent/tools/edit.js.map +1 -1
  56. package/dist/src/agent/tools/factory.js +2 -2
  57. package/dist/src/agent/tools/factory.js.map +1 -1
  58. package/dist/src/agent/tools/write.d.ts +5 -1
  59. package/dist/src/agent/tools/write.js +7 -5
  60. package/dist/src/agent/tools/write.js.map +1 -1
  61. package/dist/src/agent/workflow/agent-progress.js +2 -0
  62. package/dist/src/agent/workflow/agent-progress.js.map +1 -1
  63. package/dist/src/agent/workflow/builtins/client-proposal.d.ts +12 -0
  64. package/dist/src/agent/workflow/builtins/client-proposal.js +155 -0
  65. package/dist/src/agent/workflow/builtins/client-proposal.js.map +1 -0
  66. package/dist/src/agent/workflow/builtins/competitor-scan.d.ts +12 -0
  67. package/dist/src/agent/workflow/builtins/competitor-scan.js +150 -0
  68. package/dist/src/agent/workflow/builtins/competitor-scan.js.map +1 -0
  69. package/dist/src/agent/workflow/builtins/content-draft.d.ts +13 -0
  70. package/dist/src/agent/workflow/builtins/content-draft.js +146 -0
  71. package/dist/src/agent/workflow/builtins/content-draft.js.map +1 -0
  72. package/dist/src/agent/workflow/builtins/content-repurpose.d.ts +11 -0
  73. package/dist/src/agent/workflow/builtins/content-repurpose.js +137 -0
  74. package/dist/src/agent/workflow/builtins/content-repurpose.js.map +1 -0
  75. package/dist/src/agent/workflow/builtins/decision-compare.d.ts +13 -0
  76. package/dist/src/agent/workflow/builtins/decision-compare.js +173 -0
  77. package/dist/src/agent/workflow/builtins/decision-compare.js.map +1 -0
  78. package/dist/src/agent/workflow/builtins/inbox-triage.d.ts +11 -0
  79. package/dist/src/agent/workflow/builtins/inbox-triage.js +148 -0
  80. package/dist/src/agent/workflow/builtins/inbox-triage.js.map +1 -0
  81. package/dist/src/agent/workflow/builtins/index.d.ts +10 -1
  82. package/dist/src/agent/workflow/builtins/index.js +46 -1
  83. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  84. package/dist/src/agent/workflow/builtins/meeting-prep.d.ts +12 -0
  85. package/dist/src/agent/workflow/builtins/meeting-prep.js +144 -0
  86. package/dist/src/agent/workflow/builtins/meeting-prep.js.map +1 -0
  87. package/dist/src/agent/workflow/builtins/offer-design.d.ts +12 -0
  88. package/dist/src/agent/workflow/builtins/offer-design.js +161 -0
  89. package/dist/src/agent/workflow/builtins/offer-design.js.map +1 -0
  90. package/dist/src/agent/workflow/builtins/weekly-review.d.ts +12 -0
  91. package/dist/src/agent/workflow/builtins/weekly-review.js +131 -0
  92. package/dist/src/agent/workflow/builtins/weekly-review.js.map +1 -0
  93. package/dist/src/agent/workflow/step-labels.js +2 -2
  94. package/dist/src/agent/workflow/step-labels.js.map +1 -1
  95. package/dist/src/agent/workflow/subagent-runner.js +3 -1
  96. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  97. package/dist/src/agent/workflow/types.d.ts +4 -0
  98. package/dist/src/chat-commands/agent-edit.d.ts +4 -0
  99. package/dist/src/chat-commands/agent-edit.js +136 -0
  100. package/dist/src/chat-commands/agent-edit.js.map +1 -0
  101. package/dist/src/chat-commands/index.d.ts +1 -0
  102. package/dist/src/chat-commands/index.js +3 -1
  103. package/dist/src/chat-commands/index.js.map +1 -1
  104. package/dist/src/cli/bin.js +2 -0
  105. package/dist/src/cli/bin.js.map +1 -1
  106. package/dist/src/cli/commands/cron.js +42 -3
  107. package/dist/src/cli/commands/cron.js.map +1 -1
  108. package/dist/src/cli/commands/doctor/checks/session-integrity.js +79 -56
  109. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  110. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  111. package/dist/src/cli/commands/update.js +86 -79
  112. package/dist/src/cli/commands/update.js.map +1 -1
  113. package/dist/src/commands/agents.config.d.ts +3 -2
  114. package/dist/src/commands/agents.config.js +5 -2
  115. package/dist/src/commands/agents.config.js.map +1 -1
  116. package/dist/src/config/agent-typed-models.d.ts +2 -7
  117. package/dist/src/config/agent-typed-models.js +3 -14
  118. package/dist/src/config/agent-typed-models.js.map +1 -1
  119. package/dist/src/config/localized-text.d.ts +6 -0
  120. package/dist/src/config/localized-text.js +42 -0
  121. package/dist/src/config/localized-text.js.map +1 -0
  122. package/dist/src/config/models-json.d.ts +6 -6
  123. package/dist/src/config/schema.d.ts +6 -21
  124. package/dist/src/config/schema.js +4 -4
  125. package/dist/src/config/schema.js.map +1 -1
  126. package/dist/src/cron/executor.d.ts +2 -0
  127. package/dist/src/cron/executor.js +111 -1
  128. package/dist/src/cron/executor.js.map +1 -1
  129. package/dist/src/cron/types.d.ts +8 -1
  130. package/dist/src/cron/validation.d.ts +4 -0
  131. package/dist/src/cron/validation.js +4 -3
  132. package/dist/src/cron/validation.js.map +1 -1
  133. package/dist/src/cron/workflow-run-completion.d.ts +23 -0
  134. package/dist/src/cron/workflow-run-completion.js +72 -0
  135. package/dist/src/cron/workflow-run-completion.js.map +1 -0
  136. package/dist/src/extensions/update.d.ts +51 -0
  137. package/dist/src/extensions/update.js +260 -0
  138. package/dist/src/extensions/update.js.map +1 -0
  139. package/dist/src/gateway/agents-admin.d.ts +15 -8
  140. package/dist/src/gateway/agents-admin.js +77 -28
  141. package/dist/src/gateway/agents-admin.js.map +1 -1
  142. package/dist/src/gateway/heartbeat/service.js +1 -1
  143. package/dist/src/gateway/hono/lib/config-payload.d.ts +6 -0
  144. package/dist/src/gateway/hono/lib/config-payload.js +3 -1
  145. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  146. package/dist/src/gateway/hono/middleware/auth.d.ts +2 -0
  147. package/dist/src/gateway/hono/middleware/auth.js +11 -7
  148. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  149. package/dist/src/gateway/hono/routes/agents.js +55 -12
  150. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  151. package/dist/src/gateway/hono/routes/config-patch/agents.js +1 -1
  152. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +2 -2
  153. package/dist/src/gateway/hono/routes/config-patch/gateway.js +12 -0
  154. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  155. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  156. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  157. package/dist/src/gateway/hono/routes/notes.d.ts +3 -0
  158. package/dist/src/gateway/hono/routes/notes.js +274 -0
  159. package/dist/src/gateway/hono/routes/notes.js.map +1 -0
  160. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  161. package/dist/src/gateway/hono/routes/update.js +55 -107
  162. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  163. package/dist/src/gateway/hono/routes/workflows.js +3 -1
  164. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  165. package/dist/src/gateway/server.js +2 -0
  166. package/dist/src/gateway/server.js.map +1 -1
  167. package/dist/src/gateway/service.d.ts +3 -0
  168. package/dist/src/gateway/service.js +12 -1
  169. package/dist/src/gateway/service.js.map +1 -1
  170. package/dist/src/gateway/workspace-ripgrep.d.ts +6 -0
  171. package/dist/src/gateway/workspace-ripgrep.js +62 -11
  172. package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
  173. package/dist/src/heartbeat/index.js +1 -1
  174. package/dist/src/infra/brew.d.ts +4 -0
  175. package/dist/src/infra/brew.js +20 -0
  176. package/dist/src/infra/brew.js.map +1 -0
  177. package/dist/src/infra/package-json.d.ts +2 -0
  178. package/dist/src/infra/package-json.js +23 -0
  179. package/dist/src/infra/package-json.js.map +1 -0
  180. package/dist/src/infra/package-update-steps.d.ts +35 -0
  181. package/dist/src/infra/package-update-steps.js +304 -0
  182. package/dist/src/infra/package-update-steps.js.map +1 -0
  183. package/dist/src/infra/path-env.d.ts +11 -0
  184. package/dist/src/infra/path-env.js +90 -0
  185. package/dist/src/infra/path-env.js.map +1 -0
  186. package/dist/src/infra/path-prepend.d.ts +7 -0
  187. package/dist/src/infra/path-prepend.js +44 -0
  188. package/dist/src/infra/path-prepend.js.map +1 -0
  189. package/dist/src/infra/stable-node-path.d.ts +2 -0
  190. package/dist/src/infra/stable-node-path.js +28 -0
  191. package/dist/src/infra/stable-node-path.js.map +1 -0
  192. package/dist/src/infra/update-global.d.ts +30 -23
  193. package/dist/src/infra/update-global.js +113 -64
  194. package/dist/src/infra/update-global.js.map +1 -1
  195. package/dist/src/infra/update-log.d.ts +1 -0
  196. package/dist/src/infra/update-log.js +12 -0
  197. package/dist/src/infra/update-log.js.map +1 -0
  198. package/dist/src/infra/update-restart.d.ts +20 -0
  199. package/dist/src/infra/update-restart.js +165 -0
  200. package/dist/src/infra/update-restart.js.map +1 -0
  201. package/dist/src/infra/update-runner.d.ts +89 -1
  202. package/dist/src/infra/update-runner.js +604 -173
  203. package/dist/src/infra/update-runner.js.map +1 -1
  204. package/dist/src/infra/update-startup.d.ts +3 -0
  205. package/dist/src/infra/update-startup.js +8 -4
  206. package/dist/src/infra/update-startup.js.map +1 -1
  207. package/dist/src/notes/attachment-ref.d.ts +9 -0
  208. package/dist/src/notes/attachment-ref.js +27 -0
  209. package/dist/src/notes/attachment-ref.js.map +1 -0
  210. package/dist/src/notes/index.d.ts +4 -0
  211. package/dist/src/notes/index.js +4 -0
  212. package/dist/src/notes/note-attachment-sync.d.ts +7 -0
  213. package/dist/src/notes/note-attachment-sync.js +46 -0
  214. package/dist/src/notes/note-attachment-sync.js.map +1 -0
  215. package/dist/src/notes/note-index-meta.d.ts +14 -0
  216. package/dist/src/notes/note-index-meta.js +87 -0
  217. package/dist/src/notes/note-index-meta.js.map +1 -0
  218. package/dist/src/notes/paths.d.ts +5 -0
  219. package/dist/src/notes/paths.js +23 -0
  220. package/dist/src/notes/paths.js.map +1 -0
  221. package/dist/src/notes/service.d.ts +42 -0
  222. package/dist/src/notes/service.js +331 -0
  223. package/dist/src/notes/service.js.map +1 -0
  224. package/dist/src/notes/store.d.ts +33 -0
  225. package/dist/src/notes/store.js +317 -0
  226. package/dist/src/notes/store.js.map +1 -0
  227. package/dist/src/notes/types.d.ts +162 -0
  228. package/dist/src/notes/types.js +1 -0
  229. package/dist/src/routing/resolve-route.d.ts +3 -1
  230. package/dist/src/routing/resolve-route.js.map +1 -1
  231. package/dist/src/session/store.d.ts +5 -3
  232. package/dist/src/session/store.js +66 -20
  233. package/dist/src/session/store.js.map +1 -1
  234. package/dist/src/utils/logger/stats.d.ts +1 -1
  235. package/dist/src/workflows/domain/event.d.ts +3 -0
  236. package/dist/src/workflows/domain/run.d.ts +3 -0
  237. package/dist/src/workflows/domain/run.js.map +1 -1
  238. package/dist/src/workflows/engine/projector.js +17 -0
  239. package/dist/src/workflows/engine/projector.js.map +1 -1
  240. package/dist/src/workflows/engine/workflow-engine.js +127 -0
  241. package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
  242. package/dist/src/workflows/index.js +1 -1
  243. package/dist/src/workflows/service/run-view-to-snapshot.js +3 -1
  244. package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -1
  245. package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
  246. package/dist/src/workflows/service/workflow-run-service.js +4 -1
  247. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  248. package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
  249. package/package.json +1 -1
  250. package/dist/gateway/static/root/assets/agents-B6PJB07W.js +0 -222
  251. package/dist/gateway/static/root/assets/apps-page-BOr0B1wv.js +0 -1
  252. package/dist/gateway/static/root/assets/channels-settings-BelUKggl.js +0 -1
  253. package/dist/gateway/static/root/assets/cron-api-CjOg-BIj.js +0 -1
  254. package/dist/gateway/static/root/assets/cron-dreaming-jobs-DueM3rBz.js +0 -2
  255. package/dist/gateway/static/root/assets/cron-page-DhoZmZXb.js +0 -1
  256. package/dist/gateway/static/root/assets/dist-6LecgDx5.js +0 -1
  257. package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +0 -45
  258. package/dist/gateway/static/root/assets/index-CiN1cQiQ.css +0 -1
  259. package/dist/gateway/static/root/assets/logs-page-BwWLfqvd.js +0 -1
  260. package/dist/gateway/static/root/assets/sessions-page-DV5WN8uk.js +0 -1
  261. package/dist/gateway/static/root/assets/settings-page-CfOBRbPX.js +0 -3
  262. package/dist/gateway/static/root/assets/share-preview-page-Di5Bzh4g.js +0 -2
  263. package/dist/gateway/static/root/assets/skills-page-D0H5Kaxg.js +0 -2
  264. package/dist/gateway/static/root/assets/url-aYn-Rj1C.js +0 -7
  265. package/dist/gateway/static/root/assets/vendor-codemirror-D0yxdRpg.js +0 -58
  266. package/dist/gateway/static/root/assets/voice-api-key-field-X2UfnHeq.js +0 -1
  267. package/dist/gateway/static/root/assets/workflows-page-BOPpO3NG.js +0 -27
@@ -64,6 +64,8 @@ export interface WorkflowAgentStep {
64
64
  label: string;
65
65
  detail?: string;
66
66
  status: WorkflowAgentStepStatus;
67
+ resultPreview?: string;
68
+ error?: string;
67
69
  startedAtMs?: number;
68
70
  durationMs?: number;
69
71
  }
@@ -134,6 +136,8 @@ export type SubagentProgressEvent = {
134
136
  toolCallId: string;
135
137
  toolName: string;
136
138
  isError: boolean;
139
+ resultPreview?: string;
140
+ error?: string;
137
141
  } | {
138
142
  type: 'iteration';
139
143
  count: number;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * /agent-edit — open the current chat as an agent profile editing session.
3
+ */
4
+ export declare function registerAgentEditCommand(): void;
@@ -0,0 +1,136 @@
1
+ import { init_agent_scope, normalizeAgentId, resolveAgentProfileDir } from "../agent/agent-scope.js";
2
+ import { init_agent_session_key, resolveAgentIdFromSessionKey } from "../routing/agent-session-key.js";
3
+ import { WORKSPACE_FILES, init_paths } from "../config/paths.js";
4
+ import { commandRegistry } from "./registry.js";
5
+ import { join } from "node:path";
6
+ import { readFile } from "node:fs/promises";
7
+ //#region src/chat-commands/agent-edit.ts
8
+ /**
9
+ * /agent-edit — open the current chat as an agent profile editing session.
10
+ */
11
+ init_agent_session_key();
12
+ init_agent_scope();
13
+ init_paths();
14
+ const PROFILE_FILE_NAMES = [
15
+ WORKSPACE_FILES.SOUL,
16
+ WORKSPACE_FILES.IDENTITY,
17
+ WORKSPACE_FILES.USER,
18
+ WORKSPACE_FILES.TOOLS,
19
+ WORKSPACE_FILES.AGENTS,
20
+ WORKSPACE_FILES.HEARTBEAT,
21
+ WORKSPACE_FILES.MEMORY,
22
+ WORKSPACE_FILES.BOOTSTRAP
23
+ ];
24
+ const DEFAULT_PREVIEW_CHARS = 900;
25
+ const MAX_PREVIEW_CHARS = 4e3;
26
+ function parseArgs(args) {
27
+ const trimmed = args.trim();
28
+ if (!trimmed) return { previewChars: DEFAULT_PREVIEW_CHARS };
29
+ const parts = trimmed.split(/\s+/);
30
+ let fileName;
31
+ let previewChars = DEFAULT_PREVIEW_CHARS;
32
+ for (const part of parts) {
33
+ const limitMatch = /^--limit=(\d+)$/.exec(part);
34
+ if (limitMatch) {
35
+ previewChars = Math.min(Number(limitMatch[1]), MAX_PREVIEW_CHARS);
36
+ continue;
37
+ }
38
+ if (!fileName) fileName = part;
39
+ }
40
+ return {
41
+ fileName,
42
+ previewChars
43
+ };
44
+ }
45
+ function normalizeProfileFileName(input) {
46
+ if (!input) return;
47
+ const basename = input.trim().replace(/\\/g, "/").split("/").pop();
48
+ return PROFILE_FILE_NAMES.find((name) => name.toLowerCase() === basename?.toLowerCase());
49
+ }
50
+ async function readPreview(path, limit) {
51
+ try {
52
+ const trimmed = (await readFile(path, "utf-8")).trimEnd();
53
+ if (trimmed.length <= limit) return {
54
+ content: trimmed,
55
+ missing: false
56
+ };
57
+ return {
58
+ content: `${trimmed.slice(0, limit)}\n\n… truncated, ask me to read the full file before editing …`,
59
+ missing: false
60
+ };
61
+ } catch {
62
+ return {
63
+ content: "",
64
+ missing: true
65
+ };
66
+ }
67
+ }
68
+ function buildEditInstructions(agentId, fileNames) {
69
+ const files = fileNames.map((name) => `\`${name}\``).join(", ");
70
+ return [
71
+ `You are editing agent \`${agentId}\`.`,
72
+ "",
73
+ "Tell me what to change, or say things like:",
74
+ "- “Refine `SOUL.md` to sound warmer and more concise.”",
75
+ "- “Update `IDENTITY.md` so this agent is focused on data analysis.”",
76
+ "- “Read `SOUL.md` first, propose changes, then write them back.”",
77
+ "",
78
+ `Editable profile files: ${files}.`,
79
+ "I can read and update these by bare filename with `read_file`, `edit_file`, and `write_file`."
80
+ ].join("\n");
81
+ }
82
+ const agentEditCommand = {
83
+ id: "agent.edit",
84
+ name: "agent-edit",
85
+ aliases: ["agentedit"],
86
+ description: "Show editable profile files for the current agent and prepare this chat for profile edits.",
87
+ category: "system",
88
+ scope: [
89
+ "global",
90
+ "private",
91
+ "group"
92
+ ],
93
+ acceptsArgs: true,
94
+ examples: [
95
+ "/agent-edit",
96
+ "/agent-edit SOUL.md",
97
+ "/agent-edit IDENTITY.md --limit=2000"
98
+ ],
99
+ handler: async (ctx, args) => {
100
+ await ctx.setTyping(true);
101
+ const { fileName: rawFileName, previewChars } = parseArgs(args);
102
+ const fileName = normalizeProfileFileName(rawFileName);
103
+ if (rawFileName && !fileName) return {
104
+ content: `⚠️ Unsupported profile file: \`${rawFileName}\`. Use one of: ${PROFILE_FILE_NAMES.map((name) => `\`${name}\``).join(", ")}.`,
105
+ success: false
106
+ };
107
+ const agentId = normalizeAgentId(resolveAgentIdFromSessionKey(ctx.sessionKey));
108
+ const profileDir = resolveAgentProfileDir(ctx.config, agentId);
109
+ const namesToShow = fileName ? [fileName] : [WORKSPACE_FILES.SOUL, WORKSPACE_FILES.IDENTITY];
110
+ const sections = [];
111
+ for (const name of namesToShow) {
112
+ const preview = await readPreview(join(profileDir, name), previewChars);
113
+ if (preview.missing) sections.push(`## ${name}\n_missing_`);
114
+ else sections.push(`## ${name}\n\`\`\`markdown\n${preview.content}\n\`\`\``);
115
+ }
116
+ return {
117
+ content: [
118
+ "🛠️ Agent editor mode",
119
+ "",
120
+ buildEditInstructions(agentId, PROFILE_FILE_NAMES),
121
+ "",
122
+ `Profile directory: \`${profileDir}\``,
123
+ "",
124
+ ...sections
125
+ ].join("\n"),
126
+ success: true
127
+ };
128
+ }
129
+ };
130
+ function registerAgentEditCommand() {
131
+ commandRegistry.register(agentEditCommand);
132
+ }
133
+ //#endregion
134
+ export { registerAgentEditCommand };
135
+
136
+ //# sourceMappingURL=agent-edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-edit.js","names":[],"sources":["../../../src/chat-commands/agent-edit.ts"],"sourcesContent":["/**\n * /agent-edit — open the current chat as an agent profile editing session.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport type { CommandContext, CommandDefinition } from './types.js';\nimport { commandRegistry } from './registry.js';\nimport { resolveAgentIdFromSessionKey } from '../routing/agent-session-key.js';\nimport { normalizeAgentId, resolveAgentProfileDir } from '../agent/agent-scope.js';\nimport { WORKSPACE_FILES } from '../config/paths.js';\n\nconst PROFILE_FILE_NAMES = [\n WORKSPACE_FILES.SOUL,\n WORKSPACE_FILES.IDENTITY,\n WORKSPACE_FILES.USER,\n WORKSPACE_FILES.TOOLS,\n WORKSPACE_FILES.AGENTS,\n WORKSPACE_FILES.HEARTBEAT,\n WORKSPACE_FILES.MEMORY,\n WORKSPACE_FILES.BOOTSTRAP,\n] as const;\n\nconst DEFAULT_PREVIEW_CHARS = 900;\nconst MAX_PREVIEW_CHARS = 4_000;\n\nfunction parseArgs(args: string): { fileName?: string; previewChars: number } {\n const trimmed = args.trim();\n if (!trimmed) {\n return { previewChars: DEFAULT_PREVIEW_CHARS };\n }\n\n const parts = trimmed.split(/\\s+/);\n let fileName: string | undefined;\n let previewChars = DEFAULT_PREVIEW_CHARS;\n\n for (const part of parts) {\n const limitMatch = /^--limit=(\\d+)$/.exec(part);\n if (limitMatch) {\n previewChars = Math.min(Number(limitMatch[1]), MAX_PREVIEW_CHARS);\n continue;\n }\n if (!fileName) {\n fileName = part;\n }\n }\n\n return { fileName, previewChars };\n}\n\nfunction normalizeProfileFileName(input: string | undefined): string | undefined {\n if (!input) {\n return undefined;\n }\n const basename = input.trim().replace(/\\\\/g, '/').split('/').pop();\n const matched = PROFILE_FILE_NAMES.find((name) => name.toLowerCase() === basename?.toLowerCase());\n return matched;\n}\n\nasync function readPreview(path: string, limit: number): Promise<{ content: string; missing: boolean }> {\n try {\n const content = await readFile(path, 'utf-8');\n const trimmed = content.trimEnd();\n if (trimmed.length <= limit) {\n return { content: trimmed, missing: false };\n }\n return {\n content: `${trimmed.slice(0, limit)}\\n\\n… truncated, ask me to read the full file before editing …`,\n missing: false,\n };\n } catch {\n return { content: '', missing: true };\n }\n}\n\nfunction buildEditInstructions(agentId: string, fileNames: readonly string[]): string {\n const files = fileNames.map((name) => `\\`${name}\\``).join(', ');\n return [\n `You are editing agent \\`${agentId}\\`.`,\n '',\n 'Tell me what to change, or say things like:',\n '- “Refine `SOUL.md` to sound warmer and more concise.”',\n '- “Update `IDENTITY.md` so this agent is focused on data analysis.”',\n '- “Read `SOUL.md` first, propose changes, then write them back.”',\n '',\n `Editable profile files: ${files}.`,\n 'I can read and update these by bare filename with `read_file`, `edit_file`, and `write_file`.',\n ].join('\\n');\n}\n\nconst agentEditCommand: CommandDefinition = {\n id: 'agent.edit',\n name: 'agent-edit',\n aliases: ['agentedit'],\n description: 'Show editable profile files for the current agent and prepare this chat for profile edits.',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/agent-edit', '/agent-edit SOUL.md', '/agent-edit IDENTITY.md --limit=2000'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n\n const { fileName: rawFileName, previewChars } = parseArgs(args);\n const fileName = normalizeProfileFileName(rawFileName);\n if (rawFileName && !fileName) {\n return {\n content: `⚠️ Unsupported profile file: \\`${rawFileName}\\`. Use one of: ${PROFILE_FILE_NAMES.map((name) => `\\`${name}\\``).join(', ')}.`,\n success: false,\n };\n }\n\n const agentId = normalizeAgentId(resolveAgentIdFromSessionKey(ctx.sessionKey));\n const profileDir = resolveAgentProfileDir(ctx.config, agentId);\n const namesToShow = fileName ? [fileName] : [WORKSPACE_FILES.SOUL, WORKSPACE_FILES.IDENTITY];\n\n const sections: string[] = [];\n for (const name of namesToShow) {\n const preview = await readPreview(join(profileDir, name), previewChars);\n if (preview.missing) {\n sections.push(`## ${name}\\n_missing_`);\n } else {\n sections.push(`## ${name}\\n\\`\\`\\`markdown\\n${preview.content}\\n\\`\\`\\``);\n }\n }\n\n return {\n content: [\n '🛠️ Agent editor mode',\n '',\n buildEditInstructions(agentId, PROFILE_FILE_NAMES),\n '',\n `Profile directory: \\`${profileDir}\\``,\n '',\n ...sections,\n ].join('\\n'),\n success: true,\n };\n },\n};\n\nexport function registerAgentEditCommand(): void {\n commandRegistry.register(agentEditCommand);\n}\n"],"mappings":"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"}
@@ -20,6 +20,7 @@ export { registerContextCommands } from './builtins/context.js';
20
20
  export { registerGoalCommand } from './builtins/goal.js';
21
21
  export { registerSubgoalCommand } from './builtins/subgoal.js';
22
22
  export { registerWorkflowCommands } from './builtins/workflow.js';
23
+ export { registerAgentEditCommand } from './agent-edit.js';
23
24
  /**
24
25
  * Initialize the command system with all built-in commands
25
26
  */
@@ -11,6 +11,7 @@ import { registerContextCommands } from "./builtins/context.js";
11
11
  import { registerGoalCommand } from "./builtins/goal.js";
12
12
  import { registerSubgoalCommand } from "./builtins/subgoal.js";
13
13
  import { registerWorkflowCommands } from "./builtins/workflow.js";
14
+ import { registerAgentEditCommand } from "./agent-edit.js";
14
15
  import { CommandContextImpl, createCommandContext } from "./context.js";
15
16
  //#region src/chat-commands/index.ts
16
17
  /**
@@ -33,8 +34,9 @@ function initializeCommands() {
33
34
  registerGoalCommand();
34
35
  registerSubgoalCommand();
35
36
  registerWorkflowCommands();
37
+ registerAgentEditCommand();
36
38
  }
37
39
  //#endregion
38
- export { CommandContextImpl, CommandRegistry, commandRegistry, createCommandContext, generateSessionKey, getRoutingInfo, getSessionDisplayName, initializeCommands, isValidSessionKey, normalizeTelegramCommandName, parseSessionKey, parseSlashCommand, registerConfigCommand, registerContextCommands, registerGoalCommand, registerModelCommands, registerSessionCommands, registerSubgoalCommand, registerSystemCommands, registerTTSCommands, registerThinkingCommands, registerWorkflowCommands };
40
+ export { CommandContextImpl, CommandRegistry, commandRegistry, createCommandContext, generateSessionKey, getRoutingInfo, getSessionDisplayName, initializeCommands, isValidSessionKey, normalizeTelegramCommandName, parseSessionKey, parseSlashCommand, registerAgentEditCommand, registerConfigCommand, registerContextCommands, registerGoalCommand, registerModelCommands, registerSessionCommands, registerSubgoalCommand, registerSystemCommands, registerTTSCommands, registerThinkingCommands, registerWorkflowCommands };
39
41
 
40
42
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/chat-commands/index.ts"],"sourcesContent":["/**\n * Unified Command System\n *\n * Provides a platform-agnostic command system that works across\n * Telegram, Feishu, Web UI, CLI, and other channel surfaces.\n */\n\n// Built-in Commands (import first to avoid circular deps)\nimport { registerSessionCommands } from './builtins/session.js';\nimport { registerModelCommands } from './builtins/model.js';\nimport { registerSystemCommands } from './builtins/system.js';\nimport { registerTTSCommands } from './builtins/tts.js';\nimport { registerThinkingCommands } from './builtins/thinking.js';\nimport { registerConfigCommand } from './builtins/config.js';\nimport { registerContextCommands } from './builtins/context.js';\nimport { registerGoalCommand } from './builtins/goal.js';\nimport { registerSubgoalCommand } from './builtins/subgoal.js';\nimport { registerWorkflowCommands } from './builtins/workflow.js';\n\n// Types\nexport type {\n MessageSource,\n UnifiedMessage,\n PlatformMetadata,\n MessageAttachment,\n CommandDefinition,\n CommandCategory,\n CommandScope,\n CommandHandler,\n CommandResult,\n CommandContext,\n ReplyOptions,\n UIComponent,\n ButtonGroup,\n SelectMenu,\n ModelPicker,\n UsageDisplay,\n SessionList,\n TextInput,\n ProviderInfo,\n ModelInfo,\n UsageStats,\n SessionInfo,\n PlatformFeature,\n ChannelAdapter,\n ReplyPayload,\n} from './types.js';\n\n// Session Key\nexport {\n generateSessionKey,\n parseSessionKey,\n isValidSessionKey,\n getSessionDisplayName,\n getRoutingInfo,\n type SessionKeyContext,\n} from './session-key.js';\n\n// Command parsing helpers\nexport { normalizeTelegramCommandName, parseSlashCommand } from './command-parse.js';\n\n// Registry\nexport { CommandRegistry, commandRegistry } from './registry.js';\nexport type { CommandRegistry as CommandRegistryType } from './types.js';\n\n// Context\nexport { CommandContextImpl, createCommandContext } from './context.js';\n\n// Built-in Commands\nexport { registerSessionCommands } from './builtins/session.js';\nexport { registerModelCommands } from './builtins/model.js';\nexport { registerSystemCommands } from './builtins/system.js';\nexport { registerTTSCommands } from './builtins/tts.js';\nexport { registerThinkingCommands } from './builtins/thinking.js';\nexport { registerConfigCommand } from './builtins/config.js';\nexport { registerContextCommands } from './builtins/context.js';\nexport { registerGoalCommand } from './builtins/goal.js';\nexport { registerSubgoalCommand } from './builtins/subgoal.js';\nexport { registerWorkflowCommands } from './builtins/workflow.js';\n\n/**\n * Initialize the command system with all built-in commands\n */\nexport function initializeCommands(): void {\n registerSessionCommands();\n registerModelCommands();\n registerSystemCommands();\n registerConfigCommand();\n registerContextCommands();\n registerTTSCommands();\n registerThinkingCommands();\n registerGoalCommand();\n registerSubgoalCommand();\n registerWorkflowCommands();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmFA,SAAgB,qBAA2B;AACzC,0BAAyB;AACzB,wBAAuB;AACvB,yBAAwB;AACxB,wBAAuB;AACvB,0BAAyB;AACzB,sBAAqB;AACrB,2BAA0B;AAC1B,sBAAqB;AACrB,yBAAwB;AACxB,2BAA0B"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/chat-commands/index.ts"],"sourcesContent":["/**\n * Unified Command System\n *\n * Provides a platform-agnostic command system that works across\n * Telegram, Feishu, Web UI, CLI, and other channel surfaces.\n */\n\n// Built-in Commands (import first to avoid circular deps)\nimport { registerSessionCommands } from './builtins/session.js';\nimport { registerModelCommands } from './builtins/model.js';\nimport { registerSystemCommands } from './builtins/system.js';\nimport { registerTTSCommands } from './builtins/tts.js';\nimport { registerThinkingCommands } from './builtins/thinking.js';\nimport { registerConfigCommand } from './builtins/config.js';\nimport { registerContextCommands } from './builtins/context.js';\nimport { registerGoalCommand } from './builtins/goal.js';\nimport { registerSubgoalCommand } from './builtins/subgoal.js';\nimport { registerWorkflowCommands } from './builtins/workflow.js';\nimport { registerAgentEditCommand } from './agent-edit.js';\n\n// Types\nexport type {\n MessageSource,\n UnifiedMessage,\n PlatformMetadata,\n MessageAttachment,\n CommandDefinition,\n CommandCategory,\n CommandScope,\n CommandHandler,\n CommandResult,\n CommandContext,\n ReplyOptions,\n UIComponent,\n ButtonGroup,\n SelectMenu,\n ModelPicker,\n UsageDisplay,\n SessionList,\n TextInput,\n ProviderInfo,\n ModelInfo,\n UsageStats,\n SessionInfo,\n PlatformFeature,\n ChannelAdapter,\n ReplyPayload,\n} from './types.js';\n\n// Session Key\nexport {\n generateSessionKey,\n parseSessionKey,\n isValidSessionKey,\n getSessionDisplayName,\n getRoutingInfo,\n type SessionKeyContext,\n} from './session-key.js';\n\n// Command parsing helpers\nexport { normalizeTelegramCommandName, parseSlashCommand } from './command-parse.js';\n\n// Registry\nexport { CommandRegistry, commandRegistry } from './registry.js';\nexport type { CommandRegistry as CommandRegistryType } from './types.js';\n\n// Context\nexport { CommandContextImpl, createCommandContext } from './context.js';\n\n// Built-in Commands\nexport { registerSessionCommands } from './builtins/session.js';\nexport { registerModelCommands } from './builtins/model.js';\nexport { registerSystemCommands } from './builtins/system.js';\nexport { registerTTSCommands } from './builtins/tts.js';\nexport { registerThinkingCommands } from './builtins/thinking.js';\nexport { registerConfigCommand } from './builtins/config.js';\nexport { registerContextCommands } from './builtins/context.js';\nexport { registerGoalCommand } from './builtins/goal.js';\nexport { registerSubgoalCommand } from './builtins/subgoal.js';\nexport { registerWorkflowCommands } from './builtins/workflow.js';\nexport { registerAgentEditCommand } from './agent-edit.js';\n\n/**\n * Initialize the command system with all built-in commands\n */\nexport function initializeCommands(): void {\n registerSessionCommands();\n registerModelCommands();\n registerSystemCommands();\n registerConfigCommand();\n registerContextCommands();\n registerTTSCommands();\n registerThinkingCommands();\n registerGoalCommand();\n registerSubgoalCommand();\n registerWorkflowCommands();\n registerAgentEditCommand();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,qBAA2B;AACzC,0BAAyB;AACzB,wBAAuB;AACvB,yBAAwB;AACxB,wBAAuB;AACvB,0BAAyB;AACzB,sBAAqB;AACrB,2BAA0B;AAC1B,sBAAqB;AACrB,yBAAwB;AACxB,2BAA0B;AAC1B,2BAA0B"}
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { version } from "../../package.js";
3
3
  import "./cli-log-level-preset.js";
4
+ import { ensureXopcCliOnPath } from "../infra/path-env.js";
4
5
  import { formatRootHelp } from "./command-manifest.js";
5
6
  //#region src/cli/bin.ts
7
+ ensureXopcCliOnPath();
6
8
  function printRootHelp() {
7
9
  console.log(formatRootHelp());
8
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","names":["pkg.version"],"sources":["../../../src/cli/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI entry: log-level preset must run before any module that initializes the logger.\n * (Bundlers may reorder imports in `index.ts`; this file stays dependency-minimal.)\n */\nimport './cli-log-level-preset.js';\n\nimport pkg from '../../package.json' with { type: 'json' };\nimport { formatRootHelp } from './command-manifest.js';\n\nfunction printRootHelp(): void {\n console.log(formatRootHelp());\n}\n\nconst rootArgs = process.argv.slice(2).filter((arg) => arg !== '--');\nif (rootArgs.length === 1 && (rootArgs[0] === '--version' || rootArgs[0] === '-V')) {\n console.log(pkg.version);\n process.exit(0);\n}\nif (rootArgs.length === 1 && (rootArgs[0] === '--help' || rootArgs[0] === '-h' || rootArgs[0] === 'help')) {\n printRootHelp();\n process.exit(0);\n}\n\nconst filteredArgv = process.argv.filter((arg, index) => {\n if (arg !== '--') return true;\n return index < 2;\n});\n\nconst { tryRunGatewayRunFastPath } = await import('./gateway-run-fast-path.js');\nif (await tryRunGatewayRunFastPath(filteredArgv)) {\n process.exit(typeof process.exitCode === 'number' ? process.exitCode : 0);\n}\n\nconst { runCli } = await import('./index.js');\nawait runCli(process.argv);\n"],"mappings":";;;;;AAUA,SAAS,gBAAsB;AAC7B,SAAQ,IAAI,gBAAgB,CAAC;;AAG/B,MAAM,WAAW,QAAQ,KAAK,MAAM,EAAE,CAAC,QAAQ,QAAQ,QAAQ,KAAK;AACpE,IAAI,SAAS,WAAW,MAAM,SAAS,OAAO,eAAe,SAAS,OAAO,OAAO;AAClF,SAAQ,IAAIA,QAAY;AACxB,SAAQ,KAAK,EAAE;;AAEjB,IAAI,SAAS,WAAW,MAAM,SAAS,OAAO,YAAY,SAAS,OAAO,QAAQ,SAAS,OAAO,SAAS;AACzG,gBAAe;AACf,SAAQ,KAAK,EAAE;;AAGjB,MAAM,eAAe,QAAQ,KAAK,QAAQ,KAAK,UAAU;AACvD,KAAI,QAAQ,KAAM,QAAO;AACzB,QAAO,QAAQ;EACf;AAEF,MAAM,EAAE,6BAA6B,MAAM,OAAO;AAClD,IAAI,MAAM,yBAAyB,aAAa,CAC9C,SAAQ,KAAK,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW,EAAE;AAG3E,MAAM,EAAE,WAAW,MAAM,OAAO;AAChC,MAAM,OAAO,QAAQ,KAAK"}
1
+ {"version":3,"file":"bin.js","names":["pkg.version"],"sources":["../../../src/cli/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI entry: log-level preset must run before any module that initializes the logger.\n * (Bundlers may reorder imports in `index.ts`; this file stays dependency-minimal.)\n */\nimport './cli-log-level-preset.js';\n\nimport pkg from '../../package.json' with { type: 'json' };\nimport { ensureXopcCliOnPath } from '../infra/path-env.js';\nimport { formatRootHelp } from './command-manifest.js';\n\nensureXopcCliOnPath();\n\nfunction printRootHelp(): void {\n console.log(formatRootHelp());\n}\n\nconst rootArgs = process.argv.slice(2).filter((arg) => arg !== '--');\nif (rootArgs.length === 1 && (rootArgs[0] === '--version' || rootArgs[0] === '-V')) {\n console.log(pkg.version);\n process.exit(0);\n}\nif (rootArgs.length === 1 && (rootArgs[0] === '--help' || rootArgs[0] === '-h' || rootArgs[0] === 'help')) {\n printRootHelp();\n process.exit(0);\n}\n\nconst filteredArgv = process.argv.filter((arg, index) => {\n if (arg !== '--') return true;\n return index < 2;\n});\n\nconst { tryRunGatewayRunFastPath } = await import('./gateway-run-fast-path.js');\nif (await tryRunGatewayRunFastPath(filteredArgv)) {\n process.exit(typeof process.exitCode === 'number' ? process.exitCode : 0);\n}\n\nconst { runCli } = await import('./index.js');\nawait runCli(process.argv);\n"],"mappings":";;;;;;AAWA,qBAAqB;AAErB,SAAS,gBAAsB;AAC7B,SAAQ,IAAI,gBAAgB,CAAC;;AAG/B,MAAM,WAAW,QAAQ,KAAK,MAAM,EAAE,CAAC,QAAQ,QAAQ,QAAQ,KAAK;AACpE,IAAI,SAAS,WAAW,MAAM,SAAS,OAAO,eAAe,SAAS,OAAO,OAAO;AAClF,SAAQ,IAAIA,QAAY;AACxB,SAAQ,KAAK,EAAE;;AAEjB,IAAI,SAAS,WAAW,MAAM,SAAS,OAAO,YAAY,SAAS,OAAO,QAAQ,SAAS,OAAO,SAAS;AACzG,gBAAe;AACf,SAAQ,KAAK,EAAE;;AAGjB,MAAM,eAAe,QAAQ,KAAK,QAAQ,KAAK,UAAU;AACvD,KAAI,QAAQ,KAAM,QAAO;AACzB,QAAO,QAAQ;EACf;AAEF,MAAM,EAAE,6BAA6B,MAAM,OAAO;AAClD,IAAI,MAAM,yBAAyB,aAAa,CAC9C,SAAQ,KAAK,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW,EAAE;AAG3E,MAAM,EAAE,WAAW,MAAM,OAAO;AAChC,MAAM,OAAO,QAAQ,KAAK"}
@@ -6,6 +6,7 @@ function createCronCommand(_ctx) {
6
6
  const cmd = new Command("cron").description("Manage scheduled tasks").addHelpText("after", formatExamples([
7
7
  "xopc cron list # List all tasks",
8
8
  "xopc cron add --schedule \"0 9 * * *\" --message \"Good morning\"",
9
+ "xopc cron add --schedule \"0 17 * * 5\" --workflow weekly_review --goal \"Weekly review\"",
9
10
  "xopc cron enable <job-id> # Enable a task",
10
11
  "xopc cron disable <job-id> # Disable a task",
11
12
  "xopc cron run <job-id> # Run a task now",
@@ -29,12 +30,49 @@ function createCronCommand(_ctx) {
29
30
  }
30
31
  });
31
32
  }));
32
- cmd.addCommand(new Command("add").description("Add a scheduled task").option("--name <text>", "Task name").option("--schedule <cron>", "Cron expression (e.g., \"0 9 * * *\")").option("--message <text>", "Message to send").action(async (options) => {
33
- if (!options.schedule || !options.message) {
34
- console.error("Error: --schedule and --message are required");
33
+ cmd.addCommand(new Command("add").description("Add a scheduled task").option("--name <text>", "Task name").option("--schedule <cron>", "Cron expression (e.g., \"0 9 * * *\")").option("--message <text>", "Message to send (system event)").option("--workflow <id>", "Workflow definition id (direct workflow run)").option("--goal <text>", "Optional workflow goal").option("--input-json <json>", "Workflow input payload as JSON object").option("--agent-id <id>", "Agent profile for workflow or isolated jobs").option("--no-wait", "Start workflow and return without waiting for completion").option("--channel <name>", "Delivery channel (e.g. telegram)").option("--to <chatId>", "Delivery recipient chat id").action(async (options) => {
34
+ const hasWorkflow = Boolean(options.workflow?.trim());
35
+ const hasMessage = Boolean(options.message?.trim());
36
+ if (!options.schedule || !hasWorkflow && !hasMessage || hasWorkflow && hasMessage) {
37
+ console.error("Error: --schedule is required; provide exactly one of --message or --workflow");
35
38
  process.exit(1);
36
39
  }
37
40
  await withCronService(async (cronService) => {
41
+ if (hasWorkflow) {
42
+ const { DEFAULT_WORKFLOW_CRON_WAIT_MS } = await import("../../cron/workflow-run-completion.js");
43
+ let inputEnvelope;
44
+ if (options.inputJson) try {
45
+ inputEnvelope = { payload: JSON.parse(options.inputJson) };
46
+ } catch {
47
+ console.error("Error: --input-json must be valid JSON");
48
+ process.exit(1);
49
+ }
50
+ const agentId = options.agentId?.trim() || void 0;
51
+ const delivery = options.channel?.trim() && options.to?.trim() ? {
52
+ mode: "direct",
53
+ channel: options.channel.trim(),
54
+ to: options.to.trim()
55
+ } : void 0;
56
+ const result = await cronService.addJob(options.schedule, {
57
+ name: options.name,
58
+ sessionTarget: "isolated",
59
+ timeout: DEFAULT_WORKFLOW_CRON_WAIT_MS,
60
+ ...agentId ? { agentId } : {},
61
+ delivery,
62
+ payload: {
63
+ kind: "workflowRun",
64
+ definitionId: options.workflow.trim(),
65
+ ...options.goal?.trim() ? { goal: options.goal.trim() } : {},
66
+ ...inputEnvelope ? { inputEnvelope } : {},
67
+ ...agentId ? { agentId } : {},
68
+ ...options.noWait ? { waitForCompletion: false } : {}
69
+ }
70
+ });
71
+ console.log(`✅ Added workflow job ${result.id}`);
72
+ console.log(` Schedule: ${result.schedule}`);
73
+ console.log(` Workflow: ${options.workflow.trim()}`);
74
+ return;
75
+ }
38
76
  const result = await cronService.addJob(options.schedule, {
39
77
  name: options.name,
40
78
  payload: {
@@ -99,6 +137,7 @@ register({
99
137
  examples: [
100
138
  "xopc cron list",
101
139
  "xopc cron add --schedule \"0 9 * * *\" --message \"Hello\"",
140
+ "xopc cron add --schedule \"0 17 * * 5\" --workflow weekly_review",
102
141
  "xopc cron enable abc12345",
103
142
  "xopc cron run abc12345"
104
143
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"cron.js","names":[],"sources":["../../../../src/cli/commands/cron.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport { withCronService } from './cron-cli.js';\n\nfunction createCronCommand(_ctx: CLIContext): Command {\n const cmd = new Command('cron')\n .description('Manage scheduled tasks')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc cron list # List all tasks',\n 'xopc cron add --schedule \"0 9 * * *\" --message \"Good morning\"',\n 'xopc cron enable <job-id> # Enable a task',\n 'xopc cron disable <job-id> # Disable a task',\n 'xopc cron run <job-id> # Run a task now',\n 'xopc cron remove <job-id> # Remove a task',\n ]),\n );\n\n cmd.addCommand(\n new Command('list')\n .description('List all scheduled tasks')\n .action(async () => {\n await withCronService(async (cronService) => {\n const jobs = await cronService.listJobs();\n\n if (jobs.length === 0) {\n console.log('No scheduled tasks.');\n return;\n }\n\n console.log('Scheduled Tasks:\\n');\n const { getCronPayloadText } = await import('../../cron/job-content.js');\n for (const job of jobs) {\n const state = job.enabled ? 'enabled ' : 'disabled';\n console.log(` ${job.id} [${state}] - ${job.schedule}`);\n console.log(` ${getCronPayloadText({ payload: job.payload })}`);\n console.log(` Next: ${job.next_run || 'N/A'}`);\n console.log();\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('add')\n .description('Add a scheduled task')\n .option('--name <text>', 'Task name')\n .option('--schedule <cron>', 'Cron expression (e.g., \"0 9 * * *\")')\n .option('--message <text>', 'Message to send')\n .action(async (options) => {\n if (!options.schedule || !options.message) {\n console.error('Error: --schedule and --message are required');\n process.exit(1);\n }\n\n await withCronService(async (cronService) => {\n const result = await cronService.addJob(options.schedule, {\n name: options.name,\n payload: { kind: 'systemEvent', text: options.message },\n });\n\n console.log(`✅ Added job ${result.id}`);\n console.log(` Schedule: ${result.schedule}`);\n });\n }),\n );\n\n cmd.addCommand(\n new Command('remove')\n .description('Remove a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.removeJob(id);\n if (success) {\n console.log(`✅ Removed job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('enable')\n .description('Enable a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.toggleJob(id, true);\n if (success) {\n console.log(`✅ Enabled job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('disable')\n .description('Disable a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.toggleJob(id, false);\n if (success) {\n console.log(`✅ Disabled job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n const runNowAction = async (id: string) => {\n await withCronService(async (cronService) => {\n try {\n await cronService.runJobNow(id);\n console.log(`✅ Triggered job ${id}`);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(message);\n process.exit(1);\n }\n });\n };\n\n cmd.addCommand(\n new Command('run')\n .description('Run a scheduled task immediately')\n .argument('<id>', 'Job ID')\n .action(runNowAction),\n );\n\n cmd.addCommand(\n new Command('trigger')\n .description('Alias for `cron run`')\n .argument('<id>', 'Job ID')\n .action(runNowAction),\n );\n\n return cmd;\n}\n\nregister({\n id: 'cron',\n name: 'cron',\n description: 'Manage scheduled tasks',\n factory: createCronCommand,\n metadata: {\n category: 'utility',\n examples: [\n 'xopc cron list',\n 'xopc cron add --schedule \"0 9 * * *\" --message \"Hello\"',\n 'xopc cron enable abc12345',\n 'xopc cron run abc12345',\n ],\n },\n});\n\nexport { createCronCommand };\n"],"mappings":"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"}
1
+ {"version":3,"file":"cron.js","names":[],"sources":["../../../../src/cli/commands/cron.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport { withCronService } from './cron-cli.js';\n\nfunction createCronCommand(_ctx: CLIContext): Command {\n const cmd = new Command('cron')\n .description('Manage scheduled tasks')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc cron list # List all tasks',\n 'xopc cron add --schedule \"0 9 * * *\" --message \"Good morning\"',\n 'xopc cron add --schedule \"0 17 * * 5\" --workflow weekly_review --goal \"Weekly review\"',\n 'xopc cron enable <job-id> # Enable a task',\n 'xopc cron disable <job-id> # Disable a task',\n 'xopc cron run <job-id> # Run a task now',\n 'xopc cron remove <job-id> # Remove a task',\n ]),\n );\n\n cmd.addCommand(\n new Command('list')\n .description('List all scheduled tasks')\n .action(async () => {\n await withCronService(async (cronService) => {\n const jobs = await cronService.listJobs();\n\n if (jobs.length === 0) {\n console.log('No scheduled tasks.');\n return;\n }\n\n console.log('Scheduled Tasks:\\n');\n const { getCronPayloadText } = await import('../../cron/job-content.js');\n for (const job of jobs) {\n const state = job.enabled ? 'enabled ' : 'disabled';\n console.log(` ${job.id} [${state}] - ${job.schedule}`);\n console.log(` ${getCronPayloadText({ payload: job.payload })}`);\n console.log(` Next: ${job.next_run || 'N/A'}`);\n console.log();\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('add')\n .description('Add a scheduled task')\n .option('--name <text>', 'Task name')\n .option('--schedule <cron>', 'Cron expression (e.g., \"0 9 * * *\")')\n .option('--message <text>', 'Message to send (system event)')\n .option('--workflow <id>', 'Workflow definition id (direct workflow run)')\n .option('--goal <text>', 'Optional workflow goal')\n .option('--input-json <json>', 'Workflow input payload as JSON object')\n .option('--agent-id <id>', 'Agent profile for workflow or isolated jobs')\n .option('--no-wait', 'Start workflow and return without waiting for completion')\n .option('--channel <name>', 'Delivery channel (e.g. telegram)')\n .option('--to <chatId>', 'Delivery recipient chat id')\n .action(async (options) => {\n const hasWorkflow = Boolean(options.workflow?.trim());\n const hasMessage = Boolean(options.message?.trim());\n if (!options.schedule || (!hasWorkflow && !hasMessage) || (hasWorkflow && hasMessage)) {\n console.error(\n 'Error: --schedule is required; provide exactly one of --message or --workflow',\n );\n process.exit(1);\n }\n\n await withCronService(async (cronService) => {\n if (hasWorkflow) {\n const { DEFAULT_WORKFLOW_CRON_WAIT_MS } = await import(\n '../../cron/workflow-run-completion.js'\n );\n let inputEnvelope: { payload: unknown } | undefined;\n if (options.inputJson) {\n try {\n inputEnvelope = { payload: JSON.parse(options.inputJson) as unknown };\n } catch {\n console.error('Error: --input-json must be valid JSON');\n process.exit(1);\n }\n }\n const agentId = options.agentId?.trim() || undefined;\n const delivery =\n options.channel?.trim() && options.to?.trim()\n ? {\n mode: 'direct' as const,\n channel: options.channel.trim(),\n to: options.to.trim(),\n }\n : undefined;\n const result = await cronService.addJob(options.schedule, {\n name: options.name,\n sessionTarget: 'isolated',\n timeout: DEFAULT_WORKFLOW_CRON_WAIT_MS,\n ...(agentId ? { agentId } : {}),\n delivery,\n payload: {\n kind: 'workflowRun',\n definitionId: options.workflow.trim(),\n ...(options.goal?.trim() ? { goal: options.goal.trim() } : {}),\n ...(inputEnvelope ? { inputEnvelope } : {}),\n ...(agentId ? { agentId } : {}),\n ...(options.noWait ? { waitForCompletion: false } : {}),\n },\n });\n console.log(`✅ Added workflow job ${result.id}`);\n console.log(` Schedule: ${result.schedule}`);\n console.log(` Workflow: ${options.workflow.trim()}`);\n return;\n }\n\n const result = await cronService.addJob(options.schedule, {\n name: options.name,\n payload: { kind: 'systemEvent', text: options.message },\n });\n\n console.log(`✅ Added job ${result.id}`);\n console.log(` Schedule: ${result.schedule}`);\n });\n }),\n );\n\n cmd.addCommand(\n new Command('remove')\n .description('Remove a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.removeJob(id);\n if (success) {\n console.log(`✅ Removed job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('enable')\n .description('Enable a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.toggleJob(id, true);\n if (success) {\n console.log(`✅ Enabled job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('disable')\n .description('Disable a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.toggleJob(id, false);\n if (success) {\n console.log(`✅ Disabled job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n const runNowAction = async (id: string) => {\n await withCronService(async (cronService) => {\n try {\n await cronService.runJobNow(id);\n console.log(`✅ Triggered job ${id}`);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(message);\n process.exit(1);\n }\n });\n };\n\n cmd.addCommand(\n new Command('run')\n .description('Run a scheduled task immediately')\n .argument('<id>', 'Job ID')\n .action(runNowAction),\n );\n\n cmd.addCommand(\n new Command('trigger')\n .description('Alias for `cron run`')\n .argument('<id>', 'Job ID')\n .action(runNowAction),\n );\n\n return cmd;\n}\n\nregister({\n id: 'cron',\n name: 'cron',\n description: 'Manage scheduled tasks',\n factory: createCronCommand,\n metadata: {\n category: 'utility',\n examples: [\n 'xopc cron list',\n 'xopc cron add --schedule \"0 9 * * *\" --message \"Hello\"',\n 'xopc cron add --schedule \"0 17 * * 5\" --workflow weekly_review',\n 'xopc cron enable abc12345',\n 'xopc cron run abc12345',\n ],\n },\n});\n\nexport { createCronCommand };\n"],"mappings":"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"}
@@ -1,14 +1,42 @@
1
- import { init_agent_scope, resolveDefaultAgentId } from "../../../../agent/agent-scope.js";
1
+ import { init_agent_scope, listAgentEntries, resolveDefaultAgentId } from "../../../../agent/agent-scope.js";
2
+ import { init_session_key, parseSessionKey } from "../../../../routing/session-key.js";
2
3
  import { init_transcript_paths, resolveSessionFilePath } from "../../../../session/parity/transcript-paths.js";
3
4
  import { FILENAMES, init_paths, resolveSessionsDir } from "../../../../config/paths.js";
4
5
  import { init_loader, loadConfig } from "../../../../config/loader.js";
5
- import { join } from "node:path";
6
- import { existsSync, readFileSync } from "node:fs";
6
+ import { basename, join } from "node:path";
7
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
7
8
  //#region src/cli/commands/doctor/checks/session-integrity.ts
8
9
  init_agent_scope();
9
10
  init_loader();
10
11
  init_paths();
12
+ init_session_key();
11
13
  init_transcript_paths();
14
+ function discoverSessionMapLocations(config, stateDir) {
15
+ const agentIds = new Set([resolveDefaultAgentId(config), ...listAgentEntries(config).map((agent) => agent.id)]);
16
+ const agentsRoot = join(stateDir, "agents");
17
+ if (existsSync(agentsRoot)) {
18
+ for (const entry of readdirSync(agentsRoot, { withFileTypes: true })) if (entry.isDirectory()) agentIds.add(entry.name);
19
+ }
20
+ const seenMapPaths = /* @__PURE__ */ new Set();
21
+ const locations = [];
22
+ for (const agentId of agentIds) {
23
+ const sessionsDir = resolveSessionsDir(config, agentId);
24
+ const mapPath = join(sessionsDir, FILENAMES.SESSIONS_MAP);
25
+ if (seenMapPaths.has(mapPath)) continue;
26
+ seenMapPaths.add(mapPath);
27
+ locations.push({
28
+ agentId,
29
+ sessionsDir,
30
+ mapPath
31
+ });
32
+ }
33
+ return locations;
34
+ }
35
+ function readSessionMap(mapPath) {
36
+ const parsed = JSON.parse(readFileSync(mapPath, "utf-8"));
37
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("invalid sessions map");
38
+ return parsed;
39
+ }
12
40
  async function checkSessionIntegrity(ctx) {
13
41
  if (!ctx.options.deep) return {
14
42
  id: "session-integrity",
@@ -36,73 +64,68 @@ async function checkSessionIntegrity(ctx) {
36
64
  hints: []
37
65
  };
38
66
  }
39
- const agentId = resolveDefaultAgentId(config);
40
- const sessionsDir = resolveSessionsDir(config, agentId);
41
- const mapPath = join(sessionsDir, FILENAMES.SESSIONS_MAP);
42
- if (!existsSync(sessionsDir)) return {
43
- id: "session-integrity",
44
- label: "Sessions",
45
- status: "warn",
46
- message: "Sessions directory is missing.",
47
- hints: [sessionsDir]
48
- };
49
- if (!existsSync(mapPath)) return {
50
- id: "session-integrity",
51
- label: "Sessions",
52
- status: "warn",
53
- message: "`sessions.json` is missing.",
54
- hints: [mapPath]
55
- };
56
- let map;
57
- try {
58
- const parsed = JSON.parse(readFileSync(mapPath, "utf-8"));
59
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("invalid");
60
- map = parsed;
61
- } catch {
62
- return {
63
- id: "session-integrity",
64
- label: "Sessions",
65
- status: "warn",
66
- message: "`sessions.json` is not valid JSON.",
67
- hints: [mapPath]
68
- };
69
- }
70
- const sample = [...Object.keys(map)].sort((a, b) => {
71
- const ta = map[a]?.updatedAt ?? 0;
72
- return (map[b]?.updatedAt ?? 0) - ta;
73
- }).slice(0, 20);
74
- if (sample.length === 0) return {
75
- id: "session-integrity",
76
- label: "Sessions",
77
- status: "pass",
78
- message: "`sessions.json` is valid; no sessions to sample.",
79
- hints: []
80
- };
81
- const missing = [];
82
- for (const sessionKey of sample) {
83
- const entry = map[sessionKey];
84
- if (!entry?.sessionId) {
85
- missing.push(sessionKey);
67
+ const locations = discoverSessionMapLocations(config, ctx.stateDir);
68
+ const missingMaps = [];
69
+ const invalidMaps = [];
70
+ const missingTranscripts = [];
71
+ const agentMismatches = [];
72
+ const orphanTranscripts = [];
73
+ let sessionCount = 0;
74
+ for (const location of locations) {
75
+ if (!existsSync(location.sessionsDir)) continue;
76
+ if (!existsSync(location.mapPath)) {
77
+ missingMaps.push(location.mapPath);
86
78
  continue;
87
79
  }
80
+ let map;
88
81
  try {
89
- if (!existsSync(resolveSessionFilePath(entry.sessionId, entry, { sessionsDir }))) missing.push(sessionKey);
82
+ map = readSessionMap(location.mapPath);
90
83
  } catch {
91
- missing.push(sessionKey);
84
+ invalidMaps.push(location.mapPath);
85
+ continue;
86
+ }
87
+ const referencedTranscriptFiles = /* @__PURE__ */ new Set();
88
+ for (const [sessionKey, entry] of Object.entries(map)) {
89
+ sessionCount++;
90
+ const parsed = parseSessionKey(sessionKey);
91
+ if (parsed && parsed.agentId !== location.agentId) agentMismatches.push(`${sessionKey} in agent ${location.agentId}`);
92
+ if (!entry?.sessionId) {
93
+ missingTranscripts.push(sessionKey);
94
+ continue;
95
+ }
96
+ try {
97
+ const transcriptPath = resolveSessionFilePath(entry.sessionId, entry, { sessionsDir: location.sessionsDir });
98
+ referencedTranscriptFiles.add(basename(transcriptPath));
99
+ if (!existsSync(transcriptPath)) missingTranscripts.push(sessionKey);
100
+ } catch {
101
+ missingTranscripts.push(sessionKey);
102
+ }
103
+ }
104
+ for (const file of readdirSync(location.sessionsDir, { withFileTypes: true })) {
105
+ if (!file.isFile() || !file.name.endsWith(".jsonl") || file.name.includes(".checkpoint.")) continue;
106
+ if (!referencedTranscriptFiles.has(file.name)) orphanTranscripts.push(join(location.sessionsDir, file.name));
92
107
  }
93
108
  }
94
- if (missing.length > 0) return {
109
+ const hints = [
110
+ ...missingMaps.slice(0, 3).map((path) => `Missing sessions.json: ${path}`),
111
+ ...invalidMaps.slice(0, 3).map((path) => `Invalid sessions.json: ${path}`),
112
+ ...agentMismatches.slice(0, 3).map((item) => `Session key agent mismatch: ${item}`),
113
+ ...missingTranscripts.slice(0, 3).map((key) => `Missing transcript for: ${key}`),
114
+ ...orphanTranscripts.slice(0, 3).map((path) => `Orphan transcript: ${path}`)
115
+ ];
116
+ const warningCount = missingMaps.length + invalidMaps.length + missingTranscripts.length + agentMismatches.length + orphanTranscripts.length;
117
+ if (warningCount > 0) return {
95
118
  id: "session-integrity",
96
119
  label: "Sessions",
97
120
  status: "warn",
98
- message: `${missing.length} of ${sample.length} sampled session transcripts are missing on disk.`,
99
- hints: missing.slice(0, 5).map((k) => `Missing transcript for: ${k}`)
121
+ message: `Scanned ${locations.length} agent session dir(s), ${sessionCount} session(s); found ${warningCount} issue(s).`,
122
+ hints
100
123
  };
101
124
  return {
102
125
  id: "session-integrity",
103
126
  label: "Sessions",
104
127
  status: "pass",
105
- message: `Sampled ${sample.length} recent session(s); JSONL transcripts are present.`,
128
+ message: `Scanned ${locations.length} agent session dir(s), ${sessionCount} session(s); session maps and JSONL transcripts are consistent.`,
106
129
  hints: []
107
130
  };
108
131
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-integrity.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/session-integrity.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { resolveDefaultAgentId } from '../../../../agent/agent-scope.js';\nimport { loadConfig } from '../../../../config/loader.js';\nimport { FILENAMES, resolveSessionsDir } from '../../../../config/paths.js';\nimport { resolveSessionFilePath } from '../../../../session/parity/transcript-paths.js';\nimport type { XopcSessionDiskEntry } from '../../../../session/parity/xopc-session-disk-entry.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkSessionIntegrity(ctx: DoctorContext): Promise<CheckResult> {\n if (!ctx.options.deep) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'skip',\n message: 'Deep mode off; session scan skipped.',\n hints: ['Run: xopc doctor --deep'],\n };\n }\n\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let config;\n try {\n config = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const agentId = resolveDefaultAgentId(config);\n const sessionsDir = resolveSessionsDir(config, agentId);\n const mapPath = join(sessionsDir, FILENAMES.SESSIONS_MAP);\n\n if (!existsSync(sessionsDir)) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'warn',\n message: 'Sessions directory is missing.',\n hints: [sessionsDir],\n };\n }\n\n if (!existsSync(mapPath)) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'warn',\n message: '`sessions.json` is missing.',\n hints: [mapPath],\n };\n }\n\n let map: Record<string, XopcSessionDiskEntry>;\n try {\n const parsed = JSON.parse(readFileSync(mapPath, 'utf-8')) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('invalid');\n }\n map = parsed as Record<string, XopcSessionDiskEntry>;\n } catch {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'warn',\n message: '`sessions.json` is not valid JSON.',\n hints: [mapPath],\n };\n }\n\n const keys = Object.keys(map);\n const sorted = [...keys].sort((a, b) => {\n const ta = map[a]?.updatedAt ?? 0;\n const tb = map[b]?.updatedAt ?? 0;\n return tb - ta;\n });\n const sample = sorted.slice(0, 20);\n if (sample.length === 0) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'pass',\n message: '`sessions.json` is valid; no sessions to sample.',\n hints: [],\n };\n }\n\n const missing: string[] = [];\n for (const sessionKey of sample) {\n const entry = map[sessionKey];\n if (!entry?.sessionId) {\n missing.push(sessionKey);\n continue;\n }\n try {\n const p = resolveSessionFilePath(entry.sessionId, entry, { sessionsDir });\n if (!existsSync(p)) {\n missing.push(sessionKey);\n }\n } catch {\n missing.push(sessionKey);\n }\n }\n\n if (missing.length > 0) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'warn',\n message: `${missing.length} of ${sample.length} sampled session transcripts are missing on disk.`,\n hints: missing.slice(0, 5).map((k) => `Missing transcript for: ${k}`),\n };\n }\n\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'pass',\n message: `Sampled ${sample.length} recent session(s); JSONL transcripts are present.`,\n hints: [],\n };\n}\n"],"mappings":";;;;;;;kBAGyE;aACf;YACkB;uBACY;AAIxF,eAAsB,sBAAsB,KAA0C;AACpF,KAAI,CAAC,IAAI,QAAQ,KACf,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,0BAA0B;EACnC;AAGH,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,WAAS,WAAW,IAAI,WAAW;SAC7B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,UAAU,sBAAsB,OAAO;CAC7C,MAAM,cAAc,mBAAmB,QAAQ,QAAQ;CACvD,MAAM,UAAU,KAAK,aAAa,UAAU,aAAa;AAEzD,KAAI,CAAC,WAAW,YAAY,CAC1B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,YAAY;EACrB;AAGH,KAAI,CAAC,WAAW,QAAQ,CACtB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,QAAQ;EACjB;CAGH,IAAI;AACJ,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC;AACzD,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CAChE,OAAM,IAAI,MAAM,UAAU;AAE5B,QAAM;SACA;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,QAAQ;GACjB;;CASH,MAAM,SALS,CAAC,GADH,OAAO,KAAK,IACF,CAAC,CAAC,MAAM,GAAG,MAAM;EACtC,MAAM,KAAK,IAAI,IAAI,aAAa;AAEhC,UADW,IAAI,IAAI,aAAa,KACpB;GAEO,CAAC,MAAM,GAAG,GAAG;AAClC,KAAI,OAAO,WAAW,EACpB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,cAAc,QAAQ;EAC/B,MAAM,QAAQ,IAAI;AAClB,MAAI,CAAC,OAAO,WAAW;AACrB,WAAQ,KAAK,WAAW;AACxB;;AAEF,MAAI;AAEF,OAAI,CAAC,WADK,uBAAuB,MAAM,WAAW,OAAO,EAAE,aAAa,CACvD,CAAC,CAChB,SAAQ,KAAK,WAAW;UAEpB;AACN,WAAQ,KAAK,WAAW;;;AAI5B,KAAI,QAAQ,SAAS,EACnB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,GAAG,QAAQ,OAAO,MAAM,OAAO,OAAO;EAC/C,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,2BAA2B,IAAI;EACtE;AAGH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,WAAW,OAAO,OAAO;EAClC,OAAO,EAAE;EACV"}
1
+ {"version":3,"file":"session-integrity.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/session-integrity.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { basename, join } from 'node:path';\n\nimport { listAgentEntries, resolveDefaultAgentId } from '../../../../agent/agent-scope.js';\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport { FILENAMES, resolveSessionsDir } from '../../../../config/paths.js';\nimport { parseSessionKey } from '../../../../routing/session-key.js';\nimport { resolveSessionFilePath } from '../../../../session/parity/transcript-paths.js';\nimport type { XopcSessionDiskEntry } from '../../../../session/parity/xopc-session-disk-entry.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\ninterface SessionMapLocation {\n agentId: string;\n sessionsDir: string;\n mapPath: string;\n}\n\nfunction discoverSessionMapLocations(config: Config, stateDir: string): SessionMapLocation[] {\n const agentIds = new Set<string>([\n resolveDefaultAgentId(config),\n ...listAgentEntries(config).map((agent) => agent.id),\n ]);\n\n const agentsRoot = join(stateDir, 'agents');\n if (existsSync(agentsRoot)) {\n for (const entry of readdirSync(agentsRoot, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n agentIds.add(entry.name);\n }\n }\n }\n\n const seenMapPaths = new Set<string>();\n const locations: SessionMapLocation[] = [];\n for (const agentId of agentIds) {\n const sessionsDir = resolveSessionsDir(config, agentId);\n const mapPath = join(sessionsDir, FILENAMES.SESSIONS_MAP);\n if (seenMapPaths.has(mapPath)) {\n continue;\n }\n seenMapPaths.add(mapPath);\n locations.push({ agentId, sessionsDir, mapPath });\n }\n return locations;\n}\n\nfunction readSessionMap(mapPath: string): Record<string, XopcSessionDiskEntry> {\n const parsed = JSON.parse(readFileSync(mapPath, 'utf-8')) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('invalid sessions map');\n }\n return parsed as Record<string, XopcSessionDiskEntry>;\n}\n\nexport async function checkSessionIntegrity(ctx: DoctorContext): Promise<CheckResult> {\n if (!ctx.options.deep) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'skip',\n message: 'Deep mode off; session scan skipped.',\n hints: ['Run: xopc doctor --deep'],\n };\n }\n\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let config: Config;\n try {\n config = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const locations = discoverSessionMapLocations(config, ctx.stateDir);\n const missingMaps: string[] = [];\n const invalidMaps: string[] = [];\n const missingTranscripts: string[] = [];\n const agentMismatches: string[] = [];\n const orphanTranscripts: string[] = [];\n let sessionCount = 0;\n\n for (const location of locations) {\n if (!existsSync(location.sessionsDir)) {\n continue;\n }\n if (!existsSync(location.mapPath)) {\n missingMaps.push(location.mapPath);\n continue;\n }\n\n let map: Record<string, XopcSessionDiskEntry>;\n try {\n map = readSessionMap(location.mapPath);\n } catch {\n invalidMaps.push(location.mapPath);\n continue;\n }\n\n const referencedTranscriptFiles = new Set<string>();\n for (const [sessionKey, entry] of Object.entries(map)) {\n sessionCount++;\n const parsed = parseSessionKey(sessionKey);\n if (parsed && parsed.agentId !== location.agentId) {\n agentMismatches.push(`${sessionKey} in agent ${location.agentId}`);\n }\n if (!entry?.sessionId) {\n missingTranscripts.push(sessionKey);\n continue;\n }\n try {\n const transcriptPath = resolveSessionFilePath(entry.sessionId, entry, { sessionsDir: location.sessionsDir });\n referencedTranscriptFiles.add(basename(transcriptPath));\n if (!existsSync(transcriptPath)) {\n missingTranscripts.push(sessionKey);\n }\n } catch {\n missingTranscripts.push(sessionKey);\n }\n }\n\n for (const file of readdirSync(location.sessionsDir, { withFileTypes: true })) {\n if (!file.isFile() || !file.name.endsWith('.jsonl') || file.name.includes('.checkpoint.')) {\n continue;\n }\n if (!referencedTranscriptFiles.has(file.name)) {\n orphanTranscripts.push(join(location.sessionsDir, file.name));\n }\n }\n }\n\n const hints = [\n ...missingMaps.slice(0, 3).map((path) => `Missing sessions.json: ${path}`),\n ...invalidMaps.slice(0, 3).map((path) => `Invalid sessions.json: ${path}`),\n ...agentMismatches.slice(0, 3).map((item) => `Session key agent mismatch: ${item}`),\n ...missingTranscripts.slice(0, 3).map((key) => `Missing transcript for: ${key}`),\n ...orphanTranscripts.slice(0, 3).map((path) => `Orphan transcript: ${path}`),\n ];\n\n const warningCount =\n missingMaps.length + invalidMaps.length + missingTranscripts.length + agentMismatches.length + orphanTranscripts.length;\n\n if (warningCount > 0) {\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'warn',\n message: `Scanned ${locations.length} agent session dir(s), ${sessionCount} session(s); found ${warningCount} issue(s).`,\n hints,\n };\n }\n\n return {\n id: 'session-integrity',\n label: 'Sessions',\n status: 'pass',\n message: `Scanned ${locations.length} agent session dir(s), ${sessionCount} session(s); session maps and JSONL transcripts are consistent.`,\n hints: [],\n };\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { init_paths, resolveConfigPath } from "../../../config/paths.js";
2
2
  import { loadConfig } from "../../../config/loader.js";
3
3
  import "../../../config/index.js";
4
- import { resolveGatewayService } from "../../../daemon/service.js";
5
4
  import { isRestartEnabled } from "../../../config/commands.flags.js";
5
+ import { resolveGatewayService } from "../../../daemon/service.js";
6
6
  import { authorizeGatewaySigusr1Restart, writeGatewayRestartIntentSync } from "../../../infra/restart.js";
7
7
  import { findVerifiedGatewayListenerPidsOnPortSync, formatGatewayPidList, signalVerifiedGatewayPidSync } from "../../../infra/gateway-processes.js";
8
8
  import { runServiceRestart, runServiceStop } from "./lifecycle-core.js";