@overdeck/desktop 0.42.0 → 0.44.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 (90) hide show
  1. package/package.json +1 -1
  2. package/resources/icon.ico +0 -0
  3. package/resources/icon.png +0 -0
  4. package/server/public/assets/{architectureDiagram-3BPJPVTR-CDm4DtgX.js → architectureDiagram-3BPJPVTR-D1cX9STH.js} +1 -1
  5. package/server/public/assets/{blockDiagram-GPEHLZMM-B0CHnHbL.js → blockDiagram-GPEHLZMM-_U_GESDt.js} +1 -1
  6. package/server/public/assets/{c4Diagram-AAUBKEIU-CwHAm-Sh.js → c4Diagram-AAUBKEIU-DNF40ymm.js} +1 -1
  7. package/server/public/assets/channel-Dz_N3rZ1.js +1 -0
  8. package/server/public/assets/{chunk-2J33WTMH-BIWv54To.js → chunk-2J33WTMH-CphN4I1h.js} +1 -1
  9. package/server/public/assets/{chunk-3OPIFGDE-BIj0tSkO.js → chunk-3OPIFGDE-pHasFymF.js} +1 -1
  10. package/server/public/assets/{chunk-4BX2VUAB-xpGve5oU.js → chunk-4BX2VUAB-CDe6cmmN.js} +1 -1
  11. package/server/public/assets/chunk-55IACEB6-CB9aM4Zf.js +1 -0
  12. package/server/public/assets/{chunk-5ZQYHXKU-DYb235_b.js → chunk-5ZQYHXKU-CKfVKs9N.js} +1 -1
  13. package/server/public/assets/{chunk-727SXJPM-BYV0QAgl.js → chunk-727SXJPM-D4mt-WN2.js} +1 -1
  14. package/server/public/assets/{chunk-AQP2D5EJ-Cb8WTwxH.js → chunk-AQP2D5EJ-DKi5ScG8.js} +1 -1
  15. package/server/public/assets/chunk-BO2N2NFS-DurskaRQ.js +103 -0
  16. package/server/public/assets/{chunk-BSJP7CBP-DNZJE3LD.js → chunk-BSJP7CBP-B1PFLITi.js} +1 -1
  17. package/server/public/assets/{chunk-CSCIHK7Q-Cd19qyMq.js → chunk-CSCIHK7Q-BnyS1BnJ.js} +1 -1
  18. package/server/public/assets/{chunk-FMBD7UC4-BCMOtjng.js → chunk-FMBD7UC4-BB-NMiJi.js} +1 -1
  19. package/server/public/assets/{chunk-K2UTITRG-BDx74R_n.js → chunk-K2UTITRG-CopEAH22.js} +3 -3
  20. package/server/public/assets/{chunk-KSCS5N6A-C9ZcXuPw.js → chunk-KSCS5N6A-BF9pAvxk.js} +1 -1
  21. package/server/public/assets/{chunk-L5ZTLDWV-DERZu8R6.js → chunk-L5ZTLDWV-CSCc1dpF.js} +1 -1
  22. package/server/public/assets/{chunk-LZXEDZCA-r8NSoc_I.js → chunk-LZXEDZCA-AFwhyAh-.js} +2 -2
  23. package/server/public/assets/{chunk-ND2GUHAM-U8N4hXOQ.js → chunk-ND2GUHAM-C63t7vfP.js} +1 -1
  24. package/server/public/assets/{chunk-NZK2D7GU-Cdikaqy1.js → chunk-NZK2D7GU-D1aXVmQS.js} +1 -1
  25. package/server/public/assets/{chunk-O5CBEL6O-NPUwuba7.js → chunk-O5CBEL6O-U50Oge0P.js} +1 -1
  26. package/server/public/assets/chunk-QZHKN3VN-DTNr_gzK.js +1 -0
  27. package/server/public/assets/chunk-WU5MYG2G-CI9Sd9Ej.js +1 -0
  28. package/server/public/assets/{chunk-XPW4576I-D4sOPQ12.js → chunk-XPW4576I-TDS4g8F-.js} +1 -1
  29. package/server/public/assets/classDiagram-4FO5ZUOK-ZZslPdIZ.js +1 -0
  30. package/server/public/assets/classDiagram-v2-Q7XG4LA2-ClxLOE6-.js +1 -0
  31. package/server/public/assets/{cose-bilkent-S5V4N54A-DbIsCp9l.js → cose-bilkent-S5V4N54A-CXK4TeG6.js} +1 -1
  32. package/server/public/assets/{dagre-BM42HDAG-BjoYNKH1.js → dagre-BM42HDAG-CekTT-9t.js} +1 -1
  33. package/server/public/assets/{diagram-2AECGRRQ-BQDDtbjI.js → diagram-2AECGRRQ-C-1uR13O.js} +1 -1
  34. package/server/public/assets/{diagram-5GNKFQAL-B5QGvpdq.js → diagram-5GNKFQAL-B-7wXipw.js} +1 -1
  35. package/server/public/assets/{diagram-KO2AKTUF-BPB4GnYb.js → diagram-KO2AKTUF-BkDyAgsg.js} +1 -1
  36. package/server/public/assets/{diagram-LMA3HP47-ChM6kUb7.js → diagram-LMA3HP47-D9jVbxR4.js} +1 -1
  37. package/server/public/assets/{diagram-OG6HWLK6-DGdm9pVn.js → diagram-OG6HWLK6-Dk7-KYG7.js} +1 -1
  38. package/server/public/assets/{dist-YEo-2Oy8.js → dist-CaEw8nYm.js} +5 -5
  39. package/server/public/assets/{dist-AFfBwqj3.js → dist-Dm4--nOM.js} +1 -1
  40. package/server/public/assets/{erDiagram-TEJ5UH35-CHXlL8Uu.js → erDiagram-TEJ5UH35-BCzAAnUJ.js} +1 -1
  41. package/server/public/assets/{flowDiagram-I6XJVG4X-ChzzyfU2.js → flowDiagram-I6XJVG4X-WjKltgsz.js} +1 -1
  42. package/server/public/assets/ganttDiagram-6RSMTGT7-CFh4T1Yl.js +292 -0
  43. package/server/public/assets/{gitGraphDiagram-PVQCEYII-Ba1IlQOc.js → gitGraphDiagram-PVQCEYII-DQNkTLKB.js} +1 -1
  44. package/server/public/assets/{highlighted-body-OFNGDK62-Bq_qtCsW.js → highlighted-body-OFNGDK62--ggf-1jf.js} +1 -1
  45. package/server/public/assets/image-GAAHSSAO-rafcvWHh.js +1 -0
  46. package/server/public/assets/index-B-m-cSFg.css +1 -0
  47. package/server/public/assets/index-CuIG3f59.js +796 -0
  48. package/server/public/assets/{infoDiagram-5YYISTIA-Du9oCbUR.js → infoDiagram-5YYISTIA-kt8p6J3W.js} +1 -1
  49. package/server/public/assets/{ishikawaDiagram-YF4QCWOH-BQBblkcl.js → ishikawaDiagram-YF4QCWOH-CXB66ltc.js} +1 -1
  50. package/server/public/assets/{journeyDiagram-JHISSGLW-DalMBcZT.js → journeyDiagram-JHISSGLW-CSevbeC_.js} +1 -1
  51. package/server/public/assets/{kanban-definition-UN3LZRKU-ZZJcEe1c.js → kanban-definition-UN3LZRKU-f3SXWdY0.js} +1 -1
  52. package/server/public/assets/{line-DSWSgYSV.js → line-D5OFHSZd.js} +1 -1
  53. package/server/public/assets/linear-B_aOIp4o.js +1 -0
  54. package/server/public/assets/mermaid-GHXKKRXX-DvZawyn9.js +1 -0
  55. package/server/public/assets/{mermaid-parser.core-Yy_VTKKl.js → mermaid-parser.core-Cww26sHC.js} +1 -1
  56. package/server/public/assets/{mindmap-definition-RKZ34NQL-CZN2bKCU.js → mindmap-definition-RKZ34NQL-DCCvz6n-.js} +1 -1
  57. package/server/public/assets/{pieDiagram-4H26LBE5-B0yqF_w8.js → pieDiagram-4H26LBE5-_QEBgJ8d.js} +1 -1
  58. package/server/public/assets/{prod-DhREhukv.js → prod-oCB7Fr_u.js} +5 -5
  59. package/server/public/assets/{quadrantDiagram-W4KKPZXB-CdByrHkW.js → quadrantDiagram-W4KKPZXB-CAbpwVBa.js} +3 -3
  60. package/server/public/assets/{requirementDiagram-4Y6WPE33-qEFp8qJ8.js → requirementDiagram-4Y6WPE33-B5jvIMD7.js} +1 -1
  61. package/server/public/assets/{sankeyDiagram-5OEKKPKP-DApIsH9w.js → sankeyDiagram-5OEKKPKP-mPlHirkS.js} +1 -1
  62. package/server/public/assets/{sequenceDiagram-3UESZ5HK-DtNkSqZR.js → sequenceDiagram-3UESZ5HK-CM_I9wrD.js} +1 -1
  63. package/server/public/assets/{src-Csh0f24o.js → src-BYanRXvC.js} +1 -1
  64. package/server/public/assets/src-CdPLAdYJ.js +1 -0
  65. package/server/public/assets/{stateDiagram-AJRCARHV-DgaB8R7z.js → stateDiagram-AJRCARHV-C6Dqck84.js} +1 -1
  66. package/server/public/assets/stateDiagram-v2-BHNVJYJU-Bz3dgE_B.js +1 -0
  67. package/server/public/assets/{timeline-definition-PNZ67QCA-vhc6Bdap.js → timeline-definition-PNZ67QCA-B7tFs4Cq.js} +1 -1
  68. package/server/public/assets/{vennDiagram-CIIHVFJN-D7yEMTpU.js → vennDiagram-CIIHVFJN-DH3LC-SZ.js} +1 -1
  69. package/server/public/assets/{wardleyDiagram-YWT4CUSO-DAFN50tK.js → wardleyDiagram-YWT4CUSO-CeJXk7lo.js} +1 -1
  70. package/server/public/assets/{xychartDiagram-2RQKCTM6-BrVcjfKW.js → xychartDiagram-2RQKCTM6-m6Efea7b.js} +1 -1
  71. package/server/public/favicon.svg +4 -4
  72. package/server/public/index.html +5 -5
  73. package/server/server.js +1889 -430
  74. package/server/public/assets/channel-cOUdzmRM.js +0 -1
  75. package/server/public/assets/chunk-55IACEB6-_2lFlbAe.js +0 -1
  76. package/server/public/assets/chunk-BO2N2NFS-C4IC6tXf.js +0 -103
  77. package/server/public/assets/chunk-QZHKN3VN-zpNB_C8l.js +0 -1
  78. package/server/public/assets/chunk-WU5MYG2G-DDr09vEJ.js +0 -1
  79. package/server/public/assets/classDiagram-4FO5ZUOK-ezwkz8ms.js +0 -1
  80. package/server/public/assets/classDiagram-v2-Q7XG4LA2-DAphqbn4.js +0 -1
  81. package/server/public/assets/ganttDiagram-6RSMTGT7-BsV09cPX.js +0 -292
  82. package/server/public/assets/image-GAAHSSAO-CCe2yzQe.js +0 -1
  83. package/server/public/assets/index-C2PMO6nB.js +0 -818
  84. package/server/public/assets/index-Dzio_3kK.css +0 -1
  85. package/server/public/assets/linear-DxIhHUPD.js +0 -1
  86. package/server/public/assets/mermaid-GHXKKRXX-BZNohdCo.js +0 -1
  87. package/server/public/assets/src-CsT-wIyx.js +0 -1
  88. package/server/public/assets/stateDiagram-v2-BHNVJYJU-52jRo40M.js +0 -1
  89. /package/server/public/assets/{html-void-elements-QR3T3anA.js → html-void-elements-DIyBxxO2.js} +0 -0
  90. /package/server/public/assets/{preload-helper-Dd-HcVz_.js → preload-helper-DzyYoeor.js} +0 -0
package/server/server.js CHANGED
@@ -1,103 +1,101 @@
1
1
  import { a as __toCommonJS, i as __require, o as __toESM, t as __commonJSMin } from "./rolldown-runtime-DNCzzKnI.js";
2
- import { C as init_paths, D as sessionFilePath, E as resolveOhmypiExtensionPath, S as getOverdeckHome, T as packageRoot, _ as SYNC_SOURCES, l as LOGS_DIR, o as CLAUDE_DIR, t as AGENTS_DIR, u as OVERDECK_HOME, w as isDevMode, x as getLegacyHome, y as encodeClaudeProjectDir } from "./paths-tknG80xs.js";
3
- import { $ as resolveModelIdSync, A as getBuiltInDefaultHarness, B as defaultBackgroundAiFeatures, F as piProviderForModel, G as resolveModel, H as DEFAULT_ROLES, K as MODEL_CAPABILITIES, N as getProviderForModelSync, O as UnknownModelError, P as init_providers, Q as modelSupportsImagesSync, S as canUseHarnessSync, U as DEFAULT_WORKHORSES, V as init_registry, W as computeModelOrigin, X as hasModelCapabilitySync, Y as getModelEffortLevelsSync, Z as init_model_capabilities, _ as mergeTtsDaemonConfigs, b as init_tier_table, ct as FsError, d as loadConfigSync, et as COMPLIANCE_MODES, f as updateConversationsConfig, g as getDefaultTtsDaemonConfig, gt as TrackerError, h as mergeConfigs, i as getConversationsConfig, j as getKimiAnthropicBaseUrl, l as isTldrEnabledSync, m as jsYaml, mt as ProcessSpawnError, n as clearConfigCache, nt as ROLE_EFFORTS, o as getGlobalConfigPath, p as init_js_yaml, q as MODEL_DEPRECATIONS, r as getConversationSearchConfigSync, rt as require_dist$1, s as isClaudeCodeChannelsEnabled, t as init_config_yaml, tt as PARENT_MODEL_REF, u as loadConfigNoMigration, ut as GitHubApiError, vt as init_errors, w as init_harness_policy, x as resolveTieredExecutionEnabled } from "./config-yaml-C9tNHjAH.js";
4
- import { _ as getDashboardIdentity, c as startUatTrainReconciler, d as openFlywheelRunReportForDashboard, f as pauseFlywheelRunForDashboard, g as runDashboardDbJob, h as startFlywheelRunForDashboard, l as stopUatTrainReconciler, m as resumeFlywheelRunForDashboard, p as readCurrentFlywheelStatusForDashboard, u as abortFlywheelRunForDashboard, v as readHostDashboardApiPort, y as shouldRefuseHostDashboardPort } from "./uat-train-Di_5GWAL.js";
5
- import { A as jsonStringResponse, C as isTrustedOriginForHost, D as checkCodexAuthStatus, E as validateOriginHeaders, O as httpHandler, T as validateOrigin$1, _ as resolveClaudeSessionId, a as agentsRouteLayer, b as resolvePiSessionPath, c as invalidateAgentsCache, d as startSmeeProcessSync, f as resolveAgentGitInfo, g as resolveAgentHarness, h as readLauncherPinnedSessionId, k as jsonResponse, l as getSystemHealthSnapshot, o as saveAgentStateAndEmitEvent, p as resolveConversationGitInfo, s as saveAgentStateAndEmitEventProgram, t as specialistsRouteLayer, u as isSmeeConfiguredSync, v as resolveCodexRolloutPath, x as getHeaderFromMap, y as resolveJsonlPath } from "./specialists-CY2q5jAb.js";
6
- import { a as init_activity_logger, i as emitDashboardLifecycleSync, n as emitActivityEntrySync, r as emitActivityTtsSync } from "./activity-logger-C_Hsfouw.js";
7
- import { A as init_paths$1, O as OVERDECK_MIGRATION_PATH, d as getOverdeckDatabaseSync, f as init_infra, i as DbLive, k as getOverdeckDatabasePath, u as TmuxLive } from "./infra-BnuUQA1b.js";
8
- import { n as openDatabase, t as init_driver } from "./driver-CBobl_fb.js";
9
- import { g as getIssueWorkspacePath, v as init_record } from "./record-BzKI-k7f.js";
10
- import { a as parseIssueIdSync, n as extractPrefixSync, r as init_issue_id } from "./issue-id-CtudqaOv.js";
11
- import { A as resolveProjectFromIssueSync, D as registerProjectSync, P as setProjectAutoMergeDefaultSync, S as listProjectsSync, f as getProjectSync, k as resolveProjectFromIssue, l as getIssuePrefix, r as extractTeamPrefix, s as findProjectByTeamSync, v as init_projects, x as listProjects } from "./projects-CFpKXBDE.js";
12
- import { E as listPaneValues, F as sendKeysAsync, G as buildChildEnvSync, H as init_ui_theme, I as sendRawKeystroke, K as buildChildEnvWithoutTmuxSync, L as sessionExists, M as resizeWindow, O as listSessionNames, P as sendKeys, S as isHarnessProcessAlive, U as setUiTheme, W as BLANKED_PROVIDER_ENV, b as getWindowDimensions, c as ensureManagedTmuxContextOnce, d as exactPaneTarget, i as capturePane, k as listSessions, n as buildTmuxArgs, o as createSession, p as findManagedServerPidSync, q as init_child_env, w as killSession, x as init_tmux, z as setOption } from "./tmux-CDwrBgT9.js";
13
- import { a as init_event_store, i as initEventStore, n as getEventStore, r as getSharedDb } from "./event-store-CMfrQLgu.js";
14
- import { _ as FlywheelRunId, a as getHarnessBehavior, c as FlywheelStats, g as WS_METHODS, h as PanRpcGroup, m as PanRpcError, o as ContextLayerSaveRequest, s as ContextPreviewRequest, t as init_src, v as FlywheelStatus, x as MemoryObservation, y as EDITORS } from "./src-Bt65pH-t.js";
15
- import { a as fixStuckCommentedReviews, b as setReviewStatusSync, c as getReviewStatusSync, d as loadReviewStatuses, l as init_review_status, n as clearReviewStatus, o as fixStuckReadyForMerge, r as clearStuckMergeStatuses, s as getReviewStatus, u as loadReadyForMergeFlags, y as setReviewStatus } from "./review-status-YErqCbEB.js";
16
- import { a as EventStoreService, c as ReadModelService, d as init_read_model, f as toAgentResolution, i as init_issue_lifecycle, l as ReadModelServiceLive, m as toRole, n as IssueLifecycleLive, o as EventStoreServiceLive, p as toAgentStatus, s as init_domain_services, t as IssueLifecycle } from "./issue-lifecycle-BH8UIror.js";
17
- import { d as init_agents, i as recordBriefingSessionStart, n as ensureSessionContextBriefingFile, p as listAllAgentsSync, r as init_briefing_freshness, s as AgentsResolverLive, t as appendFreshBriefingUpdate } from "./briefing-freshness-CzRarwBd.js";
18
- import { i as init_paths$2, l as resolveObservationsFile, r as ensureParentDir, s as resolveIssueMemoryRoot, t as assertMemorySafeSegment } from "./paths-Dsd_dtCx.js";
19
- import { n as listFeatureRegistryEntries, t as init_feature_registry_storage } from "./feature-registry-storage-DCtE46iD.js";
20
- import { a as diffAgainstMainFiles, c as diffFilesAgainstHead, d as findCommitAtTime, i as diffAgainstMain, l as diffPatchFilesAgainstHead, m as listCheckpoints, o as diffCheckpointFiles, p as init_checkpoint_manager, s as diffCheckpoints, t as captureCheckpoint, u as diffPatchSinceCommit } from "./checkpoint-manager-DcU-yTPm.js";
21
- import { $ as init_concurrency, An as saveAgentStateSync, Bn as init_pty_token, D as messageAgent, Ft as getProviderAuthMode, G as listRunningAgents, Gt as getClaudeAuthStatus, Hn as clearReadySignal, Ht as piFifoPaths, Jn as getAgentRuntimeState, K as listRunningAgentsSync, Kt as init_claude_auth, Mt as getAgentRuntimeBaseCommand, O as init_operator_interventions, Rn as init_agent_record_sync, Rt as injectPiConversationMemory, St as writeBridgeTokenSync, Ut as init_ohmypi_fifo, Vn as writePtyToken, Vt as init_pi_fifo, W as listAgentStates, Wn as normalizeAgentId, Wt as ohmypiFifoPaths, Xt as getProviderEnvForModel, Yn as getAgentRuntimeStateSync, Z as emergencyBrake, Zt as getProviderExportsForModel, _n as getAgentStateFilePath, bt as probeTranscriptSince, c as init_tier_supervisor, ct as init_config, d as supervisorAgentId, dt as saveCloisterConfigSync, ft as shouldAutoStart, gn as getAgentState, hn as getAgentDir, k as operatorInterventionEvent, lt as loadCloisterConfig, nn as init_openai_auth, on as dismissDevChannelsDialog, pt as deliverAgentMessage, qn as waitForReadySignal, s as deliverCommitForReview, sn as writeChannelsBridgeMcpConfig, t as init_agents$1, tn as getOpenAIAuthStatus, ut as loadCloisterConfigSync, vn as getAgentStateSync, vt as captureTranscriptUserRecordSnapshot, xt as init_bridge_token, yt as init_transcript_landing, zn as readAgentHarnessModelRecordSync } from "./agents-I-xFLL6F.js";
22
- import { t as init_behavior } from "./behavior-B4_m9DaU.js";
23
- import { a as getCostForIssueAggregateSync, c as getDailyTrendsSync, d as init_cost_sync, l as getModelRollupSync, n as getBackgroundCostBySourceSync, r as getCavemanExperimentDataSync, s as getCostsByIssueSync, u as getTodayCostSync } from "./cost-sync-BgWKYZUZ.js";
24
- import { c as getAgentsNeedingAttention, d as CostResolverLive, n as readCloisterStateFile, o as generateHealthSummary, s as getAgentHealth, t as getCloisterService, u as CostResolver } from "./service-A2kIgSZ_.js";
25
- import { n as AgentStateServiceLive } from "./agent-state-service-CX6J_kRr.js";
26
- import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-CfMJKtu0.js";
27
- import { d as init_typed_errors, l as WorkspaceCreateError, n as AgentStartError, r as BeadsNotInitialized, s as TrackerApiError, t as AgentAlreadyRunning, u as WorkspaceNotFound } from "./typed-errors-BQLS2-a2.js";
28
- import { i as init_linear_client, r as LinearClientOptionalLive, t as LinearClient } from "./linear-client-Bsy-Vqx3.js";
29
- import { i as init_github_client, r as GitHubClientOptionalLive, t as GitHubClient } from "./github-client-BcSyQB-F.js";
30
- import { i as init_rally_client, r as RallyClientOptionalLive, t as RallyClient } from "./rally-client-sephEbzO.js";
31
- import { a as resolveTrackerTypeSync, i as resolveGitHubIssueSync, n as init_tracker_utils } from "./tracker-utils-CYlQXpfe.js";
32
- import { a as CacheService, i as startSharedIssueService, n as init_issue_service_singleton, o as init_cache_service, r as issue_service_singleton_exports, s as parseIntegerHeader, t as getSharedIssueService } from "./issue-service-singleton-DxVMDd_x.js";
33
- import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-JIiqJ_LH.js";
34
- import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-Bf-6UMyT.js";
35
- import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates } from "./review-status-sync-DSM99M9e.js";
36
- import { C as transitionToMerging, _ as markFailed, c as isFlywheelGloballyPaused, d as listDuePendingAutoMerges, f as listProblemAutoMerges, g as markBlocked, n as getActionableAutoMerge, o as init_merge_sync, t as cancelPending, u as listActiveAutoMerges, v as markMerged, x as scheduleAutoMergeWithResult, y as requeueToPending } from "./merge-sync-BZPMDIcb.js";
37
- import { E as writeSpecForIssue, L as asPanSpecDocument, M as PAN_CONTINUE_FILENAME, N as PAN_DIRNAME, R as init_types$1, S as init_specs, T as writeSpec, _ as init_types, a as findWorkspaceDraftPlan, b as findSpecByIssue, c as readPlan, d as readWorkspacePlan, f as readWorkspacePlanSync, o as init_io, r as findPlan, s as isPlanningComplete, v as subItemsOf } from "./io-BX80MEP3.js";
38
- import { C as restartCliproxy, S as readPidFile, T as stopCliproxy, b as isCliproxyInstalled, d as getCliproxyAuthDir, o as bridgeCodexAuthToCliproxy, v as init_cliproxy, w as startCliproxy, x as isCliproxyRunning, y as installCliproxy } from "./cliproxy-DKkfZuzL.js";
39
- import { f as init_config$1, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-UKaBdqtW.js";
40
- import { n as require_auth_errors, r as require_token_error, t as require_token_util } from "./token-util-BEuuoN7z.js";
41
- import { $ as updateConversationTitle, A as listArchivedConversationsWithEnrichment, C as getConversationLedgerCosts, D as incrementForkRetryCount, E as importLegacyConversation, F as markConversationRunning, G as setConversationModel, H as setConversationClaudeSessionId, J as setImportedConversationLinks, K as setFavorite, M as listFavoritedIds, N as markConversationActive, O as init_conversations, P as markConversationEnded, Q as updateConversationForkFallbackReason, R as recordConversationHandoff, S as getConversationByTmuxSession, T as hasOtherActiveConversationOnTmuxSession, U as setConversationEffort, V as setClearedToConvId, W as setConversationHarness, X as updateConversationCost, Y as unarchiveConversation, Z as updateConversationDeliveryMethod, _ as canReplaceTitle, _t as init_discovered_sessions, b as getConversationById, et as updateForkStatus, g as backfillConversationModel, h as archiveConversation, j as listConversations, k as isAgentConversationName, mt as getDiscoveredSessionBySessionId, nt as updateSpawnError, q as setForkRequest, tt as updateLastAttached, v as createConversation, w as getStuckForks, x as getConversationByName, y as getConversationByClaudeSessionId, z as removeFavorite } from "./conversations-CP2aa9q-.js";
42
- import { _ as init_codex_conversation_parser, a as contextUsageFromParseResult, c as parseEntireConversation, d as init_pi_conversation_parser, f as isPiSessionFile, g as parseOhmypiConversationMessages, h as isOhmypiSessionFile, i as computeContextUsage, l as parseFromLastCompactBoundary, m as init_ohmypi_conversation_parser, n as gateSnapshotEmission, o as summarizeConversationActivity, p as parsePiConversationMessages, r as watchConversation, s as parseConversationMessages, t as init_conversation_service, v as parseCodexConversationMessages } from "./conversation-service-MLYjzNUG.js";
43
- import { n as isBackgroundFeatureEnabled, t as init_features } from "./features-xGah4i1z.js";
44
- import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-BOrXf84x.js";
45
- import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health, t as init_providers$1 } from "./providers-DznCCZwl.js";
46
- import { n as init_launcher_generator, t as generateLauncherScriptSync } from "./launcher-generator-DPB1BgON.js";
47
- import { a as init_claude_permissions, o as resolvePermissionModeSync, r as getClaudePermissionFlagsStringSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-CB2A2ji1.js";
48
- import { a as globalContextFile$1, d as workspaceContextFile, l as projectContextFile, n as codexGlobalContextFile, o as init_layers, r as ensureProjectLayer, s as piGlobalContextFile } from "./layers-B1dMbFu0.js";
49
- import { l as init_codex, s as findRolloutPath } from "./codex-kSC1YLP_.js";
50
- import { C as setMergeTrainEnabled, S as setFlywheelRequireUatBeforeMerge, b as setFlywheelAutoPickupBacklog, c as isCloisterSpawnsPausedSync, d as isFlywheelAutoPickupBacklog, f as isFlywheelGloballyPaused$1, g as setCloisterSpawnsPausedSync, h as setBootReconciliationDecision, m as isMergeTrainEnabled, o as getSetting, p as isFlywheelRequireUatBeforeMerge, r as getBootReconciliationState, s as init_control_settings, u as isDeaconGloballyPausedSync, v as setDeaconGloballyPausedSync, w as setSetting } from "./control-settings-D3FrIdcx.js";
51
- import { c as init_docker_stats } from "./stack-health-BWegK0Ro.js";
52
- import { n as resolveHarness, t as init_harness_resolve } from "./harness-resolve-1AlLvkFa.js";
53
- import { i as checkPrdGateSync, t as init_pan_dir } from "./pan-dir-BjGKfnlg.js";
54
- import { a as init_lifecycle_io, t as appendContinueSessionEntryForIssue } from "./lifecycle-io-Boh9ge2B.js";
55
- import { r as init_swarm_readiness, t as analyzeSwarmReadiness } from "./swarm-readiness-Cm-DukdQ.js";
56
- import { At as getAgentJsonlMtime, B as createInFlightGuard, Dt as getRuntimeForAgent, F as setAgentStatusChangedNotifier, Ft as detectAwaitingInputForAgent, I as setAgentStoppedNotifier, It as detectAwaitingInputFromPaneSync, J as startBootReconciliation, K as isBootReconciliationCandidate, L as setMergeReadyNotifier, O as loadConfig, Ot as computeAgentEnrichment, Pt as scanPendingInputsPromise, Rt as parseCodexApprovalPrompt, X as resolveBootGates, Y as formatBootGateState, k as loadState, kt as countPendingAskUserQuestionsForAgent, n as assessDeaconPatrolFreshness, q as listBootReconciliationCandidates, st as withConcurrencyLimit, t as applyBootReconciliationDecision, x as getDeaconLogs } from "./deacon-JXyiFMrU.js";
57
- import { i as CostThresholdError, l as embed, s as parseRelativeTime } from "./scanner-GcaaRool.js";
58
- import { n as deduplicateEventsSync, o as readEventsSync, s as tailEventsSync } from "./events-CTjU_U8E.js";
59
- import { n as recordBackgroundAiCost, t as init_cost } from "./cost-XJB_GatY.js";
60
- import { S as writeLatestFlywheelStatus, _ as readCurrentLatestFlywheelStatus, b as subscribeLatestFlywheelStatus, f as getFlywheelRunDetail, h as listFlywheelRuns, l as buildClassifyLookups, m as isFlywheelRunId, y as resolveLiveFlywheelRunId } from "./flywheel-CTxeAxID.js";
61
- import { C as rejectUnauthorizedDashboardSessionMintRequest, S as rejectUnauthorizedDashboardRequest, T as getWorkspacePathForIssue, _ as rebuildCacheSync, b as dashboardSessionCookieHeader, c as resumeQueuedMerges, d as resolveIssueHeadlineCost, f as syncWalFromAllProjects, g as getCostsForIssueSync, h as migrateAllSessionsSync, l as processPendingFeedbackDeliveries, m as reconcilePiTranscripts, p as reconcile, t as workspacesRouteLayer, u as getCachedRunningAgents, v as syncCacheSync, w as rejectUnsafeDashboardMutationRequest, x as hasDashboardInternalToken, y as dashboardCsrfToken } from "./workspaces-BuUeTmag.js";
62
- import { l as detectPlatform, u as init_platform } from "./workspace-manager-D3HIsMs-.js";
63
- import { O as init_specialists, b as getReviewerSessionName } from "./specialists-Cy7HYT8N.js";
64
- import { t as withBdMutex } from "./bd-mutex-DXCIKL87.js";
65
- import { n as findPrdAtStatusSync, r as init_prd_locations } from "./prd-locations-BT_sPcNF.js";
66
- import { a as getShadowState, s as init_shadow_state } from "./shadow-state-bugG0OUd.js";
67
- import { c as getMergeBackendStatus, d as init_github_app, f as isGitHubAppConfigured, u as getPullRequestState } from "./github-app-DOmwGAQl.js";
68
- import { t as clearIssueClosedCache } from "./issue-closed-nHUhxUMc.js";
69
- import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-4WFZC8UZ.js";
70
- import { n as parseSequenceMd, r as writeSequenceMd, t as init_sequence_io } from "./sequence-io-h4-QtTnt.js";
71
- import { C as selectNeedsPlanning, S as normalizeGate, _ as computeCohort, b as computeStats, d as OBJECTION_LABEL, f as PARKED_LABEL, g as classifyIssue, h as VETOED_LABEL, m as RELEASED_LABEL, p as READY_LABEL, u as BLOCKS_MAIN_LABEL, v as computeEpicGroups, x as computeWaves, y as computeLanes } from "./flywheel-merge-order-BQI3T5pp.js";
72
- import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-CcVCl7Om.js";
73
- import { i as resolveAutoSpawnOnFinalize, o as spawnPlanningSession } from "./spawn-planning-session-CWstFiT-.js";
74
- import { _ as sendToRemoteAgent, a as getRemoteAgentOutput, n as checkRemoteSpendCap, o as init_remote_agents, p as loadRemoteAgentState, u as killRemoteAgent, v as spawnRemoteAgent } from "./remote-agents-CE0kVmAf.js";
75
- import { r as spawnInspectAgent } from "./inspect-agent-BJCCg3O5.js";
76
- import { a as init_pull_requests, c as getIssuePrTabCacheGeneration, d as githubPrLookupSource, f as init_github_pr_lookup, i as fetchIssuePullRequestDiff, l as init_pr_tab_cache, n as fetchIssuePullRequest, o as bumpIssuePrTabCacheGeneration, p as lookupPullRequestNumberForBranch, r as fetchIssuePullRequestDetails, s as getCachedIssuePrTabResponse, t as fetchIssueCheckRuns, u as setCachedIssuePrTabResponse } from "./pull-requests-tsM16UNo.js";
77
- import { t as enrichReviewStatus } from "./review-status-enrichment-BHLoQirZ.js";
78
- import { i as runMemoryFtsTransaction, n as init_fts_db, r as runMemoryFtsStatement, t as closeMemoryFtsDatabases } from "./fts-db-BkKcBcY2.js";
79
- import { c as init_pending, l as maybeTriggerStatusRollup, o as readRecentObservations, r as init_rollup, u as writePendingTurn } from "./rollup-B3Bt9VtQ.js";
80
- import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-0Te4IUnH.js";
81
- import { _ as init_git_activity, v as listGitOperationsSync } from "./merge-agent-DjYp-UIK.js";
82
- import { t as getAgentCommandSync } from "./settings-D5oLqghw.js";
83
- import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-x4KI2jSf.js";
84
- import { r as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remote-BullELly.js";
85
- import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-DHEbuQrw.js";
86
- import { a as init_query_expansion, i as clearQueryExpansionCache, n as init_injection, o as buildMatchQuery, r as injectPromptTimeMemory, s as init_search } from "./injection-B2ElRuNk.js";
87
- import { _ as init_transcript_adapter, a as generateFallbackSummary, c as handoffPreconditionFallbackReason, d as prependFallbackFocus, f as requestHandoffFromAgent, g as getTranscriptAdapter, i as copySessionFromCompactBoundary, l as init_summary_fork, o as generateSummaryForFork, p as reserveSummaryForkSession, r as authorHandoffExternal, s as handoffFailureReason, u as logHandoffFallback } from "./summary-fork-CIYflUCB.js";
88
- import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting } from "./conversation-compaction-DxVCfMsv.js";
89
- import { a as init_render, d as validateTemplate, i as hasManagedRegion, l as init_harness, o as userContentOutsideRegion, t as init_context_layers, u as renderForHarness } from "./context-layers-DGIdj1-C.js";
90
- import { r as parseArtifactRef } from "./forge-DyEaM-rx.js";
91
- import { t as getMergeBlockersPayload } from "./merge-blockers-ByOxbHUf.js";
92
- import { a as spawnSequencerAgent, i as init_sequencer_agent, n as clearFinishedSequencerRun, r as getSequencerRunStatus, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-D_WJsQKB.js";
93
- import "./supervisor-Bykrzblr.js";
94
- import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-BcXu6L_6.js";
2
+ import { $ as getModelEffortLevelsSync, D as init_harness_policy, Dn as resolveProjectFromIssue, Dr as SYNC_SOURCES, Dt as init_record, Fr as packageRoot, G as DEFAULT_ROLES, Gn as init_auto_commit, I as getProviderForModelSync, In as extractPrefixSync, Ir as resolveOhmypiExtensionPath, K as DEFAULT_WORKHORSES, L as init_providers, Ln as init_issue_id, Lr as sessionFilePath, Mn as setProjectAutoMergeDefaultSync, Mr as getOverdeckHome, N as getBuiltInDefaultHarness, Nr as init_paths, On as resolveProjectFromIssueSync, P as getKimiAnthropicBaseUrl, Pr as isDevMode, R as piProviderForModel, S as resolveTieredExecutionEnabled, T as canUseHarnessSync, Tn as registerProjectSync, Tt as getIssueWorkspacePath, U as defaultBackgroundAiFeatures, Un as flushAllPendingAutoCommits, Vn as require_dist$1, W as init_registry, Wn as flushAutoCommits, X as MODEL_CAPABILITIES, Y as resolveModel, Z as MODEL_DEPRECATIONS, _ as mergeTtsDaemonConfigs, an as findProjectByTeamSync, b as TieredExecutionConfigError, bn as listProjectsSync, br as OVERDECK_HOME, cr as TrackerError, ct as PARENT_MODEL_REF, d as loadConfigSync, dr as AGENTS_DIR, er as FsError, et as hasModelCapabilitySync, f as updateConversationsConfig, g as getDefaultTtsDaemonConfig, gn as init_projects, gr as CLAUDE_DIR, h as mergeConfigs, i as getConversationsConfig, j as UnknownModelError, jr as getLegacyHome, kr as encodeClaudeProjectDir, l as isTldrEnabledSync, lt as ROLE_EFFORTS, m as jsYaml, n as clearConfigCache, nr as GitHubApiError, nt as modelSupportsImagesSync, o as getGlobalConfigPath, or as ProcessSpawnError, p as init_js_yaml, q as computeModelOrigin, r as getConversationSearchConfigSync, rt as resolveModelIdSync, s as isClaudeCodeChannelsEnabled, sn as getIssuePrefix, st as COMPLIANCE_MODES, t as init_config_yaml, tn as extractTeamPrefix, tt as init_model_capabilities, u as loadConfigNoMigration, un as getProjectSync, ur as init_errors, w as validateTieredExecutionConfig, x as init_tier_table, yn as listProjects, yr as LOGS_DIR, zn as parseIssueIdSync } from "./config-yaml-D0t3y9AC.js";
3
+ import { _ as getDashboardIdentity, c as startUatTrainReconciler, d as openFlywheelRunReportForDashboard, f as pauseFlywheelRunForDashboard, g as runDashboardDbJob, h as startFlywheelRunForDashboard, l as stopUatTrainReconciler, m as resumeFlywheelRunForDashboard, p as readCurrentFlywheelStatusForDashboard, u as abortFlywheelRunForDashboard, v as readHostDashboardApiPort, y as shouldRefuseHostDashboardPort } from "./uat-train-CpEtMAyD.js";
4
+ import { A as jsonResponse, D as validateOriginHeaders, E as validateOrigin$1, O as checkCodexAuthStatus, S as getHeaderFromMap, _ as resolveAgentHarness, a as agentsRouteLayer, b as resolveJsonlPath, c as evaluateSpawnGuardrails, d as isSmeeConfiguredSync, f as startSmeeProcessSync, g as readLauncherPinnedSessionId, j as jsonStringResponse, k as httpHandler, l as invalidateAgentsCache, m as resolveConversationGitInfo, o as saveAgentStateAndEmitEvent, p as resolveAgentGitInfo, s as saveAgentStateAndEmitEventProgram, t as specialistsRouteLayer, u as getSystemHealthSnapshot, v as resolveClaudeSessionId, w as isTrustedOriginForHost, x as resolvePiSessionPath, y as resolveCodexRolloutPath } from "./specialists-CWh4WUGp.js";
5
+ import { a as init_activity_logger, i as emitDashboardLifecycleSync, n as emitActivityEntrySync, r as emitActivityTtsSync } from "./activity-logger-Dm95Ua07.js";
6
+ import { A as init_paths$1, O as OVERDECK_MIGRATION_PATH, d as getOverdeckDatabaseSync, f as init_infra, i as DbLive, k as getOverdeckDatabasePath, u as TmuxLive } from "./infra-D6hW7oZO.js";
7
+ import { n as openDatabase, t as init_driver } from "./driver-D-HBOQa_.js";
8
+ import { E as listPaneValues, F as sendKeysAsync, G as buildChildEnvSync, H as init_ui_theme, I as sendRawKeystroke, K as buildChildEnvWithoutTmuxSync, L as sessionExists, M as resizeWindow, O as listSessionNames, P as sendKeys, S as isHarnessProcessAlive, U as setUiTheme, W as BLANKED_PROVIDER_ENV, b as getWindowDimensions, c as ensureManagedTmuxContextOnce, d as exactPaneTarget, i as capturePane, k as listSessions, n as buildTmuxArgs, o as createSession, p as findManagedServerPidSync, q as init_child_env, w as killSession, x as init_tmux, z as setOption } from "./tmux-Cuf3Wudb.js";
9
+ import { a as init_event_store, i as initEventStore, n as getEventStore, r as getSharedDb } from "./event-store-B35NfIvv.js";
10
+ import { _ as FlywheelRunId, a as getHarnessBehavior, b as DomainEvent, c as FlywheelStats, g as WS_METHODS, h as PanRpcGroup, m as PanRpcError, o as ContextLayerSaveRequest, s as ContextPreviewRequest, t as init_src, v as FlywheelStatus, x as MemoryObservation, y as EDITORS } from "./src-CgBTaItZ.js";
11
+ import { a as fixStuckReadyForMerge, c as init_review_status, i as fixStuckCommentedReviews, l as loadReadyForMergeFlags, n as clearReviewStatus, o as getReviewStatus, r as clearStuckMergeStatuses, s as getReviewStatusSync, u as loadReviewStatuses, v as setReviewStatus, y as setReviewStatusSync } from "./review-status-BRzbY-uJ.js";
12
+ import { a as EventStoreService, c as ReadModelService, d as init_read_model, f as toAgentResolution, i as init_issue_lifecycle, l as ReadModelServiceLive, m as toRole, n as IssueLifecycleLive, o as EventStoreServiceLive, p as toAgentStatus, s as init_domain_services, t as IssueLifecycle } from "./issue-lifecycle-BrYCFw67.js";
13
+ import { d as init_agents, i as recordBriefingSessionStart, n as ensureSessionContextBriefingFile, p as listAllAgentsSync, r as init_briefing_freshness, s as AgentsResolverLive, t as appendFreshBriefingUpdate } from "./briefing-freshness-CKbYolyA.js";
14
+ import { i as init_paths$2, l as resolveObservationsFile, r as ensureParentDir, s as resolveIssueMemoryRoot, t as assertMemorySafeSegment } from "./paths-gB-AoCbP.js";
15
+ import { n as listFeatureRegistryEntries, t as init_feature_registry_storage } from "./feature-registry-storage-DzHcsyeM.js";
16
+ import { a as diffAgainstMainFiles, c as diffFilesAgainstHead, d as findCommitAtTime, i as diffAgainstMain, l as diffPatchFilesAgainstHead, m as listCheckpoints, o as diffCheckpointFiles, p as init_checkpoint_manager, s as diffCheckpoints, t as captureCheckpoint, u as diffPatchSinceCommit } from "./checkpoint-manager-DCSBbepE.js";
17
+ import { $n as clearReadySignal, $t as getClaudeAuthStatus, Bt as getAgentRuntimeBaseCommand, Cn as getAgentDir, Ct as init_transcript_landing, Dn as getAgentStateSync, En as getAgentStateFilePath, Et as writeBridgeTokenSync, J as listAgentStates, Kt as injectPiConversationMemory, M as init_operator_interventions, N as operatorInterventionEvent, Qn as writePtyToken, Qt as ohmypiFifoPaths, St as captureTranscriptUserRecordSnapshot, Tn as getAgentState, Tt as init_bridge_token, Ut as getProviderAuthMode, Vn as saveAgentStateSync, X as listRunningAgentsSync, Xn as readAgentHarnessModelRecordSync, Xt as piFifoPaths, Y as listRunningAgents, Yn as init_agent_record_sync, Yt as init_pi_fifo, Zn as init_pty_token, Zt as init_ohmypi_fifo, _t as deliverAgentMessage, an as getProviderExportsForModel, ar as getAgentRuntimeState, c as init_tier_supervisor, en as init_claude_auth, f as supervisorAgentId, ft as init_config, gt as shouldAutoStart, hn as writeChannelsBridgeMcpConfig, ht as saveCloisterConfigSync, in as getProviderEnvForModel, ir as waitForReadySignal, j as messageAgent, ln as getOpenAIAuthStatus, mn as dismissDevChannelsDialog, mt as loadCloisterConfigSync, or as getAgentRuntimeStateSync, pt as loadCloisterConfig, rt as init_concurrency, s as deliverCommitForReview, t as init_agents$1, tr as normalizeAgentId, tt as emergencyBrake, un as init_openai_auth, wt as probeTranscriptSince } from "./agents-BODDwjMW.js";
18
+ import { t as init_behavior } from "./behavior-DqR8vtRW.js";
19
+ import { a as getCostForIssueAggregateSync, c as getDailyTrendsSync, d as init_cost_sync, l as getModelRollupSync, n as getBackgroundCostBySourceSync, p as queryCostEventsSync, r as getCavemanExperimentDataSync, s as getCostsByIssueSync, u as getTodayCostSync } from "./cost-sync-COCS9jfM.js";
20
+ import { c as getAgentsNeedingAttention, d as CostResolverLive, f as CostWriter, l as CostDoorLive, n as readCloisterStateFile, o as generateHealthSummary, s as getAgentHealth, t as getCloisterService, u as CostResolver } from "./service-yJX2ZDTb.js";
21
+ import { n as AgentStateServiceLive } from "./agent-state-service-BDYwkuao.js";
22
+ import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-Bp-m78D_.js";
23
+ import { d as init_typed_errors, l as WorkspaceCreateError, n as AgentStartError, r as BeadsNotInitialized, s as TrackerApiError, t as AgentAlreadyRunning, u as WorkspaceNotFound } from "./typed-errors-BRDFMoss.js";
24
+ import { i as init_linear_client, r as LinearClientOptionalLive, t as LinearClient } from "./linear-client-BbxK-2hQ.js";
25
+ import { i as init_github_client, r as GitHubClientOptionalLive, t as GitHubClient } from "./github-client-ClT-9ZHn.js";
26
+ import { i as init_rally_client, r as RallyClientOptionalLive, t as RallyClient } from "./rally-client-DoecXLs_.js";
27
+ import { a as resolveTrackerTypeSync, i as resolveGitHubIssueSync, n as init_tracker_utils } from "./tracker-utils-BGlCcZn1.js";
28
+ import { a as CacheService, i as startSharedIssueService, n as init_issue_service_singleton, o as init_cache_service, r as issue_service_singleton_exports, s as parseIntegerHeader, t as getSharedIssueService } from "./issue-service-singleton-Driy3b99.js";
29
+ import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-DR2busqD.js";
30
+ import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-DpikL6KI.js";
31
+ import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates } from "./review-status-sync-TCRUOOb0.js";
32
+ import { C as transitionToMerging, _ as markFailed, c as isFlywheelGloballyPaused, d as listDuePendingAutoMerges, f as listProblemAutoMerges, g as markBlocked, n as getActionableAutoMerge, o as init_merge_sync, t as cancelPending, u as listActiveAutoMerges, v as markMerged, x as scheduleAutoMergeWithResult, y as requeueToPending } from "./merge-sync-CNvpnmSV.js";
33
+ import { B as asPanSpecDocument, C as findSpecByIssue, F as PAN_CONTINUE_FILENAME, I as PAN_DIRNAME, O as writeSpec, T as init_specs, V as init_types$1, a as findWorkspaceDraftPlan, b as init_types, k as writeSpecForIssue, m as readWorkspacePlanSync, o as init_io, p as readWorkspacePlan, r as findPlan, s as isPlanningComplete, u as readPlan, x as subItemsOf } from "./io-DyjLqqzR.js";
34
+ import { C as restartCliproxy, S as readPidFile, T as stopCliproxy, b as isCliproxyInstalled, d as getCliproxyAuthDir, o as bridgeCodexAuthToCliproxy, v as init_cliproxy, w as startCliproxy, x as isCliproxyRunning, y as installCliproxy } from "./cliproxy-BRAY5Min.js";
35
+ import { f as init_config$1, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-C83PzLrX.js";
36
+ import { n as require_auth_errors, r as require_token_error, t as require_token_util } from "./token-util-DEP-WK8V.js";
37
+ import { $ as updateConversationTitle, A as listArchivedConversationsWithEnrichment, C as getConversationLedgerCosts, D as incrementForkRetryCount, E as importLegacyConversation, F as markConversationRunning, G as setConversationModel, H as setConversationClaudeSessionId, J as setImportedConversationLinks, K as setFavorite, M as listFavoritedIds, N as markConversationActive, O as init_conversations, P as markConversationEnded, Q as updateConversationForkFallbackReason, R as recordConversationHandoff, S as getConversationByTmuxSession, T as hasOtherActiveConversationOnTmuxSession, U as setConversationEffort, V as setClearedToConvId, W as setConversationHarness, X as updateConversationCost, Y as unarchiveConversation, Z as updateConversationDeliveryMethod, _ as canReplaceTitle, b as getConversationById, et as updateForkStatus, g as backfillConversationModel, h as archiveConversation, ht as getDiscoveredSessionBySessionId, j as listConversations, k as isAgentConversationName, nt as updateSpawnError, q as setForkRequest, tt as updateLastAttached, v as createConversation, vt as init_discovered_sessions, w as getStuckForks, x as getConversationByName, y as getConversationByClaudeSessionId, z as removeFavorite } from "./conversations-DqMw7hzO.js";
38
+ import { _ as init_codex_conversation_parser, a as contextUsageFromParseResult, c as parseEntireConversation, d as init_pi_conversation_parser, f as isPiSessionFile, g as parseOhmypiConversationMessages, h as isOhmypiSessionFile, i as computeContextUsage, l as parseFromLastCompactBoundary, m as init_ohmypi_conversation_parser, n as gateSnapshotEmission, o as summarizeConversationActivity, p as parsePiConversationMessages, r as watchConversation, s as parseConversationMessages, t as init_conversation_service, v as parseCodexConversationMessages } from "./conversation-service-DMXhLnSO.js";
39
+ import { n as isBackgroundFeatureEnabled, t as init_features } from "./features-DwF4LaSW.js";
40
+ import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-DWLiz0Nr.js";
41
+ import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health, t as init_providers$1 } from "./providers-cLWAz3kQ.js";
42
+ import { n as init_launcher_generator, t as generateLauncherScriptSync } from "./launcher-generator-sMFvWgWt.js";
43
+ import { a as init_claude_permissions, o as resolvePermissionModeSync, r as getClaudePermissionFlagsStringSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-C0EMsI4l.js";
44
+ import { a as globalContextFile$1, d as workspaceContextFile, l as projectContextFile, n as codexGlobalContextFile, o as init_layers, r as ensureProjectLayer, s as piGlobalContextFile } from "./layers-DoUs5i4O.js";
45
+ import { l as init_codex, s as findRolloutPath } from "./codex-DlkETOc-.js";
46
+ import { C as setMergeTrainEnabled, S as setFlywheelRequireUatBeforeMerge, b as setFlywheelAutoPickupBacklog, c as isCloisterSpawnsPausedSync, d as isFlywheelAutoPickupBacklog, f as isFlywheelGloballyPaused$1, g as setCloisterSpawnsPausedSync, h as setBootReconciliationDecision, m as isMergeTrainEnabled, o as getSetting, p as isFlywheelRequireUatBeforeMerge, r as getBootReconciliationState, s as init_control_settings, u as isDeaconGloballyPausedSync, v as setDeaconGloballyPausedSync, w as setSetting } from "./control-settings-BTPRGd5d.js";
47
+ import { c as init_docker_stats } from "./stack-health-C_JMDfQa.js";
48
+ import { n as resolveHarness, t as init_harness_resolve } from "./harness-resolve-CYFbeWOz.js";
49
+ import { i as checkPrdGateSync, t as init_pan_dir } from "./pan-dir-DB2mqeFg.js";
50
+ import { a as init_lifecycle_io, t as appendContinueSessionEntryForIssue } from "./lifecycle-io-CKyWyVWt.js";
51
+ import { r as init_swarm_readiness, t as analyzeSwarmReadiness } from "./swarm-readiness-lyw-buM5.js";
52
+ import { B as createInFlightGuard, Dt as countPendingAskUserQuestionsForAgent, Et as computeAgentEnrichment, F as setAgentStatusChangedNotifier, I as setAgentStoppedNotifier, It as parseCodexApprovalPrompt, J as startBootReconciliation, K as isBootReconciliationCandidate, L as setMergeReadyNotifier, Mt as scanPendingInputsPromise, Nt as detectAwaitingInputForAgent, O as loadConfig, Ot as getAgentJsonlMtime, Pt as detectAwaitingInputFromPaneSync, Tt as getRuntimeForAgent, at as withConcurrencyLimit, k as loadState, n as assessDeaconPatrolFreshness, q as listBootReconciliationCandidates, t as applyBootReconciliationDecision, x as getDeaconLogs } from "./deacon-Cx7hYXMe.js";
53
+ import { i as CostThresholdError, l as embed, s as parseRelativeTime } from "./scanner-bjduWTuH.js";
54
+ import { n as deduplicateEventsSync, o as readEventsSync, s as tailEventsSync } from "./events-CiEGnZ53.js";
55
+ import { n as recordBackgroundAiCost, t as init_cost } from "./cost-DHjgeEgA.js";
56
+ import { S as writeLatestFlywheelStatus, _ as readCurrentLatestFlywheelStatus, b as subscribeLatestFlywheelStatus, f as getFlywheelRunDetail, h as listFlywheelRuns, l as buildClassifyLookups, m as isFlywheelRunId, y as resolveLiveFlywheelRunId } from "./flywheel-CkiZj2Rx.js";
57
+ import { C as rejectUnauthorizedDashboardRequest, E as getWorkspacePathForIssue, S as hasDashboardInternalToken, T as rejectUnsafeDashboardMutationRequest, _ as processPendingFeedbackDeliveries, b as dashboardCsrfToken, c as resumeQueuedMerges, d as reconcilePiTranscripts, f as reclassifyUnknownCostEventsSync, g as syncCacheSync, h as rebuildCacheSync, l as syncWalFromAllProjects, m as getCostsForIssueSync, p as migrateAllSessionsSync, t as workspacesRouteLayer, u as reconcile, v as getCachedRunningAgents, w as rejectUnauthorizedDashboardSessionMintRequest, x as dashboardSessionCookieHeader, y as resolveIssueHeadlineCost } from "./workspaces-DjApnCJp.js";
58
+ import { l as detectPlatform, u as init_platform } from "./workspace-manager-DV4u07t9.js";
59
+ import { O as init_specialists, b as getReviewerSessionName } from "./specialists-DW-1CAK5.js";
60
+ import { t as withBdMutex } from "./bd-mutex-Bqr8TTH0.js";
61
+ import { n as findPrdAtStatusSync, r as init_prd_locations } from "./prd-locations-BDN-xDNK.js";
62
+ import { a as getShadowState, s as init_shadow_state } from "./shadow-state-a0THplpo.js";
63
+ import { T as verifyAppCanMerge, d as getPullRequestState, f as init_github_app, l as getMergeBackendStatus, p as isGitHubAppConfigured } from "./github-app-Soz3tZh_.js";
64
+ import { t as clearIssueClosedCache } from "./issue-closed-7IHQ8IJf.js";
65
+ import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-DQqQJkgs.js";
66
+ import { a as init_boot_gates, i as formatBootGateState, o as resolveBootGates } from "./no-resume-mode-Hd08C18Q.js";
67
+ import { n as parseSequenceMd, r as writeSequenceMd, t as init_sequence_io } from "./sequence-io-MtiwR4U7.js";
68
+ import { C as selectNeedsPlanning, S as normalizeGate, _ as computeCohort, b as computeStats, d as OBJECTION_LABEL, f as PARKED_LABEL, g as classifyIssue, h as VETOED_LABEL, m as RELEASED_LABEL, p as READY_LABEL, u as BLOCKS_MAIN_LABEL, v as computeEpicGroups, x as computeWaves, y as computeLanes } from "./flywheel-merge-order-B-03AdML.js";
69
+ import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-DYF0ScVu.js";
70
+ import { i as resolveAutoSpawnOnFinalize, o as spawnPlanningSession } from "./spawn-planning-session-Dgxpkfaj.js";
71
+ import { _ as sendToRemoteAgent, a as getRemoteAgentOutput, n as checkRemoteSpendCap, o as init_remote_agents, p as loadRemoteAgentState, u as killRemoteAgent, v as spawnRemoteAgent } from "./remote-agents-DeiRtncQ.js";
72
+ import { r as spawnInspectAgent } from "./inspect-agent-CmBmiWSp.js";
73
+ import { n as init_github_pr_lookup, r as lookupPullRequestNumberForBranch, t as githubPrLookupSource } from "./github-pr-lookup-C-YqEBQA.js";
74
+ import { a as init_pull_requests, c as getIssuePrTabCacheGeneration, i as fetchIssuePullRequestDiff, l as init_pr_tab_cache, n as fetchIssuePullRequest, o as bumpIssuePrTabCacheGeneration, r as fetchIssuePullRequestDetails, s as getCachedIssuePrTabResponse, t as fetchIssueCheckRuns, u as setCachedIssuePrTabResponse } from "./pull-requests-V9zbOnM7.js";
75
+ import { t as enrichReviewStatus } from "./review-status-enrichment-BK-Peqqa.js";
76
+ import { i as runMemoryFtsTransaction, n as init_fts_db, r as runMemoryFtsStatement, t as closeMemoryFtsDatabases } from "./fts-db-Bk_iUuHK.js";
77
+ import { c as init_pending, l as maybeTriggerStatusRollup, o as readRecentObservations, r as init_rollup, u as writePendingTurn } from "./rollup-BlVsp5Zh.js";
78
+ import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-Ddo1RYXH.js";
79
+ import { C as init_git_activity, w as listGitOperationsSync } from "./merge-agent-DcC21G-0.js";
80
+ import { t as getAgentCommandSync } from "./settings-DqPuXxT5.js";
81
+ import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-CzUNH73m.js";
82
+ import { r as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remote-DRwGjxzx.js";
83
+ import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-rU83JjnR.js";
84
+ import { a as init_query_expansion, i as clearQueryExpansionCache, n as init_injection, o as buildMatchQuery, r as injectPromptTimeMemory, s as init_search } from "./injection-9zl1FzOq.js";
85
+ import { _ as init_transcript_adapter, a as generateFallbackSummary, c as handoffPreconditionFallbackReason, d as prependFallbackFocus, f as requestHandoffFromAgent, g as getTranscriptAdapter, i as copySessionFromCompactBoundary, l as init_summary_fork, o as generateSummaryForFork, p as reserveSummaryForkSession, r as authorHandoffExternal, s as handoffFailureReason, u as logHandoffFallback } from "./summary-fork-D8tUkORq.js";
86
+ import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting } from "./conversation-compaction-NboSkcz4.js";
87
+ import { a as init_render, d as validateTemplate, i as hasManagedRegion, l as init_harness, o as userContentOutsideRegion, t as init_context_layers, u as renderForHarness } from "./context-layers-BpZBKYnw.js";
88
+ import { r as parseArtifactRef } from "./forge-Ca3Vecel.js";
89
+ import { t as getMergeBlockersPayload } from "./merge-blockers-B7ey3CkR.js";
90
+ import { a as spawnSequencerAgent, i as init_sequencer_agent, n as clearFinishedSequencerRun, r as getSequencerRunStatus, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-C7AhB-75.js";
91
+ import "./supervisor-C0I_2Noz.js";
92
+ import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-haUPQHnM.js";
95
93
  import { createRequire } from "node:module";
96
94
  import { Cause, Context, Data, Duration, Effect, Exit, Fiber, FileSystem, Layer, Option, Path, Queue, Result, Schedule, Schema, Stream } from "effect";
97
95
  import { createReadStream, createWriteStream, existsSync, fstatSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
98
96
  import { basename, dirname, join, resolve } from "path";
99
97
  import { homedir } from "os";
100
- import { homedir as homedir$1, type } from "node:os";
98
+ import { freemem as freemem$1, homedir as homedir$1, loadavg as loadavg$1, totalmem, type } from "node:os";
101
99
  import { constants, createReadStream as createReadStream$1, existsSync as existsSync$1, mkdirSync as mkdirSync$1, promises as promises$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, readlinkSync, realpathSync as realpathSync$1, renameSync as renameSync$1, rmSync as rmSync$1, stat, statSync as statSync$1, symlinkSync as symlinkSync$1, unwatchFile, watch, watchFile, writeFileSync as writeFileSync$1 } from "node:fs";
102
100
  import * as sp from "node:path";
103
101
  import { basename as basename$1, delimiter, dirname as dirname$1, extname as extname$1, isAbsolute as isAbsolute$1, join as join$1, normalize, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "node:path";
@@ -105,10 +103,10 @@ import { FetchHttpClient, HttpRouter, HttpServer, HttpServerRequest, HttpServerR
105
103
  import { EventEmitter } from "node:events";
106
104
  import { access, appendFile, chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat as stat$1, unlink, writeFile } from "node:fs/promises";
107
105
  import { createHash, randomUUID } from "crypto";
108
- import { chmod as chmod$1, mkdir as mkdir$1, open as open$1, readFile as readFile$1, readdir as readdir$1, rename as rename$1, stat as stat$2, unlink as unlink$1, writeFile as writeFile$1 } from "fs/promises";
109
- import { createHmac, randomBytes, randomUUID as randomUUID$1, timingSafeEqual } from "node:crypto";
110
106
  import { exec, execFile, execFileSync, execSync, fork, spawn } from "node:child_process";
111
107
  import { promisify } from "node:util";
108
+ import { chmod as chmod$1, mkdir as mkdir$1, open as open$1, readFile as readFile$1, readdir as readdir$1, rename as rename$1, stat as stat$2, unlink as unlink$1, writeFile as writeFile$1 } from "fs/promises";
109
+ import { createHmac, randomBytes, randomUUID as randomUUID$1, timingSafeEqual } from "node:crypto";
112
110
  import * as pty from "@homebridge/node-pty-prebuilt-multiarch";
113
111
  import { Buffer as Buffer$1 } from "node:buffer";
114
112
  import { PassThrough, Readable } from "node:stream";
@@ -325,6 +323,7 @@ const ServerConfigLayer = Layer.effect(ServerConfig, Effect.sync(() => {
325
323
  init_activity_logger();
326
324
  init_paths$1();
327
325
  init_driver();
326
+ init_auto_commit();
328
327
  var import_dist = /* @__PURE__ */ __toESM(require_dist$1(), 1);
329
328
  init_issue_id();
330
329
  init_projects();
@@ -651,7 +650,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
651
650
  const beadsDir = join$1(workspacePath, ".beads");
652
651
  const projectBeadsDir = join$1(workspacePath, "..", "..", ".beads");
653
652
  if (!(existsSync$1(beadsDir) || existsSync$1(projectBeadsDir))) throw new BeadsNotInitialized({ workspace: workspacePath });
654
- const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-pTsn9v84.js");
653
+ const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-Ea9gfdKS.js");
655
654
  const normalizedId = normalizeAgentId(issueId);
656
655
  if ((await Effect.runPromise(getAgentState(normalizedId)))?.status === "running") throw new AgentAlreadyRunning({ id: issueId });
657
656
  return {
@@ -682,7 +681,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
682
681
  const { workspacePath, projectPath, issue } = opts;
683
682
  if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
684
683
  const sessionName = opts.sessionName ?? `planning-${issueId.toLowerCase()}`;
685
- const { spawnPlanningSession } = await import("./spawn-planning-session-CIVFNe5b.js");
684
+ const { spawnPlanningSession } = await import("./spawn-planning-session-Dr1yJhqp.js");
686
685
  const result = await spawnPlanningSession({
687
686
  issue: {
688
687
  id: issue.id,
@@ -715,14 +714,14 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
715
714
  }),
716
715
  kill: (agentId) => Effect.tryPromise({
717
716
  try: async () => {
718
- const { stopAgent } = await import("./agents-pTsn9v84.js");
717
+ const { stopAgent } = await import("./agents-Ea9gfdKS.js");
719
718
  await Effect.runPromise(stopAgent(agentId));
720
719
  },
721
720
  catch: () => void 0
722
721
  }).pipe(Effect.ignore),
723
722
  message: (agentId, msg) => Effect.tryPromise({
724
723
  try: async () => {
725
- const { messageAgent } = await import("./agents-pTsn9v84.js");
724
+ const { messageAgent } = await import("./agents-Ea9gfdKS.js");
726
725
  await messageAgent(agentId, msg);
727
726
  },
728
727
  catch: (err) => new AgentStartError({
@@ -733,8 +732,8 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
733
732
  }),
734
733
  deepWipe: (issueId, opts) => Effect.tryPromise({
735
734
  try: async () => {
736
- const { deepWipe } = await import("./workflows-B1dF2rZU.js");
737
- const { resolveProjectFromIssueSync } = await import("./projects-BGOgJTpI.js");
735
+ const { deepWipe } = await import("./workflows-C94HmADx.js");
736
+ const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
738
737
  const project = resolveProjectFromIssueSync(issueId);
739
738
  const projectPath = project?.projectPath ?? process.cwd();
740
739
  await Effect.runPromise(deepWipe({
@@ -763,7 +762,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
763
762
  * Route handlers and AgentSpawner use this instead of calling workspace-manager directly.
764
763
  */
765
764
  init_typed_errors();
766
- const execAsync$13 = promisify(exec);
765
+ const execAsync$12 = promisify(exec);
767
766
  var WorkspaceService = class extends Context.Service()("overdeck/dashboard/WorkspaceService") {};
768
767
  const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
769
768
  function getWorkspacePath(issueId) {
@@ -790,8 +789,8 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
790
789
  const { workspacePath, branch } = getWorkspacePath(issueId);
791
790
  const issueLower = issueId.toLowerCase();
792
791
  if (existsSync$1(workspacePath)) return workspacePath;
793
- const { createWorkspace } = await import("./workspace-manager-g8GBLo49.js");
794
- const { loadProjectsConfigSync } = await import("./projects-BGOgJTpI.js");
792
+ const { createWorkspace } = await import("./workspace-manager-BE2H8gD5.js");
793
+ const { loadProjectsConfigSync } = await import("./projects-BbMEBykK.js");
795
794
  const { projects } = loadProjectsConfigSync();
796
795
  const project = resolveProjectFromIssueSync(issueId);
797
796
  if (!project) throw new WorkspaceCreateError({
@@ -826,7 +825,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
826
825
  const { workspacePath } = getWorkspacePath(issueId);
827
826
  const issueLower = issueId.toLowerCase();
828
827
  if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
829
- const { removeWorkspace } = await import("./workspace-manager-g8GBLo49.js");
828
+ const { removeWorkspace } = await import("./workspace-manager-BE2H8gD5.js");
830
829
  const project = resolveProjectFromIssueSync(issueId);
831
830
  if (!project) throw new WorkspaceCreateError({
832
831
  id: issueId,
@@ -857,7 +856,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
857
856
  try: async () => {
858
857
  const { workspacePath } = getWorkspacePath(issueId);
859
858
  const issueLower = issueId.toLowerCase();
860
- const { stopWorkspaceDocker } = await import("./workspace-manager-g8GBLo49.js");
859
+ const { stopWorkspaceDocker } = await import("./workspace-manager-BE2H8gD5.js");
861
860
  await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));
862
861
  },
863
862
  catch: () => void 0
@@ -926,7 +925,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
926
925
  join$1(workspacePath, ".devcontainer", "docker-compose.devcontainer.yml")
927
926
  ];
928
927
  if (!composePaths.find((p) => existsSync$1(p))) {
929
- const { ensureDevcontainerSync } = await import("./ensure-devcontainer-BdQCQbQc.js");
928
+ const { ensureDevcontainerSync } = await import("./ensure-devcontainer-Cy2QNx5O.js");
930
929
  const ensure = ensureDevcontainerSync({
931
930
  workspacePath,
932
931
  issueId
@@ -937,7 +936,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
937
936
  });
938
937
  }
939
938
  const finalComposePath = composePaths.find((p) => existsSync$1(p));
940
- if (finalComposePath) await execAsync$13(`docker compose -f "${finalComposePath}" up -d --build`, {
939
+ if (finalComposePath) await execAsync$12(`docker compose -f "${finalComposePath}" up -d --build`, {
941
940
  cwd: dirname$1(finalComposePath),
942
941
  timeout: 3e5
943
942
  });
@@ -1100,7 +1099,7 @@ function getDefaultCwd() {
1100
1099
  //#endregion
1101
1100
  //#region services/open.ts
1102
1101
  init_src();
1103
- const execAsync$12 = promisify(exec);
1102
+ const execAsync$11 = promisify(exec);
1104
1103
  function getFileManagerCommand() {
1105
1104
  switch (process.platform) {
1106
1105
  case "linux": return "xdg-open";
@@ -1133,7 +1132,7 @@ function launchDetached(launch) {
1133
1132
  }
1134
1133
  async function isCommandAvailable(command) {
1135
1134
  try {
1136
- await execAsync$12(`${process.platform === "win32" ? "where" : "which"} ${command}`);
1135
+ await execAsync$11(`${process.platform === "win32" ? "where" : "which"} ${command}`);
1137
1136
  return true;
1138
1137
  } catch {
1139
1138
  return false;
@@ -4811,7 +4810,7 @@ init_tmux();
4811
4810
  init_config$1();
4812
4811
  const reauthSessions = /* @__PURE__ */ new Map();
4813
4812
  const SESSION_MAX_AGE_MS = 3600 * 1e3;
4814
- const readJsonBody$16 = Effect.gen(function* () {
4813
+ const readJsonBody$17 = Effect.gen(function* () {
4815
4814
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
4816
4815
  return text ? JSON.parse(text) : {};
4817
4816
  });
@@ -4916,7 +4915,7 @@ const postCodexReauthRoute = HttpRouter.add("POST", "/api/settings/codex-reauth"
4916
4915
  const postCodexReauthStatusRoute = HttpRouter.add("POST", "/api/settings/codex-reauth/status", httpHandler(Effect.gen(function* () {
4917
4916
  const originError = rejectInvalidOrigin(yield* HttpServerRequest.HttpServerRequest);
4918
4917
  if (originError) return originError;
4919
- const body = yield* readJsonBody$16;
4918
+ const body = yield* readJsonBody$17;
4920
4919
  const sessionName = typeof body.session === "string" ? body.session : "";
4921
4920
  const token = typeof body.token === "string" ? body.token : "";
4922
4921
  const session = getLiveReauthSession(sessionName);
@@ -15944,7 +15943,7 @@ var require_get_vercel_oidc_token = /* @__PURE__ */ __commonJSMin(((exports, mod
15944
15943
  err = error;
15945
15944
  }
15946
15945
  try {
15947
- const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-DP7ukS32.js").then((m) => /* @__PURE__ */ __toESM(m.default)), await import("./token-Dn9hc4Ap.js").then((m) => /* @__PURE__ */ __toESM(m.default))]);
15946
+ const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-CEZZegEl.js").then((m) => /* @__PURE__ */ __toESM(m.default)), await import("./token-Cp6YfYRY.js").then((m) => /* @__PURE__ */ __toESM(m.default))]);
15948
15947
  if (!token || isExpired(getTokenPayload(token), options?.expirationBufferMs)) {
15949
15948
  await refreshToken(options);
15950
15949
  token = getVercelOidcTokenSync();
@@ -28261,7 +28260,7 @@ var GoogleCloudTranscription = class {
28261
28260
  }
28262
28261
  async start(apiKey, model) {
28263
28262
  try {
28264
- const { SpeechClient } = await import("./src-DFWsHM-l.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).then((module) => module.v1p1beta1 ?? module.v1);
28263
+ const { SpeechClient } = await import("./src-DyS4x3Qs.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).then((module) => module.v1p1beta1 ?? module.v1);
28265
28264
  if (this.closed) return;
28266
28265
  const stream = new SpeechClient({ apiKey }).streamingRecognize({
28267
28266
  config: {
@@ -28525,7 +28524,7 @@ const DEFAULT_VOICE_SETTINGS = {
28525
28524
  model: "gpt-4.1-mini"
28526
28525
  }
28527
28526
  };
28528
- const readJsonBody$15 = Effect.gen(function* () {
28527
+ const readJsonBody$16 = Effect.gen(function* () {
28529
28528
  const request = yield* HttpServerRequest.HttpServerRequest;
28530
28529
  const contentLength = Number(request.headers["content-length"] ?? 0);
28531
28530
  if (Number.isFinite(contentLength) && contentLength > MAX_VOICE_SETTINGS_BODY_BYTES) return {
@@ -28648,7 +28647,7 @@ const getVoiceSettingsRoute = HttpRouter.add("GET", "/api/voice/settings", httpH
28648
28647
  const putVoiceSettingsRoute = HttpRouter.add("PUT", "/api/voice/settings", httpHandler(Effect.gen(function* () {
28649
28648
  const originError = requireTrustedOrigin$2(yield* HttpServerRequest.HttpServerRequest);
28650
28649
  if (originError) return originError;
28651
- const parsed = yield* readJsonBody$15;
28650
+ const parsed = yield* readJsonBody$16;
28652
28651
  if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 413 });
28653
28652
  const body = parsed.body;
28654
28653
  if (!isVoiceSettings(body)) return jsonResponse({ error: "Invalid voice settings payload" }, { status: 400 });
@@ -29286,6 +29285,12 @@ function storedToDomainEvent(stored) {
29286
29285
  payload: stored.payload
29287
29286
  };
29288
29287
  }
29288
+ const isKnownDomainEvent = Schema.is(DomainEvent);
29289
+ function passesDomainEventSchema(event) {
29290
+ if (isKnownDomainEvent(event)) return true;
29291
+ console.error(`[ws-rpc] dropping domain event that fails DomainEvent schema validation — add it to the contracts union: type=${event.type} seq=${event.sequence}`);
29292
+ return false;
29293
+ }
29289
29294
  function createSystemHeartbeatEvent() {
29290
29295
  const ts = Date.now();
29291
29296
  return {
@@ -29719,7 +29724,7 @@ const PanRpcLayer = PanRpcGroup.toLayer(Effect.gen(function* () {
29719
29724
  [WS_METHODS.subscribeDomainEvents]: (_input) => {
29720
29725
  console.log("[ws-rpc] subscribeDomainEvents invoked");
29721
29726
  const heartbeats = Stream.tick("15 seconds").pipe(Stream.map(createSystemHeartbeatEvent));
29722
- return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.merge(heartbeats));
29727
+ return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.filter(passesDomainEventSchema), Stream.merge(heartbeats));
29723
29728
  },
29724
29729
  [WS_METHODS.subscribeFlywheelStatus]: (_input) => Stream.callback((queue) => Effect.acquireRelease(Effect.promise(async () => {
29725
29730
  const latest = await readCurrentLatestFlywheelStatus({ activeRunId: await runDashboardDbJob("getSetting", "flywheel.active_run_id") });
@@ -29730,7 +29735,7 @@ const PanRpcLayer = PanRpcGroup.toLayer(Effect.gen(function* () {
29730
29735
  }), (unsubscribe) => Effect.sync(() => unsubscribe()))),
29731
29736
  [WS_METHODS.subscribeIssueEvents]: (input) => {
29732
29737
  console.log(`[ws-rpc] subscribeIssueEvents invoked issueId=${input.issueId}`);
29733
- return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.mapEffect((event) => getCachedAgentIssueLookup(readModel).pipe(Effect.map((lookup) => filterDomainEventForIssue(event, input.issueId, lookup)))), Stream.filter((event) => event !== null));
29738
+ return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.filter(passesDomainEventSchema), Stream.mapEffect((event) => getCachedAgentIssueLookup(readModel).pipe(Effect.map((lookup) => filterDomainEventForIssue(event, input.issueId, lookup)))), Stream.filter((event) => event !== null));
29734
29739
  },
29735
29740
  [WS_METHODS.subscribeTerminal]: (input) => terminalService.streamSession(input.sessionName, input.cols, input.rows),
29736
29741
  [WS_METHODS.subscribeAgentOutput]: (input) => eventStore.streamEvents.pipe(Stream.filter((e) => e.type === "agent.output_received" && e.payload["agentId"] === input.agentId), Stream.flatMap((e) => {
@@ -29752,7 +29757,7 @@ const PanRpcLayer = PanRpcGroup.toLayer(Effect.gen(function* () {
29752
29757
  message: `Failed to build dashboard snapshot: ${String(cause)}`,
29753
29758
  code: "SNAPSHOT_FAILED"
29754
29759
  }))),
29755
- [WS_METHODS.replayEvents]: (input) => eventStore.readFrom(input.fromSequence).pipe(Effect.map((stored) => stored.map(storedToDomainEvent)), Effect.mapError((cause) => new PanRpcError({
29760
+ [WS_METHODS.replayEvents]: (input) => eventStore.readFrom(input.fromSequence).pipe(Effect.map((stored) => stored.map(storedToDomainEvent).filter(passesDomainEventSchema)), Effect.mapError((cause) => new PanRpcError({
29756
29761
  message: `Failed to replay events: ${String(cause)}`,
29757
29762
  code: "REPLAY_FAILED"
29758
29763
  }))),
@@ -30379,7 +30384,7 @@ function parseIssueIdFromTextSync(value) {
30379
30384
  //#region services/resource-discovery.ts
30380
30385
  init_projects();
30381
30386
  init_tmux();
30382
- const execFileAsync$9 = promisify(execFile);
30387
+ const execFileAsync$14 = promisify(execFile);
30383
30388
  const RESOURCE_DISCOVERY_TTL_MS = 3e4;
30384
30389
  const RECENT_ACTIVITY_WINDOW_MS = 5e3;
30385
30390
  let cachedResourceIssues = null;
@@ -30487,7 +30492,7 @@ function isDiscoverableAgentSession(sessionName) {
30487
30492
  async function loadTrackerIssues() {
30488
30493
  const map = /* @__PURE__ */ new Map();
30489
30494
  try {
30490
- const { getSharedIssueService } = await import("./issue-service-singleton-BDRqGHSo.js");
30495
+ const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
30491
30496
  const issues = (await getSharedIssueService()).getIssues();
30492
30497
  for (const issue of issues) if (issue.identifier) map.set(issue.identifier.toUpperCase(), issue);
30493
30498
  } catch {
@@ -30504,7 +30509,7 @@ async function loadTmuxSessions() {
30504
30509
  }
30505
30510
  async function loadDockerContainers() {
30506
30511
  try {
30507
- const { stdout } = await execFileAsync$9("docker", [
30512
+ const { stdout } = await execFileAsync$14("docker", [
30508
30513
  "ps",
30509
30514
  "--format",
30510
30515
  "{{.Names}}"
@@ -30522,7 +30527,7 @@ async function loadOpenPullRequests() {
30522
30527
  const repos = getGitHubConfig()?.repos ?? [];
30523
30528
  await Promise.all(repos.map(async (repo) => {
30524
30529
  try {
30525
- const { stdout } = await execFileAsync$9("gh", [
30530
+ const { stdout } = await execFileAsync$14("gh", [
30526
30531
  "pr",
30527
30532
  "list",
30528
30533
  "--repo",
@@ -30553,7 +30558,7 @@ async function loadOpenPullRequests() {
30553
30558
  }
30554
30559
  async function loadProjectBranches(projectPath) {
30555
30560
  try {
30556
- const [localResult, remoteResult] = await Promise.all([execFileAsync$9("git", [
30561
+ const [localResult, remoteResult] = await Promise.all([execFileAsync$14("git", [
30557
30562
  "for-each-ref",
30558
30563
  "refs/heads/feature/*",
30559
30564
  "--format=%(refname:short)"
@@ -30561,7 +30566,7 @@ async function loadProjectBranches(projectPath) {
30561
30566
  cwd: projectPath,
30562
30567
  encoding: "utf-8",
30563
30568
  timeout: 1e4
30564
- }).catch(() => ({ stdout: "" })), execFileAsync$9("git", [
30569
+ }).catch(() => ({ stdout: "" })), execFileAsync$14("git", [
30565
30570
  "for-each-ref",
30566
30571
  "refs/remotes/origin/feature/*",
30567
30572
  "--format=%(refname:short)"
@@ -30686,7 +30691,7 @@ async function computeResourceAllocatedIssues() {
30686
30691
  if (!issue.resourceDetails.tmuxSessions.includes(sessionName)) issue.resourceDetails.tmuxSessions.push(sessionName);
30687
30692
  }
30688
30693
  try {
30689
- const { listActiveRemoteAgentStates } = await import("./remote-agents-BT_DHl8s.js");
30694
+ const { listActiveRemoteAgentStates } = await import("./remote-agents-BXKV5iBS.js");
30690
30695
  for (const remoteState of listActiveRemoteAgentStates()) {
30691
30696
  const issue = ensureIssue(remoteState.issueId);
30692
30697
  if (!issue) continue;
@@ -30890,7 +30895,7 @@ function groupResourceAllocatedIssuesByProject(issues) {
30890
30895
  //#region ../../lib/overdeck/issue-reads.ts
30891
30896
  init_tracker_config();
30892
30897
  init_projects();
30893
- const execFileAsync$8 = promisify(execFile);
30898
+ const execFileAsync$13 = promisify(execFile);
30894
30899
  function isGitHubIssue$6(issueId) {
30895
30900
  const resolved = resolveGitHubIssueSync(issueId);
30896
30901
  if (resolved.isGitHub) return {
@@ -31071,7 +31076,7 @@ function getIssueBeads(id) {
31071
31076
  const { beads, querySource } = yield* Effect.promise(async () => {
31072
31077
  try {
31073
31078
  const bdSearchDir = workspacePath && existsSync$1(workspacePath) ? workspacePath : projectPath || homedir$1();
31074
- const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$8("bd", [
31079
+ const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$13("bd", [
31075
31080
  "list",
31076
31081
  "--json",
31077
31082
  "-l",
@@ -31192,7 +31197,7 @@ init_projects();
31192
31197
  init_remote_agents();
31193
31198
  init_tracker_utils();
31194
31199
  init_tmux();
31195
- const execFileAsync$7 = promisify(execFile);
31200
+ const execFileAsync$12 = promisify(execFile);
31196
31201
  function getIssueDataService$9() {
31197
31202
  return getSharedIssueService();
31198
31203
  }
@@ -31249,7 +31254,7 @@ async function completePlanningArtifacts(options) {
31249
31254
  if (workspaceIssueId && workspaceIssueId.toLowerCase() !== issueLower) throw new Error(`Workspace vBRIEF is for ${workspaceIssueId.toUpperCase()}, not ${upperIssueId}`);
31250
31255
  assertPlanQuality(workspaceDoc);
31251
31256
  const createBeads = options.createBeads ?? (async (path) => {
31252
- const mod = await import("./beads-D1muPgsx.js");
31257
+ const mod = await import("./beads-B4ysfx-Y.js");
31253
31258
  return await Effect.runPromise(mod.createBeadsFromVBrief(path));
31254
31259
  });
31255
31260
  emitCompletePlanningPhase(upperIssueId, "specWrite", "start", "writing proposed vBRIEF spec", { projectPath });
@@ -31316,8 +31321,72 @@ function completePlanningWorkspaceGitAddCommands(gitRoot) {
31316
31321
  const commands = [];
31317
31322
  if (existsSync$1(join$1(gitRoot, ".pan"))) commands.push(["add", ".pan/"]);
31318
31323
  if (existsSync$1(join$1(gitRoot, ".beads"))) commands.push(["add", ".beads/"]);
31324
+ if (existsSync$1(join$1(gitRoot, ".gitignore"))) commands.push(["add", ".gitignore"]);
31319
31325
  return commands;
31320
31326
  }
31327
+ /**
31328
+ * PAN-2386: complete-planning can leave `.pan/records/<issue>.json` modified after
31329
+ * the main `chore(plan): complete planning` commit because record writes are
31330
+ * queued for debounced auto-commit. If auto-start is requested, the subsequent
31331
+ * start-agent dirty-workspace guard refuses to spawn the work agent. Flush any
31332
+ * pending auto-commits and explicitly stage/commit the per-issue record so the
31333
+ * tree handed to auto-start is clean.
31334
+ */
31335
+ async function commitWorkspaceRecordBeforeAutoSpawn(gitRoot, issueId) {
31336
+ if (!existsSync$1(join$1(gitRoot, ".git"))) return;
31337
+ const issueLower = issueId.toLowerCase();
31338
+ try {
31339
+ await Effect.runPromise(flushAutoCommits(gitRoot));
31340
+ } catch {}
31341
+ const recordPath = join$1(".pan", "records", `${issueLower}.json`);
31342
+ try {
31343
+ const { stdout: statusOut } = await execFileAsync$12("git", [
31344
+ "status",
31345
+ "--porcelain",
31346
+ "--",
31347
+ recordPath
31348
+ ], {
31349
+ cwd: gitRoot,
31350
+ encoding: "utf-8"
31351
+ });
31352
+ if (!statusOut.trim()) return;
31353
+ await execFileAsync$12("git", [
31354
+ "add",
31355
+ "--",
31356
+ recordPath
31357
+ ], {
31358
+ cwd: gitRoot,
31359
+ encoding: "utf-8"
31360
+ });
31361
+ try {
31362
+ await execFileAsync$12("git", [
31363
+ "diff",
31364
+ "--cached",
31365
+ "--quiet",
31366
+ "--",
31367
+ recordPath
31368
+ ], {
31369
+ cwd: gitRoot,
31370
+ encoding: "utf-8"
31371
+ });
31372
+ return;
31373
+ } catch {}
31374
+ await execFileAsync$12("git", [
31375
+ "commit",
31376
+ "-m",
31377
+ `chore(records): update ${issueId.toUpperCase()} per-issue record before auto-start`,
31378
+ "--",
31379
+ recordPath
31380
+ ], {
31381
+ cwd: gitRoot,
31382
+ encoding: "utf-8"
31383
+ });
31384
+ console.log(`[complete-planning] Committed per-issue record for ${issueId.toUpperCase()} before auto-start`);
31385
+ } catch (err) {
31386
+ const message = err instanceof Error ? err.message : String(err);
31387
+ console.warn(`[complete-planning] Could not commit per-issue record for ${issueId.toUpperCase()}: ${message}`);
31388
+ }
31389
+ }
31321
31390
  function getInternalDashboardOrigin() {
31322
31391
  const port = Number.parseInt(process.env["API_PORT"] ?? process.env["PORT"] ?? "3011", 10);
31323
31392
  return process.env["OVERDECK_INTERNAL_DASHBOARD_URL"] ?? `http://127.0.0.1:${port}`;
@@ -31517,7 +31586,7 @@ async function completePlanningForIssue(options) {
31517
31586
  const filesToStage = completePlanningFilesToStage(projectPath, proposed.filename);
31518
31587
  if (!existsSync$1(join$1(projectPath, ".git"))) console.log(`[complete-planning] Project root ${projectPath} is not a git repository (polyrepo) — pan spec updated on disk but not committed`);
31519
31588
  else {
31520
- const { stdout: branchStdout } = await execFileAsync$7("git", [
31589
+ const { stdout: branchStdout } = await execFileAsync$12("git", [
31521
31590
  "rev-parse",
31522
31591
  "--abbrev-ref",
31523
31592
  "HEAD"
@@ -31527,7 +31596,7 @@ async function completePlanningForIssue(options) {
31527
31596
  });
31528
31597
  const currentBranch = branchStdout.trim();
31529
31598
  if (currentBranch === "main") {
31530
- await execFileAsync$7("git", [
31599
+ await execFileAsync$12("git", [
31531
31600
  "add",
31532
31601
  "--",
31533
31602
  ...filesToStage
@@ -31536,7 +31605,7 @@ async function completePlanningForIssue(options) {
31536
31605
  encoding: "utf-8"
31537
31606
  });
31538
31607
  try {
31539
- await execFileAsync$7("git", [
31608
+ await execFileAsync$12("git", [
31540
31609
  "diff",
31541
31610
  "--cached",
31542
31611
  "--quiet",
@@ -31547,7 +31616,7 @@ async function completePlanningForIssue(options) {
31547
31616
  encoding: "utf-8"
31548
31617
  });
31549
31618
  } catch {
31550
- await execFileAsync$7("git", [
31619
+ await execFileAsync$12("git", [
31551
31620
  "commit",
31552
31621
  "-m",
31553
31622
  `chore(scope): propose ${upperIssueId} vBRIEF`,
@@ -31560,7 +31629,7 @@ async function completePlanningForIssue(options) {
31560
31629
  });
31561
31630
  console.log(`[complete-planning] Committed pan spec on main for ${upperIssueId}`);
31562
31631
  try {
31563
- const { stdout: remotes } = await execFileAsync$7("git", ["remote"], {
31632
+ const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
31564
31633
  cwd: projectPath,
31565
31634
  encoding: "utf-8"
31566
31635
  });
@@ -31573,16 +31642,16 @@ async function completePlanningForIssue(options) {
31573
31642
  }
31574
31643
  } else console.log(`[complete-planning] Project root not on main (${currentBranch}) — pan spec updated on disk but not committed on main`);
31575
31644
  }
31576
- if (!existsSync$1(join$1(gitRoot, ".git"))) await execFileAsync$7("git", ["init"], {
31645
+ if (!existsSync$1(join$1(gitRoot, ".git"))) await execFileAsync$12("git", ["init"], {
31577
31646
  cwd: gitRoot,
31578
31647
  encoding: "utf-8"
31579
31648
  });
31580
- for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$7("git", args, {
31649
+ for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$12("git", args, {
31581
31650
  cwd: gitRoot,
31582
31651
  encoding: "utf-8"
31583
31652
  });
31584
31653
  try {
31585
- await execFileAsync$7("git", [
31654
+ await execFileAsync$12("git", [
31586
31655
  "diff",
31587
31656
  "--cached",
31588
31657
  "--quiet"
@@ -31591,7 +31660,7 @@ async function completePlanningForIssue(options) {
31591
31660
  encoding: "utf-8"
31592
31661
  });
31593
31662
  } catch {
31594
- await execFileAsync$7("git", [
31663
+ await execFileAsync$12("git", [
31595
31664
  "commit",
31596
31665
  "-m",
31597
31666
  `chore(plan): complete planning for ${id}`,
@@ -31602,7 +31671,7 @@ async function completePlanningForIssue(options) {
31602
31671
  });
31603
31672
  }
31604
31673
  try {
31605
- const { stdout: remotes } = await execFileAsync$7("git", ["remote"], {
31674
+ const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
31606
31675
  cwd: gitRoot,
31607
31676
  encoding: "utf-8"
31608
31677
  });
@@ -31680,6 +31749,7 @@ async function completePlanningForIssue(options) {
31680
31749
  source: "planning-agent",
31681
31750
  eventType: "planning.complete"
31682
31751
  });
31752
+ if (autoSpawn && workspacePath) await commitWorkspaceRecordBeforeAutoSpawn(workspacePath, id);
31683
31753
  const autoSpawnResult = await completePlanningAutoSpawnAndKill({
31684
31754
  issueId: id,
31685
31755
  autoSpawn,
@@ -31779,8 +31849,8 @@ init_tracker_utils();
31779
31849
  init_agents$1();
31780
31850
  init_issue_id();
31781
31851
  init_io();
31782
- const execAsync$11 = promisify(exec);
31783
- const execFileAsync$6 = promisify(execFile);
31852
+ const execAsync$10 = promisify(exec);
31853
+ const execFileAsync$11 = promisify(execFile);
31784
31854
  function isGitHubIssue$4(issueId) {
31785
31855
  const resolved = resolveGitHubIssueSync(issueId);
31786
31856
  if (resolved.isGitHub) return {
@@ -31819,7 +31889,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
31819
31889
  if (!githubCheck.isGitHub || !githubCheck.owner || !githubCheck.repo) return ["No GitHub PR to close"];
31820
31890
  const branchName = `feature/${issueId.toLowerCase()}`;
31821
31891
  try {
31822
- const { stdout: prListRaw } = await execFileAsync$6("gh", [
31892
+ const { stdout: prListRaw } = await execFileAsync$11("gh", [
31823
31893
  "pr",
31824
31894
  "list",
31825
31895
  "--repo",
@@ -31838,7 +31908,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
31838
31908
  });
31839
31909
  const prNumber = prListRaw.trim();
31840
31910
  if (!prNumber) return ["No open PR found for branch"];
31841
- await execFileAsync$6("gh", [
31911
+ await execFileAsync$11("gh", [
31842
31912
  "pr",
31843
31913
  "close",
31844
31914
  prNumber,
@@ -31851,7 +31921,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
31851
31921
  timeout: 15e3
31852
31922
  });
31853
31923
  try {
31854
- const { setReviewStatusSync } = await import("./review-status-DZSsEGO4.js");
31924
+ const { setReviewStatusSync } = await import("./review-status-Rr4SB2Kj.js");
31855
31925
  setReviewStatusSync(issueId.toUpperCase(), { prUrl: void 0 });
31856
31926
  } catch {}
31857
31927
  return [`Closed PR #${prNumber} on ${githubCheck.owner}/${githubCheck.repo}`];
@@ -31905,7 +31975,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31905
31975
  const { ctx, projectConfig } = buildLifecycleContext(id, getIssueDataService$8().getIssueSource(id) ?? void 0);
31906
31976
  const deleteWorkspace = opts.deleteWorkspace ?? true;
31907
31977
  cleanupLog.push(...await closeIssuePullRequest(id, mode === "cancel" ? "Canceled via Overdeck" : "Reset to Todo via Overdeck"));
31908
- const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-BG47TprR.js");
31978
+ const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-Va4AZGRx.js");
31909
31979
  const workflow = mode === "cancel" ? cancelIssueWorkflow : resetToTodo;
31910
31980
  const result = await Effect.runPromise(workflow(ctx, {
31911
31981
  deleteWorkspace,
@@ -31920,7 +31990,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31920
31990
  }));
31921
31991
  cleanupLog.push(...result.steps.flatMap((step) => step.details || [step.error].filter(Boolean)));
31922
31992
  if (mode === "cancel") try {
31923
- const { transitionVBriefOnMain } = await import("./lifecycle-io-De3FFKAu.js");
31993
+ const { transitionVBriefOnMain } = await import("./lifecycle-io-BHi2NPxK.js");
31924
31994
  const tx = await Effect.runPromise(transitionVBriefOnMain(ctx.projectPath, id, "cancelled", "cancelled", `scope: cancel ${id.toUpperCase()} vBRIEF`));
31925
31995
  if (tx.moved) cleanupLog.push(`vBRIEF moved ${tx.fromDir} → cancelled`);
31926
31996
  if (tx.committed) cleanupLog.push(`Committed vBRIEF cancellation on main`);
@@ -31928,8 +31998,8 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31928
31998
  cleanupLog.push(`vBRIEF cancel transition failed (non-fatal): ${err?.message ?? err}`);
31929
31999
  }
31930
32000
  try {
31931
- const { killAllReviewerSessions } = await import("./review-agent-CfFA3HqX.js");
31932
- const { resolveProjectFromIssueSync } = await import("./projects-BGOgJTpI.js");
32001
+ const { killAllReviewerSessions } = await import("./review-agent-DfN-7KW6.js");
32002
+ const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
31933
32003
  const projectKey = resolveProjectFromIssueSync(id)?.projectKey;
31934
32004
  if (projectKey) {
31935
32005
  const { killed } = await Effect.runPromise(killAllReviewerSessions(projectKey, id.toUpperCase()));
@@ -31943,7 +32013,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31943
32013
  cleanupLog.push("Cleared review status");
31944
32014
  } catch {}
31945
32015
  try {
31946
- const { resetPostMergeState } = await import("./merge-agent-BmLQPYGq.js");
32016
+ const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
31947
32017
  resetPostMergeState(id);
31948
32018
  resetPostMergeState(id.toUpperCase());
31949
32019
  cleanupLog.push("Cleared merge state");
@@ -31963,7 +32033,7 @@ function closeIssueTransition(options) {
31963
32033
  const { issueId, body, eventStore } = options;
31964
32034
  const { reason } = body;
31965
32035
  const projectPath = getProjectPath$3(void 0, extractPrefixSync(issueId) ?? issueId.split("-")[0]);
31966
- const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-BG47TprR.js"));
32036
+ const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
31967
32037
  const githubCheck = isGitHubIssue$4(issueId);
31968
32038
  const issueDataService = getIssueDataService$8();
31969
32039
  const issueSource = issueDataService.getIssueSource(issueId);
@@ -31986,7 +32056,7 @@ function closeIssueTransition(options) {
31986
32056
  };
31987
32057
  }
31988
32058
  const result = yield* closeWorkflow(ctx, { reason });
31989
- if (githubCheck.isGitHub) execAsync$11("pan sync", {
32059
+ if (githubCheck.isGitHub) execAsync$10("pan sync", {
31990
32060
  encoding: "utf-8",
31991
32061
  timeout: 3e4
31992
32062
  }).catch(() => {});
@@ -32197,13 +32267,13 @@ function reopenIssueTransition(options) {
32197
32267
  }
32198
32268
  yield* Effect.promise(async () => {
32199
32269
  try {
32200
- const { stdout } = await execAsync$11(`gh pr list --head ${`feature/${id.toLowerCase()}`} --state closed --json number,mergedAt --limit 1`, {
32270
+ const { stdout } = await execAsync$10(`gh pr list --head ${`feature/${id.toLowerCase()}`} --state closed --json number,mergedAt --limit 1`, {
32201
32271
  encoding: "utf-8",
32202
32272
  timeout: 15e3
32203
32273
  });
32204
32274
  const prs = JSON.parse(stdout.trim() || "[]");
32205
32275
  if (prs.length > 0 && !prs[0].mergedAt) {
32206
- await execAsync$11(`gh pr reopen ${prs[0].number}`, {
32276
+ await execAsync$10(`gh pr reopen ${prs[0].number}`, {
32207
32277
  encoding: "utf-8",
32208
32278
  timeout: 15e3
32209
32279
  });
@@ -32230,7 +32300,7 @@ function reopenIssueTransition(options) {
32230
32300
  else clearReviewStatus(id.toUpperCase());
32231
32301
  } catch {}
32232
32302
  try {
32233
- const { resetPostMergeState } = await import("./merge-agent-BmLQPYGq.js");
32303
+ const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
32234
32304
  resetPostMergeState(id);
32235
32305
  resetPostMergeState(id.toUpperCase());
32236
32306
  } catch {}
@@ -32250,9 +32320,9 @@ function reopenIssueTransition(options) {
32250
32320
  const projectPath = (teamPrefix ? findProjectByTeamSync(teamPrefix) : null)?.path || "";
32251
32321
  if (projectPath) {
32252
32322
  const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
32253
- const { createBeadsFromVBrief } = await import("./beads-D1muPgsx.js");
32323
+ const { createBeadsFromVBrief } = await import("./beads-B4ysfx-Y.js");
32254
32324
  if (existsSync$1(workspacePath) && await Effect.runPromise(findPlan(workspacePath))) try {
32255
- const { stdout: bdCheck } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$6("bd", [
32325
+ const { stdout: bdCheck } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$11("bd", [
32256
32326
  "list",
32257
32327
  "--json",
32258
32328
  "-l",
@@ -32327,7 +32397,7 @@ function moveIssueStatus(options) {
32327
32397
  "done"
32328
32398
  ];
32329
32399
  if (!targetStatus || !validStatuses.includes(targetStatus)) return jsonResponse({ error: `Invalid targetStatus. Must be one of: ${validStatuses.join(", ")}` }, { status: 400 });
32330
- const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-BQXOrIFn.js"));
32400
+ const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-nDkNqpYC.js"));
32331
32401
  const issueState = {
32332
32402
  backlog: "open",
32333
32403
  todo: "open",
@@ -32390,7 +32460,7 @@ function moveIssueStatus(options) {
32390
32460
  init_issue_service_singleton();
32391
32461
  init_issue_id();
32392
32462
  init_agents$1();
32393
- const execAsync$10 = promisify(exec);
32463
+ const execAsync$9 = promisify(exec);
32394
32464
  function getIssueDataService$7() {
32395
32465
  return getSharedIssueService();
32396
32466
  }
@@ -32415,28 +32485,28 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
32415
32485
  if (projectRoot) {
32416
32486
  const workspacePath = join$1(projectRoot, "workspaces", `feature-${issueLower}`);
32417
32487
  try {
32418
- if ((await execAsync$10("git worktree list --porcelain", {
32488
+ if ((await execAsync$9("git worktree list --porcelain", {
32419
32489
  cwd: projectRoot,
32420
32490
  encoding: "utf-8"
32421
32491
  })).stdout.includes(workspacePath)) {
32422
- await execAsync$10(`git worktree remove "${workspacePath}" --force`, {
32492
+ await execAsync$9(`git worktree remove "${workspacePath}" --force`, {
32423
32493
  cwd: projectRoot,
32424
32494
  encoding: "utf-8"
32425
32495
  });
32426
32496
  cleanupLog.push(`Removed git worktree: ${workspacePath}`);
32427
32497
  } else if (existsSync$1(workspacePath)) {
32428
- await execAsync$10(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32498
+ await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32429
32499
  cleanupLog.push(`Removed directory: ${workspacePath}`);
32430
32500
  }
32431
32501
  } catch {
32432
32502
  if (existsSync$1(workspacePath)) {
32433
- await execAsync$10(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32503
+ await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32434
32504
  cleanupLog.push(`Removed directory: ${workspacePath}`);
32435
32505
  }
32436
32506
  }
32437
32507
  const branchName = `feature/${issueLower}`;
32438
32508
  try {
32439
- await execAsync$10(`git branch -D "${branchName}" 2>/dev/null || true`, {
32509
+ await execAsync$9(`git branch -D "${branchName}" 2>/dev/null || true`, {
32440
32510
  cwd: projectRoot,
32441
32511
  encoding: "utf-8"
32442
32512
  });
@@ -32445,7 +32515,7 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
32445
32515
  }
32446
32516
  const agentDir = join$1(homedir$1(), ".overdeck", "agents", `agent-${issueLower}`);
32447
32517
  if (existsSync$1(agentDir)) {
32448
- await execAsync$10(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
32518
+ await execAsync$9(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
32449
32519
  cleanupLog.push(`Removed agent state: ${agentDir}`);
32450
32520
  }
32451
32521
  await Effect.runPromise(eventStore.append({
@@ -32554,7 +32624,7 @@ async function copySettingsToWorkspace(id) {
32554
32624
  success: false,
32555
32625
  error: "Workspace not found"
32556
32626
  }, { status: 404 });
32557
- const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-g8GBLo49.js");
32627
+ const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-BE2H8gD5.js");
32558
32628
  const result = copyOverdeckSettingsToWorkspaceSync(workspacePath);
32559
32629
  return jsonResponse({
32560
32630
  success: result.errors.length === 0 || result.copied.length > 0,
@@ -32587,7 +32657,7 @@ function generateTasksForIssue(id) {
32587
32657
  success: false,
32588
32658
  error: `No vBRIEF spec found on main for ${id} — run planning first.`
32589
32659
  }, { status: 409 });
32590
- const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-D1muPgsx.js"));
32660
+ const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-B4ysfx-Y.js"));
32591
32661
  const result = yield* createBeadsFromVBrief(workspacePath);
32592
32662
  if (!result.success || result.created.length === 0) {
32593
32663
  const errors = result.errors.length > 0 ? result.errors : ["Beads creation produced no tasks"];
@@ -32617,8 +32687,8 @@ init_projects();
32617
32687
  init_tracker_utils();
32618
32688
  init_tmux();
32619
32689
  init_typed_errors();
32620
- const execAsync$9 = promisify(exec);
32621
- const execFileAsync$5 = promisify(execFile);
32690
+ const execAsync$8 = promisify(exec);
32691
+ const execFileAsync$10 = promisify(execFile);
32622
32692
  function getIssueDataService$6() {
32623
32693
  return getSharedIssueService();
32624
32694
  }
@@ -32918,7 +32988,7 @@ function abortPlanningForIssue(options) {
32918
32988
  const featureWorkspacePath = join$1(projectPath, "workspaces", `feature-${issueIdentifier.toLowerCase()}`);
32919
32989
  const plainWorkspacePath = join$1(projectPath, "workspaces", issueIdentifier.toLowerCase());
32920
32990
  if (existsSync$1(existsSync$1(featureWorkspacePath) ? featureWorkspacePath : plainWorkspacePath)) {
32921
- await execFileAsync$5("pan", [
32991
+ await execFileAsync$10("pan", [
32922
32992
  "workspace",
32923
32993
  "destroy",
32924
32994
  issueIdentifier.toLowerCase(),
@@ -33027,10 +33097,10 @@ function restartFromPlan(options) {
33027
33097
  });
33028
33098
  const resetResult = yield* Effect.promise(async () => {
33029
33099
  try {
33030
- const { runGitResetHard } = await import("./dangerous-git-ops-bz-261-k.js");
33100
+ const { runGitResetHard } = await import("./dangerous-git-ops-C9iYTZ0M.js");
33031
33101
  async function findPlanningCommit(grep, label) {
33032
33102
  try {
33033
- const { stdout } = await execAsync$9(`git log --grep="${grep.replace(/"/g, "\\\"")}" --format=%H -1`, {
33103
+ const { stdout } = await execAsync$8(`git log --grep="${grep.replace(/"/g, "\\\"")}" --format=%H -1`, {
33034
33104
  cwd: workspacePath,
33035
33105
  encoding: "utf-8",
33036
33106
  timeout: 1e4
@@ -33046,7 +33116,7 @@ function restartFromPlan(options) {
33046
33116
  }
33047
33117
  const found = await findPlanningCommit(`Complete planning for ${id}`, "complete-planning message") ?? await findPlanningCommit(`chore: planning artifacts for ${id}`, "agent-start message") ?? await (async () => {
33048
33118
  try {
33049
- const { stdout } = await execAsync$9(`git log --diff-filter=A --format=%H -1 -- .pan/spec.vbrief.json`, {
33119
+ const { stdout } = await execAsync$8(`git log --diff-filter=A --format=%H -1 -- .pan/spec.vbrief.json`, {
33050
33120
  cwd: workspacePath,
33051
33121
  encoding: "utf-8",
33052
33122
  timeout: 1e4
@@ -33173,6 +33243,7 @@ function getPlanningState(id) {
33173
33243
  }
33174
33244
  //#endregion
33175
33245
  //#region ../../lib/overdeck/discussions.ts
33246
+ init_cost_sync();
33176
33247
  init_github_app();
33177
33248
  init_pull_requests();
33178
33249
  init_issue_id();
@@ -33180,7 +33251,7 @@ init_github_pr_lookup();
33180
33251
  init_tracker_utils();
33181
33252
  init_tracker_config();
33182
33253
  init_pr_tab_cache();
33183
- const execAsync$8 = promisify(exec);
33254
+ const execAsync$7 = promisify(exec);
33184
33255
  function isGitHubIssue$2(issueId) {
33185
33256
  const resolved = resolveGitHubIssueSync(issueId);
33186
33257
  if (resolved.isGitHub) return {
@@ -33228,7 +33299,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33228
33299
  if (githubCheck.isGitHub && githubCheck.owner && githubCheck.repo && githubCheck.number) {
33229
33300
  const { owner, repo, number } = githubCheck;
33230
33301
  try {
33231
- const { stdout } = await execAsync$8(`gh api "repos/${owner}/${repo}/issues/${number}/comments?per_page=100"`, {
33302
+ const { stdout } = await execAsync$7(`gh api "repos/${owner}/${repo}/issues/${number}/comments?per_page=100"`, {
33232
33303
  encoding: "utf-8",
33233
33304
  timeout: 15e3,
33234
33305
  maxBuffer: 8 * 1024 * 1024
@@ -33291,7 +33362,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33291
33362
  const collectedItems = [];
33292
33363
  const prConversation = (async () => {
33293
33364
  try {
33294
- const { stdout } = await execAsync$8(`gh api "repos/${prOwner}/${prRepo}/issues/${prNumber}/comments?per_page=100"`, {
33365
+ const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/issues/${prNumber}/comments?per_page=100"`, {
33295
33366
  encoding: "utf-8",
33296
33367
  timeout: 15e3,
33297
33368
  maxBuffer: 8 * 1024 * 1024
@@ -33312,7 +33383,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33312
33383
  })();
33313
33384
  const prReviews = (async () => {
33314
33385
  try {
33315
- const { stdout } = await execAsync$8(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/reviews?per_page=100"`, {
33386
+ const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/reviews?per_page=100"`, {
33316
33387
  encoding: "utf-8",
33317
33388
  timeout: 15e3,
33318
33389
  maxBuffer: 8 * 1024 * 1024
@@ -33337,7 +33408,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33337
33408
  })();
33338
33409
  const prInlineComments = (async () => {
33339
33410
  try {
33340
- const { stdout } = await execAsync$8(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/comments?per_page=100"`, {
33411
+ const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/comments?per_page=100"`, {
33341
33412
  encoding: "utf-8",
33342
33413
  timeout: 15e3,
33343
33414
  maxBuffer: 8 * 1024 * 1024
@@ -33479,7 +33550,7 @@ function closeOutIssue(id) {
33479
33550
  }
33480
33551
  const closeOutResult = yield* Effect.promise(async () => {
33481
33552
  try {
33482
- const { closeOut } = await import("./lifecycle-BG47TprR.js");
33553
+ const { closeOut } = await import("./lifecycle-Va4AZGRx.js");
33483
33554
  return {
33484
33555
  ok: true,
33485
33556
  result: await Effect.runPromise(closeOut(ctx))
@@ -33565,7 +33636,7 @@ function bulkCloseOut(body) {
33565
33636
  const invalidIds = issueIds.filter((id) => !isValidIssueId(id));
33566
33637
  if (invalidIds.length > 0) return jsonResponse({ error: `Invalid issue ID format: ${invalidIds.join(", ")}` }, { status: 400 });
33567
33638
  const eventStore = yield* EventStoreService;
33568
- const { closeOut } = yield* Effect.promise(() => import("./lifecycle-BG47TprR.js"));
33639
+ const { closeOut } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
33569
33640
  const issueDataService = getIssueDataService$5();
33570
33641
  const tasks = [];
33571
33642
  const agentChecks = yield* withConcurrencyLimit(issueIds.map((id) => Effect.promise(async () => {
@@ -33732,7 +33803,7 @@ promisify(execFile);
33732
33803
  function getIssueDataService$4() {
33733
33804
  return getSharedIssueService();
33734
33805
  }
33735
- const readJsonBody$14 = Effect.gen(function* () {
33806
+ const readJsonBody$15 = Effect.gen(function* () {
33736
33807
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
33737
33808
  try {
33738
33809
  return text ? JSON.parse(text) : {};
@@ -33757,12 +33828,28 @@ const getIssueAnalyzeRoute = HttpRouter.add("GET", "/api/issues/:id/analyze", ht
33757
33828
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33758
33829
  return yield* analyzeIssue(id);
33759
33830
  })));
33831
+ const getIssueShipLogRoute = HttpRouter.add("GET", "/api/issues/:id/ship-log", httpHandler(Effect.gen(function* () {
33832
+ const id = (yield* HttpRouter.params)["id"] ?? "";
33833
+ if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33834
+ return yield* Effect.promise(async () => {
33835
+ const { getShipLog } = await import("./ship-log-EV79hCrN.js");
33836
+ const { getReviewStatusSync } = await import("./review-status-Rr4SB2Kj.js");
33837
+ const log = getShipLog(id);
33838
+ const rs = getReviewStatusSync(id);
33839
+ return jsonResponse({
33840
+ issueId: id.toUpperCase(),
33841
+ mergeStatus: rs?.mergeStatus ?? null,
33842
+ mergeStep: rs?.mergeStep ?? null,
33843
+ log
33844
+ });
33845
+ });
33846
+ })));
33760
33847
  const postIssueCloseRoute = HttpRouter.add("POST", "/api/issues/:issueId/close", httpHandler(Effect.gen(function* () {
33761
33848
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
33762
33849
  if (!parseIssueIdSync(issueId)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33763
33850
  return yield* closeIssueTransition({
33764
33851
  issueId,
33765
- body: yield* readJsonBody$14,
33852
+ body: yield* readJsonBody$15,
33766
33853
  eventStore: yield* EventStoreService
33767
33854
  });
33768
33855
  })));
@@ -33771,7 +33858,7 @@ const postIssueStartPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/star
33771
33858
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33772
33859
  return yield* startPlanningForIssue({
33773
33860
  id,
33774
- body: yield* readJsonBody$14,
33861
+ body: yield* readJsonBody$15,
33775
33862
  eventStore: yield* EventStoreService,
33776
33863
  linear: yield* LinearClient,
33777
33864
  github: yield* GitHubClient,
@@ -33784,7 +33871,7 @@ const postIssueAbortPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/abor
33784
33871
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33785
33872
  return yield* abortPlanningForIssue({
33786
33873
  id,
33787
- body: yield* readJsonBody$14,
33874
+ body: yield* readJsonBody$15,
33788
33875
  lifecycle: yield* IssueLifecycle,
33789
33876
  linear: yield* LinearClient,
33790
33877
  eventStore: yield* EventStoreService
@@ -33794,7 +33881,7 @@ const postIssueCompletePlanningRoute = HttpRouter.add("POST", "/api/issues/:id/c
33794
33881
  const request = yield* HttpServerRequest.HttpServerRequest;
33795
33882
  const id = (yield* HttpRouter.params)["id"] ?? "";
33796
33883
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33797
- const body = yield* readJsonBody$14;
33884
+ const body = yield* readJsonBody$15;
33798
33885
  const eventStore = yield* EventStoreService;
33799
33886
  const linear = yield* LinearClient;
33800
33887
  const lifecycle = yield* IssueLifecycle;
@@ -33820,7 +33907,7 @@ const postIssueResetRoute = HttpRouter.add("POST", "/api/issues/:id/reset", http
33820
33907
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33821
33908
  return yield* resetIssueTransition({
33822
33909
  id,
33823
- body: yield* readJsonBody$14,
33910
+ body: yield* readJsonBody$15,
33824
33911
  eventStore: yield* EventStoreService
33825
33912
  });
33826
33913
  })));
@@ -33829,7 +33916,7 @@ const postIssueCancelRoute = HttpRouter.add("POST", "/api/issues/:id/cancel", ht
33829
33916
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33830
33917
  return yield* cancelIssueTransition({
33831
33918
  id,
33832
- body: yield* readJsonBody$14,
33919
+ body: yield* readJsonBody$15,
33833
33920
  eventStore: yield* EventStoreService
33834
33921
  });
33835
33922
  })));
@@ -33840,7 +33927,7 @@ const postIssueReopenRoute = HttpRouter.add("POST", "/api/issues/:id/reopen", ht
33840
33927
  if (authError) return authError;
33841
33928
  return yield* reopenIssueTransition({
33842
33929
  id,
33843
- body: yield* readJsonBody$14,
33930
+ body: yield* readJsonBody$15,
33844
33931
  lifecycle: yield* IssueLifecycle,
33845
33932
  linear: yield* LinearClient,
33846
33933
  eventStore: yield* EventStoreService
@@ -33860,7 +33947,7 @@ const postIssueMoveStatusRoute = HttpRouter.add("POST", "/api/issues/:id/move-st
33860
33947
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33861
33948
  return yield* moveIssueStatus({
33862
33949
  id,
33863
- body: yield* readJsonBody$14,
33950
+ body: yield* readJsonBody$15,
33864
33951
  eventStore: yield* EventStoreService,
33865
33952
  lifecycle: yield* IssueLifecycle
33866
33953
  });
@@ -33872,7 +33959,7 @@ const postIssueCleanupWorkspaceRoute = HttpRouter.add("POST", "/api/issues/:id/c
33872
33959
  })));
33873
33960
  const postIssueDeepWipeRoute = HttpRouter.add("POST", "/api/issues/:id/deep-wipe", httpHandler(Effect.gen(function* () {
33874
33961
  const id = (yield* HttpRouter.params)["id"] ?? "";
33875
- const body = yield* readJsonBody$14;
33962
+ const body = yield* readJsonBody$15;
33876
33963
  const eventStore = yield* EventStoreService;
33877
33964
  return yield* Effect.promise(() => deepWipeIssue(id, body, eventStore));
33878
33965
  })));
@@ -33912,7 +33999,7 @@ const postIssueBeadInspectRoute = HttpRouter.add("POST", "/api/issues/:id/beads/
33912
33999
  return yield* inspectIssueBead({
33913
34000
  id: (params["id"] ?? "").toUpperCase(),
33914
34001
  beadId: params["beadId"] ?? "",
33915
- body: yield* readJsonBody$14
34002
+ body: yield* readJsonBody$15
33916
34003
  });
33917
34004
  })));
33918
34005
  const getIssuePlanningStateRoute = HttpRouter.add("GET", "/api/issues/:id/planning-state", httpHandler(Effect.gen(function* () {
@@ -33975,7 +34062,7 @@ const getIssueDiscussionsRoute = HttpRouter.add("GET", "/api/issues/:id/discussi
33975
34062
  const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHandler(Effect.gen(function* () {
33976
34063
  const id = (yield* HttpRouter.params)["id"] ?? "";
33977
34064
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33978
- const issueData = getCostsForIssueSync(id);
34065
+ const issueData = getCostForIssueAggregateSync(id);
33979
34066
  const agents = yield* Effect.promise(() => getCachedRunningAgents());
33980
34067
  const resolvedCost = resolveIssueHeadlineCost({
33981
34068
  issueId: id,
@@ -34023,7 +34110,7 @@ const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHa
34023
34110
  cost: stats.cost,
34024
34111
  tokens: stats.tokens
34025
34112
  }])),
34026
- budget: issueData.budget,
34113
+ budget: void 0,
34027
34114
  budgetWarning: issueData.budgetWarning,
34028
34115
  lastUpdated: issueData.lastUpdated
34029
34116
  });
@@ -34034,7 +34121,7 @@ const getResourceAllocatedIssuesRoute = HttpRouter.add("GET", "/api/issues/resou
34034
34121
  const getIssueResourceDetailsRoute = HttpRouter.add("GET", "/api/issues/:id/resource-details", httpHandler(Effect.gen(function* () {
34035
34122
  return yield* getIssueResourceDetails((yield* HttpRouter.params)["id"] ?? "");
34036
34123
  })));
34037
- const issuesRouteLayer = Layer.mergeAll(getIssuesRoute, getIssueAnalyzeRoute, postIssueCloseRoute, postIssueStartPlanningRoute, postIssueAbortPlanningRoute, postIssueCompletePlanningRoute, postIssueAbortRoute, postIssueResetRoute, postIssueCancelRoute, postIssueReopenRoute, postIssueRestartFromPlanRoute, postIssueMoveStatusRoute, postIssueCleanupWorkspaceRoute, postIssueDeepWipeRoute, postIssueCopySettingsRoute, postIssueCloseOutRoute, postIssuesBulkCloseOutRoute, getIssueBeadsRoute, postIssueBeadInspectRoute, getIssuePlanningStateRoute, postIssueGenerateTasksRoute, getIssueCostsRoute, getResourceAllocatedIssuesRoute, getIssueResourceDetailsRoute, getIssuePrRoute, getIssuePrDiffRoute, getIssuePrDetailsRoute, getIssueCheckRunsRoute, getIssueDiscussionsRoute);
34124
+ const issuesRouteLayer = Layer.mergeAll(getIssuesRoute, getIssueAnalyzeRoute, getIssueShipLogRoute, postIssueCloseRoute, postIssueStartPlanningRoute, postIssueAbortPlanningRoute, postIssueCompletePlanningRoute, postIssueAbortRoute, postIssueResetRoute, postIssueCancelRoute, postIssueReopenRoute, postIssueRestartFromPlanRoute, postIssueMoveStatusRoute, postIssueCleanupWorkspaceRoute, postIssueDeepWipeRoute, postIssueCopySettingsRoute, postIssueCloseOutRoute, postIssuesBulkCloseOutRoute, getIssueBeadsRoute, postIssueBeadInspectRoute, getIssuePlanningStateRoute, postIssueGenerateTasksRoute, getIssueCostsRoute, getResourceAllocatedIssuesRoute, getIssueResourceDetailsRoute, getIssuePrRoute, getIssuePrDiffRoute, getIssuePrDetailsRoute, getIssueCheckRunsRoute, getIssueDiscussionsRoute);
34038
34125
  //#endregion
34039
34126
  //#region routes/costs.ts
34040
34127
  /**
@@ -34069,7 +34156,7 @@ const getCostsSummaryRoute = HttpRouter.add("GET", "/api/costs/summary", httpHan
34069
34156
  const monthEntries = scope(readEventsSync({ startDate: monthAgo }));
34070
34157
  const summarize = (entries) => ({
34071
34158
  totalCost: entries.reduce((sum, e) => sum + (e.cost || 0), 0),
34072
- totalTokens: entries.reduce((sum, e) => sum + ((e.input || 0) + (e.output || 0)), 0),
34159
+ totalTokens: entries.reduce((sum, e) => sum + ((e.input || 0) + (e.output || 0) + (e.cacheRead || 0) + (e.cacheWrite || 0)), 0),
34073
34160
  entryCount: entries.length,
34074
34161
  byModel: entries.reduce((acc, e) => {
34075
34162
  if (e.model) acc[e.model] = (acc[e.model] || 0) + (e.cost || 0);
@@ -34256,16 +34343,45 @@ const postCostsSyncWalRoute = HttpRouter.add("POST", "/api/costs/sync-wal", http
34256
34343
  })
34257
34344
  });
34258
34345
  })));
34346
+ const runOverdeckCostReconcileSource = (source) => Effect.runPromise(CostWriter.use((writer) => writer.reconcile({ source })).pipe(Effect.provide(CostDoorLive)));
34347
+ const runClaudeTranscriptReconcile = () => Effect.runPromise(reconcile());
34348
+ async function runCostReconcileSources(runSource = runOverdeckCostReconcileSource, runClaude = runClaudeTranscriptReconcile) {
34349
+ const [claude, ohmypi, codex] = await Promise.all([
34350
+ runClaude(),
34351
+ runSource("ohmypi"),
34352
+ runSource("codex")
34353
+ ]);
34354
+ return {
34355
+ claude,
34356
+ ohmypi,
34357
+ codex
34358
+ };
34359
+ }
34360
+ function buildCostReconcileResponse(overdeck) {
34361
+ return {
34362
+ success: true,
34363
+ ...overdeck.claude,
34364
+ claude: overdeck.claude,
34365
+ ohmypi: overdeck.ohmypi,
34366
+ codex: overdeck.codex,
34367
+ overdeck,
34368
+ skipped: {
34369
+ ohmypi: overdeck.ohmypi.skipped,
34370
+ codex: overdeck.codex.skipped
34371
+ },
34372
+ warnings: {
34373
+ ohmypi: overdeck.ohmypi.warnings,
34374
+ codex: overdeck.codex.warnings
34375
+ }
34376
+ };
34377
+ }
34259
34378
  const postCostsReconcileRoute = HttpRouter.add("POST", "/api/costs/reconcile", httpHandler(Effect.gen(function* () {
34260
- const result = yield* Effect.tryPromise({
34261
- try: () => Effect.runPromise(reconcile()),
34379
+ const overdeck = yield* Effect.tryPromise({
34380
+ try: () => runCostReconcileSources(),
34262
34381
  catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34263
34382
  });
34264
- console.log(`[reconciler] Sweep complete: ${result.eventsImported ?? 0} imported`);
34265
- return jsonResponse({
34266
- success: true,
34267
- ...result
34268
- });
34383
+ console.log(`[reconciler] Sweep complete: claude=${overdeck.claude.eventsImported} imported, ohmypi=${overdeck.ohmypi.eventsImported} imported, codex=${overdeck.codex.eventsImported} imported`);
34384
+ return jsonResponse(buildCostReconcileResponse(overdeck));
34269
34385
  })));
34270
34386
  const getCostsExperimentsRoute = HttpRouter.add("GET", "/api/costs/experiments", httpHandler(Effect.try({
34271
34387
  try: () => jsonResponse({ experiments: getCavemanExperimentDataSync() }),
@@ -34542,7 +34658,7 @@ function reloadDurableCloisterConfig(deps = {}) {
34542
34658
  */
34543
34659
  init_config();
34544
34660
  init_agents$1();
34545
- const readJsonBody$13 = Effect.gen(function* () {
34661
+ const readJsonBody$14 = Effect.gen(function* () {
34546
34662
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
34547
34663
  try {
34548
34664
  return text ? JSON.parse(text) : {};
@@ -34645,7 +34761,7 @@ const getCloisterConfigRoute = HttpRouter.add("GET", "/api/cloister/config", htt
34645
34761
  catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34646
34762
  })));
34647
34763
  const putCloisterConfigRoute = HttpRouter.add("PUT", "/api/cloister/config", httpHandler(Effect.gen(function* () {
34648
- const updates = yield* readJsonBody$13;
34764
+ const updates = yield* readJsonBody$14;
34649
34765
  yield* Effect.try({
34650
34766
  try: () => {
34651
34767
  saveCloisterConfigSync(updates);
@@ -34669,25 +34785,18 @@ const getCloisterAgentsHealthRoute = HttpRouter.add("GET", "/api/cloister/agents
34669
34785
  })));
34670
34786
  const cloisterRouteLayer = Layer.mergeAll(getCloisterStatusRoute, postCloisterStartRoute, postCloisterStopRoute, postCloisterEmergencyStopRoute, postCloisterBrakeRoute, postCloisterResumeSpawnsRoute, getCloisterSpawnStatusRoute, getCloisterConfigRoute, putCloisterConfigRoute, getCloisterAgentsHealthRoute);
34671
34787
  //#endregion
34672
- //#region routes/resources.ts
34673
- /**
34674
- * Resources route module — Effect HttpRouter.Layer (PAN-428 B12)
34675
- *
34676
- * Implements all /api/resources/* endpoints from the Express server:
34677
- * GET /api/resources
34678
- * GET /api/resources/:containerId/history
34679
- * GET /api/resources/:containerId/details
34680
- * DELETE /api/resources/docker/container/:id
34681
- * POST /api/resources/docker/prune-containers
34682
- * DELETE /api/resources/docker/network/:name
34683
- * DELETE /api/resources/docker/volume/:name
34684
- * POST /api/resources/docker/prune-volumes
34685
- */
34788
+ //#region routes/resources/shared.ts
34686
34789
  init_docker_stats();
34687
- init_domain_services();
34688
- init_agents$1();
34689
- init_tmux();
34690
- const execAsync$7 = promisify(exec);
34790
+ function getCurrentDockerStats() {
34791
+ return [];
34792
+ }
34793
+ function getContainerHistory(containerId) {
34794
+ return {
34795
+ timestamps: [],
34796
+ cpuPercent: [],
34797
+ memoryPercent: []
34798
+ };
34799
+ }
34691
34800
  function formatUptime(startedAt) {
34692
34801
  const ms = Date.now() - new Date(startedAt).getTime();
34693
34802
  const h = Math.floor(ms / 36e5);
@@ -34696,45 +34805,933 @@ function formatUptime(startedAt) {
34696
34805
  if (h > 0) return `${h}h ${m}m`;
34697
34806
  return `${m}m`;
34698
34807
  }
34808
+ //#endregion
34809
+ //#region routes/resources/agents-stats.ts
34810
+ init_agents$1();
34811
+ init_cost_sync();
34812
+ init_tmux();
34813
+ const execFileAsync$9 = promisify(execFile);
34814
+ const BURN_WINDOW_MS = 1800 * 1e3;
34815
+ function buildAgentStatsSnapshot(options) {
34816
+ const nowMs = options.nowMs ?? Date.now();
34817
+ const processTotalsByRoot = buildProcessTotalsByRoot(options.sessionRoots, options.processes);
34818
+ const rows = options.agents.filter((agent) => agent.status !== "stopped").map((agent) => {
34819
+ const root = options.sessionRoots.find((sessionRoot) => sessionRoot.agentId === agent.id);
34820
+ const processTotals = root ? processTotalsByRoot.get(root.rootPid) : void 0;
34821
+ const costStats = computeAgentCostStats(options.costEventsByAgent?.get(agent.id) ?? [], nowMs);
34822
+ const idleMinutes = getIdleMinutes(agent.lastActivity ?? agent.startedAt, nowMs);
34823
+ return {
34824
+ id: agent.id,
34825
+ issueId: agent.issueId,
34826
+ role: agent.role,
34827
+ model: agent.model,
34828
+ status: agent.status,
34829
+ statusChip: {
34830
+ state: idleMinutes > 5 ? "idle" : "working",
34831
+ idleMinutes,
34832
+ fanOut: (processTotals?.processCount ?? 0) >= 8
34833
+ },
34834
+ rootPid: root?.rootPid ?? null,
34835
+ processCount: processTotals?.processCount ?? 0,
34836
+ cpuPercent: processTotals?.cpuPercent ?? 0,
34837
+ memoryBytes: processTotals?.memoryBytes ?? 0,
34838
+ burnUsdPerHour: costStats.burnUsdPerHour,
34839
+ ...costStats.hypotheticalUsdPerHour > 0 ? { hypotheticalUsdPerHour: costStats.hypotheticalUsdPerHour } : {},
34840
+ totalUsd: costStats.totalUsd
34841
+ };
34842
+ });
34843
+ return {
34844
+ agents: rows,
34845
+ hostVitals: { agents: {
34846
+ burnUsdPerHour: roundCurrency(rows.reduce((sum, row) => sum + row.burnUsdPerHour, 0)),
34847
+ hypotheticalUsdPerHour: roundCurrency(rows.reduce((sum, row) => sum + (row.hypotheticalUsdPerHour ?? 0), 0)),
34848
+ totalUsd: roundCurrency(rows.reduce((sum, row) => sum + row.totalUsd, 0))
34849
+ } }
34850
+ };
34851
+ }
34852
+ function getAgentStatsSnapshotEffect(deps = {}) {
34853
+ return Effect.gen(function* () {
34854
+ const nowMs = deps.nowMs ?? Date.now();
34855
+ const agents = (deps.listAgents ?? listAgentStates)().filter((agent) => agent.status !== "stopped");
34856
+ const sessionNames = yield* (deps.listSessionNames ?? defaultListSessionNames)().pipe(Effect.catch(() => Effect.succeed([])));
34857
+ const liveSessionNames = new Set(sessionNames);
34858
+ const sessionRoots = [];
34859
+ for (const agent of agents) {
34860
+ if (!liveSessionNames.has(agent.id)) continue;
34861
+ const rootPid = (yield* (deps.listPanePids ?? defaultListPanePids)(agent.id).pipe(Effect.catch(() => Effect.succeed([]))))[0];
34862
+ if (rootPid) sessionRoots.push({
34863
+ agentId: agent.id,
34864
+ rootPid
34865
+ });
34866
+ }
34867
+ const processes = sessionRoots.length > 0 ? yield* Effect.promise(() => (deps.readProcessTable ?? readBatchedProcessTable)()).pipe(Effect.catch(() => Effect.succeed([]))) : [];
34868
+ const costEventsByAgent = /* @__PURE__ */ new Map();
34869
+ const costQuery = deps.queryCostEvents ?? queryCostEventsSync;
34870
+ for (const agent of agents) {
34871
+ const recentStartTs = new Date(nowMs - BURN_WINDOW_MS).toISOString();
34872
+ costEventsByAgent.set(agent.id, [...costQuery({ agentId: agent.id }), ...costQuery({
34873
+ agentId: agent.id,
34874
+ startTs: recentStartTs
34875
+ })]);
34876
+ }
34877
+ return buildAgentStatsSnapshot({
34878
+ agents,
34879
+ sessionRoots,
34880
+ processes,
34881
+ costEventsByAgent,
34882
+ nowMs
34883
+ });
34884
+ });
34885
+ }
34886
+ function parseProcessTable$1(psTable) {
34887
+ return psTable.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
34888
+ const match = line.match(/^(\d+)\s+(\d+)\s+([0-9.]+)\s+(\d+)$/);
34889
+ if (!match) return [];
34890
+ return [{
34891
+ pid: Number(match[1]),
34892
+ ppid: Number(match[2]),
34893
+ cpuPercent: Number(match[3]),
34894
+ rssBytes: Number(match[4]) * 1024
34895
+ }];
34896
+ });
34897
+ }
34898
+ async function readBatchedProcessTable() {
34899
+ const { stdout } = await execFileAsync$9("ps", ["-eo", "pid=,ppid=,pcpu=,rss="], { encoding: "utf8" });
34900
+ return parseProcessTable$1(stdout);
34901
+ }
34902
+ function defaultListSessionNames() {
34903
+ return listSessions().pipe(Effect.map((sessions) => sessions.map((session) => session.name)));
34904
+ }
34905
+ function defaultListPanePids(sessionName) {
34906
+ return listPaneValues(sessionName, "#{pane_pid}").pipe(Effect.map((values) => values.map((value) => Number(value.trim())).filter((value) => Number.isFinite(value) && value > 0)));
34907
+ }
34908
+ function buildProcessTotalsByRoot(roots, processes) {
34909
+ const processByPid = new Map(processes.map((process) => [process.pid, process]));
34910
+ const childrenByPpid = /* @__PURE__ */ new Map();
34911
+ const totals = /* @__PURE__ */ new Map();
34912
+ for (const process of processes) {
34913
+ const siblings = childrenByPpid.get(process.ppid);
34914
+ if (siblings) siblings.push(process);
34915
+ else childrenByPpid.set(process.ppid, [process]);
34916
+ }
34917
+ for (const root of roots) {
34918
+ const queue = [root.rootPid];
34919
+ const seen = /* @__PURE__ */ new Set();
34920
+ let cpuPercent = 0;
34921
+ let memoryBytes = 0;
34922
+ while (queue.length > 0) {
34923
+ const pid = queue.pop();
34924
+ if (seen.has(pid)) continue;
34925
+ seen.add(pid);
34926
+ const process = processByPid.get(pid);
34927
+ if (process) {
34928
+ cpuPercent += process.cpuPercent;
34929
+ memoryBytes += process.rssBytes;
34930
+ }
34931
+ for (const child of childrenByPpid.get(pid) ?? []) queue.push(child.pid);
34932
+ }
34933
+ totals.set(root.rootPid, {
34934
+ processCount: seen.size,
34935
+ cpuPercent: roundOneDecimal$3(cpuPercent),
34936
+ memoryBytes
34937
+ });
34938
+ }
34939
+ return totals;
34940
+ }
34941
+ function computeAgentCostStats(events, nowMs) {
34942
+ const burnStartMs = nowMs - BURN_WINDOW_MS;
34943
+ let recentBillable = 0;
34944
+ let recentHypothetical = 0;
34945
+ let totalUsd = 0;
34946
+ for (const event of events) {
34947
+ const eventCost = event.hypotheticalCost ?? event.cost;
34948
+ if (isSubscriptionCovered(event)) recentHypothetical += Date.parse(event.ts) >= burnStartMs ? eventCost : 0;
34949
+ else {
34950
+ totalUsd += event.cost;
34951
+ recentBillable += Date.parse(event.ts) >= burnStartMs ? event.cost : 0;
34952
+ }
34953
+ }
34954
+ return {
34955
+ burnUsdPerHour: roundCurrency(recentBillable * 2),
34956
+ hypotheticalUsdPerHour: roundCurrency(recentHypothetical * 2),
34957
+ totalUsd: roundCurrency(totalUsd)
34958
+ };
34959
+ }
34960
+ function isSubscriptionCovered(event) {
34961
+ return event.subscriptionCovered === true || event.source === "subscription-covered";
34962
+ }
34963
+ function getIdleMinutes(lastActivity, nowMs) {
34964
+ if (!lastActivity) return 0;
34965
+ const lastActivityMs = Date.parse(lastActivity);
34966
+ if (!Number.isFinite(lastActivityMs)) return 0;
34967
+ return Math.max(0, Math.floor((nowMs - lastActivityMs) / 6e4));
34968
+ }
34969
+ function roundOneDecimal$3(value) {
34970
+ return Math.round(value * 10) / 10;
34971
+ }
34972
+ function roundCurrency(value) {
34973
+ return Math.round(value * 100) / 100;
34974
+ }
34975
+ //#endregion
34976
+ //#region services/event-loop-monitor.ts
34977
+ init_activity_logger();
34978
+ const EVENT_LOOP_MONITOR_WINDOW_MS = 6e4;
34979
+ let lastSample = {
34980
+ p50: 0,
34981
+ p99: 0,
34982
+ max: 0,
34983
+ unit: "ms",
34984
+ sampledAt: null,
34985
+ windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
34986
+ };
34987
+ let timer$3 = null;
34988
+ let activeHistogram = null;
34989
+ function nsToRoundedMs(ns) {
34990
+ if (!Number.isFinite(ns) || ns <= 0) return 0;
34991
+ return Math.round(ns / 1e6 * 100) / 100;
34992
+ }
34993
+ function sampleEventLoopDelay(histogram, deps = {}) {
34994
+ const warnThresholdMs = deps.warnThresholdMs ?? 100;
34995
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
34996
+ const warn = deps.warn ?? ((message) => console.warn(message));
34997
+ const emit = deps.emit ?? emitActivityEntrySync;
34998
+ const sample = {
34999
+ p50: nsToRoundedMs(histogram.percentile(50)),
35000
+ p99: nsToRoundedMs(histogram.percentile(99)),
35001
+ max: nsToRoundedMs(histogram.max),
35002
+ unit: "ms",
35003
+ sampledAt: now().toISOString(),
35004
+ windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
35005
+ };
35006
+ lastSample = sample;
35007
+ if (sample.p99 > warnThresholdMs) {
35008
+ const message = `Dashboard event loop p99 delay ${sample.p99}ms exceeded ${warnThresholdMs}ms`;
35009
+ warn(`[event-loop-monitor] ${message}`);
35010
+ emit({
35011
+ source: "dashboard",
35012
+ level: "warn",
35013
+ message,
35014
+ details: `p50=${sample.p50}ms p99=${sample.p99}ms max=${sample.max}ms over ${sample.windowMs}ms`
35015
+ });
35016
+ }
35017
+ histogram.reset();
35018
+ return sample;
35019
+ }
35020
+ function getEventLoopDelaySample() {
35021
+ return lastSample;
35022
+ }
35023
+ function startEventLoopMonitor(deps = {}) {
35024
+ if (timer$3) return;
35025
+ const intervalMs = deps.intervalMs ?? 6e4;
35026
+ activeHistogram = (deps.createHistogram ?? (() => monitorEventLoopDelay({ resolution: 20 })))();
35027
+ activeHistogram.enable();
35028
+ timer$3 = setInterval(() => {
35029
+ if (!activeHistogram) return;
35030
+ sampleEventLoopDelay(activeHistogram, deps);
35031
+ }, intervalMs);
35032
+ timer$3.unref?.();
35033
+ }
35034
+ function stopEventLoopMonitor() {
35035
+ if (timer$3) {
35036
+ clearInterval(timer$3);
35037
+ timer$3 = null;
35038
+ }
35039
+ activeHistogram?.disable();
35040
+ activeHistogram = null;
35041
+ }
35042
+ //#endregion
35043
+ //#region routes/resources/core-services.ts
35044
+ function buildCoreServices(options = {}) {
35045
+ const nowMs = options.nowMs ?? Date.now();
35046
+ const processInfo = options.processInfo ?? getCurrentProcessInfo();
35047
+ const eventLoopSample = options.eventLoopSample ?? getEventLoopDelaySample();
35048
+ const deaconStatus = options.deaconStatus ?? readDurableDeaconStatus();
35049
+ const supportFleet = aggregateSupportFleet(options.supportProcesses ?? []);
35050
+ return [
35051
+ {
35052
+ id: "dashboard",
35053
+ label: "Dashboard server",
35054
+ status: "running",
35055
+ cpuPercent: processInfo.cpuPercent,
35056
+ memoryBytes: processInfo.memoryBytes,
35057
+ memberCount: 1,
35058
+ nodeVersion: processInfo.nodeVersion,
35059
+ distPath: processInfo.distPath,
35060
+ uptime: formatDuration(processInfo.uptimeSeconds),
35061
+ uptimeSeconds: Math.round(processInfo.uptimeSeconds),
35062
+ eventLoopP99Ms: eventLoopSample.p99,
35063
+ actions: [{
35064
+ label: "Metrics",
35065
+ href: "/api/metrics/summary",
35066
+ method: "GET"
35067
+ }]
35068
+ },
35069
+ {
35070
+ id: "deacon",
35071
+ label: "Deacon",
35072
+ status: deaconStatus.isRunning ? "running" : "stopped",
35073
+ cpuPercent: 0,
35074
+ memoryBytes: 0,
35075
+ memberCount: deaconStatus.pid ? 1 : 0,
35076
+ pid: deaconStatus.pid,
35077
+ lastTickAgeSeconds: getLastTickAgeSeconds(deaconStatus, nowMs),
35078
+ patrolCycle: deaconStatus.lastPatrol?.cycle ?? deaconStatus.state?.patrolCycle ?? 0,
35079
+ patrolSummaryCount: deaconStatus.lastPatrol?.actions.length ?? 0,
35080
+ stuckCount: deaconStatus.state?.stuckCount ?? 0,
35081
+ actions: [
35082
+ {
35083
+ label: "Status",
35084
+ href: "/api/deacon/status",
35085
+ method: "GET"
35086
+ },
35087
+ {
35088
+ label: "Logs",
35089
+ href: "/api/deacon/logs",
35090
+ method: "GET"
35091
+ },
35092
+ {
35093
+ label: "Patrol",
35094
+ href: "/api/deacon/patrol",
35095
+ method: "POST"
35096
+ }
35097
+ ]
35098
+ },
35099
+ {
35100
+ id: "support-fleet",
35101
+ label: "Support fleet",
35102
+ status: supportFleet.memberCount > 0 ? "running" : "unknown",
35103
+ cpuPercent: supportFleet.cpuPercent,
35104
+ memoryBytes: supportFleet.memoryBytes,
35105
+ memberCount: supportFleet.memberCount,
35106
+ members: supportFleet.members,
35107
+ actions: []
35108
+ }
35109
+ ];
35110
+ }
35111
+ function getCoreServicesSnapshot() {
35112
+ return buildCoreServices();
35113
+ }
35114
+ function getCurrentProcessInfo() {
35115
+ return {
35116
+ uptimeSeconds: process.uptime(),
35117
+ nodeVersion: process.version,
35118
+ distPath: process.argv.find((arg) => arg.includes("/dist/")) ?? process.argv[1] ?? "",
35119
+ cpuPercent: 0,
35120
+ memoryBytes: process.memoryUsage().rss
35121
+ };
35122
+ }
35123
+ function aggregateSupportFleet(processes) {
35124
+ const supportProcesses = processes.filter(isSupportFleetProcess);
35125
+ const members = supportProcesses.map((process) => process.command);
35126
+ return {
35127
+ cpuPercent: roundOneDecimal$2(supportProcesses.reduce((sum, process) => sum + process.cpuPercent, 0)),
35128
+ memoryBytes: supportProcesses.reduce((sum, process) => sum + process.memoryBytes, 0),
35129
+ memberCount: supportProcesses.length,
35130
+ members
35131
+ };
35132
+ }
35133
+ function isSupportFleetProcess(process) {
35134
+ return /\b(traefik|smee|pty-supervisor|pty-supervisors|tldr)\b/i.test(process.command);
35135
+ }
35136
+ function getLastTickAgeSeconds(deaconStatus, nowMs) {
35137
+ const timestamp = deaconStatus.lastPatrol?.timestamp ?? deaconStatus.state?.lastPatrol;
35138
+ if (!timestamp) return null;
35139
+ const tickMs = Date.parse(timestamp);
35140
+ if (!Number.isFinite(tickMs)) return null;
35141
+ return Math.max(0, Math.floor((nowMs - tickMs) / 1e3));
35142
+ }
35143
+ function formatDuration(totalSeconds) {
35144
+ const roundedSeconds = Math.max(0, Math.floor(totalSeconds));
35145
+ const days = Math.floor(roundedSeconds / 86400);
35146
+ const hours = Math.floor(roundedSeconds % 86400 / 3600);
35147
+ const minutes = Math.floor(roundedSeconds % 3600 / 60);
35148
+ const seconds = roundedSeconds % 60;
35149
+ if (days > 0) return `${days}d ${hours}h`;
35150
+ if (hours > 0) return `${hours}h ${minutes}m`;
35151
+ if (minutes > 0) return `${minutes}m ${seconds}s`;
35152
+ return `${seconds}s`;
35153
+ }
35154
+ function roundOneDecimal$2(value) {
35155
+ return Math.round(value * 10) / 10;
35156
+ }
35157
+ //#endregion
35158
+ //#region routes/resources/forecast.ts
35159
+ init_paths();
35160
+ let forecastStateFileForTests = null;
35161
+ function buildCapacityForecast(stacks, options) {
35162
+ const records = loadForecastStore().peaks;
35163
+ const median = medianPeak(Object.values(records).map((record) => record.peakRamBytes));
35164
+ return {
35165
+ stacks: stacks.filter((stack) => stack.services.every((service) => service.status === "stopped")).map((stack) => {
35166
+ const record = records[stack.id];
35167
+ const predictedRamBytes = record?.peakRamBytes ?? median;
35168
+ return {
35169
+ stackId: stack.id,
35170
+ issueId: stack.issueId,
35171
+ composeProject: stack.composeProject,
35172
+ predictedRamBytes,
35173
+ predictedLoad: record?.peakCpuPercent ?? 0,
35174
+ approximate: true,
35175
+ source: record ? "last-run-peak" : "fleet-median"
35176
+ };
35177
+ }),
35178
+ headroom: {
35179
+ freeRamBytes: options.hostVitals.mem.availableBytes,
35180
+ loadHeadroom: Math.max(0, 100 - options.hostVitals.cpu.percent)
35181
+ }
35182
+ };
35183
+ }
35184
+ function forecastStateFile() {
35185
+ return forecastStateFileForTests ?? join$1(getOverdeckHome(), "resource-forecast-peaks.json");
35186
+ }
35187
+ function loadForecastStore() {
35188
+ const file = forecastStateFile();
35189
+ if (!existsSync$1(file)) return { peaks: {} };
35190
+ try {
35191
+ return { peaks: JSON.parse(readFileSync$1(file, "utf8")).peaks ?? {} };
35192
+ } catch {
35193
+ return { peaks: {} };
35194
+ }
35195
+ }
35196
+ function medianPeak(values) {
35197
+ const sorted = values.filter((value) => value > 0).sort((a, b) => a - b);
35198
+ if (sorted.length === 0) return 0;
35199
+ return sorted[Math.floor(sorted.length / 2)] ?? 0;
35200
+ }
35201
+ //#endregion
35202
+ //#region routes/resources/host-processes.ts
35203
+ const DEFAULT_LIMIT$1 = 10;
35204
+ const DEFAULT_BURST_CPU_THRESHOLD = 50;
35205
+ const DEFAULT_BURST_RETENTION_MS = 3600 * 1e3;
35206
+ const retainedBursts = /* @__PURE__ */ new Map();
35207
+ function buildHostProcesses(processes, options = {}) {
35208
+ const nowMs = options.nowMs ?? Date.now();
35209
+ const limit = options.limit ?? DEFAULT_LIMIT$1;
35210
+ const burstCpuThreshold = options.burstCpuThreshold ?? DEFAULT_BURST_CPU_THRESHOLD;
35211
+ const burstRetentionMs = options.burstRetentionMs ?? DEFAULT_BURST_RETENTION_MS;
35212
+ const coreServicePids = new Set(options.coreServicePids ?? []);
35213
+ const processByPid = new Map(processes.map((process) => [process.pid, process]));
35214
+ const agentRootByPid = new Map((options.agentSessions ?? []).map((session) => [session.rootPid, session]));
35215
+ const groups = /* @__PURE__ */ new Map();
35216
+ for (const process of processes) {
35217
+ if (coreServicePids.has(process.pid) || agentRootByPid.has(process.pid) || isDockerProcess(process)) continue;
35218
+ const family = classifyCommandFamily(process.command);
35219
+ const owner = resolveOwner(process, processByPid, agentRootByPid, family);
35220
+ const id = [
35221
+ family,
35222
+ owner.agentId ?? owner.label,
35223
+ owner.issueId ?? "host"
35224
+ ].join(":");
35225
+ const existing = groups.get(id);
35226
+ if (existing) {
35227
+ existing.pidCount += 1;
35228
+ existing.pids.push(process.pid);
35229
+ existing.cpuPercent += process.cpuPercent;
35230
+ existing.memoryBytes += process.memoryBytes;
35231
+ continue;
35232
+ }
35233
+ groups.set(id, {
35234
+ id,
35235
+ family,
35236
+ label: family,
35237
+ owner,
35238
+ pidCount: 1,
35239
+ pids: [process.pid],
35240
+ cpuPercent: process.cpuPercent,
35241
+ memoryBytes: process.memoryBytes
35242
+ });
35243
+ }
35244
+ const activeRows = [...groups.values()].map((group) => {
35245
+ const retained = retainedBursts.get(group.id);
35246
+ const peakCpuPercent = Math.max(group.cpuPercent, retained?.row.peakCpuPercent ?? 0);
35247
+ const peakMemoryBytes = Math.max(group.memoryBytes, retained?.row.peakMemoryBytes ?? 0);
35248
+ const row = {
35249
+ ...group,
35250
+ pids: group.pids.sort((a, b) => a - b),
35251
+ cpuPercent: roundOneDecimal$1(group.cpuPercent),
35252
+ peakCpuPercent: roundOneDecimal$1(peakCpuPercent),
35253
+ peakMemoryBytes
35254
+ };
35255
+ if (peakCpuPercent >= burstCpuThreshold) retainedBursts.set(group.id, {
35256
+ row,
35257
+ lastSeenMs: nowMs
35258
+ });
35259
+ else retainedBursts.delete(group.id);
35260
+ return row;
35261
+ });
35262
+ const activeGroupIds = new Set(activeRows.map((row) => row.id));
35263
+ const retainedRows = [];
35264
+ for (const [groupId, retained] of retainedBursts) {
35265
+ if (activeGroupIds.has(groupId)) continue;
35266
+ const retainedUntilMs = retained.lastSeenMs + burstRetentionMs;
35267
+ if (retainedUntilMs <= nowMs) {
35268
+ retainedBursts.delete(groupId);
35269
+ continue;
35270
+ }
35271
+ retainedRows.push({
35272
+ ...retained.row,
35273
+ cpuPercent: 0,
35274
+ memoryBytes: 0,
35275
+ retainedUntil: new Date(retainedUntilMs).toISOString(),
35276
+ note: buildRetainedBurstNote(retained.row, options.spikeAnnotations)
35277
+ });
35278
+ }
35279
+ return selectTopRows([...activeRows, ...retainedRows], limit);
35280
+ }
35281
+ function getHostProcessesSnapshot() {
35282
+ return buildHostProcesses([]);
35283
+ }
35284
+ function isDockerProcess(process) {
35285
+ return /\b(docker|containerd|kubepods)\b/i.test(process.cgroup ?? "");
35286
+ }
35287
+ function classifyCommandFamily(command) {
35288
+ const normalized = command.toLowerCase();
35289
+ if (normalized.includes("vitest")) return "vitest workers";
35290
+ if (normalized.includes("chrome") || normalized.includes("chromium")) return "chrome renderers";
35291
+ if (/\bmvn\b|maven/.test(normalized)) return "mvn";
35292
+ if (/\bnode\b/.test(normalized)) return "node";
35293
+ if (/\bbun\b/.test(normalized)) return "bun";
35294
+ return command.trim().split(/\s+/)[0] || "unknown";
35295
+ }
35296
+ function resolveOwner(process, processByPid, agentRootByPid, family) {
35297
+ if (family === "chrome renderers") return { label: "your browser" };
35298
+ let current = process;
35299
+ const seenPids = /* @__PURE__ */ new Set();
35300
+ while (current && !seenPids.has(current.pid)) {
35301
+ seenPids.add(current.pid);
35302
+ const agentSession = agentRootByPid.get(current.pid);
35303
+ if (agentSession) return {
35304
+ label: `spawned by ${agentSession.agentId}`,
35305
+ agentId: agentSession.agentId,
35306
+ issueId: agentSession.issueId
35307
+ };
35308
+ current = processByPid.get(current.ppid);
35309
+ }
35310
+ return { label: "host" };
35311
+ }
35312
+ function buildRetainedBurstNote(row, annotations) {
35313
+ const matchingAnnotation = annotations?.find((annotation) => {
35314
+ if (annotation.targetId && annotation.targetId === row.owner.agentId) return true;
35315
+ return annotation.label.includes(row.family) || annotation.label.includes(row.owner.label);
35316
+ });
35317
+ if (!matchingAnnotation) return "caused spike";
35318
+ return `caused spike: ${matchingAnnotation.label}`;
35319
+ }
35320
+ function selectTopRows(rows, limit) {
35321
+ const topByCpu = [...rows].sort((a, b) => b.cpuPercent - a.cpuPercent || b.peakCpuPercent - a.peakCpuPercent).slice(0, limit);
35322
+ const topByMemory = [...rows].sort((a, b) => b.memoryBytes - a.memoryBytes || b.peakMemoryBytes - a.peakMemoryBytes).slice(0, limit);
35323
+ const merged = /* @__PURE__ */ new Map();
35324
+ for (const row of [...topByCpu, ...topByMemory]) merged.set(row.id, row);
35325
+ return [...merged.values()].sort((a, b) => b.cpuPercent - a.cpuPercent || b.peakCpuPercent - a.peakCpuPercent).slice(0, limit);
35326
+ }
35327
+ function roundOneDecimal$1(value) {
35328
+ return Math.round(value * 10) / 10;
35329
+ }
35330
+ //#endregion
35331
+ //#region routes/resources/host-vitals.ts
35332
+ const CPU_SPARK_MAX = 30;
35333
+ let cpuSpark = [];
35334
+ let cachedDockerCounts = {
35335
+ containers: 0,
35336
+ running: 0,
35337
+ stacks: 0,
35338
+ networks: 0,
35339
+ networkPool: {
35340
+ used: 0,
35341
+ total: 31
35342
+ }
35343
+ };
35344
+ function buildHostVitalsSnapshot(options = {}) {
35345
+ const cpuPercent = roundOneDecimal(options.cpuPercent ?? 0);
35346
+ cpuSpark.push(cpuPercent);
35347
+ cpuSpark = cpuSpark.slice(-CPU_SPARK_MAX);
35348
+ const dockerCounts = options.dockerStale ? cachedDockerCounts : computeDockerCounts(options.containers ?? [], options.networkCount ?? 0, options.networkPoolTotal ?? 31);
35349
+ if (!options.dockerStale) cachedDockerCounts = dockerCounts;
35350
+ const memory = options.mem ?? getDefaultMemoryVitals();
35351
+ const disk = options.disk ?? {
35352
+ usedBytes: 0,
35353
+ freeBytes: 0,
35354
+ reclaimableBytes: 0
35355
+ };
35356
+ const agentCounts = computeAgentCounts(options.agents ?? [], options.agentFleet ?? {}, options.nowMs ?? Date.now());
35357
+ return {
35358
+ stale: options.dockerStale === true,
35359
+ cpu: {
35360
+ percent: cpuPercent,
35361
+ load: options.load ?? normalizeLoad(loadavg$1()),
35362
+ spark: [...cpuSpark]
35363
+ },
35364
+ mem: memory,
35365
+ disk: {
35366
+ usedBytes: disk.usedBytes,
35367
+ freeBytes: disk.freeBytes,
35368
+ reclaimableBytes: disk.reclaimableBytes ?? 0
35369
+ },
35370
+ docker: {
35371
+ ...dockerCounts,
35372
+ stale: options.dockerStale === true
35373
+ },
35374
+ agents: agentCounts
35375
+ };
35376
+ }
35377
+ function computeDockerCounts(containers, networkCount, networkPoolTotal) {
35378
+ const stacks = /* @__PURE__ */ new Set();
35379
+ for (const container of containers) {
35380
+ const project = container.labels?.["com.docker.compose.project"];
35381
+ if (project) stacks.add(project);
35382
+ }
35383
+ return {
35384
+ containers: containers.length,
35385
+ running: containers.filter((container) => container.status === "running").length,
35386
+ stacks: stacks.size,
35387
+ networks: networkCount,
35388
+ networkPool: {
35389
+ used: networkCount,
35390
+ total: networkPoolTotal
35391
+ }
35392
+ };
35393
+ }
35394
+ function computeAgentCounts(agents, fleet, nowMs) {
35395
+ return {
35396
+ sessions: agents.length,
35397
+ active: agents.filter((agent) => agent.hasLiveTmuxSession).length,
35398
+ idleOver15m: agents.filter((agent) => {
35399
+ const lastActivityMs = Date.parse(agent.lastActivity ?? "");
35400
+ return Number.isFinite(lastActivityMs) && nowMs - lastActivityMs > 900 * 1e3;
35401
+ }).length,
35402
+ burnUsdPerHour: fleet.burnUsdPerHour ?? 0,
35403
+ hypotheticalUsdPerHour: fleet.hypotheticalUsdPerHour ?? 0,
35404
+ totalUsd: fleet.totalUsd ?? 0
35405
+ };
35406
+ }
35407
+ function getDefaultMemoryVitals() {
35408
+ const total = totalmem();
35409
+ const available = freemem$1();
35410
+ return {
35411
+ usedBytes: Math.max(0, total - available),
35412
+ availableBytes: available,
35413
+ swapUsedBytes: 0,
35414
+ swapTotalBytes: 0
35415
+ };
35416
+ }
35417
+ function normalizeLoad(load) {
35418
+ return [
35419
+ roundOneDecimal(load[0] ?? 0),
35420
+ roundOneDecimal(load[1] ?? 0),
35421
+ roundOneDecimal(load[2] ?? 0)
35422
+ ];
35423
+ }
35424
+ function roundOneDecimal(value) {
35425
+ return Math.round(value * 10) / 10;
35426
+ }
35427
+ //#endregion
35428
+ //#region routes/resources/limits.ts
35429
+ const OOM_WINDOW_MS = 1440 * 60 * 1e3;
35430
+ let cachedOomEvents = [];
35431
+ function enrichContainersWithLimits(containers, options = {}) {
35432
+ const nowMs = options.nowMs ?? Date.now();
35433
+ const oomEvents = options.oomEvents ?? cachedOomEvents;
35434
+ return containers.map((container) => {
35435
+ const limitBytes = getLimitBytes(container.memoryLimit);
35436
+ const percent = limitBytes === null ? void 0 : Math.round((container.memoryUsage ?? 0) / limitBytes * 100);
35437
+ return {
35438
+ ...container,
35439
+ memLimitBytes: limitBytes,
35440
+ ...percent === void 0 ? {} : { memPercentOfLimit: percent },
35441
+ oomKills24h: countRecentOomEvents(container, oomEvents, nowMs),
35442
+ ...composeFileHint(container.labels)
35443
+ };
35444
+ });
35445
+ }
35446
+ function getLimitBytes(memoryLimit) {
35447
+ if (!memoryLimit || memoryLimit <= 0 || !Number.isFinite(memoryLimit)) return null;
35448
+ return memoryLimit;
35449
+ }
35450
+ function countRecentOomEvents(container, events, nowMs) {
35451
+ const cutoffMs = nowMs - OOM_WINDOW_MS;
35452
+ return events.filter((event) => {
35453
+ const eventMs = Date.parse(event.timestamp);
35454
+ if (!Number.isFinite(eventMs) || eventMs < cutoffMs || eventMs > nowMs) return false;
35455
+ if (event.containerId && container.id.startsWith(event.containerId)) return true;
35456
+ if (event.containerName && event.containerName === container.name) return true;
35457
+ return false;
35458
+ }).length;
35459
+ }
35460
+ function composeFileHint(labels) {
35461
+ const composeFile = labels?.["com.docker.compose.project.config_files"];
35462
+ if (!composeFile) return {};
35463
+ return { composeFile: composeFile.split(",")[0]?.trim() };
35464
+ }
35465
+ //#endregion
35466
+ //#region routes/resources/reclaim.ts
35467
+ init_review_status();
35468
+ const RECLAIM_THRESHOLD_BYTES = 500 * 1024 ** 2;
35469
+ const VENV_THRESHOLD_BYTES = 1024 ** 3;
35470
+ let cachedVenvCandidates = [];
35471
+ let venvDelete = async (path) => {
35472
+ await rm(path, {
35473
+ recursive: true,
35474
+ force: true
35475
+ });
35476
+ };
35477
+ let issueClosedReader = (issueId) => isClosedReviewStatus(issueId);
35478
+ let projectRootReader = () => resolve$1(process.cwd(), "..", "..");
35479
+ function buildReclaimPayload(stacks, agents, options = {}) {
35480
+ const liveIssueIds = new Set(agents.filter((agent) => agent.hasLiveTmuxSession === true).map((agent) => agent.issueId?.toUpperCase()).filter((issueId) => Boolean(issueId)));
35481
+ const candidates = [...stacks.filter((stack) => stack.issueId && isClosedStack(stack) && !liveIssueIds.has(stack.issueId.toUpperCase())).map((stack) => ({
35482
+ kind: "stack",
35483
+ label: stack.issueId ?? stack.composeProject,
35484
+ why: "Issue is merged or closed and no live agent references it.",
35485
+ ramBytes: stack.aggregates.memoryBytes,
35486
+ diskBytes: stack.aggregates.diskBytes,
35487
+ action: `GET /api/resources/stacks/${stack.issueId}/teardown-estimate`,
35488
+ issueId: stack.issueId ?? void 0
35489
+ })), ...(options.venvs ?? cachedVenvCandidates).filter((venv) => venv.diskBytes >= VENV_THRESHOLD_BYTES && issueClosedReader(venv.issueId)).map((venv) => ({
35490
+ kind: "venv",
35491
+ label: `${venv.issueId} virtualenv`,
35492
+ why: "Closed issue has an orphaned workspace .venv over 1 GB.",
35493
+ ramBytes: 0,
35494
+ diskBytes: venv.diskBytes,
35495
+ action: `DELETE /api/resources/venvs/${venv.issueId}`,
35496
+ issueId: venv.issueId
35497
+ }))];
35498
+ return {
35499
+ reclaimCandidates: candidates,
35500
+ reclaimTotals: {
35501
+ ramBytes: candidates.reduce((sum, candidate) => sum + candidate.ramBytes, 0),
35502
+ diskBytes: candidates.reduce((sum, candidate) => sum + candidate.diskBytes, 0)
35503
+ },
35504
+ reclaimThresholdBytes: RECLAIM_THRESHOLD_BYTES
35505
+ };
35506
+ }
35507
+ const deleteResourceVenvRoute = HttpRouter.add("DELETE", "/api/resources/venvs/:issue", httpHandler(Effect.gen(function* () {
35508
+ return yield* deleteResourceVenvEffect((yield* HttpRouter.schemaParams()).issue);
35509
+ })));
35510
+ function deleteResourceVenvEffect(issue) {
35511
+ return Effect.gen(function* () {
35512
+ const issueId = issue.toUpperCase();
35513
+ if (!issueClosedReader(issueId)) return jsonResponse({
35514
+ ok: false,
35515
+ error: `${issueId} is not closed; refusing to delete workspace venv.`
35516
+ }, { status: 409 });
35517
+ const venvPath = join$1(projectRootReader(), "workspaces", `feature-${issueId.toLowerCase()}`, ".venv");
35518
+ yield* Effect.tryPromise({
35519
+ try: () => venvDelete(venvPath),
35520
+ catch: (error) => error
35521
+ }).pipe(Effect.catch(() => Effect.succeed(void 0)));
35522
+ return jsonResponse({
35523
+ ok: true,
35524
+ issueId,
35525
+ path: venvPath
35526
+ });
35527
+ });
35528
+ }
35529
+ function isClosedStack(stack) {
35530
+ return stack.phase === "merged";
35531
+ }
35532
+ function isClosedReviewStatus(issueId) {
35533
+ return getReviewStatusSync(issueId)?.mergeStatus === "merged";
35534
+ }
35535
+ //#endregion
35536
+ //#region routes/resources/spawn-gate.ts
35537
+ let readSystemHealthSnapshot = () => getSystemHealthSnapshot();
35538
+ function mapSpawnGateDecision(decision) {
35539
+ const reason = decision.error ?? decision.warnings[0]?.message ?? "";
35540
+ return {
35541
+ state: decision.blocked ? "BLOCKED" : decision.requiresAcknowledgement ? "SOFT" : "OPEN",
35542
+ reason,
35543
+ warnings: decision.warnings,
35544
+ pressure: spawnGatePressure(decision)
35545
+ };
35546
+ }
35547
+ function getSpawnGatePayloadEffect() {
35548
+ return Effect.tryPromise({
35549
+ try: () => readSystemHealthSnapshot(),
35550
+ catch: (error) => error
35551
+ }).pipe(Effect.map((health) => mapSpawnGateDecision(evaluateSpawnGuardrails(health))), Effect.catch(() => Effect.succeed({
35552
+ state: "SOFT",
35553
+ reason: "Spawn gate health is unavailable.",
35554
+ warnings: [],
35555
+ pressure: 0,
35556
+ stale: true
35557
+ })));
35558
+ }
35559
+ function spawnGatePressure(decision) {
35560
+ if (decision.blocked) return 100;
35561
+ if (decision.requiresAcknowledgement) return 67;
35562
+ return 12;
35563
+ }
35564
+ //#endregion
35565
+ //#region routes/resources/stacks.ts
35566
+ init_review_status();
35567
+ let reviewStatusReader = (issueId) => getReviewStatusSync(issueId);
35568
+ function getResourceStacks(containers) {
35569
+ return buildResourceStacks(containers, reviewStatusesFor(containers));
35570
+ }
35571
+ function buildResourceStacks(containers, reviewStatuses = {}) {
35572
+ const groups = /* @__PURE__ */ new Map();
35573
+ for (const container of containers) {
35574
+ const key = composeProjectFor(container) ?? "unassigned";
35575
+ groups.set(key, [...groups.get(key) ?? [], container]);
35576
+ }
35577
+ return [...groups.entries()].map(([composeProject, services]) => {
35578
+ const issueId = issueIdFor$1(composeProject, services);
35579
+ const reviewStatus = issueId ? reviewStatuses[issueId] : void 0;
35580
+ return {
35581
+ id: issueId ?? composeProject,
35582
+ issueId,
35583
+ issueTitle: issueId ?? "Unassigned",
35584
+ composeProject,
35585
+ serviceCount: services.length,
35586
+ services,
35587
+ aggregates: {
35588
+ cpuPercent: roundOne(services.reduce((sum, service) => sum + (service.cpuPercent ?? 0), 0)),
35589
+ memoryBytes: services.reduce((sum, service) => sum + (service.memoryUsage ?? 0), 0),
35590
+ diskBytes: services.reduce((sum, service) => sum + (service.diskUsage ?? 0), 0)
35591
+ },
35592
+ phase: phaseFor(reviewStatus)
35593
+ };
35594
+ }).sort((a, b) => a.id.localeCompare(b.id));
35595
+ }
35596
+ function reviewStatusesFor(containers) {
35597
+ const issueIds = /* @__PURE__ */ new Set();
35598
+ const groups = /* @__PURE__ */ new Map();
35599
+ for (const container of containers) {
35600
+ const key = composeProjectFor(container) ?? "unassigned";
35601
+ groups.set(key, [...groups.get(key) ?? [], container]);
35602
+ }
35603
+ for (const [composeProject, services] of groups.entries()) {
35604
+ const issueId = issueIdFor$1(composeProject, services);
35605
+ if (issueId) issueIds.add(issueId);
35606
+ }
35607
+ const statuses = {};
35608
+ for (const issueId of issueIds) statuses[issueId] = reviewStatusReader(issueId) ?? void 0;
35609
+ return statuses;
35610
+ }
35611
+ function composeProjectFor(container) {
35612
+ const labelProject = container.labels?.["com.docker.compose.project"]?.trim();
35613
+ if (labelProject) return labelProject;
35614
+ const issueProject = container.name.match(/^(.+?feature[-_](?:pan|min|aur|krux)[-_]?\d+)/i);
35615
+ if (issueProject?.[1]) return issueProject[1];
35616
+ return container.name.match(/^(.+?)[_-][a-z0-9]+[._-][0-9]+$/i)?.[1] ?? null;
35617
+ }
35618
+ function issueIdFor$1(composeProject, services) {
35619
+ if (composeProject === "unassigned") return null;
35620
+ const fromProject = issueIdFromText(composeProject);
35621
+ if (fromProject) return fromProject;
35622
+ for (const service of services) {
35623
+ const fromLabels = issueIdFromText(Object.values(service.labels ?? {}).join(" "));
35624
+ if (fromLabels) return fromLabels;
35625
+ }
35626
+ return null;
35627
+ }
35628
+ function issueIdFromText(value) {
35629
+ const match = value.match(/(?:workspaces[\\/])?feature[-_](pan|min|aur|krux)[-_]?(\d+)/i) ?? value.match(/\b(pan|min|aur|krux)[-_]?(\d+)\b/i);
35630
+ if (!match) return null;
35631
+ return `${match[1].toUpperCase()}-${match[2]}`;
35632
+ }
35633
+ function phaseFor(status) {
35634
+ if (!status) return "todo";
35635
+ if (status.mergeStatus === "merged") return "merged";
35636
+ if (status.mergeStatus === "queued" || status.mergeStatus === "merging" || status.mergeStatus === "verifying" || status.mergeStatus === "failed" || status.readyForMerge === true) return "ship";
35637
+ if (status.verificationStatus === "running") return "verifying";
35638
+ if (status.reviewStatus === "reviewing" || status.reviewStatus === "passed" || status.reviewStatus === "failed" || status.reviewStatus === "blocked" || status.testStatus === "testing" || status.testStatus === "passed" || status.testStatus === "failed" || status.inspectStatus === "inspecting" || status.inspectStatus === "passed" || status.inspectStatus === "failed" || status.inspectStatus === "error") return "review";
35639
+ return "todo";
35640
+ }
35641
+ function roundOne(value) {
35642
+ return Math.round(value * 10) / 10;
35643
+ }
35644
+ //#endregion
35645
+ //#region routes/resources/snapshot.ts
35646
+ init_agents$1();
35647
+ init_tmux();
34699
35648
  /** Build the GET /api/resources response from the SQLite agents table. */
34700
35649
  function getResourcesEffect() {
34701
35650
  return Effect.gen(function* () {
34702
- const containers = [];
35651
+ const containers = enrichContainersWithLimits(getCurrentDockerStats());
35652
+ const agentStats = yield* getAgentStatsSnapshotEffect();
35653
+ const spawnGate = yield* getSpawnGatePayloadEffect();
34703
35654
  const stoppedContainers = [];
34704
35655
  const sessions = yield* listSessions().pipe(Effect.catch(() => Effect.succeed([])));
34705
35656
  const tmuxSessionNames = new Set(sessions.map((s) => s.name));
35657
+ const agents = listAgentStates().filter((state) => state.status !== "stopped").map((state) => ({
35658
+ ...state,
35659
+ hasLiveTmuxSession: tmuxSessionNames.has(state.id)
35660
+ }));
35661
+ const agentStatsById = new Map(agentStats.agents.map((agent) => [agent.id, agent]));
35662
+ const baseHostVitals = buildHostVitalsSnapshot({
35663
+ containers,
35664
+ agents: agents.map((agent) => ({
35665
+ id: String(agent.id),
35666
+ lastActivity: typeof agent.lastActivity === "string" ? agent.lastActivity : void 0,
35667
+ hasLiveTmuxSession: agent.hasLiveTmuxSession === true
35668
+ })),
35669
+ agentFleet: agentStats.hostVitals.agents
35670
+ });
35671
+ const stacks = getResourceStacks(containers);
35672
+ const reclaim = buildReclaimPayload(stacks, agents);
35673
+ const hostVitals = {
35674
+ ...baseHostVitals,
35675
+ disk: {
35676
+ ...baseHostVitals.disk,
35677
+ reclaimableBytes: reclaim.reclaimTotals.diskBytes
35678
+ }
35679
+ };
34706
35680
  return jsonResponse({
35681
+ agents: agents.map((agent) => ({
35682
+ ...agent,
35683
+ resourceStats: agentStatsById.get(String(agent.id)) ?? null
35684
+ })),
35685
+ coreServices: getCoreServicesSnapshot(),
34707
35686
  containers,
35687
+ forecast: buildCapacityForecast(stacks, { hostVitals }),
35688
+ hostVitals,
35689
+ hostProcesses: getHostProcessesSnapshot(),
34708
35690
  stoppedContainers,
34709
35691
  networks: [],
35692
+ reclaimCandidates: reclaim.reclaimCandidates,
35693
+ reclaimThresholdBytes: reclaim.reclaimThresholdBytes,
35694
+ reclaimTotals: reclaim.reclaimTotals,
35695
+ spawnGate,
35696
+ stacks,
34710
35697
  volumes: [],
34711
- agents: listAgentStates().filter((state) => state.status !== "stopped").map((state) => ({
34712
- ...state,
34713
- hasLiveTmuxSession: tmuxSessionNames.has(state.id)
34714
- })),
34715
35698
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
34716
35699
  });
34717
35700
  });
34718
35701
  }
34719
35702
  const getResourcesRoute = HttpRouter.add("GET", "/api/resources", httpHandler(getResourcesEffect()));
35703
+ //#endregion
35704
+ //#region routes/resources/containers.ts
35705
+ init_domain_services();
35706
+ let dockerExec = promisify(execFile);
35707
+ function dockerActionErrorPayload(error) {
35708
+ const maybeError = error;
35709
+ return { error: (typeof maybeError.stderr === "string" ? maybeError.stderr.trim() : "") || (typeof maybeError.message === "string" ? maybeError.message : String(error)) };
35710
+ }
34720
35711
  const getContainerHistoryRoute = HttpRouter.add("GET", "/api/resources/:containerId/history", Effect.gen(function* () {
34721
35712
  const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
34722
35713
  if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34723
- return jsonResponse({
34724
- timestamps: [],
34725
- cpuPercent: [],
34726
- memoryPercent: []
34727
- });
35714
+ return jsonResponse(getContainerHistory(containerId));
34728
35715
  }));
34729
35716
  const getContainerDetailsRoute = HttpRouter.add("GET", "/api/resources/:containerId/details", Effect.gen(function* () {
34730
35717
  const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
34731
35718
  if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34732
35719
  return yield* httpHandler(Effect.gen(function* () {
34733
35720
  const [inspectResult, logsResult] = yield* Effect.tryPromise({
34734
- try: () => Promise.all([execAsync$7(`docker inspect --format '{{json .}}' "${containerId}" 2>/dev/null`, {
35721
+ try: () => Promise.all([dockerExec("docker", [
35722
+ "inspect",
35723
+ "--format",
35724
+ "{{json .}}",
35725
+ containerId
35726
+ ], {
34735
35727
  encoding: "utf-8",
34736
35728
  timeout: 5e3
34737
- }).catch(() => ({ stdout: "null" })), execAsync$7(`docker logs --tail 100 "${containerId}" 2>&1`, {
35729
+ }).catch(() => ({ stdout: "null" })), dockerExec("docker", [
35730
+ "logs",
35731
+ "--tail",
35732
+ "100",
35733
+ containerId
35734
+ ], {
34738
35735
  encoding: "utf-8",
34739
35736
  timeout: 5e3
34740
35737
  }).catch(() => ({ stdout: "" }))]),
@@ -34773,7 +35770,7 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
34773
35770
  const eventStore = yield* EventStoreService;
34774
35771
  if (!/^[a-f0-9]{12,64}$/.test(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34775
35772
  yield* Effect.tryPromise({
34776
- try: () => execAsync$7(`docker rm "${id}" 2>&1`, {
35773
+ try: () => dockerExec("docker", ["rm", id], {
34777
35774
  encoding: "utf-8",
34778
35775
  timeout: 1e4
34779
35776
  }),
@@ -34782,90 +35779,16 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
34782
35779
  yield* eventStore.append({
34783
35780
  type: "resources.updated",
34784
35781
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34785
- payload: { resources: { containers: [] } }
35782
+ payload: { resources: { containers: getCurrentDockerStats() } }
34786
35783
  });
34787
35784
  return jsonResponse({ ok: true });
34788
35785
  })));
34789
- const postPruneContainersRoute = HttpRouter.add("POST", "/api/resources/docker/prune-containers", httpHandler(Effect.gen(function* () {
34790
- const eventStore = yield* EventStoreService;
34791
- const { stdout } = yield* Effect.tryPromise({
34792
- try: () => execAsync$7("docker container prune -f 2>&1", {
34793
- encoding: "utf-8",
34794
- timeout: 3e4
34795
- }),
34796
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34797
- });
34798
- yield* eventStore.append({
34799
- type: "resources.updated",
34800
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34801
- payload: { resources: { containers: [] } }
34802
- });
34803
- return jsonResponse({
34804
- ok: true,
34805
- output: stdout.trim()
34806
- });
34807
- })));
34808
- const deleteDockerNetworkRoute = HttpRouter.add("DELETE", "/api/resources/docker/network/:name", httpHandler(Effect.gen(function* () {
34809
- const name = (yield* HttpRouter.params)["name"] ?? "";
34810
- const eventStore = yield* EventStoreService;
34811
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid network name" }, { status: 400 });
34812
- yield* Effect.tryPromise({
34813
- try: () => execAsync$7(`docker network rm "${name}" 2>&1`, {
34814
- encoding: "utf-8",
34815
- timeout: 1e4
34816
- }),
34817
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34818
- });
34819
- yield* eventStore.append({
34820
- type: "resources.updated",
34821
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34822
- payload: { resources: { containers: [] } }
34823
- });
34824
- return jsonResponse({ ok: true });
34825
- })));
34826
- const deleteDockerVolumeRoute = HttpRouter.add("DELETE", "/api/resources/docker/volume/:name", httpHandler(Effect.gen(function* () {
34827
- const name = (yield* HttpRouter.params)["name"] ?? "";
34828
- const eventStore = yield* EventStoreService;
34829
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid volume name" }, { status: 400 });
34830
- yield* Effect.tryPromise({
34831
- try: () => execAsync$7(`docker volume rm "${name}" 2>&1`, {
34832
- encoding: "utf-8",
34833
- timeout: 1e4
34834
- }),
34835
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34836
- });
34837
- yield* eventStore.append({
34838
- type: "resources.updated",
34839
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34840
- payload: { resources: { containers: [] } }
34841
- });
34842
- return jsonResponse({ ok: true });
34843
- })));
34844
- const postPruneVolumesRoute = HttpRouter.add("POST", "/api/resources/docker/prune-volumes", httpHandler(Effect.gen(function* () {
34845
- const eventStore = yield* EventStoreService;
34846
- const { stdout } = yield* Effect.tryPromise({
34847
- try: () => execAsync$7("docker volume prune -f 2>&1", {
34848
- encoding: "utf-8",
34849
- timeout: 3e4
34850
- }),
34851
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34852
- });
34853
- yield* eventStore.append({
34854
- type: "resources.updated",
34855
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34856
- payload: { resources: { containers: [] } }
34857
- });
34858
- return jsonResponse({
34859
- ok: true,
34860
- output: stdout.trim()
34861
- });
34862
- })));
34863
35786
  const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/restart", httpHandler(Effect.gen(function* () {
34864
35787
  const id = (yield* HttpRouter.params)["id"] ?? "";
34865
35788
  const eventStore = yield* EventStoreService;
34866
- if (!id) return jsonResponse({ error: "Container ID required" }, { status: 400 });
35789
+ if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34867
35790
  const { stdout } = yield* Effect.tryPromise({
34868
- try: () => execAsync$7(`docker restart "${id}"`, {
35791
+ try: () => dockerExec("docker", ["restart", id], {
34869
35792
  encoding: "utf-8",
34870
35793
  timeout: 3e4
34871
35794
  }),
@@ -34874,7 +35797,7 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
34874
35797
  yield* eventStore.append({
34875
35798
  type: "resources.updated",
34876
35799
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34877
- payload: { resources: { containers: [] } }
35800
+ payload: { resources: { containers: getCurrentDockerStats() } }
34878
35801
  });
34879
35802
  return jsonResponse({
34880
35803
  ok: true,
@@ -34885,9 +35808,9 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
34885
35808
  const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/start", httpHandler(Effect.gen(function* () {
34886
35809
  const id = (yield* HttpRouter.params)["id"] ?? "";
34887
35810
  const eventStore = yield* EventStoreService;
34888
- if (!id) return jsonResponse({ error: "Container ID required" }, { status: 400 });
35811
+ if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34889
35812
  const { stdout } = yield* Effect.tryPromise({
34890
- try: () => execAsync$7(`docker start "${id}"`, {
35813
+ try: () => dockerExec("docker", ["start", id], {
34891
35814
  encoding: "utf-8",
34892
35815
  timeout: 3e4
34893
35816
  }),
@@ -34896,7 +35819,7 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
34896
35819
  yield* eventStore.append({
34897
35820
  type: "resources.updated",
34898
35821
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34899
- payload: { resources: { containers: [] } }
35822
+ payload: { resources: { containers: getCurrentDockerStats() } }
34900
35823
  });
34901
35824
  return jsonResponse({
34902
35825
  ok: true,
@@ -34906,9 +35829,15 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
34906
35829
  })));
34907
35830
  const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/container/:id/logs", httpHandler(Effect.gen(function* () {
34908
35831
  const id = (yield* HttpRouter.params)["id"] ?? "";
34909
- if (!id) return jsonResponse({ error: "Container ID required" }, { status: 400 });
35832
+ if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34910
35833
  const { stdout } = yield* Effect.tryPromise({
34911
- try: () => execAsync$7(`docker logs --tail 200 --timestamps "${id}"`, {
35834
+ try: () => dockerExec("docker", [
35835
+ "logs",
35836
+ "--tail",
35837
+ "200",
35838
+ "--timestamps",
35839
+ id
35840
+ ], {
34912
35841
  encoding: "utf-8",
34913
35842
  timeout: 1e4
34914
35843
  }),
@@ -34916,7 +35845,516 @@ const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/conta
34916
35845
  });
34917
35846
  return jsonResponse({ logs: stdout });
34918
35847
  })));
34919
- const resourcesRouteLayer = Layer.mergeAll(getResourcesRoute, getContainerHistoryRoute, getContainerDetailsRoute, deleteDockerContainerRoute, postPruneContainersRoute, deleteDockerNetworkRoute, deleteDockerVolumeRoute, postPruneVolumesRoute, postRestartContainerRoute, postStartContainerRoute, getContainerLogsRoute);
35848
+ function dockerContainerActionEffect(id, action) {
35849
+ return Effect.gen(function* () {
35850
+ if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
35851
+ const eventStore = yield* EventStoreService;
35852
+ const args = action === "stop" ? [
35853
+ "stop",
35854
+ "--time",
35855
+ "30",
35856
+ id
35857
+ ] : [action, id];
35858
+ const timeout = action === "stop" ? 35e3 : 1e4;
35859
+ const result = yield* Effect.tryPromise({
35860
+ try: () => dockerExec("docker", args, {
35861
+ encoding: "utf-8",
35862
+ timeout
35863
+ }),
35864
+ catch: (error) => error
35865
+ }).pipe(Effect.matchEffect({
35866
+ onFailure: (error) => Effect.succeed({
35867
+ ok: false,
35868
+ error
35869
+ }),
35870
+ onSuccess: ({ stdout }) => Effect.succeed({
35871
+ ok: true,
35872
+ stdout
35873
+ })
35874
+ }));
35875
+ if (!result.ok) return jsonResponse(dockerActionErrorPayload(result.error), { status: 500 });
35876
+ const containers = getCurrentDockerStats();
35877
+ yield* eventStore.append({
35878
+ type: "resources.updated",
35879
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35880
+ payload: { resources: { containers } }
35881
+ });
35882
+ return jsonResponse({
35883
+ ok: true,
35884
+ container: id,
35885
+ action,
35886
+ output: result.stdout.trim(),
35887
+ containers
35888
+ });
35889
+ });
35890
+ }
35891
+ function isDockerIdentifier$2(value) {
35892
+ return /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(value);
35893
+ }
35894
+ const postStopContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/stop", httpHandler(Effect.gen(function* () {
35895
+ return yield* dockerContainerActionEffect((yield* HttpRouter.params)["id"] ?? "", "stop");
35896
+ })));
35897
+ const postPauseContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/pause", httpHandler(Effect.gen(function* () {
35898
+ return yield* dockerContainerActionEffect((yield* HttpRouter.params)["id"] ?? "", "pause");
35899
+ })));
35900
+ const postUnpauseContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/unpause", httpHandler(Effect.gen(function* () {
35901
+ return yield* dockerContainerActionEffect((yield* HttpRouter.params)["id"] ?? "", "unpause");
35902
+ })));
35903
+ //#endregion
35904
+ //#region routes/resources/prune.ts
35905
+ init_domain_services();
35906
+ const execFileAsync$7 = promisify(execFile);
35907
+ const postPruneContainersRoute = HttpRouter.add("POST", "/api/resources/docker/prune-containers", httpHandler(Effect.gen(function* () {
35908
+ const eventStore = yield* EventStoreService;
35909
+ const { stdout } = yield* Effect.tryPromise({
35910
+ try: () => execFileAsync$7("docker", [
35911
+ "container",
35912
+ "prune",
35913
+ "-f"
35914
+ ], {
35915
+ encoding: "utf-8",
35916
+ timeout: 3e4
35917
+ }),
35918
+ catch: (err) => new Error(err instanceof Error ? err.message : String(err))
35919
+ });
35920
+ yield* eventStore.append({
35921
+ type: "resources.updated",
35922
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35923
+ payload: { resources: { containers: getCurrentDockerStats() } }
35924
+ });
35925
+ return jsonResponse({
35926
+ ok: true,
35927
+ output: stdout.trim()
35928
+ });
35929
+ })));
35930
+ const deleteDockerNetworkRoute = HttpRouter.add("DELETE", "/api/resources/docker/network/:name", httpHandler(Effect.gen(function* () {
35931
+ const name = (yield* HttpRouter.params)["name"] ?? "";
35932
+ const eventStore = yield* EventStoreService;
35933
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid network name" }, { status: 400 });
35934
+ yield* Effect.tryPromise({
35935
+ try: () => execFileAsync$7("docker", [
35936
+ "network",
35937
+ "rm",
35938
+ name
35939
+ ], {
35940
+ encoding: "utf-8",
35941
+ timeout: 1e4
35942
+ }),
35943
+ catch: (err) => new Error(err instanceof Error ? err.message : String(err))
35944
+ });
35945
+ yield* eventStore.append({
35946
+ type: "resources.updated",
35947
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35948
+ payload: { resources: { containers: getCurrentDockerStats() } }
35949
+ });
35950
+ return jsonResponse({ ok: true });
35951
+ })));
35952
+ const deleteDockerVolumeRoute = HttpRouter.add("DELETE", "/api/resources/docker/volume/:name", httpHandler(Effect.gen(function* () {
35953
+ const name = (yield* HttpRouter.params)["name"] ?? "";
35954
+ const eventStore = yield* EventStoreService;
35955
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid volume name" }, { status: 400 });
35956
+ yield* Effect.tryPromise({
35957
+ try: () => execFileAsync$7("docker", [
35958
+ "volume",
35959
+ "rm",
35960
+ name
35961
+ ], {
35962
+ encoding: "utf-8",
35963
+ timeout: 1e4
35964
+ }),
35965
+ catch: (err) => new Error(err instanceof Error ? err.message : String(err))
35966
+ });
35967
+ yield* eventStore.append({
35968
+ type: "resources.updated",
35969
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35970
+ payload: { resources: { containers: getCurrentDockerStats() } }
35971
+ });
35972
+ return jsonResponse({ ok: true });
35973
+ })));
35974
+ const postPruneVolumesRoute = HttpRouter.add("POST", "/api/resources/docker/prune-volumes", httpHandler(Effect.gen(function* () {
35975
+ const eventStore = yield* EventStoreService;
35976
+ const { stdout } = yield* Effect.tryPromise({
35977
+ try: () => execFileAsync$7("docker", [
35978
+ "volume",
35979
+ "prune",
35980
+ "-f"
35981
+ ], {
35982
+ encoding: "utf-8",
35983
+ timeout: 3e4
35984
+ }),
35985
+ catch: (err) => new Error(err instanceof Error ? err.message : String(err))
35986
+ });
35987
+ yield* eventStore.append({
35988
+ type: "resources.updated",
35989
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35990
+ payload: { resources: { containers: getCurrentDockerStats() } }
35991
+ });
35992
+ return jsonResponse({
35993
+ ok: true,
35994
+ output: stdout.trim()
35995
+ });
35996
+ })));
35997
+ //#endregion
35998
+ //#region routes/resources/history.ts
35999
+ init_domain_services();
36000
+ const WINDOW_MS = 1440 * 60 * 1e3;
36001
+ const BUCKET_MS = 300 * 1e3;
36002
+ const MAX_POINTS = 288;
36003
+ const MAX_ACTIVITY_EVENTS = 1e3;
36004
+ const samples = [];
36005
+ function nowMs() {
36006
+ return Date.now();
36007
+ }
36008
+ function pruneSamples(now = nowMs()) {
36009
+ const cutoff = now - WINDOW_MS;
36010
+ while (samples.length > 0 && samples[0].ts < cutoff) samples.shift();
36011
+ }
36012
+ function average(values) {
36013
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
36014
+ }
36015
+ function downsample(metric) {
36016
+ pruneSamples();
36017
+ const buckets = /* @__PURE__ */ new Map();
36018
+ for (const sample of samples) {
36019
+ const bucket = Math.floor(sample.ts / BUCKET_MS) * BUCKET_MS;
36020
+ const values = buckets.get(bucket) ?? [];
36021
+ values.push(sample[metric]);
36022
+ buckets.set(bucket, values);
36023
+ }
36024
+ return [...buckets.entries()].sort(([a], [b]) => a - b).slice(-MAX_POINTS).map(([bucket, values]) => ({
36025
+ ts: new Date(bucket).toISOString(),
36026
+ value: Number(average(values).toFixed(2))
36027
+ }));
36028
+ }
36029
+ function asRecord(value) {
36030
+ if (!value) return null;
36031
+ if (typeof value === "object" && !Array.isArray(value)) return value;
36032
+ if (typeof value !== "string") return null;
36033
+ try {
36034
+ const parsed = JSON.parse(value);
36035
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
36036
+ } catch {
36037
+ return null;
36038
+ }
36039
+ }
36040
+ function resourceAnnotationFromEvent(event, now = nowMs()) {
36041
+ const eventTs = new Date(event.timestamp).getTime();
36042
+ if (!Number.isFinite(eventTs) || eventTs <= now - WINDOW_MS || eventTs > now) return null;
36043
+ const payload = asRecord(event.payload);
36044
+ const details = asRecord(payload?.["details"]);
36045
+ if (details?.["category"] !== "resources") return null;
36046
+ const targetKind = details["targetKind"];
36047
+ const targetId = details["targetId"];
36048
+ if (typeof targetKind !== "string" || typeof targetId !== "string") return null;
36049
+ const message = payload?.["message"];
36050
+ return {
36051
+ ts: event.timestamp,
36052
+ label: typeof message === "string" ? message : event.type,
36053
+ targetKind,
36054
+ targetId
36055
+ };
36056
+ }
36057
+ function buildResourceHistoryResponse(activityEvents, now = nowMs()) {
36058
+ pruneSamples(now);
36059
+ const startedAt = samples[0]?.ts ?? now;
36060
+ return {
36061
+ startedAt: new Date(startedAt).toISOString(),
36062
+ cpu: downsample("cpuPercent"),
36063
+ mem: downsample("memoryPercent"),
36064
+ annotations: activityEvents.map((event) => resourceAnnotationFromEvent(event, now)).filter((annotation) => Boolean(annotation))
36065
+ };
36066
+ }
36067
+ function getResourceHistoryEffect() {
36068
+ return Effect.gen(function* () {
36069
+ return jsonResponse(buildResourceHistoryResponse(yield* (yield* EventStoreService).queryByType("activity.entry", MAX_ACTIVITY_EVENTS)));
36070
+ });
36071
+ }
36072
+ const getResourceHistoryRoute = HttpRouter.add("GET", "/api/resources/history/24h", httpHandler(getResourceHistoryEffect()));
36073
+ //#endregion
36074
+ //#region routes/resources/stack-verbs.ts
36075
+ init_domain_services();
36076
+ let dockerStackVerbExec = promisify(execFile);
36077
+ const postStartStackRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/start", httpHandler(Effect.gen(function* () {
36078
+ return yield* dockerStackVerbEffect((yield* HttpRouter.params)["issueId"] ?? "", "start");
36079
+ })));
36080
+ const postStopStackRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/stop", httpHandler(Effect.gen(function* () {
36081
+ return yield* dockerStackVerbEffect((yield* HttpRouter.params)["issueId"] ?? "", "stop");
36082
+ })));
36083
+ const postPauseStackRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/pause", httpHandler(Effect.gen(function* () {
36084
+ return yield* dockerStackVerbEffect((yield* HttpRouter.params)["issueId"] ?? "", "pause");
36085
+ })));
36086
+ function dockerStackVerbEffect(issue, verb) {
36087
+ return Effect.gen(function* () {
36088
+ const issueId = issue.trim().toUpperCase();
36089
+ const stack = findStack$1(issueId);
36090
+ if (!stack) return jsonResponse({
36091
+ ok: false,
36092
+ error: `Stack not found for ${issueId || issue}`
36093
+ }, { status: 404 });
36094
+ const results = [];
36095
+ for (const service of stack.services) {
36096
+ if (!isDockerIdentifier$1(service.id)) {
36097
+ results.push({
36098
+ containerId: service.id,
36099
+ ok: false,
36100
+ error: "Invalid container ID"
36101
+ });
36102
+ continue;
36103
+ }
36104
+ const args = argsFor(service.id, service.status, verb);
36105
+ const timeout = verb === "stop" ? 35e3 : 3e4;
36106
+ const result = yield* Effect.tryPromise({
36107
+ try: () => dockerStackVerbExec("docker", args, {
36108
+ encoding: "utf-8",
36109
+ timeout
36110
+ }),
36111
+ catch: (error) => error
36112
+ }).pipe(Effect.matchEffect({
36113
+ onFailure: (error) => Effect.succeed({
36114
+ containerId: service.id,
36115
+ ok: false,
36116
+ error: dockerActionErrorPayload(error).error
36117
+ }),
36118
+ onSuccess: ({ stdout }) => Effect.succeed({
36119
+ containerId: service.id,
36120
+ ok: true,
36121
+ output: stdout.trim()
36122
+ })
36123
+ }));
36124
+ results.push(result);
36125
+ }
36126
+ const containers = getCurrentDockerStats();
36127
+ yield* (yield* EventStoreService).append({
36128
+ type: "resources.updated",
36129
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
36130
+ payload: { resources: { containers } }
36131
+ });
36132
+ return jsonResponse({
36133
+ ok: results.every((result) => result.ok),
36134
+ issueId,
36135
+ action: verb,
36136
+ results,
36137
+ containers
36138
+ });
36139
+ });
36140
+ }
36141
+ function argsFor(containerId, status, verb) {
36142
+ if (verb === "stop") return [
36143
+ "stop",
36144
+ "--time",
36145
+ "30",
36146
+ containerId
36147
+ ];
36148
+ if (verb === "pause") return ["pause", containerId];
36149
+ if (status === "paused") return ["unpause", containerId];
36150
+ return ["start", containerId];
36151
+ }
36152
+ function isDockerIdentifier$1(value) {
36153
+ return /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(value);
36154
+ }
36155
+ function findStack$1(issueId) {
36156
+ return getResourceStacks(getCurrentDockerStats()).find((stack) => stack.issueId?.toUpperCase() === issueId);
36157
+ }
36158
+ //#endregion
36159
+ //#region routes/resources/teardown.ts
36160
+ init_activity_logger();
36161
+ init_domain_services();
36162
+ const execFileAsync$5 = promisify(execFile);
36163
+ const CONFIRM_TOKEN_TTL_MS = 300 * 1e3;
36164
+ let dockerTeardownExec = execFileAsync$5;
36165
+ let teardownTokenGenerator = () => `td-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
36166
+ let activityEmitter = emitActivityEntrySync;
36167
+ const teardownTokens = /* @__PURE__ */ new Map();
36168
+ const getStackTeardownEstimateRoute = HttpRouter.add("GET", "/api/resources/stacks/:issueId/teardown-estimate", httpHandler(Effect.gen(function* () {
36169
+ return yield* getStackTeardownEstimateEffect((yield* HttpRouter.params)["issueId"] ?? "");
36170
+ })));
36171
+ const postStackTeardownRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/teardown", httpHandler(Effect.gen(function* () {
36172
+ const params = yield* HttpRouter.params;
36173
+ const body = yield* readJsonBody$13;
36174
+ return yield* postStackTeardownEffect(params["issueId"] ?? "", body);
36175
+ })));
36176
+ function getStackTeardownEstimateEffect(issue) {
36177
+ return Effect.gen(function* () {
36178
+ const issueId = normalizeIssueId$1(issue);
36179
+ const stack = findStack(issueId);
36180
+ if (!stack) return jsonResponse({ error: `Stack not found for ${issueId || issue}` }, { status: 404 });
36181
+ const token = teardownTokenGenerator();
36182
+ const expiresAt = Date.now() + CONFIRM_TOKEN_TTL_MS;
36183
+ teardownTokens.set(token, {
36184
+ issueId,
36185
+ composeProject: stack.composeProject,
36186
+ expiresAt
36187
+ });
36188
+ return jsonResponse(buildStackTeardownEstimate(stack, token, expiresAt));
36189
+ });
36190
+ }
36191
+ function postStackTeardownEffect(issue, input) {
36192
+ return Effect.gen(function* () {
36193
+ const issueId = normalizeIssueId$1(issue);
36194
+ const confirmToken = typeof input.confirmToken === "string" ? input.confirmToken : "";
36195
+ const typedText = typeof input.typedText === "string" ? input.typedText : "";
36196
+ const token = teardownTokens.get(confirmToken);
36197
+ if (!token || token.issueId !== issueId || token.expiresAt <= Date.now()) {
36198
+ teardownTokens.delete(confirmToken);
36199
+ return jsonResponse({
36200
+ ok: false,
36201
+ error: "Invalid or expired confirmation token."
36202
+ }, { status: 422 });
36203
+ }
36204
+ if (typedText !== token.composeProject) return jsonResponse({
36205
+ ok: false,
36206
+ error: `Type ${token.composeProject} to confirm stack teardown.`
36207
+ }, { status: 422 });
36208
+ const stack = findStack(issueId);
36209
+ if (!stack) return jsonResponse({
36210
+ ok: false,
36211
+ error: `Stack not found for ${issueId}`
36212
+ }, { status: 404 });
36213
+ const estimate = buildStackTeardownEstimate(stack, confirmToken, token.expiresAt);
36214
+ const result = yield* runDockerStackTeardown(stack);
36215
+ teardownTokens.delete(confirmToken);
36216
+ yield* (yield* EventStoreService).append({
36217
+ type: "resources.updated",
36218
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
36219
+ payload: { resources: { containers: getCurrentDockerStats() } }
36220
+ });
36221
+ activityEmitter({
36222
+ source: "dashboard",
36223
+ level: "info",
36224
+ issueId,
36225
+ message: `Tore down Docker stack for ${issueId}, freeing ${estimate.ramBytes} RAM bytes and ${estimate.diskBytes} disk bytes.`,
36226
+ details: JSON.stringify({
36227
+ issueId,
36228
+ composeProject: stack.composeProject,
36229
+ ramBytes: estimate.ramBytes,
36230
+ diskBytes: estimate.diskBytes,
36231
+ removedContainers: result.containers.length,
36232
+ removedNetworks: result.networks.length,
36233
+ removedVolumes: result.volumes.length
36234
+ })
36235
+ });
36236
+ return jsonResponse({
36237
+ ok: true,
36238
+ issueId,
36239
+ composeProject: stack.composeProject,
36240
+ ramBytes: estimate.ramBytes,
36241
+ diskBytes: estimate.diskBytes,
36242
+ removedContainers: result.containers,
36243
+ removedNetworks: result.networks,
36244
+ removedVolumes: result.volumes
36245
+ });
36246
+ });
36247
+ }
36248
+ function buildStackTeardownEstimate(stack, confirmToken, expiresAt) {
36249
+ return {
36250
+ issueId: stack.issueId ?? stack.id,
36251
+ composeProject: stack.composeProject,
36252
+ ramBytes: stack.aggregates.memoryBytes,
36253
+ diskBytes: stack.aggregates.diskBytes,
36254
+ confirmToken,
36255
+ expiresAt: new Date(expiresAt).toISOString()
36256
+ };
36257
+ }
36258
+ function runDockerStackTeardown(stack) {
36259
+ return Effect.tryPromise({
36260
+ try: async () => {
36261
+ const containers = stack.services.map((service) => service.id).filter(Boolean);
36262
+ for (const container of containers) {
36263
+ if (!isDockerIdentifier(container)) throw new Error(`Invalid container ID: ${container}`);
36264
+ await dockerTeardownExec("docker", [
36265
+ "stop",
36266
+ "--time",
36267
+ "30",
36268
+ container
36269
+ ], {
36270
+ encoding: "utf-8",
36271
+ timeout: 35e3
36272
+ });
36273
+ }
36274
+ for (const container of containers) await dockerTeardownExec("docker", [
36275
+ "rm",
36276
+ "-f",
36277
+ container
36278
+ ], {
36279
+ encoding: "utf-8",
36280
+ timeout: 3e4
36281
+ });
36282
+ const networks = lines((await dockerTeardownExec("docker", [
36283
+ "network",
36284
+ "ls",
36285
+ "--filter",
36286
+ `label=com.docker.compose.project=${stack.composeProject}`,
36287
+ "--format",
36288
+ "{{.Name}}"
36289
+ ], {
36290
+ encoding: "utf-8",
36291
+ timeout: 1e4
36292
+ })).stdout);
36293
+ for (const network of networks) {
36294
+ if (!isDockerIdentifier(network)) throw new Error(`Invalid network name: ${network}`);
36295
+ await dockerTeardownExec("docker", [
36296
+ "network",
36297
+ "rm",
36298
+ network
36299
+ ], {
36300
+ encoding: "utf-8",
36301
+ timeout: 1e4
36302
+ });
36303
+ }
36304
+ const volumes = lines((await dockerTeardownExec("docker", [
36305
+ "volume",
36306
+ "ls",
36307
+ "--filter",
36308
+ `label=com.docker.compose.project=${stack.composeProject}`,
36309
+ "--format",
36310
+ "{{.Name}}"
36311
+ ], {
36312
+ encoding: "utf-8",
36313
+ timeout: 1e4
36314
+ })).stdout);
36315
+ for (const volume of volumes) {
36316
+ if (!isDockerIdentifier(volume)) throw new Error(`Invalid volume name: ${volume}`);
36317
+ await dockerTeardownExec("docker", [
36318
+ "volume",
36319
+ "rm",
36320
+ volume
36321
+ ], {
36322
+ encoding: "utf-8",
36323
+ timeout: 1e4
36324
+ });
36325
+ }
36326
+ return {
36327
+ containers,
36328
+ networks,
36329
+ volumes
36330
+ };
36331
+ },
36332
+ catch: (error) => error
36333
+ });
36334
+ }
36335
+ function findStack(issueId) {
36336
+ return getResourceStacks(getCurrentDockerStats()).find((stack) => stack.issueId?.toUpperCase() === issueId);
36337
+ }
36338
+ function normalizeIssueId$1(issue) {
36339
+ return issue.trim().toUpperCase();
36340
+ }
36341
+ function lines(value) {
36342
+ return value.split("\n").map((line) => line.trim()).filter(Boolean);
36343
+ }
36344
+ function isDockerIdentifier(value) {
36345
+ return /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(value);
36346
+ }
36347
+ const readJsonBody$13 = Effect.gen(function* () {
36348
+ const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
36349
+ try {
36350
+ return text ? JSON.parse(text) : {};
36351
+ } catch {
36352
+ return {};
36353
+ }
36354
+ });
36355
+ //#endregion
36356
+ //#region routes/resources/index.ts
36357
+ const resourcesRouteLayer = Layer.mergeAll(getResourcesRoute, getResourceHistoryRoute, getContainerHistoryRoute, getContainerDetailsRoute, deleteDockerContainerRoute, postPruneContainersRoute, deleteDockerNetworkRoute, deleteDockerVolumeRoute, postPruneVolumesRoute, postRestartContainerRoute, postStartContainerRoute, postStopContainerRoute, postPauseContainerRoute, postUnpauseContainerRoute, getContainerLogsRoute, deleteResourceVenvRoute, postStartStackRoute, postStopStackRoute, postPauseStackRoute, getStackTeardownEstimateRoute, postStackTeardownRoute);
34920
36358
  //#endregion
34921
36359
  //#region services/model-origin-enrich.ts
34922
36360
  init_agents$1();
@@ -34979,8 +36417,34 @@ async function deriveSessionPresence(agentId, rtState, tmuxSessionNames) {
34979
36417
  return "idle";
34980
36418
  }
34981
36419
  //#endregion
34982
- //#region ../../lib/settings-api.ts
36420
+ //#region ../../lib/settings-api-tiered-execution.ts
34983
36421
  init_prd_locations();
36422
+ function tieredExecutionConfigForSave(config, providerAuth) {
36423
+ if (config === void 0) return void 0;
36424
+ const validated = validateTieredExecutionConfig(config, { providerAuth });
36425
+ return {
36426
+ enabled: validated.enabled,
36427
+ tiers: validated.tiers,
36428
+ supervisor: validated.supervisor,
36429
+ by_kind: validated.by_kind,
36430
+ feed: validated.feed,
36431
+ escalation: validated.escalation,
36432
+ compaction_reroute: validated.compaction_reroute,
36433
+ replay_threshold: validated.replay_threshold
36434
+ };
36435
+ }
36436
+ function validateTieredExecutionSettings(config, providerAuth) {
36437
+ if (config === void 0) return null;
36438
+ try {
36439
+ validateTieredExecutionConfig(config, { providerAuth });
36440
+ return null;
36441
+ } catch (error) {
36442
+ if (error instanceof TieredExecutionConfigError) return error.message;
36443
+ throw error;
36444
+ }
36445
+ }
36446
+ //#endregion
36447
+ //#region ../../lib/settings-api.ts
34984
36448
  /**
34985
36449
  * Settings API Adapter
34986
36450
  *
@@ -35440,7 +36904,8 @@ async function writeYamlConfigPreservingComments(yamlConfig) {
35440
36904
  ["tracker_keys", config.tracker_keys],
35441
36905
  ["experimental", config.experimental],
35442
36906
  ["claude", config.claude],
35443
- ["codex", config.codex]
36907
+ ["codex", config.codex],
36908
+ ["tiered_execution", config.tiered_execution]
35444
36909
  ];
35445
36910
  for (const [key, value] of topLevelSections) if (value === void 0) doc.deleteIn([key]);
35446
36911
  else doc.setIn([key], value);
@@ -35544,7 +37009,8 @@ async function saveSettingsApiPromise(settings) {
35544
37009
  } : void 0,
35545
37010
  claude: settings.claude?.permissionMode ? { permissionMode: settings.claude.permissionMode } : void 0,
35546
37011
  codex: settings.codex?.permissionMode ? { permissionMode: settings.codex.permissionMode } : void 0,
35547
- remote: settings.remote
37012
+ remote: settings.remote,
37013
+ tiered_execution: tieredExecutionConfigForSave(settings.tiered_execution, currentConfig.providerAuth)
35548
37014
  });
35549
37015
  clearConfigCache();
35550
37016
  }
@@ -35711,6 +37177,10 @@ function validateSettingsApi(settings) {
35711
37177
  if (settings.remote.resiliency_tier !== void 0 && settings.remote.resiliency_tier !== "ephemeral" && settings.remote.resiliency_tier !== "durable") errors.push("remote.resiliency_tier must be ephemeral or durable");
35712
37178
  if (settings.remote.max_concurrent_agents !== void 0 && (!Number.isInteger(settings.remote.max_concurrent_agents) || settings.remote.max_concurrent_agents < 0)) errors.push("remote.max_concurrent_agents must be a non-negative integer");
35713
37179
  }
37180
+ if (settings.tiered_execution !== void 0) {
37181
+ const tieredExecutionError = validateTieredExecutionSettings(settings.tiered_execution, loadConfigSync().config.providerAuth);
37182
+ if (tieredExecutionError) errors.push(tieredExecutionError);
37183
+ }
35714
37184
  return {
35715
37185
  valid: errors.length === 0,
35716
37186
  errors,
@@ -36881,7 +38351,7 @@ Be specific: reference actual file names, function names, requirement text, disc
36881
38351
  const { command: cliCmd, args: cliArgs } = getAgentCommandSync(statusModelId);
36882
38352
  const modelFlag = cliArgs.length > 0 ? ` ${cliArgs.join(" ")}` : "";
36883
38353
  const promptFile = join$1(planningDir, ".status-review-prompt.tmp");
36884
- const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-n0vv1kSW.js");
38354
+ const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-Djkuu1CD.js");
36885
38355
  let providerEnvStr = "";
36886
38356
  let providerEnv = {};
36887
38357
  const statusProvider = getProviderForModelSync(statusModelId);
@@ -36964,7 +38434,7 @@ const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/
36964
38434
  let safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "-");
36965
38435
  let processedContent = content;
36966
38436
  if (safeName.endsWith(".vtt")) {
36967
- const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-DxyN_zxJ.js"));
38437
+ const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-d-QMKZn4.js"));
36968
38438
  processedContent = vttToMarkdown(content);
36969
38439
  safeName = safeName.replace(/\.vtt$/, ".md");
36970
38440
  }
@@ -42302,73 +43772,6 @@ const resetRoute = HttpRouter.add("POST", "/api/autopreso/session/reset", httpHa
42302
43772
  })));
42303
43773
  const autopresoRouteLayer = Layer.mergeAll(startRoute, backToStagingRoute, resetRoute);
42304
43774
  //#endregion
42305
- //#region services/event-loop-monitor.ts
42306
- init_activity_logger();
42307
- const EVENT_LOOP_MONITOR_WINDOW_MS = 6e4;
42308
- let lastSample = {
42309
- p50: 0,
42310
- p99: 0,
42311
- max: 0,
42312
- unit: "ms",
42313
- sampledAt: null,
42314
- windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
42315
- };
42316
- let timer$3 = null;
42317
- let activeHistogram = null;
42318
- function nsToRoundedMs(ns) {
42319
- if (!Number.isFinite(ns) || ns <= 0) return 0;
42320
- return Math.round(ns / 1e6 * 100) / 100;
42321
- }
42322
- function sampleEventLoopDelay(histogram, deps = {}) {
42323
- const warnThresholdMs = deps.warnThresholdMs ?? 100;
42324
- const now = deps.now ?? (() => /* @__PURE__ */ new Date());
42325
- const warn = deps.warn ?? ((message) => console.warn(message));
42326
- const emit = deps.emit ?? emitActivityEntrySync;
42327
- const sample = {
42328
- p50: nsToRoundedMs(histogram.percentile(50)),
42329
- p99: nsToRoundedMs(histogram.percentile(99)),
42330
- max: nsToRoundedMs(histogram.max),
42331
- unit: "ms",
42332
- sampledAt: now().toISOString(),
42333
- windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
42334
- };
42335
- lastSample = sample;
42336
- if (sample.p99 > warnThresholdMs) {
42337
- const message = `Dashboard event loop p99 delay ${sample.p99}ms exceeded ${warnThresholdMs}ms`;
42338
- warn(`[event-loop-monitor] ${message}`);
42339
- emit({
42340
- source: "dashboard",
42341
- level: "warn",
42342
- message,
42343
- details: `p50=${sample.p50}ms p99=${sample.p99}ms max=${sample.max}ms over ${sample.windowMs}ms`
42344
- });
42345
- }
42346
- histogram.reset();
42347
- return sample;
42348
- }
42349
- function getEventLoopDelaySample() {
42350
- return lastSample;
42351
- }
42352
- function startEventLoopMonitor(deps = {}) {
42353
- if (timer$3) return;
42354
- const intervalMs = deps.intervalMs ?? 6e4;
42355
- activeHistogram = (deps.createHistogram ?? (() => monitorEventLoopDelay({ resolution: 20 })))();
42356
- activeHistogram.enable();
42357
- timer$3 = setInterval(() => {
42358
- if (!activeHistogram) return;
42359
- sampleEventLoopDelay(activeHistogram, deps);
42360
- }, intervalMs);
42361
- timer$3.unref?.();
42362
- }
42363
- function stopEventLoopMonitor() {
42364
- if (timer$3) {
42365
- clearInterval(timer$3);
42366
- timer$3 = null;
42367
- }
42368
- activeHistogram?.disable();
42369
- activeHistogram = null;
42370
- }
42371
- //#endregion
42372
43775
  //#region routes/metrics.ts
42373
43776
  /**
42374
43777
  * Metrics route module — Effect HttpRouter.Layer (PAN-428 B16)
@@ -42842,7 +44245,7 @@ const postRallyValidateRoute = HttpRouter.add("POST", "/api/rally/validate", Eff
42842
44245
  }, { status: 400 });
42843
44246
  return yield* Effect.promise(async () => {
42844
44247
  try {
42845
- const { RallyRestApi } = await import("./rally-api-CdbdHW2C.js");
44248
+ const { RallyRestApi } = await import("./rally-api-DpFjC_KQ.js");
42846
44249
  const api = new RallyRestApi({
42847
44250
  apiKey,
42848
44251
  server: server || "https://rally1.rallydev.com"
@@ -43170,7 +44573,7 @@ const getPlanningStatusRoute = HttpRouter.add("GET", "/api/planning/:issueId/sta
43170
44573
  const remoteState = loadRemoteAgentState(sessionName);
43171
44574
  const isRemote = !!remoteState;
43172
44575
  const vmName = remoteState?.vmName ?? "";
43173
- const { getAgentStateSync } = await import("./agents-pTsn9v84.js");
44576
+ const { getAgentStateSync } = await import("./agents-Ea9gfdKS.js");
43174
44577
  const agentStarting = getAgentStateSync(sessionName)?.status === "starting";
43175
44578
  let tmuxSessionAlive = false;
43176
44579
  if (!isRemote) try {
@@ -43313,7 +44716,7 @@ Continue the PLANNING session. Do NOT implement anything.
43313
44716
  `);
43314
44717
  const agentCwd = workspacePath;
43315
44718
  if (existsSync$1(outputFile)) await rename(outputFile, join$1(planningDir, `output-${Date.now()}.jsonl`));
43316
- const { getAgentCommandSync } = await import("./settings-CWqi2OdX.js");
44719
+ const { getAgentCommandSync } = await import("./settings-DXeuoifi.js");
43317
44720
  let msgPlanningModel = "claude-sonnet-5";
43318
44721
  try {
43319
44722
  msgPlanningModel = resolveModel("plan", void 0, loadConfigSync().config);
@@ -43434,7 +44837,7 @@ async function getIndexStats(rootPath, isMain) {
43434
44837
  }
43435
44838
  const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Effect.promise(async () => {
43436
44839
  try {
43437
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DvyN2P1o.js");
44840
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43438
44841
  const projectRoot = process.cwd();
43439
44842
  const venvPath = join$1(projectRoot, ".venv");
43440
44843
  const results = [];
@@ -43479,7 +44882,7 @@ const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Ef
43479
44882
  }));
43480
44883
  const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Effect.promise(async () => {
43481
44884
  try {
43482
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DvyN2P1o.js");
44885
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43483
44886
  const projectRoot = process.cwd();
43484
44887
  const venvPath = join$1(projectRoot, ".venv");
43485
44888
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -43496,7 +44899,7 @@ const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Ef
43496
44899
  }));
43497
44900
  const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effect.promise(async () => {
43498
44901
  try {
43499
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DvyN2P1o.js");
44902
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43500
44903
  const projectRoot = process.cwd();
43501
44904
  const venvPath = join$1(projectRoot, ".venv");
43502
44905
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -43513,7 +44916,7 @@ const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effe
43513
44916
  }));
43514
44917
  const postTldrReloadRoute = HttpRouter.add("POST", "/api/services/tldr/reload", Effect.promise(async () => {
43515
44918
  try {
43516
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DvyN2P1o.js");
44919
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43517
44920
  const enabled = isTldrEnabledSync();
43518
44921
  const projectRoot = process.cwd();
43519
44922
  const targets = [];
@@ -43589,7 +44992,7 @@ const getVersionRoute = HttpRouter.add("GET", "/api/version", Effect.promise(asy
43589
44992
  const version = await getOverdeckVersion();
43590
44993
  let supervisorUrl = null;
43591
44994
  try {
43592
- const { getSupervisorUrlSync } = await import("./supervisor-C4oXDjJt.js");
44995
+ const { getSupervisorUrlSync } = await import("./supervisor-BY_xA7eS.js");
43593
44996
  supervisorUrl = getSupervisorUrlSync();
43594
44997
  } catch {}
43595
44998
  return jsonResponse({
@@ -43753,7 +45156,7 @@ const postShadowMonitorRoute = HttpRouter.add("POST", "/api/shadow/:issueId/moni
43753
45156
  const projectPath = await getProjectPath(issuePrefix);
43754
45157
  const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
43755
45158
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
43756
- const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-CXnG47QM.js");
45159
+ const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-DMC2pBPa.js");
43757
45160
  const config = {
43758
45161
  issueId,
43759
45162
  workspacePath,
@@ -43783,7 +45186,7 @@ const postShadowObserveRoute = HttpRouter.add("POST", "/api/shadow/:issueId/obse
43783
45186
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
43784
45187
  const ghConfig = getGitHubConfig();
43785
45188
  if (!ghConfig) return jsonResponse({ error: "GitHub not configured - Observer requires GitHub" }, { status: 400 });
43786
- const { runObserverCycle } = await import("./shadow-engineering-CXnG47QM.js");
45189
+ const { runObserverCycle } = await import("./shadow-engineering-DMC2pBPa.js");
43787
45190
  const firstRepo = ghConfig.repos[0];
43788
45191
  const config = {
43789
45192
  issueId,
@@ -44848,7 +46251,7 @@ async function cleanupConversationAttachments(name) {
44848
46251
  * sessions that the lifecycle poller missed. */
44849
46252
  async function cleanupOrphanedConversationAttachments() {
44850
46253
  try {
44851
- const { listConversations } = await import("./conversations-RY7OLgoS.js");
46254
+ const { listConversations } = await import("./conversations-DSOZG_Sz.js");
44852
46255
  const convNames = new Set(listConversations().map((c) => c.name));
44853
46256
  const root = getConversationAttachmentsRoot();
44854
46257
  await runInBatches((await readdir(root, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !convNames.has(entry.name)).map((entry) => join$1(root, entry.name)), 10, (path) => rm(path, {
@@ -45716,7 +47119,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
45716
47119
  const codexApprovalPolicy = codexPermMode === "full-access" ? "never" : "on-request";
45717
47120
  const codexSandboxMode = codexPermMode === "full-access" ? "danger-full-access" : codexPermMode === "read-only" ? "read-only" : "workspace-write";
45718
47121
  const codexApprovalsReviewer = codexPermMode === "auto-review" ? "auto_review" : void 0;
45719
- const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-BTxuHR7v.js");
47122
+ const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-BObh4thD.js");
45720
47123
  initCodexHome(codexHome, {
45721
47124
  trustedDir: cwd,
45722
47125
  approvalPolicy: codexApprovalPolicy,
@@ -45786,7 +47189,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
45786
47189
  } catch {}
45787
47190
  console.log(`[claude-invoke] purpose=conversation-session | model=${model || "default"} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command="${runtimeCommand}"`);
45788
47191
  try {
45789
- const { preTrustDirectory } = await import("./workspace-manager-g8GBLo49.js");
47192
+ const { preTrustDirectory } = await import("./workspace-manager-BE2H8gD5.js");
45790
47193
  preTrustDirectory(cwd);
45791
47194
  } catch {}
45792
47195
  try {
@@ -45803,7 +47206,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
45803
47206
  const codexHomeDir = codexFields.codexHome;
45804
47207
  (async () => {
45805
47208
  try {
45806
- const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-BTxuHR7v.js");
47209
+ const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-BObh4thD.js");
45807
47210
  const rollout = await waitForCodexRollout(codexHomeDir, 12e4);
45808
47211
  if (rollout) {
45809
47212
  const threadId = extractThreadIdFromRollout(rollout);
@@ -46729,6 +48132,19 @@ async function resolveSessionFile(conv) {
46729
48132
  console.error(`[conversations] UNRESOLVED claude-code session for conversation '${conv.name}' (tmux=${conv.tmuxSession}, status=${conv.status}, cwd=${conv.cwd}): no --session-id pinned in launcher.sh and no recorded claudeSessionId. The transcript panel cannot be trusted.`);
46730
48133
  return null;
46731
48134
  }
48135
+ /**
48136
+ * True when a conversation that demonstrably had activity (tokens recorded, or
48137
+ * a title derived from its messages) no longer has a transcript file on disk.
48138
+ * This is the operator-facing "history was lost" signal — e.g. the 2026-07-05
48139
+ * incident where a boot-time cleanup deleted conv-* dirs holding ohmypi/codex
48140
+ * transcripts. Live sessions are excluded: a claude-code session writes its
48141
+ * JSONL only on the first message, so a missing file is normal while alive.
48142
+ */
48143
+ function conversationTranscriptMissing(conv, sessionAlive, sessionFile) {
48144
+ if (sessionAlive) return false;
48145
+ if (!((conv.totalTokens ?? 0) > 0 || conv.titleSource === "ai" || conv.titleSource === "auto")) return false;
48146
+ return !sessionFile || !existsSync$1(sessionFile);
48147
+ }
46732
48148
  const MESSAGES_CACHE_MAX = 100;
46733
48149
  const messagesCache = /* @__PURE__ */ new Map();
46734
48150
  function isCodexSessionFile(sessionFile) {
@@ -46897,7 +48313,8 @@ async function getConversationRead(rawId, deps) {
46897
48313
  isWorktree: gitInfo.isWorktree,
46898
48314
  pendingInputCount,
46899
48315
  pendingInputKinds,
46900
- pendingAskUserQuestion
48316
+ pendingAskUserQuestion,
48317
+ transcriptMissing: conversationTranscriptMissing(conv, sessionAlive, convSf)
46901
48318
  });
46902
48319
  } catch (error) {
46903
48320
  const msg = error instanceof Error ? error.message : String(error);
@@ -47406,7 +48823,8 @@ async function enrichConversationList(limit, offset) {
47406
48823
  isWorktree: gitInfo.isWorktree,
47407
48824
  pendingInputCount,
47408
48825
  pendingInputKinds,
47409
- pendingAskUserQuestion
48826
+ pendingAskUserQuestion,
48827
+ transcriptMissing: conversationTranscriptMissing(row, sessionAlive, convSf)
47410
48828
  };
47411
48829
  })), CONVERSATION_LIST_ENRICHMENT_CONCURRENCY));
47412
48830
  }
@@ -48872,7 +50290,7 @@ async function registerProjectFromPath(opts) {
48872
50290
  registerProjectSync(key, projectConfig);
48873
50291
  const seededContextLayer = ensureProjectLayer(fullPath);
48874
50292
  try {
48875
- const { preTrustDirectorySync } = await import("./workspace-manager-g8GBLo49.js");
50293
+ const { preTrustDirectorySync } = await import("./workspace-manager-BE2H8gD5.js");
48876
50294
  preTrustDirectorySync(fullPath);
48877
50295
  } catch {}
48878
50296
  let hooksInstalled = 0;
@@ -48908,7 +50326,7 @@ init_pan_dir();
48908
50326
  init_paths();
48909
50327
  const execAsync$3 = promisify(exec);
48910
50328
  async function getIssueDataService() {
48911
- const { getSharedIssueService } = await import("./issue-service-singleton-BDRqGHSo.js");
50329
+ const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
48912
50330
  return getSharedIssueService();
48913
50331
  }
48914
50332
  /** Returns true if the path exists (any type). */
@@ -50835,7 +52253,7 @@ async function refreshMergeStateFromGitHub(issueId, repo, prNumber) {
50835
52253
  let mergeState;
50836
52254
  let isDraft;
50837
52255
  let checksFailed;
50838
- const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-vFcUfTcd.js");
52256
+ const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-Dg25z05Z.js");
50839
52257
  if (isGitHubAppConfigured()) {
50840
52258
  const prState = await Effect.runPromise(getPullRequestState(owner, repoName, prNumber));
50841
52259
  mergeable = prState.mergeable === null ? "UNKNOWN" : prState.mergeable ? "MERGEABLE" : "CONFLICTING";
@@ -50984,8 +52402,8 @@ async function handlePullRequestPromise(payload) {
50984
52402
  bumpIssuePrTabCacheGeneration(issueId);
50985
52403
  const repo = payload.repository.full_name;
50986
52404
  if (payload.action === "closed" && pr.merged === true) try {
50987
- const { postMergeLifecycle } = await import("./merge-agent-BmLQPYGq.js");
50988
- const { resolveProjectFromIssueSync } = await import("./projects-BGOgJTpI.js");
52405
+ const { postMergeLifecycle } = await import("./merge-agent-DZaRBHQA.js");
52406
+ const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
50989
52407
  const project = resolveProjectFromIssueSync(issueId);
50990
52408
  if (project) {
50991
52409
  const branchName = pr.head.ref;
@@ -51484,7 +52902,7 @@ function getCheckpointWorker() {
51484
52902
  return worker;
51485
52903
  }
51486
52904
  async function runInline(operation, payload) {
51487
- const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-Cpq2lR3l.js");
52905
+ const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-auGbx5b0.js");
51488
52906
  switch (operation) {
51489
52907
  case "claimTranscriptRange": return claimTranscriptRange(payload);
51490
52908
  case "commitTranscriptRange": return commitTranscriptRange(payload);
@@ -52638,7 +54056,7 @@ function buildPendingTurn(input, fromOffset, toOffset, compressed) {
52638
54056
  };
52639
54057
  }
52640
54058
  async function emitMemoryObservationCreated(observation, timestamp) {
52641
- const { initEventStore } = await import("./event-store-C3uFXcUQ.js");
54059
+ const { initEventStore } = await import("./event-store-D7_dSgP8.js");
52642
54060
  await (await initEventStore()).appendAsync({
52643
54061
  type: "memory.observation_created",
52644
54062
  timestamp,
@@ -55677,7 +57095,7 @@ async function getFlywheelConversationPayload() {
55677
57095
  };
55678
57096
  }
55679
57097
  async function postFlywheelReportPayload() {
55680
- const { flywheelReportCommand } = await import("./flywheel-CiKfMXDy.js");
57098
+ const { flywheelReportCommand } = await import("./flywheel-CFPOlOUd.js");
55681
57099
  await flywheelReportCommand({ cwd: process.cwd() });
55682
57100
  return {
55683
57101
  status: 200,
@@ -55835,11 +57253,11 @@ const getMergeBlockersRoute = HttpRouter.add("GET", "/api/flywheel/merge-blocker
55835
57253
  async function defaultGetOrderedIssueIds() {
55836
57254
  const status = await readCurrentFlywheelStatusForDashboard();
55837
57255
  if (!status) return [];
55838
- const { computeMergeQueue } = await import("./flywheel-merge-order-5KNitgPL.js");
57256
+ const { computeMergeQueue } = await import("./flywheel-merge-order-FN6zqVXG.js");
55839
57257
  return (await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd()).pipe(Effect.provide(layer)))).map((i) => i.issueId);
55840
57258
  }
55841
57259
  async function defaultMergeOne(issueId) {
55842
- const { triggerMerge } = await import("./merge-ops-BZz86Atd.js");
57260
+ const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
55843
57261
  const r = await triggerMerge(issueId);
55844
57262
  return r.success ? { ok: true } : {
55845
57263
  ok: false,
@@ -55859,7 +57277,7 @@ async function postFlywheelMergeNextPayload(payload, deps = {}) {
55859
57277
  body: { error: "n must be a positive integer" }
55860
57278
  };
55861
57279
  const issueIds = (await (deps.getOrderedIssueIds ?? defaultGetOrderedIssueIds)()).slice(0, n);
55862
- const { shipMergeBatch } = await import("./merge-batch-UhBPrssc.js");
57280
+ const { shipMergeBatch } = await import("./merge-batch-BbG78hx-.js");
55863
57281
  return {
55864
57282
  status: 200,
55865
57283
  body: { outcomes: await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne }) }
@@ -56016,7 +57434,7 @@ const getFlywheelMergeQueueRoute = HttpRouter.add("GET", "/api/flywheel/merge-qu
56016
57434
  return yield* Effect.promise(async () => {
56017
57435
  const status = await readCurrentFlywheelStatusForDashboard();
56018
57436
  if (!status) return jsonResponse([]);
56019
- const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-5KNitgPL.js");
57437
+ const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-FN6zqVXG.js");
56020
57438
  return jsonResponse(await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd(), { getPrUrl: resolveMergeQueuePrUrl }).pipe(Effect.provide(layer))));
56021
57439
  });
56022
57440
  })));
@@ -56030,18 +57448,18 @@ function uatGenerationNameFromParam(param) {
56030
57448
  return decoded.startsWith("uat/") ? decoded : `uat/${decoded}`;
56031
57449
  }
56032
57450
  const getUatGenerationsRoute = HttpRouter.add("GET", "/api/flywheel/uat-generations", httpHandler(Effect.gen(function* () {
56033
- const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-BfTMZmcU.js"));
57451
+ const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56034
57452
  return jsonResponse(yield* Effect.promise(() => getUatGenerationsPayload()));
56035
57453
  })));
56036
57454
  const getUatCandidateRoute = HttpRouter.add("GET", "/api/flywheel/uat-candidate", httpHandler(Effect.gen(function* () {
56037
- const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-BfTMZmcU.js"));
57455
+ const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56038
57456
  return jsonResponse(yield* Effect.promise(() => getUatCandidatePayload()));
56039
57457
  })));
56040
57458
  const postUatGenerationStackRoute = HttpRouter.add("POST", "/api/flywheel/uat-generations/:name/stack", httpHandler(Effect.gen(function* () {
56041
57459
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
56042
57460
  if (authError) return authError;
56043
57461
  const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
56044
- const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-BfTMZmcU.js"));
57462
+ const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56045
57463
  const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
56046
57464
  if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
56047
57465
  return jsonResponse({
@@ -56053,8 +57471,8 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
56053
57471
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
56054
57472
  if (authError) return authError;
56055
57473
  const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
56056
- const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-BfTMZmcU.js"));
56057
- const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-BAT59U9k.js"));
57474
+ const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
57475
+ const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-B6m2H7J6.js"));
56058
57476
  const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle));
56059
57477
  if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
56060
57478
  return jsonResponse(result);
@@ -56067,7 +57485,7 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
56067
57485
  const postFlywheelAssembleUatRoute = HttpRouter.add("POST", "/api/flywheel/assemble-uat", httpHandler(Effect.gen(function* () {
56068
57486
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
56069
57487
  if (authError) return authError;
56070
- const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-BfTMZmcU.js"));
57488
+ const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56071
57489
  return jsonResponse(yield* Effect.promise(() => runUatTrainReconcile({ force: true })));
56072
57490
  })));
56073
57491
  const getFlywheelStateRoute = HttpRouter.add("GET", "/api/flywheel/state", httpHandler(Effect.gen(function* () {
@@ -58805,6 +60223,7 @@ init_config_yaml();
58805
60223
  init_review_status();
58806
60224
  init_activity_logger();
58807
60225
  init_tier_supervisor();
60226
+ init_tier_table();
58808
60227
  function parseCalloutBody(body) {
58809
60228
  if (!body || typeof body !== "object") return void 0;
58810
60229
  const record = body;
@@ -59582,7 +61001,7 @@ async function pollOnce(state) {
59582
61001
  try {
59583
61002
  const remoteState = await readFile(join$1(homedir$1(), ".overdeck", "agents", agentId, "remote-state.json"), "utf-8").then((text) => JSON.parse(text)).catch(() => null);
59584
61003
  if (remoteState?.location === "remote" && remoteState?.vmName) {
59585
- const { getRemoteAgentOutput } = await import("./remote-agents-BT_DHl8s.js");
61004
+ const { getRemoteAgentOutput } = await import("./remote-agents-BXKV5iBS.js");
59586
61005
  stdout = await getRemoteAgentOutput(agentId, remoteState.vmName, 50);
59587
61006
  } else stdout = await Effect.runPromise(capturePane(agentId, 50));
59588
61007
  } catch {
@@ -60765,7 +62184,7 @@ const RESTART_MARKER = join(homedir(), ".overdeck", "dashboard-restarting.json")
60765
62184
  * loads new content-hashed chunk filenames (no ERR_MODULE_NOT_FOUND after rebuild).
60766
62185
  */
60767
62186
  async function defaultLifecycleRunner(pending) {
60768
- const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-BmLQPYGq.js");
62187
+ const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-DZaRBHQA.js");
60769
62188
  await postMergeLifecycle(pending.issueId, pending.projectPath, pending.sourceBranch, { skipDeploy: true });
60770
62189
  if (pending.sourceBranch) await notifyTldrDaemon(pending.projectPath, pending.sourceBranch);
60771
62190
  }
@@ -61086,7 +62505,7 @@ function failureReason(result) {
61086
62505
  return result.error ?? result.message ?? `merge returned status ${result.statusCode ?? "unknown"}`;
61087
62506
  }
61088
62507
  async function defaultMergeIssue(issueId) {
61089
- const { triggerMerge } = await import("./merge-ops-BZz86Atd.js");
62508
+ const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
61090
62509
  return triggerMerge(issueId);
61091
62510
  }
61092
62511
  function defaultAnnounceFailure(issueId, reason) {
@@ -61190,6 +62609,34 @@ async function warnIfAutonomousMergeBackendUnavailable(deps = {}) {
61190
62609
  }
61191
62610
  }
61192
62611
  //#endregion
62612
+ //#region services/merge-app-scopes-health.ts
62613
+ init_activity_logger();
62614
+ init_github_app();
62615
+ function shouldWarnAppCannotMerge(result) {
62616
+ return result.configured === true && result.canMerge === false;
62617
+ }
62618
+ async function warnIfAppCannotMerge(deps = {}) {
62619
+ try {
62620
+ const result = await (deps.getResult ?? (() => verifyAppCanMerge()))();
62621
+ if (!shouldWarnAppCannotMerge(result)) return;
62622
+ const missing = result.missing ?? [];
62623
+ const message = [
62624
+ "[overdeck] WARNING: overdeck-agent GitHub App installation cannot merge pull requests.",
62625
+ `Missing required permissions: ${missing.join(", ")}.`,
62626
+ "Grant these permissions to the App installation to enable autonomous merges."
62627
+ ].join("\n");
62628
+ (deps.warn ?? console.warn)(message);
62629
+ (deps.emit ?? emitActivityEntrySync)({
62630
+ source: "dashboard",
62631
+ level: "warn",
62632
+ message: `GitHub App installation cannot merge: missing ${missing.join(", ")}`
62633
+ });
62634
+ } catch (error) {
62635
+ const message = error instanceof Error ? error.message : String(error);
62636
+ (deps.warn ?? console.warn)(`[overdeck] WARNING: failed to verify GitHub App merge permissions: ${message}`);
62637
+ }
62638
+ }
62639
+ //#endregion
61193
62640
  //#region services/cost-reconcile-service.ts
61194
62641
  const RECONCILE_INTERVAL_MS = 5 * 6e4;
61195
62642
  let timer = null;
@@ -61200,6 +62647,14 @@ async function runCostReconcileOnce(reason) {
61200
62647
  const result = await reconcilePiTranscripts();
61201
62648
  if (result.eventsImported > 0 || result.errors.length > 0) console.log(`[cost-reconciler] ${reason} sweep: ${result.eventsImported} imported, ${result.duplicatesSkipped} duplicate(s), ${result.errors.length} error(s)`);
61202
62649
  if (result.errors.length > 0) for (const err of result.errors.slice(0, 5)) console.warn(`[cost-reconciler] ${err.path}: ${err.error}`);
62650
+ const backfillResult = reclassifyUnknownCostEventsSync();
62651
+ if (backfillResult.updated > 0) console.log(`[cost-reconciler] ${reason} UNKNOWN backfill: ${backfillResult.updated} updated`);
62652
+ try {
62653
+ const codexResult = await Effect.runPromise(CostWriter.use((writer) => writer.reconcile({ source: "codex" })).pipe(Effect.provide(CostDoorLive)));
62654
+ if (codexResult.imported > 0) console.log(`[cost-reconciler] ${reason} codex sweep: ${codexResult.imported} imported`);
62655
+ } catch (err) {
62656
+ console.warn("[cost-reconciler] codex sweep failed:", err instanceof Error ? err.message : err);
62657
+ }
61203
62658
  })().finally(() => {
61204
62659
  inFlight = null;
61205
62660
  });
@@ -61225,6 +62680,7 @@ function stopCostReconcileService() {
61225
62680
  }
61226
62681
  //#endregion
61227
62682
  //#region ../../../scripts/create-overdeck-db.ts
62683
+ init_boot_gates();
61228
62684
  init_driver();
61229
62685
  function migrationStatements(migrationPath) {
61230
62686
  return readFileSync$1(migrationPath, "utf8").split("--> statement-breakpoint").map((statement) => statement.trim()).filter(Boolean);
@@ -61291,6 +62747,7 @@ init_paths();
61291
62747
  init_tmux();
61292
62748
  init_fts_db();
61293
62749
  init_paths$1();
62750
+ init_auto_commit();
61294
62751
  initDashboardLogFile();
61295
62752
  console.log(`[boot-timing] module graph loaded at +${Math.round(performance.now())}ms (since process start)`);
61296
62753
  console.log(`[overdeck] Boot gates: ${formatBootGateState(resolveBootGates())}`);
@@ -61299,6 +62756,7 @@ console.log("[overdeck] Event loop delay monitor started");
61299
62756
  await mkdir(getOverdeckHome(), { recursive: true });
61300
62757
  ensureInternalTokenSync();
61301
62758
  warnIfAutonomousMergeBackendUnavailable();
62759
+ warnIfAppCannotMerge();
61302
62760
  await ensureManagedTmuxContextOnce();
61303
62761
  await initTrackerConfigCache().catch((err) => {
61304
62762
  console.log("[tracker-config] Warning: failed to cache .overdeck.env:", err.message);
@@ -61635,6 +63093,7 @@ const handleShutdownSignal = async (signal) => {
61635
63093
  stopCostReconcileService();
61636
63094
  stopRestartAnnouncer();
61637
63095
  await stopDeaconChild().catch((err) => console.warn("[deacon-supervisor] child shutdown failed:", err));
63096
+ await Effect.runPromise(flushAllPendingAutoCommits()).catch((err) => console.warn("[pan-dir/auto-commit] shutdown flush failed:", err));
61638
63097
  await stopConversationSearchWatcher().catch((err) => console.warn("[conversation-search] watcher shutdown failed:", err));
61639
63098
  closeConversationSearchService();
61640
63099
  closeMemoryFtsDatabases();
@@ -61683,7 +63142,7 @@ reconcileStaleGitHubBlockers().then((n) => {
61683
63142
  }
61684
63143
  }).catch((err) => console.warn(`[overdeck] Startup blocker reconciliation failed: ${err.message}`));
61685
63144
  try {
61686
- const { resetProcessingToQueued } = await import("./merge-sync-C_in5zj_.js");
63145
+ const { resetProcessingToQueued } = await import("./merge-sync-D3wPN2-T.js");
61687
63146
  const resetCount = resetProcessingToQueued();
61688
63147
  if (resetCount > 0) {
61689
63148
  console.log(`[overdeck] Reset ${resetCount} stuck merge queue entries to queued`);