@overdeck/desktop 0.43.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-CILjxFV5.js → diagram-OG6HWLK6-Dk7-KYG7.js} +1 -1
  38. package/server/public/assets/{dist-WopvNJhW.js → dist-CaEw8nYm.js} +5 -5
  39. package/server/public/assets/{dist-BBtIkNyE.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-BTG45eDk.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-C9XG5bKT.js → pieDiagram-4H26LBE5-_QEBgJ8d.js} +1 -1
  58. package/server/public/assets/{prod-BjCGQWZg.js → prod-oCB7Fr_u.js} +5 -5
  59. package/server/public/assets/{quadrantDiagram-W4KKPZXB-BqyPPpJS.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-DOiWA53d.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-BkfiVkrb.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 +1844 -424
  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-DT2hpoe9.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-CByisuCB.js +0 -292
  82. package/server/public/assets/image-GAAHSSAO-CCe2yzQe.js +0 -1
  83. package/server/public/assets/index-etdSnOxp.js +0 -818
  84. package/server/public/assets/index-qrDOFwsw.css +0 -1
  85. package/server/public/assets/linear-BlUaWFF8.js +0 -1
  86. package/server/public/assets/mermaid-GHXKKRXX-7G86hyh-.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 init_model_capabilities, A as UnknownModelError, C as validateTieredExecutionConfig, E as init_harness_policy, F as getProviderForModelSync, G as DEFAULT_WORKHORSES, H as defaultBackgroundAiFeatures, I as init_providers, J as MODEL_CAPABILITIES, K as computeModelOrigin, L as piProviderForModel, M as getBuiltInDefaultHarness, N as getKimiAnthropicBaseUrl, Q as hasModelCapabilitySync, S as resolveTieredExecutionEnabled, U as init_registry, W as DEFAULT_ROLES, Y as MODEL_DEPRECATIONS, Z as getModelEffortLevelsSync, _ as mergeTtsDaemonConfigs, at as require_dist$1, b as TieredExecutionConfigError, bt as init_errors, d as loadConfigSync, et as modelSupportsImagesSync, f as updateConversationsConfig, ft as GitHubApiError, g as getDefaultTtsDaemonConfig, gt as ProcessSpawnError, h as mergeConfigs, i as getConversationsConfig, it as ROLE_EFFORTS, l as isTldrEnabledSync, m as jsYaml, n as clearConfigCache, nt as COMPLIANCE_MODES, o as getGlobalConfigPath, p as init_js_yaml, q as resolveModel, r as getConversationSearchConfigSync, rt as PARENT_MODEL_REF, s as isClaudeCodeChannelsEnabled, t as init_config_yaml, tt as resolveModelIdSync, u as loadConfigNoMigration, ut as FsError, vt as TrackerError, w as canUseHarnessSync, x as init_tier_table } from "./config-yaml-B5z7J6BP.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-BX0_9wAC.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-lL_oaMMz.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-BnUODQb7.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-BRRVhnOm.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-BWA18sW9.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-DwRDpZ6x.js";
13
- import { a as init_event_store, i as initEventStore, n as getEventStore, r as getSharedDb } from "./event-store-BMvjb-ek.js";
14
- 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-CEKNqdTd.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-Sdng4KbL.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-BQ7MbVQW.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-to38wymF.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-CKcDJjLT.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-B6tWmcXf.js";
22
- import { t as init_behavior } from "./behavior-RdPVwq8Z.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-DYokw49s.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-BCLgzZEy.js";
25
- import { n as AgentStateServiceLive } from "./agent-state-service-ATZbN2LT.js";
26
- import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-JlYBFawD.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-DtBd7iee.js";
29
- import { i as init_github_client, r as GitHubClientOptionalLive, t as GitHubClient } from "./github-client-BLwJoTrW.js";
30
- import { i as init_rally_client, r as RallyClientOptionalLive, t as RallyClient } from "./rally-client-BqrgQ0Pu.js";
31
- import { a as resolveTrackerTypeSync, i as resolveGitHubIssueSync, n as init_tracker_utils } from "./tracker-utils-DZaFGJUO.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-B1aiia7z.js";
33
- import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-ByEURFQq.js";
34
- import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-BpZ4S0Hi.js";
35
- import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates } from "./review-status-sync-C4eeN5DR.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-B79eYXWq.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-Bh8VelYD.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-BY-a7oH8.js";
39
- import { f as init_config$1, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-D9ULtCLv.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-CSxhAzME.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-Dxivfe0w.js";
43
- import { n as isBackgroundFeatureEnabled, t as init_features } from "./features-D6BM-oE5.js";
44
- import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-ncJepdos.js";
45
- import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health, t as init_providers$1 } from "./providers-DitjC5u4.js";
46
- import { n as init_launcher_generator, t as generateLauncherScriptSync } from "./launcher-generator-DrSU75_W.js";
47
- import { a as init_claude_permissions, o as resolvePermissionModeSync, r as getClaudePermissionFlagsStringSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-CfMr0eIT.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-BcSHb3Oi.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-CcllfQYK.js";
51
- import { c as init_docker_stats } from "./stack-health-BDjtNZ9V.js";
52
- import { n as resolveHarness, t as init_harness_resolve } from "./harness-resolve-DzutoEqt.js";
53
- import { i as checkPrdGateSync, t as init_pan_dir } from "./pan-dir-BwlWqB_Z.js";
54
- import { a as init_lifecycle_io, t as appendContinueSessionEntryForIssue } from "./lifecycle-io-wMIX4D4p.js";
55
- import { r as init_swarm_readiness, t as analyzeSwarmReadiness } from "./swarm-readiness-B2JKxIBj.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-Da1gKxDd.js";
57
- import { i as CostThresholdError, l as embed, s as parseRelativeTime } from "./scanner-CzCiMLJf.js";
58
- import { n as deduplicateEventsSync, o as readEventsSync, s as tailEventsSync } from "./events-B3ytKOJM.js";
59
- import { n as recordBackgroundAiCost, t as init_cost } from "./cost-CGo8texI.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-By5V03uu.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-JkQdW0Rd.js";
62
- import { l as detectPlatform, u as init_platform } from "./workspace-manager-DA59Z70a.js";
63
- import { O as init_specialists, b as getReviewerSessionName } from "./specialists-DALlYmo_.js";
64
- import { t as withBdMutex } from "./bd-mutex-DXCIKL87.js";
65
- import { n as findPrdAtStatusSync, r as init_prd_locations } from "./prd-locations-D9J9Dei3.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-DpbY6nKj.js";
68
- import { t as clearIssueClosedCache } from "./issue-closed-BEVoKUiE.js";
69
- import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-DUcHN5E1.js";
70
- import { n as parseSequenceMd, r as writeSequenceMd, t as init_sequence_io } from "./sequence-io-DzHGjEQG.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-DS83DYov.js";
72
- import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-BWP0Gghc.js";
73
- import { i as resolveAutoSpawnOnFinalize, o as spawnPlanningSession } from "./spawn-planning-session-DyKxcVGi.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-DHPxoyuA.js";
75
- import { r as spawnInspectAgent } from "./inspect-agent-CCGhgZwF.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-R3t6SZ7b.js";
77
- import { t as enrichReviewStatus } from "./review-status-enrichment-Ddj7NTYe.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-DjuYNjJA.js";
80
- import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-BUdzAlOX.js";
81
- import { _ as init_git_activity, v as listGitOperationsSync } from "./merge-agent-NmPcMe48.js";
82
- import { t as getAgentCommandSync } from "./settings-CHBd7dXM.js";
83
- import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-Fy6N7Kgq.js";
84
- import { r as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remote-D9u5oAKU.js";
85
- import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-CwvL66eF.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-GRwakte4.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-B9qYAHl6.js";
88
- import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting } from "./conversation-compaction-CKJRITVq.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-C7RLCYC_.js";
91
- import { t as getMergeBlockersPayload } from "./merge-blockers-B3Y4Sprl.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-BEVXaOSB.js";
93
- import "./supervisor-CNcM3RIc.js";
94
- import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-0C9dS0-x.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-Bi9Y6h5B.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-B-vx-dop.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-Bi9Y6h5B.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-Bi9Y6h5B.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-DaV5vcGg.js");
737
- const { resolveProjectFromIssueSync } = await import("./projects-CKnFqWB0.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-CK4yZQev.js");
794
- const { loadProjectsConfigSync } = await import("./projects-CKnFqWB0.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-CK4yZQev.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-CK4yZQev.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-C18oIaYa.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 });
@@ -30385,7 +30384,7 @@ function parseIssueIdFromTextSync(value) {
30385
30384
  //#region services/resource-discovery.ts
30386
30385
  init_projects();
30387
30386
  init_tmux();
30388
- const execFileAsync$9 = promisify(execFile);
30387
+ const execFileAsync$14 = promisify(execFile);
30389
30388
  const RESOURCE_DISCOVERY_TTL_MS = 3e4;
30390
30389
  const RECENT_ACTIVITY_WINDOW_MS = 5e3;
30391
30390
  let cachedResourceIssues = null;
@@ -30493,7 +30492,7 @@ function isDiscoverableAgentSession(sessionName) {
30493
30492
  async function loadTrackerIssues() {
30494
30493
  const map = /* @__PURE__ */ new Map();
30495
30494
  try {
30496
- const { getSharedIssueService } = await import("./issue-service-singleton-BSgap-ST.js");
30495
+ const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
30497
30496
  const issues = (await getSharedIssueService()).getIssues();
30498
30497
  for (const issue of issues) if (issue.identifier) map.set(issue.identifier.toUpperCase(), issue);
30499
30498
  } catch {
@@ -30510,7 +30509,7 @@ async function loadTmuxSessions() {
30510
30509
  }
30511
30510
  async function loadDockerContainers() {
30512
30511
  try {
30513
- const { stdout } = await execFileAsync$9("docker", [
30512
+ const { stdout } = await execFileAsync$14("docker", [
30514
30513
  "ps",
30515
30514
  "--format",
30516
30515
  "{{.Names}}"
@@ -30528,7 +30527,7 @@ async function loadOpenPullRequests() {
30528
30527
  const repos = getGitHubConfig()?.repos ?? [];
30529
30528
  await Promise.all(repos.map(async (repo) => {
30530
30529
  try {
30531
- const { stdout } = await execFileAsync$9("gh", [
30530
+ const { stdout } = await execFileAsync$14("gh", [
30532
30531
  "pr",
30533
30532
  "list",
30534
30533
  "--repo",
@@ -30559,7 +30558,7 @@ async function loadOpenPullRequests() {
30559
30558
  }
30560
30559
  async function loadProjectBranches(projectPath) {
30561
30560
  try {
30562
- const [localResult, remoteResult] = await Promise.all([execFileAsync$9("git", [
30561
+ const [localResult, remoteResult] = await Promise.all([execFileAsync$14("git", [
30563
30562
  "for-each-ref",
30564
30563
  "refs/heads/feature/*",
30565
30564
  "--format=%(refname:short)"
@@ -30567,7 +30566,7 @@ async function loadProjectBranches(projectPath) {
30567
30566
  cwd: projectPath,
30568
30567
  encoding: "utf-8",
30569
30568
  timeout: 1e4
30570
- }).catch(() => ({ stdout: "" })), execFileAsync$9("git", [
30569
+ }).catch(() => ({ stdout: "" })), execFileAsync$14("git", [
30571
30570
  "for-each-ref",
30572
30571
  "refs/remotes/origin/feature/*",
30573
30572
  "--format=%(refname:short)"
@@ -30692,7 +30691,7 @@ async function computeResourceAllocatedIssues() {
30692
30691
  if (!issue.resourceDetails.tmuxSessions.includes(sessionName)) issue.resourceDetails.tmuxSessions.push(sessionName);
30693
30692
  }
30694
30693
  try {
30695
- const { listActiveRemoteAgentStates } = await import("./remote-agents-tBpE_KXg.js");
30694
+ const { listActiveRemoteAgentStates } = await import("./remote-agents-BXKV5iBS.js");
30696
30695
  for (const remoteState of listActiveRemoteAgentStates()) {
30697
30696
  const issue = ensureIssue(remoteState.issueId);
30698
30697
  if (!issue) continue;
@@ -30896,7 +30895,7 @@ function groupResourceAllocatedIssuesByProject(issues) {
30896
30895
  //#region ../../lib/overdeck/issue-reads.ts
30897
30896
  init_tracker_config();
30898
30897
  init_projects();
30899
- const execFileAsync$8 = promisify(execFile);
30898
+ const execFileAsync$13 = promisify(execFile);
30900
30899
  function isGitHubIssue$6(issueId) {
30901
30900
  const resolved = resolveGitHubIssueSync(issueId);
30902
30901
  if (resolved.isGitHub) return {
@@ -31077,7 +31076,7 @@ function getIssueBeads(id) {
31077
31076
  const { beads, querySource } = yield* Effect.promise(async () => {
31078
31077
  try {
31079
31078
  const bdSearchDir = workspacePath && existsSync$1(workspacePath) ? workspacePath : projectPath || homedir$1();
31080
- const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$8("bd", [
31079
+ const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$13("bd", [
31081
31080
  "list",
31082
31081
  "--json",
31083
31082
  "-l",
@@ -31198,7 +31197,7 @@ init_projects();
31198
31197
  init_remote_agents();
31199
31198
  init_tracker_utils();
31200
31199
  init_tmux();
31201
- const execFileAsync$7 = promisify(execFile);
31200
+ const execFileAsync$12 = promisify(execFile);
31202
31201
  function getIssueDataService$9() {
31203
31202
  return getSharedIssueService();
31204
31203
  }
@@ -31255,7 +31254,7 @@ async function completePlanningArtifacts(options) {
31255
31254
  if (workspaceIssueId && workspaceIssueId.toLowerCase() !== issueLower) throw new Error(`Workspace vBRIEF is for ${workspaceIssueId.toUpperCase()}, not ${upperIssueId}`);
31256
31255
  assertPlanQuality(workspaceDoc);
31257
31256
  const createBeads = options.createBeads ?? (async (path) => {
31258
- const mod = await import("./beads-BRYZM_Zz.js");
31257
+ const mod = await import("./beads-B4ysfx-Y.js");
31259
31258
  return await Effect.runPromise(mod.createBeadsFromVBrief(path));
31260
31259
  });
31261
31260
  emitCompletePlanningPhase(upperIssueId, "specWrite", "start", "writing proposed vBRIEF spec", { projectPath });
@@ -31322,8 +31321,72 @@ function completePlanningWorkspaceGitAddCommands(gitRoot) {
31322
31321
  const commands = [];
31323
31322
  if (existsSync$1(join$1(gitRoot, ".pan"))) commands.push(["add", ".pan/"]);
31324
31323
  if (existsSync$1(join$1(gitRoot, ".beads"))) commands.push(["add", ".beads/"]);
31324
+ if (existsSync$1(join$1(gitRoot, ".gitignore"))) commands.push(["add", ".gitignore"]);
31325
31325
  return commands;
31326
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
+ }
31327
31390
  function getInternalDashboardOrigin() {
31328
31391
  const port = Number.parseInt(process.env["API_PORT"] ?? process.env["PORT"] ?? "3011", 10);
31329
31392
  return process.env["OVERDECK_INTERNAL_DASHBOARD_URL"] ?? `http://127.0.0.1:${port}`;
@@ -31523,7 +31586,7 @@ async function completePlanningForIssue(options) {
31523
31586
  const filesToStage = completePlanningFilesToStage(projectPath, proposed.filename);
31524
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`);
31525
31588
  else {
31526
- const { stdout: branchStdout } = await execFileAsync$7("git", [
31589
+ const { stdout: branchStdout } = await execFileAsync$12("git", [
31527
31590
  "rev-parse",
31528
31591
  "--abbrev-ref",
31529
31592
  "HEAD"
@@ -31533,7 +31596,7 @@ async function completePlanningForIssue(options) {
31533
31596
  });
31534
31597
  const currentBranch = branchStdout.trim();
31535
31598
  if (currentBranch === "main") {
31536
- await execFileAsync$7("git", [
31599
+ await execFileAsync$12("git", [
31537
31600
  "add",
31538
31601
  "--",
31539
31602
  ...filesToStage
@@ -31542,7 +31605,7 @@ async function completePlanningForIssue(options) {
31542
31605
  encoding: "utf-8"
31543
31606
  });
31544
31607
  try {
31545
- await execFileAsync$7("git", [
31608
+ await execFileAsync$12("git", [
31546
31609
  "diff",
31547
31610
  "--cached",
31548
31611
  "--quiet",
@@ -31553,7 +31616,7 @@ async function completePlanningForIssue(options) {
31553
31616
  encoding: "utf-8"
31554
31617
  });
31555
31618
  } catch {
31556
- await execFileAsync$7("git", [
31619
+ await execFileAsync$12("git", [
31557
31620
  "commit",
31558
31621
  "-m",
31559
31622
  `chore(scope): propose ${upperIssueId} vBRIEF`,
@@ -31566,7 +31629,7 @@ async function completePlanningForIssue(options) {
31566
31629
  });
31567
31630
  console.log(`[complete-planning] Committed pan spec on main for ${upperIssueId}`);
31568
31631
  try {
31569
- const { stdout: remotes } = await execFileAsync$7("git", ["remote"], {
31632
+ const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
31570
31633
  cwd: projectPath,
31571
31634
  encoding: "utf-8"
31572
31635
  });
@@ -31579,16 +31642,16 @@ async function completePlanningForIssue(options) {
31579
31642
  }
31580
31643
  } else console.log(`[complete-planning] Project root not on main (${currentBranch}) — pan spec updated on disk but not committed on main`);
31581
31644
  }
31582
- 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"], {
31583
31646
  cwd: gitRoot,
31584
31647
  encoding: "utf-8"
31585
31648
  });
31586
- for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$7("git", args, {
31649
+ for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$12("git", args, {
31587
31650
  cwd: gitRoot,
31588
31651
  encoding: "utf-8"
31589
31652
  });
31590
31653
  try {
31591
- await execFileAsync$7("git", [
31654
+ await execFileAsync$12("git", [
31592
31655
  "diff",
31593
31656
  "--cached",
31594
31657
  "--quiet"
@@ -31597,7 +31660,7 @@ async function completePlanningForIssue(options) {
31597
31660
  encoding: "utf-8"
31598
31661
  });
31599
31662
  } catch {
31600
- await execFileAsync$7("git", [
31663
+ await execFileAsync$12("git", [
31601
31664
  "commit",
31602
31665
  "-m",
31603
31666
  `chore(plan): complete planning for ${id}`,
@@ -31608,7 +31671,7 @@ async function completePlanningForIssue(options) {
31608
31671
  });
31609
31672
  }
31610
31673
  try {
31611
- const { stdout: remotes } = await execFileAsync$7("git", ["remote"], {
31674
+ const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
31612
31675
  cwd: gitRoot,
31613
31676
  encoding: "utf-8"
31614
31677
  });
@@ -31686,6 +31749,7 @@ async function completePlanningForIssue(options) {
31686
31749
  source: "planning-agent",
31687
31750
  eventType: "planning.complete"
31688
31751
  });
31752
+ if (autoSpawn && workspacePath) await commitWorkspaceRecordBeforeAutoSpawn(workspacePath, id);
31689
31753
  const autoSpawnResult = await completePlanningAutoSpawnAndKill({
31690
31754
  issueId: id,
31691
31755
  autoSpawn,
@@ -31785,8 +31849,8 @@ init_tracker_utils();
31785
31849
  init_agents$1();
31786
31850
  init_issue_id();
31787
31851
  init_io();
31788
- const execAsync$11 = promisify(exec);
31789
- const execFileAsync$6 = promisify(execFile);
31852
+ const execAsync$10 = promisify(exec);
31853
+ const execFileAsync$11 = promisify(execFile);
31790
31854
  function isGitHubIssue$4(issueId) {
31791
31855
  const resolved = resolveGitHubIssueSync(issueId);
31792
31856
  if (resolved.isGitHub) return {
@@ -31825,7 +31889,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
31825
31889
  if (!githubCheck.isGitHub || !githubCheck.owner || !githubCheck.repo) return ["No GitHub PR to close"];
31826
31890
  const branchName = `feature/${issueId.toLowerCase()}`;
31827
31891
  try {
31828
- const { stdout: prListRaw } = await execFileAsync$6("gh", [
31892
+ const { stdout: prListRaw } = await execFileAsync$11("gh", [
31829
31893
  "pr",
31830
31894
  "list",
31831
31895
  "--repo",
@@ -31844,7 +31908,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
31844
31908
  });
31845
31909
  const prNumber = prListRaw.trim();
31846
31910
  if (!prNumber) return ["No open PR found for branch"];
31847
- await execFileAsync$6("gh", [
31911
+ await execFileAsync$11("gh", [
31848
31912
  "pr",
31849
31913
  "close",
31850
31914
  prNumber,
@@ -31857,7 +31921,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
31857
31921
  timeout: 15e3
31858
31922
  });
31859
31923
  try {
31860
- const { setReviewStatusSync } = await import("./review-status-BzMWJsVk.js");
31924
+ const { setReviewStatusSync } = await import("./review-status-Rr4SB2Kj.js");
31861
31925
  setReviewStatusSync(issueId.toUpperCase(), { prUrl: void 0 });
31862
31926
  } catch {}
31863
31927
  return [`Closed PR #${prNumber} on ${githubCheck.owner}/${githubCheck.repo}`];
@@ -31911,7 +31975,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31911
31975
  const { ctx, projectConfig } = buildLifecycleContext(id, getIssueDataService$8().getIssueSource(id) ?? void 0);
31912
31976
  const deleteWorkspace = opts.deleteWorkspace ?? true;
31913
31977
  cleanupLog.push(...await closeIssuePullRequest(id, mode === "cancel" ? "Canceled via Overdeck" : "Reset to Todo via Overdeck"));
31914
- const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-Bu2YCv15.js");
31978
+ const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-Va4AZGRx.js");
31915
31979
  const workflow = mode === "cancel" ? cancelIssueWorkflow : resetToTodo;
31916
31980
  const result = await Effect.runPromise(workflow(ctx, {
31917
31981
  deleteWorkspace,
@@ -31926,7 +31990,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31926
31990
  }));
31927
31991
  cleanupLog.push(...result.steps.flatMap((step) => step.details || [step.error].filter(Boolean)));
31928
31992
  if (mode === "cancel") try {
31929
- const { transitionVBriefOnMain } = await import("./lifecycle-io-DmVYRVq5.js");
31993
+ const { transitionVBriefOnMain } = await import("./lifecycle-io-BHi2NPxK.js");
31930
31994
  const tx = await Effect.runPromise(transitionVBriefOnMain(ctx.projectPath, id, "cancelled", "cancelled", `scope: cancel ${id.toUpperCase()} vBRIEF`));
31931
31995
  if (tx.moved) cleanupLog.push(`vBRIEF moved ${tx.fromDir} → cancelled`);
31932
31996
  if (tx.committed) cleanupLog.push(`Committed vBRIEF cancellation on main`);
@@ -31934,8 +31998,8 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31934
31998
  cleanupLog.push(`vBRIEF cancel transition failed (non-fatal): ${err?.message ?? err}`);
31935
31999
  }
31936
32000
  try {
31937
- const { killAllReviewerSessions } = await import("./review-agent-D-gfTx9G.js");
31938
- const { resolveProjectFromIssueSync } = await import("./projects-CKnFqWB0.js");
32001
+ const { killAllReviewerSessions } = await import("./review-agent-DfN-7KW6.js");
32002
+ const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
31939
32003
  const projectKey = resolveProjectFromIssueSync(id)?.projectKey;
31940
32004
  if (projectKey) {
31941
32005
  const { killed } = await Effect.runPromise(killAllReviewerSessions(projectKey, id.toUpperCase()));
@@ -31949,7 +32013,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
31949
32013
  cleanupLog.push("Cleared review status");
31950
32014
  } catch {}
31951
32015
  try {
31952
- const { resetPostMergeState } = await import("./merge-agent-CvVDCFOp.js");
32016
+ const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
31953
32017
  resetPostMergeState(id);
31954
32018
  resetPostMergeState(id.toUpperCase());
31955
32019
  cleanupLog.push("Cleared merge state");
@@ -31969,7 +32033,7 @@ function closeIssueTransition(options) {
31969
32033
  const { issueId, body, eventStore } = options;
31970
32034
  const { reason } = body;
31971
32035
  const projectPath = getProjectPath$3(void 0, extractPrefixSync(issueId) ?? issueId.split("-")[0]);
31972
- const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-Bu2YCv15.js"));
32036
+ const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
31973
32037
  const githubCheck = isGitHubIssue$4(issueId);
31974
32038
  const issueDataService = getIssueDataService$8();
31975
32039
  const issueSource = issueDataService.getIssueSource(issueId);
@@ -31992,7 +32056,7 @@ function closeIssueTransition(options) {
31992
32056
  };
31993
32057
  }
31994
32058
  const result = yield* closeWorkflow(ctx, { reason });
31995
- if (githubCheck.isGitHub) execAsync$11("pan sync", {
32059
+ if (githubCheck.isGitHub) execAsync$10("pan sync", {
31996
32060
  encoding: "utf-8",
31997
32061
  timeout: 3e4
31998
32062
  }).catch(() => {});
@@ -32203,13 +32267,13 @@ function reopenIssueTransition(options) {
32203
32267
  }
32204
32268
  yield* Effect.promise(async () => {
32205
32269
  try {
32206
- 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`, {
32207
32271
  encoding: "utf-8",
32208
32272
  timeout: 15e3
32209
32273
  });
32210
32274
  const prs = JSON.parse(stdout.trim() || "[]");
32211
32275
  if (prs.length > 0 && !prs[0].mergedAt) {
32212
- await execAsync$11(`gh pr reopen ${prs[0].number}`, {
32276
+ await execAsync$10(`gh pr reopen ${prs[0].number}`, {
32213
32277
  encoding: "utf-8",
32214
32278
  timeout: 15e3
32215
32279
  });
@@ -32236,7 +32300,7 @@ function reopenIssueTransition(options) {
32236
32300
  else clearReviewStatus(id.toUpperCase());
32237
32301
  } catch {}
32238
32302
  try {
32239
- const { resetPostMergeState } = await import("./merge-agent-CvVDCFOp.js");
32303
+ const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
32240
32304
  resetPostMergeState(id);
32241
32305
  resetPostMergeState(id.toUpperCase());
32242
32306
  } catch {}
@@ -32256,9 +32320,9 @@ function reopenIssueTransition(options) {
32256
32320
  const projectPath = (teamPrefix ? findProjectByTeamSync(teamPrefix) : null)?.path || "";
32257
32321
  if (projectPath) {
32258
32322
  const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
32259
- const { createBeadsFromVBrief } = await import("./beads-BRYZM_Zz.js");
32323
+ const { createBeadsFromVBrief } = await import("./beads-B4ysfx-Y.js");
32260
32324
  if (existsSync$1(workspacePath) && await Effect.runPromise(findPlan(workspacePath))) try {
32261
- 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", [
32262
32326
  "list",
32263
32327
  "--json",
32264
32328
  "-l",
@@ -32333,7 +32397,7 @@ function moveIssueStatus(options) {
32333
32397
  "done"
32334
32398
  ];
32335
32399
  if (!targetStatus || !validStatuses.includes(targetStatus)) return jsonResponse({ error: `Invalid targetStatus. Must be one of: ${validStatuses.join(", ")}` }, { status: 400 });
32336
- const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-BQXOrIFn.js"));
32400
+ const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-nDkNqpYC.js"));
32337
32401
  const issueState = {
32338
32402
  backlog: "open",
32339
32403
  todo: "open",
@@ -32396,7 +32460,7 @@ function moveIssueStatus(options) {
32396
32460
  init_issue_service_singleton();
32397
32461
  init_issue_id();
32398
32462
  init_agents$1();
32399
- const execAsync$10 = promisify(exec);
32463
+ const execAsync$9 = promisify(exec);
32400
32464
  function getIssueDataService$7() {
32401
32465
  return getSharedIssueService();
32402
32466
  }
@@ -32421,28 +32485,28 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
32421
32485
  if (projectRoot) {
32422
32486
  const workspacePath = join$1(projectRoot, "workspaces", `feature-${issueLower}`);
32423
32487
  try {
32424
- if ((await execAsync$10("git worktree list --porcelain", {
32488
+ if ((await execAsync$9("git worktree list --porcelain", {
32425
32489
  cwd: projectRoot,
32426
32490
  encoding: "utf-8"
32427
32491
  })).stdout.includes(workspacePath)) {
32428
- await execAsync$10(`git worktree remove "${workspacePath}" --force`, {
32492
+ await execAsync$9(`git worktree remove "${workspacePath}" --force`, {
32429
32493
  cwd: projectRoot,
32430
32494
  encoding: "utf-8"
32431
32495
  });
32432
32496
  cleanupLog.push(`Removed git worktree: ${workspacePath}`);
32433
32497
  } else if (existsSync$1(workspacePath)) {
32434
- await execAsync$10(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32498
+ await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32435
32499
  cleanupLog.push(`Removed directory: ${workspacePath}`);
32436
32500
  }
32437
32501
  } catch {
32438
32502
  if (existsSync$1(workspacePath)) {
32439
- await execAsync$10(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32503
+ await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
32440
32504
  cleanupLog.push(`Removed directory: ${workspacePath}`);
32441
32505
  }
32442
32506
  }
32443
32507
  const branchName = `feature/${issueLower}`;
32444
32508
  try {
32445
- await execAsync$10(`git branch -D "${branchName}" 2>/dev/null || true`, {
32509
+ await execAsync$9(`git branch -D "${branchName}" 2>/dev/null || true`, {
32446
32510
  cwd: projectRoot,
32447
32511
  encoding: "utf-8"
32448
32512
  });
@@ -32451,7 +32515,7 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
32451
32515
  }
32452
32516
  const agentDir = join$1(homedir$1(), ".overdeck", "agents", `agent-${issueLower}`);
32453
32517
  if (existsSync$1(agentDir)) {
32454
- await execAsync$10(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
32518
+ await execAsync$9(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
32455
32519
  cleanupLog.push(`Removed agent state: ${agentDir}`);
32456
32520
  }
32457
32521
  await Effect.runPromise(eventStore.append({
@@ -32560,7 +32624,7 @@ async function copySettingsToWorkspace(id) {
32560
32624
  success: false,
32561
32625
  error: "Workspace not found"
32562
32626
  }, { status: 404 });
32563
- const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-CK4yZQev.js");
32627
+ const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-BE2H8gD5.js");
32564
32628
  const result = copyOverdeckSettingsToWorkspaceSync(workspacePath);
32565
32629
  return jsonResponse({
32566
32630
  success: result.errors.length === 0 || result.copied.length > 0,
@@ -32593,7 +32657,7 @@ function generateTasksForIssue(id) {
32593
32657
  success: false,
32594
32658
  error: `No vBRIEF spec found on main for ${id} — run planning first.`
32595
32659
  }, { status: 409 });
32596
- const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-BRYZM_Zz.js"));
32660
+ const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-B4ysfx-Y.js"));
32597
32661
  const result = yield* createBeadsFromVBrief(workspacePath);
32598
32662
  if (!result.success || result.created.length === 0) {
32599
32663
  const errors = result.errors.length > 0 ? result.errors : ["Beads creation produced no tasks"];
@@ -32623,8 +32687,8 @@ init_projects();
32623
32687
  init_tracker_utils();
32624
32688
  init_tmux();
32625
32689
  init_typed_errors();
32626
- const execAsync$9 = promisify(exec);
32627
- const execFileAsync$5 = promisify(execFile);
32690
+ const execAsync$8 = promisify(exec);
32691
+ const execFileAsync$10 = promisify(execFile);
32628
32692
  function getIssueDataService$6() {
32629
32693
  return getSharedIssueService();
32630
32694
  }
@@ -32924,7 +32988,7 @@ function abortPlanningForIssue(options) {
32924
32988
  const featureWorkspacePath = join$1(projectPath, "workspaces", `feature-${issueIdentifier.toLowerCase()}`);
32925
32989
  const plainWorkspacePath = join$1(projectPath, "workspaces", issueIdentifier.toLowerCase());
32926
32990
  if (existsSync$1(existsSync$1(featureWorkspacePath) ? featureWorkspacePath : plainWorkspacePath)) {
32927
- await execFileAsync$5("pan", [
32991
+ await execFileAsync$10("pan", [
32928
32992
  "workspace",
32929
32993
  "destroy",
32930
32994
  issueIdentifier.toLowerCase(),
@@ -33033,10 +33097,10 @@ function restartFromPlan(options) {
33033
33097
  });
33034
33098
  const resetResult = yield* Effect.promise(async () => {
33035
33099
  try {
33036
- const { runGitResetHard } = await import("./dangerous-git-ops-bz-261-k.js");
33100
+ const { runGitResetHard } = await import("./dangerous-git-ops-C9iYTZ0M.js");
33037
33101
  async function findPlanningCommit(grep, label) {
33038
33102
  try {
33039
- 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`, {
33040
33104
  cwd: workspacePath,
33041
33105
  encoding: "utf-8",
33042
33106
  timeout: 1e4
@@ -33052,7 +33116,7 @@ function restartFromPlan(options) {
33052
33116
  }
33053
33117
  const found = await findPlanningCommit(`Complete planning for ${id}`, "complete-planning message") ?? await findPlanningCommit(`chore: planning artifacts for ${id}`, "agent-start message") ?? await (async () => {
33054
33118
  try {
33055
- 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`, {
33056
33120
  cwd: workspacePath,
33057
33121
  encoding: "utf-8",
33058
33122
  timeout: 1e4
@@ -33179,6 +33243,7 @@ function getPlanningState(id) {
33179
33243
  }
33180
33244
  //#endregion
33181
33245
  //#region ../../lib/overdeck/discussions.ts
33246
+ init_cost_sync();
33182
33247
  init_github_app();
33183
33248
  init_pull_requests();
33184
33249
  init_issue_id();
@@ -33186,7 +33251,7 @@ init_github_pr_lookup();
33186
33251
  init_tracker_utils();
33187
33252
  init_tracker_config();
33188
33253
  init_pr_tab_cache();
33189
- const execAsync$8 = promisify(exec);
33254
+ const execAsync$7 = promisify(exec);
33190
33255
  function isGitHubIssue$2(issueId) {
33191
33256
  const resolved = resolveGitHubIssueSync(issueId);
33192
33257
  if (resolved.isGitHub) return {
@@ -33234,7 +33299,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33234
33299
  if (githubCheck.isGitHub && githubCheck.owner && githubCheck.repo && githubCheck.number) {
33235
33300
  const { owner, repo, number } = githubCheck;
33236
33301
  try {
33237
- 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"`, {
33238
33303
  encoding: "utf-8",
33239
33304
  timeout: 15e3,
33240
33305
  maxBuffer: 8 * 1024 * 1024
@@ -33297,7 +33362,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33297
33362
  const collectedItems = [];
33298
33363
  const prConversation = (async () => {
33299
33364
  try {
33300
- 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"`, {
33301
33366
  encoding: "utf-8",
33302
33367
  timeout: 15e3,
33303
33368
  maxBuffer: 8 * 1024 * 1024
@@ -33318,7 +33383,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33318
33383
  })();
33319
33384
  const prReviews = (async () => {
33320
33385
  try {
33321
- 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"`, {
33322
33387
  encoding: "utf-8",
33323
33388
  timeout: 15e3,
33324
33389
  maxBuffer: 8 * 1024 * 1024
@@ -33343,7 +33408,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
33343
33408
  })();
33344
33409
  const prInlineComments = (async () => {
33345
33410
  try {
33346
- 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"`, {
33347
33412
  encoding: "utf-8",
33348
33413
  timeout: 15e3,
33349
33414
  maxBuffer: 8 * 1024 * 1024
@@ -33485,7 +33550,7 @@ function closeOutIssue(id) {
33485
33550
  }
33486
33551
  const closeOutResult = yield* Effect.promise(async () => {
33487
33552
  try {
33488
- const { closeOut } = await import("./lifecycle-Bu2YCv15.js");
33553
+ const { closeOut } = await import("./lifecycle-Va4AZGRx.js");
33489
33554
  return {
33490
33555
  ok: true,
33491
33556
  result: await Effect.runPromise(closeOut(ctx))
@@ -33571,7 +33636,7 @@ function bulkCloseOut(body) {
33571
33636
  const invalidIds = issueIds.filter((id) => !isValidIssueId(id));
33572
33637
  if (invalidIds.length > 0) return jsonResponse({ error: `Invalid issue ID format: ${invalidIds.join(", ")}` }, { status: 400 });
33573
33638
  const eventStore = yield* EventStoreService;
33574
- const { closeOut } = yield* Effect.promise(() => import("./lifecycle-Bu2YCv15.js"));
33639
+ const { closeOut } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
33575
33640
  const issueDataService = getIssueDataService$5();
33576
33641
  const tasks = [];
33577
33642
  const agentChecks = yield* withConcurrencyLimit(issueIds.map((id) => Effect.promise(async () => {
@@ -33738,7 +33803,7 @@ promisify(execFile);
33738
33803
  function getIssueDataService$4() {
33739
33804
  return getSharedIssueService();
33740
33805
  }
33741
- const readJsonBody$14 = Effect.gen(function* () {
33806
+ const readJsonBody$15 = Effect.gen(function* () {
33742
33807
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
33743
33808
  try {
33744
33809
  return text ? JSON.parse(text) : {};
@@ -33763,12 +33828,28 @@ const getIssueAnalyzeRoute = HttpRouter.add("GET", "/api/issues/:id/analyze", ht
33763
33828
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33764
33829
  return yield* analyzeIssue(id);
33765
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
+ })));
33766
33847
  const postIssueCloseRoute = HttpRouter.add("POST", "/api/issues/:issueId/close", httpHandler(Effect.gen(function* () {
33767
33848
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
33768
33849
  if (!parseIssueIdSync(issueId)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33769
33850
  return yield* closeIssueTransition({
33770
33851
  issueId,
33771
- body: yield* readJsonBody$14,
33852
+ body: yield* readJsonBody$15,
33772
33853
  eventStore: yield* EventStoreService
33773
33854
  });
33774
33855
  })));
@@ -33777,7 +33858,7 @@ const postIssueStartPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/star
33777
33858
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33778
33859
  return yield* startPlanningForIssue({
33779
33860
  id,
33780
- body: yield* readJsonBody$14,
33861
+ body: yield* readJsonBody$15,
33781
33862
  eventStore: yield* EventStoreService,
33782
33863
  linear: yield* LinearClient,
33783
33864
  github: yield* GitHubClient,
@@ -33790,7 +33871,7 @@ const postIssueAbortPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/abor
33790
33871
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33791
33872
  return yield* abortPlanningForIssue({
33792
33873
  id,
33793
- body: yield* readJsonBody$14,
33874
+ body: yield* readJsonBody$15,
33794
33875
  lifecycle: yield* IssueLifecycle,
33795
33876
  linear: yield* LinearClient,
33796
33877
  eventStore: yield* EventStoreService
@@ -33800,7 +33881,7 @@ const postIssueCompletePlanningRoute = HttpRouter.add("POST", "/api/issues/:id/c
33800
33881
  const request = yield* HttpServerRequest.HttpServerRequest;
33801
33882
  const id = (yield* HttpRouter.params)["id"] ?? "";
33802
33883
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33803
- const body = yield* readJsonBody$14;
33884
+ const body = yield* readJsonBody$15;
33804
33885
  const eventStore = yield* EventStoreService;
33805
33886
  const linear = yield* LinearClient;
33806
33887
  const lifecycle = yield* IssueLifecycle;
@@ -33826,7 +33907,7 @@ const postIssueResetRoute = HttpRouter.add("POST", "/api/issues/:id/reset", http
33826
33907
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33827
33908
  return yield* resetIssueTransition({
33828
33909
  id,
33829
- body: yield* readJsonBody$14,
33910
+ body: yield* readJsonBody$15,
33830
33911
  eventStore: yield* EventStoreService
33831
33912
  });
33832
33913
  })));
@@ -33835,7 +33916,7 @@ const postIssueCancelRoute = HttpRouter.add("POST", "/api/issues/:id/cancel", ht
33835
33916
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33836
33917
  return yield* cancelIssueTransition({
33837
33918
  id,
33838
- body: yield* readJsonBody$14,
33919
+ body: yield* readJsonBody$15,
33839
33920
  eventStore: yield* EventStoreService
33840
33921
  });
33841
33922
  })));
@@ -33846,7 +33927,7 @@ const postIssueReopenRoute = HttpRouter.add("POST", "/api/issues/:id/reopen", ht
33846
33927
  if (authError) return authError;
33847
33928
  return yield* reopenIssueTransition({
33848
33929
  id,
33849
- body: yield* readJsonBody$14,
33930
+ body: yield* readJsonBody$15,
33850
33931
  lifecycle: yield* IssueLifecycle,
33851
33932
  linear: yield* LinearClient,
33852
33933
  eventStore: yield* EventStoreService
@@ -33866,7 +33947,7 @@ const postIssueMoveStatusRoute = HttpRouter.add("POST", "/api/issues/:id/move-st
33866
33947
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33867
33948
  return yield* moveIssueStatus({
33868
33949
  id,
33869
- body: yield* readJsonBody$14,
33950
+ body: yield* readJsonBody$15,
33870
33951
  eventStore: yield* EventStoreService,
33871
33952
  lifecycle: yield* IssueLifecycle
33872
33953
  });
@@ -33878,7 +33959,7 @@ const postIssueCleanupWorkspaceRoute = HttpRouter.add("POST", "/api/issues/:id/c
33878
33959
  })));
33879
33960
  const postIssueDeepWipeRoute = HttpRouter.add("POST", "/api/issues/:id/deep-wipe", httpHandler(Effect.gen(function* () {
33880
33961
  const id = (yield* HttpRouter.params)["id"] ?? "";
33881
- const body = yield* readJsonBody$14;
33962
+ const body = yield* readJsonBody$15;
33882
33963
  const eventStore = yield* EventStoreService;
33883
33964
  return yield* Effect.promise(() => deepWipeIssue(id, body, eventStore));
33884
33965
  })));
@@ -33918,7 +33999,7 @@ const postIssueBeadInspectRoute = HttpRouter.add("POST", "/api/issues/:id/beads/
33918
33999
  return yield* inspectIssueBead({
33919
34000
  id: (params["id"] ?? "").toUpperCase(),
33920
34001
  beadId: params["beadId"] ?? "",
33921
- body: yield* readJsonBody$14
34002
+ body: yield* readJsonBody$15
33922
34003
  });
33923
34004
  })));
33924
34005
  const getIssuePlanningStateRoute = HttpRouter.add("GET", "/api/issues/:id/planning-state", httpHandler(Effect.gen(function* () {
@@ -33981,7 +34062,7 @@ const getIssueDiscussionsRoute = HttpRouter.add("GET", "/api/issues/:id/discussi
33981
34062
  const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHandler(Effect.gen(function* () {
33982
34063
  const id = (yield* HttpRouter.params)["id"] ?? "";
33983
34064
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
33984
- const issueData = getCostsForIssueSync(id);
34065
+ const issueData = getCostForIssueAggregateSync(id);
33985
34066
  const agents = yield* Effect.promise(() => getCachedRunningAgents());
33986
34067
  const resolvedCost = resolveIssueHeadlineCost({
33987
34068
  issueId: id,
@@ -34029,7 +34110,7 @@ const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHa
34029
34110
  cost: stats.cost,
34030
34111
  tokens: stats.tokens
34031
34112
  }])),
34032
- budget: issueData.budget,
34113
+ budget: void 0,
34033
34114
  budgetWarning: issueData.budgetWarning,
34034
34115
  lastUpdated: issueData.lastUpdated
34035
34116
  });
@@ -34040,7 +34121,7 @@ const getResourceAllocatedIssuesRoute = HttpRouter.add("GET", "/api/issues/resou
34040
34121
  const getIssueResourceDetailsRoute = HttpRouter.add("GET", "/api/issues/:id/resource-details", httpHandler(Effect.gen(function* () {
34041
34122
  return yield* getIssueResourceDetails((yield* HttpRouter.params)["id"] ?? "");
34042
34123
  })));
34043
- 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);
34044
34125
  //#endregion
34045
34126
  //#region routes/costs.ts
34046
34127
  /**
@@ -34075,7 +34156,7 @@ const getCostsSummaryRoute = HttpRouter.add("GET", "/api/costs/summary", httpHan
34075
34156
  const monthEntries = scope(readEventsSync({ startDate: monthAgo }));
34076
34157
  const summarize = (entries) => ({
34077
34158
  totalCost: entries.reduce((sum, e) => sum + (e.cost || 0), 0),
34078
- 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),
34079
34160
  entryCount: entries.length,
34080
34161
  byModel: entries.reduce((acc, e) => {
34081
34162
  if (e.model) acc[e.model] = (acc[e.model] || 0) + (e.cost || 0);
@@ -34262,16 +34343,45 @@ const postCostsSyncWalRoute = HttpRouter.add("POST", "/api/costs/sync-wal", http
34262
34343
  })
34263
34344
  });
34264
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
+ }
34265
34378
  const postCostsReconcileRoute = HttpRouter.add("POST", "/api/costs/reconcile", httpHandler(Effect.gen(function* () {
34266
- const result = yield* Effect.tryPromise({
34267
- try: () => Effect.runPromise(reconcile()),
34379
+ const overdeck = yield* Effect.tryPromise({
34380
+ try: () => runCostReconcileSources(),
34268
34381
  catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34269
34382
  });
34270
- console.log(`[reconciler] Sweep complete: ${result.eventsImported ?? 0} imported`);
34271
- return jsonResponse({
34272
- success: true,
34273
- ...result
34274
- });
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));
34275
34385
  })));
34276
34386
  const getCostsExperimentsRoute = HttpRouter.add("GET", "/api/costs/experiments", httpHandler(Effect.try({
34277
34387
  try: () => jsonResponse({ experiments: getCavemanExperimentDataSync() }),
@@ -34548,7 +34658,7 @@ function reloadDurableCloisterConfig(deps = {}) {
34548
34658
  */
34549
34659
  init_config();
34550
34660
  init_agents$1();
34551
- const readJsonBody$13 = Effect.gen(function* () {
34661
+ const readJsonBody$14 = Effect.gen(function* () {
34552
34662
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
34553
34663
  try {
34554
34664
  return text ? JSON.parse(text) : {};
@@ -34651,7 +34761,7 @@ const getCloisterConfigRoute = HttpRouter.add("GET", "/api/cloister/config", htt
34651
34761
  catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34652
34762
  })));
34653
34763
  const putCloisterConfigRoute = HttpRouter.add("PUT", "/api/cloister/config", httpHandler(Effect.gen(function* () {
34654
- const updates = yield* readJsonBody$13;
34764
+ const updates = yield* readJsonBody$14;
34655
34765
  yield* Effect.try({
34656
34766
  try: () => {
34657
34767
  saveCloisterConfigSync(updates);
@@ -34675,25 +34785,18 @@ const getCloisterAgentsHealthRoute = HttpRouter.add("GET", "/api/cloister/agents
34675
34785
  })));
34676
34786
  const cloisterRouteLayer = Layer.mergeAll(getCloisterStatusRoute, postCloisterStartRoute, postCloisterStopRoute, postCloisterEmergencyStopRoute, postCloisterBrakeRoute, postCloisterResumeSpawnsRoute, getCloisterSpawnStatusRoute, getCloisterConfigRoute, putCloisterConfigRoute, getCloisterAgentsHealthRoute);
34677
34787
  //#endregion
34678
- //#region routes/resources.ts
34679
- /**
34680
- * Resources route module — Effect HttpRouter.Layer (PAN-428 B12)
34681
- *
34682
- * Implements all /api/resources/* endpoints from the Express server:
34683
- * GET /api/resources
34684
- * GET /api/resources/:containerId/history
34685
- * GET /api/resources/:containerId/details
34686
- * DELETE /api/resources/docker/container/:id
34687
- * POST /api/resources/docker/prune-containers
34688
- * DELETE /api/resources/docker/network/:name
34689
- * DELETE /api/resources/docker/volume/:name
34690
- * POST /api/resources/docker/prune-volumes
34691
- */
34788
+ //#region routes/resources/shared.ts
34692
34789
  init_docker_stats();
34693
- init_domain_services();
34694
- init_agents$1();
34695
- init_tmux();
34696
- 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
+ }
34697
34800
  function formatUptime(startedAt) {
34698
34801
  const ms = Date.now() - new Date(startedAt).getTime();
34699
34802
  const h = Math.floor(ms / 36e5);
@@ -34702,45 +34805,933 @@ function formatUptime(startedAt) {
34702
34805
  if (h > 0) return `${h}h ${m}m`;
34703
34806
  return `${m}m`;
34704
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();
34705
35648
  /** Build the GET /api/resources response from the SQLite agents table. */
34706
35649
  function getResourcesEffect() {
34707
35650
  return Effect.gen(function* () {
34708
- const containers = [];
35651
+ const containers = enrichContainersWithLimits(getCurrentDockerStats());
35652
+ const agentStats = yield* getAgentStatsSnapshotEffect();
35653
+ const spawnGate = yield* getSpawnGatePayloadEffect();
34709
35654
  const stoppedContainers = [];
34710
35655
  const sessions = yield* listSessions().pipe(Effect.catch(() => Effect.succeed([])));
34711
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
+ };
34712
35680
  return jsonResponse({
35681
+ agents: agents.map((agent) => ({
35682
+ ...agent,
35683
+ resourceStats: agentStatsById.get(String(agent.id)) ?? null
35684
+ })),
35685
+ coreServices: getCoreServicesSnapshot(),
34713
35686
  containers,
35687
+ forecast: buildCapacityForecast(stacks, { hostVitals }),
35688
+ hostVitals,
35689
+ hostProcesses: getHostProcessesSnapshot(),
34714
35690
  stoppedContainers,
34715
35691
  networks: [],
35692
+ reclaimCandidates: reclaim.reclaimCandidates,
35693
+ reclaimThresholdBytes: reclaim.reclaimThresholdBytes,
35694
+ reclaimTotals: reclaim.reclaimTotals,
35695
+ spawnGate,
35696
+ stacks,
34716
35697
  volumes: [],
34717
- agents: listAgentStates().filter((state) => state.status !== "stopped").map((state) => ({
34718
- ...state,
34719
- hasLiveTmuxSession: tmuxSessionNames.has(state.id)
34720
- })),
34721
35698
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
34722
35699
  });
34723
35700
  });
34724
35701
  }
34725
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
+ }
34726
35711
  const getContainerHistoryRoute = HttpRouter.add("GET", "/api/resources/:containerId/history", Effect.gen(function* () {
34727
35712
  const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
34728
35713
  if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34729
- return jsonResponse({
34730
- timestamps: [],
34731
- cpuPercent: [],
34732
- memoryPercent: []
34733
- });
35714
+ return jsonResponse(getContainerHistory(containerId));
34734
35715
  }));
34735
35716
  const getContainerDetailsRoute = HttpRouter.add("GET", "/api/resources/:containerId/details", Effect.gen(function* () {
34736
35717
  const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
34737
35718
  if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34738
35719
  return yield* httpHandler(Effect.gen(function* () {
34739
35720
  const [inspectResult, logsResult] = yield* Effect.tryPromise({
34740
- 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
+ ], {
34741
35727
  encoding: "utf-8",
34742
35728
  timeout: 5e3
34743
- }).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
+ ], {
34744
35735
  encoding: "utf-8",
34745
35736
  timeout: 5e3
34746
35737
  }).catch(() => ({ stdout: "" }))]),
@@ -34779,7 +35770,7 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
34779
35770
  const eventStore = yield* EventStoreService;
34780
35771
  if (!/^[a-f0-9]{12,64}$/.test(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34781
35772
  yield* Effect.tryPromise({
34782
- try: () => execAsync$7(`docker rm "${id}" 2>&1`, {
35773
+ try: () => dockerExec("docker", ["rm", id], {
34783
35774
  encoding: "utf-8",
34784
35775
  timeout: 1e4
34785
35776
  }),
@@ -34788,90 +35779,16 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
34788
35779
  yield* eventStore.append({
34789
35780
  type: "resources.updated",
34790
35781
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34791
- payload: { resources: { containers: [] } }
35782
+ payload: { resources: { containers: getCurrentDockerStats() } }
34792
35783
  });
34793
35784
  return jsonResponse({ ok: true });
34794
35785
  })));
34795
- const postPruneContainersRoute = HttpRouter.add("POST", "/api/resources/docker/prune-containers", httpHandler(Effect.gen(function* () {
34796
- const eventStore = yield* EventStoreService;
34797
- const { stdout } = yield* Effect.tryPromise({
34798
- try: () => execAsync$7("docker container prune -f 2>&1", {
34799
- encoding: "utf-8",
34800
- timeout: 3e4
34801
- }),
34802
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34803
- });
34804
- yield* eventStore.append({
34805
- type: "resources.updated",
34806
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34807
- payload: { resources: { containers: [] } }
34808
- });
34809
- return jsonResponse({
34810
- ok: true,
34811
- output: stdout.trim()
34812
- });
34813
- })));
34814
- const deleteDockerNetworkRoute = HttpRouter.add("DELETE", "/api/resources/docker/network/:name", httpHandler(Effect.gen(function* () {
34815
- const name = (yield* HttpRouter.params)["name"] ?? "";
34816
- const eventStore = yield* EventStoreService;
34817
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid network name" }, { status: 400 });
34818
- yield* Effect.tryPromise({
34819
- try: () => execAsync$7(`docker network rm "${name}" 2>&1`, {
34820
- encoding: "utf-8",
34821
- timeout: 1e4
34822
- }),
34823
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34824
- });
34825
- yield* eventStore.append({
34826
- type: "resources.updated",
34827
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34828
- payload: { resources: { containers: [] } }
34829
- });
34830
- return jsonResponse({ ok: true });
34831
- })));
34832
- const deleteDockerVolumeRoute = HttpRouter.add("DELETE", "/api/resources/docker/volume/:name", httpHandler(Effect.gen(function* () {
34833
- const name = (yield* HttpRouter.params)["name"] ?? "";
34834
- const eventStore = yield* EventStoreService;
34835
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid volume name" }, { status: 400 });
34836
- yield* Effect.tryPromise({
34837
- try: () => execAsync$7(`docker volume rm "${name}" 2>&1`, {
34838
- encoding: "utf-8",
34839
- timeout: 1e4
34840
- }),
34841
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34842
- });
34843
- yield* eventStore.append({
34844
- type: "resources.updated",
34845
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34846
- payload: { resources: { containers: [] } }
34847
- });
34848
- return jsonResponse({ ok: true });
34849
- })));
34850
- const postPruneVolumesRoute = HttpRouter.add("POST", "/api/resources/docker/prune-volumes", httpHandler(Effect.gen(function* () {
34851
- const eventStore = yield* EventStoreService;
34852
- const { stdout } = yield* Effect.tryPromise({
34853
- try: () => execAsync$7("docker volume prune -f 2>&1", {
34854
- encoding: "utf-8",
34855
- timeout: 3e4
34856
- }),
34857
- catch: (err) => new Error(err instanceof Error ? err.message : String(err))
34858
- });
34859
- yield* eventStore.append({
34860
- type: "resources.updated",
34861
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34862
- payload: { resources: { containers: [] } }
34863
- });
34864
- return jsonResponse({
34865
- ok: true,
34866
- output: stdout.trim()
34867
- });
34868
- })));
34869
35786
  const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/restart", httpHandler(Effect.gen(function* () {
34870
35787
  const id = (yield* HttpRouter.params)["id"] ?? "";
34871
35788
  const eventStore = yield* EventStoreService;
34872
- if (!id) return jsonResponse({ error: "Container ID required" }, { status: 400 });
35789
+ if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34873
35790
  const { stdout } = yield* Effect.tryPromise({
34874
- try: () => execAsync$7(`docker restart "${id}"`, {
35791
+ try: () => dockerExec("docker", ["restart", id], {
34875
35792
  encoding: "utf-8",
34876
35793
  timeout: 3e4
34877
35794
  }),
@@ -34880,7 +35797,7 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
34880
35797
  yield* eventStore.append({
34881
35798
  type: "resources.updated",
34882
35799
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34883
- payload: { resources: { containers: [] } }
35800
+ payload: { resources: { containers: getCurrentDockerStats() } }
34884
35801
  });
34885
35802
  return jsonResponse({
34886
35803
  ok: true,
@@ -34891,9 +35808,9 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
34891
35808
  const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/start", httpHandler(Effect.gen(function* () {
34892
35809
  const id = (yield* HttpRouter.params)["id"] ?? "";
34893
35810
  const eventStore = yield* EventStoreService;
34894
- if (!id) return jsonResponse({ error: "Container ID required" }, { status: 400 });
35811
+ if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34895
35812
  const { stdout } = yield* Effect.tryPromise({
34896
- try: () => execAsync$7(`docker start "${id}"`, {
35813
+ try: () => dockerExec("docker", ["start", id], {
34897
35814
  encoding: "utf-8",
34898
35815
  timeout: 3e4
34899
35816
  }),
@@ -34902,7 +35819,7 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
34902
35819
  yield* eventStore.append({
34903
35820
  type: "resources.updated",
34904
35821
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34905
- payload: { resources: { containers: [] } }
35822
+ payload: { resources: { containers: getCurrentDockerStats() } }
34906
35823
  });
34907
35824
  return jsonResponse({
34908
35825
  ok: true,
@@ -34912,9 +35829,15 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
34912
35829
  })));
34913
35830
  const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/container/:id/logs", httpHandler(Effect.gen(function* () {
34914
35831
  const id = (yield* HttpRouter.params)["id"] ?? "";
34915
- if (!id) return jsonResponse({ error: "Container ID required" }, { status: 400 });
35832
+ if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
34916
35833
  const { stdout } = yield* Effect.tryPromise({
34917
- try: () => execAsync$7(`docker logs --tail 200 --timestamps "${id}"`, {
35834
+ try: () => dockerExec("docker", [
35835
+ "logs",
35836
+ "--tail",
35837
+ "200",
35838
+ "--timestamps",
35839
+ id
35840
+ ], {
34918
35841
  encoding: "utf-8",
34919
35842
  timeout: 1e4
34920
35843
  }),
@@ -34922,7 +35845,516 @@ const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/conta
34922
35845
  });
34923
35846
  return jsonResponse({ logs: stdout });
34924
35847
  })));
34925
- 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);
34926
36358
  //#endregion
34927
36359
  //#region services/model-origin-enrich.ts
34928
36360
  init_agents$1();
@@ -36919,7 +38351,7 @@ Be specific: reference actual file names, function names, requirement text, disc
36919
38351
  const { command: cliCmd, args: cliArgs } = getAgentCommandSync(statusModelId);
36920
38352
  const modelFlag = cliArgs.length > 0 ? ` ${cliArgs.join(" ")}` : "";
36921
38353
  const promptFile = join$1(planningDir, ".status-review-prompt.tmp");
36922
- const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-B0dqjvFa.js");
38354
+ const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-Djkuu1CD.js");
36923
38355
  let providerEnvStr = "";
36924
38356
  let providerEnv = {};
36925
38357
  const statusProvider = getProviderForModelSync(statusModelId);
@@ -37002,7 +38434,7 @@ const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/
37002
38434
  let safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "-");
37003
38435
  let processedContent = content;
37004
38436
  if (safeName.endsWith(".vtt")) {
37005
- const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-DxyN_zxJ.js"));
38437
+ const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-d-QMKZn4.js"));
37006
38438
  processedContent = vttToMarkdown(content);
37007
38439
  safeName = safeName.replace(/\.vtt$/, ".md");
37008
38440
  }
@@ -42340,73 +43772,6 @@ const resetRoute = HttpRouter.add("POST", "/api/autopreso/session/reset", httpHa
42340
43772
  })));
42341
43773
  const autopresoRouteLayer = Layer.mergeAll(startRoute, backToStagingRoute, resetRoute);
42342
43774
  //#endregion
42343
- //#region services/event-loop-monitor.ts
42344
- init_activity_logger();
42345
- const EVENT_LOOP_MONITOR_WINDOW_MS = 6e4;
42346
- let lastSample = {
42347
- p50: 0,
42348
- p99: 0,
42349
- max: 0,
42350
- unit: "ms",
42351
- sampledAt: null,
42352
- windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
42353
- };
42354
- let timer$3 = null;
42355
- let activeHistogram = null;
42356
- function nsToRoundedMs(ns) {
42357
- if (!Number.isFinite(ns) || ns <= 0) return 0;
42358
- return Math.round(ns / 1e6 * 100) / 100;
42359
- }
42360
- function sampleEventLoopDelay(histogram, deps = {}) {
42361
- const warnThresholdMs = deps.warnThresholdMs ?? 100;
42362
- const now = deps.now ?? (() => /* @__PURE__ */ new Date());
42363
- const warn = deps.warn ?? ((message) => console.warn(message));
42364
- const emit = deps.emit ?? emitActivityEntrySync;
42365
- const sample = {
42366
- p50: nsToRoundedMs(histogram.percentile(50)),
42367
- p99: nsToRoundedMs(histogram.percentile(99)),
42368
- max: nsToRoundedMs(histogram.max),
42369
- unit: "ms",
42370
- sampledAt: now().toISOString(),
42371
- windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
42372
- };
42373
- lastSample = sample;
42374
- if (sample.p99 > warnThresholdMs) {
42375
- const message = `Dashboard event loop p99 delay ${sample.p99}ms exceeded ${warnThresholdMs}ms`;
42376
- warn(`[event-loop-monitor] ${message}`);
42377
- emit({
42378
- source: "dashboard",
42379
- level: "warn",
42380
- message,
42381
- details: `p50=${sample.p50}ms p99=${sample.p99}ms max=${sample.max}ms over ${sample.windowMs}ms`
42382
- });
42383
- }
42384
- histogram.reset();
42385
- return sample;
42386
- }
42387
- function getEventLoopDelaySample() {
42388
- return lastSample;
42389
- }
42390
- function startEventLoopMonitor(deps = {}) {
42391
- if (timer$3) return;
42392
- const intervalMs = deps.intervalMs ?? 6e4;
42393
- activeHistogram = (deps.createHistogram ?? (() => monitorEventLoopDelay({ resolution: 20 })))();
42394
- activeHistogram.enable();
42395
- timer$3 = setInterval(() => {
42396
- if (!activeHistogram) return;
42397
- sampleEventLoopDelay(activeHistogram, deps);
42398
- }, intervalMs);
42399
- timer$3.unref?.();
42400
- }
42401
- function stopEventLoopMonitor() {
42402
- if (timer$3) {
42403
- clearInterval(timer$3);
42404
- timer$3 = null;
42405
- }
42406
- activeHistogram?.disable();
42407
- activeHistogram = null;
42408
- }
42409
- //#endregion
42410
43775
  //#region routes/metrics.ts
42411
43776
  /**
42412
43777
  * Metrics route module — Effect HttpRouter.Layer (PAN-428 B16)
@@ -42880,7 +44245,7 @@ const postRallyValidateRoute = HttpRouter.add("POST", "/api/rally/validate", Eff
42880
44245
  }, { status: 400 });
42881
44246
  return yield* Effect.promise(async () => {
42882
44247
  try {
42883
- const { RallyRestApi } = await import("./rally-api-ukAD0De8.js");
44248
+ const { RallyRestApi } = await import("./rally-api-DpFjC_KQ.js");
42884
44249
  const api = new RallyRestApi({
42885
44250
  apiKey,
42886
44251
  server: server || "https://rally1.rallydev.com"
@@ -43208,7 +44573,7 @@ const getPlanningStatusRoute = HttpRouter.add("GET", "/api/planning/:issueId/sta
43208
44573
  const remoteState = loadRemoteAgentState(sessionName);
43209
44574
  const isRemote = !!remoteState;
43210
44575
  const vmName = remoteState?.vmName ?? "";
43211
- const { getAgentStateSync } = await import("./agents-Bi9Y6h5B.js");
44576
+ const { getAgentStateSync } = await import("./agents-Ea9gfdKS.js");
43212
44577
  const agentStarting = getAgentStateSync(sessionName)?.status === "starting";
43213
44578
  let tmuxSessionAlive = false;
43214
44579
  if (!isRemote) try {
@@ -43351,7 +44716,7 @@ Continue the PLANNING session. Do NOT implement anything.
43351
44716
  `);
43352
44717
  const agentCwd = workspacePath;
43353
44718
  if (existsSync$1(outputFile)) await rename(outputFile, join$1(planningDir, `output-${Date.now()}.jsonl`));
43354
- const { getAgentCommandSync } = await import("./settings-DPQ5nEA-.js");
44719
+ const { getAgentCommandSync } = await import("./settings-DXeuoifi.js");
43355
44720
  let msgPlanningModel = "claude-sonnet-5";
43356
44721
  try {
43357
44722
  msgPlanningModel = resolveModel("plan", void 0, loadConfigSync().config);
@@ -43472,7 +44837,7 @@ async function getIndexStats(rootPath, isMain) {
43472
44837
  }
43473
44838
  const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Effect.promise(async () => {
43474
44839
  try {
43475
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-LV_URGSC.js");
44840
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43476
44841
  const projectRoot = process.cwd();
43477
44842
  const venvPath = join$1(projectRoot, ".venv");
43478
44843
  const results = [];
@@ -43517,7 +44882,7 @@ const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Ef
43517
44882
  }));
43518
44883
  const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Effect.promise(async () => {
43519
44884
  try {
43520
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-LV_URGSC.js");
44885
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43521
44886
  const projectRoot = process.cwd();
43522
44887
  const venvPath = join$1(projectRoot, ".venv");
43523
44888
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -43534,7 +44899,7 @@ const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Ef
43534
44899
  }));
43535
44900
  const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effect.promise(async () => {
43536
44901
  try {
43537
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-LV_URGSC.js");
44902
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43538
44903
  const projectRoot = process.cwd();
43539
44904
  const venvPath = join$1(projectRoot, ".venv");
43540
44905
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -43551,7 +44916,7 @@ const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effe
43551
44916
  }));
43552
44917
  const postTldrReloadRoute = HttpRouter.add("POST", "/api/services/tldr/reload", Effect.promise(async () => {
43553
44918
  try {
43554
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-LV_URGSC.js");
44919
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
43555
44920
  const enabled = isTldrEnabledSync();
43556
44921
  const projectRoot = process.cwd();
43557
44922
  const targets = [];
@@ -43627,7 +44992,7 @@ const getVersionRoute = HttpRouter.add("GET", "/api/version", Effect.promise(asy
43627
44992
  const version = await getOverdeckVersion();
43628
44993
  let supervisorUrl = null;
43629
44994
  try {
43630
- const { getSupervisorUrlSync } = await import("./supervisor-DyNL2rTl.js");
44995
+ const { getSupervisorUrlSync } = await import("./supervisor-BY_xA7eS.js");
43631
44996
  supervisorUrl = getSupervisorUrlSync();
43632
44997
  } catch {}
43633
44998
  return jsonResponse({
@@ -43791,7 +45156,7 @@ const postShadowMonitorRoute = HttpRouter.add("POST", "/api/shadow/:issueId/moni
43791
45156
  const projectPath = await getProjectPath(issuePrefix);
43792
45157
  const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
43793
45158
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
43794
- const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-CStyQ6yp.js");
45159
+ const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-DMC2pBPa.js");
43795
45160
  const config = {
43796
45161
  issueId,
43797
45162
  workspacePath,
@@ -43821,7 +45186,7 @@ const postShadowObserveRoute = HttpRouter.add("POST", "/api/shadow/:issueId/obse
43821
45186
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
43822
45187
  const ghConfig = getGitHubConfig();
43823
45188
  if (!ghConfig) return jsonResponse({ error: "GitHub not configured - Observer requires GitHub" }, { status: 400 });
43824
- const { runObserverCycle } = await import("./shadow-engineering-CStyQ6yp.js");
45189
+ const { runObserverCycle } = await import("./shadow-engineering-DMC2pBPa.js");
43825
45190
  const firstRepo = ghConfig.repos[0];
43826
45191
  const config = {
43827
45192
  issueId,
@@ -44886,7 +46251,7 @@ async function cleanupConversationAttachments(name) {
44886
46251
  * sessions that the lifecycle poller missed. */
44887
46252
  async function cleanupOrphanedConversationAttachments() {
44888
46253
  try {
44889
- const { listConversations } = await import("./conversations-DYz9OyMT.js");
46254
+ const { listConversations } = await import("./conversations-DSOZG_Sz.js");
44890
46255
  const convNames = new Set(listConversations().map((c) => c.name));
44891
46256
  const root = getConversationAttachmentsRoot();
44892
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, {
@@ -45754,7 +47119,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
45754
47119
  const codexApprovalPolicy = codexPermMode === "full-access" ? "never" : "on-request";
45755
47120
  const codexSandboxMode = codexPermMode === "full-access" ? "danger-full-access" : codexPermMode === "read-only" ? "read-only" : "workspace-write";
45756
47121
  const codexApprovalsReviewer = codexPermMode === "auto-review" ? "auto_review" : void 0;
45757
- const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-RRg-7JsH.js");
47122
+ const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-BObh4thD.js");
45758
47123
  initCodexHome(codexHome, {
45759
47124
  trustedDir: cwd,
45760
47125
  approvalPolicy: codexApprovalPolicy,
@@ -45824,7 +47189,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
45824
47189
  } catch {}
45825
47190
  console.log(`[claude-invoke] purpose=conversation-session | model=${model || "default"} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command="${runtimeCommand}"`);
45826
47191
  try {
45827
- const { preTrustDirectory } = await import("./workspace-manager-CK4yZQev.js");
47192
+ const { preTrustDirectory } = await import("./workspace-manager-BE2H8gD5.js");
45828
47193
  preTrustDirectory(cwd);
45829
47194
  } catch {}
45830
47195
  try {
@@ -45841,7 +47206,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
45841
47206
  const codexHomeDir = codexFields.codexHome;
45842
47207
  (async () => {
45843
47208
  try {
45844
- const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-RRg-7JsH.js");
47209
+ const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-BObh4thD.js");
45845
47210
  const rollout = await waitForCodexRollout(codexHomeDir, 12e4);
45846
47211
  if (rollout) {
45847
47212
  const threadId = extractThreadIdFromRollout(rollout);
@@ -46767,6 +48132,19 @@ async function resolveSessionFile(conv) {
46767
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.`);
46768
48133
  return null;
46769
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
+ }
46770
48148
  const MESSAGES_CACHE_MAX = 100;
46771
48149
  const messagesCache = /* @__PURE__ */ new Map();
46772
48150
  function isCodexSessionFile(sessionFile) {
@@ -46935,7 +48313,8 @@ async function getConversationRead(rawId, deps) {
46935
48313
  isWorktree: gitInfo.isWorktree,
46936
48314
  pendingInputCount,
46937
48315
  pendingInputKinds,
46938
- pendingAskUserQuestion
48316
+ pendingAskUserQuestion,
48317
+ transcriptMissing: conversationTranscriptMissing(conv, sessionAlive, convSf)
46939
48318
  });
46940
48319
  } catch (error) {
46941
48320
  const msg = error instanceof Error ? error.message : String(error);
@@ -47444,7 +48823,8 @@ async function enrichConversationList(limit, offset) {
47444
48823
  isWorktree: gitInfo.isWorktree,
47445
48824
  pendingInputCount,
47446
48825
  pendingInputKinds,
47447
- pendingAskUserQuestion
48826
+ pendingAskUserQuestion,
48827
+ transcriptMissing: conversationTranscriptMissing(row, sessionAlive, convSf)
47448
48828
  };
47449
48829
  })), CONVERSATION_LIST_ENRICHMENT_CONCURRENCY));
47450
48830
  }
@@ -48910,7 +50290,7 @@ async function registerProjectFromPath(opts) {
48910
50290
  registerProjectSync(key, projectConfig);
48911
50291
  const seededContextLayer = ensureProjectLayer(fullPath);
48912
50292
  try {
48913
- const { preTrustDirectorySync } = await import("./workspace-manager-CK4yZQev.js");
50293
+ const { preTrustDirectorySync } = await import("./workspace-manager-BE2H8gD5.js");
48914
50294
  preTrustDirectorySync(fullPath);
48915
50295
  } catch {}
48916
50296
  let hooksInstalled = 0;
@@ -48946,7 +50326,7 @@ init_pan_dir();
48946
50326
  init_paths();
48947
50327
  const execAsync$3 = promisify(exec);
48948
50328
  async function getIssueDataService() {
48949
- const { getSharedIssueService } = await import("./issue-service-singleton-BSgap-ST.js");
50329
+ const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
48950
50330
  return getSharedIssueService();
48951
50331
  }
48952
50332
  /** Returns true if the path exists (any type). */
@@ -50873,7 +52253,7 @@ async function refreshMergeStateFromGitHub(issueId, repo, prNumber) {
50873
52253
  let mergeState;
50874
52254
  let isDraft;
50875
52255
  let checksFailed;
50876
- const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-BG36jYzm.js");
52256
+ const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-Dg25z05Z.js");
50877
52257
  if (isGitHubAppConfigured()) {
50878
52258
  const prState = await Effect.runPromise(getPullRequestState(owner, repoName, prNumber));
50879
52259
  mergeable = prState.mergeable === null ? "UNKNOWN" : prState.mergeable ? "MERGEABLE" : "CONFLICTING";
@@ -51022,8 +52402,8 @@ async function handlePullRequestPromise(payload) {
51022
52402
  bumpIssuePrTabCacheGeneration(issueId);
51023
52403
  const repo = payload.repository.full_name;
51024
52404
  if (payload.action === "closed" && pr.merged === true) try {
51025
- const { postMergeLifecycle } = await import("./merge-agent-CvVDCFOp.js");
51026
- const { resolveProjectFromIssueSync } = await import("./projects-CKnFqWB0.js");
52405
+ const { postMergeLifecycle } = await import("./merge-agent-DZaRBHQA.js");
52406
+ const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
51027
52407
  const project = resolveProjectFromIssueSync(issueId);
51028
52408
  if (project) {
51029
52409
  const branchName = pr.head.ref;
@@ -51522,7 +52902,7 @@ function getCheckpointWorker() {
51522
52902
  return worker;
51523
52903
  }
51524
52904
  async function runInline(operation, payload) {
51525
- const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-DMO68jIJ.js");
52905
+ const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-auGbx5b0.js");
51526
52906
  switch (operation) {
51527
52907
  case "claimTranscriptRange": return claimTranscriptRange(payload);
51528
52908
  case "commitTranscriptRange": return commitTranscriptRange(payload);
@@ -52676,7 +54056,7 @@ function buildPendingTurn(input, fromOffset, toOffset, compressed) {
52676
54056
  };
52677
54057
  }
52678
54058
  async function emitMemoryObservationCreated(observation, timestamp) {
52679
- const { initEventStore } = await import("./event-store-BQRWQeyx.js");
54059
+ const { initEventStore } = await import("./event-store-D7_dSgP8.js");
52680
54060
  await (await initEventStore()).appendAsync({
52681
54061
  type: "memory.observation_created",
52682
54062
  timestamp,
@@ -55715,7 +57095,7 @@ async function getFlywheelConversationPayload() {
55715
57095
  };
55716
57096
  }
55717
57097
  async function postFlywheelReportPayload() {
55718
- const { flywheelReportCommand } = await import("./flywheel-iZNbJfS_.js");
57098
+ const { flywheelReportCommand } = await import("./flywheel-CFPOlOUd.js");
55719
57099
  await flywheelReportCommand({ cwd: process.cwd() });
55720
57100
  return {
55721
57101
  status: 200,
@@ -55873,11 +57253,11 @@ const getMergeBlockersRoute = HttpRouter.add("GET", "/api/flywheel/merge-blocker
55873
57253
  async function defaultGetOrderedIssueIds() {
55874
57254
  const status = await readCurrentFlywheelStatusForDashboard();
55875
57255
  if (!status) return [];
55876
- const { computeMergeQueue } = await import("./flywheel-merge-order-PW8nIENP.js");
57256
+ const { computeMergeQueue } = await import("./flywheel-merge-order-FN6zqVXG.js");
55877
57257
  return (await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd()).pipe(Effect.provide(layer)))).map((i) => i.issueId);
55878
57258
  }
55879
57259
  async function defaultMergeOne(issueId) {
55880
- const { triggerMerge } = await import("./merge-ops-iaG5kd-C.js");
57260
+ const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
55881
57261
  const r = await triggerMerge(issueId);
55882
57262
  return r.success ? { ok: true } : {
55883
57263
  ok: false,
@@ -55897,7 +57277,7 @@ async function postFlywheelMergeNextPayload(payload, deps = {}) {
55897
57277
  body: { error: "n must be a positive integer" }
55898
57278
  };
55899
57279
  const issueIds = (await (deps.getOrderedIssueIds ?? defaultGetOrderedIssueIds)()).slice(0, n);
55900
- const { shipMergeBatch } = await import("./merge-batch-UhBPrssc.js");
57280
+ const { shipMergeBatch } = await import("./merge-batch-BbG78hx-.js");
55901
57281
  return {
55902
57282
  status: 200,
55903
57283
  body: { outcomes: await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne }) }
@@ -56054,7 +57434,7 @@ const getFlywheelMergeQueueRoute = HttpRouter.add("GET", "/api/flywheel/merge-qu
56054
57434
  return yield* Effect.promise(async () => {
56055
57435
  const status = await readCurrentFlywheelStatusForDashboard();
56056
57436
  if (!status) return jsonResponse([]);
56057
- const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-PW8nIENP.js");
57437
+ const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-FN6zqVXG.js");
56058
57438
  return jsonResponse(await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd(), { getPrUrl: resolveMergeQueuePrUrl }).pipe(Effect.provide(layer))));
56059
57439
  });
56060
57440
  })));
@@ -56068,18 +57448,18 @@ function uatGenerationNameFromParam(param) {
56068
57448
  return decoded.startsWith("uat/") ? decoded : `uat/${decoded}`;
56069
57449
  }
56070
57450
  const getUatGenerationsRoute = HttpRouter.add("GET", "/api/flywheel/uat-generations", httpHandler(Effect.gen(function* () {
56071
- const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-CwAd0sZ1.js"));
57451
+ const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56072
57452
  return jsonResponse(yield* Effect.promise(() => getUatGenerationsPayload()));
56073
57453
  })));
56074
57454
  const getUatCandidateRoute = HttpRouter.add("GET", "/api/flywheel/uat-candidate", httpHandler(Effect.gen(function* () {
56075
- const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-CwAd0sZ1.js"));
57455
+ const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56076
57456
  return jsonResponse(yield* Effect.promise(() => getUatCandidatePayload()));
56077
57457
  })));
56078
57458
  const postUatGenerationStackRoute = HttpRouter.add("POST", "/api/flywheel/uat-generations/:name/stack", httpHandler(Effect.gen(function* () {
56079
57459
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
56080
57460
  if (authError) return authError;
56081
57461
  const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
56082
- const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-CwAd0sZ1.js"));
57462
+ const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56083
57463
  const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
56084
57464
  if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
56085
57465
  return jsonResponse({
@@ -56091,8 +57471,8 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
56091
57471
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
56092
57472
  if (authError) return authError;
56093
57473
  const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
56094
- const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-CwAd0sZ1.js"));
56095
- const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-uq-O3uxF.js"));
57474
+ const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
57475
+ const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-B6m2H7J6.js"));
56096
57476
  const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle));
56097
57477
  if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
56098
57478
  return jsonResponse(result);
@@ -56105,7 +57485,7 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
56105
57485
  const postFlywheelAssembleUatRoute = HttpRouter.add("POST", "/api/flywheel/assemble-uat", httpHandler(Effect.gen(function* () {
56106
57486
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
56107
57487
  if (authError) return authError;
56108
- const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-CwAd0sZ1.js"));
57488
+ const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
56109
57489
  return jsonResponse(yield* Effect.promise(() => runUatTrainReconcile({ force: true })));
56110
57490
  })));
56111
57491
  const getFlywheelStateRoute = HttpRouter.add("GET", "/api/flywheel/state", httpHandler(Effect.gen(function* () {
@@ -59621,7 +61001,7 @@ async function pollOnce(state) {
59621
61001
  try {
59622
61002
  const remoteState = await readFile(join$1(homedir$1(), ".overdeck", "agents", agentId, "remote-state.json"), "utf-8").then((text) => JSON.parse(text)).catch(() => null);
59623
61003
  if (remoteState?.location === "remote" && remoteState?.vmName) {
59624
- const { getRemoteAgentOutput } = await import("./remote-agents-tBpE_KXg.js");
61004
+ const { getRemoteAgentOutput } = await import("./remote-agents-BXKV5iBS.js");
59625
61005
  stdout = await getRemoteAgentOutput(agentId, remoteState.vmName, 50);
59626
61006
  } else stdout = await Effect.runPromise(capturePane(agentId, 50));
59627
61007
  } catch {
@@ -60804,7 +62184,7 @@ const RESTART_MARKER = join(homedir(), ".overdeck", "dashboard-restarting.json")
60804
62184
  * loads new content-hashed chunk filenames (no ERR_MODULE_NOT_FOUND after rebuild).
60805
62185
  */
60806
62186
  async function defaultLifecycleRunner(pending) {
60807
- const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-CvVDCFOp.js");
62187
+ const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-DZaRBHQA.js");
60808
62188
  await postMergeLifecycle(pending.issueId, pending.projectPath, pending.sourceBranch, { skipDeploy: true });
60809
62189
  if (pending.sourceBranch) await notifyTldrDaemon(pending.projectPath, pending.sourceBranch);
60810
62190
  }
@@ -61125,7 +62505,7 @@ function failureReason(result) {
61125
62505
  return result.error ?? result.message ?? `merge returned status ${result.statusCode ?? "unknown"}`;
61126
62506
  }
61127
62507
  async function defaultMergeIssue(issueId) {
61128
- const { triggerMerge } = await import("./merge-ops-iaG5kd-C.js");
62508
+ const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
61129
62509
  return triggerMerge(issueId);
61130
62510
  }
61131
62511
  function defaultAnnounceFailure(issueId, reason) {
@@ -61229,6 +62609,34 @@ async function warnIfAutonomousMergeBackendUnavailable(deps = {}) {
61229
62609
  }
61230
62610
  }
61231
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
61232
62640
  //#region services/cost-reconcile-service.ts
61233
62641
  const RECONCILE_INTERVAL_MS = 5 * 6e4;
61234
62642
  let timer = null;
@@ -61239,6 +62647,14 @@ async function runCostReconcileOnce(reason) {
61239
62647
  const result = await reconcilePiTranscripts();
61240
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)`);
61241
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
+ }
61242
62658
  })().finally(() => {
61243
62659
  inFlight = null;
61244
62660
  });
@@ -61264,6 +62680,7 @@ function stopCostReconcileService() {
61264
62680
  }
61265
62681
  //#endregion
61266
62682
  //#region ../../../scripts/create-overdeck-db.ts
62683
+ init_boot_gates();
61267
62684
  init_driver();
61268
62685
  function migrationStatements(migrationPath) {
61269
62686
  return readFileSync$1(migrationPath, "utf8").split("--> statement-breakpoint").map((statement) => statement.trim()).filter(Boolean);
@@ -61330,6 +62747,7 @@ init_paths();
61330
62747
  init_tmux();
61331
62748
  init_fts_db();
61332
62749
  init_paths$1();
62750
+ init_auto_commit();
61333
62751
  initDashboardLogFile();
61334
62752
  console.log(`[boot-timing] module graph loaded at +${Math.round(performance.now())}ms (since process start)`);
61335
62753
  console.log(`[overdeck] Boot gates: ${formatBootGateState(resolveBootGates())}`);
@@ -61338,6 +62756,7 @@ console.log("[overdeck] Event loop delay monitor started");
61338
62756
  await mkdir(getOverdeckHome(), { recursive: true });
61339
62757
  ensureInternalTokenSync();
61340
62758
  warnIfAutonomousMergeBackendUnavailable();
62759
+ warnIfAppCannotMerge();
61341
62760
  await ensureManagedTmuxContextOnce();
61342
62761
  await initTrackerConfigCache().catch((err) => {
61343
62762
  console.log("[tracker-config] Warning: failed to cache .overdeck.env:", err.message);
@@ -61674,6 +63093,7 @@ const handleShutdownSignal = async (signal) => {
61674
63093
  stopCostReconcileService();
61675
63094
  stopRestartAnnouncer();
61676
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));
61677
63097
  await stopConversationSearchWatcher().catch((err) => console.warn("[conversation-search] watcher shutdown failed:", err));
61678
63098
  closeConversationSearchService();
61679
63099
  closeMemoryFtsDatabases();
@@ -61722,7 +63142,7 @@ reconcileStaleGitHubBlockers().then((n) => {
61722
63142
  }
61723
63143
  }).catch((err) => console.warn(`[overdeck] Startup blocker reconciliation failed: ${err.message}`));
61724
63144
  try {
61725
- const { resetProcessingToQueued } = await import("./merge-sync-Dz7-euH2.js");
63145
+ const { resetProcessingToQueued } = await import("./merge-sync-D3wPN2-T.js");
61726
63146
  const resetCount = resetProcessingToQueued();
61727
63147
  if (resetCount > 0) {
61728
63148
  console.log(`[overdeck] Reset ${resetCount} stuck merge queue entries to queued`);