crewx 0.8.9 → 0.9.0-rc.0

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 (57) hide show
  1. package/bin/cli-commands.js +1 -1
  2. package/dist/assets/{MarketPage-Bk13HBH8.js → MarketPage-DcsjTevG.js} +1 -1
  3. package/dist/assets/{PromptTab-BROKndtk.js → PromptTab-BrYbpZgD.js} +1 -1
  4. package/dist/assets/{arc-8uTktI_9.js → arc-C8trOm68.js} +1 -1
  5. package/dist/assets/{architectureDiagram-UYN6MBPD-CJyGJizJ.js → architectureDiagram-UYN6MBPD-BhulQbll.js} +1 -1
  6. package/dist/assets/{blockDiagram-ZHA2E4KO-tb7_qUXV.js → blockDiagram-ZHA2E4KO-BBGijVDJ.js} +1 -1
  7. package/dist/assets/{c4Diagram-6F5ED5ID-Bh0hYgUZ.js → c4Diagram-6F5ED5ID-BFc7Kmvi.js} +1 -1
  8. package/dist/assets/channel-CldZ_Wzz.js +1 -0
  9. package/dist/assets/{chunk-5HRBRIJM-Ctb_uxZ5.js → chunk-5HRBRIJM-D2aGr_3q.js} +1 -1
  10. package/dist/assets/{chunk-7U56Z5CX-h1fDz-WT.js → chunk-7U56Z5CX-CYHfdi4H.js} +1 -1
  11. package/dist/assets/{chunk-ASOPGD6M-CgfyXIoW.js → chunk-ASOPGD6M-BAuVIDQR.js} +1 -1
  12. package/dist/assets/{chunk-KFBOBJHC-CR7Ttw70.js → chunk-KFBOBJHC-Dp9z-742.js} +1 -1
  13. package/dist/assets/{chunk-T2TOU4HS-C2u5B7pp.js → chunk-T2TOU4HS-CWW5BN2j.js} +1 -1
  14. package/dist/assets/{chunk-TMUBEWPD-x_AdOhrN.js → chunk-TMUBEWPD-DZBJaaAc.js} +1 -1
  15. package/dist/assets/classDiagram-LNE6IOMH-CVRBmdDQ.js +1 -0
  16. package/dist/assets/classDiagram-v2-MQ7JQ4JX-CVRBmdDQ.js +1 -0
  17. package/dist/assets/{dagre-4EVJKHTY-BfYeEJwg.js → dagre-4EVJKHTY-DTS2hGsu.js} +1 -1
  18. package/dist/assets/{diagram-QW4FP2JN-Bkxhu2Ro.js → diagram-QW4FP2JN-BrdV6-yQ.js} +1 -1
  19. package/dist/assets/{erDiagram-6RL3IURR-CEJ086-x.js → erDiagram-6RL3IURR-DrFMd6eu.js} +1 -1
  20. package/dist/assets/{flowDiagram-7ASYPVHJ-DpYeXRcJ.js → flowDiagram-7ASYPVHJ-Ctz5vjkO.js} +1 -1
  21. package/dist/assets/{ganttDiagram-NTVNEXSI-DPWdP6sZ.js → ganttDiagram-NTVNEXSI-Bb6rEfpn.js} +1 -1
  22. package/dist/assets/{gitGraph-YCYPL57B-nXetpL58.js → gitGraph-YCYPL57B-D8MdWNT2.js} +1 -1
  23. package/dist/assets/{gitGraphDiagram-NRZ2UAAF-C2wvk5wx.js → gitGraphDiagram-NRZ2UAAF-24jJcFhA.js} +1 -1
  24. package/dist/assets/{infoDiagram-A4XQUW5V-CO4mymBv.js → infoDiagram-A4XQUW5V-CgY7qCUY.js} +1 -1
  25. package/dist/assets/{journeyDiagram-G5WM74LC-BwPltDU3.js → journeyDiagram-G5WM74LC-m4w2Tza8.js} +1 -1
  26. package/dist/assets/{kanban-definition-QRCXZQQD-nVR8EB4Y.js → kanban-definition-QRCXZQQD-DREQJFO3.js} +1 -1
  27. package/dist/assets/{linear-BmiykovK.js → linear-DYyTPHva.js} +1 -1
  28. package/dist/assets/main-D1ireQV_.css +10 -0
  29. package/dist/assets/{main-CkEY2gMZ.js → main-DPkCDg49.js} +414 -394
  30. package/dist/assets/{mindmap-definition-GWI6TPTV-LMan8jbf.js → mindmap-definition-GWI6TPTV-DPONWDUf.js} +1 -1
  31. package/dist/assets/{pieDiagram-YF2LJOPJ-pAuujp_Z.js → pieDiagram-YF2LJOPJ-DAYIG173.js} +1 -1
  32. package/dist/assets/{quadrantDiagram-OS5C2QUG-MVRGxfWA.js → quadrantDiagram-OS5C2QUG-DcmEMhdV.js} +1 -1
  33. package/dist/assets/{requirementDiagram-MIRIMTAZ-CNm_y0ia.js → requirementDiagram-MIRIMTAZ-DuVLEj1X.js} +1 -1
  34. package/dist/assets/{sankeyDiagram-Y46BX6SQ-C3xgOXdi.js → sankeyDiagram-Y46BX6SQ-BLAFrPmy.js} +1 -1
  35. package/dist/assets/{sequenceDiagram-G6AWOVSC-DdyyPE0m.js → sequenceDiagram-G6AWOVSC-DVPAo02S.js} +1 -1
  36. package/dist/assets/{stateDiagram-MAYHULR4-DrYVdByC.js → stateDiagram-MAYHULR4-DxmF2ETD.js} +1 -1
  37. package/dist/assets/stateDiagram-v2-4JROLMXI-CqwDwDBX.js +1 -0
  38. package/dist/assets/{timeline-definition-U7ZMHBDA-Bw30fcXQ.js → timeline-definition-U7ZMHBDA-CPrvbS9i.js} +1 -1
  39. package/dist/assets/{xychartDiagram-6QU3TZC5-bNAui1Ve.js → xychartDiagram-6QU3TZC5-CuNlh3o9.js} +1 -1
  40. package/dist/index.html +2 -2
  41. package/dist-server/domain/message/message.service.js +1 -0
  42. package/dist-server/domain/task/dto/send-message.dto.js +11 -1
  43. package/dist-server/domain/task/task.controller.js +15 -0
  44. package/dist-server/domain/task/task.service.js +170 -0
  45. package/dist-server/domain/thread/thread.service.js +67 -1
  46. package/dist-server/repository/task.repository.js +110 -0
  47. package/package.json +12 -8
  48. package/packages/cli/dist/commands/registry.js +1 -1
  49. package/packages/cli/dist/commands/restart.d.ts +23 -0
  50. package/packages/cli/dist/commands/restart.js +148 -0
  51. package/packages/cli/dist/main.js +6 -0
  52. package/packages/cli/package.json +1 -1
  53. package/dist/assets/channel-DqvfwSMt.js +0 -1
  54. package/dist/assets/classDiagram-LNE6IOMH-Bfz8M3CC.js +0 -1
  55. package/dist/assets/classDiagram-v2-MQ7JQ4JX-Bfz8M3CC.js +0 -1
  56. package/dist/assets/main-BgbJXb8c.css +0 -10
  57. package/dist/assets/stateDiagram-v2-4JROLMXI-rKn6dm3O.js +0 -1
@@ -302,6 +302,11 @@ let ThreadService = ThreadService_1 = class ThreadService {
302
302
  // Not JSON — strip CLI noise from raw content
303
303
  content = this.stripCliNoise(content);
304
304
  }
305
+ const taskMetadata = this.parseTaskMetadata(task.metadata);
306
+ const providerSessionId = this.readString(taskMetadata.provider_session_id)
307
+ ?? this.readString(taskMetadata.providerSessionId)
308
+ ?? sessionId
309
+ ?? this.extractProviderSessionIdFromLogs(task.logs);
305
310
  messages.push({
306
311
  id: `${task.id}-assistant`,
307
312
  role: 'assistant',
@@ -325,6 +330,10 @@ let ThreadService = ThreadService_1 = class ThreadService {
325
330
  error: task.error || null,
326
331
  logs: this.parseLogs(task.logs, 50, this.extractAdapterKey(task)),
327
332
  callerAgentId: task.caller_agent_id ?? null,
333
+ metadata: {
334
+ ...taskMetadata,
335
+ ...(providerSessionId ? { provider_session_id: providerSessionId } : {}),
336
+ },
328
337
  },
329
338
  },
330
339
  });
@@ -510,7 +519,7 @@ let ThreadService = ThreadService_1 = class ThreadService {
510
519
  extractAdapterKey(row) {
511
520
  if (row.metadata) {
512
521
  try {
513
- const meta = JSON.parse(row.metadata);
522
+ const meta = this.parseTaskMetadata(row.metadata);
514
523
  const provider = meta.provider;
515
524
  if (provider) {
516
525
  const slash = provider.lastIndexOf('/');
@@ -526,6 +535,63 @@ let ThreadService = ThreadService_1 = class ThreadService {
526
535
  }
527
536
  return undefined;
528
537
  }
538
+ parseTaskMetadata(metadata) {
539
+ if (!metadata) {
540
+ return {};
541
+ }
542
+ try {
543
+ const parsed = JSON.parse(metadata);
544
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
545
+ ? parsed
546
+ : {};
547
+ }
548
+ catch {
549
+ return {};
550
+ }
551
+ }
552
+ readString(value) {
553
+ return typeof value === 'string' && value.trim().length > 0 ? value : null;
554
+ }
555
+ extractProviderSessionIdFromLogs(logs) {
556
+ if (!logs) {
557
+ return null;
558
+ }
559
+ let entries;
560
+ try {
561
+ const parsed = JSON.parse(logs);
562
+ if (!Array.isArray(parsed)) {
563
+ return null;
564
+ }
565
+ entries = parsed;
566
+ }
567
+ catch {
568
+ return null;
569
+ }
570
+ for (const entry of entries) {
571
+ if (typeof entry.message !== 'string') {
572
+ continue;
573
+ }
574
+ try {
575
+ const message = JSON.parse(entry.message);
576
+ const codexThreadId = message.type === 'thread.started'
577
+ ? this.readString(message.thread_id)
578
+ : null;
579
+ if (codexThreadId) {
580
+ return codexThreadId;
581
+ }
582
+ const claudeSessionId = message.type === 'system' && message.subtype === 'init'
583
+ ? this.readString(message.session_id)
584
+ : null;
585
+ if (claudeSessionId) {
586
+ return claudeSessionId;
587
+ }
588
+ }
589
+ catch {
590
+ // Ignore non-JSON log lines.
591
+ }
592
+ }
593
+ return null;
594
+ }
529
595
  /**
530
596
  * ACP providers (claude, opencode) emit the same toolUseId twice — first with
531
597
  * a placeholder input, then with the real one. Keep only the last occurrence.
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.TaskRepository = exports.WORKFLOW_STARTUP_REAP_ERROR = void 0;
10
10
  const common_1 = require("@nestjs/common");
11
11
  const repository_1 = require("@crewx/sdk/repository");
12
+ const drizzle_orm_1 = require("drizzle-orm");
12
13
  const base_sqlite_repository_js_1 = require("./base-sqlite.repository.js");
13
14
  exports.WORKFLOW_STARTUP_REAP_ERROR = 'Reaped: server restarted (in-process workflow)';
14
15
  let TaskRepository = class TaskRepository extends base_sqlite_repository_js_1.BaseSqliteRepository {
@@ -172,6 +173,115 @@ let TaskRepository = class TaskRepository extends base_sqlite_repository_js_1.Ba
172
173
  // SDK performs status='running' guard internally; assume updated if task was running
173
174
  return true;
174
175
  }
176
+ markTaskPausedForInstruction(taskId, opts) {
177
+ const handle = (0, repository_1.openDrizzleDb)(this.resolveDbPath());
178
+ try {
179
+ const condition = opts.workspaceId
180
+ ? (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(repository_1.tasks.id, taskId), (0, drizzle_orm_1.eq)(repository_1.tasks.workspace_id, opts.workspaceId))
181
+ : (0, drizzle_orm_1.eq)(repository_1.tasks.id, taskId);
182
+ const row = handle.db
183
+ .select({ metadata: repository_1.tasks.metadata })
184
+ .from(repository_1.tasks)
185
+ .where(condition)
186
+ .limit(1)
187
+ .get();
188
+ if (!row)
189
+ return false;
190
+ let metadata = {};
191
+ if (row.metadata) {
192
+ try {
193
+ metadata = JSON.parse(row.metadata);
194
+ }
195
+ catch {
196
+ metadata = {};
197
+ }
198
+ }
199
+ const nextMetadata = JSON.stringify({
200
+ ...metadata,
201
+ pauseReason: 'instruction',
202
+ pendingInstruction: opts.instruction,
203
+ resumedByTaskId: opts.resumedByTaskId,
204
+ });
205
+ const params = opts.workspaceId
206
+ ? ['paused', null, opts.completedAt, nextMetadata, taskId, opts.workspaceId]
207
+ : ['paused', null, opts.completedAt, nextMetadata, taskId];
208
+ const where = opts.workspaceId ? 'WHERE id = ? AND workspace_id = ?' : 'WHERE id = ?';
209
+ handle.runRaw(`UPDATE tasks SET status = ?, error = ?, completed_at = ?, metadata = ? ${where}`, params);
210
+ return true;
211
+ }
212
+ catch (e) {
213
+ if (e instanceof repository_1.RepositoryError)
214
+ throw e;
215
+ throw new repository_1.RepositoryError('DB_ERROR', 'Failed to mark task paused for instruction', e);
216
+ }
217
+ finally {
218
+ handle.close();
219
+ }
220
+ }
221
+ /**
222
+ * Restore a paused task to `running` so the same task row resumes in place,
223
+ * and bump `run_epoch` to the next generation. No new row is created — the
224
+ * resume execute reuses this taskId, appends to the existing logs, and
225
+ * finishTask's epoch guard finalizes only the matching generation.
226
+ *
227
+ * Bumping the epoch is what neutralizes the kill→resume race: the killed
228
+ * prior run's late task:end carries the old epoch (0) and is ignored, while
229
+ * the resume's task:end (the returned epoch) is the only one that closes the
230
+ * row. The user-visible instruction itself is appended to task logs by
231
+ * TaskService so it appears in ActivityLog instead of being hidden in
232
+ * metadata.
233
+ *
234
+ * @returns the new run_epoch to pass into the resume execute, or null if the
235
+ * task row was not found.
236
+ */
237
+ markTaskResumedForInstruction(taskId, opts) {
238
+ const handle = (0, repository_1.openDrizzleDb)(this.resolveDbPath());
239
+ try {
240
+ const condition = opts.workspaceId
241
+ ? (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(repository_1.tasks.id, taskId), (0, drizzle_orm_1.eq)(repository_1.tasks.workspace_id, opts.workspaceId))
242
+ : (0, drizzle_orm_1.eq)(repository_1.tasks.id, taskId);
243
+ const row = handle.db
244
+ .select({ metadata: repository_1.tasks.metadata, run_epoch: repository_1.tasks.run_epoch })
245
+ .from(repository_1.tasks)
246
+ .where(condition)
247
+ .limit(1)
248
+ .get();
249
+ if (!row)
250
+ return null;
251
+ let metadata = {};
252
+ if (row.metadata) {
253
+ try {
254
+ metadata = JSON.parse(row.metadata);
255
+ }
256
+ catch {
257
+ metadata = {};
258
+ }
259
+ }
260
+ const nextEpoch = (typeof row.run_epoch === 'number' ? row.run_epoch : 0) + 1;
261
+ const { pendingInstruction: _pending, pauseReason: _reason, resumedByTaskId: _resumed, ...rest } = metadata;
262
+ const nextMetadata = JSON.stringify({
263
+ ...rest,
264
+ provider_session_id: opts.providerSessionId,
265
+ });
266
+ const params = opts.workspaceId
267
+ ? ['running', null, null, nextMetadata, nextEpoch, taskId, opts.workspaceId]
268
+ : ['running', null, null, nextMetadata, nextEpoch, taskId];
269
+ const where = opts.workspaceId ? 'WHERE id = ? AND workspace_id = ?' : 'WHERE id = ?';
270
+ handle.runRaw(`UPDATE tasks SET status = ?, error = ?, completed_at = ?, metadata = ?, run_epoch = ?, pid = NULL ${where}`, params);
271
+ return nextEpoch;
272
+ }
273
+ catch (e) {
274
+ if (e instanceof repository_1.RepositoryError)
275
+ throw e;
276
+ throw new repository_1.RepositoryError('DB_ERROR', 'Failed to mark task resumed for instruction', e);
277
+ }
278
+ finally {
279
+ handle.close();
280
+ }
281
+ }
282
+ appendLog(taskId, entry) {
283
+ this.sdk.appendLog(taskId, entry);
284
+ }
175
285
  /** Fallback: find tasks by prompt text hint. */
176
286
  findTasksByPromptHint(hint, workspaceId) {
177
287
  return this.sdk.findTasksByPromptHint(hint, workspaceId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crewx",
3
- "version": "0.8.9",
3
+ "version": "0.9.0-rc.0",
4
4
  "description": "CrewX — AI agent team dashboard with Electron UI and CLI (Web + Electron + Global CLI)",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -81,19 +81,19 @@
81
81
  "yargs": "17.7.0",
82
82
  "zod": "3.25.76",
83
83
  "@crewx/chromex": "0.1.0",
84
+ "@crewx/cli": "0.9.0-rc.0",
84
85
  "@crewx/cron": "0.1.10",
85
- "@crewx/dreaming": "0.1.0",
86
- "@crewx/cli": "0.8.9",
87
86
  "@crewx/doc": "0.1.9",
87
+ "@crewx/dreaming": "0.1.0",
88
88
  "@crewx/knowledge-core": "0.1.17",
89
- "@crewx/memory": "0.1.23",
90
- "@crewx/sdk": "0.8.9",
89
+ "@crewx/sdk": "0.9.0-rc.0",
91
90
  "@crewx/search": "0.1.10",
92
- "@crewx/wi": "0.1.10",
91
+ "@crewx/memory": "0.1.23",
92
+ "@crewx/wbs": "0.1.10",
93
+ "@crewx/shared": "0.0.6",
93
94
  "@crewx/skill": "0.1.20",
94
95
  "@crewx/workflow": "0.3.22",
95
- "@crewx/wbs": "0.1.10",
96
- "@crewx/shared": "0.0.6"
96
+ "@crewx/wi": "0.1.10"
97
97
  },
98
98
  "devDependencies": {
99
99
  "@ccusage/codex": "0.0.1",
@@ -127,6 +127,8 @@
127
127
  "@types/react-dom": "18.2.0",
128
128
  "@types/supertest": "7.2.0",
129
129
  "@types/uuid": "9.0.0",
130
+ "@typescript-eslint/eslint-plugin": "6.21.0",
131
+ "@typescript-eslint/parser": "6.21.0",
130
132
  "@vitejs/plugin-react": "4.2.0",
131
133
  "autoprefixer": "10.4.16",
132
134
  "chart.js": "4.5.1",
@@ -138,6 +140,8 @@
138
140
  "electron": "39.8.10",
139
141
  "electron-builder": "25.1.8",
140
142
  "eslint": "8.55.0",
143
+ "eslint-plugin-react": "^7.37.5",
144
+ "eslint-plugin-react-hooks": "^7.1.1",
141
145
  "highlight.js": "11.11.1",
142
146
  "i18next": "25.8.11",
143
147
  "i18next-browser-languagedetector": "8.2.1",
@@ -13,7 +13,7 @@ exports.KNOWN_COMMANDS = new Set([
13
13
  'q', 'query',
14
14
  'x', 'execute',
15
15
  'agent',
16
- 'ps', 'kill', 'result', 'log',
16
+ 'ps', 'kill', 'result', 'log', 'restart',
17
17
  'doctor', 'init',
18
18
  'db',
19
19
  'help',
@@ -0,0 +1,23 @@
1
+ /**
2
+ * crewx restart handler.
3
+ * Restarts a failed task as a new task, reusing the original task's
4
+ * thread / agent / mode / model / provider. The `provider` column is not
5
+ * persisted on the task row, but the SDK records the effective provider in
6
+ * `metadata.provider`, so we recover it from there (falling back to the
7
+ * agent's configured provider when absent).
8
+ *
9
+ * Unlike the Web UI `Retry` (fire-and-forget), CLI `restart` awaits the new
10
+ * task to completion and prints the final response — the CLI process is the
11
+ * tracking owner, so it must not exit before the task finishes.
12
+ *
13
+ * Usage:
14
+ * crewx restart <task-id>
15
+ *
16
+ * Behavior:
17
+ * - Only tasks with status='failed' can be restarted.
18
+ * - A new taskId is generated; the original task row is never mutated.
19
+ * - metadata.restartedFromTaskId (+ retriedFromTaskId for compat) is recorded.
20
+ * - The Web UI `TSK.RETRY` system-message prefix is reused verbatim.
21
+ * - stdout carries the agent final response; guidance/errors go to stderr.
22
+ */
23
+ export declare function handleRestart(args: string[]): Promise<void>;
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /**
3
+ * crewx restart handler.
4
+ * Restarts a failed task as a new task, reusing the original task's
5
+ * thread / agent / mode / model / provider. The `provider` column is not
6
+ * persisted on the task row, but the SDK records the effective provider in
7
+ * `metadata.provider`, so we recover it from there (falling back to the
8
+ * agent's configured provider when absent).
9
+ *
10
+ * Unlike the Web UI `Retry` (fire-and-forget), CLI `restart` awaits the new
11
+ * task to completion and prints the final response — the CLI process is the
12
+ * tracking owner, so it must not exit before the task finishes.
13
+ *
14
+ * Usage:
15
+ * crewx restart <task-id>
16
+ *
17
+ * Behavior:
18
+ * - Only tasks with status='failed' can be restarted.
19
+ * - A new taskId is generated; the original task row is never mutated.
20
+ * - metadata.restartedFromTaskId (+ retriedFromTaskId for compat) is recorded.
21
+ * - The Web UI `TSK.RETRY` system-message prefix is reused verbatim.
22
+ * - stdout carries the agent final response; guidance/errors go to stderr.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.handleRestart = handleRestart;
26
+ const sdk_1 = require("@crewx/sdk");
27
+ const crewx_cli_1 = require("../bootstrap/crewx-cli");
28
+ const inherited_trace_1 = require("../utils/inherited-trace");
29
+ /**
30
+ * Build the retry system-message prefix.
31
+ * Intentionally identical to the server `ThreadService.retryTask()` prefix so
32
+ * stored conversations / parsers stay compatible. `type="retry"` is preserved.
33
+ */
34
+ function buildRetryPrefix(originalTaskId) {
35
+ return (`<crewx_system_message type="retry">\n` +
36
+ `Retrying a failed task.\n` +
37
+ `You must run \`crewx dreaming --task=${originalTaskId}\` to review where it stopped before continuing.\n` +
38
+ `</crewx_system_message>\n\n`);
39
+ }
40
+ /**
41
+ * Recover the effective provider from a task row's `metadata` JSON.
42
+ * The SDK persists the resolved provider as `metadata.provider`. Returns
43
+ * the string when present and non-empty, otherwise `undefined` so the
44
+ * agent's configured provider default is used.
45
+ */
46
+ function extractProvider(metadata) {
47
+ if (!metadata)
48
+ return undefined;
49
+ try {
50
+ const parsed = JSON.parse(metadata);
51
+ const provider = parsed.provider;
52
+ return typeof provider === 'string' && provider.length > 0 ? provider : undefined;
53
+ }
54
+ catch {
55
+ return undefined;
56
+ }
57
+ }
58
+ async function handleRestart(args) {
59
+ const verbose = args.includes('--verbose');
60
+ const taskId = args.find((a) => !a.startsWith('--'));
61
+ if (!taskId) {
62
+ process.stderr.write('Usage: crewx restart <task-id>\n');
63
+ process.exit(1);
64
+ return;
65
+ }
66
+ const repo = new sdk_1.TaskRepository();
67
+ const original = repo.getTask(taskId);
68
+ if (!original) {
69
+ process.stderr.write(`Error: Task not found: ${taskId}\n`);
70
+ process.exit(1);
71
+ return;
72
+ }
73
+ if (original.status !== 'failed') {
74
+ process.stderr.write(`Error: Task ${taskId} is not in failed state (current: ${original.status ?? 'unknown'}). ` +
75
+ `Only failed tasks can be restarted.\n`);
76
+ process.exit(1);
77
+ return;
78
+ }
79
+ if (!original.prompt) {
80
+ process.stderr.write(`Error: Task ${taskId} has no prompt to restart.\n`);
81
+ process.exit(1);
82
+ return;
83
+ }
84
+ if (!original.agent_id) {
85
+ process.stderr.write(`Error: Task ${taskId} has no agent to restart.\n`);
86
+ process.exit(1);
87
+ return;
88
+ }
89
+ const mode = original.mode === 'query' ? 'query' : 'execute';
90
+ const agentRef = `@${original.agent_id}`;
91
+ const newTaskId = (0, sdk_1.generateId)('tsk');
92
+ const restartPrompt = `${buildRetryPrefix(taskId)}${original.prompt}`;
93
+ // model: reuse the original task row value when present, otherwise fall back
94
+ // to the agent's configured default (resolved inside Crewx).
95
+ const model = original.model ?? undefined;
96
+ // provider: recovered from metadata.provider (SDK-persisted). Falls back to
97
+ // the agent's configured provider when absent. This preserves a
98
+ // `crewx x --provider ...` choice across restart.
99
+ const provider = extractProvider(original.metadata);
100
+ (0, sdk_1.setAuditVerbose)(verbose);
101
+ if (verbose) {
102
+ process.stderr.write(`🔁 Restart: ${taskId} → ${newTaskId}\n`);
103
+ process.stderr.write(`🤖 Agent: ${agentRef} Mode: ${mode}\n`);
104
+ if (original.thread_id)
105
+ process.stderr.write(`🔗 Thread: ${original.thread_id}\n`);
106
+ process.stderr.write('─'.repeat(60) + '\n');
107
+ }
108
+ const crewx = await (0, crewx_cli_1.createCliCrewx)();
109
+ const sdkOptions = {
110
+ taskId: newTaskId,
111
+ threadId: original.thread_id ?? undefined,
112
+ model,
113
+ provider,
114
+ metadata: {
115
+ restartedFromTaskId: taskId,
116
+ // Back-compat / search convenience: mirror the Web UI metadata key.
117
+ retriedFromTaskId: taskId,
118
+ },
119
+ trace: (0, inherited_trace_1.readInheritedTrace)(),
120
+ };
121
+ let exitCode = 0;
122
+ try {
123
+ const result = mode === 'query'
124
+ ? await crewx.query(agentRef, restartPrompt, sdkOptions)
125
+ : await crewx.execute(agentRef, restartPrompt, sdkOptions);
126
+ if (result.ok) {
127
+ process.stdout.write(result.data + '\n');
128
+ }
129
+ else {
130
+ const errMsg = result.error?.message ?? 'Restart failed';
131
+ const resultTaskId = result.meta?.taskId ?? newTaskId;
132
+ process.stderr.write(`Error: ${errMsg}\n`);
133
+ process.stderr.write(`taskId=${resultTaskId}\n`);
134
+ exitCode = 1;
135
+ }
136
+ }
137
+ catch (err) {
138
+ const errMsg = err instanceof Error ? err.message : String(err);
139
+ process.stderr.write(`Error: ${errMsg}\n`);
140
+ process.stderr.write(`taskId=${newTaskId}\n`);
141
+ exitCode = 1;
142
+ }
143
+ finally {
144
+ await crewx.close();
145
+ }
146
+ if (exitCode !== 0)
147
+ process.exit(exitCode);
148
+ }
@@ -55,6 +55,7 @@ const agent_1 = require("./commands/agent");
55
55
  const ps_1 = require("./commands/ps");
56
56
  const kill_1 = require("./commands/kill");
57
57
  const result_1 = require("./commands/result");
58
+ const restart_1 = require("./commands/restart");
58
59
  const log_1 = require("./commands/log");
59
60
  const doctor_1 = require("./commands/doctor");
60
61
  const init_1 = require("./commands/init");
@@ -133,6 +134,10 @@ async function main() {
133
134
  case 'result':
134
135
  await (0, result_1.handleResult)(args.slice(1));
135
136
  return;
137
+ // CLI Task Lifecycle 2단계: restart a failed task as a new task
138
+ case 'restart':
139
+ await (0, restart_1.handleRestart)(args.slice(1));
140
+ return;
136
141
  // P1-1: log
137
142
  case 'log':
138
143
  await (0, log_1.handleLog)(args.slice(1));
@@ -269,6 +274,7 @@ Task Management:
269
274
  kill <task-id> Kill a running task
270
275
  kill --all Kill all running tasks
271
276
  result [task-id] Get task result (or list recent tasks)
277
+ restart <task-id> Restart a failed task as a new task
272
278
 
273
279
  Logs & Diagnostics:
274
280
  log [ls|<task-id>] View task logs
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewx/cli",
3
- "version": "0.8.9",
3
+ "version": "0.9.0-rc.0",
4
4
  "license": "UNLICENSED",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -1 +0,0 @@
1
- import{ah as o,ai as n}from"./main-CkEY2gMZ.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s,c as e,a as r,b as l}from"./chunk-T2TOU4HS-C2u5B7pp.js";import{_ as t}from"./main-CkEY2gMZ.js";import"./chunk-5HRBRIJM-Ctb_uxZ5.js";var _={parser:l,db:r,renderer:e,styles:s,init:t(a=>{a.class||(a.class={}),a.class.arrowMarkerAbsolute=a.arrowMarkerAbsolute,r.clear()},"init")};export{_ as diagram};
@@ -1 +0,0 @@
1
- import{s,c as e,a as r,b as l}from"./chunk-T2TOU4HS-C2u5B7pp.js";import{_ as t}from"./main-CkEY2gMZ.js";import"./chunk-5HRBRIJM-Ctb_uxZ5.js";var _={parser:l,db:r,renderer:e,styles:s,init:t(a=>{a.class||(a.class={}),a.class.arrowMarkerAbsolute=a.arrowMarkerAbsolute,r.clear()},"init")};export{_ as diagram};