@runfusion/fusion 0.45.0 → 0.46.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/dist/bin.js +531 -99
- package/dist/client/.vite/manifest.json +244 -244
- package/dist/client/assets/{AgentDetailView-DJTi6hKC.js → AgentDetailView-D1cA9Bfj.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-C5DR9dUS.js → AgentPermissionPolicyEditor-DE0wv8-L.js} +1 -1
- package/dist/client/assets/{AgentsView-BNd90PSx.js → AgentsView-Bm_FA_1w.js} +3 -3
- package/dist/client/assets/{ChatView-B7JaZCL_.js → ChatView-CHEkov6q.js} +1 -1
- package/dist/client/assets/CommandCenter-Cfgek-dC.js +71 -0
- package/dist/client/assets/{CommandCenter-Xs5Ftv8s.css → CommandCenter-DDjpYkPe.css} +1 -1
- package/dist/client/assets/DevServerView-DgDksVH2.css +1 -0
- package/dist/client/assets/DevServerView-_or2KLVw.js +1 -0
- package/dist/client/assets/{DirectoryPicker-DegxT8ia.js → DirectoryPicker-etVwa-6X.js} +1 -1
- package/dist/client/assets/{DocumentsView-kVVQYISc.js → DocumentsView-e8ZTdPIZ.js} +1 -1
- package/dist/client/assets/{EvalsView-C9-iTSat.js → EvalsView-CjDRQFMQ.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-DjP_xNkc.js → ExperimentalAgentOnboardingModal-DXR0OAsR.js} +1 -1
- package/dist/client/assets/{GoalsView-VoIHJJ5l.js → GoalsView-WWDKVslN.js} +1 -1
- package/dist/client/assets/{InsightsView-BIZaUcpd.js → InsightsView-DxIfxOzx.js} +1 -1
- package/dist/client/assets/{MemoryView-7v7rv6ns.js → MemoryView-BVW2hkWf.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-CB89rV4U.js → PiExtensionsManager-CpSfMok2.js} +1 -1
- package/dist/client/assets/{PluginManager-BNSuW_R4.js → PluginManager-CpBCy656.js} +1 -1
- package/dist/client/assets/{PullRequestView-CI5jdKBL.js → PullRequestView-BMspX1ga.js} +1 -1
- package/dist/client/assets/{ResearchView-BT5dR_m8.js → ResearchView--YEtcv_s.js} +1 -1
- package/dist/client/assets/{SecretsView-WPYI58Uo.js → SecretsView-CC6QDMWN.js} +1 -1
- package/dist/client/assets/{SessionTerminal-C6gyOWLt.js → SessionTerminal-DJGtgIls.js} +1 -1
- package/dist/client/assets/{SettingsModal-Bq_3AbaB.js → SettingsModal-4d_p8D7W.js} +1 -1
- package/dist/client/assets/SettingsModal-ino68pI2.css +1 -0
- package/dist/client/assets/SettingsModal-wABa7xmZ.js +21 -0
- package/dist/client/assets/{SetupWizardModal-CrgbnR_d.js → SetupWizardModal-DHe2tXzx.js} +2 -2
- package/dist/client/assets/{ShadcnColorPicker-1icHuwhC.js → ShadcnColorPicker-HX3j1v66.js} +1 -1
- package/dist/client/assets/{SkillsView-byuSe3e0.js → SkillsView-mdFVTX-r.js} +1 -1
- package/dist/client/assets/{TodoView-CQ9GjYBg.js → TodoView-DQVugDpW.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-DW7sJZUX.js → WorkflowNodeEditor-CeERQHHu.js} +1 -1
- package/dist/client/assets/{agentSkills-CeytXLHF.js → agentSkills-C2v1wbxh.js} +1 -1
- package/dist/client/assets/{app-7-5DWLtg.js → app-BJGkp7Tt.js} +3 -3
- package/dist/client/assets/{app-CZ2LTFce.js → app-BKLUvvm8.js} +3 -3
- package/dist/client/assets/{app-Cv9xEnll.js → app-BaoNC9zg.js} +1 -1
- package/dist/client/assets/{app-x_yPcO5Y.js → app-CZQ_fFNf.js} +3 -3
- package/dist/client/assets/{app-CSvE5Noc.js → app-DAu3AzsS.js} +3 -3
- package/dist/client/assets/{app-3wbZ08YC.js → app-DgezOJub.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-BRI0QwDf.js → architectureDiagram-3BPJPVTR-CGEhhgX_.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-BXkN8Hv_.js → blockDiagram-GPEHLZMM-D0DTo0HT.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-C_Kcokep.js → c4Diagram-AAUBKEIU-DNykNaRb.js} +1 -1
- package/dist/client/assets/channel-Dwf1FoGU.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-BGeuCz9C.js → chunk-2J33WTMH-5oQfAhmr.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-DcVdctr0.js → chunk-4BX2VUAB-BqLZXgFw.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-Dg6gDigQ.js → chunk-55IACEB6-C1oIjX3T.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-B9B-vlEb.js → chunk-727SXJPM-BaovQTgk.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-LrWQ-2yi.js → chunk-AQP2D5EJ-B4ikBK42.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-Bnd6hAED.js → chunk-FMBD7UC4-5zgtNnG2.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-nAw93UB5.js → chunk-ND2GUHAM-ByrypRna.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CgprKcZo.js → chunk-QZHKN3VN-B7D1exsu.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-WqSUwcK_.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-WqSUwcK_.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-Bhn0g_iK.js → cose-bilkent-S5V4N54A-CQmiNzGr.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-CP4kfwqd.js → dagre-BM42HDAG-BH8Uk0Up.js} +1 -1
- package/dist/client/assets/{dashboard-view-CM6Xin_H.js → dashboard-view-CWJ-b59n.js} +1 -1
- package/dist/client/assets/{dashboard-view-DV1nEhh9.js → dashboard-view-D3PWY2PH.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-DPhzVEUf.js → diagram-2AECGRRQ-CDoHfkxY.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-8QOC3K_L.js → diagram-5GNKFQAL-DARILCuD.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-C1twgEch.js → diagram-KO2AKTUF-CNP-qi0y.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-3bEegSuf.js → diagram-LMA3HP47-SCiVmkcf.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BJ_x0rm6.js → diagram-OG6HWLK6-iTJLXH61.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-DNiiS5q6.js → erDiagram-TEJ5UH35-ByMpiPIa.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-DoouPklt.js → flowDiagram-I6XJVG4X-DW3m3ZpM.js} +1 -1
- package/dist/client/assets/{folder-open-CuUs2W7f.js → folder-open-G-9vPAdv.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BrR6VnP5.js → ganttDiagram-6RSMTGT7-BJeYH7Ol.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BV8_J6Ee.js → gitGraphDiagram-PVQCEYII-CK_Chj4m.js} +1 -1
- package/dist/client/assets/index-ccFe_1av.js +1459 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-DGwIIFkR.js → infoDiagram-5YYISTIA-D8Wg1IgN.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-D5dEH4RE.js → ishikawaDiagram-YF4QCWOH-DWROR2aX.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BuuSWPw5.js → journeyDiagram-JHISSGLW-Bns9774_.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-DA34uUla.js → kanban-definition-UN3LZRKU-QtURfAoI.js} +1 -1
- package/dist/client/assets/{mermaid.core-Ch4HatqA.js → mermaid.core-DqgrgWWm.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DUCG-qD3.js → mindmap-definition-RKZ34NQL-jLaDb5_X.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DWvFGiLa.js → pieDiagram-4H26LBE5-CjbHuIje.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-OPTVW3WT.js → quadrantDiagram-W4KKPZXB-xgHbylQr.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-D7feZvlU.js → requirementDiagram-4Y6WPE33-CHdD7W41.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-AtnSsEX1.js → sankeyDiagram-5OEKKPKP-C3me87g9.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BZjHUeI5.js → sequenceDiagram-3UESZ5HK-DSHfs1kU.js} +1 -1
- package/dist/client/assets/{shield-alert-CzaF9ROV.js → shield-alert-DkGlhVjI.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-C8j23Pvn.js → stateDiagram-AJRCARHV-C08n7sMP.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-CiZR_S5-.js → stateDiagram-v2-BHNVJYJU-BosoAbfO.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DvfNz25n.js → timeline-definition-PNZ67QCA-C21h9Zqh.js} +1 -1
- package/dist/client/assets/{upload-DTsYywhN.js → upload-B9B-tYt9.js} +1 -1
- package/dist/client/assets/{users-yAd09tKP.js → users-C_BA3C7E.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-CG2aMt3v.js → vennDiagram-CIIHVFJN-Bv3G0dwI.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-BnhBteen.js → wardley-L42UT6IY-WX0hHKcN.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DwqTzM3T.js → wardleyDiagram-YWT4CUSO-CMwIPWm7.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-5Cv2ZKMV.js → xychartDiagram-2RQKCTM6-CZvFdt6j.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +472 -94
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +11 -0
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/CommandCenter-CHO21xEW.js +0 -74
- package/dist/client/assets/DevServerView-B_p91Mj_.css +0 -1
- package/dist/client/assets/DevServerView-H9OkT1ld.js +0 -1
- package/dist/client/assets/SettingsModal-BZokaNyx.css +0 -1
- package/dist/client/assets/SettingsModal-JHACoQJe.js +0 -21
- package/dist/client/assets/channel-Clzpzu32.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CzYHP-af.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CzYHP-af.js +0 -1
- package/dist/client/assets/index-CZcWjLAb.js +0 -1459
package/dist/bin.js
CHANGED
|
@@ -435,6 +435,9 @@ var init_settings_schema = __esm({
|
|
|
435
435
|
defaultProvider: void 0,
|
|
436
436
|
defaultModelId: void 0,
|
|
437
437
|
testMode: void 0,
|
|
438
|
+
modelPricingOverrides: void 0,
|
|
439
|
+
modelPricingFetchedAt: void 0,
|
|
440
|
+
modelPricingSource: void 0,
|
|
438
441
|
modelRouterEnabled: void 0,
|
|
439
442
|
modelRouterCheapProvider: void 0,
|
|
440
443
|
modelRouterCheapModelId: void 0,
|
|
@@ -49378,6 +49381,9 @@ import { randomUUID as randomUUID15 } from "node:crypto";
|
|
|
49378
49381
|
import { mkdir as mkdir7, readdir as readdir5, readFile as readFile8, stat as stat3, writeFile as writeFile6, rename as rename4, unlink as unlink3 } from "node:fs/promises";
|
|
49379
49382
|
import { join as join20 } from "node:path";
|
|
49380
49383
|
import { existsSync as existsSync16, watch } from "node:fs";
|
|
49384
|
+
function isWorkflowColumnsCompatibilityFlagEnabled(settings) {
|
|
49385
|
+
return settings?.experimentalFeatures?.workflowColumns === true;
|
|
49386
|
+
}
|
|
49381
49387
|
function defineTaskColumn(column, serialize, sqlIdentifier = column) {
|
|
49382
49388
|
return { column, sqlIdentifier, serialize };
|
|
49383
49389
|
}
|
|
@@ -49692,7 +49698,6 @@ var init_store = __esm({
|
|
|
49692
49698
|
init_moved_settings();
|
|
49693
49699
|
init_workflow_ir();
|
|
49694
49700
|
init_workflow_steps_to_ir();
|
|
49695
|
-
init_workflow_columns_settings();
|
|
49696
49701
|
init_workflow_transitions();
|
|
49697
49702
|
init_plugin_gate_verdict();
|
|
49698
49703
|
init_trait_registry();
|
|
@@ -50490,7 +50495,7 @@ var init_store = __esm({
|
|
|
50490
50495
|
}
|
|
50491
50496
|
try {
|
|
50492
50497
|
const settings = await this.getSettingsFast();
|
|
50493
|
-
if (
|
|
50498
|
+
if (isWorkflowColumnsCompatibilityFlagEnabled(settings)) {
|
|
50494
50499
|
await this.runWorkflowColumnsIntegrityPass();
|
|
50495
50500
|
await this.recoverStaleTransitionPending();
|
|
50496
50501
|
} else {
|
|
@@ -52294,7 +52299,7 @@ ${TASK_UPSERT_SQL_ASSIGNMENTS}
|
|
|
52294
52299
|
await this.writeConfig(config);
|
|
52295
52300
|
const updatedMerged = { ...DEFAULT_SETTINGS, ...globalSettings, ...updatedProjectSettings };
|
|
52296
52301
|
this.emit("settings:updated", { settings: updatedMerged, previous: previousMerged });
|
|
52297
|
-
if (
|
|
52302
|
+
if (isWorkflowColumnsCompatibilityFlagEnabled(previousMerged) && !isWorkflowColumnsCompatibilityFlagEnabled(updatedMerged)) {
|
|
52298
52303
|
try {
|
|
52299
52304
|
await this.evacuateCustomColumnsToLegacy("flag-toggled-off");
|
|
52300
52305
|
} catch (err) {
|
|
@@ -52377,7 +52382,7 @@ ${TASK_UPSERT_SQL_ASSIGNMENTS}
|
|
|
52377
52382
|
merged.secretsSyncPassphraseConfigured = false;
|
|
52378
52383
|
}
|
|
52379
52384
|
this.emit("settings:updated", { settings: merged, previous });
|
|
52380
|
-
if (
|
|
52385
|
+
if (isWorkflowColumnsCompatibilityFlagEnabled(previous) && !isWorkflowColumnsCompatibilityFlagEnabled(merged)) {
|
|
52381
52386
|
try {
|
|
52382
52387
|
await this.evacuateCustomColumnsToLegacy("flag-toggled-off");
|
|
52383
52388
|
} catch (err) {
|
|
@@ -54621,7 +54626,8 @@ ${newTask.description}
|
|
|
54621
54626
|
return { kind: "engine" };
|
|
54622
54627
|
}
|
|
54623
54628
|
resolveWorkflowBypassGuards(moveSource, options) {
|
|
54624
|
-
|
|
54629
|
+
void moveSource;
|
|
54630
|
+
return options?.recoveryRehome === true || (options?.bypassGuards ?? (options?.moveSource === "engine" || options?.moveSource === "scheduler" || options?.skipMergeBlocker === true));
|
|
54625
54631
|
}
|
|
54626
54632
|
shouldSkipWorkflowMovePolicies(params) {
|
|
54627
54633
|
if (params.bypassGuards) return true;
|
|
@@ -54632,7 +54638,7 @@ ${newTask.description}
|
|
|
54632
54638
|
const task = await this.readTaskForMove(id);
|
|
54633
54639
|
const moveSource = options?.moveSource ?? "engine";
|
|
54634
54640
|
const mergedSettingsForMove = await this.getSettingsFast();
|
|
54635
|
-
if (!
|
|
54641
|
+
if (!isWorkflowColumnsCompatibilityFlagEnabled(mergedSettingsForMove)) return void 0;
|
|
54636
54642
|
if (task.column === toColumn) return void 0;
|
|
54637
54643
|
const workflowIr = this.resolveTaskWorkflowIrSync(id);
|
|
54638
54644
|
const workflowSignature = serializeWorkflowIr(workflowIr);
|
|
@@ -54716,7 +54722,7 @@ ${newTask.description}
|
|
|
54716
54722
|
const task = currentTask ?? await this.readTaskForMove(id);
|
|
54717
54723
|
const moveSource = options?.moveSource ?? "engine";
|
|
54718
54724
|
const mergedSettingsForMove = await this.getSettingsFast();
|
|
54719
|
-
const useWorkflow =
|
|
54725
|
+
const useWorkflow = isWorkflowColumnsCompatibilityFlagEnabled(mergedSettingsForMove);
|
|
54720
54726
|
const bypassGuards = this.resolveWorkflowBypassGuards(moveSource, options);
|
|
54721
54727
|
const workflowIr = useWorkflow ? this.resolveTaskWorkflowIrSync(id) : void 0;
|
|
54722
54728
|
if (task.column === toColumn) {
|
|
@@ -59343,12 +59349,32 @@ ${newTask.description}
|
|
|
59343
59349
|
/**
|
|
59344
59350
|
* FNXC:ArtifactRegistry 2026-06-19-22:04:
|
|
59345
59351
|
* Cross-agent registry query path for filtering artifacts across tasks, authors, and media types. LEFT JOIN keeps task-less registry artifacts visible while excluding artifacts attached to soft-deleted tasks.
|
|
59352
|
+
*
|
|
59353
|
+
* FNXC:ArtifactRegistry 2026-06-23-12:48:
|
|
59354
|
+
* Agent execution can list artifacts frequently while large generated outputs are stored inline. The registry list is metadata-only, so avoid selecting artifact content here and require callers to use getArtifact for the full payload.
|
|
59346
59355
|
*/
|
|
59347
59356
|
async listArtifacts(options) {
|
|
59348
59357
|
const limit = Math.min(Math.max(1, options?.limit ?? 200), 1e3);
|
|
59349
59358
|
const offset = Math.max(0, options?.offset ?? 0);
|
|
59350
59359
|
let sql = `
|
|
59351
|
-
SELECT
|
|
59360
|
+
SELECT
|
|
59361
|
+
a.id,
|
|
59362
|
+
a.type,
|
|
59363
|
+
a.title,
|
|
59364
|
+
a.description,
|
|
59365
|
+
a.mimeType,
|
|
59366
|
+
a.sizeBytes,
|
|
59367
|
+
a.uri,
|
|
59368
|
+
NULL as content,
|
|
59369
|
+
a.authorId,
|
|
59370
|
+
a.authorType,
|
|
59371
|
+
a.taskId,
|
|
59372
|
+
a.metadata,
|
|
59373
|
+
a.createdAt,
|
|
59374
|
+
a.updatedAt,
|
|
59375
|
+
t.title as taskTitle,
|
|
59376
|
+
t.description as taskDescription,
|
|
59377
|
+
t.column as taskColumn
|
|
59352
59378
|
FROM artifacts a
|
|
59353
59379
|
LEFT JOIN tasks t ON a.taskId = t.id
|
|
59354
59380
|
WHERE (a.taskId IS NULL OR t.${_TaskStore.ACTIVE_TASKS_WHERE})
|
|
@@ -61008,9 +61034,9 @@ ${stepsSection}`;
|
|
|
61008
61034
|
// always route through `moveTask` with `moveSource: "engine"` + `bypassGuards`
|
|
61009
61035
|
// (a recovery-class move, KTD-9) — never a raw column write — so capacity
|
|
61010
61036
|
// (KTD-10) and the single transition authority (KTD-3) are honored.
|
|
61011
|
-
/** True when the `workflowColumns` flag is ON (merged global + project). */
|
|
61037
|
+
/** True when the raw `workflowColumns` compatibility flag is ON (merged global + project). */
|
|
61012
61038
|
async workflowColumnsFlagOn() {
|
|
61013
|
-
return
|
|
61039
|
+
return isWorkflowColumnsCompatibilityFlagEnabled(await this.getSettingsFast());
|
|
61014
61040
|
}
|
|
61015
61041
|
/** The active (non-deleted) task ids currently selecting `workflowId`. A
|
|
61016
61042
|
* built-in/default workflow additionally owns every task with NO selection
|
|
@@ -62972,18 +62998,70 @@ var init_agent_token_usage = __esm({
|
|
|
62972
62998
|
function normalize4(s3) {
|
|
62973
62999
|
return (s3 ?? "").trim().toLowerCase();
|
|
62974
63000
|
}
|
|
62975
|
-
function
|
|
63001
|
+
function findBareModelPricing(model, entries) {
|
|
63002
|
+
for (const [key, entry] of Object.entries(entries)) {
|
|
63003
|
+
if (key.endsWith(`:${model}`)) return entry;
|
|
63004
|
+
}
|
|
63005
|
+
return void 0;
|
|
63006
|
+
}
|
|
63007
|
+
function lookupPricing(ref, overrides2) {
|
|
62976
63008
|
const provider = normalize4(ref.provider);
|
|
62977
63009
|
const model = normalize4(ref.model);
|
|
62978
63010
|
if (!model) return void 0;
|
|
63011
|
+
if (provider) {
|
|
63012
|
+
const exactOverride = overrides2?.[`${provider}:${model}`];
|
|
63013
|
+
if (exactOverride) return exactOverride;
|
|
63014
|
+
}
|
|
63015
|
+
const bareOverride = overrides2 ? findBareModelPricing(model, overrides2) : void 0;
|
|
63016
|
+
if (bareOverride) return bareOverride;
|
|
62979
63017
|
if (provider) {
|
|
62980
63018
|
const exact = MODEL_PRICING[`${provider}:${model}`];
|
|
62981
63019
|
if (exact) return exact;
|
|
62982
63020
|
}
|
|
62983
|
-
|
|
62984
|
-
|
|
62985
|
-
|
|
62986
|
-
return
|
|
63021
|
+
return findBareModelPricing(model, MODEL_PRICING);
|
|
63022
|
+
}
|
|
63023
|
+
function litellmProviderToFusionProvider(provider) {
|
|
63024
|
+
if (typeof provider !== "string") return null;
|
|
63025
|
+
const normalized = normalize4(provider);
|
|
63026
|
+
if (normalized === "openai") return "openai";
|
|
63027
|
+
if (normalized === "anthropic") return "anthropic";
|
|
63028
|
+
if (normalized === "gemini" || normalized.startsWith("gemini")) return "google";
|
|
63029
|
+
if (normalized === "vertex_ai" || normalized.startsWith("vertex_ai")) return "google";
|
|
63030
|
+
if (normalized === "vertex_ai-language-models") return "google";
|
|
63031
|
+
return null;
|
|
63032
|
+
}
|
|
63033
|
+
function numericField(entry, key) {
|
|
63034
|
+
const value = entry[key];
|
|
63035
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
63036
|
+
}
|
|
63037
|
+
function parseLiteLLMPricing(json) {
|
|
63038
|
+
const overrides2 = {};
|
|
63039
|
+
if (json === null || typeof json !== "object" || Array.isArray(json)) {
|
|
63040
|
+
return { overrides: overrides2, count: 0 };
|
|
63041
|
+
}
|
|
63042
|
+
for (const [modelId, value] of Object.entries(json)) {
|
|
63043
|
+
if (modelId === "sample_spec") continue;
|
|
63044
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) continue;
|
|
63045
|
+
const entry = value;
|
|
63046
|
+
if (entry.mode !== "chat") continue;
|
|
63047
|
+
const provider = litellmProviderToFusionProvider(entry.litellm_provider);
|
|
63048
|
+
if (!provider) continue;
|
|
63049
|
+
const inputCost = numericField(entry, "input_cost_per_token");
|
|
63050
|
+
const outputCost = numericField(entry, "output_cost_per_token");
|
|
63051
|
+
if (inputCost === void 0 || outputCost === void 0) continue;
|
|
63052
|
+
const inputPer1M = inputCost * 1e6;
|
|
63053
|
+
const outputPer1M = outputCost * 1e6;
|
|
63054
|
+
const cacheRead = numericField(entry, "cache_read_input_token_cost");
|
|
63055
|
+
const cacheWrite = numericField(entry, "cache_creation_input_token_cost");
|
|
63056
|
+
overrides2[`${provider}:${normalize4(modelId)}`] = {
|
|
63057
|
+
inputPer1M,
|
|
63058
|
+
outputPer1M,
|
|
63059
|
+
cacheReadPer1M: cacheRead === void 0 ? inputPer1M : cacheRead * 1e6,
|
|
63060
|
+
cacheWritePer1M: cacheWrite === void 0 ? inputPer1M : cacheWrite * 1e6,
|
|
63061
|
+
source: LITELLM_PRICING_SOURCE_LABEL
|
|
63062
|
+
};
|
|
63063
|
+
}
|
|
63064
|
+
return { overrides: overrides2, count: Object.keys(overrides2).length };
|
|
62987
63065
|
}
|
|
62988
63066
|
function isStale(now) {
|
|
62989
63067
|
if (now === void 0) return false;
|
|
@@ -62991,21 +63069,23 @@ function isStale(now) {
|
|
|
62991
63069
|
if (Number.isNaN(asOf)) return false;
|
|
62992
63070
|
return now - asOf > PRICING_STALE_AFTER_MS;
|
|
62993
63071
|
}
|
|
62994
|
-
function costFor(usage, model, now) {
|
|
63072
|
+
function costFor(usage, model, now, overrides2) {
|
|
62995
63073
|
const stale = isStale(now);
|
|
62996
|
-
const pricing = lookupPricing(model);
|
|
63074
|
+
const pricing = lookupPricing(model, overrides2);
|
|
62997
63075
|
if (!pricing) {
|
|
62998
63076
|
return { usd: null, unavailable: true, stale };
|
|
62999
63077
|
}
|
|
63000
63078
|
const usd = (usage.inputTokens * pricing.inputPer1M + usage.outputTokens * pricing.outputPer1M + usage.cachedTokens * pricing.cacheReadPer1M + usage.cacheWriteTokens * pricing.cacheWritePer1M) / 1e6;
|
|
63001
63079
|
return { usd, unavailable: false, stale };
|
|
63002
63080
|
}
|
|
63003
|
-
var pricingAsOf, PRICING_STALE_AFTER_MS, MODEL_PRICING;
|
|
63081
|
+
var pricingAsOf, PRICING_STALE_AFTER_MS, LITELLM_PRICING_SOURCE_URL, LITELLM_PRICING_SOURCE_LABEL, MODEL_PRICING;
|
|
63004
63082
|
var init_model_pricing = __esm({
|
|
63005
63083
|
"../core/src/model-pricing.ts"() {
|
|
63006
63084
|
"use strict";
|
|
63007
63085
|
pricingAsOf = "2026-06-21";
|
|
63008
63086
|
PRICING_STALE_AFTER_MS = 180 * 24 * 60 * 60 * 1e3;
|
|
63087
|
+
LITELLM_PRICING_SOURCE_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
63088
|
+
LITELLM_PRICING_SOURCE_LABEL = "litellm/model_prices_and_context_window.json";
|
|
63009
63089
|
MODEL_PRICING = {
|
|
63010
63090
|
// ── Anthropic Claude ────────────────────────────────────────────────
|
|
63011
63091
|
// input / output / cacheRead(0.1×) / cacheWrite(1.25×, 5-min TTL)
|
|
@@ -63252,7 +63332,7 @@ function groupKeyFor(row, groupBy2) {
|
|
|
63252
63332
|
function emptyCostAccumulator() {
|
|
63253
63333
|
return { usd: 0, anyPriced: false, anyUnavailable: false, anyStale: false };
|
|
63254
63334
|
}
|
|
63255
|
-
function addRowCost(acc, row, now) {
|
|
63335
|
+
function addRowCost(acc, row, now, pricingOverrides) {
|
|
63256
63336
|
const result = costFor(
|
|
63257
63337
|
{
|
|
63258
63338
|
inputTokens: row.inputTokens ?? 0,
|
|
@@ -63264,7 +63344,8 @@ function addRowCost(acc, row, now) {
|
|
|
63264
63344
|
provider: row.tokenUsageModelProvider ?? row.modelProvider,
|
|
63265
63345
|
model: row.tokenUsageModelId ?? row.modelId
|
|
63266
63346
|
},
|
|
63267
|
-
now
|
|
63347
|
+
now,
|
|
63348
|
+
pricingOverrides
|
|
63268
63349
|
);
|
|
63269
63350
|
if (result.stale) acc.anyStale = true;
|
|
63270
63351
|
if (result.unavailable || result.usd === null) {
|
|
@@ -63373,9 +63454,10 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
63373
63454
|
const groupBy2 = query.groupBy;
|
|
63374
63455
|
const granularity = query.granularity;
|
|
63375
63456
|
const now = query.now;
|
|
63457
|
+
const pricingOverrides = query.pricingOverrides;
|
|
63376
63458
|
for (const row of rows) {
|
|
63377
63459
|
addRow(totals, row);
|
|
63378
|
-
addRowCost(totalCost, row, now);
|
|
63460
|
+
addRowCost(totalCost, row, now, pricingOverrides);
|
|
63379
63461
|
if (groupBy2) {
|
|
63380
63462
|
const groupRows = groupBy2 === "model" || groupBy2 === "provider" ? parsePerModelRows(row) : [];
|
|
63381
63463
|
const rowsForGroup = groupRows.length > 0 ? groupRows : [row];
|
|
@@ -63388,7 +63470,7 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
63388
63470
|
groupCostMap.set(key, emptyCostAccumulator());
|
|
63389
63471
|
}
|
|
63390
63472
|
addRow(group, groupRow);
|
|
63391
|
-
addRowCost(groupCostMap.get(key), groupRow, now);
|
|
63473
|
+
addRowCost(groupCostMap.get(key), groupRow, now, pricingOverrides);
|
|
63392
63474
|
}
|
|
63393
63475
|
}
|
|
63394
63476
|
if (granularity) {
|
|
@@ -63400,7 +63482,7 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
63400
63482
|
seriesCostMap.set(bucket, emptyCostAccumulator());
|
|
63401
63483
|
}
|
|
63402
63484
|
addRow(point, row);
|
|
63403
|
-
addRowCost(seriesCostMap.get(bucket), row, now);
|
|
63485
|
+
addRowCost(seriesCostMap.get(bucket), row, now, pricingOverrides);
|
|
63404
63486
|
}
|
|
63405
63487
|
}
|
|
63406
63488
|
for (const [key, group] of groupMap) {
|
|
@@ -64051,7 +64133,7 @@ function addTokenRow(totals, row) {
|
|
|
64051
64133
|
totals.totalTokens += row.totalTokens ?? (row.inputTokens ?? 0) + (row.outputTokens ?? 0) + (row.cachedTokens ?? 0) + (row.cacheWriteTokens ?? 0);
|
|
64052
64134
|
totals.nTasks += 1;
|
|
64053
64135
|
}
|
|
64054
|
-
function addRowCost2(acc, row, now) {
|
|
64136
|
+
function addRowCost2(acc, row, now, pricingOverrides) {
|
|
64055
64137
|
const result = costFor(
|
|
64056
64138
|
{
|
|
64057
64139
|
inputTokens: row.inputTokens ?? 0,
|
|
@@ -64060,7 +64142,8 @@ function addRowCost2(acc, row, now) {
|
|
|
64060
64142
|
cacheWriteTokens: row.cacheWriteTokens ?? 0
|
|
64061
64143
|
},
|
|
64062
64144
|
{ provider: row.modelProvider, model: row.modelId },
|
|
64063
|
-
now
|
|
64145
|
+
now,
|
|
64146
|
+
pricingOverrides
|
|
64064
64147
|
);
|
|
64065
64148
|
if (result.stale) acc.anyStale = true;
|
|
64066
64149
|
if (result.unavailable || result.usd === null) {
|
|
@@ -64119,6 +64202,7 @@ function aggregateTeamAnalytics(db, query = {}) {
|
|
|
64119
64202
|
const costAccumulators = /* @__PURE__ */ new Map();
|
|
64120
64203
|
const totalTokens = emptyTokenTotals();
|
|
64121
64204
|
const totalCost = emptyCostAccumulator2();
|
|
64205
|
+
const pricingOverrides = query.pricingOverrides;
|
|
64122
64206
|
const agents = db.prepare(`SELECT id, name, role, state FROM agents ORDER BY id`).all();
|
|
64123
64207
|
for (const agent of agents) {
|
|
64124
64208
|
summaries.set(agent.id, makeSummary(agent.id, agent));
|
|
@@ -64154,8 +64238,8 @@ function aggregateTeamAnalytics(db, query = {}) {
|
|
|
64154
64238
|
costAccumulators.set(row.agentId, agentCost);
|
|
64155
64239
|
addTokenRow(summary.tokens, row);
|
|
64156
64240
|
addTokenRow(totalTokens, row);
|
|
64157
|
-
addRowCost2(agentCost, row, query.now);
|
|
64158
|
-
addRowCost2(totalCost, row, query.now);
|
|
64241
|
+
addRowCost2(agentCost, row, query.now, pricingOverrides);
|
|
64242
|
+
addRowCost2(totalCost, row, query.now, pricingOverrides);
|
|
64159
64243
|
}
|
|
64160
64244
|
const completedClauses = ["assignedAgentId IS NOT NULL", `"column" = 'done'`, "columnMovedAt IS NOT NULL"];
|
|
64161
64245
|
const completedParams = [];
|
|
@@ -86359,6 +86443,8 @@ __export(src_exports, {
|
|
|
86359
86443
|
InvalidMergeQueueLeaseDurationError: () => InvalidMergeQueueLeaseDurationError,
|
|
86360
86444
|
InvalidRehomeTargetError: () => InvalidRehomeTargetError,
|
|
86361
86445
|
LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES: () => LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES,
|
|
86446
|
+
LITELLM_PRICING_SOURCE_LABEL: () => LITELLM_PRICING_SOURCE_LABEL,
|
|
86447
|
+
LITELLM_PRICING_SOURCE_URL: () => LITELLM_PRICING_SOURCE_URL,
|
|
86362
86448
|
LinkedWorktreeBootstrapRefusedError: () => LinkedWorktreeBootstrapRefusedError,
|
|
86363
86449
|
MANUAL_RETRY_RESET_COUNTER_KEYS: () => MANUAL_RETRY_RESET_COUNTER_KEYS,
|
|
86364
86450
|
MASTER_KEY_FILENAME: () => MASTER_KEY_FILENAME,
|
|
@@ -86899,6 +86985,7 @@ __export(src_exports, {
|
|
|
86899
86985
|
parseInsightExtractionResponse: () => parseInsightExtractionResponse,
|
|
86900
86986
|
parseInstanceNodeId: () => parseInstanceNodeId,
|
|
86901
86987
|
parseJsonSteps: () => parseJsonSteps,
|
|
86988
|
+
parseLiteLLMPricing: () => parseLiteLLMPricing,
|
|
86902
86989
|
parseNoOpCompletionMarker: () => parseNoOpCompletionMarker,
|
|
86903
86990
|
parseProjectManifest: () => parseProjectManifest,
|
|
86904
86991
|
parseRepoFromRemote: () => parseRepoFromRemote,
|
|
@@ -88349,6 +88436,74 @@ var init_logger2 = __esm({
|
|
|
88349
88436
|
});
|
|
88350
88437
|
|
|
88351
88438
|
// ../engine/src/agent-logger.ts
|
|
88439
|
+
function truncateToolResultDetail(value) {
|
|
88440
|
+
if (value.length <= TOOL_RESULT_DETAIL_LIMIT) return value;
|
|
88441
|
+
return `${value.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
|
|
88442
|
+
}
|
|
88443
|
+
function summarizeToolResultValue(value, state) {
|
|
88444
|
+
if (state.remainingChars <= 0) return "[truncated]";
|
|
88445
|
+
if (value === null || value === void 0) return value;
|
|
88446
|
+
if (typeof value === "string") {
|
|
88447
|
+
const truncated = value.length > state.remainingChars ? `${value.slice(0, state.remainingChars)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}` : value;
|
|
88448
|
+
state.remainingChars -= Math.min(value.length, state.remainingChars);
|
|
88449
|
+
return truncated;
|
|
88450
|
+
}
|
|
88451
|
+
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
88452
|
+
if (typeof value === "bigint") return value.toString();
|
|
88453
|
+
if (typeof value === "function" || typeof value === "symbol") return `[${typeof value}]`;
|
|
88454
|
+
if (value instanceof Error) {
|
|
88455
|
+
return {
|
|
88456
|
+
name: value.name,
|
|
88457
|
+
message: summarizeToolResultValue(value.message, state),
|
|
88458
|
+
...value.stack ? { stack: summarizeToolResultValue(value.stack, state) } : {}
|
|
88459
|
+
};
|
|
88460
|
+
}
|
|
88461
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
|
|
88462
|
+
return `<Buffer ${value.byteLength} bytes>`;
|
|
88463
|
+
}
|
|
88464
|
+
if (ArrayBuffer.isView(value)) {
|
|
88465
|
+
return `<${value.constructor.name} ${value.byteLength} bytes>`;
|
|
88466
|
+
}
|
|
88467
|
+
if (value instanceof ArrayBuffer) {
|
|
88468
|
+
return `<ArrayBuffer ${value.byteLength} bytes>`;
|
|
88469
|
+
}
|
|
88470
|
+
if (typeof value !== "object") return String(value);
|
|
88471
|
+
if (state.seen.has(value)) return "[Circular]";
|
|
88472
|
+
state.seen.add(value);
|
|
88473
|
+
if (Array.isArray(value)) {
|
|
88474
|
+
const summarized2 = value.slice(0, TOOL_RESULT_MAX_ARRAY_ITEMS).map((item) => summarizeToolResultValue(item, state));
|
|
88475
|
+
if (value.length > TOOL_RESULT_MAX_ARRAY_ITEMS) summarized2.push(`[${value.length - TOOL_RESULT_MAX_ARRAY_ITEMS} more items truncated]`);
|
|
88476
|
+
return summarized2;
|
|
88477
|
+
}
|
|
88478
|
+
const summarized = {};
|
|
88479
|
+
let count2 = 0;
|
|
88480
|
+
for (const key of Object.keys(value)) {
|
|
88481
|
+
if (count2 >= TOOL_RESULT_MAX_OBJECT_KEYS) {
|
|
88482
|
+
summarized.__truncatedKeys = true;
|
|
88483
|
+
break;
|
|
88484
|
+
}
|
|
88485
|
+
summarized[key] = summarizeToolResultValue(value[key], state);
|
|
88486
|
+
count2 += 1;
|
|
88487
|
+
if (state.remainingChars <= 0) {
|
|
88488
|
+
summarized.__truncated = true;
|
|
88489
|
+
break;
|
|
88490
|
+
}
|
|
88491
|
+
}
|
|
88492
|
+
return summarized;
|
|
88493
|
+
}
|
|
88494
|
+
function summarizeToolResultDetail(result) {
|
|
88495
|
+
if (result === void 0 || result === null) return void 0;
|
|
88496
|
+
if (typeof result === "string") return truncateToolResultDetail(result);
|
|
88497
|
+
try {
|
|
88498
|
+
const preview = summarizeToolResultValue(result, {
|
|
88499
|
+
remainingChars: TOOL_RESULT_DETAIL_LIMIT,
|
|
88500
|
+
seen: /* @__PURE__ */ new WeakSet()
|
|
88501
|
+
});
|
|
88502
|
+
return truncateToolResultDetail(JSON.stringify(preview));
|
|
88503
|
+
} catch {
|
|
88504
|
+
return truncateToolResultDetail(String(result));
|
|
88505
|
+
}
|
|
88506
|
+
}
|
|
88352
88507
|
function summarizeToolArgs(name, args) {
|
|
88353
88508
|
if (!args) return void 0;
|
|
88354
88509
|
const lowerName = name.toLowerCase();
|
|
@@ -88365,7 +88520,7 @@ function summarizeToolArgs(name, args) {
|
|
|
88365
88520
|
}
|
|
88366
88521
|
return void 0;
|
|
88367
88522
|
}
|
|
88368
|
-
var FLUSH_SIZE_BYTES, FLUSH_INTERVAL_MS, ENTRY_BATCH_SIZE, AgentLogger;
|
|
88523
|
+
var FLUSH_SIZE_BYTES, FLUSH_INTERVAL_MS, ENTRY_BATCH_SIZE, TOOL_RESULT_DETAIL_LIMIT, TOOL_RESULT_DETAIL_TRUNCATION_NOTICE, TOOL_RESULT_MAX_ARRAY_ITEMS, TOOL_RESULT_MAX_OBJECT_KEYS, AgentLogger;
|
|
88369
88524
|
var init_agent_logger = __esm({
|
|
88370
88525
|
"../engine/src/agent-logger.ts"() {
|
|
88371
88526
|
"use strict";
|
|
@@ -88374,6 +88529,10 @@ var init_agent_logger = __esm({
|
|
|
88374
88529
|
FLUSH_SIZE_BYTES = 1024;
|
|
88375
88530
|
FLUSH_INTERVAL_MS = 500;
|
|
88376
88531
|
ENTRY_BATCH_SIZE = 50;
|
|
88532
|
+
TOOL_RESULT_DETAIL_LIMIT = 4096;
|
|
88533
|
+
TOOL_RESULT_DETAIL_TRUNCATION_NOTICE = "\n\n[tool output truncated to keep dashboard log views responsive]";
|
|
88534
|
+
TOOL_RESULT_MAX_ARRAY_ITEMS = 25;
|
|
88535
|
+
TOOL_RESULT_MAX_OBJECT_KEYS = 50;
|
|
88377
88536
|
AgentLogger = class {
|
|
88378
88537
|
textBuffer = "";
|
|
88379
88538
|
thinkingBuffer = "";
|
|
@@ -88519,10 +88678,7 @@ var init_agent_logger = __esm({
|
|
|
88519
88678
|
*/
|
|
88520
88679
|
onToolEnd(name, isError, result) {
|
|
88521
88680
|
const type = isError ? "tool_error" : "tool_result";
|
|
88522
|
-
|
|
88523
|
-
if (result !== void 0 && result !== null) {
|
|
88524
|
-
detail = typeof result === "string" ? result : JSON.stringify(result);
|
|
88525
|
-
}
|
|
88681
|
+
const detail = summarizeToolResultDetail(result);
|
|
88526
88682
|
this.writeEntry(name, type, detail, `Failed to log tool end "${name}" (${type}) for ${this.taskId}`);
|
|
88527
88683
|
const startedAt = this.toolStartedAt.get(name);
|
|
88528
88684
|
if (startedAt !== void 0) this.toolStartedAt.delete(name);
|
|
@@ -89569,7 +89725,6 @@ function countCapacitySlot(allTasks, effectiveWorkflowIdByTask, targetColumn, wo
|
|
|
89569
89725
|
async function runHoldReleaseSweep(store, deps) {
|
|
89570
89726
|
const result = { released: [], held: [] };
|
|
89571
89727
|
const settings = await store.getSettings();
|
|
89572
|
-
if (!isWorkflowColumnsEnabled(settings)) return result;
|
|
89573
89728
|
const allTasks = await store.listTasks({ includeArchived: false });
|
|
89574
89729
|
const irCache = /* @__PURE__ */ new Map();
|
|
89575
89730
|
const effectiveWorkflowIdByTask = /* @__PURE__ */ new Map();
|
|
@@ -108567,10 +108722,83 @@ var init_unlinked_missions_advisory_reporter = __esm({
|
|
|
108567
108722
|
}
|
|
108568
108723
|
});
|
|
108569
108724
|
|
|
108725
|
+
// ../engine/src/task-agent-sync.ts
|
|
108726
|
+
function hasFreshActiveHeartbeatRun(activeRun, now = Date.now(), freshRunMs = PARKED_AGENT_LINK_FRESH_RUN_MS) {
|
|
108727
|
+
const runStartedAt = activeRun?.startedAt;
|
|
108728
|
+
const runAgeMs = runStartedAt ? now - Date.parse(runStartedAt) : Number.POSITIVE_INFINITY;
|
|
108729
|
+
return {
|
|
108730
|
+
hasFreshRun: Boolean(activeRun) && Number.isFinite(runAgeMs) && runAgeMs <= freshRunMs,
|
|
108731
|
+
runAgeMs
|
|
108732
|
+
};
|
|
108733
|
+
}
|
|
108734
|
+
function isParkedTaskColumn(task) {
|
|
108735
|
+
return task?.column === "todo" || task?.column === "triage";
|
|
108736
|
+
}
|
|
108737
|
+
function evaluateParkedAgentTaskLink(options) {
|
|
108738
|
+
const { hasFreshRun, runAgeMs } = hasFreshActiveHeartbeatRun(options.activeRun, options.now);
|
|
108739
|
+
const hasActiveExecution = options.hasActiveAgentExecution?.(options.agent.id) === true;
|
|
108740
|
+
return {
|
|
108741
|
+
hasFreshRun,
|
|
108742
|
+
hasActiveExecution,
|
|
108743
|
+
shouldPreserveParkedLink: isParkedTaskColumn(options.linkedTask) && (hasFreshRun || hasActiveExecution),
|
|
108744
|
+
runAgeMs
|
|
108745
|
+
};
|
|
108746
|
+
}
|
|
108747
|
+
function attachAgentLinkSync(opts) {
|
|
108748
|
+
const logger3 = opts.logger ?? console;
|
|
108749
|
+
const handler = async ({ task, from, to: to2 }) => {
|
|
108750
|
+
if (!CLEAR_COLUMNS.has(to2)) {
|
|
108751
|
+
return;
|
|
108752
|
+
}
|
|
108753
|
+
try {
|
|
108754
|
+
const agents = await opts.agentStore.listAgents({ includeEphemeral: false });
|
|
108755
|
+
const linkedAgents = agents.filter((agent) => agent.taskId === task.id);
|
|
108756
|
+
for (const agent of linkedAgents) {
|
|
108757
|
+
if (to2 === "todo" || to2 === "triage") {
|
|
108758
|
+
const activeRun = await opts.agentStore.getActiveHeartbeatRun?.(agent.id);
|
|
108759
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
108760
|
+
agent,
|
|
108761
|
+
linkedTask: { column: to2 },
|
|
108762
|
+
activeRun,
|
|
108763
|
+
hasActiveAgentExecution: opts.hasActiveAgentExecution
|
|
108764
|
+
});
|
|
108765
|
+
if (proof.shouldPreserveParkedLink) {
|
|
108766
|
+
continue;
|
|
108767
|
+
}
|
|
108768
|
+
}
|
|
108769
|
+
if (agent.state === "running") {
|
|
108770
|
+
await opts.agentStore.updateAgentState(agent.id, "active");
|
|
108771
|
+
}
|
|
108772
|
+
await opts.agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
108773
|
+
logger3.log(`taskAgentLinkSync: cleared agent ${agent.id} taskId from ${task.id} after move ${from} \u2192 ${to2}`);
|
|
108774
|
+
}
|
|
108775
|
+
} catch (error) {
|
|
108776
|
+
logger3.warn(
|
|
108777
|
+
`taskAgentLinkSync: failed to sync agents for task ${task.id} after move ${from} \u2192 ${to2}: ${error instanceof Error ? error.message : String(error)}`
|
|
108778
|
+
);
|
|
108779
|
+
}
|
|
108780
|
+
};
|
|
108781
|
+
opts.store.on("task:moved", handler);
|
|
108782
|
+
return () => {
|
|
108783
|
+
opts.store.off("task:moved", handler);
|
|
108784
|
+
};
|
|
108785
|
+
}
|
|
108786
|
+
var PARKED_AGENT_LINK_FRESH_RUN_MS, CLEAR_COLUMNS;
|
|
108787
|
+
var init_task_agent_sync = __esm({
|
|
108788
|
+
"../engine/src/task-agent-sync.ts"() {
|
|
108789
|
+
"use strict";
|
|
108790
|
+
PARKED_AGENT_LINK_FRESH_RUN_MS = 5 * 6e4;
|
|
108791
|
+
CLEAR_COLUMNS = /* @__PURE__ */ new Set(["done", "archived", "todo", "triage"]);
|
|
108792
|
+
}
|
|
108793
|
+
});
|
|
108794
|
+
|
|
108570
108795
|
// ../engine/src/scheduler.ts
|
|
108571
108796
|
import { existsSync as existsSync37 } from "node:fs";
|
|
108572
108797
|
import { readFile as readFile20 } from "node:fs/promises";
|
|
108573
108798
|
import { join as join42 } from "node:path";
|
|
108799
|
+
function shouldRunWorkflowColumnScheduler(_settings) {
|
|
108800
|
+
return true;
|
|
108801
|
+
}
|
|
108574
108802
|
function pathsOverlap2(a, b2) {
|
|
108575
108803
|
for (const pa of a) {
|
|
108576
108804
|
const prefixA = pa.endsWith("/*") ? pa.slice(0, -1) : null;
|
|
@@ -108811,6 +109039,7 @@ var init_scheduler = __esm({
|
|
|
108811
109039
|
init_run_audit();
|
|
108812
109040
|
init_src();
|
|
108813
109041
|
init_hold_release();
|
|
109042
|
+
init_task_agent_sync();
|
|
108814
109043
|
COORDINATION_SAFE_SCOPE_EXACT = /* @__PURE__ */ new Set([
|
|
108815
109044
|
// Literal glob scope entries from PROMPT.md are kept here; concrete
|
|
108816
109045
|
// `.changeset/<name>.md` files are covered by COORDINATION_SAFE_SCOPE_PREFIXES.
|
|
@@ -109257,12 +109486,27 @@ var init_scheduler = __esm({
|
|
|
109257
109486
|
async rollbackRunningAgentsForQueuedTodoTask(taskId) {
|
|
109258
109487
|
const agentStore = this.options.agentStore;
|
|
109259
109488
|
if (!agentStore) return;
|
|
109260
|
-
const runningAgents = await agentStore.listAgents({ state: "running", includeEphemeral:
|
|
109489
|
+
const runningAgents = await agentStore.listAgents({ state: "running", includeEphemeral: false });
|
|
109261
109490
|
const linkedAgents = runningAgents.filter((agent) => agent.taskId === taskId);
|
|
109262
109491
|
for (const agent of linkedAgents) {
|
|
109492
|
+
const activeRun = await agentStore.getActiveHeartbeatRun?.(agent.id);
|
|
109493
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
109494
|
+
agent,
|
|
109495
|
+
linkedTask: { column: "todo" },
|
|
109496
|
+
activeRun,
|
|
109497
|
+
hasActiveAgentExecution: this.options.hasActiveAgentExecution
|
|
109498
|
+
});
|
|
109499
|
+
if (proof.shouldPreserveParkedLink) {
|
|
109500
|
+
schedulerLog.log(
|
|
109501
|
+
`Preserved running agent ${agent.id} for queued ${taskId}; live proof freshRun=${proof.hasFreshRun} activeExecution=${proof.hasActiveExecution}`
|
|
109502
|
+
);
|
|
109503
|
+
continue;
|
|
109504
|
+
}
|
|
109263
109505
|
await agentStore.updateAgentState(agent.id, "active");
|
|
109264
109506
|
await agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
109265
|
-
schedulerLog.log(
|
|
109507
|
+
schedulerLog.log(
|
|
109508
|
+
`Cleared stale running agent ${agent.id} after overlap requeue of ${taskId}; file-scope lease remains queued`
|
|
109509
|
+
);
|
|
109266
109510
|
}
|
|
109267
109511
|
}
|
|
109268
109512
|
/**
|
|
@@ -109382,7 +109626,7 @@ var init_scheduler = __esm({
|
|
|
109382
109626
|
schedulerLog.log("Engine pause cleared \u2014 scheduling resumed");
|
|
109383
109627
|
}
|
|
109384
109628
|
this.wasEnginePaused = false;
|
|
109385
|
-
if (
|
|
109629
|
+
if (shouldRunWorkflowColumnScheduler(settings)) {
|
|
109386
109630
|
await this.runHoldReleaseSweepPass(tasks, settings);
|
|
109387
109631
|
tasks = await this.store.listTasks({ slim: true, includeArchived: false, startupMemo: false });
|
|
109388
109632
|
settings = await this.store.getSettings();
|
|
@@ -110408,6 +110652,7 @@ var init_scheduler = __esm({
|
|
|
110408
110652
|
blockedBy: null,
|
|
110409
110653
|
overlapBlockedBy: overlappingTaskId
|
|
110410
110654
|
});
|
|
110655
|
+
await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
|
|
110411
110656
|
await this.logDispatchQueuedReason(
|
|
110412
110657
|
task.id,
|
|
110413
110658
|
`queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
|
|
@@ -125336,6 +125581,7 @@ var init_self_healing = __esm({
|
|
|
125336
125581
|
init_notifier();
|
|
125337
125582
|
init_dependency_blocked_todo_reporter();
|
|
125338
125583
|
init_scheduler();
|
|
125584
|
+
init_task_agent_sync();
|
|
125339
125585
|
init_transient_merge_error_classifier();
|
|
125340
125586
|
init_transient_merge_error_classifier();
|
|
125341
125587
|
log22 = createLogger2("self-healing");
|
|
@@ -125402,7 +125648,7 @@ var init_self_healing = __esm({
|
|
|
125402
125648
|
DURABLE_ERROR_RECOVERY_MAX_RETRIES = 5;
|
|
125403
125649
|
DURABLE_ERROR_RECOVERY_BASE_COOLDOWN_MS = 3e4;
|
|
125404
125650
|
DURABLE_ERROR_RECOVERY_MAX_COOLDOWN_MS = 15 * 6e4;
|
|
125405
|
-
RUNNING_ON_INACTIVE_TASK_STALE_RUN_MS =
|
|
125651
|
+
RUNNING_ON_INACTIVE_TASK_STALE_RUN_MS = PARKED_AGENT_LINK_FRESH_RUN_MS;
|
|
125406
125652
|
SelfHealingManager = class _SelfHealingManager {
|
|
125407
125653
|
constructor(store, options) {
|
|
125408
125654
|
this.store = store;
|
|
@@ -132230,6 +132476,36 @@ var init_self_healing = __esm({
|
|
|
132230
132476
|
const exponential = DURABLE_ERROR_RECOVERY_BASE_COOLDOWN_MS * Math.pow(2, clampedAttempts - 1);
|
|
132231
132477
|
return Math.min(exponential, DURABLE_ERROR_RECOVERY_MAX_COOLDOWN_MS);
|
|
132232
132478
|
}
|
|
132479
|
+
async emitStaleAgentAssignmentAudit(options) {
|
|
132480
|
+
try {
|
|
132481
|
+
await createRunAuditor(this.store, {
|
|
132482
|
+
runId: generateSyntheticRunId("self-healing-stale-agent-assignment", options.taskId),
|
|
132483
|
+
agentId: "self-healing",
|
|
132484
|
+
taskId: options.taskId,
|
|
132485
|
+
taskLineageId: options.linkedTask?.lineageId,
|
|
132486
|
+
phase: "reconcile-stale-agent-assignment"
|
|
132487
|
+
}).database({
|
|
132488
|
+
type: "task:reconcile-stale-agent-assignment",
|
|
132489
|
+
target: options.agent.id,
|
|
132490
|
+
metadata: {
|
|
132491
|
+
agentId: options.agent.id,
|
|
132492
|
+
taskId: options.taskId,
|
|
132493
|
+
taskColumn: options.linkedTask?.column ?? null,
|
|
132494
|
+
agentState: options.agent.state,
|
|
132495
|
+
status: options.linkedTask?.status ?? null,
|
|
132496
|
+
blockedBy: options.linkedTask?.blockedBy ?? null,
|
|
132497
|
+
overlapBlockedBy: options.linkedTask?.overlapBlockedBy ?? null,
|
|
132498
|
+
hadFreshRun: options.hadFreshRun,
|
|
132499
|
+
hadActiveExecution: options.hadActiveExecution,
|
|
132500
|
+
reason: options.reason
|
|
132501
|
+
}
|
|
132502
|
+
});
|
|
132503
|
+
} catch (error) {
|
|
132504
|
+
log22.warn(
|
|
132505
|
+
`Failed to emit stale agent assignment audit for ${options.agent.id}/${options.taskId}: ${error instanceof Error ? error.message : String(error)}`
|
|
132506
|
+
);
|
|
132507
|
+
}
|
|
132508
|
+
}
|
|
132233
132509
|
async recoverAgentsRunningOnInactiveTasks() {
|
|
132234
132510
|
const agentStore = this.options.agentStore;
|
|
132235
132511
|
if (!agentStore) {
|
|
@@ -132247,16 +132523,30 @@ var init_self_healing = __esm({
|
|
|
132247
132523
|
continue;
|
|
132248
132524
|
}
|
|
132249
132525
|
const activeRun = await agentStore.getActiveHeartbeatRun(agent.id);
|
|
132250
|
-
const
|
|
132251
|
-
|
|
132252
|
-
|
|
132253
|
-
|
|
132526
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
132527
|
+
agent,
|
|
132528
|
+
linkedTask: linkedTask ?? { column: "todo" },
|
|
132529
|
+
activeRun,
|
|
132530
|
+
hasActiveAgentExecution: this.options.hasActiveAgentExecution,
|
|
132531
|
+
now
|
|
132532
|
+
});
|
|
132533
|
+
if (proof.hasFreshRun || proof.hasActiveExecution) {
|
|
132254
132534
|
continue;
|
|
132255
132535
|
}
|
|
132536
|
+
const reason = linkedTask ? `running durable agent linked to inactive ${linkedTask.column} task without live execution proof` : "running durable agent linked to missing task without live execution proof";
|
|
132537
|
+
const staleAgentState = agent.state;
|
|
132256
132538
|
await agentStore.updateAgentState(agent.id, "active");
|
|
132257
132539
|
await agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
132540
|
+
await this.emitStaleAgentAssignmentAudit({
|
|
132541
|
+
agent: { id: agent.id, state: staleAgentState },
|
|
132542
|
+
taskId: agent.taskId,
|
|
132543
|
+
linkedTask,
|
|
132544
|
+
hadFreshRun: proof.hasFreshRun,
|
|
132545
|
+
hadActiveExecution: proof.hasActiveExecution,
|
|
132546
|
+
reason
|
|
132547
|
+
});
|
|
132258
132548
|
recoveredAgentIds.add(agent.id);
|
|
132259
|
-
log22.log(`Recovered running durable agent ${agent.id} on inactive task ${agent.taskId}`);
|
|
132549
|
+
log22.log(`Recovered running durable agent ${agent.id} on inactive task ${agent.taskId}; file-scope lease preserved when present`);
|
|
132260
132550
|
}
|
|
132261
132551
|
return recoveredAgentIds.size;
|
|
132262
132552
|
}
|
|
@@ -132276,6 +132566,8 @@ var init_self_healing = __esm({
|
|
|
132276
132566
|
const linkedTask = await this.store.getTask(linkedTaskId);
|
|
132277
132567
|
let shouldClear = false;
|
|
132278
132568
|
let reason = "";
|
|
132569
|
+
let hadFreshRun = false;
|
|
132570
|
+
let hadActiveExecution = false;
|
|
132279
132571
|
if (!linkedTask) {
|
|
132280
132572
|
shouldClear = true;
|
|
132281
132573
|
reason = "linked task missing";
|
|
@@ -132287,21 +132579,38 @@ var init_self_healing = __esm({
|
|
|
132287
132579
|
reason = `linked task assigned to ${linkedTask.assignedAgentId}`;
|
|
132288
132580
|
} else if (linkedTask.column === "todo" || linkedTask.column === "triage") {
|
|
132289
132581
|
const activeRun = await agentStore.getActiveHeartbeatRun(agent.id);
|
|
132290
|
-
const
|
|
132291
|
-
|
|
132292
|
-
|
|
132293
|
-
|
|
132294
|
-
|
|
132582
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
132583
|
+
agent,
|
|
132584
|
+
linkedTask,
|
|
132585
|
+
activeRun,
|
|
132586
|
+
hasActiveAgentExecution: this.options.hasActiveAgentExecution,
|
|
132587
|
+
now
|
|
132588
|
+
});
|
|
132589
|
+
hadFreshRun = proof.hasFreshRun;
|
|
132590
|
+
hadActiveExecution = proof.hasActiveExecution;
|
|
132591
|
+
if (!proof.shouldPreserveParkedLink) {
|
|
132295
132592
|
shouldClear = true;
|
|
132296
|
-
reason = `linked task in queued column ${linkedTask.column} without fresh run`;
|
|
132593
|
+
reason = `linked task in queued column ${linkedTask.column} without fresh run or active execution`;
|
|
132297
132594
|
}
|
|
132298
132595
|
}
|
|
132299
132596
|
if (!shouldClear) {
|
|
132300
132597
|
continue;
|
|
132301
132598
|
}
|
|
132599
|
+
const staleAgentState = agent.state;
|
|
132600
|
+
if (agent.state === "running") {
|
|
132601
|
+
await agentStore.updateAgentState(agent.id, "active");
|
|
132602
|
+
}
|
|
132302
132603
|
await agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
132604
|
+
await this.emitStaleAgentAssignmentAudit({
|
|
132605
|
+
agent: { id: agent.id, state: staleAgentState },
|
|
132606
|
+
taskId: linkedTaskId,
|
|
132607
|
+
linkedTask,
|
|
132608
|
+
hadFreshRun,
|
|
132609
|
+
hadActiveExecution,
|
|
132610
|
+
reason
|
|
132611
|
+
});
|
|
132303
132612
|
clearedAgentIds.add(agent.id);
|
|
132304
|
-
log22.log(`Cleared drifted durable agent task link for ${agent.id} (${linkedTaskId}): ${reason}`);
|
|
132613
|
+
log22.log(`Cleared drifted durable agent task link for ${agent.id} (${linkedTaskId}): ${reason}; file-scope lease preserved when present`);
|
|
132305
132614
|
}
|
|
132306
132615
|
log22.log(`Recovered ${clearedAgentIds.size} drifted durable agent task link(s)`);
|
|
132307
132616
|
return clearedAgentIds.size;
|
|
@@ -135456,6 +135765,10 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
135456
135765
|
// Project-level validator fallback
|
|
135457
135766
|
projectValidatorFallbackProvider: currentSettings.validatorFallbackProvider,
|
|
135458
135767
|
projectValidatorFallbackModelId: currentSettings.validatorFallbackModelId,
|
|
135768
|
+
// FNXC:SpecReviewerFallback 2026-06-23-08:50:
|
|
135769
|
+
// Spec review must inherit global/default fallback reviewer model settings when no validator-specific fallback is configured, plus the project settings/prompt payload that reviewer sessions use for memory and custom prompt behavior.
|
|
135770
|
+
fallbackProvider: currentSettings.fallbackProvider,
|
|
135771
|
+
fallbackModelId: currentSettings.fallbackModelId,
|
|
135459
135772
|
// Global validator lane
|
|
135460
135773
|
globalValidatorProvider: currentSettings.validatorGlobalProvider,
|
|
135461
135774
|
globalValidatorModelId: currentSettings.validatorGlobalModelId,
|
|
@@ -135467,8 +135780,10 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
135467
135780
|
taskId,
|
|
135468
135781
|
task: currentDetail,
|
|
135469
135782
|
userComments: currentUserComments.length > 0 ? currentUserComments : void 0,
|
|
135783
|
+
agentPrompts: currentSettings.agentPrompts,
|
|
135470
135784
|
agentStore: this.options.agentStore,
|
|
135471
135785
|
rootDir,
|
|
135786
|
+
settings: currentSettings,
|
|
135472
135787
|
// Track the spec reviewer's session under this task so it's
|
|
135473
135788
|
// disposed alongside the main triage session on global pause.
|
|
135474
135789
|
onSessionCreated: (s3) => this.registerSubagentSession(taskId, s3),
|
|
@@ -153878,7 +154193,14 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
153878
154193
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
153879
154194
|
executorLog.warn(`Child agent ${agentId} failed: ${errorMessage}`);
|
|
153880
154195
|
} finally {
|
|
153881
|
-
this.childSessions.
|
|
154196
|
+
if (this.childSessions.get(agentId) === childSession) {
|
|
154197
|
+
try {
|
|
154198
|
+
await childSession.dispose();
|
|
154199
|
+
} catch (disposeErr) {
|
|
154200
|
+
executorLog.warn(`Child agent ${agentId} session dispose failed: ${disposeErr instanceof Error ? disposeErr.message : String(disposeErr)}`);
|
|
154201
|
+
}
|
|
154202
|
+
this.childSessions.delete(agentId);
|
|
154203
|
+
}
|
|
153882
154204
|
this.totalSpawnedCount = Math.max(0, this.totalSpawnedCount - 1);
|
|
153883
154205
|
}
|
|
153884
154206
|
}
|
|
@@ -158567,6 +158889,7 @@ var init_agent_heartbeat = __esm({
|
|
|
158567
158889
|
init_heartbeat_prompt_trim();
|
|
158568
158890
|
init_room_ambiguity();
|
|
158569
158891
|
init_room_coordination();
|
|
158892
|
+
init_task_agent_sync();
|
|
158570
158893
|
promptSizeLog = createLogger2("prompt-size");
|
|
158571
158894
|
REPORTS_STALE_INTERVAL_MULTIPLIER = 1.5;
|
|
158572
158895
|
MIN_HEARTBEAT_STALENESS_MS = 10 * 6e4;
|
|
@@ -159273,6 +159596,37 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
159273
159596
|
void this.checkMissedHeartbeats();
|
|
159274
159597
|
}, this.pollIntervalMs);
|
|
159275
159598
|
}
|
|
159599
|
+
async emitStaleAgentAssignmentAudit(options) {
|
|
159600
|
+
if (!this.taskStore) return;
|
|
159601
|
+
try {
|
|
159602
|
+
await createRunAuditor(this.taskStore, {
|
|
159603
|
+
runId: generateSyntheticRunId("heartbeat-stale-agent-assignment", options.taskId),
|
|
159604
|
+
agentId: "heartbeat-monitor",
|
|
159605
|
+
taskId: options.taskId,
|
|
159606
|
+
taskLineageId: options.linkedTask?.lineageId,
|
|
159607
|
+
phase: "reconcile-stale-agent-assignment"
|
|
159608
|
+
}).database({
|
|
159609
|
+
type: "task:reconcile-stale-agent-assignment",
|
|
159610
|
+
target: options.agent.id,
|
|
159611
|
+
metadata: {
|
|
159612
|
+
agentId: options.agent.id,
|
|
159613
|
+
taskId: options.taskId,
|
|
159614
|
+
taskColumn: options.linkedTask?.column ?? null,
|
|
159615
|
+
agentState: options.agent.state,
|
|
159616
|
+
status: options.linkedTask?.status ?? null,
|
|
159617
|
+
blockedBy: options.linkedTask?.blockedBy ?? null,
|
|
159618
|
+
overlapBlockedBy: options.linkedTask?.overlapBlockedBy ?? null,
|
|
159619
|
+
hadFreshRun: options.hadFreshRun,
|
|
159620
|
+
hadActiveExecution: options.hadActiveExecution,
|
|
159621
|
+
reason: options.reason
|
|
159622
|
+
}
|
|
159623
|
+
});
|
|
159624
|
+
} catch (error) {
|
|
159625
|
+
heartbeatLog.warn(
|
|
159626
|
+
`Failed to emit stale agent assignment audit for ${options.agent.id}/${options.taskId}: ${error instanceof Error ? error.message : String(error)}`
|
|
159627
|
+
);
|
|
159628
|
+
}
|
|
159629
|
+
}
|
|
159276
159630
|
/**
|
|
159277
159631
|
* Find agents in `state="running"` that are not actually running and flip
|
|
159278
159632
|
* them to `"active"`. An agent is considered orphaned when either:
|
|
@@ -159290,8 +159644,8 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
159290
159644
|
* logged but do not block the caller.
|
|
159291
159645
|
*
|
|
159292
159646
|
* Complements SelfHealingManager.recoverAgentsRunningOnInactiveTasks():
|
|
159293
|
-
* heartbeat reconciliation handles stale/no-run conditions
|
|
159294
|
-
*
|
|
159647
|
+
* heartbeat reconciliation handles stale/no-run conditions and the prompt-critical
|
|
159648
|
+
* parked todo/triage assignment drift before Reports Health Check renders.
|
|
159295
159649
|
*/
|
|
159296
159650
|
async reconcileOrphanedRunningAgents() {
|
|
159297
159651
|
try {
|
|
@@ -159299,10 +159653,29 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
159299
159653
|
const now = Date.now();
|
|
159300
159654
|
for (const agent of runningAgents) {
|
|
159301
159655
|
let reason = null;
|
|
159656
|
+
let clearTaskLink = false;
|
|
159657
|
+
let taskIdToClear = null;
|
|
159658
|
+
let parkedProof = null;
|
|
159659
|
+
let linkedTask = null;
|
|
159302
159660
|
const activeRun = await this.store.getActiveHeartbeatRun(agent.id);
|
|
159303
|
-
if (!
|
|
159661
|
+
if (!isEphemeralAgent(agent) && agent.taskId && this.taskStore) {
|
|
159662
|
+
linkedTask = await this.taskStore.getTask(agent.taskId);
|
|
159663
|
+
parkedProof = evaluateParkedAgentTaskLink({
|
|
159664
|
+
agent,
|
|
159665
|
+
linkedTask,
|
|
159666
|
+
activeRun,
|
|
159667
|
+
hasActiveAgentExecution: (agentId) => this.trackedAgents.has(agentId),
|
|
159668
|
+
now
|
|
159669
|
+
});
|
|
159670
|
+
if (isParkedTaskColumn(linkedTask) && !parkedProof.shouldPreserveParkedLink) {
|
|
159671
|
+
reason = `parked ${linkedTask.column} task ${agent.taskId} without live execution proof`;
|
|
159672
|
+
clearTaskLink = true;
|
|
159673
|
+
taskIdToClear = agent.taskId;
|
|
159674
|
+
}
|
|
159675
|
+
}
|
|
159676
|
+
if (!reason && !activeRun) {
|
|
159304
159677
|
reason = "no active run";
|
|
159305
|
-
} else if (!this.trackedAgents.has(agent.id)) {
|
|
159678
|
+
} else if (!reason && activeRun && !this.trackedAgents.has(agent.id)) {
|
|
159306
159679
|
const timeoutMs = this.resolveAgentConfig(agent.id).heartbeatTimeoutMs;
|
|
159307
159680
|
const heartbeatAgeMs = getHeartbeatAgeMs(agent, now);
|
|
159308
159681
|
if (!Number.isFinite(heartbeatAgeMs) || heartbeatAgeMs > timeoutMs * 3) {
|
|
@@ -159321,9 +159694,21 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
159321
159694
|
}
|
|
159322
159695
|
if (!reason) continue;
|
|
159323
159696
|
try {
|
|
159697
|
+
const staleAgentState = agent.state;
|
|
159324
159698
|
await this.store.updateAgentState(agent.id, "active");
|
|
159699
|
+
if (clearTaskLink) {
|
|
159700
|
+
await this.store.syncExecutionTaskLink(agent.id, void 0);
|
|
159701
|
+
await this.emitStaleAgentAssignmentAudit({
|
|
159702
|
+
agent: { id: agent.id, state: staleAgentState },
|
|
159703
|
+
taskId: taskIdToClear,
|
|
159704
|
+
linkedTask,
|
|
159705
|
+
hadFreshRun: parkedProof?.hasFreshRun ?? false,
|
|
159706
|
+
hadActiveExecution: parkedProof?.hasActiveExecution ?? false,
|
|
159707
|
+
reason
|
|
159708
|
+
});
|
|
159709
|
+
}
|
|
159325
159710
|
this.clearRunState(agent.id);
|
|
159326
|
-
heartbeatLog.log(`Reconciled orphaned running agent ${agent.id} \u2192 active (${reason})`);
|
|
159711
|
+
heartbeatLog.log(`Reconciled orphaned running agent ${agent.id} \u2192 active (${reason})${clearTaskLink ? "; stale task link cleared" : ""}`);
|
|
159327
159712
|
} catch (err) {
|
|
159328
159713
|
heartbeatLog.warn(`Failed to reconcile orphaned running agent ${agent.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
159329
159714
|
}
|
|
@@ -160876,8 +161261,35 @@ ${trimPromptMd(taskDetail.prompt, promptTemplate)}` : "No PROMPT.md available.",
|
|
|
160876
161261
|
);
|
|
160877
161262
|
const lastHeartbeatTs = report.lastHeartbeatAt ? Date.parse(report.lastHeartbeatAt) : NaN;
|
|
160878
161263
|
const heartbeatAgeMs = Number.isFinite(lastHeartbeatTs) ? Math.max(0, now - lastHeartbeatTs) : Infinity;
|
|
161264
|
+
let renderedState = report.state;
|
|
161265
|
+
let renderedTask = report.taskId ?? "\u2014";
|
|
161266
|
+
let staleParkedAssignment = false;
|
|
161267
|
+
if (report.state === "running" && !isEphemeralAgent(report) && report.taskId && this.taskStore) {
|
|
161268
|
+
try {
|
|
161269
|
+
const linkedTask = await this.taskStore.getTask(report.taskId);
|
|
161270
|
+
if (isParkedTaskColumn(linkedTask)) {
|
|
161271
|
+
const activeRun = await agentStore.getActiveHeartbeatRun(report.id);
|
|
161272
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
161273
|
+
agent: report,
|
|
161274
|
+
linkedTask,
|
|
161275
|
+
activeRun,
|
|
161276
|
+
hasActiveAgentExecution: (candidateId) => this.trackedAgents.has(candidateId),
|
|
161277
|
+
now
|
|
161278
|
+
});
|
|
161279
|
+
if (!proof.shouldPreserveParkedLink) {
|
|
161280
|
+
staleParkedAssignment = true;
|
|
161281
|
+
renderedState = "active";
|
|
161282
|
+
renderedTask = `${report.taskId} (queued/no live run)`;
|
|
161283
|
+
}
|
|
161284
|
+
}
|
|
161285
|
+
} catch (error) {
|
|
161286
|
+
heartbeatLog.warn(`[reports-health] failed to validate task link for ${report.id}/${report.taskId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
161287
|
+
}
|
|
161288
|
+
}
|
|
160879
161289
|
let health = "healthy";
|
|
160880
|
-
if (
|
|
161290
|
+
if (staleParkedAssignment) {
|
|
161291
|
+
health = "**stale** assignment";
|
|
161292
|
+
} else if (report.state === "paused") {
|
|
160881
161293
|
health = report.pauseReason ? `paused (${report.pauseReason})` : "paused";
|
|
160882
161294
|
} else if (report.state === "error") {
|
|
160883
161295
|
health = "**stuck**";
|
|
@@ -160887,8 +161299,8 @@ ${trimPromptMd(taskDetail.prompt, promptTemplate)}` : "No PROMPT.md available.",
|
|
|
160887
161299
|
health = "**stale**";
|
|
160888
161300
|
heartbeatLog.log(`[reports-health] stale report ${report.id} intervalSource=${intervalSource} staleThresholdMs=${staleThresholdMs} heartbeatAgeMs=${heartbeatAgeMs}`);
|
|
160889
161301
|
}
|
|
160890
|
-
const task =
|
|
160891
|
-
const state =
|
|
161302
|
+
const task = renderedTask;
|
|
161303
|
+
const state = renderedState;
|
|
160892
161304
|
const heartbeat = formatRelativeTime(report.lastHeartbeatAt);
|
|
160893
161305
|
return `| ${report.name} | ${state} | ${task} | ${heartbeat} | ${health} |`;
|
|
160894
161306
|
}));
|
|
@@ -168969,42 +169381,6 @@ var init_node_dispatch_validation = __esm({
|
|
|
168969
169381
|
}
|
|
168970
169382
|
});
|
|
168971
169383
|
|
|
168972
|
-
// ../engine/src/task-agent-sync.ts
|
|
168973
|
-
function attachAgentLinkSync(opts) {
|
|
168974
|
-
const logger3 = opts.logger ?? console;
|
|
168975
|
-
const handler = async ({ task, from, to: to2 }) => {
|
|
168976
|
-
if (!CLEAR_COLUMNS.has(to2)) {
|
|
168977
|
-
return;
|
|
168978
|
-
}
|
|
168979
|
-
try {
|
|
168980
|
-
const agents = await opts.agentStore.listAgents({ includeEphemeral: false });
|
|
168981
|
-
const linkedAgents = agents.filter((agent) => agent.taskId === task.id);
|
|
168982
|
-
for (const agent of linkedAgents) {
|
|
168983
|
-
if ((to2 === "todo" || to2 === "triage") && opts.hasActiveAgentExecution?.(agent.id) === true) {
|
|
168984
|
-
continue;
|
|
168985
|
-
}
|
|
168986
|
-
await opts.agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
168987
|
-
logger3.log(`taskAgentLinkSync: cleared agent ${agent.id} taskId from ${task.id} after move ${from} \u2192 ${to2}`);
|
|
168988
|
-
}
|
|
168989
|
-
} catch (error) {
|
|
168990
|
-
logger3.warn(
|
|
168991
|
-
`taskAgentLinkSync: failed to sync agents for task ${task.id} after move ${from} \u2192 ${to2}: ${error instanceof Error ? error.message : String(error)}`
|
|
168992
|
-
);
|
|
168993
|
-
}
|
|
168994
|
-
};
|
|
168995
|
-
opts.store.on("task:moved", handler);
|
|
168996
|
-
return () => {
|
|
168997
|
-
opts.store.off("task:moved", handler);
|
|
168998
|
-
};
|
|
168999
|
-
}
|
|
169000
|
-
var CLEAR_COLUMNS;
|
|
169001
|
-
var init_task_agent_sync = __esm({
|
|
169002
|
-
"../engine/src/task-agent-sync.ts"() {
|
|
169003
|
-
"use strict";
|
|
169004
|
-
CLEAR_COLUMNS = /* @__PURE__ */ new Set(["done", "archived", "todo", "triage"]);
|
|
169005
|
-
}
|
|
169006
|
-
});
|
|
169007
|
-
|
|
169008
169384
|
// ../engine/src/runtimes/in-process-runtime.ts
|
|
169009
169385
|
import { EventEmitter as EventEmitter22 } from "node:events";
|
|
169010
169386
|
import { setImmediate as setImmediateCb4 } from "node:timers";
|
|
@@ -169259,6 +169635,7 @@ var init_in_process_runtime = __esm({
|
|
|
169259
169635
|
maxWorktrees: this.config.maxWorktrees,
|
|
169260
169636
|
semaphore: this.globalSemaphore,
|
|
169261
169637
|
agentStore: this.agentStore,
|
|
169638
|
+
hasActiveAgentExecution: (agentId) => this.heartbeatMonitor?.getTrackedAgents().includes(agentId) ?? false,
|
|
169262
169639
|
missionStore,
|
|
169263
169640
|
missionAutopilot,
|
|
169264
169641
|
missionExecutionLoop,
|
|
@@ -185806,7 +186183,7 @@ var init_sse = __esm({
|
|
|
185806
186183
|
highWaterMark = 0;
|
|
185807
186184
|
nextConnectionId = 1;
|
|
185808
186185
|
SSE_CLIENT_ID_MAX_LENGTH = 128;
|
|
185809
|
-
SSE_CLIENT_STALE_MS =
|
|
186186
|
+
SSE_CLIENT_STALE_MS = 75e3;
|
|
185810
186187
|
SSE_MAX_BUFFERED_BYTES = 4 * 1024 * 1024;
|
|
185811
186188
|
managedConnections = /* @__PURE__ */ new Map();
|
|
185812
186189
|
approvalSseListeners = /* @__PURE__ */ new Set();
|
|
@@ -255737,13 +256114,27 @@ function sendCsv(res, filename, table) {
|
|
|
255737
256114
|
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
|
|
255738
256115
|
res.send(serializeCsv(table));
|
|
255739
256116
|
}
|
|
255740
|
-
|
|
256117
|
+
async function fetchLatestLiteLLMPricing() {
|
|
256118
|
+
const response = await fetch(LITELLM_PRICING_SOURCE_URL, {
|
|
256119
|
+
signal: AbortSignal.timeout(PRICING_FETCH_TIMEOUT_MS)
|
|
256120
|
+
});
|
|
256121
|
+
if (!response.ok) {
|
|
256122
|
+
const body = await response.text().catch(() => "");
|
|
256123
|
+
throw new ApiError(
|
|
256124
|
+
502,
|
|
256125
|
+
`Failed to fetch pricing source: ${response.status} ${response.statusText}${body ? `: ${body.slice(0, 200)}` : ""}`
|
|
256126
|
+
);
|
|
256127
|
+
}
|
|
256128
|
+
return response.json();
|
|
256129
|
+
}
|
|
256130
|
+
var DEFAULT_WINDOW_DAYS, VALID_GROUP_BY, VALID_TOKEN_GRANULARITY, PRICING_FETCH_TIMEOUT_MS, registerCommandCenterRoutes;
|
|
255741
256131
|
var init_register_command_center_routes = __esm({
|
|
255742
256132
|
"../dashboard/src/routes/register-command-center-routes.ts"() {
|
|
255743
256133
|
"use strict";
|
|
255744
256134
|
init_src();
|
|
255745
256135
|
init_api_error();
|
|
255746
256136
|
init_command_center_csv();
|
|
256137
|
+
init_project_store_resolver();
|
|
255747
256138
|
DEFAULT_WINDOW_DAYS = 7;
|
|
255748
256139
|
VALID_GROUP_BY = /* @__PURE__ */ new Set([
|
|
255749
256140
|
"model",
|
|
@@ -255756,6 +256147,7 @@ var init_register_command_center_routes = __esm({
|
|
|
255756
256147
|
"day",
|
|
255757
256148
|
"week"
|
|
255758
256149
|
]);
|
|
256150
|
+
PRICING_FETCH_TIMEOUT_MS = 1e4;
|
|
255759
256151
|
registerCommandCenterRoutes = (ctx) => {
|
|
255760
256152
|
const { router, getScopedStore: getScopedStore2, rethrowAsApiError: rethrowAsApiError9 } = ctx;
|
|
255761
256153
|
router.get("/command-center/tokens", async (req, res) => {
|
|
@@ -255764,12 +256156,14 @@ var init_register_command_center_routes = __esm({
|
|
|
255764
256156
|
const range2 = resolveRange(req.query);
|
|
255765
256157
|
const groupBy2 = resolveGroupBy(req.query);
|
|
255766
256158
|
const granularity = resolveTokenGranularity(req.query);
|
|
256159
|
+
const settings = await store.getGlobalSettingsStore().getSettings();
|
|
255767
256160
|
const result = aggregateTokenAnalytics(store.getDatabase(), {
|
|
255768
256161
|
from: range2.from,
|
|
255769
256162
|
to: range2.to,
|
|
255770
256163
|
groupBy: groupBy2,
|
|
255771
256164
|
granularity,
|
|
255772
|
-
now: Date.now()
|
|
256165
|
+
now: Date.now(),
|
|
256166
|
+
pricingOverrides: settings.modelPricingOverrides
|
|
255773
256167
|
});
|
|
255774
256168
|
if (wantsCsv(req.query)) {
|
|
255775
256169
|
sendCsv(res, "command-center-tokens.csv", tokenAnalyticsToTable(result));
|
|
@@ -255781,6 +256175,31 @@ var init_register_command_center_routes = __esm({
|
|
|
255781
256175
|
rethrowAsApiError9(err, "Failed to aggregate token analytics");
|
|
255782
256176
|
}
|
|
255783
256177
|
});
|
|
256178
|
+
router.post("/command-center/pricing/fetch", async (req, res) => {
|
|
256179
|
+
try {
|
|
256180
|
+
const store = await getScopedStore2(req);
|
|
256181
|
+
const json = await fetchLatestLiteLLMPricing();
|
|
256182
|
+
const parsed = parseLiteLLMPricing(json);
|
|
256183
|
+
if (parsed.count === 0) {
|
|
256184
|
+
throw new ApiError(502, "No chat-mode pricing entries found in fetched LiteLLM data");
|
|
256185
|
+
}
|
|
256186
|
+
const settings = await store.getGlobalSettingsStore().getSettings();
|
|
256187
|
+
const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
256188
|
+
await store.updateGlobalSettings({
|
|
256189
|
+
modelPricingOverrides: {
|
|
256190
|
+
...settings.modelPricingOverrides ?? {},
|
|
256191
|
+
...parsed.overrides
|
|
256192
|
+
},
|
|
256193
|
+
modelPricingFetchedAt: fetchedAt,
|
|
256194
|
+
modelPricingSource: LITELLM_PRICING_SOURCE_URL
|
|
256195
|
+
});
|
|
256196
|
+
invalidateAllGlobalSettingsCaches();
|
|
256197
|
+
res.json({ count: parsed.count, fetchedAt, source: LITELLM_PRICING_SOURCE_URL });
|
|
256198
|
+
} catch (err) {
|
|
256199
|
+
if (err instanceof ApiError) throw err;
|
|
256200
|
+
rethrowAsApiError9(err, "Failed to fetch model pricing");
|
|
256201
|
+
}
|
|
256202
|
+
});
|
|
255784
256203
|
router.get("/command-center/tools", async (req, res) => {
|
|
255785
256204
|
try {
|
|
255786
256205
|
const store = await getScopedStore2(req);
|
|
@@ -255855,10 +256274,12 @@ var init_register_command_center_routes = __esm({
|
|
|
255855
256274
|
try {
|
|
255856
256275
|
const store = await getScopedStore2(req);
|
|
255857
256276
|
const range2 = resolveRange(req.query);
|
|
256277
|
+
const settings = await store.getGlobalSettingsStore().getSettings();
|
|
255858
256278
|
const result = aggregateTeamAnalytics(store.getDatabase(), {
|
|
255859
256279
|
from: range2.from,
|
|
255860
256280
|
to: range2.to,
|
|
255861
|
-
now: Date.now()
|
|
256281
|
+
now: Date.now(),
|
|
256282
|
+
pricingOverrides: settings.modelPricingOverrides
|
|
255862
256283
|
});
|
|
255863
256284
|
res.json(result);
|
|
255864
256285
|
} catch (err) {
|
|
@@ -279068,6 +279489,13 @@ function tuiDebug(tag, data) {
|
|
|
279068
279489
|
} catch {
|
|
279069
279490
|
}
|
|
279070
279491
|
}
|
|
279492
|
+
function drainTuiPerformanceTimeline() {
|
|
279493
|
+
const perf = globalThis.performance;
|
|
279494
|
+
if (!perf) return;
|
|
279495
|
+
perf.clearMeasures?.();
|
|
279496
|
+
perf.clearMarks?.();
|
|
279497
|
+
perf.clearResourceTimings?.();
|
|
279498
|
+
}
|
|
279071
279499
|
function openInBrowser(url) {
|
|
279072
279500
|
let cmd;
|
|
279073
279501
|
let args;
|
|
@@ -282121,6 +282549,9 @@ function DashboardApp({ controller }) {
|
|
|
282121
282549
|
const { t } = useTranslation("cli");
|
|
282122
282550
|
const { exit } = useApp();
|
|
282123
282551
|
const { stdout } = useStdout();
|
|
282552
|
+
useEffect2(() => {
|
|
282553
|
+
drainTuiPerformanceTimeline();
|
|
282554
|
+
});
|
|
282124
282555
|
const [resizeTick, setResizeTick] = useState2(0);
|
|
282125
282556
|
useEffect2(() => {
|
|
282126
282557
|
if (!stdout) return;
|
|
@@ -298957,7 +299388,8 @@ configurePiPackage();
|
|
|
298957
299388
|
setInterval(() => {
|
|
298958
299389
|
performance3.clearMeasures();
|
|
298959
299390
|
performance3.clearMarks();
|
|
298960
|
-
|
|
299391
|
+
performance3.clearResourceTimings();
|
|
299392
|
+
}, 1e3).unref();
|
|
298961
299393
|
function loadEnvFile(path10) {
|
|
298962
299394
|
if (!existsSync81(path10)) return;
|
|
298963
299395
|
const contents = readFileSync39(path10, "utf-8");
|