@runfusion/fusion 0.75.0-beta.1 → 0.75.0-beta.2
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 +389 -134
- package/dist/child-process-worker.js +224 -123
- package/dist/client/.vite/manifest.json +249 -249
- package/dist/client/assets/{AgentDetailView-CsCVX96A.js → AgentDetailView-C4Xpag_i.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-Dh5wDe8g.js → AgentPermissionPolicyEditor-D-Fp7-sy.js} +1 -1
- package/dist/client/assets/{AgentsView-Dd_oZbFn.js → AgentsView-BmFsoY1Q.js} +3 -3
- package/dist/client/assets/{ChatView-gC7_BCmZ.js → ChatView-5KsB3BVO.js} +1 -1
- package/dist/client/assets/{CommandCenter-Bk6RQ-hm.js → CommandCenter-CacGI8AV.js} +1 -1
- package/dist/client/assets/{DevServerView-Da8h5OC-.js → DevServerView-DufjauVn.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-DX9H1EdH.js → DirectoryPicker-DvwqfQDS.js} +1 -1
- package/dist/client/assets/{DocumentsView-BZ0g_Am7.js → DocumentsView-BbYJFIQ1.js} +1 -1
- package/dist/client/assets/{EvalsView-DsI_X6eQ.js → EvalsView-NBC8kOc-.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-BfE5f-XK.js → ExperimentalAgentOnboardingModal-C809yXXY.js} +1 -1
- package/dist/client/assets/{GoalsView-43sqRFfC.js → GoalsView-CpNO9eI0.js} +1 -1
- package/dist/client/assets/{InsightsView-Dvgn_hrA.js → InsightsView-Cahy5FcR.js} +1 -1
- package/dist/client/assets/{MemoryView-DWbHznu3.js → MemoryView-BNJAQjqn.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-BKfmIRDt.js → PiExtensionsManager-DCHTyOmJ.js} +1 -1
- package/dist/client/assets/{PluginManager-xhkrAjSK.js → PluginManager-CBt-CqUz.js} +1 -1
- package/dist/client/assets/{PullRequestView-CyEwGgRD.js → PullRequestView-Dsb9RwmX.js} +1 -1
- package/dist/client/assets/{ReportModal--NSmch5M.js → ReportModal-8FjZOcbC.js} +1 -1
- package/dist/client/assets/{ResearchView-CgqZlNMD.js → ResearchView-DlSG7dOd.js} +1 -1
- package/dist/client/assets/{SecretsView-am_ZT9z8.js → SecretsView-BSajNE-f.js} +1 -1
- package/dist/client/assets/{SessionTerminal-DSc_7NQ-.js → SessionTerminal-M2fIpHrL.js} +1 -1
- package/dist/client/assets/{SettingsModal-Bp6sytUe.js → SettingsModal-DLcvlp44.js} +3 -3
- package/dist/client/assets/{SettingsModal-D2jjC8m8.js → SettingsModal-z6gOf7dG.js} +1 -1
- package/dist/client/assets/{SettingsTextareaRow-DjJJD-Af.js → SettingsTextareaRow-DeC19053.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-DLIeefbh.js → SetupWizardModal-BOjMePFh.js} +2 -2
- package/dist/client/assets/{SkillsView-C67Gdb5y.js → SkillsView-BSBEC-Xm.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-BxhdNPdn.js → WorkflowNodeEditor-DZ9-RBqo.js} +1 -1
- package/dist/client/assets/{agent-import-generation-Bk9fEowy.js → agent-import-generation-COItiXMT.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-s2YViNU1.js → architectureDiagram-3BPJPVTR-BwlCePoB.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-DMsf6VWl.js → blockDiagram-GPEHLZMM-KKrLi9v4.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-DXJExfmV.js → c4Diagram-AAUBKEIU-CkIDt_8M.js} +1 -1
- package/dist/client/assets/channel-CeYcnOtQ.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-CH99GAl7.js → chunk-2J33WTMH-D9sbdaqY.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-BTV9SXg5.js → chunk-4BX2VUAB-D3DtFoAY.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-ntvLX3jz.js → chunk-55IACEB6-gxblxRIN.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-DBNBtu-e.js → chunk-727SXJPM-D0AEDoHN.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DvbtWh_x.js → chunk-AQP2D5EJ-BweoM7qY.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-CUdRlAnf.js → chunk-FMBD7UC4-sjblhrjI.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-B7J-met1.js → chunk-ND2GUHAM-CYuRENSs.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-D7CahWp7.js → chunk-QZHKN3VN-CFKJr4kG.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CZwh2k1X.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CZwh2k1X.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-6E0Aaw7K.js → cose-bilkent-S5V4N54A-6Jb6SgHM.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-Wi236AfH.js → dagre-BM42HDAG-FQJ87Ol3.js} +1 -1
- package/dist/client/assets/{dashboard-view-CrHqKsfL.js → dashboard-view-B1Rq2-73.js} +1 -1
- package/dist/client/assets/{dashboard-view-NpkFaG9s.js → dashboard-view-DH1Zo0m4.js} +1 -1
- package/dist/client/assets/{dashboard-view-hLsXVJJN.js → dashboard-view-DVMZNVoc.js} +1 -1
- package/dist/client/assets/{dashboard-view-BZDf9tzC.js → dashboard-view-DgLbkn5n.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-B3TxtVcq.js → diagram-2AECGRRQ-CKYOu9to.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-45Y_FHjp.js → diagram-5GNKFQAL-DPwjFnde.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-D4w3m4cp.js → diagram-KO2AKTUF-DzZS9Nk3.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-Dv5F4ha2.js → diagram-LMA3HP47-DW9e5YSP.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-CvOQ1IYN.js → diagram-OG6HWLK6-C7V3Mj63.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-DYcp25dX.js → erDiagram-TEJ5UH35-DSQzBA0e.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-DnjuETCJ.js → flowDiagram-I6XJVG4X-BUuOq9tO.js} +1 -1
- package/dist/client/assets/{folder-open-ovJcpdOX.js → folder-open-DXNu4ED4.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-SU7_CTa4.js → ganttDiagram-6RSMTGT7-Bc32G_Ef.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DFvxZu8I.js → gitGraphDiagram-PVQCEYII-Cvfa0W6a.js} +1 -1
- package/dist/client/assets/{index-C_ZHzU4F.js → index-C5YdAqNQ.js} +14 -14
- package/dist/client/assets/index-DHk_s6rA.css +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-DgMEfdKk.js → infoDiagram-5YYISTIA-TqRUTfvy.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CFjnOb7V.js → ishikawaDiagram-YF4QCWOH-D33iop1_.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-46dGsf_j.js → journeyDiagram-JHISSGLW-DH-TDR5Y.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-9-nz-BFT.js → kanban-definition-UN3LZRKU-CAcebzyW.js} +1 -1
- package/dist/client/assets/{mermaid.core-CYHDO5ss.js → mermaid.core-C3Nh1Sak.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-BYQE0Nqo.js → mindmap-definition-RKZ34NQL-CSIccWV8.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BNSMLSjW.js → pieDiagram-4H26LBE5-hXJ3G3MR.js} +1 -1
- package/dist/client/assets/{puzzle-D2SwAtRX.js → puzzle-DI3elldh.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CNdSBA7P.js → quadrantDiagram-W4KKPZXB-B3wpvneC.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-B6EJvc9H.js → requirementDiagram-4Y6WPE33-DPpYL2ca.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-8DcYdUmL.js → sankeyDiagram-5OEKKPKP-BS9QXSEF.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BjRXhtKa.js → sequenceDiagram-3UESZ5HK-C_fqU0MW.js} +1 -1
- package/dist/client/assets/{shield-alert-b4thCabv.js → shield-alert-CzZYSqmH.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-B1X-jWip.js → standing-instructions-template-CsXA4WMZ.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-Btrn_84P.js → stateDiagram-AJRCARHV-DbSZrprS.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-DiyZxQF1.js → stateDiagram-v2-BHNVJYJU-Cf_ckwmM.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CjEYU1kO.js → timeline-definition-PNZ67QCA-B1ohvKjs.js} +1 -1
- package/dist/client/assets/{upload-DMts5fzZ.js → upload-D7nNe9wD.js} +1 -1
- package/dist/client/assets/{users-C9O98ACz.js → users-B79Dvre4.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DtEb-r3I.js → vennDiagram-CIIHVFJN-CxZYCQ72.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-Ds-v1VZi.js → wardley-L42UT6IY-clJem8VU.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-C15pYn-T.js → wardleyDiagram-YWT4CUSO-zE8lyaWe.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-ChJ77iYU.js → xychartDiagram-2RQKCTM6-DsdjuSxQ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +242 -125
- package/dist/migrations/0000_initial.sql +1 -0
- package/dist/migrations/0044_fn_8785_queued_episode_signature.sql +3 -0
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +16 -0
- package/dist/plugin-sdk/index.js +6 -5
- package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/.bundled.reload-3.js +19167 -0
- package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-linear-import/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
- package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-quality/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-quality/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-todos/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-todos/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +6 -5
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/channel-BRBjryTS.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-26ZYCgHv.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-26ZYCgHv.js +0 -1
- package/dist/client/assets/index-CJRmyxnw.css +0 -1
package/dist/bin.js
CHANGED
|
@@ -11925,6 +11925,11 @@ APPROVE when the plan is executable and verifiable, not when it is cosmetically
|
|
|
11925
11925
|
});
|
|
11926
11926
|
|
|
11927
11927
|
// ../core/src/agents/agent-prompts.ts
|
|
11928
|
+
function buildPlanningDuplicatePolicyInstruction() {
|
|
11929
|
+
return `## Duplicate policy for this task
|
|
11930
|
+
|
|
11931
|
+
Only active tasks can be duplicate blockers. A matching task in a done or archived column is historical evidence, not a duplicate verdict: inspect it for context, then write a new plan for the reported work or regression. Do not emit \`DUPLICATE: ...\` for completed or archived work.`;
|
|
11932
|
+
}
|
|
11928
11933
|
function buildTriageHeartbeatGuidance(options = {}) {
|
|
11929
11934
|
const noTaskLine = options.plannerHeartbeatPatrolEnabled === false ? TRIAGE_HEARTBEAT_PATROL_DISABLED_INSTRUCTION : "If no task is assigned: execute your planning instructions. Patrol for vague requests, blocked tasks that need better specification, review follow-ups that should become new tasks, and dependency gaps that are slowing executors down.";
|
|
11930
11935
|
const patrolSafetyLines = options.plannerHeartbeatPatrolEnabled === false ? "" : `
|
|
@@ -12216,7 +12221,7 @@ Write a lean, executable PROMPT.md quickly. Preserve safety gates, but skip heav
|
|
|
12216
12221
|
- Preserve required safety sections for bugs, workflow routing, forensic tasks, and decision-only work.
|
|
12217
12222
|
|
|
12218
12223
|
## Duplicate check
|
|
12219
|
-
Before writing a spec, call \`fn_task_list\` for active work, then call \`fn_task_search\` with 2-4 targeted keyword phrases from the title/description, such as file paths, symptoms, and symbols.
|
|
12224
|
+
Before writing a spec, call \`fn_task_list\` for active work, then call \`fn_task_search\` with \`includeDone: false\` and \`includeArchived: false\` for 2-4 targeted keyword phrases from the title/description, such as file paths, symptoms, and symbols. Do not search completed or archived work for duplicate candidates. When an active match is a duplicate, do not write a spec \u2014 but still write PROMPT.md, with its entire contents being the single line \`DUPLICATE: {existing-task-id}\` and nothing else. That file is how the duplicate is recorded; announcing it only in your reply leaves no plan behind and re-plans the task in a loop.
|
|
12220
12225
|
|
|
12221
12226
|
## Required PROMPT.md shape
|
|
12222
12227
|
Write PROMPT.md with Original Description, Before \u2192 After Transformation, Mission, Dependencies, Context to Read First, File Scope, Steps, Documentation Requirements, Completion Criteria, Git Commit Convention, and Do NOT. Put \`## Original Description\` immediately after the title/\`Created\`/\`Size\` metadata with the operator's original task description copied **verbatim** (do not paraphrase). Put \`## Before \u2192 After Transformation\` next, before \`## Mission\`, with concise Before/After bullets: current state, target state, why it satisfies the user's request at a glance. In \`## Steps\`, every executable heading MUST use \`### Step N: <name>\` (e.g. \`### Step 1: Preflight\`). Do not write bare \`### Preflight\` / \`### Implementation\` headings, and do not add review-level, triage subtask, or proactive subtask headings.
|
|
@@ -12484,9 +12489,9 @@ Verified facts about this codebase's storage \u2014 cite these correctly so Plan
|
|
|
12484
12489
|
- New Postgres migrations must be **registered explicitly** in \`packages/core/src/postgres/schema-applier.ts\` (version constant + bookkeeping check); a \`.sql\` file dropped in the migrations dir that is not wired there silently never runs.
|
|
12485
12490
|
|
|
12486
12491
|
## Duplicate check
|
|
12487
|
-
Before writing a spec, first call \`fn_task_list\` to see active tasks, then call \`fn_task_search\` with 2-4 distinct keyword phrases from the task title and description (for example file paths, error symptoms, and symbol names).
|
|
12488
|
-
|
|
12489
|
-
If
|
|
12492
|
+
Before writing a spec, first call \`fn_task_list\` to see active tasks, then call \`fn_task_search\` with \`includeDone: false\` and \`includeArchived: false\` for 2-4 distinct keyword phrases from the task title and description (for example file paths, error symptoms, and symbol names).
|
|
12493
|
+
Do not search completed or archived work for duplicate candidates.
|
|
12494
|
+
If an actionable task already covers the same work (even if worded differently), do not write a spec.
|
|
12490
12495
|
Instead you MUST still write PROMPT.md, with its ENTIRE contents being this one line and nothing else:
|
|
12491
12496
|
\`DUPLICATE: {existing-task-id}\`
|
|
12492
12497
|
Writing that file IS how you report the duplicate. Reporting it only in your reply is not recorded:
|
|
@@ -12517,7 +12522,7 @@ Workflow policy can disable proactive oversized-task splitting for operators who
|
|
|
12517
12522
|
## Triage tools
|
|
12518
12523
|
You have these extra tools during triage:
|
|
12519
12524
|
- \`fn_task_list\` \u2014 list existing active tasks
|
|
12520
|
-
- \`fn_task_search\` \u2014 keyword search across tasks
|
|
12525
|
+
- \`fn_task_search\` \u2014 keyword search across active tasks by default; history is opt-in for non-duplicate research
|
|
12521
12526
|
- \`fn_task_show\` \u2014 inspect a task and its PROMPT.md
|
|
12522
12527
|
- \`fn_task_create\` \u2014 create a child/follow-up task while triaging
|
|
12523
12528
|
- \`fn_task_document_write\` \u2014 save a planning document (e.g., key="plan")
|
|
@@ -20257,6 +20262,7 @@ var init_project = __esm({
|
|
|
20257
20262
|
worktree: text("worktree"),
|
|
20258
20263
|
blockedBy: text("blocked_by"),
|
|
20259
20264
|
overlapBlockedBy: text("overlap_blocked_by"),
|
|
20265
|
+
queuedLogEpisodeSignature: text("queued_log_episode_signature"),
|
|
20260
20266
|
paused: integer("paused").default(0),
|
|
20261
20267
|
userPaused: integer("user_paused").default(0),
|
|
20262
20268
|
pausedReason: text("paused_reason"),
|
|
@@ -24407,6 +24413,22 @@ var init_near_duplicate_canonical = __esm({
|
|
|
24407
24413
|
}
|
|
24408
24414
|
});
|
|
24409
24415
|
|
|
24416
|
+
// ../core/src/duplicates/near-duplicate-canonical-flags.ts
|
|
24417
|
+
async function resolveNearDuplicateCanonicalFlags(store, canonical) {
|
|
24418
|
+
if (!canonical?.column) return void 0;
|
|
24419
|
+
const workflow = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
|
|
24420
|
+
if (!workflow || workflow.version !== "v2") return void 0;
|
|
24421
|
+
const column = workflow.columns.find((candidate) => candidate.id === canonical.column);
|
|
24422
|
+
return column ? resolveColumnFlags(column) : void 0;
|
|
24423
|
+
}
|
|
24424
|
+
var init_near_duplicate_canonical_flags = __esm({
|
|
24425
|
+
"../core/src/duplicates/near-duplicate-canonical-flags.ts"() {
|
|
24426
|
+
"use strict";
|
|
24427
|
+
init_trait_registry();
|
|
24428
|
+
init_workflow_ir_resolver();
|
|
24429
|
+
}
|
|
24430
|
+
});
|
|
24431
|
+
|
|
24410
24432
|
// ../core/src/git/gitlab-tracking.ts
|
|
24411
24433
|
function formatGitLabTrackedItemRef(item) {
|
|
24412
24434
|
const marker = item.kind === "merge_request" ? "!" : "#";
|
|
@@ -38168,6 +38190,7 @@ async function applySchemaBaseline(db, options = {}) {
|
|
|
38168
38190
|
const taskLifecycleConsumersAlreadyApplied = applied.includes(TASK_LIFECYCLE_CONSUMERS_VERSION);
|
|
38169
38191
|
const validatorInputFingerprintAlreadyApplied = applied.includes(VALIDATOR_INPUT_FINGERPRINT_VERSION);
|
|
38170
38192
|
const unplannedExecutionBlockDedupeAlreadyApplied = applied.includes(UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION);
|
|
38193
|
+
const queuedEpisodeSignatureAlreadyApplied = applied.includes(QUEUED_EPISODE_SIGNATURE_VERSION);
|
|
38171
38194
|
assertBinaryNotOlderThanDatabase(applied);
|
|
38172
38195
|
let schemaChanged = false;
|
|
38173
38196
|
if (!baselineAlreadyApplied) {
|
|
@@ -38561,17 +38584,23 @@ async function applySchemaBaseline(db, options = {}) {
|
|
|
38561
38584
|
await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION}) ON CONFLICT (version) DO NOTHING`);
|
|
38562
38585
|
schemaChanged = true;
|
|
38563
38586
|
}
|
|
38587
|
+
if (!queuedEpisodeSignatureAlreadyApplied) {
|
|
38588
|
+
const migrationSql = await readFile4(QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH, "utf8");
|
|
38589
|
+
await tx.execute(sql.raw(migrationSql));
|
|
38590
|
+
await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${QUEUED_EPISODE_SIGNATURE_VERSION}) ON CONFLICT (version) DO NOTHING`);
|
|
38591
|
+
schemaChanged = true;
|
|
38592
|
+
}
|
|
38564
38593
|
return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
|
|
38565
38594
|
});
|
|
38566
38595
|
}
|
|
38567
|
-
var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, TASK_WEDGE_NOTIFICATION_VERSION, MILESTONE_ASSERTION_PROVENANCE_VERSION, MISSION_LINEAGE_STOP_VERSION, CHAT_SESSION_TAGS_VERSION, DROP_GLOBAL_CONCURRENCY_VERSION, MISSION_TASK_PREFIX_VERSION, CREDENTIAL_INSTANCE_SELECTION_VERSION, TASK_LIFECYCLE_OUTBOX_VERSION, TASK_LIFECYCLE_CONSUMERS_VERSION, VALIDATOR_INPUT_FINGERPRINT_VERSION, UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH, TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, MILESTONE_ASSERTION_PROVENANCE_MIGRATION_PATH, MISSION_LINEAGE_STOP_MIGRATION_PATH, CHAT_SESSION_TAGS_MIGRATION_PATH, DROP_GLOBAL_CONCURRENCY_MIGRATION_PATH, MISSION_TASK_PREFIX_MIGRATION_PATH, CREDENTIAL_INSTANCE_SELECTION_MIGRATION_PATH, TASK_LIFECYCLE_OUTBOX_MIGRATION_PATH, TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH, VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH, UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH;
|
|
38596
|
+
var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, TASK_WEDGE_NOTIFICATION_VERSION, MILESTONE_ASSERTION_PROVENANCE_VERSION, MISSION_LINEAGE_STOP_VERSION, CHAT_SESSION_TAGS_VERSION, DROP_GLOBAL_CONCURRENCY_VERSION, MISSION_TASK_PREFIX_VERSION, CREDENTIAL_INSTANCE_SELECTION_VERSION, TASK_LIFECYCLE_OUTBOX_VERSION, TASK_LIFECYCLE_CONSUMERS_VERSION, VALIDATOR_INPUT_FINGERPRINT_VERSION, UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION, QUEUED_EPISODE_SIGNATURE_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH, TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, MILESTONE_ASSERTION_PROVENANCE_MIGRATION_PATH, MISSION_LINEAGE_STOP_MIGRATION_PATH, CHAT_SESSION_TAGS_MIGRATION_PATH, DROP_GLOBAL_CONCURRENCY_MIGRATION_PATH, MISSION_TASK_PREFIX_MIGRATION_PATH, CREDENTIAL_INSTANCE_SELECTION_MIGRATION_PATH, TASK_LIFECYCLE_OUTBOX_MIGRATION_PATH, TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH, VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH, UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH, QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH;
|
|
38568
38597
|
var init_schema_applier = __esm({
|
|
38569
38598
|
"../core/src/postgres/schema-applier.ts"() {
|
|
38570
38599
|
"use strict";
|
|
38571
38600
|
init_drizzle_orm();
|
|
38572
38601
|
init_plugin_schema_hook();
|
|
38573
38602
|
init_advisory_locks();
|
|
38574
|
-
SCHEMA_BASELINE_VERSION = "
|
|
38603
|
+
SCHEMA_BASELINE_VERSION = "0044";
|
|
38575
38604
|
TASK_DECLARED_SYMBOLS_VERSION = "0028";
|
|
38576
38605
|
INITIAL_SCHEMA_VERSION = "0000";
|
|
38577
38606
|
AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
|
|
@@ -38615,6 +38644,7 @@ var init_schema_applier = __esm({
|
|
|
38615
38644
|
TASK_LIFECYCLE_CONSUMERS_VERSION = "0041";
|
|
38616
38645
|
VALIDATOR_INPUT_FINGERPRINT_VERSION = "0042";
|
|
38617
38646
|
UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION = "0043";
|
|
38647
|
+
QUEUED_EPISODE_SIGNATURE_VERSION = "0044";
|
|
38618
38648
|
LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
|
|
38619
38649
|
StaleBinarySchemaError = class extends Error {
|
|
38620
38650
|
constructor(databaseVersion, binaryVersion) {
|
|
@@ -38738,6 +38768,7 @@ var init_schema_applier = __esm({
|
|
|
38738
38768
|
TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0041_fn_8685_task_lifecycle_consumers.sql");
|
|
38739
38769
|
VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0042_fn_8694_validator_input_fingerprint.sql");
|
|
38740
38770
|
UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0043_fn8768_dispatch_dedupe.sql");
|
|
38771
|
+
QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0044_fn_8785_queued_episode_signature.sql");
|
|
38741
38772
|
}
|
|
38742
38773
|
});
|
|
38743
38774
|
|
|
@@ -61677,9 +61708,14 @@ var init_central_core = __esm({
|
|
|
61677
61708
|
incremented by real work and the durable counter it maintained was fiction.
|
|
61678
61709
|
|
|
61679
61710
|
`getLiveRunningAgentCounts` SURVIVES and is now the only global readout. It is
|
|
61680
|
-
TELEMETRY, not a limiter: it derives live per-project agent counts
|
|
61681
|
-
registered side-effect-safe source so the dashboard can show "N
|
|
61682
|
-
projects)".
|
|
61711
|
+
TELEMETRY, not a capacity limiter: it derives live per-project agent counts
|
|
61712
|
+
from the registered side-effect-safe source so the dashboard can show "N
|
|
61713
|
+
running (all projects)". Task admission does not gate on it.
|
|
61714
|
+
|
|
61715
|
+
FNXC:DevEngineWatch 2026-08-04-01:25:
|
|
61716
|
+
The opt-in source-development restart also reads this conservative telemetry
|
|
61717
|
+
to wait for active work to drain. That is shutdown safety, not capacity
|
|
61718
|
+
arbitration; it never reserves slots or changes task admission.
|
|
61683
61719
|
*/
|
|
61684
61720
|
async getLiveRunningAgentCounts(options) {
|
|
61685
61721
|
this.ensureInitialized();
|
|
@@ -62941,6 +62977,7 @@ var init_persistence = __esm({
|
|
|
62941
62977
|
defineTaskColumn("worktree", (task) => task.worktree ?? null),
|
|
62942
62978
|
defineTaskColumn("blockedBy", (task) => task.blockedBy ?? null),
|
|
62943
62979
|
defineTaskColumn("overlapBlockedBy", (task) => task.overlapBlockedBy ?? null),
|
|
62980
|
+
defineTaskColumn("queuedLogEpisodeSignature", (task) => task.queuedLogEpisodeSignature ?? null),
|
|
62944
62981
|
defineTaskColumn("paused", (task) => task.paused ? 1 : 0),
|
|
62945
62982
|
defineTaskColumn("pausedReason", (task) => task.pausedReason ?? null),
|
|
62946
62983
|
defineTaskColumn("wedgeNotification", (task) => toJsonNullable(task.wedgeNotification)),
|
|
@@ -63355,6 +63392,7 @@ function rowToTask(row) {
|
|
|
63355
63392
|
worktree: row.worktree || void 0,
|
|
63356
63393
|
blockedBy: row.blockedBy || void 0,
|
|
63357
63394
|
overlapBlockedBy: row.overlapBlockedBy || void 0,
|
|
63395
|
+
queuedLogEpisodeSignature: row.queuedLogEpisodeSignature || void 0,
|
|
63358
63396
|
paused: row.paused ? true : void 0,
|
|
63359
63397
|
pausedReason: row.pausedReason || void 0,
|
|
63360
63398
|
wedgeNotification: fromJson(row.wedgeNotification) ?? void 0,
|
|
@@ -69474,6 +69512,14 @@ function noop3() {
|
|
|
69474
69512
|
function matchesParentScope(task, sourceParentTaskId) {
|
|
69475
69513
|
return !sourceParentTaskId || task.sourceParentTaskId === sourceParentTaskId;
|
|
69476
69514
|
}
|
|
69515
|
+
async function findActiveDuplicate(store, candidates, predicate) {
|
|
69516
|
+
for (const candidate of candidates) {
|
|
69517
|
+
if (!predicate(candidate)) continue;
|
|
69518
|
+
const flags = await resolveNearDuplicateCanonicalFlags(store, candidate);
|
|
69519
|
+
if (!isNearDuplicateCanonicalInactive(candidate, flags)) return candidate;
|
|
69520
|
+
}
|
|
69521
|
+
return void 0;
|
|
69522
|
+
}
|
|
69477
69523
|
async function runDeterministicDuplicateGuard(store, input, opts) {
|
|
69478
69524
|
const fingerprint = computeContentFingerprint(input);
|
|
69479
69525
|
if (opts?.bypass === true || !fingerprint) {
|
|
@@ -69487,7 +69533,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
|
|
|
69487
69533
|
windowMs,
|
|
69488
69534
|
includeArchived: false
|
|
69489
69535
|
});
|
|
69490
|
-
const deterministicConflict =
|
|
69536
|
+
const deterministicConflict = await findActiveDuplicate(
|
|
69537
|
+
store,
|
|
69538
|
+
deterministicMatches,
|
|
69491
69539
|
(match) => matchesParentScope(match, opts?.sourceParentTaskId) && !acknowledged.has(match.id)
|
|
69492
69540
|
);
|
|
69493
69541
|
if (deterministicConflict) {
|
|
@@ -69534,7 +69582,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
|
|
|
69534
69582
|
windowMs,
|
|
69535
69583
|
includeArchived: false
|
|
69536
69584
|
});
|
|
69537
|
-
const deterministicConflict =
|
|
69585
|
+
const deterministicConflict = await findActiveDuplicate(
|
|
69586
|
+
store,
|
|
69587
|
+
deterministicMatches,
|
|
69538
69588
|
(match) => matchesParentScope(match, opts.sourceParentTaskId) && !acknowledged.has(match.id)
|
|
69539
69589
|
);
|
|
69540
69590
|
if (deterministicConflict) {
|
|
@@ -69558,7 +69608,9 @@ async function reconcileDeterministicDuplicate(store, args) {
|
|
|
69558
69608
|
windowMs: clampWindowMs(args.windowMs),
|
|
69559
69609
|
includeArchived: false
|
|
69560
69610
|
});
|
|
69561
|
-
const olderSibling =
|
|
69611
|
+
const olderSibling = await findActiveDuplicate(
|
|
69612
|
+
store,
|
|
69613
|
+
siblings,
|
|
69562
69614
|
(sibling) => sibling.id !== args.createdTask.id && sibling.createdAt < args.createdTask.createdAt && matchesParentScope(sibling, args.sourceParentTaskId)
|
|
69563
69615
|
);
|
|
69564
69616
|
if (!olderSibling) {
|
|
@@ -69617,6 +69669,8 @@ var init_duplicate_guard = __esm({
|
|
|
69617
69669
|
"../core/src/duplicates/duplicate-guard.ts"() {
|
|
69618
69670
|
"use strict";
|
|
69619
69671
|
init_duplicate_detection();
|
|
69672
|
+
init_near_duplicate_canonical();
|
|
69673
|
+
init_near_duplicate_canonical_flags();
|
|
69620
69674
|
init_workflow_lifecycle_traits();
|
|
69621
69675
|
FINGERPRINT_WINDOW_DEFAULT_MS = 6e5;
|
|
69622
69676
|
FINGERPRINT_WINDOW_MAX_MS = 36e5;
|
|
@@ -93228,6 +93282,50 @@ async function runPluginColumnTransitionHooksImpl(store, taskId, workflowIr, fro
|
|
|
93228
93282
|
await writeMarker2(remaining);
|
|
93229
93283
|
}
|
|
93230
93284
|
}
|
|
93285
|
+
async function transitionQueuedEpisodeImpl(store, id, transition) {
|
|
93286
|
+
const layer = store.asyncLayer;
|
|
93287
|
+
const projectId = layer.projectId?.trim() || "__legacy_unscoped__";
|
|
93288
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
93289
|
+
const result2 = await layer.transactionImmediate(async (tx) => {
|
|
93290
|
+
await acquireTaskAdvisoryXactLock(tx, projectId, id);
|
|
93291
|
+
const rows = await tx.select().from(project_exports.tasks).where(and(
|
|
93292
|
+
eq(project_exports.tasks.projectId, projectId),
|
|
93293
|
+
eq(project_exports.tasks.id, id),
|
|
93294
|
+
isNull(project_exports.tasks.deletedAt)
|
|
93295
|
+
));
|
|
93296
|
+
const current = rows[0];
|
|
93297
|
+
if (!current) throw new Error(`Task ${id} not found or archived while queuing`);
|
|
93298
|
+
const appended = !(current.status === "queued" && (current.blockedBy ?? null) === transition.blockedBy && (current.overlapBlockedBy ?? null) === transition.overlapBlockedBy && (current.queuedLogEpisodeSignature ?? null) === transition.signature);
|
|
93299
|
+
const log43 = Array.isArray(current.log) ? [...current.log] : [];
|
|
93300
|
+
if (appended) {
|
|
93301
|
+
log43.push({
|
|
93302
|
+
timestamp: now,
|
|
93303
|
+
action: transition.action,
|
|
93304
|
+
outcome: truncateTaskLogOutcome(transition.outcome),
|
|
93305
|
+
...transition.runContext ? { runContext: transition.runContext } : {}
|
|
93306
|
+
});
|
|
93307
|
+
const limit = getTaskActivityLogEntryLimit();
|
|
93308
|
+
if (log43.length > limit) log43.splice(0, log43.length - limit);
|
|
93309
|
+
}
|
|
93310
|
+
const updated = await tx.update(project_exports.tasks).set({
|
|
93311
|
+
status: "queued",
|
|
93312
|
+
blockedBy: transition.blockedBy,
|
|
93313
|
+
overlapBlockedBy: transition.overlapBlockedBy,
|
|
93314
|
+
queuedLogEpisodeSignature: transition.signature,
|
|
93315
|
+
...appended ? { log: log43 } : {},
|
|
93316
|
+
updatedAt: now
|
|
93317
|
+
}).where(and(
|
|
93318
|
+
eq(project_exports.tasks.projectId, projectId),
|
|
93319
|
+
eq(project_exports.tasks.id, id)
|
|
93320
|
+
)).returning();
|
|
93321
|
+
return { appended, task: updated[0] };
|
|
93322
|
+
});
|
|
93323
|
+
const task = store.rowToTask(store.pgRowToTaskRow(result2.task));
|
|
93324
|
+
await store.writeTaskJsonFile(store.taskDir(id), task);
|
|
93325
|
+
if (store.isWatching) store.taskCache.set(id, { ...task });
|
|
93326
|
+
store.emitTaskLifecycleEventSafely("task:updated", [task]);
|
|
93327
|
+
return { appended: result2.appended, task };
|
|
93328
|
+
}
|
|
93231
93329
|
async function checkAndRecordUnplannedExecutionBlockImpl(store, id, episode) {
|
|
93232
93330
|
const layer = store.asyncLayer;
|
|
93233
93331
|
const projectId = layer.projectId ?? "__legacy_unscoped__";
|
|
@@ -93339,6 +93437,7 @@ var init_audit_ops = __esm({
|
|
|
93339
93437
|
init_comments();
|
|
93340
93438
|
init_async_persistence();
|
|
93341
93439
|
init_async_comments_attachments();
|
|
93440
|
+
init_task_advisory_lock();
|
|
93342
93441
|
init_project_lane_vocabulary();
|
|
93343
93442
|
init_schema2();
|
|
93344
93443
|
}
|
|
@@ -99121,6 +99220,9 @@ var init_store = __esm({
|
|
|
99121
99220
|
async checkAndRecordUnplannedExecutionBlock(id, episode) {
|
|
99122
99221
|
return checkAndRecordUnplannedExecutionBlockImpl(this, id, episode);
|
|
99123
99222
|
}
|
|
99223
|
+
async transitionQueuedEpisode(id, transition) {
|
|
99224
|
+
return transitionQueuedEpisodeImpl(this, id, transition);
|
|
99225
|
+
}
|
|
99124
99226
|
async logEntry(id, action, outcome, runContext) {
|
|
99125
99227
|
return logEntryImpl(this, id, action, outcome, runContext);
|
|
99126
99228
|
}
|
|
@@ -127887,6 +127989,7 @@ __export(src_exports, {
|
|
|
127887
127989
|
buildInsightExtractionPrompt: () => buildInsightExtractionPrompt,
|
|
127888
127990
|
buildManualRetryResetPatch: () => buildManualRetryResetPatch,
|
|
127889
127991
|
buildOriginalDescriptionSection: () => buildOriginalDescriptionSection,
|
|
127992
|
+
buildPlanningDuplicatePolicyInstruction: () => buildPlanningDuplicatePolicyInstruction,
|
|
127890
127993
|
buildQmdCollectionAddArgs: () => buildQmdCollectionAddArgs,
|
|
127891
127994
|
buildQmdRefreshCommands: () => buildQmdRefreshCommands,
|
|
127892
127995
|
buildQmdSearchArgs: () => buildQmdSearchArgs,
|
|
@@ -128581,6 +128684,7 @@ __export(src_exports, {
|
|
|
128581
128684
|
resolveMergerFallbackModel: () => resolveMergerFallbackModel,
|
|
128582
128685
|
resolveMergerSettingsModel: () => resolveMergerSettingsModel,
|
|
128583
128686
|
resolveMobileNavPrimaryItems: () => resolveMobileNavPrimaryItems,
|
|
128687
|
+
resolveNearDuplicateCanonicalFlags: () => resolveNearDuplicateCanonicalFlags,
|
|
128584
128688
|
resolveNodeOverrideLanes: () => resolveNodeOverrideLanes,
|
|
128585
128689
|
resolveOptionalReviewRevisionBudget: () => resolveOptionalReviewRevisionBudget,
|
|
128586
128690
|
resolveOptionalStepRevisionBudget: () => resolveOptionalStepRevisionBudget,
|
|
@@ -128813,6 +128917,7 @@ var init_src2 = __esm({
|
|
|
128813
128917
|
init_plugin_prompt_condition();
|
|
128814
128918
|
init_plan_approval();
|
|
128815
128919
|
init_near_duplicate_canonical();
|
|
128920
|
+
init_near_duplicate_canonical_flags();
|
|
128816
128921
|
init_gitlab_tracking();
|
|
128817
128922
|
init_planner_intervention2();
|
|
128818
128923
|
init_planner_overseer_events();
|
|
@@ -169121,14 +169226,14 @@ async function findDefinedFeatureBootstrapDuplicate(store, input, sourceAgentId,
|
|
|
169121
169226
|
if (!sourceAgentId && !sourceParentTaskId) return void 0;
|
|
169122
169227
|
const candidates = await store.listTasks({ slim: true, includeArchived: true, includeDeleted: true });
|
|
169123
169228
|
const byId = new Map(candidates.map((task) => [task.id, task]));
|
|
169124
|
-
const
|
|
169229
|
+
const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
|
|
169125
169230
|
const matches = findSameAgentDuplicates({
|
|
169126
169231
|
title: input.title,
|
|
169127
169232
|
description: input.description,
|
|
169128
169233
|
sourceParentTaskId
|
|
169129
169234
|
}, candidates.flatMap((task) => {
|
|
169130
169235
|
const createdAt = Date.parse(task.createdAt);
|
|
169131
|
-
if (Number.isNaN(createdAt) || task.deletedAt ||
|
|
169236
|
+
if (Number.isNaN(createdAt) || task.deletedAt || isTerminalCandidate(task)) return [];
|
|
169132
169237
|
return [{
|
|
169133
169238
|
id: task.id,
|
|
169134
169239
|
title: task.title ?? "",
|
|
@@ -169179,21 +169284,6 @@ async function resolveTerminalColumnsForTasks(store, tasks2) {
|
|
|
169179
169284
|
}
|
|
169180
169285
|
return (task) => terminalByTaskId.get(task.id)?.has(task.column) === true;
|
|
169181
169286
|
}
|
|
169182
|
-
async function resolveArchivedColumnsForTasks(store, tasks2) {
|
|
169183
|
-
const cache7 = /* @__PURE__ */ new Map();
|
|
169184
|
-
const archivedByTaskId = /* @__PURE__ */ new Map();
|
|
169185
|
-
for (const task of tasks2) {
|
|
169186
|
-
if (archivedByTaskId.has(task.id)) continue;
|
|
169187
|
-
const columns = /* @__PURE__ */ new Set(["archived"]);
|
|
169188
|
-
try {
|
|
169189
|
-
const ir2 = await resolveWorkflowIrForTask(store, task.id, cache7);
|
|
169190
|
-
if (ir2) for (const id of columnsWithFlag(ir2, "archived")) columns.add(id);
|
|
169191
|
-
} catch {
|
|
169192
|
-
}
|
|
169193
|
-
archivedByTaskId.set(task.id, columns);
|
|
169194
|
-
}
|
|
169195
|
-
return (task) => archivedByTaskId.get(task.id)?.has(task.column) === true;
|
|
169196
|
-
}
|
|
169197
169287
|
async function createAgentTask(store, input, options) {
|
|
169198
169288
|
const validateDuplicateCanonical = input.validateDuplicateCanonical;
|
|
169199
169289
|
const settings = typeof store.getSettings === "function" ? await store.getSettings() : {};
|
|
@@ -169258,11 +169348,12 @@ async function createAgentTask(store, input, options) {
|
|
|
169258
169348
|
try {
|
|
169259
169349
|
const acknowledged = new Set(options?.acknowledgedDuplicates ?? []);
|
|
169260
169350
|
const candidates = await store.findRecentTasksBySourceParentTaskId(sourceParentTaskId);
|
|
169351
|
+
const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
|
|
169261
169352
|
const matches = findSameAgentDuplicates({
|
|
169262
169353
|
title: input.title,
|
|
169263
169354
|
description: input.description,
|
|
169264
169355
|
sourceParentTaskId
|
|
169265
|
-
}, candidates.map((candidate) => ({
|
|
169356
|
+
}, candidates.filter((candidate) => !isTerminalCandidate(candidate)).map((candidate) => ({
|
|
169266
169357
|
id: candidate.id,
|
|
169267
169358
|
title: candidate.title ?? "",
|
|
169268
169359
|
description: candidate.description,
|
|
@@ -169490,7 +169581,7 @@ function createTaskSearchTool(store) {
|
|
|
169490
169581
|
return {
|
|
169491
169582
|
name: "fn_task_search",
|
|
169492
169583
|
label: "Search Tasks",
|
|
169493
|
-
description: "Keyword search across
|
|
169584
|
+
description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
|
|
169494
169585
|
parameters: taskSearchParams,
|
|
169495
169586
|
execute: async (_id, params) => {
|
|
169496
169587
|
const query = params.query.trim();
|
|
@@ -169503,10 +169594,10 @@ function createTaskSearchTool(store) {
|
|
|
169503
169594
|
const limit = Math.min(50, Math.max(1, Math.floor(params.limit ?? 20)));
|
|
169504
169595
|
const results = await store.searchTasks(query, {
|
|
169505
169596
|
slim: true,
|
|
169506
|
-
includeArchived: params.includeArchived ??
|
|
169597
|
+
includeArchived: params.includeArchived ?? false,
|
|
169507
169598
|
limit
|
|
169508
169599
|
});
|
|
169509
|
-
const includeDone = params.includeDone ??
|
|
169600
|
+
const includeDone = params.includeDone ?? false;
|
|
169510
169601
|
const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
|
|
169511
169602
|
const filtered = includeDone ? results : results.filter((task) => !isTerminalResult(task));
|
|
169512
169603
|
const lines = filtered.map(formatTaskSummaryLine);
|
|
@@ -186340,6 +186431,9 @@ var init_scheduler = __esm({
|
|
|
186340
186431
|
}
|
|
186341
186432
|
}
|
|
186342
186433
|
}
|
|
186434
|
+
async transitionQueuedEpisode(task, input) {
|
|
186435
|
+
return (await this.store.transitionQueuedEpisode(task.id, input)).appended;
|
|
186436
|
+
}
|
|
186343
186437
|
async logDispatchQueuedReason(taskId, reason, memoKey) {
|
|
186344
186438
|
const key = `${taskId}:${memoKey ?? reason}`;
|
|
186345
186439
|
if (this.wasDispatchQueuedReasonLogged.has(key)) {
|
|
@@ -186852,11 +186946,13 @@ var init_scheduler = __esm({
|
|
|
186852
186946
|
}
|
|
186853
186947
|
const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
|
|
186854
186948
|
if (unmetDeps.length > 0) {
|
|
186855
|
-
|
|
186856
|
-
|
|
186857
|
-
|
|
186949
|
+
const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
|
|
186950
|
+
await this.transitionQueuedEpisode(task, {
|
|
186951
|
+
signature: `dependency:${normalizedUnmetDeps.join(",")}`,
|
|
186952
|
+
blockedBy: unmetDeps[0] ?? null,
|
|
186953
|
+
overlapBlockedBy: task.overlapBlockedBy ?? null,
|
|
186954
|
+
action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
|
|
186858
186955
|
});
|
|
186859
|
-
await this.logDispatchQueuedReason(task.id, `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`);
|
|
186860
186956
|
this.options.onBlocked?.(task, unmetDeps);
|
|
186861
186957
|
return null;
|
|
186862
186958
|
}
|
|
@@ -187217,16 +187313,13 @@ var init_scheduler = __esm({
|
|
|
187217
187313
|
const overlappingTaskId = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
|
|
187218
187314
|
if (overlappingTaskId) {
|
|
187219
187315
|
const activeLeaseColumn = activeScopeColumns.get(overlappingTaskId) ?? "in-progress";
|
|
187220
|
-
await this.
|
|
187221
|
-
|
|
187316
|
+
await this.transitionQueuedEpisode(task, {
|
|
187317
|
+
signature: `file-scope:${overlappingTaskId}`,
|
|
187222
187318
|
blockedBy: null,
|
|
187223
|
-
overlapBlockedBy: overlappingTaskId
|
|
187319
|
+
overlapBlockedBy: overlappingTaskId,
|
|
187320
|
+
action: `queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
|
|
187224
187321
|
});
|
|
187225
187322
|
await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
|
|
187226
|
-
await this.logDispatchQueuedReason(
|
|
187227
|
-
task.id,
|
|
187228
|
-
`queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
|
|
187229
|
-
);
|
|
187230
187323
|
return null;
|
|
187231
187324
|
}
|
|
187232
187325
|
if (task.overlapBlockedBy) {
|
|
@@ -202705,13 +202798,6 @@ function hasTerminalInvalidDoneTransition(task) {
|
|
|
202705
202798
|
const error3 = task.error ?? "";
|
|
202706
202799
|
return error3.includes("Invalid transition:") && error3.includes("\u2192 'done'");
|
|
202707
202800
|
}
|
|
202708
|
-
async function resolveNearDuplicateCanonicalFlags(store, canonical) {
|
|
202709
|
-
if (!canonical?.column) return void 0;
|
|
202710
|
-
const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
|
|
202711
|
-
if (!ir2 || ir2.version !== "v2") return void 0;
|
|
202712
|
-
const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
|
|
202713
|
-
return column ? resolveColumnFlags(column) : void 0;
|
|
202714
|
-
}
|
|
202715
202801
|
function isTaskWorkComplete(task) {
|
|
202716
202802
|
if (task.steps.length === 0) return false;
|
|
202717
202803
|
return task.steps.every((step) => step.status === "done" || step.status === "skipped");
|
|
@@ -202871,7 +202957,6 @@ var init_self_healing = __esm({
|
|
|
202871
202957
|
strandedHoldContinuationNoActionAudited = /* @__PURE__ */ new Set();
|
|
202872
202958
|
/* FNXC:SymbolLock 2026-07-30-14:20: idle symbol-lock sweeps emit one no-action audit until a stale lock re-arms the diagnostic. */
|
|
202873
202959
|
symbolLockNoActionAudited = false;
|
|
202874
|
-
preservedQueuedOverlapLogged = /* @__PURE__ */ new Map();
|
|
202875
202960
|
maintenanceTickCounter = 0;
|
|
202876
202961
|
taskLifecycleRetentionLastPrunedAt = /* @__PURE__ */ new Map();
|
|
202877
202962
|
processBootStartedAt = Date.now();
|
|
@@ -203531,7 +203616,6 @@ var init_self_healing = __esm({
|
|
|
203531
203616
|
}
|
|
203532
203617
|
this.finalizeUnprovenWarned.clear();
|
|
203533
203618
|
this.strandedCompletedFailureProvenanceWarned.clear();
|
|
203534
|
-
this.preservedQueuedOverlapLogged.clear();
|
|
203535
203619
|
log33.debug("Stopped");
|
|
203536
203620
|
}
|
|
203537
203621
|
// ── Auto-unpause ───────────────────────────────────────────────────
|
|
@@ -206100,17 +206184,20 @@ var init_self_healing = __esm({
|
|
|
206100
206184
|
if (todoTaskIds.has(dependent.id)) {
|
|
206101
206185
|
if (unresolvedDeps.length > 0) {
|
|
206102
206186
|
const nextBlocker = unresolvedDeps[0];
|
|
206103
|
-
|
|
206104
|
-
await this.store.
|
|
206105
|
-
|
|
206106
|
-
|
|
206107
|
-
|
|
206187
|
+
const normalizedUnresolvedDeps = [...new Set(unresolvedDeps)].sort();
|
|
206188
|
+
await this.store.transitionQueuedEpisode(dependent.id, {
|
|
206189
|
+
signature: `dependency:${normalizedUnresolvedDeps.join(",")}`,
|
|
206190
|
+
blockedBy: nextBlocker,
|
|
206191
|
+
overlapBlockedBy,
|
|
206192
|
+
action: `Auto-recovered (FN-4523): cleared stale blockedBy \u2014 blocker ${taskId} is done; now blocked by ${nextBlocker}`
|
|
206193
|
+
});
|
|
206108
206194
|
} else if (hasActiveOverlapBlocker) {
|
|
206109
|
-
await this.store.
|
|
206110
|
-
|
|
206111
|
-
|
|
206112
|
-
|
|
206113
|
-
|
|
206195
|
+
await this.store.transitionQueuedEpisode(dependent.id, {
|
|
206196
|
+
signature: `file-scope:${overlapBlockedBy}`,
|
|
206197
|
+
blockedBy: null,
|
|
206198
|
+
overlapBlockedBy,
|
|
206199
|
+
action: `Auto-recovered (FN-4523): preserved queued status \u2014 still blocked by file scope overlap with ${overlapBlockedBy}`
|
|
206200
|
+
});
|
|
206114
206201
|
} else {
|
|
206115
206202
|
await this.store.updateTask(dependent.id, { blockedBy: null, overlapBlockedBy: null, status: null });
|
|
206116
206203
|
await this.store.logEntry(
|
|
@@ -206623,16 +206710,6 @@ var init_self_healing = __esm({
|
|
|
206623
206710
|
countBlockedDepth(tasks2) {
|
|
206624
206711
|
return tasks2.filter((task) => typeof task.blockedBy === "string" && task.blockedBy.trim().length > 0).length;
|
|
206625
206712
|
}
|
|
206626
|
-
shouldLogPreservedQueuedOverlap(taskId, overlapBlockedBy) {
|
|
206627
|
-
if (!overlapBlockedBy) return false;
|
|
206628
|
-
const previous = this.preservedQueuedOverlapLogged.get(taskId);
|
|
206629
|
-
if (previous === overlapBlockedBy) return false;
|
|
206630
|
-
this.preservedQueuedOverlapLogged.set(taskId, overlapBlockedBy);
|
|
206631
|
-
return true;
|
|
206632
|
-
}
|
|
206633
|
-
clearPreservedQueuedOverlapMemo(taskId) {
|
|
206634
|
-
this.preservedQueuedOverlapLogged.delete(taskId);
|
|
206635
|
-
}
|
|
206636
206713
|
/**
|
|
206637
206714
|
* #1401: periodic transitionPending recovery sweep. Delegates to the store's
|
|
206638
206715
|
* idempotent recovery method (a no-op when no stale markers exist), keeping
|
|
@@ -206865,7 +206942,6 @@ var init_self_healing = __esm({
|
|
|
206865
206942
|
(task) => task.status === "queued" && (task.dependencies.length > 0 || Boolean(task.overlapBlockedBy))
|
|
206866
206943
|
);
|
|
206867
206944
|
if (blockedTasks.length === 0 && queuedDependencyTasks.length === 0) {
|
|
206868
|
-
this.preservedQueuedOverlapLogged.clear();
|
|
206869
206945
|
return 0;
|
|
206870
206946
|
}
|
|
206871
206947
|
const allTasks = await this.store.listTasks({ includeArchived: true });
|
|
@@ -206937,13 +207013,6 @@ var init_self_healing = __esm({
|
|
|
206937
207013
|
hold: /* @__PURE__ */ new Set(["todo"]),
|
|
206938
207014
|
review: /* @__PURE__ */ new Set(["in-review"])
|
|
206939
207015
|
};
|
|
206940
|
-
for (const [taskId, lastLoggedBlockerId] of this.preservedQueuedOverlapLogged) {
|
|
206941
|
-
const memoTask = taskById.get(taskId);
|
|
206942
|
-
const memoHasActiveOverlapBlocker = memoTask ? await hasActiveFileScopeOverlapBlocker(memoTask, memoTask.overlapBlockedBy) : false;
|
|
206943
|
-
if (!candidates.has(taskId) || !memoTask || !lanesOf(taskId).hold.has(memoTask.column) || memoTask.status !== "queued" || memoTask.overlapBlockedBy !== lastLoggedBlockerId || !memoHasActiveOverlapBlocker) {
|
|
206944
|
-
this.clearPreservedQueuedOverlapMemo(taskId);
|
|
206945
|
-
}
|
|
206946
|
-
}
|
|
206947
207016
|
for (const task of candidates.values()) {
|
|
206948
207017
|
const blockerId = task.blockedBy;
|
|
206949
207018
|
const unresolvedDeps = task.dependencies.filter((depId) => {
|
|
@@ -207016,7 +207085,6 @@ var init_self_healing = __esm({
|
|
|
207016
207085
|
let didRecover = false;
|
|
207017
207086
|
if (todoTaskIds.has(task.id)) {
|
|
207018
207087
|
if (unresolvedDeps.length > 0) {
|
|
207019
|
-
this.clearPreservedQueuedOverlapMemo(task.id);
|
|
207020
207088
|
const nextBlocker2 = unresolvedDeps[0];
|
|
207021
207089
|
if (nextBlocker2 === blockerId) {
|
|
207022
207090
|
continue;
|
|
@@ -207025,19 +207093,19 @@ var init_self_healing = __esm({
|
|
|
207025
207093
|
await this.store.logEntry(task.id, `Auto-recovered (FN-5488): refreshed stale blockedBy \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; ${reason}; now blocked by ${nextBlocker2}`);
|
|
207026
207094
|
didRecover = true;
|
|
207027
207095
|
} else if (hasActiveOverlapBlocker) {
|
|
207028
|
-
await this.store.
|
|
207029
|
-
|
|
207030
|
-
|
|
207031
|
-
|
|
207032
|
-
|
|
207096
|
+
const transition = await this.store.transitionQueuedEpisode(task.id, {
|
|
207097
|
+
signature: `file-scope:${task.overlapBlockedBy}`,
|
|
207098
|
+
blockedBy: null,
|
|
207099
|
+
overlapBlockedBy: task.overlapBlockedBy ?? null,
|
|
207100
|
+
action: `Auto-recovered (FN-5488): preserved queued status \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; still blocked by file scope overlap with ${task.overlapBlockedBy}`
|
|
207101
|
+
});
|
|
207102
|
+
didRecover = transition.appended;
|
|
207033
207103
|
} else {
|
|
207034
|
-
this.clearPreservedQueuedOverlapMemo(task.id);
|
|
207035
207104
|
await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
|
|
207036
207105
|
await this.store.logEntry(task.id, `Auto-recovered (FN-5488): cleared stale blockedBy \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; ${reason}`);
|
|
207037
207106
|
didRecover = true;
|
|
207038
207107
|
}
|
|
207039
207108
|
} else {
|
|
207040
|
-
this.clearPreservedQueuedOverlapMemo(task.id);
|
|
207041
207109
|
await this.store.updateTask(task.id, { blockedBy: null });
|
|
207042
207110
|
await this.store.logEntry(task.id, `Auto-recovered (FN-4091): cleared stale blockedBy \u2014 ${reason}`);
|
|
207043
207111
|
didRecover = true;
|
|
@@ -207057,13 +207125,14 @@ var init_self_healing = __esm({
|
|
|
207057
207125
|
if (queuedDependencyTaskIds.has(task.id)) {
|
|
207058
207126
|
try {
|
|
207059
207127
|
if (hasActiveOverlapBlocker) {
|
|
207060
|
-
await this.store.
|
|
207061
|
-
|
|
207062
|
-
|
|
207063
|
-
|
|
207064
|
-
|
|
207128
|
+
const transition = await this.store.transitionQueuedEpisode(task.id, {
|
|
207129
|
+
signature: `file-scope:${task.overlapBlockedBy}`,
|
|
207130
|
+
blockedBy: null,
|
|
207131
|
+
overlapBlockedBy: task.overlapBlockedBy ?? null,
|
|
207132
|
+
action: `Auto-recovered: preserved queued status \u2014 still blocked by file scope overlap with ${task.overlapBlockedBy}`
|
|
207133
|
+
});
|
|
207134
|
+
if (transition.appended) recovered++;
|
|
207065
207135
|
} else {
|
|
207066
|
-
this.clearPreservedQueuedOverlapMemo(task.id);
|
|
207067
207136
|
await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
|
|
207068
207137
|
}
|
|
207069
207138
|
} catch (err) {
|
|
@@ -207073,7 +207142,6 @@ var init_self_healing = __esm({
|
|
|
207073
207142
|
}
|
|
207074
207143
|
continue;
|
|
207075
207144
|
}
|
|
207076
|
-
this.clearPreservedQueuedOverlapMemo(task.id);
|
|
207077
207145
|
const nextBlocker = unresolvedDeps[0] ?? null;
|
|
207078
207146
|
if (nextBlocker && task.blockedBy !== nextBlocker) {
|
|
207079
207147
|
try {
|
|
@@ -213284,14 +213352,12 @@ var init_self_healing = __esm({
|
|
|
213284
213352
|
if (canClearInactiveMarker) {
|
|
213285
213353
|
rmSync7(promptPath, { force: true });
|
|
213286
213354
|
const priorClearCount = typeof task.sourceMetadata?.duplicateMarkerClearCount === "number" ? task.sourceMetadata.duplicateMarkerClearCount : 0;
|
|
213287
|
-
const
|
|
213288
|
-
const exhausted = alreadyDismissed || priorClearCount >= 1;
|
|
213289
|
-
const patch = exhausted ? buildMarkerExhaustedFailedTaskPatch(marker.canonicalId, priorClearCount) : buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
|
|
213355
|
+
const patch = buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
|
|
213290
213356
|
await this.store.updateTask(task.id, patch);
|
|
213291
213357
|
if (typeof this.store.logEntry === "function") {
|
|
213292
213358
|
await Promise.resolve(this.store.logEntry(
|
|
213293
213359
|
task.id,
|
|
213294
|
-
|
|
213360
|
+
TRIAGE_MARKER_CLEARED_REPLAN_LOG_ACTION,
|
|
213295
213361
|
buildInactiveDuplicateClearFeedback(marker.canonicalId)
|
|
213296
213362
|
)).catch(() => {
|
|
213297
213363
|
});
|
|
@@ -214103,13 +214169,6 @@ function inlineTaskListFallback(lines, opts = {}) {
|
|
|
214103
214169
|
function resolveTaskListFormatter(core) {
|
|
214104
214170
|
return typeof core.formatTaskListText === "function" ? core.formatTaskListText : inlineTaskListFallback;
|
|
214105
214171
|
}
|
|
214106
|
-
async function resolveNearDuplicateCanonicalFlags2(store, canonical) {
|
|
214107
|
-
if (!canonical?.column) return void 0;
|
|
214108
|
-
const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
|
|
214109
|
-
if (!ir2 || ir2.version !== "v2") return void 0;
|
|
214110
|
-
const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
|
|
214111
|
-
return column ? resolveColumnFlags(column) : void 0;
|
|
214112
|
-
}
|
|
214113
214172
|
function isOrphanedLegacyTriageRow(column, declaresTriage) {
|
|
214114
214173
|
return column === "triage" && !declaresTriage;
|
|
214115
214174
|
}
|
|
@@ -215838,11 +215897,13 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
215838
215897
|
const defaultTriagePrompt = resolveAgentPrompt("triage", void 0, { plannerHeartbeatPatrolEnabled });
|
|
215839
215898
|
const resolvedBasePrompt = userTriagePrompt || (leanPlanning ? workflowFastPlanningPrompt || builtinSeamPrompt("planning-fast") || defaultTriagePrompt : workflowPlanningPrompt || defaultTriagePrompt);
|
|
215840
215899
|
const renderedBasePrompt = renderTriagePolicyPlaceholders(resolvedBasePrompt, triagePolicySettings);
|
|
215900
|
+
const duplicatePolicyInstruction = buildPlanningDuplicatePolicyInstruction();
|
|
215841
215901
|
const triageLayers = buildPromptLayers({
|
|
215842
215902
|
basePrompt: renderedBasePrompt,
|
|
215843
215903
|
goalContext: triageGoalResolution.goalContext,
|
|
215844
215904
|
agentInstructions: [
|
|
215845
215905
|
triageIdentitySection,
|
|
215906
|
+
duplicatePolicyInstruction,
|
|
215846
215907
|
triageInstructions,
|
|
215847
215908
|
isResearchToolSurfaceEnabled(settings) ? getResearchGuidanceForSurface("triage") : ""
|
|
215848
215909
|
].filter((section) => section.trim()).join("\n\n"),
|
|
@@ -216471,7 +216532,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
216471
216532
|
const taskSearch = {
|
|
216472
216533
|
name: "fn_task_search",
|
|
216473
216534
|
label: "Search Tasks",
|
|
216474
|
-
description: "Keyword search across tasks
|
|
216535
|
+
description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
|
|
216475
216536
|
parameters: taskSearchParams2,
|
|
216476
216537
|
execute: async (_callId, params) => {
|
|
216477
216538
|
const query = params.query.trim();
|
|
@@ -216483,10 +216544,10 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
216483
216544
|
}
|
|
216484
216545
|
const results = await store.searchTasks(query, {
|
|
216485
216546
|
slim: true,
|
|
216486
|
-
includeArchived: params.includeArchived ??
|
|
216547
|
+
includeArchived: params.includeArchived ?? false,
|
|
216487
216548
|
limit: params.limit ?? 20
|
|
216488
216549
|
});
|
|
216489
|
-
const includeDone = params.includeDone ??
|
|
216550
|
+
const includeDone = params.includeDone ?? false;
|
|
216490
216551
|
const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
|
|
216491
216552
|
const filtered = includeDone ? results : results.filter((t) => !isTerminalResult(t));
|
|
216492
216553
|
if (filtered.length === 0) {
|
|
@@ -216725,7 +216786,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
216725
216786
|
if (canonicalTask?.id.toLowerCase() === task.id.toLowerCase()) {
|
|
216726
216787
|
return false;
|
|
216727
216788
|
}
|
|
216728
|
-
const canonicalFlags = await
|
|
216789
|
+
const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
|
|
216729
216790
|
if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
|
|
216730
216791
|
planLog.log(`${task.id} explicit duplicate marker targets inactive ${canonicalId}; clearing marker for replanning`);
|
|
216731
216792
|
} else {
|
|
@@ -216869,17 +216930,16 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
216869
216930
|
const canonicalId = explicitDuplicateMarker.canonicalId;
|
|
216870
216931
|
const canonicalTask = await this.store.getTask(canonicalId).catch(() => null);
|
|
216871
216932
|
const canClearInactiveMarker = task.userPaused !== true && (task.paused !== true || task.pausedReason === "duplicate-decision-required") && (task.pausedReason == null || task.pausedReason === "duplicate-decision-required");
|
|
216872
|
-
const canonicalFlags = await
|
|
216933
|
+
const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
|
|
216873
216934
|
const liveMeta = (await this.store.getTask(task.id).catch(() => null))?.sourceMetadata ?? task.sourceMetadata;
|
|
216874
216935
|
const priorClearCount = typeof liveMeta?.duplicateMarkerClearCount === "number" ? liveMeta.duplicateMarkerClearCount : 0;
|
|
216875
216936
|
if (isNearDuplicateCanonicalInactive(canonicalTask ?? void 0, canonicalFlags)) {
|
|
216876
216937
|
if (canClearInactiveMarker) {
|
|
216877
|
-
const alreadyDismissed = isTriageDuplicateKeepAcknowledged(liveMeta, canonicalId);
|
|
216878
216938
|
await this.clearDuplicateMarkerForReplan(
|
|
216879
216939
|
task,
|
|
216880
216940
|
canonicalId,
|
|
216881
216941
|
buildInactiveDuplicateClearFeedback(canonicalId),
|
|
216882
|
-
{ exhausted:
|
|
216942
|
+
{ exhausted: false, priorClearCount }
|
|
216883
216943
|
);
|
|
216884
216944
|
}
|
|
216885
216945
|
return;
|
|
@@ -217119,7 +217179,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
217119
217179
|
if (!canonicalTask) {
|
|
217120
217180
|
return;
|
|
217121
217181
|
}
|
|
217122
|
-
const canonicalFlags = await
|
|
217182
|
+
const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
|
|
217123
217183
|
if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
|
|
217124
217184
|
planLog.log(`${task.id}: near-duplicate candidate ${canonical.id} is inactive; skipping near-duplicate flag`);
|
|
217125
217185
|
return;
|
|
@@ -232990,13 +233050,15 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
232990
233050
|
recoveryRehome: true
|
|
232991
233051
|
});
|
|
232992
233052
|
}
|
|
232993
|
-
|
|
232994
|
-
await this.store.
|
|
232995
|
-
|
|
232996
|
-
|
|
232997
|
-
|
|
232998
|
-
|
|
232999
|
-
|
|
233053
|
+
const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
|
|
233054
|
+
await this.store.transitionQueuedEpisode(liveTask.id, {
|
|
233055
|
+
signature: `dependency:${normalizedUnmetDeps.join(",")}`,
|
|
233056
|
+
blockedBy: unmetDeps[0] ?? null,
|
|
233057
|
+
overlapBlockedBy: liveTask.overlapBlockedBy ?? null,
|
|
233058
|
+
action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`,
|
|
233059
|
+
outcome: "Executor pre-dispatch dependency gate blocked workflow/authoritative execution.",
|
|
233060
|
+
runContext: this.getRunContextFor(liveTask.id)
|
|
233061
|
+
});
|
|
233000
233062
|
executorLog.log(`${liveTask.id}: executor dispatch blocked by unmet dependencies: ${unmetDeps.join(", ")}`);
|
|
233001
233063
|
return true;
|
|
233002
233064
|
}
|
|
@@ -258624,6 +258686,40 @@ var init_in_process_runtime = __esm({
|
|
|
258624
258686
|
throw err;
|
|
258625
258687
|
}
|
|
258626
258688
|
}
|
|
258689
|
+
/**
|
|
258690
|
+
* Close every process-local admission source without aborting work that is
|
|
258691
|
+
* already running. Development source reload uses this boundary before it
|
|
258692
|
+
* waits for the live-agent count to reach zero.
|
|
258693
|
+
*/
|
|
258694
|
+
beginDrain() {
|
|
258695
|
+
if (this.status !== "active") return;
|
|
258696
|
+
this.setStatus("paused");
|
|
258697
|
+
if (this.workflowContinuationTimer) {
|
|
258698
|
+
clearInterval(this.workflowContinuationTimer);
|
|
258699
|
+
this.workflowContinuationTimer = void 0;
|
|
258700
|
+
}
|
|
258701
|
+
const admissionStops = [
|
|
258702
|
+
["self-healing manager", () => this.selfHealingManager?.stop()],
|
|
258703
|
+
["routine scheduler", () => this.routineScheduler?.stop()],
|
|
258704
|
+
["trigger scheduler", () => this.triggerScheduler?.stop()],
|
|
258705
|
+
["stuck task detector", () => this.stuckTaskDetector?.stop()],
|
|
258706
|
+
["heartbeat monitor", () => this.heartbeatMonitor?.stop()],
|
|
258707
|
+
["triage processor", () => this.triageProcessor?.stop()],
|
|
258708
|
+
["scheduler", () => this.scheduler?.stop()],
|
|
258709
|
+
["mission autopilot", () => this.missionAutopilot?.stop()],
|
|
258710
|
+
["mission execution loop", () => this.missionExecutionLoop?.stop()]
|
|
258711
|
+
];
|
|
258712
|
+
for (const [label, stop] of admissionStops) {
|
|
258713
|
+
try {
|
|
258714
|
+
stop();
|
|
258715
|
+
} catch (error3) {
|
|
258716
|
+
runtimeLog.warn(
|
|
258717
|
+
`Failed to stop ${label} while draining ${this.config.projectId}: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
258718
|
+
);
|
|
258719
|
+
}
|
|
258720
|
+
}
|
|
258721
|
+
runtimeLog.log(`InProcessRuntime draining for ${this.config.projectId}`);
|
|
258722
|
+
}
|
|
258627
258723
|
/**
|
|
258628
258724
|
* Stop the runtime with graceful shutdown.
|
|
258629
258725
|
*
|
|
@@ -262738,6 +262834,11 @@ ${detail}`);
|
|
|
262738
262834
|
this.started = false;
|
|
262739
262835
|
runtimeLog.log(`ProjectEngine stopped for ${this.config.projectId}`);
|
|
262740
262836
|
}
|
|
262837
|
+
/** Stop new lifecycle admission while allowing active work to finish. */
|
|
262838
|
+
beginDrain() {
|
|
262839
|
+
this.shuttingDown = true;
|
|
262840
|
+
this.runtime.beginDrain();
|
|
262841
|
+
}
|
|
262741
262842
|
// ── Public accessors ──
|
|
262742
262843
|
/** Get the underlying InProcessRuntime. */
|
|
262743
262844
|
getRuntime() {
|
|
@@ -266316,14 +266417,30 @@ var init_project_engine_manager = __esm({
|
|
|
266316
266417
|
}
|
|
266317
266418
|
runtimeLog.log(`Engine startup complete: ${started} started, ${failed} failed`);
|
|
266318
266419
|
}
|
|
266319
|
-
/**
|
|
266320
|
-
|
|
266420
|
+
/** Close admission on every engine and stop reconciliation without tearing engines down. */
|
|
266421
|
+
beginDrain() {
|
|
266321
266422
|
this.stopped = true;
|
|
266322
266423
|
this.reconciliationStopped = true;
|
|
266323
266424
|
if (this.reconciliationInterval !== null) {
|
|
266324
266425
|
clearInterval(this.reconciliationInterval);
|
|
266325
266426
|
this.reconciliationInterval = null;
|
|
266326
266427
|
}
|
|
266428
|
+
for (const engine of this.engines.values()) {
|
|
266429
|
+
try {
|
|
266430
|
+
engine.beginDrain?.();
|
|
266431
|
+
} catch (error3) {
|
|
266432
|
+
runtimeLog.warn(
|
|
266433
|
+
`Engine drain error: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
266434
|
+
);
|
|
266435
|
+
}
|
|
266436
|
+
}
|
|
266437
|
+
for (const starting of this.starting.values()) {
|
|
266438
|
+
void starting.then((engine) => engine.beginDrain?.()).catch(() => void 0);
|
|
266439
|
+
}
|
|
266440
|
+
}
|
|
266441
|
+
/** Gracefully stop all engines and reconciliation. */
|
|
266442
|
+
async stopAll() {
|
|
266443
|
+
this.beginDrain();
|
|
266327
266444
|
try {
|
|
266328
266445
|
this.centralCore.stopDiscovery();
|
|
266329
266446
|
await this.centralCore.markLocalNodeOffline();
|
|
@@ -292512,7 +292629,7 @@ function buildDuplicateQuery(title2, description) {
|
|
|
292512
292629
|
const selected = deduped.sort((left, right) => right.length - left.length).slice(0, 5);
|
|
292513
292630
|
return selected.join(" ");
|
|
292514
292631
|
}
|
|
292515
|
-
async function computeDuplicateMatches(scopedStore, input) {
|
|
292632
|
+
async function computeDuplicateMatches(scopedStore, input, classifyBlocker) {
|
|
292516
292633
|
const query = buildDuplicateQuery(input.title, input.description);
|
|
292517
292634
|
if (query.length === 0) {
|
|
292518
292635
|
return [];
|
|
@@ -292522,7 +292639,12 @@ async function computeDuplicateMatches(scopedStore, input) {
|
|
|
292522
292639
|
includeArchived: false,
|
|
292523
292640
|
limit: 20
|
|
292524
292641
|
});
|
|
292525
|
-
const
|
|
292642
|
+
const eligibility = await Promise.all(results.map(async (task) => ({
|
|
292643
|
+
task,
|
|
292644
|
+
blocker: await classifyBlocker(task)
|
|
292645
|
+
})));
|
|
292646
|
+
const eligibleResults = eligibility.filter(({ blocker }) => blocker).map(({ task }) => task);
|
|
292647
|
+
const candidates = eligibleResults.map((task) => ({
|
|
292526
292648
|
id: task.id,
|
|
292527
292649
|
title: task.title ?? "",
|
|
292528
292650
|
description: task.description ?? "",
|
|
@@ -292540,6 +292662,10 @@ async function computeDuplicateMatches(scopedStore, input) {
|
|
|
292540
292662
|
}
|
|
292541
292663
|
);
|
|
292542
292664
|
}
|
|
292665
|
+
async function isDuplicateBlocker(store, canonical) {
|
|
292666
|
+
const flags = await resolveNearDuplicateCanonicalFlags(store, canonical);
|
|
292667
|
+
return !isNearDuplicateCanonicalInactive(canonical, flags);
|
|
292668
|
+
}
|
|
292543
292669
|
function buildReviewerAgentItemId(input) {
|
|
292544
292670
|
const stepPart = input.step ? `step-${input.step}` : "step-na";
|
|
292545
292671
|
const verdictPart = (input.verdict ?? "unknown").toLowerCase();
|
|
@@ -292951,7 +293077,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
292951
293077
|
description: description.trim(),
|
|
292952
293078
|
limit,
|
|
292953
293079
|
threshold
|
|
292954
|
-
});
|
|
293080
|
+
}, (canonical) => isDuplicateBlocker(scopedStore, canonical));
|
|
292955
293081
|
res.json({ matches });
|
|
292956
293082
|
return;
|
|
292957
293083
|
} catch (err) {
|
|
@@ -293071,6 +293197,15 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
293071
293197
|
}
|
|
293072
293198
|
} : void 0;
|
|
293073
293199
|
const normalizedSource = source && typeof source === "object" && "sourceType" in source && typeof source.sourceType === "string" ? source : { sourceType: "api" };
|
|
293200
|
+
const duplicateBlockerVerdicts = /* @__PURE__ */ new Map();
|
|
293201
|
+
const classifyDuplicateBlocker = (canonical) => {
|
|
293202
|
+
const key = `${canonical.id}:${canonical.column}:${canonical.deletedAt ?? ""}`;
|
|
293203
|
+
const existing = duplicateBlockerVerdicts.get(key);
|
|
293204
|
+
if (existing) return existing;
|
|
293205
|
+
const verdict = isDuplicateBlocker(scopedStore, canonical);
|
|
293206
|
+
duplicateBlockerVerdicts.set(key, verdict);
|
|
293207
|
+
return verdict;
|
|
293208
|
+
};
|
|
293074
293209
|
const requestedBranchMode = getBranchSelectionMode(branchSelection);
|
|
293075
293210
|
const { branch: normalizedBranch, baseBranch: normalizedBaseBranch, sharedFeatureBranch } = resolveBranchSelection(branchSelection, branch, baseBranch);
|
|
293076
293211
|
let validatedGithubTracking;
|
|
@@ -293156,7 +293291,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
293156
293291
|
contentFingerprint = deterministicGuard.fingerprint;
|
|
293157
293292
|
let matchesAfterAckFilter = [];
|
|
293158
293293
|
try {
|
|
293159
|
-
if (deterministicGuard.action === "duplicate" && deterministicGuard.existing) {
|
|
293294
|
+
if (deterministicGuard.action === "duplicate" && deterministicGuard.existing && await classifyDuplicateBlocker(deterministicGuard.existing)) {
|
|
293160
293295
|
throw conflict("duplicate_candidates", {
|
|
293161
293296
|
matches: [{
|
|
293162
293297
|
id: deterministicGuard.existing.id,
|
|
@@ -293171,7 +293306,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
293171
293306
|
const duplicateMatches = bypassDuplicateCheck === true ? [] : await computeDuplicateMatches(scopedStore, {
|
|
293172
293307
|
title: normalizedTitle,
|
|
293173
293308
|
description: normalizedDescription
|
|
293174
|
-
});
|
|
293309
|
+
}, classifyDuplicateBlocker);
|
|
293175
293310
|
matchesAfterAckFilter = duplicateMatches.filter((match) => !acknowledgedDuplicateIds.includes(match.id));
|
|
293176
293311
|
if (matchesAfterAckFilter.length > 0) {
|
|
293177
293312
|
throw conflict("duplicate_candidates", { matches: matchesAfterAckFilter });
|
|
@@ -293197,11 +293332,17 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
293197
293332
|
const fullRows = await scopedStore.listTasks({ slim: false, includeArchived: false });
|
|
293198
293333
|
const byId = new Map(fullRows.map((row) => [row.id, row]));
|
|
293199
293334
|
const candidateMap = /* @__PURE__ */ new Map();
|
|
293200
|
-
|
|
293335
|
+
const classifiedRows = await Promise.all(candidateRows.map(async (row) => {
|
|
293336
|
+
const full = byId.get(row.id);
|
|
293337
|
+
return { row, full, blocker: full ? await classifyDuplicateBlocker(full) : true };
|
|
293338
|
+
}));
|
|
293339
|
+
for (const { row, full, blocker } of classifiedRows) {
|
|
293201
293340
|
if (acknowledgedDuplicateIds.includes(row.id)) {
|
|
293202
293341
|
continue;
|
|
293203
293342
|
}
|
|
293204
|
-
|
|
293343
|
+
if (!blocker) {
|
|
293344
|
+
continue;
|
|
293345
|
+
}
|
|
293205
293346
|
candidateMap.set(row.id, {
|
|
293206
293347
|
id: row.id,
|
|
293207
293348
|
title: row.title ?? "",
|
|
@@ -293249,7 +293390,7 @@ ${normalizedDescription}`;
|
|
|
293249
293390
|
const explicitMarkerBypassed = bypassDuplicateCheck === true || (explicitDuplicateMarker ? acknowledgedDuplicateIds.includes(explicitDuplicateMarker.canonicalId) : false);
|
|
293250
293391
|
if (explicitDuplicateMarker && !explicitMarkerBypassed) {
|
|
293251
293392
|
const canonical = await scopedStore.getTask(explicitDuplicateMarker.canonicalId).catch(() => null);
|
|
293252
|
-
if (canonical && !canonical.deletedAt) {
|
|
293393
|
+
if (canonical && !canonical.deletedAt && await classifyDuplicateBlocker(canonical)) {
|
|
293253
293394
|
try {
|
|
293254
293395
|
await scopedStore.recordActivity({
|
|
293255
293396
|
type: "task:auto-archived-duplicate",
|
|
@@ -293356,7 +293497,8 @@ ${normalizedDescription}`;
|
|
|
293356
293497
|
createdTask: taskWithBranchContext,
|
|
293357
293498
|
fingerprint: bypassDuplicateCheck === true ? null : contentFingerprint,
|
|
293358
293499
|
windowMs: 6e4,
|
|
293359
|
-
logger: runtimeLogger
|
|
293500
|
+
logger: runtimeLogger,
|
|
293501
|
+
onDuplicate: async (canonical) => await classifyDuplicateBlocker(canonical) ? "archive-created" : "keep-created"
|
|
293360
293502
|
});
|
|
293361
293503
|
if (deterministicReconcile.outcome === "archived") {
|
|
293362
293504
|
res.status(200).json(deterministicReconcile.canonical);
|
|
@@ -396597,6 +396739,96 @@ var init_startup_phase = __esm({
|
|
|
396597
396739
|
}
|
|
396598
396740
|
});
|
|
396599
396741
|
|
|
396742
|
+
// src/commands/dev-source-restart.ts
|
|
396743
|
+
function isDevSourceChangeMessage(message) {
|
|
396744
|
+
return Boolean(message) && typeof message === "object" && message.type === DEV_SOURCE_CHANGE_MESSAGE;
|
|
396745
|
+
}
|
|
396746
|
+
function registerDevSourceRestart({
|
|
396747
|
+
enabled,
|
|
396748
|
+
processEvents = process,
|
|
396749
|
+
notifyArmed,
|
|
396750
|
+
beginDrain,
|
|
396751
|
+
getLiveRunningAgentCounts,
|
|
396752
|
+
requestRestart,
|
|
396753
|
+
logger: logger3 = console,
|
|
396754
|
+
recheckIntervalMs = 15e3
|
|
396755
|
+
}) {
|
|
396756
|
+
if (!enabled) return () => void 0;
|
|
396757
|
+
let disposed = false;
|
|
396758
|
+
let pending = false;
|
|
396759
|
+
let checking = false;
|
|
396760
|
+
let restartAccepted = false;
|
|
396761
|
+
let deferredForActiveWork = false;
|
|
396762
|
+
let drainStarted = false;
|
|
396763
|
+
let timer2;
|
|
396764
|
+
const schedule = (delayMs) => {
|
|
396765
|
+
if (disposed || timer2) return;
|
|
396766
|
+
timer2 = setTimeout(() => {
|
|
396767
|
+
timer2 = void 0;
|
|
396768
|
+
void checkRestartSafety();
|
|
396769
|
+
}, delayMs);
|
|
396770
|
+
timer2.unref?.();
|
|
396771
|
+
};
|
|
396772
|
+
const checkRestartSafety = async () => {
|
|
396773
|
+
if (disposed || restartAccepted || !pending || checking) return;
|
|
396774
|
+
checking = true;
|
|
396775
|
+
try {
|
|
396776
|
+
const { currentlyActive } = await getLiveRunningAgentCounts();
|
|
396777
|
+
if (currentlyActive > 0) {
|
|
396778
|
+
if (!deferredForActiveWork) {
|
|
396779
|
+
const noun = currentlyActive === 1 ? "agent" : "agents";
|
|
396780
|
+
logger3.log(`[fusion:dev] source restart deferred until ${currentlyActive} active ${noun} reach a safe boundary`);
|
|
396781
|
+
deferredForActiveWork = true;
|
|
396782
|
+
}
|
|
396783
|
+
schedule(recheckIntervalMs);
|
|
396784
|
+
return;
|
|
396785
|
+
}
|
|
396786
|
+
if (deferredForActiveWork) {
|
|
396787
|
+
logger3.log("[fusion:dev] active work drained \u2014 applying pending source restart");
|
|
396788
|
+
deferredForActiveWork = false;
|
|
396789
|
+
}
|
|
396790
|
+
restartAccepted = requestRestart("dev-source-change");
|
|
396791
|
+
if (restartAccepted) {
|
|
396792
|
+
pending = false;
|
|
396793
|
+
} else {
|
|
396794
|
+
logger3.warn("[fusion:dev] source restart request was declined by the host lifecycle");
|
|
396795
|
+
schedule(recheckIntervalMs);
|
|
396796
|
+
}
|
|
396797
|
+
} catch (error3) {
|
|
396798
|
+
logger3.warn(`[fusion:dev] could not verify restart safety: ${error3 instanceof Error ? error3.message : String(error3)}`);
|
|
396799
|
+
schedule(recheckIntervalMs);
|
|
396800
|
+
} finally {
|
|
396801
|
+
checking = false;
|
|
396802
|
+
}
|
|
396803
|
+
};
|
|
396804
|
+
const onMessage = (message) => {
|
|
396805
|
+
if (restartAccepted || !isDevSourceChangeMessage(message)) return;
|
|
396806
|
+
pending = true;
|
|
396807
|
+
if (drainStarted) return;
|
|
396808
|
+
drainStarted = true;
|
|
396809
|
+
void Promise.resolve().then(beginDrain).then(() => schedule(0)).catch((error3) => {
|
|
396810
|
+
logger3.warn(`[fusion:dev] could not begin source restart drain: ${error3 instanceof Error ? error3.message : String(error3)}`);
|
|
396811
|
+
});
|
|
396812
|
+
};
|
|
396813
|
+
processEvents.on("message", onMessage);
|
|
396814
|
+
notifyArmed?.();
|
|
396815
|
+
return () => {
|
|
396816
|
+
disposed = true;
|
|
396817
|
+
pending = false;
|
|
396818
|
+
if (timer2) clearTimeout(timer2);
|
|
396819
|
+
timer2 = void 0;
|
|
396820
|
+
processEvents.off("message", onMessage);
|
|
396821
|
+
};
|
|
396822
|
+
}
|
|
396823
|
+
var DEV_SOURCE_CHANGE_MESSAGE, DEV_SOURCE_RESTART_ARMED_MESSAGE;
|
|
396824
|
+
var init_dev_source_restart = __esm({
|
|
396825
|
+
"src/commands/dev-source-restart.ts"() {
|
|
396826
|
+
"use strict";
|
|
396827
|
+
DEV_SOURCE_CHANGE_MESSAGE = "fusion:dev-source-changed";
|
|
396828
|
+
DEV_SOURCE_RESTART_ARMED_MESSAGE = "fusion:dev-source-restart-armed";
|
|
396829
|
+
}
|
|
396830
|
+
});
|
|
396831
|
+
|
|
396600
396832
|
// src/commands/dashboard.ts
|
|
396601
396833
|
var dashboard_exports = {};
|
|
396602
396834
|
__export(dashboard_exports, {
|
|
@@ -397342,6 +397574,21 @@ async function runDashboard(port, opts = {}) {
|
|
|
397342
397574
|
getRecent: (limit) => logSink.getRecentEntries(limit),
|
|
397343
397575
|
subscribe: (listener) => logSink.subscribeEntries(listener)
|
|
397344
397576
|
};
|
|
397577
|
+
const bindDevSourceRestart = (centralCore, beginDrain) => {
|
|
397578
|
+
disposeCallbacks.push(registerDevSourceRestart({
|
|
397579
|
+
enabled: process.env.FUSION_DEV_WATCH === "1" && systemControlForServer.supervised && Boolean(systemControlForServer.sourceWorkspaceRoot),
|
|
397580
|
+
beginDrain,
|
|
397581
|
+
notifyArmed: () => {
|
|
397582
|
+
process.send?.({ type: DEV_SOURCE_RESTART_ARMED_MESSAGE });
|
|
397583
|
+
},
|
|
397584
|
+
getLiveRunningAgentCounts: () => centralCore.getLiveRunningAgentCounts(),
|
|
397585
|
+
requestRestart: (reason) => requestSelfRestart?.(reason) ?? false,
|
|
397586
|
+
logger: {
|
|
397587
|
+
log: (message) => logSink.log(message, "dashboard"),
|
|
397588
|
+
warn: (message) => logSink.warn(message, "dashboard")
|
|
397589
|
+
}
|
|
397590
|
+
}));
|
|
397591
|
+
};
|
|
397345
397592
|
const SHUTDOWN_HARD_EXIT_GRACE_MS = 3e3;
|
|
397346
397593
|
const SHUTDOWN_STEP_SLOW_MS = 1e3;
|
|
397347
397594
|
let currentShutdownStep = null;
|
|
@@ -398079,6 +398326,7 @@ async function runDashboard(port, opts = {}) {
|
|
|
398079
398326
|
}, 300);
|
|
398080
398327
|
return true;
|
|
398081
398328
|
};
|
|
398329
|
+
bindDevSourceRestart(centralCoreForEngine, () => engineManager.beginDrain());
|
|
398082
398330
|
registerHandler(process, "SIGINT", () => void shutdown("SIGINT"));
|
|
398083
398331
|
registerHandler(process, "SIGTERM", () => void shutdown("SIGTERM"));
|
|
398084
398332
|
registerHandler(process, "SIGHUP", () => {
|
|
@@ -398339,6 +398587,12 @@ async function runDashboard(port, opts = {}) {
|
|
|
398339
398587
|
}, 300);
|
|
398340
398588
|
return true;
|
|
398341
398589
|
};
|
|
398590
|
+
if (centralCoreForMesh) {
|
|
398591
|
+
bindDevSourceRestart(centralCoreForMesh, () => {
|
|
398592
|
+
triggerScheduler?.stop();
|
|
398593
|
+
heartbeatMonitorImpl?.stop();
|
|
398594
|
+
});
|
|
398595
|
+
}
|
|
398342
398596
|
registerHandler(process, "SIGINT", () => void devShutdown("SIGINT"));
|
|
398343
398597
|
registerHandler(process, "SIGTERM", () => void devShutdown("SIGTERM"));
|
|
398344
398598
|
registerHandler(process, "SIGHUP", () => {
|
|
@@ -399113,6 +399367,7 @@ var init_dashboard = __esm({
|
|
|
399113
399367
|
init_dashboard_tui();
|
|
399114
399368
|
init_dashboard_startup_chain();
|
|
399115
399369
|
init_startup_phase();
|
|
399370
|
+
init_dev_source_restart();
|
|
399116
399371
|
processDiagnosticsRegistered = false;
|
|
399117
399372
|
diagnosticIntervalHandle = null;
|
|
399118
399373
|
DIAGNOSTIC_INTERVAL_MS = 30 * 60 * 1e3;
|