@dv.nghiem/flowdeck 0.4.6 → 0.4.7
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/dist/index.js +162 -55
- package/dist/tools/delegate.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1236,6 +1236,136 @@ function extractText2(parts) {
|
|
|
1236
1236
|
return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
|
|
1237
1237
|
`);
|
|
1238
1238
|
}
|
|
1239
|
+
async function runWithStreaming(client, childId, agentName, fullPrompt, toolsConfig, directory, abort, onTitle) {
|
|
1240
|
+
const sseResult = await client.event.subscribe({ query: { directory } });
|
|
1241
|
+
const stream = sseResult.stream;
|
|
1242
|
+
const asyncRes = await client.session.promptAsync({
|
|
1243
|
+
path: { id: childId },
|
|
1244
|
+
query: { directory },
|
|
1245
|
+
body: {
|
|
1246
|
+
agent: agentName,
|
|
1247
|
+
tools: toolsConfig,
|
|
1248
|
+
parts: [{ type: "text", text: fullPrompt }]
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
if (asyncRes.error) {
|
|
1252
|
+
return {
|
|
1253
|
+
output: "",
|
|
1254
|
+
error: `promptAsync failed: ${JSON.stringify(asyncRes.error)}`
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
let streamedText = "";
|
|
1258
|
+
let currentTool = "";
|
|
1259
|
+
onTitle(`⏳ ${agentName} — starting…`);
|
|
1260
|
+
try {
|
|
1261
|
+
for await (const raw of stream) {
|
|
1262
|
+
if (abort.aborted)
|
|
1263
|
+
break;
|
|
1264
|
+
const event = typeof raw === "object" && raw !== null ? Object.values(raw)[0] ?? raw : raw;
|
|
1265
|
+
if (!event || typeof event !== "object")
|
|
1266
|
+
continue;
|
|
1267
|
+
const sid = event.properties?.sessionID;
|
|
1268
|
+
if (sid && sid !== childId)
|
|
1269
|
+
continue;
|
|
1270
|
+
switch (event.type) {
|
|
1271
|
+
case "session.next.step.started": {
|
|
1272
|
+
const model = event.properties?.model?.id ?? "";
|
|
1273
|
+
onTitle(`\uD83E\uDD14 ${agentName} — thinking${model ? ` (${model})` : ""}…`);
|
|
1274
|
+
break;
|
|
1275
|
+
}
|
|
1276
|
+
case "session.next.text.delta": {
|
|
1277
|
+
const delta = event.properties?.delta ?? "";
|
|
1278
|
+
streamedText += delta;
|
|
1279
|
+
const preview = streamedText.slice(-80).replace(/\n/g, " ").trim();
|
|
1280
|
+
onTitle(`✍️ ${agentName} — ${preview}`);
|
|
1281
|
+
break;
|
|
1282
|
+
}
|
|
1283
|
+
case "session.next.text.ended": {
|
|
1284
|
+
const text = event.properties?.text ?? streamedText;
|
|
1285
|
+
streamedText = text;
|
|
1286
|
+
break;
|
|
1287
|
+
}
|
|
1288
|
+
case "session.next.reasoning.delta": {
|
|
1289
|
+
const delta = event.properties?.delta ?? "";
|
|
1290
|
+
const preview = delta.slice(0, 60).replace(/\n/g, " ").trim();
|
|
1291
|
+
onTitle(`\uD83D\uDCAD ${agentName} — ${preview}`);
|
|
1292
|
+
break;
|
|
1293
|
+
}
|
|
1294
|
+
case "session.next.tool.called": {
|
|
1295
|
+
currentTool = event.properties?.tool ?? "tool";
|
|
1296
|
+
onTitle(`\uD83D\uDD27 ${agentName} → ${currentTool}…`);
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
case "session.next.tool.progress": {
|
|
1300
|
+
const content = event.properties?.content ?? [];
|
|
1301
|
+
const progressText = content.filter((c) => c.type === "text").map((c) => c.text).join(" ").slice(0, 80).replace(/\n/g, " ").trim();
|
|
1302
|
+
if (progressText) {
|
|
1303
|
+
onTitle(`\uD83D\uDD27 ${agentName} → ${currentTool}: ${progressText}`);
|
|
1304
|
+
}
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
case "session.next.tool.success": {
|
|
1308
|
+
onTitle(`✅ ${agentName} → ${currentTool} done`);
|
|
1309
|
+
currentTool = "";
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
case "session.next.tool.failed": {
|
|
1313
|
+
onTitle(`❌ ${agentName} → ${currentTool} failed`);
|
|
1314
|
+
currentTool = "";
|
|
1315
|
+
break;
|
|
1316
|
+
}
|
|
1317
|
+
case "session.next.retried": {
|
|
1318
|
+
onTitle(`↻ ${agentName} — retrying…`);
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
case "session.next.step.ended": {
|
|
1322
|
+
const cost = event.properties?.cost ?? 0;
|
|
1323
|
+
const finish = event.properties?.finish ?? "";
|
|
1324
|
+
if (cost > 0) {
|
|
1325
|
+
onTitle(`\uD83D\uDCCA ${agentName} — step done ($${cost.toFixed(4)}) [${finish}]`);
|
|
1326
|
+
} else {
|
|
1327
|
+
onTitle(`\uD83D\uDCCA ${agentName} — step done [${finish}]`);
|
|
1328
|
+
}
|
|
1329
|
+
break;
|
|
1330
|
+
}
|
|
1331
|
+
case "session.error": {
|
|
1332
|
+
const msg = event.properties?.error?.message ?? JSON.stringify(event.properties?.error);
|
|
1333
|
+
return { output: streamedText, error: `Session error: ${msg}` };
|
|
1334
|
+
}
|
|
1335
|
+
case "session.idle": {
|
|
1336
|
+
onTitle(`✓ ${agentName} — complete`);
|
|
1337
|
+
goto_done:
|
|
1338
|
+
break goto_done;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
if (event.type === "session.idle")
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
} catch (err) {
|
|
1345
|
+
if (!abort.aborted) {
|
|
1346
|
+
onTitle(`⚠️ ${agentName} — stream closed (${err?.message ?? err})`);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
if (streamedText) {
|
|
1350
|
+
return { output: streamedText };
|
|
1351
|
+
}
|
|
1352
|
+
try {
|
|
1353
|
+
const msgsRes = await client.session.messages({
|
|
1354
|
+
path: { id: childId },
|
|
1355
|
+
query: { directory }
|
|
1356
|
+
});
|
|
1357
|
+
const messages = msgsRes.data ?? [];
|
|
1358
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
1359
|
+
const msg = messages[i];
|
|
1360
|
+
if (msg.role === "assistant") {
|
|
1361
|
+
const text = extractText2(msg.parts ?? []);
|
|
1362
|
+
if (text)
|
|
1363
|
+
return { output: text };
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
} catch {}
|
|
1367
|
+
return { output: "" };
|
|
1368
|
+
}
|
|
1239
1369
|
function createDelegateTool(client) {
|
|
1240
1370
|
return tool4({
|
|
1241
1371
|
description: "Delegate a task to a single agent via a child session. Returns the agent's output.",
|
|
@@ -1250,14 +1380,14 @@ function createDelegateTool(client) {
|
|
|
1250
1380
|
workflow_id: tool4.schema.string().optional(),
|
|
1251
1381
|
stage: tool4.schema.string().optional()
|
|
1252
1382
|
},
|
|
1253
|
-
async execute(args,
|
|
1383
|
+
async execute(args, execContext) {
|
|
1254
1384
|
const startTime = Date.now();
|
|
1255
1385
|
const taskType = normalizeTaskType(args.task_type, args.agent);
|
|
1256
1386
|
const retryAttempts = typeof args.retry_attempts === "number" ? args.retry_attempts : 1;
|
|
1257
1387
|
const maxRetries = Math.max(0, Math.floor(retryAttempts));
|
|
1258
1388
|
let agentModel = "";
|
|
1259
1389
|
try {
|
|
1260
|
-
const cfg = loadFlowDeckConfig(
|
|
1390
|
+
const cfg = loadFlowDeckConfig(execContext.directory);
|
|
1261
1391
|
agentModel = cfg.agents?.[args.agent]?.model ?? "";
|
|
1262
1392
|
} catch {}
|
|
1263
1393
|
const metricsWorkflowId = args.workflow_id ?? "";
|
|
@@ -1271,16 +1401,16 @@ ${args.prompt}` : args.prompt;
|
|
|
1271
1401
|
let stateVersion = 0;
|
|
1272
1402
|
let indexVersion = 0;
|
|
1273
1403
|
if (safe_to_cache) {
|
|
1274
|
-
const index = readCodebaseIndex(
|
|
1275
|
-
const sp = statePath(
|
|
1404
|
+
const index = readCodebaseIndex(execContext.directory);
|
|
1405
|
+
const sp = statePath(execContext.directory);
|
|
1276
1406
|
const rawState = existsSync10(sp) ? readFileSync10(sp, "utf-8") : "";
|
|
1277
1407
|
const state = rawState ? parseState(rawState) : {};
|
|
1278
1408
|
stateVersion = typeof state.summaryVersion === "number" ? state.summaryVersion : 0;
|
|
1279
1409
|
indexVersion = typeof index.summaryVersion === "number" ? index.summaryVersion : 0;
|
|
1280
|
-
const cached = getCached(
|
|
1410
|
+
const cached = getCached(execContext.directory, args.agent, fullPrompt, args.context ?? "", stateVersion, indexVersion, true);
|
|
1281
1411
|
if (cached !== null) {
|
|
1282
1412
|
if (metricsWorkflowId) {
|
|
1283
|
-
recordCacheHit(
|
|
1413
|
+
recordCacheHit(execContext.directory, metricsWorkflowId, metricsStage, fullPrompt, args.agent, agentModel);
|
|
1284
1414
|
}
|
|
1285
1415
|
return JSON.stringify({
|
|
1286
1416
|
agent: args.agent,
|
|
@@ -1295,8 +1425,8 @@ ${args.prompt}` : args.prompt;
|
|
|
1295
1425
|
}
|
|
1296
1426
|
}
|
|
1297
1427
|
const createRes = await client.session.create({
|
|
1298
|
-
body: { parentID:
|
|
1299
|
-
query: { directory:
|
|
1428
|
+
body: { parentID: execContext.sessionID, title: `${args.agent}-delegate` },
|
|
1429
|
+
query: { directory: execContext.directory }
|
|
1300
1430
|
});
|
|
1301
1431
|
if (createRes.error || !createRes.data?.id) {
|
|
1302
1432
|
return JSON.stringify({
|
|
@@ -1307,84 +1437,61 @@ ${args.prompt}` : args.prompt;
|
|
|
1307
1437
|
});
|
|
1308
1438
|
}
|
|
1309
1439
|
const childId = createRes.data.id;
|
|
1310
|
-
|
|
1440
|
+
execContext.abort.addEventListener("abort", () => {
|
|
1311
1441
|
client.session.abort({
|
|
1312
1442
|
path: { id: childId },
|
|
1313
|
-
query: { directory:
|
|
1443
|
+
query: { directory: execContext.directory }
|
|
1314
1444
|
}).catch(() => {});
|
|
1315
1445
|
});
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
---
|
|
1319
|
-
|
|
1320
|
-
${args.prompt}` : args.prompt;
|
|
1321
|
-
let promptRes = null;
|
|
1446
|
+
let lastOutput = "";
|
|
1447
|
+
let lastError;
|
|
1322
1448
|
let retriesUsed = 0;
|
|
1323
1449
|
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
1324
1450
|
const attemptStart = Date.now();
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
});
|
|
1334
|
-
if (!shouldRetry(promptRes) || attempt === maxRetries)
|
|
1451
|
+
if (attempt > 0) {
|
|
1452
|
+
execContext.metadata({ title: `↻ ${args.agent} — retry ${attempt}/${maxRetries}…` });
|
|
1453
|
+
}
|
|
1454
|
+
const result = await runWithStreaming(client, childId, args.agent, fullPrompt, { question: false }, execContext.directory, execContext.abort, (title) => execContext.metadata({ title }));
|
|
1455
|
+
lastOutput = result.output;
|
|
1456
|
+
lastError = result.error;
|
|
1457
|
+
const shouldRetryAttempt = !!(lastError || !lastOutput.trim());
|
|
1458
|
+
if (!shouldRetryAttempt || attempt === maxRetries)
|
|
1335
1459
|
break;
|
|
1336
1460
|
if (metricsWorkflowId) {
|
|
1337
|
-
const retryInputTokens = estimateTokens(
|
|
1461
|
+
const retryInputTokens = estimateTokens(fullPrompt);
|
|
1338
1462
|
const retryCostUsd = agentModel ? estimateCostUSD(agentModel, retryInputTokens, 0) : undefined;
|
|
1339
|
-
recordRetryCall(
|
|
1463
|
+
recordRetryCall(execContext.directory, metricsWorkflowId, metricsStage, fullPrompt, "", args.agent, Date.now() - attemptStart, agentModel, retryCostUsd);
|
|
1340
1464
|
}
|
|
1341
1465
|
retriesUsed++;
|
|
1342
1466
|
}
|
|
1343
|
-
if (
|
|
1344
|
-
|
|
1345
|
-
recordRun(context.directory, args.agent, "", taskType, false, Date.now() - startTime);
|
|
1346
|
-
return JSON.stringify({
|
|
1347
|
-
agent: args.agent,
|
|
1348
|
-
session_id: childId,
|
|
1349
|
-
success: false,
|
|
1350
|
-
error: errMsg,
|
|
1351
|
-
task_type: taskType,
|
|
1352
|
-
model: "",
|
|
1353
|
-
retries_used: retriesUsed,
|
|
1354
|
-
duration_ms: Date.now() - startTime
|
|
1355
|
-
});
|
|
1356
|
-
}
|
|
1357
|
-
const info = promptRes.data?.info;
|
|
1358
|
-
if (info?.error) {
|
|
1359
|
-
const errMsg = `Agent error: ${JSON.stringify(info.error)}`;
|
|
1360
|
-
recordRun(context.directory, args.agent, "", taskType, false, Date.now() - startTime);
|
|
1467
|
+
if (lastError && !lastOutput.trim()) {
|
|
1468
|
+
recordRun(execContext.directory, args.agent, "", taskType, false, Date.now() - startTime);
|
|
1361
1469
|
return JSON.stringify({
|
|
1362
1470
|
agent: args.agent,
|
|
1363
1471
|
session_id: childId,
|
|
1364
1472
|
success: false,
|
|
1365
|
-
error:
|
|
1473
|
+
error: lastError,
|
|
1366
1474
|
task_type: taskType,
|
|
1367
1475
|
model: "",
|
|
1368
1476
|
retries_used: retriesUsed,
|
|
1369
1477
|
duration_ms: Date.now() - startTime
|
|
1370
1478
|
});
|
|
1371
1479
|
}
|
|
1372
|
-
|
|
1373
|
-
recordRun(context.directory, args.agent, "", taskType, true, Date.now() - startTime);
|
|
1480
|
+
recordRun(execContext.directory, args.agent, "", taskType, true, Date.now() - startTime);
|
|
1374
1481
|
if (metricsWorkflowId) {
|
|
1375
|
-
const inputTokens = estimateTokens(
|
|
1376
|
-
const outputTokens = estimateTokens(
|
|
1482
|
+
const inputTokens = estimateTokens(fullPrompt);
|
|
1483
|
+
const outputTokens = estimateTokens(lastOutput);
|
|
1377
1484
|
const costUsd = agentModel ? estimateCostUSD(agentModel, inputTokens, outputTokens) : undefined;
|
|
1378
|
-
recordModelCall(
|
|
1485
|
+
recordModelCall(execContext.directory, metricsWorkflowId, metricsStage, fullPrompt, lastOutput, args.agent, Date.now() - startTime, agentModel, costUsd);
|
|
1379
1486
|
}
|
|
1380
|
-
if (safe_to_cache &&
|
|
1381
|
-
setCached(
|
|
1487
|
+
if (safe_to_cache && lastOutput) {
|
|
1488
|
+
setCached(execContext.directory, args.agent, fullPrompt, args.context ?? "", stateVersion, indexVersion, lastOutput, true, args.cache_ttl_ms);
|
|
1382
1489
|
}
|
|
1383
1490
|
return JSON.stringify({
|
|
1384
1491
|
agent: args.agent,
|
|
1385
1492
|
session_id: childId,
|
|
1386
1493
|
success: true,
|
|
1387
|
-
output:
|
|
1494
|
+
output: lastOutput || "(no text output)",
|
|
1388
1495
|
task_type: taskType,
|
|
1389
1496
|
model: "",
|
|
1390
1497
|
retries_used: retriesUsed,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../../src/tools/delegate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../../src/tools/delegate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAgOtD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CA6NzE"}
|