glimmervoid 0.37.0 → 0.38.1

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 (38) hide show
  1. package/dist/bin/glimmervoid.js +16 -14
  2. package/dist/chunks/{adapters-BmfVPxlH.js → adapters-B0EJp_oE.js} +19 -4
  3. package/dist/chunks/{adapters-CqFFmM2R.js → adapters-BIbF8RJB.js} +1 -1
  4. package/dist/chunks/{agent-api-cli-CBgOKXoO.js → agent-api-cli-B5Tm0wXO.js} +27 -8
  5. package/dist/chunks/{agent-api-cli-BTHEyJKS.js → agent-api-cli-BDu1CxZf.js} +1 -1
  6. package/dist/chunks/{agent-api-core-DzKhakCv.js → agent-api-core-B_mUSenR.js} +30 -7
  7. package/dist/chunks/{agent-setup-cli-C8DGxlA-.js → agent-setup-cli-C9MMiask.js} +2 -2
  8. package/dist/chunks/{config-BYc2z1Oc.js → browser-config-BrBT8269.js} +9 -223
  9. package/dist/chunks/config-CjawSOBk.js +219 -0
  10. package/dist/chunks/config-store-B6mX2phK.js +1 -0
  11. package/dist/chunks/{config-store-B7MpiYxG.js → config-store-CKcDJebD.js} +42 -64
  12. package/dist/chunks/{contracts-C6ipN_zw.js → contracts-HVCEJfxs.js} +23 -250
  13. package/dist/chunks/factory-D7ryES9n.js +240 -0
  14. package/dist/chunks/{git-version-6QFnKbWa.js → git-version-rWZjF2Px.js} +1 -1
  15. package/dist/chunks/{grok-CZ6kP_vD.js → grok-y65HOvlU.js} +1 -1
  16. package/dist/chunks/{json-file-BaxGcB2R.js → json-file-CIJf0Cel.js} +190 -9
  17. package/dist/chunks/{knowledge-graph-cli-DApeMzA8.js → knowledge-graph-cli-ByUJoKw_.js} +1 -1
  18. package/dist/chunks/{loopback-post-DJ6iYWuL.js → loopback-post-DZK7YibD.js} +1 -1
  19. package/dist/chunks/{main-BUfjroXE.js → main-DeiiNNI7.js} +1186 -475
  20. package/dist/chunks/{pair-cli-GZ9t1mIO.js → pair-cli-B9hHUHLl.js} +2 -2
  21. package/dist/chunks/{remote-config-D2Is89Kj.js → remote-config-DlquYoVe.js} +2 -2
  22. package/dist/chunks/{session-BZbariv5.js → session-BlT68ppa.js} +6 -3
  23. package/dist/chunks/{session-routes-C4t8O8yg.js → session-routes-CT1JrjoT.js} +1 -1
  24. package/dist/chunks/{settings-cli-DQOjmjev.js → settings-cli-C5i9v59a.js} +3 -3
  25. package/dist/chunks/{ssh-sign-cli-LIBP8ppO.js → ssh-sign-cli-C74BGTQS.js} +2 -2
  26. package/dist/client/assets/index-ChXRDbjt.js +275 -0
  27. package/dist/client/assets/index-DA251uL1.css +1 -0
  28. package/dist/client/index.html +11 -3
  29. package/dist/server/index.js +1 -1
  30. package/dist/session/command-hook-relay.js +1 -1
  31. package/dist/session/hook-relay.js +1 -1
  32. package/dist/session/statusline-relay.js +1 -1
  33. package/dist/session/visions-relay.js +7 -7
  34. package/package.json +3 -2
  35. package/scripts/recover-handoff.mjs +1 -1
  36. package/dist/chunks/config-store-CsUoy88Q.js +0 -1
  37. package/dist/client/assets/index-B_Qt7fbZ.css +0 -1
  38. package/dist/client/assets/index-D-upBor0.js +0 -265
@@ -1,11 +1,11 @@
1
1
  import { i as sanitizeOneLine, n as requireExecutableSpawnHelper, r as firstLine$1 } from "./node-pty-preflight-C8QIlrtU.js";
2
2
  import { a as isMissingFileError, i as errorText$1, r as errorMessage, t as errorCode } from "./text-DoirglWV.js";
3
- import { a as sanitizeWorktreeName, i as safePathSegment, r as isPathInside, t as caseFoldedPathKey } from "./paths-Bu4eH1Yd.js";
3
+ import { a as sanitizeWorktreeName, i as safePathSegment, n as equalsIgnoringCaseOnWindows, r as isPathInside, t as caseFoldedPathKey } from "./paths-Bu4eH1Yd.js";
4
4
  import { a as packageRoot, n as cliPath, o as relayPath, r as clientDir, s as resolvePackageBin, t as bundled } from "./runtime-paths-BkeftE60.js";
5
- import { At as HEX_SHA_RE, B as MyPrMergeMethod, Bt as MERGEABLE_LIVE_STATES, C as SESSION_DOC_BODY_CAP_BYTES, Ct as TeamReviewThreadResult, E as SSH_SIGN_BODY_CAP_BYTES, Et as decisionAsIfApprovalRequired, F as MyPrAutoRebase, Ft as repoParts, Ht as STATES, I as MyPrCheckContext, J as MyPrThreadNode, K as MyPrSearchNode, L as MyPrCheckContextsResponse, M as TASK_TITLE_CONTROL_CHARACTERS, Mt as GH_SEGMENT, N as TaskTitle, Nt as NODE_ID_RE, Ot as hasStandingViewerApproval, P as LinkedPr, Pt as REPO_SLUG_RE, R as MyPrKeepMergeableRequest, St as TeamReviewThreadCommentsResponse, T as SIGN_URL_ENV, Tt as canApproveAfterComment, U as MyPrMergeStateResponse, V as MyPrMergeRequest, Vt as RESTARTABLE_STATES, W as MyPrMergeWhenReadyRequest, X as MyPrsState, Y as MyPrThreadsResponse, Z as MyPrsStatus, a as AgentDispatchRequest, at as PostingPlan, bt as TeamReviewStateEntry, ct as ReviewChecksState, d as AgentTicketRequest, dt as ReviewFinding, et as CommitSha, f as CONVERSATION_APPROVAL_SUMMARY_MAX_CHARS, ft as ReviewResult, gt as TeamReviewActionRequest, ht as SearchedPr, it as GithubReviewState, j as SshSignRequest, jt as shortSha$1, kt as FULL_SHA_RE, lt as ReviewComment, m as CONVERSATION_TOOL_RESULT_HOOK_EVENT, mt as ReviewVerdict, n as AGENT_URL_ENV, nt as FindingSeverity, ot as PrDetail, p as CONVERSATION_PERMISSION_HOOK_EVENT, pt as ReviewThreadId, q as MyPrSearchResponse, r as ASK_USER_QUESTION_TOOL_NAME, rt as GithubReviewDecision, st as QueuedReview, tt as DECIDING_REVIEW_STATES, ut as ReviewDraft, v as ConversationQuestion, vt as TeamReviewCompareFiles, wt as TeamReviewThreadsRepository, x as RefinedTaskTitle, xt as TeamReviewStatus, y as ORCHESTRATOR_AGENT_VERBS, yt as TeamReviewResolveResponse, zt as KILLABLE_STATES } from "./session-BZbariv5.js";
5
+ import { At as FULL_SHA_RE, Bt as KILLABLE_STATES, Ct as TeamReviewThreadCommentsResponse, D as SSH_SIGN_BODY_CAP_BYTES, Dt as decisionAsIfApprovalRequired, E as SIGN_URL_ENV, Et as canApproveAfterComment, F as LinkedPr, Ft as REPO_SLUG_RE, G as MyPrMergeWhenReadyRequest, H as MyPrMergeRequest, Ht as RESTARTABLE_STATES, I as MyPrAutoRebase, It as repoParts, J as MyPrSearchResponse, L as MyPrCheckContext, M as SshSignRequest, Mt as shortSha$1, N as TASK_TITLE_CONTROL_CHARACTERS, Nt as GH_SEGMENT, P as TaskTitle, Pt as NODE_ID_RE, Q as MyPrsStatus, R as MyPrCheckContextsResponse, S as RefinedTaskTitle, St as TeamReviewStatus, Tt as TeamReviewThreadsRepository, Ut as STATES, V as MyPrMergeMethod, Vt as MERGEABLE_LIVE_STATES, W as MyPrMergeStateResponse, X as MyPrThreadsResponse, Y as MyPrThreadNode, Z as MyPrsState, _t as TeamReviewActionRequest, a as AgentDispatchRequest, at as GithubReviewState, b as ORCHESTRATOR_AGENT_VERBS, bt as TeamReviewResolveResponse, ct as QueuedReview, dt as ReviewDraft, f as AgentTicketRequest, ft as ReviewFinding, gt as SearchedPr, h as CONVERSATION_TOOL_RESULT_HOOK_EVENT, ht as ReviewVerdict, it as GithubReviewDecision, jt as HEX_SHA_RE, kt as hasStandingViewerApproval, lt as ReviewChecksState, m as CONVERSATION_PERMISSION_HOOK_EVENT, mt as ReviewThreadId, n as AGENT_URL_ENV, nt as DECIDING_REVIEW_STATES, ot as PostingPlan, p as CONVERSATION_APPROVAL_SUMMARY_MAX_CHARS, pt as ReviewResult, q as MyPrSearchNode, r as ASK_USER_QUESTION_TOOL_NAME, rt as FindingSeverity, st as PrDetail, tt as CommitSha, ut as ReviewComment, w as SESSION_DOC_BODY_CAP_BYTES, wt as TeamReviewThreadResult, xt as TeamReviewStateEntry, y as ConversationQuestion, yt as TeamReviewCompareFiles, z as MyPrKeepMergeableRequest } from "./session-BlT68ppa.js";
6
6
  import { n as execFileAsync, o as spawn, r as execFileSync, t as execFile } from "./child-process-safe-CbQnyh7D.js";
7
7
  import { n as glimmervoidHomeDir, t as decideConfigPath } from "./config-path-core-DFt-TemS.js";
8
- import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-6QFnKbWa.js";
8
+ import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-rWZjF2Px.js";
9
9
  import { n as checkGuardrailsDependencies, t as allowGuardedSpawn } from "./guardrails-deps-Cb-ki4EU.js";
10
10
  import { t as HOOK_URL_ENV } from "./hook-relay-core-DA_bz2KA.js";
11
11
  import { t as HOOK_VIA_GUARDRAILS_PROXY_ENV } from "./guardrails-proxy-core-BI3rdY-m.js";
@@ -16,18 +16,20 @@ import { a as SANE_YOLO_FILES } from "./rtk-hook-core-VrUafGAp.js";
16
16
  import { _ as lineStartOffsets, a as projectForUri, b as replacedSpanOfWholeTextChange, c as visionsLaneReloadSignature, d as applyDidOpen, f as createDocStore, g as lineOfOffset, h as getDoc, i as isUriInProjects, l as applyDidChange, m as formatRange, n as applyEditorNotification, o as resolveVisionsScopeProjects, p as detectBlankLineBoundary, r as createEditorState, s as scopePathsOf, t as ACTIVITY_METHOD, u as applyDidClose, v as listDocs, x as uriOfParams, y as offsetOfPosition } from "./ingest-editor-core-B2tycW_m.js";
17
17
  import { n as DEFAULT_MAX_MS, r as nextBackoffMs, t as DEFAULT_BASE_MS } from "./backoff-DjVpfHk7.js";
18
18
  import { n as unrefTimer, t as DEFAULT_TIMER_FNS } from "./timer-deps-u2DAQLok.js";
19
- import { o as permitsHelpCommand, r as describeSessionLimits } from "./session-routes-C4t8O8yg.js";
20
- import { a as decideFactoryControlAllowance, c as deriveChildSessionName, d as describeUnavailableFactoryState, f as formatFactoryStatus, h as parseAgentVerb, i as decideEditAllowance, l as describePushLanding, m as isOrchestratorAgentVerb, n as decideAgentRequest, o as decideSpawnAllowance, r as decideAttentionAllowance, s as decideTicketAction, t as REFUSAL_REASON, u as describePushRouteRefusal } from "./agent-api-core-DzKhakCv.js";
21
- import { s as GLIMMERVOID_SECRET_KEYS } from "./grok-CZ6kP_vD.js";
19
+ import { o as permitsHelpCommand, r as describeSessionLimits } from "./session-routes-CT1JrjoT.js";
20
+ import { $ as CoherenceTicketCreated, C as WorkflowsState, G as USAGE_INTEGER_RANGES, N as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, T as USAGE_VENDOR_KEYS, X as CoherenceOrient, a as DEFAULT_FACTORY_CHECKS, b as WorkflowSearchNode, et as CoherenceWorkCreated, o as DEFAULT_FACTORY_PROTECTED_PATHS, tt as CoherenceWorkInspect, v as WorkflowCommentBody, w as USAGE_COST_MODES, x as WorkflowsSettings, y as WorkflowLabelName } from "./browser-config-BrBT8269.js";
21
+ import { _ as isSingleFactoryPathSegment, a as FactoryControlRequest, c as FactoryHarnessVerdict, d as FactoryQueueIntentRequest, g as FactoryWorkerEvent, i as FactoryConsequences, l as FactoryLaneState, m as FactoryReviewerOutput, n as FACTORY_ERROR_MAX_CHARS, p as FactoryReviewVerdict, r as FACTORY_PROTECTED_PATH_FLOOR, s as FactoryHarnessStateLine, u as FactoryProjectState } from "./factory-D7ryES9n.js";
22
+ import { _ as parseAgentVerb, a as decideFactoryControlAllowance, c as deriveChildSessionName, d as describePushRouteRefusal, f as describeUnavailableFactoryState, g as isOrchestratorAgentVerb, i as decideEditAllowance, l as describeFactorySetupProfile, m as formatFactoryStatus, n as decideAgentRequest, o as decideSpawnAllowance, p as findSessionProjectId, r as decideAttentionAllowance, s as decideTicketAction, t as REFUSAL_REASON, u as describePushLanding } from "./agent-api-core-B_mUSenR.js";
23
+ import { s as GLIMMERVOID_SECRET_KEYS } from "./grok-y65HOvlU.js";
22
24
  import { n as shellQuote } from "./hook-command-core-qTNK-xMF.js";
23
- import { _ as isQueueAdmissionTimeout, a as jsonStateLoadError, c as loadedJsonValue, d as writeJsonAtomicSync, f as writeTextAtomic, g as createSpawnGate, h as createSerialQueue, i as createJsonStateWriter, m as createKeyedSerialQueue, n as appendJsonLines, o as loadJsonStateFile, p as writeTextAtomicSync, r as createJsonStateStore, s as loadJsonStateFileSync, t as appendJsonLine, u as writeJsonAtomic } from "./json-file-BaxGcB2R.js";
24
- import { $ as CoherenceWorkInspect, C as USAGE_COST_MODES, J as CoherenceOrient, M as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Q as CoherenceWorkCreated, S as WorkflowsState, U as USAGE_INTEGER_RANGES, Z as CoherenceTicketCreated, a as ConfigUpdate, b as WorkflowSearchNode, c as RUNTIME_CONFIG_SCALAR_KEYS, l as TASK_TITLE_SOURCE_PRIORITY, m as DEFAULT_FACTORY_PROTECTED_PATHS, o as PersistedTaskTitle, p as DEFAULT_FACTORY_CHECKS, r as BrowserConfig, s as ProjectConfig, u as configIssueMessage, v as WorkflowCommentBody, w as USAGE_VENDOR_KEYS, x as WorkflowsSettings, y as WorkflowLabelName } from "./config-BYc2z1Oc.js";
25
- import { B as buildExceptionProperties, F as TELEMETRY_BATCH_URL, G as decideTelemetryConsent, H as classifySessionExit, I as TELEMETRY_FLAGS_URL, J as parsePosthogReleaseId, K as exceptionFingerprint, L as adapterBucket, P as FIRST_RUN_NOTICE, Q as stableConfigKey, R as buildAiGenerationEvents, U as decideIsDevInstall, V as buildFactoryDecisionEvent, W as decideRemoteTelemetryState, Y as resolveProjectToken, a as generateProjectId, at as usablePrefixes, b as validateSettingsBlock, ct as comparableDirectoryPath, d as assetForPlatform, et as DEFAULT_BRANCH_GC_PREFIXES, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as ensureProjectIds, it as sessionIdFromBranch, lt as isSameDirectoryPath, m as installTargetPath, n as DEFAULT_CONFIG, nt as planBranchGc, o as glimmervoidHomeDir$1, ot as worktreeIntegrationTips, p as findEscapingArchiveMember, q as nodeMajorVersion, r as createConfigStore, rt as planWorktreeGc, st as canonicalizePath, t as ABORT_CONFIG_SAVE, tt as matchedPrefix, v as mergeSettingsBlock, y as settingsBlockSpecFor, z as buildBrowserExceptionProperties } from "./config-store-B7MpiYxG.js";
26
- import { $ as FactoryHarnessVerdict, A as BenchmarkActionRequest, B as PrCheckoutCaseInput, C as IssueKey, D as IssuesStatus, E as IssuesState, F as BenchmarkStatus, G as TelemetryState, H as PendingCrashReport, I as BenchmarkSuite, J as FACTORY_ERROR_MAX_CHARS, K as FACTORY_DECISION_OUTPUT_MAX_BYTES, L as CommitComparison, M as BenchmarkId, N as BenchmarkJudgement, O as ChangeMap, P as BenchmarkRun, Q as FactoryHarnessStateLine, R as MergedPrListing, S as GithubIssueResponse, T as IssuesSearchResponse, U as TELEMETRY_EVENT_SCHEMAS, X as FactoryConsequences, Y as FACTORY_PROTECTED_PATH_FLOOR, Z as FactoryControlRequest, _ as PlanDecision, a as ClientMessage, at as FactoryWorkerEvent, b as planTitle, c as UpdateJournal, d as TraceRecord, et as FactoryLaneState, f as PLAN_BODY_CAP_BYTES, g as PLAN_TOOL_NAME, h as PLAN_RESULT_HOOK_EVENT, i as CONTROL_FRAME_MAX_BYTES, it as FactoryReviewerOutput, j as BenchmarkCase, k as changeMapFactId, l as MAX_RAW_LINE_CHARS, m as PLAN_HOOK_OUTPUT_MAX_CHARS, n as HookEnvelope, nt as FactoryQueueIntentRequest, o as DIFF_ANNOTATION_NOTE_MAX_CHARS, ot as isSingleFactoryPathSegment, p as PLAN_HOOK_EVENT, q as FactoryDecisionLogLine, r as parseDataClientMessage, rt as FactoryReviewVerdict, s as InstallFlavor, t as HTTP_URL_RE, tt as FactoryProjectState, u as TraceCheckpoint, v as PlanRevision, w as IssueUpdate, x as GithubIssuePullRequestsRepository, y as parseExitPlanModeHookPayload, z as MinedPrReviewData } from "./contracts-C6ipN_zw.js";
27
- import { A as splitLines$1, C as isUsageFile, D as resolveProjectsDirs, E as projectDirCandidates, O as shouldEvaluateDespiteIoFailures, S as grokRootCandidates, T as passOutcome, _ as continuationDelayMs, a as describeAgentResolvability, b as expandTilde, c as isKnownAgentId, d as resolveAdapter, f as setCustomAgents, g as codexSessionIdFromPath, h as codexRootCandidates, i as customAgentFingerprint, j as claudeCode, k as shouldPersistWarehouse, l as listAgentIds, m as codexHomes, n as cachedAgentResolvability, o as getAdapter, p as codexFallbackRoots, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resetCommandCache, v as decideFileRead, w as laneSpendStatus, x as grokHomes, y as dedupeCodexFiles } from "./adapters-BmfVPxlH.js";
25
+ import { C as headroomFromReadings, D as parseMemAvailableBytes, E as parseCgroupV2Path, S as cgroupLimitBytes, T as parseCgroupBytes, _ as createSpawnGate, a as jsonStateLoadError, c as loadedJsonValue, d as writeJsonAtomicSync, f as writeTextAtomic, g as createSerialQueue, h as createMemoryAdmission, i as createJsonStateWriter, m as createKeyedSerialQueue, n as appendJsonLines, o as loadJsonStateFile, p as writeTextAtomicSync, r as createJsonStateStore, s as loadJsonStateFileSync, t as appendJsonLine, u as writeJsonAtomic, v as isQueueAdmissionAbandoned, w as memoryReserveBytes, x as cgroupAncestorPaths, y as isQueueAdmissionTimeout } from "./json-file-CIJf0Cel.js";
26
+ import { B as buildExceptionProperties, F as TELEMETRY_BATCH_URL, G as decideTelemetryConsent, H as classifySessionExit, I as TELEMETRY_FLAGS_URL, J as parsePosthogReleaseId, K as exceptionFingerprint, L as adapterBucket, P as FIRST_RUN_NOTICE, Q as stableConfigKey, R as buildAiGenerationEvents, U as decideIsDevInstall, V as buildFactoryDecisionEvent, W as decideRemoteTelemetryState, Y as resolveProjectToken, a as generateProjectId, at as usablePrefixes, b as validateSettingsBlock, ct as comparableDirectoryPath, d as assetForPlatform, et as DEFAULT_BRANCH_GC_PREFIXES, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as ensureProjectIds, it as sessionIdFromBranch, lt as isSameDirectoryPath, m as installTargetPath, n as DEFAULT_CONFIG, nt as planBranchGc, o as glimmervoidHomeDir$1, ot as worktreeIntegrationTips, p as findEscapingArchiveMember, q as nodeMajorVersion, r as createConfigStore, rt as planWorktreeGc, st as canonicalizePath, t as ABORT_CONFIG_SAVE, tt as matchedPrefix, v as mergeSettingsBlock, y as settingsBlockSpecFor, z as buildBrowserExceptionProperties } from "./config-store-CKcDJebD.js";
27
+ import { a as ConfigUpdate, c as RUNTIME_CONFIG_SCALAR_KEYS, l as TASK_TITLE_SOURCE_PRIORITY, o as PersistedTaskTitle, r as BrowserConfig, s as ProjectConfig, u as configIssueMessage } from "./config-CjawSOBk.js";
28
+ import { A as ChangeMap, B as MergedPrListing, C as GithubIssuePullRequestsRepository, D as IssuesSearchResponse, E as IssueUpdate, F as BenchmarkJudgement, G as TELEMETRY_EVENT_SCHEMAS, H as PrCheckoutCaseInput, I as BenchmarkRun, J as FACTORY_DECISION_OUTPUT_MAX_BYTES, L as BenchmarkStatus, M as BenchmarkActionRequest, N as BenchmarkCase, O as IssuesState, P as BenchmarkId, R as BenchmarkSuite, S as planTitle, T as IssueKey, V as MinedPrReviewData, W as PendingCrashReport, Y as FactoryDecisionLogLine, _ as PLAN_RESULT_HOOK_EVENT, a as parseDataClientMessage, b as PlanRevision, c as DIFF_ANNOTATION_NOTE_MAX_CHARS, d as MAX_RAW_LINE_CHARS, f as TraceCheckpoint, g as PLAN_HOOK_OUTPUT_MAX_CHARS, h as PLAN_HOOK_EVENT, i as parseDataAttachHistory, j as changeMapFactId, k as IssuesStatus, l as InstallFlavor, m as PLAN_BODY_CAP_BYTES, n as HookEnvelope, o as CONTROL_FRAME_MAX_BYTES, p as TraceRecord, q as TelemetryState, r as DEEP_HISTORY_SCROLLBACK_LINES, s as ClientMessage, t as HTTP_URL_RE, u as UpdateJournal, v as PLAN_TOOL_NAME, w as GithubIssueResponse, x as parseExitPlanModeHookPayload, y as PlanDecision, z as CommitComparison } from "./contracts-HVCEJfxs.js";
29
+ import { A as shouldPersistWarehouse, C as isUsageFile, D as resolveProjectsDirs, E as projectDirCandidates, M as claudeCode, O as scopedClaudeRoots, S as grokRootCandidates, T as passOutcome, _ as continuationDelayMs, a as describeAgentResolvability, b as expandTilde, c as isKnownAgentId, d as resolveAdapter, f as setCustomAgents, g as codexSessionIdFromPath, h as codexRootCandidates, i as customAgentFingerprint, j as splitLines$1, k as shouldEvaluateDespiteIoFailures, l as listAgentIds, m as codexHomes, n as cachedAgentResolvability, o as getAdapter, p as codexFallbackRoots, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resetCommandCache, v as decideFileRead, w as laneSpendStatus, x as grokHomes, y as dedupeCodexFiles } from "./adapters-B0EJp_oE.js";
28
30
  import { t as HOOK_TOOLS } from "./hook-tools-BkxllUHE.js";
29
31
  import { a as formatSshPublicKey, c as orderAgentSockets, i as commitSigningShimText, l as parseSshPublicKey, n as NO_AGENT_HOLDS_KEY, o as isForwardedAgentDirName, r as agentListsKey, s as isSignableGitObject, t as COMMIT_SIGNING_SHIM_NAME } from "./ssh-signing-core-CRhVVWNW.js";
30
- import { a as configSiblingPath, c as defaultPairingsPath, d as decideDeviceAuth, f as deviceNameFromUserAgent, l as defaultSeenPath, n as normalizeRemoteConfig, o as createPairingsStore, p as hashSecret, r as validateRemoteConfig, s as createSeenStore, t as decideBindHost, u as DEFAULT_DEVICE_MAX_AGE_MS } from "./remote-config-D2Is89Kj.js";
32
+ import { a as configSiblingPath, c as defaultPairingsPath, d as decideDeviceAuth, f as deviceNameFromUserAgent, l as defaultSeenPath, n as normalizeRemoteConfig, o as createPairingsStore, p as hashSecret, r as validateRemoteConfig, s as createSeenStore, t as decideBindHost, u as DEFAULT_DEVICE_MAX_AGE_MS } from "./remote-config-DlquYoVe.js";
31
33
  import { _ as escapeMarkup, g as createLaneLog, n as createVisionsSetup } from "./visions-setup-B-ZeRdsi.js";
32
34
  import { t as getRtkPath } from "./rtk-resolver-C_micGrL.js";
33
35
  import { createRequire } from "node:module";
@@ -36,11 +38,11 @@ import path, { join, resolve } from "node:path";
36
38
  import os from "node:os";
37
39
  import { z } from "zod";
38
40
  import { stripVTControlCharacters } from "node:util";
41
+ import WebSocket$1, { WebSocket, WebSocketServer } from "ws";
39
42
  import http from "node:http";
40
- import WebSocket, { WebSocketServer } from "ws";
41
43
  import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
42
44
  import { isIPv6 } from "node:net";
43
- import fsp, { access, chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, symlink, writeFile } from "node:fs/promises";
45
+ import fsp, { access, chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
44
46
  import { pipeline } from "node:stream";
45
47
  import express from "express";
46
48
  import pty from "node-pty";
@@ -198,6 +200,7 @@ function createLifecycle({ shutdown, httpServer, extraServers = [], onRestart =
198
200
  warn: log
199
201
  });
200
202
  closeExtraServers(extraServers);
203
+ await prepareHandOffOnce();
201
204
  let exited = false;
202
205
  const doExit = () => {
203
206
  if (exited) return;
@@ -1136,7 +1139,7 @@ function isPairPath(pathname) {
1136
1139
  if (typeof pathname !== "string") return false;
1137
1140
  const normalized = normalizePathname(pathname);
1138
1141
  if (normalized.suspicious) return false;
1139
- return normalized.pathname === "/pair" || normalized.pathname.startsWith(PAIR_PATH_PREFIX);
1142
+ return normalized.pathname.startsWith(PAIR_PATH_PREFIX);
1140
1143
  }
1141
1144
  var ENCODED_WORD_SHAPE = /=\?[^?]*\?[^?]*\?[^?]*\?=/;
1142
1145
  var ENCODED_WORD_PARTS = /^=\?([^?]*)\?([^?]*)\?([^?]*)\?=$/;
@@ -2052,7 +2055,6 @@ function projectSessionSnapshots(source) {
2052
2055
  agent: source.agent,
2053
2056
  state: source.state,
2054
2057
  stateSince: source.stateSince,
2055
- sleeping: source.sleeping,
2056
2058
  dangerouslySkipPermissions: source.dangerouslySkipPermissions,
2057
2059
  saneYolo: !!source.saneYolo,
2058
2060
  ephemeral: source.ephemeral,
@@ -2063,6 +2065,7 @@ function projectSessionSnapshots(source) {
2063
2065
  activeAgents: source.activeAgents,
2064
2066
  awaitingBackgroundTasks: source.awaitingBackgroundTasks,
2065
2067
  isCompacting: source.isCompacting ?? false,
2068
+ isWaitingForMemory: source.isWaitingForMemory === true,
2066
2069
  pendingWakeup: source.pendingWakeup,
2067
2070
  pendingPromptKind: source.pendingPromptKind,
2068
2071
  conversationApproval: source.conversationApproval ?? null,
@@ -3265,6 +3268,87 @@ function resolveFactoryProfile(globalSettings, project) {
3265
3268
  };
3266
3269
  }
3267
3270
  //#endregion
3271
+ //#region server/factory/core/factory-setup-core.ts
3272
+ var PACKAGE_MANAGERS = [
3273
+ {
3274
+ lockfiles: ["npm-shrinkwrap.json", "package-lock.json"],
3275
+ prefix: "npm run",
3276
+ test: "npm test"
3277
+ },
3278
+ {
3279
+ lockfiles: ["pnpm-lock.yaml"],
3280
+ prefix: "pnpm run",
3281
+ test: "pnpm run test"
3282
+ },
3283
+ {
3284
+ lockfiles: ["yarn.lock"],
3285
+ prefix: "yarn",
3286
+ test: "yarn test"
3287
+ },
3288
+ {
3289
+ lockfiles: ["bun.lockb", "bun.lock"],
3290
+ prefix: "bun run",
3291
+ test: "bun run test"
3292
+ }
3293
+ ];
3294
+ var FACTORY_TEST_COMMANDS = PACKAGE_MANAGERS.map((manager) => manager.test);
3295
+ var FOCUSED_RUNNERS = [
3296
+ {
3297
+ dependency: "vitest",
3298
+ command: "npx vitest run"
3299
+ },
3300
+ {
3301
+ dependency: "jest",
3302
+ command: "npx jest"
3303
+ },
3304
+ {
3305
+ dependency: "mocha",
3306
+ command: "npx mocha"
3307
+ }
3308
+ ];
3309
+ function detectFactorySetup({ scripts, lockfiles, dependencies, globalChecks }) {
3310
+ const packageManager = PACKAGE_MANAGERS.find((manager) => manager.lockfiles.some((lockfile) => lockfiles.includes(lockfile))) ?? PACKAGE_MANAGERS[0];
3311
+ const hasScript = (name) => typeof scripts?.[name] === "string" && String(scripts[name]).trim().length > 0;
3312
+ return {
3313
+ checks: globalChecks === void 0 ? [
3314
+ "typecheck",
3315
+ "lint",
3316
+ "test"
3317
+ ].filter(hasScript).map((name) => name === "test" ? packageManager.test : `${packageManager.prefix} ${name}`) : [...globalChecks],
3318
+ focusedTestCommandCandidates: [...hasScript("test:files") ? [`${packageManager.prefix} test:files`] : [], ...FOCUSED_RUNNERS.filter((runner) => dependencies.includes(runner.dependency)).map((runner) => runner.command)],
3319
+ widenableScopes: [...DEFAULT_WIDENABLE_SCOPES]
3320
+ };
3321
+ }
3322
+ var TEST_RESULT_LINE = /^[ \t]*(?:not )?ok \d+\b|^# tests \d+\b/m;
3323
+ var SHELL_NOT_FOUND_LINE = /^(?:\/[^\s:]*\/)?(?:sh|bash|dash|ash|zsh)(?:: (?:line )?\d+)?: [^\n]*?: (?:command )?not found/m;
3324
+ var LAUNCH_FAILURE_LINE = /(?:^|\n)(?:Error: )?(?:spawn[^\n]*\b(?:ENOENT|EACCES|EPERM|failed|error)\b|(?:npm (?:error|ERR!) )?missing script:|(?:error )?(?:script|command)[^\n]*(?:not found|does not exist|not recognized)|Check command contains shell syntax or is empty)/im;
3325
+ function classifyFactorySetupCheck(check) {
3326
+ const hasLaunchFailure = !check.pass && !TEST_RESULT_LINE.test(check.output) && (SHELL_NOT_FOUND_LINE.test(check.output) || LAUNCH_FAILURE_LINE.test(check.output));
3327
+ return {
3328
+ ...check,
3329
+ status: hasLaunchFailure ? "could-not-run" : "ran"
3330
+ };
3331
+ }
3332
+ function decideFactorySetupProof(proposal, fullChecks, focusedChecks) {
3333
+ const checks = [...new Set(proposal.checks)].filter((command) => fullChecks.some((check) => check.command === command && check.status === "ran"));
3334
+ if (checks.length === 0) {
3335
+ const failures = fullChecks.map((check) => `${check.command}: ${check.output}`).join("\n");
3336
+ return {
3337
+ ok: false,
3338
+ reason: `Factory setup could not run any full checks.${failures ? `\n${failures}` : " No check commands were detected."}`
3339
+ };
3340
+ }
3341
+ const focusedTestCommand = proposal.focusedTestCommandCandidates.find((command) => focusedChecks.some((check) => check.command === command && check.pass));
3342
+ return {
3343
+ ok: true,
3344
+ profile: {
3345
+ checks,
3346
+ widenableScopes: [...proposal.widenableScopes],
3347
+ ...focusedTestCommand ? { focusedTestCommand } : {}
3348
+ }
3349
+ };
3350
+ }
3351
+ //#endregion
3268
3352
  //#region server/lanes/core/lane-permissions-core.ts
3269
3353
  var ACCEPT_EDITS_MODE = "acceptEdits";
3270
3354
  var LANE_CONFIG_EDIT_DENY_RULES = Object.freeze(["Edit(**/.git/**)", "Edit(**/.claude/**)"]);
@@ -3872,6 +3956,17 @@ var FACTORY_INTEGRATION_MOVED_OUTSIDE = "integration branch moved outside the fa
3872
3956
  function appendFactoryLandedSha(landedShas, landedSha) {
3873
3957
  return [...landedShas.filter((recordedSha) => recordedSha !== landedSha), landedSha].slice(-50);
3874
3958
  }
3959
+ function workerChecksOf(checks) {
3960
+ return checks.filter((command) => !FACTORY_TEST_COMMANDS.some((testCommand) => command === testCommand || command.startsWith(`${testCommand} `)));
3961
+ }
3962
+ var CLOSE_OUT_RUNS_EVERY_CHECK = "Do not run the full test suite; Glimmervoid runs every check in a clean checkout when you stop.";
3963
+ function workerCheckInstruction(checks, focusedTestCommand) {
3964
+ const workerChecks = workerChecksOf(checks);
3965
+ const workerChecksText = workerChecks.length > 0 ? ` and these checks: ${JSON.stringify(workerChecks)}` : "";
3966
+ if (focusedTestCommand) return `While iterating, run the test files you touched with ${focusedTestCommand} <file> ... . Before finishing, run those test files once more${workerChecksText}. ${CLOSE_OUT_RUNS_EVERY_CHECK}`;
3967
+ if (workerChecks.length === 0) return CLOSE_OUT_RUNS_EVERY_CHECK;
3968
+ return `Run these checks before finishing: ${JSON.stringify(workerChecks)}. ${CLOSE_OUT_RUNS_EVERY_CHECK}`;
3969
+ }
3875
3970
  function buildWorkerPrompt({ projectName, intent, order, claudeSessionId, checks = DEFAULT_FACTORY_CHECKS, focusedTestCommand }) {
3876
3971
  const orderSections = [
3877
3972
  ["Objective", JSON.stringify(order.opened.objective)],
@@ -3890,7 +3985,7 @@ function buildWorkerPrompt({ projectName, intent, order, claudeSessionId, checks
3890
3985
  "Edit only inside these write scopes. Do not expand the ticket or the intent.",
3891
3986
  `Start by running coherence context ${scopes} --max-bytes 12000`,
3892
3987
  "Leave your finished changes in the working tree and never run git add or git commit. Glimmervoid commits every change in this worktree when you stop, so remove any file you do not want landed.",
3893
- focusedTestCommand ? `While iterating, run the test files you touched with ${focusedTestCommand} <file> ... . Run the full checks once at the end before finishing: ${JSON.stringify(checks)}` : `Run these checks before finishing: ${JSON.stringify(checks)}`,
3988
+ workerCheckInstruction(checks, focusedTestCommand),
3894
3989
  "Never push, never open PRs, never change coherence work state. Glimmervoid closes the ticket.",
3895
3990
  "Run each shell command on its own with no pipes, &&, ; or subshells, because chained commands are denied.",
3896
3991
  "A denied command means \"not allowed here\". Do not retry it another way.",
@@ -4730,7 +4825,15 @@ function registerEphemeralSession({ map, id, sess, closeSessionDataClients, logP
4730
4825
  closeSessionDataClients(id);
4731
4826
  }
4732
4827
  };
4733
- sess.on("exit", removeFromMap);
4828
+ const releaseAfterListeners = () => {
4829
+ removeFromMap();
4830
+ setImmediate(() => sess.destroy());
4831
+ };
4832
+ sess.on("exit", releaseAfterListeners);
4833
+ sess.on("error", () => {
4834
+ if (sess.hasLivePty !== false) return;
4835
+ releaseAfterListeners();
4836
+ });
4734
4837
  const origDestroy = sess.destroy.bind(sess);
4735
4838
  sess.destroy = () => {
4736
4839
  origDestroy();
@@ -4741,19 +4844,32 @@ function registerEphemeralSession({ map, id, sess, closeSessionDataClients, logP
4741
4844
  //#region server/agent-api-wiring.ts
4742
4845
  var AGENT_LANE_TAG = "agent-spawn";
4743
4846
  var SPAWN_FAILED_MESSAGE = "could not spawn the session";
4744
- function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoardSessions, makeSession, wireSessionEvents, closeSessionDataClients, broadcastControl, spawnGate, sessionDocs, factory, recordLane = null, logger = console }) {
4745
- async function spawn(parent, request) {
4746
- const budget = parent.agentSpawnBudget();
4847
+ var BYTES_PER_MEGABYTE = 1048576;
4848
+ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoardSessions, makeSession, wireSessionEvents, closeSessionDataClients, broadcastControl, spawnGate, sessionDocs, factory, recordLane = null, memoryAdmission = null, logger = console }) {
4849
+ async function describeMemoryShortfall() {
4850
+ const shortfall = await memoryAdmission?.describeShortfallNow("session") ?? null;
4851
+ if (shortfall === null) return null;
4852
+ const toMegabytes = (bytes) => Math.round(bytes / BYTES_PER_MEGABYTE);
4853
+ const heldDetails = [...shortfall.heldBytes > 0 ? [`${toMegabytes(shortfall.heldBytes)} MB held for recent starts`] : [], ...shortfall.waitingStartCount > 0 ? [`${shortfall.waitingStartCount} ${shortfall.waitingStartCount === 1 ? "start" : "starts"} already waiting for memory`] : []];
4854
+ const heldClause = heldDetails.length > 0 ? ` (${heldDetails.join(", ")})` : "";
4855
+ return `memory is low: ${toMegabytes(shortfall.headroomBytes)} MB free${heldClause}, and a new session needs ${toMegabytes(shortfall.reserveBytes)} MB. Try again once other work finishes`;
4856
+ }
4857
+ function refuseByBudget(parent) {
4747
4858
  const allowance = decideSpawnAllowance({
4748
- ...budget,
4859
+ ...parent.agentSpawnBudget(),
4749
4860
  isFactoryWorker: parent.factoryRole === "worker",
4750
4861
  isEphemeral: parent.ephemeral
4751
4862
  });
4752
- if (!allowance.ok) return {
4863
+ if (allowance.ok) return null;
4864
+ return {
4753
4865
  ok: false,
4754
4866
  status: allowance.status,
4755
4867
  error: allowance.reason
4756
4868
  };
4869
+ }
4870
+ async function spawn(parent, request) {
4871
+ const budgetRefusal = refuseByBudget(parent);
4872
+ if (budgetRefusal) return budgetRefusal;
4757
4873
  if (request.agent && !isKnownAgentId(request.agent)) return {
4758
4874
  ok: false,
4759
4875
  status: 400,
@@ -4765,6 +4881,15 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
4765
4881
  status: 403,
4766
4882
  error: `a guarded session can only spawn a guarded agent, and ${agentId} does not run inside the guardrails`
4767
4883
  };
4884
+ const memoryShortfall = await describeMemoryShortfall();
4885
+ if (memoryShortfall) return {
4886
+ ok: false,
4887
+ status: 503,
4888
+ error: memoryShortfall
4889
+ };
4890
+ const budgetRefusalAfterMemoryRead = refuseByBudget(parent);
4891
+ if (budgetRefusalAfterMemoryRead) return budgetRefusalAfterMemoryRead;
4892
+ const budget = parent.agentSpawnBudget();
4768
4893
  const name = deriveChildSessionName(request.name || parent.name, listAllSessions().map((session) => session.name));
4769
4894
  const id = `${AGENT_LANE_TAG}-${crypto.randomUUID()}`;
4770
4895
  const project = {
@@ -4886,6 +5011,70 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
4886
5011
  if (routeRefusal) return routeRefusal;
4887
5012
  return describePushLanding(await session.mergeAndContinue({ force: true }));
4888
5013
  }
5014
+ async function setUpFactoryProject(session, factoryPort, projects) {
5015
+ const projectId = findSessionProjectId(config.projects, session.path);
5016
+ if (!projectId) return {
5017
+ status: 404,
5018
+ body: {
5019
+ ok: false,
5020
+ error: `no configured project has the path ${session.path}, so the operator has to add this repository as a project first`
5021
+ }
5022
+ };
5023
+ const outcome = await factoryPort.rerunSetup(projectId);
5024
+ if (!outcome.ok) return {
5025
+ status: 409,
5026
+ body: {
5027
+ ok: false,
5028
+ error: outcome.reason
5029
+ }
5030
+ };
5031
+ return {
5032
+ status: 200,
5033
+ body: {
5034
+ ok: true,
5035
+ projectId,
5036
+ profile: outcome.profile,
5037
+ text: describeFactorySetupProfile(outcome.profile, projects.some((project) => project.projectId === projectId))
5038
+ }
5039
+ };
5040
+ }
5041
+ async function queueFactoryIntent(session, factoryPort, projects, request) {
5042
+ const projectId = findSessionProjectId(config.projects, session.path);
5043
+ if (!projectId) return {
5044
+ status: 404,
5045
+ body: {
5046
+ ok: false,
5047
+ error: `no configured project has the path ${session.path}, so the operator has to add this repository as a project first`
5048
+ }
5049
+ };
5050
+ if (!projects.some((project) => project.projectId === projectId)) return {
5051
+ status: 409,
5052
+ body: {
5053
+ ok: false,
5054
+ error: `${projectId} is not a factory project yet: commit a coherence.config.json holding {} at the repository root, land it on the integration branch with glimmervoid push, then queue again`
5055
+ }
5056
+ };
5057
+ const outcome = await factoryPort.queueIntent({
5058
+ ...request,
5059
+ projectId
5060
+ });
5061
+ if (!outcome.ok) return {
5062
+ status: 409,
5063
+ body: {
5064
+ ok: false,
5065
+ error: outcome.error ?? "the factory refused the intent"
5066
+ }
5067
+ };
5068
+ return {
5069
+ status: 200,
5070
+ body: {
5071
+ ok: true,
5072
+ projectId,
5073
+ workId: outcome.workId,
5074
+ text: `Queued ${outcome.workId ?? "the intent"} on ${projectId}. Follow it with glimmervoid factory status --project ${projectId}`
5075
+ }
5076
+ };
5077
+ }
4889
5078
  async function handle(session, verb, payload) {
4890
5079
  const parsed = parseAgentVerb(verb, payload);
4891
5080
  if (!parsed.ok) return {
@@ -4895,7 +5084,7 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
4895
5084
  error: parsed.error
4896
5085
  }
4897
5086
  };
4898
- if (parsed.verb === "factory-status" || parsed.verb === "factory-retry") {
5087
+ if (parsed.verb === "factory-status" || parsed.verb === "factory-retry" || parsed.verb === "factory-setup" || parsed.verb === "factory-queue") {
4899
5088
  const allowance = decideFactoryControlAllowance(roleFactsOf(session));
4900
5089
  if (!allowance.ok) return {
4901
5090
  status: allowance.status,
@@ -4912,6 +5101,8 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
4912
5101
  error: "Factory is not running"
4913
5102
  }
4914
5103
  };
5104
+ if (parsed.verb === "factory-setup") return setUpFactoryProject(session, factory, projects);
5105
+ if (parsed.verb === "factory-queue") return queueFactoryIntent(session, factory, projects, parsed.request);
4915
5106
  if (parsed.verb === "factory-status") {
4916
5107
  const selectedProjects = parsed.request.projectId === void 0 ? projects : projects.filter((project) => project.projectId === parsed.request.projectId);
4917
5108
  if (parsed.request.projectId !== void 0 && selectedProjects.length === 0) return {
@@ -4961,13 +5152,6 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
4961
5152
  }
4962
5153
  };
4963
5154
  }
4964
- if (parsed.verb === "dispatch") return {
4965
- status: 403,
4966
- body: {
4967
- ok: false,
4968
- reason: "dispatch requires a live factory orchestrator"
4969
- }
4970
- };
4971
5155
  if (parsed.verb === "board") return {
4972
5156
  status: 200,
4973
5157
  body: {
@@ -5200,6 +5384,14 @@ function resolveSessionSize({ viewers, departingKey, current }) {
5200
5384
  //#endregion
5201
5385
  //#region session/core/screen-keeper-core.ts
5202
5386
  var SCREEN_RESET = "\x1Bc\x1B[2J\x1B[3J\x1B[H";
5387
+ var SCREEN_KEEPER_SCROLLBACK = DEEP_HISTORY_SCROLLBACK_LINES;
5388
+ var SCREEN_KEEPER_SLICE_ROWS = 1e3;
5389
+ var SCREEN_KEEPER_SLICE_JOINER = "\x1B[0m\r\n";
5390
+ var SCREEN_KEEPER_PENDING_CAP = 4194304;
5391
+ var SCREEN_KEEPER_SEED_LIMIT = 1048576;
5392
+ function isKeeperOverrun(pushedOffset, parsedOffset, pendingCap = SCREEN_KEEPER_PENDING_CAP) {
5393
+ return pushedOffset - parsedOffset > pendingCap;
5394
+ }
5203
5395
  function pendingResizes(queue, parsedOffset) {
5204
5396
  let cut = 0;
5205
5397
  while (cut < queue.length) {
@@ -5212,6 +5404,36 @@ function pendingResizes(queue, parsedOffset) {
5212
5404
  rest: queue.slice(cut)
5213
5405
  };
5214
5406
  }
5407
+ function unwrappedLineAtOrBefore(line, isWrapped) {
5408
+ let candidate = line;
5409
+ while (candidate > 0 && isWrapped(candidate)) candidate -= 1;
5410
+ return candidate;
5411
+ }
5412
+ function sliceEndLine(start, limit, sliceRows, isWrapped) {
5413
+ const target = Math.min(start + sliceRows, limit);
5414
+ const unwrappedBefore = unwrappedLineAtOrBefore(target, isWrapped);
5415
+ if (unwrappedBefore > start) return unwrappedBefore;
5416
+ let unwrappedAfter = target;
5417
+ while (unwrappedAfter < limit && isWrapped(unwrappedAfter)) unwrappedAfter += 1;
5418
+ return unwrappedAfter;
5419
+ }
5420
+ function planDeepSlices({ lineCount, rows, sliceRows, isWrapped }) {
5421
+ const finalStart = unwrappedLineAtOrBefore(Math.max(0, lineCount - rows - sliceRows), isWrapped);
5422
+ const ranges = [];
5423
+ let start = 0;
5424
+ while (start < finalStart) {
5425
+ const end = sliceEndLine(start, finalStart, sliceRows, isWrapped);
5426
+ ranges.push({
5427
+ start,
5428
+ end: end - 1
5429
+ });
5430
+ start = end;
5431
+ }
5432
+ return {
5433
+ ranges,
5434
+ finalScrollback: Math.max(0, lineCount - rows - finalStart)
5435
+ };
5436
+ }
5215
5437
  //#endregion
5216
5438
  //#region server/ws-sender.ts
5217
5439
  var OPEN = 1;
@@ -5451,93 +5673,117 @@ function createBackendWebSockets(dependencies) {
5451
5673
  socket.close(1008, "Session not found");
5452
5674
  return;
5453
5675
  }
5454
- let viewerSizes = sessionDataClients.get(sessionId);
5455
- if (!viewerSizes) {
5456
- viewerSizes = /* @__PURE__ */ new Map();
5457
- sessionDataClients.set(sessionId, viewerSizes);
5676
+ socket.on("error", (error) => reportDataSocketError(session, error));
5677
+ if (parseDataAttachHistory(request.url) === "screen") {
5678
+ wireDataSocket(socket, session, sessionId, session.getScreenSnapshot());
5679
+ return;
5458
5680
  }
5459
- const activeSessionId = sessionId;
5460
- const viewerSizeMap = viewerSizes;
5461
- viewerSizeMap.set(socket, null);
5462
- const applyResolvedSize = (departing) => {
5463
- const resolved = resolveSessionSize({
5464
- viewers: viewerSizeMap,
5465
- ...departing ? { departingKey: socket } : {},
5466
- current: session.ptySize()
5467
- });
5468
- if (!resolved.changed) return;
5469
- session.resize(resolved.cols, resolved.rows);
5470
- };
5471
- const releaseViewerSize = () => {
5472
- if (!viewerSizeMap.get(socket)) return;
5473
- viewerSizeMap.set(socket, null);
5474
- if (sessionDataClients.get(activeSessionId) !== viewerSizeMap) return;
5475
- applyResolvedSize(true);
5476
- };
5477
- const snapshot = session.getScreenSnapshot();
5478
- const sender = createWsSender(socket, {
5479
- source: { getBufferSince: (offset) => session.getBufferSince(offset) },
5480
- startOffset: snapshot.offset
5481
- });
5482
- sendPtySizeFrame(sender, session.ptySize());
5483
- if (snapshot.data) sender.sendImmediate(snapshot.data, 0);
5484
- const dataListener = (data) => sender.onData(data);
5485
- session.on("data", dataListener);
5486
- const resizeListener = (size) => sendPtySizeFrame(sender, size);
5487
- session.on("resize", resizeListener);
5488
- socket.on("message", (raw) => {
5489
- const message = parseDataClientMessage(String(raw));
5490
- if (!message) return;
5491
- if (message.type === "input") {
5492
- if (message.data.length > 16384) {
5493
- logger.warn(`[data-ws] Rejected oversized input (${message.data.length} chars) for ${session.name}`);
5494
- broadcastControl({
5495
- type: "session-error",
5496
- id: session.id,
5497
- session: session.name,
5498
- message: "Paste too large - try pasting smaller chunks",
5499
- timestamp: Date.now()
5500
- });
5501
- return;
5502
- }
5503
- session.write(message.data);
5504
- sender.markInputFlush();
5505
- session.noteOperatorInput(message.data);
5681
+ const messagesBeforeWiring = [];
5682
+ const holdMessage = (raw) => {
5683
+ messagesBeforeWiring.push(raw);
5684
+ };
5685
+ socket.on("message", holdMessage);
5686
+ session.getDeepScreenSnapshot().then((snapshot) => {
5687
+ socket.off("message", holdMessage);
5688
+ if (socket.readyState !== WebSocket.OPEN) return;
5689
+ if (getSession(sessionId) !== session) {
5690
+ socket.close(1008, "Session not found");
5506
5691
  return;
5507
5692
  }
5508
- if (message.type === "claim") {
5509
- nextViewerResizeSeq += 1;
5510
- viewerSizeMap.set(socket, {
5511
- cols: message.cols,
5512
- rows: message.rows,
5513
- resizeSeq: nextViewerResizeSeq
5693
+ wireDataSocket(socket, session, sessionId, snapshot);
5694
+ for (const raw of messagesBeforeWiring) socket.emit("message", raw, false);
5695
+ }, (error) => {
5696
+ logger.warn(`[data-ws] Deep history failed for ${session.name}: ${String(error)}`);
5697
+ socket.close(1011, "History unavailable");
5698
+ });
5699
+ });
5700
+ }
5701
+ function reportDataSocketError(session, error) {
5702
+ const reason = error.code === "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH" ? "Message too large - try pasting smaller chunks" : error.message;
5703
+ logger.warn(`[data-ws] Error for ${session.name}: ${error.message}`);
5704
+ broadcastControl({
5705
+ type: "session-error",
5706
+ id: session.id,
5707
+ session: session.name,
5708
+ message: reason,
5709
+ timestamp: Date.now()
5710
+ });
5711
+ }
5712
+ function wireDataSocket(socket, session, sessionId, snapshot) {
5713
+ let viewerSizes = sessionDataClients.get(sessionId);
5714
+ if (!viewerSizes) {
5715
+ viewerSizes = /* @__PURE__ */ new Map();
5716
+ sessionDataClients.set(sessionId, viewerSizes);
5717
+ }
5718
+ const activeSessionId = sessionId;
5719
+ const viewerSizeMap = viewerSizes;
5720
+ viewerSizeMap.set(socket, null);
5721
+ const applyResolvedSize = (departing) => {
5722
+ const resolved = resolveSessionSize({
5723
+ viewers: viewerSizeMap,
5724
+ ...departing ? { departingKey: socket } : {},
5725
+ current: session.ptySize()
5726
+ });
5727
+ if (!resolved.changed) return;
5728
+ session.resize(resolved.cols, resolved.rows);
5729
+ };
5730
+ const releaseViewerSize = () => {
5731
+ if (!viewerSizeMap.get(socket)) return;
5732
+ viewerSizeMap.set(socket, null);
5733
+ if (sessionDataClients.get(activeSessionId) !== viewerSizeMap) return;
5734
+ applyResolvedSize(true);
5735
+ };
5736
+ const sender = createWsSender(socket, {
5737
+ source: { getBufferSince: (offset) => session.getBufferSince(offset) },
5738
+ startOffset: snapshot.offset
5739
+ });
5740
+ sendPtySizeFrame(sender, session.ptySize());
5741
+ if (snapshot.data) sender.sendImmediate(snapshot.data, 0);
5742
+ const dataListener = (data) => sender.onData(data);
5743
+ session.on("data", dataListener);
5744
+ const resizeListener = (size) => sendPtySizeFrame(sender, size);
5745
+ session.on("resize", resizeListener);
5746
+ socket.on("message", (raw) => {
5747
+ const message = parseDataClientMessage(String(raw));
5748
+ if (!message) return;
5749
+ if (message.type === "input") {
5750
+ if (message.data.length > 16384) {
5751
+ logger.warn(`[data-ws] Rejected oversized input (${message.data.length} chars) for ${session.name}`);
5752
+ broadcastControl({
5753
+ type: "session-error",
5754
+ id: session.id,
5755
+ session: session.name,
5756
+ message: "Paste too large - try pasting smaller chunks",
5757
+ timestamp: Date.now()
5514
5758
  });
5515
- applyResolvedSize(false);
5516
5759
  return;
5517
5760
  }
5518
- if (message.type === "unview") releaseViewerSize();
5519
- });
5520
- socket.on("error", (error) => {
5521
- const reason = error.code === "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH" ? "Message too large - try pasting smaller chunks" : error.message;
5522
- logger.warn(`[data-ws] Error for ${session.name}: ${error.message}`);
5523
- broadcastControl({
5524
- type: "session-error",
5525
- id: session.id,
5526
- session: session.name,
5527
- message: reason,
5528
- timestamp: Date.now()
5761
+ session.write(message.data);
5762
+ sender.markInputFlush();
5763
+ session.noteOperatorInput(message.data);
5764
+ return;
5765
+ }
5766
+ if (message.type === "claim") {
5767
+ nextViewerResizeSeq += 1;
5768
+ viewerSizeMap.set(socket, {
5769
+ cols: message.cols,
5770
+ rows: message.rows,
5771
+ resizeSeq: nextViewerResizeSeq
5529
5772
  });
5530
- });
5531
- socket.on("close", () => {
5532
- session.removeListener("data", dataListener);
5533
- session.removeListener("resize", resizeListener);
5534
- sender.destroy();
5535
- releaseViewerSize();
5536
- const clients = sessionDataClients.get(activeSessionId);
5537
- if (!clients) return;
5538
- clients.delete(socket);
5539
- if (clients.size === 0) sessionDataClients.delete(activeSessionId);
5540
- });
5773
+ applyResolvedSize(false);
5774
+ return;
5775
+ }
5776
+ if (message.type === "unview") releaseViewerSize();
5777
+ });
5778
+ socket.on("close", () => {
5779
+ session.removeListener("data", dataListener);
5780
+ session.removeListener("resize", resizeListener);
5781
+ sender.destroy();
5782
+ releaseViewerSize();
5783
+ const clients = sessionDataClients.get(activeSessionId);
5784
+ if (!clients) return;
5785
+ clients.delete(socket);
5786
+ if (clients.size === 0) sessionDataClients.delete(activeSessionId);
5541
5787
  });
5542
5788
  }
5543
5789
  function handleUpgrade(request, socket, head) {
@@ -5622,6 +5868,7 @@ function createBackendShutdown(dependencies) {
5622
5868
  isShuttingDown = true;
5623
5869
  const stoppers = createStopperCollector();
5624
5870
  dependencies.cancelAutoResume();
5871
+ dependencies.cancelPostTurnLeftoverCleanup?.();
5625
5872
  clearInterval(dependencies.healthInterval);
5626
5873
  const stopConfigWatch = dependencies.getStopConfigWatch();
5627
5874
  if (stopConfigWatch) stopConfigWatch();
@@ -6561,7 +6808,7 @@ function runGh(args, options = {}) {
6561
6808
  }
6562
6809
  //#endregion
6563
6810
  //#region server/post-turn-checker.ts
6564
- var GIT_TIMEOUT_MS$1 = 5e3;
6811
+ var GIT_TIMEOUT_MS$2 = 5e3;
6565
6812
  var DEFAULTS = Object.freeze({
6566
6813
  enabled: true,
6567
6814
  mode: "report",
@@ -6597,9 +6844,7 @@ var DEFAULTS = Object.freeze({
6597
6844
  ],
6598
6845
  maxFiles: 200,
6599
6846
  maxFileBytes: 524288,
6600
- debounceMs: 1500,
6601
- runOnExit: false,
6602
- reportDir: ".glimmervoid/post-turn"
6847
+ debounceMs: 1500
6603
6848
  });
6604
6849
  function explicitMode(mode) {
6605
6850
  return mode === "fix" ? "fix" : "report";
@@ -6630,9 +6875,7 @@ var SCALAR_KEYS = [
6630
6875
  "mode",
6631
6876
  "maxFiles",
6632
6877
  "maxFileBytes",
6633
- "debounceMs",
6634
- "runOnExit",
6635
- "reportDir"
6878
+ "debounceMs"
6636
6879
  ];
6637
6880
  function resolveCheckConfig(globalCfg, projectCfg) {
6638
6881
  const base = JSON.parse(JSON.stringify(DEFAULTS));
@@ -6654,16 +6897,14 @@ function resolveCheckConfig(globalCfg, projectCfg) {
6654
6897
  exclude: base.exclude,
6655
6898
  maxFiles: base.maxFiles,
6656
6899
  maxFileBytes: base.maxFileBytes,
6657
- debounceMs: base.debounceMs,
6658
- runOnExit: !!base.runOnExit,
6659
- reportDir: base.reportDir
6900
+ debounceMs: base.debounceMs
6660
6901
  };
6661
6902
  }
6662
6903
  var GIT_MAX_BUFFER_BYTES$1 = 8388608;
6663
6904
  async function execGit(args, cwd) {
6664
6905
  const result = await runGit(args, {
6665
6906
  cwd,
6666
- timeoutMs: GIT_TIMEOUT_MS$1,
6907
+ timeoutMs: GIT_TIMEOUT_MS$2,
6667
6908
  maxBuffer: GIT_MAX_BUFFER_BYTES$1,
6668
6909
  trim: false
6669
6910
  });
@@ -6694,7 +6935,7 @@ async function listChangedFiles(root) {
6694
6935
  }
6695
6936
  return files;
6696
6937
  }
6697
- async function runPostTurnChecks({ cwd, config, sessionId, deps = {} } = {}) {
6938
+ async function runPostTurnChecks({ cwd, config, deps = {} } = {}) {
6698
6939
  const started = Date.now();
6699
6940
  const cfg = config || resolveCheckConfig();
6700
6941
  const report = {
@@ -6794,14 +7035,7 @@ async function runPostTurnChecks({ cwd, config, sessionId, deps = {} } = {}) {
6794
7035
  });
6795
7036
  }
6796
7037
  }
6797
- const finalReport = done();
6798
- if (sessionId) try {
6799
- const dir = path.join(root, cfg.reportDir);
6800
- fs.mkdirSync(dir, { recursive: true });
6801
- const safe = String(sessionId).replace(/[^a-zA-Z0-9_.-]/g, "_");
6802
- writeJsonAtomicSync(path.join(dir, `${safe}.json`), finalReport);
6803
- } catch {}
6804
- return finalReport;
7038
+ return done();
6805
7039
  }
6806
7040
  //#endregion
6807
7041
  //#region server/session/session-event-wiring.ts
@@ -6879,7 +7113,9 @@ function createSessionEventWiring(dependencies) {
6879
7113
  duration_seconds: durationSeconds
6880
7114
  });
6881
7115
  };
6882
- session.on("claude-session-id", ({ id, vendor, isResumeTarget }) => {
7116
+ let liveTranscriptPath = null;
7117
+ session.on("claude-session-id", ({ id, vendor, isResumeTarget, transcriptPath }) => {
7118
+ liveTranscriptPath = transcriptPath;
6883
7119
  if (isResumeTarget) persistProjectField("resumeSessionId", id);
6884
7120
  dependencies.recordLane(id, INTERACTIVE_LANE, vendor);
6885
7121
  dependencies.usage.refreshSessions();
@@ -6912,6 +7148,7 @@ function createSessionEventWiring(dependencies) {
6912
7148
  session.on("teardown", () => {
6913
7149
  clearCompactionConfirm();
6914
7150
  isAcknowledgeDeferredForCompaction = false;
7151
+ dependencies.getIngestLane()?.detachSessionTap(session);
6915
7152
  });
6916
7153
  const resolvePostTurn = () => {
6917
7154
  const project = dependencies.config.projects.find((candidate) => candidate.id === session.id);
@@ -6927,8 +7164,7 @@ function createSessionEventWiring(dependencies) {
6927
7164
  if (!runConfig?.enabled) return;
6928
7165
  runPostTurnChecks({
6929
7166
  cwd: session.effectiveCwd(),
6930
- config: runConfig,
6931
- sessionId: session.id
7167
+ config: runConfig
6932
7168
  }).then((report) => {
6933
7169
  dependencies.broadcastControl({
6934
7170
  type: "post-turn-result",
@@ -6940,7 +7176,7 @@ function createSessionEventWiring(dependencies) {
6940
7176
  }).catch((error) => dependencies.logger.warn(`[${session.name}] post-turn checks failed: ${errorMessage(error)}`));
6941
7177
  }, config.debounceMs);
6942
7178
  });
6943
- session.on("post-turn-check", () => dependencies.usage.nudgeSession());
7179
+ session.on("post-turn-check", () => dependencies.usage.nudgeSession(liveTranscriptPath));
6944
7180
  session.on("state-change", ({ from, to, event, detail }) => {
6945
7181
  if (to === STATES.IDLE || to === STATES.COMPLETE) dependencies.taskTitleRefiner?.onTurnEnd(session);
6946
7182
  if (event === "spawn_success") {
@@ -7038,8 +7274,6 @@ function createSessionEventWiring(dependencies) {
7038
7274
  relay("wakeup-change", "session-wakeup");
7039
7275
  relay("prompt-kind-change", "session-prompt");
7040
7276
  relay("conversation-change", "session-conversation");
7041
- relay("sleep", "session-sleep");
7042
- relay("wake", "session-wake");
7043
7277
  session.on("merge-status", ({ mergeStatus, reason, parked }) => {
7044
7278
  dependencies.broadcastControl({
7045
7279
  type: "session-merge-status",
@@ -7392,6 +7626,9 @@ function runAutoResume(sessions, config, spawnGate, logger = console) {
7392
7626
  runs.push(spawnGate.run(() => {
7393
7627
  if (session.state !== STATES.DORMANT) return null;
7394
7628
  return session.start({ resumePrompt });
7629
+ }, {
7630
+ isStillWanted: () => session.state === STATES.DORMANT,
7631
+ onMemoryWait: (isWaiting) => session.setWaitingForMemory(isWaiting)
7395
7632
  }).catch((error) => {
7396
7633
  logger.warn(`[boot] auto-resume failed for ${session.name}: ${errorMessage(error)}`);
7397
7634
  }));
@@ -12732,22 +12969,33 @@ var DEFAULT_STABILIZATION_MS = 1500;
12732
12969
  var OSC_START = "\x1B]0;";
12733
12970
  var BEL = "\x07";
12734
12971
  var ST = "\x1B\\";
12735
- function findOscTitle(buffer, fromIndex) {
12972
+ var MAX_PENDING_TITLE = 4096;
12973
+ function nextTerminatorIndex(buffer, terminator, previousIndex, fromIndex) {
12974
+ if (previousIndex >= fromIndex) return previousIndex;
12975
+ const index = buffer.indexOf(terminator, fromIndex);
12976
+ return index === -1 ? Number.POSITIVE_INFINITY : index;
12977
+ }
12978
+ function findOscTitle(buffer, fromIndex, terminators) {
12736
12979
  const start = buffer.indexOf(OSC_START, fromIndex);
12737
12980
  if (start === -1) return null;
12738
12981
  const contentStart = start + 4;
12739
- const belEnd = buffer.indexOf(BEL, contentStart);
12740
- const stEnd = buffer.indexOf(ST, contentStart);
12741
- if (belEnd === -1 && stEnd === -1) return null;
12742
- const useBel = belEnd !== -1 && (stEnd === -1 || belEnd < stEnd);
12743
- const end = useBel ? belEnd : stEnd;
12982
+ terminators.bel = nextTerminatorIndex(buffer, BEL, terminators.bel, contentStart);
12983
+ terminators.st = nextTerminatorIndex(buffer, ST, terminators.st, contentStart);
12984
+ const end = Math.min(terminators.bel, terminators.st);
12985
+ if (end === Number.POSITIVE_INFINITY) return null;
12744
12986
  return {
12745
12987
  start,
12746
12988
  end,
12747
- next: end + (useBel ? 1 : 2),
12989
+ next: end + (end === terminators.bel ? 1 : 2),
12748
12990
  title: buffer.slice(contentStart, end)
12749
12991
  };
12750
12992
  }
12993
+ function unterminatedTail(buffer, fromIndex) {
12994
+ const openStart = buffer.indexOf(OSC_START, fromIndex);
12995
+ if (openStart !== -1) return buffer.length - openStart > MAX_PENDING_TITLE ? "" : buffer.slice(openStart);
12996
+ for (let prefixLength = 3; prefixLength > 0; prefixLength -= 1) if (buffer.endsWith(OSC_START.slice(0, prefixLength))) return buffer.slice(buffer.length - prefixLength);
12997
+ return "";
12998
+ }
12751
12999
  var OscTitleSource = class extends EventEmitter {
12752
13000
  _stabilizationMs;
12753
13001
  _profile;
@@ -12774,16 +13022,19 @@ var OscTitleSource = class extends EventEmitter {
12774
13022
  }
12775
13023
  feed(chunk) {
12776
13024
  if (this._destroyed || !chunk) return;
12777
- this._pending += chunk;
12778
- if (this._pending.length > 8192) this._pending = this._pending.slice(-4096);
13025
+ const buffer = this._pending ? this._pending + chunk : chunk;
13026
+ const terminators = {
13027
+ bel: -1,
13028
+ st: -1
13029
+ };
12779
13030
  let cursor = 0;
12780
13031
  while (true) {
12781
- const osc = findOscTitle(this._pending, cursor);
13032
+ const osc = findOscTitle(buffer, cursor, terminators);
12782
13033
  if (!osc) break;
12783
13034
  this._processTitle(osc.title);
12784
13035
  cursor = osc.next;
12785
13036
  }
12786
- if (cursor > 0) this._pending = this._pending.slice(cursor);
13037
+ this._pending = unterminatedTail(buffer, cursor);
12787
13038
  }
12788
13039
  _classifyTitle(trimmed, char) {
12789
13040
  const profile = this._profile;
@@ -13532,7 +13783,7 @@ async function startCodexAppServerBridge(options) {
13532
13783
  clearTimeout(killTimer);
13533
13784
  }
13534
13785
  } finally {
13535
- if (socket && socket.readyState !== WebSocket.CLOSED) guard(() => socket?.terminate());
13786
+ if (socket && socket.readyState !== WebSocket$1.CLOSED) guard(() => socket?.terminate());
13536
13787
  await rm(privateDirectory, {
13537
13788
  recursive: true,
13538
13789
  force: true
@@ -13542,7 +13793,7 @@ async function startCodexAppServerBridge(options) {
13542
13793
  return stopPromise;
13543
13794
  }
13544
13795
  function send(message) {
13545
- if (isStopped || socket?.readyState !== WebSocket.OPEN) throw new Error("Codex app-server socket is not open");
13796
+ if (isStopped || socket?.readyState !== WebSocket$1.OPEN) throw new Error("Codex app-server socket is not open");
13546
13797
  socket.send(JSON.stringify(message));
13547
13798
  }
13548
13799
  function request(method, params, timeoutMs = 15e3) {
@@ -13572,7 +13823,7 @@ async function startCodexAppServerBridge(options) {
13572
13823
  });
13573
13824
  }
13574
13825
  function answerApproval(requestId, decision) {
13575
- if (!activeApproval || activeApproval.requestId !== requestId || isStopped || socket?.readyState !== WebSocket.OPEN) return false;
13826
+ if (!activeApproval || activeApproval.requestId !== requestId || isStopped || socket?.readyState !== WebSocket$1.OPEN) return false;
13576
13827
  try {
13577
13828
  if (activeApproval.kind === "permissions") {
13578
13829
  if (!approvalPermissions) return false;
@@ -13762,7 +14013,7 @@ async function startCodexAppServerBridge(options) {
13762
14013
  function connect(url) {
13763
14014
  if (socket || isStopped) return;
13764
14015
  bridge.url = url;
13765
- socket = options.createSocket?.(url, { Authorization: `Bearer ${token}` }) ?? new WebSocket(url, { headers: { Authorization: `Bearer ${token}` } });
14016
+ socket = options.createSocket?.(url, { Authorization: `Bearer ${token}` }) ?? new WebSocket$1(url, { headers: { Authorization: `Bearer ${token}` } });
13766
14017
  socket.on("error", (failure) => guard(() => reportError(failure)));
13767
14018
  socket.on("message", (frame, isBinary) => guard(() => handleMessage(frame, isBinary)));
13768
14019
  socket.on("close", () => guard(() => {
@@ -14694,11 +14945,16 @@ function serializeMouseEncoding(encoding) {
14694
14945
  //#region session/screen-keeper.ts
14695
14946
  var { Terminal } = headless;
14696
14947
  var { SerializeAddon } = createRequire(import.meta.url)("@xterm/addon-serialize");
14697
- function createScreenKeeper({ cols, rows }) {
14948
+ function yieldToEventLoop$2() {
14949
+ return new Promise((resolve) => {
14950
+ setImmediate(resolve);
14951
+ });
14952
+ }
14953
+ function createScreenKeeper({ cols, rows }, { pendingCap = SCREEN_KEEPER_PENDING_CAP, sliceRows = SCREEN_KEEPER_SLICE_ROWS } = {}) {
14698
14954
  const terminal = new Terminal({
14699
14955
  cols,
14700
14956
  rows,
14701
- scrollback: 500,
14957
+ scrollback: SCREEN_KEEPER_SCROLLBACK,
14702
14958
  allowProposedApi: true
14703
14959
  });
14704
14960
  const serializer = new SerializeAddon();
@@ -14716,25 +14972,106 @@ function createScreenKeeper({ cols, rows }) {
14716
14972
  return false;
14717
14973
  }));
14718
14974
  let pushedOffset = 0;
14975
+ let writtenOffset = 0;
14719
14976
  let parsedOffset = 0;
14720
14977
  let queue = [];
14721
14978
  let disposed = false;
14979
+ let overrun = false;
14980
+ let heldChunks = [];
14981
+ let isHoldingWrites = false;
14982
+ let isBufferFrozen = false;
14983
+ let drainWaiters = [];
14984
+ let deepSerializeInFlight = null;
14722
14985
  function drainResizes() {
14723
- if (disposed) return;
14986
+ if (disposed || isBufferFrozen) return;
14724
14987
  const split = pendingResizes(queue, parsedOffset);
14725
14988
  queue = split.rest;
14726
14989
  for (const marker of split.due) terminal.resize(marker.cols, marker.rows);
14727
14990
  }
14991
+ function settleDrainWaiters() {
14992
+ const waiters = drainWaiters;
14993
+ drainWaiters = [];
14994
+ for (const resolve of waiters) resolve();
14995
+ }
14996
+ function writeToTerminal(chunk) {
14997
+ writtenOffset += chunk.length;
14998
+ const reachedOffset = writtenOffset;
14999
+ terminal.write(chunk, () => {
15000
+ if (disposed) return;
15001
+ parsedOffset = reachedOffset;
15002
+ drainResizes();
15003
+ if (parsedOffset === writtenOffset) settleDrainWaiters();
15004
+ });
15005
+ }
15006
+ function waitForWrittenBytesToParse() {
15007
+ if (disposed || parsedOffset === writtenOffset) return Promise.resolve();
15008
+ return new Promise((resolve) => {
15009
+ drainWaiters.push(resolve);
15010
+ });
15011
+ }
15012
+ function releaseHeldWrites() {
15013
+ isBufferFrozen = false;
15014
+ isHoldingWrites = false;
15015
+ if (disposed) return;
15016
+ drainResizes();
15017
+ const chunks = heldChunks;
15018
+ heldChunks = [];
15019
+ for (const chunk of chunks) writeToTerminal(chunk);
15020
+ }
15021
+ function serializeAttach() {
15022
+ return serializer.serialize({ scrollback: 500 }) + serializeMouseEncoding(mouseEncoding);
15023
+ }
15024
+ async function serializeFrozenSlices() {
15025
+ const buffer = terminal.buffer.normal;
15026
+ const plan = planDeepSlices({
15027
+ lineCount: buffer.length,
15028
+ rows: terminal.rows,
15029
+ sliceRows,
15030
+ isWrapped: (line) => buffer.getLine(line)?.isWrapped ?? false
15031
+ });
15032
+ const slices = [];
15033
+ for (const range of plan.ranges) {
15034
+ slices.push(serializer.serialize({
15035
+ range,
15036
+ excludeAltBuffer: true,
15037
+ excludeModes: true
15038
+ }));
15039
+ await yieldToEventLoop$2();
15040
+ if (disposed) return null;
15041
+ }
15042
+ slices.push(serializer.serialize({ scrollback: plan.finalScrollback }) + serializeMouseEncoding(mouseEncoding));
15043
+ return {
15044
+ data: slices.join(SCREEN_KEEPER_SLICE_JOINER),
15045
+ parsedOffset
15046
+ };
15047
+ }
15048
+ async function serializeDeepHoldingWrites() {
15049
+ isHoldingWrites = true;
15050
+ try {
15051
+ await waitForWrittenBytesToParse();
15052
+ if (disposed) return null;
15053
+ isBufferFrozen = true;
15054
+ return await serializeFrozenSlices();
15055
+ } finally {
15056
+ releaseHeldWrites();
15057
+ deepSerializeInFlight = null;
15058
+ }
15059
+ }
14728
15060
  return {
14729
15061
  push(chunk) {
14730
- if (disposed || chunk.length === 0) return;
15062
+ if (disposed || overrun) return false;
15063
+ if (chunk.length === 0) return true;
15064
+ if (isKeeperOverrun(pushedOffset, parsedOffset, pendingCap)) {
15065
+ overrun = true;
15066
+ return false;
15067
+ }
14731
15068
  pushedOffset += chunk.length;
14732
- const reachedOffset = pushedOffset;
14733
- terminal.write(chunk, () => {
14734
- if (disposed) return;
14735
- parsedOffset = reachedOffset;
14736
- drainResizes();
14737
- });
15069
+ if (isHoldingWrites) {
15070
+ heldChunks.push(chunk);
15071
+ return true;
15072
+ }
15073
+ writeToTerminal(chunk);
15074
+ return true;
14738
15075
  },
14739
15076
  resize(nextCols, nextRows) {
14740
15077
  if (disposed) return;
@@ -14746,11 +15083,21 @@ function createScreenKeeper({ cols, rows }) {
14746
15083
  drainResizes();
14747
15084
  },
14748
15085
  parsedOffset: () => parsedOffset,
14749
- serialize: () => serializer.serialize({ scrollback: 500 }) + serializeMouseEncoding(mouseEncoding),
15086
+ isOverrun: () => overrun,
15087
+ isDrained: () => parsedOffset === pushedOffset,
15088
+ serialize: serializeAttach,
15089
+ serializeDeep() {
15090
+ if (disposed) return Promise.resolve(null);
15091
+ if (deepSerializeInFlight) return deepSerializeInFlight;
15092
+ deepSerializeInFlight = serializeDeepHoldingWrites();
15093
+ return deepSerializeInFlight;
15094
+ },
14750
15095
  dispose() {
14751
15096
  if (disposed) return;
14752
15097
  disposed = true;
14753
15098
  queue = [];
15099
+ heldChunks = [];
15100
+ settleDrainWaiters();
14754
15101
  for (const handler of mouseEncodingHandlers) handler.dispose();
14755
15102
  serializer.dispose();
14756
15103
  terminal.dispose();
@@ -14838,31 +15185,44 @@ function createSessionOutput(options) {
14838
15185
  return changed;
14839
15186
  }
14840
15187
  function push(chunk) {
14841
- if (!keeper) {
14842
- keeperBaseOffset = ring.stats().total;
14843
- keeper = keeperFactory(ptySize());
14844
- }
14845
- keeper.push(chunk);
14846
15188
  ring.push(chunk);
15189
+ if (keeper?.push(chunk)) return;
15190
+ if (keeper && !keeper.isDrained()) return;
15191
+ disposeScreenKeeper();
15192
+ const { total, bytes } = ring.stats();
15193
+ keeperBaseOffset = Math.max(total - bytes, total - SCREEN_KEEPER_SEED_LIMIT);
15194
+ keeper = keeperFactory(ptySize());
15195
+ keeper.push(ring.since(keeperBaseOffset).data);
14847
15196
  }
14848
15197
  function reset() {
14849
15198
  disposeScreenKeeper();
14850
15199
  ring.reset();
14851
15200
  }
14852
- function snapshot() {
14853
- const offset = ring.stats().total;
14854
- if (!keeper) return {
15201
+ function ringSnapshot() {
15202
+ return {
14855
15203
  data: SCREEN_RESET + ring.replay(),
14856
- offset
15204
+ offset: ring.stats().total
14857
15205
  };
15206
+ }
15207
+ function snapshot() {
15208
+ if (!keeper || keeper.isOverrun()) return ringSnapshot();
14858
15209
  const tail = ring.since(keeperBaseOffset + keeper.parsedOffset());
14859
- if (tail.evicted) return {
14860
- data: SCREEN_RESET + ring.replay(),
14861
- offset
14862
- };
15210
+ if (tail.evicted) return ringSnapshot();
14863
15211
  return {
14864
15212
  data: SCREEN_RESET + keeper.serialize() + tail.data,
14865
- offset
15213
+ offset: ring.stats().total
15214
+ };
15215
+ }
15216
+ async function deepSnapshot() {
15217
+ const deepKeeper = keeper;
15218
+ if (!deepKeeper || deepKeeper.isOverrun()) return ringSnapshot();
15219
+ const deepScreen = await deepKeeper.serializeDeep();
15220
+ if (!deepScreen || keeper !== deepKeeper) return ringSnapshot();
15221
+ const tail = ring.since(keeperBaseOffset + deepScreen.parsedOffset);
15222
+ if (tail.evicted) return ringSnapshot();
15223
+ return {
15224
+ data: SCREEN_RESET + deepScreen.data + tail.data,
15225
+ offset: ring.stats().total
14866
15226
  };
14867
15227
  }
14868
15228
  options.on("exit", disposeScreenKeeper);
@@ -14870,6 +15230,7 @@ function createSessionOutput(options) {
14870
15230
  push,
14871
15231
  since: (offset) => ring.since(offset),
14872
15232
  snapshot,
15233
+ deepSnapshot,
14873
15234
  reset,
14874
15235
  setMax: (bytes) => ring.setMax(bytes),
14875
15236
  stats: () => ring.stats(),
@@ -21148,7 +21509,6 @@ function createSessionBackgroundTracking({ detectBackgroundAgents, agentTtlMs, s
21148
21509
  var KILL_POLL_INTERVAL_MS = 200;
21149
21510
  var KILL_MAX_WAIT_MS = 3e3;
21150
21511
  var KILL_REAP_MAX_WAIT_MS = 2400;
21151
- var SLEEP_KILL_TIMEOUT_MS = 9e5;
21152
21512
  var MAX_PENDING_ATTENTION_NOTES = 5;
21153
21513
  var GUARDRAILS_UNAPPLIED_ERROR = "guardrail settings could not be applied, session not started";
21154
21514
  var GUARDRAILS_REFUSED_PREFIX = "session not started, the guardrails refused: ";
@@ -21203,13 +21563,10 @@ var Session = class extends EventEmitter {
21203
21563
  _receivedFirstOutput;
21204
21564
  _killPollTimer;
21205
21565
  _killReapTimer;
21206
- _sleepKillTimer;
21207
21566
  _titleQuietFallbackTimer;
21208
21567
  _resolveKillReap;
21209
21568
  _killReap;
21210
21569
  _exitReap;
21211
- _sleeping;
21212
- _autoKilled;
21213
21570
  _destroyed;
21214
21571
  _pendingRestart;
21215
21572
  _finishing;
@@ -21223,6 +21580,7 @@ var Session = class extends EventEmitter {
21223
21580
  _lastSignal;
21224
21581
  _compaction;
21225
21582
  _pendingPromptKind;
21583
+ _isWaitingForMemory;
21226
21584
  _pendingPromptToolName;
21227
21585
  _pendingAttentionNotes;
21228
21586
  _titleQuiet;
@@ -21292,9 +21650,6 @@ var Session = class extends EventEmitter {
21292
21650
  this._resolveKillReap = null;
21293
21651
  this._killReap = null;
21294
21652
  this._exitReap = null;
21295
- this._sleeping = false;
21296
- this._sleepKillTimer = null;
21297
- this._autoKilled = false;
21298
21653
  this._destroyed = false;
21299
21654
  this._pendingRestart = false;
21300
21655
  this._finishing = false;
@@ -21346,6 +21701,7 @@ var Session = class extends EventEmitter {
21346
21701
  this._lastSignal = null;
21347
21702
  this._compaction = createCompactionTracking();
21348
21703
  this._pendingPromptKind = null;
21704
+ this._isWaitingForMemory = false;
21349
21705
  this._pendingPromptToolName = null;
21350
21706
  this._pendingAttentionNotes = [];
21351
21707
  this._titleQuiet = false;
@@ -21489,11 +21845,8 @@ var Session = class extends EventEmitter {
21489
21845
  this._taskTitleSources.customTitle = this.customTitle;
21490
21846
  this._updateTaskTitle();
21491
21847
  }
21492
- get settledTaskTitle() {
21493
- return resolveTaskTitle({
21494
- ...this._taskTitleSources,
21495
- pendingPromptTitle: null
21496
- }).taskTitle;
21848
+ get refinedTaskTitle() {
21849
+ return this._taskTitleSources.refinedTitle ?? null;
21497
21850
  }
21498
21851
  get persistedTaskTitle() {
21499
21852
  return buildPersistedTaskTitle(this._taskTitleSources);
@@ -21717,10 +22070,16 @@ var Session = class extends EventEmitter {
21717
22070
  });
21718
22071
  if (this._compaction.isCompacting !== wasCompacting) this._emitSessionDetail();
21719
22072
  }
22073
+ setWaitingForMemory(isWaiting) {
22074
+ if (isWaiting === this._isWaitingForMemory) return;
22075
+ this._isWaitingForMemory = isWaiting;
22076
+ this._emitSessionDetail();
22077
+ }
21720
22078
  _emitSessionDetail() {
21721
22079
  this.emit("prompt-kind-change", {
21722
22080
  pendingPromptKind: this._pendingPromptKind,
21723
- isCompacting: this._compaction.isCompacting
22081
+ isCompacting: this._compaction.isCompacting,
22082
+ isWaitingForMemory: this._isWaitingForMemory
21724
22083
  });
21725
22084
  }
21726
22085
  _finishCompaction() {
@@ -21907,9 +22266,6 @@ var Session = class extends EventEmitter {
21907
22266
  this._pendingAttentionNotes = [];
21908
22267
  this._raiseAttention(releasing.join(" | "));
21909
22268
  }
21910
- get sleeping() {
21911
- return this._sleeping;
21912
- }
21913
22269
  get hasLivePty() {
21914
22270
  return !this._destroyed && !!this.ptyProcess && !!this._ptyAlive;
21915
22271
  }
@@ -22051,8 +22407,6 @@ var Session = class extends EventEmitter {
22051
22407
  id: this.id,
22052
22408
  name: this.name,
22053
22409
  state: this.state,
22054
- sleeping: this._sleeping,
22055
- autoKilled: this._autoKilled,
22056
22410
  destroyed: this._destroyed,
22057
22411
  pendingRestart: this._pendingRestart,
22058
22412
  hasPty: this.ptyProcess !== null,
@@ -22063,10 +22417,7 @@ var Session = class extends EventEmitter {
22063
22417
  auditLogLength: this.auditLog.length,
22064
22418
  dataListenerCount: this.listenerCount("data"),
22065
22419
  hookSeen: this._hookSeen,
22066
- timers: {
22067
- sleepKill: this._sleepKillTimer !== null,
22068
- killPoll: this._killPollTimer !== null
22069
- }
22420
+ timers: { killPoll: this._killPollTimer !== null }
22070
22421
  };
22071
22422
  }
22072
22423
  getDebugState() {
@@ -22084,7 +22435,6 @@ var Session = class extends EventEmitter {
22084
22435
  agent: this.agentId,
22085
22436
  state: this.state,
22086
22437
  stateSince: this.stateSince,
22087
- sleeping: this._sleeping,
22088
22438
  dangerouslySkipPermissions: this.dangerouslySkipPermissions,
22089
22439
  saneYolo: this.saneYolo,
22090
22440
  ephemeral: this.ephemeral,
@@ -22100,6 +22450,7 @@ var Session = class extends EventEmitter {
22100
22450
  hasConversationBridge: this._codexBridge !== null,
22101
22451
  isConversationViewOn: this._isConversationViewEnabled(),
22102
22452
  isCompacting: this._compaction.isCompacting,
22453
+ isWaitingForMemory: this._isWaitingForMemory,
22103
22454
  hasPlan: this._planReviewPort?.hasPlan(this.id) === true,
22104
22455
  mergeStatus: this.mergeStatus,
22105
22456
  mergeReason: this.mergeReason,
@@ -22180,6 +22531,7 @@ var Session = class extends EventEmitter {
22180
22531
  }
22181
22532
  async _startBody({ fresh = false, resumePrompt = null } = {}) {
22182
22533
  if (this._destroyed) return;
22534
+ this.setWaitingForMemory(false);
22183
22535
  if (this.ptyProcess) {
22184
22536
  console.warn(`[session:${this.name}] start() called while PTY exists - killing previous PTY first`);
22185
22537
  const oldPid = this.ptyProcess.pid;
@@ -22201,9 +22553,7 @@ var Session = class extends EventEmitter {
22201
22553
  }
22202
22554
  if (this.state === STATES.DORMANT) this.transition("user_start");
22203
22555
  this._receivedFirstOutput = false;
22204
- this._sleeping = false;
22205
22556
  this._clearDetectionTracking();
22206
- this._autoKilled = false;
22207
22557
  this._output.reset();
22208
22558
  this.emit("rebaseline");
22209
22559
  this._resetDetectionSources({ quiet: false });
@@ -22555,7 +22905,7 @@ var Session = class extends EventEmitter {
22555
22905
  this._receivedFirstOutput = true;
22556
22906
  this.transition("first_output");
22557
22907
  }
22558
- if (!this._sleeping) try {
22908
+ try {
22559
22909
  this._titleSource.feed(data);
22560
22910
  } catch (err) {
22561
22911
  console.error(`[session:${this.name}] title source error: ${errorMessage(err)}`);
@@ -22599,6 +22949,9 @@ var Session = class extends EventEmitter {
22599
22949
  getScreenSnapshot() {
22600
22950
  return this._output.snapshot();
22601
22951
  }
22952
+ getDeepScreenSnapshot() {
22953
+ return this._output.deepSnapshot();
22954
+ }
22602
22955
  ptySize() {
22603
22956
  const size = this._output.ptySize();
22604
22957
  return {
@@ -22759,36 +23112,9 @@ var Session = class extends EventEmitter {
22759
23112
  if (!DISMISSIBLE_STATES.has(this.state)) return false;
22760
23113
  return this.transition("user_dismiss");
22761
23114
  }
22762
- sleep() {
22763
- if (this._sleeping) return;
22764
- if (!RESTARTABLE_STATES.includes(this.state)) return;
22765
- this._sleeping = true;
22766
- this._resetDetectionSources();
22767
- this._scheduleSleepKill();
22768
- this.emit("sleep");
22769
- }
22770
- wake() {
22771
- if (this._destroyed) return;
22772
- if (!this._sleeping) return;
22773
- this._sleeping = false;
22774
- this._clearSleepKill();
22775
- this.emit("wake");
22776
- if (this._autoKilled && !this._teardownPending() && RESTARTABLE_STATES.includes(this.state)) this.restart();
22777
- }
22778
23115
  _teardownPending() {
22779
23116
  return this._pendingRestart || this._finishing;
22780
23117
  }
22781
- _scheduleSleepKill() {
22782
- this._armTimer("_sleepKillTimer", SLEEP_KILL_TIMEOUT_MS, () => {
22783
- if (!this._sleeping) return;
22784
- const wasActive = KILLABLE_STATES.includes(this.state);
22785
- this.killSession();
22786
- if (wasActive && RESTARTABLE_STATES.includes(this.state)) this._autoKilled = true;
22787
- });
22788
- }
22789
- _clearSleepKill() {
22790
- this._clearTimer("_sleepKillTimer");
22791
- }
22792
23118
  killSession() {
22793
23119
  if (!KILLABLE_STATES.includes(this.state)) return false;
22794
23120
  this.kill();
@@ -22876,7 +23202,6 @@ var Session = class extends EventEmitter {
22876
23202
  this._agentToken = null;
22877
23203
  this._commitSigningToken = null;
22878
23204
  this._pendingAttentionNotes = [];
22879
- this._clearSleepKill();
22880
23205
  this._clearPendingPaste();
22881
23206
  this._hooks.cleanup();
22882
23207
  this._disposeGuardrailsLaunch(this._guardrailsLaunch);
@@ -23007,6 +23332,20 @@ function appendTrailStep(trail, step, maxSteps = TRAIL_MAX_STEPS) {
23007
23332
  };
23008
23333
  }
23009
23334
  //#endregion
23335
+ //#region shared/contracts/guardrails-launch.ts
23336
+ var GuardrailsLaunchLogin = z.object({
23337
+ linkPath: z.string().min(1),
23338
+ targetPath: z.string().min(1)
23339
+ }).strict();
23340
+ var GuardrailsLaunchOwner = z.object({
23341
+ ownerPid: z.number().int().positive(),
23342
+ ownerHost: z.string(),
23343
+ ownerPidNamespace: z.string().nullable(),
23344
+ ownsTempDir: z.boolean(),
23345
+ logins: z.array(GuardrailsLaunchLogin),
23346
+ placeholderPaths: z.array(z.string().min(1))
23347
+ }).strict();
23348
+ //#endregion
23010
23349
  //#region server/core/claude-trust-core.ts
23011
23350
  var CLAUDE_GLOBAL_CONFIG_FILE_NAME = ".claude.json";
23012
23351
  var CLAUDE_LEGACY_CONFIG_FILE_NAME = ".config.json";
@@ -23099,6 +23438,21 @@ function decideFolderTrust(configText, repositoryRoot, platform) {
23099
23438
  };
23100
23439
  }
23101
23440
  //#endregion
23441
+ //#region server/guardrails/core/guardrails-bridge-socket-core.ts
23442
+ var LONGEST_BRIDGE_SOCKET_NAME = "claude-socks-0123456789abcdef.sock";
23443
+ var UNIX_SOCKET_PATH_BYTE_LIMIT = 107;
23444
+ var SHORT_BRIDGE_SOCKET_FOLDER = "/tmp";
23445
+ function bridgeSocketPathBytes(tempDirectory) {
23446
+ return Buffer.byteLength(path.join(tempDirectory, LONGEST_BRIDGE_SOCKET_NAME));
23447
+ }
23448
+ function hasRoomForBridgeSocket(tempDirectory, platform) {
23449
+ return platform !== "linux" || bridgeSocketPathBytes(tempDirectory) <= UNIX_SOCKET_PATH_BYTE_LIMIT;
23450
+ }
23451
+ function bridgeSocketLauncherEnv(inheritedTempDirectory, platform) {
23452
+ if (hasRoomForBridgeSocket(inheritedTempDirectory, platform)) return {};
23453
+ return { TMPDIR: SHORT_BRIDGE_SOCKET_FOLDER };
23454
+ }
23455
+ //#endregion
23102
23456
  //#region server/guardrails/guardrails-launch.ts
23103
23457
  var LAUNCH_ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
23104
23458
  var GUARDED_ENV_COMMAND = "/usr/bin/env";
@@ -23106,9 +23460,11 @@ var POLICY_FOLDER = "guardrails-policies";
23106
23460
  var TEMP_FOLDER = "guardrails-temp";
23107
23461
  var LAUNCH_HOME_FOLDER = "guardrails-homes";
23108
23462
  var START_FOLDER = "start";
23463
+ var OWNER_FILE_SUFFIX = ".owner.json";
23109
23464
  var ENTER_FOLDER_SCRIPT = "cd \"$1\" && shift && exec \"$@\"";
23110
23465
  var LOGIN_FILE_MODE = 384;
23111
23466
  var ABSENT_PATH_DENY_PLATFORM = "darwin";
23467
+ var OWN_PID_NAMESPACE_LINK = "/proc/self/ns/pid";
23112
23468
  async function isPresent(candidate) {
23113
23469
  try {
23114
23470
  await access(candidate);
@@ -23216,7 +23572,7 @@ async function lstatWhenPresent(candidate) {
23216
23572
  async function isRegularFile(candidate) {
23217
23573
  return (await lstatWhenPresent(candidate))?.isFile() ?? false;
23218
23574
  }
23219
- async function isRealDirectory(candidate) {
23575
+ async function isRealDirectory$1(candidate) {
23220
23576
  return (await lstatWhenPresent(candidate))?.isDirectory() ?? false;
23221
23577
  }
23222
23578
  async function absentPaths(candidates) {
@@ -23231,21 +23587,94 @@ async function removeLeftoverPlaceholders(placeholderPaths) {
23231
23587
  }
23232
23588
  async function createMissingMountPoints(mountPoints) {
23233
23589
  for (const mountPoint of mountPoints) {
23234
- if (!await isRealDirectory(path.dirname(mountPoint))) continue;
23590
+ if (!await isRealDirectory$1(path.dirname(mountPoint))) continue;
23235
23591
  await mkdir(mountPoint).catch((error) => {
23236
23592
  if (errorCode(error) !== "EEXIST") throw error;
23237
23593
  });
23238
23594
  }
23239
23595
  }
23240
- async function keepRefreshedLogins(links, launchId) {
23241
- for (const { linkPath, targetPath } of links.filter((link) => link.isLogin)) {
23596
+ async function isNewerThanTarget(linkPath, targetPath) {
23597
+ const targetStats = await stat(targetPath).catch(() => null);
23598
+ if (targetStats === null) return true;
23599
+ return (await lstat(linkPath)).mtimeMs > targetStats.mtimeMs;
23600
+ }
23601
+ async function keepRefreshedLogins(logins, launchId, { onlyWhenNewer }) {
23602
+ for (const { linkPath, targetPath } of logins) {
23242
23603
  if (!await isRegularFile(linkPath)) continue;
23604
+ if (onlyWhenNewer && !await isNewerThanTarget(linkPath, targetPath)) continue;
23243
23605
  const stagedPath = `${targetPath}.${launchId}`;
23244
23606
  await copyFile(linkPath, stagedPath);
23245
23607
  await chmod(stagedPath, LOGIN_FILE_MODE);
23246
23608
  await rename(stagedPath, targetPath);
23247
23609
  }
23248
23610
  }
23611
+ async function removeLaunch(folders, owner, launchId, { onlyNewerLogins }) {
23612
+ await rm(folders.policyPath, { force: true });
23613
+ await removeLeftoverPlaceholders(owner.placeholderPaths);
23614
+ await keepRefreshedLogins(owner.logins.filter((login) => isPathInside(folders.launchHomeDir, login.linkPath, { allowEqual: false })), launchId, { onlyWhenNewer: onlyNewerLogins });
23615
+ await rm(folders.launchHomeDir, {
23616
+ recursive: true,
23617
+ force: true
23618
+ });
23619
+ if (folders.ownedTempDir !== null) await rm(folders.ownedTempDir, {
23620
+ recursive: true,
23621
+ force: true
23622
+ });
23623
+ await rm(folders.ownerPath, { force: true });
23624
+ }
23625
+ function isProcessAlive$1(pid) {
23626
+ try {
23627
+ process.kill(pid, 0);
23628
+ return true;
23629
+ } catch (error) {
23630
+ return errorCode(error) === "EPERM";
23631
+ }
23632
+ }
23633
+ function readOwnPidNamespace() {
23634
+ return readlink(OWN_PID_NAMESPACE_LINK).catch(() => null);
23635
+ }
23636
+ async function readOwnerFile(ownerPath) {
23637
+ const outcome = await loadJsonStateFile({
23638
+ filePath: ownerPath,
23639
+ quarantine: false,
23640
+ parse: (raw) => GuardrailsLaunchOwner.safeParse(raw).data ?? null
23641
+ });
23642
+ return outcome.status === "loaded" ? outcome.value : null;
23643
+ }
23644
+ async function sweepOrphanedGuardrailsLaunches(log = console) {
23645
+ try {
23646
+ const policyDir = path.join(glimmervoidHomeDir$1(), POLICY_FOLDER);
23647
+ const entryNames = await readdir(policyDir).catch((error) => {
23648
+ if (isMissingFileError(error)) return [];
23649
+ throw error;
23650
+ });
23651
+ const glimmervoidHome = await realpath(glimmervoidHomeDir$1());
23652
+ const sweeperPidNamespace = await readOwnPidNamespace();
23653
+ let removedLaunchCount = 0;
23654
+ for (const entryName of entryNames) {
23655
+ if (!entryName.endsWith(OWNER_FILE_SUFFIX)) continue;
23656
+ const launchId = entryName.slice(0, -11);
23657
+ if (!LAUNCH_ID_SHAPE.test(launchId)) continue;
23658
+ const ownerPath = path.join(policyDir, entryName);
23659
+ const owner = await readOwnerFile(ownerPath);
23660
+ if (owner === null || owner.ownerHost !== os.hostname() || owner.ownerPidNamespace !== sweeperPidNamespace || isProcessAlive$1(owner.ownerPid)) continue;
23661
+ try {
23662
+ await removeLaunch({
23663
+ policyPath: path.join(policyDir, `${launchId}.json`),
23664
+ ownerPath,
23665
+ launchHomeDir: path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId),
23666
+ ownedTempDir: owner.ownsTempDir ? path.join(glimmervoidHome, TEMP_FOLDER, launchId) : null
23667
+ }, owner, launchId, { onlyNewerLogins: true });
23668
+ removedLaunchCount += 1;
23669
+ } catch (error) {
23670
+ log.warn(`[guardrails] could not remove the leftover launch ${launchId}: ${errorMessage(error)}`);
23671
+ }
23672
+ }
23673
+ if (removedLaunchCount > 0) log.log(`[guardrails] removed ${removedLaunchCount} launch folders left by sessions that ended without a clean shutdown`);
23674
+ } catch (error) {
23675
+ log.warn(`[guardrails] could not sweep leftover launch folders: ${errorMessage(error)}`);
23676
+ }
23677
+ }
23249
23678
  function launcherCommand(plan, policyPath) {
23250
23679
  if (plan.launcher === "open-network") return {
23251
23680
  file: process.execPath,
@@ -23290,10 +23719,11 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23290
23719
  recursive: true,
23291
23720
  mode: 448
23292
23721
  });
23293
- const [homeDir, glimmervoidHome, realRole] = await Promise.all([
23722
+ const [homeDir, glimmervoidHome, realRole, ownerPidNamespace] = await Promise.all([
23294
23723
  realpath(os.homedir()),
23295
23724
  realpath(glimmervoidHomeDir$1()),
23296
- withRealRoots(role)
23725
+ withRealRoots(role),
23726
+ readOwnPidNamespace()
23297
23727
  ]);
23298
23728
  const ownsTempDir = tempDir === void 0;
23299
23729
  const launchTempDir = tempDir === void 0 ? path.join(glimmervoidHome, TEMP_FOLDER, launchId) : path.resolve(tempDir);
@@ -23342,8 +23772,25 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23342
23772
  if (startDir !== void 0) await mkdir(startDir, { recursive: true });
23343
23773
  await Promise.all(plan.launchHomeLinks.map((link) => symlink(link.targetPath, link.linkPath)));
23344
23774
  await writeJsonAtomic(policyPath, plan.policy);
23775
+ const folders = {
23776
+ policyPath,
23777
+ ownerPath: path.join(policyDir, `${launchId}${OWNER_FILE_SUFFIX}`),
23778
+ launchHomeDir,
23779
+ ownedTempDir: ownsTempDir ? launchTempDir : null
23780
+ };
23781
+ const owner = {
23782
+ ownerPid: process.pid,
23783
+ ownerHost: os.hostname(),
23784
+ ownerPidNamespace,
23785
+ ownsTempDir,
23786
+ logins: plan.launchHomeLinks.filter((link) => link.isLogin).map(({ linkPath, targetPath }) => ({
23787
+ linkPath,
23788
+ targetPath
23789
+ })),
23790
+ placeholderPaths: []
23791
+ };
23792
+ await writeJsonAtomic(folders.ownerPath, owner);
23345
23793
  const envPrefix = guardedEnvPrefix(plan);
23346
- let placeholderPathsAbsentAtWrap = [];
23347
23794
  return {
23348
23795
  ok: true,
23349
23796
  launch: {
@@ -23354,7 +23801,8 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23354
23801
  ...rebuilt.plan.policy,
23355
23802
  allowPty: hasTerminal && rebuilt.plan.launcher === "open-network"
23356
23803
  });
23357
- placeholderPathsAbsentAtWrap = await absentPaths(guardrailsPlaceholderPaths(realRole));
23804
+ owner.placeholderPaths = await absentPaths(guardrailsPlaceholderPaths(realRole));
23805
+ await writeJsonAtomic(folders.ownerPath, owner);
23358
23806
  await createMissingMountPoints(lockedClaudeMountPoints(realRole));
23359
23807
  return {
23360
23808
  file: launcher.file,
@@ -23365,21 +23813,12 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23365
23813
  ]
23366
23814
  };
23367
23815
  },
23368
- env: { ...plan.launchEnv },
23816
+ env: {
23817
+ ...plan.launchEnv,
23818
+ ...bridgeSocketLauncherEnv(os.tmpdir(), process.platform)
23819
+ },
23369
23820
  ...startDir === void 0 ? {} : { cwd: startDir },
23370
- dispose: async () => {
23371
- await rm(policyPath, { force: true });
23372
- await removeLeftoverPlaceholders(placeholderPathsAbsentAtWrap);
23373
- await keepRefreshedLogins(plan.launchHomeLinks, launchId);
23374
- await rm(launchHomeDir, {
23375
- recursive: true,
23376
- force: true
23377
- });
23378
- if (ownsTempDir) await rm(launchTempDir, {
23379
- recursive: true,
23380
- force: true
23381
- });
23382
- }
23821
+ dispose: () => removeLaunch(folders, owner, launchId, { onlyNewerLogins: false })
23383
23822
  }
23384
23823
  };
23385
23824
  } catch (error) {
@@ -26974,6 +27413,7 @@ function withForcedTapReporter(runCheck) {
26974
27413
  }
26975
27414
  };
26976
27415
  }
27416
+ var QUEUED_RUN_LIVENESS_POLL_MS = 1e3;
26977
27417
  async function readCheckoutSecretValues(checkoutPath, sharedEntries) {
26978
27418
  const sharedEnvFiles = sharedEntries.filter((sharedEntry) => !sharedEntry.includes("..") && path.basename(sharedEntry).startsWith(".env"));
26979
27419
  return (await Promise.all(sharedEnvFiles.map(async (sharedEnvFile) => {
@@ -26985,7 +27425,33 @@ async function readCheckoutSecretValues(checkoutPath, sharedEntries) {
26985
27425
  }
26986
27426
  }))).flatMap(envFileSecretValues);
26987
27427
  }
26988
- async function runChecksInCleanCheckout({ projectPath, sha, commands, writeScopes, sharedEntries, gitWorkspace, baseEnv, secretValues, signal, isLive = () => true, prepareCheckRunner = prepareGuardedCheckRunner, rerunFailedChecks = false, writeFailedCheckOutputFile = async () => null }) {
27428
+ async function runChecksInCleanCheckout(options) {
27429
+ const { signal, isLive = () => true, checkRunGate = null, onMemoryWait } = options;
27430
+ if (!checkRunGate) return runChecksInStagedCheckout(options);
27431
+ const isStillWanted = () => isLive() && !signal.aborted;
27432
+ const livenessLost = new AbortController();
27433
+ const livenessPoll = setInterval(() => {
27434
+ if (!isLive()) livenessLost.abort();
27435
+ }, QUEUED_RUN_LIVENESS_POLL_MS);
27436
+ const stopLivenessPoll = () => clearInterval(livenessPoll);
27437
+ try {
27438
+ return await checkRunGate.run(async () => {
27439
+ stopLivenessPoll();
27440
+ if (!isStillWanted()) return null;
27441
+ return runChecksInStagedCheckout(options);
27442
+ }, {
27443
+ isStillWanted,
27444
+ onMemoryWait,
27445
+ signal: AbortSignal.any([signal, livenessLost.signal])
27446
+ });
27447
+ } catch (error) {
27448
+ if (isQueueAdmissionAbandoned(error)) return null;
27449
+ throw error;
27450
+ } finally {
27451
+ stopLivenessPoll();
27452
+ }
27453
+ }
27454
+ async function runChecksInStagedCheckout({ projectPath, sha, commands, writeScopes, sharedEntries, gitWorkspace, baseEnv, secretValues, signal, isLive = () => true, prepareCheckRunner = prepareGuardedCheckRunner, rerunFailedChecks = false, writeFailedCheckOutputFile = async () => null }) {
26989
27455
  const checkoutParent = await makeHostScratchDirectory("check-");
26990
27456
  const checkoutPath = path.join(checkoutParent, "checkout");
26991
27457
  const env = buildFactoryCheckEnv(baseEnv, process.platform);
@@ -27309,7 +27775,7 @@ async function runFactoryReview(options) {
27309
27775
  };
27310
27776
  return review.value;
27311
27777
  }
27312
- function createFactoryCloseOut({ config, spawnReviewer, gitWorkspace, serializeProject, ensureLedger, runCoherence, commitAndLand, readIntegrationSha, recordFactoryLandedSha, readPaused, readIntentFloorAllowances = async () => [], countTicketBlock = createMemoryCapCounter(), trustVerification, notifyOrchestrator, setException, clearException = () => {}, recordDecision = async () => {}, recordConfirmedFlakes = () => {}, recordScopeNeeds = createMemoryScopeNeedRecorder(), onReviewingChanged = () => {}, baseEnv = process.env, prepareCheckRunner = prepareGuardedCheckRunner }) {
27778
+ function createFactoryCloseOut({ config, spawnReviewer, gitWorkspace, serializeProject, ensureLedger, runCoherence, commitAndLand, readIntegrationSha, recordFactoryLandedSha, readPaused, readIntentFloorAllowances = async () => [], countTicketBlock = createMemoryCapCounter(), trustVerification, notifyOrchestrator, setException, clearException = () => {}, recordDecision = async () => {}, recordConfirmedFlakes = () => {}, recordScopeNeeds = createMemoryScopeNeedRecorder(), onReviewingChanged = () => {}, baseEnv = process.env, prepareCheckRunner = prepareGuardedCheckRunner, checkRunGate = null, onCheckMemoryWait = () => {} }) {
27313
27779
  const reviewing = /* @__PURE__ */ new Set();
27314
27780
  const attempts = /* @__PURE__ */ new Map();
27315
27781
  const failingFilesOutsideScopesByWork = /* @__PURE__ */ new Map();
@@ -27498,7 +27964,9 @@ function createFactoryCloseOut({ config, spawnReviewer, gitWorkspace, serializeP
27498
27964
  isLive: () => isLive(worker),
27499
27965
  prepareCheckRunner,
27500
27966
  rerunFailedChecks,
27501
- writeFailedCheckOutputFile: (outputText) => writeFailedCheckOutputFile(worker, outputText)
27967
+ writeFailedCheckOutputFile: (outputText) => writeFailedCheckOutputFile(worker, outputText),
27968
+ checkRunGate,
27969
+ onMemoryWait: (isWaiting) => onCheckMemoryWait(worker.projectId, isWaiting)
27502
27970
  });
27503
27971
  }
27504
27972
  async function listInheritedCheckCommands(worker, baseSha, headRun) {
@@ -28762,7 +29230,7 @@ function createFactoryDispatch({ config, sessions, makeSession, wireSessionEvent
28762
29230
  allowCommands: [
28763
29231
  "coherence context",
28764
29232
  "rm",
28765
- ...checks,
29233
+ ...workerChecksOf(checks),
28766
29234
  ...profile.focusedTestCommand ? [profile.focusedTestCommand] : []
28767
29235
  ],
28768
29236
  extraDeny: FACTORY_WORKER_DENY
@@ -29520,10 +29988,11 @@ function createFactoryPoller({ shouldHoldCheckout = () => false, beforeTick = ()
29520
29988
  };
29521
29989
  try {
29522
29990
  paused = await readPaused(project.id);
29523
- if (!await hasCoherenceConfigAt(project.path, headSha)) return null;
29524
- const confirmedFlakesByIntent = await readIntentConfirmedFlakes(project.id);
29525
29991
  const previous = processedProjects.get(project.id);
29526
- if (previous?.path === project.path && (previous.state.headSha === headSha || shouldHoldCheckout(project.id))) return {
29992
+ const isUnchangedTip = previous?.path === project.path && previous.state.headSha === headSha;
29993
+ if (!isUnchangedTip && !await hasCoherenceConfigAt(project.path, headSha)) return null;
29994
+ const confirmedFlakesByIntent = await readIntentConfirmedFlakes(project.id);
29995
+ if (previous?.path === project.path && (isUnchangedTip || shouldHoldCheckout(project.id))) return {
29527
29996
  ...previous.state,
29528
29997
  paused,
29529
29998
  projectName: project.name,
@@ -29957,92 +30426,12 @@ function createFactoryVerifier({ config, spawnVerifier, serializeProject, ensure
29957
30426
  };
29958
30427
  }
29959
30428
  //#endregion
29960
- //#region server/factory/core/factory-setup-core.ts
29961
- var PACKAGE_MANAGERS = [
29962
- {
29963
- lockfiles: ["npm-shrinkwrap.json", "package-lock.json"],
29964
- prefix: "npm run",
29965
- test: "npm test"
29966
- },
29967
- {
29968
- lockfiles: ["pnpm-lock.yaml"],
29969
- prefix: "pnpm run",
29970
- test: "pnpm run test"
29971
- },
29972
- {
29973
- lockfiles: ["yarn.lock"],
29974
- prefix: "yarn",
29975
- test: "yarn test"
29976
- },
29977
- {
29978
- lockfiles: ["bun.lockb", "bun.lock"],
29979
- prefix: "bun run",
29980
- test: "bun run test"
29981
- }
29982
- ];
29983
- var FOCUSED_RUNNERS = [
29984
- {
29985
- dependency: "vitest",
29986
- command: "npx vitest run"
29987
- },
29988
- {
29989
- dependency: "jest",
29990
- command: "npx jest"
29991
- },
29992
- {
29993
- dependency: "mocha",
29994
- command: "npx mocha"
29995
- }
29996
- ];
29997
- function detectFactorySetup({ scripts, lockfiles, dependencies, globalChecks }) {
29998
- const packageManager = PACKAGE_MANAGERS.find((manager) => manager.lockfiles.some((lockfile) => lockfiles.includes(lockfile))) ?? PACKAGE_MANAGERS[0];
29999
- const hasScript = (name) => typeof scripts?.[name] === "string" && String(scripts[name]).trim().length > 0;
30000
- return {
30001
- checks: globalChecks === void 0 ? [
30002
- "typecheck",
30003
- "lint",
30004
- "test"
30005
- ].filter(hasScript).map((name) => name === "test" ? packageManager.test : `${packageManager.prefix} ${name}`) : [...globalChecks],
30006
- focusedTestCommandCandidates: [...hasScript("test:files") ? [`${packageManager.prefix} test:files`] : [], ...FOCUSED_RUNNERS.filter((runner) => dependencies.includes(runner.dependency)).map((runner) => runner.command)],
30007
- widenableScopes: [...DEFAULT_WIDENABLE_SCOPES]
30008
- };
30009
- }
30010
- var TEST_RESULT_LINE = /^[ \t]*(?:not )?ok \d+\b|^# tests \d+\b/m;
30011
- var SHELL_NOT_FOUND_LINE = /^(?:\/[^\s:]*\/)?(?:sh|bash|dash|ash|zsh)(?:: (?:line )?\d+)?: [^\n]*?: (?:command )?not found/m;
30012
- var LAUNCH_FAILURE_LINE = /(?:^|\n)(?:Error: )?(?:spawn[^\n]*\b(?:ENOENT|EACCES|EPERM|failed|error)\b|(?:npm (?:error|ERR!) )?missing script:|(?:error )?(?:script|command)[^\n]*(?:not found|does not exist|not recognized)|Check command contains shell syntax or is empty)/im;
30013
- function classifyFactorySetupCheck(check) {
30014
- const hasLaunchFailure = !check.pass && !TEST_RESULT_LINE.test(check.output) && (SHELL_NOT_FOUND_LINE.test(check.output) || LAUNCH_FAILURE_LINE.test(check.output));
30015
- return {
30016
- ...check,
30017
- status: hasLaunchFailure ? "could-not-run" : "ran"
30018
- };
30019
- }
30020
- function decideFactorySetupProof(proposal, fullChecks, focusedChecks) {
30021
- const checks = [...new Set(proposal.checks)].filter((command) => fullChecks.some((check) => check.command === command && check.status === "ran"));
30022
- if (checks.length === 0) {
30023
- const failures = fullChecks.map((check) => `${check.command}: ${check.output}`).join("\n");
30024
- return {
30025
- ok: false,
30026
- reason: `Factory setup could not run any full checks.${failures ? `\n${failures}` : " No check commands were detected."}`
30027
- };
30028
- }
30029
- const focusedTestCommand = proposal.focusedTestCommandCandidates.find((command) => focusedChecks.some((check) => check.command === command && check.pass));
30030
- return {
30031
- ok: true,
30032
- profile: {
30033
- checks,
30034
- widenableScopes: [...proposal.widenableScopes],
30035
- ...focusedTestCommand ? { focusedTestCommand } : {}
30036
- }
30037
- };
30038
- }
30039
- //#endregion
30040
30429
  //#region server/factory/factory-setup.ts
30041
30430
  var setupAgentProfileSchema = ProjectConfig.shape.factoryProfile.unwrap().pick({
30042
30431
  checks: true,
30043
30432
  focusedTestCommand: true
30044
30433
  });
30045
- function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, gitWorkspace, saveProjectFactoryProfile, recordBaseline = async () => {}, spawnSetupAgent, model = () => null, prepareCheckRunner, baseEnv = process.env, secretValues = [] }) {
30434
+ function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, gitWorkspace, saveProjectFactoryProfile, recordBaseline = async () => {}, spawnSetupAgent, model = () => null, prepareCheckRunner, checkRunGate = null, baseEnv = process.env, secretValues = [] }) {
30046
30435
  const pending = /* @__PURE__ */ new Map();
30047
30436
  const controller = new AbortController();
30048
30437
  async function setupProject(projectId, projectPath) {
@@ -30087,7 +30476,8 @@ function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, g
30087
30476
  baseEnv,
30088
30477
  secretValues,
30089
30478
  signal: controller.signal,
30090
- prepareCheckRunner
30479
+ prepareCheckRunner,
30480
+ checkRunGate
30091
30481
  });
30092
30482
  if (!run) throw new Error("Factory setup was interrupted");
30093
30483
  for (const check of run.checks) checkProofs.set(check.command, classifyFactorySetupCheck({
@@ -30205,10 +30595,11 @@ function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, g
30205
30595
  //#region server/factory/factory-wiring.ts
30206
30596
  var COHERENCE_CONFIG_PROBE_TIMEOUT_MS = 5e3;
30207
30597
  var ORCHESTRATOR_EXCEPTION_PREFIX = "factory-exception";
30598
+ var CHECK_RUN_HELD_FOR_MEMORY_NOTE = "Waiting for memory to run checks";
30208
30599
  function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorkspace(), homeDir = glimmervoidHomeDir$1(), createPoller = createFactoryPoller, log = console, telemetry, orchestratorOptions, trustClaudeFolder, spawnReviewer, prepareCheckRunner, saveProjectFactoryProfile, spawnVerifier = spawnReviewer, readSpentTodayUsd = () => ({
30209
30600
  status: "known",
30210
30601
  amountUsd: 0
30211
- }), notify = () => {}, runCoherence: customRunCoherence, ...pollerDeps }) {
30602
+ }), notify = () => {}, runCoherence: customRunCoherence, checkRunGate = null, ...pollerDeps }) {
30212
30603
  gitWorkspace = isolateGitWorkspace(gitWorkspace, { disableRepoCommands: true });
30213
30604
  const coherenceCliPath = resolvePackageBin("@danilocampos/coherence", "coherence");
30214
30605
  const coherenceHookCliPath = resolvePackageBin("@danilocampos/coherence", "coherence-hook");
@@ -30297,6 +30688,7 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
30297
30688
  sharedEntries: () => config.worktreeShare ?? DEFAULT_CONFIG.worktreeShare,
30298
30689
  gitWorkspace,
30299
30690
  prepareCheckRunner,
30691
+ checkRunGate,
30300
30692
  spawnSetupAgent: spawnReviewer,
30301
30693
  model: () => config.factory?.reviewerModel ?? null,
30302
30694
  secretValues: factorySecretValues(process.env),
@@ -31024,6 +31416,17 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
31024
31416
  };
31025
31417
  const refresh = refreshAfter("state");
31026
31418
  const refusalsByWork = /* @__PURE__ */ new Map();
31419
+ const projectsWithCheckRunHeldForMemory = /* @__PURE__ */ new Set();
31420
+ const noteCheckRunHeldForMemory = (projectId, isWaiting) => {
31421
+ if (isWaiting === projectsWithCheckRunHeldForMemory.has(projectId)) return;
31422
+ if (isWaiting) projectsWithCheckRunHeldForMemory.add(projectId);
31423
+ if (!isWaiting) projectsWithCheckRunHeldForMemory.delete(projectId);
31424
+ refresh();
31425
+ };
31426
+ const withCheckMemoryNote = (projectId, note) => {
31427
+ if (!projectsWithCheckRunHeldForMemory.has(projectId)) return note;
31428
+ return note ? `${note}\n${CHECK_RUN_HELD_FOR_MEMORY_NOTE}` : CHECK_RUN_HELD_FOR_MEMORY_NOTE;
31429
+ };
31027
31430
  const notifyOrchestrator = (projectId, event) => {
31028
31431
  const refusalKey = JSON.stringify([projectId, event.workId]);
31029
31432
  const shouldRecord = event.event !== "refused" || refusalsByWork.get(refusalKey)?.reason !== (event.detail ?? null);
@@ -31071,6 +31474,8 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
31071
31474
  readIntentFloorAllowances,
31072
31475
  countTicketBlock,
31073
31476
  prepareCheckRunner,
31477
+ checkRunGate,
31478
+ onCheckMemoryWait: noteCheckRunHeldForMemory,
31074
31479
  recordDecision: decisionLog.record,
31075
31480
  recordScopeNeeds: async (worker, files) => {
31076
31481
  let rememberedNeeds = [];
@@ -31231,6 +31636,7 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
31231
31636
  const factoryProfile = resolveFactoryProfile(config.factory, config.projects?.find((entry) => entry.id === project.projectId));
31232
31637
  return {
31233
31638
  ...updated,
31639
+ note: withCheckMemoryNote(project.projectId, updated.note),
31234
31640
  reviewing,
31235
31641
  liveWorkers,
31236
31642
  factoryProfile: {
@@ -32235,13 +32641,12 @@ function branchGcShouldStart(config) {
32235
32641
  function branchGcCfgKey(config) {
32236
32642
  return JSON.stringify(config.branchGc ?? null);
32237
32643
  }
32238
- function createBranchGcWiring({ config, gitWorkspace, liveSessionIds, liveWorktreePaths, broadcast = () => {}, log = console, decisionTrace = (entry) => log.info(`[branch-gc] decision ${JSON.stringify(entry)}`), createPoller = createBranchGcPoller }) {
32644
+ function createBranchGcWiring({ config, gitWorkspace, liveSessionIds, liveWorktreePaths, log = console, decisionTrace = (entry) => log.info(`[branch-gc] decision ${JSON.stringify(entry)}`), createPoller = createBranchGcPoller }) {
32239
32645
  const runner = createLaneRunner({
32240
32646
  tag: "branch-gc",
32241
32647
  gate: () => branchGcShouldStart(config),
32242
32648
  cfgKey: () => branchGcCfgKey(config),
32243
32649
  emptyStatus: () => emptyLaneStatus("branch-gc-status", branchGcShouldStart(config)),
32244
- broadcast,
32245
32650
  createPoller: ({ onTickComplete }) => createPoller({
32246
32651
  gitWorkspace,
32247
32652
  getConfig: () => config,
@@ -32262,8 +32667,7 @@ function createBranchGcWiring({ config, gitWorkspace, liveSessionIds, liveWorktr
32262
32667
  return {
32263
32668
  start: runner.startPoller,
32264
32669
  stop: runner.stopPoller,
32265
- restartIfConfigChanged: runner.restartIfConfigChanged,
32266
- getStatus: runner.getStatus
32670
+ restartIfConfigChanged: runner.restartIfConfigChanged
32267
32671
  };
32268
32672
  }
32269
32673
  //#endregion
@@ -32724,6 +33128,9 @@ function buildContextDigest(store, { scopes = null, budgetChars = DEFAULT_DIGEST
32724
33128
  if (lines.length === 1) return "";
32725
33129
  return lines.join("\n");
32726
33130
  }
33131
+ //#endregion
33132
+ //#region server/ingest/core/ingest-fs-core.ts
33133
+ var SOURCE$3 = "fs";
32727
33134
  var KIND = "file-change";
32728
33135
  var DEFAULT_BATCH_MS = SOURCE_DEFAULTS.fs.batchMs;
32729
33136
  var MAX_FILES_PER_BATCH = SOURCE_DEFAULTS.fs.digestQuota;
@@ -32773,15 +33180,13 @@ var DAEMON_CONFIG_SIBLINGS = Object.freeze([
32773
33180
  "usage-lanes.json",
32774
33181
  "usage-warehouse.json",
32775
33182
  "usage-budget-state.json",
32776
- "pr-review-state.json",
32777
33183
  "team-review-state.json",
32778
33184
  "pairings.json",
32779
33185
  "pairings-seen.json",
32780
33186
  "litellm-pricing.json",
32781
33187
  "update-check.json",
32782
33188
  "recordings",
32783
- "uploads",
32784
- "memory"
33189
+ "uploads"
32785
33190
  ]);
32786
33191
  var ACTIVE_SESSION_STATES = Object.freeze([
32787
33192
  "INITIALIZING",
@@ -32964,7 +33369,7 @@ function batchSize(batch) {
32964
33369
  }
32965
33370
  function fileChangeEvent({ root, relPath, change, now }) {
32966
33371
  return {
32967
- source: "fs",
33372
+ source: SOURCE$3,
32968
33373
  kind: KIND,
32969
33374
  ts: now,
32970
33375
  scope: {
@@ -33019,7 +33424,7 @@ function decideFsEvents(batch, { root = null, now = 0 } = {}) {
33019
33424
  }
33020
33425
  const { summary, counts } = burstSummary(batch);
33021
33426
  return [{
33022
- source: "fs",
33427
+ source: SOURCE$3,
33023
33428
  kind: KIND,
33024
33429
  ts: now,
33025
33430
  scope: {
@@ -33072,6 +33477,7 @@ function drainBatchLog(state) {
33072
33477
  //#endregion
33073
33478
  //#region server/ingest/core/ingest-tail-core.ts
33074
33479
  var MAX_CATCH_UP_BYTES = 262144;
33480
+ var DEFAULT_MAX_TRACKED$1 = 256;
33075
33481
  var LISTING_SETTLE_MS = 2e3;
33076
33482
  function fileIdentity(stat) {
33077
33483
  return `${Math.floor(coercedNumberOr(stat?.ino, 0))}:${Math.floor(coercedNumberOr(stat?.birthtimeMs, 0))}`;
@@ -33181,13 +33587,18 @@ function applyRead(state, { text = "", end = 0, stat = null, head = null, reset
33181
33587
  if (stat) state.identity = fileIdentity(stat);
33182
33588
  return split.lines;
33183
33589
  }
33590
+ function isTailCurrent(state, stat) {
33591
+ if (!stat || fileIdentity(stat) !== state.identity) return false;
33592
+ if (coercedNumberOr(stat.size, -1) !== state.size || state.offset < state.size) return false;
33593
+ return coercedNumberOr(stat.mtimeMs, -1) === state.mtimeMs;
33594
+ }
33184
33595
  function isActiveMtime(mtimeMs, { now, withinMs }) {
33185
33596
  return coercedNumberOr(mtimeMs, 0) >= coercedNumberOr(now, 0) - Math.max(0, coercedNumberOr(withinMs, 0));
33186
33597
  }
33187
33598
  function canTrustCachedListing({ mtimeMs, listedAtMs } = {}) {
33188
33599
  return coercedNumberOr(listedAtMs, 0) - coercedNumberOr(mtimeMs, 0) >= LISTING_SETTLE_MS;
33189
33600
  }
33190
- function pickStaleByMtime(entriesByKey, { maxTracked = 256 } = {}) {
33601
+ function pickStaleByMtime(entriesByKey, { maxTracked = DEFAULT_MAX_TRACKED$1 } = {}) {
33191
33602
  const entries = [...entriesByKey.entries()];
33192
33603
  const bound = Math.max(1, Math.floor(maxTracked));
33193
33604
  if (entries.length <= bound) return [];
@@ -34397,6 +34808,9 @@ function createFsIngest({ publish, sourceConfig = {}, configPath = null, logger
34397
34808
  }
34398
34809
  };
34399
34810
  }
34811
+ //#endregion
34812
+ //#region server/ingest/core/ingest-git-core.ts
34813
+ var SOURCE$1 = "git";
34400
34814
  var DEFAULT_DEBOUNCE_MS = SOURCE_DEFAULTS.git.debounceMs;
34401
34815
  var DEFAULT_POLL_MS$1 = SOURCE_DEFAULTS.git.pollMs;
34402
34816
  var CLEAN_SIGNATURE = "clean";
@@ -34581,7 +34995,7 @@ function subjectSuffix(commit) {
34581
34995
  function commitEvent({ status, commit, root = null, now = 0 }) {
34582
34996
  const sha = shortSha$1(commit?.sha || status.oid) || null;
34583
34997
  return {
34584
- source: "git",
34998
+ source: SOURCE$1,
34585
34999
  kind: "commit",
34586
35000
  ts: now,
34587
35001
  scope: {
@@ -34601,7 +35015,7 @@ function branchChangeEvent({ status, commit, root = null, now = 0 }) {
34601
35015
  const sha = shortSha$1(commit?.sha || status.oid) || null;
34602
35016
  const at = sha ? ` at ${sha}` : "";
34603
35017
  return {
34604
- source: "git",
35018
+ source: SOURCE$1,
34605
35019
  kind: "branch-change",
34606
35020
  ts: now,
34607
35021
  scope: {
@@ -34624,7 +35038,7 @@ function statusChangeEvent({ status, root = null, now = 0 }) {
34624
35038
  if (counts.untracked) parts.push(`${counts.untracked} untracked`);
34625
35039
  if (counts.conflicted) parts.push(`${counts.conflicted} conflicted`);
34626
35040
  return {
34627
- source: "git",
35041
+ source: SOURCE$1,
34628
35042
  kind: "status-change",
34629
35043
  ts: now,
34630
35044
  scope: {
@@ -35081,10 +35495,11 @@ function createGitIngest({ publish, sourceConfig = {}, reposProvider = null, log
35081
35495
  //#region server/ingest/ingest-shell-history.ts
35082
35496
  var DEFAULT_POLL_MS = 2e3;
35083
35497
  var DEFAULT_DISCOVER_MS = 3e4;
35498
+ var DEFAULT_MAX_TRACKED = 8;
35084
35499
  var DEFAULT_MAX_COMMANDS_PER_DRAIN = 50;
35085
35500
  var WATCH_POLL_DEBOUNCE_MS = 100;
35086
35501
  var WATCH_SWEEP_DEBOUNCE_MS = 500;
35087
- function createShellHistoryIngest({ publish, sourceConfig = {}, logger = console, env = process.env, homeDir = os.homedir(), platform = process.platform, fsPromises = fs.promises, watchFn = fs.watch, nowFn = Date.now, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, discoverIntervalMs = DEFAULT_DISCOVER_MS, maxTrackedFiles = 8, maxCommandsPerDrain = DEFAULT_MAX_COMMANDS_PER_DRAIN, maxCatchUpBytes = MAX_CATCH_UP_BYTES } = {}) {
35502
+ function createShellHistoryIngest({ publish, sourceConfig = {}, logger = console, env = process.env, homeDir = os.homedir(), platform = process.platform, fsPromises = fs.promises, watchFn = fs.watch, nowFn = Date.now, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, discoverIntervalMs = DEFAULT_DISCOVER_MS, maxTrackedFiles = DEFAULT_MAX_TRACKED, maxCommandsPerDrain = DEFAULT_MAX_COMMANDS_PER_DRAIN, maxCatchUpBytes = MAX_CATCH_UP_BYTES } = {}) {
35088
35503
  if (typeof publish !== "function") throw new Error("createShellHistoryIngest requires publish");
35089
35504
  const publishEvent = publish;
35090
35505
  const pollMs = positiveIntOr(sourceConfig.pollMs, DEFAULT_POLL_MS);
@@ -36576,7 +36991,7 @@ function createTaskTitleRefiner({ getConfig, spawnLane, nowFn = () => Date.now()
36576
36991
  const request = {
36577
36992
  session,
36578
36993
  revision: session.taskTitlePromptRevision,
36579
- currentTitle: session.settledTaskTitle,
36994
+ currentTitle: session.refinedTaskTitle,
36580
36995
  recentPrompts: session.recentTaskPrompts
36581
36996
  };
36582
36997
  queuedBySession.set(session, request);
@@ -38850,24 +39265,15 @@ function resolvePosthogReportPath(issueId, reportDir = DEFAULT_POSTHOG_REPORT_DI
38850
39265
  ok: false,
38851
39266
  reason: "Invalid issue id"
38852
39267
  };
38853
- const resolvedReportDir = path.resolve(reportDir);
38854
- const htmlReportPath = confinePath(resolvedReportDir, `${rawIssueId}.html`);
38855
- const markdownReportPath = confinePath(resolvedReportDir, `${rawIssueId}.md`);
38856
- if (!htmlReportPath || !markdownReportPath) return {
39268
+ const htmlReportPath = confinePath(path.resolve(reportDir), `${rawIssueId}.html`);
39269
+ if (!htmlReportPath) return {
38857
39270
  ok: false,
38858
39271
  reason: "Invalid report path"
38859
39272
  };
38860
39273
  return {
38861
39274
  ok: true,
38862
39275
  issueId: rawIssueId,
38863
- reportDir: resolvedReportDir,
38864
- candidates: [{
38865
- format: "html",
38866
- reportPath: htmlReportPath
38867
- }, {
38868
- format: "markdown",
38869
- reportPath: markdownReportPath
38870
- }]
39276
+ reportPath: htmlReportPath
38871
39277
  };
38872
39278
  }
38873
39279
  async function readPosthogReport(issueId, { reportDir = DEFAULT_POSTHOG_REPORT_DIR } = {}) {
@@ -38878,17 +39284,22 @@ async function readPosthogReport(issueId, { reportDir = DEFAULT_POSTHOG_REPORT_D
38878
39284
  issueId: typeof issueId === "string" ? issueId : null,
38879
39285
  error: resolved.reason
38880
39286
  };
38881
- for (const candidate of resolved.candidates) try {
38882
- const content = await fs.promises.readFile(candidate.reportPath, "utf8");
39287
+ try {
39288
+ const content = await fs.promises.readFile(resolved.reportPath, "utf8");
38883
39289
  return {
38884
39290
  ok: true,
38885
39291
  found: true,
38886
39292
  issueId: resolved.issueId,
38887
- format: candidate.format,
39293
+ format: "html",
38888
39294
  content
38889
39295
  };
38890
39296
  } catch (err) {
38891
- if (isMissingFileError(err, { includeNotDir: false })) continue;
39297
+ if (isMissingFileError(err, { includeNotDir: false })) return {
39298
+ ok: true,
39299
+ found: false,
39300
+ issueId: resolved.issueId,
39301
+ message: "Report not found"
39302
+ };
38892
39303
  return {
38893
39304
  ok: false,
38894
39305
  found: false,
@@ -38896,12 +39307,6 @@ async function readPosthogReport(issueId, { reportDir = DEFAULT_POSTHOG_REPORT_D
38896
39307
  error: "Could not read report"
38897
39308
  };
38898
39309
  }
38899
- return {
38900
- ok: true,
38901
- found: false,
38902
- issueId: resolved.issueId,
38903
- message: "Report not found"
38904
- };
38905
39310
  }
38906
39311
  //#endregion
38907
39312
  //#region server/telegram-transport.ts
@@ -39747,6 +40152,69 @@ function createRadarWiring({ config, investigationSessions, closeSessionDataClie
39747
40152
  };
39748
40153
  }
39749
40154
  //#endregion
40155
+ //#region server/memory-probe.ts
40156
+ var CGROUP_ROOT = "/sys/fs/cgroup";
40157
+ var MACOS_FREE_MEMORY_REASON = "macOS reports only never-used pages as free, so headroom is not measured there";
40158
+ function isMissingFile(error) {
40159
+ return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
40160
+ }
40161
+ async function readTextIfPresent(filePath) {
40162
+ try {
40163
+ return await readFile(filePath, "utf8");
40164
+ } catch (error) {
40165
+ if (isMissingFile(error)) return null;
40166
+ throw error;
40167
+ }
40168
+ }
40169
+ async function readCgroupLevel(cgroupPath) {
40170
+ const cgroupDirectory = path.posix.join(CGROUP_ROOT, cgroupPath);
40171
+ const [memoryHighText, memoryMaxText, memoryCurrentText] = await Promise.all([
40172
+ "memory.high",
40173
+ "memory.max",
40174
+ "memory.current"
40175
+ ].map((fileName) => readTextIfPresent(path.posix.join(cgroupDirectory, fileName))));
40176
+ return {
40177
+ limitBytes: cgroupLimitBytes(memoryHighText ?? null, memoryMaxText ?? null),
40178
+ currentBytes: parseCgroupBytes(memoryCurrentText ?? null)
40179
+ };
40180
+ }
40181
+ async function readLinuxHeadroom() {
40182
+ const [procSelfCgroupText, meminfoText] = await Promise.all([readTextIfPresent("/proc/self/cgroup"), readTextIfPresent("/proc/meminfo")]);
40183
+ const cgroupPath = procSelfCgroupText === null ? null : parseCgroupV2Path(procSelfCgroupText);
40184
+ const cgroupLevels = cgroupPath === null ? [] : await Promise.all(cgroupAncestorPaths(cgroupPath).map(readCgroupLevel));
40185
+ const hostAvailableBytes = meminfoText === null ? null : parseMemAvailableBytes(meminfoText);
40186
+ const headroomBytes = headroomFromReadings({
40187
+ cgroupLevels,
40188
+ hostAvailableBytes
40189
+ });
40190
+ if (headroomBytes === null) return {
40191
+ ok: false,
40192
+ reason: "neither cgroup v2 nor /proc/meminfo reported memory"
40193
+ };
40194
+ return {
40195
+ ok: true,
40196
+ headroomBytes
40197
+ };
40198
+ }
40199
+ async function readMemoryHeadroom() {
40200
+ if (process.platform === "darwin") return {
40201
+ ok: false,
40202
+ reason: MACOS_FREE_MEMORY_REASON
40203
+ };
40204
+ if (process.platform !== "linux") return {
40205
+ ok: true,
40206
+ headroomBytes: os.freemem()
40207
+ };
40208
+ try {
40209
+ return await readLinuxHeadroom();
40210
+ } catch (error) {
40211
+ return {
40212
+ ok: false,
40213
+ reason: errorMessage(error)
40214
+ };
40215
+ }
40216
+ }
40217
+ //#endregion
39750
40218
  //#region shared/my-pr-merge.ts
39751
40219
  var FAILING_CHECK_STATES$1 = /* @__PURE__ */ new Set(["FAILURE", "ERROR"]);
39752
40220
  function myPrMergeBlocker(pr) {
@@ -44151,6 +44619,7 @@ function createUsageScanner(deps = {}) {
44151
44619
  let lastIoFailures = 0;
44152
44620
  let activePass = null;
44153
44621
  let pendingForce = false;
44622
+ let pendingScopeDirs = null;
44154
44623
  let isReportDirty = true;
44155
44624
  let resolutionError = null;
44156
44625
  const cachedRollupsByDays = /* @__PURE__ */ new Map();
@@ -44702,7 +45171,7 @@ function createUsageScanner(deps = {}) {
44702
45171
  resolutionError
44703
45172
  };
44704
45173
  }
44705
- async function runPassInternal({ force }) {
45174
+ async function runPassInternal({ force, scopeDirs = null }) {
44706
45175
  const startedAt = nowFn();
44707
45176
  let parsedLineCount = 0;
44708
45177
  let newEntryCount = 0;
@@ -44727,7 +45196,11 @@ function createUsageScanner(deps = {}) {
44727
45196
  claudeDirs = resolved.dirs;
44728
45197
  resolutionError = resolved.error;
44729
45198
  ioFailures += resolved.ioFailures;
44730
- const vendorRoots = await resolveVendorRootsAsync({
45199
+ const scopedRoots = force || !isHistoryCaughtUp ? null : scopedClaudeRoots(scopeDirs, claudeDirs);
45200
+ const vendorRoots = scopedRoots ? {
45201
+ roots: [],
45202
+ ioFailures: 0
45203
+ } : await resolveVendorRootsAsync({
44731
45204
  fsPromises,
44732
45205
  env,
44733
45206
  homeDir,
@@ -44735,16 +45208,16 @@ function createUsageScanner(deps = {}) {
44735
45208
  laneLog
44736
45209
  });
44737
45210
  ioFailures += vendorRoots.ioFailures;
44738
- const roots = [...claudeDirs.map((dir) => ({
45211
+ const roots = scopedRoots ?? [...claudeDirs.map((dir) => ({
44739
45212
  vendor: "claude",
44740
45213
  dir,
44741
45214
  kind: "active"
44742
45215
  })), ...vendorRoots.roots];
44743
- dirs = roots.map((root) => root.dir);
45216
+ if (!scopedRoots) dirs = roots.map((root) => root.dir);
44744
45217
  const walked = await walkSourceFiles(roots, fsPromises, laneLog);
44745
45218
  const files = walked.files;
44746
45219
  ioFailures += walked.ioFailures;
44747
- lastFileCount = files.length;
45220
+ if (!scopedRoots) lastFileCount = files.length;
44748
45221
  for (const file of files) {
44749
45222
  const remainingBudget = byteBudget - bytesReadThisPass;
44750
45223
  if (remainingBudget <= 0) {
@@ -44816,25 +45289,36 @@ function createUsageScanner(deps = {}) {
44816
45289
  generationRollup: Array.from(passGenerationRollup.values())
44817
45290
  };
44818
45291
  }
44819
- async function runPassChain({ force }) {
45292
+ async function runPassChain({ force, scopeDirs }) {
44820
45293
  try {
44821
- let result = await runPassInternal({ force });
44822
- while (pendingForce) {
45294
+ let result = await runPassInternal({
45295
+ force,
45296
+ scopeDirs
45297
+ });
45298
+ while (pendingForce || pendingScopeDirs) {
45299
+ const queued = {
45300
+ force: pendingForce,
45301
+ scopeDirs: pendingForce ? null : pendingScopeDirs
45302
+ };
44823
45303
  pendingForce = false;
44824
- result = await runPassInternal({ force: true });
45304
+ pendingScopeDirs = null;
45305
+ result = await runPassInternal(queued);
44825
45306
  }
44826
45307
  return result;
44827
45308
  } finally {
44828
45309
  activePass = null;
44829
45310
  }
44830
45311
  }
44831
- function runPass({ force = false } = {}) {
45312
+ function runPass({ force = false, scopeDirs = null } = {}) {
44832
45313
  if (activePass) {
44833
- if (!force) return activePass;
44834
- pendingForce = true;
45314
+ if (force) pendingForce = true;
45315
+ if (!force && scopeDirs) pendingScopeDirs = [...pendingScopeDirs ?? [], ...scopeDirs];
44835
45316
  return activePass;
44836
45317
  }
44837
- activePass = runPassChain({ force });
45318
+ activePass = runPassChain({
45319
+ force,
45320
+ scopeDirs
45321
+ });
44838
45322
  return activePass;
44839
45323
  }
44840
45324
  const api = {
@@ -45045,12 +45529,15 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
45045
45529
  })().catch((error) => laneLog.warn("start failed", { error: errorMessage(error) }));
45046
45530
  return startPromise;
45047
45531
  }
45048
- async function runPassAndPush({ force }) {
45532
+ async function runPassAndPush({ force, scopeDirs = null }) {
45049
45533
  if (stopped || !scanner) return null;
45050
45534
  passInFlight = true;
45051
45535
  let result = null;
45052
45536
  try {
45053
- result = await scanner.runPass({ force });
45537
+ result = await scanner.runPass({
45538
+ force,
45539
+ scopeDirs
45540
+ });
45054
45541
  } catch (error) {
45055
45542
  laneLog.warn("scan pass failed", { error: errorMessage(error) });
45056
45543
  } finally {
@@ -45107,22 +45594,40 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
45107
45594
  }, delayMs);
45108
45595
  unrefTimer(continueTimer);
45109
45596
  }
45597
+ let nudgedTranscriptDirs = /* @__PURE__ */ new Set();
45598
+ function takeNudgedTranscriptDirs() {
45599
+ const scopeDirs = nudgedTranscriptDirs === null ? null : [...nudgedTranscriptDirs];
45600
+ nudgedTranscriptDirs = /* @__PURE__ */ new Set();
45601
+ return scopeDirs;
45602
+ }
45603
+ function noteNudgedTranscript(transcriptPath) {
45604
+ if (nudgedTranscriptDirs === null) return;
45605
+ if (!transcriptPath) {
45606
+ nudgedTranscriptDirs = null;
45607
+ return;
45608
+ }
45609
+ nudgedTranscriptDirs.add(path.dirname(transcriptPath));
45610
+ }
45110
45611
  const nudge = createCoalescedTimer({
45111
45612
  mode: "trailing",
45112
45613
  delayMs: NUDGE_DEBOUNCE_MS,
45113
45614
  run: () => {
45114
45615
  if (stopped || !scanner) return;
45115
45616
  if (passInFlight) {
45116
- nudgeSession();
45617
+ nudge.schedule();
45117
45618
  return;
45118
45619
  }
45119
- runPassAndPush({ force: false });
45620
+ runPassAndPush({
45621
+ force: false,
45622
+ scopeDirs: takeNudgedTranscriptDirs()
45623
+ });
45120
45624
  },
45121
45625
  setTimeoutFn,
45122
45626
  clearTimeoutFn
45123
45627
  });
45124
- function nudgeSession() {
45628
+ function nudgeSession(transcriptPath = null) {
45125
45629
  if (stopped || !scanner) return;
45630
+ noteNudgedTranscript(transcriptPath);
45126
45631
  nudge.schedule();
45127
45632
  }
45128
45633
  function getSessionsMessage() {
@@ -46264,7 +46769,17 @@ function createTraceWiring({ configPath = null, logger = console, debug = false,
46264
46769
  appendedRecordCount: recordCount
46265
46770
  };
46266
46771
  }
46772
+ async function isTranscriptUnchanged(binding) {
46773
+ if (!binding.hasOpenedTranscript || binding.bindingBeforeFirstOpen) return false;
46774
+ if (pendingRecordsBySessionId.has(binding.glimmervoidSessionId)) return false;
46775
+ try {
46776
+ return isTailCurrent(binding.tailState, await fs.promises.stat(binding.transcriptPath));
46777
+ } catch {
46778
+ return false;
46779
+ }
46780
+ }
46267
46781
  async function drainBinding(binding) {
46782
+ if (await isTranscriptUnchanged(binding)) return;
46268
46783
  const opened = await openContainedFile(binding.transcriptPath, projectsRoot());
46269
46784
  if (!opened.ok) {
46270
46785
  if (binding.bindingBeforeFirstOpen) await drainBinding(binding.bindingBeforeFirstOpen);
@@ -48155,7 +48670,16 @@ function createVisionsWiring({ debounceMs = 300, setTimeoutFn = DEFAULT_TIMER_FN
48155
48670
  //#region server/backend/backend-lanes.ts
48156
48671
  function createBackendLanes(dependencies) {
48157
48672
  const { config, makeSession, wireSessionEvents, configStore, sessions, agentSessions, guardrailsRefusal, reviewSessions, investigationSessions, closeSessionDataClients, hookRouter, getHookPort, broadcastControl, broadcastLocalControl, controlWss, options, notificationManager, telemetry, logger } = dependencies;
48158
- const spawnGate = createSpawnGate();
48673
+ const memoryAdmission = createMemoryAdmission({
48674
+ readHeadroom: () => readMemoryHeadroom(),
48675
+ reserveBytesFor: (kind) => memoryReserveBytes(configStore.config, kind),
48676
+ warn: (message) => logger.warn(message)
48677
+ });
48678
+ const spawnGate = createSpawnGate({ memoryAdmission });
48679
+ const checkRunGate = createSpawnGate({
48680
+ memoryAdmission,
48681
+ kind: "checkRun"
48682
+ });
48159
48683
  const gitWorkspace = createGitWorkspace({ rerere: config.worktreeRerere !== false });
48160
48684
  const gitWorkspaceSync = createGitWorkspaceSync();
48161
48685
  const laneLedger = createLaneLedger({
@@ -48186,7 +48710,6 @@ function createBackendLanes(dependencies) {
48186
48710
  const branchGc = createBranchGcWiring({
48187
48711
  config,
48188
48712
  gitWorkspace,
48189
- broadcast: broadcastControl,
48190
48713
  liveSessionIds: () => new Set(allLiveSessions().map((session) => session.id)),
48191
48714
  liveWorktreePaths: async () => new Set(await Promise.all(allLiveSessions().flatMap((session) => [session.worktreeDir, session.path]).filter((sessionDirectory) => Boolean(sessionDirectory)).map((sessionDirectory) => comparableDirectoryPath(sessionDirectory)))),
48192
48715
  ...options.branchGcWiringOptions || {}
@@ -48203,6 +48726,7 @@ function createBackendLanes(dependencies) {
48203
48726
  broadcast: broadcastControl,
48204
48727
  log: logger,
48205
48728
  telemetry,
48729
+ checkRunGate,
48206
48730
  saveProjectFactoryProfile: async (projectPath, profile) => {
48207
48731
  if (!await configStore.saveAsync((freshConfig) => {
48208
48732
  const project = freshConfig.projects?.find((entry) => entry.path === projectPath);
@@ -48654,6 +49178,7 @@ function createBackendLanes(dependencies) {
48654
49178
  restartServiceLanes,
48655
49179
  reviewSessions,
48656
49180
  spawnGate,
49181
+ memoryAdmission,
48657
49182
  teamReview,
48658
49183
  myPrs,
48659
49184
  issues,
@@ -48676,7 +49201,6 @@ function createBackendHealth(dependencies) {
48676
49201
  const memory = process.memoryUsage();
48677
49202
  const sessionStats = [];
48678
49203
  let alivePtyCount = 0;
48679
- let sleepingCount = 0;
48680
49204
  let totalDataListeners = 0;
48681
49205
  let totalOutputBufferBytes = 0;
48682
49206
  let listenerMismatch = false;
@@ -48690,7 +49214,6 @@ function createBackendHealth(dependencies) {
48690
49214
  };
48691
49215
  sessionStats.push(stats);
48692
49216
  if (stats.hasPty) alivePtyCount += 1;
48693
- if (stats.sleeping) sleepingCount += 1;
48694
49217
  totalDataListeners += stats.dataListenerCount;
48695
49218
  totalOutputBufferBytes += stats.outputBufferBytes;
48696
49219
  if (!session.ephemeral) {
@@ -48721,11 +49244,11 @@ function createBackendHealth(dependencies) {
48721
49244
  sessions: {
48722
49245
  total: dependencies.sessions.size,
48723
49246
  alivePty: alivePtyCount,
48724
- sleeping: sleepingCount,
48725
49247
  totalDataListeners,
48726
49248
  totalOutputBufferBytes,
48727
49249
  list: sessionStats
48728
49250
  },
49251
+ memoryGate: dependencies.getMemoryGateStatus(),
48729
49252
  websockets: {
48730
49253
  control: dependencies.controlWss.clients.size,
48731
49254
  data: dependencies.dataWss.clients.size,
@@ -50893,7 +51416,7 @@ function computeCrossRepoLinks(repos) {
50893
51416
  }
50894
51417
  //#endregion
50895
51418
  //#region server/change-map/change-map-wiring.ts
50896
- var GIT_TIMEOUT_MS = 15e3;
51419
+ var GIT_TIMEOUT_MS$1 = 15e3;
50897
51420
  var GIT_MAX_BUFFER_BYTES = 67108864;
50898
51421
  var FILE_READ_BATCH = 64;
50899
51422
  var MAX_CACHED_FILES = 5e4;
@@ -50905,14 +51428,14 @@ var MAX_MEMOIZED_REPO_FACTS = 64;
50905
51428
  async function runGitAsync(args, cwd) {
50906
51429
  const result = await runGit(args, {
50907
51430
  cwd,
50908
- timeoutMs: GIT_TIMEOUT_MS,
51431
+ timeoutMs: GIT_TIMEOUT_MS$1,
50909
51432
  maxBuffer: GIT_MAX_BUFFER_BYTES,
50910
51433
  trim: false
50911
51434
  });
50912
- if (!result.ok) throw gitCommandFailure(args, result, GIT_TIMEOUT_MS);
51435
+ if (!result.ok) throw gitCommandFailure(args, result, GIT_TIMEOUT_MS$1);
50913
51436
  return result.out;
50914
51437
  }
50915
- function yieldToEventLoop() {
51438
+ function yieldToEventLoop$1() {
50916
51439
  return new Promise((resolve) => {
50917
51440
  setImmediate(resolve);
50918
51441
  });
@@ -50959,7 +51482,7 @@ function createFileFactCache(derive, oversizedValue) {
50959
51482
  const value = values[index];
50960
51483
  if (value !== null) valueByPath.set(repoPath, value);
50961
51484
  });
50962
- await yieldToEventLoop();
51485
+ await yieldToEventLoop$1();
50963
51486
  }
50964
51487
  return valueByPath;
50965
51488
  }
@@ -51575,7 +52098,7 @@ function parseSinceParam(url) {
51575
52098
  return Number.isFinite(n) ? n : null;
51576
52099
  }
51577
52100
  function registerControlHandlers(controlWss, deps) {
51578
- const { sessions, agentSessions = /* @__PURE__ */ new Map(), config, configStore, broadcastControl, generateProjectId, applyConfigReload, applySettingsReload, requestShutdown, requestRestart, handleClientFocus, buildHealthSnapshot, getUpdateStatus, getUpdateJournal = null, checkNow = null, applyUpdate = null, isStaging = null, noteRestartRequested = null, getRadarStatus, posthogReportsDir = null, posthogSetIssueStatus = null, radarArchiveInvestigation = null, getTeamReviewStatus = null, getMyPrsStatus = null, getIssuesStatus = null, issues = null, teamReview = null, myPrs = null, getBenchmarkStatus = null, getFactoryState = null, benchmarks = null, factory = null, createGithubClient = createPrGh, issueProjectRepos = null, serverBuild = () => null, getUsageSessions = null, getUsageReport = null, requestUsageReport = null, getPlanLimits = null, controlReplayLog = null, getRtkInstallStatus = () => null, resolveRtkPath = () => getRtkPath(), conversationFs = fs, conversationGit = runGitForConversationHistory, conversationProjectsDir = claudeProjectsDir(process.env, os.homedir()), readTracePage = null, readPlanRevision = null, decidePlanReview = null, sessionDocs = null, changeMapNarrator = null } = deps;
52101
+ const { sessions, agentSessions = /* @__PURE__ */ new Map(), config, configStore, broadcastControl, generateProjectId, applyConfigReload, applySettingsReload, requestShutdown, requestRestart, handleClientFocus, buildHealthSnapshot, getUpdateStatus, getUpdateJournal = null, getPostTurnLeftoversNotice = null, recheckPostTurnLeftovers = null, checkNow = null, applyUpdate = null, noteRestartRequested = null, getRadarStatus, posthogReportsDir = null, posthogSetIssueStatus = null, radarArchiveInvestigation = null, getTeamReviewStatus = null, getMyPrsStatus = null, getIssuesStatus = null, issues = null, teamReview = null, myPrs = null, getBenchmarkStatus = null, getFactoryState = null, benchmarks = null, factory = null, createGithubClient = createPrGh, issueProjectRepos = null, serverBuild = () => null, getUsageSessions = null, getUsageReport = null, requestUsageReport = null, getPlanLimits = null, controlReplayLog = null, getRtkInstallStatus = () => null, resolveRtkPath = () => getRtkPath(), conversationFs = fs, conversationGit = runGitForConversationHistory, conversationProjectsDir = claudeProjectsDir(process.env, os.homedir()), readTracePage = null, readPlanRevision = null, decidePlanReview = null, sessionDocs = null, changeMapNarrator = null } = deps;
51579
52102
  const changeMaps = createChangeMapService({
51580
52103
  sessions,
51581
52104
  narrator: changeMapNarrator,
@@ -51848,35 +52371,6 @@ function registerControlHandlers(controlWss, deps) {
51848
52371
  }
51849
52372
  session.unlinkPr(msg.key);
51850
52373
  }
51851
- function handleReorderSessions(msg, ws) {
51852
- const order = msg.order;
51853
- if (!Array.isArray(order) || order.length === 0) {
51854
- sendError(ws, "order must be a non-empty array");
51855
- return;
51856
- }
51857
- if (!order.every((id) => sessions.has(id))) {
51858
- sendError(ws, "Session list changed during reorder");
51859
- broadcastControl(buildSnapshot());
51860
- return;
51861
- }
51862
- const entries = new Map(sessions);
51863
- sessions.clear();
51864
- for (const id of order) {
51865
- const existing = entries.get(id);
51866
- if (existing) sessions.set(id, existing);
51867
- }
51868
- for (const [id, sess] of entries) if (!sessions.has(id)) sessions.set(id, sess);
51869
- configStore.save((cfg) => {
51870
- const projectMap = new Map(cfg.projects.map((p) => [p.id, p]));
51871
- cfg.projects = order.map((id) => projectMap.get(id)).filter((project) => project !== void 0);
51872
- for (const p of projectMap.values()) if (!cfg.projects.some((x) => x.id === p.id)) cfg.projects.push(p);
51873
- });
51874
- broadcastControl({
51875
- type: "sessions-reordered",
51876
- order
51877
- });
51878
- console.log(`[control] Sessions reordered`);
51879
- }
51880
52374
  async function handleListConversations(msg, ws) {
51881
52375
  const sess = findSession(msg);
51882
52376
  if (!sess) {
@@ -52828,29 +53322,16 @@ function registerControlHandlers(controlWss, deps) {
52828
53322
  }
52829
53323
  await checkNow();
52830
53324
  }
52831
- async function handleUpdateApply(msg, ws) {
53325
+ async function handleUpdateApply(_msg, ws) {
52832
53326
  if (!applyUpdate) {
52833
53327
  sendError(ws, "[update-apply-unavailable] Update application is unavailable.");
52834
53328
  return;
52835
53329
  }
52836
53330
  const outcome = await applyUpdate();
52837
- if (!outcome.ok) {
52838
- sendError(ws, `[${outcome.reason || "update-refused"}] ${outcome.message}`);
52839
- return;
52840
- }
52841
- if (msg.restartWhenStaged !== true) return;
52842
- const confirmedSessionIds = new Set(msg.confirmedSessionIds ?? []);
52843
- if ([...sessions.keys(), ...agentSessions.keys()].some((liveSessionId) => !confirmedSessionIds.has(liveSessionId))) {
52844
- sendError(ws, "[update-restart-skipped] The update is staged, but sessions started since you confirmed. Restart when ready.");
52845
- return;
52846
- }
52847
- handleRestart(msg, ws);
53331
+ if (outcome.ok) return;
53332
+ sendError(ws, `[${outcome.reason || "update-refused"}] ${outcome.message}`);
52848
53333
  }
52849
- function handleRestart(_msg, ws) {
52850
- if (isStaging?.()) {
52851
- sendError(ws, "[update-staging] Wait for the update staging run to finish before restarting.");
52852
- return;
52853
- }
53334
+ function handleRestart(_msg) {
52854
53335
  noteRestartRequested?.();
52855
53336
  console.log("[control] Restart requested via UI");
52856
53337
  broadcastControl({ type: "restarting" });
@@ -52869,7 +53350,6 @@ function registerControlHandlers(controlWss, deps) {
52869
53350
  "set-session-title": handleSetSessionTitle,
52870
53351
  "link-session-pr": handleLinkSessionPr,
52871
53352
  "unlink-session-pr": handleUnlinkSessionPr,
52872
- "reorder-sessions": handleReorderSessions,
52873
53353
  "ping": handlePing,
52874
53354
  "get-settings": handleGetSettings,
52875
53355
  "update-settings": handleUpdateSettings,
@@ -52914,14 +53394,6 @@ function registerControlHandlers(controlWss, deps) {
52914
53394
  if (msg.expectedState !== void 0 && session.state !== msg.expectedState) return;
52915
53395
  session.dismiss();
52916
53396
  },
52917
- "sleep": (msg) => {
52918
- const s = findSession(msg);
52919
- if (s) s.sleep();
52920
- },
52921
- "wake": (msg) => {
52922
- const s = findSession(msg);
52923
- if (s) s.wake();
52924
- },
52925
53397
  "merge-session": async (msg, ws) => {
52926
53398
  const s = findSession(msg);
52927
53399
  if (s) reportMergeRefusal(ws, s, await s.mergeWorktree());
@@ -53239,6 +53711,9 @@ function registerControlHandlers(controlWss, deps) {
53239
53711
  type: "update-progress",
53240
53712
  journal: updateJournal
53241
53713
  }));
53714
+ const postTurnLeftoversNotice = getPostTurnLeftoversNotice?.() ?? null;
53715
+ if (postTurnLeftoversNotice) ws.send(JSON.stringify(postTurnLeftoversNotice));
53716
+ recheckPostTurnLeftovers?.();
53242
53717
  const radarStatus = typeof getRadarStatus === "function" ? getRadarStatus() : null;
53243
53718
  if (radarStatus) ws.send(JSON.stringify(radarStatus));
53244
53719
  const teamReviewStatus = typeof getTeamReviewStatus === "function" ? getTeamReviewStatus() : null;
@@ -53313,9 +53788,10 @@ function createBackendControl(dependencies) {
53313
53788
  buildHealthSnapshot: dependencies.buildHealthSnapshot,
53314
53789
  getUpdateStatus: dependencies.getUpdateStatus,
53315
53790
  getUpdateJournal: dependencies.getUpdateJournal,
53791
+ getPostTurnLeftoversNotice: dependencies.getPostTurnLeftoversNotice,
53792
+ recheckPostTurnLeftovers: dependencies.recheckPostTurnLeftovers,
53316
53793
  checkNow: dependencies.checkNow,
53317
53794
  applyUpdate: dependencies.applyUpdate,
53318
- isStaging: dependencies.isStaging,
53319
53795
  noteRestartRequested: dependencies.noteRestartRequested,
53320
53796
  getRadarStatus: () => posthog.getStatus(),
53321
53797
  posthogSetIssueStatus: (args) => posthog.setIssueStatus(args),
@@ -53367,6 +53843,7 @@ var PREVIOUS_DEPENDENCIES_BACKUP_NAME = "prev-node_modules";
53367
53843
  var QUARANTINED_DIST_NAME = "broken-dist";
53368
53844
  var QUARANTINED_DEPENDENCIES_NAME = "broken-node_modules";
53369
53845
  var SUPPORTED_UPDATE_REMOTE = "origin";
53846
+ var LOWEST_SCHEDULING_PRIORITY = "19";
53370
53847
  var TRANSITIONS_ALLOWED_BY_STATE = Object.freeze({
53371
53848
  idle: Object.freeze(["begin-run"]),
53372
53849
  running: Object.freeze([
@@ -53528,6 +54005,21 @@ function planSteps({ lockfileChanged }) {
53528
54005
  "build"
53529
54006
  ];
53530
54007
  }
54008
+ function lowPriorityCommand({ file, args, platform }) {
54009
+ if (platform === "win32") return {
54010
+ file,
54011
+ args
54012
+ };
54013
+ return {
54014
+ file: "nice",
54015
+ args: [
54016
+ "-n",
54017
+ LOWEST_SCHEDULING_PRIORITY,
54018
+ file,
54019
+ ...args
54020
+ ]
54021
+ };
54022
+ }
53531
54023
  function planHandOffRenames({ root, stagingPath, lockfileChanged }) {
53532
54024
  const updatePath = `${root}/.glimmervoid/update`;
53533
54025
  const renames = [{
@@ -54054,6 +54546,7 @@ function createBackendUpdateCheck(dependencies) {
54054
54546
  let inFlight = null;
54055
54547
  let lastChannel = normalizeUpdateChannel(dependencies.config.updateChannel);
54056
54548
  let lastSurfacedIdentity = null;
54549
+ let lastDownloadedIdentity = null;
54057
54550
  let pendingChannelRefresh = false;
54058
54551
  let stopped = false;
54059
54552
  const now = dependencies.now || (() => Date.now());
@@ -54157,6 +54650,26 @@ function createBackendUpdateCheck(dependencies) {
54157
54650
  const latestRelease = status.latest || shortSha(status.latestSha) || "unknown";
54158
54651
  dependencies.logger.log(`[update] A newer glimmervoid is available: ${currentRelease} -> ${latestRelease}. Update: ${status.command}`);
54159
54652
  }
54653
+ function downloadInBackground(status) {
54654
+ const downloadUpdate = dependencies.downloadUpdate;
54655
+ if (!downloadUpdate || dependencies.config.checkForUpdates === false) return;
54656
+ if (!status.updateAvailable || status.applyRefusal) return;
54657
+ const identity = status.latestSha || status.latest;
54658
+ if (!identity || identity === lastDownloadedIdentity) return;
54659
+ lastDownloadedIdentity = identity;
54660
+ dependencies.logger.log(`[update] downloading ${status.latest || shortSha(status.latestSha)} in the background`);
54661
+ const allowRetryAtNextCheck = () => {
54662
+ if (lastDownloadedIdentity === identity) lastDownloadedIdentity = null;
54663
+ };
54664
+ downloadUpdate().then((outcome) => {
54665
+ if (outcome.ok) return;
54666
+ allowRetryAtNextCheck();
54667
+ dependencies.logger.log(`[update] background download stopped: ${outcome.message}`);
54668
+ }, (error) => {
54669
+ allowRetryAtNextCheck();
54670
+ dependencies.logger.log(`[update] background download failed: ${String(error)}`);
54671
+ });
54672
+ }
54160
54673
  function runCheck(ttlMs) {
54161
54674
  if (inFlight) return inFlight;
54162
54675
  const channel = normalizeUpdateChannel(dependencies.config.updateChannel);
@@ -54175,6 +54688,7 @@ function createBackendUpdateCheck(dependencies) {
54175
54688
  });
54176
54689
  const status = recordStatus(updateResult, checkedAt, channel);
54177
54690
  surfaceAvailableUpdate(status);
54691
+ downloadInBackground(status);
54178
54692
  return status;
54179
54693
  }).finally(() => {
54180
54694
  inFlight = null;
@@ -54197,6 +54711,7 @@ function createBackendUpdateCheck(dependencies) {
54197
54711
  recordedStatus = null;
54198
54712
  lastLaneSignature = null;
54199
54713
  lastSurfacedIdentity = null;
54714
+ lastDownloadedIdentity = null;
54200
54715
  updateAbort?.abort();
54201
54716
  return true;
54202
54717
  }
@@ -54950,7 +55465,12 @@ function createUpdateApplyLane(dependencies) {
54950
55465
  stepId,
54951
55466
  now: clock()
54952
55467
  }));
54953
- const outcome = await command(file, args, cwd, timeout);
55468
+ const throttled = lowPriorityCommand({
55469
+ file,
55470
+ args,
55471
+ platform: dependencies.platform || process.platform
55472
+ });
55473
+ const outcome = await command(throttled.file, throttled.args, cwd, timeout);
54954
55474
  journal = appendStepOutput(journal, stepId, outcome.output);
54955
55475
  if (!outcome.ok) throw new Error(outcome.reason || `${stepId} failed`);
54956
55476
  await publish(finishStep(journal, {
@@ -55245,9 +55765,6 @@ function createUpdateApplyLane(dependencies) {
55245
55765
  activeRun = run;
55246
55766
  await run;
55247
55767
  }
55248
- function isStaging() {
55249
- return inFlight;
55250
- }
55251
55768
  function isRestartRequested() {
55252
55769
  return restartRequested;
55253
55770
  }
@@ -55354,13 +55871,158 @@ function createUpdateApplyLane(dependencies) {
55354
55871
  getJournal,
55355
55872
  handOffStagedUpdate,
55356
55873
  isRestartRequested,
55357
- isStaging,
55358
55874
  noteRestartRequested,
55359
55875
  startAfterListening,
55360
55876
  stop,
55361
55877
  whenIdle
55362
55878
  };
55363
55879
  }
55880
+ //#endregion
55881
+ //#region shared/post-turn-leftovers.ts
55882
+ var REPORT_FOLDER_GIT_PATH = ".glimmervoid/post-turn";
55883
+ var REPORT_FILE_NAME = /^[a-zA-Z0-9_.-]+\.json$/;
55884
+ var POWERSHELL_SINGLE_QUOTES = /['\u2018\u2019\u201A\u201B]/g;
55885
+ function isReportGitPath(gitPath) {
55886
+ const folderPrefix = `${REPORT_FOLDER_GIT_PATH}/`;
55887
+ return gitPath.startsWith(folderPrefix) && REPORT_FILE_NAME.test(gitPath.slice(folderPrefix.length));
55888
+ }
55889
+ function untrackedReportFileNames(fileNames, trackedGitPaths) {
55890
+ const trackedPaths = new Set(trackedGitPaths);
55891
+ return fileNames.filter((fileName) => REPORT_FILE_NAME.test(fileName) && !trackedPaths.has(`.glimmervoid/post-turn/${fileName}`));
55892
+ }
55893
+ function quoteShellArgument(argument, platform) {
55894
+ if (platform === "win32") return `'${argument.replace(POWERSHELL_SINGLE_QUOTES, "$&$&")}'`;
55895
+ return `'${argument.replaceAll("'", "'\\''")}'`;
55896
+ }
55897
+ function trackedReportCleanupCommands(repositories, platform) {
55898
+ return repositories.map(({ repositoryPath, trackedReportGitPaths }) => {
55899
+ const quotedReportPaths = trackedReportGitPaths.map((gitPath) => quoteShellArgument(gitPath, platform)).join(" ");
55900
+ return `git -C ${quoteShellArgument(repositoryPath, platform)} rm --cached -q -- ${quotedReportPaths}`;
55901
+ }).join("\n");
55902
+ }
55903
+ function trackedReportNoticeText(repositoryCount) {
55904
+ return `Git tracks an old Glimmervoid post-turn report in ${repositoryCount === 1 ? "1 repository" : `${repositoryCount} repositories`}. Nothing reads it, so run this to stop git tracking it and commit the deletion git then shows. Glimmervoid deletes the file itself on its next start:`;
55905
+ }
55906
+ //#endregion
55907
+ //#region server/post-turn-leftovers.ts
55908
+ var GIT_TIMEOUT_MS = 1e4;
55909
+ var CONTROL_CHARACTER = /[\x00-\x1f\x7f]/;
55910
+ var NOTHING_CLEANED = {
55911
+ removedFileCount: 0,
55912
+ trackedReportGitPaths: []
55913
+ };
55914
+ async function gitOutput(args, cwd) {
55915
+ const result = await runGit(args, {
55916
+ cwd,
55917
+ timeoutMs: GIT_TIMEOUT_MS,
55918
+ trim: false
55919
+ });
55920
+ if (!result.ok) throw result.error ?? new Error(result.err);
55921
+ return result.out;
55922
+ }
55923
+ function yieldToEventLoop() {
55924
+ return new Promise((resolve) => setImmediate(resolve));
55925
+ }
55926
+ async function collectWorktreePaths(projects) {
55927
+ const repositoryPaths = new Set(projects.flatMap((project) => [project.path, ...project.repos ?? []]).filter(Boolean));
55928
+ const worktreePaths = /* @__PURE__ */ new Set();
55929
+ for (const repositoryPath of repositoryPaths) {
55930
+ if (worktreePaths.has(repositoryPath)) continue;
55931
+ await yieldToEventLoop();
55932
+ for (const worktreePath of await listRepoWorktreePaths(repositoryPath, gitOutput)) worktreePaths.add(worktreePath);
55933
+ }
55934
+ return [...worktreePaths];
55935
+ }
55936
+ async function readFileNames(folder) {
55937
+ try {
55938
+ return (await fsp.readdir(folder, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name);
55939
+ } catch (error) {
55940
+ if (isMissingFileError(error)) return null;
55941
+ throw error;
55942
+ }
55943
+ }
55944
+ async function removeFolderIfEmpty(folder) {
55945
+ if ((await fsp.readdir(folder)).length > 0) return;
55946
+ await fsp.rmdir(folder);
55947
+ }
55948
+ async function isRealDirectory(folder) {
55949
+ try {
55950
+ return (await fsp.lstat(folder)).isDirectory();
55951
+ } catch (error) {
55952
+ if (isMissingFileError(error)) return false;
55953
+ throw error;
55954
+ }
55955
+ }
55956
+ async function isReportFolderOwnedByWorktree(worktreePath, reportFolder) {
55957
+ if (!await isRealDirectory(path.dirname(reportFolder)) || !await isRealDirectory(reportFolder)) return false;
55958
+ const realWorktreePath = await fsp.realpath(worktreePath);
55959
+ if (!isPathInside(realWorktreePath, await fsp.realpath(reportFolder), { allowEqual: false })) return false;
55960
+ const reportFolderTopLevel = (await gitOutput(["rev-parse", "--show-toplevel"], reportFolder)).trim();
55961
+ return equalsIgnoringCaseOnWindows(await fsp.realpath(reportFolderTopLevel), realWorktreePath);
55962
+ }
55963
+ async function listTrackedReportGitPaths(worktreePath) {
55964
+ return (await gitOutput([
55965
+ "ls-files",
55966
+ "-z",
55967
+ "--",
55968
+ REPORT_FOLDER_GIT_PATH
55969
+ ], worktreePath)).split("\0").filter(isReportGitPath);
55970
+ }
55971
+ async function cleanWorktree(worktreePath) {
55972
+ const reportFolder = path.join(worktreePath, ...REPORT_FOLDER_GIT_PATH.split("/"));
55973
+ if (!await isReportFolderOwnedByWorktree(worktreePath, reportFolder)) return NOTHING_CLEANED;
55974
+ const fileNames = await readFileNames(reportFolder);
55975
+ if (fileNames === null) return NOTHING_CLEANED;
55976
+ const trackedReportGitPaths = await listTrackedReportGitPaths(worktreePath);
55977
+ const deletableFileNames = untrackedReportFileNames(fileNames, trackedReportGitPaths);
55978
+ for (const fileName of deletableFileNames) await fsp.rm(path.join(reportFolder, fileName), { force: true });
55979
+ await removeFolderIfEmpty(reportFolder);
55980
+ await removeFolderIfEmpty(path.dirname(reportFolder));
55981
+ return {
55982
+ removedFileCount: deletableFileNames.length,
55983
+ trackedReportGitPaths
55984
+ };
55985
+ }
55986
+ async function cleanPostTurnLeftovers({ projects, logger = console, platform = process.platform }) {
55987
+ const repositoriesTrackingReports = [];
55988
+ let removedFileCount = 0;
55989
+ for (const worktreePath of await collectWorktreePaths(projects)) {
55990
+ await yieldToEventLoop();
55991
+ if (CONTROL_CHARACTER.test(worktreePath)) {
55992
+ logger.warn(`[post-turn] skipped the old report folder in ${JSON.stringify(worktreePath)} because its path contains a control character`);
55993
+ continue;
55994
+ }
55995
+ try {
55996
+ const cleanup = await cleanWorktree(worktreePath);
55997
+ removedFileCount += cleanup.removedFileCount;
55998
+ if (cleanup.trackedReportGitPaths.length > 0) repositoriesTrackingReports.push({
55999
+ repositoryPath: worktreePath,
56000
+ trackedReportGitPaths: cleanup.trackedReportGitPaths
56001
+ });
56002
+ } catch (error) {
56003
+ logger.warn(`[post-turn] could not clean the old report folder in ${worktreePath}: ${errorMessage(error)}`);
56004
+ }
56005
+ }
56006
+ if (removedFileCount > 0) logger.log(`[post-turn] removed ${removedFileCount} old post-turn report files that earlier versions left in your repositories`);
56007
+ if (repositoriesTrackingReports.length > 0) logger.warn(`[post-turn] ${trackedReportNoticeText(repositoriesTrackingReports.length)}\n${trackedReportCleanupCommands(repositoriesTrackingReports, platform)}`);
56008
+ return repositoriesTrackingReports;
56009
+ }
56010
+ async function recheckRepositoriesTrackingReports(repositories, logger = console) {
56011
+ const repositoriesStillTrackingReports = [];
56012
+ for (const { repositoryPath } of repositories) {
56013
+ await yieldToEventLoop();
56014
+ try {
56015
+ const trackedReportGitPaths = await listTrackedReportGitPaths(repositoryPath);
56016
+ if (trackedReportGitPaths.length > 0) repositoriesStillTrackingReports.push({
56017
+ repositoryPath,
56018
+ trackedReportGitPaths
56019
+ });
56020
+ } catch (error) {
56021
+ logger.warn(`[post-turn] could not re-check the old report folder in ${repositoryPath}: ${errorMessage(error)}`);
56022
+ }
56023
+ }
56024
+ return repositoriesStillTrackingReports;
56025
+ }
55364
56026
  var FLUSH_AT_EVENT_COUNT = 20;
55365
56027
  var FLUSH_INTERVAL_MS = 6e4;
55366
56028
  var SEND_TIMEOUT_MS = 5e3;
@@ -56086,6 +56748,7 @@ function createBackend(httpServer, options = {}) {
56086
56748
  getAllSessions: () => allLiveSessions(),
56087
56749
  sessionDataClients,
56088
56750
  getIngestLane: getCurrentIngestLane,
56751
+ getMemoryGateStatus: () => laneAssembly.memoryAdmission.gateStatus(),
56089
56752
  controlWss,
56090
56753
  dataWss,
56091
56754
  broadcastControl
@@ -56138,6 +56801,7 @@ function createBackend(httpServer, options = {}) {
56138
56801
  closeSessionDataClients,
56139
56802
  broadcastControl,
56140
56803
  spawnGate,
56804
+ memoryAdmission: laneAssembly.memoryAdmission,
56141
56805
  recordLane,
56142
56806
  sessionDocs,
56143
56807
  logger: console
@@ -56187,6 +56851,7 @@ function createBackend(httpServer, options = {}) {
56187
56851
  sessionRegistry.initialize();
56188
56852
  const reconcileProjectsOnReload = sessionRegistry.applyConfigReload;
56189
56853
  rtkInstall.maybeInstall();
56854
+ sweepOrphanedGuardrailsLaunches();
56190
56855
  laneAssembly.startRuntimeLanes();
56191
56856
  function applyCustomAgents(sourceConfig) {
56192
56857
  setCustomAgents(sourceConfig.customAgents ?? []);
@@ -56207,6 +56872,12 @@ function createBackend(httpServer, options = {}) {
56207
56872
  visionsSetup.maybeApply();
56208
56873
  laneAssembly.restartDynamicLanes();
56209
56874
  }
56875
+ function downloadUpdateOnceListening() {
56876
+ if (httpServer.listening) return updateApply.applyUpdate();
56877
+ return new Promise((resolve) => {
56878
+ httpServer.once("listening", resolve);
56879
+ }).then(() => updateApply.applyUpdate());
56880
+ }
56210
56881
  const updateCheck = createBackendUpdateCheck({
56211
56882
  config,
56212
56883
  currentVersion: packageJson.version,
@@ -56217,7 +56888,8 @@ function createBackend(httpServer, options = {}) {
56217
56888
  isRestartRequested: () => updateApply.isRestartRequested(),
56218
56889
  getControlClientCount: () => controlWss.clients.size,
56219
56890
  broadcastControl,
56220
- logger: console
56891
+ logger: console,
56892
+ ...options.downloadUpdatesInBackground ? { downloadUpdate: downloadUpdateOnceListening } : {}
56221
56893
  });
56222
56894
  const updateApply = createUpdateApplyLane({
56223
56895
  gitWorkspace: assembledGitWorkspace,
@@ -56233,10 +56905,42 @@ function createBackend(httpServer, options = {}) {
56233
56905
  },
56234
56906
  logger: console
56235
56907
  });
56908
+ let repositoriesTrackingPostTurnReports = [];
56909
+ let isRecheckingPostTurnReports = false;
56910
+ function postTurnLeftoversMessage() {
56911
+ return {
56912
+ type: "post-turn-leftovers",
56913
+ repositories: repositoriesTrackingPostTurnReports.map(({ repositoryPath }) => repositoryPath),
56914
+ command: trackedReportCleanupCommands(repositoriesTrackingPostTurnReports, process.platform)
56915
+ };
56916
+ }
56917
+ function startPostTurnLeftoverCleanup() {
56918
+ cleanPostTurnLeftovers({ projects: config.projects }).then((repositories) => {
56919
+ repositoriesTrackingPostTurnReports = repositories;
56920
+ if (repositories.length > 0) broadcastControlFromWebSockets(postTurnLeftoversMessage());
56921
+ }).catch((error) => {
56922
+ console.warn(`[post-turn] old report cleanup failed: ${errorMessage(error)}`);
56923
+ });
56924
+ }
56925
+ function recheckPostTurnLeftovers() {
56926
+ if (isRecheckingPostTurnReports || repositoriesTrackingPostTurnReports.length === 0) return;
56927
+ isRecheckingPostTurnReports = true;
56928
+ const previousRepositories = repositoriesTrackingPostTurnReports;
56929
+ recheckRepositoriesTrackingReports(previousRepositories).then((repositories) => {
56930
+ if (JSON.stringify(repositories) === JSON.stringify(previousRepositories)) return;
56931
+ repositoriesTrackingPostTurnReports = repositories;
56932
+ broadcastControlFromWebSockets(postTurnLeftoversMessage());
56933
+ }).catch((error) => {
56934
+ console.warn(`[post-turn] old report re-check failed: ${errorMessage(error)}`);
56935
+ }).finally(() => {
56936
+ isRecheckingPostTurnReports = false;
56937
+ });
56938
+ }
56236
56939
  let stopConfigWatch = null;
56237
56940
  const shutdown = createBackendShutdown({
56238
56941
  configStore,
56239
56942
  cancelAutoResume: sessionRegistry.cancelAutoResume,
56943
+ cancelPostTurnLeftoverCleanup: () => httpServer.off("listening", startPostTurnLeftoverCleanup),
56240
56944
  healthInterval,
56241
56945
  getStopConfigWatch: () => stopConfigWatch,
56242
56946
  remoteAuth,
@@ -56304,9 +57008,10 @@ function createBackend(httpServer, options = {}) {
56304
57008
  buildHealthSnapshot,
56305
57009
  getUpdateStatus: updateCheck.getStatus,
56306
57010
  getUpdateJournal: updateApply.getJournal,
57011
+ getPostTurnLeftoversNotice: () => repositoriesTrackingPostTurnReports.length > 0 ? postTurnLeftoversMessage() : null,
57012
+ recheckPostTurnLeftovers,
56307
57013
  checkNow: updateCheck.checkNow,
56308
57014
  applyUpdate: updateApply.applyUpdate,
56309
- isStaging: updateApply.isStaging,
56310
57015
  noteRestartRequested: () => {
56311
57016
  updateApply.noteRestartRequested();
56312
57017
  updateCheck.refreshApplyAvailability();
@@ -56332,6 +57037,7 @@ function createBackend(httpServer, options = {}) {
56332
57037
  const { handleUpgrade } = webSockets;
56333
57038
  httpServer.on("upgrade", handleUpgrade);
56334
57039
  updateApply.startAfterListening(httpServer);
57040
+ httpServer.once("listening", startPostTurnLeftoverCleanup);
56335
57041
  stopConfigWatch = configStore.watchForChanges((newConfig) => {
56336
57042
  applyConfigReload(newConfig);
56337
57043
  broadcastControl({
@@ -56345,6 +57051,7 @@ function createBackend(httpServer, options = {}) {
56345
57051
  updateCheck.start();
56346
57052
  return {
56347
57053
  shutdown,
57054
+ handOffStagedUpdate: updateApply.handOffStagedUpdate,
56348
57055
  port,
56349
57056
  app,
56350
57057
  telemetry,
@@ -56394,7 +57101,10 @@ function isBootRefusal(error) {
56394
57101
  }
56395
57102
  function createBackendOrExit() {
56396
57103
  try {
56397
- return createBackend(server, { staticDir: "auto" });
57104
+ return createBackend(server, {
57105
+ staticDir: "auto",
57106
+ downloadUpdatesInBackground: true
57107
+ });
56398
57108
  } catch (err) {
56399
57109
  if (!isBootRefusal(err)) throw err;
56400
57110
  console.error(err.message);
@@ -56402,7 +57112,7 @@ function createBackendOrExit() {
56402
57112
  }
56403
57113
  }
56404
57114
  var backend = createBackendOrExit();
56405
- var { shutdown, port, app, telemetry, version } = backend;
57115
+ var { shutdown, handOffStagedUpdate, port, app, telemetry, version } = backend;
56406
57116
  server.on("request", app);
56407
57117
  function writeTerminalTitle(sequence) {
56408
57118
  if (!process.stdout.isTTY) return;
@@ -56483,7 +57193,8 @@ var { requestShutdown } = createLifecycle({
56483
57193
  httpServer: server,
56484
57194
  extraServers: remoteServers,
56485
57195
  spawn,
56486
- exit: exitWithClearedTerminalTitle
57196
+ exit: exitWithClearedTerminalTitle,
57197
+ beforeHandOff: handOffStagedUpdate
56487
57198
  });
56488
57199
  function handleShutdownSignal(signal) {
56489
57200
  console.log(`\n${signal} received - shutting down...`);