@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/extension.js
CHANGED
|
@@ -433,6 +433,9 @@ var init_settings_schema = __esm({
|
|
|
433
433
|
defaultProvider: void 0,
|
|
434
434
|
defaultModelId: void 0,
|
|
435
435
|
testMode: void 0,
|
|
436
|
+
modelPricingOverrides: void 0,
|
|
437
|
+
modelPricingFetchedAt: void 0,
|
|
438
|
+
modelPricingSource: void 0,
|
|
436
439
|
modelRouterEnabled: void 0,
|
|
437
440
|
modelRouterCheapProvider: void 0,
|
|
438
441
|
modelRouterCheapModelId: void 0,
|
|
@@ -49376,6 +49379,9 @@ import { randomUUID as randomUUID15 } from "node:crypto";
|
|
|
49376
49379
|
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";
|
|
49377
49380
|
import { join as join20 } from "node:path";
|
|
49378
49381
|
import { existsSync as existsSync16, watch } from "node:fs";
|
|
49382
|
+
function isWorkflowColumnsCompatibilityFlagEnabled(settings) {
|
|
49383
|
+
return settings?.experimentalFeatures?.workflowColumns === true;
|
|
49384
|
+
}
|
|
49379
49385
|
function defineTaskColumn(column, serialize, sqlIdentifier = column) {
|
|
49380
49386
|
return { column, sqlIdentifier, serialize };
|
|
49381
49387
|
}
|
|
@@ -49690,7 +49696,6 @@ var init_store = __esm({
|
|
|
49690
49696
|
init_moved_settings();
|
|
49691
49697
|
init_workflow_ir();
|
|
49692
49698
|
init_workflow_steps_to_ir();
|
|
49693
|
-
init_workflow_columns_settings();
|
|
49694
49699
|
init_workflow_transitions();
|
|
49695
49700
|
init_plugin_gate_verdict();
|
|
49696
49701
|
init_trait_registry();
|
|
@@ -50488,7 +50493,7 @@ var init_store = __esm({
|
|
|
50488
50493
|
}
|
|
50489
50494
|
try {
|
|
50490
50495
|
const settings = await this.getSettingsFast();
|
|
50491
|
-
if (
|
|
50496
|
+
if (isWorkflowColumnsCompatibilityFlagEnabled(settings)) {
|
|
50492
50497
|
await this.runWorkflowColumnsIntegrityPass();
|
|
50493
50498
|
await this.recoverStaleTransitionPending();
|
|
50494
50499
|
} else {
|
|
@@ -52292,7 +52297,7 @@ ${TASK_UPSERT_SQL_ASSIGNMENTS}
|
|
|
52292
52297
|
await this.writeConfig(config);
|
|
52293
52298
|
const updatedMerged = { ...DEFAULT_SETTINGS, ...globalSettings, ...updatedProjectSettings };
|
|
52294
52299
|
this.emit("settings:updated", { settings: updatedMerged, previous: previousMerged });
|
|
52295
|
-
if (
|
|
52300
|
+
if (isWorkflowColumnsCompatibilityFlagEnabled(previousMerged) && !isWorkflowColumnsCompatibilityFlagEnabled(updatedMerged)) {
|
|
52296
52301
|
try {
|
|
52297
52302
|
await this.evacuateCustomColumnsToLegacy("flag-toggled-off");
|
|
52298
52303
|
} catch (err) {
|
|
@@ -52375,7 +52380,7 @@ ${TASK_UPSERT_SQL_ASSIGNMENTS}
|
|
|
52375
52380
|
merged.secretsSyncPassphraseConfigured = false;
|
|
52376
52381
|
}
|
|
52377
52382
|
this.emit("settings:updated", { settings: merged, previous });
|
|
52378
|
-
if (
|
|
52383
|
+
if (isWorkflowColumnsCompatibilityFlagEnabled(previous) && !isWorkflowColumnsCompatibilityFlagEnabled(merged)) {
|
|
52379
52384
|
try {
|
|
52380
52385
|
await this.evacuateCustomColumnsToLegacy("flag-toggled-off");
|
|
52381
52386
|
} catch (err) {
|
|
@@ -54619,7 +54624,8 @@ ${newTask.description}
|
|
|
54619
54624
|
return { kind: "engine" };
|
|
54620
54625
|
}
|
|
54621
54626
|
resolveWorkflowBypassGuards(moveSource, options) {
|
|
54622
|
-
|
|
54627
|
+
void moveSource;
|
|
54628
|
+
return options?.recoveryRehome === true || (options?.bypassGuards ?? (options?.moveSource === "engine" || options?.moveSource === "scheduler" || options?.skipMergeBlocker === true));
|
|
54623
54629
|
}
|
|
54624
54630
|
shouldSkipWorkflowMovePolicies(params) {
|
|
54625
54631
|
if (params.bypassGuards) return true;
|
|
@@ -54630,7 +54636,7 @@ ${newTask.description}
|
|
|
54630
54636
|
const task = await this.readTaskForMove(id);
|
|
54631
54637
|
const moveSource = options?.moveSource ?? "engine";
|
|
54632
54638
|
const mergedSettingsForMove = await this.getSettingsFast();
|
|
54633
|
-
if (!
|
|
54639
|
+
if (!isWorkflowColumnsCompatibilityFlagEnabled(mergedSettingsForMove)) return void 0;
|
|
54634
54640
|
if (task.column === toColumn) return void 0;
|
|
54635
54641
|
const workflowIr = this.resolveTaskWorkflowIrSync(id);
|
|
54636
54642
|
const workflowSignature = serializeWorkflowIr(workflowIr);
|
|
@@ -54714,7 +54720,7 @@ ${newTask.description}
|
|
|
54714
54720
|
const task = currentTask ?? await this.readTaskForMove(id);
|
|
54715
54721
|
const moveSource = options?.moveSource ?? "engine";
|
|
54716
54722
|
const mergedSettingsForMove = await this.getSettingsFast();
|
|
54717
|
-
const useWorkflow =
|
|
54723
|
+
const useWorkflow = isWorkflowColumnsCompatibilityFlagEnabled(mergedSettingsForMove);
|
|
54718
54724
|
const bypassGuards = this.resolveWorkflowBypassGuards(moveSource, options);
|
|
54719
54725
|
const workflowIr = useWorkflow ? this.resolveTaskWorkflowIrSync(id) : void 0;
|
|
54720
54726
|
if (task.column === toColumn) {
|
|
@@ -59341,12 +59347,32 @@ ${newTask.description}
|
|
|
59341
59347
|
/**
|
|
59342
59348
|
* FNXC:ArtifactRegistry 2026-06-19-22:04:
|
|
59343
59349
|
* 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.
|
|
59350
|
+
*
|
|
59351
|
+
* FNXC:ArtifactRegistry 2026-06-23-12:48:
|
|
59352
|
+
* 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.
|
|
59344
59353
|
*/
|
|
59345
59354
|
async listArtifacts(options) {
|
|
59346
59355
|
const limit = Math.min(Math.max(1, options?.limit ?? 200), 1e3);
|
|
59347
59356
|
const offset = Math.max(0, options?.offset ?? 0);
|
|
59348
59357
|
let sql = `
|
|
59349
|
-
SELECT
|
|
59358
|
+
SELECT
|
|
59359
|
+
a.id,
|
|
59360
|
+
a.type,
|
|
59361
|
+
a.title,
|
|
59362
|
+
a.description,
|
|
59363
|
+
a.mimeType,
|
|
59364
|
+
a.sizeBytes,
|
|
59365
|
+
a.uri,
|
|
59366
|
+
NULL as content,
|
|
59367
|
+
a.authorId,
|
|
59368
|
+
a.authorType,
|
|
59369
|
+
a.taskId,
|
|
59370
|
+
a.metadata,
|
|
59371
|
+
a.createdAt,
|
|
59372
|
+
a.updatedAt,
|
|
59373
|
+
t.title as taskTitle,
|
|
59374
|
+
t.description as taskDescription,
|
|
59375
|
+
t.column as taskColumn
|
|
59350
59376
|
FROM artifacts a
|
|
59351
59377
|
LEFT JOIN tasks t ON a.taskId = t.id
|
|
59352
59378
|
WHERE (a.taskId IS NULL OR t.${_TaskStore.ACTIVE_TASKS_WHERE})
|
|
@@ -61006,9 +61032,9 @@ ${stepsSection}`;
|
|
|
61006
61032
|
// always route through `moveTask` with `moveSource: "engine"` + `bypassGuards`
|
|
61007
61033
|
// (a recovery-class move, KTD-9) — never a raw column write — so capacity
|
|
61008
61034
|
// (KTD-10) and the single transition authority (KTD-3) are honored.
|
|
61009
|
-
/** True when the `workflowColumns` flag is ON (merged global + project). */
|
|
61035
|
+
/** True when the raw `workflowColumns` compatibility flag is ON (merged global + project). */
|
|
61010
61036
|
async workflowColumnsFlagOn() {
|
|
61011
|
-
return
|
|
61037
|
+
return isWorkflowColumnsCompatibilityFlagEnabled(await this.getSettingsFast());
|
|
61012
61038
|
}
|
|
61013
61039
|
/** The active (non-deleted) task ids currently selecting `workflowId`. A
|
|
61014
61040
|
* built-in/default workflow additionally owns every task with NO selection
|
|
@@ -62970,18 +62996,70 @@ var init_agent_token_usage = __esm({
|
|
|
62970
62996
|
function normalize4(s3) {
|
|
62971
62997
|
return (s3 ?? "").trim().toLowerCase();
|
|
62972
62998
|
}
|
|
62973
|
-
function
|
|
62999
|
+
function findBareModelPricing(model, entries) {
|
|
63000
|
+
for (const [key, entry] of Object.entries(entries)) {
|
|
63001
|
+
if (key.endsWith(`:${model}`)) return entry;
|
|
63002
|
+
}
|
|
63003
|
+
return void 0;
|
|
63004
|
+
}
|
|
63005
|
+
function lookupPricing(ref, overrides2) {
|
|
62974
63006
|
const provider = normalize4(ref.provider);
|
|
62975
63007
|
const model = normalize4(ref.model);
|
|
62976
63008
|
if (!model) return void 0;
|
|
63009
|
+
if (provider) {
|
|
63010
|
+
const exactOverride = overrides2?.[`${provider}:${model}`];
|
|
63011
|
+
if (exactOverride) return exactOverride;
|
|
63012
|
+
}
|
|
63013
|
+
const bareOverride = overrides2 ? findBareModelPricing(model, overrides2) : void 0;
|
|
63014
|
+
if (bareOverride) return bareOverride;
|
|
62977
63015
|
if (provider) {
|
|
62978
63016
|
const exact = MODEL_PRICING[`${provider}:${model}`];
|
|
62979
63017
|
if (exact) return exact;
|
|
62980
63018
|
}
|
|
62981
|
-
|
|
62982
|
-
|
|
62983
|
-
|
|
62984
|
-
return
|
|
63019
|
+
return findBareModelPricing(model, MODEL_PRICING);
|
|
63020
|
+
}
|
|
63021
|
+
function litellmProviderToFusionProvider(provider) {
|
|
63022
|
+
if (typeof provider !== "string") return null;
|
|
63023
|
+
const normalized = normalize4(provider);
|
|
63024
|
+
if (normalized === "openai") return "openai";
|
|
63025
|
+
if (normalized === "anthropic") return "anthropic";
|
|
63026
|
+
if (normalized === "gemini" || normalized.startsWith("gemini")) return "google";
|
|
63027
|
+
if (normalized === "vertex_ai" || normalized.startsWith("vertex_ai")) return "google";
|
|
63028
|
+
if (normalized === "vertex_ai-language-models") return "google";
|
|
63029
|
+
return null;
|
|
63030
|
+
}
|
|
63031
|
+
function numericField(entry, key) {
|
|
63032
|
+
const value = entry[key];
|
|
63033
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
63034
|
+
}
|
|
63035
|
+
function parseLiteLLMPricing(json) {
|
|
63036
|
+
const overrides2 = {};
|
|
63037
|
+
if (json === null || typeof json !== "object" || Array.isArray(json)) {
|
|
63038
|
+
return { overrides: overrides2, count: 0 };
|
|
63039
|
+
}
|
|
63040
|
+
for (const [modelId, value] of Object.entries(json)) {
|
|
63041
|
+
if (modelId === "sample_spec") continue;
|
|
63042
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) continue;
|
|
63043
|
+
const entry = value;
|
|
63044
|
+
if (entry.mode !== "chat") continue;
|
|
63045
|
+
const provider = litellmProviderToFusionProvider(entry.litellm_provider);
|
|
63046
|
+
if (!provider) continue;
|
|
63047
|
+
const inputCost = numericField(entry, "input_cost_per_token");
|
|
63048
|
+
const outputCost = numericField(entry, "output_cost_per_token");
|
|
63049
|
+
if (inputCost === void 0 || outputCost === void 0) continue;
|
|
63050
|
+
const inputPer1M = inputCost * 1e6;
|
|
63051
|
+
const outputPer1M = outputCost * 1e6;
|
|
63052
|
+
const cacheRead = numericField(entry, "cache_read_input_token_cost");
|
|
63053
|
+
const cacheWrite = numericField(entry, "cache_creation_input_token_cost");
|
|
63054
|
+
overrides2[`${provider}:${normalize4(modelId)}`] = {
|
|
63055
|
+
inputPer1M,
|
|
63056
|
+
outputPer1M,
|
|
63057
|
+
cacheReadPer1M: cacheRead === void 0 ? inputPer1M : cacheRead * 1e6,
|
|
63058
|
+
cacheWritePer1M: cacheWrite === void 0 ? inputPer1M : cacheWrite * 1e6,
|
|
63059
|
+
source: LITELLM_PRICING_SOURCE_LABEL
|
|
63060
|
+
};
|
|
63061
|
+
}
|
|
63062
|
+
return { overrides: overrides2, count: Object.keys(overrides2).length };
|
|
62985
63063
|
}
|
|
62986
63064
|
function isStale(now) {
|
|
62987
63065
|
if (now === void 0) return false;
|
|
@@ -62989,21 +63067,23 @@ function isStale(now) {
|
|
|
62989
63067
|
if (Number.isNaN(asOf)) return false;
|
|
62990
63068
|
return now - asOf > PRICING_STALE_AFTER_MS;
|
|
62991
63069
|
}
|
|
62992
|
-
function costFor(usage, model, now) {
|
|
63070
|
+
function costFor(usage, model, now, overrides2) {
|
|
62993
63071
|
const stale = isStale(now);
|
|
62994
|
-
const pricing = lookupPricing(model);
|
|
63072
|
+
const pricing = lookupPricing(model, overrides2);
|
|
62995
63073
|
if (!pricing) {
|
|
62996
63074
|
return { usd: null, unavailable: true, stale };
|
|
62997
63075
|
}
|
|
62998
63076
|
const usd = (usage.inputTokens * pricing.inputPer1M + usage.outputTokens * pricing.outputPer1M + usage.cachedTokens * pricing.cacheReadPer1M + usage.cacheWriteTokens * pricing.cacheWritePer1M) / 1e6;
|
|
62999
63077
|
return { usd, unavailable: false, stale };
|
|
63000
63078
|
}
|
|
63001
|
-
var pricingAsOf, PRICING_STALE_AFTER_MS, MODEL_PRICING;
|
|
63079
|
+
var pricingAsOf, PRICING_STALE_AFTER_MS, LITELLM_PRICING_SOURCE_URL, LITELLM_PRICING_SOURCE_LABEL, MODEL_PRICING;
|
|
63002
63080
|
var init_model_pricing = __esm({
|
|
63003
63081
|
"../core/src/model-pricing.ts"() {
|
|
63004
63082
|
"use strict";
|
|
63005
63083
|
pricingAsOf = "2026-06-21";
|
|
63006
63084
|
PRICING_STALE_AFTER_MS = 180 * 24 * 60 * 60 * 1e3;
|
|
63085
|
+
LITELLM_PRICING_SOURCE_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
63086
|
+
LITELLM_PRICING_SOURCE_LABEL = "litellm/model_prices_and_context_window.json";
|
|
63007
63087
|
MODEL_PRICING = {
|
|
63008
63088
|
// ── Anthropic Claude ────────────────────────────────────────────────
|
|
63009
63089
|
// input / output / cacheRead(0.1×) / cacheWrite(1.25×, 5-min TTL)
|
|
@@ -63250,7 +63330,7 @@ function groupKeyFor(row, groupBy) {
|
|
|
63250
63330
|
function emptyCostAccumulator() {
|
|
63251
63331
|
return { usd: 0, anyPriced: false, anyUnavailable: false, anyStale: false };
|
|
63252
63332
|
}
|
|
63253
|
-
function addRowCost(acc, row, now) {
|
|
63333
|
+
function addRowCost(acc, row, now, pricingOverrides) {
|
|
63254
63334
|
const result = costFor(
|
|
63255
63335
|
{
|
|
63256
63336
|
inputTokens: row.inputTokens ?? 0,
|
|
@@ -63262,7 +63342,8 @@ function addRowCost(acc, row, now) {
|
|
|
63262
63342
|
provider: row.tokenUsageModelProvider ?? row.modelProvider,
|
|
63263
63343
|
model: row.tokenUsageModelId ?? row.modelId
|
|
63264
63344
|
},
|
|
63265
|
-
now
|
|
63345
|
+
now,
|
|
63346
|
+
pricingOverrides
|
|
63266
63347
|
);
|
|
63267
63348
|
if (result.stale) acc.anyStale = true;
|
|
63268
63349
|
if (result.unavailable || result.usd === null) {
|
|
@@ -63371,9 +63452,10 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
63371
63452
|
const groupBy = query.groupBy;
|
|
63372
63453
|
const granularity = query.granularity;
|
|
63373
63454
|
const now = query.now;
|
|
63455
|
+
const pricingOverrides = query.pricingOverrides;
|
|
63374
63456
|
for (const row of rows) {
|
|
63375
63457
|
addRow(totals, row);
|
|
63376
|
-
addRowCost(totalCost, row, now);
|
|
63458
|
+
addRowCost(totalCost, row, now, pricingOverrides);
|
|
63377
63459
|
if (groupBy) {
|
|
63378
63460
|
const groupRows = groupBy === "model" || groupBy === "provider" ? parsePerModelRows(row) : [];
|
|
63379
63461
|
const rowsForGroup = groupRows.length > 0 ? groupRows : [row];
|
|
@@ -63386,7 +63468,7 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
63386
63468
|
groupCostMap.set(key, emptyCostAccumulator());
|
|
63387
63469
|
}
|
|
63388
63470
|
addRow(group, groupRow);
|
|
63389
|
-
addRowCost(groupCostMap.get(key), groupRow, now);
|
|
63471
|
+
addRowCost(groupCostMap.get(key), groupRow, now, pricingOverrides);
|
|
63390
63472
|
}
|
|
63391
63473
|
}
|
|
63392
63474
|
if (granularity) {
|
|
@@ -63398,7 +63480,7 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
63398
63480
|
seriesCostMap.set(bucket, emptyCostAccumulator());
|
|
63399
63481
|
}
|
|
63400
63482
|
addRow(point, row);
|
|
63401
|
-
addRowCost(seriesCostMap.get(bucket), row, now);
|
|
63483
|
+
addRowCost(seriesCostMap.get(bucket), row, now, pricingOverrides);
|
|
63402
63484
|
}
|
|
63403
63485
|
}
|
|
63404
63486
|
for (const [key, group] of groupMap) {
|
|
@@ -64049,7 +64131,7 @@ function addTokenRow(totals, row) {
|
|
|
64049
64131
|
totals.totalTokens += row.totalTokens ?? (row.inputTokens ?? 0) + (row.outputTokens ?? 0) + (row.cachedTokens ?? 0) + (row.cacheWriteTokens ?? 0);
|
|
64050
64132
|
totals.nTasks += 1;
|
|
64051
64133
|
}
|
|
64052
|
-
function addRowCost2(acc, row, now) {
|
|
64134
|
+
function addRowCost2(acc, row, now, pricingOverrides) {
|
|
64053
64135
|
const result = costFor(
|
|
64054
64136
|
{
|
|
64055
64137
|
inputTokens: row.inputTokens ?? 0,
|
|
@@ -64058,7 +64140,8 @@ function addRowCost2(acc, row, now) {
|
|
|
64058
64140
|
cacheWriteTokens: row.cacheWriteTokens ?? 0
|
|
64059
64141
|
},
|
|
64060
64142
|
{ provider: row.modelProvider, model: row.modelId },
|
|
64061
|
-
now
|
|
64143
|
+
now,
|
|
64144
|
+
pricingOverrides
|
|
64062
64145
|
);
|
|
64063
64146
|
if (result.stale) acc.anyStale = true;
|
|
64064
64147
|
if (result.unavailable || result.usd === null) {
|
|
@@ -64117,6 +64200,7 @@ function aggregateTeamAnalytics(db, query = {}) {
|
|
|
64117
64200
|
const costAccumulators = /* @__PURE__ */ new Map();
|
|
64118
64201
|
const totalTokens = emptyTokenTotals();
|
|
64119
64202
|
const totalCost = emptyCostAccumulator2();
|
|
64203
|
+
const pricingOverrides = query.pricingOverrides;
|
|
64120
64204
|
const agents = db.prepare(`SELECT id, name, role, state FROM agents ORDER BY id`).all();
|
|
64121
64205
|
for (const agent of agents) {
|
|
64122
64206
|
summaries.set(agent.id, makeSummary(agent.id, agent));
|
|
@@ -64152,8 +64236,8 @@ function aggregateTeamAnalytics(db, query = {}) {
|
|
|
64152
64236
|
costAccumulators.set(row.agentId, agentCost);
|
|
64153
64237
|
addTokenRow(summary.tokens, row);
|
|
64154
64238
|
addTokenRow(totalTokens, row);
|
|
64155
|
-
addRowCost2(agentCost, row, query.now);
|
|
64156
|
-
addRowCost2(totalCost, row, query.now);
|
|
64239
|
+
addRowCost2(agentCost, row, query.now, pricingOverrides);
|
|
64240
|
+
addRowCost2(totalCost, row, query.now, pricingOverrides);
|
|
64157
64241
|
}
|
|
64158
64242
|
const completedClauses = ["assignedAgentId IS NOT NULL", `"column" = 'done'`, "columnMovedAt IS NOT NULL"];
|
|
64159
64243
|
const completedParams = [];
|
|
@@ -86357,6 +86441,8 @@ __export(src_exports, {
|
|
|
86357
86441
|
InvalidMergeQueueLeaseDurationError: () => InvalidMergeQueueLeaseDurationError,
|
|
86358
86442
|
InvalidRehomeTargetError: () => InvalidRehomeTargetError,
|
|
86359
86443
|
LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES: () => LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES,
|
|
86444
|
+
LITELLM_PRICING_SOURCE_LABEL: () => LITELLM_PRICING_SOURCE_LABEL,
|
|
86445
|
+
LITELLM_PRICING_SOURCE_URL: () => LITELLM_PRICING_SOURCE_URL,
|
|
86360
86446
|
LinkedWorktreeBootstrapRefusedError: () => LinkedWorktreeBootstrapRefusedError,
|
|
86361
86447
|
MANUAL_RETRY_RESET_COUNTER_KEYS: () => MANUAL_RETRY_RESET_COUNTER_KEYS,
|
|
86362
86448
|
MASTER_KEY_FILENAME: () => MASTER_KEY_FILENAME,
|
|
@@ -86897,6 +86983,7 @@ __export(src_exports, {
|
|
|
86897
86983
|
parseInsightExtractionResponse: () => parseInsightExtractionResponse,
|
|
86898
86984
|
parseInstanceNodeId: () => parseInstanceNodeId,
|
|
86899
86985
|
parseJsonSteps: () => parseJsonSteps,
|
|
86986
|
+
parseLiteLLMPricing: () => parseLiteLLMPricing,
|
|
86900
86987
|
parseNoOpCompletionMarker: () => parseNoOpCompletionMarker,
|
|
86901
86988
|
parseProjectManifest: () => parseProjectManifest,
|
|
86902
86989
|
parseRepoFromRemote: () => parseRepoFromRemote,
|
|
@@ -87398,6 +87485,74 @@ var init_logger2 = __esm({
|
|
|
87398
87485
|
});
|
|
87399
87486
|
|
|
87400
87487
|
// ../engine/src/agent-logger.ts
|
|
87488
|
+
function truncateToolResultDetail(value) {
|
|
87489
|
+
if (value.length <= TOOL_RESULT_DETAIL_LIMIT) return value;
|
|
87490
|
+
return `${value.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
|
|
87491
|
+
}
|
|
87492
|
+
function summarizeToolResultValue(value, state) {
|
|
87493
|
+
if (state.remainingChars <= 0) return "[truncated]";
|
|
87494
|
+
if (value === null || value === void 0) return value;
|
|
87495
|
+
if (typeof value === "string") {
|
|
87496
|
+
const truncated = value.length > state.remainingChars ? `${value.slice(0, state.remainingChars)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}` : value;
|
|
87497
|
+
state.remainingChars -= Math.min(value.length, state.remainingChars);
|
|
87498
|
+
return truncated;
|
|
87499
|
+
}
|
|
87500
|
+
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
87501
|
+
if (typeof value === "bigint") return value.toString();
|
|
87502
|
+
if (typeof value === "function" || typeof value === "symbol") return `[${typeof value}]`;
|
|
87503
|
+
if (value instanceof Error) {
|
|
87504
|
+
return {
|
|
87505
|
+
name: value.name,
|
|
87506
|
+
message: summarizeToolResultValue(value.message, state),
|
|
87507
|
+
...value.stack ? { stack: summarizeToolResultValue(value.stack, state) } : {}
|
|
87508
|
+
};
|
|
87509
|
+
}
|
|
87510
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
|
|
87511
|
+
return `<Buffer ${value.byteLength} bytes>`;
|
|
87512
|
+
}
|
|
87513
|
+
if (ArrayBuffer.isView(value)) {
|
|
87514
|
+
return `<${value.constructor.name} ${value.byteLength} bytes>`;
|
|
87515
|
+
}
|
|
87516
|
+
if (value instanceof ArrayBuffer) {
|
|
87517
|
+
return `<ArrayBuffer ${value.byteLength} bytes>`;
|
|
87518
|
+
}
|
|
87519
|
+
if (typeof value !== "object") return String(value);
|
|
87520
|
+
if (state.seen.has(value)) return "[Circular]";
|
|
87521
|
+
state.seen.add(value);
|
|
87522
|
+
if (Array.isArray(value)) {
|
|
87523
|
+
const summarized2 = value.slice(0, TOOL_RESULT_MAX_ARRAY_ITEMS).map((item) => summarizeToolResultValue(item, state));
|
|
87524
|
+
if (value.length > TOOL_RESULT_MAX_ARRAY_ITEMS) summarized2.push(`[${value.length - TOOL_RESULT_MAX_ARRAY_ITEMS} more items truncated]`);
|
|
87525
|
+
return summarized2;
|
|
87526
|
+
}
|
|
87527
|
+
const summarized = {};
|
|
87528
|
+
let count2 = 0;
|
|
87529
|
+
for (const key of Object.keys(value)) {
|
|
87530
|
+
if (count2 >= TOOL_RESULT_MAX_OBJECT_KEYS) {
|
|
87531
|
+
summarized.__truncatedKeys = true;
|
|
87532
|
+
break;
|
|
87533
|
+
}
|
|
87534
|
+
summarized[key] = summarizeToolResultValue(value[key], state);
|
|
87535
|
+
count2 += 1;
|
|
87536
|
+
if (state.remainingChars <= 0) {
|
|
87537
|
+
summarized.__truncated = true;
|
|
87538
|
+
break;
|
|
87539
|
+
}
|
|
87540
|
+
}
|
|
87541
|
+
return summarized;
|
|
87542
|
+
}
|
|
87543
|
+
function summarizeToolResultDetail(result) {
|
|
87544
|
+
if (result === void 0 || result === null) return void 0;
|
|
87545
|
+
if (typeof result === "string") return truncateToolResultDetail(result);
|
|
87546
|
+
try {
|
|
87547
|
+
const preview = summarizeToolResultValue(result, {
|
|
87548
|
+
remainingChars: TOOL_RESULT_DETAIL_LIMIT,
|
|
87549
|
+
seen: /* @__PURE__ */ new WeakSet()
|
|
87550
|
+
});
|
|
87551
|
+
return truncateToolResultDetail(JSON.stringify(preview));
|
|
87552
|
+
} catch {
|
|
87553
|
+
return truncateToolResultDetail(String(result));
|
|
87554
|
+
}
|
|
87555
|
+
}
|
|
87401
87556
|
function summarizeToolArgs(name, args) {
|
|
87402
87557
|
if (!args) return void 0;
|
|
87403
87558
|
const lowerName = name.toLowerCase();
|
|
@@ -87414,7 +87569,7 @@ function summarizeToolArgs(name, args) {
|
|
|
87414
87569
|
}
|
|
87415
87570
|
return void 0;
|
|
87416
87571
|
}
|
|
87417
|
-
var FLUSH_SIZE_BYTES, FLUSH_INTERVAL_MS, ENTRY_BATCH_SIZE, AgentLogger;
|
|
87572
|
+
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;
|
|
87418
87573
|
var init_agent_logger = __esm({
|
|
87419
87574
|
"../engine/src/agent-logger.ts"() {
|
|
87420
87575
|
"use strict";
|
|
@@ -87423,6 +87578,10 @@ var init_agent_logger = __esm({
|
|
|
87423
87578
|
FLUSH_SIZE_BYTES = 1024;
|
|
87424
87579
|
FLUSH_INTERVAL_MS = 500;
|
|
87425
87580
|
ENTRY_BATCH_SIZE = 50;
|
|
87581
|
+
TOOL_RESULT_DETAIL_LIMIT = 4096;
|
|
87582
|
+
TOOL_RESULT_DETAIL_TRUNCATION_NOTICE = "\n\n[tool output truncated to keep dashboard log views responsive]";
|
|
87583
|
+
TOOL_RESULT_MAX_ARRAY_ITEMS = 25;
|
|
87584
|
+
TOOL_RESULT_MAX_OBJECT_KEYS = 50;
|
|
87426
87585
|
AgentLogger = class {
|
|
87427
87586
|
textBuffer = "";
|
|
87428
87587
|
thinkingBuffer = "";
|
|
@@ -87568,10 +87727,7 @@ var init_agent_logger = __esm({
|
|
|
87568
87727
|
*/
|
|
87569
87728
|
onToolEnd(name, isError, result) {
|
|
87570
87729
|
const type = isError ? "tool_error" : "tool_result";
|
|
87571
|
-
|
|
87572
|
-
if (result !== void 0 && result !== null) {
|
|
87573
|
-
detail = typeof result === "string" ? result : JSON.stringify(result);
|
|
87574
|
-
}
|
|
87730
|
+
const detail = summarizeToolResultDetail(result);
|
|
87575
87731
|
this.writeEntry(name, type, detail, `Failed to log tool end "${name}" (${type}) for ${this.taskId}`);
|
|
87576
87732
|
const startedAt = this.toolStartedAt.get(name);
|
|
87577
87733
|
if (startedAt !== void 0) this.toolStartedAt.delete(name);
|
|
@@ -88618,7 +88774,6 @@ function countCapacitySlot(allTasks, effectiveWorkflowIdByTask, targetColumn, wo
|
|
|
88618
88774
|
async function runHoldReleaseSweep(store, deps) {
|
|
88619
88775
|
const result = { released: [], held: [] };
|
|
88620
88776
|
const settings = await store.getSettings();
|
|
88621
|
-
if (!isWorkflowColumnsEnabled(settings)) return result;
|
|
88622
88777
|
const allTasks = await store.listTasks({ includeArchived: false });
|
|
88623
88778
|
const irCache = /* @__PURE__ */ new Map();
|
|
88624
88779
|
const effectiveWorkflowIdByTask = /* @__PURE__ */ new Map();
|
|
@@ -107616,10 +107771,83 @@ var init_unlinked_missions_advisory_reporter = __esm({
|
|
|
107616
107771
|
}
|
|
107617
107772
|
});
|
|
107618
107773
|
|
|
107774
|
+
// ../engine/src/task-agent-sync.ts
|
|
107775
|
+
function hasFreshActiveHeartbeatRun(activeRun, now = Date.now(), freshRunMs = PARKED_AGENT_LINK_FRESH_RUN_MS) {
|
|
107776
|
+
const runStartedAt = activeRun?.startedAt;
|
|
107777
|
+
const runAgeMs = runStartedAt ? now - Date.parse(runStartedAt) : Number.POSITIVE_INFINITY;
|
|
107778
|
+
return {
|
|
107779
|
+
hasFreshRun: Boolean(activeRun) && Number.isFinite(runAgeMs) && runAgeMs <= freshRunMs,
|
|
107780
|
+
runAgeMs
|
|
107781
|
+
};
|
|
107782
|
+
}
|
|
107783
|
+
function isParkedTaskColumn(task) {
|
|
107784
|
+
return task?.column === "todo" || task?.column === "triage";
|
|
107785
|
+
}
|
|
107786
|
+
function evaluateParkedAgentTaskLink(options) {
|
|
107787
|
+
const { hasFreshRun, runAgeMs } = hasFreshActiveHeartbeatRun(options.activeRun, options.now);
|
|
107788
|
+
const hasActiveExecution = options.hasActiveAgentExecution?.(options.agent.id) === true;
|
|
107789
|
+
return {
|
|
107790
|
+
hasFreshRun,
|
|
107791
|
+
hasActiveExecution,
|
|
107792
|
+
shouldPreserveParkedLink: isParkedTaskColumn(options.linkedTask) && (hasFreshRun || hasActiveExecution),
|
|
107793
|
+
runAgeMs
|
|
107794
|
+
};
|
|
107795
|
+
}
|
|
107796
|
+
function attachAgentLinkSync(opts) {
|
|
107797
|
+
const logger3 = opts.logger ?? console;
|
|
107798
|
+
const handler = async ({ task, from, to: to2 }) => {
|
|
107799
|
+
if (!CLEAR_COLUMNS.has(to2)) {
|
|
107800
|
+
return;
|
|
107801
|
+
}
|
|
107802
|
+
try {
|
|
107803
|
+
const agents = await opts.agentStore.listAgents({ includeEphemeral: false });
|
|
107804
|
+
const linkedAgents = agents.filter((agent) => agent.taskId === task.id);
|
|
107805
|
+
for (const agent of linkedAgents) {
|
|
107806
|
+
if (to2 === "todo" || to2 === "triage") {
|
|
107807
|
+
const activeRun = await opts.agentStore.getActiveHeartbeatRun?.(agent.id);
|
|
107808
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
107809
|
+
agent,
|
|
107810
|
+
linkedTask: { column: to2 },
|
|
107811
|
+
activeRun,
|
|
107812
|
+
hasActiveAgentExecution: opts.hasActiveAgentExecution
|
|
107813
|
+
});
|
|
107814
|
+
if (proof.shouldPreserveParkedLink) {
|
|
107815
|
+
continue;
|
|
107816
|
+
}
|
|
107817
|
+
}
|
|
107818
|
+
if (agent.state === "running") {
|
|
107819
|
+
await opts.agentStore.updateAgentState(agent.id, "active");
|
|
107820
|
+
}
|
|
107821
|
+
await opts.agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
107822
|
+
logger3.log(`taskAgentLinkSync: cleared agent ${agent.id} taskId from ${task.id} after move ${from} \u2192 ${to2}`);
|
|
107823
|
+
}
|
|
107824
|
+
} catch (error) {
|
|
107825
|
+
logger3.warn(
|
|
107826
|
+
`taskAgentLinkSync: failed to sync agents for task ${task.id} after move ${from} \u2192 ${to2}: ${error instanceof Error ? error.message : String(error)}`
|
|
107827
|
+
);
|
|
107828
|
+
}
|
|
107829
|
+
};
|
|
107830
|
+
opts.store.on("task:moved", handler);
|
|
107831
|
+
return () => {
|
|
107832
|
+
opts.store.off("task:moved", handler);
|
|
107833
|
+
};
|
|
107834
|
+
}
|
|
107835
|
+
var PARKED_AGENT_LINK_FRESH_RUN_MS, CLEAR_COLUMNS;
|
|
107836
|
+
var init_task_agent_sync = __esm({
|
|
107837
|
+
"../engine/src/task-agent-sync.ts"() {
|
|
107838
|
+
"use strict";
|
|
107839
|
+
PARKED_AGENT_LINK_FRESH_RUN_MS = 5 * 6e4;
|
|
107840
|
+
CLEAR_COLUMNS = /* @__PURE__ */ new Set(["done", "archived", "todo", "triage"]);
|
|
107841
|
+
}
|
|
107842
|
+
});
|
|
107843
|
+
|
|
107619
107844
|
// ../engine/src/scheduler.ts
|
|
107620
107845
|
import { existsSync as existsSync37 } from "node:fs";
|
|
107621
107846
|
import { readFile as readFile20 } from "node:fs/promises";
|
|
107622
107847
|
import { join as join42 } from "node:path";
|
|
107848
|
+
function shouldRunWorkflowColumnScheduler(_settings) {
|
|
107849
|
+
return true;
|
|
107850
|
+
}
|
|
107623
107851
|
function pathsOverlap2(a, b2) {
|
|
107624
107852
|
for (const pa of a) {
|
|
107625
107853
|
const prefixA = pa.endsWith("/*") ? pa.slice(0, -1) : null;
|
|
@@ -107860,6 +108088,7 @@ var init_scheduler = __esm({
|
|
|
107860
108088
|
init_run_audit();
|
|
107861
108089
|
init_src();
|
|
107862
108090
|
init_hold_release();
|
|
108091
|
+
init_task_agent_sync();
|
|
107863
108092
|
COORDINATION_SAFE_SCOPE_EXACT = /* @__PURE__ */ new Set([
|
|
107864
108093
|
// Literal glob scope entries from PROMPT.md are kept here; concrete
|
|
107865
108094
|
// `.changeset/<name>.md` files are covered by COORDINATION_SAFE_SCOPE_PREFIXES.
|
|
@@ -108306,12 +108535,27 @@ var init_scheduler = __esm({
|
|
|
108306
108535
|
async rollbackRunningAgentsForQueuedTodoTask(taskId) {
|
|
108307
108536
|
const agentStore = this.options.agentStore;
|
|
108308
108537
|
if (!agentStore) return;
|
|
108309
|
-
const runningAgents = await agentStore.listAgents({ state: "running", includeEphemeral:
|
|
108538
|
+
const runningAgents = await agentStore.listAgents({ state: "running", includeEphemeral: false });
|
|
108310
108539
|
const linkedAgents = runningAgents.filter((agent) => agent.taskId === taskId);
|
|
108311
108540
|
for (const agent of linkedAgents) {
|
|
108541
|
+
const activeRun = await agentStore.getActiveHeartbeatRun?.(agent.id);
|
|
108542
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
108543
|
+
agent,
|
|
108544
|
+
linkedTask: { column: "todo" },
|
|
108545
|
+
activeRun,
|
|
108546
|
+
hasActiveAgentExecution: this.options.hasActiveAgentExecution
|
|
108547
|
+
});
|
|
108548
|
+
if (proof.shouldPreserveParkedLink) {
|
|
108549
|
+
schedulerLog.log(
|
|
108550
|
+
`Preserved running agent ${agent.id} for queued ${taskId}; live proof freshRun=${proof.hasFreshRun} activeExecution=${proof.hasActiveExecution}`
|
|
108551
|
+
);
|
|
108552
|
+
continue;
|
|
108553
|
+
}
|
|
108312
108554
|
await agentStore.updateAgentState(agent.id, "active");
|
|
108313
108555
|
await agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
108314
|
-
schedulerLog.log(
|
|
108556
|
+
schedulerLog.log(
|
|
108557
|
+
`Cleared stale running agent ${agent.id} after overlap requeue of ${taskId}; file-scope lease remains queued`
|
|
108558
|
+
);
|
|
108315
108559
|
}
|
|
108316
108560
|
}
|
|
108317
108561
|
/**
|
|
@@ -108431,7 +108675,7 @@ var init_scheduler = __esm({
|
|
|
108431
108675
|
schedulerLog.log("Engine pause cleared \u2014 scheduling resumed");
|
|
108432
108676
|
}
|
|
108433
108677
|
this.wasEnginePaused = false;
|
|
108434
|
-
if (
|
|
108678
|
+
if (shouldRunWorkflowColumnScheduler(settings)) {
|
|
108435
108679
|
await this.runHoldReleaseSweepPass(tasks, settings);
|
|
108436
108680
|
tasks = await this.store.listTasks({ slim: true, includeArchived: false, startupMemo: false });
|
|
108437
108681
|
settings = await this.store.getSettings();
|
|
@@ -109457,6 +109701,7 @@ var init_scheduler = __esm({
|
|
|
109457
109701
|
blockedBy: null,
|
|
109458
109702
|
overlapBlockedBy: overlappingTaskId
|
|
109459
109703
|
});
|
|
109704
|
+
await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
|
|
109460
109705
|
await this.logDispatchQueuedReason(
|
|
109461
109706
|
task.id,
|
|
109462
109707
|
`queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
|
|
@@ -124385,6 +124630,7 @@ var init_self_healing = __esm({
|
|
|
124385
124630
|
init_notifier();
|
|
124386
124631
|
init_dependency_blocked_todo_reporter();
|
|
124387
124632
|
init_scheduler();
|
|
124633
|
+
init_task_agent_sync();
|
|
124388
124634
|
init_transient_merge_error_classifier();
|
|
124389
124635
|
init_transient_merge_error_classifier();
|
|
124390
124636
|
log22 = createLogger2("self-healing");
|
|
@@ -124451,7 +124697,7 @@ var init_self_healing = __esm({
|
|
|
124451
124697
|
DURABLE_ERROR_RECOVERY_MAX_RETRIES = 5;
|
|
124452
124698
|
DURABLE_ERROR_RECOVERY_BASE_COOLDOWN_MS = 3e4;
|
|
124453
124699
|
DURABLE_ERROR_RECOVERY_MAX_COOLDOWN_MS = 15 * 6e4;
|
|
124454
|
-
RUNNING_ON_INACTIVE_TASK_STALE_RUN_MS =
|
|
124700
|
+
RUNNING_ON_INACTIVE_TASK_STALE_RUN_MS = PARKED_AGENT_LINK_FRESH_RUN_MS;
|
|
124455
124701
|
SelfHealingManager = class _SelfHealingManager {
|
|
124456
124702
|
constructor(store, options) {
|
|
124457
124703
|
this.store = store;
|
|
@@ -131279,6 +131525,36 @@ var init_self_healing = __esm({
|
|
|
131279
131525
|
const exponential = DURABLE_ERROR_RECOVERY_BASE_COOLDOWN_MS * Math.pow(2, clampedAttempts - 1);
|
|
131280
131526
|
return Math.min(exponential, DURABLE_ERROR_RECOVERY_MAX_COOLDOWN_MS);
|
|
131281
131527
|
}
|
|
131528
|
+
async emitStaleAgentAssignmentAudit(options) {
|
|
131529
|
+
try {
|
|
131530
|
+
await createRunAuditor(this.store, {
|
|
131531
|
+
runId: generateSyntheticRunId("self-healing-stale-agent-assignment", options.taskId),
|
|
131532
|
+
agentId: "self-healing",
|
|
131533
|
+
taskId: options.taskId,
|
|
131534
|
+
taskLineageId: options.linkedTask?.lineageId,
|
|
131535
|
+
phase: "reconcile-stale-agent-assignment"
|
|
131536
|
+
}).database({
|
|
131537
|
+
type: "task:reconcile-stale-agent-assignment",
|
|
131538
|
+
target: options.agent.id,
|
|
131539
|
+
metadata: {
|
|
131540
|
+
agentId: options.agent.id,
|
|
131541
|
+
taskId: options.taskId,
|
|
131542
|
+
taskColumn: options.linkedTask?.column ?? null,
|
|
131543
|
+
agentState: options.agent.state,
|
|
131544
|
+
status: options.linkedTask?.status ?? null,
|
|
131545
|
+
blockedBy: options.linkedTask?.blockedBy ?? null,
|
|
131546
|
+
overlapBlockedBy: options.linkedTask?.overlapBlockedBy ?? null,
|
|
131547
|
+
hadFreshRun: options.hadFreshRun,
|
|
131548
|
+
hadActiveExecution: options.hadActiveExecution,
|
|
131549
|
+
reason: options.reason
|
|
131550
|
+
}
|
|
131551
|
+
});
|
|
131552
|
+
} catch (error) {
|
|
131553
|
+
log22.warn(
|
|
131554
|
+
`Failed to emit stale agent assignment audit for ${options.agent.id}/${options.taskId}: ${error instanceof Error ? error.message : String(error)}`
|
|
131555
|
+
);
|
|
131556
|
+
}
|
|
131557
|
+
}
|
|
131282
131558
|
async recoverAgentsRunningOnInactiveTasks() {
|
|
131283
131559
|
const agentStore = this.options.agentStore;
|
|
131284
131560
|
if (!agentStore) {
|
|
@@ -131296,16 +131572,30 @@ var init_self_healing = __esm({
|
|
|
131296
131572
|
continue;
|
|
131297
131573
|
}
|
|
131298
131574
|
const activeRun = await agentStore.getActiveHeartbeatRun(agent.id);
|
|
131299
|
-
const
|
|
131300
|
-
|
|
131301
|
-
|
|
131302
|
-
|
|
131575
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
131576
|
+
agent,
|
|
131577
|
+
linkedTask: linkedTask ?? { column: "todo" },
|
|
131578
|
+
activeRun,
|
|
131579
|
+
hasActiveAgentExecution: this.options.hasActiveAgentExecution,
|
|
131580
|
+
now
|
|
131581
|
+
});
|
|
131582
|
+
if (proof.hasFreshRun || proof.hasActiveExecution) {
|
|
131303
131583
|
continue;
|
|
131304
131584
|
}
|
|
131585
|
+
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";
|
|
131586
|
+
const staleAgentState = agent.state;
|
|
131305
131587
|
await agentStore.updateAgentState(agent.id, "active");
|
|
131306
131588
|
await agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
131589
|
+
await this.emitStaleAgentAssignmentAudit({
|
|
131590
|
+
agent: { id: agent.id, state: staleAgentState },
|
|
131591
|
+
taskId: agent.taskId,
|
|
131592
|
+
linkedTask,
|
|
131593
|
+
hadFreshRun: proof.hasFreshRun,
|
|
131594
|
+
hadActiveExecution: proof.hasActiveExecution,
|
|
131595
|
+
reason
|
|
131596
|
+
});
|
|
131307
131597
|
recoveredAgentIds.add(agent.id);
|
|
131308
|
-
log22.log(`Recovered running durable agent ${agent.id} on inactive task ${agent.taskId}`);
|
|
131598
|
+
log22.log(`Recovered running durable agent ${agent.id} on inactive task ${agent.taskId}; file-scope lease preserved when present`);
|
|
131309
131599
|
}
|
|
131310
131600
|
return recoveredAgentIds.size;
|
|
131311
131601
|
}
|
|
@@ -131325,6 +131615,8 @@ var init_self_healing = __esm({
|
|
|
131325
131615
|
const linkedTask = await this.store.getTask(linkedTaskId);
|
|
131326
131616
|
let shouldClear = false;
|
|
131327
131617
|
let reason = "";
|
|
131618
|
+
let hadFreshRun = false;
|
|
131619
|
+
let hadActiveExecution = false;
|
|
131328
131620
|
if (!linkedTask) {
|
|
131329
131621
|
shouldClear = true;
|
|
131330
131622
|
reason = "linked task missing";
|
|
@@ -131336,21 +131628,38 @@ var init_self_healing = __esm({
|
|
|
131336
131628
|
reason = `linked task assigned to ${linkedTask.assignedAgentId}`;
|
|
131337
131629
|
} else if (linkedTask.column === "todo" || linkedTask.column === "triage") {
|
|
131338
131630
|
const activeRun = await agentStore.getActiveHeartbeatRun(agent.id);
|
|
131339
|
-
const
|
|
131340
|
-
|
|
131341
|
-
|
|
131342
|
-
|
|
131343
|
-
|
|
131631
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
131632
|
+
agent,
|
|
131633
|
+
linkedTask,
|
|
131634
|
+
activeRun,
|
|
131635
|
+
hasActiveAgentExecution: this.options.hasActiveAgentExecution,
|
|
131636
|
+
now
|
|
131637
|
+
});
|
|
131638
|
+
hadFreshRun = proof.hasFreshRun;
|
|
131639
|
+
hadActiveExecution = proof.hasActiveExecution;
|
|
131640
|
+
if (!proof.shouldPreserveParkedLink) {
|
|
131344
131641
|
shouldClear = true;
|
|
131345
|
-
reason = `linked task in queued column ${linkedTask.column} without fresh run`;
|
|
131642
|
+
reason = `linked task in queued column ${linkedTask.column} without fresh run or active execution`;
|
|
131346
131643
|
}
|
|
131347
131644
|
}
|
|
131348
131645
|
if (!shouldClear) {
|
|
131349
131646
|
continue;
|
|
131350
131647
|
}
|
|
131648
|
+
const staleAgentState = agent.state;
|
|
131649
|
+
if (agent.state === "running") {
|
|
131650
|
+
await agentStore.updateAgentState(agent.id, "active");
|
|
131651
|
+
}
|
|
131351
131652
|
await agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
131653
|
+
await this.emitStaleAgentAssignmentAudit({
|
|
131654
|
+
agent: { id: agent.id, state: staleAgentState },
|
|
131655
|
+
taskId: linkedTaskId,
|
|
131656
|
+
linkedTask,
|
|
131657
|
+
hadFreshRun,
|
|
131658
|
+
hadActiveExecution,
|
|
131659
|
+
reason
|
|
131660
|
+
});
|
|
131352
131661
|
clearedAgentIds.add(agent.id);
|
|
131353
|
-
log22.log(`Cleared drifted durable agent task link for ${agent.id} (${linkedTaskId}): ${reason}`);
|
|
131662
|
+
log22.log(`Cleared drifted durable agent task link for ${agent.id} (${linkedTaskId}): ${reason}; file-scope lease preserved when present`);
|
|
131354
131663
|
}
|
|
131355
131664
|
log22.log(`Recovered ${clearedAgentIds.size} drifted durable agent task link(s)`);
|
|
131356
131665
|
return clearedAgentIds.size;
|
|
@@ -134505,6 +134814,10 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
134505
134814
|
// Project-level validator fallback
|
|
134506
134815
|
projectValidatorFallbackProvider: currentSettings.validatorFallbackProvider,
|
|
134507
134816
|
projectValidatorFallbackModelId: currentSettings.validatorFallbackModelId,
|
|
134817
|
+
// FNXC:SpecReviewerFallback 2026-06-23-08:50:
|
|
134818
|
+
// 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.
|
|
134819
|
+
fallbackProvider: currentSettings.fallbackProvider,
|
|
134820
|
+
fallbackModelId: currentSettings.fallbackModelId,
|
|
134508
134821
|
// Global validator lane
|
|
134509
134822
|
globalValidatorProvider: currentSettings.validatorGlobalProvider,
|
|
134510
134823
|
globalValidatorModelId: currentSettings.validatorGlobalModelId,
|
|
@@ -134516,8 +134829,10 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
134516
134829
|
taskId,
|
|
134517
134830
|
task: currentDetail,
|
|
134518
134831
|
userComments: currentUserComments.length > 0 ? currentUserComments : void 0,
|
|
134832
|
+
agentPrompts: currentSettings.agentPrompts,
|
|
134519
134833
|
agentStore: this.options.agentStore,
|
|
134520
134834
|
rootDir,
|
|
134835
|
+
settings: currentSettings,
|
|
134521
134836
|
// Track the spec reviewer's session under this task so it's
|
|
134522
134837
|
// disposed alongside the main triage session on global pause.
|
|
134523
134838
|
onSessionCreated: (s3) => this.registerSubagentSession(taskId, s3),
|
|
@@ -152927,7 +153242,14 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
152927
153242
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
152928
153243
|
executorLog.warn(`Child agent ${agentId} failed: ${errorMessage}`);
|
|
152929
153244
|
} finally {
|
|
152930
|
-
this.childSessions.
|
|
153245
|
+
if (this.childSessions.get(agentId) === childSession) {
|
|
153246
|
+
try {
|
|
153247
|
+
await childSession.dispose();
|
|
153248
|
+
} catch (disposeErr) {
|
|
153249
|
+
executorLog.warn(`Child agent ${agentId} session dispose failed: ${disposeErr instanceof Error ? disposeErr.message : String(disposeErr)}`);
|
|
153250
|
+
}
|
|
153251
|
+
this.childSessions.delete(agentId);
|
|
153252
|
+
}
|
|
152931
153253
|
this.totalSpawnedCount = Math.max(0, this.totalSpawnedCount - 1);
|
|
152932
153254
|
}
|
|
152933
153255
|
}
|
|
@@ -157616,6 +157938,7 @@ var init_agent_heartbeat = __esm({
|
|
|
157616
157938
|
init_heartbeat_prompt_trim();
|
|
157617
157939
|
init_room_ambiguity();
|
|
157618
157940
|
init_room_coordination();
|
|
157941
|
+
init_task_agent_sync();
|
|
157619
157942
|
promptSizeLog = createLogger2("prompt-size");
|
|
157620
157943
|
REPORTS_STALE_INTERVAL_MULTIPLIER = 1.5;
|
|
157621
157944
|
MIN_HEARTBEAT_STALENESS_MS = 10 * 6e4;
|
|
@@ -158322,6 +158645,37 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
158322
158645
|
void this.checkMissedHeartbeats();
|
|
158323
158646
|
}, this.pollIntervalMs);
|
|
158324
158647
|
}
|
|
158648
|
+
async emitStaleAgentAssignmentAudit(options) {
|
|
158649
|
+
if (!this.taskStore) return;
|
|
158650
|
+
try {
|
|
158651
|
+
await createRunAuditor(this.taskStore, {
|
|
158652
|
+
runId: generateSyntheticRunId("heartbeat-stale-agent-assignment", options.taskId),
|
|
158653
|
+
agentId: "heartbeat-monitor",
|
|
158654
|
+
taskId: options.taskId,
|
|
158655
|
+
taskLineageId: options.linkedTask?.lineageId,
|
|
158656
|
+
phase: "reconcile-stale-agent-assignment"
|
|
158657
|
+
}).database({
|
|
158658
|
+
type: "task:reconcile-stale-agent-assignment",
|
|
158659
|
+
target: options.agent.id,
|
|
158660
|
+
metadata: {
|
|
158661
|
+
agentId: options.agent.id,
|
|
158662
|
+
taskId: options.taskId,
|
|
158663
|
+
taskColumn: options.linkedTask?.column ?? null,
|
|
158664
|
+
agentState: options.agent.state,
|
|
158665
|
+
status: options.linkedTask?.status ?? null,
|
|
158666
|
+
blockedBy: options.linkedTask?.blockedBy ?? null,
|
|
158667
|
+
overlapBlockedBy: options.linkedTask?.overlapBlockedBy ?? null,
|
|
158668
|
+
hadFreshRun: options.hadFreshRun,
|
|
158669
|
+
hadActiveExecution: options.hadActiveExecution,
|
|
158670
|
+
reason: options.reason
|
|
158671
|
+
}
|
|
158672
|
+
});
|
|
158673
|
+
} catch (error) {
|
|
158674
|
+
heartbeatLog.warn(
|
|
158675
|
+
`Failed to emit stale agent assignment audit for ${options.agent.id}/${options.taskId}: ${error instanceof Error ? error.message : String(error)}`
|
|
158676
|
+
);
|
|
158677
|
+
}
|
|
158678
|
+
}
|
|
158325
158679
|
/**
|
|
158326
158680
|
* Find agents in `state="running"` that are not actually running and flip
|
|
158327
158681
|
* them to `"active"`. An agent is considered orphaned when either:
|
|
@@ -158339,8 +158693,8 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
158339
158693
|
* logged but do not block the caller.
|
|
158340
158694
|
*
|
|
158341
158695
|
* Complements SelfHealingManager.recoverAgentsRunningOnInactiveTasks():
|
|
158342
|
-
* heartbeat reconciliation handles stale/no-run conditions
|
|
158343
|
-
*
|
|
158696
|
+
* heartbeat reconciliation handles stale/no-run conditions and the prompt-critical
|
|
158697
|
+
* parked todo/triage assignment drift before Reports Health Check renders.
|
|
158344
158698
|
*/
|
|
158345
158699
|
async reconcileOrphanedRunningAgents() {
|
|
158346
158700
|
try {
|
|
@@ -158348,10 +158702,29 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
158348
158702
|
const now = Date.now();
|
|
158349
158703
|
for (const agent of runningAgents) {
|
|
158350
158704
|
let reason = null;
|
|
158705
|
+
let clearTaskLink = false;
|
|
158706
|
+
let taskIdToClear = null;
|
|
158707
|
+
let parkedProof = null;
|
|
158708
|
+
let linkedTask = null;
|
|
158351
158709
|
const activeRun = await this.store.getActiveHeartbeatRun(agent.id);
|
|
158352
|
-
if (!
|
|
158710
|
+
if (!isEphemeralAgent(agent) && agent.taskId && this.taskStore) {
|
|
158711
|
+
linkedTask = await this.taskStore.getTask(agent.taskId);
|
|
158712
|
+
parkedProof = evaluateParkedAgentTaskLink({
|
|
158713
|
+
agent,
|
|
158714
|
+
linkedTask,
|
|
158715
|
+
activeRun,
|
|
158716
|
+
hasActiveAgentExecution: (agentId) => this.trackedAgents.has(agentId),
|
|
158717
|
+
now
|
|
158718
|
+
});
|
|
158719
|
+
if (isParkedTaskColumn(linkedTask) && !parkedProof.shouldPreserveParkedLink) {
|
|
158720
|
+
reason = `parked ${linkedTask.column} task ${agent.taskId} without live execution proof`;
|
|
158721
|
+
clearTaskLink = true;
|
|
158722
|
+
taskIdToClear = agent.taskId;
|
|
158723
|
+
}
|
|
158724
|
+
}
|
|
158725
|
+
if (!reason && !activeRun) {
|
|
158353
158726
|
reason = "no active run";
|
|
158354
|
-
} else if (!this.trackedAgents.has(agent.id)) {
|
|
158727
|
+
} else if (!reason && activeRun && !this.trackedAgents.has(agent.id)) {
|
|
158355
158728
|
const timeoutMs = this.resolveAgentConfig(agent.id).heartbeatTimeoutMs;
|
|
158356
158729
|
const heartbeatAgeMs = getHeartbeatAgeMs(agent, now);
|
|
158357
158730
|
if (!Number.isFinite(heartbeatAgeMs) || heartbeatAgeMs > timeoutMs * 3) {
|
|
@@ -158370,9 +158743,21 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
158370
158743
|
}
|
|
158371
158744
|
if (!reason) continue;
|
|
158372
158745
|
try {
|
|
158746
|
+
const staleAgentState = agent.state;
|
|
158373
158747
|
await this.store.updateAgentState(agent.id, "active");
|
|
158748
|
+
if (clearTaskLink) {
|
|
158749
|
+
await this.store.syncExecutionTaskLink(agent.id, void 0);
|
|
158750
|
+
await this.emitStaleAgentAssignmentAudit({
|
|
158751
|
+
agent: { id: agent.id, state: staleAgentState },
|
|
158752
|
+
taskId: taskIdToClear,
|
|
158753
|
+
linkedTask,
|
|
158754
|
+
hadFreshRun: parkedProof?.hasFreshRun ?? false,
|
|
158755
|
+
hadActiveExecution: parkedProof?.hasActiveExecution ?? false,
|
|
158756
|
+
reason
|
|
158757
|
+
});
|
|
158758
|
+
}
|
|
158374
158759
|
this.clearRunState(agent.id);
|
|
158375
|
-
heartbeatLog.log(`Reconciled orphaned running agent ${agent.id} \u2192 active (${reason})`);
|
|
158760
|
+
heartbeatLog.log(`Reconciled orphaned running agent ${agent.id} \u2192 active (${reason})${clearTaskLink ? "; stale task link cleared" : ""}`);
|
|
158376
158761
|
} catch (err) {
|
|
158377
158762
|
heartbeatLog.warn(`Failed to reconcile orphaned running agent ${agent.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
158378
158763
|
}
|
|
@@ -159925,8 +160310,35 @@ ${trimPromptMd(taskDetail.prompt, promptTemplate)}` : "No PROMPT.md available.",
|
|
|
159925
160310
|
);
|
|
159926
160311
|
const lastHeartbeatTs = report.lastHeartbeatAt ? Date.parse(report.lastHeartbeatAt) : NaN;
|
|
159927
160312
|
const heartbeatAgeMs = Number.isFinite(lastHeartbeatTs) ? Math.max(0, now - lastHeartbeatTs) : Infinity;
|
|
160313
|
+
let renderedState = report.state;
|
|
160314
|
+
let renderedTask = report.taskId ?? "\u2014";
|
|
160315
|
+
let staleParkedAssignment = false;
|
|
160316
|
+
if (report.state === "running" && !isEphemeralAgent(report) && report.taskId && this.taskStore) {
|
|
160317
|
+
try {
|
|
160318
|
+
const linkedTask = await this.taskStore.getTask(report.taskId);
|
|
160319
|
+
if (isParkedTaskColumn(linkedTask)) {
|
|
160320
|
+
const activeRun = await agentStore.getActiveHeartbeatRun(report.id);
|
|
160321
|
+
const proof = evaluateParkedAgentTaskLink({
|
|
160322
|
+
agent: report,
|
|
160323
|
+
linkedTask,
|
|
160324
|
+
activeRun,
|
|
160325
|
+
hasActiveAgentExecution: (candidateId) => this.trackedAgents.has(candidateId),
|
|
160326
|
+
now
|
|
160327
|
+
});
|
|
160328
|
+
if (!proof.shouldPreserveParkedLink) {
|
|
160329
|
+
staleParkedAssignment = true;
|
|
160330
|
+
renderedState = "active";
|
|
160331
|
+
renderedTask = `${report.taskId} (queued/no live run)`;
|
|
160332
|
+
}
|
|
160333
|
+
}
|
|
160334
|
+
} catch (error) {
|
|
160335
|
+
heartbeatLog.warn(`[reports-health] failed to validate task link for ${report.id}/${report.taskId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
160336
|
+
}
|
|
160337
|
+
}
|
|
159928
160338
|
let health = "healthy";
|
|
159929
|
-
if (
|
|
160339
|
+
if (staleParkedAssignment) {
|
|
160340
|
+
health = "**stale** assignment";
|
|
160341
|
+
} else if (report.state === "paused") {
|
|
159930
160342
|
health = report.pauseReason ? `paused (${report.pauseReason})` : "paused";
|
|
159931
160343
|
} else if (report.state === "error") {
|
|
159932
160344
|
health = "**stuck**";
|
|
@@ -159936,8 +160348,8 @@ ${trimPromptMd(taskDetail.prompt, promptTemplate)}` : "No PROMPT.md available.",
|
|
|
159936
160348
|
health = "**stale**";
|
|
159937
160349
|
heartbeatLog.log(`[reports-health] stale report ${report.id} intervalSource=${intervalSource} staleThresholdMs=${staleThresholdMs} heartbeatAgeMs=${heartbeatAgeMs}`);
|
|
159938
160350
|
}
|
|
159939
|
-
const task =
|
|
159940
|
-
const state =
|
|
160351
|
+
const task = renderedTask;
|
|
160352
|
+
const state = renderedState;
|
|
159941
160353
|
const heartbeat = formatRelativeTime(report.lastHeartbeatAt);
|
|
159942
160354
|
return `| ${report.name} | ${state} | ${task} | ${heartbeat} | ${health} |`;
|
|
159943
160355
|
}));
|
|
@@ -168018,42 +168430,6 @@ var init_node_dispatch_validation = __esm({
|
|
|
168018
168430
|
}
|
|
168019
168431
|
});
|
|
168020
168432
|
|
|
168021
|
-
// ../engine/src/task-agent-sync.ts
|
|
168022
|
-
function attachAgentLinkSync(opts) {
|
|
168023
|
-
const logger3 = opts.logger ?? console;
|
|
168024
|
-
const handler = async ({ task, from, to: to2 }) => {
|
|
168025
|
-
if (!CLEAR_COLUMNS.has(to2)) {
|
|
168026
|
-
return;
|
|
168027
|
-
}
|
|
168028
|
-
try {
|
|
168029
|
-
const agents = await opts.agentStore.listAgents({ includeEphemeral: false });
|
|
168030
|
-
const linkedAgents = agents.filter((agent) => agent.taskId === task.id);
|
|
168031
|
-
for (const agent of linkedAgents) {
|
|
168032
|
-
if ((to2 === "todo" || to2 === "triage") && opts.hasActiveAgentExecution?.(agent.id) === true) {
|
|
168033
|
-
continue;
|
|
168034
|
-
}
|
|
168035
|
-
await opts.agentStore.syncExecutionTaskLink(agent.id, void 0);
|
|
168036
|
-
logger3.log(`taskAgentLinkSync: cleared agent ${agent.id} taskId from ${task.id} after move ${from} \u2192 ${to2}`);
|
|
168037
|
-
}
|
|
168038
|
-
} catch (error) {
|
|
168039
|
-
logger3.warn(
|
|
168040
|
-
`taskAgentLinkSync: failed to sync agents for task ${task.id} after move ${from} \u2192 ${to2}: ${error instanceof Error ? error.message : String(error)}`
|
|
168041
|
-
);
|
|
168042
|
-
}
|
|
168043
|
-
};
|
|
168044
|
-
opts.store.on("task:moved", handler);
|
|
168045
|
-
return () => {
|
|
168046
|
-
opts.store.off("task:moved", handler);
|
|
168047
|
-
};
|
|
168048
|
-
}
|
|
168049
|
-
var CLEAR_COLUMNS;
|
|
168050
|
-
var init_task_agent_sync = __esm({
|
|
168051
|
-
"../engine/src/task-agent-sync.ts"() {
|
|
168052
|
-
"use strict";
|
|
168053
|
-
CLEAR_COLUMNS = /* @__PURE__ */ new Set(["done", "archived", "todo", "triage"]);
|
|
168054
|
-
}
|
|
168055
|
-
});
|
|
168056
|
-
|
|
168057
168433
|
// ../engine/src/runtimes/in-process-runtime.ts
|
|
168058
168434
|
import { EventEmitter as EventEmitter21 } from "node:events";
|
|
168059
168435
|
import { setImmediate as setImmediateCb4 } from "node:timers";
|
|
@@ -168308,6 +168684,7 @@ var init_in_process_runtime = __esm({
|
|
|
168308
168684
|
maxWorktrees: this.config.maxWorktrees,
|
|
168309
168685
|
semaphore: this.globalSemaphore,
|
|
168310
168686
|
agentStore: this.agentStore,
|
|
168687
|
+
hasActiveAgentExecution: (agentId) => this.heartbeatMonitor?.getTrackedAgents().includes(agentId) ?? false,
|
|
168311
168688
|
missionStore,
|
|
168312
168689
|
missionAutopilot,
|
|
168313
168690
|
missionExecutionLoop,
|
|
@@ -191936,6 +192313,7 @@ var init_register_command_center_routes = __esm({
|
|
|
191936
192313
|
init_src();
|
|
191937
192314
|
init_api_error();
|
|
191938
192315
|
init_command_center_csv();
|
|
192316
|
+
init_project_store_resolver();
|
|
191939
192317
|
}
|
|
191940
192318
|
});
|
|
191941
192319
|
|