crewx 0.9.0-rc.2 → 0.9.0-rc.21

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 (104) hide show
  1. package/dist/assets/MarketPage-DDPL2d7x.js +36 -0
  2. package/dist/assets/{PromptTab-hdXDbbT6.js → PromptTab-UC3K4qx2.js} +3 -3
  3. package/dist/assets/{arc--CBVVcyH.js → arc-DdP3-fUR.js} +1 -1
  4. package/dist/assets/{architectureDiagram-UYN6MBPD-DUiYwqJn.js → architectureDiagram-UYN6MBPD-D16VON1i.js} +1 -1
  5. package/dist/assets/{blockDiagram-ZHA2E4KO-DV86vAgG.js → blockDiagram-ZHA2E4KO-qs1dIRkl.js} +4 -4
  6. package/dist/assets/{c4Diagram-6F5ED5ID-CvWE7Cx_.js → c4Diagram-6F5ED5ID-BeDy8akQ.js} +1 -1
  7. package/dist/assets/channel-DtrxBSkc.js +1 -0
  8. package/dist/assets/{chunk-5HRBRIJM-DAxM5Ul_.js → chunk-5HRBRIJM-BC1zIb11.js} +1 -1
  9. package/dist/assets/{chunk-7U56Z5CX-BuYaEj8L.js → chunk-7U56Z5CX-BuGil3QD.js} +1 -1
  10. package/dist/assets/{chunk-ASOPGD6M-2Kx0e45C.js → chunk-ASOPGD6M-DmKdDtBQ.js} +1 -1
  11. package/dist/assets/{chunk-KFBOBJHC-CR4QS6Rt.js → chunk-KFBOBJHC-D3BvHLPa.js} +1 -1
  12. package/dist/assets/{chunk-T2TOU4HS-CffcwNHx.js → chunk-T2TOU4HS-B5MoUp-N.js} +1 -1
  13. package/dist/assets/{chunk-TMUBEWPD-BGpYtekN.js → chunk-TMUBEWPD-AvWe_lsV.js} +1 -1
  14. package/dist/assets/classDiagram-LNE6IOMH-BMT5B_JB.js +1 -0
  15. package/dist/assets/classDiagram-v2-MQ7JQ4JX-BMT5B_JB.js +1 -0
  16. package/dist/assets/{dagre-4EVJKHTY-yBWukkrE.js → dagre-4EVJKHTY-B6DewFyM.js} +1 -1
  17. package/dist/assets/{diagram-QW4FP2JN-CIr1PLq3.js → diagram-QW4FP2JN-BTsQ8JSd.js} +1 -1
  18. package/dist/assets/{erDiagram-6RL3IURR-xG7SygyU.js → erDiagram-6RL3IURR-BM5nvgVR.js} +1 -1
  19. package/dist/assets/{flowDiagram-7ASYPVHJ-CKalfOqp.js → flowDiagram-7ASYPVHJ-D1UCCuOG.js} +1 -1
  20. package/dist/assets/{ganttDiagram-NTVNEXSI-Bnx7zrX4.js → ganttDiagram-NTVNEXSI-CNIGs7Cn.js} +1 -1
  21. package/dist/assets/{gitGraph-YCYPL57B-TQlF0fiA.js → gitGraph-YCYPL57B-DSLjx1yQ.js} +1 -1
  22. package/dist/assets/{gitGraphDiagram-NRZ2UAAF-DxlaFkwO.js → gitGraphDiagram-NRZ2UAAF-B11zX1Fa.js} +1 -1
  23. package/dist/assets/{infoDiagram-A4XQUW5V-B1-arkqa.js → infoDiagram-A4XQUW5V-DkjvODYl.js} +1 -1
  24. package/dist/assets/{journeyDiagram-G5WM74LC-Cj439GUX.js → journeyDiagram-G5WM74LC-BkCQPMn6.js} +1 -1
  25. package/dist/assets/{kanban-definition-QRCXZQQD-BvrIwEIc.js → kanban-definition-QRCXZQQD-_OX7pNfJ.js} +1 -1
  26. package/dist/assets/{linear-BKYNK0ri.js → linear-BYjwVAR6.js} +1 -1
  27. package/dist/assets/main-CZdKLdUl.css +10 -0
  28. package/dist/assets/main-CkkZNCy-.js +1561 -0
  29. package/dist/assets/{mindmap-definition-GWI6TPTV-BExfJY0G.js → mindmap-definition-GWI6TPTV-BzYpSnv8.js} +1 -1
  30. package/dist/assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm +0 -0
  31. package/dist/assets/{pieDiagram-YF2LJOPJ-RPWpvrC8.js → pieDiagram-YF2LJOPJ-CEFg6ao0.js} +1 -1
  32. package/dist/assets/{quadrantDiagram-OS5C2QUG-DmbBzk5Q.js → quadrantDiagram-OS5C2QUG-_5w2x3VV.js} +1 -1
  33. package/dist/assets/{requirementDiagram-MIRIMTAZ-DFQuFXq3.js → requirementDiagram-MIRIMTAZ-B_VQYReZ.js} +1 -1
  34. package/dist/assets/{sankeyDiagram-Y46BX6SQ-p_bw56lJ.js → sankeyDiagram-Y46BX6SQ-CgmOlLhh.js} +1 -1
  35. package/dist/assets/{sequenceDiagram-G6AWOVSC-DTbBkLeD.js → sequenceDiagram-G6AWOVSC-MGVt0Ai2.js} +1 -1
  36. package/dist/assets/{stateDiagram-MAYHULR4-DBvC6UcF.js → stateDiagram-MAYHULR4-DaZjgTu-.js} +1 -1
  37. package/dist/assets/stateDiagram-v2-4JROLMXI-DN1wGYkV.js +1 -0
  38. package/dist/assets/{timeline-definition-U7ZMHBDA-B6utvIg1.js → timeline-definition-U7ZMHBDA-CusetmK6.js} +1 -1
  39. package/dist/assets/transformers.web-BN8Ug43_.js +45 -0
  40. package/dist/assets/{xychartDiagram-6QU3TZC5-BHG0s1Ye.js → xychartDiagram-6QU3TZC5-CTndf-if.js} +1 -1
  41. package/dist/index.html +2 -2
  42. package/dist-server/app.module.js +4 -0
  43. package/dist-server/bootstrap/codex-writable-roots.js +88 -0
  44. package/dist-server/bootstrap/crewx-server.js +10 -2
  45. package/dist-server/common/decorator/skip-request-log.decorator.js +8 -0
  46. package/dist-server/common/interceptor/http-logging.interceptor.js +17 -4
  47. package/dist-server/common/task-log/stdout-json-fallback.js +200 -0
  48. package/dist-server/domain/agent/agent.controller.js +14 -0
  49. package/dist-server/domain/agent/agent.service.js +246 -18
  50. package/dist-server/domain/agent/dto/create-agent.dto.js +12 -0
  51. package/dist-server/domain/agent/dto/remote-provider.dto.js +82 -0
  52. package/dist-server/domain/agent/dto/reorder-agents.dto.js +51 -0
  53. package/dist-server/domain/agent/dto/update-agent.dto.js +3 -1
  54. package/dist-server/domain/agent/provider.controller.js +12 -1
  55. package/dist-server/domain/doc/doc.controller.js +43 -9
  56. package/dist-server/domain/doc/doc.service.js +80 -6
  57. package/dist-server/domain/doc/image-constants.js +11 -1
  58. package/dist-server/domain/events/workspace-events.controller.js +2 -0
  59. package/dist-server/domain/message/dto/send-message.dto.js +49 -1
  60. package/dist-server/domain/message/message.controller.js +1 -1
  61. package/dist-server/domain/message/message.module.js +2 -1
  62. package/dist-server/domain/message/message.service.js +83 -13
  63. package/dist-server/domain/settings/settings.service.js +188 -1
  64. package/dist-server/domain/system/system.controller.js +41 -0
  65. package/dist-server/domain/system/system.module.js +19 -0
  66. package/dist-server/domain/task/task.constants.js +8 -1
  67. package/dist-server/domain/task/task.service.js +59 -67
  68. package/dist-server/domain/thread/dto/list-threads.dto.js +18 -0
  69. package/dist-server/domain/thread/overdrive.types.js +38 -0
  70. package/dist-server/domain/thread/thread.service.js +83 -70
  71. package/dist-server/domain/usage/usage.controller.js +18 -0
  72. package/dist-server/domain/usage/usage.module.js +1 -0
  73. package/dist-server/domain/usage/usage.service.js +246 -4
  74. package/dist-server/domain/usage-report/dto/usage-report.dto.js +2 -0
  75. package/dist-server/domain/usage-report/usage-report.controller.js +72 -0
  76. package/dist-server/domain/usage-report/usage-report.module.js +23 -0
  77. package/dist-server/domain/usage-report/usage-report.service.js +135 -0
  78. package/dist-server/domain/wi/wi.service.js +45 -3
  79. package/dist-server/main.js +1 -0
  80. package/dist-server/repository/repository.module.js +2 -0
  81. package/dist-server/repository/task.repository.js +56 -0
  82. package/dist-server/repository/thread.repository.js +13 -0
  83. package/dist-server/repository/usage-report.repository.js +27 -0
  84. package/package.json +9 -9
  85. package/packages/cli/dist/bootstrap/codex-writable-roots.d.ts +45 -0
  86. package/packages/cli/dist/bootstrap/codex-writable-roots.js +88 -0
  87. package/packages/cli/dist/bootstrap/crewx-cli.js +2 -0
  88. package/packages/cli/dist/commands/execute.js +10 -2
  89. package/packages/cli/dist/commands/parse-common-flags.d.ts +5 -1
  90. package/packages/cli/dist/commands/parse-common-flags.js +9 -2
  91. package/packages/cli/dist/commands/ps.js +51 -2
  92. package/packages/cli/dist/commands/query.js +10 -2
  93. package/packages/cli/dist/commands/restart.js +20 -6
  94. package/packages/cli/dist/commands/write-output.d.ts +3 -0
  95. package/packages/cli/dist/commands/write-output.js +24 -0
  96. package/packages/cli/dist/main.js +7 -0
  97. package/packages/cli/package.json +1 -1
  98. package/dist/assets/MarketPage-T1o_oIrw.js +0 -41
  99. package/dist/assets/channel-7q16D7P9.js +0 -1
  100. package/dist/assets/classDiagram-LNE6IOMH-C6qjk4uc.js +0 -1
  101. package/dist/assets/classDiagram-v2-MQ7JQ4JX-C6qjk4uc.js +0 -1
  102. package/dist/assets/main-C5mwv79r.css +0 -10
  103. package/dist/assets/main-DmvFaz-d.js +0 -1486
  104. package/dist/assets/stateDiagram-v2-4JROLMXI-CCMApx_v.js +0 -1
@@ -44,11 +44,28 @@ var __metadata = (this && this.__metadata) || function (k, v) {
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.SettingsService = void 0;
46
46
  const fs_1 = require("fs");
47
+ const node_path_1 = require("node:path");
47
48
  const common_1 = require("@nestjs/common");
48
49
  const yaml = __importStar(require("js-yaml"));
50
+ const knowledge_core_1 = require("@crewx/knowledge-core");
49
51
  const analytics_client_js_1 = require("../../common/analytics.client.js");
50
52
  const crewx_pool_service_js_1 = require("../../modules/crewx-pool.service.js");
51
53
  const workspace_context_store_js_1 = require("../../common/workspace-context.store.js");
54
+ const DEFAULT_OVERDRIVE_TURNS = 3;
55
+ // wbs defaults — mirrors packages/built-in/wbs/src/engine.ts:58-78 (getSourceBranch/getTargetBranch/getMergeStrategy)
56
+ const DEFAULT_WBS_SOURCE_BRANCH = 'auto';
57
+ const DEFAULT_WBS_TARGET_BRANCH = 'auto';
58
+ const DEFAULT_WBS_MERGE_STRATEGY = 'local';
59
+ // memory defaults — mirrors packages/built-in/memory/src/engine.ts:130-163 (MemoryEngine constructor)
60
+ const DEFAULT_MEMORY_SUMMARY_DAYS = 7;
61
+ const DEFAULT_MEMORY_RECENT_DAYS = 30;
62
+ const DEFAULT_MEMORY_SHORT_TERM_HOURS = 24;
63
+ const DEFAULT_MEMORY_SUMMARIZER_AGENT = '@memory_summarizer';
64
+ const DEFAULT_MEMORY_SEARCHER_AGENT = '@memory_searcher';
65
+ const DEFAULT_MEMORY_SUMMARIZER_TIMEOUT = 60000;
66
+ const DEFAULT_MEMORY_SEARCHER_TIMEOUT = 300000;
67
+ // workflow defaults — mirrors packages/built-in/workflow/cli.ts:88-97 (getWorkflowConfig)
68
+ const DEFAULT_WORKFLOW_PATHS = ['workflows'];
52
69
  let SettingsService = class SettingsService {
53
70
  analyticsService;
54
71
  pool;
@@ -57,17 +74,28 @@ let SettingsService = class SettingsService {
57
74
  this.pool = pool;
58
75
  }
59
76
  get configPath() {
60
- return this.pool.resolveConfigPath((0, workspace_context_store_js_1.getWorkspacePath)()) ?? 'crewx.yaml';
77
+ const workspacePath = (0, workspace_context_store_js_1.getWorkspacePath)();
78
+ return this.pool.resolveConfigPath(workspacePath) ?? (0, node_path_1.join)(workspacePath, 'crewx.yaml');
61
79
  }
62
80
  getSettings() {
63
81
  const config = this.loadConfig();
64
82
  const settings = (config.settings ?? {});
65
83
  const dreaming = (settings.dreaming ?? {});
66
84
  const continuity = (dreaming.continuity ?? {});
85
+ const overdrive = (settings.overdrive ?? {});
67
86
  const template = (settings.template ?? {});
68
87
  const exec = (template.exec ?? {});
88
+ const wbs = (settings.wbs ?? {});
89
+ const docs = (settings.docs ?? {});
90
+ const search = (settings.search ?? {});
91
+ const searchCache = (search.cache ?? {});
92
+ const memory = (settings.memory ?? {});
93
+ const workflow = (settings.workflow ?? {});
69
94
  return {
70
95
  analytics: settings.analytics !== false,
96
+ overdrive: {
97
+ defaultTurns: this.normalizeOverdriveTurns(overdrive.defaultTurns),
98
+ },
71
99
  continuity: {
72
100
  lookback: typeof continuity.lookback === 'number' ? continuity.lookback : 3,
73
101
  reuseReadOnly: continuity.reuseReadOnly !== false,
@@ -86,6 +114,36 @@ let SettingsService = class SettingsService {
86
114
  timeout: typeof exec.timeout === 'number' ? exec.timeout : 30,
87
115
  allow: Array.isArray(exec.allow) ? exec.allow : [],
88
116
  },
117
+ wbs: {
118
+ sourceBranch: typeof wbs.source_branch === 'string' ? wbs.source_branch : DEFAULT_WBS_SOURCE_BRANCH,
119
+ targetBranch: typeof wbs.target_branch === 'string' ? wbs.target_branch : DEFAULT_WBS_TARGET_BRANCH,
120
+ mergeStrategy: wbs.merge_strategy === 'pr' ? 'pr' : DEFAULT_WBS_MERGE_STRATEGY,
121
+ },
122
+ docs: {
123
+ extraRoots: Array.isArray(docs.extra_roots) ? docs.extra_roots : [],
124
+ rootFiles: Array.isArray(docs.root_files) ? docs.root_files : [...knowledge_core_1.DEFAULT_ROOT_FILES],
125
+ exclude: Array.isArray(docs.exclude) ? docs.exclude : [...knowledge_core_1.DEFAULT_EXCLUDE],
126
+ },
127
+ search: {
128
+ cache: {
129
+ ttl: typeof searchCache.ttl === 'number' ? searchCache.ttl : knowledge_core_1.DEFAULT_SEARCH_CONFIG.cache.ttl,
130
+ max: typeof searchCache.max === 'number' ? searchCache.max : knowledge_core_1.DEFAULT_SEARCH_CONFIG.cache.max,
131
+ },
132
+ },
133
+ memory: {
134
+ summaryDays: typeof memory.summary_days === 'number' ? memory.summary_days : DEFAULT_MEMORY_SUMMARY_DAYS,
135
+ recentDays: typeof memory.recent_days === 'number' ? memory.recent_days : DEFAULT_MEMORY_RECENT_DAYS,
136
+ shortTermHours: typeof memory.short_term_hours === 'number' ? memory.short_term_hours : DEFAULT_MEMORY_SHORT_TERM_HOURS,
137
+ summarizerAgent: typeof memory.summarizer_agent === 'string' ? memory.summarizer_agent : DEFAULT_MEMORY_SUMMARIZER_AGENT,
138
+ searcherAgent: typeof memory.searcher_agent === 'string' ? memory.searcher_agent : DEFAULT_MEMORY_SEARCHER_AGENT,
139
+ summarizerTimeout: typeof memory.summarizer_timeout === 'number'
140
+ ? memory.summarizer_timeout
141
+ : DEFAULT_MEMORY_SUMMARIZER_TIMEOUT,
142
+ searcherTimeout: typeof memory.searcher_timeout === 'number' ? memory.searcher_timeout : DEFAULT_MEMORY_SEARCHER_TIMEOUT,
143
+ },
144
+ workflow: {
145
+ paths: Array.isArray(workflow.paths) ? workflow.paths : [...DEFAULT_WORKFLOW_PATHS],
146
+ },
89
147
  };
90
148
  }
91
149
  updateSettings(patch) {
@@ -160,6 +218,130 @@ let SettingsService = class SettingsService {
160
218
  }
161
219
  dirty = true;
162
220
  }
221
+ if (patch.overdrive !== undefined) {
222
+ if (!config.settings || typeof config.settings !== 'object') {
223
+ config.settings = {};
224
+ }
225
+ const settings = config.settings;
226
+ if (!settings.overdrive || typeof settings.overdrive !== 'object') {
227
+ settings.overdrive = {};
228
+ }
229
+ const overdrive = settings.overdrive;
230
+ if (patch.overdrive.defaultTurns !== undefined) {
231
+ const val = patch.overdrive.defaultTurns;
232
+ if (Number.isInteger(val) && val > 0) {
233
+ overdrive.defaultTurns = val;
234
+ }
235
+ }
236
+ dirty = true;
237
+ }
238
+ if (patch.wbs !== undefined) {
239
+ if (!config.settings || typeof config.settings !== 'object') {
240
+ config.settings = {};
241
+ }
242
+ const settings = config.settings;
243
+ if (!settings.wbs || typeof settings.wbs !== 'object') {
244
+ settings.wbs = {};
245
+ }
246
+ const wbs = settings.wbs;
247
+ if (patch.wbs.sourceBranch !== undefined) {
248
+ wbs.source_branch = patch.wbs.sourceBranch;
249
+ }
250
+ if (patch.wbs.targetBranch !== undefined) {
251
+ wbs.target_branch = patch.wbs.targetBranch;
252
+ }
253
+ if (patch.wbs.mergeStrategy !== undefined) {
254
+ wbs.merge_strategy = patch.wbs.mergeStrategy;
255
+ }
256
+ dirty = true;
257
+ }
258
+ if (patch.docs !== undefined) {
259
+ if (!config.settings || typeof config.settings !== 'object') {
260
+ config.settings = {};
261
+ }
262
+ const settings = config.settings;
263
+ if (!settings.docs || typeof settings.docs !== 'object') {
264
+ settings.docs = {};
265
+ }
266
+ const docs = settings.docs;
267
+ if (patch.docs.extraRoots !== undefined) {
268
+ docs.extra_roots = patch.docs.extraRoots;
269
+ }
270
+ if (patch.docs.rootFiles !== undefined) {
271
+ docs.root_files = patch.docs.rootFiles;
272
+ }
273
+ if (patch.docs.exclude !== undefined) {
274
+ docs.exclude = patch.docs.exclude;
275
+ }
276
+ dirty = true;
277
+ }
278
+ if (patch.search?.cache !== undefined) {
279
+ if (!config.settings || typeof config.settings !== 'object') {
280
+ config.settings = {};
281
+ }
282
+ const settings = config.settings;
283
+ if (!settings.search || typeof settings.search !== 'object') {
284
+ settings.search = {};
285
+ }
286
+ const search = settings.search;
287
+ if (!search.cache || typeof search.cache !== 'object') {
288
+ search.cache = {};
289
+ }
290
+ const cache = search.cache;
291
+ if (patch.search.cache.ttl !== undefined) {
292
+ cache.ttl = patch.search.cache.ttl;
293
+ }
294
+ if (patch.search.cache.max !== undefined) {
295
+ cache.max = patch.search.cache.max;
296
+ }
297
+ dirty = true;
298
+ }
299
+ if (patch.memory !== undefined) {
300
+ if (!config.settings || typeof config.settings !== 'object') {
301
+ config.settings = {};
302
+ }
303
+ const settings = config.settings;
304
+ if (!settings.memory || typeof settings.memory !== 'object') {
305
+ settings.memory = {};
306
+ }
307
+ const memory = settings.memory;
308
+ if (patch.memory.summaryDays !== undefined) {
309
+ memory.summary_days = patch.memory.summaryDays;
310
+ }
311
+ if (patch.memory.recentDays !== undefined) {
312
+ memory.recent_days = patch.memory.recentDays;
313
+ }
314
+ if (patch.memory.shortTermHours !== undefined) {
315
+ memory.short_term_hours = patch.memory.shortTermHours;
316
+ }
317
+ if (patch.memory.summarizerAgent !== undefined) {
318
+ memory.summarizer_agent = patch.memory.summarizerAgent;
319
+ }
320
+ if (patch.memory.searcherAgent !== undefined) {
321
+ memory.searcher_agent = patch.memory.searcherAgent;
322
+ }
323
+ if (patch.memory.summarizerTimeout !== undefined) {
324
+ memory.summarizer_timeout = patch.memory.summarizerTimeout;
325
+ }
326
+ if (patch.memory.searcherTimeout !== undefined) {
327
+ memory.searcher_timeout = patch.memory.searcherTimeout;
328
+ }
329
+ dirty = true;
330
+ }
331
+ if (patch.workflow !== undefined) {
332
+ if (!config.settings || typeof config.settings !== 'object') {
333
+ config.settings = {};
334
+ }
335
+ const settings = config.settings;
336
+ if (!settings.workflow || typeof settings.workflow !== 'object') {
337
+ settings.workflow = {};
338
+ }
339
+ const workflow = settings.workflow;
340
+ if (patch.workflow.paths !== undefined) {
341
+ workflow.paths = patch.workflow.paths;
342
+ }
343
+ dirty = true;
344
+ }
163
345
  if (dirty) {
164
346
  this.saveConfig(config);
165
347
  if (analyticsValue !== undefined) {
@@ -168,6 +350,11 @@ let SettingsService = class SettingsService {
168
350
  }
169
351
  return this.getSettings();
170
352
  }
353
+ normalizeOverdriveTurns(value) {
354
+ return Number.isInteger(value) && value > 0
355
+ ? value
356
+ : DEFAULT_OVERDRIVE_TURNS;
357
+ }
171
358
  loadConfig() {
172
359
  if (!(0, fs_1.existsSync)(this.configPath))
173
360
  return {};
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SystemController = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const swagger_1 = require("@nestjs/swagger");
15
+ let SystemController = class SystemController {
16
+ shutdown() {
17
+ // Delay the signal to the next tick so the 202 response reaches the
18
+ // client before shutdown begins. Send SIGTERM to self instead of
19
+ // process.exit(0) so Nest's shutdown hooks (enableShutdownHooks in
20
+ // main.ts) run and onApplicationShutdown -> crewx.close() can clean up
21
+ // active tasks gracefully. forceCloseConnections (see main.ts) forces
22
+ // any lingering keep-alive/SSE connections closed once the process exits.
23
+ setTimeout(() => {
24
+ process.kill(process.pid, 'SIGTERM');
25
+ }, 100);
26
+ return { success: true, data: { message: 'Server is shutting down' } };
27
+ }
28
+ };
29
+ exports.SystemController = SystemController;
30
+ __decorate([
31
+ (0, swagger_1.ApiOperation)({ operationId: 'SYS.SHUTDOWN', summary: 'Shut down the server' }),
32
+ (0, common_1.Post)('shutdown'),
33
+ (0, common_1.HttpCode)(202),
34
+ __metadata("design:type", Function),
35
+ __metadata("design:paramtypes", []),
36
+ __metadata("design:returntype", void 0)
37
+ ], SystemController.prototype, "shutdown", null);
38
+ exports.SystemController = SystemController = __decorate([
39
+ (0, swagger_1.ApiTags)('system'),
40
+ (0, common_1.Controller)('system')
41
+ ], SystemController);
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.SystemModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const system_controller_js_1 = require("./system.controller.js");
12
+ let SystemModule = class SystemModule {
13
+ };
14
+ exports.SystemModule = SystemModule;
15
+ exports.SystemModule = SystemModule = __decorate([
16
+ (0, common_1.Module)({
17
+ controllers: [system_controller_js_1.SystemController],
18
+ })
19
+ ], SystemModule);
@@ -1,5 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_TASK_LOG_LIMIT = exports.DEFAULT_TASK_LOG_LIMIT = void 0;
3
+ exports.OWNED_PROCESS_MARKERS = exports.MAX_TASK_LOG_LIMIT = exports.DEFAULT_TASK_LOG_LIMIT = void 0;
4
4
  exports.DEFAULT_TASK_LOG_LIMIT = 1000;
5
5
  exports.MAX_TASK_LOG_LIMIT = 1000;
6
+ /**
7
+ * Markers used to verify a PID is owned by a crewx-spawned process before
8
+ * sending it a kill signal. This is a safety check against PID reuse — it is
9
+ * NOT a provider whitelist. Any provider CLI that crewx can spawn must have
10
+ * its command name (or a wrapper token) listed here.
11
+ */
12
+ exports.OWNED_PROCESS_MARKERS = ['crewx', 'claude', 'codex', 'opencode', 'node'];
@@ -22,6 +22,7 @@ const sdk_1 = require("@crewx/sdk");
22
22
  const task_repository_js_1 = require("../../repository/task.repository.js");
23
23
  const project_repository_js_1 = require("../../repository/project.repository.js");
24
24
  const workspace_context_store_js_1 = require("../../common/workspace-context.store.js");
25
+ const stdout_json_fallback_js_1 = require("../../common/task-log/stdout-json-fallback.js");
25
26
  const crewx_pool_service_js_1 = require("../../modules/crewx-pool.service.js");
26
27
  const MAX_LOG_ENTRY_FIELD_LEN = 128 * 1024; // 128KB worth of UTF-16 code units
27
28
  const RUNNING_INSTRUCTION_SYSTEM_MESSAGE = '<crewx_system_message type="running_instruction">\n' +
@@ -140,7 +141,7 @@ let TaskService = TaskService_1 = class TaskService {
140
141
  // PID ownership verification: ensure PID belongs to a crewx-related process
141
142
  try {
142
143
  const cmdOutput = (0, child_process_1.execSync)(`ps -p ${task.pid} -o command=`, { encoding: 'utf-8', timeout: 3000 }).trim();
143
- if (!cmdOutput.includes('crewx') && !cmdOutput.includes('claude') && !cmdOutput.includes('node')) {
144
+ if (!this.isOwnedProcess(cmdOutput)) {
144
145
  throw new common_1.HttpException({ code: 'PID_NOT_FOUND', message: 'Process ID not found' }, 422);
145
146
  }
146
147
  }
@@ -200,10 +201,12 @@ let TaskService = TaskService_1 = class TaskService {
200
201
  }
201
202
  const metadata = this.parseTaskMetadata(task.metadata);
202
203
  const provider = this.extractProviderFromMeta(task.metadata);
204
+ const wasOverdrive = metadata.overdrive === true;
205
+ const overdriveState = metadata.overdriveState === 'latch' ? 'latch' : 'count';
203
206
  const providerSessionId = this.readString(metadata.provider_session_id)
204
207
  ?? this.readString(metadata.providerSessionId)
205
208
  ?? this.extractProviderSessionIdFromLogs(task.logs);
206
- if (!provider || (provider !== 'cli/codex' && provider !== 'cli/claude')) {
209
+ if (!provider || (provider !== 'cli/codex' && provider !== 'cli/claude' && provider !== 'cli/opencode')) {
207
210
  throw new common_1.HttpException({ code: 'PROVIDER_UNSUPPORTED', message: 'Provider does not support running instructions' }, 422);
208
211
  }
209
212
  if (!providerSessionId) {
@@ -242,6 +245,7 @@ let TaskService = TaskService_1 = class TaskService {
242
245
  threadId,
243
246
  taskId,
244
247
  provider,
248
+ model: task.model ?? undefined,
245
249
  platform: 'cli',
246
250
  resumeSessionId: providerSessionId,
247
251
  metadata: {
@@ -250,6 +254,11 @@ let TaskService = TaskService_1 = class TaskService {
250
254
  provider,
251
255
  provider_session_id: providerSessionId,
252
256
  runningInstruction: instruction,
257
+ ...(wasOverdrive ? {
258
+ overdrive: true,
259
+ overdriveState,
260
+ overdriveMode: 'execute',
261
+ } : {}),
253
262
  // Tags this run's task:end so finishTask only closes the matching
254
263
  // generation, ignoring the killed prior run's stale task:end.
255
264
  runEpoch,
@@ -257,6 +266,15 @@ let TaskService = TaskService_1 = class TaskService {
257
266
  cwd: (0, workspace_context_store_js_1.getWorkspacePath)(),
258
267
  }).catch((err) => {
259
268
  this.logger.error(`Running instruction resume failed [task=${taskId}]: ${err.message}`, err.stack);
269
+ // Safety net: if execute() rejected before the new process ever reported
270
+ // its pid (task:start), the row would be a permanent zombie
271
+ // (status=running, pid=NULL). Only transition to failed if the row is
272
+ // still exactly in that pending-resume state — if a new process already
273
+ // reported its pid, or a later instruction bumped run_epoch again, this
274
+ // is a no-op so we never clobber a task that already recovered.
275
+ if (runEpoch != null) {
276
+ this.taskRepo.markTaskFailedIfResumePending(taskId, runEpoch, `Running instruction resume failed: ${err.message}`, workspaceId);
277
+ }
260
278
  });
261
279
  return { taskId, status: 'running', agentId: task.agent_id, resumedFromTaskId: taskId };
262
280
  }
@@ -445,7 +463,7 @@ let TaskService = TaskService_1 = class TaskService {
445
463
  }
446
464
  const adapterKey = this.extractAdapterKey(row);
447
465
  if (wantLogs) {
448
- result.logs = this.parseLogs(row.logs, logLimit, adapterKey);
466
+ result.logs = this.parseLogs(row.logs, logLimit, adapterKey).logs;
449
467
  }
450
468
  result.toolUsage = this.aggregateToolUsage(row.logs, adapterKey);
451
469
  if (wantChildren) {
@@ -493,7 +511,7 @@ let TaskService = TaskService_1 = class TaskService {
493
511
  }
494
512
  // --- TSK005 private helpers ---
495
513
  toChildTaskStatus(row) {
496
- const logs = this.parseLogs(row.logs, 1, this.extractAdapterKey(row));
514
+ const { logs } = this.parseLogs(row.logs, 1, this.extractAdapterKey(row));
497
515
  return {
498
516
  taskId: row.id,
499
517
  agentId: row.agent_id,
@@ -515,19 +533,19 @@ let TaskService = TaskService_1 = class TaskService {
515
533
  return undefined;
516
534
  }
517
535
  }
518
- /** @see ThreadService.parseLogs — 동일 역할, ACP 파싱 로직 동기화 필요 */
536
+ /** @see ThreadService.parseLogs — 동일 역할, ACP 파싱 로직 동기화 필요 (TSK.GET 응답에는 progressSteps 미포함) */
519
537
  parseLogs(logsJson, logLimit, providerId) {
520
538
  if (!logsJson)
521
- return [];
539
+ return { logs: [], progressSteps: [] };
522
540
  let rawLogs;
523
541
  try {
524
542
  rawLogs = JSON.parse(logsJson);
525
543
  }
526
544
  catch {
527
- return [];
545
+ return { logs: [], progressSteps: [] };
528
546
  }
529
547
  if (!Array.isArray(rawLogs))
530
- return [];
548
+ return { logs: [], progressSteps: [] };
531
549
  const entries = [];
532
550
  for (const log of rawLogs) {
533
551
  if (log.level === 'stdout') {
@@ -539,7 +557,7 @@ let TaskService = TaskService_1 = class TaskService {
539
557
  }
540
558
  // Parsed successfully but no displayable entries (e.g. thinking-only assistant message).
541
559
  // Skip fallback if the raw message is a known metadata-only stream-json event type.
542
- if (this.shouldSkipRawStdoutJsonFallback(log.message))
560
+ if ((0, stdout_json_fallback_js_1.shouldSkipRawStdoutJsonFallback)(log.message, providerId))
543
561
  continue;
544
562
  }
545
563
  catch { /* adapter parse failure — fallback to text */ }
@@ -555,18 +573,17 @@ let TaskService = TaskService_1 = class TaskService {
555
573
  }
556
574
  }
557
575
  }
558
- // Pipeline: merge consecutive text → deduplicate tool_use → truncate → logLimit slice
576
+ // Pipeline: merge consecutive text → deduplicate tool_use → truncate → [branch: text-only slice(-200) = progressSteps] → logLimit slice = logs
559
577
  const merged = this.mergeConsecutiveText(entries);
560
578
  const deduped = this.deduplicateToolUse(merged);
561
579
  const truncated = deduped.map((e) => this.truncateEntry(e));
580
+ const progressSteps = truncated.filter((e) => e.type === 'text').slice(-200);
562
581
  // Return the latest N entries (trim from the beginning)
563
- if (truncated.length > logLimit) {
564
- return truncated.slice(truncated.length - logLimit);
565
- }
566
- return truncated;
582
+ const logs = truncated.length > logLimit ? truncated.slice(truncated.length - logLimit) : truncated;
583
+ return { logs, progressSteps };
567
584
  }
568
585
  aggregateToolUsage(logsJson, providerId) {
569
- const entries = this.parseLogs(logsJson, task_constants_js_1.MAX_TASK_LOG_LIMIT, providerId);
586
+ const { logs: entries } = this.parseLogs(logsJson, task_constants_js_1.MAX_TASK_LOG_LIMIT, providerId);
570
587
  const counts = new Map();
571
588
  for (const entry of entries) {
572
589
  if (entry.type === 'tool_use' && entry.toolName) {
@@ -639,6 +656,11 @@ let TaskService = TaskService_1 = class TaskService {
639
656
  if (claudeSessionId) {
640
657
  return claudeSessionId;
641
658
  }
659
+ // OpenCode: every event carries a top-level `sessionID` (e.g. step_start/tool_use/text).
660
+ const opencodeSessionId = this.readString(message.sessionID);
661
+ if (opencodeSessionId) {
662
+ return opencodeSessionId;
663
+ }
642
664
  }
643
665
  catch {
644
666
  // Ignore non-JSON log lines.
@@ -674,6 +696,9 @@ let TaskService = TaskService_1 = class TaskService {
674
696
  if (providerKey === 'codex' && this.isCodexResumableEvent(message)) {
675
697
  return true;
676
698
  }
699
+ if (providerKey === 'opencode' && this.isOpencodeResumableEvent(message)) {
700
+ return true;
701
+ }
677
702
  }
678
703
  catch {
679
704
  // Ignore non-JSON log lines.
@@ -681,6 +706,18 @@ let TaskService = TaskService_1 = class TaskService {
681
706
  }
682
707
  return false;
683
708
  }
709
+ isOpencodeResumableEvent(message) {
710
+ const type = typeof message.type === 'string' ? message.type : '';
711
+ if (type === 'tool_use') {
712
+ return true;
713
+ }
714
+ if (type === 'text') {
715
+ const part = message.part;
716
+ const text = part?.text;
717
+ return typeof text === 'string' && text.trim().length > 0;
718
+ }
719
+ return false;
720
+ }
684
721
  isClaudeResumableEvent(message) {
685
722
  if (message.type !== 'assistant') {
686
723
  return false;
@@ -717,13 +754,18 @@ let TaskService = TaskService_1 = class TaskService {
717
754
  }
718
755
  return false;
719
756
  }
757
+ /** PID ownership check: is this `ps -o command=` output a process we're allowed to kill?
758
+ * Guards against PID reuse — NOT a provider whitelist (see OWNED_PROCESS_MARKERS doc). */
759
+ isOwnedProcess(cmdOutput) {
760
+ return task_constants_js_1.OWNED_PROCESS_MARKERS.some((marker) => cmdOutput.includes(marker));
761
+ }
720
762
  terminateRunningProcess(task) {
721
763
  if (task.pid == null) {
722
764
  throw new common_1.HttpException({ code: 'PID_NOT_FOUND', message: 'Process ID not found' }, 422);
723
765
  }
724
766
  try {
725
767
  const cmdOutput = (0, child_process_1.execSync)(`ps -p ${task.pid} -o command=`, { encoding: 'utf-8', timeout: 3000 }).trim();
726
- if (!cmdOutput.includes('crewx') && !cmdOutput.includes('claude') && !cmdOutput.includes('codex') && !cmdOutput.includes('node')) {
768
+ if (!this.isOwnedProcess(cmdOutput)) {
727
769
  throw new common_1.HttpException({ code: 'PID_NOT_FOUND', message: 'Process ID not found' }, 422);
728
770
  }
729
771
  }
@@ -868,7 +910,7 @@ let TaskService = TaskService_1 = class TaskService {
868
910
  // Merge logs from all matching tasks
869
911
  const allLogs = [];
870
912
  for (const task of matchingTasks) {
871
- const parsed = this.parseLogs(task.logs, 100, this.extractAdapterKey(task));
913
+ const { logs: parsed } = this.parseLogs(task.logs, 100, this.extractAdapterKey(task));
872
914
  allLogs.push(...parsed);
873
915
  }
874
916
  // Sort by timestamp
@@ -971,56 +1013,6 @@ let TaskService = TaskService_1 = class TaskService {
971
1013
  return text;
972
1014
  return text.slice(0, maxLen) + '...';
973
1015
  }
974
- /**
975
- * Returns true when the raw stdout line is a known metadata-only JSON event that
976
- * should be silently dropped rather than shown as raw text.
977
- *
978
- * Covers Claude, Codex, and OpenCode stream-json protocols.
979
- * Only called after parseStdoutEvent() returns [] (no displayable entries).
980
- * Partial / truncated JSON → JSON.parse throws → returns false (keep raw text fallback).
981
- */
982
- shouldSkipRawStdoutJsonFallback(message) {
983
- const trimmed = message.trimStart();
984
- if (!trimmed.startsWith('{'))
985
- return false;
986
- let obj;
987
- try {
988
- obj = JSON.parse(trimmed);
989
- }
990
- catch {
991
- return false;
992
- }
993
- const type = obj.type;
994
- if (typeof type !== 'string')
995
- return false;
996
- // Claude stream-json metadata events
997
- if (type === 'assistant' ||
998
- type === 'user' ||
999
- type === 'system' ||
1000
- type === 'result' ||
1001
- type === 'rate_limit_event') {
1002
- return true;
1003
- }
1004
- // Codex metadata-only events
1005
- if (type === 'thread.started' ||
1006
- type === 'turn.started' ||
1007
- type === 'turn.completed' ||
1008
- type === 'turn.failed' ||
1009
- type === 'item.updated') {
1010
- return true;
1011
- }
1012
- // Codex item.started/item.completed reach this function only after
1013
- // parseStdoutEvent() returned no displayable entries. Skip raw fallback while
1014
- // preserving the original line in tasks.logs for future reprocessing.
1015
- if (type === 'item.started' || type === 'item.completed') {
1016
- return true;
1017
- }
1018
- // OpenCode metadata-only events
1019
- if (type === 'step_start' || type === 'step_finish') {
1020
- return true;
1021
- }
1022
- return false;
1023
- }
1024
1016
  normalizeLimit(limit, fallback, max) {
1025
1017
  const num = Number(limit);
1026
1018
  if (!Number.isFinite(num) || num <= 0)
@@ -22,6 +22,7 @@ class ListThreadsDto {
22
22
  order;
23
23
  limit;
24
24
  offset;
25
+ pinnedFirst;
25
26
  }
26
27
  exports.ListThreadsDto = ListThreadsDto;
27
28
  __decorate([
@@ -68,3 +69,20 @@ __decorate([
68
69
  (0, class_validator_1.IsNumber)(),
69
70
  __metadata("design:type", Number)
70
71
  ], ListThreadsDto.prototype, "offset", void 0);
72
+ __decorate([
73
+ (0, swagger_1.ApiPropertyOptional)({
74
+ description: 'Whether pinned threads are always shown first, unaffected by pagination (default: true)',
75
+ }),
76
+ (0, class_validator_1.IsOptional)(),
77
+ (0, class_transformer_1.Transform)(({ value }) => {
78
+ if (typeof value === 'boolean')
79
+ return value;
80
+ if (value === 'false')
81
+ return false;
82
+ if (value === 'true')
83
+ return true;
84
+ return value;
85
+ }),
86
+ (0, class_validator_1.IsBoolean)(),
87
+ __metadata("design:type", Boolean)
88
+ ], ListThreadsDto.prototype, "pinnedFirst", void 0);
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ /**
3
+ * Overdrive (부스트/각성 모드) state model.
4
+ *
5
+ * User-facing term: Boost / 부스트 / 갉성 모드
6
+ * Internal key: overdrive
7
+ *
8
+ * The overdrive state is stored under `thread.metadata.overdrive`. Web chat
9
+ * uses the count state as a multi-request boost; CLI flags can still trigger a
10
+ * single boosted request without requiring a persistent UI session.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.OVERDRIVE_DEFAULT_TURNS = void 0;
14
+ exports.parseOverdriveSummary = parseOverdriveSummary;
15
+ const DEFAULT_TURNS = 3;
16
+ /** Parse a raw thread metadata blob into a normalized OverdriveSummary. */
17
+ function parseOverdriveSummary(metadataJson) {
18
+ if (!metadataJson) {
19
+ return { state: 'off', remaining: 0 };
20
+ }
21
+ try {
22
+ const meta = JSON.parse(metadataJson);
23
+ const od = meta?.overdrive;
24
+ if (!od || typeof od !== 'object') {
25
+ return { state: 'off', remaining: 0 };
26
+ }
27
+ const state = (od.state ?? 'off');
28
+ const remaining = typeof od.remaining === 'number' ? od.remaining : 0;
29
+ if (state === 'count' && remaining <= 0) {
30
+ return { state: 'off', remaining: 0 };
31
+ }
32
+ return { state, remaining };
33
+ }
34
+ catch {
35
+ return { state: 'off', remaining: 0 };
36
+ }
37
+ }
38
+ exports.OVERDRIVE_DEFAULT_TURNS = DEFAULT_TURNS;