crewx 0.8.1 → 0.8.2-rc.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 (125) hide show
  1. package/README.md +268 -268
  2. package/bin/cli-commands.js +34 -0
  3. package/bin/crewx-lib.js +213 -108
  4. package/bin/crewx-ui.js +83 -83
  5. package/bin/crewx.js +219 -147
  6. package/bin/launcher-flags.js +29 -0
  7. package/bin/package.json +1 -1
  8. package/dist/assets/MarketPage-DptjaFpT.js +36 -0
  9. package/dist/assets/{PromptTab-DVKc7hJY.js → PromptTab-DZha2_v1.js} +1 -1
  10. package/dist/assets/{_baseUniq-wjlVo2E6.js → _baseUniq-jd6NubI3.js} +1 -1
  11. package/dist/assets/{arc-BfPgRtzW.js → arc-C2te3-8P.js} +1 -1
  12. package/dist/assets/{architectureDiagram-Q4EWVU46-ewcueFAG.js → architectureDiagram-Q4EWVU46-CbIQua02.js} +1 -1
  13. package/dist/assets/{blockDiagram-DXYQGD6D-TxlbbvKn.js → blockDiagram-DXYQGD6D-Cg7qkpSM.js} +1 -1
  14. package/dist/assets/{c4Diagram-AHTNJAMY-C1lT_bl_.js → c4Diagram-AHTNJAMY-BkffDY1F.js} +1 -1
  15. package/dist/assets/channel-beae0DeI.js +1 -0
  16. package/dist/assets/chatgpt-logo-dark.svg +15 -15
  17. package/dist/assets/chatgpt-logo.svg +15 -15
  18. package/dist/assets/{chunk-4BX2VUAB-C41j2mCL.js → chunk-4BX2VUAB-BxHe9wPE.js} +1 -1
  19. package/dist/assets/{chunk-4TB4RGXK-HNNsUbz0.js → chunk-4TB4RGXK--f1tN90O.js} +1 -1
  20. package/dist/assets/{chunk-55IACEB6-qtCgO0r2.js → chunk-55IACEB6-B5QXfPXQ.js} +1 -1
  21. package/dist/assets/{chunk-EDXVE4YY-BSnDYtsd.js → chunk-EDXVE4YY-DqKhblOg.js} +1 -1
  22. package/dist/assets/{chunk-FMBD7UC4-DyHRLQqX.js → chunk-FMBD7UC4-DZ1w_G02.js} +1 -1
  23. package/dist/assets/{chunk-OYMX7WX6-CCjfi6WS.js → chunk-OYMX7WX6-BqAgQpv8.js} +1 -1
  24. package/dist/assets/{chunk-QZHKN3VN-COLty8kd.js → chunk-QZHKN3VN-DPqnGqVi.js} +1 -1
  25. package/dist/assets/{chunk-YZCP3GAM-CHUUnGeN.js → chunk-YZCP3GAM-x-ZYSQLd.js} +1 -1
  26. package/dist/assets/classDiagram-6PBFFD2Q-BquWrs1y.js +1 -0
  27. package/dist/assets/classDiagram-v2-HSJHXN6E-BquWrs1y.js +1 -0
  28. package/dist/assets/clone-C9wSPtDN.js +1 -0
  29. package/dist/assets/{cose-bilkent-S5V4N54A-CSip-V2g.js → cose-bilkent-S5V4N54A-4VCNRP-E.js} +1 -1
  30. package/dist/assets/{dagre-KV5264BT-DkdpnWhv.js → dagre-KV5264BT-DucVi1QS.js} +1 -1
  31. package/dist/assets/{diagram-5BDNPKRD-PH4qc6PV.js → diagram-5BDNPKRD-ChdRA8bE.js} +1 -1
  32. package/dist/assets/{diagram-G4DWMVQ6-Cg5xZcjx.js → diagram-G4DWMVQ6-B1-97yHr.js} +1 -1
  33. package/dist/assets/{diagram-MMDJMWI5-soKmeTCW.js → diagram-MMDJMWI5-CQs3cO7G.js} +1 -1
  34. package/dist/assets/{diagram-TYMM5635-Daq5Mihu.js → diagram-TYMM5635-CuNCxDfO.js} +1 -1
  35. package/dist/assets/{erDiagram-SMLLAGMA-kr2OtY0Y.js → erDiagram-SMLLAGMA-DdR8v8g-.js} +1 -1
  36. package/dist/assets/{flowDiagram-DWJPFMVM-DQZCb8gm.js → flowDiagram-DWJPFMVM-Dt02upId.js} +1 -1
  37. package/dist/assets/{ganttDiagram-T4ZO3ILL-BHkn485T.js → ganttDiagram-T4ZO3ILL-cG_k9VOa.js} +1 -1
  38. package/dist/assets/{gitGraphDiagram-UUTBAWPF-FaCyYFmC.js → gitGraphDiagram-UUTBAWPF-Dz1DjhKq.js} +1 -1
  39. package/dist/assets/{graph-BVJlrP6V.js → graph-CF2NtM33.js} +1 -1
  40. package/dist/assets/{infoDiagram-42DDH7IO-DJOWkKdM.js → infoDiagram-42DDH7IO-tQWKrYM6.js} +1 -1
  41. package/dist/assets/{ishikawaDiagram-UXIWVN3A-VfpvNaIf.js → ishikawaDiagram-UXIWVN3A-CLuUMkF0.js} +1 -1
  42. package/dist/assets/{journeyDiagram-VCZTEJTY-CPzsak-v.js → journeyDiagram-VCZTEJTY-a6JenLCk.js} +1 -1
  43. package/dist/assets/{kanban-definition-6JOO6SKY-DFqLDBU0.js → kanban-definition-6JOO6SKY-rqOxTzYb.js} +1 -1
  44. package/dist/assets/{layout-CCSbNPHm.js → layout-Dvic1Hpy.js} +1 -1
  45. package/dist/assets/{linear-C4T7PCKE.js → linear-CfMV1S6a.js} +1 -1
  46. package/dist/assets/main-05K4ggqd.css +10 -0
  47. package/dist/assets/main-CCM1gtr8.js +1165 -0
  48. package/dist/assets/{min-CGQNEYGh.js → min-GpF3DZux.js} +1 -1
  49. package/dist/assets/{mindmap-definition-QFDTVHPH-AuU1EqwS.js → mindmap-definition-QFDTVHPH-Cg80z0Jx.js} +1 -1
  50. package/dist/assets/{pieDiagram-DEJITSTG-CopkCZwp.js → pieDiagram-DEJITSTG-BrtK7lAq.js} +1 -1
  51. package/dist/assets/{quadrantDiagram-34T5L4WZ-lMKrSv_t.js → quadrantDiagram-34T5L4WZ-BL2txAAS.js} +1 -1
  52. package/dist/assets/{requirementDiagram-MS252O5E-dWUpHOFb.js → requirementDiagram-MS252O5E-Co3wpBnu.js} +1 -1
  53. package/dist/assets/{sankeyDiagram-XADWPNL6-C8UQx9Bb.js → sankeyDiagram-XADWPNL6-B4KJXdQ4.js} +1 -1
  54. package/dist/assets/{sequenceDiagram-FGHM5R23-CUVNIItJ.js → sequenceDiagram-FGHM5R23-xs5OuzvV.js} +1 -1
  55. package/dist/assets/{stateDiagram-FHFEXIEX-Ct0GamGl.js → stateDiagram-FHFEXIEX-bbEP20JD.js} +1 -1
  56. package/dist/assets/stateDiagram-v2-QKLJ7IA2-XYh9U1A7.js +1 -0
  57. package/dist/assets/{timeline-definition-GMOUNBTQ-ul8Po7f7.js → timeline-definition-GMOUNBTQ-CTc2wVwC.js} +1 -1
  58. package/dist/assets/{vennDiagram-DHZGUBPP-B4AOWQnP.js → vennDiagram-DHZGUBPP-oJpXott7.js} +1 -1
  59. package/dist/assets/{wardley-RL74JXVD-Dr7Wp3AJ.js → wardley-RL74JXVD-aTmOXkKh.js} +1 -1
  60. package/dist/assets/{wardleyDiagram-NUSXRM2D-Ck70faXX.js → wardleyDiagram-NUSXRM2D-C83SOkig.js} +1 -1
  61. package/dist/assets/{xychartDiagram-5P7HB3ND-Bsy5-cNt.js → xychartDiagram-5P7HB3ND-BScws0tF.js} +1 -1
  62. package/dist/index.html +13 -13
  63. package/dist-electron/main.js +153 -116
  64. package/dist-electron/overlay.js +102 -65
  65. package/dist-electron/package.json +1 -0
  66. package/dist-electron/preload.js +8 -8
  67. package/dist-server/bootstrap/crewx-server.js +19 -10
  68. package/dist-server/domain/agent/agent.service.js +12 -0
  69. package/dist-server/domain/agent/dto/update-agent.dto.js +20 -1
  70. package/dist-server/domain/mcp/crewx-tool.factory.js +44 -113
  71. package/dist-server/domain/mcp/mcp.module.js +2 -0
  72. package/dist-server/domain/mcp/mcp.service.js +37 -12
  73. package/dist-server/domain/message/message.service.js +21 -13
  74. package/dist-server/domain/skill/skill.service.js +63 -43
  75. package/dist-server/domain/task/task.module.js +2 -0
  76. package/dist-server/domain/task/task.service.js +17 -10
  77. package/dist-server/domain/thread/dto/update-thread.dto.js +23 -0
  78. package/dist-server/domain/thread/thread.controller.js +16 -0
  79. package/dist-server/domain/thread/thread.service.js +9 -0
  80. package/dist-server/main.js +1 -1
  81. package/dist-server/modules/crewx.module.js +16 -1
  82. package/dist-server/repository/box.repository.js +20 -20
  83. package/dist-server/repository/project.repository.js +13 -13
  84. package/dist-server/repository/request-log.repository.js +10 -10
  85. package/dist-server/repository/task.repository.js +72 -72
  86. package/dist-server/repository/thread.repository.js +78 -58
  87. package/package.json +6 -6
  88. package/packages/cli/dist/bootstrap/crewx-cli.js +12 -0
  89. package/packages/cli/dist/commands/agent.js +23 -23
  90. package/packages/cli/dist/commands/init.js +19 -19
  91. package/packages/cli/dist/commands/parse-common-flags.d.ts +19 -3
  92. package/packages/cli/dist/commands/parse-common-flags.js +46 -6
  93. package/packages/cli/dist/commands/registry.d.ts +13 -0
  94. package/packages/cli/dist/commands/registry.js +29 -0
  95. package/packages/cli/dist/commands/task-db.js +7 -7
  96. package/packages/cli/dist/examples/deny-secrets-plugin.d.ts +22 -0
  97. package/packages/cli/dist/examples/deny-secrets-plugin.js +40 -0
  98. package/packages/cli/dist/main.js +134 -68
  99. package/packages/cli/dist/plugins/examples/echo-hook.d.ts +24 -0
  100. package/packages/cli/dist/plugins/examples/echo-hook.js +60 -0
  101. package/packages/cli/dist/plugins/examples/verify-echo-hook.d.ts +8 -0
  102. package/packages/cli/dist/plugins/examples/verify-echo-hook.js +47 -0
  103. package/packages/cli/dist/plugins/sqlite-tracing.d.ts +11 -0
  104. package/packages/cli/dist/plugins/sqlite-tracing.js +19 -0
  105. package/packages/cli/dist/schema/tasks.d.ts +7 -0
  106. package/packages/cli/dist/schema/tasks.js +48 -0
  107. package/packages/cli/package.json +52 -52
  108. package/scripts/analyze-task-logs.mjs +569 -0
  109. package/scripts/build-manual.mjs +266 -266
  110. package/scripts/emit-dist-server-package-json.mjs +7 -7
  111. package/scripts/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  112. package/scripts/postinstall.mjs +44 -44
  113. package/scripts/smoke-tarball.mjs +285 -285
  114. package/scripts/snapshot-msg-list.sh +52 -52
  115. package/server.js +167 -164
  116. package/dist/assets/MarketPage-Dwsg6K-B.js +0 -31
  117. package/dist/assets/channel-BP4PNMmz.js +0 -1
  118. package/dist/assets/classDiagram-6PBFFD2Q-Upr3UAcM.js +0 -1
  119. package/dist/assets/classDiagram-v2-HSJHXN6E-Upr3UAcM.js +0 -1
  120. package/dist/assets/clone-B8BP7ReZ.js +0 -1
  121. package/dist/assets/main-CELBpK6r.js +0 -1166
  122. package/dist/assets/main-CmP-VosD.css +0 -10
  123. package/dist/assets/stateDiagram-v2-QKLJ7IA2-CFBLQDEx.js +0 -1
  124. package/dist-server/domain/task/dto/project-usage.dto.js +0 -38
  125. package/dist-server/domain/thread/dto/send-message.dto.js +0 -10
@@ -9,9 +9,20 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.UpdateAgentDto = void 0;
12
+ exports.UpdateAgentDto = exports.AgentSkillsDto = void 0;
13
13
  const swagger_1 = require("@nestjs/swagger");
14
14
  const class_validator_1 = require("class-validator");
15
+ const class_transformer_1 = require("class-transformer");
16
+ class AgentSkillsDto {
17
+ include;
18
+ }
19
+ exports.AgentSkillsDto = AgentSkillsDto;
20
+ __decorate([
21
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Whitelist of skill names. Empty array means no skills allowed.', type: [String] }),
22
+ (0, class_validator_1.IsArray)(),
23
+ (0, class_validator_1.IsString)({ each: true }),
24
+ __metadata("design:type", Array)
25
+ ], AgentSkillsDto.prototype, "include", void 0);
15
26
  class UpdateAgentDto {
16
27
  name;
17
28
  role;
@@ -20,6 +31,7 @@ class UpdateAgentDto {
20
31
  provider;
21
32
  model;
22
33
  prompt;
34
+ skills;
23
35
  }
24
36
  exports.UpdateAgentDto = UpdateAgentDto;
25
37
  __decorate([
@@ -64,3 +76,10 @@ __decorate([
64
76
  (0, class_validator_1.IsString)(),
65
77
  __metadata("design:type", String)
66
78
  ], UpdateAgentDto.prototype, "prompt", void 0);
79
+ __decorate([
80
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Skill configuration. Undefined = keep existing; include:[] = block all; include:[...] = whitelist.' }),
81
+ (0, class_validator_1.IsOptional)(),
82
+ (0, class_validator_1.ValidateNested)(),
83
+ (0, class_transformer_1.Type)(() => AgentSkillsDto),
84
+ __metadata("design:type", AgentSkillsDto)
85
+ ], UpdateAgentDto.prototype, "skills", void 0);
@@ -1,65 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCrewXTool = getCrewXTool;
3
+ exports.createCrewxTool = createCrewxTool;
4
4
  const common_1 = require("@nestjs/common");
5
- const fs_1 = require("fs");
6
- const path_1 = require("path");
7
- const child_process_1 = require("child_process");
8
- const workspace_context_store_js_1 = require("../../common/workspace-context.store.js");
9
- const crewx_server_js_1 = require("../../bootstrap/crewx-server.js");
10
5
  const logger = new common_1.Logger('CrewXToolFactory');
11
- let crewxToolInstance = null;
12
6
  /**
13
- * Resolve the crewx CLI command args for subprocess spawn.
14
- * Priority: CREWX_CLI env → local packages/cli/dist/main.js → npx crewx
7
+ * Create ICrewXTool implementation backed by a shared Crewx singleton (NestJS DI).
15
8
  *
16
- * CREWX_CLI may be a space-separated string like "npx crewx"split it so
17
- * spawn() receives the executable and its prefix args separately.
9
+ * All paths delegate to `crewx.query/execute`threads row upsert, platform,
10
+ * title, first_message, and message persistence are handled by the SDK's
11
+ * ConversationPlugin chain. The Crewx instance lifecycle is owned by
12
+ * CrewxModule; this factory must NEVER call crewx.close().
18
13
  */
19
- function resolveCrlewxCmd() {
20
- const envCli = process.env.CREWX_CLI;
21
- if (envCli) {
22
- const parts = envCli.trim().split(/\s+/);
23
- return { cmd: parts[0], prefix: parts.slice(1) };
24
- }
25
- const localCli = (0, path_1.join)(process.cwd(), 'packages', 'cli', 'dist', 'main.js');
26
- if ((0, fs_1.existsSync)(localCli)) {
27
- return { cmd: 'node', prefix: [localCli] };
28
- }
29
- return { cmd: 'npx', prefix: ['crewx'] };
30
- }
31
- /**
32
- * Resolve crewx.yaml config path.
33
- * Priority: CREWX_CONFIG env → workspace crewx.yaml → workspace crewx.yml
34
- */
35
- function resolveConfigPath(workspacePath) {
36
- const envConfig = process.env.CREWX_CONFIG;
37
- if (envConfig && (0, fs_1.existsSync)(envConfig))
38
- return envConfig;
39
- const yaml = (0, path_1.join)(workspacePath, 'crewx.yaml');
40
- const yml = (0, path_1.join)(workspacePath, 'crewx.yml');
41
- if ((0, fs_1.existsSync)(yaml))
42
- return yaml;
43
- if ((0, fs_1.existsSync)(yml))
44
- return yml;
45
- return yaml; // will fail gracefully in loadYaml
46
- }
47
- /**
48
- * Create ICrewXTool implementation backed by the new @crewx/sdk + subprocess spawn.
49
- *
50
- * - listAgents / queryAgent: use Crewx SDK in-process (createServerCrewx for plugin coverage)
51
- * - executeAgent: spawn crewx CLI subprocess so FileLoggerPlugin + SqliteTracingPlugin run
52
- *
53
- * This replaces the old NestJS AppModule bootstrap which required packages/cli to be a
54
- * NestJS application (cli-bak pattern). The new CLI is a plain command dispatcher.
55
- */
56
- function createTool() {
14
+ function createCrewxTool(crewx) {
15
+ // Pre-call helper: ensures threads row exists before fire-and-forget SDK call,
16
+ // closing the race window between task:start emission and REST response.
17
+ const preEnsureThread = async (threadId, platform) => {
18
+ if (!threadId)
19
+ return;
20
+ const conv = crewx.plugins.find((p) => p.name === 'conversation')?.conversationProvider;
21
+ try {
22
+ await conv?.ensureThread(threadId, platform, crewx.workspaceId);
23
+ }
24
+ catch (err) {
25
+ logger.warn(`ensureThread skipped [thread=${threadId}]: ${err.message}`);
26
+ }
27
+ };
57
28
  return {
58
29
  async listAgents() {
59
- let crewx;
60
30
  try {
61
- const configPath = resolveConfigPath((0, workspace_context_store_js_1.getWorkspacePath)());
62
- crewx = await (0, crewx_server_js_1.createServerCrewx)(configPath);
63
31
  const availableAgents = Array.from(crewx.agents.values()).map((a) => ({
64
32
  id: a.id,
65
33
  name: a.name,
@@ -73,27 +41,24 @@ function createTool() {
73
41
  success: true,
74
42
  availableAgents,
75
43
  totalCount: availableAgents.length,
76
- configurationSource: configPath,
77
44
  };
78
45
  }
79
46
  catch (err) {
80
47
  logger.error(`listAgents error: ${err.message}`, err.stack);
81
48
  return { content: [], success: false, totalCount: 0, error: err.message };
82
49
  }
83
- finally {
84
- if (crewx)
85
- await crewx.close();
86
- }
87
50
  },
88
51
  async queryAgent(args) {
89
- let crewx;
90
52
  try {
91
- const configPath = resolveConfigPath((0, workspace_context_store_js_1.getWorkspacePath)());
92
- crewx = await (0, crewx_server_js_1.createServerCrewx)(configPath);
53
+ const platform = args.platform ?? 'channel/mcp';
54
+ await preEnsureThread(args.threadId, platform);
93
55
  const result = await crewx.query(`@${args.agentId}`, args.query, {
94
56
  provider: args.provider,
95
57
  model: args.model,
58
+ context: args.context,
59
+ threadId: args.threadId,
96
60
  metadata: args.metadata,
61
+ platform,
97
62
  });
98
63
  return {
99
64
  content: [{ type: 'text', text: result.data }],
@@ -109,67 +74,33 @@ function createTool() {
109
74
  logger.error(`queryAgent error [agent=${args.agentId}]: ${err.message}`, err.stack);
110
75
  return { content: [], success: false, isError: true, error: err.message };
111
76
  }
112
- finally {
113
- if (crewx)
114
- await crewx.close();
115
- }
116
77
  },
117
78
  async executeAgent(args) {
118
79
  const { agentId, task, metadata } = args;
119
- const thread = metadata?.['thread'];
120
- // Get workspace path at call time (AsyncLocalStorage-aware)
121
- const workspacePath = (0, workspace_context_store_js_1.getWorkspacePath)();
122
- const { cmd, prefix } = resolveCrlewxCmd();
123
- const spawnArgs = [
124
- ...prefix,
125
- 'execute',
126
- `@${agentId}`,
127
- task,
128
- ...(thread ? [`--thread=${thread}`] : []),
129
- ];
130
- logger.log(`Spawning executeAgent [thread=${thread ?? 'none'}, agent=${agentId}]: ${cmd} ${spawnArgs.join(' ')}`);
131
- const child = (0, child_process_1.spawn)(cmd, spawnArgs, {
132
- detached: true,
133
- stdio: ['ignore', 'pipe', 'pipe'],
134
- cwd: workspacePath,
135
- env: { ...process.env },
136
- });
137
- child.stdout?.on('data', (data) => {
138
- logger.debug(`[execute:${agentId}] stdout: ${data.toString().trim()}`);
139
- });
140
- child.stderr?.on('data', (data) => {
141
- logger.warn(`[execute:${agentId}] stderr: ${data.toString().trim()}`);
142
- });
143
- child.on('error', (err) => {
144
- logger.error(`executeAgent spawn error [thread=${thread}, agent=${agentId}]: ${err.message}`);
145
- });
146
- child.on('close', (code) => {
147
- if (code !== 0 && code !== null) {
148
- logger.error(`[execute:${agentId}] process exited with code ${code}`);
149
- }
150
- else {
151
- logger.log(`[execute:${agentId}] process completed (code=${code})`);
152
- }
153
- });
154
- // Unref child so the server process isn't blocked waiting for it
155
- child.unref();
80
+ // threadId sibling takes priority; fall back to legacy metadata.thread for backward compat
81
+ const threadId = args.threadId ?? metadata?.['thread'];
82
+ logger.log(`[execute:${agentId}] starting (thread=${threadId ?? 'none'}) via shared Crewx`);
83
+ const platform = args.platform ?? 'channel/mcp';
84
+ await preEnsureThread(threadId, platform);
85
+ // Fire-and-forget: return immediately, task runs in background.
86
+ // Never close the shared Crewx instance — CrewxModule owns its lifecycle.
87
+ crewx
88
+ .execute(`@${agentId}`, task, {
89
+ provider: args.provider,
90
+ model: args.model,
91
+ context: args.context,
92
+ threadId,
93
+ metadata: args.metadata,
94
+ platform,
95
+ })
96
+ .then((r) => logger.log(`[execute:${agentId}] done (ok=${r.ok})`))
97
+ .catch((e) => logger.error(`[execute:${agentId}] error: ${e.message}`, e.stack));
156
98
  return {
157
99
  content: [{ type: 'text', text: 'Task started' }],
158
100
  success: true,
159
101
  agent: agentId,
160
- taskId: thread,
102
+ taskId: threadId,
161
103
  };
162
104
  },
163
105
  };
164
106
  }
165
- /**
166
- * Return the singleton ICrewXTool implementation.
167
- * Memoised — only creates the tool once per server lifetime.
168
- */
169
- async function getCrewXTool() {
170
- if (!crewxToolInstance) {
171
- crewxToolInstance = createTool();
172
- logger.log('CrewXTool initialised (SDK + subprocess mode)');
173
- }
174
- return crewxToolInstance;
175
- }
@@ -13,11 +13,13 @@ const crypto_1 = require("crypto");
13
13
  const mcp_controller_js_1 = require("./mcp.controller.js");
14
14
  const mcp_service_js_1 = require("./mcp.service.js");
15
15
  const mcp_constants_js_1 = require("./mcp.constants.js");
16
+ const crewx_module_js_1 = require("../../modules/crewx.module.js");
16
17
  let McpModule = class McpModule {
17
18
  };
18
19
  exports.McpModule = McpModule;
19
20
  exports.McpModule = McpModule = __decorate([
20
21
  (0, common_1.Module)({
22
+ imports: [crewx_module_js_1.CrewxModule],
21
23
  controllers: [mcp_controller_js_1.McpController],
22
24
  providers: [
23
25
  mcp_service_js_1.McpService,
@@ -5,10 +5,17 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
7
  };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
8
14
  var McpService_1;
9
15
  Object.defineProperty(exports, "__esModule", { value: true });
10
16
  exports.McpService = void 0;
11
17
  const common_1 = require("@nestjs/common");
18
+ const sdk_1 = require("@crewx/sdk");
12
19
  const mcp_dto_js_1 = require("./mcp.dto.js");
13
20
  const crewx_tool_factory_js_1 = require("./crewx-tool.factory.js");
14
21
  const SERVER_NAME = 'crewx-ui';
@@ -36,7 +43,12 @@ const TOOL_DEFINITIONS = [
36
43
  },
37
44
  thread: {
38
45
  type: 'string',
39
- description: 'Optional thread ID for conversation continuity',
46
+ description: 'Thread ID for conversation continuity. Previous messages with the same thread ID will be included in the context automatically.',
47
+ },
48
+ metadata: {
49
+ type: 'object',
50
+ additionalProperties: true,
51
+ description: 'Free-form metadata (e.g., workflow_id, project_id) persisted to the task record for tracing.',
40
52
  },
41
53
  },
42
54
  required: ['agentId', 'query'],
@@ -62,7 +74,12 @@ const TOOL_DEFINITIONS = [
62
74
  },
63
75
  thread: {
64
76
  type: 'string',
65
- description: 'Optional thread ID for conversation continuity',
77
+ description: 'Thread ID for conversation continuity. Previous messages with the same thread ID will be included in the context automatically.',
78
+ },
79
+ metadata: {
80
+ type: 'object',
81
+ additionalProperties: true,
82
+ description: 'Free-form metadata (e.g., workflow_id, project_id) persisted to the task record for tracing.',
66
83
  },
67
84
  },
68
85
  required: ['agentId', 'task'],
@@ -79,6 +96,10 @@ const TOOL_DEFINITIONS = [
79
96
  ];
80
97
  let McpService = McpService_1 = class McpService {
81
98
  logger = new common_1.Logger(McpService_1.name);
99
+ tool;
100
+ constructor(crewx) {
101
+ this.tool = (0, crewx_tool_factory_js_1.createCrewxTool)(crewx);
102
+ }
82
103
  // Validate JSON-RPC 2.0 request structure
83
104
  validateRequest(body) {
84
105
  if (!body || typeof body !== 'object') {
@@ -249,7 +270,7 @@ let McpService = McpService_1 = class McpService {
249
270
  */
250
271
  async execListAgents(id) {
251
272
  try {
252
- const tool = await (0, crewx_tool_factory_js_1.getCrewXTool)();
273
+ const tool = this.tool;
253
274
  const result = await tool.listAgents();
254
275
  return {
255
276
  jsonrpc: '2.0',
@@ -281,7 +302,7 @@ let McpService = McpService_1 = class McpService {
281
302
  * crewx_queryAgent — delegates to CrewXTool.queryAgent()
282
303
  */
283
304
  async execQueryAgent(id, args) {
284
- const { agentId, query, context, thread } = args;
305
+ const { agentId, query, context, thread, metadata } = args;
285
306
  if (!agentId || !query) {
286
307
  return {
287
308
  jsonrpc: '2.0',
@@ -293,14 +314,15 @@ let McpService = McpService_1 = class McpService {
293
314
  };
294
315
  }
295
316
  try {
296
- const tool = await (0, crewx_tool_factory_js_1.getCrewXTool)();
317
+ const tool = this.tool;
297
318
  const startTime = Date.now();
298
319
  const result = await tool.queryAgent({
299
320
  agentId,
300
321
  query,
301
322
  context,
302
- platform: 'cli',
303
- metadata: thread ? { thread } : undefined,
323
+ threadId: thread,
324
+ metadata: metadata ?? {},
325
+ platform: 'channel/mcp',
304
326
  });
305
327
  const duration = Date.now() - startTime;
306
328
  const content = normalizeContent(result);
@@ -342,7 +364,7 @@ let McpService = McpService_1 = class McpService {
342
364
  * crewx_executeAgent — delegates to CrewXTool.executeAgent()
343
365
  */
344
366
  async execExecuteAgent(id, args) {
345
- const { agentId, task, context, thread } = args;
367
+ const { agentId, task, context, thread, metadata } = args;
346
368
  if (!agentId || !task) {
347
369
  return {
348
370
  jsonrpc: '2.0',
@@ -354,14 +376,15 @@ let McpService = McpService_1 = class McpService {
354
376
  };
355
377
  }
356
378
  try {
357
- const tool = await (0, crewx_tool_factory_js_1.getCrewXTool)();
379
+ const tool = this.tool;
358
380
  const startTime = Date.now();
359
381
  const result = await tool.executeAgent({
360
382
  agentId,
361
383
  task,
362
384
  context,
363
- platform: 'cli',
364
- metadata: thread ? { thread } : undefined,
385
+ threadId: thread,
386
+ metadata: metadata ?? {},
387
+ platform: 'channel/mcp',
365
388
  });
366
389
  const duration = Date.now() - startTime;
367
390
  const content = normalizeContent(result);
@@ -402,7 +425,9 @@ let McpService = McpService_1 = class McpService {
402
425
  };
403
426
  exports.McpService = McpService;
404
427
  exports.McpService = McpService = McpService_1 = __decorate([
405
- (0, common_1.Injectable)()
428
+ (0, common_1.Injectable)(),
429
+ __param(0, (0, common_1.Inject)(sdk_1.Crewx)),
430
+ __metadata("design:paramtypes", [sdk_1.Crewx])
406
431
  ], McpService);
407
432
  function normalizeContent(result) {
408
433
  if (Array.isArray(result.content) && result.content.length > 0) {
@@ -98,7 +98,7 @@ let MessageService = MessageService_1 = class MessageService {
98
98
  config: ctx.config,
99
99
  };
100
100
  }
101
- // MSG.SEND: Send message — UPSERT thread + fire crewx CLI in background
101
+ // MSG.SEND: Send message — fire crewx SDK; ConversationPlugin persists threads row
102
102
  async sendMessage(threadId, message, agent, mode = 'query', title, workspaceId) {
103
103
  this.validateId(threadId);
104
104
  if (!message || !message.trim()) {
@@ -107,17 +107,30 @@ let MessageService = MessageService_1 = class MessageService {
107
107
  if (!agent || !agent.trim()) {
108
108
  throw new common_1.BadRequestException('agent is required');
109
109
  }
110
- // UPSERT thread in DB before firing CLI
111
- const threadTitle = title || this.truncate(message, 60);
112
- this.threadRepo.upsertThread(threadId, {
113
- title: threadTitle,
114
- firstMessage: this.truncate(message, 200),
115
- workspaceId,
116
- });
110
+ // Explicit user-supplied title locks the thread title against plugin overwrite.
111
+ if (title) {
112
+ try {
113
+ this.threadRepo.updateThreadTitle(threadId, title, workspaceId);
114
+ }
115
+ catch (err) {
116
+ // Non-fatal: plugin chain will still persist message/thread row shortly.
117
+ this.logger.warn(`updateThreadTitle skipped [thread=${threadId}]: ${err.message}`);
118
+ }
119
+ }
117
120
  const sdkOptions = {
118
121
  threadId,
122
+ platform: 'cli',
119
123
  metadata: { thread: threadId, parentTaskId: null },
120
124
  };
125
+ // Pre-call ensureThread so REST response is safe to follow with GET /threads/:id
126
+ // (frontend ThreadNewPage navigates to /threads/:id right after POST /messages).
127
+ const conv = this.crewx.plugins.find((p) => p.name === 'conversation')?.conversationProvider;
128
+ try {
129
+ await conv?.ensureThread(threadId, sdkOptions.platform, this.crewx.workspaceId);
130
+ }
131
+ catch (err) {
132
+ this.logger.warn(`ensureThread skipped [thread=${threadId}]: ${err.message}`);
133
+ }
121
134
  const agentRef = `@${agent}`;
122
135
  const sdkCall = mode === 'execute'
123
136
  ? this.crewx.execute(agentRef, message, sdkOptions)
@@ -137,11 +150,6 @@ let MessageService = MessageService_1 = class MessageService {
137
150
  filterLogs(logs) {
138
151
  return logs;
139
152
  }
140
- truncate(text, maxLen) {
141
- if (text.length <= maxLen)
142
- return text;
143
- return text.slice(0, maxLen) + '...';
144
- }
145
153
  };
146
154
  exports.MessageService = MessageService;
147
155
  exports.MessageService = MessageService = MessageService_1 = __decorate([
@@ -47,6 +47,7 @@ const promises_1 = require("fs/promises");
47
47
  const path_1 = require("path");
48
48
  const crypto_1 = require("crypto");
49
49
  const yaml = __importStar(require("js-yaml"));
50
+ const skill_1 = require("@crewx/skill");
50
51
  const workspace_context_store_js_1 = require("../../common/workspace-context.store.js");
51
52
  // --- Constants ---
52
53
  const DEFAULT_REGISTRY_URL = 'https://github.com/sowonlabs/crewx-templates';
@@ -199,53 +200,72 @@ let SkillService = SkillService_1 = class SkillService {
199
200
  this.logger.log(`Uninstalled skill '${name}'`);
200
201
  }
201
202
  // --- Installed skills ---
202
- async listInstalledSkills() {
203
- if (!(0, fs_1.existsSync)(this.skillsDir)) {
204
- return { skills: [] };
203
+ resolveSkillSource(dir, projectRoot) {
204
+ const nmDir = (0, path_1.join)(projectRoot, 'node_modules') + '/';
205
+ if (!dir.startsWith(nmDir) && dir !== (0, path_1.join)(projectRoot, 'node_modules')) {
206
+ return 'local';
205
207
  }
206
- const entries = await (0, promises_1.readdir)(this.skillsDir);
208
+ const relFromNm = dir.slice(nmDir.length);
209
+ return relFromNm.startsWith('@crewx/') ? 'builtin' : 'node_modules';
210
+ }
211
+ async listInstalledSkills() {
212
+ const projectRoot = (0, path_1.dirname)(this.skillsDir);
213
+ const nmDir = (0, path_1.join)(projectRoot, 'node_modules');
214
+ const engine = new skill_1.SkillEngine(projectRoot, (0, fs_1.existsSync)(nmDir) ? [nmDir] : []);
215
+ const discovered = engine.discover();
207
216
  const skills = [];
208
- for (const entry of entries) {
209
- const entryPath = (0, path_1.join)(this.skillsDir, entry);
210
- try {
211
- if (!(await (0, promises_1.stat)(entryPath)).isDirectory())
212
- continue;
213
- }
214
- catch {
215
- continue;
216
- }
217
- const skillMdPath = (0, path_1.join)(entryPath, 'SKILL.md');
218
- if (!(0, fs_1.existsSync)(skillMdPath))
219
- continue;
220
- const { frontmatter } = await this.parseSkillMdAsync(skillMdPath);
221
- const installSource = this.detectInstallSource(entry);
222
- const cached = this.getGitHubCacheEntry(entry);
223
- // updatedAt: prefer GitHub pushed_at from cache, fallback to SKILL.md mtime
224
- let updatedAt = cached?.updatedAt;
225
- if (!updatedAt) {
226
- try {
227
- updatedAt = (await (0, promises_1.stat)(skillMdPath)).mtime.toISOString();
228
- }
229
- catch {
230
- // ignore stat errors
217
+ for (const item of discovered) {
218
+ const source = this.resolveSkillSource(item.dir, projectRoot);
219
+ if (source === 'local') {
220
+ // For local skills keep rich metadata from frontmatter + GitHub cache
221
+ const skillMdPath = item.skillMdPath ?? (0, path_1.join)(item.dir, 'SKILL.md');
222
+ const { frontmatter } = (0, fs_1.existsSync)(skillMdPath)
223
+ ? await this.parseSkillMdAsync(skillMdPath)
224
+ : { frontmatter: {} };
225
+ const installSource = this.detectInstallSource(item.name);
226
+ const cached = this.getGitHubCacheEntry(item.name);
227
+ let updatedAt = cached?.updatedAt;
228
+ if (!updatedAt) {
229
+ try {
230
+ updatedAt = (await (0, promises_1.stat)(skillMdPath)).mtime.toISOString();
231
+ }
232
+ catch {
233
+ // ignore stat errors
234
+ }
231
235
  }
236
+ skills.push({
237
+ name: item.name,
238
+ displayName: item.name,
239
+ description: item.description || frontmatter.description || '',
240
+ author: frontmatter.author || '',
241
+ version: item.version || frontmatter.version || '0.0.0',
242
+ tags: frontmatter.tags ? String(frontmatter.tags).split(',').map((t) => t.trim()) : [],
243
+ type: item.type,
244
+ isVerified: false,
245
+ isInstalled: true,
246
+ source,
247
+ installSource,
248
+ authors: cached?.authors,
249
+ updatedAt,
250
+ stars: cached?.stars,
251
+ license: cached?.license,
252
+ });
253
+ }
254
+ else {
255
+ // For builtin / node_modules skills use basic discover() info
256
+ skills.push({
257
+ name: item.name,
258
+ displayName: item.name,
259
+ description: item.description || '',
260
+ author: '',
261
+ version: item.version || '0.0.0',
262
+ tags: [],
263
+ type: item.type,
264
+ isVerified: false,
265
+ isInstalled: false,
266
+ source,
267
+ });
232
268
  }
233
- skills.push({
234
- name: frontmatter.name || entry,
235
- displayName: frontmatter.name || entry,
236
- description: frontmatter.description || '',
237
- author: frontmatter.author || '',
238
- version: frontmatter.version || '0.0.0',
239
- tags: frontmatter.tags ? frontmatter.tags.split(',').map((t) => t.trim()) : [],
240
- type: 'skill',
241
- isVerified: false,
242
- isInstalled: true,
243
- installSource,
244
- authors: cached?.authors,
245
- updatedAt,
246
- stars: cached?.stars,
247
- license: cached?.license,
248
- });
249
269
  }
250
270
  return { skills };
251
271
  }
@@ -10,11 +10,13 @@ exports.TaskModule = void 0;
10
10
  const common_1 = require("@nestjs/common");
11
11
  const task_controller_js_1 = require("./task.controller.js");
12
12
  const task_service_js_1 = require("./task.service.js");
13
+ const crewx_module_js_1 = require("../../modules/crewx.module.js");
13
14
  let TaskModule = class TaskModule {
14
15
  };
15
16
  exports.TaskModule = TaskModule;
16
17
  exports.TaskModule = TaskModule = __decorate([
17
18
  (0, common_1.Module)({
19
+ imports: [crewx_module_js_1.CrewxModule],
18
20
  controllers: [task_controller_js_1.TaskController],
19
21
  providers: [task_service_js_1.TaskService],
20
22
  })