@runfusion/fusion 0.73.0-beta.6 → 0.74.0-beta.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.
Files changed (106) hide show
  1. package/dist/bin.js +524 -106
  2. package/dist/child-process-worker.js +252 -29
  3. package/dist/client/.vite/manifest.json +263 -263
  4. package/dist/client/assets/{AgentDetailView-jg-430Wv.js → AgentDetailView-CUXS0Odh.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-0iybC0Dg.js → AgentPermissionPolicyEditor-CzjHjB5r.js} +1 -1
  6. package/dist/client/assets/{AgentsView-DeVI7IJy.js → AgentsView-B5stgI5m.js} +3 -3
  7. package/dist/client/assets/ChatView-CpoOgZ4X.js +13 -0
  8. package/dist/client/assets/{CommandCenter-CPpYSmxm.js → CommandCenter-BdEQ_ZOX.js} +1 -1
  9. package/dist/client/assets/{DevServerView-C7Uib0ck.js → DevServerView-BHgAmF3y.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-Ba9x3E72.js → DirectoryPicker-BQXVc5YW.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-DkuxxEAC.js → DocumentsView-CxN0Ptrj.js} +1 -1
  12. package/dist/client/assets/{EvalsView-DwRsXihY.js → EvalsView-DAgJtpO5.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-C2g8TiAY.js → ExperimentalAgentOnboardingModal-NZcXkGrN.js} +1 -1
  14. package/dist/client/assets/{GoalsView-hQSajINq.js → GoalsView-Dwq7NW4j.js} +1 -1
  15. package/dist/client/assets/{InsightsView-p7j5T5a8.js → InsightsView-CUlL5NSC.js} +1 -1
  16. package/dist/client/assets/{MemoryView-DoVBk38U.js → MemoryView-WfHzJflp.js} +1 -1
  17. package/dist/client/assets/{PiExtensionsManager-DzT94cPg.js → PiExtensionsManager-BS1dkauB.js} +1 -1
  18. package/dist/client/assets/{PluginManager-5DK-HUkf.js → PluginManager-B1oLb0pn.js} +1 -1
  19. package/dist/client/assets/{PullRequestView-c8_Nl91A.js → PullRequestView-Cdp662lx.js} +1 -1
  20. package/dist/client/assets/{ReportModal-BfNu73zb.js → ReportModal-C92wQO-k.js} +1 -1
  21. package/dist/client/assets/{ResearchView-Cc_DZq5v.js → ResearchView-4-SoxmQv.js} +1 -1
  22. package/dist/client/assets/{SecretsView-DMETxbnI.js → SecretsView-C5UQ1Mms.js} +1 -1
  23. package/dist/client/assets/SessionTerminal-BTjX9p3a.js +2 -0
  24. package/dist/client/assets/SettingsModal-BcnOOo9g.js +21 -0
  25. package/dist/client/assets/{SettingsModal-oTMDyzjF.js → SettingsModal-BfHAE14s.js} +1 -1
  26. package/dist/client/assets/{SettingsTextareaRow-B63cJNsI.js → SettingsTextareaRow-C2JqzQdD.js} +1 -1
  27. package/dist/client/assets/{SetupWizardModal-BZnyRPT7.js → SetupWizardModal-C1KmMVfU.js} +2 -2
  28. package/dist/client/assets/{SkillsView-DGwWIOqX.js → SkillsView-DyLNdfDy.js} +1 -1
  29. package/dist/client/assets/{TodoView-BoLH6qHP.js → TodoView-DyrniX19.js} +1 -1
  30. package/dist/client/assets/{WorkflowNodeEditor-DHqpPcSv.js → WorkflowNodeEditor-Bh-Hn1Kx.js} +1 -1
  31. package/dist/client/assets/{agent-import-generation-Dl3UAfFr.js → agent-import-generation-C5pyefIj.js} +1 -1
  32. package/dist/client/assets/{app-B4dURBVF.js → app-Bl764LdY.js} +1 -1
  33. package/dist/client/assets/{app-DxbSLeuH.js → app-DNrauoYS.js} +1 -1
  34. package/dist/client/assets/{app-DfQxLcCr.js → app-DdCnRHSe.js} +1 -1
  35. package/dist/client/assets/{app-R0cG-sq5.js → app-QmIotwFZ.js} +1 -1
  36. package/dist/client/assets/{app-DLG7AA6O.js → app-YM47G7Ks.js} +1 -1
  37. package/dist/client/assets/{app-JOLkwq4O.js → app-oUEtzFOj.js} +1 -1
  38. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DRSMeXsR.js → architectureDiagram-3BPJPVTR-B6axGlaS.js} +1 -1
  39. package/dist/client/assets/{blockDiagram-GPEHLZMM-xBxQf_Gy.js → blockDiagram-GPEHLZMM-BENjJaZD.js} +1 -1
  40. package/dist/client/assets/{c4Diagram-AAUBKEIU-CememwlP.js → c4Diagram-AAUBKEIU-Cl2qKhuO.js} +1 -1
  41. package/dist/client/assets/channel-CktWdAhK.js +1 -0
  42. package/dist/client/assets/{chunk-2J33WTMH-B62t8Ow1.js → chunk-2J33WTMH-BWo7R8T7.js} +1 -1
  43. package/dist/client/assets/{chunk-4BX2VUAB-CHzxnH6A.js → chunk-4BX2VUAB-DiRoI7pq.js} +1 -1
  44. package/dist/client/assets/{chunk-55IACEB6-BXJotoWf.js → chunk-55IACEB6-DPtv1GVO.js} +1 -1
  45. package/dist/client/assets/{chunk-727SXJPM-DGFhyp03.js → chunk-727SXJPM-BVcm5ITs.js} +1 -1
  46. package/dist/client/assets/{chunk-AQP2D5EJ-C-nRZZBE.js → chunk-AQP2D5EJ-CFT4bmXK.js} +1 -1
  47. package/dist/client/assets/{chunk-FMBD7UC4-CFS2iW51.js → chunk-FMBD7UC4-BDcEHQ0o.js} +1 -1
  48. package/dist/client/assets/{chunk-ND2GUHAM-DFleK3tP.js → chunk-ND2GUHAM-CYlDBFdI.js} +1 -1
  49. package/dist/client/assets/{chunk-QZHKN3VN-E7rqTspj.js → chunk-QZHKN3VN-3Wv3WxYt.js} +1 -1
  50. package/dist/client/assets/classDiagram-4FO5ZUOK-CrUS4gHO.js +1 -0
  51. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CrUS4gHO.js +1 -0
  52. package/dist/client/assets/{cose-bilkent-S5V4N54A-VYcUIreA.js → cose-bilkent-S5V4N54A-BuMkKBMC.js} +1 -1
  53. package/dist/client/assets/{dagre-BM42HDAG-B4VeH0Nz.js → dagre-BM42HDAG-D1P4z_OM.js} +1 -1
  54. package/dist/client/assets/{dashboard-view-DoNpCbHQ.js → dashboard-view-BgQBLynI.js} +1 -1
  55. package/dist/client/assets/{dashboard-view-BpeWPcmh.js → dashboard-view-CcK2sH0v.js} +1 -1
  56. package/dist/client/assets/{dashboard-view-DGcOsOey.js → dashboard-view-DaRKU5oU.js} +1 -1
  57. package/dist/client/assets/{diagram-2AECGRRQ-BM8FU7fY.js → diagram-2AECGRRQ-hmY-reWF.js} +1 -1
  58. package/dist/client/assets/{diagram-5GNKFQAL-Do8IJG5T.js → diagram-5GNKFQAL-CGfRqm_0.js} +1 -1
  59. package/dist/client/assets/{diagram-KO2AKTUF-C_-zygdh.js → diagram-KO2AKTUF-BfKo4Jd4.js} +1 -1
  60. package/dist/client/assets/{diagram-LMA3HP47-CSPiBke9.js → diagram-LMA3HP47-BZqpNemd.js} +1 -1
  61. package/dist/client/assets/{diagram-OG6HWLK6-BVuoiZg-.js → diagram-OG6HWLK6-CxLTV4DH.js} +1 -1
  62. package/dist/client/assets/{erDiagram-TEJ5UH35-CIY48OYH.js → erDiagram-TEJ5UH35-vw3tx2tK.js} +1 -1
  63. package/dist/client/assets/{flowDiagram-I6XJVG4X-B6zav8SR.js → flowDiagram-I6XJVG4X-OEm-Us2U.js} +1 -1
  64. package/dist/client/assets/{folder-open-Dc23V8n6.js → folder-open-D_zgZHWi.js} +1 -1
  65. package/dist/client/assets/{ganttDiagram-6RSMTGT7-_F49rUcG.js → ganttDiagram-6RSMTGT7-C5bc1DG1.js} +1 -1
  66. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-Fk-V-buo.js → gitGraphDiagram-PVQCEYII-DEvcWRBq.js} +1 -1
  67. package/dist/client/assets/index-BinRUCFV.js +2661 -0
  68. package/dist/client/assets/index-DGmuyTtv.css +1 -0
  69. package/dist/client/assets/{infoDiagram-5YYISTIA-DvnBGHv4.js → infoDiagram-5YYISTIA-kFOE9wxd.js} +1 -1
  70. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-Df7TrITT.js → ishikawaDiagram-YF4QCWOH-DvLbzHmR.js} +1 -1
  71. package/dist/client/assets/{journeyDiagram-JHISSGLW-CieUO8Cz.js → journeyDiagram-JHISSGLW-ov6Tbcuz.js} +1 -1
  72. package/dist/client/assets/{kanban-definition-UN3LZRKU-BBR_Cj7I.js → kanban-definition-UN3LZRKU-Dr0foW2M.js} +1 -1
  73. package/dist/client/assets/{mermaid.core-whEVIkYk.js → mermaid.core-CM55309V.js} +4 -4
  74. package/dist/client/assets/{mindmap-definition-RKZ34NQL-utsoG884.js → mindmap-definition-RKZ34NQL-xNyB6Z6o.js} +1 -1
  75. package/dist/client/assets/{pieDiagram-4H26LBE5-B6_-ma_S.js → pieDiagram-4H26LBE5-DlS5Kpga.js} +1 -1
  76. package/dist/client/assets/{puzzle-CY7o6u4N.js → puzzle-BrEvi5DH.js} +1 -1
  77. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CCAEPNEU.js → quadrantDiagram-W4KKPZXB-D4z2CHAA.js} +1 -1
  78. package/dist/client/assets/{requirementDiagram-4Y6WPE33-C0rKKe1e.js → requirementDiagram-4Y6WPE33-Bcwt6-p9.js} +1 -1
  79. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DpeRsnkc.js → sankeyDiagram-5OEKKPKP-C54N76yk.js} +1 -1
  80. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DWYg86A1.js → sequenceDiagram-3UESZ5HK-DtqryztE.js} +1 -1
  81. package/dist/client/assets/{shield-alert-j_jHfoeI.js → shield-alert-DhxKVKek.js} +1 -1
  82. package/dist/client/assets/{standing-instructions-template-CCI99V8d.js → standing-instructions-template-CdHJwmLI.js} +1 -1
  83. package/dist/client/assets/{stateDiagram-AJRCARHV-CalOQ-6v.js → stateDiagram-AJRCARHV-DQAvSrHw.js} +1 -1
  84. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-Dd8RLebl.js → stateDiagram-v2-BHNVJYJU-BzIGKq7y.js} +1 -1
  85. package/dist/client/assets/{timeline-definition-PNZ67QCA-D1PZw-aB.js → timeline-definition-PNZ67QCA-GDj_48Um.js} +1 -1
  86. package/dist/client/assets/{upload-CFL3ZlNN.js → upload-B7GKZiF1.js} +1 -1
  87. package/dist/client/assets/{users-DGOPLxzy.js → users-CBtvFVLM.js} +1 -1
  88. package/dist/client/assets/{vennDiagram-CIIHVFJN-DKQAihMR.js → vennDiagram-CIIHVFJN-PQDmLFbj.js} +1 -1
  89. package/dist/client/assets/{wardley-L42UT6IY-B_XzYoGe.js → wardley-L42UT6IY-GmGtXctI.js} +1 -1
  90. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DBu7IvQK.js → wardleyDiagram-YWT4CUSO-Bv6slmsW.js} +1 -1
  91. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CKrqMXvh.js → xychartDiagram-2RQKCTM6-CK1RJHwB.js} +1 -1
  92. package/dist/client/index.html +2 -2
  93. package/dist/client/version.json +1 -1
  94. package/dist/extension.js +349 -60
  95. package/dist/migrations/0036_chat_session_tags.sql +64 -0
  96. package/dist/plugin-sdk/index.js +27 -1
  97. package/dist/plugins/fusion-plugin-hermes-runtime/.bundled.reload-1.js +8835 -0
  98. package/package.json +3 -3
  99. package/dist/client/assets/ChatView-BWosYwf6.js +0 -8
  100. package/dist/client/assets/SessionTerminal-B4c2w5zb.js +0 -2
  101. package/dist/client/assets/SettingsModal-vpkxpahA.js +0 -21
  102. package/dist/client/assets/channel-DwxxVJWv.js +0 -1
  103. package/dist/client/assets/classDiagram-4FO5ZUOK-CU_MxE3u.js +0 -1
  104. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CU_MxE3u.js +0 -1
  105. package/dist/client/assets/index-C77ORgtm.css +0 -1
  106. package/dist/client/assets/index-D8TQBkB7.js +0 -2661
package/dist/bin.js CHANGED
@@ -707,7 +707,7 @@ var init_merge_queue = __esm({
707
707
  function isLocale(value) {
708
708
  return typeof value === "string" && SUPPORTED_LOCALES.includes(value);
709
709
  }
710
- var HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, PLANNER_OVERSIGHT_LEVELS, DEFAULT_PLANNER_OVERSIGHT_LEVEL, REVIEW_ARTIFACTS_MODES, THEME_MODES, COLOR_THEMES, SUPPORTED_LOCALES, DEFAULT_LOCALE;
710
+ var HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, PLANNER_OVERSIGHT_LEVELS, DEFAULT_PLANNER_OVERSIGHT_LEVEL, REVIEW_ARTIFACTS_MODES, ANTHROPIC_AUTH_PREFERENCES, THEME_MODES, COLOR_THEMES, SUPPORTED_LOCALES, DEFAULT_LOCALE;
711
711
  var init_execution_and_ui = __esm({
712
712
  "../core/src/types/execution-and-ui.ts"() {
713
713
  "use strict";
@@ -718,6 +718,7 @@ var init_execution_and_ui = __esm({
718
718
  PLANNER_OVERSIGHT_LEVELS = ["off", "observe", "steer", "autonomous"];
719
719
  DEFAULT_PLANNER_OVERSIGHT_LEVEL = "autonomous";
720
720
  REVIEW_ARTIFACTS_MODES = ["off", "user-facing", "on"];
721
+ ANTHROPIC_AUTH_PREFERENCES = ["api-key", "subscription"];
721
722
  THEME_MODES = ["dark", "light", "system"];
722
723
  COLOR_THEMES = [
723
724
  "default",
@@ -1531,6 +1532,12 @@ var init_settings_schema = __esm({
1531
1532
  FNXC:DashboardTheming 2026-07-03-00:00:
1532
1533
  Fresh installs must follow the operating system theme until the user explicitly chooses Light, Dark, or System. Keep this global default aligned with dashboard and desktop pre-hydration fallbacks.
1533
1534
  */
1535
+ /*
1536
+ FNXC:ProviderAuth 2026-07-24-17:05:
1537
+ Default to the historical precedence (raw API key beats subscription OAuth, FN-7391/FN-7396)
1538
+ so upgrading never silently moves an operator's traffic from their key onto their subscription.
1539
+ */
1540
+ anthropicAuthPreference: "api-key",
1534
1541
  themeMode: "system",
1535
1542
  /*
1536
1543
  FNXC:DashboardTheming 2026-06-30-00:00:
@@ -12799,7 +12806,9 @@ __export(project_exports, {
12799
12806
  chatRoomMembers: () => chatRoomMembers,
12800
12807
  chatRoomMessages: () => chatRoomMessages,
12801
12808
  chatRooms: () => chatRooms,
12809
+ chatSessionTags: () => chatSessionTags,
12802
12810
  chatSessions: () => chatSessions,
12811
+ chatTags: () => chatTags,
12803
12812
  chatTokenUsage: () => chatTokenUsage,
12804
12813
  cliSessions: () => cliSessions,
12805
12814
  completionHandoffMarkers: () => completionHandoffMarkers,
@@ -12876,7 +12885,7 @@ __export(project_exports, {
12876
12885
  workflowWorkItems: () => workflowWorkItems,
12877
12886
  workflows: () => workflows
12878
12887
  });
12879
- var projectSchema, tasks, config, legacyBoards, legacyProjectAuthUsers, legacyProjectAuthMemberships, legacyProjectAuthProviders, legacyProjectAuthSessions, legacyTaskReviewerRuns, distributedTaskIdState, distributedTaskIdReservations, symbolLocks, workflowSteps, workflows, taskWorkflowSelection, taskVerificationRequests, activityLog, archivedTasks, taskCommitAssociations, automations, agents, agentHeartbeats, agentRuns, agentTaskSessions, agentApiKeys, agentConfigRevisions, agentBlockedStates, mergeQueue, mergeRequests, completionHandoffMarkers, workflowWorkItems, workflowRunBranches, workflowRunStepInstances, workflowSettings, workflowPromptOverrides, configurationRevisions, taskDocuments, artifacts, taskDocumentRevisions, researchRuns, researchExports, researchRunEvents, experimentSessions, experimentSessionRecords, evalRuns, evalTaskResults, evalRunEvents, secrets, projectMeta, missions, branchGroups, pullRequests, pullRequestThreadState, goals, missionGoals, goalCitations, milestones, slices, missionFeatures, ideationSessions, ideationCandidates, missionEvents, plugins, routines, projectInsights, projectInsightRuns, projectInsightRunEvents, todoLists, todoItems, usageEvents, pluginActivations, knowledgePages, deployments, incidents, aiSessions, messages, agentRatings, chatSessions, cliSessions, chatMessages, chatTokenUsage, runAuditEvents, missionContractAssertions, missionFeatureAssertions, missionValidatorRuns, missionValidatorFailures, missionFixFeatureLineage, missionLineageStops, verificationCache, importTranslationCache, approvalRequests, approvalRequestAuditEvents, chatRooms, chatRoomMembers, chatRoomMessages, projectTableNames;
12888
+ var projectSchema, tasks, config, legacyBoards, legacyProjectAuthUsers, legacyProjectAuthMemberships, legacyProjectAuthProviders, legacyProjectAuthSessions, legacyTaskReviewerRuns, distributedTaskIdState, distributedTaskIdReservations, symbolLocks, workflowSteps, workflows, taskWorkflowSelection, taskVerificationRequests, activityLog, archivedTasks, taskCommitAssociations, automations, agents, agentHeartbeats, agentRuns, agentTaskSessions, agentApiKeys, agentConfigRevisions, agentBlockedStates, mergeQueue, mergeRequests, completionHandoffMarkers, workflowWorkItems, workflowRunBranches, workflowRunStepInstances, workflowSettings, workflowPromptOverrides, configurationRevisions, taskDocuments, artifacts, taskDocumentRevisions, researchRuns, researchExports, researchRunEvents, experimentSessions, experimentSessionRecords, evalRuns, evalTaskResults, evalRunEvents, secrets, projectMeta, missions, branchGroups, pullRequests, pullRequestThreadState, goals, missionGoals, goalCitations, milestones, slices, missionFeatures, ideationSessions, ideationCandidates, missionEvents, plugins, routines, projectInsights, projectInsightRuns, projectInsightRunEvents, todoLists, todoItems, usageEvents, pluginActivations, knowledgePages, deployments, incidents, aiSessions, messages, agentRatings, chatSessions, chatTags, chatSessionTags, cliSessions, chatMessages, chatTokenUsage, runAuditEvents, missionContractAssertions, missionFeatureAssertions, missionValidatorRuns, missionValidatorFailures, missionFixFeatureLineage, missionLineageStops, verificationCache, importTranslationCache, approvalRequests, approvalRequestAuditEvents, chatRooms, chatRoomMembers, chatRoomMessages, projectTableNames;
12880
12889
  var init_project = __esm({
12881
12890
  "../core/src/postgres/schema/project.ts"() {
12882
12891
  "use strict";
@@ -14587,7 +14596,33 @@ var init_project = __esm({
14587
14596
  cliExecutorAdapterId: text("cli_executor_adapter_id")
14588
14597
  }, (t) => [
14589
14598
  index("idxChatSessionsAgentId").on(t.agentId),
14590
- index("idxChatSessionsProjectId").on(t.projectId)
14599
+ index("idxChatSessionsProjectId").on(t.projectId),
14600
+ uniqueIndex("uqChatSessionsProjectIdId").on(t.projectId, t.id)
14601
+ ]);
14602
+ chatTags = projectSchema.table("chat_tags", {
14603
+ id: text("id").notNull(),
14604
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
14605
+ ownerProjectId: text("owner_project_id").notNull(),
14606
+ name: text("name").notNull(),
14607
+ normalizedName: text("normalized_name").notNull(),
14608
+ createdAt: text("created_at").notNull(),
14609
+ updatedAt: text("updated_at").notNull()
14610
+ }, (t) => [
14611
+ primaryKey({ columns: [t.projectId, t.id] }),
14612
+ uniqueIndex("uqChatTagsScopeName").on(t.projectId, t.normalizedName),
14613
+ index("idxChatTagsScopeName").on(t.projectId, t.normalizedName)
14614
+ ]);
14615
+ chatSessionTags = projectSchema.table("chat_session_tags", {
14616
+ sessionId: text("session_id").notNull(),
14617
+ tagId: text("tag_id").notNull(),
14618
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
14619
+ assignedAt: text("assigned_at").notNull()
14620
+ }, (t) => [
14621
+ // FNXC:ChatTags 2026-08-05-12:15: both parents include the RLS partition, preventing bypass/admin access from joining a same-named ID in another project.
14622
+ primaryKey({ columns: [t.projectId, t.sessionId, t.tagId] }),
14623
+ foreignKey({ columns: [t.projectId, t.sessionId], foreignColumns: [chatSessions.projectId, chatSessions.id] }).onDelete("cascade"),
14624
+ foreignKey({ columns: [t.projectId, t.tagId], foreignColumns: [chatTags.projectId, chatTags.id] }).onDelete("cascade"),
14625
+ index("idxChatSessionTagsTag").on(t.projectId, t.tagId, t.sessionId)
14591
14626
  ]);
14592
14627
  cliSessions = projectSchema.table("cli_sessions", {
14593
14628
  id: text("id").primaryKey(),
@@ -36530,6 +36565,7 @@ async function applySchemaBaseline(db, options = {}) {
36530
36565
  const taskWedgeNotificationAlreadyApplied = applied.includes(TASK_WEDGE_NOTIFICATION_VERSION);
36531
36566
  const milestoneAssertionProvenanceAlreadyApplied = applied.includes(MILESTONE_ASSERTION_PROVENANCE_VERSION);
36532
36567
  const missionLineageStopAlreadyApplied = applied.includes(MISSION_LINEAGE_STOP_VERSION);
36568
+ const chatSessionTagsAlreadyApplied = applied.includes(CHAT_SESSION_TAGS_VERSION);
36533
36569
  assertBinaryNotOlderThanDatabase(applied);
36534
36570
  let schemaChanged = false;
36535
36571
  if (!baselineAlreadyApplied) {
@@ -36875,17 +36911,23 @@ async function applySchemaBaseline(db, options = {}) {
36875
36911
  await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${MISSION_LINEAGE_STOP_VERSION}) ON CONFLICT (version) DO NOTHING`);
36876
36912
  schemaChanged = true;
36877
36913
  }
36914
+ if (!chatSessionTagsAlreadyApplied) {
36915
+ const migrationSql = await readFile4(CHAT_SESSION_TAGS_MIGRATION_PATH, "utf8");
36916
+ await tx.execute(sql.raw(migrationSql));
36917
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${CHAT_SESSION_TAGS_VERSION}) ON CONFLICT (version) DO NOTHING`);
36918
+ schemaChanged = true;
36919
+ }
36878
36920
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
36879
36921
  });
36880
36922
  }
36881
- 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, 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;
36923
+ 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, 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;
36882
36924
  var init_schema_applier = __esm({
36883
36925
  "../core/src/postgres/schema-applier.ts"() {
36884
36926
  "use strict";
36885
36927
  init_drizzle_orm();
36886
36928
  init_plugin_schema_hook();
36887
36929
  init_advisory_locks();
36888
- SCHEMA_BASELINE_VERSION = "0035";
36930
+ SCHEMA_BASELINE_VERSION = "0036";
36889
36931
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
36890
36932
  INITIAL_SCHEMA_VERSION = "0000";
36891
36933
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -36921,6 +36963,7 @@ var init_schema_applier = __esm({
36921
36963
  TASK_WEDGE_NOTIFICATION_VERSION = "0033";
36922
36964
  MILESTONE_ASSERTION_PROVENANCE_VERSION = "0034";
36923
36965
  MISSION_LINEAGE_STOP_VERSION = "0035";
36966
+ CHAT_SESSION_TAGS_VERSION = "0036";
36924
36967
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
36925
36968
  StaleBinarySchemaError = class extends Error {
36926
36969
  constructor(databaseVersion, binaryVersion) {
@@ -37036,6 +37079,7 @@ var init_schema_applier = __esm({
37036
37079
  "0034_milestone_assertion_provenance.sql"
37037
37080
  );
37038
37081
  MISSION_LINEAGE_STOP_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0035_fn_8543_mission_lineage_stop.sql");
37082
+ CHAT_SESSION_TAGS_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0036_chat_session_tags.sql");
37039
37083
  }
37040
37084
  });
37041
37085
 
@@ -63261,6 +63305,14 @@ function archiveEntryToTask(entry, slim = false) {
63261
63305
  planningStartedAt: entry.planningStartedAt,
63262
63306
  executionStartedAt: entry.executionStartedAt,
63263
63307
  executionCompletedAt: entry.executionCompletedAt,
63308
+ /*
63309
+ FNXC:ArchiveLifecycle 2026-07-24-11:02:
63310
+ FN-8561 needs archived TaskCard completion fallback to use the immutable
63311
+ archive transition, not the pre-archive columnMovedAt snapshot or updatedAt.
63312
+ Preserve archivedAt on every slim and full archive read without changing
63313
+ restore persistence semantics.
63314
+ */
63315
+ archivedAt: entry.archivedAt,
63264
63316
  modelPresetId: entry.modelPresetId,
63265
63317
  modelProvider: entry.modelProvider,
63266
63318
  modelId: entry.modelId,
@@ -127002,9 +127054,14 @@ var init_org_bundle = __esm({
127002
127054
  });
127003
127055
 
127004
127056
  // ../core/src/async-chat-store.ts
127057
+ import { randomUUID as randomUUID41 } from "node:crypto";
127058
+ function rowToTag(row) {
127059
+ return { id: row.id, projectId: row.ownerProjectId === "__default__" ? null : row.ownerProjectId, name: row.name, createdAt: row.createdAt, updatedAt: row.updatedAt };
127060
+ }
127005
127061
  function rowToSession3(row) {
127006
127062
  return {
127007
127063
  id: row.id,
127064
+ tags: [],
127008
127065
  agentId: row.agentId,
127009
127066
  title: row.title ?? null,
127010
127067
  status: row.status,
@@ -127093,7 +127150,7 @@ async function createChatSession(handle, session) {
127093
127150
  }
127094
127151
  async function getChatSession(handle, id) {
127095
127152
  const rows = await handle.select().from(project_exports.chatSessions).where(eq(project_exports.chatSessions.id, id));
127096
- return rows[0] ? rowToSession3(rows[0]) : void 0;
127153
+ return rows[0] ? attachTags(handle, rowToSession3(rows[0])) : void 0;
127097
127154
  }
127098
127155
  async function listChatSessions(handle, options) {
127099
127156
  const conditions = [];
@@ -127102,12 +127159,104 @@ async function listChatSessions(handle, options) {
127102
127159
  if (options?.status) conditions.push(eq(project_exports.chatSessions.status, options.status));
127103
127160
  const query = handle.select().from(project_exports.chatSessions).orderBy(desc(project_exports.chatSessions.updatedAt));
127104
127161
  const rows = conditions.length > 0 ? await query.where(and(...conditions)) : await query;
127105
- return rows.map(rowToSession3);
127162
+ return attachTagsToSessions(handle, rows.map(rowToSession3));
127106
127163
  }
127107
127164
  async function deleteChatSession(handle, id) {
127108
- const result = await handle.delete(project_exports.chatSessions).where(eq(project_exports.chatSessions.id, id)).returning({ id: project_exports.chatSessions.id });
127165
+ const [session] = await handle.select({ projectId: project_exports.chatSessions.projectId }).from(project_exports.chatSessions).where(eq(project_exports.chatSessions.id, id));
127166
+ if (!session) return false;
127167
+ const sessionProjectId = session.projectId;
127168
+ if (!sessionProjectId) throw new Error("Chat session is missing its required project partition");
127169
+ await handle.delete(project_exports.chatSessionTags).where(and(
127170
+ eq(project_exports.chatSessionTags.sessionId, id),
127171
+ eq(project_exports.chatSessionTags.projectId, sessionProjectId)
127172
+ ));
127173
+ const result = await handle.delete(project_exports.chatSessions).where(and(eq(project_exports.chatSessions.id, id), eq(project_exports.chatSessions.projectId, sessionProjectId))).returning({ id: project_exports.chatSessions.id });
127109
127174
  return result.length > 0;
127110
127175
  }
127176
+ function tagScope(projectId) {
127177
+ return projectId ?? "__default__";
127178
+ }
127179
+ function normalizeTagName(name2) {
127180
+ const cleaned = name2.trim().replace(/\s+/g, " ");
127181
+ if (!cleaned || cleaned.length > 64) throw new Error("Tag name must be between 1 and 64 characters");
127182
+ return { name: cleaned, normalizedName: cleaned.toLocaleLowerCase() };
127183
+ }
127184
+ async function attachTags(handle, session) {
127185
+ const [attached] = await attachTagsToSessions(handle, [session]);
127186
+ return attached ?? session;
127187
+ }
127188
+ async function attachTagsToSessions(handle, sessions7) {
127189
+ if (!sessions7.length) return sessions7;
127190
+ const rows = await handle.select({ sessionId: project_exports.chatSessionTags.sessionId, id: project_exports.chatTags.id, ownerProjectId: project_exports.chatTags.ownerProjectId, name: project_exports.chatTags.name, createdAt: project_exports.chatTags.createdAt, updatedAt: project_exports.chatTags.updatedAt }).from(project_exports.chatSessionTags).innerJoin(project_exports.chatTags, and(
127191
+ eq(project_exports.chatSessionTags.tagId, project_exports.chatTags.id),
127192
+ eq(project_exports.chatSessionTags.projectId, project_exports.chatTags.projectId)
127193
+ )).innerJoin(project_exports.chatSessions, and(
127194
+ eq(project_exports.chatSessionTags.sessionId, project_exports.chatSessions.id),
127195
+ eq(project_exports.chatSessionTags.projectId, project_exports.chatSessions.projectId)
127196
+ )).where(inArray(project_exports.chatSessionTags.sessionId, sessions7.map((session) => session.id))).orderBy(asc(project_exports.chatTags.normalizedName), asc(project_exports.chatTags.id));
127197
+ const tagsBySession = /* @__PURE__ */ new Map();
127198
+ for (const row of rows) {
127199
+ const tags = tagsBySession.get(row.sessionId) ?? [];
127200
+ tags.push(rowToTag(row));
127201
+ tagsBySession.set(row.sessionId, tags);
127202
+ }
127203
+ return sessions7.map((session) => ({ ...session, tags: tagsBySession.get(session.id) ?? [] }));
127204
+ }
127205
+ async function listChatTags(handle, projectId) {
127206
+ const rows = await handle.select().from(project_exports.chatTags).where(eq(project_exports.chatTags.ownerProjectId, tagScope(projectId))).orderBy(asc(project_exports.chatTags.normalizedName), asc(project_exports.chatTags.id));
127207
+ return rows.map(rowToTag);
127208
+ }
127209
+ async function createChatTag(layer, input) {
127210
+ const { name: name2, normalizedName } = normalizeTagName(input.name);
127211
+ const projectId = input.projectId ?? null;
127212
+ const now = (/* @__PURE__ */ new Date()).toISOString();
127213
+ return layer.transactionImmediate(async (tx) => {
127214
+ const id = `chat-tag-${randomUUID41().slice(0, 8)}`;
127215
+ const rows = await tx.insert(project_exports.chatTags).values({ id, ownerProjectId: tagScope(projectId), name: name2, normalizedName, createdAt: now, updatedAt: now }).onConflictDoNothing().returning();
127216
+ if (!rows[0]) throw new Error("A tag with that name already exists");
127217
+ return rowToTag(rows[0]);
127218
+ });
127219
+ }
127220
+ async function renameChatTag(layer, id, projectId, input) {
127221
+ const { name: name2, normalizedName } = normalizeTagName(input.name);
127222
+ return layer.transactionImmediate(async (tx) => {
127223
+ const rows = await tx.update(project_exports.chatTags).set({ name: name2, normalizedName, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(eq(project_exports.chatTags.id, id), eq(project_exports.chatTags.ownerProjectId, tagScope(projectId)))).returning();
127224
+ if (!rows[0]) return void 0;
127225
+ return rowToTag(rows[0]);
127226
+ });
127227
+ }
127228
+ async function deleteChatTag(layer, id, projectId) {
127229
+ return layer.transactionImmediate(async (tx) => {
127230
+ const [tag] = await tx.select({ projectId: project_exports.chatTags.projectId }).from(project_exports.chatTags).where(and(eq(project_exports.chatTags.id, id), eq(project_exports.chatTags.ownerProjectId, tagScope(projectId))));
127231
+ if (!tag?.projectId) return false;
127232
+ const tagProjectId = tag.projectId;
127233
+ await tx.delete(project_exports.chatSessionTags).where(and(
127234
+ eq(project_exports.chatSessionTags.tagId, id),
127235
+ eq(project_exports.chatSessionTags.projectId, tagProjectId)
127236
+ ));
127237
+ return (await tx.delete(project_exports.chatTags).where(and(eq(project_exports.chatTags.id, id), eq(project_exports.chatTags.projectId, tagProjectId))).returning({ id: project_exports.chatTags.id })).length > 0;
127238
+ });
127239
+ }
127240
+ async function replaceChatSessionTags(layer, sessionId, projectId, tagIds) {
127241
+ return layer.transactionImmediate(async (tx) => {
127242
+ const session = await getChatSessionForUpdate(tx, sessionId);
127243
+ if (!session || session.projectId !== projectId) return void 0;
127244
+ const ownerProjectCondition = projectId === null ? isNull(project_exports.chatSessions.ownerProjectId) : eq(project_exports.chatSessions.ownerProjectId, projectId);
127245
+ const [sessionPartition] = await tx.select({ projectId: project_exports.chatSessions.projectId }).from(project_exports.chatSessions).where(and(eq(project_exports.chatSessions.id, sessionId), ownerProjectCondition)).for("update");
127246
+ if (!sessionPartition?.projectId) return void 0;
127247
+ const sessionProjectId = sessionPartition.projectId;
127248
+ const uniqueIds = [...new Set(tagIds)];
127249
+ const tags = uniqueIds.length ? await tx.select().from(project_exports.chatTags).where(and(
127250
+ inArray(project_exports.chatTags.id, uniqueIds),
127251
+ eq(project_exports.chatTags.ownerProjectId, tagScope(projectId)),
127252
+ eq(project_exports.chatTags.projectId, sessionProjectId)
127253
+ )) : [];
127254
+ if (tags.length !== uniqueIds.length) throw new Error("One or more tags do not belong to this project");
127255
+ await tx.delete(project_exports.chatSessionTags).where(and(eq(project_exports.chatSessionTags.sessionId, sessionId), eq(project_exports.chatSessionTags.projectId, sessionProjectId)));
127256
+ if (uniqueIds.length) await tx.insert(project_exports.chatSessionTags).values(uniqueIds.map((tagId) => ({ projectId: sessionProjectId, sessionId, tagId, assignedAt: (/* @__PURE__ */ new Date()).toISOString() }))).onConflictDoNothing();
127257
+ return attachTags(tx, session);
127258
+ });
127259
+ }
127111
127260
  async function addChatMessage(handle, message) {
127112
127261
  const sanitizedAttachments = message.attachments === void 0 ? void 0 : sanitizeJsonbValue(message.attachments);
127113
127262
  const sanitized = {
@@ -127499,7 +127648,7 @@ var init_async_chat_store = __esm({
127499
127648
 
127500
127649
  // ../core/src/chat-store.ts
127501
127650
  import { EventEmitter as EventEmitter23 } from "node:events";
127502
- import { randomUUID as randomUUID41 } from "node:crypto";
127651
+ import { randomUUID as randomUUID42 } from "node:crypto";
127503
127652
  var ChatStore;
127504
127653
  var init_chat_store = __esm({
127505
127654
  "../core/src/chat-store.ts"() {
@@ -127534,8 +127683,9 @@ var init_chat_store = __esm({
127534
127683
  async createSession(input) {
127535
127684
  const now = (/* @__PURE__ */ new Date()).toISOString();
127536
127685
  const session = {
127537
- id: `chat-${randomUUID41().slice(0, 8)}`,
127686
+ id: `chat-${randomUUID42().slice(0, 8)}`,
127538
127687
  agentId: input.agentId,
127688
+ tags: [],
127539
127689
  title: input.title ?? null,
127540
127690
  status: "active",
127541
127691
  projectId: input.projectId ?? null,
@@ -127705,6 +127855,29 @@ var init_chat_store = __esm({
127705
127855
  }
127706
127856
  return deletedCount;
127707
127857
  }
127858
+ /*
127859
+ FNXC:ChatTags 2026-08-05-10:55:
127860
+ Direct-conversation tags are project-scoped by the session's owner project.
127861
+ Tag mutations are transactional and return an enriched session so SSE clients
127862
+ never receive an assignment update without its deterministic tag array.
127863
+ */
127864
+ async listTags(projectId = null) {
127865
+ return listChatTags(this.asyncLayer.db, projectId);
127866
+ }
127867
+ async createTag(input) {
127868
+ return createChatTag(this.asyncLayer, input);
127869
+ }
127870
+ async renameTag(id, projectId, input) {
127871
+ return renameChatTag(this.asyncLayer, id, projectId, input);
127872
+ }
127873
+ async deleteTag(id, projectId) {
127874
+ return deleteChatTag(this.asyncLayer, id, projectId);
127875
+ }
127876
+ async replaceSessionTags(id, projectId, tagIds) {
127877
+ const updated = await replaceChatSessionTags(this.asyncLayer, id, projectId, tagIds);
127878
+ if (updated) this.emit("chat:session:updated", updated);
127879
+ return updated;
127880
+ }
127708
127881
  // ── Message CRUD Operations ───────────────────────────────────────
127709
127882
  /**
127710
127883
  * Add a message to a chat session.
@@ -127721,7 +127894,7 @@ var init_chat_store = __esm({
127721
127894
  }
127722
127895
  const now = (/* @__PURE__ */ new Date()).toISOString();
127723
127896
  const message = {
127724
- id: `msg-${randomUUID41().slice(0, 8)}`,
127897
+ id: `msg-${randomUUID42().slice(0, 8)}`,
127725
127898
  sessionId,
127726
127899
  role: input.role,
127727
127900
  content: input.content,
@@ -127863,7 +128036,7 @@ var init_chat_store = __esm({
127863
128036
  if (!slug) throw new Error("Room name must include letters or numbers");
127864
128037
  const now = (/* @__PURE__ */ new Date()).toISOString();
127865
128038
  const room = {
127866
- id: `room-${randomUUID41().slice(0, 8)}`,
128039
+ id: `room-${randomUUID42().slice(0, 8)}`,
127867
128040
  name: normalizedName,
127868
128041
  slug,
127869
128042
  description: input.description ?? null,
@@ -127955,7 +128128,7 @@ var init_chat_store = __esm({
127955
128128
  }
127956
128129
  const now = (/* @__PURE__ */ new Date()).toISOString();
127957
128130
  const message = {
127958
- id: `rmsg-${randomUUID41().slice(0, 8)}`,
128131
+ id: `rmsg-${randomUUID42().slice(0, 8)}`,
127959
128132
  roomId,
127960
128133
  role: input.role,
127961
128134
  content: input.content,
@@ -128010,7 +128183,7 @@ var init_chat_store = __esm({
128010
128183
  return void 0;
128011
128184
  }
128012
128185
  const record2 = {
128013
- id: `chat-tokens-${randomUUID41().slice(0, 12)}`,
128186
+ id: `chat-tokens-${randomUUID42().slice(0, 12)}`,
128014
128187
  sourceKind: input.sourceKind,
128015
128188
  chatSessionId: input.chatSessionId ?? null,
128016
128189
  roomId: input.roomId ?? null,
@@ -128106,7 +128279,7 @@ var init_cli_session_types = __esm({
128106
128279
  });
128107
128280
 
128108
128281
  // ../core/src/cli-session-store.ts
128109
- import { randomUUID as randomUUID42 } from "node:crypto";
128282
+ import { randomUUID as randomUUID43 } from "node:crypto";
128110
128283
  import { EventEmitter as EventEmitter24 } from "node:events";
128111
128284
  function parsePosture(value) {
128112
128285
  return fromJson(value) ?? null;
@@ -128188,7 +128361,7 @@ var init_cli_session_store = __esm({
128188
128361
  if (!input.adapterId) throw new Error("CLI session requires an adapterId");
128189
128362
  const now = (/* @__PURE__ */ new Date()).toISOString();
128190
128363
  const session = {
128191
- id: input.id ?? `cli-${randomUUID42().slice(0, 8)}`,
128364
+ id: input.id ?? `cli-${randomUUID43().slice(0, 8)}`,
128192
128365
  taskId: input.taskId ?? null,
128193
128366
  chatSessionId: input.chatSessionId ?? null,
128194
128367
  purpose: input.purpose,
@@ -128581,7 +128754,7 @@ var init_task_prefix = __esm({
128581
128754
  });
128582
128755
 
128583
128756
  // ../core/src/task-store/async-monitor.ts
128584
- import { randomUUID as randomUUID43 } from "node:crypto";
128757
+ import { randomUUID as randomUUID44 } from "node:crypto";
128585
128758
  function monitorProjectPartition(projectId) {
128586
128759
  return projectId.trim() || "__legacy_unscoped__";
128587
128760
  }
@@ -128625,7 +128798,7 @@ function incidentFromRow(row) {
128625
128798
  }
128626
128799
  async function recordDeploymentAsync(db, input, projectId = "") {
128627
128800
  const ownerProjectId = monitorProjectPartition(projectId);
128628
- const deploymentId = input.deploymentId?.trim() || `dep-${randomUUID43()}`;
128801
+ const deploymentId = input.deploymentId?.trim() || `dep-${randomUUID44()}`;
128629
128802
  const now = (/* @__PURE__ */ new Date()).toISOString();
128630
128803
  const deployedAt = input.deployedAt ?? now;
128631
128804
  await db.insert(project_exports.deployments).values({
@@ -128683,7 +128856,7 @@ async function ingestIncidentSignalAsync(db, input, projectId = "") {
128683
128856
  const updated = await getIncidentAsync(db, existing.incidentId, ownerProjectId);
128684
128857
  return { incident: updated ?? existing, created: false };
128685
128858
  }
128686
- const incidentId = `inc-${randomUUID43()}`;
128859
+ const incidentId = `inc-${randomUUID44()}`;
128687
128860
  const meta2 = {
128688
128861
  ...input.meta ?? {},
128689
128862
  [OCCURRENCES_META_KEY]: 1,
@@ -129271,6 +129444,7 @@ __export(src_exports, {
129271
129444
  AGENT_PERMISSION_POLICY_PRESET_IDS: () => AGENT_PERMISSION_POLICY_PRESET_IDS,
129272
129445
  AGENT_PROVISIONING_APPROVAL_MODES: () => AGENT_PROVISIONING_APPROVAL_MODES,
129273
129446
  AGENT_VALID_TRANSITIONS: () => AGENT_VALID_TRANSITIONS,
129447
+ ANTHROPIC_AUTH_PREFERENCES: () => ANTHROPIC_AUTH_PREFERENCES,
129274
129448
  ANTHROPIC_PROVIDER_ID: () => ANTHROPIC_PROVIDER_ID,
129275
129449
  APPROVAL_REQUEST_AUDIT_EVENT_TYPES: () => APPROVAL_REQUEST_AUDIT_EVENT_TYPES,
129276
129450
  APPROVAL_REQUEST_STATUSES: () => APPROVAL_REQUEST_STATUSES,
@@ -130910,7 +131084,7 @@ var init_src2 = __esm({
130910
131084
  });
130911
131085
 
130912
131086
  // ../dashboard/src/ai-session-diagnostics.ts
130913
- import { randomUUID as randomUUID44 } from "node:crypto";
131087
+ import { randomUUID as randomUUID45 } from "node:crypto";
130914
131088
  function defaultSink(level, scope, message, context) {
130915
131089
  const prefix = `[${scope}]`;
130916
131090
  const logArgs = [prefix, message, context];
@@ -130957,7 +131131,7 @@ function emit(level, scope, message, context) {
130957
131131
  const fullContext = {
130958
131132
  ...context,
130959
131133
  _emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
130960
- _diagnosticsId: randomUUID44()
131134
+ _diagnosticsId: randomUUID45()
130961
131135
  };
130962
131136
  try {
130963
131137
  _sink(level, scope, message, fullContext);
@@ -134371,6 +134545,26 @@ function resolveStoredCredentialApiKey(providerId, credential) {
134371
134545
  }
134372
134546
  return void 0;
134373
134547
  }
134548
+ function getFusionGlobalSettingsPath(home = getHomeDir5()) {
134549
+ const candidates = [
134550
+ join54(home, ".fusion", "settings.json"),
134551
+ join54(home, ".pi", "fusion", "settings.json"),
134552
+ join54(home, ".pi", "kb", "settings.json")
134553
+ ];
134554
+ return candidates.find((candidate) => existsSync41(candidate)) ?? candidates[0];
134555
+ }
134556
+ function readAnthropicAuthPreference(home = getHomeDir5()) {
134557
+ const settingsPath = getFusionGlobalSettingsPath(home);
134558
+ if (!existsSync41(settingsPath)) {
134559
+ return "api-key";
134560
+ }
134561
+ try {
134562
+ const parsed = JSON.parse(readFileSync12(settingsPath, "utf-8"));
134563
+ return parsed?.anthropicAuthPreference === "subscription" ? "subscription" : "api-key";
134564
+ } catch {
134565
+ return "api-key";
134566
+ }
134567
+ }
134374
134568
  function readModelsJsonApiKeys(home = getHomeDir5()) {
134375
134569
  const apiKeys = /* @__PURE__ */ new Map();
134376
134570
  const modelsPath = getModelRegistryModelsPath(home);
@@ -134576,11 +134770,15 @@ function createFusionAuthStorage() {
134576
134770
  };
134577
134771
  const resolveAnthropicRuntimeApiKey = async () => {
134578
134772
  const rawProviderLoggedOut = isAnthropicRawProviderLoggedOut();
134579
- if (!rawProviderLoggedOut) {
134773
+ const preferSubscription = readAnthropicAuthPreference() === "subscription";
134774
+ const resolveRawApiKey = () => {
134775
+ if (rawProviderLoggedOut) return void 0;
134580
134776
  const anthropicApiKeyCredential = selectStoredCredentialByType(ANTHROPIC_PROVIDER_ID2, "api_key");
134581
- if (anthropicApiKeyCredential) {
134582
- return resolveStoredCredentialApiKey(ANTHROPIC_PROVIDER_ID2, anthropicApiKeyCredential);
134583
- }
134777
+ return anthropicApiKeyCredential ? resolveStoredCredentialApiKey(ANTHROPIC_PROVIDER_ID2, anthropicApiKeyCredential) : void 0;
134778
+ };
134779
+ if (!preferSubscription) {
134780
+ const rawKey = resolveRawApiKey();
134781
+ if (rawKey) return rawKey;
134584
134782
  }
134585
134783
  const subscriptionLoggedOut = loggedOutProviders.has(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID);
134586
134784
  const legacyAnthropicOAuthCredential = rawProviderLoggedOut ? void 0 : selectStoredCredentialByType(ANTHROPIC_PROVIDER_ID2, "oauth");
@@ -134595,6 +134793,10 @@ function createFusionAuthStorage() {
134595
134793
  if (subscriptionKey) return subscriptionKey;
134596
134794
  }
134597
134795
  }
134796
+ if (preferSubscription) {
134797
+ const rawKey = resolveRawApiKey();
134798
+ if (rawKey) return rawKey;
134799
+ }
134598
134800
  if (!rawProviderLoggedOut) {
134599
134801
  const modelsJsonApiKey = modelsJsonApiKeys.get(ANTHROPIC_PROVIDER_ID2);
134600
134802
  if (modelsJsonApiKey) return modelsJsonApiKey;
@@ -161773,8 +161975,12 @@ var init_provider_registry = __esm({
161773
161975
  const maxResults = Number(this.settings.researchGlobalMaxSearchResults ?? 10);
161774
161976
  const fetchTimeoutMs = Number(this.settings.researchGlobalFetchTimeoutMs ?? 3e4);
161775
161977
  const userAgent = this.settings.researchGlobalUserAgent ?? "FusionResearchBot/1.0";
161776
- const synthesisProvider = this.settings.researchGlobalDefaults?.synthesisProvider ?? this.settings.defaultProvider;
161777
- const synthesisModelId = this.settings.researchGlobalDefaults?.synthesisModelId ?? this.settings.defaultModelId;
161978
+ const researchSynthesisPair = this.settings.researchGlobalDefaults?.synthesisProvider && this.settings.researchGlobalDefaults?.synthesisModelId ? {
161979
+ provider: this.settings.researchGlobalDefaults.synthesisProvider,
161980
+ modelId: this.settings.researchGlobalDefaults.synthesisModelId
161981
+ } : void 0;
161982
+ const synthesisProvider = researchSynthesisPair?.provider ?? this.settings.defaultProvider;
161983
+ const synthesisModelId = researchSynthesisPair?.modelId ?? this.settings.defaultModelId;
161778
161984
  this.providers = /* @__PURE__ */ new Map([
161779
161985
  [
161780
161986
  "web-search",
@@ -169339,7 +169545,7 @@ var init_code_node_runner = __esm({
169339
169545
  // ../engine/src/agent-tools.ts
169340
169546
  import { appendFile as appendFile3, mkdir as mkdir26, readFile as readFile31, readdir as readdir9, realpath, stat as stat9, writeFile as writeFile25 } from "node:fs/promises";
169341
169547
  import { existsSync as existsSync50 } from "node:fs";
169342
- import { createHash as createHash16, randomUUID as randomUUID45 } from "node:crypto";
169548
+ import { createHash as createHash16, randomUUID as randomUUID46 } from "node:crypto";
169343
169549
  import { tmpdir as tmpdir5 } from "node:os";
169344
169550
  import { extname as extname3, isAbsolute as isAbsolute12, join as join64, relative as relative11, resolve as resolve32, sep as sep9 } from "node:path";
169345
169551
  import { Type as Type2 } from "@earendil-works/pi-ai";
@@ -169946,7 +170152,7 @@ function createTaskCreateTool(store, provenance, options) {
169946
170152
  toType: "user",
169947
170153
  type: "agent-to-user",
169948
170154
  content: `Task proposal awaiting validation: ${title2}`,
169949
- metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey: randomUUID45(), taskId: options.sourceTaskId, proposedTask: { title: title2, description: params.description, priority: params.priority, workflowId: params.workflow_id, dependencies: params.dependencies } }
170155
+ metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey: randomUUID46(), taskId: options.sourceTaskId, proposedTask: { title: title2, description: params.description, priority: params.priority, workflowId: params.workflow_id, dependencies: params.dependencies } }
169950
170156
  });
169951
170157
  return { content: [{ type: "text", text: "Task proposal submitted to the operator for validation; no task was created." }], details: { proposed: true } };
169952
170158
  }
@@ -244052,6 +244258,7 @@ var init_agent_reflection = __esm({
244052
244258
  "../engine/src/agent-reflection.ts"() {
244053
244259
  "use strict";
244054
244260
  init_logger3();
244261
+ init_src2();
244055
244262
  init_pi();
244056
244263
  init_mcp_resolution();
244057
244264
  init_run_audit();
@@ -244092,6 +244299,22 @@ Rules:
244092
244299
  this.modelProvider = options.modelProvider;
244093
244300
  this.modelId = options.modelId;
244094
244301
  }
244302
+ async resolveReflectionModel() {
244303
+ if (this.modelProvider && this.modelId) {
244304
+ return { provider: this.modelProvider, modelId: this.modelId };
244305
+ }
244306
+ try {
244307
+ const settings = await this.taskStore.getSettings();
244308
+ const resolved = resolveProjectDefaultModel(settings);
244309
+ if (resolved.provider && resolved.modelId) {
244310
+ return { provider: resolved.provider, modelId: resolved.modelId };
244311
+ }
244312
+ } catch (error2) {
244313
+ reflectionLog.warn(`Failed to resolve reflection model from settings: ${String(error2)}`);
244314
+ }
244315
+ reflectionLog.warn("No provider/model pair resolved for reflection; the runtime will use its built-in default model");
244316
+ return {};
244317
+ }
244095
244318
  async generateReflection(agentId, trigger, options = {}) {
244096
244319
  const runContext = {
244097
244320
  runId: generateSyntheticRunId("reflection", agentId),
@@ -244112,12 +244335,12 @@ Rules:
244112
244335
  return null;
244113
244336
  }
244114
244337
  let responseText = "";
244338
+ const reflectionModel = await this.resolveReflectionModel();
244115
244339
  const { session } = await createFnAgent2({
244116
244340
  cwd: this.rootDir,
244117
244341
  systemPrompt: REFLECTION_SYSTEM_PROMPT,
244118
244342
  tools: "readonly",
244119
- defaultProvider: this.modelProvider,
244120
- defaultModelId: this.modelId,
244343
+ ...reflectionModel.provider && reflectionModel.modelId ? { defaultProvider: reflectionModel.provider, defaultModelId: reflectionModel.modelId } : {},
244121
244344
  mcpServers: (await resolveMcpServersForStore(this.taskStore, { agentId })).servers,
244122
244345
  onText: (delta) => {
244123
244346
  responseText += delta;
@@ -245190,7 +245413,7 @@ var init_metric_parser = __esm({
245190
245413
  });
245191
245414
 
245192
245415
  // ../engine/src/experiment-executor.ts
245193
- import { randomUUID as randomUUID46 } from "node:crypto";
245416
+ import { randomUUID as randomUUID47 } from "node:crypto";
245194
245417
  var ExperimentMaxIterationsError, ExperimentGitNotConfiguredError, ExperimentExecutor;
245195
245418
  var init_experiment_executor = __esm({
245196
245419
  "../engine/src/experiment-executor.ts"() {
@@ -245258,7 +245481,7 @@ var init_experiment_executor = __esm({
245258
245481
  }
245259
245482
  await this.semaphore.acquire();
245260
245483
  const controller = new AbortController();
245261
- const runHandle = randomUUID46();
245484
+ const runHandle = randomUUID47();
245262
245485
  if (opts?.abortSignal) {
245263
245486
  opts.abortSignal.addEventListener("abort", () => controller.abort(), { once: true });
245264
245487
  }
@@ -263983,8 +264206,19 @@ __export(planning_exports, {
263983
264206
  updatePlanningSessionTitle: () => updatePlanningSessionTitle,
263984
264207
  validateSession: () => validateSession
263985
264208
  });
263986
- import { randomUUID as randomUUID47 } from "node:crypto";
264209
+ import { randomUUID as randomUUID48 } from "node:crypto";
263987
264210
  import { EventEmitter as EventEmitter34 } from "node:events";
264211
+ async function createPlanningRuntimeSession(options) {
264212
+ const { pluginRunner, runtimeHint, settings, ...runtimeOptions } = options ?? {};
264213
+ const create = createResolvedAgentSession ?? createResolvedAgentSession;
264214
+ return create({
264215
+ sessionPurpose: "executor",
264216
+ ...runtimeHint ? { runtimeHint } : {},
264217
+ ...pluginRunner ? { pluginRunner } : {},
264218
+ ...settings ? { settings } : {},
264219
+ ...runtimeOptions
264220
+ });
264221
+ }
263988
264222
  function isThinkingLevel(value) {
263989
264223
  return THINKING_LEVELS.includes(value);
263990
264224
  }
@@ -264436,7 +264670,7 @@ async function createSession(ip, initialPlan, store, rootDir, promptOverrides, p
264436
264670
  if (!store) {
264437
264671
  throw new Error("store is required for AI-powered planning sessions");
264438
264672
  }
264439
- const sessionId = randomUUID47();
264673
+ const sessionId = randomUUID48();
264440
264674
  const session = {
264441
264675
  id: sessionId,
264442
264676
  ip,
@@ -264475,6 +264709,7 @@ async function runCreateSessionFirstTurn(session, rootDir, store, promptOverride
264475
264709
  await ensureEngineReady();
264476
264710
  }
264477
264711
  const skillContext = buildSessionSkillContextSync(null, "executor", rootDir, pluginRunner);
264712
+ const { provider: startProvider, modelId: startModelId } = await resolveSessionPlanningModel(session, store);
264478
264713
  const agentResult = await createFnAgent4({
264479
264714
  cwd: rootDir,
264480
264715
  systemPrompt,
@@ -264498,6 +264733,8 @@ async function runCreateSessionFirstTurn(session, rootDir, store, promptOverride
264498
264733
  ...createChatTaskDocumentTools(store),
264499
264734
  createChatTaskLogsReadTool(store)
264500
264735
  ],
264736
+ ...startProvider && startModelId ? { defaultProvider: startProvider, defaultModelId: startModelId } : {},
264737
+ ...pluginRunner ? { pluginRunner } : {},
264501
264738
  onThinking: () => {
264502
264739
  },
264503
264740
  onText: () => {
@@ -264648,7 +264885,7 @@ async function createDraftSession(ip, initialPlan, _rootDir, modelProvider, mode
264648
264885
  `Rate limit exceeded. Maximum ${MAX_SESSIONS_PER_IP_PER_HOUR} planning sessions per hour. Reset at ${resetTime?.toISOString() || "unknown"}`
264649
264886
  );
264650
264887
  }
264651
- const sessionId = randomUUID47();
264888
+ const sessionId = randomUUID48();
264652
264889
  const title2 = DRAFT_PLACEHOLDER_TITLE;
264653
264890
  const hasModelOverride = Boolean(modelProvider && modelId);
264654
264891
  const session = {
@@ -264824,7 +265061,7 @@ async function createSessionWithAgent(ip, initialPlan, rootDir, store, modelProv
264824
265061
  `Rate limit exceeded. Maximum ${MAX_SESSIONS_PER_IP_PER_HOUR} planning sessions per hour. Reset at ${resetTime?.toISOString() || "unknown"}`
264825
265062
  );
264826
265063
  }
264827
- const sessionId = randomUUID47();
265064
+ const sessionId = randomUUID48();
264828
265065
  const session = {
264829
265066
  id: sessionId,
264830
265067
  ip,
@@ -264969,6 +265206,8 @@ async function createPlanningAgent(session, rootDir, store, modelProvider, model
264969
265206
  defaultModelId: modelId
264970
265207
  } : {},
264971
265208
  ...thinkingLevel ? { defaultThinkingLevel: thinkingLevel } : {},
265209
+ // Runtime resolution needs the plugin runner to see plugin-provided runtimes.
265210
+ ...pluginRunner ? { pluginRunner } : {},
264972
265211
  onThinking: (delta) => {
264973
265212
  if (callbacksInvalidated()) return;
264974
265213
  markPlanningGenerationProgress(session.id, delta);
@@ -265002,6 +265241,31 @@ function buildHistoryReplayPrompt(history) {
265002
265241
  "Treat every recorded selection and Other answer as an accumulated durable decision. Rebuild affected plan fields around those decisions; do not preserve superseded or unselected alternatives as the plan backbone. Use this as context for the next response. Do not repeat prior questions unless necessary."
265003
265242
  ].join("\n\n");
265004
265243
  }
265244
+ async function resolveSessionPlanningModel(session, store) {
265245
+ if (session.draftModelProvider && session.draftModelId) {
265246
+ return { provider: session.draftModelProvider, modelId: session.draftModelId };
265247
+ }
265248
+ try {
265249
+ const settings = await store.getSettings();
265250
+ const resolved = resolvePlanningSettingsModel(settings);
265251
+ if (resolved.provider && resolved.modelId) {
265252
+ session.draftModelProvider = resolved.provider;
265253
+ session.draftModelId = resolved.modelId;
265254
+ return { provider: resolved.provider, modelId: resolved.modelId };
265255
+ }
265256
+ } catch (err) {
265257
+ diagnostics2.warn("Failed to resolve planning model for rebuilt agent", {
265258
+ sessionId: session.id,
265259
+ operation: "resolve-session-planning-model",
265260
+ error: String(err)
265261
+ });
265262
+ }
265263
+ diagnostics2.warn(
265264
+ "Rebuilt planning agent has no resolved provider/model pair; the runtime will use its built-in default model",
265265
+ { sessionId: session.id, operation: "resolve-session-planning-model" }
265266
+ );
265267
+ return {};
265268
+ }
265005
265269
  async function ensureSessionAgent(session, rootDir, historyForReplay, promptOverrides, store) {
265006
265270
  if (session.agent) {
265007
265271
  return;
@@ -265018,7 +265282,20 @@ async function ensureSessionAgent(session, rootDir, historyForReplay, promptOver
265018
265282
  "Planning session has no task store and cannot be resumed without project context"
265019
265283
  );
265020
265284
  }
265021
- session.agent = await createPlanningAgent(session, effectiveRootDir, effectiveStore, void 0, void 0, session.draftThinkingLevel, promptOverrides, session.pluginRunner);
265285
+ const { provider: resumeProvider, modelId: resumeModelId } = await resolveSessionPlanningModel(
265286
+ session,
265287
+ effectiveStore
265288
+ );
265289
+ session.agent = await createPlanningAgent(
265290
+ session,
265291
+ effectiveRootDir,
265292
+ effectiveStore,
265293
+ resumeProvider,
265294
+ resumeModelId,
265295
+ session.draftThinkingLevel,
265296
+ promptOverrides,
265297
+ session.pluginRunner
265298
+ );
265022
265299
  if (historyForReplay.length === 0) {
265023
265300
  return;
265024
265301
  }
@@ -265294,7 +265571,7 @@ function normalizePlanningQuestion(input, userInput = "") {
265294
265571
  const type = source.type === "multi_select" || source.type === "single_select" ? source.type : "single_select";
265295
265572
  const question = typeof source.question === "string" && source.question.trim() ? source.question.trim() : fallback2.question;
265296
265573
  const normalized = {
265297
- id: typeof source.id === "string" && source.id.trim() ? source.id : randomUUID47(),
265574
+ id: typeof source.id === "string" && source.id.trim() ? source.id : randomUUID48(),
265298
265575
  type,
265299
265576
  question,
265300
265577
  ...typeof source.description === "string" && source.description.trim() ? { description: source.description.trim() } : {}
@@ -265320,7 +265597,7 @@ function normalizePlanningQuestion(input, userInput = "") {
265320
265597
  }
265321
265598
  function coerceQuestionResponse(response, session) {
265322
265599
  if (response.type === "question") return normalizePlanningQuestion(response.data, session.initialPlan);
265323
- return normalizePlanningQuestion({ id: randomUUID47(), type: "single_select", question: planningFallbackCopy(session.initialPlan).question }, session.initialPlan);
265600
+ return normalizePlanningQuestion({ id: randomUUID48(), type: "single_select", question: planningFallbackCopy(session.initialPlan).question }, session.initialPlan);
265324
265601
  }
265325
265602
  async function validateSession(sessionId) {
265326
265603
  const session = await getSession(sessionId);
@@ -266250,7 +266527,7 @@ async function createTaskFromPlanSession(sessionId, store, options) {
266250
266527
  throw new InvalidSessionStateError("PLANNING_CREATED_TASK_MISSING");
266251
266528
  }
266252
266529
  }
266253
- const claimOwnerToken = randomUUID47();
266530
+ const claimOwnerToken = randomUUID48();
266254
266531
  let claimed = await claimPlanningTaskCreation(sessionId, claimOwnerToken, (/* @__PURE__ */ new Date()).toISOString(), claimEpoch);
266255
266532
  if (!claimed) {
266256
266533
  session = await getDurablePlanningSession(sessionId).catch(() => void 0) ?? session;
@@ -266545,7 +266822,7 @@ var init_planning = __esm({
266545
266822
  init_planning_board_tools();
266546
266823
  PLANNING_NO_AMBIENT_TASK_ID = "";
266547
266824
  PLANNING_BUILTIN_WEB_TOOLS = ["WebSearch", "WebFetch"];
266548
- createFnAgent4 = createFnAgent2;
266825
+ createFnAgent4 = createPlanningRuntimeSession;
266549
266826
  diagnostics2 = createSessionDiagnostics("planning");
266550
266827
  PLANNING_SYSTEM_PROMPT = `## Collaborative Planning Mode
266551
266828
 
@@ -266845,6 +267122,37 @@ var init_ai_session_timeout = __esm({
266845
267122
  }
266846
267123
  });
266847
267124
 
267125
+ // ../dashboard/src/lane-session-model.ts
267126
+ function laneModelOptions(model) {
267127
+ return model.provider && model.modelId ? { defaultProvider: model.provider, defaultModelId: model.modelId } : {};
267128
+ }
267129
+ async function resolveLaneSessionModel(store, cached2, onUnresolved) {
267130
+ if (cached2?.provider && cached2?.modelId) {
267131
+ return { provider: cached2.provider, modelId: cached2.modelId };
267132
+ }
267133
+ if (!store) {
267134
+ onUnresolved?.("no-store");
267135
+ return {};
267136
+ }
267137
+ try {
267138
+ const settings = await store.getSettings();
267139
+ const resolved = resolvePlanningSettingsModel(settings);
267140
+ if (resolved.provider && resolved.modelId) {
267141
+ return { provider: resolved.provider, modelId: resolved.modelId };
267142
+ }
267143
+ onUnresolved?.("unset");
267144
+ } catch (error2) {
267145
+ onUnresolved?.("error", error2);
267146
+ }
267147
+ return {};
267148
+ }
267149
+ var init_lane_session_model = __esm({
267150
+ "../dashboard/src/lane-session-model.ts"() {
267151
+ "use strict";
267152
+ init_src2();
267153
+ }
267154
+ });
267155
+
266848
267156
  // ../dashboard/src/subtask-breakdown.ts
266849
267157
  var subtask_breakdown_exports = {};
266850
267158
  __export(subtask_breakdown_exports, {
@@ -266868,7 +267176,7 @@ __export(subtask_breakdown_exports, {
266868
267176
  stopSubtaskGeneration: () => stopSubtaskGeneration,
266869
267177
  subtaskStreamManager: () => subtaskStreamManager
266870
267178
  });
266871
- import { randomUUID as randomUUID48 } from "node:crypto";
267179
+ import { randomUUID as randomUUID49 } from "node:crypto";
266872
267180
  import { EventEmitter as EventEmitter35 } from "node:events";
266873
267181
  function __getSubtaskBreakdownDiagnostics() {
266874
267182
  return diagnostics3;
@@ -267022,7 +267330,8 @@ async function decomposeForTriage(description, rootDir, promptOverrides, store)
267022
267330
  return generateFallbackSubtasks(description);
267023
267331
  }
267024
267332
  const mcpServers = (await resolveMcpServersForStore(store ?? {})).servers;
267025
- const agent = await createFnAgent5({ cwd, systemPrompt, tools: "readonly", mcpServers });
267333
+ const subtaskModel = await resolveLaneSessionModel(store);
267334
+ const agent = await createFnAgent5({ cwd, systemPrompt, tools: "readonly", mcpServers, ...laneModelOptions(subtaskModel) });
267026
267335
  try {
267027
267336
  await agent.session.prompt(description);
267028
267337
  const messages3 = agent.session.state.messages;
@@ -267042,7 +267351,7 @@ async function decomposeForTriage(description, rootDir, promptOverrides, store)
267042
267351
  }
267043
267352
  }
267044
267353
  async function createSubtaskSession(initialDescription, store, rootDir, promptOverrides, projectId) {
267045
- const sessionId = randomUUID48();
267354
+ const sessionId = randomUUID49();
267046
267355
  const session = {
267047
267356
  sessionId,
267048
267357
  projectId,
@@ -267107,11 +267416,13 @@ async function generateSubtasks(sessionId, cwd, promptOverrides, store) {
267107
267416
  },
267108
267417
  async (abortSignal) => {
267109
267418
  const mcpServers = (await resolveMcpServersForStore(store ?? {})).servers;
267419
+ const streamingModel = await resolveLaneSessionModel(store);
267110
267420
  const agentPromise = createFnAgent5({
267111
267421
  cwd,
267112
267422
  systemPrompt,
267113
267423
  tools: "readonly",
267114
267424
  mcpServers,
267425
+ ...laneModelOptions(streamingModel),
267115
267426
  onThinking: (delta) => {
267116
267427
  const current = sessions2.get(sessionId);
267117
267428
  if (!current) return;
@@ -267325,6 +267636,7 @@ var init_subtask_breakdown = __esm({
267325
267636
  init_process_lifecycle();
267326
267637
  init_ai_session_diagnostics();
267327
267638
  init_ai_session_timeout();
267639
+ init_lane_session_model();
267328
267640
  init_src5();
267329
267641
  createFnAgent5 = createFnAgent2;
267330
267642
  diagnostics3 = createSessionDiagnostics("subtask-breakdown");
@@ -267476,7 +267788,7 @@ __export(mission_interview_exports, {
267476
267788
  stopMissionInterviewGeneration: () => stopMissionInterviewGeneration,
267477
267789
  submitMissionInterviewResponse: () => submitMissionInterviewResponse
267478
267790
  });
267479
- import { randomUUID as randomUUID49 } from "node:crypto";
267791
+ import { randomUUID as randomUUID50 } from "node:crypto";
267480
267792
  import { EventEmitter as EventEmitter36 } from "node:events";
267481
267793
  function __getMissionInterviewDiagnostics() {
267482
267794
  return diagnostics4;
@@ -268219,7 +268531,7 @@ async function createMissionInterviewSession(ip, missionTitle, rootDir, store, p
268219
268531
  `Rate limit exceeded. Maximum ${MAX_SESSIONS_PER_IP_PER_HOUR2} sessions per hour. Reset at ${resetTime?.toISOString() || "unknown"}`
268220
268532
  );
268221
268533
  }
268222
- const sessionId = randomUUID49();
268534
+ const sessionId = randomUUID50();
268223
268535
  const session = {
268224
268536
  id: sessionId,
268225
268537
  ip,
@@ -268682,7 +268994,7 @@ __export(milestone_slice_interview_exports, {
268682
268994
  stopMilestoneSliceInterviewGeneration: () => stopMilestoneSliceInterviewGeneration,
268683
268995
  submitTargetInterviewResponse: () => submitTargetInterviewResponse
268684
268996
  });
268685
- import { randomUUID as randomUUID50 } from "node:crypto";
268997
+ import { randomUUID as randomUUID51 } from "node:crypto";
268686
268998
  import { EventEmitter as EventEmitter37 } from "node:events";
268687
268999
  function parseTargetInterviewResponseImpl(text3) {
268688
269000
  const candidate = extractJsonCandidate3(text3);
@@ -269016,11 +269328,19 @@ async function createTargetInterviewAgent(session, rootDir, store, pluginRunner)
269016
269328
  await ensureEngineReady4();
269017
269329
  const skillContext = buildSessionSkillContextSync(null, "executor", rootDir, pluginRunner);
269018
269330
  const mcpServers = (await resolveMcpServersForStore(store)).servers;
269331
+ const model = await resolveLaneSessionModel(
269332
+ store,
269333
+ { provider: session.modelProvider, modelId: session.modelId },
269334
+ (reason) => diagnostics5.warn("Milestone/slice interview has no resolved provider/model pair; the runtime will use its built-in default model", { sessionId: session.id, operation: "resolve-interview-model", reason })
269335
+ );
269336
+ session.modelProvider = model.provider;
269337
+ session.modelId = model.modelId;
269019
269338
  return createFnAgent7({
269020
269339
  cwd: rootDir,
269021
269340
  systemPrompt: getSystemPrompt(session.targetType),
269022
269341
  tools: "readonly",
269023
269342
  mcpServers,
269343
+ ...laneModelOptions(model),
269024
269344
  allowMcpToolsInReadonly: true,
269025
269345
  customTools: [...createPlanningBoardTools(store)],
269026
269346
  /*
@@ -269303,7 +269623,7 @@ async function createTargetInterviewSession(ip, targetType, targetId, targetTitl
269303
269623
  `Rate limit exceeded. Maximum ${MAX_SESSIONS_PER_IP_PER_HOUR3} sessions per hour. Reset at ${resetTime?.toISOString() || "unknown"}`
269304
269624
  );
269305
269625
  }
269306
- const sessionId = randomUUID50();
269626
+ const sessionId = randomUUID51();
269307
269627
  const session = {
269308
269628
  id: sessionId,
269309
269629
  ip,
@@ -269548,6 +269868,7 @@ var init_milestone_slice_interview = __esm({
269548
269868
  init_mission_interview();
269549
269869
  init_src5();
269550
269870
  init_planning_board_tools();
269871
+ init_lane_session_model();
269551
269872
  diagnostics5 = createSessionDiagnostics("milestone-slice-interview");
269552
269873
  createFnAgent7 = createFnAgent2;
269553
269874
  SESSION_TTL_MS4 = 7 * 24 * 60 * 60 * 1e3;
@@ -279183,16 +279504,8 @@ function getHeadBranch(taskId) {
279183
279504
  return `fusion/${taskId.toLowerCase()}`;
279184
279505
  }
279185
279506
  function getDefaultSessionModel(settings) {
279186
- if (settings.defaultProviderOverride && settings.defaultModelIdOverride) {
279187
- return {
279188
- provider: settings.defaultProviderOverride,
279189
- modelId: settings.defaultModelIdOverride
279190
- };
279191
- }
279192
- return {
279193
- provider: settings.defaultProvider,
279194
- modelId: settings.defaultModelId
279195
- };
279507
+ const resolved = resolveProjectDefaultModel(settings);
279508
+ return resolved.provider && resolved.modelId ? { provider: resolved.provider, modelId: resolved.modelId } : { provider: void 0, modelId: void 0 };
279196
279509
  }
279197
279510
  async function pathExists(path14) {
279198
279511
  try {
@@ -279423,6 +279736,7 @@ var GIT_TIMEOUT_MS10, SESSION_PROMPT;
279423
279736
  var init_pr_conflict_resolver = __esm({
279424
279737
  "../dashboard/src/pr-conflict-resolver.ts"() {
279425
279738
  "use strict";
279739
+ init_src2();
279426
279740
  init_src5();
279427
279741
  init_resolve_diff_base();
279428
279742
  GIT_TIMEOUT_MS10 = 6e4;
@@ -279783,11 +280097,13 @@ async function refineText(text3, type, rootDir, promptOverrides, store) {
279783
280097
  }
279784
280098
  const effectivePrompt = resolvePrompt("ai-refine-system", promptOverrides);
279785
280099
  const mcpServers = (await resolveMcpServersForStore(store ?? {})).servers;
280100
+ const refineModel = await resolveLaneSessionModel(store);
279786
280101
  const agentResult = await createFnAgent8({
279787
280102
  cwd: rootDir,
279788
280103
  systemPrompt: effectivePrompt,
279789
280104
  tools: "readonly",
279790
- mcpServers
280105
+ mcpServers,
280106
+ ...laneModelOptions(refineModel)
279791
280107
  });
279792
280108
  if (!agentResult?.session) {
279793
280109
  throw new AiServiceError3("Failed to initialize AI agent");
@@ -279826,11 +280142,13 @@ async function draftGoalDescription(title2, rootDir, _promptOverrides, store) {
279826
280142
  throw new AiServiceError3("AI engine not available");
279827
280143
  }
279828
280144
  const mcpServers = (await resolveMcpServersForStore(store ?? {})).servers;
280145
+ const goalDraftModel = await resolveLaneSessionModel(store);
279829
280146
  const agentResult = await createFnAgent8({
279830
280147
  cwd: rootDir,
279831
280148
  systemPrompt: GOAL_DRAFT_SYSTEM_PROMPT,
279832
280149
  tools: "readonly",
279833
- mcpServers
280150
+ mcpServers,
280151
+ ...laneModelOptions(goalDraftModel)
279834
280152
  });
279835
280153
  if (!agentResult?.session) {
279836
280154
  throw new AiServiceError3("Failed to initialize AI agent");
@@ -279868,6 +280186,7 @@ var init_ai_refine = __esm({
279868
280186
  init_src2();
279869
280187
  init_src5();
279870
280188
  init_process_lifecycle();
280189
+ init_lane_session_model();
279871
280190
  createFnAgent8 = createFnAgent2;
279872
280191
  VALID_REFINEMENT_TYPES = [
279873
280192
  "clarify",
@@ -292138,7 +292457,7 @@ at this boundary regardless.`;
292138
292457
  });
292139
292458
 
292140
292459
  // ../dashboard/src/routes/register-planning-subtask-routes.ts
292141
- import { randomUUID as randomUUID51 } from "node:crypto";
292460
+ import { randomUUID as randomUUID52 } from "node:crypto";
292142
292461
  async function acquirePlanningCreateLock(sessionId) {
292143
292462
  const previous = planningCreateLocks.get(sessionId);
292144
292463
  let releaseGate;
@@ -293098,7 +293417,7 @@ function registerPlanningSubtaskRoutes(ctx, deps) {
293098
293417
  }
293099
293418
  session = await getDurablePlanningSession2(sessionId) ?? session;
293100
293419
  if (await returnLinkedTask(session)) return;
293101
- const claimOwnerToken = randomUUID51();
293420
+ const claimOwnerToken = randomUUID52();
293102
293421
  claimedOwnerToken = claimOwnerToken;
293103
293422
  claimedSessionId = sessionId;
293104
293423
  const claimStartedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -294272,7 +294591,7 @@ __export(chat_exports, {
294272
294591
  resolveFileReferences: () => resolveFileReferences
294273
294592
  });
294274
294593
  import { EventEmitter as EventEmitter40 } from "node:events";
294275
- import { randomUUID as randomUUID52 } from "node:crypto";
294594
+ import { randomUUID as randomUUID53 } from "node:crypto";
294276
294595
  import { existsSync as existsSync70 } from "node:fs";
294277
294596
  import { join as join92, resolve as resolve47, relative as relative20 } from "node:path";
294278
294597
  import { SessionManager as SessionManager3 } from "@earendil-works/pi-coding-agent";
@@ -294479,7 +294798,7 @@ function createTaskVerificationTools(taskStore, actionGateContext) {
294479
294798
  const settings = await taskStore.getSettings();
294480
294799
  const command = profile === "verify:fast" ? "pnpm verify:fast" : typeof settings.testCommand === "string" ? settings.testCommand : "";
294481
294800
  if (!command) return { content: [{ type: "text", text: "ERROR: the selected verification profile is not configured." }], isError: true, details: {} };
294482
- const created = await taskStore.createTaskVerificationRequest({ taskId, requestId: randomUUID52(), profile, command, scope: "workspace", requestedBy: "chat" });
294801
+ const created = await taskStore.createTaskVerificationRequest({ taskId, requestId: randomUUID53(), profile, command, scope: "workspace", requestedBy: "chat" });
294483
294802
  if (created.inFlightRequestId) return { content: [{ type: "text", text: `ERROR: verification request ${created.inFlightRequestId} is already in flight.` }], isError: true, details: created };
294484
294803
  return { content: [{ type: "text", text: `Queued verification request ${created.request.requestId} for ${taskId}.` }], details: created.request };
294485
294804
  }
@@ -296601,7 +296920,7 @@ var init_rate_limit = __esm({
296601
296920
  });
296602
296921
 
296603
296922
  // ../dashboard/src/routes/register-chat-routes.ts
296604
- import { randomUUID as randomUUID53 } from "node:crypto";
296923
+ import { randomUUID as randomUUID54 } from "node:crypto";
296605
296924
  import { createReadStream as createReadStream2 } from "node:fs";
296606
296925
  import { mkdir as mkdir30, rm as rm17, writeFile as writeFile33 } from "node:fs/promises";
296607
296926
  import { basename as basename23, join as join93, resolve as resolve48 } from "node:path";
@@ -296660,7 +296979,7 @@ function registerChatRoutes(ctx, deps) {
296660
296979
  const filePath = join93(sessionDir, filename);
296661
296980
  await writeFile33(filePath, file.buffer);
296662
296981
  return {
296663
- id: `att-${randomUUID53().slice(0, 8)}`,
296982
+ id: `att-${randomUUID54().slice(0, 8)}`,
296664
296983
  filename,
296665
296984
  originalName: file.originalname,
296666
296985
  mimeType: file.mimetype,
@@ -296762,6 +297081,55 @@ function registerChatRoutes(ctx, deps) {
296762
297081
  rethrowAsApiError9(err, "Failed to create task planner chat session");
296763
297082
  }
296764
297083
  });
297084
+ router.get("/chat/tags", rateLimit(RATE_LIMITS.api), async (req, res) => {
297085
+ try {
297086
+ const { projectId } = await getProjectContext2(req);
297087
+ const { chatStore } = await resolveScopedChatStore(projectId);
297088
+ res.json({ tags: await chatStore.listTags(projectId ?? null) });
297089
+ } catch (err) {
297090
+ rethrowAsApiError9(err, "Failed to list chat tags");
297091
+ }
297092
+ });
297093
+ router.post("/chat/tags", rateLimit(RATE_LIMITS.mutation), async (req, res) => {
297094
+ try {
297095
+ if (typeof req.body?.name !== "string") throw badRequest("name must be a string");
297096
+ const { projectId } = await getProjectContext2(req);
297097
+ const { chatStore } = await resolveScopedChatStore(projectId);
297098
+ const tag = await chatStore.createTag({ name: req.body.name, projectId: projectId ?? null });
297099
+ res.status(201).json({ tag });
297100
+ } catch (err) {
297101
+ if (err instanceof ApiError) throw err;
297102
+ const message = err instanceof Error ? err.message : "Invalid tag";
297103
+ if (message.includes("tag") || message.includes("Tag")) throw badRequest(message);
297104
+ rethrowAsApiError9(err, "Failed to create chat tag");
297105
+ }
297106
+ });
297107
+ router.patch("/chat/tags/:id", rateLimit(RATE_LIMITS.mutation), async (req, res) => {
297108
+ try {
297109
+ if (typeof req.body?.name !== "string") throw badRequest("name must be a string");
297110
+ const { projectId } = await getProjectContext2(req);
297111
+ const { chatStore } = await resolveScopedChatStore(projectId);
297112
+ const tag = await chatStore.renameTag(String(req.params.id), projectId ?? null, { name: req.body.name });
297113
+ if (!tag) throw notFound("Chat tag not found");
297114
+ res.json({ tag });
297115
+ } catch (err) {
297116
+ if (err instanceof ApiError) throw err;
297117
+ const message = err instanceof Error ? err.message : "Invalid tag";
297118
+ if (message.includes("tag") || message.includes("Tag")) throw badRequest(message);
297119
+ rethrowAsApiError9(err, "Failed to rename chat tag");
297120
+ }
297121
+ });
297122
+ router.delete("/chat/tags/:id", rateLimit(RATE_LIMITS.mutation), async (req, res) => {
297123
+ try {
297124
+ const { projectId } = await getProjectContext2(req);
297125
+ const { chatStore } = await resolveScopedChatStore(projectId);
297126
+ if (!await chatStore.deleteTag(String(req.params.id), projectId ?? null)) throw notFound("Chat tag not found");
297127
+ res.json({ success: true });
297128
+ } catch (err) {
297129
+ if (err instanceof ApiError) throw err;
297130
+ rethrowAsApiError9(err, "Failed to delete chat tag");
297131
+ }
297132
+ });
296765
297133
  router.get("/chat/sessions", rateLimit(RATE_LIMITS.api), async (req, res) => {
296766
297134
  try {
296767
297135
  const { projectId, status, agentId, lookup: lookup2, modelProvider, modelId, q: q2, titleOnly } = req.query;
@@ -296916,11 +297284,15 @@ function registerChatRoutes(ctx, deps) {
296916
297284
  modelProvider: rawModelProvider,
296917
297285
  modelId: rawModelId,
296918
297286
  agentId: rawAgentId,
296919
- pinned: rawPinned
297287
+ pinned: rawPinned,
297288
+ tagIds: rawTagIds
296920
297289
  } = req.body;
296921
297290
  if (status !== void 0 && status !== "active" && status !== "archived") {
296922
297291
  throw badRequest("status must be 'active' or 'archived'");
296923
297292
  }
297293
+ if (rawTagIds !== void 0 && (!Array.isArray(rawTagIds) || rawTagIds.some((id) => typeof id !== "string" || !id.trim()))) {
297294
+ throw badRequest("tagIds must be an array of tag IDs");
297295
+ }
296924
297296
  if (rawPinned !== void 0 && typeof rawPinned !== "boolean") {
296925
297297
  throw badRequest("pinned must be a boolean");
296926
297298
  }
@@ -296952,6 +297324,15 @@ function registerChatRoutes(ctx, deps) {
296952
297324
  if (!session) {
296953
297325
  throw notFound(`Chat session ${sessionId} not found`);
296954
297326
  }
297327
+ if (rawTagIds !== void 0) {
297328
+ try {
297329
+ session = await chatStore.replaceSessionTags(sessionId, session.projectId ?? null, rawTagIds.map((id) => id.trim()));
297330
+ } catch (err) {
297331
+ const message = err instanceof Error ? err.message : "Unable to update conversation tags";
297332
+ throw badRequest(message);
297333
+ }
297334
+ if (!session) throw notFound(`Chat session ${sessionId} not found`);
297335
+ }
296955
297336
  if (rawPinned !== void 0) {
296956
297337
  try {
296957
297338
  session = await chatStore.setSessionPinned(sessionId, rawPinned);
@@ -297364,7 +297745,7 @@ var init_register_chat_routes = __esm({
297364
297745
  });
297365
297746
 
297366
297747
  // ../dashboard/src/routes/register-chat-room-routes.ts
297367
- import { randomUUID as randomUUID54 } from "node:crypto";
297748
+ import { randomUUID as randomUUID55 } from "node:crypto";
297368
297749
  import { createReadStream as createReadStream3 } from "node:fs";
297369
297750
  import { mkdir as mkdir31, writeFile as writeFile34 } from "node:fs/promises";
297370
297751
  import { basename as basename24, join as join94, resolve as resolve49 } from "node:path";
@@ -297705,7 +298086,7 @@ function registerChatRoomRoutes(ctx, deps) {
297705
298086
  const filePath = join94(roomDir, filename);
297706
298087
  await writeFile34(filePath, file.buffer);
297707
298088
  const attachment = {
297708
- id: `att-${randomUUID54().slice(0, 8)}`,
298089
+ id: `att-${randomUUID55().slice(0, 8)}`,
297709
298090
  filename,
297710
298091
  originalName: file.originalname,
297711
298092
  mimeType: file.mimetype,
@@ -305997,7 +306378,7 @@ var init_register_gitlab = __esm({
305997
306378
 
305998
306379
  // ../dashboard/src/terminal.ts
305999
306380
  import { spawn as spawn11 } from "node:child_process";
306000
- import { randomUUID as randomUUID55 } from "node:crypto";
306381
+ import { randomUUID as randomUUID56 } from "node:crypto";
306001
306382
  import { EventEmitter as EventEmitter42 } from "node:events";
306002
306383
  function extractBaseCommand(command) {
306003
306384
  let trimmed = command.trim();
@@ -306157,7 +306538,7 @@ var init_terminal = __esm({
306157
306538
  if (!validation.valid) {
306158
306539
  return { sessionId: "", error: validation.error };
306159
306540
  }
306160
- const sessionId = randomUUID55();
306541
+ const sessionId = randomUUID56();
306161
306542
  const childProcess6 = spawn11(command, [], {
306162
306543
  cwd,
306163
306544
  shell: true,
@@ -334941,7 +335322,7 @@ var init_register_node_routes = __esm({
334941
335322
  });
334942
335323
 
334943
335324
  // ../dashboard/src/routes/register-docker-node-routes.ts
334944
- import { randomUUID as randomUUID56 } from "node:crypto";
335325
+ import { randomUUID as randomUUID57 } from "node:crypto";
334945
335326
  function sanitizeHostConfig(input) {
334946
335327
  const host = input ?? {};
334947
335328
  return {
@@ -335320,7 +335701,7 @@ var init_register_docker_node_routes = __esm({
335320
335701
  if (!managedNode) {
335321
335702
  throw notFound("Managed Docker node not found");
335322
335703
  }
335323
- const newKey = randomUUID56().replace(/-/g, "");
335704
+ const newKey = randomUUID57().replace(/-/g, "");
335324
335705
  await central.updateManagedDockerNode(managedId, { apiKey: newKey });
335325
335706
  if (managedNode.nodeId) {
335326
335707
  await central.updateNode(managedNode.nodeId, { apiKey: newKey });
@@ -343831,7 +344212,7 @@ var init_register_agent_reflection_rating_routes = __esm({
343831
344212
  });
343832
344213
 
343833
344214
  // ../dashboard/src/agent-generation.ts
343834
- import { randomUUID as randomUUID57 } from "node:crypto";
344215
+ import { randomUUID as randomUUID58 } from "node:crypto";
343835
344216
  async function initPromptCatalog() {
343836
344217
  if (promptCatalogReady) return;
343837
344218
  try {
@@ -344037,7 +344418,7 @@ async function startAgentGeneration(ip, roleDescription) {
344037
344418
  `Rate limit exceeded. Maximum ${MAX_SESSIONS_PER_IP_PER_HOUR4} generation sessions per hour. Reset at ${resetTime?.toISOString() || "unknown"}`
344038
344419
  );
344039
344420
  }
344040
- const sessionId = randomUUID57();
344421
+ const sessionId = randomUUID58();
344041
344422
  const session = {
344042
344423
  id: sessionId,
344043
344424
  ip,
@@ -344074,11 +344455,13 @@ async function generateSpecWithAI(session, rootDir, promptOverrides, store) {
344074
344455
  }
344075
344456
  const effectiveSystemPrompt = resolvePrompt2("agent-generation-system", promptOverrides) || AGENT_GENERATION_SYSTEM_PROMPT;
344076
344457
  const mcpServers = (await resolveMcpServersForStore(store ?? {})).servers;
344458
+ const generationModel = await resolveLaneSessionModel(store);
344077
344459
  const agent = await createFnAgent11({
344078
344460
  cwd: rootDir,
344079
344461
  systemPrompt: effectiveSystemPrompt,
344080
344462
  tools: "none",
344081
- mcpServers
344463
+ mcpServers,
344464
+ ...laneModelOptions(generationModel)
344082
344465
  });
344083
344466
  try {
344084
344467
  await agent.session.prompt(
@@ -344131,6 +344514,7 @@ var init_agent_generation = __esm({
344131
344514
  "use strict";
344132
344515
  init_ai_session_diagnostics();
344133
344516
  init_process_lifecycle();
344517
+ init_lane_session_model();
344134
344518
  init_src5();
344135
344519
  resolvePrompt2 = () => "";
344136
344520
  promptCatalogReady = false;
@@ -344233,7 +344617,7 @@ __export(agent_onboarding_exports, {
344233
344617
  startAgentOnboardingSession: () => startAgentOnboardingSession,
344234
344618
  stopAgentOnboardingGeneration: () => stopAgentOnboardingGeneration
344235
344619
  });
344236
- import { randomUUID as randomUUID58 } from "node:crypto";
344620
+ import { randomUUID as randomUUID59 } from "node:crypto";
344237
344621
  import { EventEmitter as EventEmitter43 } from "node:events";
344238
344622
  function extractJsonCandidate6(text3) {
344239
344623
  const codeBlockMatch = text3.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
@@ -344333,7 +344717,7 @@ Current agent configuration:
344333
344717
  ${currentConfigLines}`;
344334
344718
  }
344335
344719
  async function startAgentOnboardingSession(ip, initialContext, rootDir, modelProvider, modelId, promptOverrides, pluginRunner, store) {
344336
- const id = randomUUID58();
344720
+ const id = randomUUID59();
344337
344721
  const mode = initialContext.mode ?? "create";
344338
344722
  const session = {
344339
344723
  id,
@@ -347105,7 +347489,7 @@ var init_src8 = __esm({
347105
347489
 
347106
347490
  // ../../plugins/fusion-plugin-openclaw-runtime/src/pi-module.ts
347107
347491
  import { spawn as spawn15 } from "node:child_process";
347108
- import { randomUUID as randomUUID59 } from "node:crypto";
347492
+ import { randomUUID as randomUUID60 } from "node:crypto";
347109
347493
  import { readFile as readFile48 } from "node:fs/promises";
347110
347494
  function asString2(v2) {
347111
347495
  return typeof v2 === "string" && v2.trim() !== "" ? v2.trim() : void 0;
@@ -347165,7 +347549,7 @@ function extractStderrError(stderr, stdout) {
347165
347549
  }
347166
347550
  function createCliSession(opts) {
347167
347551
  return {
347168
- sessionId: randomUUID59(),
347552
+ sessionId: randomUUID60(),
347169
347553
  agentId: opts.agentId ?? DEFAULT_AGENT_ID,
347170
347554
  systemPrompt: opts.systemPrompt,
347171
347555
  messages: [{ role: "developer", content: opts.systemPrompt }],
@@ -347360,7 +347744,7 @@ var init_mcp_config2 = __esm({
347360
347744
  });
347361
347745
 
347362
347746
  // ../../plugins/fusion-plugin-openclaw-runtime/src/runtime-adapter.ts
347363
- import { randomUUID as randomUUID60 } from "node:crypto";
347747
+ import { randomUUID as randomUUID61 } from "node:crypto";
347364
347748
  var OpenClawRuntimeAdapter;
347365
347749
  var init_runtime_adapter2 = __esm({
347366
347750
  "../../plugins/fusion-plugin-openclaw-runtime/src/runtime-adapter.ts"() {
@@ -347383,8 +347767,8 @@ var init_runtime_adapter2 = __esm({
347383
347767
  let mcpProfile;
347384
347768
  let mcpConfigPath;
347385
347769
  if (toolDefs.length > 0) {
347386
- const bridge = writeOpenClawMcpBridgeFiles(toolDefs, randomUUID60());
347387
- mcpProfile = `fusion-${randomUUID60()}`;
347770
+ const bridge = writeOpenClawMcpBridgeFiles(toolDefs, randomUUID61());
347771
+ mcpProfile = `fusion-${randomUUID61()}`;
347388
347772
  mcpConfigPath = bridge.serverConfigPath;
347389
347773
  await configureOpenClawMcpServer({
347390
347774
  binaryPath: this.config.binaryPath,
@@ -351438,7 +351822,7 @@ import { existsSync as existsSync76, writeFileSync as writeFileSync15 } from "no
351438
351822
  import { tmpdir as tmpdir17 } from "node:os";
351439
351823
  import { dirname as dirname45, join as join106 } from "node:path";
351440
351824
  import { fileURLToPath as fileURLToPath11 } from "node:url";
351441
- import { randomUUID as randomUUID61 } from "node:crypto";
351825
+ import { randomUUID as randomUUID62 } from "node:crypto";
351442
351826
  function toolsToMcpToolDefs2(tools) {
351443
351827
  if (!Array.isArray(tools)) return [];
351444
351828
  return tools.filter((tool) => tool && typeof tool.name === "string" && tool.name.trim().length > 0 && !BUILT_IN_TOOL_NAMES2.has(tool.name)).map((tool) => ({
@@ -351489,7 +351873,7 @@ async function startFusionToolBridge(tools) {
351489
351873
  byName.set(tool.name, tool);
351490
351874
  }
351491
351875
  }
351492
- const schemaPath = join106(tmpdir17(), `fusion-grok-mcp-schemas-${process.pid}-${randomUUID61()}.json`);
351876
+ const schemaPath = join106(tmpdir17(), `fusion-grok-mcp-schemas-${process.pid}-${randomUUID62()}.json`);
351493
351877
  writeFileSync15(schemaPath, JSON.stringify(defs));
351494
351878
  const server = createServer2(async (req, res) => {
351495
351879
  if (req.method !== "POST" || req.url !== "/tool-call") {
@@ -351515,7 +351899,7 @@ async function startFusionToolBridge(tools) {
351515
351899
  return;
351516
351900
  }
351517
351901
  try {
351518
- const result = await tool.execute(`grok-mcp-${randomUUID61()}`, parsed.arguments ?? {}, void 0, void 0, void 0);
351902
+ const result = await tool.execute(`grok-mcp-${randomUUID62()}`, parsed.arguments ?? {}, void 0, void 0, void 0);
351519
351903
  res.statusCode = 200;
351520
351904
  res.setHeader("content-type", "application/json");
351521
351905
  res.end(
@@ -353559,7 +353943,7 @@ import { existsSync as existsSync80, rmSync as rmSync13, writeFileSync as writeF
353559
353943
  import { tmpdir as tmpdir19 } from "node:os";
353560
353944
  import { dirname as dirname50, join as join111 } from "node:path";
353561
353945
  import { fileURLToPath as fileURLToPath15 } from "node:url";
353562
- import { randomBytes as randomBytes8, randomUUID as randomUUID62 } from "node:crypto";
353946
+ import { randomBytes as randomBytes8, randomUUID as randomUUID63 } from "node:crypto";
353563
353947
  function toolsToMcpToolDefs3(tools) {
353564
353948
  if (!Array.isArray(tools)) return [];
353565
353949
  return tools.filter((tool) => tool && typeof tool.name === "string" && tool.name.trim().length > 0 && !BUILT_IN_TOOL_NAMES3.has(tool.name)).map((tool) => ({
@@ -353610,7 +353994,7 @@ async function startFusionToolBridge2(tools, options = {}) {
353610
353994
  byName.set(tool.name, tool);
353611
353995
  }
353612
353996
  }
353613
- const schemaPath = join111(tmpdir19(), `fusion-claude-mcp-schemas-${process.pid}-${randomUUID62()}.json`);
353997
+ const schemaPath = join111(tmpdir19(), `fusion-claude-mcp-schemas-${process.pid}-${randomUUID63()}.json`);
353614
353998
  writeFileSync17(schemaPath, JSON.stringify(defs));
353615
353999
  const capabilityToken = randomBytes8(32).toString("base64url");
353616
354000
  const server = createServer3(async (req, res) => {
@@ -353675,7 +354059,7 @@ async function startFusionToolBridge2(tools, options = {}) {
353675
354059
  return;
353676
354060
  }
353677
354061
  try {
353678
- const result = await tool.execute(`claude-mcp-${randomUUID62()}`, parsed.arguments ?? {}, void 0, void 0, void 0);
354062
+ const result = await tool.execute(`claude-mcp-${randomUUID63()}`, parsed.arguments ?? {}, void 0, void 0, void 0);
353679
354063
  res.statusCode = 200;
353680
354064
  res.setHeader("content-type", "application/json");
353681
354065
  res.end(
@@ -355752,7 +356136,7 @@ import { existsSync as existsSync82, unlinkSync as unlinkSync5, writeFileSync as
355752
356136
  import { tmpdir as tmpdir20 } from "node:os";
355753
356137
  import { dirname as dirname53, join as join114 } from "node:path";
355754
356138
  import { fileURLToPath as fileURLToPath17 } from "node:url";
355755
- import { randomUUID as randomUUID63 } from "node:crypto";
356139
+ import { randomUUID as randomUUID64 } from "node:crypto";
355756
356140
  function toolsToMcpToolDefs4(tools) {
355757
356141
  if (!Array.isArray(tools)) return [];
355758
356142
  return tools.filter(
@@ -355809,7 +356193,7 @@ async function startFusionToolBridge3(tools) {
355809
356193
  byName.set(tool.name, tool);
355810
356194
  }
355811
356195
  }
355812
- const schemaPath = join114(tmpdir20(), `fusion-omp-mcp-schemas-${process.pid}-${randomUUID63()}.json`);
356196
+ const schemaPath = join114(tmpdir20(), `fusion-omp-mcp-schemas-${process.pid}-${randomUUID64()}.json`);
355813
356197
  writeFileSync18(schemaPath, JSON.stringify(defs));
355814
356198
  const server = createServer4(async (req, res) => {
355815
356199
  if (req.method !== "POST" || req.url !== "/tool-call") {
@@ -355836,7 +356220,7 @@ async function startFusionToolBridge3(tools) {
355836
356220
  }
355837
356221
  try {
355838
356222
  const result = await tool.execute(
355839
- `omp-mcp-${randomUUID63()}`,
356223
+ `omp-mcp-${randomUUID64()}`,
355840
356224
  parsed.arguments ?? {},
355841
356225
  void 0,
355842
356226
  void 0,
@@ -356932,7 +357316,7 @@ var init_paperclip_client = __esm({
356932
357316
  });
356933
357317
 
356934
357318
  // ../../plugins/fusion-plugin-paperclip-runtime/src/runtime-adapter.ts
356935
- import { randomUUID as randomUUID64 } from "node:crypto";
357319
+ import { randomUUID as randomUUID65 } from "node:crypto";
356936
357320
  function sleep7(ms2) {
356937
357321
  return new Promise((resolve102) => setTimeout(resolve102, ms2));
356938
357322
  }
@@ -357061,7 +357445,7 @@ var init_runtime_adapter10 = __esm({
357061
357445
  apiKey: effectiveApiKey,
357062
357446
  agentId,
357063
357447
  companyId,
357064
- sessionId: randomUUID64(),
357448
+ sessionId: randomUUID65(),
357065
357449
  systemPrompt: options.systemPrompt,
357066
357450
  cwd: options.cwd,
357067
357451
  mode: normalizeMode3(this.config.mode),
@@ -360489,7 +360873,7 @@ var init_command_center_csv = __esm({
360489
360873
  });
360490
360874
 
360491
360875
  // ../dashboard/src/monitor-store.ts
360492
- import { randomUUID as randomUUID65 } from "node:crypto";
360876
+ import { randomUUID as randomUUID66 } from "node:crypto";
360493
360877
  function monitorProjectId(layer) {
360494
360878
  if (!layer.projectId) {
360495
360879
  throw new Error("PostgreSQL monitor writes require asyncLayer.projectId");
@@ -360521,7 +360905,7 @@ async function recordDeployment(db, input) {
360521
360905
  return recordDeploymentAsync(layer.db, input, monitorProjectId(layer));
360522
360906
  }
360523
360907
  const sqliteDb = db;
360524
- const deploymentId = input.deploymentId?.trim() || `dep-${randomUUID65()}`;
360908
+ const deploymentId = input.deploymentId?.trim() || `dep-${randomUUID66()}`;
360525
360909
  const now = (/* @__PURE__ */ new Date()).toISOString();
360526
360910
  const deployedAt = input.deployedAt ?? now;
360527
360911
  const meta2 = input.meta ? JSON.stringify(input.meta) : null;
@@ -360587,7 +360971,7 @@ async function ingestIncidentSignal(db, input) {
360587
360971
  const updated = getIncident(sqliteDb, existing.incidentId);
360588
360972
  return { incident: updated ?? existing, created: false };
360589
360973
  }
360590
- const incidentId = `inc-${randomUUID65()}`;
360974
+ const incidentId = `inc-${randomUUID66()}`;
360591
360975
  const meta2 = {
360592
360976
  ...input.meta ?? {},
360593
360977
  [OCCURRENCES_META_KEY2]: 1,
@@ -365277,7 +365661,7 @@ var init_fn_binary_local_install = __esm({
365277
365661
  });
365278
365662
 
365279
365663
  // ../dashboard/src/routes/register-system-routes.ts
365280
- import { randomUUID as randomUUID66 } from "node:crypto";
365664
+ import { randomUUID as randomUUID67 } from "node:crypto";
365281
365665
  import { existsSync as existsSync86 } from "node:fs";
365282
365666
  import { join as join121 } from "node:path";
365283
365667
  function rejectCrossOrigin(req, res) {
@@ -365474,7 +365858,7 @@ function registerSystemRoutes(ctx, deps) {
365474
365858
  throw new ApiError(409, `Build script missing: ${scriptPath}`);
365475
365859
  }
365476
365860
  const job = {
365477
- id: randomUUID66(),
365861
+ id: randomUUID67(),
365478
365862
  kind: "rebuild",
365479
365863
  scope,
365480
365864
  restartAfter,
@@ -365760,7 +366144,7 @@ function registerSystemRoutes(ctx, deps) {
365760
366144
  throw new ApiError(409, `A ${activeJob.kind}/${activeJob.scope} job is already running`);
365761
366145
  }
365762
366146
  const job = {
365763
- id: randomUUID66(),
366147
+ id: randomUUID67(),
365764
366148
  kind,
365765
366149
  scope,
365766
366150
  restartAfter,
@@ -376999,7 +377383,7 @@ var init_provider_health_monitor = __esm({
376999
377383
 
377000
377384
  // ../dashboard/src/server.ts
377001
377385
  import express2 from "express";
377002
- import { randomUUID as randomUUID67 } from "node:crypto";
377386
+ import { randomUUID as randomUUID68 } from "node:crypto";
377003
377387
  import { join as join126, dirname as dirname57 } from "node:path";
377004
377388
  import { existsSync as existsSync88, readFileSync as readFileSync32, statSync as statSync16 } from "node:fs";
377005
377389
  import { fileURLToPath as fileURLToPath18 } from "node:url";
@@ -378429,7 +378813,7 @@ function setupBadgeWebSocket(app, server, store, options) {
378429
378813
  const dashboardApp = app;
378430
378814
  const wsManager = new WebSocketManager();
378431
378815
  const badgeSnapshots = /* @__PURE__ */ new Map();
378432
- const serverId = randomUUID67();
378816
+ const serverId = randomUUID68();
378433
378817
  const badgePubSub = options?.badgePubSub ?? createBadgePubSub({ sourceId: serverId });
378434
378818
  void badgePubSub.start();
378435
378819
  const scopedStores = /* @__PURE__ */ new Map();
@@ -378571,7 +378955,7 @@ function setupBadgeWebSocket(app, server, store, options) {
378571
378955
  const url2 = new URL(req.url || "", `http://${req.headers.host}`);
378572
378956
  const projectId = url2.searchParams.get("projectId") ?? "default";
378573
378957
  void ensureScopedListeners(projectId);
378574
- wsManager.addClient(ws2, randomUUID67(), projectId);
378958
+ wsManager.addClient(ws2, randomUUID68(), projectId);
378575
378959
  });
378576
378960
  server.once("close", async () => {
378577
378961
  for (const cleanup of scopedCleanups.values()) {
@@ -384987,7 +385371,8 @@ function DashboardApp({ controller }) {
384987
385371
  controller.setInteractiveView("files");
384988
385372
  return;
384989
385373
  }
384990
- if (input === "t" || input === "T") {
385374
+ const utilitiesOwnsInput = state.mode === "status" && state.activeSection === "utilities" && !logsFocused;
385375
+ if ((input === "t" || input === "T") && !utilitiesOwnsInput) {
384991
385376
  controller.setMode("interactive");
384992
385377
  controller.setInteractiveView("git");
384993
385378
  return;
@@ -388701,7 +389086,7 @@ function tuiDebug2(tag, data) {
388701
389086
  } catch {
388702
389087
  }
388703
389088
  }
388704
- var TUI_DEBUG_LOG2, DashboardTUI;
389089
+ var TUI_DEBUG_LOG2, PAUSE_CONFIRM_WINDOW_MS, DashboardTUI;
388705
389090
  var init_controller = __esm({
388706
389091
  "src/commands/dashboard-tui/controller.ts"() {
388707
389092
  "use strict";
@@ -388709,6 +389094,7 @@ var init_controller = __esm({
388709
389094
  init_log_ring_buffer();
388710
389095
  init_state();
388711
389096
  TUI_DEBUG_LOG2 = process.env.FUSION_TUI_DEBUG_LOG;
389097
+ PAUSE_CONFIRM_WINDOW_MS = 5e3;
388712
389098
  DashboardTUI = class {
388713
389099
  // State fields mirror the original private layout so tests can access them.
388714
389100
  activeSection = "system";
@@ -388749,6 +389135,15 @@ var init_controller = __esm({
388749
389135
  lastAutoKillAt = 0;
388750
389136
  clipboardFlash = null;
388751
389137
  clipboardFlashTimer = null;
389138
+ /*
389139
+ FNXC:DashboardTui 2026-07-24-18:40:
389140
+ Pausing the engine from Utilities [t] is a silent, board-wide stop: triage and
389141
+ planning stall while in-flight tasks drain, so the board keeps moving for
389142
+ minutes and the cause is invisible from the terminal. One unmodified keystroke
389143
+ must not do that — the first `t` arms this deadline and only a second `t`
389144
+ inside the window pauses. Resuming is not destructive and stays single-press.
389145
+ */
389146
+ pausePendingConfirmUntil = 0;
388752
389147
  interactiveData = null;
388753
389148
  interactiveView = "board";
388754
389149
  interactiveInputLocked = false;
@@ -389167,6 +389562,22 @@ var init_controller = __esm({
389167
389562
  const all = this.logBuffer.getAll();
389168
389563
  return this.logsSeverityFilter === "all" ? all : all.filter((e) => e.level === this.logsSeverityFilter);
389169
389564
  }
389565
+ /**
389566
+ * Two-press confirmation for an engine pause initiated from Utilities [t].
389567
+ *
389568
+ * Returns true when the caller may pause: either a prior press armed the
389569
+ * window and it has not expired, or this call arms it and returns false so the
389570
+ * caller can prompt. Any successful confirmation disarms, so a third press
389571
+ * re-arms rather than toggling twice.
389572
+ */
389573
+ consumePauseConfirmation(now = Date.now()) {
389574
+ if (this.pausePendingConfirmUntil > now) {
389575
+ this.pausePendingConfirmUntil = 0;
389576
+ return true;
389577
+ }
389578
+ this.pausePendingConfirmUntil = now + PAUSE_CONFIRM_WINDOW_MS;
389579
+ return false;
389580
+ }
389170
389581
  async handleUtilityAction(key) {
389171
389582
  if (!this.callbacks) return;
389172
389583
  switch (key.toLowerCase()) {
@@ -389180,6 +389591,13 @@ var init_controller = __esm({
389180
389591
  case "t":
389181
389592
  if (this.systemInfo) {
389182
389593
  const newPaused = this.systemInfo.engineMode !== "paused";
389594
+ if (newPaused && !this.consumePauseConfirmation()) {
389595
+ this.warn(
389596
+ `Press [t] again within ${Math.round(PAUSE_CONFIRM_WINDOW_MS / 1e3)}s to pause the engine (stops triage, planning, and new dispatch).`,
389597
+ "engine"
389598
+ );
389599
+ break;
389600
+ }
389183
389601
  const newSettings = await this.callbacks.onTogglePause(newPaused);
389184
389602
  const newEngineMode = newSettings.enginePaused ? "paused" : "active";
389185
389603
  this.setSystemInfo({ ...this.systemInfo, engineMode: newEngineMode });