@overdeck/desktop 0.30.2 → 0.40.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 (21) hide show
  1. package/package.json +1 -1
  2. package/server/public/assets/{chunk-BO2N2NFS-D647WvzH.js → chunk-BO2N2NFS-CW1ZdFdY.js} +4 -4
  3. package/server/public/assets/{diagram-OG6HWLK6-D94iJ8UD.js → diagram-OG6HWLK6-Cw_BgPPR.js} +1 -1
  4. package/server/public/assets/{dist-D-Is2vOq.js → dist-913yenhq.js} +1 -1
  5. package/server/public/assets/{dist-DJs-blp3.js → dist-CGMAxsiA.js} +3 -3
  6. package/server/public/assets/{ganttDiagram-6RSMTGT7-BCO0lQgo.js → ganttDiagram-6RSMTGT7-CUPZhXKC.js} +1 -1
  7. package/server/public/assets/{highlighted-body-OFNGDK62-Hms9Zx_K.js → highlighted-body-OFNGDK62-PZCZRTma.js} +1 -1
  8. package/server/public/assets/index-2sJIy1Mo.js +805 -0
  9. package/server/public/assets/index-D_duUQjN.css +1 -0
  10. package/server/public/assets/{linear-X3dJyawe.js → linear-CaWsupFC.js} +1 -1
  11. package/server/public/assets/mermaid-GHXKKRXX-CD7SbVD6.js +1 -0
  12. package/server/public/assets/{pieDiagram-4H26LBE5-BYZIa_Gs.js → pieDiagram-4H26LBE5-BhU2fIWh.js} +1 -1
  13. package/server/public/assets/{prod-De0nXELP.js → prod-Dk-iWRhQ.js} +4 -4
  14. package/server/public/assets/{quadrantDiagram-W4KKPZXB-CWvcQZvc.js → quadrantDiagram-W4KKPZXB-DGOsJ_nS.js} +1 -1
  15. package/server/public/assets/{sankeyDiagram-5OEKKPKP-CXmvKnWY.js → sankeyDiagram-5OEKKPKP-CzirInk6.js} +1 -1
  16. package/server/public/assets/{xychartDiagram-2RQKCTM6-BypSwt-2.js → xychartDiagram-2RQKCTM6-CG8FHgaF.js} +1 -1
  17. package/server/public/index.html +2 -2
  18. package/server/server.js +818 -1024
  19. package/server/public/assets/index-7_dXwqNm.css +0 -1
  20. package/server/public/assets/index-DnfNk7ij.js +0 -801
  21. package/server/public/assets/mermaid-GHXKKRXX-D7b6S76i.js +0 -1
package/server/server.js CHANGED
@@ -1,93 +1,90 @@
1
- import { a as __toCommonJS, i as __require, o as __toESM, t as __commonJSMin } from "./chunk-HkwdBwDg.js";
2
- import { C as isDevMode, S as init_paths, T as sessionFilePath, _ as SYNC_SOURCES, l as LOGS_DIR, o as CLAUDE_DIR, t as AGENTS_DIR, u as OVERDECK_HOME, w as packageRoot, x as getOverdeckHome, y as encodeClaudeProjectDir } from "./paths-wLUq63xa.js";
3
- import { a as FsError, d as ProcessSpawnError, h as init_errors, p as TrackerError, s as GitHubApiError } from "./errors-LhRGtRIA.js";
4
- import { A as jsonResponse, C as isTrustedOriginForHost, D as checkCodexAuthStatus, E as validateOriginHeaders, O as httpHandler, T as validateOrigin$1, _ as scanPendingInputsPromise, a as saveAgentStateAndEmitEventProgram, b as parseCodexApprovalPrompt, c as getSystemHealthSnapshot, d as getRuntimeForAgent, f as getAgentHealth, g as getAgentWorkspace, h as getAgentJsonlMtime, i as saveAgentStateAndEmitEvent, p as computeAgentEnrichment, s as resolveConversationGitInfo, t as agentsRouteLayer, u as getCloisterService, v as detectAwaitingInputForAgent, x as getHeaderFromMap, y as detectAwaitingInputFromPaneSync } from "./agents-CRTpkAgz.js";
5
- import { a as init_activity_logger, i as emitDashboardLifecycleSync, n as emitActivityEntrySync, r as emitActivityTtsSync } from "./activity-logger-U396zbXm.js";
6
- import { A as getOverdeckDatabasePath, d as getOverdeckDatabaseSync, f as init_infra, i as DbLive, j as init_paths$1, k as OVERDECK_MIGRATION_PATH, u as TmuxLive } from "./infra-DkNKs3ZE.js";
7
- import { n as openDatabase, t as init_driver } from "./driver-CmBD8_I7.js";
8
- import { t as require_dist$1 } from "./dist-CChAhNVv.js";
9
- import { a as parseIssueIdSync, n as extractPrefixSync, r as init_issue_id } from "./issue-id-ChfDs-cT.js";
10
- import { E as registerProjectSync, N as setProjectAutoMergeDefaultSync, O as resolveProjectFromIssue, _ as init_projects, b as listProjects, d as getProjectSync, k as resolveProjectFromIssueSync, l as getIssuePrefix, r as extractTeamPrefix, s as findProjectByTeamSync, x as listProjectsSync } from "./projects-Cowx0oPu.js";
11
- import { A as mergeTtsDaemonConfigs, C as isTldrEnabledSync, D as mergeConfigs, E as loadConfigSync, F as defaultBackgroundAiFeatures, I as init_registry, L as init_js_yaml, P as updateConversationsConfig, R as jsYaml, T as loadConfigNoMigration, a as PARENT_MODEL_REF, b as init_config_yaml, c as clearConfigCache, d as getConversationsConfig, g as getGlobalConfigPath, h as getDefaultTtsDaemonConfig, i as DEFAULT_WORKHORSES, o as ROLE_EFFORTS, r as DEFAULT_ROLES, t as COMPLIANCE_MODES, u as getConversationSearchConfigSync, x as isClaudeCodeChannelsEnabled } from "./config-yaml-Dbgr5RAI.js";
12
- import { a as getModelEffortLevelsSync, c as modelSupportsImagesSync, l as resolveModelIdSync, n as MODEL_CAPABILITIES, o as hasModelCapabilitySync, r as MODEL_DEPRECATIONS, s as init_model_capabilities, t as CLIPROXY_CODEX_CONTEXT_WINDOW } from "./model-capabilities-blGzeIJ2.js";
13
- import { B as init_ui_theme, D as listSessionNames, F as sessionExists, G as init_child_env, H as BLANKED_PROVIDER_ENV, L as setOption, N as sendKeys, P as sendRawKeystroke, S as isHarnessProcessAlive, U as buildChildEnvSync, V as setUiTheme, W as buildChildEnvWithoutTmuxSync, b as getWindowDimensions, c as ensureManagedTmuxContextOnce, d as exactPaneTarget, i as capturePane, j as resizeWindow, k as listSessionsSync, n as buildTmuxArgs, o as createSession, p as findManagedServerPidSync, w as killSession, x as init_tmux } from "./tmux-DBDERLCd.js";
14
- import { a as init_event_store, i as initEventStore, n as getEventStore, r as getSharedDb } from "./event-store-DYX1bQU6.js";
15
- import { d as WS_METHODS, f as FlywheelRunId, g as MemoryObservation, l as PanRpcError, m as EDITORS, n as ContextPreviewRequest, p as FlywheelStatus, r as FlywheelStats, t as ContextLayerSaveRequest, u as PanRpcGroup } from "./src-BZPsVCQI.js";
16
- import { i as recordBriefingSessionStart, n as ensureSessionContextBriefingFile, r as init_briefing_freshness, t as appendFreshBriefingUpdate } from "./briefing-freshness-CJKdk-JL.js";
17
- import { a as EventStoreServiceLive, d as toRole, i as EventStoreService, l as toAgentResolution, n as IssueLifecycleLive, o as ReadModelService, s as ReadModelServiceLive, u as toAgentStatus } from "./issue-lifecycle-CUG38hdz.js";
18
- import { n as AgentsResolverLive, o as init_agents } from "./agents-9qeRnPEQ.js";
19
- import { i as init_paths$2, l as resolveObservationsFile, r as ensureParentDir, s as resolveIssueMemoryRoot, t as assertMemorySafeSegment } from "./paths-DgfOFn_7.js";
20
- import { n as listFeatureRegistryEntries, t as init_feature_registry_storage } from "./feature-registry-storage-B3VDkSaU.js";
21
- import { a as diffAgainstMainFiles, c as diffFilesAgainstHead, d as findCommitAtTime, i as diffAgainstMain, l as diffPatchFilesAgainstHead, o as diffCheckpointFiles, p as listCheckpoints, s as diffCheckpoints, t as captureCheckpoint, u as diffPatchSinceCommit } from "./checkpoint-manager-DStOuMAH.js";
22
- import { a as migrateAllSessionsSync, c as syncCacheSync, i as reconcile, l as CostResolver, n as resolveIssueHeadlineCost, o as getCostsForIssueSync, r as syncWalFromAllProjects, s as rebuildCacheSync, t as getCachedRunningAgents, u as CostResolverLive } from "./running-agents-cache-B6qFwhYH.js";
23
- import { n as AgentStateServiceLive } from "./agent-state-service-weSkcN1i.js";
24
- import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-CY1aIAUg.js";
25
- import { l as WorkspaceCreateError, n as AgentStartError, r as BeadsNotInitialized, t as AgentAlreadyRunning, u as WorkspaceNotFound } from "./typed-errors-zngUR5Fa.js";
26
- import { r as LinearClientOptionalLive, t as LinearClient } from "./linear-client-f2x21nCi.js";
27
- import { r as GitHubClientOptionalLive } from "./github-client-B9TlbtyU.js";
28
- import { r as RallyClientOptionalLive } from "./rally-client-BbAVOlvB.js";
29
- import { r as resolveGitHubIssueSync } from "./tracker-utils-CVIUkgod.js";
30
- 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-CGEMzi0p.js";
31
- import { i as ensureInternalTokenSync, s as init_internal_token } from "./internal-token-DeMSS93S.js";
32
- import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-xD_7wc0U.js";
33
- import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates, r as getAllReviewStatusesFromDb } from "./review-status-sync-6a1M-qUz.js";
34
- import { a as getCostForIssueAggregateSync, c as getDailyTrendsSync, l as getModelRollupSync, n as getBackgroundCostBySourceSync, r as getCavemanExperimentDataSync, s as getCostsByIssueSync, u as init_cost_sync } from "./cost-sync-BY2D98Ng.js";
35
- 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-CaApkXcY.js";
36
- import { a as fixStuckCommentedReviews, c as getReviewStatusSync, g as setReviewStatus, l as init_review_status, n as clearReviewStatus, o as fixStuckReadyForMerge, r as clearStuckMergeStatuses, s as getReviewStatus, u as loadReviewStatuses } from "./review-status-D7oxzZLw.js";
37
- import { A as PAN_CONTINUE_FILENAME, I as init_types, _ as findSpecByIssue, a as init_io, j as PAN_DIRNAME, l as readWorkspacePlan, n as findPlan, s as readPlan, y as init_specs } from "./io-B2VY861T.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-D1hyiowM.js";
39
- import { f as init_config, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-XdUlww0L.js";
40
- import { n as require_auth_errors, r as require_token_error, t as require_token_util } from "./token-util-M91UIf3c.js";
41
- import { A as listConversations, B as setConversationClaudeSessionId, C as getStuckForks, E as init_conversations, G as unarchiveConversation, H as setConversationModel, I as recordConversationHandoff, J as updateConversationForkFallbackReason, K as updateConversationCost, L as removeFavorite, M as markConversationActive, N as markConversationEnded, O as listActiveConversations, P as markConversationRunning, S as getConversationByTmuxSession, T as incrementForkRetryCount, U as setFavorite, V as setConversationHarness, W as setForkRequest, X as updateForkStatus, Y as updateConversationTitle, Z as updateLastAttached, _ as canReplaceTitle, b as getConversationById, g as backfillConversationModel, h as archiveConversation, j as listFavoritedIds, k as listArchivedConversationsWithEnrichment, q as updateConversationDeliveryMethod, v as createConversation, w as hasOtherActiveConversationOnTmuxSession, x as getConversationByName, y as getConversationByClaudeSessionId, z as setClearedToConvId } from "./conversations-BOXFUrBX.js";
42
- import { a as init_conversation_service, c as summarizeConversationActivity, d as isPiSessionFile, f as parsePiConversationMessages, i as gateSnapshotEmission, l as watchConversation, m as parseCodexConversationMessages, n as contextUsageFromParseResult, o as parseConversationMessages, p as init_codex_conversation_parser, s as parseFromLastCompactBoundary, t as computeContextUsage, u as init_pi_conversation_parser } from "./conversation-service-Bzn1HWcE.js";
43
- import { i as getClaudePermissionFlagsStringSync, o as init_claude_permissions, s as resolvePermissionModeSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-Ql4bavTE.js";
44
- import { f as writeThreadId, l as init_codex, s as findRolloutPath } from "./codex-CDAqRWlw.js";
45
- import { $t as init_openai_auth, A as getAgentStateFilePath, At as init_operator_interventions, B as init_agents$1, Bt as init_bridge_token, C as dismissDevChannelsDialog, D as getAgentRuntimeState, Dt as captureTranscriptUserRecordSnapshot, E as getAgentRuntimeBaseCommand, Et as writeChannelsBridgeMcpConfig, F as getProviderEnvForModel, Ft as init_pi_fifo, G as listAgentStates, I as getProviderExportsForModel, It as piFifoPaths, Jt as init_agent_rollback_state, K as listRunningAgents, O as getAgentRuntimeStateSync, Ot as init_transcript_landing, P as getProviderAuthMode, Qt as getOpenAIAuthStatus, Rt as init_pty_token, St as waitForReadySignal, T as getAgentDir, V as injectPiConversationMemory, Vt as writeBridgeTokenSync, Xt as getClaudeAuthStatus, Yt as readRollbackAgentHarnessFromDir, Z as messageAgent, Zt as init_claude_auth, h as clearReadySignal, j as getAgentStateSync, jt as operatorInterventionEvent, k as getAgentState, kt as probeTranscriptSince, q as listRunningAgentsSync, v as deliverAgentMessage, zt as writePtyToken } from "./agents-CoZumDXi.js";
46
- import { a as getBuiltInDefaultHarness, c as getProviderForModelSync, l as init_providers, o as getKimiAnthropicBaseUrl, u as piProviderForModel } from "./providers-Bal6r0b4.js";
47
- import { n as isBackgroundFeatureEnabled, t as init_features } from "./features-DLrA0guP.js";
48
- import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-D2e30bjI.js";
49
- import { t as init_pan_dir } from "./pan-dir-CF3eJjQG.js";
50
- import { n as init_launcher_generator, t as generateLauncherScriptSync } from "./launcher-generator-D2eL0ybn.js";
51
- 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-YEs5QEfF.js";
52
- import { i as init_harness_policy, n as resolveHarness, r as canUseHarnessSync, t as init_harness_resolve } from "./harness-resolve-DWUovlI5.js";
53
- import { c as init_docker_stats } from "./stack-health-DFXzDbx-.js";
54
- import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health, t as init_providers$1 } from "./providers-m2RBUHqD.js";
55
- import { a as getSetting, b as setSetting, c as isDeaconGloballyPausedSync, d as isFlywheelRequireUatBeforeMerge, f as isMergeTrainEnabled, g as setFlywheelAutoPickupBacklog, l as isFlywheelAutoPickupBacklog, m as setDeaconGloballyPausedSync, o as init_control_settings, u as isFlywheelGloballyPaused$1, v as setFlywheelRequireUatBeforeMerge, y as setMergeTrainEnabled } from "./control-settings-Dt8ZP3y4.js";
56
- import { a as parseRelativeTime, n as CostThresholdError, s as embed } from "./scanner-07yNZxQN.js";
57
- import { n as deduplicateEventsSync, o as readEventsSync, s as tailEventsSync } from "./events-B9JAl2Vn.js";
58
- import { n as recordBackgroundAiCost, t as init_cost } from "./cost-CjhAEexd.js";
59
- import { a as startUatTrainReconciler, c as openFlywheelRunReportForDashboard, d as resumeFlywheelRunForDashboard, f as startFlywheelRunForDashboard, l as pauseFlywheelRunForDashboard, o as stopUatTrainReconciler, p as runDashboardDbJob, s as abortFlywheelRunForDashboard, u as readCurrentFlywheelStatusForDashboard } from "./uat-train-Co7Vdqcb.js";
60
- import { c as buildClassifyLookups, d as getFlywheelRunDetail, g as readCurrentLatestFlywheelStatus, m as listFlywheelRuns, p as isFlywheelRunId, v as resolveLiveFlywheelRunId, x as writeLatestFlywheelStatus, y as subscribeLatestFlywheelStatus } from "./flywheel-Dh-BJ0wD.js";
61
- import { f as workspacesRouteLayer, g as getWorkspacePathForIssue, h as processPendingFeedbackDeliveries, m as resumeQueuedMerges, p as enrichReviewStatus } from "./workspaces-Bm5IvBNs.js";
62
- import { g as init_platform, h as detectPlatform } from "./workspace-manager-BkZBOkYD.js";
63
- import { a as getShadowState, s as init_shadow_state } from "./shadow-state-C3a-XR8T.js";
64
- import { S as rejectUnsafeDashboardMutationRequest, _ as dashboardCsrfToken, b as rejectUnauthorizedDashboardRequest, f as issuesRouteLayer, g as sanitizeResourceAllocatedIssues, h as groupResourceAllocatedIssuesByProject, m as getCachedResourceAllocatedIssues, v as dashboardSessionCookieHeader, x as rejectUnauthorizedDashboardSessionMintRequest, y as hasDashboardInternalToken } from "./issues-C-kal-Se.js";
65
- import { a as saveCloisterConfigSync, i as loadCloisterConfigSync, o as shouldAutoStart, r as loadCloisterConfig } from "./config-OYvCWYs5.js";
66
- import { O as init_specialists, b as getReviewerSessionName } from "./specialists-CxKFRxKR.js";
67
- import { at as setMergeReadyNotifier, dt as withConcurrencyLimit, ft as emergencyBrake, it as setAgentStoppedNotifier, rt as setAgentStatusChangedNotifier } from "./deacon-BgdWPMqm.js";
68
- import { t as getNoResumeMode } from "./no-resume-mode-CK1zTSyc.js";
69
- import { o as findPrdAtStatusSync } from "./archive-planning-CIamwvkR.js";
70
- import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-BUY2cbmq.js";
71
- import { n as writeSequenceMd, t as parseSequenceMd } from "./sequence-io-D5N1X-nD.js";
72
- import { c as PARKED_LABEL, d as classifyIssue, f as computeCohort, g as normalizeGate, h as computeWaves, l as READY_LABEL, m as computeStats, p as computeLanes, s as BLOCKS_MAIN_LABEL, u as VETOED_LABEL } from "./flywheel-merge-order-CEygIYNw.js";
73
- 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-CS94rsth.js";
74
- import { h as listGitOperationsSync } from "./merge-agent-CGNzsX7x.js";
75
- import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-G1HK5zsr.js";
76
- import { i as runMemoryFtsTransaction, n as init_fts_db, r as runMemoryFtsStatement, t as closeMemoryFtsDatabases } from "./fts-db-eiIUjmRQ.js";
77
- import { a as readRecentObservations, c as writePendingTurn, s as maybeTriggerStatusRollup } from "./rollup-DxdMEBML.js";
78
- import { r as specialistsRouteLayer } from "./specialists-CyVAp3cB.js";
79
- import { t as getAgentCommandSync } from "./settings-Dp9vKSf9.js";
80
- import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-CgDjIOGw.js";
81
- import { r as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remote-K7NPj_ah.js";
82
- import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-DdI3J1rj.js";
83
- 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-GDIgqCF3.js";
84
- 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-C149zUUo.js";
85
- import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting, s as maybeCompactBeforeRespawn } from "./conversation-compaction-Dbv7FC4s.js";
86
- 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-BpuXeHhe.js";
87
- import { c as init_github_app, s as getPullRequestState } from "./github-app-DAbOi7t-.js";
88
- import { r as parseArtifactRef } from "./forge-i30UYoWv.js";
89
- import { n as spawnSequencerAgent, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-nsW6G44K.js";
90
- import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-BpQ8wIW1.js";
1
+ import { a as __toCommonJS, i as __require, o as __toESM, t as __commonJSMin } from "./rolldown-runtime-DNCzzKnI.js";
2
+ import { C as isDevMode, E as sessionFilePath, S as init_paths, T as resolveOhmypiExtensionPath, _ as SYNC_SOURCES, l as LOGS_DIR, o as CLAUDE_DIR, t as AGENTS_DIR, u as OVERDECK_HOME, w as packageRoot, x as getOverdeckHome, y as encodeClaudeProjectDir } from "./paths-jZvjmiV6.js";
3
+ import { A as init_model_capabilities, B as FsError, C as defaultBackgroundAiFeatures, E as MODEL_DEPRECATIONS, F as require_dist$1, H as GitHubApiError, J as TrackerError, K as ProcessSpawnError, M as resolveModelIdSync, N as init_js_yaml, O as getModelEffortLevelsSync, P as jsYaml, S as updateConversationsConfig, T as MODEL_CAPABILITIES, X as init_errors, _ as loadConfigNoMigration, a as ROLE_EFFORTS, b as mergeTtsDaemonConfigs, c as getConversationSearchConfigSync, d as getDefaultTtsDaemonConfig, f as getGlobalConfigPath, g as isTldrEnabledSync, i as PARENT_MODEL_REF, j as modelSupportsImagesSync, k as hasModelCapabilitySync, l as getConversationsConfig, m as isClaudeCodeChannelsEnabled, n as DEFAULT_ROLES, o as clearConfigCache, p as init_config_yaml, r as DEFAULT_WORKHORSES, s as computeModelOrigin, t as COMPLIANCE_MODES, v as loadConfigSync, w as init_registry, x as resolveModel, y as mergeConfigs } from "./config-yaml-DKmldcyb.js";
4
+ import { A as getAgentJsonlMtime, B as isTrustedOriginForHost, C as resolveAgentHarness, D as resolvePiSessionPath, E as resolveJsonlPath, F as init_agent_input_detection, G as init_codex_auth, H as validateOrigin$1, I as parseCodexApprovalPrompt, K as httpHandler, L as getHeaderFromMap, M as scanPendingInputsPromise, N as detectAwaitingInputForAgent, O as computeAgentEnrichment, P as detectAwaitingInputFromPaneSync, S as readLauncherPinnedSessionId, T as resolveCodexRolloutPath, U as validateOriginHeaders, W as checkCodexAuthStatus, X as init_http_helpers, Z as jsonResponse, _ as getRuntimeForAgent, a as init_agent_projection, b as init_health, c as init_git_info, d as getSystemHealthSnapshot, f as init_system_health_service, g as init_service, h as getCloisterService, j as init_agent_enrichment, n as init_agents, o as saveAgentStateAndEmitEvent, q as init_http_handler, s as saveAgentStateAndEmitEventProgram, t as agentsRouteLayer, u as resolveConversationGitInfo, v as init_runtimes, w as resolveClaudeSessionId, x as init_jsonl_resolver, y as getAgentHealth, z as init_origin_validation } from "./agents-L3yA6mO7.js";
5
+ import { a as init_activity_logger, i as emitDashboardLifecycleSync, n as emitActivityEntrySync, r as emitActivityTtsSync } from "./activity-logger-DW1XjyNu.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-V576nNrp.js";
7
+ import { n as openDatabase, t as init_driver } from "./driver-CYPr28GN.js";
8
+ import { a as parseIssueIdSync, n as extractPrefixSync, r as init_issue_id } from "./issue-id-9iS2dwrp.js";
9
+ import { E as registerProjectSync, N as setProjectAutoMergeDefaultSync, O as resolveProjectFromIssue, _ as init_projects, b as listProjects, d as getProjectSync, k as resolveProjectFromIssueSync, l as getIssuePrefix, r as extractTeamPrefix, s as findProjectByTeamSync, x as listProjectsSync } from "./projects-BpstKN1Q.js";
10
+ import { B as init_ui_theme, D as listSessionNames, F as sessionExists, G as init_child_env, H as BLANKED_PROVIDER_ENV, L as setOption, N as sendKeys, P as sendRawKeystroke, S as isHarnessProcessAlive, U as buildChildEnvSync, V as setUiTheme, W as buildChildEnvWithoutTmuxSync, b as getWindowDimensions, c as ensureManagedTmuxContextOnce, d as exactPaneTarget, i as capturePane, j as resizeWindow, k as listSessionsSync, n as buildTmuxArgs, o as createSession, p as findManagedServerPidSync, w as killSession, x as init_tmux } from "./tmux-DuzZlrCh.js";
11
+ import { a as init_event_store, i as initEventStore, n as getEventStore, r as getSharedDb } from "./event-store-hdZZ-JnB.js";
12
+ import { _ as MemoryObservation, d as PanRpcGroup, f as WS_METHODS, h as EDITORS, i as FlywheelStats, m as FlywheelStatus, n as ContextLayerSaveRequest, p as FlywheelRunId, r as ContextPreviewRequest, t as init_src, u as PanRpcError } from "./src-wyzx0Z0w.js";
13
+ import { _ as setReviewStatus, a as fixStuckCommentedReviews, c as getReviewStatusSync, l as init_review_status, n as clearReviewStatus, o as fixStuckReadyForMerge, r as clearStuckMergeStatuses, s as getReviewStatus, u as loadReviewStatuses } from "./review-status-D_w3kLBb.js";
14
+ 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 } from "./issue-lifecycle-D4DcNPZ-.js";
15
+ import { n as AgentsResolverLive, o as init_agents$1 } from "./agents-BOX5w2kT.js";
16
+ import { i as init_paths$2, l as resolveObservationsFile, r as ensureParentDir, s as resolveIssueMemoryRoot, t as assertMemorySafeSegment } from "./paths-zPOV7_VB.js";
17
+ import { i as recordBriefingSessionStart, n as ensureSessionContextBriefingFile, r as init_briefing_freshness, t as appendFreshBriefingUpdate } from "./briefing-freshness--_mHhmzI.js";
18
+ import { n as listFeatureRegistryEntries, t as init_feature_registry_storage } from "./feature-registry-storage-5mIu-zwl.js";
19
+ 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-B8USYP2J.js";
20
+ import { _ as init_cost, a as init_costs, c as init_reconciler, d as migrateAllSessionsSync, f as getCostsForIssueSync, g as CostResolverLive, h as CostResolver, i as resolveIssueHeadlineCost, l as reconcile, m as syncCacheSync, n as init_running_agents_cache, o as init_sync_wal, p as rebuildCacheSync, r as init_issue_cost_resolver, s as syncWalFromAllProjects, t as getCachedRunningAgents, u as reconcilePiTranscripts } from "./running-agents-cache-CoFoCGgg.js";
21
+ import { i as init_agent_state_service, n as AgentStateServiceLive } from "./agent-state-service-CoVk44dM.js";
22
+ import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-Clt5yfo6.js";
23
+ import { d as init_typed_errors, l as WorkspaceCreateError, n as AgentStartError, r as BeadsNotInitialized, 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-DCCtUBEM.js";
25
+ import { i as init_github_client, r as GitHubClientOptionalLive } from "./github-client-Dxt2nXAf.js";
26
+ import { i as init_rally_client, r as RallyClientOptionalLive } from "./rally-client-CFxmPAi2.js";
27
+ import { i as resolveGitHubIssueSync, n as init_tracker_utils } from "./tracker-utils-k1WKm_mu.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-0AVb8ADy.js";
29
+ import { i as ensureInternalTokenSync, s as init_internal_token } from "./internal-token-R4gY8z44.js";
30
+ import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-C5ZCbH9K.js";
31
+ import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates, r as getAllReviewStatusesFromDb } from "./review-status-sync-DGaMxP95.js";
32
+ import { a as getCostForIssueAggregateSync, c as getDailyTrendsSync, l as getModelRollupSync, n as getBackgroundCostBySourceSync, r as getCavemanExperimentDataSync, s as getCostsByIssueSync, u as init_cost_sync } from "./cost-sync-CI1Gfwnb.js";
33
+ 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-CKUHrPQl.js";
34
+ import { A as PAN_CONTINUE_FILENAME, I as init_types, _ as findSpecByIssue, a as init_io, j as PAN_DIRNAME, l as readWorkspacePlan, n as findPlan, s as readPlan, y as init_specs } from "./io-cnr9mD4B.js";
35
+ 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-O755gANb.js";
36
+ import { f as init_config, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-B8QT8B9M.js";
37
+ import { n as require_auth_errors, r as require_token_error, t as require_token_util } from "./token-util-CROoF5ic.js";
38
+ import { $ as updateSpawnError, A as listConversations, B as setClearedToConvId, C as getStuckForks, E as init_conversations, G as setForkRequest, H as setConversationHarness, J as updateConversationDeliveryMethod, K as unarchiveConversation, L as recordConversationHandoff, M as markConversationActive, N as markConversationEnded, O as listActiveConversations, P as markConversationRunning, Q as updateLastAttached, R as removeFavorite, S as getConversationByTmuxSession, T as incrementForkRetryCount, U as setConversationModel, V as setConversationClaudeSessionId, W as setFavorite, X as updateConversationTitle, Y as updateConversationForkFallbackReason, Z as updateForkStatus, _ as canReplaceTitle, b as getConversationById, g as backfillConversationModel, h as archiveConversation, j as listFavoritedIds, k as listArchivedConversationsWithEnrichment, q as updateConversationCost, v as createConversation, w as hasOtherActiveConversationOnTmuxSession, x as getConversationByName, y as getConversationByClaudeSessionId } from "./conversations-_HbvcXw1.js";
39
+ import { _ as init_codex_conversation_parser, a as init_conversation_service, c as parseFromLastCompactBoundary, d as init_ohmypi_conversation_parser, f as isOhmypiSessionFile, g as parsePiConversationMessages, h as isPiSessionFile, i as gateSnapshotEmission, l as summarizeConversationActivity, m as init_pi_conversation_parser, n as contextUsageFromParseResult, o as parseConversationMessages, p as parseOhmypiConversationMessages, t as computeContextUsage, u as watchConversation, v as parseCodexConversationMessages } from "./conversation-service-CJimCSkV.js";
40
+ import { i as getClaudePermissionFlagsStringSync, o as init_claude_permissions, s as resolvePermissionModeSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-BqmkURDk.js";
41
+ import { l as init_codex, s as findRolloutPath } from "./codex-B8fyHBID.js";
42
+ import { $t as init_claude_auth, A as getAgentStateFilePath, At as init_operator_interventions, B as init_agents$2, Bt as init_pty_token, C as dismissDevChannelsDialog, D as getAgentRuntimeState, Dt as captureTranscriptUserRecordSnapshot, E as getAgentRuntimeBaseCommand, Et as writeChannelsBridgeMcpConfig, F as getProviderEnvForModel, G as listAgentStates, Ht as init_bridge_token, I as getProviderExportsForModel, K as listRunningAgents, O as getAgentRuntimeStateSync, Ot as init_transcript_landing, P as getProviderAuthMode, Qt as getClaudeAuthStatus, Rt as init_pi_fifo, St as waitForReadySignal, T as getAgentDir, Ut as writeBridgeTokenSync, V as injectPiConversationMemory, Vt as writePtyToken, Xt as init_agent_record_sync, Z as messageAgent, Zt as readAgentHarnessModelRecordSync, en as getOpenAIAuthStatus, h as clearReadySignal, j as getAgentStateSync, jt as operatorInterventionEvent, k as getAgentState, kt as probeTranscriptSince, tn as init_openai_auth, v as deliverAgentMessage, zt as piFifoPaths } from "./agents-qDfx6gda.js";
43
+ import { a as getBuiltInDefaultHarness, c as getProviderForModelSync, l as init_providers, o as getKimiAnthropicBaseUrl, u as piProviderForModel } from "./providers-Cju7HorZ.js";
44
+ import { n as isBackgroundFeatureEnabled, t as init_features } from "./features-BsiM65ZI.js";
45
+ import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-s_9BJkQW.js";
46
+ import { t as init_pan_dir } from "./pan-dir-BfyAUgin.js";
47
+ import { n as init_launcher_generator, t as generateLauncherScriptSync } from "./launcher-generator-CZaEkOZW.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-CoMbLSYY.js";
49
+ import { i as init_harness_policy, n as resolveHarness, r as canUseHarnessSync, t as init_harness_resolve } from "./harness-resolve-CMrgPv1e.js";
50
+ import { c as init_docker_stats } from "./stack-health-KI4ZA4dt.js";
51
+ import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health$1, t as init_providers$1 } from "./providers-0791nfjR.js";
52
+ import { a as getSetting, b as setSetting, c as isDeaconGloballyPausedSync, d as isFlywheelRequireUatBeforeMerge, f as isMergeTrainEnabled, g as setFlywheelAutoPickupBacklog, l as isFlywheelAutoPickupBacklog, m as setDeaconGloballyPausedSync, o as init_control_settings, u as isFlywheelGloballyPaused$1, v as setFlywheelRequireUatBeforeMerge, y as setMergeTrainEnabled } from "./control-settings-BaPQ0U5p.js";
53
+ import { a as parseRelativeTime, n as CostThresholdError, s as embed } from "./scanner-DYR1QDik.js";
54
+ import { n as deduplicateEventsSync, o as readEventsSync, s as tailEventsSync } from "./events-C0JbknXU.js";
55
+ import { n as recordBackgroundAiCost, t as init_cost$1 } from "./cost-CBa8loyG.js";
56
+ import { a as startUatTrainReconciler, c as openFlywheelRunReportForDashboard, d as resumeFlywheelRunForDashboard, f as startFlywheelRunForDashboard, l as pauseFlywheelRunForDashboard, o as stopUatTrainReconciler, p as runDashboardDbJob, s as abortFlywheelRunForDashboard, u as readCurrentFlywheelStatusForDashboard } from "./uat-train-BF905HkK.js";
57
+ import { S as subscribeLatestFlywheelStatus, _ as listFlywheelRuns, g as isFlywheelRunId, h as init_flywheel_run_state, l as buildClassifyLookups, p as getFlywheelRunDetail, u as init_lookups, w as writeLatestFlywheelStatus, x as resolveLiveFlywheelRunId, y as readCurrentLatestFlywheelStatus } from "./flywheel-rQwXVZR5.js";
58
+ import { f as workspacesRouteLayer, g as getWorkspacePathForIssue, h as processPendingFeedbackDeliveries, m as resumeQueuedMerges, p as enrichReviewStatus } from "./workspaces-CvQySqp3.js";
59
+ import { _ as init_platform, g as detectPlatform } from "./workspace-manager-oMP5s9VY.js";
60
+ import { a as getShadowState, s as init_shadow_state } from "./shadow-state-xPxOHguz.js";
61
+ import { C as init_dashboard_auth, D as init_review_status$1, E as rejectUnsafeDashboardMutationRequest, S as hasDashboardInternalToken, T as rejectUnauthorizedDashboardSessionMintRequest, _ as groupResourceAllocatedIssuesByProject, b as dashboardCsrfToken, g as getCachedResourceAllocatedIssues, m as issuesRouteLayer, p as init_issues, v as init_resource_discovery, w as rejectUnauthorizedDashboardRequest, x as dashboardSessionCookieHeader, y as sanitizeResourceAllocatedIssues } from "./issues-D909CPHX.js";
62
+ import { a as loadCloisterConfigSync, i as loadCloisterConfig, o as saveCloisterConfigSync, r as init_config$1, s as shouldAutoStart } from "./config-wf_W38L3.js";
63
+ import { O as init_specialists, b as getReviewerSessionName } from "./specialists-C5uBTD_g.js";
64
+ import { H as init_deacon, _t as emergencyBrake, ct as setAgentStoppedNotifier, gt as withConcurrencyLimit, ht as init_concurrency$1, i as autoResumeStoppedWorkAgents, lt as setMergeReadyNotifier, rt as resetPatrolHeartbeatForStartup, st as setAgentStatusChangedNotifier, vt as init_concurrency } from "./deacon-BJOrAeJn.js";
65
+ import { n as getNoResumeMode, r as init_no_resume_mode, t as disableNoResumeMode } from "./no-resume-mode-DlMPigWY.js";
66
+ import { c as init_prd_locations, s as findPrdAtStatusSync } from "./archive-planning-O-QKXVWX.js";
67
+ import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-D5GpgTWq.js";
68
+ import { n as parseSequenceMd, r as writeSequenceMd, t as init_sequence_io } from "./sequence-io-CMiS9KMK.js";
69
+ import { _ as computeStats, b as normalizeGate, c as BLOCKS_MAIN_LABEL, d as READY_LABEL, f as RELEASED_LABEL, g as computeLanes, h as computeCohort, l as OBJECTION_LABEL, m as classifyIssue, p as VETOED_LABEL, u as PARKED_LABEL, v as computeWaves, y as init_pickup } from "./flywheel-merge-order-bSoDRUVw.js";
70
+ 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-DQRd2At2.js";
71
+ import { _ as init_git_activity, v as listGitOperationsSync } from "./merge-agent-BsELKm49.js";
72
+ import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-C4f0Cv5Z.js";
73
+ import { i as runMemoryFtsTransaction, n as init_fts_db, r as runMemoryFtsStatement, t as closeMemoryFtsDatabases } from "./fts-db-DH6pFiOa.js";
74
+ import { c as init_pending, l as maybeTriggerStatusRollup, o as readRecentObservations, r as init_rollup, u as writePendingTurn } from "./rollup-_dL7JXdO.js";
75
+ import { r as specialistsRouteLayer } from "./specialists-DFDg83jj.js";
76
+ import { t as getAgentCommandSync } from "./settings-DVFIQrHs.js";
77
+ import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-GjWZBuhW.js";
78
+ import { i as isRemoteAvailable, r as init_remote, t as createFlyProviderFromConfig } from "./remote-br-7Zy2V.js";
79
+ import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-Ck8qNezZ.js";
80
+ 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-CcJERZro.js";
81
+ 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-znoS388v.js";
82
+ import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting } from "./conversation-compaction-DfmqrfYi.js";
83
+ 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-CIMR-y8_.js";
84
+ import { c as init_github_app, s as getPullRequestState } from "./github-app-DgISqJqI.js";
85
+ import { i as parseArtifactRef, r as init_forge } from "./forge-mcRx-ba-.js";
86
+ import { a as spawnSequencerAgent, i as init_sequencer_agent, n as clearFinishedSequencerRun, r as getSequencerRunStatus, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-CFrrqU0M.js";
87
+ import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-9h0pTZ5k.js";
91
88
  import { createRequire } from "node:module";
92
89
  import { Cause, Context, Data, Effect, Exit, Fiber, FileSystem, Layer, Option, Path, Queue, Result, Schedule, Schema, Stream } from "effect";
93
90
  import { createReadStream, createWriteStream, existsSync, fstatSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
@@ -102,7 +99,7 @@ import { createHash, randomUUID } from "crypto";
102
99
  import * as sp from "node:path";
103
100
  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";
104
101
  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";
105
- import { createHmac, randomBytes as randomBytes$1, randomUUID as randomUUID$1, timingSafeEqual } from "node:crypto";
102
+ import { createHmac, randomBytes, randomUUID as randomUUID$1, timingSafeEqual } from "node:crypto";
106
103
  import { exec, execFile, execFileSync, execSync, spawn } from "node:child_process";
107
104
  import { promisify } from "node:util";
108
105
  import * as pty from "@homebridge/node-pty-prebuilt-multiarch";
@@ -303,6 +300,8 @@ const ServerConfigLayer = Layer.effect(ServerConfig, Effect.sync(() => {
303
300
  }));
304
301
  //#endregion
305
302
  //#region services/terminal-service.ts
303
+ init_http_helpers();
304
+ init_activity_logger();
306
305
  init_paths$1();
307
306
  init_driver();
308
307
  var import_dist = /* @__PURE__ */ __toESM(require_dist$1(), 1);
@@ -312,7 +311,13 @@ init_registry();
312
311
  init_config_yaml();
313
312
  init_ui_theme();
314
313
  init_infra();
315
- init_agents();
314
+ init_event_store();
315
+ init_agents$1();
316
+ init_read_model();
317
+ init_checkpoint_manager();
318
+ init_domain_services();
319
+ init_cost();
320
+ init_agent_state_service();
316
321
  /**
317
322
  * TerminalService — dual-runtime PTY terminal streaming (PAN-428 B20)
318
323
  *
@@ -325,6 +330,7 @@ init_agents();
325
330
  * toggle dimensions to force SIGWINCH + full repaint.
326
331
  * - On stream/close: do NOT kill the PTY — just remove from tracking.
327
332
  */
333
+ init_src();
328
334
  init_tmux();
329
335
  init_child_env();
330
336
  function isBun() {
@@ -592,12 +598,23 @@ const TerminalServiceLive = Layer.effect(TerminalService, Effect.sync(() => {
592
598
  }));
593
599
  //#endregion
594
600
  //#region services/agent-spawner.ts
601
+ init_issue_id();
602
+ init_projects();
603
+ init_tracker_config();
604
+ init_linear_client();
605
+ init_github_client();
606
+ init_rally_client();
607
+ init_tracker_utils();
608
+ init_cache_service();
609
+ init_issue_service_singleton();
610
+ init_issue_lifecycle();
595
611
  /**
596
612
  * AgentSpawner Effect service (PAN-449)
597
613
  *
598
614
  * Wraps agents.ts in an Effect service with typed errors.
599
615
  * Route handlers use this instead of importing from lib/agents.ts directly.
600
616
  */
617
+ init_typed_errors();
601
618
  var AgentSpawner = class extends Context.Service()("overdeck/dashboard/AgentSpawner") {};
602
619
  const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
603
620
  startWork: (issueId, opts) => Effect.tryPromise({
@@ -611,7 +628,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
611
628
  const beadsDir = join$1(workspacePath, ".beads");
612
629
  const projectBeadsDir = join$1(workspacePath, "..", "..", ".beads");
613
630
  if (!(existsSync$1(beadsDir) || existsSync$1(projectBeadsDir))) throw new BeadsNotInitialized({ workspace: workspacePath });
614
- const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-DD8lFXJe2.js");
631
+ const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-DsFz3A8y.js");
615
632
  const normalizedId = normalizeAgentId(issueId);
616
633
  if ((await Effect.runPromise(getAgentState(normalizedId)))?.status === "running") throw new AgentAlreadyRunning({ id: issueId });
617
634
  return {
@@ -642,7 +659,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
642
659
  const { workspacePath, projectPath, issue } = opts;
643
660
  if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
644
661
  const sessionName = opts.sessionName ?? `planning-${issueId.toLowerCase()}`;
645
- const { spawnPlanningSession } = await import("./spawn-planning-session-BAdlPinE.js");
662
+ const { spawnPlanningSession } = await import("./spawn-planning-session-DnUv0hSH.js");
646
663
  const result = await spawnPlanningSession({
647
664
  issue: {
648
665
  id: issue.id,
@@ -675,14 +692,14 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
675
692
  }),
676
693
  kill: (agentId) => Effect.tryPromise({
677
694
  try: async () => {
678
- const { stopAgent } = await import("./agents-DD8lFXJe2.js");
695
+ const { stopAgent } = await import("./agents-DsFz3A8y.js");
679
696
  await Effect.runPromise(stopAgent(agentId));
680
697
  },
681
698
  catch: () => void 0
682
699
  }).pipe(Effect.ignore),
683
700
  message: (agentId, msg) => Effect.tryPromise({
684
701
  try: async () => {
685
- const { messageAgent } = await import("./agents-DD8lFXJe2.js");
702
+ const { messageAgent } = await import("./agents-DsFz3A8y.js");
686
703
  await messageAgent(agentId, msg);
687
704
  },
688
705
  catch: (err) => new AgentStartError({
@@ -693,8 +710,8 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
693
710
  }),
694
711
  deepWipe: (issueId, opts) => Effect.tryPromise({
695
712
  try: async () => {
696
- const { deepWipe } = await import("./workflows-DW4lNeVW.js");
697
- const { resolveProjectFromIssueSync } = await import("./projects-BBiAZIGj.js");
713
+ const { deepWipe } = await import("./workflows-LNUGDzVu.js");
714
+ const { resolveProjectFromIssueSync } = await import("./projects-DWBDcB6V.js");
698
715
  const project = resolveProjectFromIssueSync(issueId);
699
716
  const projectPath = project?.projectPath ?? process.cwd();
700
717
  await Effect.runPromise(deepWipe({
@@ -722,7 +739,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
722
739
  * Wraps workspace-manager.ts in an Effect service with typed errors.
723
740
  * Route handlers and AgentSpawner use this instead of calling workspace-manager directly.
724
741
  */
725
- init_projects();
742
+ init_typed_errors();
726
743
  const execAsync$8 = promisify(exec);
727
744
  var WorkspaceService = class extends Context.Service()("overdeck/dashboard/WorkspaceService") {};
728
745
  const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
@@ -750,8 +767,8 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
750
767
  const { workspacePath, branch } = getWorkspacePath(issueId);
751
768
  const issueLower = issueId.toLowerCase();
752
769
  if (existsSync$1(workspacePath)) return workspacePath;
753
- const { createWorkspace } = await import("./workspace-manager-D6cUCwLt.js");
754
- const { loadProjectsConfigSync } = await import("./projects-BBiAZIGj.js");
770
+ const { createWorkspace } = await import("./workspace-manager-XDNuCCXG.js");
771
+ const { loadProjectsConfigSync } = await import("./projects-DWBDcB6V.js");
755
772
  const { projects } = loadProjectsConfigSync();
756
773
  const project = resolveProjectFromIssueSync(issueId);
757
774
  if (!project) throw new WorkspaceCreateError({
@@ -786,7 +803,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
786
803
  const { workspacePath } = getWorkspacePath(issueId);
787
804
  const issueLower = issueId.toLowerCase();
788
805
  if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
789
- const { removeWorkspace } = await import("./workspace-manager-D6cUCwLt.js");
806
+ const { removeWorkspace } = await import("./workspace-manager-XDNuCCXG.js");
790
807
  const project = resolveProjectFromIssueSync(issueId);
791
808
  if (!project) throw new WorkspaceCreateError({
792
809
  id: issueId,
@@ -817,7 +834,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
817
834
  try: async () => {
818
835
  const { workspacePath } = getWorkspacePath(issueId);
819
836
  const issueLower = issueId.toLowerCase();
820
- const { stopWorkspaceDocker } = await import("./workspace-manager-D6cUCwLt.js");
837
+ const { stopWorkspaceDocker } = await import("./workspace-manager-XDNuCCXG.js");
821
838
  await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));
822
839
  },
823
840
  catch: () => void 0
@@ -886,7 +903,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
886
903
  join$1(workspacePath, ".devcontainer", "docker-compose.devcontainer.yml")
887
904
  ];
888
905
  if (!composePaths.find((p) => existsSync$1(p))) {
889
- const { ensureDevcontainerSync } = await import("./ensure-devcontainer-CA6HkbfR.js");
906
+ const { ensureDevcontainerSync } = await import("./ensure-devcontainer-Uu-fGqwo.js");
890
907
  const ensure = ensureDevcontainerSync({
891
908
  workspacePath,
892
909
  issueId
@@ -915,7 +932,6 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
915
932
  }));
916
933
  //#endregion
917
934
  //#region services/openrouter-service.ts
918
- init_issue_id();
919
935
  /**
920
936
  * OpenRouter Service
921
937
  *
@@ -1060,6 +1076,7 @@ function getDefaultCwd() {
1060
1076
  }
1061
1077
  //#endregion
1062
1078
  //#region services/open.ts
1079
+ init_src();
1063
1080
  const execAsync$7 = promisify(exec);
1064
1081
  function getFileManagerCommand() {
1065
1082
  switch (process.platform) {
@@ -3156,7 +3173,7 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3156
3173
  const http$1 = __require("http");
3157
3174
  const net = __require("net");
3158
3175
  const tls = __require("tls");
3159
- const { randomBytes: randomBytes$2, createHash: createHash$3 } = __require("crypto");
3176
+ const { randomBytes: randomBytes$1, createHash: createHash$3 } = __require("crypto");
3160
3177
  const { Duplex: Duplex$2, Readable: Readable$1 } = __require("stream");
3161
3178
  const { URL: URL$1 } = __require("url");
3162
3179
  const PerMessageDeflate = require_permessage_deflate();
@@ -3711,7 +3728,7 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3711
3728
  }
3712
3729
  }
3713
3730
  const defaultPort = isSecure ? 443 : 80;
3714
- const key = randomBytes$2(16).toString("base64");
3731
+ const key = randomBytes$1(16).toString("base64");
3715
3732
  const request = isSecure ? https.request : http$1.request;
3716
3733
  const protocolSet = /* @__PURE__ */ new Set();
3717
3734
  let perMessageDeflate;
@@ -4766,9 +4783,12 @@ function removeClientFromHub(hubs, sessionName, ws) {
4766
4783
  }
4767
4784
  //#endregion
4768
4785
  //#region routes/codex-auth.ts
4786
+ init_http_handler();
4787
+ init_codex_auth();
4769
4788
  init_cliproxy();
4770
4789
  init_tmux();
4771
4790
  init_config();
4791
+ init_origin_validation();
4772
4792
  const reauthSessions = /* @__PURE__ */ new Map();
4773
4793
  const SESSION_MAX_AGE_MS = 3600 * 1e3;
4774
4794
  const readJsonBody$13 = Effect.gen(function* () {
@@ -4973,6 +4993,7 @@ async function waitForSessionRespawn(sessionName, timeoutMs) {
4973
4993
  //#endregion
4974
4994
  //#region ws-terminal.ts
4975
4995
  init_tmux();
4996
+ init_origin_validation();
4976
4997
  init_child_env();
4977
4998
  const RESPAWN_WAIT_MS = 35e3;
4978
4999
  const ATTACH_TIMEOUT_MS = 5e3;
@@ -15904,7 +15925,7 @@ var require_get_vercel_oidc_token = /* @__PURE__ */ __commonJSMin(((exports, mod
15904
15925
  err = error;
15905
15926
  }
15906
15927
  try {
15907
- const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-DpHQzShA.js").then((m) => /* @__PURE__ */ __toESM(m.default)), await import("./token-DDSrzs5Z.js").then((m) => /* @__PURE__ */ __toESM(m.default))]);
15928
+ const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-xNaV-00f.js").then((m) => /* @__PURE__ */ __toESM(m.default)), await import("./token-mVJcOeCq.js").then((m) => /* @__PURE__ */ __toESM(m.default))]);
15908
15929
  if (!token || isExpired(getTokenPayload(token), options?.expirationBufferMs)) {
15909
15930
  await refreshToken(options);
15910
15931
  token = getVercelOidcTokenSync();
@@ -28221,7 +28242,7 @@ var GoogleCloudTranscription = class {
28221
28242
  }
28222
28243
  async start(apiKey, model) {
28223
28244
  try {
28224
- const { SpeechClient } = await import("./src-DXazRIFk.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).then((module) => module.v1p1beta1 ?? module.v1);
28245
+ const { SpeechClient } = await import("./src-BuEHMMFm.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).then((module) => module.v1p1beta1 ?? module.v1);
28225
28246
  if (this.closed) return;
28226
28247
  const stream = new SpeechClient({ apiKey }).streamingRecognize({
28227
28248
  config: {
@@ -28470,6 +28491,9 @@ function createTurnQueue(emitter, onTurn) {
28470
28491
  //#endregion
28471
28492
  //#region routes/voice.ts
28472
28493
  init_paths();
28494
+ init_http_helpers();
28495
+ init_http_handler();
28496
+ init_origin_validation();
28473
28497
  const MAX_VOICE_SETTINGS_BODY_BYTES = 16384;
28474
28498
  const DEFAULT_VOICE_SETTINGS = {
28475
28499
  stt: {
@@ -28621,6 +28645,7 @@ const putVoiceSettingsRoute = HttpRouter.add("PUT", "/api/voice/settings", httpH
28621
28645
  const voiceRouteLayer = Layer.mergeAll(getVoiceSettingsRoute, putVoiceSettingsRoute);
28622
28646
  //#endregion
28623
28647
  //#region ws-voice.ts
28648
+ init_origin_validation();
28624
28649
  function sendJson$1(ws, payload) {
28625
28650
  if (ws.readyState === import_websocket.default.OPEN) ws.send(JSON.stringify(payload));
28626
28651
  }
@@ -28836,6 +28861,7 @@ function boundedAutoPresoElements(elements) {
28836
28861
  }
28837
28862
  //#endregion
28838
28863
  //#region ws-autopreso.ts
28864
+ init_origin_validation();
28839
28865
  function sendJson(ws, payload) {
28840
28866
  if (ws.readyState === import_websocket.default.OPEN) ws.send(JSON.stringify(payload));
28841
28867
  }
@@ -28886,226 +28912,24 @@ function setupAutoPresoWebSocket(server) {
28886
28912
  });
28887
28913
  }
28888
28914
  //#endregion
28889
- //#region routes/jsonl-resolver.ts
28915
+ //#region services/read-workspace-file.ts
28916
+ init_briefing_freshness();
28917
+ init_internal_token();
28918
+ init_pipeline_notifier();
28919
+ init_review_status_sync();
28920
+ init_review_status();
28890
28921
  init_conversations();
28891
28922
  init_codex_conversation_parser();
28892
28923
  init_pi_conversation_parser();
28924
+ init_ohmypi_conversation_parser();
28893
28925
  init_conversation_service();
28894
- /**
28895
- * JSONL transcript resolver for the Command Deck (PAN-830).
28896
- *
28897
- * Maps an agent ID (e.g. `agent-pan-830`, `planning-pan-830`, or a canonical
28898
- * specialist tmux session name) to the agent's JSONL transcript file on disk.
28899
- *
28900
- * Codex agents (PAN-1805) write rollout JSONLs under the per-agent
28901
- * `codex-home/sessions/` tree — resolution dispatches on the harness recorded
28902
- * in state.json (thread-id fast path, then latest-rollout fallback).
28903
- *
28904
- * For claude-code agents the JSONL filename is the *Claude session ID* (a UUID
28905
- * written by Claude Code itself), NOT the agent/tmux name. Lookup order:
28906
- * 1. session.id — single UUID written by auto-suspend
28907
- * 2. sessions.json — array of UUIDs the agent has used; the heartbeat hook
28908
- * APPENDS new IDs and dedupes, so once a session has been
28909
- * seen its array index never moves. After a `pan resume`
28910
- * brings an older session back as the live one, the array
28911
- * order no longer reflects time-recency. We disambiguate
28912
- * across all listed IDs by JSONL mtime.
28913
- * 3. runtime state — in-process mirror, populated by hooks
28914
- *
28915
- * Async-only (fs/promises) because this code path runs inside the dashboard
28916
- * server's event loop.
28917
- */
28918
- init_agents$1();
28919
- init_paths();
28920
- init_agent_rollback_state();
28921
- async function pathExists$5(p) {
28922
- return access(p).then(() => true, () => false);
28923
- }
28924
- async function readOptional$3(p) {
28925
- return readFile(p, "utf-8").catch(() => null);
28926
- }
28927
- /**
28928
- * Pick the candidate UUID whose JSONL transcript has the most recent mtime.
28929
- *
28930
- * The heartbeat hook appends to sessions.json and dedupes — so once an ID is
28931
- * recorded, its array index never moves even if a later resume makes it the
28932
- * current session again. Choosing by JSONL mtime is the only reliable signal
28933
- * for "which session is actually live right now."
28934
- *
28935
- * Returns null if no candidate has a corresponding JSONL on disk. Falls back
28936
- * to scanning every project dir if the workspace path can't be resolved
28937
- * (covers specialist sessions whose workspace lives elsewhere).
28938
- */
28939
- async function pickFreshestSessionId(candidates, agentId, opts) {
28940
- if (candidates.length === 0) return null;
28941
- if (candidates.length === 1) return candidates[0];
28942
- const projectsRoot = opts.claudeProjectsDirOverride ?? join$1(homedir$1(), ".claude", "projects");
28943
- let candidatePaths = [];
28944
- try {
28945
- const workspace = await Effect.runPromise(getAgentWorkspace(agentId));
28946
- if (workspace) {
28947
- const projectDir = join$1(projectsRoot, encodeClaudeProjectDir(workspace));
28948
- candidatePaths = candidates.map((id) => ({
28949
- id,
28950
- path: join$1(projectDir, `${id}.jsonl`)
28951
- }));
28952
- }
28953
- } catch {}
28954
- if (candidatePaths.length === 0) try {
28955
- const dirs = await readdir(projectsRoot);
28956
- const SAFE_DIR = /^[a-zA-Z0-9_.-]+$/;
28957
- for (const id of candidates) for (const dir of dirs) {
28958
- if (!SAFE_DIR.test(dir)) continue;
28959
- candidatePaths.push({
28960
- id,
28961
- path: join$1(projectsRoot, dir, `${id}.jsonl`)
28962
- });
28963
- }
28964
- } catch {}
28965
- let best = null;
28966
- for (const { id, path } of candidatePaths) try {
28967
- const s = await stat$1(path);
28968
- if (!best || s.mtimeMs > best.mtimeMs) best = {
28969
- id,
28970
- mtimeMs: s.mtimeMs
28971
- };
28972
- } catch {}
28973
- return best ? best.id : candidates[candidates.length - 1] ?? null;
28974
- }
28975
- /** Async equivalent of getLatestSessionId from lib/agents.ts. */
28976
- async function resolveClaudeSessionId(agentId, opts = {}) {
28977
- const agentDir = join$1(opts.agentsDirOverride ?? join$1(getOverdeckHome(), "agents"), agentId);
28978
- const sessionIdTrimmed = (await readOptional$3(join$1(agentDir, "session.id")))?.trim();
28979
- if (sessionIdTrimmed) return sessionIdTrimmed;
28980
- const sessionsRaw = await readOptional$3(join$1(agentDir, "sessions.json"));
28981
- if (sessionsRaw) try {
28982
- const parsed = JSON.parse(sessionsRaw);
28983
- if (Array.isArray(parsed)) {
28984
- const fresh = await pickFreshestSessionId(parsed.filter((v) => typeof v === "string" && v.trim().length > 0), agentId, opts);
28985
- if (fresh) return fresh;
28986
- }
28987
- } catch {}
28988
- try {
28989
- const runtimeState = await (opts.getRuntimeStateAsync ?? ((id) => Effect.runPromise(getAgentRuntimeState(id))))(agentId);
28990
- if (runtimeState?.claudeSessionId) return runtimeState.claudeSessionId;
28991
- } catch {}
28992
- return null;
28993
- }
28994
- /**
28995
- * Read the Claude session id PINNED into an agent/conversation launcher.sh.
28996
- *
28997
- * The launcher is what spawns the live tmux pane: it runs
28998
- * `claude … --session-id <uuid>` (or `--resume <uuid>`), so the pinned id is the
28999
- * EXACT session the Terminal tab attaches to — the only deterministic ground
29000
- * truth for "which session is live right now." Resolving the transcript panel
29001
- * from this id makes the Conversation tab match the Terminal tab by construction,
29002
- * instead of guessing via JSONL mtime (racy: an older session's file gets touched
29003
- * by a compaction summary write-back or a transient relaunch, its mtime jumps
29004
- * ahead of the live session's, and the panel renders the wrong transcript).
29005
- *
29006
- * Checks the conversation launcher dir first, then the agent launcher dir.
29007
- * Returns the uuid, or null when no launcher exists or it pins no session id.
29008
- */
29009
- const LAUNCHER_UUID = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
29010
- const LAUNCHER_SESSION_ID_RE = new RegExp(`--session-id\\s+'?(${LAUNCHER_UUID})'?`);
29011
- const LAUNCHER_RESUME_RE = new RegExp(`--resume\\s+'?(${LAUNCHER_UUID})'?`);
29012
- async function readLauncherPinnedSessionId(tmuxSession, opts = {}) {
29013
- const home = opts.overdeckHomeOverride ?? getOverdeckHome();
29014
- const candidates = [join$1(home, "conversations", tmuxSession, "launcher.sh"), join$1(home, "agents", tmuxSession, "launcher.sh")];
29015
- for (const path of candidates) {
29016
- const text = await readOptional$3(path);
29017
- if (!text) continue;
29018
- const match = LAUNCHER_SESSION_ID_RE.exec(text) ?? LAUNCHER_RESUME_RE.exec(text);
29019
- if (match) return match[1];
29020
- }
29021
- return null;
29022
- }
29023
- /** Read the harness recorded in the agent's state.json, or null when absent. */
29024
- async function resolveAgentHarness(agentId, opts = {}) {
29025
- if (opts.agentsDirOverride) return readRollbackAgentHarnessFromDir(opts.agentsDirOverride, agentId);
29026
- return getAgentStateSync(agentId)?.harness ?? null;
29027
- }
29028
- /**
29029
- * Resolve the Codex rollout JSONL for a codex-harness agent (PAN-1805).
29030
- *
29031
- * Fast path: the persisted codex-thread-id maps directly to its rollout file.
29032
- * Lazy fallback (same shape as the conversation panel's PAN-1690 fix): codex
29033
- * writes the rollout only on the first turn, so a spawn-time thread-id capture
29034
- * can miss it — the per-agent CODEX_HOME holds only this agent's rollouts, so
29035
- * the newest one is its current thread.
29036
- */
29037
- async function resolveCodexRolloutPath(agentId, opts = {}) {
29038
- const agentDir = join$1(opts.agentsDirOverride ?? join$1(getOverdeckHome(), "agents"), agentId);
29039
- const codexHome = join$1(agentDir, "codex-home");
29040
- if (!await pathExists$5(codexHome)) return null;
29041
- const { findRolloutPath, findLatestRollout } = await import("./codex-D3QWVTal.js");
29042
- const threadId = (await readOptional$3(join$1(agentDir, "codex-thread-id")))?.trim();
29043
- if (threadId) {
29044
- const rollout = findRolloutPath(codexHome, threadId);
29045
- if (rollout) return rollout;
29046
- }
29047
- return findLatestRollout(codexHome);
29048
- }
29049
- /**
29050
- * Resolve the pi/kimi session JSONL (PAN-1908). Pi writes its session transcript
29051
- * as `<iso-ts>_<session-id>.jsonl` either in the agent dir's `sessions/` subdir
29052
- * (conversations) OR in the agent dir root (work agents) — so check both. The dir
29053
- * also holds `cost-events.jsonl` / `activity.jsonl`, which are NOT transcripts.
29054
- * Return the freshest transcript by mtime, or null if pi hasn't written one yet.
29055
- */
29056
- async function resolvePiSessionPath(agentId, opts = {}) {
29057
- const agentDir = join$1(opts.agentsDirOverride ?? join$1(getOverdeckHome(), "agents"), agentId);
29058
- const NON_TRANSCRIPT = new Set(["cost-events.jsonl", "activity.jsonl"]);
29059
- let best = null;
29060
- for (const dir of [join$1(agentDir, "sessions"), agentDir]) {
29061
- let entries;
29062
- try {
29063
- entries = await readdir(dir);
29064
- } catch {
29065
- continue;
29066
- }
29067
- for (const name of entries) {
29068
- if (!name.endsWith(".jsonl") || NON_TRANSCRIPT.has(name)) continue;
29069
- const p = join$1(dir, name);
29070
- try {
29071
- const m = (await stat$1(p)).mtimeMs;
29072
- if (!best || m > best.mtime) best = {
29073
- path: p,
29074
- mtime: m
29075
- };
29076
- } catch {}
29077
- }
29078
- }
29079
- return best?.path ?? null;
29080
- }
29081
- /**
29082
- * Resolve the JSONL transcript for an agent.
29083
- *
29084
- * Codex agents resolve to their rollout JSONL (PAN-1805). For claude-code
29085
- * agents, returns the absolute path if both the claudeSessionId is known AND
29086
- * the corresponding JSONL file exists; otherwise null.
29087
- */
29088
- async function resolveJsonlPath(agentId, workspacePath, opts = {}) {
29089
- const harness = await resolveAgentHarness(agentId, opts);
29090
- if (harness === "codex") return resolveCodexRolloutPath(agentId, opts);
29091
- if (harness === "pi") return resolvePiSessionPath(agentId, opts);
29092
- const claudeSessionId = await resolveClaudeSessionId(agentId, opts);
29093
- if (!claudeSessionId) return null;
29094
- const jsonlPath = join$1(opts.claudeProjectsDirOverride ?? join$1(homedir$1(), ".claude", "projects"), encodeClaudeProjectDir(workspacePath), `${claudeSessionId}.jsonl`);
29095
- if (await pathExists$5(jsonlPath)) return jsonlPath;
29096
- return null;
29097
- }
29098
- //#endregion
29099
- //#region services/read-workspace-file.ts
29100
- init_activity_logger();
29101
- init_event_store();
29102
- init_briefing_freshness();
29103
28926
  init_claude_permissions();
29104
28927
  init_codex();
29105
28928
  init_providers();
29106
28929
  init_openai_auth();
29107
28930
  init_claude_auth();
29108
28931
  init_launcher_generator();
28932
+ init_agent_record_sync();
29109
28933
  init_layers();
29110
28934
  init_bridge_token();
29111
28935
  init_pty_token();
@@ -29115,6 +28939,12 @@ init_pi_fifo();
29115
28939
  init_control_settings();
29116
28940
  init_operator_interventions();
29117
28941
  init_transcript_landing();
28942
+ init_agents$2();
28943
+ init_agent_input_detection();
28944
+ init_agent_enrichment();
28945
+ init_jsonl_resolver();
28946
+ init_flywheel_run_state();
28947
+ init_src();
29118
28948
  init_projects();
29119
28949
  const MAX_WORKSPACE_FILE_BYTES = 256 * 1024;
29120
28950
  const DEFAULT_CONTEXT_LINES = 10;
@@ -29283,6 +29113,7 @@ function readWorkspaceFileEffect(input) {
29283
29113
  * out of cwd is no broader than what the caller could query directly
29284
29114
  * with an absolute path.
29285
29115
  */
29116
+ init_src();
29286
29117
  const MAX_PATH_LENGTH = 4096;
29287
29118
  async function resolveFilePathExists(input) {
29288
29119
  const { cwd, path } = input;
@@ -29334,9 +29165,12 @@ function resolveFilePathExistsEffect(input) {
29334
29165
  * Terminal RPC methods (subscribeTerminal, terminalOpen/Write/Resize/Close)
29335
29166
  * are implemented via TerminalService (dual-runtime PTY, B20).
29336
29167
  */
29168
+ init_src();
29337
29169
  init_paths();
29338
29170
  init_tmux();
29339
29171
  init_projects();
29172
+ init_origin_validation();
29173
+ init_http_helpers();
29340
29174
  function storedToDomainEvent(stored) {
29341
29175
  return {
29342
29176
  type: stored.type,
@@ -29359,7 +29193,7 @@ function normalizedIssueId(value) {
29359
29193
  function conversationDiscoveringStream() {
29360
29194
  return Stream.succeed({ kind: "discovering" }).pipe(Stream.repeat(Schedule.fixed("2 seconds")));
29361
29195
  }
29362
- function streamResolvedFullParseSnapshots(resolve, parse, model) {
29196
+ function streamResolvedFullParseSnapshots(resolve, parse, model, unresolvedMeansEmpty = false) {
29363
29197
  return Stream.callback((queue) => Effect.acquireRelease(Effect.promise(async () => {
29364
29198
  let stopped = false;
29365
29199
  let resolving = false;
@@ -29369,6 +29203,14 @@ function streamResolvedFullParseSnapshots(resolve, parse, model) {
29369
29203
  let parsing = false;
29370
29204
  let pendingReparse = false;
29371
29205
  let sessionFile = null;
29206
+ let announcedEmpty = false;
29207
+ let hasContent = false;
29208
+ const stopDiscovery = () => {
29209
+ if (discoveryTimer) {
29210
+ clearInterval(discoveryTimer);
29211
+ discoveryTimer = null;
29212
+ }
29213
+ };
29372
29214
  const offer = (event) => {
29373
29215
  try {
29374
29216
  Queue.offerUnsafe(queue, event);
@@ -29383,6 +29225,10 @@ function streamResolvedFullParseSnapshots(resolve, parse, model) {
29383
29225
  parsing = true;
29384
29226
  try {
29385
29227
  const result = await parse(sessionFile);
29228
+ if (result.messages.length > 0 && !hasContent) {
29229
+ hasContent = true;
29230
+ stopDiscovery();
29231
+ }
29386
29232
  offer({
29387
29233
  kind: "messages",
29388
29234
  messages: result.messages,
@@ -29401,36 +29247,56 @@ function streamResolvedFullParseSnapshots(resolve, parse, model) {
29401
29247
  }
29402
29248
  }
29403
29249
  };
29250
+ const watchFile = (file) => {
29251
+ try {
29252
+ watcher = watch(file, () => {
29253
+ if (debounce) return;
29254
+ debounce = setTimeout(() => {
29255
+ debounce = null;
29256
+ emit();
29257
+ }, 300);
29258
+ });
29259
+ } catch {}
29260
+ };
29404
29261
  const tryResolve = async () => {
29405
- if (stopped || resolving || sessionFile) return;
29262
+ if (stopped || resolving || hasContent) return;
29406
29263
  resolving = true;
29407
29264
  try {
29408
29265
  const resolved = await resolve();
29409
29266
  if (!resolved) {
29410
- offer({ kind: "discovering" });
29267
+ if (unresolvedMeansEmpty) {
29268
+ if (!sessionFile && !announcedEmpty) {
29269
+ announcedEmpty = true;
29270
+ offer({
29271
+ kind: "messages",
29272
+ messages: [],
29273
+ workLog: [],
29274
+ streaming: false,
29275
+ snapshot: true
29276
+ });
29277
+ }
29278
+ return;
29279
+ }
29280
+ if (!sessionFile) offer({ kind: "discovering" });
29411
29281
  return;
29412
29282
  }
29413
- sessionFile = resolved;
29414
- if (discoveryTimer) {
29415
- clearInterval(discoveryTimer);
29416
- discoveryTimer = null;
29417
- }
29418
- await emit();
29419
- try {
29420
- watcher = watch(resolved, () => {
29421
- if (debounce) return;
29422
- debounce = setTimeout(() => {
29423
- debounce = null;
29424
- emit();
29425
- }, 300);
29426
- });
29427
- } catch {}
29283
+ if (resolved !== sessionFile) {
29284
+ if (watcher) {
29285
+ try {
29286
+ watcher.close();
29287
+ } catch {}
29288
+ watcher = null;
29289
+ }
29290
+ sessionFile = resolved;
29291
+ await emit();
29292
+ if (!stopped) watchFile(resolved);
29293
+ } else await emit();
29428
29294
  } finally {
29429
29295
  resolving = false;
29430
29296
  }
29431
29297
  };
29432
29298
  await tryResolve();
29433
- if (!sessionFile) discoveryTimer = setInterval(() => {
29299
+ if (!hasContent) discoveryTimer = setInterval(() => {
29434
29300
  tryResolve();
29435
29301
  }, 2e3);
29436
29302
  return { stop: () => {
@@ -29808,13 +29674,15 @@ const PanRpcLayer = PanRpcGroup.toLayer(Effect.gen(function* () {
29808
29674
  const conv = getConversationByName(input.conversationName);
29809
29675
  if (!conv && /^(agent-|planning-|specialist-)/.test(input.conversationName)) {
29810
29676
  const harness = yield* Effect.promise(() => resolveAgentHarness(input.conversationName));
29677
+ if (harness === "ohmypi") return streamResolvedFullParseSnapshots(() => resolvePiSessionPath(input.conversationName), parseOhmypiConversationMessages, null);
29811
29678
  if (harness === "pi") return streamResolvedFullParseSnapshots(() => resolvePiSessionPath(input.conversationName), parsePiConversationMessages, null);
29812
29679
  if (harness === "codex") return streamResolvedFullParseSnapshots(() => resolveCodexRolloutPath(input.conversationName), parseCodexConversationMessages, null);
29813
29680
  return conversationDiscoveringStream();
29814
29681
  }
29815
29682
  if (!conv) return conversationDiscoveringStream();
29816
- if (conv.harness === "pi") return streamResolvedFullParseSnapshots(() => resolvePiSessionPath(conv.tmuxSession), parsePiConversationMessages, conv.model ?? null);
29817
- if (conv.harness === "codex") return streamResolvedFullParseSnapshots(() => resolveCodexRolloutPath(conv.tmuxSession), parseCodexConversationMessages, conv.model ?? null);
29683
+ if (conv.harness === "ohmypi") return streamResolvedFullParseSnapshots(() => resolvePiSessionPath(conv.tmuxSession), parseOhmypiConversationMessages, conv.model ?? null, true);
29684
+ if (conv.harness === "pi") return streamResolvedFullParseSnapshots(() => resolvePiSessionPath(conv.tmuxSession), parsePiConversationMessages, conv.model ?? null, true);
29685
+ if (conv.harness === "codex") return streamResolvedFullParseSnapshots(() => resolveCodexRolloutPath(conv.tmuxSession), parseCodexConversationMessages, conv.model ?? null, true);
29818
29686
  if (conv.harness !== "claude-code" && conv.harness != null) return conversationDiscoveringStream();
29819
29687
  const launcherTmuxSession = conv.tmuxSession;
29820
29688
  const resolvedSessionId = (launcherTmuxSession ? yield* Effect.promise(() => readLauncherPinnedSessionId(launcherTmuxSession)) : null) ?? conv.claudeSessionId;
@@ -30077,6 +29945,32 @@ const websocketRpcRouteLayer = Layer.unwrap(Effect.gen(function* () {
30077
29945
  }));
30078
29946
  //#endregion
30079
29947
  //#region routes/costs.ts
29948
+ init_types();
29949
+ init_specs();
29950
+ init_io();
29951
+ init_pan_dir();
29952
+ init_docker_stats();
29953
+ init_review_status$1();
29954
+ init_dashboard_auth();
29955
+ init_reconciler();
29956
+ init_issue_cost_resolver();
29957
+ init_running_agents_cache();
29958
+ init_config$1();
29959
+ init_health();
29960
+ init_runtimes();
29961
+ init_specialists();
29962
+ init_concurrency();
29963
+ init_no_resume_mode();
29964
+ init_concurrency$1();
29965
+ init_deacon();
29966
+ init_service();
29967
+ init_system_health_service();
29968
+ init_git_info();
29969
+ init_agent_projection();
29970
+ init_agents();
29971
+ init_remote_agents();
29972
+ init_resource_discovery();
29973
+ init_issues();
30080
29974
  /**
30081
29975
  * Costs route module — Effect HttpRouter.Layer (PAN-428 B10)
30082
29976
  *
@@ -30093,7 +29987,11 @@ const websocketRpcRouteLayer = Layer.unwrap(Effect.gen(function* () {
30093
29987
  * POST /api/costs/sync-wal
30094
29988
  * POST /api/costs/reconcile
30095
29989
  */
29990
+ init_http_helpers();
29991
+ init_costs();
30096
29992
  init_cost_sync();
29993
+ init_sync_wal();
29994
+ init_http_handler();
30097
29995
  const getCostsSummaryRoute = HttpRouter.add("GET", "/api/costs/summary", httpHandler(Effect.gen(function* () {
30098
29996
  const request = yield* HttpServerRequest.HttpServerRequest;
30099
29997
  const urlOpt = HttpServerRequest.toURL(request);
@@ -30298,7 +30196,7 @@ const postCostsSyncWalRoute = HttpRouter.add("POST", "/api/costs/sync-wal", http
30298
30196
  })));
30299
30197
  const postCostsReconcileRoute = HttpRouter.add("POST", "/api/costs/reconcile", httpHandler(Effect.gen(function* () {
30300
30198
  const result = yield* Effect.tryPromise({
30301
- try: () => reconcile(),
30199
+ try: () => Effect.runPromise(reconcile()),
30302
30200
  catch: (err) => new Error(err instanceof Error ? err.message : String(err))
30303
30201
  });
30304
30202
  console.log(`[reconciler] Sweep complete: ${result.eventsImported ?? 0} imported`);
@@ -30338,7 +30236,8 @@ const costsRouteLayer = Layer.mergeAll(getCostsSummaryRoute, getCostsByIssueRout
30338
30236
  * PUT /api/cloister/config
30339
30237
  * GET /api/cloister/agents/health
30340
30238
  */
30341
- init_agents$1();
30239
+ init_http_helpers();
30240
+ init_http_handler();
30342
30241
  const readJsonBody$11 = Effect.gen(function* () {
30343
30242
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
30344
30243
  try {
@@ -30475,8 +30374,10 @@ const cloisterRouteLayer = Layer.mergeAll(getCloisterStatusRoute, postCloisterSt
30475
30374
  * DELETE /api/resources/docker/volume/:name
30476
30375
  * POST /api/resources/docker/prune-volumes
30477
30376
  */
30478
- init_docker_stats();
30479
- init_agents$1();
30377
+ init_http_helpers();
30378
+ init_domain_services();
30379
+ init_http_handler();
30380
+ init_agents$2();
30480
30381
  init_tmux();
30481
30382
  const execAsync$6 = promisify(exec);
30482
30383
  function formatUptime(startedAt) {
@@ -30708,6 +30609,47 @@ const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/conta
30708
30609
  })));
30709
30610
  const resourcesRouteLayer = Layer.mergeAll(getResourcesRoute, getContainerHistoryRoute, getContainerDetailsRoute, deleteDockerContainerRoute, postPruneContainersRoute, deleteDockerNetworkRoute, deleteDockerVolumeRoute, postPruneVolumesRoute, postRestartContainerRoute, postStartContainerRoute, getContainerLogsRoute);
30710
30611
  //#endregion
30612
+ //#region services/model-origin-enrich.ts
30613
+ init_agents$2();
30614
+ init_config_yaml();
30615
+ /**
30616
+ * Map a session-node type to the pipeline Role whose model distribution it draws
30617
+ * from. Returns null for types that never sample a top-level weighted role
30618
+ * distribution: 'reviewer' (sub-role convoy — resolves via a sub-role scalar),
30619
+ * 'legacy', and anything unrecognized.
30620
+ */
30621
+ function sessionTypeToModelRole(type) {
30622
+ switch (type) {
30623
+ case "work": return "work";
30624
+ case "review": return "review";
30625
+ case "test": return "test";
30626
+ case "ship":
30627
+ case "merge": return "ship";
30628
+ case "planning": return "plan";
30629
+ case "strike": return "strike";
30630
+ default: return null;
30631
+ }
30632
+ }
30633
+ /**
30634
+ * Mutate `sections` in place, attaching `modelOrigin` to each node whose role uses
30635
+ * a weighted distribution. Scalar roles are left untouched (the client renders the
30636
+ * resolved model with no bars/hash). Reconstructs the spawn key from the agent's
30637
+ * persisted `modelSpawnKey` (exact) or, for pre-PAN-2053 agents, `${role}:${issueId}`.
30638
+ * Wrapped so a config/state error can never break the tree.
30639
+ */
30640
+ function enrichSessionsWithModelOrigin(sections, fallbackIssueId) {
30641
+ try {
30642
+ const { config } = loadConfigSync();
30643
+ for (const section of sections) {
30644
+ const role = sessionTypeToModelRole(section.type);
30645
+ if (!role) continue;
30646
+ const state = getAgentStateSync(section.sessionId);
30647
+ const origin = computeModelOrigin(role, state?.modelSpawnKey ?? `${role}:${state?.issueId ?? fallbackIssueId}`, config);
30648
+ if (origin) section.modelOrigin = origin;
30649
+ }
30650
+ } catch {}
30651
+ }
30652
+ //#endregion
30711
30653
  //#region services/session-presence.ts
30712
30654
  /**
30713
30655
  * Derive session presence from runtime state, tmux session existence, and recent activity.
@@ -30729,6 +30671,7 @@ async function deriveSessionPresence(agentId, rtState, tmuxSessionNames) {
30729
30671
  }
30730
30672
  //#endregion
30731
30673
  //#region ../../lib/settings-api.ts
30674
+ init_prd_locations();
30732
30675
  /**
30733
30676
  * Settings API Adapter
30734
30677
  *
@@ -30790,19 +30733,7 @@ function sanitizeApiTtsConfig(tts) {
30790
30733
  function getDefaultConversationModelApi() {
30791
30734
  const { config } = loadConfigSync();
30792
30735
  if (config.defaultConversationModel) return resolveModelIdSync(config.defaultConversationModel);
30793
- if (config.enabledProviders.has("openai")) return resolveModelIdSync("gpt-5.5");
30794
- if (config.enabledProviders.has("minimax")) return resolveModelIdSync("minimax-m2.7-highspeed");
30795
- if (config.enabledProviders.has("google")) return resolveModelIdSync("gemini-3.1-pro-preview");
30796
- if (config.enabledProviders.has("kimi")) return resolveModelIdSync("kimi-k2.5");
30797
- if (config.enabledProviders.has("zai")) return resolveModelIdSync("glm-5.2");
30798
- if (config.enabledProviders.has("mimo")) return resolveModelIdSync("mimo-v2.5-pro");
30799
- if (config.enabledProviders.has("nous")) return resolveModelIdSync("qwen/qwen3.6-plus");
30800
- if (config.enabledProviders.has("dashscope")) return resolveModelIdSync("qwen3-coder-plus");
30801
- if (config.enabledProviders.has("openrouter")) {
30802
- const fav = config.openrouterFavorites[0];
30803
- if (fav) return resolveModelIdSync(fav);
30804
- }
30805
- return resolveModelIdSync("claude-sonnet-4-6");
30736
+ throw new Error("No default model configured — set models.default_conversation_model");
30806
30737
  }
30807
30738
  const ROLE_NAMES = [
30808
30739
  "plan",
@@ -30927,7 +30858,27 @@ function normalizeRolesConfig(roles) {
30927
30858
  for (const [role, roleConfig] of Object.entries(roles)) normalized[role] = normalizeRoleConfig(roleConfig);
30928
30859
  return normalized;
30929
30860
  }
30930
- function validateModelRef(fieldPath, ref, effectiveWorkhorses, errors, warnings, allowWorkhorseRef, allowParentRef = false) {
30861
+ function validateModelRef(fieldPath, ref, effectiveWorkhorses, errors, warnings, allowWorkhorseRef, allowParentRef = false, allowDistribution = false) {
30862
+ if (Array.isArray(ref)) {
30863
+ if (!allowDistribution) {
30864
+ errors.push(`${fieldPath} distribution not allowed here; must be a scalar model reference`);
30865
+ return;
30866
+ }
30867
+ if (ref.length === 0) {
30868
+ errors.push(`${fieldPath} distribution must be a non-empty array`);
30869
+ return;
30870
+ }
30871
+ for (let i = 0; i < ref.length; i++) {
30872
+ const entry = ref[i];
30873
+ if (!isRecord$3(entry)) {
30874
+ errors.push(`${fieldPath}[${i}] must be an object with model and weight`);
30875
+ continue;
30876
+ }
30877
+ if (!Number.isInteger(entry.weight) || entry.weight <= 0) errors.push(`${fieldPath}[${i}].weight must be a positive integer`);
30878
+ validateModelRef(`${fieldPath}[${i}].model`, entry.model, effectiveWorkhorses, errors, warnings, allowWorkhorseRef);
30879
+ }
30880
+ return;
30881
+ }
30931
30882
  if (typeof ref !== "string" || ref.trim() === "") {
30932
30883
  errors.push(`${fieldPath} must be a non-empty model reference`);
30933
30884
  return;
@@ -30957,7 +30908,7 @@ function validateModelRef(fieldPath, ref, effectiveWorkhorses, errors, warnings,
30957
30908
  }
30958
30909
  function validateRoleFields(fieldPath, roleConfig, errors) {
30959
30910
  const harness = roleConfig.harness;
30960
- if (harness !== void 0 && harness !== null && harness !== "" && harness !== "claude-code" && harness !== "pi" && harness !== "codex") errors.push(`${fieldPath}.harness must be claude-code, pi, codex, null, or empty string`);
30911
+ if (harness !== void 0 && harness !== null && harness !== "" && harness !== "claude-code" && harness !== "ohmypi" && harness !== "codex") errors.push(`${fieldPath}.harness must be claude-code, ohmypi, codex, null, or empty string`);
30961
30912
  const effort = roleConfig.effort;
30962
30913
  if (effort !== void 0 && !ROLE_EFFORTS.includes(effort)) errors.push(`${fieldPath}.effort must be one of ${ROLE_EFFORTS.join(", ")}`);
30963
30914
  const maxAgents = roleConfig.maxAgents;
@@ -30997,14 +30948,24 @@ function validateWorkhorsesAndRoles(settings, errors, warnings) {
30997
30948
  errors.push(`roles.${role}.model must be a non-empty model reference`);
30998
30949
  continue;
30999
30950
  }
31000
- validateModelRef(`roles.${role}.model`, rawRoleConfig.model, effectiveWorkhorses, errors, warnings, true);
30951
+ validateModelRef(`roles.${role}.model`, rawRoleConfig.model, effectiveWorkhorses, errors, warnings, true, false, true);
31001
30952
  validateRoleFields(`roles.${role}`, rawRoleConfig, errors);
31002
30953
  const effort = rawRoleConfig.effort;
31003
30954
  if (typeof effort === "string" && ROLE_EFFORTS.includes(effort)) {
31004
- const resolvedModel = resolveModelRefToId(rawRoleConfig.model ?? DEFAULT_ROLES[role]?.model, effectiveWorkhorses);
31005
- if (resolvedModel) {
31006
- const supported = getModelEffortLevelsSync(resolvedModel);
31007
- if (supported !== void 0 && !supported.includes(effort)) errors.push(`roles.${role}.effort '${effort}' is not supported by ${resolvedModel} (supported: ${supported.join(", ")})`);
30955
+ const modelRef = rawRoleConfig.model ?? DEFAULT_ROLES[role]?.model;
30956
+ if (Array.isArray(modelRef)) for (const entry of modelRef) {
30957
+ const resolvedModel = resolveModelRefToId(entry.model, effectiveWorkhorses);
30958
+ if (resolvedModel) {
30959
+ const supported = getModelEffortLevelsSync(resolvedModel);
30960
+ if (supported !== void 0 && !supported.includes(effort)) errors.push(`roles.${role}.effort '${effort}' is not supported by ${resolvedModel} (supported: ${supported.join(", ")})`);
30961
+ }
30962
+ }
30963
+ else {
30964
+ const resolvedModel = resolveModelRefToId(modelRef, effectiveWorkhorses);
30965
+ if (resolvedModel) {
30966
+ const supported = getModelEffortLevelsSync(resolvedModel);
30967
+ if (supported !== void 0 && !supported.includes(effort)) errors.push(`roles.${role}.effort '${effort}' is not supported by ${resolvedModel} (supported: ${supported.join(", ")})`);
30968
+ }
31008
30969
  }
31009
30970
  }
31010
30971
  if (rawRoleConfig.sub !== void 0) if (!isRecord$3(rawRoleConfig.sub)) errors.push(`roles.${role}.sub must be an object`);
@@ -31392,7 +31353,7 @@ function validateSettingsApi(settings) {
31392
31353
  errors.push(`Unknown provider harness entry "${provider}"`);
31393
31354
  continue;
31394
31355
  }
31395
- if (harness !== void 0 && harness !== "" && harness !== "claude-code" && harness !== "pi" && harness !== "codex") errors.push(`models.provider_harnesses.${provider} must be claude-code, pi, codex, or empty string`);
31356
+ if (harness !== void 0 && harness !== "" && harness !== "claude-code" && harness !== "ohmypi" && harness !== "codex") errors.push(`models.provider_harnesses.${provider} must be claude-code, ohmypi, codex, or empty string`);
31396
31357
  }
31397
31358
  if (settings.models?.overrides) {
31398
31359
  const validModelIds = Object.keys(MODEL_CAPABILITIES);
@@ -31631,14 +31592,6 @@ const saveOpenRouterFavorites = (favorites) => Effect.tryPromise({
31631
31592
  });
31632
31593
  //#endregion
31633
31594
  //#region services/agent-status.ts
31634
- init_tracker_config();
31635
- init_cache_service();
31636
- init_internal_token();
31637
- init_pipeline_notifier();
31638
- init_review_status_sync();
31639
- init_review_status();
31640
- init_types();
31641
- init_issue_service_singleton();
31642
31595
  init_review_agent();
31643
31596
  function normalizeAgentStatus(status) {
31644
31597
  switch (status) {
@@ -31681,8 +31634,8 @@ function normalizeAgentStatus(status) {
31681
31634
  *
31682
31635
  * Async-only (fs/promises); safe to call from the dashboard server.
31683
31636
  */
31684
- init_agents$1();
31685
- init_specialists();
31637
+ init_agents$2();
31638
+ init_jsonl_resolver();
31686
31639
  init_tmux();
31687
31640
  const CONVOY_REVIEWER_ROLES = [
31688
31641
  "correctness",
@@ -31878,11 +31831,17 @@ async function buildReviewerNodes(opts) {
31878
31831
  * POST /api/command-deck/planning/:issueId/init
31879
31832
  * GET /api/command-deck/projects
31880
31833
  */
31834
+ init_http_helpers();
31881
31835
  init_child_env();
31882
- init_agents$1();
31836
+ init_domain_services();
31837
+ init_read_model();
31838
+ init_agents$2();
31839
+ init_costs();
31883
31840
  init_tmux();
31884
31841
  init_projects();
31885
- init_io();
31842
+ init_config_yaml();
31843
+ init_http_handler();
31844
+ init_jsonl_resolver();
31886
31845
  const execAsync$5 = promisify(exec);
31887
31846
  const execFileAsync$4 = promisify(execFile);
31888
31847
  function getIssueDataService$2() {
@@ -32217,6 +32176,7 @@ async function fetchActivityDataWithContext(issueId, context = {}) {
32217
32176
  if (!b.startedAt) return -1;
32218
32177
  return a.startedAt.localeCompare(b.startedAt);
32219
32178
  });
32179
+ enrichSessionsWithModelOrigin(sections, issueId);
32220
32180
  let costByStage = {};
32221
32181
  let totalCost = null;
32222
32182
  try {
@@ -32610,13 +32570,12 @@ Be specific: reference actual file names, function names, requirement text, disc
32610
32570
  const { command: cliCmd, args: cliArgs } = getAgentCommandSync(statusModelId);
32611
32571
  const modelFlag = cliArgs.length > 0 ? ` ${cliArgs.join(" ")}` : "";
32612
32572
  const promptFile = join$1(planningDir, ".status-review-prompt.tmp");
32613
- const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-CX4sIKbS.js");
32614
- const { loadConfigSync: loadYamlConfig } = await import("./config-yaml-BPbPSBp2.js");
32573
+ const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-Dre9thog.js");
32615
32574
  let providerEnvStr = "";
32616
32575
  let providerEnv = {};
32617
32576
  const statusProvider = getProviderForModelSync(statusModelId);
32618
32577
  if (statusProvider.name !== "anthropic") {
32619
- const { config } = loadYamlConfig();
32578
+ const { config } = loadConfigSync();
32620
32579
  const apiKey = config.apiKeys[statusProvider.name];
32621
32580
  if (apiKey) {
32622
32581
  providerEnv = getProviderEnvSync(statusProvider, apiKey);
@@ -32694,7 +32653,7 @@ const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/
32694
32653
  let safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "-");
32695
32654
  let processedContent = content;
32696
32655
  if (safeName.endsWith(".vtt")) {
32697
- const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-DsT8ov3-.js"));
32656
+ const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-Dik5Bh9h.js"));
32698
32657
  processedContent = vttToMarkdown(content);
32699
32658
  safeName = safeName.replace(/\.vtt$/, ".md");
32700
32659
  }
@@ -32906,7 +32865,6 @@ async function fetchProjectTree() {
32906
32865
  const commandDeckRouteLayer = Layer.mergeAll(getMissionControlActivityRoute, getMissionControlPlanningRoute, postMissionControlStatusReviewRoute, postMissionControlUploadRoute, postMissionControlSyncDiscussionsRoute, postMissionControlPlanningInitRoute, getMissionControlProjectsRoute);
32907
32866
  //#endregion
32908
32867
  //#region routes/remote.ts
32909
- init_specs();
32910
32868
  /**
32911
32869
  * Remote route module — Effect HttpRouter.Layer (PAN-428 B14)
32912
32870
  *
@@ -32921,7 +32879,11 @@ init_specs();
32921
32879
  * GET /api/remote/workspaces/:issueId/agent/output
32922
32880
  * POST /api/remote/workspaces/:issueId/agent/tell
32923
32881
  */
32882
+ init_http_helpers();
32883
+ init_remote();
32924
32884
  init_config();
32885
+ init_domain_services();
32886
+ init_http_handler();
32925
32887
  async function loadRemoteWorkspaceMetadata(issueId) {
32926
32888
  const normalizedId = issueId.toLowerCase().replace(/[^a-z0-9-]/g, "-");
32927
32889
  const content = await readFile(join$1(homedir$1(), ".overdeck", "workspaces", `${normalizedId}.yaml`), "utf-8").catch(() => null);
@@ -33285,7 +33247,7 @@ async function getTtsDaemonAuthTokenPromise() {
33285
33247
  return existing;
33286
33248
  }
33287
33249
  } catch {}
33288
- const token = randomBytes$1(32).toString("base64url");
33250
+ const token = randomBytes(32).toString("base64url");
33289
33251
  await mkdir(dirname$1(QWEN_TTS_AUTH_TOKEN_PATH), {
33290
33252
  recursive: true,
33291
33253
  mode: 448
@@ -36920,8 +36882,10 @@ function isAbortError(error) {
36920
36882
  * POST /api/settings/validate-api-key
36921
36883
  * PUT /api/settings
36922
36884
  */
36885
+ init_http_helpers();
36923
36886
  init_providers();
36924
- init_agents$1();
36887
+ init_http_handler();
36888
+ init_agents$2();
36925
36889
  init_config_yaml();
36926
36890
  const REINDEX_CONFIRM_THRESHOLD_USD = 1;
36927
36891
  const REINDEX_CONFIRM_TTL_MS = 600 * 1e3;
@@ -36934,7 +36898,7 @@ let conversationReindexProgress = {
36934
36898
  chunksIndexed: 0
36935
36899
  };
36936
36900
  function createReindexConfirmationNonce(estimatedUsd) {
36937
- const nonce = randomBytes$1(24).toString("base64url");
36901
+ const nonce = randomBytes(24).toString("base64url");
36938
36902
  const now = Date.now();
36939
36903
  for (const [key, entry] of reindexConfirmationNonces) if (entry.expiresAt <= now) reindexConfirmationNonces.delete(key);
36940
36904
  reindexConfirmationNonces.set(nonce, {
@@ -37727,6 +37691,9 @@ const getProviderEnvConflictsRoute = HttpRouter.add("GET", "/api/settings/provid
37727
37691
  const settingsRouteLayer = Layer.mergeAll(getSettingsRoute, getAvailableModelsRoute, getOptimalDefaultsRoute, getMiniMaxDefaultsRoute, getClaudeAuthRoute, getOpenAIAuthRoute, postTestApiKeyRoute, postValidateApiKeyRoute, getConversationSearchStatusRoute, getConversationSearchReindexEstimateRoute, postConversationSearchReindexRoute, getConversationSearchReindexProgressRoute, putSettingsRoute, putUiThemeRoute, getOpenRouterModelsRoute, putOpenRouterFavoritesRoute, putOpenRouterApiKeyRoute, postOpenRouterTestKeyRoute, getHarnessPolicyRoute, getProviderEnvConflictsRoute);
37728
37692
  //#endregion
37729
37693
  //#region routes/autopreso.ts
37694
+ init_http_helpers();
37695
+ init_http_handler();
37696
+ init_origin_validation();
37730
37697
  const readJsonBody$7 = Effect.gen(function* () {
37731
37698
  const request = yield* HttpServerRequest.HttpServerRequest;
37732
37699
  const contentLength = Number(request.headers["content-length"] ?? 0);
@@ -37790,7 +37757,12 @@ const autopresoRouteLayer = Layer.mergeAll(startRoute, backToStagingRoute, reset
37790
37757
  * GET /api/activity
37791
37758
  * GET /api/activity/:id
37792
37759
  */
37793
- init_agents$1();
37760
+ init_http_helpers();
37761
+ init_domain_services();
37762
+ init_service();
37763
+ init_agents$2();
37764
+ init_git_activity();
37765
+ init_http_handler();
37794
37766
  let _cachedReviewStatuses = null;
37795
37767
  let _cachedReviewStatusesAt = 0;
37796
37768
  const REVIEW_STATUS_CACHE_TTL_MS = 5e3;
@@ -37951,6 +37923,7 @@ const metricsRouteLayer = Layer.mergeAll(getMetricsSummaryRoute, getMetricsCosts
37951
37923
  * Health filtering logic for dashboard health API
37952
37924
  * Determines which agents should be visible in health checks
37953
37925
  */
37926
+ init_config$1();
37954
37927
  init_tmux();
37955
37928
  const KICKOFF_STALL_GRACE_MS = 300 * 1e3;
37956
37929
  /**
@@ -38071,6 +38044,8 @@ const determineHealthStatus = (agentId, stateFile, liveSessions) => Effect.gen(f
38071
38044
  * POST /api/dev/rebuild
38072
38045
  * POST /api/system/restart-dashboard
38073
38046
  */
38047
+ init_http_helpers();
38048
+ init_service();
38074
38049
  init_tmux();
38075
38050
  init_projects();
38076
38051
  init_tracker_config();
@@ -38078,9 +38053,11 @@ init_config_yaml();
38078
38053
  init_config();
38079
38054
  init_issue_id();
38080
38055
  init_io();
38056
+ init_domain_services();
38057
+ init_read_model();
38058
+ init_http_handler();
38081
38059
  init_types();
38082
- init_agents$1();
38083
- init_remote_agents();
38060
+ init_agents$2();
38084
38061
  const execAsync$4 = promisify(exec);
38085
38062
  async function readPackageVersion() {
38086
38063
  let dir = dirname$1(fileURLToPath(import.meta.url));
@@ -38437,7 +38414,7 @@ const postRallyValidateRoute = HttpRouter.add("POST", "/api/rally/validate", Eff
38437
38414
  }, { status: 400 });
38438
38415
  return yield* Effect.promise(async () => {
38439
38416
  try {
38440
- const { RallyRestApi } = await import("./rally-api-D9MqLzbe.js");
38417
+ const { RallyRestApi } = await import("./rally-api-CKayMhOp.js");
38441
38418
  const api = new RallyRestApi({
38442
38419
  apiKey,
38443
38420
  server: server || "https://rally1.rallydev.com"
@@ -38467,6 +38444,33 @@ const postRallyValidateRoute = HttpRouter.add("POST", "/api/rally/validate", Eff
38467
38444
  });
38468
38445
  }));
38469
38446
  const getNoResumeModeRoute = HttpRouter.add("GET", "/api/no-resume-mode", Effect.sync(() => jsonResponse(getNoResumeMode())));
38447
+ /**
38448
+ * Operator "Resume all" — the call-to-action behind the no-resume banner.
38449
+ * PAN-1963 makes agent auto-resume OFF by default at every boot, so after the
38450
+ * dashboard loads, stopped agents wait for the operator to put them back to
38451
+ * work. This clears no-resume mode for the running process (so the Deacon's
38452
+ * patrols and lifecycle-event handlers resume agents again) and immediately
38453
+ * sweeps the stopped work agents rather than waiting for the next patrol tick.
38454
+ */
38455
+ const postResumeAllRoute = HttpRouter.add("POST", "/api/resume-all", Effect.promise(async () => {
38456
+ try {
38457
+ disableNoResumeMode();
38458
+ const resumed = await autoResumeStoppedWorkAgents();
38459
+ console.log(`[resume-all] No-resume mode cleared; resumed ${resumed.length} work agent(s)${resumed.length ? `: ${resumed.join(", ")}` : ""}`);
38460
+ return jsonResponse({
38461
+ ok: true,
38462
+ resumed,
38463
+ count: resumed.length
38464
+ });
38465
+ } catch (error) {
38466
+ const msg = error instanceof Error ? error.message : String(error);
38467
+ console.error("Error resuming all agents:", error);
38468
+ return jsonResponse({
38469
+ ok: false,
38470
+ error: "Failed to resume agents: " + msg
38471
+ }, { status: 500 });
38472
+ }
38473
+ }));
38470
38474
  const getDeaconStatusRoute = HttpRouter.add("GET", "/api/deacon/status", Effect.try({
38471
38475
  try: () => {
38472
38476
  const service = getCloisterService();
@@ -38537,7 +38541,7 @@ const getVersionRoute = HttpRouter.add("GET", "/api/version", Effect.promise(asy
38537
38541
  const version = await getOverdeckVersion();
38538
38542
  let supervisorUrl = null;
38539
38543
  try {
38540
- const { getSupervisorUrlSync } = await import("./supervisor-sXlJGJUM.js");
38544
+ const { getSupervisorUrlSync } = await import("./supervisor-ti-xrTNH.js");
38541
38545
  supervisorUrl = getSupervisorUrlSync();
38542
38546
  } catch {}
38543
38547
  return jsonResponse({
@@ -38633,7 +38637,7 @@ const getPlanningStatusRoute = HttpRouter.add("GET", "/api/planning/:issueId/sta
38633
38637
  const remoteState = loadRemoteAgentState(sessionName);
38634
38638
  const isRemote = !!remoteState;
38635
38639
  const vmName = remoteState?.vmName ?? "";
38636
- const { getAgentStateSync } = await import("./agents-DD8lFXJe2.js");
38640
+ const { getAgentStateSync } = await import("./agents-DsFz3A8y.js");
38637
38641
  const agentStarting = getAgentStateSync(sessionName)?.status === "starting";
38638
38642
  let tmuxSessionAlive = false;
38639
38643
  if (!isRemote) try {
@@ -38776,10 +38780,9 @@ Continue the PLANNING session. Do NOT implement anything.
38776
38780
  `);
38777
38781
  const agentCwd = workspacePath;
38778
38782
  if (existsSync$1(outputFile)) await rename(outputFile, join$1(planningDir, `output-${Date.now()}.jsonl`));
38779
- const { getAgentCommandSync } = await import("./settings-Ce28-uYS.js");
38783
+ const { getAgentCommandSync } = await import("./settings-oOlkt2wY.js");
38780
38784
  let msgPlanningModel = "claude-sonnet-4-6";
38781
38785
  try {
38782
- const { loadConfigSync, resolveModel } = await import("./config-yaml-BPbPSBp2.js");
38783
38786
  msgPlanningModel = resolveModel("plan", void 0, loadConfigSync().config);
38784
38787
  } catch {}
38785
38788
  const msgAgentCmd = getAgentCommandSync(msgPlanningModel);
@@ -38839,7 +38842,7 @@ const deletePlanningSessionRoute = HttpRouter.add("DELETE", "/api/planning/:issu
38839
38842
  }));
38840
38843
  const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Effect.promise(async () => {
38841
38844
  try {
38842
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DANmNff5.js");
38845
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CdfMe3Oe.js");
38843
38846
  const projectRoot = process.cwd();
38844
38847
  const venvPath = join$1(projectRoot, ".venv");
38845
38848
  const results = [];
@@ -38884,7 +38887,7 @@ const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Ef
38884
38887
  }));
38885
38888
  const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Effect.promise(async () => {
38886
38889
  try {
38887
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DANmNff5.js");
38890
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CdfMe3Oe.js");
38888
38891
  const projectRoot = process.cwd();
38889
38892
  const venvPath = join$1(projectRoot, ".venv");
38890
38893
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -38901,7 +38904,7 @@ const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Ef
38901
38904
  }));
38902
38905
  const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effect.promise(async () => {
38903
38906
  try {
38904
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DANmNff5.js");
38907
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CdfMe3Oe.js");
38905
38908
  const projectRoot = process.cwd();
38906
38909
  const venvPath = join$1(projectRoot, ".venv");
38907
38910
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -38918,7 +38921,7 @@ const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effe
38918
38921
  }));
38919
38922
  const postTldrReloadRoute = HttpRouter.add("POST", "/api/services/tldr/reload", Effect.promise(async () => {
38920
38923
  try {
38921
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-DANmNff5.js");
38924
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CdfMe3Oe.js");
38922
38925
  const enabled = isTldrEnabledSync();
38923
38926
  const projectRoot = process.cwd();
38924
38927
  const targets = [];
@@ -39043,7 +39046,7 @@ const postShadowMonitorRoute = HttpRouter.add("POST", "/api/shadow/:issueId/moni
39043
39046
  const projectPath = await getProjectPath(issuePrefix);
39044
39047
  const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
39045
39048
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
39046
- const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-Ci8In91q.js");
39049
+ const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-eRFcHl7F.js");
39047
39050
  const config = {
39048
39051
  issueId,
39049
39052
  workspacePath,
@@ -39073,7 +39076,7 @@ const postShadowObserveRoute = HttpRouter.add("POST", "/api/shadow/:issueId/obse
39073
39076
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
39074
39077
  const ghConfig = getGitHubConfig();
39075
39078
  if (!ghConfig) return jsonResponse({ error: "GitHub not configured - Observer requires GitHub" }, { status: 400 });
39076
- const { runObserverCycle } = await import("./shadow-engineering-Ci8In91q.js");
39079
+ const { runObserverCycle } = await import("./shadow-engineering-eRFcHl7F.js");
39077
39080
  const firstRepo = ghConfig.repos[0];
39078
39081
  const config = {
39079
39082
  issueId,
@@ -39137,7 +39140,7 @@ const postRestartDashboardRoute = HttpRouter.add("POST", "/api/system/restart-da
39137
39140
  return jsonResponse({ error: "Restart failed: " + (error instanceof Error ? error.message : String(error)) }, { status: 500 });
39138
39141
  }
39139
39142
  }));
39140
- const miscRouteLayer = Layer.mergeAll(postTrackersRefreshRoute, getProjectMappingsRoute, putProjectMappingsRoute, postProjectMappingsRoute, getSystemHealthRoute, getGodviewSystemHealthRoute, getHealthAgentsRoute, postHealthAgentPingRoute, getTrackerStatusRoute, postRallyValidateRoute, getNoResumeModeRoute, getDeaconStatusRoute, getDeaconLogsRoute, postDeaconPatrolRoute, getDeaconPauseRoute, postDeaconPauseRoute, getVersionRoute, getRegisteredProjectsRoute, getConfirmationsRoute, postConfirmationRespondRoute, getSkillsRoute, getPlanningStatusRoute, postPlanningMessageRoute, deletePlanningSessionRoute, getTldrStatusRoute, postTldrStartRoute, postTldrStopRoute, postTldrReloadRoute, getCacheStatusRoute, clearCacheRoute, getMetricsRuntimesRoute, getMetricsTasksRoute, postShadowMonitorRoute, postShadowObserveRoute, postDevRebuildRoute, postRestartDashboardRoute);
39143
+ const miscRouteLayer = Layer.mergeAll(postTrackersRefreshRoute, getProjectMappingsRoute, putProjectMappingsRoute, postProjectMappingsRoute, getSystemHealthRoute, getGodviewSystemHealthRoute, getHealthAgentsRoute, postHealthAgentPingRoute, getTrackerStatusRoute, postRallyValidateRoute, getNoResumeModeRoute, postResumeAllRoute, getDeaconStatusRoute, getDeaconLogsRoute, postDeaconPatrolRoute, getDeaconPauseRoute, postDeaconPauseRoute, getVersionRoute, getRegisteredProjectsRoute, getConfirmationsRoute, postConfirmationRespondRoute, getSkillsRoute, getPlanningStatusRoute, postPlanningMessageRoute, deletePlanningSessionRoute, getTldrStatusRoute, postTldrStartRoute, postTldrStopRoute, postTldrReloadRoute, getCacheStatusRoute, clearCacheRoute, getMetricsRuntimesRoute, getMetricsTasksRoute, postShadowMonitorRoute, postShadowObserveRoute, postDevRebuildRoute, postRestartDashboardRoute);
39141
39144
  //#endregion
39142
39145
  //#region ../../lib/conversation-search/ranker.ts
39143
39146
  init_fts_db();
@@ -39315,8 +39318,11 @@ async function searchConversationChunks(input) {
39315
39318
  * own embedding pipeline and config (see linked GitHub issue at end of
39316
39319
  * commit).
39317
39320
  */
39321
+ init_http_helpers();
39318
39322
  init_projects();
39319
39323
  init_conversations();
39324
+ init_dashboard_auth();
39325
+ init_origin_validation();
39320
39326
  const PAN_COMMANDS = [
39321
39327
  {
39322
39328
  name: "pan up",
@@ -39891,66 +39897,32 @@ const getPaletteSearchRoute = HttpRouter.add("GET", "/api/palette/search", Effec
39891
39897
  }));
39892
39898
  const paletteRouteLayer = Layer.mergeAll(getPaletteCommandsRoute, getPaletteSearchRoute);
39893
39899
  //#endregion
39894
- //#region ../../lib/conversations/switch-strategy.ts
39895
- init_model_capabilities();
39896
- const EXTENDED_CONTEXT_WINDOW_FLOOR = 1e6;
39897
- function getEffectiveTargetWindow({ registryWindow, currentObservedCeiling, cliproxyRouted, providerRoutingChanged }) {
39898
- if (cliproxyRouted) return CLIPROXY_CODEX_CONTEXT_WINDOW;
39899
- if (!providerRoutingChanged && currentObservedCeiling > registryWindow) return Math.max(EXTENDED_CONTEXT_WINDOW_FLOOR, registryWindow);
39900
- return registryWindow;
39901
- }
39902
- function decideSwitchStrategy({ harnessChanged, providerRoutingChanged, contextTokens, effectiveTargetWindow }) {
39903
- if (harnessChanged) return {
39904
- tier: 4,
39905
- compact: false,
39906
- useModelCommand: false
39907
- };
39908
- if (contextTokens > effectiveTargetWindow * .8) return {
39909
- tier: 3,
39910
- compact: true,
39911
- useModelCommand: false
39912
- };
39913
- if (!providerRoutingChanged) return {
39914
- tier: 1,
39915
- compact: false,
39916
- useModelCommand: true
39917
- };
39918
- return {
39919
- tier: 2,
39920
- compact: false,
39921
- useModelCommand: false
39922
- };
39923
- }
39924
- //#endregion
39925
- //#region ../../lib/pi-codex-auth.ts
39900
+ //#region ../../lib/ohmypi-codex-auth.ts
39926
39901
  init_features();
39927
39902
  /**
39928
- * Pi (ChatGPT / Codex subscription) OAuth helpers — PAN-1520.
39903
+ * oh-my-pi (omp) OAuth helpers for the openai-codex (ChatGPT subscription) provider.
39904
+ * Forked from pi-codex-auth.ts (PAN-1520) for the omp migration (PAN-1989).
39929
39905
  *
39930
- * Pi conversations on a GPT-5.x model authenticate with the user's ChatGPT
39931
- * subscription through an OAuth credential under the `openai-codex` provider,
39932
- * stored at ~/.pi/agent/auth.json. When that access token expires AND its
39933
- * refresh token can no longer renew it, Pi fails with the opaque
39934
- * "No API key for provider: openai-codex".
39906
+ * omp stores its general credentials in ~/.omp/agent/agent.db (SQLite). The
39907
+ * openai-codex OAuth credential is persisted separately at ~/.omp/agent/auth.json
39908
+ * (same JSON format as Pi's ~/.pi/agent/auth.json) so that this helper's
39909
+ * read/write logic is unchanged from the Pi side.
39935
39910
  *
39936
39911
  * This module:
39937
- * - reads/writes that credential in Pi's exact on-disk shape,
39912
+ * - reads/writes the openai-codex credential under ~/.omp/agent/auth.json,
39938
39913
  * - reports auth status (for `pan doctor` and the conversation spawn gate),
39939
39914
  * - refreshes an expired-but-recoverable token, and
39940
- * - drives Pi's headless device-code login to re-authenticate.
39915
+ * - drives omp's headless device-code login to re-authenticate.
39941
39916
  *
39942
- * Refresh and login reuse Pi's OWN OAuth implementation, dynamically loaded
39943
- * from the installed `pi` package (the lightweight oauth submodule only — it
39944
- * imports no provider SDKs), so the credential format and OAuth endpoints
39945
- * never drift from whatever the installed Pi expects. Every reuse is wrapped
39946
- * so a missing/relocated Pi degrades gracefully rather than throwing.
39917
+ * The OAuth module is loaded from the installed `omp` package dynamically.
39918
+ * A missing or relocated omp OAuth module degrades to 'unavailable' — never throws.
39947
39919
  */
39948
39920
  const PI_CODEX_PROVIDER = "openai-codex";
39949
- function getPiAuthPath() {
39950
- return join$1(homedir$1(), ".pi", "agent", "auth.json");
39921
+ function getOhmypiAuthPath() {
39922
+ return join$1(homedir$1(), ".omp", "agent", "auth.json");
39951
39923
  }
39952
39924
  function readAuthFile() {
39953
- const path = getPiAuthPath();
39925
+ const path = getOhmypiAuthPath();
39954
39926
  if (!existsSync$1(path)) return {};
39955
39927
  try {
39956
39928
  const parsed = JSON.parse(readFileSync$1(path, "utf-8"));
@@ -39959,14 +39931,14 @@ function readAuthFile() {
39959
39931
  return {};
39960
39932
  }
39961
39933
  }
39962
- function readPiCodexCredential() {
39934
+ function readOhmypiCodexCredential() {
39963
39935
  const cred = readAuthFile()[PI_CODEX_PROVIDER];
39964
39936
  if (cred && typeof cred === "object" && typeof cred["access"] === "string" && typeof cred["refresh"] === "string") return cred;
39965
39937
  return null;
39966
39938
  }
39967
39939
  /** Atomically merge a fresh codex credential into auth.json (mode 0600). */
39968
- function writePiCodexCredential(tokens) {
39969
- const path = getPiAuthPath();
39940
+ function writeOhmypiCodexCredential(tokens) {
39941
+ const path = getOhmypiAuthPath();
39970
39942
  mkdirSync$1(dirname$1(path), { recursive: true });
39971
39943
  const auth = readAuthFile();
39972
39944
  const stored = {
@@ -39982,27 +39954,26 @@ function writePiCodexCredential(tokens) {
39982
39954
  renameSync$1(tmp, path);
39983
39955
  return stored;
39984
39956
  }
39985
- /** Locate the `pi` executable on PATH (no subprocess — event-loop safe). */
39986
- function findPiBinary() {
39957
+ /** Locate the `omp` executable on PATH (no subprocess — event-loop safe). */
39958
+ function findOmpBinary() {
39987
39959
  for (const dir of (process.env["PATH"] ?? "").split(delimiter)) {
39988
39960
  if (!dir) continue;
39989
- const candidate = join$1(dir, "pi");
39961
+ const candidate = join$1(dir, "omp");
39990
39962
  if (existsSync$1(candidate)) return candidate;
39991
39963
  }
39992
39964
  return null;
39993
39965
  }
39994
- /** pi-ai exports ESM-only, so the CJS resolver rejects it. */
39995
- const PI_AI_OAUTH_SUBPATH = join$1("@earendil-works", "pi-ai", "dist", "utils", "oauth", "openai-codex.js");
39966
+ /** omp is @oh-my-pi/pi-coding-agent; the oauth submodule path mirrors pi-ai's layout. */
39967
+ const OMP_AI_OAUTH_SUBPATH = join$1("@oh-my-pi", "pi-ai", "dist", "utils", "oauth", "openai-codex.js");
39996
39968
  /**
39997
- * Resolve Pi's openai-codex OAuth submodule by walking node_modules upward
39998
- * from the real pi binary — covering both a nested install (pi-ai under
39999
- * pi-coding-agent/node_modules) and a hoisted one. Avoids createRequire,
40000
- * which can't resolve pi-ai's ESM-only `exports` map.
39969
+ * Resolve omp's openai-codex OAuth submodule by walking node_modules upward
39970
+ * from the real omp binary. Returns null when not found — the caller degrades
39971
+ * gracefully to 'unavailable' status.
40001
39972
  */
40002
- function resolvePiCodexOAuthFile(piBin) {
40003
- let dir = dirname$1(realpathSync$1(piBin));
39973
+ function resolveOmpCodexOAuthFile(ompBin) {
39974
+ let dir = dirname$1(realpathSync$1(ompBin));
40004
39975
  for (let i = 0; i < 10; i++) {
40005
- const candidate = join$1(dir, "node_modules", PI_AI_OAUTH_SUBPATH);
39976
+ const candidate = join$1(dir, "node_modules", OMP_AI_OAUTH_SUBPATH);
40006
39977
  if (existsSync$1(candidate)) return candidate;
40007
39978
  const parent = dirname$1(dir);
40008
39979
  if (parent === dir) break;
@@ -40012,18 +39983,16 @@ function resolvePiCodexOAuthFile(piBin) {
40012
39983
  }
40013
39984
  let cachedModule = null;
40014
39985
  /**
40015
- * Dynamically load Pi's openai-codex OAuth submodule from the installed `pi`
40016
- * package. The submodule imports only local helpers (pkce, device-code
40017
- * polling), never the heavy provider SDKs. Returns null on any failure so
40018
- * callers can degrade gracefully.
39986
+ * Dynamically load omp's openai-codex OAuth submodule from the installed `omp`
39987
+ * package. Returns null on any failure so callers can degrade gracefully.
40019
39988
  */
40020
- async function loadPiCodexOAuth() {
39989
+ async function loadOhmypiCodexOAuth() {
40021
39990
  if (cachedModule) return cachedModule;
40022
39991
  cachedModule = (async () => {
40023
39992
  try {
40024
- const piBin = findPiBinary();
40025
- if (!piBin) return null;
40026
- const oauthFile = resolvePiCodexOAuthFile(piBin);
39993
+ const ompBin = findOmpBinary();
39994
+ if (!ompBin) return null;
39995
+ const oauthFile = resolveOmpCodexOAuthFile(ompBin);
40027
39996
  if (!oauthFile) return null;
40028
39997
  const mod = await import(pathToFileURL(oauthFile).href);
40029
39998
  if (typeof mod.refreshOpenAICodexToken !== "function" || typeof mod.loginOpenAICodexDeviceCode !== "function") return null;
@@ -40034,22 +40003,16 @@ async function loadPiCodexOAuth() {
40034
40003
  })();
40035
40004
  return cachedModule;
40036
40005
  }
40037
- /** Treat a token expiring within this window as already expired. */
40038
40006
  const EXPIRY_MARGIN_MS = 6e4;
40039
- /**
40040
- * Report Pi openai-codex auth status. With `refreshIfExpired`, an expired
40041
- * token triggers one refresh attempt (writing the renewed token back on
40042
- * success) so the caller learns whether the credential is actually dead.
40043
- */
40044
- async function getPiCodexAuthStatus(opts) {
40045
- const cred = readPiCodexCredential();
40007
+ async function getOhmypiCodexAuthStatus(opts) {
40008
+ const cred = readOhmypiCodexCredential();
40046
40009
  if (!cred) return { status: "missing" };
40047
40010
  if (cred.expires > Date.now() + EXPIRY_MARGIN_MS) return {
40048
40011
  status: "ok",
40049
40012
  expiresAt: cred.expires
40050
40013
  };
40051
40014
  if (opts?.refreshIfExpired) {
40052
- const refreshed = await refreshPiCodexAuth();
40015
+ const refreshed = await refreshOhmypiCodexAuth();
40053
40016
  if (refreshed) return {
40054
40017
  status: "ok",
40055
40018
  expiresAt: refreshed.expires
@@ -40066,19 +40029,13 @@ async function getPiCodexAuthStatus(opts) {
40066
40029
  refreshFailed: false
40067
40030
  };
40068
40031
  }
40069
- /**
40070
- * Refresh an expired openai-codex token using its refresh token. Persists and
40071
- * returns the renewed credential on success, or null if there is no refresh
40072
- * token, Pi's OAuth module is unavailable, or the refresh was rejected (the
40073
- * refresh token itself is dead → a full re-login is required).
40074
- */
40075
- async function refreshPiCodexAuth() {
40076
- const cred = readPiCodexCredential();
40032
+ async function refreshOhmypiCodexAuth() {
40033
+ const cred = readOhmypiCodexCredential();
40077
40034
  if (!cred?.refresh) return null;
40078
- const mod = await loadPiCodexOAuth();
40035
+ const mod = await loadOhmypiCodexOAuth();
40079
40036
  if (!mod) return null;
40080
40037
  try {
40081
- return writePiCodexCredential(await mod.refreshOpenAICodexToken(cred.refresh));
40038
+ return writeOhmypiCodexCredential(await mod.refreshOpenAICodexToken(cred.refresh));
40082
40039
  } catch {
40083
40040
  return null;
40084
40041
  }
@@ -40155,7 +40112,7 @@ function isNonEmptyJsonlLine(buffer) {
40155
40112
  }
40156
40113
  //#endregion
40157
40114
  //#region services/conversation-eaten-message-watcher.ts
40158
- init_agents$1();
40115
+ init_agents$2();
40159
40116
  const WATCH_TIMEOUT_MS = 5 * 6e4;
40160
40117
  const WATCH_INTERVAL_MS = 3e3;
40161
40118
  const REDELIVERY_GRACE_MS = 1e4;
@@ -40191,299 +40148,11 @@ async function watchForEatenConversationMessage(args) {
40191
40148
  return "unverified";
40192
40149
  }
40193
40150
  //#endregion
40194
- //#region ../../lib/session-format-converter.ts
40151
+ //#region ../../lib/conversations/transcript-summary.ts
40195
40152
  init_transcript_adapter();
40196
40153
  init_summary_fork();
40197
40154
  init_conversation_compaction();
40198
40155
  /**
40199
- * Session-format converter (P0, 2026-05-14).
40200
- *
40201
- * When a conversation's harness changes (Claude Code <-> Pi Agent), the two
40202
- * runtimes store transcripts in incompatible JSONL formats and in different
40203
- * locations:
40204
- *
40205
- * - Claude Code: ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
40206
- * - Pi Agent: ~/.overdeck/agents/<tmux-session>/sessions/<iso>_<id>.jsonl
40207
- *
40208
- * Historically the orchestrator would silently flip the harness and leave the
40209
- * old transcript orphaned (the DB kept pointing at a session id with no file),
40210
- * and the compaction path would even append Claude-format records into a Pi
40211
- * JSONL. The chosen fix is to *convert* on an explicit harness change rather
40212
- * than guard against it or build a multi-format reader.
40213
- *
40214
- * The conversion is deliberately lossless-of-content but lossy-of-structure:
40215
- * we extract a readable transcript from the source format and seed a fresh
40216
- * session in the target format with that transcript carried in as a single
40217
- * continuation/summary message — the same "faux compaction boundary" mechanism
40218
- * Overdeck already uses for native compaction. Tool-call structure, thinking
40219
- * blocks, and per-message token accounting do not round-trip; the conversation
40220
- * *content* does. This keeps the converter robust as both harness formats
40221
- * continue to evolve.
40222
- */
40223
- init_paths();
40224
- /** Pull plain text out of a Claude/Pi `message.content` (string or block array). */
40225
- function extractContentText(content) {
40226
- if (typeof content === "string") return content;
40227
- if (!Array.isArray(content)) return "";
40228
- const parts = [];
40229
- for (const block of content) {
40230
- if (!block || typeof block !== "object") continue;
40231
- const b = block;
40232
- if (typeof b["text"] === "string") parts.push(b["text"]);
40233
- else if (typeof b["thinking"] === "string") parts.push(`[thinking] ${b["thinking"]}`);
40234
- else if ((b["type"] === "tool_use" || b["type"] === "toolCall") && typeof b["name"] === "string") parts.push(`[tool_use: ${b["name"]}]`);
40235
- else if (b["type"] === "tool_result" || b["type"] === "toolResult") {
40236
- const inner = extractContentText(b["content"]);
40237
- if (inner) parts.push(`[tool_result] ${inner}`);
40238
- }
40239
- }
40240
- return parts.join("\n").trim();
40241
- }
40242
- /** Read a Claude Code JSONL transcript into ordered role/text turns. */
40243
- function extractClaudeTranscript(raw) {
40244
- const turns = [];
40245
- for (const line of raw.split("\n")) {
40246
- const trimmed = line.trim();
40247
- if (!trimmed) continue;
40248
- let entry;
40249
- try {
40250
- entry = JSON.parse(trimmed);
40251
- } catch {
40252
- continue;
40253
- }
40254
- const type = entry["type"];
40255
- if (type !== "user" && type !== "assistant") continue;
40256
- const message = entry["message"];
40257
- if (!message || typeof message !== "object") continue;
40258
- const m = message;
40259
- const role = typeof m["role"] === "string" ? m["role"] : type;
40260
- const text = extractContentText(m["content"]);
40261
- if (text) turns.push({
40262
- role,
40263
- text
40264
- });
40265
- }
40266
- return turns;
40267
- }
40268
- /** Read a Pi Agent JSONL transcript into ordered role/text turns. */
40269
- function extractPiTranscript(raw) {
40270
- const turns = [];
40271
- for (const line of raw.split("\n")) {
40272
- const trimmed = line.trim();
40273
- if (!trimmed) continue;
40274
- let entry;
40275
- try {
40276
- entry = JSON.parse(trimmed);
40277
- } catch {
40278
- continue;
40279
- }
40280
- if (entry["type"] !== "message") continue;
40281
- const message = entry["message"];
40282
- if (!message || typeof message !== "object") continue;
40283
- const m = message;
40284
- const role = typeof m["role"] === "string" ? m["role"] : "assistant";
40285
- const text = extractContentText(m["content"]);
40286
- if (text) turns.push({
40287
- role,
40288
- text
40289
- });
40290
- }
40291
- return turns;
40292
- }
40293
- /**
40294
- * Read a Codex rollout JSONL transcript into ordered role/text turns.
40295
- *
40296
- * Rollout files use a different schema from --json stdout:
40297
- * - task_started: the initial task (user role)
40298
- * - agent_message: a model turn (assistant role)
40299
- * - token_count: metadata — skipped
40300
- *
40301
- * We extract task_started as the user prompt and agent_message events as
40302
- * assistant turns to produce a readable transcript.
40303
- */
40304
- function extractCodexTranscript(raw) {
40305
- const turns = [];
40306
- for (const line of raw.split("\n")) {
40307
- const trimmed = line.trim();
40308
- if (!trimmed) continue;
40309
- let entry;
40310
- try {
40311
- entry = JSON.parse(trimmed);
40312
- } catch {
40313
- continue;
40314
- }
40315
- const type = entry["type"];
40316
- if (type === "task_started") {
40317
- const task = typeof entry["task"] === "string" ? entry["task"] : "";
40318
- if (task) turns.push({
40319
- role: "user",
40320
- text: task
40321
- });
40322
- } else if (type === "agent_message") {
40323
- const content = typeof entry["content"] === "string" ? entry["content"] : extractContentText(entry["content"]);
40324
- if (content) turns.push({
40325
- role: "assistant",
40326
- text: content
40327
- });
40328
- }
40329
- }
40330
- return turns;
40331
- }
40332
- /** Render ordered turns into a single readable transcript block. */
40333
- function renderTranscript(turns) {
40334
- return turns.map((t) => `### ${t.role}\n${t.text}`).join("\n\n");
40335
- }
40336
- function buildContinuation(fromHarness, transcript) {
40337
- return [
40338
- `This session is being continued from a previous conversation that ran under the ${fromHarness} harness.`,
40339
- "The runtime was switched, so the transcript below is carried forward as context rather than as a native session.",
40340
- "Continue from this history without redoing already-completed work.",
40341
- "",
40342
- "--- PRIOR TRANSCRIPT ---",
40343
- transcript || "(the prior transcript was empty)",
40344
- "--- END PRIOR TRANSCRIPT ---"
40345
- ].join("\n");
40346
- }
40347
- /** Short opaque id in Pi's `<8-hex>` style. */
40348
- function shortId() {
40349
- return randomBytes$1(4).toString("hex");
40350
- }
40351
- async function convertConversationTranscriptPromise(opts) {
40352
- if (opts.fromHarness === opts.toHarness) throw new Error(`convertConversationTranscript called with no harness change (${opts.fromHarness})`);
40353
- const raw = await readFile(opts.sourceSessionFile, "utf-8");
40354
- let turns;
40355
- switch (opts.fromHarness) {
40356
- case "pi":
40357
- turns = extractPiTranscript(raw);
40358
- break;
40359
- case "codex":
40360
- turns = extractCodexTranscript(raw);
40361
- break;
40362
- default: turns = extractClaudeTranscript(raw);
40363
- }
40364
- const continuation = buildContinuation(opts.fromHarness, renderTranscript(turns));
40365
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
40366
- if (opts.toHarness === "codex") {
40367
- const threadId = randomUUID$1().replace(/-/g, "").slice(0, 16);
40368
- const codexAgentDir = join$1(homedir$1(), ".overdeck", "agents", opts.tmuxSession, "codex-home");
40369
- const sessionDate = /* @__PURE__ */ new Date();
40370
- const sessionsDir = join$1(codexAgentDir, "sessions", [
40371
- sessionDate.getFullYear().toString(),
40372
- String(sessionDate.getMonth() + 1).padStart(2, "0"),
40373
- String(sessionDate.getDate()).padStart(2, "0")
40374
- ].join("/"));
40375
- await mkdir(sessionsDir, {
40376
- recursive: true,
40377
- mode: 448
40378
- });
40379
- const targetSessionFile = join$1(sessionsDir, `rollout-${randomUUID$1()}-${threadId}.jsonl`);
40380
- await writeFile(targetSessionFile, `${[JSON.stringify({
40381
- type: "task_started",
40382
- model: "codex",
40383
- task: continuation,
40384
- thread_id: threadId,
40385
- timestamp
40386
- })].join("\n")}\n`, "utf-8");
40387
- await mkdir(join$1(homedir$1(), ".overdeck", "agents", opts.tmuxSession), {
40388
- recursive: true,
40389
- mode: 448
40390
- });
40391
- writeThreadId(opts.tmuxSession, threadId);
40392
- return {
40393
- sessionId: threadId,
40394
- targetSessionFile
40395
- };
40396
- }
40397
- if (opts.toHarness === "pi") {
40398
- const sessionId = randomUUID$1();
40399
- const agentDir = join$1(homedir$1(), ".overdeck", "agents", opts.tmuxSession);
40400
- const sessionDir = join$1(agentDir, "sessions");
40401
- await mkdir(sessionDir, {
40402
- recursive: true,
40403
- mode: 448
40404
- });
40405
- const targetSessionFile = join$1(sessionDir, `${timestamp.replace(/[:.]/g, "-")}_${sessionId}.jsonl`);
40406
- await writeFile(targetSessionFile, `${[JSON.stringify({
40407
- type: "session",
40408
- version: 3,
40409
- id: sessionId,
40410
- timestamp,
40411
- cwd: opts.cwd
40412
- }), JSON.stringify({
40413
- type: "message",
40414
- id: shortId(),
40415
- parentId: null,
40416
- timestamp,
40417
- message: {
40418
- role: "user",
40419
- content: [{
40420
- type: "text",
40421
- text: continuation
40422
- }]
40423
- }
40424
- })].join("\n")}\n`, "utf-8");
40425
- await writeFile(join$1(agentDir, "session.id"), `${sessionId}\n`, "utf-8");
40426
- return {
40427
- sessionId,
40428
- targetSessionFile
40429
- };
40430
- }
40431
- const sessionId = randomUUID$1();
40432
- const targetSessionFile = sessionFilePath(opts.cwd, sessionId);
40433
- await mkdir(join$1(targetSessionFile, ".."), { recursive: true });
40434
- const boundaryUuid = randomUUID$1();
40435
- await writeFile(targetSessionFile, `${[JSON.stringify({
40436
- parentUuid: null,
40437
- isSidechain: false,
40438
- type: "system",
40439
- subtype: "compact_boundary",
40440
- content: `Conversation runtime switched from ${opts.fromHarness} to claude-code`,
40441
- isMeta: false,
40442
- timestamp,
40443
- uuid: boundaryUuid,
40444
- sessionId,
40445
- cwd: opts.cwd,
40446
- level: "info",
40447
- compactMetadata: {
40448
- trigger: "overdeck-harness-switch",
40449
- preTokens: 0
40450
- }
40451
- }), JSON.stringify({
40452
- parentUuid: boundaryUuid,
40453
- isSidechain: false,
40454
- type: "user",
40455
- message: {
40456
- role: "user",
40457
- content: continuation
40458
- },
40459
- isVisibleInTranscriptOnly: true,
40460
- isCompactSummary: true,
40461
- uuid: randomUUID$1(),
40462
- sessionId,
40463
- cwd: opts.cwd,
40464
- timestamp
40465
- })].join("\n")}\n`, "utf-8");
40466
- return {
40467
- sessionId,
40468
- targetSessionFile
40469
- };
40470
- }
40471
- /** Tagged error for session-format-converter Effect variants. */
40472
- var SessionConvertError = class extends Data.TaggedError("SessionConvertError") {};
40473
- /** Effect variant of `convertConversationTranscript`. */
40474
- const convertConversationTranscript = (opts) => Effect.tryPromise({
40475
- try: () => convertConversationTranscriptPromise(opts),
40476
- catch: (cause) => new SessionConvertError({
40477
- fromHarness: opts.fromHarness,
40478
- toHarness: opts.toHarness,
40479
- sourceSessionFile: opts.sourceSessionFile,
40480
- message: cause instanceof Error ? cause.message : String(cause),
40481
- cause
40482
- })
40483
- });
40484
- //#endregion
40485
- //#region ../../lib/conversations/transcript-summary.ts
40486
- /**
40487
40156
  * Conversation transcript summarization.
40488
40157
  *
40489
40158
  * Shared helpers for turning a conversation's parsed messages into LLM-generated
@@ -40506,8 +40175,8 @@ const convertConversationTranscript = (opts) => Effect.tryPromise({
40506
40175
  * layering inversion.
40507
40176
  */
40508
40177
  init_child_env();
40509
- init_agents$1();
40510
- init_cost();
40178
+ init_agents$2();
40179
+ init_cost$1();
40511
40180
  /** Haiku — fast/cheap, used for every conversation title and about-summary. */
40512
40181
  const CONVERSATION_TITLE_MODEL = "claude-haiku-4-5-20251001";
40513
40182
  /** Total transcript characters allowed into a single prompt. */
@@ -40553,6 +40222,17 @@ function sanitizeTitle(raw) {
40553
40222
  return raw.trim().split(/\r?\n/)[0]?.trim().replace(/^['"`]+|['"`]+$/g, "").trim().replace(/\s+/g, " ") ?? "";
40554
40223
  }
40555
40224
  /**
40225
+ * Generate a deterministic title from a serialized transcript when the model
40226
+ * path is unavailable. Prefer the latest user request because explicit retitle
40227
+ * should describe where the conversation currently landed.
40228
+ */
40229
+ function fallbackTranscriptTitle(transcript) {
40230
+ const lines = transcript.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("[…"));
40231
+ const compact = (lines.filter((line) => line.startsWith("User:")).map((line) => line.slice(5).trim()).filter(Boolean).at(-1) ?? lines[0]?.replace(/^(User|Assistant):\s*/i, "").trim() ?? "").replace(/```[\s\S]*?```/g, " ").replace(/`([^`]+)`/g, "$1").replace(/\b(?:please|can you|could you|would you)\b/gi, " ").replace(/\b(?:i need you to|i want you to|help me|let's|lets)\b/gi, " ").replace(/https?:\/\/\S+/gi, " ").replace(/[^\p{L}\p{N}#/_+.-]+/gu, " ").replace(/\s+/g, " ").trim();
40232
+ if (!compact) return "";
40233
+ return sanitizeTitle(compact.split(/\s+/).slice(0, 8).join(" ").replace(/\s+(?:and|or|to|for|with)$/i, "").replace(/[.,:;!?]+$/g, ""));
40234
+ }
40235
+ /**
40556
40236
  * Invoke `claude -p` with a JSON schema and return the structured output.
40557
40237
  * Throws on non-zero exit, timeout, spawn error, or unparseable output.
40558
40238
  */
@@ -40736,7 +40416,7 @@ async function cleanupConversationAttachments(name) {
40736
40416
  * sessions that the lifecycle poller missed. */
40737
40417
  async function cleanupOrphanedConversationAttachments() {
40738
40418
  try {
40739
- const { listConversations } = await import("./conversations-ZEtsnn7V.js");
40419
+ const { listConversations } = await import("./conversations-BtRONkWR.js");
40740
40420
  const convNames = new Set(listConversations().map((c) => c.name));
40741
40421
  const root = getConversationAttachmentsRoot();
40742
40422
  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, {
@@ -40931,22 +40611,27 @@ async function removeConversationAttachment(name, attachmentPath) {
40931
40611
  *
40932
40612
  * Conversations are NEVER deleted from the database, and JSONL transcript files are never removed.
40933
40613
  */
40614
+ init_http_helpers();
40934
40615
  init_child_env();
40935
40616
  init_claude_permissions();
40617
+ init_jsonl_resolver();
40618
+ init_origin_validation();
40936
40619
  init_projects();
40937
40620
  init_model_capabilities();
40938
40621
  init_conversations();
40939
40622
  init_tmux();
40940
- init_agents$1();
40623
+ init_agents$2();
40941
40624
  init_config_yaml();
40942
40625
  init_harness_policy();
40943
40626
  init_providers();
40627
+ init_agent_input_detection();
40944
40628
  init_launcher_generator();
40945
40629
  init_layers();
40946
40630
  init_briefing_freshness();
40947
40631
  init_conversation_service();
40948
40632
  init_transcript_landing();
40949
40633
  init_pi_conversation_parser();
40634
+ init_ohmypi_conversation_parser();
40950
40635
  init_codex_conversation_parser();
40951
40636
  init_paths();
40952
40637
  init_event_store();
@@ -41055,66 +40740,11 @@ function shellQuote(str) {
41055
40740
  return "'" + str.replace(/'/g, "'\"'\"'") + "'";
41056
40741
  }
41057
40742
  const SAFE_MODEL_PATTERN = /^[a-zA-Z0-9_.:\/-]+$/;
41058
- const DEFAULT_SWITCH_MODEL_CONTEXT_WINDOW = 2e5;
41059
- const SWITCH_MODEL_STATUSLINE_TIMEOUT_MS = 5e3;
41060
- const SWITCH_MODEL_STATUSLINE_POLL_MS = 250;
41061
40743
  const SAFE_EFFORT_PATTERN = /^(low|medium|high)$/;
41062
- function modelCapabilityForModel(model) {
41063
- if (!model) return void 0;
41064
- const resolvedModel = resolveModelIdSync(model);
41065
- return Object.prototype.hasOwnProperty.call(MODEL_CAPABILITIES, resolvedModel) ? MODEL_CAPABILITIES[resolvedModel] : void 0;
41066
- }
41067
- function registryContextWindowForModel(model) {
41068
- return modelCapabilityForModel(model)?.contextWindow ?? DEFAULT_SWITCH_MODEL_CONTEXT_WINDOW;
41069
- }
41070
- function displayNameForModel(model) {
41071
- return modelCapabilityForModel(model)?.displayName ?? model;
41072
- }
41073
- async function isCliproxyRoutedModel(model) {
41074
- if (!model) return false;
41075
- return getProviderForModelSync(model).name === "openai" && await getProviderAuthMode(model) === "subscription";
41076
- }
41077
- function providerRoutingSignature(exports) {
41078
- return exports.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("unset ")).join("\n");
41079
- }
41080
- async function hasProviderRoutingChanged(currentModel, targetModel) {
41081
- if (!targetModel || currentModel === targetModel) return false;
41082
- const targetSignature = providerRoutingSignature(await getProviderExportsForModel(targetModel));
41083
- if (!currentModel) return targetSignature !== "";
41084
- return providerRoutingSignature(await getProviderExportsForModel(currentModel)) !== targetSignature;
41085
- }
41086
- const ANSI_ESCAPE_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
41087
- function normalizePaneLine(line) {
41088
- return line.replace(ANSI_ESCAPE_PATTERN, "").trim().toLowerCase();
41089
- }
41090
- function modelStatuslinePrefix(line) {
41091
- return line.startsWith("statusline:") ? line.slice(11).trimStart() : line;
41092
- }
41093
- function hasStatuslineModelConfirmation(pane, targetModel) {
41094
- const targetDisplayName = displayNameForModel(targetModel).toLowerCase();
41095
- const targetModelId = targetModel.toLowerCase();
41096
- const expectedWithId = `${targetDisplayName} (${targetModelId})`;
41097
- for (const rawLine of pane.split(/\r?\n/)) {
41098
- const line = normalizePaneLine(rawLine);
41099
- if (!line || line.includes("/model")) continue;
41100
- const modelLine = modelStatuslinePrefix(line);
41101
- if (modelLine === targetModelId || modelLine.startsWith(`${targetModelId} `)) return true;
41102
- if (modelLine === expectedWithId || modelLine.startsWith(`${expectedWithId} `)) return true;
41103
- }
41104
- return false;
41105
- }
41106
- async function waitForModelStatusline(tmuxSession, targetModel) {
41107
- const start = Date.now();
41108
- while (Date.now() - start < SWITCH_MODEL_STATUSLINE_TIMEOUT_MS) {
41109
- if (hasStatuslineModelConfirmation(await Effect.runPromise(capturePane(tmuxSession, 10).pipe(Effect.catch(() => Effect.succeed("")))), targetModel)) return true;
41110
- await new Promise((r) => setTimeout(r, SWITCH_MODEL_STATUSLINE_POLL_MS));
41111
- }
41112
- return false;
41113
- }
41114
40744
  const SAFE_ISSUE_ID_PATTERN = /^[A-Z0-9]+-[0-9]+$/;
41115
40745
  async function resolveAllowedHarness(requested, model) {
41116
40746
  if (!model) return "claude-code";
41117
- const explicit = requested === "pi" || requested === "claude-code" || requested === "codex" ? requested : void 0;
40747
+ const explicit = requested === "ohmypi" || requested === "claude-code" || requested === "codex" ? requested : void 0;
41118
40748
  try {
41119
40749
  return await resolveHarness({
41120
40750
  model,
@@ -41178,6 +40808,7 @@ async function getCachedMessages(sessionFile, isSpecialist) {
41178
40808
  if (cached && cached.mtimeMs === fileStats.mtimeMs && cached.size === fileStats.size && cached.byteOffset >= fileStats.size) return cached.result;
41179
40809
  let result;
41180
40810
  if (isCodexSessionFile(sessionFile)) result = await parseCodexConversationMessages(sessionFile);
40811
+ else if (isOhmypiSessionFile(sessionFile)) result = await parseOhmypiConversationMessages(sessionFile);
41181
40812
  else if (isPiSessionFile(sessionFile)) result = await parsePiConversationMessages(sessionFile);
41182
40813
  else if (isSpecialist) result = await parseFromLastCompactBoundary(sessionFile);
41183
40814
  else if (cached && cached.parseState && cached.byteOffset <= fileStats.size && cached.size <= fileStats.size) {
@@ -41332,8 +40963,14 @@ async function waitForPiTuiReady(tmuxSession, timeoutMs = 3e4) {
41332
40963
  return false;
41333
40964
  }
41334
40965
  async function resolveSessionFile(conv) {
41335
- if (conv.harness === "pi") return resolvePiSessionPath(conv.tmuxSession);
41336
- if (conv.harness === "codex") return resolveCodexRolloutPath(conv.tmuxSession);
40966
+ if (conv.harness === "ohmypi" || conv.harness === "pi") {
40967
+ const piPath = await resolvePiSessionPath(conv.tmuxSession);
40968
+ if (piPath) return piPath;
40969
+ }
40970
+ if (conv.harness === "codex") {
40971
+ const codexPath = await resolveCodexRolloutPath(conv.tmuxSession);
40972
+ if (codexPath) return codexPath;
40973
+ }
41337
40974
  const sessionId = await readLauncherPinnedSessionId(conv.tmuxSession) ?? conv.claudeSessionId;
41338
40975
  if (sessionId) return sessionFilePath(conv.cwd, sessionId);
41339
40976
  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.`);
@@ -41483,6 +41120,7 @@ async function handleConversationImageUpload(name, filename, bytes, mimeType) {
41483
41120
  async function handleConversationMessage(name, body) {
41484
41121
  const conv = getConversationByName(name);
41485
41122
  if (!conv) return jsonResponse({ error: "Conversation not found" }, { status: 404 });
41123
+ if (conv.status === "ended") return jsonResponse({ error: "Session has ended — start a new run to interact" }, { status: 422 });
41486
41124
  const message = typeof body["message"] === "string" ? body["message"].trim() : "";
41487
41125
  if (!message) return jsonResponse({ error: "Message is required" }, { status: 400 });
41488
41126
  if (message.length > MAX_MESSAGE_LENGTH) return jsonResponse({ error: `Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters` }, { status: 400 });
@@ -41524,7 +41162,7 @@ async function handleConversationMessage(name, body) {
41524
41162
  if (!outboundMessage) return jsonResponse({ error: `${conv.model ?? "This model"} can't read images. Switch to a vision-capable model (e.g. mimo-v2.5) to send images.` }, { status: 422 });
41525
41163
  }
41526
41164
  let deliveredMessage = transformMessageForHarness(outboundMessage, harness, effectiveAttachmentPaths);
41527
- if (harness === "pi") deliveredMessage = await injectPiConversationMemory({
41165
+ if (harness === "ohmypi") deliveredMessage = await injectPiConversationMemory({
41528
41166
  cwd: conv.cwd,
41529
41167
  issueId: conv.issueId,
41530
41168
  conversationName: conv.name
@@ -41585,7 +41223,7 @@ function shouldUseSupervisorForConversation(harness) {
41585
41223
  }
41586
41224
  function resolveConversationDeliveryMethod(conv) {
41587
41225
  const harness = conv.harness ?? "claude-code";
41588
- return conv.deliveryMethod ?? (harness === "pi" ? "tmux" : "auto");
41226
+ return conv.deliveryMethod ?? (harness === "ohmypi" ? "tmux" : "auto");
41589
41227
  }
41590
41228
  /** Synthetic toolUseId prefix marking a Codex pane-detected approval (PAN-1690). */
41591
41229
  const CODEX_APPROVAL_TOOL_PREFIX = "codex-approval:";
@@ -41801,10 +41439,10 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
41801
41439
  if (!runtimeCommand.includes("--permission-mode")) runtimeCommand = `${runtimeCommand} --permission-mode ${mode === "auto" ? "auto" : BYPASS_PERMISSION_MODE}`;
41802
41440
  providerExportsStr = (await getProviderExportsForModel(model)).trim();
41803
41441
  await getProviderEnvForModel(model);
41804
- if (harness === "pi") {
41442
+ if (harness === "ohmypi") {
41805
41443
  if (getProviderForModelSync(model).name === "openai") {
41806
- const auth = await getPiCodexAuthStatus({ refreshIfExpired: true });
41807
- if (auth.status === "missing" || auth.status === "expired") throw new Error("Pi ChatGPT/Codex login (openai-codex) has expired and could not be refreshed. Re-authenticate with `pan pi-auth login`, then retry.");
41444
+ const auth = await getOhmypiCodexAuthStatus({ refreshIfExpired: true });
41445
+ if (auth.status === "missing" || auth.status === "expired") throw new Error("ohmypi ChatGPT/Codex login (openai-codex) has expired and could not be refreshed. Re-authenticate with `pan pi-auth login`, then retry.");
41808
41446
  }
41809
41447
  const paths = piFifoPaths(tmuxSession);
41810
41448
  const piSessionDir = join$1(paths.agentDir, "sessions");
@@ -41818,9 +41456,9 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
41818
41456
  });
41819
41457
  const storedPiSessionId = resume ? await readFile(join$1(paths.agentDir, "session.id"), "utf-8").then((s) => s.trim()).catch(() => void 0) : void 0;
41820
41458
  piFields = {
41821
- harness: "pi",
41459
+ harness: "ohmypi",
41822
41460
  piMode: "tui",
41823
- piExtensionPath: resolve$1(process.cwd(), "packages/pi-extension/dist/index.js"),
41461
+ piExtensionPath: resolveOhmypiExtensionPath() ?? resolve$1(process.cwd(), "packages/ohmypi-extension/dist/index.js"),
41824
41462
  piSessionDir,
41825
41463
  resumeSessionId: storedPiSessionId || void 0
41826
41464
  };
@@ -41830,7 +41468,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
41830
41468
  const codexApprovalPolicy = codexPermMode === "full-access" ? "never" : "on-request";
41831
41469
  const codexSandboxMode = codexPermMode === "full-access" ? "danger-full-access" : codexPermMode === "read-only" ? "read-only" : "workspace-write";
41832
41470
  const codexApprovalsReviewer = codexPermMode === "auto-review" ? "auto_review" : void 0;
41833
- const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-D3QWVTal.js");
41471
+ const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-sd8uk-U3.js");
41834
41472
  initCodexHome(codexHome, {
41835
41473
  trustedDir: cwd,
41836
41474
  approvalPolicy: codexApprovalPolicy,
@@ -41848,7 +41486,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
41848
41486
  }
41849
41487
  }
41850
41488
  let launcherModel = model;
41851
- if (harness === "pi" && model) {
41489
+ if (harness === "ohmypi" && model) {
41852
41490
  const piProvider = piProviderForModel(model);
41853
41491
  if (piProvider) launcherModel = `${piProvider}/${model}`;
41854
41492
  }
@@ -41900,7 +41538,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
41900
41538
  } catch {}
41901
41539
  console.log(`[claude-invoke] purpose=conversation-session | model=${model || "default"} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command="${runtimeCommand}"`);
41902
41540
  try {
41903
- const { preTrustDirectory } = await import("./workspace-manager-D6cUCwLt.js");
41541
+ const { preTrustDirectory } = await import("./workspace-manager-XDNuCCXG.js");
41904
41542
  preTrustDirectory(cwd);
41905
41543
  } catch {}
41906
41544
  try {
@@ -41917,7 +41555,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
41917
41555
  const codexHomeDir = codexFields.codexHome;
41918
41556
  (async () => {
41919
41557
  try {
41920
- const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-D3QWVTal.js");
41558
+ const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-sd8uk-U3.js");
41921
41559
  const rollout = await waitForCodexRollout(codexHomeDir, 12e4);
41922
41560
  if (rollout) {
41923
41561
  const threadId = extractThreadIdFromRollout(rollout);
@@ -42051,6 +41689,9 @@ function scheduleTitleRefinement(conversationName) {
42051
41689
  /** Conversations with a retitle currently running — guards against double-clicks. */
42052
41690
  const retitleInFlight = /* @__PURE__ */ new Set();
42053
41691
  const EXPLICIT_RETITLE_TIMEOUT_MS = 9e4;
41692
+ function isClaudeInvocationTimeout(error) {
41693
+ return error instanceof Error && /claude invocation timed out after \d+ms/.test(error.message);
41694
+ }
42054
41695
  /** transcript-size-keyed cache so re-opening the About drawer doesn't re-summarize. */
42055
41696
  const aboutSummaryCache = /* @__PURE__ */ new Map();
42056
41697
  const ABOUT_SUMMARY_CACHE_MAX = 100;
@@ -42452,7 +42093,7 @@ const postConversationRoute = HttpRouter.add("POST", "/api/conversations", Effec
42452
42093
  try {
42453
42094
  await spawnConversationSession(tmuxSession, cwd, claudeSessionId, model, effort, issueId, false, harness);
42454
42095
  console.log(`[conversations] tmux session ${tmuxSession} spawned, sessionId: ${claudeSessionId}`);
42455
- if (harness === "pi") await waitForPiTuiReady(tmuxSession);
42096
+ if (harness === "ohmypi") await waitForPiTuiReady(tmuxSession);
42456
42097
  else if (harness !== "codex") {
42457
42098
  await waitForClaudeReady(tmuxSession);
42458
42099
  console.log(`[conversations] Claude ready in ${tmuxSession}`);
@@ -42461,6 +42102,7 @@ const postConversationRoute = HttpRouter.add("POST", "/api/conversations", Effec
42461
42102
  } catch (spawnErr) {
42462
42103
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
42463
42104
  console.error(`[conversations] background spawn failed for ${tmuxSession}: ${msg}`);
42105
+ updateSpawnError(name, msg);
42464
42106
  getEventStore().emitOnly({
42465
42107
  type: "conversation.created",
42466
42108
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -42546,7 +42188,7 @@ const postConversationResumeRoute = HttpRouter.add("POST", "/api/conversations/:
42546
42188
  try {
42547
42189
  await spawnConversationSession(conv.tmuxSession, conv.cwd, oldSessionId ?? randomUUID$1(), model, effort, conv.issueId ?? void 0, canResume, harness);
42548
42190
  await waitForTmuxSession(conv.tmuxSession);
42549
- if (harness === "pi") await waitForPiTuiReady(conv.tmuxSession);
42191
+ if (harness === "ohmypi") await waitForPiTuiReady(conv.tmuxSession);
42550
42192
  else if (harness !== "codex") await waitForReadySignal(conv.tmuxSession, 30);
42551
42193
  markConversationActive(name);
42552
42194
  return jsonResponse({
@@ -42576,11 +42218,12 @@ const postConversationSwitchModelRoute = HttpRouter.add("POST", "/api/conversati
42576
42218
  try {
42577
42219
  const conv = getConversationByName(name);
42578
42220
  if (!conv) return jsonResponse({ error: "Conversation not found" }, { status: 404 });
42221
+ if (conv.claudeSessionId) return jsonResponse({ error: "Conversation model is locked once a conversation has started" }, { status: 409 });
42579
42222
  const model = typeof body["model"] === "string" && body["model"].trim() ? body["model"].trim() : conv.model ?? void 0;
42580
42223
  const currentHarness = conv.harness ?? "claude-code";
42581
42224
  const requestedHarness = body["harness"];
42582
42225
  let harness = currentHarness;
42583
- if (requestedHarness === "pi" || requestedHarness === "claude-code" || requestedHarness === "codex") {
42226
+ if (requestedHarness === "ohmypi" || requestedHarness === "claude-code" || requestedHarness === "codex") {
42584
42227
  if (requestedHarness !== currentHarness) {
42585
42228
  const policyModel = model ?? conv.model ?? "";
42586
42229
  const decision = canUseHarnessSync(requestedHarness, policyModel, await getProviderAuthMode(policyModel));
@@ -42591,101 +42234,15 @@ const postConversationSwitchModelRoute = HttpRouter.add("POST", "/api/conversati
42591
42234
  const harnessChanged = harness !== currentHarness;
42592
42235
  if (!await validateCwdContainment(conv.cwd)) return jsonResponse({ error: "Invalid cwd" }, { status: 400 });
42593
42236
  if (model && !SAFE_MODEL_PATTERN.test(model)) return jsonResponse({ error: "Invalid model" }, { status: 400 });
42594
- const oldSessionId = conv.claudeSessionId;
42595
- const sessionFile = await resolveSessionFile(conv);
42596
- const cwd = conv.cwd;
42597
- const tmuxSession = conv.tmuxSession;
42598
- const effort = conv.effort ?? void 0;
42599
- const issueId = conv.issueId ?? void 0;
42600
- const targetModel = model ?? conv.model ?? void 0;
42601
- const providerRoutingChanged = await hasProviderRoutingChanged(conv.model, targetModel);
42602
- const contextUsage = sessionFile && existsSync$1(sessionFile) ? await computeContextUsage(sessionFile, conv.model ?? targetModel ?? null) : null;
42603
- const contextTokens = contextUsage?.estimatedTokens ?? 0;
42604
- const currentObservedCeiling = Math.max(contextUsage?.maxObservedInputTokens ?? 0, contextTokens);
42605
- const switchStrategy = decideSwitchStrategy({
42606
- harnessChanged,
42607
- providerRoutingChanged,
42608
- contextTokens,
42609
- effectiveTargetWindow: getEffectiveTargetWindow({
42610
- registryWindow: registryContextWindowForModel(targetModel),
42611
- currentObservedCeiling,
42612
- cliproxyRouted: await isCliproxyRoutedModel(targetModel),
42613
- providerRoutingChanged
42614
- })
42237
+ if (model) setConversationModel(name, model);
42238
+ if (harnessChanged) setConversationHarness(name, harness);
42239
+ const updated = getConversationByName(name) ?? conv;
42240
+ return jsonResponse({
42241
+ ...updated,
42242
+ model: model ?? updated.model,
42243
+ harness,
42244
+ sessionAlive: false
42615
42245
  });
42616
- if (harness === "claude-code" && switchStrategy.useModelCommand && targetModel) {
42617
- if (model) setConversationModel(name, model);
42618
- try {
42619
- await deliverAgentMessage(tmuxSession, `/model ${targetModel}`, "conversation-switch-model", resolveConversationDeliveryMethod(conv));
42620
- if (await waitForModelStatusline(tmuxSession, targetModel)) {
42621
- markConversationActive(name);
42622
- return jsonResponse({
42623
- ...conv,
42624
- status: "active",
42625
- model: targetModel,
42626
- harness,
42627
- reattached: false,
42628
- sessionAlive: true
42629
- });
42630
- }
42631
- console.warn(`[conversations] switch-model Tier 1 statusline verification timed out for ${name}; falling back to respawn`);
42632
- } catch (deliveryErr) {
42633
- const msg = deliveryErr instanceof Error ? deliveryErr.message : String(deliveryErr);
42634
- console.warn(`[conversations] switch-model Tier 1 delivery failed for ${name}; falling back to respawn: ${msg}`);
42635
- }
42636
- }
42637
- const respawn = markRespawnPending(tmuxSession);
42638
- try {
42639
- await Effect.runPromise(killSession(tmuxSession).pipe(Effect.catch(() => Effect.succeed(void 0))));
42640
- if (model) setConversationModel(name, model);
42641
- if (harnessChanged) setConversationHarness(name, harness);
42642
- let resumeSessionId = oldSessionId ?? randomUUID$1();
42643
- let canResume = !!oldSessionId && !!sessionFile && existsSync$1(sessionFile);
42644
- if (oldSessionId && !canResume) console.error(`[conversations] SESSION-LOST ${name} harness=${currentHarness} claudeSessionId=${oldSessionId} resolved=${sessionFile ?? "null"} — switch-model will start a fresh session`);
42645
- if (harnessChanged) if (canResume && sessionFile) try {
42646
- const result = await Effect.runPromise(convertConversationTranscript({
42647
- fromHarness: currentHarness,
42648
- toHarness: harness,
42649
- sourceSessionFile: sessionFile,
42650
- cwd,
42651
- tmuxSession
42652
- }));
42653
- resumeSessionId = result.sessionId;
42654
- canResume = true;
42655
- if (harness === "claude-code") setConversationClaudeSessionId(name, result.sessionId);
42656
- } catch (convErr) {
42657
- const cm = convErr instanceof Error ? convErr.message : String(convErr);
42658
- console.error(`[conversations] SESSION-CONVERT-FAILED ${name} ${currentHarness}->${harness}: ${cm}`);
42659
- canResume = false;
42660
- }
42661
- else canResume = false;
42662
- else if (harness === "claude-code") {
42663
- const compactionFork = await maybeCompactBeforeRespawn({
42664
- sessionFile,
42665
- cwd,
42666
- shouldCompact: switchStrategy.compact
42667
- });
42668
- if (compactionFork) {
42669
- resumeSessionId = compactionFork.forkedSessionId;
42670
- setConversationClaudeSessionId(name, compactionFork.forkedSessionId);
42671
- }
42672
- }
42673
- await spawnConversationSession(tmuxSession, cwd, resumeSessionId, model, effort, issueId, canResume, harness);
42674
- await waitForTmuxSession(tmuxSession);
42675
- if (harness === "pi") await waitForPiTuiReady(tmuxSession);
42676
- else if (harness !== "codex") await waitForReadySignal(tmuxSession, 30);
42677
- markConversationActive(name);
42678
- return jsonResponse({
42679
- ...conv,
42680
- status: "active",
42681
- model,
42682
- harness,
42683
- reattached: false,
42684
- sessionAlive: true
42685
- });
42686
- } finally {
42687
- respawn.done();
42688
- }
42689
42246
  } catch (error) {
42690
42247
  const msg = error instanceof Error ? error.message : String(error);
42691
42248
  console.error("[conversations] switch model failed:", msg);
@@ -42735,7 +42292,7 @@ const getConversationMessagesRoute = HttpRouter.add("GET", "/api/conversations/:
42735
42292
  sessionFile = rollout;
42736
42293
  setSpecialistSessionCache(name, rollout);
42737
42294
  }
42738
- } else if (agentHarness === "pi") {
42295
+ } else if (agentHarness === "ohmypi") {
42739
42296
  const piSession = await resolvePiSessionPath(name);
42740
42297
  if (piSession) {
42741
42298
  sessionFile = piSession;
@@ -43238,7 +42795,7 @@ async function ensureForkSessionReady(conv, sessionId, resume, plainFork = false
43238
42795
  async function injectForkSummary(conv, summary, caller) {
43239
42796
  updateForkStatus(conv.name, "injecting");
43240
42797
  const method = resolveConversationDeliveryMethod(conv);
43241
- if (conv.harness === "pi") {
42798
+ if (conv.harness === "ohmypi") {
43242
42799
  await waitForPiTuiReady(conv.tmuxSession, 6e4);
43243
42800
  await deliverAgentMessage(conv.tmuxSession, summary, caller, method);
43244
42801
  return;
@@ -43268,7 +42825,7 @@ async function runForkPipeline(convName, parentConv, sessionId, summaryModel, fo
43268
42825
  const parentSessionFile = await resolveForkSourceSessionFile(parentConv);
43269
42826
  if (!parentSessionFile) throw new Error(`Parent has no session file`);
43270
42827
  if (forkMode === "plain") {
43271
- if (conv.harness === "pi" || conv.harness === "codex") throw new Error(`Plain forks cannot launch under the ${conv.harness} harness — it cannot consume Claude session history.`);
42828
+ if (conv.harness === "ohmypi" || conv.harness === "codex") throw new Error(`Plain forks cannot launch under the ${conv.harness} harness — it cannot consume Claude session history.`);
43272
42829
  if (!(await forkSessionExists(conv.tmuxSession) && await forkHarnessProcessAlive(conv.tmuxSession))) {
43273
42830
  const forkSessionFile = await resolveSessionFile(conv);
43274
42831
  if (!forkSessionFile) throw new Error(`Fork conversation ${convName} has no session file`);
@@ -43485,7 +43042,7 @@ const postConversationSummaryForkRoute = HttpRouter.add("POST", "/api/conversati
43485
43042
  const launchHarness = await resolveAllowedHarness(body["harness"], launchModel);
43486
43043
  const summaryHarness = await resolveAllowedHarness(body["summaryHarness"], effectiveSummaryModel);
43487
43044
  const handoffAuthorHarness = body["handoffAuthorHarness"] !== void 0 ? await resolveAllowedHarness(body["handoffAuthorHarness"], handoffAuthorModel || effectiveSummaryModel) : void 0;
43488
- if (forkMode === "plain" && (launchHarness === "pi" || launchHarness === "codex")) return jsonResponse({ error: `Plain forks cannot launch under ${launchHarness} — it cannot consume Claude session history. Use a summary fork instead.` }, { status: 400 });
43045
+ if (forkMode === "plain" && (launchHarness === "ohmypi" || launchHarness === "codex")) return jsonResponse({ error: `Plain forks cannot launch under ${launchHarness} — it cannot consume Claude session history. Use a summary fork instead.` }, { status: 400 });
43489
43046
  const defaultTitle = forkMode === "plain" ? `Fork: ${conv.title || conv.name}` : forkMode === "handoff" ? handoffTitleFromFocus(handoffFocus, conv.title || conv.name) : `Summary Fork: ${conv.title || conv.name}`;
43490
43047
  const newConv = createConversation({
43491
43048
  name: newName,
@@ -43818,7 +43375,15 @@ const postConversationRetitleRoute = HttpRouter.add("POST", "/api/conversations/
43818
43375
  try {
43819
43376
  const model = configuredTitleModel();
43820
43377
  console.log(`[claude-invoke] purpose=conversation-retitle | model=${model} | conversation=${name} | transcriptChars=${transcript.length}`);
43821
- const title = await summarizeTranscriptTitle(transcript, model, EXPLICIT_RETITLE_TIMEOUT_MS);
43378
+ let title;
43379
+ try {
43380
+ title = await summarizeTranscriptTitle(transcript, model, EXPLICIT_RETITLE_TIMEOUT_MS);
43381
+ } catch (error) {
43382
+ if (!isClaudeInvocationTimeout(error)) throw error;
43383
+ title = fallbackTranscriptTitle(transcript);
43384
+ if (!title) throw error;
43385
+ console.warn(`[conversations] retitle timed out for "${name}"; using deterministic fallback title "${title}"`);
43386
+ }
43822
43387
  if (!title) return jsonResponse({ error: "Title model returned an empty result" }, { status: 502 });
43823
43388
  updateConversationTitle(name, title, "ai");
43824
43389
  console.log(`[claude-invoke] SUCCESS purpose=conversation-retitle | conversation=${name} | title="${title}"`);
@@ -43914,6 +43479,8 @@ const conversationsRouteLayer = Layer.mergeAll(getConversationsRoute, getConvers
43914
43479
  * contract. Internal events are not exposed on this route.
43915
43480
  */
43916
43481
  init_event_store();
43482
+ init_http_helpers();
43483
+ init_http_handler();
43917
43484
  const PUBLIC_CATALOG = [
43918
43485
  "activity.entry",
43919
43486
  "activity.detailed",
@@ -44070,6 +43637,8 @@ const eventsRouteLayer = Layer.mergeAll(getEventStreamRoute, getEventVersionRout
44070
43637
  * GET /api/show/:issueId/shadow — shadow state details
44071
43638
  * GET /api/show/:issueId/health — health + heartbeat only
44072
43639
  */
43640
+ init_http_helpers();
43641
+ init_http_handler();
44073
43642
  init_shadow_state();
44074
43643
  const getShowRoute = HttpRouter.add("GET", "/api/show/:issueId", httpHandler(Effect.gen(function* () {
44075
43644
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
@@ -44168,7 +43737,7 @@ async function registerProjectFromPath(opts) {
44168
43737
  registerProjectSync(key, projectConfig);
44169
43738
  const seededContextLayer = ensureProjectLayer(fullPath);
44170
43739
  try {
44171
- const { preTrustDirectorySync } = await import("./workspace-manager-D6cUCwLt.js");
43740
+ const { preTrustDirectorySync } = await import("./workspace-manager-XDNuCCXG.js");
44172
43741
  preTrustDirectorySync(fullPath);
44173
43742
  } catch {}
44174
43743
  let hooksInstalled = 0;
@@ -44183,7 +43752,6 @@ async function registerProjectFromPath(opts) {
44183
43752
  }
44184
43753
  //#endregion
44185
43754
  //#region ../../lib/overdeck/specs.ts
44186
- init_pan_dir();
44187
43755
  init_harness();
44188
43756
  init_render();
44189
43757
  const findSpecByIssueThroughOverdeck = findSpecByIssue;
@@ -44195,14 +43763,22 @@ const findSpecByIssueThroughOverdeck = findSpecByIssue;
44195
43763
  * Implements:
44196
43764
  * GET /api/projects/:projectKey/session-tree
44197
43765
  */
43766
+ init_http_helpers();
43767
+ init_http_handler();
44198
43768
  init_projects();
43769
+ init_dashboard_auth();
44199
43770
  init_issue_id();
44200
43771
  init_tmux();
44201
- init_agents$1();
43772
+ init_concurrency$1();
43773
+ init_read_model();
43774
+ init_agents$2();
43775
+ init_agent_input_detection();
43776
+ init_review_status$1();
43777
+ init_jsonl_resolver();
44202
43778
  init_paths();
44203
43779
  const execAsync$2 = promisify(exec);
44204
43780
  async function getIssueDataService() {
44205
- const { getSharedIssueService } = await import("./issue-service-singleton-HZ65Do8r.js");
43781
+ const { getSharedIssueService } = await import("./issue-service-singleton-DfRSzdFO.js");
44206
43782
  return getSharedIssueService();
44207
43783
  }
44208
43784
  /** Returns true if the path exists (any type). */
@@ -44452,6 +44028,7 @@ async function collectSessionTreeNodes(issueId, workspacePath, projectPath, cont
44452
44028
  });
44453
44029
  }
44454
44030
  }
44031
+ enrichSessionsWithModelOrigin(sections, issueId);
44455
44032
  return sections.filter((s) => !isStaleLegacySession(s));
44456
44033
  }
44457
44034
  async function resolveFeatureTitle(issueId, issueLower, issueTitles, project) {
@@ -44715,12 +44292,16 @@ const postProjectsRoute = HttpRouter.add("POST", "/api/projects", httpHandler(Ef
44715
44292
  const projectsRouteLayer = Layer.mergeAll(getProjectSessionTreeRoute, getAllSessionTreesRoute, getProjectAutoMergeDefaultRoute, postProjectAutoMergeDefaultRoute, postProjectsRoute);
44716
44293
  //#endregion
44717
44294
  //#region routes/context.ts
44295
+ init_src();
44718
44296
  init_layers();
44719
44297
  init_paths();
44720
44298
  init_projects();
44299
+ init_http_helpers();
44300
+ init_domain_services();
44301
+ init_http_handler();
44721
44302
  const PREVIEW_HARNESSES = [
44722
44303
  "claude-code",
44723
- "pi",
44304
+ "ohmypi",
44724
44305
  "codex"
44725
44306
  ];
44726
44307
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
@@ -44979,9 +44560,9 @@ function fullPromptPreview(previews) {
44979
44560
  "",
44980
44561
  previews["claude-code"] || "(no rendered context)",
44981
44562
  "",
44982
- "## Overdeck-controlled Pi bundle",
44563
+ "## Overdeck-controlled oh-my-pi bundle",
44983
44564
  "",
44984
- previews.pi || "(no rendered context)",
44565
+ previews.ohmypi || "(no rendered context)",
44985
44566
  "",
44986
44567
  "## Overdeck-controlled Codex bundle",
44987
44568
  "",
@@ -45022,7 +44603,7 @@ async function previewContextLayers(projects, requestOrSelectedLayer, maybeDraft
45022
44603
  operation: "preview",
45023
44604
  previews: {
45024
44605
  "claude-code": previews["claude-code"],
45025
- pi: previews.pi,
44606
+ ohmypi: previews.ohmypi,
45026
44607
  codex: previews.codex,
45027
44608
  fullPrompt: fullPromptPreview(previews)
45028
44609
  },
@@ -45155,6 +44736,8 @@ const contextRouteLayer = Layer.mergeAll(getContextLayersRoute, postContextPrevi
45155
44736
  * Implements /api/admin/* endpoints mirroring the `pan admin` CLI namespace:
45156
44737
  * GET /api/admin/tldr/:issueId — TLDR daemon status for a workspace
45157
44738
  */
44739
+ init_http_helpers();
44740
+ init_http_handler();
45158
44741
  init_tldr_daemon();
45159
44742
  init_projects();
45160
44743
  const getAdminTldrRoute = HttpRouter.add("GET", "/api/admin/tldr/:issueId", httpHandler(Effect.gen(function* () {
@@ -45335,6 +44918,8 @@ async function installOx() {
45335
44918
  * GET /api/prereqs/:feature — check which tools are missing for a feature
45336
44919
  * POST /api/prereqs/install — install a specific tool by name
45337
44920
  */
44921
+ init_http_helpers();
44922
+ init_http_handler();
45338
44923
  const readJsonBody$3 = Effect.gen(function* () {
45339
44924
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
45340
44925
  try {
@@ -45381,6 +44966,8 @@ const installToolRoute = HttpRouter.add("POST", "/api/prereqs/install", httpHand
45381
44966
  const prereqsRouteLayer = Layer.mergeAll(listPrereqsRoute, checkFeatureRoute, installToolRoute);
45382
44967
  //#endregion
45383
44968
  //#region routes/cliproxy.ts
44969
+ init_http_helpers();
44970
+ init_http_handler();
45384
44971
  init_cliproxy();
45385
44972
  let lastStatus = null;
45386
44973
  async function refreshStatus() {
@@ -45452,6 +45039,10 @@ const postCliproxyRestartRoute = HttpRouter.add("POST", "/api/cliproxy/restart",
45452
45039
  });
45453
45040
  })));
45454
45041
  const cliproxyRouteLayer = Layer.mergeAll(getCliproxyStatusRoute, postCliproxyRestartRoute);
45042
+ //#endregion
45043
+ //#region routes/tts.ts
45044
+ init_origin_validation();
45045
+ init_http_helpers();
45455
45046
  const TTS_ROUTE_BODY_MAX_BYTES = 64 * 1024;
45456
45047
  async function checkTtsHealth(options = {}) {
45457
45048
  const runtimeConfig = getTtsRuntimeConfig();
@@ -45780,7 +45371,7 @@ const ttsRouteLayer = Layer.mergeAll(getTtsHealthRoute, postTtsStartRoute, getTt
45780
45371
  * - Diff's the PR's failing check names against main's current failing set so
45781
45372
  * inherited main-red failures are labelled as such.
45782
45373
  */
45783
- init_agents$1();
45374
+ init_agents$2();
45784
45375
  init_projects();
45785
45376
  init_feedback_writer();
45786
45377
  function execFilePromise(file, args, options) {
@@ -46090,6 +45681,7 @@ async function refreshMergeStateFromGitHub(issueId, repo, prNumber) {
46090
45681
  encoding: "utf-8",
46091
45682
  timeout: 15e3
46092
45683
  });
45684
+ if (!stdout.trim()) return;
46093
45685
  const pr = JSON.parse(stdout);
46094
45686
  const status = await Effect.runPromise(getReviewStatus(issueId));
46095
45687
  if (!status) return;
@@ -46203,8 +45795,8 @@ async function handlePullRequestPromise(payload) {
46203
45795
  if (!issueId) return;
46204
45796
  const repo = payload.repository.full_name;
46205
45797
  if (payload.action === "closed" && pr.merged === true) try {
46206
- const { postMergeLifecycle } = await import("./merge-agent-Bn54C3oA.js");
46207
- const { resolveProjectFromIssueSync } = await import("./projects-BBiAZIGj.js");
45798
+ const { postMergeLifecycle } = await import("./merge-agent-CdxCcqFv.js");
45799
+ const { resolveProjectFromIssueSync } = await import("./projects-DWBDcB6V.js");
46208
45800
  const project = resolveProjectFromIssueSync(issueId);
46209
45801
  if (project) {
46210
45802
  const branchName = pr.head.ref;
@@ -46472,6 +46064,8 @@ async function reconcileStaleGitHubBlockers() {
46472
46064
  * Verifies X-Hub-Signature-256 HMAC-SHA256 signature.
46473
46065
  * Dispatches to per-event-type handlers.
46474
46066
  */
46067
+ init_http_helpers();
46068
+ init_http_handler();
46475
46069
  const WEBHOOK_SECRET_PATH = join$1(homedir$1(), ".overdeck", "github-app", "webhook-secret");
46476
46070
  let _cachedWebhookSecret = void 0;
46477
46071
  let _secretLoadPromise = null;
@@ -46584,6 +46178,7 @@ const webhooksRouteLayer = HttpRouter.add("POST", "/api/webhooks/github", httpHa
46584
46178
  //#region ../../lib/compliance/advisory-warning.ts
46585
46179
  init_config_yaml();
46586
46180
  init_paths$2();
46181
+ init_rollup();
46587
46182
  const COMPLIANCE_ADVISORY_WARNING = "Last turn included a memory-first trigger but the search wasn't called. Try pan memory search first next time.";
46588
46183
  async function resolveComplianceAdvisoryWarning(input) {
46589
46184
  if (await (input.loadComplianceMode ?? loadComplianceMode)() === "off") return null;
@@ -46625,8 +46220,9 @@ function isCurrentSessionMiss(observation, identity) {
46625
46220
  //#endregion
46626
46221
  //#region ../../lib/memory/checkpoint-client.ts
46627
46222
  init_settings();
46628
- init_health();
46223
+ init_health$1();
46629
46224
  init_providers$1();
46225
+ init_pending();
46630
46226
  init_query_expansion();
46631
46227
  init_injection();
46632
46228
  let worker = null;
@@ -46661,7 +46257,7 @@ function getCheckpointWorker() {
46661
46257
  return worker;
46662
46258
  }
46663
46259
  async function runInline(operation, payload) {
46664
- const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-BnDRdb3A.js");
46260
+ const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-BRNSJDyI.js");
46665
46261
  switch (operation) {
46666
46262
  case "claimTranscriptRange": return claimTranscriptRange(payload);
46667
46263
  case "commitTranscriptRange": return commitTranscriptRange(payload);
@@ -46711,6 +46307,92 @@ const getTranscriptCheckpoint = (sessionId) => Effect.tryPromise({
46711
46307
  catch: toError
46712
46308
  });
46713
46309
  //#endregion
46310
+ //#region ../../lib/session-format-converter.ts
46311
+ init_paths();
46312
+ /** Pull plain text out of a Claude/Pi `message.content` (string or block array). */
46313
+ function extractContentText(content) {
46314
+ if (typeof content === "string") return content;
46315
+ if (!Array.isArray(content)) return "";
46316
+ const parts = [];
46317
+ for (const block of content) {
46318
+ if (!block || typeof block !== "object") continue;
46319
+ const b = block;
46320
+ if (typeof b["text"] === "string") parts.push(b["text"]);
46321
+ else if (typeof b["thinking"] === "string") parts.push(`[thinking] ${b["thinking"]}`);
46322
+ else if ((b["type"] === "tool_use" || b["type"] === "toolCall") && typeof b["name"] === "string") parts.push(`[tool_use: ${b["name"]}]`);
46323
+ else if (b["type"] === "tool_result" || b["type"] === "toolResult") {
46324
+ const inner = extractContentText(b["content"]);
46325
+ if (inner) parts.push(`[tool_result] ${inner}`);
46326
+ }
46327
+ }
46328
+ return parts.join("\n").trim();
46329
+ }
46330
+ /** Read a Pi Agent JSONL transcript into ordered role/text turns. */
46331
+ function extractPiTranscript(raw) {
46332
+ const turns = [];
46333
+ for (const line of raw.split("\n")) {
46334
+ const trimmed = line.trim();
46335
+ if (!trimmed) continue;
46336
+ let entry;
46337
+ try {
46338
+ entry = JSON.parse(trimmed);
46339
+ } catch {
46340
+ continue;
46341
+ }
46342
+ if (entry["type"] !== "message") continue;
46343
+ const message = entry["message"];
46344
+ if (!message || typeof message !== "object") continue;
46345
+ const m = message;
46346
+ const role = typeof m["role"] === "string" ? m["role"] : "assistant";
46347
+ const text = extractContentText(m["content"]);
46348
+ if (text) turns.push({
46349
+ role,
46350
+ text
46351
+ });
46352
+ }
46353
+ return turns;
46354
+ }
46355
+ /**
46356
+ * Read a Codex rollout JSONL transcript into ordered role/text turns.
46357
+ *
46358
+ * Rollout files use a different schema from --json stdout:
46359
+ * - task_started: the initial task (user role)
46360
+ * - agent_message: a model turn (assistant role)
46361
+ * - token_count: metadata — skipped
46362
+ *
46363
+ * We extract task_started as the user prompt and agent_message events as
46364
+ * assistant turns to produce a readable transcript.
46365
+ */
46366
+ function extractCodexTranscript(raw) {
46367
+ const turns = [];
46368
+ for (const line of raw.split("\n")) {
46369
+ const trimmed = line.trim();
46370
+ if (!trimmed) continue;
46371
+ let entry;
46372
+ try {
46373
+ entry = JSON.parse(trimmed);
46374
+ } catch {
46375
+ continue;
46376
+ }
46377
+ const type = entry["type"];
46378
+ if (type === "task_started") {
46379
+ const task = typeof entry["task"] === "string" ? entry["task"] : "";
46380
+ if (task) turns.push({
46381
+ role: "user",
46382
+ text: task
46383
+ });
46384
+ } else if (type === "agent_message") {
46385
+ const content = typeof entry["content"] === "string" ? entry["content"] : extractContentText(entry["content"]);
46386
+ if (content) turns.push({
46387
+ role: "assistant",
46388
+ text: content
46389
+ });
46390
+ }
46391
+ }
46392
+ return turns;
46393
+ }
46394
+ Data.TaggedError("SessionConvertError");
46395
+ //#endregion
46714
46396
  //#region ../../lib/memory/compress.ts
46715
46397
  const MAX_USER_CHARS = 4e4;
46716
46398
  const MAX_TRANSCRIPT_DELTA_BYTES = 1024 * 1024;
@@ -46830,7 +46512,7 @@ function isRecord$1(value) {
46830
46512
  }
46831
46513
  //#endregion
46832
46514
  //#region ../../lib/memory/transcript-source.ts
46833
- init_agents$1();
46515
+ init_agents$2();
46834
46516
  init_paths();
46835
46517
  init_codex();
46836
46518
  var ClaudeCodeTranscriptSource = class {
@@ -46883,7 +46565,7 @@ var ClaudeCodeTranscriptSource = class {
46883
46565
  }
46884
46566
  };
46885
46567
  var PiTranscriptSource = class {
46886
- harness = "pi";
46568
+ harness = "ohmypi";
46887
46569
  listAgents;
46888
46570
  readSessionId;
46889
46571
  resolveTranscriptPath;
@@ -46896,7 +46578,7 @@ var PiTranscriptSource = class {
46896
46578
  }
46897
46579
  async getActiveTranscripts() {
46898
46580
  const agents = await this.listAgents();
46899
- return (await Promise.all(agents.filter((agent) => agent.tmuxActive && agent.status === "running" && agent.role === "work" && agent.harness === "pi").map((agent) => this.resolveAgentTranscript(agent)))).filter((entry) => entry !== null);
46581
+ return (await Promise.all(agents.filter((agent) => agent.tmuxActive && agent.status === "running" && agent.role === "work" && agent.harness === "ohmypi").map((agent) => this.resolveAgentTranscript(agent)))).filter((entry) => entry !== null);
46900
46582
  }
46901
46583
  parseDelta(buffer, fromOffset = 0) {
46902
46584
  const text = Buffer.isBuffer(buffer) ? buffer.toString("utf8") : buffer;
@@ -46927,7 +46609,7 @@ var PiTranscriptSource = class {
46927
46609
  sessionId,
46928
46610
  transcriptPath,
46929
46611
  identity: buildMemoryIdentity(agent, sessionId),
46930
- harness: "pi",
46612
+ harness: "ohmypi",
46931
46613
  size: fileStat.size,
46932
46614
  mtimeMs: fileStat.mtimeMs
46933
46615
  };
@@ -47064,6 +46746,7 @@ async function readCodexThreadId(agent) {
47064
46746
  }
47065
46747
  //#endregion
47066
46748
  //#region ../../lib/memory/extract.ts
46749
+ init_src();
47067
46750
  const MEMORY_DOMAIN_TAGS = [
47068
46751
  "review-blocker",
47069
46752
  "test-failure",
@@ -47377,7 +47060,7 @@ function isRecord(value) {
47377
47060
  }
47378
47061
  //#endregion
47379
47062
  //#region ../../lib/memory/pipeline.ts
47380
- init_health();
47063
+ init_health$1();
47381
47064
  async function extractFromTranscriptDelta(input) {
47382
47065
  if (isSubagentHookPayload(input.hookPayload)) return {
47383
47066
  status: "noop",
@@ -47728,7 +47411,7 @@ function buildPendingTurn(input, fromOffset, toOffset, compressed) {
47728
47411
  };
47729
47412
  }
47730
47413
  async function emitMemoryObservationCreated(observation, timestamp) {
47731
- const { initEventStore } = await import("./event-store-f-xTPhth.js");
47414
+ const { initEventStore } = await import("./event-store-D_9DeP8Y.js");
47732
47415
  await (await initEventStore()).appendAsync({
47733
47416
  type: "memory.observation_created",
47734
47417
  timestamp,
@@ -47737,7 +47420,7 @@ async function emitMemoryObservationCreated(observation, timestamp) {
47737
47420
  }
47738
47421
  //#endregion
47739
47422
  //#region ../../lib/memory/worker-pool.ts
47740
- init_health();
47423
+ init_health$1();
47741
47424
  init_providers$1();
47742
47425
  function normalizeQueueLimit(value) {
47743
47426
  return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : 500;
@@ -48231,10 +47914,14 @@ function countCompleteLines(text) {
48231
47914
  * `hook_event_name`. We look up the conversation by claude_session_id and
48232
47915
  * emit an in-memory-only event via emitOnly().
48233
47916
  */
47917
+ init_http_helpers();
47918
+ init_http_handler();
48234
47919
  init_event_store();
48235
- init_agents$1();
47920
+ init_agents$2();
48236
47921
  init_paths();
48237
47922
  init_paths$2();
47923
+ init_dashboard_auth();
47924
+ init_read_model();
48238
47925
  init_conversations();
48239
47926
  init_briefing_freshness();
48240
47927
  init_settings();
@@ -48593,7 +48280,10 @@ function isRole(value) {
48593
48280
  * GET /api/agents/:agentId/diffs/:turnId — unified diff for a specific turn
48594
48281
  * GET /api/agents/:agentId/diffs/full — full thread diff (all turns combined)
48595
48282
  */
48283
+ init_http_helpers();
48284
+ init_read_model();
48596
48285
  init_event_store();
48286
+ init_checkpoint_manager();
48597
48287
  function validateOrigin(request) {
48598
48288
  const origin = request.headers["origin"];
48599
48289
  if (origin && typeof origin === "string" && !origin.includes("localhost") && !origin.includes("127.0.0.1")) return {
@@ -48789,7 +48479,11 @@ const diffsRouteLayer = Layer.mergeAll(getDiffsRoute, getTurnDiffRoute, getFullD
48789
48479
  * t3code's `terminal.*` contracts, this create/kill pair is the seam that gets
48790
48480
  * folded into that RPC surface.
48791
48481
  */
48482
+ init_http_helpers();
48483
+ init_http_handler();
48792
48484
  init_tmux();
48485
+ init_origin_validation();
48486
+ init_dashboard_auth();
48793
48487
  const readJsonBody$2 = Effect.gen(function* () {
48794
48488
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
48795
48489
  return text ? JSON.parse(text) : {};
@@ -48850,7 +48544,12 @@ const terminalsRouteLayer = Layer.mergeAll(postTerminalRoute, deleteTerminalRout
48850
48544
  *
48851
48545
  * Zero sync FS calls — lib functions use fs/promises; SQLite work stays in the existing sync DB layer.
48852
48546
  */
48547
+ init_domain_services();
48548
+ init_http_helpers();
48549
+ init_http_handler();
48853
48550
  init_config_yaml();
48551
+ init_origin_validation();
48552
+ init_dashboard_auth();
48854
48553
  function getRequestHeader(request, name) {
48855
48554
  const value = request.headers[name];
48856
48555
  if (Array.isArray(value)) return value[0];
@@ -50143,7 +49842,9 @@ const AUTO_MERGE_COOLDOWN_MS = 3e5;
50143
49842
  //#endregion
50144
49843
  //#region ../../lib/cloister/auto-merge-eligibility.ts
50145
49844
  init_github_app();
49845
+ init_forge();
50146
49846
  init_review_status();
49847
+ init_tracker_utils();
50147
49848
  const execFileAsync = promisify(execFile);
50148
49849
  const BLOCKER_LABELS = [
50149
49850
  "needs-design",
@@ -50324,8 +50025,15 @@ function getProjectAutoMergeDefault(issueId) {
50324
50025
  }
50325
50026
  //#endregion
50326
50027
  //#region routes/flywheel.ts
50028
+ init_http_helpers();
50029
+ init_src();
50030
+ init_http_handler();
50031
+ init_origin_validation();
50032
+ init_flywheel_run_state();
50033
+ init_dashboard_auth();
50327
50034
  init_tmux();
50328
50035
  init_control_settings();
50036
+ init_forge();
50329
50037
  init_review_status();
50330
50038
  init_projects();
50331
50039
  init_merge_sync();
@@ -50631,7 +50339,7 @@ async function getFlywheelConversationPayload() {
50631
50339
  };
50632
50340
  }
50633
50341
  async function postFlywheelReportPayload() {
50634
- const { flywheelReportCommand } = await import("./flywheel-BkaWOjhl.js");
50342
+ const { flywheelReportCommand } = await import("./flywheel-NZJyASRc.js");
50635
50343
  await flywheelReportCommand({ cwd: process.cwd() });
50636
50344
  return {
50637
50345
  status: 200,
@@ -50820,11 +50528,11 @@ const getMergeBlockersRoute = HttpRouter.add("GET", "/api/flywheel/merge-blocker
50820
50528
  async function defaultGetOrderedIssueIds() {
50821
50529
  const status = await readCurrentFlywheelStatusForDashboard();
50822
50530
  if (!status) return [];
50823
- const { computeMergeQueue } = await import("./flywheel-merge-order-B_SKQBbD.js");
50531
+ const { computeMergeQueue } = await import("./flywheel-merge-order-BkA6937p.js");
50824
50532
  return (await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd()).pipe(Effect.provide(layer)))).map((i) => i.issueId);
50825
50533
  }
50826
50534
  async function defaultMergeOne(issueId) {
50827
- const { triggerMerge } = await import("./workspaces-DTnkVSs7.js");
50535
+ const { triggerMerge } = await import("./workspaces-DL8hfuDT.js");
50828
50536
  const r = await triggerMerge(issueId);
50829
50537
  return r.success ? { ok: true } : {
50830
50538
  ok: false,
@@ -50844,7 +50552,7 @@ async function postFlywheelMergeNextPayload(payload, deps = {}) {
50844
50552
  body: { error: "n must be a positive integer" }
50845
50553
  };
50846
50554
  const issueIds = (await (deps.getOrderedIssueIds ?? defaultGetOrderedIssueIds)()).slice(0, n);
50847
- const { shipMergeBatch } = await import("./merge-batch-BrWZSfeO.js");
50555
+ const { shipMergeBatch } = await import("./merge-batch-CaSP7OL6.js");
50848
50556
  return {
50849
50557
  status: 200,
50850
50558
  body: { outcomes: await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne }) }
@@ -51001,7 +50709,7 @@ const getFlywheelMergeQueueRoute = HttpRouter.add("GET", "/api/flywheel/merge-qu
51001
50709
  return yield* Effect.promise(async () => {
51002
50710
  const status = await readCurrentFlywheelStatusForDashboard();
51003
50711
  if (!status) return jsonResponse([]);
51004
- const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-B_SKQBbD.js");
50712
+ const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-BkA6937p.js");
51005
50713
  return jsonResponse(await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd(), { getPrUrl: resolveMergeQueuePrUrl }).pipe(Effect.provide(layer))));
51006
50714
  });
51007
50715
  })));
@@ -51015,14 +50723,14 @@ function uatGenerationNameFromParam(param) {
51015
50723
  return decoded.startsWith("uat/") ? decoded : `uat/${decoded}`;
51016
50724
  }
51017
50725
  const getUatGenerationsRoute = HttpRouter.add("GET", "/api/flywheel/uat-generations", httpHandler(Effect.gen(function* () {
51018
- const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-B3-shx6M.js"));
50726
+ const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-C6_NdOfR.js"));
51019
50727
  return jsonResponse(yield* Effect.promise(() => getUatGenerationsPayload()));
51020
50728
  })));
51021
50729
  const postUatGenerationStackRoute = HttpRouter.add("POST", "/api/flywheel/uat-generations/:name/stack", httpHandler(Effect.gen(function* () {
51022
50730
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
51023
50731
  if (authError) return authError;
51024
50732
  const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
51025
- const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-B3-shx6M.js"));
50733
+ const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-C6_NdOfR.js"));
51026
50734
  const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
51027
50735
  if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
51028
50736
  return jsonResponse({
@@ -51034,8 +50742,8 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
51034
50742
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
51035
50743
  if (authError) return authError;
51036
50744
  const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
51037
- const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-B3-shx6M.js"));
51038
- const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-CuT8QcV6.js"));
50745
+ const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-C6_NdOfR.js"));
50746
+ const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-Bbj7bCwh.js"));
51039
50747
  const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle));
51040
50748
  if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
51041
50749
  return jsonResponse(result);
@@ -51048,7 +50756,7 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
51048
50756
  const postFlywheelAssembleUatRoute = HttpRouter.add("POST", "/api/flywheel/assemble-uat", httpHandler(Effect.gen(function* () {
51049
50757
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
51050
50758
  if (authError) return authError;
51051
- const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-B3-shx6M.js"));
50759
+ const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-C6_NdOfR.js"));
51052
50760
  return jsonResponse(yield* Effect.promise(() => runUatTrainReconcile({ force: true })));
51053
50761
  })));
51054
50762
  const getFlywheelStateRoute = HttpRouter.add("GET", "/api/flywheel/state", httpHandler(Effect.gen(function* () {
@@ -51156,6 +50864,9 @@ function escapeXml(value) {
51156
50864
  //#endregion
51157
50865
  //#region routes/artifacts.ts
51158
50866
  init_paths();
50867
+ init_http_helpers();
50868
+ init_dashboard_auth();
50869
+ init_http_handler();
51159
50870
  const ARTIFACT_SLUG_PATTERN = /^[A-Za-z0-9_-]{8}$/;
51160
50871
  const WORKSPACE_ARTIFACT_SELECTOR_PATTERN = /^[A-Za-z0-9._:-]+$/;
51161
50872
  const RAW_ARTIFACT_CSP = [
@@ -51469,6 +51180,9 @@ const postArtifactUnshareRoute = HttpRouter.add("POST", "/api/artifacts/:slug/un
51469
51180
  const artifactsRouteLayer = Layer.mergeAll(getRawArtifactRoute, getArtifactDetailRoute, getWorkspaceArtifactsRoute, getArtifactThumbnailRoute, postArtifactUnshareRoute);
51470
51181
  //#endregion
51471
51182
  //#region ../../lib/backlog/label-ops.ts
51183
+ init_sequence_io();
51184
+ init_tracker_utils();
51185
+ init_pickup();
51472
51186
  const execAsync = promisify(exec);
51473
51187
  /**
51474
51188
  * Add or remove a GitHub label on an issue. Non-GitHub issues are silently
@@ -51490,8 +51204,13 @@ const applyIssueBlocksMainLabel = (id) => addIssueLabel(id, BLOCKS_MAIN_LABEL);
51490
51204
  const removeIssueBlocksMainLabel = (id) => removeIssueLabel(id, BLOCKS_MAIN_LABEL);
51491
51205
  const applyIssueReadyLabel = (id) => addIssueLabel(id, READY_LABEL);
51492
51206
  const removeIssueReadyLabel = (id) => removeIssueLabel(id, READY_LABEL);
51207
+ const applyIssueReleasedLabel = (id) => addIssueLabel(id, RELEASED_LABEL);
51208
+ const removeIssueReleasedLabel = (id) => removeIssueLabel(id, RELEASED_LABEL);
51209
+ const applyIssueObjectionLabel = (id) => addIssueLabel(id, OBJECTION_LABEL);
51210
+ const removeIssueObjectionLabel = (id) => removeIssueLabel(id, OBJECTION_LABEL);
51493
51211
  //#endregion
51494
51212
  //#region ../../lib/database/agent-mappers.ts
51213
+ init_lookups();
51495
51214
  function agentStateToDbAgent(state) {
51496
51215
  return {
51497
51216
  id: state.id,
@@ -51666,6 +51385,7 @@ function buildNamedParams(row) {
51666
51385
  function backfillAgentsFromStateJsonSync(db, options) {
51667
51386
  const agentsDir = join$1(getOverdeckHome(), "agents");
51668
51387
  const liveSessions = options?.listLiveSessions?.() ?? listLiveTmuxSessionNames();
51388
+ const readHarnessModel = options?.readHarnessModel ?? readAgentHarnessModelRecordSync;
51669
51389
  let processed = 0;
51670
51390
  let skipped = 0;
51671
51391
  let markedStopped = 0;
@@ -51706,7 +51426,16 @@ function backfillAgentsFromStateJsonSync(db, options) {
51706
51426
  }
51707
51427
  const reconciled = reconcileAgentStatus(state, liveSessions);
51708
51428
  if (reconciled.status === "stopped" && state.status !== "stopped") markedStopped++;
51709
- const row = agentStateToDbAgent(reconciled);
51429
+ let effective = reconciled;
51430
+ if (reconciled.issueId) {
51431
+ const rec = readHarnessModel(reconciled.issueId);
51432
+ if (rec?.harness || rec?.model) {
51433
+ effective = { ...reconciled };
51434
+ if (rec.harness) effective.harness = rec.harness;
51435
+ if (rec.model) effective.model = rec.model;
51436
+ }
51437
+ }
51438
+ const row = agentStateToDbAgent(effective);
51710
51439
  upsert.run(buildNamedParams(row));
51711
51440
  processed++;
51712
51441
  if (options?.verbose) console.log(`[backfill] ${row.id} -> ${row.status}`);
@@ -52440,7 +52169,7 @@ function initSchema(db) {
52440
52169
  */
52441
52170
  function runMigrations(db, dbPath) {
52442
52171
  const currentVersion = db.pragma("user_version", { simple: true });
52443
- if (currentVersion === 57) return;
52172
+ if (currentVersion >= 57) return;
52444
52173
  if (currentVersion === 0) {
52445
52174
  initSchema(db);
52446
52175
  return;
@@ -53318,8 +53047,14 @@ function getBacklogSequence(projectKey) {
53318
53047
  }
53319
53048
  //#endregion
53320
53049
  //#region routes/backlog.ts
53050
+ init_http_handler();
53051
+ init_http_helpers();
53052
+ init_dashboard_auth();
53053
+ init_sequence_io();
53054
+ init_pickup();
53321
53055
  init_review_status();
53322
- init_agents$1();
53056
+ init_sequencer_agent();
53057
+ init_jsonl_resolver();
53323
53058
  const readJsonBody = Effect.gen(function* () {
53324
53059
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
53325
53060
  try {
@@ -53413,6 +53148,7 @@ const postBacklogRegenerateRoute = HttpRouter.add("POST", "/api/backlog/sequence
53413
53148
  const { getSharedIssueService } = (init_issue_service_singleton(), __toCommonJS(issue_service_singleton_exports));
53414
53149
  const issues = getSharedIssueService().getIssues();
53415
53150
  try {
53151
+ await clearFinishedSequencerRun(projectRoot);
53416
53152
  return jsonResponse({
53417
53153
  status: "spawned",
53418
53154
  agentId: (await spawnSequencerAgent(pass, {
@@ -53580,6 +53316,8 @@ const postBacklogLabelsRoute = HttpRouter.add("POST", "/api/backlog/sequence/lab
53580
53316
  if (typeof body["ready"] === "boolean") await (body["ready"] ? applyIssueReadyLabel : removeIssueReadyLabel)(issueId);
53581
53317
  if (typeof body["parked"] === "boolean") await (body["parked"] ? applyIssueParkedLabel : removeIssueParkedLabel)(issueId);
53582
53318
  if (typeof body["blocksMain"] === "boolean") await (body["blocksMain"] ? applyIssueBlocksMainLabel : removeIssueBlocksMainLabel)(issueId);
53319
+ if (typeof body["released"] === "boolean") await (body["released"] ? applyIssueReleasedLabel : removeIssueReleasedLabel)(issueId);
53320
+ if (typeof body["objection"] === "boolean") await (body["objection"] ? applyIssueObjectionLabel : removeIssueObjectionLabel)(issueId);
53583
53321
  return jsonResponse({
53584
53322
  status: "ok",
53585
53323
  issueId
@@ -53608,14 +53346,7 @@ const postBacklogClearRoute = HttpRouter.add("POST", "/api/backlog/sequence/clea
53608
53346
  const getSequencerStatusRoute = HttpRouter.add("GET", "/api/backlog/sequencer-status", httpHandler(Effect.gen(function* () {
53609
53347
  return yield* Effect.promise(async () => {
53610
53348
  const projectRoot = process.cwd();
53611
- const alive = listRunningAgentsSync().some((a) => a.id === "sequencer-runner" && a.tmuxActive);
53612
- const startedAt = alive ? getAgentStateSync("sequencer-runner")?.startedAt ?? null : null;
53613
- const seqPath = join$1(projectRoot, ".pan", "backlog", "sequence.md");
53614
- let freshSequence = false;
53615
- if (alive && startedAt && existsSync$1(seqPath)) try {
53616
- freshSequence = statSync$1(seqPath).mtimeMs >= new Date(startedAt).getTime();
53617
- } catch {}
53618
- const running = alive && !freshSequence;
53349
+ const { running, startedAt } = getSequencerRunStatus(projectRoot);
53619
53350
  let total = 0;
53620
53351
  const manifestPath = join$1(projectRoot, ".pan", "backlog", "manifest.json");
53621
53352
  if (existsSync$1(manifestPath)) try {
@@ -53657,6 +53388,8 @@ function listFeatureRegistryForDashboard(filter = {}) {
53657
53388
  }
53658
53389
  //#endregion
53659
53390
  //#region routes/feature-registry.ts
53391
+ init_http_helpers();
53392
+ init_http_handler();
53660
53393
  async function getFeatureRegistryListPayload(options = {}) {
53661
53394
  return { entries: await (options.listEntries ?? listFeatureRegistryForDashboard)({ limit: 100 }) };
53662
53395
  }
@@ -53671,6 +53404,9 @@ const featureRegistryRouteLayer = Layer.provideMerge(listFeatureRegistryRoute, L
53671
53404
  *
53672
53405
  * GET /api/fs/list-dirs?path=<abs> — list immediate subdirectories, home-clamped + auth-gated
53673
53406
  */
53407
+ init_http_helpers();
53408
+ init_http_handler();
53409
+ init_dashboard_auth();
53674
53410
  const listDirsRoute = HttpRouter.add("GET", "/api/fs/list-dirs", httpHandler(Effect.gen(function* () {
53675
53411
  const req = yield* HttpServerRequest.HttpServerRequest;
53676
53412
  const authError = rejectUnauthorizedDashboardRequest(req);
@@ -53721,7 +53457,14 @@ const fsRouteLayer = Layer.mergeAll(listDirsRoute);
53721
53457
  * GET /ws/terminal → Raw WebSocket terminal (bypasses Effect RPC)
53722
53458
  * GET * → static files from OVERDECK_FRONTEND_DIR
53723
53459
  */
53460
+ init_http_helpers();
53461
+ init_domain_services();
53462
+ init_read_model();
53463
+ init_cost();
53724
53464
  init_infra();
53465
+ init_linear_client();
53466
+ init_dashboard_auth();
53467
+ init_origin_validation();
53725
53468
  init_activity_logger();
53726
53469
  const HttpServerLive = Layer.unwrap(Effect.gen(function* () {
53727
53470
  const config = yield* ServerConfig;
@@ -53858,10 +53601,14 @@ const runServer = Layer.launch(makeServerLayer);
53858
53601
  * These fields were dropped in the Effect server migration (PAN-428) and are
53859
53602
  * restored here via the event-driven projection pipeline.
53860
53603
  */
53861
- init_agents$1();
53604
+ init_agents$2();
53605
+ init_agent_enrichment();
53862
53606
  init_review_status();
53607
+ init_concurrency$1();
53863
53608
  init_event_store();
53609
+ init_agent_projection();
53864
53610
  init_activity_logger();
53611
+ init_read_model();
53865
53612
  function enrichmentChanged(prev, next) {
53866
53613
  if (!prev) return true;
53867
53614
  return prev.role !== next.role || prev.hasPendingQuestion !== next.hasPendingQuestion || prev.pendingQuestionCount !== next.pendingQuestionCount || prev.pendingQuestionPrompt !== next.pendingQuestionPrompt || prev.pendingQuestionReason !== next.pendingQuestionReason || prev.pendingInputCount !== next.pendingInputCount || prev.pendingInputKinds.join(",") !== next.pendingInputKinds.join(",") || prev.pendingAskUserQuestion?.toolUseId !== next.pendingAskUserQuestion?.toolUseId || prev.resolution !== next.resolution || prev.resolutionCount !== next.resolutionCount;
@@ -54140,8 +53887,9 @@ function drainRefreshQueue(state) {
54140
53887
  * expects — the reducer and WebSocket subscription already exist, but no
54141
53888
  * server code was emitting the events.
54142
53889
  */
54143
- init_agents$1();
53890
+ init_agents$2();
54144
53891
  init_tmux();
53892
+ init_concurrency$1();
54145
53893
  init_event_store();
54146
53894
  function splitLines(text) {
54147
53895
  const lines = text.split("\n");
@@ -54179,7 +53927,7 @@ async function pollOnce(state) {
54179
53927
  try {
54180
53928
  const remoteState = await readFile(join$1(homedir$1(), ".overdeck", "agents", agentId, "remote-state.json"), "utf-8").then((text) => JSON.parse(text)).catch(() => null);
54181
53929
  if (remoteState?.location === "remote" && remoteState?.vmName) {
54182
- const { getRemoteAgentOutput } = await import("./remote-agents-BiAcNt37.js");
53930
+ const { getRemoteAgentOutput } = await import("./remote-agents-DQ2gRI6m.js");
54183
53931
  stdout = await getRemoteAgentOutput(agentId, remoteState.vmName, 50);
54184
53932
  } else stdout = await Effect.runPromise(capturePane(agentId, 50));
54185
53933
  } catch {
@@ -54382,7 +54130,7 @@ async function detectOrphanedClaudeCodeSessions(activeConvs) {
54382
54130
  for (const conv of activeConvs) {
54383
54131
  if (!conv.cwd) continue;
54384
54132
  if (!conv.claudeSessionId) continue;
54385
- if (conv.harness === "pi" || conv.harness === "codex") continue;
54133
+ if (conv.harness === "ohmypi" || conv.harness === "codex") continue;
54386
54134
  const list = cwdGroups.get(conv.cwd) ?? [];
54387
54135
  list.push(conv);
54388
54136
  cwdGroups.set(conv.cwd, list);
@@ -54703,7 +54451,7 @@ async function announceNewRestart(deps = {}) {
54703
54451
  setLastAnnounced(status.ts);
54704
54452
  return true;
54705
54453
  }
54706
- let timer$1 = null;
54454
+ let timer$2 = null;
54707
54455
  let running = false;
54708
54456
  let runToken = 0;
54709
54457
  function startRestartAnnouncer(deps = {}) {
@@ -54713,8 +54461,8 @@ function startRestartAnnouncer(deps = {}) {
54713
54461
  let remainingBootstrapPolls = BOOTSTRAP_FAST_POLL_COUNT;
54714
54462
  const schedule = (delayMs) => {
54715
54463
  if (!running || token !== runToken) return;
54716
- timer$1 = setTimeout(pass, delayMs);
54717
- timer$1.unref?.();
54464
+ timer$2 = setTimeout(pass, delayMs);
54465
+ timer$2.unref?.();
54718
54466
  };
54719
54467
  const nextDelay = (emitted) => {
54720
54468
  if (emitted) return POLL_MS;
@@ -54732,8 +54480,8 @@ function startRestartAnnouncer(deps = {}) {
54732
54480
  function stopRestartAnnouncer() {
54733
54481
  running = false;
54734
54482
  runToken += 1;
54735
- if (timer$1) clearTimeout(timer$1);
54736
- timer$1 = null;
54483
+ if (timer$2) clearTimeout(timer$2);
54484
+ timer$2 = null;
54737
54485
  }
54738
54486
  //#endregion
54739
54487
  //#region services/substrate-bug-poller.ts
@@ -54845,7 +54593,7 @@ function createSubstrateBugPoller(options = {}) {
54845
54593
  }
54846
54594
  async function scanIssueCandidates(config, store) {
54847
54595
  const since = isoLookback(now(), lookbackMs);
54848
- for (const repo of config.repos) {
54596
+ for (const repo of config.repos) try {
54849
54597
  const repoQualifier = `repo:${repo.owner}/${repo.repo}`;
54850
54598
  const authorResults = await fetchSearch(config.token, searchQuery([
54851
54599
  repoQualifier,
@@ -54882,11 +54630,13 @@ function createSubstrateBugPoller(options = {}) {
54882
54630
  });
54883
54631
  if (!existing) await appendSubstrateBugFiledEvent(store, bug);
54884
54632
  }
54633
+ } catch (err) {
54634
+ log.warn(`[substrate-bug-poller] skipping ${repo.owner}/${repo.repo}:`, err instanceof Error ? err.message : err);
54885
54635
  }
54886
54636
  }
54887
54637
  async function scanMergedPullRequests(config) {
54888
54638
  const since = isoLookback(now(), lookbackMs);
54889
- for (const repo of config.repos) {
54639
+ for (const repo of config.repos) try {
54890
54640
  const repoQualifier = `repo:${repo.owner}/${repo.repo}`;
54891
54641
  const pullResults = await fetchSearch(config.token, searchQuery([
54892
54642
  repoQualifier,
@@ -54914,6 +54664,8 @@ function createSubstrateBugPoller(options = {}) {
54914
54664
  repository.markFixed(issueId, commitSha, fixedAt);
54915
54665
  }
54916
54666
  }
54667
+ } catch (err) {
54668
+ log.warn(`[substrate-bug-poller] skipping ${repo.owner}/${repo.repo}:`, err instanceof Error ? err.message : err);
54917
54669
  }
54918
54670
  }
54919
54671
  async function pollOnce() {
@@ -54962,7 +54714,7 @@ init_config_yaml();
54962
54714
  init_event_store();
54963
54715
  init_activity_logger();
54964
54716
  init_features();
54965
- init_cost();
54717
+ init_cost$1();
54966
54718
  const SYSTEM_PROMPT = `You are a concise voice narrator for a software development dashboard.
54967
54719
  Your job: review recent activity and produce a SINGLE brief utterance suitable for text-to-speech.
54968
54720
 
@@ -55128,7 +54880,7 @@ const RESTART_MARKER = join(homedir(), ".overdeck", "dashboard-restarting.json")
55128
54880
  * loads new content-hashed chunk filenames (no ERR_MODULE_NOT_FOUND after rebuild).
55129
54881
  */
55130
54882
  async function defaultLifecycleRunner(pending) {
55131
- const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-Bn54C3oA.js");
54883
+ const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-CdxCcqFv.js");
55132
54884
  await postMergeLifecycle(pending.issueId, pending.projectPath, pending.sourceBranch, { skipDeploy: true });
55133
54885
  if (pending.sourceBranch) await notifyTldrDaemon(pending.projectPath, pending.sourceBranch);
55134
54886
  }
@@ -55196,7 +54948,7 @@ async function processPendingLifecycle(options) {
55196
54948
  }
55197
54949
  //#endregion
55198
54950
  //#region ../../lib/memory/reconciliation.ts
55199
- init_agents$1();
54951
+ init_agents$2();
55200
54952
  async function reconcileStaleTranscriptCheckpoints(options = {}) {
55201
54953
  const limit = options.limit ?? 100;
55202
54954
  const checkpoints = (options.listCheckpoints ?? listTranscriptCheckpoints)(limit);
@@ -55260,7 +55012,7 @@ function checkpointIdentity(checkpoint) {
55260
55012
  };
55261
55013
  }
55262
55014
  function inferCheckpointHarness(transcriptPath) {
55263
- if (transcriptPath.includes("/.overdeck/agents/") && transcriptPath.includes("/sessions/")) return "pi";
55015
+ if (transcriptPath.includes("/.overdeck/agents/") && transcriptPath.includes("/sessions/")) return "ohmypi";
55264
55016
  if (transcriptPath.includes("/codex-home/sessions/") && transcriptPath.includes("rollout-")) return "codex";
55265
55017
  return "claude-code";
55266
55018
  }
@@ -55296,7 +55048,7 @@ init_paths();
55296
55048
  init_tmux();
55297
55049
  init_issue_id();
55298
55050
  init_errors();
55299
- init_agents$1();
55051
+ init_agents$2();
55300
55052
  function getAgentDirectoryIssueId(name) {
55301
55053
  const match = name.match(/^(?:agent|planning)-(.+)$/);
55302
55054
  if (!match) return null;
@@ -55439,7 +55191,7 @@ init_activity_logger();
55439
55191
  init_merge_sync();
55440
55192
  const AUTO_MERGE_EXECUTOR_INTERVAL_MS = 3e4;
55441
55193
  const REQUEUE_BACKOFF_MS = 6e4;
55442
- let timer = null;
55194
+ let timer$1 = null;
55443
55195
  let activeTick = null;
55444
55196
  function errorMessage(error) {
55445
55197
  if (error instanceof Error && error.message) return error.message;
@@ -55449,7 +55201,7 @@ function failureReason(result) {
55449
55201
  return result.error ?? result.message ?? `merge returned status ${result.statusCode ?? "unknown"}`;
55450
55202
  }
55451
55203
  async function defaultMergeIssue(issueId) {
55452
- const { triggerMerge } = await import("./workspaces-DTnkVSs7.js");
55204
+ const { triggerMerge } = await import("./workspaces-DL8hfuDT.js");
55453
55205
  return triggerMerge(issueId);
55454
55206
  }
55455
55207
  function defaultAnnounceFailure(issueId, reason) {
@@ -55519,14 +55271,48 @@ function runTick(deps) {
55519
55271
  }
55520
55272
  function startAutoMergeExecutor(deps = {}) {
55521
55273
  if (process.env.OVERDECK_DISABLE_AUTO_MERGE === "1") return false;
55522
- if (timer) return false;
55523
- timer = setInterval(() => runTick(deps), AUTO_MERGE_EXECUTOR_INTERVAL_MS);
55274
+ if (timer$1) return false;
55275
+ timer$1 = setInterval(() => runTick(deps), AUTO_MERGE_EXECUTOR_INTERVAL_MS);
55524
55276
  return true;
55525
55277
  }
55526
55278
  function stopAutoMergeExecutor() {
55527
- if (!timer) return;
55528
- clearInterval(timer);
55529
- timer = null;
55279
+ if (!timer$1) return;
55280
+ clearInterval(timer$1);
55281
+ timer$1 = null;
55282
+ }
55283
+ //#endregion
55284
+ //#region services/cost-reconcile-service.ts
55285
+ const RECONCILE_INTERVAL_MS = 5 * 6e4;
55286
+ let timer = null;
55287
+ let inFlight = null;
55288
+ async function runCostReconcileOnce(reason) {
55289
+ if (inFlight) return inFlight;
55290
+ inFlight = (async () => {
55291
+ const result = await reconcilePiTranscripts();
55292
+ 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)`);
55293
+ if (result.errors.length > 0) for (const err of result.errors.slice(0, 5)) console.warn(`[cost-reconciler] ${err.path}: ${err.error}`);
55294
+ })().finally(() => {
55295
+ inFlight = null;
55296
+ });
55297
+ return inFlight;
55298
+ }
55299
+ function startCostReconcileService() {
55300
+ if (timer) return;
55301
+ timer = setInterval(() => {
55302
+ runCostReconcileOnce("interval").catch((err) => {
55303
+ console.warn("[cost-reconciler] interval sweep failed:", err instanceof Error ? err.message : err);
55304
+ });
55305
+ }, RECONCILE_INTERVAL_MS);
55306
+ timer.unref?.();
55307
+ runCostReconcileOnce("startup").catch((err) => {
55308
+ console.warn("[cost-reconciler] startup sweep failed:", err instanceof Error ? err.message : err);
55309
+ });
55310
+ }
55311
+ function stopCostReconcileService() {
55312
+ if (timer) {
55313
+ clearInterval(timer);
55314
+ timer = null;
55315
+ }
55530
55316
  }
55531
55317
  //#endregion
55532
55318
  //#region ../../lib/boot-gates.ts
@@ -55622,14 +55408,14 @@ function createOverdeckDatabase(options = {}) {
55622
55408
  try {
55623
55409
  applyMigration(db, migrationPath);
55624
55410
  const tables = userTableNames(db);
55625
- if (tables.length !== 32) throw new Error(`Expected 32 overdeck tables, found ${tables.length}: ${tables.join(", ")}`);
55411
+ if (tables.length !== 30) throw new Error(`Expected 30 overdeck tables, found ${tables.length}: ${tables.join(", ")}`);
55626
55412
  assertEmptyTables(db, tables);
55627
55413
  } finally {
55628
55414
  db.close();
55629
55415
  }
55630
55416
  return {
55631
55417
  dbPath,
55632
- tableCount: 32
55418
+ tableCount: 30
55633
55419
  };
55634
55420
  }
55635
55421
  //#endregion
@@ -55645,7 +55431,11 @@ init_tracker_config();
55645
55431
  init_review_status();
55646
55432
  init_event_store();
55647
55433
  init_activity_logger();
55648
- init_agents$1();
55434
+ init_service();
55435
+ init_config$1();
55436
+ init_deacon();
55437
+ init_agents$2();
55438
+ init_agent_projection();
55649
55439
  init_paths();
55650
55440
  init_tmux();
55651
55441
  init_fts_db();
@@ -55921,6 +55711,8 @@ syncTranscriptPollerRegistry().catch((err) => console.warn("[memory-poller] init
55921
55711
  reconcileStaleTranscriptCheckpoints({ log: (message) => console.log(message) }).catch((err) => console.warn("[memory-reconciliation] startup sweep failed:", err?.message ?? err));
55922
55712
  startTranscriptPoller();
55923
55713
  console.log("[overdeck] Memory transcript poller started");
55714
+ startCostReconcileService();
55715
+ console.log("[overdeck] Cost reconciler started");
55924
55716
  const conversationSearchWatcher = startConversationSearchWatcher();
55925
55717
  console.log(conversationSearchWatcher ? "[overdeck] Conversation search watcher started" : "[overdeck] Conversation search watcher skipped (conversationSearch.enabled=false)");
55926
55718
  (async () => {
@@ -55970,6 +55762,7 @@ const handleShutdownSignal = async (signal) => {
55970
55762
  stopTtsPlayback();
55971
55763
  stopAutoMergeExecutor();
55972
55764
  stopTranscriptPoller();
55765
+ stopCostReconcileService();
55973
55766
  stopRestartAnnouncer();
55974
55767
  await stopConversationSearchWatcher().catch((err) => console.warn("[conversation-search] watcher shutdown failed:", err));
55975
55768
  closeConversationSearchService();
@@ -56019,7 +55812,7 @@ reconcileStaleGitHubBlockers().then((n) => {
56019
55812
  }
56020
55813
  }).catch((err) => console.warn(`[overdeck] Startup blocker reconciliation failed: ${err.message}`));
56021
55814
  try {
56022
- const { resetProcessingToQueued } = await import("./merge-sync-BcXooyxk.js");
55815
+ const { resetProcessingToQueued } = await import("./merge-sync-CmpwVmNH.js");
56023
55816
  const resetCount = resetProcessingToQueued();
56024
55817
  if (resetCount > 0) {
56025
55818
  console.log(`[overdeck] Reset ${resetCount} stuck merge queue entries to queued`);
@@ -56048,6 +55841,7 @@ if (process.env.OVERDECK_DISABLE_DEACON === "1") {
56048
55841
  message: "Cloister auto-start skipped via OVERDECK_DISABLE_DEACON — deacon is not running"
56049
55842
  });
56050
55843
  } else if (shouldAutoStart()) {
55844
+ resetPatrolHeartbeatForStartup();
56051
55845
  getCloisterService().start().catch((err) => {
56052
55846
  console.error("[overdeck] Cloister auto-start failed:", err);
56053
55847
  emitActivityEntrySync({