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.
- package/bin/cli-commands.js +1 -1
- package/dist/assets/{MarketPage-Bk13HBH8.js → MarketPage-DcsjTevG.js} +1 -1
- package/dist/assets/{PromptTab-BROKndtk.js → PromptTab-BrYbpZgD.js} +1 -1
- package/dist/assets/{arc-8uTktI_9.js → arc-C8trOm68.js} +1 -1
- package/dist/assets/{architectureDiagram-UYN6MBPD-CJyGJizJ.js → architectureDiagram-UYN6MBPD-BhulQbll.js} +1 -1
- package/dist/assets/{blockDiagram-ZHA2E4KO-tb7_qUXV.js → blockDiagram-ZHA2E4KO-BBGijVDJ.js} +1 -1
- package/dist/assets/{c4Diagram-6F5ED5ID-Bh0hYgUZ.js → c4Diagram-6F5ED5ID-BFc7Kmvi.js} +1 -1
- package/dist/assets/channel-CldZ_Wzz.js +1 -0
- package/dist/assets/{chunk-5HRBRIJM-Ctb_uxZ5.js → chunk-5HRBRIJM-D2aGr_3q.js} +1 -1
- package/dist/assets/{chunk-7U56Z5CX-h1fDz-WT.js → chunk-7U56Z5CX-CYHfdi4H.js} +1 -1
- package/dist/assets/{chunk-ASOPGD6M-CgfyXIoW.js → chunk-ASOPGD6M-BAuVIDQR.js} +1 -1
- package/dist/assets/{chunk-KFBOBJHC-CR7Ttw70.js → chunk-KFBOBJHC-Dp9z-742.js} +1 -1
- package/dist/assets/{chunk-T2TOU4HS-C2u5B7pp.js → chunk-T2TOU4HS-CWW5BN2j.js} +1 -1
- package/dist/assets/{chunk-TMUBEWPD-x_AdOhrN.js → chunk-TMUBEWPD-DZBJaaAc.js} +1 -1
- package/dist/assets/classDiagram-LNE6IOMH-CVRBmdDQ.js +1 -0
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-CVRBmdDQ.js +1 -0
- package/dist/assets/{dagre-4EVJKHTY-BfYeEJwg.js → dagre-4EVJKHTY-DTS2hGsu.js} +1 -1
- package/dist/assets/{diagram-QW4FP2JN-Bkxhu2Ro.js → diagram-QW4FP2JN-BrdV6-yQ.js} +1 -1
- package/dist/assets/{erDiagram-6RL3IURR-CEJ086-x.js → erDiagram-6RL3IURR-DrFMd6eu.js} +1 -1
- package/dist/assets/{flowDiagram-7ASYPVHJ-DpYeXRcJ.js → flowDiagram-7ASYPVHJ-Ctz5vjkO.js} +1 -1
- package/dist/assets/{ganttDiagram-NTVNEXSI-DPWdP6sZ.js → ganttDiagram-NTVNEXSI-Bb6rEfpn.js} +1 -1
- package/dist/assets/{gitGraph-YCYPL57B-nXetpL58.js → gitGraph-YCYPL57B-D8MdWNT2.js} +1 -1
- package/dist/assets/{gitGraphDiagram-NRZ2UAAF-C2wvk5wx.js → gitGraphDiagram-NRZ2UAAF-24jJcFhA.js} +1 -1
- package/dist/assets/{infoDiagram-A4XQUW5V-CO4mymBv.js → infoDiagram-A4XQUW5V-CgY7qCUY.js} +1 -1
- package/dist/assets/{journeyDiagram-G5WM74LC-BwPltDU3.js → journeyDiagram-G5WM74LC-m4w2Tza8.js} +1 -1
- package/dist/assets/{kanban-definition-QRCXZQQD-nVR8EB4Y.js → kanban-definition-QRCXZQQD-DREQJFO3.js} +1 -1
- package/dist/assets/{linear-BmiykovK.js → linear-DYyTPHva.js} +1 -1
- package/dist/assets/main-D1ireQV_.css +10 -0
- package/dist/assets/{main-CkEY2gMZ.js → main-DPkCDg49.js} +414 -394
- package/dist/assets/{mindmap-definition-GWI6TPTV-LMan8jbf.js → mindmap-definition-GWI6TPTV-DPONWDUf.js} +1 -1
- package/dist/assets/{pieDiagram-YF2LJOPJ-pAuujp_Z.js → pieDiagram-YF2LJOPJ-DAYIG173.js} +1 -1
- package/dist/assets/{quadrantDiagram-OS5C2QUG-MVRGxfWA.js → quadrantDiagram-OS5C2QUG-DcmEMhdV.js} +1 -1
- package/dist/assets/{requirementDiagram-MIRIMTAZ-CNm_y0ia.js → requirementDiagram-MIRIMTAZ-DuVLEj1X.js} +1 -1
- package/dist/assets/{sankeyDiagram-Y46BX6SQ-C3xgOXdi.js → sankeyDiagram-Y46BX6SQ-BLAFrPmy.js} +1 -1
- package/dist/assets/{sequenceDiagram-G6AWOVSC-DdyyPE0m.js → sequenceDiagram-G6AWOVSC-DVPAo02S.js} +1 -1
- package/dist/assets/{stateDiagram-MAYHULR4-DrYVdByC.js → stateDiagram-MAYHULR4-DxmF2ETD.js} +1 -1
- package/dist/assets/stateDiagram-v2-4JROLMXI-CqwDwDBX.js +1 -0
- package/dist/assets/{timeline-definition-U7ZMHBDA-Bw30fcXQ.js → timeline-definition-U7ZMHBDA-CPrvbS9i.js} +1 -1
- package/dist/assets/{xychartDiagram-6QU3TZC5-bNAui1Ve.js → xychartDiagram-6QU3TZC5-CuNlh3o9.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/domain/message/message.service.js +1 -0
- package/dist-server/domain/task/dto/send-message.dto.js +11 -1
- package/dist-server/domain/task/task.controller.js +15 -0
- package/dist-server/domain/task/task.service.js +170 -0
- package/dist-server/domain/thread/thread.service.js +67 -1
- package/dist-server/repository/task.repository.js +110 -0
- package/package.json +12 -8
- package/packages/cli/dist/commands/registry.js +1 -1
- package/packages/cli/dist/commands/restart.d.ts +23 -0
- package/packages/cli/dist/commands/restart.js +148 -0
- package/packages/cli/dist/main.js +6 -0
- package/packages/cli/package.json +1 -1
- package/dist/assets/channel-DqvfwSMt.js +0 -1
- package/dist/assets/classDiagram-LNE6IOMH-Bfz8M3CC.js +0 -1
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-Bfz8M3CC.js +0 -1
- package/dist/assets/main-BgbJXb8c.css +0 -10
- 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 =
|
|
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.
|
|
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/
|
|
90
|
-
"@crewx/sdk": "0.8.9",
|
|
89
|
+
"@crewx/sdk": "0.9.0-rc.0",
|
|
91
90
|
"@crewx/search": "0.1.10",
|
|
92
|
-
"@crewx/
|
|
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/
|
|
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",
|
|
@@ -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 +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};
|