glimmervoid 0.37.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/bin/glimmervoid.js +14 -14
  2. package/dist/chunks/{adapters-BmfVPxlH.js → adapters-B-IkWl-r.js} +19 -4
  3. package/dist/chunks/{adapters-CqFFmM2R.js → adapters-Dt3vwxwY.js} +1 -1
  4. package/dist/chunks/{agent-api-cli-BTHEyJKS.js → agent-api-cli-3hryuewm.js} +1 -1
  5. package/dist/chunks/{agent-api-cli-CBgOKXoO.js → agent-api-cli-BcZ9DLCF.js} +3 -3
  6. package/dist/chunks/{agent-api-core-DzKhakCv.js → agent-api-core--8WtU1xU.js} +3 -12
  7. package/dist/chunks/{agent-setup-cli-C8DGxlA-.js → agent-setup-cli-BixDoCkF.js} +2 -2
  8. package/dist/chunks/{config-BYc2z1Oc.js → config-BHBZsCBc.js} +9 -8
  9. package/dist/chunks/config-store-DYj3wYgw.js +1 -0
  10. package/dist/chunks/{config-store-B7MpiYxG.js → config-store-DbfMbz4j.js} +41 -64
  11. package/dist/chunks/{contracts-C6ipN_zw.js → contracts-u40eJhaf.js} +20 -14
  12. package/dist/chunks/{git-version-6QFnKbWa.js → git-version-BpnTd8sU.js} +1 -1
  13. package/dist/chunks/{grok-CZ6kP_vD.js → grok-xFs6zIu4.js} +1 -1
  14. package/dist/chunks/{json-file-BaxGcB2R.js → json-file-CIJf0Cel.js} +190 -9
  15. package/dist/chunks/{knowledge-graph-cli-DApeMzA8.js → knowledge-graph-cli-DcspNqhz.js} +1 -1
  16. package/dist/chunks/{loopback-post-DJ6iYWuL.js → loopback-post-DZK7YibD.js} +1 -1
  17. package/dist/chunks/{main-BUfjroXE.js → main-w01-P1oZ.js} +1048 -440
  18. package/dist/chunks/{pair-cli-GZ9t1mIO.js → pair-cli-BdoyrDCr.js} +2 -2
  19. package/dist/chunks/{remote-config-D2Is89Kj.js → remote-config-61zJ0hME.js} +2 -2
  20. package/dist/chunks/{session-BZbariv5.js → session-8Pv0y1vz.js} +1 -1
  21. package/dist/chunks/{session-routes-C4t8O8yg.js → session-routes-D-GZC-Sy.js} +1 -1
  22. package/dist/chunks/{settings-cli-DQOjmjev.js → settings-cli-DKJsm3AP.js} +3 -3
  23. package/dist/chunks/{ssh-sign-cli-LIBP8ppO.js → ssh-sign-cli-Dc2gOCwE.js} +2 -2
  24. package/dist/client/assets/index-BfqmOGgc.js +275 -0
  25. package/dist/client/assets/index-DxABp5yJ.css +1 -0
  26. package/dist/client/index.html +10 -3
  27. package/dist/server/index.js +1 -1
  28. package/dist/session/command-hook-relay.js +1 -1
  29. package/dist/session/hook-relay.js +1 -1
  30. package/dist/session/statusline-relay.js +1 -1
  31. package/dist/session/visions-relay.js +7 -7
  32. package/package.json +1 -1
  33. package/scripts/recover-handoff.mjs +1 -1
  34. package/dist/chunks/config-store-CsUoy88Q.js +0 -1
  35. package/dist/client/assets/index-B_Qt7fbZ.css +0 -1
  36. 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 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-8Pv0y1vz.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-BpnTd8sU.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,18 @@ 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-D-GZC-Sy.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--8WtU1xU.js";
21
+ import { s as GLIMMERVOID_SECRET_KEYS } from "./grok-xFs6zIu4.js";
22
22
  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";
23
+ 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";
24
+ import { $ as CoherenceWorkCreated, C as USAGE_COST_MODES, M as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Q as CoherenceTicketCreated, S as WorkflowsState, W as USAGE_INTEGER_RANGES, Y as CoherenceOrient, a as ConfigUpdate, b as WorkflowSearchNode, c as RUNTIME_CONFIG_SCALAR_KEYS, et as CoherenceWorkInspect, 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-BHBZsCBc.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-DbfMbz4j.js";
26
+ import { $ as FactoryControlRequest, 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, Q as FactoryConsequences, R as BenchmarkSuite, S as planTitle, T as IssueKey, V as MinedPrReviewData, W as PendingCrashReport, X as FACTORY_ERROR_MAX_CHARS, Y as FactoryDecisionLogLine, Z as FACTORY_PROTECTED_PATH_FLOOR, _ as PLAN_RESULT_HOOK_EVENT, a as parseDataClientMessage, at as FactoryReviewVerdict, b as PlanRevision, c as DIFF_ANNOTATION_NOTE_MAX_CHARS, ct as isSingleFactoryPathSegment, d as MAX_RAW_LINE_CHARS, et as FactoryHarnessStateLine, f as TraceCheckpoint, g as PLAN_HOOK_OUTPUT_MAX_CHARS, h as PLAN_HOOK_EVENT, i as parseDataAttachHistory, it as FactoryQueueIntentRequest, j as changeMapFactId, k as IssuesStatus, l as InstallFlavor, m as PLAN_BODY_CAP_BYTES, n as HookEnvelope, nt as FactoryLaneState, o as CONTROL_FRAME_MAX_BYTES, ot as FactoryReviewerOutput, p as TraceRecord, q as TelemetryState, r as DEEP_HISTORY_SCROLLBACK_LINES, rt as FactoryProjectState, s as ClientMessage, st as FactoryWorkerEvent, t as HTTP_URL_RE, tt as FactoryHarnessVerdict, u as UpdateJournal, v as PLAN_TOOL_NAME, w as GithubIssueResponse, x as parseExitPlanModeHookPayload, y as PlanDecision, z as CommitComparison } from "./contracts-u40eJhaf.js";
27
+ 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-B-IkWl-r.js";
28
28
  import { t as HOOK_TOOLS } from "./hook-tools-BkxllUHE.js";
29
29
  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";
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-61zJ0hME.js";
31
31
  import { _ as escapeMarkup, g as createLaneLog, n as createVisionsSetup } from "./visions-setup-B-ZeRdsi.js";
32
32
  import { t as getRtkPath } from "./rtk-resolver-C_micGrL.js";
33
33
  import { createRequire } from "node:module";
@@ -36,11 +36,11 @@ import path, { join, resolve } from "node:path";
36
36
  import os from "node:os";
37
37
  import { z } from "zod";
38
38
  import { stripVTControlCharacters } from "node:util";
39
+ import WebSocket$1, { WebSocket, WebSocketServer } from "ws";
39
40
  import http from "node:http";
40
- import WebSocket, { WebSocketServer } from "ws";
41
41
  import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
42
42
  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";
43
+ import fsp, { access, chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
44
44
  import { pipeline } from "node:stream";
45
45
  import express from "express";
46
46
  import pty from "node-pty";
@@ -1136,7 +1136,7 @@ function isPairPath(pathname) {
1136
1136
  if (typeof pathname !== "string") return false;
1137
1137
  const normalized = normalizePathname(pathname);
1138
1138
  if (normalized.suspicious) return false;
1139
- return normalized.pathname === "/pair" || normalized.pathname.startsWith(PAIR_PATH_PREFIX);
1139
+ return normalized.pathname.startsWith(PAIR_PATH_PREFIX);
1140
1140
  }
1141
1141
  var ENCODED_WORD_SHAPE = /=\?[^?]*\?[^?]*\?[^?]*\?=/;
1142
1142
  var ENCODED_WORD_PARTS = /^=\?([^?]*)\?([^?]*)\?([^?]*)\?=$/;
@@ -2052,7 +2052,6 @@ function projectSessionSnapshots(source) {
2052
2052
  agent: source.agent,
2053
2053
  state: source.state,
2054
2054
  stateSince: source.stateSince,
2055
- sleeping: source.sleeping,
2056
2055
  dangerouslySkipPermissions: source.dangerouslySkipPermissions,
2057
2056
  saneYolo: !!source.saneYolo,
2058
2057
  ephemeral: source.ephemeral,
@@ -2063,6 +2062,7 @@ function projectSessionSnapshots(source) {
2063
2062
  activeAgents: source.activeAgents,
2064
2063
  awaitingBackgroundTasks: source.awaitingBackgroundTasks,
2065
2064
  isCompacting: source.isCompacting ?? false,
2065
+ isWaitingForMemory: source.isWaitingForMemory === true,
2066
2066
  pendingWakeup: source.pendingWakeup,
2067
2067
  pendingPromptKind: source.pendingPromptKind,
2068
2068
  conversationApproval: source.conversationApproval ?? null,
@@ -3265,6 +3265,87 @@ function resolveFactoryProfile(globalSettings, project) {
3265
3265
  };
3266
3266
  }
3267
3267
  //#endregion
3268
+ //#region server/factory/core/factory-setup-core.ts
3269
+ var PACKAGE_MANAGERS = [
3270
+ {
3271
+ lockfiles: ["npm-shrinkwrap.json", "package-lock.json"],
3272
+ prefix: "npm run",
3273
+ test: "npm test"
3274
+ },
3275
+ {
3276
+ lockfiles: ["pnpm-lock.yaml"],
3277
+ prefix: "pnpm run",
3278
+ test: "pnpm run test"
3279
+ },
3280
+ {
3281
+ lockfiles: ["yarn.lock"],
3282
+ prefix: "yarn",
3283
+ test: "yarn test"
3284
+ },
3285
+ {
3286
+ lockfiles: ["bun.lockb", "bun.lock"],
3287
+ prefix: "bun run",
3288
+ test: "bun run test"
3289
+ }
3290
+ ];
3291
+ var FACTORY_TEST_COMMANDS = PACKAGE_MANAGERS.map((manager) => manager.test);
3292
+ var FOCUSED_RUNNERS = [
3293
+ {
3294
+ dependency: "vitest",
3295
+ command: "npx vitest run"
3296
+ },
3297
+ {
3298
+ dependency: "jest",
3299
+ command: "npx jest"
3300
+ },
3301
+ {
3302
+ dependency: "mocha",
3303
+ command: "npx mocha"
3304
+ }
3305
+ ];
3306
+ function detectFactorySetup({ scripts, lockfiles, dependencies, globalChecks }) {
3307
+ const packageManager = PACKAGE_MANAGERS.find((manager) => manager.lockfiles.some((lockfile) => lockfiles.includes(lockfile))) ?? PACKAGE_MANAGERS[0];
3308
+ const hasScript = (name) => typeof scripts?.[name] === "string" && String(scripts[name]).trim().length > 0;
3309
+ return {
3310
+ checks: globalChecks === void 0 ? [
3311
+ "typecheck",
3312
+ "lint",
3313
+ "test"
3314
+ ].filter(hasScript).map((name) => name === "test" ? packageManager.test : `${packageManager.prefix} ${name}`) : [...globalChecks],
3315
+ focusedTestCommandCandidates: [...hasScript("test:files") ? [`${packageManager.prefix} test:files`] : [], ...FOCUSED_RUNNERS.filter((runner) => dependencies.includes(runner.dependency)).map((runner) => runner.command)],
3316
+ widenableScopes: [...DEFAULT_WIDENABLE_SCOPES]
3317
+ };
3318
+ }
3319
+ var TEST_RESULT_LINE = /^[ \t]*(?:not )?ok \d+\b|^# tests \d+\b/m;
3320
+ var SHELL_NOT_FOUND_LINE = /^(?:\/[^\s:]*\/)?(?:sh|bash|dash|ash|zsh)(?:: (?:line )?\d+)?: [^\n]*?: (?:command )?not found/m;
3321
+ 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;
3322
+ function classifyFactorySetupCheck(check) {
3323
+ const hasLaunchFailure = !check.pass && !TEST_RESULT_LINE.test(check.output) && (SHELL_NOT_FOUND_LINE.test(check.output) || LAUNCH_FAILURE_LINE.test(check.output));
3324
+ return {
3325
+ ...check,
3326
+ status: hasLaunchFailure ? "could-not-run" : "ran"
3327
+ };
3328
+ }
3329
+ function decideFactorySetupProof(proposal, fullChecks, focusedChecks) {
3330
+ const checks = [...new Set(proposal.checks)].filter((command) => fullChecks.some((check) => check.command === command && check.status === "ran"));
3331
+ if (checks.length === 0) {
3332
+ const failures = fullChecks.map((check) => `${check.command}: ${check.output}`).join("\n");
3333
+ return {
3334
+ ok: false,
3335
+ reason: `Factory setup could not run any full checks.${failures ? `\n${failures}` : " No check commands were detected."}`
3336
+ };
3337
+ }
3338
+ const focusedTestCommand = proposal.focusedTestCommandCandidates.find((command) => focusedChecks.some((check) => check.command === command && check.pass));
3339
+ return {
3340
+ ok: true,
3341
+ profile: {
3342
+ checks,
3343
+ widenableScopes: [...proposal.widenableScopes],
3344
+ ...focusedTestCommand ? { focusedTestCommand } : {}
3345
+ }
3346
+ };
3347
+ }
3348
+ //#endregion
3268
3349
  //#region server/lanes/core/lane-permissions-core.ts
3269
3350
  var ACCEPT_EDITS_MODE = "acceptEdits";
3270
3351
  var LANE_CONFIG_EDIT_DENY_RULES = Object.freeze(["Edit(**/.git/**)", "Edit(**/.claude/**)"]);
@@ -3872,6 +3953,17 @@ var FACTORY_INTEGRATION_MOVED_OUTSIDE = "integration branch moved outside the fa
3872
3953
  function appendFactoryLandedSha(landedShas, landedSha) {
3873
3954
  return [...landedShas.filter((recordedSha) => recordedSha !== landedSha), landedSha].slice(-50);
3874
3955
  }
3956
+ function workerChecksOf(checks) {
3957
+ return checks.filter((command) => !FACTORY_TEST_COMMANDS.some((testCommand) => command === testCommand || command.startsWith(`${testCommand} `)));
3958
+ }
3959
+ var CLOSE_OUT_RUNS_EVERY_CHECK = "Do not run the full test suite; Glimmervoid runs every check in a clean checkout when you stop.";
3960
+ function workerCheckInstruction(checks, focusedTestCommand) {
3961
+ const workerChecks = workerChecksOf(checks);
3962
+ const workerChecksText = workerChecks.length > 0 ? ` and these checks: ${JSON.stringify(workerChecks)}` : "";
3963
+ 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}`;
3964
+ if (workerChecks.length === 0) return CLOSE_OUT_RUNS_EVERY_CHECK;
3965
+ return `Run these checks before finishing: ${JSON.stringify(workerChecks)}. ${CLOSE_OUT_RUNS_EVERY_CHECK}`;
3966
+ }
3875
3967
  function buildWorkerPrompt({ projectName, intent, order, claudeSessionId, checks = DEFAULT_FACTORY_CHECKS, focusedTestCommand }) {
3876
3968
  const orderSections = [
3877
3969
  ["Objective", JSON.stringify(order.opened.objective)],
@@ -3890,7 +3982,7 @@ function buildWorkerPrompt({ projectName, intent, order, claudeSessionId, checks
3890
3982
  "Edit only inside these write scopes. Do not expand the ticket or the intent.",
3891
3983
  `Start by running coherence context ${scopes} --max-bytes 12000`,
3892
3984
  "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)}`,
3985
+ workerCheckInstruction(checks, focusedTestCommand),
3894
3986
  "Never push, never open PRs, never change coherence work state. Glimmervoid closes the ticket.",
3895
3987
  "Run each shell command on its own with no pipes, &&, ; or subshells, because chained commands are denied.",
3896
3988
  "A denied command means \"not allowed here\". Do not retry it another way.",
@@ -4730,7 +4822,15 @@ function registerEphemeralSession({ map, id, sess, closeSessionDataClients, logP
4730
4822
  closeSessionDataClients(id);
4731
4823
  }
4732
4824
  };
4733
- sess.on("exit", removeFromMap);
4825
+ const releaseAfterListeners = () => {
4826
+ removeFromMap();
4827
+ setImmediate(() => sess.destroy());
4828
+ };
4829
+ sess.on("exit", releaseAfterListeners);
4830
+ sess.on("error", () => {
4831
+ if (sess.hasLivePty !== false) return;
4832
+ releaseAfterListeners();
4833
+ });
4734
4834
  const origDestroy = sess.destroy.bind(sess);
4735
4835
  sess.destroy = () => {
4736
4836
  origDestroy();
@@ -4741,19 +4841,32 @@ function registerEphemeralSession({ map, id, sess, closeSessionDataClients, logP
4741
4841
  //#region server/agent-api-wiring.ts
4742
4842
  var AGENT_LANE_TAG = "agent-spawn";
4743
4843
  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();
4844
+ var BYTES_PER_MEGABYTE = 1048576;
4845
+ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoardSessions, makeSession, wireSessionEvents, closeSessionDataClients, broadcastControl, spawnGate, sessionDocs, factory, recordLane = null, memoryAdmission = null, logger = console }) {
4846
+ async function describeMemoryShortfall() {
4847
+ const shortfall = await memoryAdmission?.describeShortfallNow("session") ?? null;
4848
+ if (shortfall === null) return null;
4849
+ const toMegabytes = (bytes) => Math.round(bytes / BYTES_PER_MEGABYTE);
4850
+ 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`] : []];
4851
+ const heldClause = heldDetails.length > 0 ? ` (${heldDetails.join(", ")})` : "";
4852
+ 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`;
4853
+ }
4854
+ function refuseByBudget(parent) {
4747
4855
  const allowance = decideSpawnAllowance({
4748
- ...budget,
4856
+ ...parent.agentSpawnBudget(),
4749
4857
  isFactoryWorker: parent.factoryRole === "worker",
4750
4858
  isEphemeral: parent.ephemeral
4751
4859
  });
4752
- if (!allowance.ok) return {
4860
+ if (allowance.ok) return null;
4861
+ return {
4753
4862
  ok: false,
4754
4863
  status: allowance.status,
4755
4864
  error: allowance.reason
4756
4865
  };
4866
+ }
4867
+ async function spawn(parent, request) {
4868
+ const budgetRefusal = refuseByBudget(parent);
4869
+ if (budgetRefusal) return budgetRefusal;
4757
4870
  if (request.agent && !isKnownAgentId(request.agent)) return {
4758
4871
  ok: false,
4759
4872
  status: 400,
@@ -4765,6 +4878,15 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
4765
4878
  status: 403,
4766
4879
  error: `a guarded session can only spawn a guarded agent, and ${agentId} does not run inside the guardrails`
4767
4880
  };
4881
+ const memoryShortfall = await describeMemoryShortfall();
4882
+ if (memoryShortfall) return {
4883
+ ok: false,
4884
+ status: 503,
4885
+ error: memoryShortfall
4886
+ };
4887
+ const budgetRefusalAfterMemoryRead = refuseByBudget(parent);
4888
+ if (budgetRefusalAfterMemoryRead) return budgetRefusalAfterMemoryRead;
4889
+ const budget = parent.agentSpawnBudget();
4768
4890
  const name = deriveChildSessionName(request.name || parent.name, listAllSessions().map((session) => session.name));
4769
4891
  const id = `${AGENT_LANE_TAG}-${crypto.randomUUID()}`;
4770
4892
  const project = {
@@ -4961,13 +5083,6 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
4961
5083
  }
4962
5084
  };
4963
5085
  }
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
5086
  if (parsed.verb === "board") return {
4972
5087
  status: 200,
4973
5088
  body: {
@@ -5200,6 +5315,14 @@ function resolveSessionSize({ viewers, departingKey, current }) {
5200
5315
  //#endregion
5201
5316
  //#region session/core/screen-keeper-core.ts
5202
5317
  var SCREEN_RESET = "\x1Bc\x1B[2J\x1B[3J\x1B[H";
5318
+ var SCREEN_KEEPER_SCROLLBACK = DEEP_HISTORY_SCROLLBACK_LINES;
5319
+ var SCREEN_KEEPER_SLICE_ROWS = 1e3;
5320
+ var SCREEN_KEEPER_SLICE_JOINER = "\x1B[0m\r\n";
5321
+ var SCREEN_KEEPER_PENDING_CAP = 4194304;
5322
+ var SCREEN_KEEPER_SEED_LIMIT = 1048576;
5323
+ function isKeeperOverrun(pushedOffset, parsedOffset, pendingCap = SCREEN_KEEPER_PENDING_CAP) {
5324
+ return pushedOffset - parsedOffset > pendingCap;
5325
+ }
5203
5326
  function pendingResizes(queue, parsedOffset) {
5204
5327
  let cut = 0;
5205
5328
  while (cut < queue.length) {
@@ -5212,6 +5335,36 @@ function pendingResizes(queue, parsedOffset) {
5212
5335
  rest: queue.slice(cut)
5213
5336
  };
5214
5337
  }
5338
+ function unwrappedLineAtOrBefore(line, isWrapped) {
5339
+ let candidate = line;
5340
+ while (candidate > 0 && isWrapped(candidate)) candidate -= 1;
5341
+ return candidate;
5342
+ }
5343
+ function sliceEndLine(start, limit, sliceRows, isWrapped) {
5344
+ const target = Math.min(start + sliceRows, limit);
5345
+ const unwrappedBefore = unwrappedLineAtOrBefore(target, isWrapped);
5346
+ if (unwrappedBefore > start) return unwrappedBefore;
5347
+ let unwrappedAfter = target;
5348
+ while (unwrappedAfter < limit && isWrapped(unwrappedAfter)) unwrappedAfter += 1;
5349
+ return unwrappedAfter;
5350
+ }
5351
+ function planDeepSlices({ lineCount, rows, sliceRows, isWrapped }) {
5352
+ const finalStart = unwrappedLineAtOrBefore(Math.max(0, lineCount - rows - sliceRows), isWrapped);
5353
+ const ranges = [];
5354
+ let start = 0;
5355
+ while (start < finalStart) {
5356
+ const end = sliceEndLine(start, finalStart, sliceRows, isWrapped);
5357
+ ranges.push({
5358
+ start,
5359
+ end: end - 1
5360
+ });
5361
+ start = end;
5362
+ }
5363
+ return {
5364
+ ranges,
5365
+ finalScrollback: Math.max(0, lineCount - rows - finalStart)
5366
+ };
5367
+ }
5215
5368
  //#endregion
5216
5369
  //#region server/ws-sender.ts
5217
5370
  var OPEN = 1;
@@ -5451,93 +5604,117 @@ function createBackendWebSockets(dependencies) {
5451
5604
  socket.close(1008, "Session not found");
5452
5605
  return;
5453
5606
  }
5454
- let viewerSizes = sessionDataClients.get(sessionId);
5455
- if (!viewerSizes) {
5456
- viewerSizes = /* @__PURE__ */ new Map();
5457
- sessionDataClients.set(sessionId, viewerSizes);
5607
+ socket.on("error", (error) => reportDataSocketError(session, error));
5608
+ if (parseDataAttachHistory(request.url) === "screen") {
5609
+ wireDataSocket(socket, session, sessionId, session.getScreenSnapshot());
5610
+ return;
5458
5611
  }
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);
5612
+ const messagesBeforeWiring = [];
5613
+ const holdMessage = (raw) => {
5614
+ messagesBeforeWiring.push(raw);
5615
+ };
5616
+ socket.on("message", holdMessage);
5617
+ session.getDeepScreenSnapshot().then((snapshot) => {
5618
+ socket.off("message", holdMessage);
5619
+ if (socket.readyState !== WebSocket.OPEN) return;
5620
+ if (getSession(sessionId) !== session) {
5621
+ socket.close(1008, "Session not found");
5506
5622
  return;
5507
5623
  }
5508
- if (message.type === "claim") {
5509
- nextViewerResizeSeq += 1;
5510
- viewerSizeMap.set(socket, {
5511
- cols: message.cols,
5512
- rows: message.rows,
5513
- resizeSeq: nextViewerResizeSeq
5624
+ wireDataSocket(socket, session, sessionId, snapshot);
5625
+ for (const raw of messagesBeforeWiring) socket.emit("message", raw, false);
5626
+ }, (error) => {
5627
+ logger.warn(`[data-ws] Deep history failed for ${session.name}: ${String(error)}`);
5628
+ socket.close(1011, "History unavailable");
5629
+ });
5630
+ });
5631
+ }
5632
+ function reportDataSocketError(session, error) {
5633
+ const reason = error.code === "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH" ? "Message too large - try pasting smaller chunks" : error.message;
5634
+ logger.warn(`[data-ws] Error for ${session.name}: ${error.message}`);
5635
+ broadcastControl({
5636
+ type: "session-error",
5637
+ id: session.id,
5638
+ session: session.name,
5639
+ message: reason,
5640
+ timestamp: Date.now()
5641
+ });
5642
+ }
5643
+ function wireDataSocket(socket, session, sessionId, snapshot) {
5644
+ let viewerSizes = sessionDataClients.get(sessionId);
5645
+ if (!viewerSizes) {
5646
+ viewerSizes = /* @__PURE__ */ new Map();
5647
+ sessionDataClients.set(sessionId, viewerSizes);
5648
+ }
5649
+ const activeSessionId = sessionId;
5650
+ const viewerSizeMap = viewerSizes;
5651
+ viewerSizeMap.set(socket, null);
5652
+ const applyResolvedSize = (departing) => {
5653
+ const resolved = resolveSessionSize({
5654
+ viewers: viewerSizeMap,
5655
+ ...departing ? { departingKey: socket } : {},
5656
+ current: session.ptySize()
5657
+ });
5658
+ if (!resolved.changed) return;
5659
+ session.resize(resolved.cols, resolved.rows);
5660
+ };
5661
+ const releaseViewerSize = () => {
5662
+ if (!viewerSizeMap.get(socket)) return;
5663
+ viewerSizeMap.set(socket, null);
5664
+ if (sessionDataClients.get(activeSessionId) !== viewerSizeMap) return;
5665
+ applyResolvedSize(true);
5666
+ };
5667
+ const sender = createWsSender(socket, {
5668
+ source: { getBufferSince: (offset) => session.getBufferSince(offset) },
5669
+ startOffset: snapshot.offset
5670
+ });
5671
+ sendPtySizeFrame(sender, session.ptySize());
5672
+ if (snapshot.data) sender.sendImmediate(snapshot.data, 0);
5673
+ const dataListener = (data) => sender.onData(data);
5674
+ session.on("data", dataListener);
5675
+ const resizeListener = (size) => sendPtySizeFrame(sender, size);
5676
+ session.on("resize", resizeListener);
5677
+ socket.on("message", (raw) => {
5678
+ const message = parseDataClientMessage(String(raw));
5679
+ if (!message) return;
5680
+ if (message.type === "input") {
5681
+ if (message.data.length > 16384) {
5682
+ logger.warn(`[data-ws] Rejected oversized input (${message.data.length} chars) for ${session.name}`);
5683
+ broadcastControl({
5684
+ type: "session-error",
5685
+ id: session.id,
5686
+ session: session.name,
5687
+ message: "Paste too large - try pasting smaller chunks",
5688
+ timestamp: Date.now()
5514
5689
  });
5515
- applyResolvedSize(false);
5516
5690
  return;
5517
5691
  }
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()
5692
+ session.write(message.data);
5693
+ sender.markInputFlush();
5694
+ session.noteOperatorInput(message.data);
5695
+ return;
5696
+ }
5697
+ if (message.type === "claim") {
5698
+ nextViewerResizeSeq += 1;
5699
+ viewerSizeMap.set(socket, {
5700
+ cols: message.cols,
5701
+ rows: message.rows,
5702
+ resizeSeq: nextViewerResizeSeq
5529
5703
  });
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
- });
5704
+ applyResolvedSize(false);
5705
+ return;
5706
+ }
5707
+ if (message.type === "unview") releaseViewerSize();
5708
+ });
5709
+ socket.on("close", () => {
5710
+ session.removeListener("data", dataListener);
5711
+ session.removeListener("resize", resizeListener);
5712
+ sender.destroy();
5713
+ releaseViewerSize();
5714
+ const clients = sessionDataClients.get(activeSessionId);
5715
+ if (!clients) return;
5716
+ clients.delete(socket);
5717
+ if (clients.size === 0) sessionDataClients.delete(activeSessionId);
5541
5718
  });
5542
5719
  }
5543
5720
  function handleUpgrade(request, socket, head) {
@@ -5622,6 +5799,7 @@ function createBackendShutdown(dependencies) {
5622
5799
  isShuttingDown = true;
5623
5800
  const stoppers = createStopperCollector();
5624
5801
  dependencies.cancelAutoResume();
5802
+ dependencies.cancelPostTurnLeftoverCleanup?.();
5625
5803
  clearInterval(dependencies.healthInterval);
5626
5804
  const stopConfigWatch = dependencies.getStopConfigWatch();
5627
5805
  if (stopConfigWatch) stopConfigWatch();
@@ -6561,7 +6739,7 @@ function runGh(args, options = {}) {
6561
6739
  }
6562
6740
  //#endregion
6563
6741
  //#region server/post-turn-checker.ts
6564
- var GIT_TIMEOUT_MS$1 = 5e3;
6742
+ var GIT_TIMEOUT_MS$2 = 5e3;
6565
6743
  var DEFAULTS = Object.freeze({
6566
6744
  enabled: true,
6567
6745
  mode: "report",
@@ -6597,9 +6775,7 @@ var DEFAULTS = Object.freeze({
6597
6775
  ],
6598
6776
  maxFiles: 200,
6599
6777
  maxFileBytes: 524288,
6600
- debounceMs: 1500,
6601
- runOnExit: false,
6602
- reportDir: ".glimmervoid/post-turn"
6778
+ debounceMs: 1500
6603
6779
  });
6604
6780
  function explicitMode(mode) {
6605
6781
  return mode === "fix" ? "fix" : "report";
@@ -6630,9 +6806,7 @@ var SCALAR_KEYS = [
6630
6806
  "mode",
6631
6807
  "maxFiles",
6632
6808
  "maxFileBytes",
6633
- "debounceMs",
6634
- "runOnExit",
6635
- "reportDir"
6809
+ "debounceMs"
6636
6810
  ];
6637
6811
  function resolveCheckConfig(globalCfg, projectCfg) {
6638
6812
  const base = JSON.parse(JSON.stringify(DEFAULTS));
@@ -6654,16 +6828,14 @@ function resolveCheckConfig(globalCfg, projectCfg) {
6654
6828
  exclude: base.exclude,
6655
6829
  maxFiles: base.maxFiles,
6656
6830
  maxFileBytes: base.maxFileBytes,
6657
- debounceMs: base.debounceMs,
6658
- runOnExit: !!base.runOnExit,
6659
- reportDir: base.reportDir
6831
+ debounceMs: base.debounceMs
6660
6832
  };
6661
6833
  }
6662
6834
  var GIT_MAX_BUFFER_BYTES$1 = 8388608;
6663
6835
  async function execGit(args, cwd) {
6664
6836
  const result = await runGit(args, {
6665
6837
  cwd,
6666
- timeoutMs: GIT_TIMEOUT_MS$1,
6838
+ timeoutMs: GIT_TIMEOUT_MS$2,
6667
6839
  maxBuffer: GIT_MAX_BUFFER_BYTES$1,
6668
6840
  trim: false
6669
6841
  });
@@ -6694,7 +6866,7 @@ async function listChangedFiles(root) {
6694
6866
  }
6695
6867
  return files;
6696
6868
  }
6697
- async function runPostTurnChecks({ cwd, config, sessionId, deps = {} } = {}) {
6869
+ async function runPostTurnChecks({ cwd, config, deps = {} } = {}) {
6698
6870
  const started = Date.now();
6699
6871
  const cfg = config || resolveCheckConfig();
6700
6872
  const report = {
@@ -6794,14 +6966,7 @@ async function runPostTurnChecks({ cwd, config, sessionId, deps = {} } = {}) {
6794
6966
  });
6795
6967
  }
6796
6968
  }
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;
6969
+ return done();
6805
6970
  }
6806
6971
  //#endregion
6807
6972
  //#region server/session/session-event-wiring.ts
@@ -6879,7 +7044,9 @@ function createSessionEventWiring(dependencies) {
6879
7044
  duration_seconds: durationSeconds
6880
7045
  });
6881
7046
  };
6882
- session.on("claude-session-id", ({ id, vendor, isResumeTarget }) => {
7047
+ let liveTranscriptPath = null;
7048
+ session.on("claude-session-id", ({ id, vendor, isResumeTarget, transcriptPath }) => {
7049
+ liveTranscriptPath = transcriptPath;
6883
7050
  if (isResumeTarget) persistProjectField("resumeSessionId", id);
6884
7051
  dependencies.recordLane(id, INTERACTIVE_LANE, vendor);
6885
7052
  dependencies.usage.refreshSessions();
@@ -6912,6 +7079,7 @@ function createSessionEventWiring(dependencies) {
6912
7079
  session.on("teardown", () => {
6913
7080
  clearCompactionConfirm();
6914
7081
  isAcknowledgeDeferredForCompaction = false;
7082
+ dependencies.getIngestLane()?.detachSessionTap(session);
6915
7083
  });
6916
7084
  const resolvePostTurn = () => {
6917
7085
  const project = dependencies.config.projects.find((candidate) => candidate.id === session.id);
@@ -6927,8 +7095,7 @@ function createSessionEventWiring(dependencies) {
6927
7095
  if (!runConfig?.enabled) return;
6928
7096
  runPostTurnChecks({
6929
7097
  cwd: session.effectiveCwd(),
6930
- config: runConfig,
6931
- sessionId: session.id
7098
+ config: runConfig
6932
7099
  }).then((report) => {
6933
7100
  dependencies.broadcastControl({
6934
7101
  type: "post-turn-result",
@@ -6940,7 +7107,7 @@ function createSessionEventWiring(dependencies) {
6940
7107
  }).catch((error) => dependencies.logger.warn(`[${session.name}] post-turn checks failed: ${errorMessage(error)}`));
6941
7108
  }, config.debounceMs);
6942
7109
  });
6943
- session.on("post-turn-check", () => dependencies.usage.nudgeSession());
7110
+ session.on("post-turn-check", () => dependencies.usage.nudgeSession(liveTranscriptPath));
6944
7111
  session.on("state-change", ({ from, to, event, detail }) => {
6945
7112
  if (to === STATES.IDLE || to === STATES.COMPLETE) dependencies.taskTitleRefiner?.onTurnEnd(session);
6946
7113
  if (event === "spawn_success") {
@@ -7038,8 +7205,6 @@ function createSessionEventWiring(dependencies) {
7038
7205
  relay("wakeup-change", "session-wakeup");
7039
7206
  relay("prompt-kind-change", "session-prompt");
7040
7207
  relay("conversation-change", "session-conversation");
7041
- relay("sleep", "session-sleep");
7042
- relay("wake", "session-wake");
7043
7208
  session.on("merge-status", ({ mergeStatus, reason, parked }) => {
7044
7209
  dependencies.broadcastControl({
7045
7210
  type: "session-merge-status",
@@ -7392,6 +7557,9 @@ function runAutoResume(sessions, config, spawnGate, logger = console) {
7392
7557
  runs.push(spawnGate.run(() => {
7393
7558
  if (session.state !== STATES.DORMANT) return null;
7394
7559
  return session.start({ resumePrompt });
7560
+ }, {
7561
+ isStillWanted: () => session.state === STATES.DORMANT,
7562
+ onMemoryWait: (isWaiting) => session.setWaitingForMemory(isWaiting)
7395
7563
  }).catch((error) => {
7396
7564
  logger.warn(`[boot] auto-resume failed for ${session.name}: ${errorMessage(error)}`);
7397
7565
  }));
@@ -12732,22 +12900,33 @@ var DEFAULT_STABILIZATION_MS = 1500;
12732
12900
  var OSC_START = "\x1B]0;";
12733
12901
  var BEL = "\x07";
12734
12902
  var ST = "\x1B\\";
12735
- function findOscTitle(buffer, fromIndex) {
12903
+ var MAX_PENDING_TITLE = 4096;
12904
+ function nextTerminatorIndex(buffer, terminator, previousIndex, fromIndex) {
12905
+ if (previousIndex >= fromIndex) return previousIndex;
12906
+ const index = buffer.indexOf(terminator, fromIndex);
12907
+ return index === -1 ? Number.POSITIVE_INFINITY : index;
12908
+ }
12909
+ function findOscTitle(buffer, fromIndex, terminators) {
12736
12910
  const start = buffer.indexOf(OSC_START, fromIndex);
12737
12911
  if (start === -1) return null;
12738
12912
  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;
12913
+ terminators.bel = nextTerminatorIndex(buffer, BEL, terminators.bel, contentStart);
12914
+ terminators.st = nextTerminatorIndex(buffer, ST, terminators.st, contentStart);
12915
+ const end = Math.min(terminators.bel, terminators.st);
12916
+ if (end === Number.POSITIVE_INFINITY) return null;
12744
12917
  return {
12745
12918
  start,
12746
12919
  end,
12747
- next: end + (useBel ? 1 : 2),
12920
+ next: end + (end === terminators.bel ? 1 : 2),
12748
12921
  title: buffer.slice(contentStart, end)
12749
12922
  };
12750
12923
  }
12924
+ function unterminatedTail(buffer, fromIndex) {
12925
+ const openStart = buffer.indexOf(OSC_START, fromIndex);
12926
+ if (openStart !== -1) return buffer.length - openStart > MAX_PENDING_TITLE ? "" : buffer.slice(openStart);
12927
+ for (let prefixLength = 3; prefixLength > 0; prefixLength -= 1) if (buffer.endsWith(OSC_START.slice(0, prefixLength))) return buffer.slice(buffer.length - prefixLength);
12928
+ return "";
12929
+ }
12751
12930
  var OscTitleSource = class extends EventEmitter {
12752
12931
  _stabilizationMs;
12753
12932
  _profile;
@@ -12774,16 +12953,19 @@ var OscTitleSource = class extends EventEmitter {
12774
12953
  }
12775
12954
  feed(chunk) {
12776
12955
  if (this._destroyed || !chunk) return;
12777
- this._pending += chunk;
12778
- if (this._pending.length > 8192) this._pending = this._pending.slice(-4096);
12956
+ const buffer = this._pending ? this._pending + chunk : chunk;
12957
+ const terminators = {
12958
+ bel: -1,
12959
+ st: -1
12960
+ };
12779
12961
  let cursor = 0;
12780
12962
  while (true) {
12781
- const osc = findOscTitle(this._pending, cursor);
12963
+ const osc = findOscTitle(buffer, cursor, terminators);
12782
12964
  if (!osc) break;
12783
12965
  this._processTitle(osc.title);
12784
12966
  cursor = osc.next;
12785
12967
  }
12786
- if (cursor > 0) this._pending = this._pending.slice(cursor);
12968
+ this._pending = unterminatedTail(buffer, cursor);
12787
12969
  }
12788
12970
  _classifyTitle(trimmed, char) {
12789
12971
  const profile = this._profile;
@@ -13532,7 +13714,7 @@ async function startCodexAppServerBridge(options) {
13532
13714
  clearTimeout(killTimer);
13533
13715
  }
13534
13716
  } finally {
13535
- if (socket && socket.readyState !== WebSocket.CLOSED) guard(() => socket?.terminate());
13717
+ if (socket && socket.readyState !== WebSocket$1.CLOSED) guard(() => socket?.terminate());
13536
13718
  await rm(privateDirectory, {
13537
13719
  recursive: true,
13538
13720
  force: true
@@ -13542,7 +13724,7 @@ async function startCodexAppServerBridge(options) {
13542
13724
  return stopPromise;
13543
13725
  }
13544
13726
  function send(message) {
13545
- if (isStopped || socket?.readyState !== WebSocket.OPEN) throw new Error("Codex app-server socket is not open");
13727
+ if (isStopped || socket?.readyState !== WebSocket$1.OPEN) throw new Error("Codex app-server socket is not open");
13546
13728
  socket.send(JSON.stringify(message));
13547
13729
  }
13548
13730
  function request(method, params, timeoutMs = 15e3) {
@@ -13572,7 +13754,7 @@ async function startCodexAppServerBridge(options) {
13572
13754
  });
13573
13755
  }
13574
13756
  function answerApproval(requestId, decision) {
13575
- if (!activeApproval || activeApproval.requestId !== requestId || isStopped || socket?.readyState !== WebSocket.OPEN) return false;
13757
+ if (!activeApproval || activeApproval.requestId !== requestId || isStopped || socket?.readyState !== WebSocket$1.OPEN) return false;
13576
13758
  try {
13577
13759
  if (activeApproval.kind === "permissions") {
13578
13760
  if (!approvalPermissions) return false;
@@ -13762,7 +13944,7 @@ async function startCodexAppServerBridge(options) {
13762
13944
  function connect(url) {
13763
13945
  if (socket || isStopped) return;
13764
13946
  bridge.url = url;
13765
- socket = options.createSocket?.(url, { Authorization: `Bearer ${token}` }) ?? new WebSocket(url, { headers: { Authorization: `Bearer ${token}` } });
13947
+ socket = options.createSocket?.(url, { Authorization: `Bearer ${token}` }) ?? new WebSocket$1(url, { headers: { Authorization: `Bearer ${token}` } });
13766
13948
  socket.on("error", (failure) => guard(() => reportError(failure)));
13767
13949
  socket.on("message", (frame, isBinary) => guard(() => handleMessage(frame, isBinary)));
13768
13950
  socket.on("close", () => guard(() => {
@@ -14694,11 +14876,16 @@ function serializeMouseEncoding(encoding) {
14694
14876
  //#region session/screen-keeper.ts
14695
14877
  var { Terminal } = headless;
14696
14878
  var { SerializeAddon } = createRequire(import.meta.url)("@xterm/addon-serialize");
14697
- function createScreenKeeper({ cols, rows }) {
14879
+ function yieldToEventLoop$2() {
14880
+ return new Promise((resolve) => {
14881
+ setImmediate(resolve);
14882
+ });
14883
+ }
14884
+ function createScreenKeeper({ cols, rows }, { pendingCap = SCREEN_KEEPER_PENDING_CAP, sliceRows = SCREEN_KEEPER_SLICE_ROWS } = {}) {
14698
14885
  const terminal = new Terminal({
14699
14886
  cols,
14700
14887
  rows,
14701
- scrollback: 500,
14888
+ scrollback: SCREEN_KEEPER_SCROLLBACK,
14702
14889
  allowProposedApi: true
14703
14890
  });
14704
14891
  const serializer = new SerializeAddon();
@@ -14716,25 +14903,106 @@ function createScreenKeeper({ cols, rows }) {
14716
14903
  return false;
14717
14904
  }));
14718
14905
  let pushedOffset = 0;
14906
+ let writtenOffset = 0;
14719
14907
  let parsedOffset = 0;
14720
14908
  let queue = [];
14721
14909
  let disposed = false;
14910
+ let overrun = false;
14911
+ let heldChunks = [];
14912
+ let isHoldingWrites = false;
14913
+ let isBufferFrozen = false;
14914
+ let drainWaiters = [];
14915
+ let deepSerializeInFlight = null;
14722
14916
  function drainResizes() {
14723
- if (disposed) return;
14917
+ if (disposed || isBufferFrozen) return;
14724
14918
  const split = pendingResizes(queue, parsedOffset);
14725
14919
  queue = split.rest;
14726
14920
  for (const marker of split.due) terminal.resize(marker.cols, marker.rows);
14727
14921
  }
14922
+ function settleDrainWaiters() {
14923
+ const waiters = drainWaiters;
14924
+ drainWaiters = [];
14925
+ for (const resolve of waiters) resolve();
14926
+ }
14927
+ function writeToTerminal(chunk) {
14928
+ writtenOffset += chunk.length;
14929
+ const reachedOffset = writtenOffset;
14930
+ terminal.write(chunk, () => {
14931
+ if (disposed) return;
14932
+ parsedOffset = reachedOffset;
14933
+ drainResizes();
14934
+ if (parsedOffset === writtenOffset) settleDrainWaiters();
14935
+ });
14936
+ }
14937
+ function waitForWrittenBytesToParse() {
14938
+ if (disposed || parsedOffset === writtenOffset) return Promise.resolve();
14939
+ return new Promise((resolve) => {
14940
+ drainWaiters.push(resolve);
14941
+ });
14942
+ }
14943
+ function releaseHeldWrites() {
14944
+ isBufferFrozen = false;
14945
+ isHoldingWrites = false;
14946
+ if (disposed) return;
14947
+ drainResizes();
14948
+ const chunks = heldChunks;
14949
+ heldChunks = [];
14950
+ for (const chunk of chunks) writeToTerminal(chunk);
14951
+ }
14952
+ function serializeAttach() {
14953
+ return serializer.serialize({ scrollback: 500 }) + serializeMouseEncoding(mouseEncoding);
14954
+ }
14955
+ async function serializeFrozenSlices() {
14956
+ const buffer = terminal.buffer.normal;
14957
+ const plan = planDeepSlices({
14958
+ lineCount: buffer.length,
14959
+ rows: terminal.rows,
14960
+ sliceRows,
14961
+ isWrapped: (line) => buffer.getLine(line)?.isWrapped ?? false
14962
+ });
14963
+ const slices = [];
14964
+ for (const range of plan.ranges) {
14965
+ slices.push(serializer.serialize({
14966
+ range,
14967
+ excludeAltBuffer: true,
14968
+ excludeModes: true
14969
+ }));
14970
+ await yieldToEventLoop$2();
14971
+ if (disposed) return null;
14972
+ }
14973
+ slices.push(serializer.serialize({ scrollback: plan.finalScrollback }) + serializeMouseEncoding(mouseEncoding));
14974
+ return {
14975
+ data: slices.join(SCREEN_KEEPER_SLICE_JOINER),
14976
+ parsedOffset
14977
+ };
14978
+ }
14979
+ async function serializeDeepHoldingWrites() {
14980
+ isHoldingWrites = true;
14981
+ try {
14982
+ await waitForWrittenBytesToParse();
14983
+ if (disposed) return null;
14984
+ isBufferFrozen = true;
14985
+ return await serializeFrozenSlices();
14986
+ } finally {
14987
+ releaseHeldWrites();
14988
+ deepSerializeInFlight = null;
14989
+ }
14990
+ }
14728
14991
  return {
14729
14992
  push(chunk) {
14730
- if (disposed || chunk.length === 0) return;
14993
+ if (disposed || overrun) return false;
14994
+ if (chunk.length === 0) return true;
14995
+ if (isKeeperOverrun(pushedOffset, parsedOffset, pendingCap)) {
14996
+ overrun = true;
14997
+ return false;
14998
+ }
14731
14999
  pushedOffset += chunk.length;
14732
- const reachedOffset = pushedOffset;
14733
- terminal.write(chunk, () => {
14734
- if (disposed) return;
14735
- parsedOffset = reachedOffset;
14736
- drainResizes();
14737
- });
15000
+ if (isHoldingWrites) {
15001
+ heldChunks.push(chunk);
15002
+ return true;
15003
+ }
15004
+ writeToTerminal(chunk);
15005
+ return true;
14738
15006
  },
14739
15007
  resize(nextCols, nextRows) {
14740
15008
  if (disposed) return;
@@ -14746,11 +15014,21 @@ function createScreenKeeper({ cols, rows }) {
14746
15014
  drainResizes();
14747
15015
  },
14748
15016
  parsedOffset: () => parsedOffset,
14749
- serialize: () => serializer.serialize({ scrollback: 500 }) + serializeMouseEncoding(mouseEncoding),
15017
+ isOverrun: () => overrun,
15018
+ isDrained: () => parsedOffset === pushedOffset,
15019
+ serialize: serializeAttach,
15020
+ serializeDeep() {
15021
+ if (disposed) return Promise.resolve(null);
15022
+ if (deepSerializeInFlight) return deepSerializeInFlight;
15023
+ deepSerializeInFlight = serializeDeepHoldingWrites();
15024
+ return deepSerializeInFlight;
15025
+ },
14750
15026
  dispose() {
14751
15027
  if (disposed) return;
14752
15028
  disposed = true;
14753
15029
  queue = [];
15030
+ heldChunks = [];
15031
+ settleDrainWaiters();
14754
15032
  for (const handler of mouseEncodingHandlers) handler.dispose();
14755
15033
  serializer.dispose();
14756
15034
  terminal.dispose();
@@ -14838,31 +15116,44 @@ function createSessionOutput(options) {
14838
15116
  return changed;
14839
15117
  }
14840
15118
  function push(chunk) {
14841
- if (!keeper) {
14842
- keeperBaseOffset = ring.stats().total;
14843
- keeper = keeperFactory(ptySize());
14844
- }
14845
- keeper.push(chunk);
14846
15119
  ring.push(chunk);
15120
+ if (keeper?.push(chunk)) return;
15121
+ if (keeper && !keeper.isDrained()) return;
15122
+ disposeScreenKeeper();
15123
+ const { total, bytes } = ring.stats();
15124
+ keeperBaseOffset = Math.max(total - bytes, total - SCREEN_KEEPER_SEED_LIMIT);
15125
+ keeper = keeperFactory(ptySize());
15126
+ keeper.push(ring.since(keeperBaseOffset).data);
14847
15127
  }
14848
15128
  function reset() {
14849
15129
  disposeScreenKeeper();
14850
15130
  ring.reset();
14851
15131
  }
14852
- function snapshot() {
14853
- const offset = ring.stats().total;
14854
- if (!keeper) return {
15132
+ function ringSnapshot() {
15133
+ return {
14855
15134
  data: SCREEN_RESET + ring.replay(),
14856
- offset
15135
+ offset: ring.stats().total
14857
15136
  };
15137
+ }
15138
+ function snapshot() {
15139
+ if (!keeper || keeper.isOverrun()) return ringSnapshot();
14858
15140
  const tail = ring.since(keeperBaseOffset + keeper.parsedOffset());
14859
- if (tail.evicted) return {
14860
- data: SCREEN_RESET + ring.replay(),
14861
- offset
14862
- };
15141
+ if (tail.evicted) return ringSnapshot();
14863
15142
  return {
14864
15143
  data: SCREEN_RESET + keeper.serialize() + tail.data,
14865
- offset
15144
+ offset: ring.stats().total
15145
+ };
15146
+ }
15147
+ async function deepSnapshot() {
15148
+ const deepKeeper = keeper;
15149
+ if (!deepKeeper || deepKeeper.isOverrun()) return ringSnapshot();
15150
+ const deepScreen = await deepKeeper.serializeDeep();
15151
+ if (!deepScreen || keeper !== deepKeeper) return ringSnapshot();
15152
+ const tail = ring.since(keeperBaseOffset + deepScreen.parsedOffset);
15153
+ if (tail.evicted) return ringSnapshot();
15154
+ return {
15155
+ data: SCREEN_RESET + deepScreen.data + tail.data,
15156
+ offset: ring.stats().total
14866
15157
  };
14867
15158
  }
14868
15159
  options.on("exit", disposeScreenKeeper);
@@ -14870,6 +15161,7 @@ function createSessionOutput(options) {
14870
15161
  push,
14871
15162
  since: (offset) => ring.since(offset),
14872
15163
  snapshot,
15164
+ deepSnapshot,
14873
15165
  reset,
14874
15166
  setMax: (bytes) => ring.setMax(bytes),
14875
15167
  stats: () => ring.stats(),
@@ -21148,7 +21440,6 @@ function createSessionBackgroundTracking({ detectBackgroundAgents, agentTtlMs, s
21148
21440
  var KILL_POLL_INTERVAL_MS = 200;
21149
21441
  var KILL_MAX_WAIT_MS = 3e3;
21150
21442
  var KILL_REAP_MAX_WAIT_MS = 2400;
21151
- var SLEEP_KILL_TIMEOUT_MS = 9e5;
21152
21443
  var MAX_PENDING_ATTENTION_NOTES = 5;
21153
21444
  var GUARDRAILS_UNAPPLIED_ERROR = "guardrail settings could not be applied, session not started";
21154
21445
  var GUARDRAILS_REFUSED_PREFIX = "session not started, the guardrails refused: ";
@@ -21203,13 +21494,10 @@ var Session = class extends EventEmitter {
21203
21494
  _receivedFirstOutput;
21204
21495
  _killPollTimer;
21205
21496
  _killReapTimer;
21206
- _sleepKillTimer;
21207
21497
  _titleQuietFallbackTimer;
21208
21498
  _resolveKillReap;
21209
21499
  _killReap;
21210
21500
  _exitReap;
21211
- _sleeping;
21212
- _autoKilled;
21213
21501
  _destroyed;
21214
21502
  _pendingRestart;
21215
21503
  _finishing;
@@ -21223,6 +21511,7 @@ var Session = class extends EventEmitter {
21223
21511
  _lastSignal;
21224
21512
  _compaction;
21225
21513
  _pendingPromptKind;
21514
+ _isWaitingForMemory;
21226
21515
  _pendingPromptToolName;
21227
21516
  _pendingAttentionNotes;
21228
21517
  _titleQuiet;
@@ -21292,9 +21581,6 @@ var Session = class extends EventEmitter {
21292
21581
  this._resolveKillReap = null;
21293
21582
  this._killReap = null;
21294
21583
  this._exitReap = null;
21295
- this._sleeping = false;
21296
- this._sleepKillTimer = null;
21297
- this._autoKilled = false;
21298
21584
  this._destroyed = false;
21299
21585
  this._pendingRestart = false;
21300
21586
  this._finishing = false;
@@ -21346,6 +21632,7 @@ var Session = class extends EventEmitter {
21346
21632
  this._lastSignal = null;
21347
21633
  this._compaction = createCompactionTracking();
21348
21634
  this._pendingPromptKind = null;
21635
+ this._isWaitingForMemory = false;
21349
21636
  this._pendingPromptToolName = null;
21350
21637
  this._pendingAttentionNotes = [];
21351
21638
  this._titleQuiet = false;
@@ -21717,10 +22004,16 @@ var Session = class extends EventEmitter {
21717
22004
  });
21718
22005
  if (this._compaction.isCompacting !== wasCompacting) this._emitSessionDetail();
21719
22006
  }
22007
+ setWaitingForMemory(isWaiting) {
22008
+ if (isWaiting === this._isWaitingForMemory) return;
22009
+ this._isWaitingForMemory = isWaiting;
22010
+ this._emitSessionDetail();
22011
+ }
21720
22012
  _emitSessionDetail() {
21721
22013
  this.emit("prompt-kind-change", {
21722
22014
  pendingPromptKind: this._pendingPromptKind,
21723
- isCompacting: this._compaction.isCompacting
22015
+ isCompacting: this._compaction.isCompacting,
22016
+ isWaitingForMemory: this._isWaitingForMemory
21724
22017
  });
21725
22018
  }
21726
22019
  _finishCompaction() {
@@ -21907,9 +22200,6 @@ var Session = class extends EventEmitter {
21907
22200
  this._pendingAttentionNotes = [];
21908
22201
  this._raiseAttention(releasing.join(" | "));
21909
22202
  }
21910
- get sleeping() {
21911
- return this._sleeping;
21912
- }
21913
22203
  get hasLivePty() {
21914
22204
  return !this._destroyed && !!this.ptyProcess && !!this._ptyAlive;
21915
22205
  }
@@ -22051,8 +22341,6 @@ var Session = class extends EventEmitter {
22051
22341
  id: this.id,
22052
22342
  name: this.name,
22053
22343
  state: this.state,
22054
- sleeping: this._sleeping,
22055
- autoKilled: this._autoKilled,
22056
22344
  destroyed: this._destroyed,
22057
22345
  pendingRestart: this._pendingRestart,
22058
22346
  hasPty: this.ptyProcess !== null,
@@ -22063,10 +22351,7 @@ var Session = class extends EventEmitter {
22063
22351
  auditLogLength: this.auditLog.length,
22064
22352
  dataListenerCount: this.listenerCount("data"),
22065
22353
  hookSeen: this._hookSeen,
22066
- timers: {
22067
- sleepKill: this._sleepKillTimer !== null,
22068
- killPoll: this._killPollTimer !== null
22069
- }
22354
+ timers: { killPoll: this._killPollTimer !== null }
22070
22355
  };
22071
22356
  }
22072
22357
  getDebugState() {
@@ -22084,7 +22369,6 @@ var Session = class extends EventEmitter {
22084
22369
  agent: this.agentId,
22085
22370
  state: this.state,
22086
22371
  stateSince: this.stateSince,
22087
- sleeping: this._sleeping,
22088
22372
  dangerouslySkipPermissions: this.dangerouslySkipPermissions,
22089
22373
  saneYolo: this.saneYolo,
22090
22374
  ephemeral: this.ephemeral,
@@ -22100,6 +22384,7 @@ var Session = class extends EventEmitter {
22100
22384
  hasConversationBridge: this._codexBridge !== null,
22101
22385
  isConversationViewOn: this._isConversationViewEnabled(),
22102
22386
  isCompacting: this._compaction.isCompacting,
22387
+ isWaitingForMemory: this._isWaitingForMemory,
22103
22388
  hasPlan: this._planReviewPort?.hasPlan(this.id) === true,
22104
22389
  mergeStatus: this.mergeStatus,
22105
22390
  mergeReason: this.mergeReason,
@@ -22180,6 +22465,7 @@ var Session = class extends EventEmitter {
22180
22465
  }
22181
22466
  async _startBody({ fresh = false, resumePrompt = null } = {}) {
22182
22467
  if (this._destroyed) return;
22468
+ this.setWaitingForMemory(false);
22183
22469
  if (this.ptyProcess) {
22184
22470
  console.warn(`[session:${this.name}] start() called while PTY exists - killing previous PTY first`);
22185
22471
  const oldPid = this.ptyProcess.pid;
@@ -22201,9 +22487,7 @@ var Session = class extends EventEmitter {
22201
22487
  }
22202
22488
  if (this.state === STATES.DORMANT) this.transition("user_start");
22203
22489
  this._receivedFirstOutput = false;
22204
- this._sleeping = false;
22205
22490
  this._clearDetectionTracking();
22206
- this._autoKilled = false;
22207
22491
  this._output.reset();
22208
22492
  this.emit("rebaseline");
22209
22493
  this._resetDetectionSources({ quiet: false });
@@ -22555,7 +22839,7 @@ var Session = class extends EventEmitter {
22555
22839
  this._receivedFirstOutput = true;
22556
22840
  this.transition("first_output");
22557
22841
  }
22558
- if (!this._sleeping) try {
22842
+ try {
22559
22843
  this._titleSource.feed(data);
22560
22844
  } catch (err) {
22561
22845
  console.error(`[session:${this.name}] title source error: ${errorMessage(err)}`);
@@ -22599,6 +22883,9 @@ var Session = class extends EventEmitter {
22599
22883
  getScreenSnapshot() {
22600
22884
  return this._output.snapshot();
22601
22885
  }
22886
+ getDeepScreenSnapshot() {
22887
+ return this._output.deepSnapshot();
22888
+ }
22602
22889
  ptySize() {
22603
22890
  const size = this._output.ptySize();
22604
22891
  return {
@@ -22759,36 +23046,9 @@ var Session = class extends EventEmitter {
22759
23046
  if (!DISMISSIBLE_STATES.has(this.state)) return false;
22760
23047
  return this.transition("user_dismiss");
22761
23048
  }
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
23049
  _teardownPending() {
22779
23050
  return this._pendingRestart || this._finishing;
22780
23051
  }
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
23052
  killSession() {
22793
23053
  if (!KILLABLE_STATES.includes(this.state)) return false;
22794
23054
  this.kill();
@@ -22876,7 +23136,6 @@ var Session = class extends EventEmitter {
22876
23136
  this._agentToken = null;
22877
23137
  this._commitSigningToken = null;
22878
23138
  this._pendingAttentionNotes = [];
22879
- this._clearSleepKill();
22880
23139
  this._clearPendingPaste();
22881
23140
  this._hooks.cleanup();
22882
23141
  this._disposeGuardrailsLaunch(this._guardrailsLaunch);
@@ -23007,6 +23266,20 @@ function appendTrailStep(trail, step, maxSteps = TRAIL_MAX_STEPS) {
23007
23266
  };
23008
23267
  }
23009
23268
  //#endregion
23269
+ //#region shared/contracts/guardrails-launch.ts
23270
+ var GuardrailsLaunchLogin = z.object({
23271
+ linkPath: z.string().min(1),
23272
+ targetPath: z.string().min(1)
23273
+ }).strict();
23274
+ var GuardrailsLaunchOwner = z.object({
23275
+ ownerPid: z.number().int().positive(),
23276
+ ownerHost: z.string(),
23277
+ ownerPidNamespace: z.string().nullable(),
23278
+ ownsTempDir: z.boolean(),
23279
+ logins: z.array(GuardrailsLaunchLogin),
23280
+ placeholderPaths: z.array(z.string().min(1))
23281
+ }).strict();
23282
+ //#endregion
23010
23283
  //#region server/core/claude-trust-core.ts
23011
23284
  var CLAUDE_GLOBAL_CONFIG_FILE_NAME = ".claude.json";
23012
23285
  var CLAUDE_LEGACY_CONFIG_FILE_NAME = ".config.json";
@@ -23099,6 +23372,21 @@ function decideFolderTrust(configText, repositoryRoot, platform) {
23099
23372
  };
23100
23373
  }
23101
23374
  //#endregion
23375
+ //#region server/guardrails/core/guardrails-bridge-socket-core.ts
23376
+ var LONGEST_BRIDGE_SOCKET_NAME = "claude-socks-0123456789abcdef.sock";
23377
+ var UNIX_SOCKET_PATH_BYTE_LIMIT = 107;
23378
+ var SHORT_BRIDGE_SOCKET_FOLDER = "/tmp";
23379
+ function bridgeSocketPathBytes(tempDirectory) {
23380
+ return Buffer.byteLength(path.join(tempDirectory, LONGEST_BRIDGE_SOCKET_NAME));
23381
+ }
23382
+ function hasRoomForBridgeSocket(tempDirectory, platform) {
23383
+ return platform !== "linux" || bridgeSocketPathBytes(tempDirectory) <= UNIX_SOCKET_PATH_BYTE_LIMIT;
23384
+ }
23385
+ function bridgeSocketLauncherEnv(inheritedTempDirectory, platform) {
23386
+ if (hasRoomForBridgeSocket(inheritedTempDirectory, platform)) return {};
23387
+ return { TMPDIR: SHORT_BRIDGE_SOCKET_FOLDER };
23388
+ }
23389
+ //#endregion
23102
23390
  //#region server/guardrails/guardrails-launch.ts
23103
23391
  var LAUNCH_ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
23104
23392
  var GUARDED_ENV_COMMAND = "/usr/bin/env";
@@ -23106,9 +23394,11 @@ var POLICY_FOLDER = "guardrails-policies";
23106
23394
  var TEMP_FOLDER = "guardrails-temp";
23107
23395
  var LAUNCH_HOME_FOLDER = "guardrails-homes";
23108
23396
  var START_FOLDER = "start";
23397
+ var OWNER_FILE_SUFFIX = ".owner.json";
23109
23398
  var ENTER_FOLDER_SCRIPT = "cd \"$1\" && shift && exec \"$@\"";
23110
23399
  var LOGIN_FILE_MODE = 384;
23111
23400
  var ABSENT_PATH_DENY_PLATFORM = "darwin";
23401
+ var OWN_PID_NAMESPACE_LINK = "/proc/self/ns/pid";
23112
23402
  async function isPresent(candidate) {
23113
23403
  try {
23114
23404
  await access(candidate);
@@ -23216,7 +23506,7 @@ async function lstatWhenPresent(candidate) {
23216
23506
  async function isRegularFile(candidate) {
23217
23507
  return (await lstatWhenPresent(candidate))?.isFile() ?? false;
23218
23508
  }
23219
- async function isRealDirectory(candidate) {
23509
+ async function isRealDirectory$1(candidate) {
23220
23510
  return (await lstatWhenPresent(candidate))?.isDirectory() ?? false;
23221
23511
  }
23222
23512
  async function absentPaths(candidates) {
@@ -23231,21 +23521,94 @@ async function removeLeftoverPlaceholders(placeholderPaths) {
23231
23521
  }
23232
23522
  async function createMissingMountPoints(mountPoints) {
23233
23523
  for (const mountPoint of mountPoints) {
23234
- if (!await isRealDirectory(path.dirname(mountPoint))) continue;
23524
+ if (!await isRealDirectory$1(path.dirname(mountPoint))) continue;
23235
23525
  await mkdir(mountPoint).catch((error) => {
23236
23526
  if (errorCode(error) !== "EEXIST") throw error;
23237
23527
  });
23238
23528
  }
23239
23529
  }
23240
- async function keepRefreshedLogins(links, launchId) {
23241
- for (const { linkPath, targetPath } of links.filter((link) => link.isLogin)) {
23530
+ async function isNewerThanTarget(linkPath, targetPath) {
23531
+ const targetStats = await stat(targetPath).catch(() => null);
23532
+ if (targetStats === null) return true;
23533
+ return (await lstat(linkPath)).mtimeMs > targetStats.mtimeMs;
23534
+ }
23535
+ async function keepRefreshedLogins(logins, launchId, { onlyWhenNewer }) {
23536
+ for (const { linkPath, targetPath } of logins) {
23242
23537
  if (!await isRegularFile(linkPath)) continue;
23538
+ if (onlyWhenNewer && !await isNewerThanTarget(linkPath, targetPath)) continue;
23243
23539
  const stagedPath = `${targetPath}.${launchId}`;
23244
23540
  await copyFile(linkPath, stagedPath);
23245
23541
  await chmod(stagedPath, LOGIN_FILE_MODE);
23246
23542
  await rename(stagedPath, targetPath);
23247
23543
  }
23248
23544
  }
23545
+ async function removeLaunch(folders, owner, launchId, { onlyNewerLogins }) {
23546
+ await rm(folders.policyPath, { force: true });
23547
+ await removeLeftoverPlaceholders(owner.placeholderPaths);
23548
+ await keepRefreshedLogins(owner.logins.filter((login) => isPathInside(folders.launchHomeDir, login.linkPath, { allowEqual: false })), launchId, { onlyWhenNewer: onlyNewerLogins });
23549
+ await rm(folders.launchHomeDir, {
23550
+ recursive: true,
23551
+ force: true
23552
+ });
23553
+ if (folders.ownedTempDir !== null) await rm(folders.ownedTempDir, {
23554
+ recursive: true,
23555
+ force: true
23556
+ });
23557
+ await rm(folders.ownerPath, { force: true });
23558
+ }
23559
+ function isProcessAlive$1(pid) {
23560
+ try {
23561
+ process.kill(pid, 0);
23562
+ return true;
23563
+ } catch (error) {
23564
+ return errorCode(error) === "EPERM";
23565
+ }
23566
+ }
23567
+ function readOwnPidNamespace() {
23568
+ return readlink(OWN_PID_NAMESPACE_LINK).catch(() => null);
23569
+ }
23570
+ async function readOwnerFile(ownerPath) {
23571
+ const outcome = await loadJsonStateFile({
23572
+ filePath: ownerPath,
23573
+ quarantine: false,
23574
+ parse: (raw) => GuardrailsLaunchOwner.safeParse(raw).data ?? null
23575
+ });
23576
+ return outcome.status === "loaded" ? outcome.value : null;
23577
+ }
23578
+ async function sweepOrphanedGuardrailsLaunches(log = console) {
23579
+ try {
23580
+ const policyDir = path.join(glimmervoidHomeDir$1(), POLICY_FOLDER);
23581
+ const entryNames = await readdir(policyDir).catch((error) => {
23582
+ if (isMissingFileError(error)) return [];
23583
+ throw error;
23584
+ });
23585
+ const glimmervoidHome = await realpath(glimmervoidHomeDir$1());
23586
+ const sweeperPidNamespace = await readOwnPidNamespace();
23587
+ let removedLaunchCount = 0;
23588
+ for (const entryName of entryNames) {
23589
+ if (!entryName.endsWith(OWNER_FILE_SUFFIX)) continue;
23590
+ const launchId = entryName.slice(0, -11);
23591
+ if (!LAUNCH_ID_SHAPE.test(launchId)) continue;
23592
+ const ownerPath = path.join(policyDir, entryName);
23593
+ const owner = await readOwnerFile(ownerPath);
23594
+ if (owner === null || owner.ownerHost !== os.hostname() || owner.ownerPidNamespace !== sweeperPidNamespace || isProcessAlive$1(owner.ownerPid)) continue;
23595
+ try {
23596
+ await removeLaunch({
23597
+ policyPath: path.join(policyDir, `${launchId}.json`),
23598
+ ownerPath,
23599
+ launchHomeDir: path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId),
23600
+ ownedTempDir: owner.ownsTempDir ? path.join(glimmervoidHome, TEMP_FOLDER, launchId) : null
23601
+ }, owner, launchId, { onlyNewerLogins: true });
23602
+ removedLaunchCount += 1;
23603
+ } catch (error) {
23604
+ log.warn(`[guardrails] could not remove the leftover launch ${launchId}: ${errorMessage(error)}`);
23605
+ }
23606
+ }
23607
+ if (removedLaunchCount > 0) log.log(`[guardrails] removed ${removedLaunchCount} launch folders left by sessions that ended without a clean shutdown`);
23608
+ } catch (error) {
23609
+ log.warn(`[guardrails] could not sweep leftover launch folders: ${errorMessage(error)}`);
23610
+ }
23611
+ }
23249
23612
  function launcherCommand(plan, policyPath) {
23250
23613
  if (plan.launcher === "open-network") return {
23251
23614
  file: process.execPath,
@@ -23290,10 +23653,11 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23290
23653
  recursive: true,
23291
23654
  mode: 448
23292
23655
  });
23293
- const [homeDir, glimmervoidHome, realRole] = await Promise.all([
23656
+ const [homeDir, glimmervoidHome, realRole, ownerPidNamespace] = await Promise.all([
23294
23657
  realpath(os.homedir()),
23295
23658
  realpath(glimmervoidHomeDir$1()),
23296
- withRealRoots(role)
23659
+ withRealRoots(role),
23660
+ readOwnPidNamespace()
23297
23661
  ]);
23298
23662
  const ownsTempDir = tempDir === void 0;
23299
23663
  const launchTempDir = tempDir === void 0 ? path.join(glimmervoidHome, TEMP_FOLDER, launchId) : path.resolve(tempDir);
@@ -23342,8 +23706,25 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23342
23706
  if (startDir !== void 0) await mkdir(startDir, { recursive: true });
23343
23707
  await Promise.all(plan.launchHomeLinks.map((link) => symlink(link.targetPath, link.linkPath)));
23344
23708
  await writeJsonAtomic(policyPath, plan.policy);
23709
+ const folders = {
23710
+ policyPath,
23711
+ ownerPath: path.join(policyDir, `${launchId}${OWNER_FILE_SUFFIX}`),
23712
+ launchHomeDir,
23713
+ ownedTempDir: ownsTempDir ? launchTempDir : null
23714
+ };
23715
+ const owner = {
23716
+ ownerPid: process.pid,
23717
+ ownerHost: os.hostname(),
23718
+ ownerPidNamespace,
23719
+ ownsTempDir,
23720
+ logins: plan.launchHomeLinks.filter((link) => link.isLogin).map(({ linkPath, targetPath }) => ({
23721
+ linkPath,
23722
+ targetPath
23723
+ })),
23724
+ placeholderPaths: []
23725
+ };
23726
+ await writeJsonAtomic(folders.ownerPath, owner);
23345
23727
  const envPrefix = guardedEnvPrefix(plan);
23346
- let placeholderPathsAbsentAtWrap = [];
23347
23728
  return {
23348
23729
  ok: true,
23349
23730
  launch: {
@@ -23354,7 +23735,8 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23354
23735
  ...rebuilt.plan.policy,
23355
23736
  allowPty: hasTerminal && rebuilt.plan.launcher === "open-network"
23356
23737
  });
23357
- placeholderPathsAbsentAtWrap = await absentPaths(guardrailsPlaceholderPaths(realRole));
23738
+ owner.placeholderPaths = await absentPaths(guardrailsPlaceholderPaths(realRole));
23739
+ await writeJsonAtomic(folders.ownerPath, owner);
23358
23740
  await createMissingMountPoints(lockedClaudeMountPoints(realRole));
23359
23741
  return {
23360
23742
  file: launcher.file,
@@ -23365,21 +23747,12 @@ async function prepareGuardrailsLaunch({ role, launchId, tempDir, enterDir }) {
23365
23747
  ]
23366
23748
  };
23367
23749
  },
23368
- env: { ...plan.launchEnv },
23750
+ env: {
23751
+ ...plan.launchEnv,
23752
+ ...bridgeSocketLauncherEnv(os.tmpdir(), process.platform)
23753
+ },
23369
23754
  ...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
- }
23755
+ dispose: () => removeLaunch(folders, owner, launchId, { onlyNewerLogins: false })
23383
23756
  }
23384
23757
  };
23385
23758
  } catch (error) {
@@ -26974,6 +27347,7 @@ function withForcedTapReporter(runCheck) {
26974
27347
  }
26975
27348
  };
26976
27349
  }
27350
+ var QUEUED_RUN_LIVENESS_POLL_MS = 1e3;
26977
27351
  async function readCheckoutSecretValues(checkoutPath, sharedEntries) {
26978
27352
  const sharedEnvFiles = sharedEntries.filter((sharedEntry) => !sharedEntry.includes("..") && path.basename(sharedEntry).startsWith(".env"));
26979
27353
  return (await Promise.all(sharedEnvFiles.map(async (sharedEnvFile) => {
@@ -26985,7 +27359,33 @@ async function readCheckoutSecretValues(checkoutPath, sharedEntries) {
26985
27359
  }
26986
27360
  }))).flatMap(envFileSecretValues);
26987
27361
  }
26988
- async function runChecksInCleanCheckout({ projectPath, sha, commands, writeScopes, sharedEntries, gitWorkspace, baseEnv, secretValues, signal, isLive = () => true, prepareCheckRunner = prepareGuardedCheckRunner, rerunFailedChecks = false, writeFailedCheckOutputFile = async () => null }) {
27362
+ async function runChecksInCleanCheckout(options) {
27363
+ const { signal, isLive = () => true, checkRunGate = null, onMemoryWait } = options;
27364
+ if (!checkRunGate) return runChecksInStagedCheckout(options);
27365
+ const isStillWanted = () => isLive() && !signal.aborted;
27366
+ const livenessLost = new AbortController();
27367
+ const livenessPoll = setInterval(() => {
27368
+ if (!isLive()) livenessLost.abort();
27369
+ }, QUEUED_RUN_LIVENESS_POLL_MS);
27370
+ const stopLivenessPoll = () => clearInterval(livenessPoll);
27371
+ try {
27372
+ return await checkRunGate.run(async () => {
27373
+ stopLivenessPoll();
27374
+ if (!isStillWanted()) return null;
27375
+ return runChecksInStagedCheckout(options);
27376
+ }, {
27377
+ isStillWanted,
27378
+ onMemoryWait,
27379
+ signal: AbortSignal.any([signal, livenessLost.signal])
27380
+ });
27381
+ } catch (error) {
27382
+ if (isQueueAdmissionAbandoned(error)) return null;
27383
+ throw error;
27384
+ } finally {
27385
+ stopLivenessPoll();
27386
+ }
27387
+ }
27388
+ async function runChecksInStagedCheckout({ projectPath, sha, commands, writeScopes, sharedEntries, gitWorkspace, baseEnv, secretValues, signal, isLive = () => true, prepareCheckRunner = prepareGuardedCheckRunner, rerunFailedChecks = false, writeFailedCheckOutputFile = async () => null }) {
26989
27389
  const checkoutParent = await makeHostScratchDirectory("check-");
26990
27390
  const checkoutPath = path.join(checkoutParent, "checkout");
26991
27391
  const env = buildFactoryCheckEnv(baseEnv, process.platform);
@@ -27309,7 +27709,7 @@ async function runFactoryReview(options) {
27309
27709
  };
27310
27710
  return review.value;
27311
27711
  }
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 }) {
27712
+ 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
27713
  const reviewing = /* @__PURE__ */ new Set();
27314
27714
  const attempts = /* @__PURE__ */ new Map();
27315
27715
  const failingFilesOutsideScopesByWork = /* @__PURE__ */ new Map();
@@ -27498,7 +27898,9 @@ function createFactoryCloseOut({ config, spawnReviewer, gitWorkspace, serializeP
27498
27898
  isLive: () => isLive(worker),
27499
27899
  prepareCheckRunner,
27500
27900
  rerunFailedChecks,
27501
- writeFailedCheckOutputFile: (outputText) => writeFailedCheckOutputFile(worker, outputText)
27901
+ writeFailedCheckOutputFile: (outputText) => writeFailedCheckOutputFile(worker, outputText),
27902
+ checkRunGate,
27903
+ onMemoryWait: (isWaiting) => onCheckMemoryWait(worker.projectId, isWaiting)
27502
27904
  });
27503
27905
  }
27504
27906
  async function listInheritedCheckCommands(worker, baseSha, headRun) {
@@ -28762,7 +29164,7 @@ function createFactoryDispatch({ config, sessions, makeSession, wireSessionEvent
28762
29164
  allowCommands: [
28763
29165
  "coherence context",
28764
29166
  "rm",
28765
- ...checks,
29167
+ ...workerChecksOf(checks),
28766
29168
  ...profile.focusedTestCommand ? [profile.focusedTestCommand] : []
28767
29169
  ],
28768
29170
  extraDeny: FACTORY_WORKER_DENY
@@ -29520,10 +29922,11 @@ function createFactoryPoller({ shouldHoldCheckout = () => false, beforeTick = ()
29520
29922
  };
29521
29923
  try {
29522
29924
  paused = await readPaused(project.id);
29523
- if (!await hasCoherenceConfigAt(project.path, headSha)) return null;
29524
- const confirmedFlakesByIntent = await readIntentConfirmedFlakes(project.id);
29525
29925
  const previous = processedProjects.get(project.id);
29526
- if (previous?.path === project.path && (previous.state.headSha === headSha || shouldHoldCheckout(project.id))) return {
29926
+ const isUnchangedTip = previous?.path === project.path && previous.state.headSha === headSha;
29927
+ if (!isUnchangedTip && !await hasCoherenceConfigAt(project.path, headSha)) return null;
29928
+ const confirmedFlakesByIntent = await readIntentConfirmedFlakes(project.id);
29929
+ if (previous?.path === project.path && (isUnchangedTip || shouldHoldCheckout(project.id))) return {
29527
29930
  ...previous.state,
29528
29931
  paused,
29529
29932
  projectName: project.name,
@@ -29957,92 +30360,12 @@ function createFactoryVerifier({ config, spawnVerifier, serializeProject, ensure
29957
30360
  };
29958
30361
  }
29959
30362
  //#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
30363
  //#region server/factory/factory-setup.ts
30041
30364
  var setupAgentProfileSchema = ProjectConfig.shape.factoryProfile.unwrap().pick({
30042
30365
  checks: true,
30043
30366
  focusedTestCommand: true
30044
30367
  });
30045
- function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, gitWorkspace, saveProjectFactoryProfile, recordBaseline = async () => {}, spawnSetupAgent, model = () => null, prepareCheckRunner, baseEnv = process.env, secretValues = [] }) {
30368
+ function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, gitWorkspace, saveProjectFactoryProfile, recordBaseline = async () => {}, spawnSetupAgent, model = () => null, prepareCheckRunner, checkRunGate = null, baseEnv = process.env, secretValues = [] }) {
30046
30369
  const pending = /* @__PURE__ */ new Map();
30047
30370
  const controller = new AbortController();
30048
30371
  async function setupProject(projectId, projectPath) {
@@ -30087,7 +30410,8 @@ function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, g
30087
30410
  baseEnv,
30088
30411
  secretValues,
30089
30412
  signal: controller.signal,
30090
- prepareCheckRunner
30413
+ prepareCheckRunner,
30414
+ checkRunGate
30091
30415
  });
30092
30416
  if (!run) throw new Error("Factory setup was interrupted");
30093
30417
  for (const check of run.checks) checkProofs.set(check.command, classifyFactorySetupCheck({
@@ -30205,10 +30529,11 @@ function createFactorySetup({ readIntegrationSha, globalChecks, sharedEntries, g
30205
30529
  //#region server/factory/factory-wiring.ts
30206
30530
  var COHERENCE_CONFIG_PROBE_TIMEOUT_MS = 5e3;
30207
30531
  var ORCHESTRATOR_EXCEPTION_PREFIX = "factory-exception";
30532
+ var CHECK_RUN_HELD_FOR_MEMORY_NOTE = "Waiting for memory to run checks";
30208
30533
  function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorkspace(), homeDir = glimmervoidHomeDir$1(), createPoller = createFactoryPoller, log = console, telemetry, orchestratorOptions, trustClaudeFolder, spawnReviewer, prepareCheckRunner, saveProjectFactoryProfile, spawnVerifier = spawnReviewer, readSpentTodayUsd = () => ({
30209
30534
  status: "known",
30210
30535
  amountUsd: 0
30211
- }), notify = () => {}, runCoherence: customRunCoherence, ...pollerDeps }) {
30536
+ }), notify = () => {}, runCoherence: customRunCoherence, checkRunGate = null, ...pollerDeps }) {
30212
30537
  gitWorkspace = isolateGitWorkspace(gitWorkspace, { disableRepoCommands: true });
30213
30538
  const coherenceCliPath = resolvePackageBin("@danilocampos/coherence", "coherence");
30214
30539
  const coherenceHookCliPath = resolvePackageBin("@danilocampos/coherence", "coherence-hook");
@@ -30297,6 +30622,7 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
30297
30622
  sharedEntries: () => config.worktreeShare ?? DEFAULT_CONFIG.worktreeShare,
30298
30623
  gitWorkspace,
30299
30624
  prepareCheckRunner,
30625
+ checkRunGate,
30300
30626
  spawnSetupAgent: spawnReviewer,
30301
30627
  model: () => config.factory?.reviewerModel ?? null,
30302
30628
  secretValues: factorySecretValues(process.env),
@@ -31024,6 +31350,17 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
31024
31350
  };
31025
31351
  const refresh = refreshAfter("state");
31026
31352
  const refusalsByWork = /* @__PURE__ */ new Map();
31353
+ const projectsWithCheckRunHeldForMemory = /* @__PURE__ */ new Set();
31354
+ const noteCheckRunHeldForMemory = (projectId, isWaiting) => {
31355
+ if (isWaiting === projectsWithCheckRunHeldForMemory.has(projectId)) return;
31356
+ if (isWaiting) projectsWithCheckRunHeldForMemory.add(projectId);
31357
+ if (!isWaiting) projectsWithCheckRunHeldForMemory.delete(projectId);
31358
+ refresh();
31359
+ };
31360
+ const withCheckMemoryNote = (projectId, note) => {
31361
+ if (!projectsWithCheckRunHeldForMemory.has(projectId)) return note;
31362
+ return note ? `${note}\n${CHECK_RUN_HELD_FOR_MEMORY_NOTE}` : CHECK_RUN_HELD_FOR_MEMORY_NOTE;
31363
+ };
31027
31364
  const notifyOrchestrator = (projectId, event) => {
31028
31365
  const refusalKey = JSON.stringify([projectId, event.workId]);
31029
31366
  const shouldRecord = event.event !== "refused" || refusalsByWork.get(refusalKey)?.reason !== (event.detail ?? null);
@@ -31071,6 +31408,8 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
31071
31408
  readIntentFloorAllowances,
31072
31409
  countTicketBlock,
31073
31410
  prepareCheckRunner,
31411
+ checkRunGate,
31412
+ onCheckMemoryWait: noteCheckRunHeldForMemory,
31074
31413
  recordDecision: decisionLog.record,
31075
31414
  recordScopeNeeds: async (worker, files) => {
31076
31415
  let rememberedNeeds = [];
@@ -31231,6 +31570,7 @@ function createFactoryWiring({ config, broadcast, gitWorkspace = createGitWorksp
31231
31570
  const factoryProfile = resolveFactoryProfile(config.factory, config.projects?.find((entry) => entry.id === project.projectId));
31232
31571
  return {
31233
31572
  ...updated,
31573
+ note: withCheckMemoryNote(project.projectId, updated.note),
31234
31574
  reviewing,
31235
31575
  liveWorkers,
31236
31576
  factoryProfile: {
@@ -32235,13 +32575,12 @@ function branchGcShouldStart(config) {
32235
32575
  function branchGcCfgKey(config) {
32236
32576
  return JSON.stringify(config.branchGc ?? null);
32237
32577
  }
32238
- function createBranchGcWiring({ config, gitWorkspace, liveSessionIds, liveWorktreePaths, broadcast = () => {}, log = console, decisionTrace = (entry) => log.info(`[branch-gc] decision ${JSON.stringify(entry)}`), createPoller = createBranchGcPoller }) {
32578
+ function createBranchGcWiring({ config, gitWorkspace, liveSessionIds, liveWorktreePaths, log = console, decisionTrace = (entry) => log.info(`[branch-gc] decision ${JSON.stringify(entry)}`), createPoller = createBranchGcPoller }) {
32239
32579
  const runner = createLaneRunner({
32240
32580
  tag: "branch-gc",
32241
32581
  gate: () => branchGcShouldStart(config),
32242
32582
  cfgKey: () => branchGcCfgKey(config),
32243
32583
  emptyStatus: () => emptyLaneStatus("branch-gc-status", branchGcShouldStart(config)),
32244
- broadcast,
32245
32584
  createPoller: ({ onTickComplete }) => createPoller({
32246
32585
  gitWorkspace,
32247
32586
  getConfig: () => config,
@@ -32262,8 +32601,7 @@ function createBranchGcWiring({ config, gitWorkspace, liveSessionIds, liveWorktr
32262
32601
  return {
32263
32602
  start: runner.startPoller,
32264
32603
  stop: runner.stopPoller,
32265
- restartIfConfigChanged: runner.restartIfConfigChanged,
32266
- getStatus: runner.getStatus
32604
+ restartIfConfigChanged: runner.restartIfConfigChanged
32267
32605
  };
32268
32606
  }
32269
32607
  //#endregion
@@ -32724,6 +33062,9 @@ function buildContextDigest(store, { scopes = null, budgetChars = DEFAULT_DIGEST
32724
33062
  if (lines.length === 1) return "";
32725
33063
  return lines.join("\n");
32726
33064
  }
33065
+ //#endregion
33066
+ //#region server/ingest/core/ingest-fs-core.ts
33067
+ var SOURCE$3 = "fs";
32727
33068
  var KIND = "file-change";
32728
33069
  var DEFAULT_BATCH_MS = SOURCE_DEFAULTS.fs.batchMs;
32729
33070
  var MAX_FILES_PER_BATCH = SOURCE_DEFAULTS.fs.digestQuota;
@@ -32773,15 +33114,13 @@ var DAEMON_CONFIG_SIBLINGS = Object.freeze([
32773
33114
  "usage-lanes.json",
32774
33115
  "usage-warehouse.json",
32775
33116
  "usage-budget-state.json",
32776
- "pr-review-state.json",
32777
33117
  "team-review-state.json",
32778
33118
  "pairings.json",
32779
33119
  "pairings-seen.json",
32780
33120
  "litellm-pricing.json",
32781
33121
  "update-check.json",
32782
33122
  "recordings",
32783
- "uploads",
32784
- "memory"
33123
+ "uploads"
32785
33124
  ]);
32786
33125
  var ACTIVE_SESSION_STATES = Object.freeze([
32787
33126
  "INITIALIZING",
@@ -32964,7 +33303,7 @@ function batchSize(batch) {
32964
33303
  }
32965
33304
  function fileChangeEvent({ root, relPath, change, now }) {
32966
33305
  return {
32967
- source: "fs",
33306
+ source: SOURCE$3,
32968
33307
  kind: KIND,
32969
33308
  ts: now,
32970
33309
  scope: {
@@ -33019,7 +33358,7 @@ function decideFsEvents(batch, { root = null, now = 0 } = {}) {
33019
33358
  }
33020
33359
  const { summary, counts } = burstSummary(batch);
33021
33360
  return [{
33022
- source: "fs",
33361
+ source: SOURCE$3,
33023
33362
  kind: KIND,
33024
33363
  ts: now,
33025
33364
  scope: {
@@ -33072,6 +33411,7 @@ function drainBatchLog(state) {
33072
33411
  //#endregion
33073
33412
  //#region server/ingest/core/ingest-tail-core.ts
33074
33413
  var MAX_CATCH_UP_BYTES = 262144;
33414
+ var DEFAULT_MAX_TRACKED$1 = 256;
33075
33415
  var LISTING_SETTLE_MS = 2e3;
33076
33416
  function fileIdentity(stat) {
33077
33417
  return `${Math.floor(coercedNumberOr(stat?.ino, 0))}:${Math.floor(coercedNumberOr(stat?.birthtimeMs, 0))}`;
@@ -33181,13 +33521,18 @@ function applyRead(state, { text = "", end = 0, stat = null, head = null, reset
33181
33521
  if (stat) state.identity = fileIdentity(stat);
33182
33522
  return split.lines;
33183
33523
  }
33524
+ function isTailCurrent(state, stat) {
33525
+ if (!stat || fileIdentity(stat) !== state.identity) return false;
33526
+ if (coercedNumberOr(stat.size, -1) !== state.size || state.offset < state.size) return false;
33527
+ return coercedNumberOr(stat.mtimeMs, -1) === state.mtimeMs;
33528
+ }
33184
33529
  function isActiveMtime(mtimeMs, { now, withinMs }) {
33185
33530
  return coercedNumberOr(mtimeMs, 0) >= coercedNumberOr(now, 0) - Math.max(0, coercedNumberOr(withinMs, 0));
33186
33531
  }
33187
33532
  function canTrustCachedListing({ mtimeMs, listedAtMs } = {}) {
33188
33533
  return coercedNumberOr(listedAtMs, 0) - coercedNumberOr(mtimeMs, 0) >= LISTING_SETTLE_MS;
33189
33534
  }
33190
- function pickStaleByMtime(entriesByKey, { maxTracked = 256 } = {}) {
33535
+ function pickStaleByMtime(entriesByKey, { maxTracked = DEFAULT_MAX_TRACKED$1 } = {}) {
33191
33536
  const entries = [...entriesByKey.entries()];
33192
33537
  const bound = Math.max(1, Math.floor(maxTracked));
33193
33538
  if (entries.length <= bound) return [];
@@ -34397,6 +34742,9 @@ function createFsIngest({ publish, sourceConfig = {}, configPath = null, logger
34397
34742
  }
34398
34743
  };
34399
34744
  }
34745
+ //#endregion
34746
+ //#region server/ingest/core/ingest-git-core.ts
34747
+ var SOURCE$1 = "git";
34400
34748
  var DEFAULT_DEBOUNCE_MS = SOURCE_DEFAULTS.git.debounceMs;
34401
34749
  var DEFAULT_POLL_MS$1 = SOURCE_DEFAULTS.git.pollMs;
34402
34750
  var CLEAN_SIGNATURE = "clean";
@@ -34581,7 +34929,7 @@ function subjectSuffix(commit) {
34581
34929
  function commitEvent({ status, commit, root = null, now = 0 }) {
34582
34930
  const sha = shortSha$1(commit?.sha || status.oid) || null;
34583
34931
  return {
34584
- source: "git",
34932
+ source: SOURCE$1,
34585
34933
  kind: "commit",
34586
34934
  ts: now,
34587
34935
  scope: {
@@ -34601,7 +34949,7 @@ function branchChangeEvent({ status, commit, root = null, now = 0 }) {
34601
34949
  const sha = shortSha$1(commit?.sha || status.oid) || null;
34602
34950
  const at = sha ? ` at ${sha}` : "";
34603
34951
  return {
34604
- source: "git",
34952
+ source: SOURCE$1,
34605
34953
  kind: "branch-change",
34606
34954
  ts: now,
34607
34955
  scope: {
@@ -34624,7 +34972,7 @@ function statusChangeEvent({ status, root = null, now = 0 }) {
34624
34972
  if (counts.untracked) parts.push(`${counts.untracked} untracked`);
34625
34973
  if (counts.conflicted) parts.push(`${counts.conflicted} conflicted`);
34626
34974
  return {
34627
- source: "git",
34975
+ source: SOURCE$1,
34628
34976
  kind: "status-change",
34629
34977
  ts: now,
34630
34978
  scope: {
@@ -35081,10 +35429,11 @@ function createGitIngest({ publish, sourceConfig = {}, reposProvider = null, log
35081
35429
  //#region server/ingest/ingest-shell-history.ts
35082
35430
  var DEFAULT_POLL_MS = 2e3;
35083
35431
  var DEFAULT_DISCOVER_MS = 3e4;
35432
+ var DEFAULT_MAX_TRACKED = 8;
35084
35433
  var DEFAULT_MAX_COMMANDS_PER_DRAIN = 50;
35085
35434
  var WATCH_POLL_DEBOUNCE_MS = 100;
35086
35435
  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 } = {}) {
35436
+ 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
35437
  if (typeof publish !== "function") throw new Error("createShellHistoryIngest requires publish");
35089
35438
  const publishEvent = publish;
35090
35439
  const pollMs = positiveIntOr(sourceConfig.pollMs, DEFAULT_POLL_MS);
@@ -38850,24 +39199,15 @@ function resolvePosthogReportPath(issueId, reportDir = DEFAULT_POSTHOG_REPORT_DI
38850
39199
  ok: false,
38851
39200
  reason: "Invalid issue id"
38852
39201
  };
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 {
39202
+ const htmlReportPath = confinePath(path.resolve(reportDir), `${rawIssueId}.html`);
39203
+ if (!htmlReportPath) return {
38857
39204
  ok: false,
38858
39205
  reason: "Invalid report path"
38859
39206
  };
38860
39207
  return {
38861
39208
  ok: true,
38862
39209
  issueId: rawIssueId,
38863
- reportDir: resolvedReportDir,
38864
- candidates: [{
38865
- format: "html",
38866
- reportPath: htmlReportPath
38867
- }, {
38868
- format: "markdown",
38869
- reportPath: markdownReportPath
38870
- }]
39210
+ reportPath: htmlReportPath
38871
39211
  };
38872
39212
  }
38873
39213
  async function readPosthogReport(issueId, { reportDir = DEFAULT_POSTHOG_REPORT_DIR } = {}) {
@@ -38878,17 +39218,22 @@ async function readPosthogReport(issueId, { reportDir = DEFAULT_POSTHOG_REPORT_D
38878
39218
  issueId: typeof issueId === "string" ? issueId : null,
38879
39219
  error: resolved.reason
38880
39220
  };
38881
- for (const candidate of resolved.candidates) try {
38882
- const content = await fs.promises.readFile(candidate.reportPath, "utf8");
39221
+ try {
39222
+ const content = await fs.promises.readFile(resolved.reportPath, "utf8");
38883
39223
  return {
38884
39224
  ok: true,
38885
39225
  found: true,
38886
39226
  issueId: resolved.issueId,
38887
- format: candidate.format,
39227
+ format: "html",
38888
39228
  content
38889
39229
  };
38890
39230
  } catch (err) {
38891
- if (isMissingFileError(err, { includeNotDir: false })) continue;
39231
+ if (isMissingFileError(err, { includeNotDir: false })) return {
39232
+ ok: true,
39233
+ found: false,
39234
+ issueId: resolved.issueId,
39235
+ message: "Report not found"
39236
+ };
38892
39237
  return {
38893
39238
  ok: false,
38894
39239
  found: false,
@@ -38896,12 +39241,6 @@ async function readPosthogReport(issueId, { reportDir = DEFAULT_POSTHOG_REPORT_D
38896
39241
  error: "Could not read report"
38897
39242
  };
38898
39243
  }
38899
- return {
38900
- ok: true,
38901
- found: false,
38902
- issueId: resolved.issueId,
38903
- message: "Report not found"
38904
- };
38905
39244
  }
38906
39245
  //#endregion
38907
39246
  //#region server/telegram-transport.ts
@@ -39747,6 +40086,69 @@ function createRadarWiring({ config, investigationSessions, closeSessionDataClie
39747
40086
  };
39748
40087
  }
39749
40088
  //#endregion
40089
+ //#region server/memory-probe.ts
40090
+ var CGROUP_ROOT = "/sys/fs/cgroup";
40091
+ var MACOS_FREE_MEMORY_REASON = "macOS reports only never-used pages as free, so headroom is not measured there";
40092
+ function isMissingFile(error) {
40093
+ return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
40094
+ }
40095
+ async function readTextIfPresent(filePath) {
40096
+ try {
40097
+ return await readFile(filePath, "utf8");
40098
+ } catch (error) {
40099
+ if (isMissingFile(error)) return null;
40100
+ throw error;
40101
+ }
40102
+ }
40103
+ async function readCgroupLevel(cgroupPath) {
40104
+ const cgroupDirectory = path.posix.join(CGROUP_ROOT, cgroupPath);
40105
+ const [memoryHighText, memoryMaxText, memoryCurrentText] = await Promise.all([
40106
+ "memory.high",
40107
+ "memory.max",
40108
+ "memory.current"
40109
+ ].map((fileName) => readTextIfPresent(path.posix.join(cgroupDirectory, fileName))));
40110
+ return {
40111
+ limitBytes: cgroupLimitBytes(memoryHighText ?? null, memoryMaxText ?? null),
40112
+ currentBytes: parseCgroupBytes(memoryCurrentText ?? null)
40113
+ };
40114
+ }
40115
+ async function readLinuxHeadroom() {
40116
+ const [procSelfCgroupText, meminfoText] = await Promise.all([readTextIfPresent("/proc/self/cgroup"), readTextIfPresent("/proc/meminfo")]);
40117
+ const cgroupPath = procSelfCgroupText === null ? null : parseCgroupV2Path(procSelfCgroupText);
40118
+ const cgroupLevels = cgroupPath === null ? [] : await Promise.all(cgroupAncestorPaths(cgroupPath).map(readCgroupLevel));
40119
+ const hostAvailableBytes = meminfoText === null ? null : parseMemAvailableBytes(meminfoText);
40120
+ const headroomBytes = headroomFromReadings({
40121
+ cgroupLevels,
40122
+ hostAvailableBytes
40123
+ });
40124
+ if (headroomBytes === null) return {
40125
+ ok: false,
40126
+ reason: "neither cgroup v2 nor /proc/meminfo reported memory"
40127
+ };
40128
+ return {
40129
+ ok: true,
40130
+ headroomBytes
40131
+ };
40132
+ }
40133
+ async function readMemoryHeadroom() {
40134
+ if (process.platform === "darwin") return {
40135
+ ok: false,
40136
+ reason: MACOS_FREE_MEMORY_REASON
40137
+ };
40138
+ if (process.platform !== "linux") return {
40139
+ ok: true,
40140
+ headroomBytes: os.freemem()
40141
+ };
40142
+ try {
40143
+ return await readLinuxHeadroom();
40144
+ } catch (error) {
40145
+ return {
40146
+ ok: false,
40147
+ reason: errorMessage(error)
40148
+ };
40149
+ }
40150
+ }
40151
+ //#endregion
39750
40152
  //#region shared/my-pr-merge.ts
39751
40153
  var FAILING_CHECK_STATES$1 = /* @__PURE__ */ new Set(["FAILURE", "ERROR"]);
39752
40154
  function myPrMergeBlocker(pr) {
@@ -44151,6 +44553,7 @@ function createUsageScanner(deps = {}) {
44151
44553
  let lastIoFailures = 0;
44152
44554
  let activePass = null;
44153
44555
  let pendingForce = false;
44556
+ let pendingScopeDirs = null;
44154
44557
  let isReportDirty = true;
44155
44558
  let resolutionError = null;
44156
44559
  const cachedRollupsByDays = /* @__PURE__ */ new Map();
@@ -44702,7 +45105,7 @@ function createUsageScanner(deps = {}) {
44702
45105
  resolutionError
44703
45106
  };
44704
45107
  }
44705
- async function runPassInternal({ force }) {
45108
+ async function runPassInternal({ force, scopeDirs = null }) {
44706
45109
  const startedAt = nowFn();
44707
45110
  let parsedLineCount = 0;
44708
45111
  let newEntryCount = 0;
@@ -44727,7 +45130,11 @@ function createUsageScanner(deps = {}) {
44727
45130
  claudeDirs = resolved.dirs;
44728
45131
  resolutionError = resolved.error;
44729
45132
  ioFailures += resolved.ioFailures;
44730
- const vendorRoots = await resolveVendorRootsAsync({
45133
+ const scopedRoots = force || !isHistoryCaughtUp ? null : scopedClaudeRoots(scopeDirs, claudeDirs);
45134
+ const vendorRoots = scopedRoots ? {
45135
+ roots: [],
45136
+ ioFailures: 0
45137
+ } : await resolveVendorRootsAsync({
44731
45138
  fsPromises,
44732
45139
  env,
44733
45140
  homeDir,
@@ -44735,16 +45142,16 @@ function createUsageScanner(deps = {}) {
44735
45142
  laneLog
44736
45143
  });
44737
45144
  ioFailures += vendorRoots.ioFailures;
44738
- const roots = [...claudeDirs.map((dir) => ({
45145
+ const roots = scopedRoots ?? [...claudeDirs.map((dir) => ({
44739
45146
  vendor: "claude",
44740
45147
  dir,
44741
45148
  kind: "active"
44742
45149
  })), ...vendorRoots.roots];
44743
- dirs = roots.map((root) => root.dir);
45150
+ if (!scopedRoots) dirs = roots.map((root) => root.dir);
44744
45151
  const walked = await walkSourceFiles(roots, fsPromises, laneLog);
44745
45152
  const files = walked.files;
44746
45153
  ioFailures += walked.ioFailures;
44747
- lastFileCount = files.length;
45154
+ if (!scopedRoots) lastFileCount = files.length;
44748
45155
  for (const file of files) {
44749
45156
  const remainingBudget = byteBudget - bytesReadThisPass;
44750
45157
  if (remainingBudget <= 0) {
@@ -44816,25 +45223,36 @@ function createUsageScanner(deps = {}) {
44816
45223
  generationRollup: Array.from(passGenerationRollup.values())
44817
45224
  };
44818
45225
  }
44819
- async function runPassChain({ force }) {
45226
+ async function runPassChain({ force, scopeDirs }) {
44820
45227
  try {
44821
- let result = await runPassInternal({ force });
44822
- while (pendingForce) {
45228
+ let result = await runPassInternal({
45229
+ force,
45230
+ scopeDirs
45231
+ });
45232
+ while (pendingForce || pendingScopeDirs) {
45233
+ const queued = {
45234
+ force: pendingForce,
45235
+ scopeDirs: pendingForce ? null : pendingScopeDirs
45236
+ };
44823
45237
  pendingForce = false;
44824
- result = await runPassInternal({ force: true });
45238
+ pendingScopeDirs = null;
45239
+ result = await runPassInternal(queued);
44825
45240
  }
44826
45241
  return result;
44827
45242
  } finally {
44828
45243
  activePass = null;
44829
45244
  }
44830
45245
  }
44831
- function runPass({ force = false } = {}) {
45246
+ function runPass({ force = false, scopeDirs = null } = {}) {
44832
45247
  if (activePass) {
44833
- if (!force) return activePass;
44834
- pendingForce = true;
45248
+ if (force) pendingForce = true;
45249
+ if (!force && scopeDirs) pendingScopeDirs = [...pendingScopeDirs ?? [], ...scopeDirs];
44835
45250
  return activePass;
44836
45251
  }
44837
- activePass = runPassChain({ force });
45252
+ activePass = runPassChain({
45253
+ force,
45254
+ scopeDirs
45255
+ });
44838
45256
  return activePass;
44839
45257
  }
44840
45258
  const api = {
@@ -45045,12 +45463,15 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
45045
45463
  })().catch((error) => laneLog.warn("start failed", { error: errorMessage(error) }));
45046
45464
  return startPromise;
45047
45465
  }
45048
- async function runPassAndPush({ force }) {
45466
+ async function runPassAndPush({ force, scopeDirs = null }) {
45049
45467
  if (stopped || !scanner) return null;
45050
45468
  passInFlight = true;
45051
45469
  let result = null;
45052
45470
  try {
45053
- result = await scanner.runPass({ force });
45471
+ result = await scanner.runPass({
45472
+ force,
45473
+ scopeDirs
45474
+ });
45054
45475
  } catch (error) {
45055
45476
  laneLog.warn("scan pass failed", { error: errorMessage(error) });
45056
45477
  } finally {
@@ -45107,22 +45528,40 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
45107
45528
  }, delayMs);
45108
45529
  unrefTimer(continueTimer);
45109
45530
  }
45531
+ let nudgedTranscriptDirs = /* @__PURE__ */ new Set();
45532
+ function takeNudgedTranscriptDirs() {
45533
+ const scopeDirs = nudgedTranscriptDirs === null ? null : [...nudgedTranscriptDirs];
45534
+ nudgedTranscriptDirs = /* @__PURE__ */ new Set();
45535
+ return scopeDirs;
45536
+ }
45537
+ function noteNudgedTranscript(transcriptPath) {
45538
+ if (nudgedTranscriptDirs === null) return;
45539
+ if (!transcriptPath) {
45540
+ nudgedTranscriptDirs = null;
45541
+ return;
45542
+ }
45543
+ nudgedTranscriptDirs.add(path.dirname(transcriptPath));
45544
+ }
45110
45545
  const nudge = createCoalescedTimer({
45111
45546
  mode: "trailing",
45112
45547
  delayMs: NUDGE_DEBOUNCE_MS,
45113
45548
  run: () => {
45114
45549
  if (stopped || !scanner) return;
45115
45550
  if (passInFlight) {
45116
- nudgeSession();
45551
+ nudge.schedule();
45117
45552
  return;
45118
45553
  }
45119
- runPassAndPush({ force: false });
45554
+ runPassAndPush({
45555
+ force: false,
45556
+ scopeDirs: takeNudgedTranscriptDirs()
45557
+ });
45120
45558
  },
45121
45559
  setTimeoutFn,
45122
45560
  clearTimeoutFn
45123
45561
  });
45124
- function nudgeSession() {
45562
+ function nudgeSession(transcriptPath = null) {
45125
45563
  if (stopped || !scanner) return;
45564
+ noteNudgedTranscript(transcriptPath);
45126
45565
  nudge.schedule();
45127
45566
  }
45128
45567
  function getSessionsMessage() {
@@ -46264,7 +46703,17 @@ function createTraceWiring({ configPath = null, logger = console, debug = false,
46264
46703
  appendedRecordCount: recordCount
46265
46704
  };
46266
46705
  }
46706
+ async function isTranscriptUnchanged(binding) {
46707
+ if (!binding.hasOpenedTranscript || binding.bindingBeforeFirstOpen) return false;
46708
+ if (pendingRecordsBySessionId.has(binding.glimmervoidSessionId)) return false;
46709
+ try {
46710
+ return isTailCurrent(binding.tailState, await fs.promises.stat(binding.transcriptPath));
46711
+ } catch {
46712
+ return false;
46713
+ }
46714
+ }
46267
46715
  async function drainBinding(binding) {
46716
+ if (await isTranscriptUnchanged(binding)) return;
46268
46717
  const opened = await openContainedFile(binding.transcriptPath, projectsRoot());
46269
46718
  if (!opened.ok) {
46270
46719
  if (binding.bindingBeforeFirstOpen) await drainBinding(binding.bindingBeforeFirstOpen);
@@ -48155,7 +48604,16 @@ function createVisionsWiring({ debounceMs = 300, setTimeoutFn = DEFAULT_TIMER_FN
48155
48604
  //#region server/backend/backend-lanes.ts
48156
48605
  function createBackendLanes(dependencies) {
48157
48606
  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();
48607
+ const memoryAdmission = createMemoryAdmission({
48608
+ readHeadroom: () => readMemoryHeadroom(),
48609
+ reserveBytesFor: (kind) => memoryReserveBytes(configStore.config, kind),
48610
+ warn: (message) => logger.warn(message)
48611
+ });
48612
+ const spawnGate = createSpawnGate({ memoryAdmission });
48613
+ const checkRunGate = createSpawnGate({
48614
+ memoryAdmission,
48615
+ kind: "checkRun"
48616
+ });
48159
48617
  const gitWorkspace = createGitWorkspace({ rerere: config.worktreeRerere !== false });
48160
48618
  const gitWorkspaceSync = createGitWorkspaceSync();
48161
48619
  const laneLedger = createLaneLedger({
@@ -48186,7 +48644,6 @@ function createBackendLanes(dependencies) {
48186
48644
  const branchGc = createBranchGcWiring({
48187
48645
  config,
48188
48646
  gitWorkspace,
48189
- broadcast: broadcastControl,
48190
48647
  liveSessionIds: () => new Set(allLiveSessions().map((session) => session.id)),
48191
48648
  liveWorktreePaths: async () => new Set(await Promise.all(allLiveSessions().flatMap((session) => [session.worktreeDir, session.path]).filter((sessionDirectory) => Boolean(sessionDirectory)).map((sessionDirectory) => comparableDirectoryPath(sessionDirectory)))),
48192
48649
  ...options.branchGcWiringOptions || {}
@@ -48203,6 +48660,7 @@ function createBackendLanes(dependencies) {
48203
48660
  broadcast: broadcastControl,
48204
48661
  log: logger,
48205
48662
  telemetry,
48663
+ checkRunGate,
48206
48664
  saveProjectFactoryProfile: async (projectPath, profile) => {
48207
48665
  if (!await configStore.saveAsync((freshConfig) => {
48208
48666
  const project = freshConfig.projects?.find((entry) => entry.path === projectPath);
@@ -48654,6 +49112,7 @@ function createBackendLanes(dependencies) {
48654
49112
  restartServiceLanes,
48655
49113
  reviewSessions,
48656
49114
  spawnGate,
49115
+ memoryAdmission,
48657
49116
  teamReview,
48658
49117
  myPrs,
48659
49118
  issues,
@@ -48676,7 +49135,6 @@ function createBackendHealth(dependencies) {
48676
49135
  const memory = process.memoryUsage();
48677
49136
  const sessionStats = [];
48678
49137
  let alivePtyCount = 0;
48679
- let sleepingCount = 0;
48680
49138
  let totalDataListeners = 0;
48681
49139
  let totalOutputBufferBytes = 0;
48682
49140
  let listenerMismatch = false;
@@ -48690,7 +49148,6 @@ function createBackendHealth(dependencies) {
48690
49148
  };
48691
49149
  sessionStats.push(stats);
48692
49150
  if (stats.hasPty) alivePtyCount += 1;
48693
- if (stats.sleeping) sleepingCount += 1;
48694
49151
  totalDataListeners += stats.dataListenerCount;
48695
49152
  totalOutputBufferBytes += stats.outputBufferBytes;
48696
49153
  if (!session.ephemeral) {
@@ -48721,11 +49178,11 @@ function createBackendHealth(dependencies) {
48721
49178
  sessions: {
48722
49179
  total: dependencies.sessions.size,
48723
49180
  alivePty: alivePtyCount,
48724
- sleeping: sleepingCount,
48725
49181
  totalDataListeners,
48726
49182
  totalOutputBufferBytes,
48727
49183
  list: sessionStats
48728
49184
  },
49185
+ memoryGate: dependencies.getMemoryGateStatus(),
48729
49186
  websockets: {
48730
49187
  control: dependencies.controlWss.clients.size,
48731
49188
  data: dependencies.dataWss.clients.size,
@@ -50893,7 +51350,7 @@ function computeCrossRepoLinks(repos) {
50893
51350
  }
50894
51351
  //#endregion
50895
51352
  //#region server/change-map/change-map-wiring.ts
50896
- var GIT_TIMEOUT_MS = 15e3;
51353
+ var GIT_TIMEOUT_MS$1 = 15e3;
50897
51354
  var GIT_MAX_BUFFER_BYTES = 67108864;
50898
51355
  var FILE_READ_BATCH = 64;
50899
51356
  var MAX_CACHED_FILES = 5e4;
@@ -50905,14 +51362,14 @@ var MAX_MEMOIZED_REPO_FACTS = 64;
50905
51362
  async function runGitAsync(args, cwd) {
50906
51363
  const result = await runGit(args, {
50907
51364
  cwd,
50908
- timeoutMs: GIT_TIMEOUT_MS,
51365
+ timeoutMs: GIT_TIMEOUT_MS$1,
50909
51366
  maxBuffer: GIT_MAX_BUFFER_BYTES,
50910
51367
  trim: false
50911
51368
  });
50912
- if (!result.ok) throw gitCommandFailure(args, result, GIT_TIMEOUT_MS);
51369
+ if (!result.ok) throw gitCommandFailure(args, result, GIT_TIMEOUT_MS$1);
50913
51370
  return result.out;
50914
51371
  }
50915
- function yieldToEventLoop() {
51372
+ function yieldToEventLoop$1() {
50916
51373
  return new Promise((resolve) => {
50917
51374
  setImmediate(resolve);
50918
51375
  });
@@ -50959,7 +51416,7 @@ function createFileFactCache(derive, oversizedValue) {
50959
51416
  const value = values[index];
50960
51417
  if (value !== null) valueByPath.set(repoPath, value);
50961
51418
  });
50962
- await yieldToEventLoop();
51419
+ await yieldToEventLoop$1();
50963
51420
  }
50964
51421
  return valueByPath;
50965
51422
  }
@@ -51575,7 +52032,7 @@ function parseSinceParam(url) {
51575
52032
  return Number.isFinite(n) ? n : null;
51576
52033
  }
51577
52034
  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;
52035
+ 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, 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;
51579
52036
  const changeMaps = createChangeMapService({
51580
52037
  sessions,
51581
52038
  narrator: changeMapNarrator,
@@ -51848,35 +52305,6 @@ function registerControlHandlers(controlWss, deps) {
51848
52305
  }
51849
52306
  session.unlinkPr(msg.key);
51850
52307
  }
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
52308
  async function handleListConversations(msg, ws) {
51881
52309
  const sess = findSession(msg);
51882
52310
  if (!sess) {
@@ -52869,7 +53297,6 @@ function registerControlHandlers(controlWss, deps) {
52869
53297
  "set-session-title": handleSetSessionTitle,
52870
53298
  "link-session-pr": handleLinkSessionPr,
52871
53299
  "unlink-session-pr": handleUnlinkSessionPr,
52872
- "reorder-sessions": handleReorderSessions,
52873
53300
  "ping": handlePing,
52874
53301
  "get-settings": handleGetSettings,
52875
53302
  "update-settings": handleUpdateSettings,
@@ -52914,14 +53341,6 @@ function registerControlHandlers(controlWss, deps) {
52914
53341
  if (msg.expectedState !== void 0 && session.state !== msg.expectedState) return;
52915
53342
  session.dismiss();
52916
53343
  },
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
53344
  "merge-session": async (msg, ws) => {
52926
53345
  const s = findSession(msg);
52927
53346
  if (s) reportMergeRefusal(ws, s, await s.mergeWorktree());
@@ -53239,6 +53658,9 @@ function registerControlHandlers(controlWss, deps) {
53239
53658
  type: "update-progress",
53240
53659
  journal: updateJournal
53241
53660
  }));
53661
+ const postTurnLeftoversNotice = getPostTurnLeftoversNotice?.() ?? null;
53662
+ if (postTurnLeftoversNotice) ws.send(JSON.stringify(postTurnLeftoversNotice));
53663
+ recheckPostTurnLeftovers?.();
53242
53664
  const radarStatus = typeof getRadarStatus === "function" ? getRadarStatus() : null;
53243
53665
  if (radarStatus) ws.send(JSON.stringify(radarStatus));
53244
53666
  const teamReviewStatus = typeof getTeamReviewStatus === "function" ? getTeamReviewStatus() : null;
@@ -53313,6 +53735,8 @@ function createBackendControl(dependencies) {
53313
53735
  buildHealthSnapshot: dependencies.buildHealthSnapshot,
53314
53736
  getUpdateStatus: dependencies.getUpdateStatus,
53315
53737
  getUpdateJournal: dependencies.getUpdateJournal,
53738
+ getPostTurnLeftoversNotice: dependencies.getPostTurnLeftoversNotice,
53739
+ recheckPostTurnLeftovers: dependencies.recheckPostTurnLeftovers,
53316
53740
  checkNow: dependencies.checkNow,
53317
53741
  applyUpdate: dependencies.applyUpdate,
53318
53742
  isStaging: dependencies.isStaging,
@@ -55361,6 +55785,152 @@ function createUpdateApplyLane(dependencies) {
55361
55785
  whenIdle
55362
55786
  };
55363
55787
  }
55788
+ //#endregion
55789
+ //#region shared/post-turn-leftovers.ts
55790
+ var REPORT_FOLDER_GIT_PATH = ".glimmervoid/post-turn";
55791
+ var REPORT_FILE_NAME = /^[a-zA-Z0-9_.-]+\.json$/;
55792
+ var POWERSHELL_SINGLE_QUOTES = /['\u2018\u2019\u201A\u201B]/g;
55793
+ function isReportGitPath(gitPath) {
55794
+ const folderPrefix = `${REPORT_FOLDER_GIT_PATH}/`;
55795
+ return gitPath.startsWith(folderPrefix) && REPORT_FILE_NAME.test(gitPath.slice(folderPrefix.length));
55796
+ }
55797
+ function untrackedReportFileNames(fileNames, trackedGitPaths) {
55798
+ const trackedPaths = new Set(trackedGitPaths);
55799
+ return fileNames.filter((fileName) => REPORT_FILE_NAME.test(fileName) && !trackedPaths.has(`.glimmervoid/post-turn/${fileName}`));
55800
+ }
55801
+ function quoteShellArgument(argument, platform) {
55802
+ if (platform === "win32") return `'${argument.replace(POWERSHELL_SINGLE_QUOTES, "$&$&")}'`;
55803
+ return `'${argument.replaceAll("'", "'\\''")}'`;
55804
+ }
55805
+ function trackedReportCleanupCommands(repositories, platform) {
55806
+ return repositories.map(({ repositoryPath, trackedReportGitPaths }) => {
55807
+ const quotedReportPaths = trackedReportGitPaths.map((gitPath) => quoteShellArgument(gitPath, platform)).join(" ");
55808
+ return `git -C ${quoteShellArgument(repositoryPath, platform)} rm --cached -q -- ${quotedReportPaths}`;
55809
+ }).join("\n");
55810
+ }
55811
+ function trackedReportNoticeText(repositoryCount) {
55812
+ 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:`;
55813
+ }
55814
+ //#endregion
55815
+ //#region server/post-turn-leftovers.ts
55816
+ var GIT_TIMEOUT_MS = 1e4;
55817
+ var CONTROL_CHARACTER = /[\x00-\x1f\x7f]/;
55818
+ var NOTHING_CLEANED = {
55819
+ removedFileCount: 0,
55820
+ trackedReportGitPaths: []
55821
+ };
55822
+ async function gitOutput(args, cwd) {
55823
+ const result = await runGit(args, {
55824
+ cwd,
55825
+ timeoutMs: GIT_TIMEOUT_MS,
55826
+ trim: false
55827
+ });
55828
+ if (!result.ok) throw result.error ?? new Error(result.err);
55829
+ return result.out;
55830
+ }
55831
+ function yieldToEventLoop() {
55832
+ return new Promise((resolve) => setImmediate(resolve));
55833
+ }
55834
+ async function collectWorktreePaths(projects) {
55835
+ const repositoryPaths = new Set(projects.flatMap((project) => [project.path, ...project.repos ?? []]).filter(Boolean));
55836
+ const worktreePaths = /* @__PURE__ */ new Set();
55837
+ for (const repositoryPath of repositoryPaths) {
55838
+ if (worktreePaths.has(repositoryPath)) continue;
55839
+ await yieldToEventLoop();
55840
+ for (const worktreePath of await listRepoWorktreePaths(repositoryPath, gitOutput)) worktreePaths.add(worktreePath);
55841
+ }
55842
+ return [...worktreePaths];
55843
+ }
55844
+ async function readFileNames(folder) {
55845
+ try {
55846
+ return (await fsp.readdir(folder, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name);
55847
+ } catch (error) {
55848
+ if (isMissingFileError(error)) return null;
55849
+ throw error;
55850
+ }
55851
+ }
55852
+ async function removeFolderIfEmpty(folder) {
55853
+ if ((await fsp.readdir(folder)).length > 0) return;
55854
+ await fsp.rmdir(folder);
55855
+ }
55856
+ async function isRealDirectory(folder) {
55857
+ try {
55858
+ return (await fsp.lstat(folder)).isDirectory();
55859
+ } catch (error) {
55860
+ if (isMissingFileError(error)) return false;
55861
+ throw error;
55862
+ }
55863
+ }
55864
+ async function isReportFolderOwnedByWorktree(worktreePath, reportFolder) {
55865
+ if (!await isRealDirectory(path.dirname(reportFolder)) || !await isRealDirectory(reportFolder)) return false;
55866
+ const realWorktreePath = await fsp.realpath(worktreePath);
55867
+ if (!isPathInside(realWorktreePath, await fsp.realpath(reportFolder), { allowEqual: false })) return false;
55868
+ const reportFolderTopLevel = (await gitOutput(["rev-parse", "--show-toplevel"], reportFolder)).trim();
55869
+ return equalsIgnoringCaseOnWindows(await fsp.realpath(reportFolderTopLevel), realWorktreePath);
55870
+ }
55871
+ async function listTrackedReportGitPaths(worktreePath) {
55872
+ return (await gitOutput([
55873
+ "ls-files",
55874
+ "-z",
55875
+ "--",
55876
+ REPORT_FOLDER_GIT_PATH
55877
+ ], worktreePath)).split("\0").filter(isReportGitPath);
55878
+ }
55879
+ async function cleanWorktree(worktreePath) {
55880
+ const reportFolder = path.join(worktreePath, ...REPORT_FOLDER_GIT_PATH.split("/"));
55881
+ if (!await isReportFolderOwnedByWorktree(worktreePath, reportFolder)) return NOTHING_CLEANED;
55882
+ const fileNames = await readFileNames(reportFolder);
55883
+ if (fileNames === null) return NOTHING_CLEANED;
55884
+ const trackedReportGitPaths = await listTrackedReportGitPaths(worktreePath);
55885
+ const deletableFileNames = untrackedReportFileNames(fileNames, trackedReportGitPaths);
55886
+ for (const fileName of deletableFileNames) await fsp.rm(path.join(reportFolder, fileName), { force: true });
55887
+ await removeFolderIfEmpty(reportFolder);
55888
+ await removeFolderIfEmpty(path.dirname(reportFolder));
55889
+ return {
55890
+ removedFileCount: deletableFileNames.length,
55891
+ trackedReportGitPaths
55892
+ };
55893
+ }
55894
+ async function cleanPostTurnLeftovers({ projects, logger = console, platform = process.platform }) {
55895
+ const repositoriesTrackingReports = [];
55896
+ let removedFileCount = 0;
55897
+ for (const worktreePath of await collectWorktreePaths(projects)) {
55898
+ await yieldToEventLoop();
55899
+ if (CONTROL_CHARACTER.test(worktreePath)) {
55900
+ logger.warn(`[post-turn] skipped the old report folder in ${JSON.stringify(worktreePath)} because its path contains a control character`);
55901
+ continue;
55902
+ }
55903
+ try {
55904
+ const cleanup = await cleanWorktree(worktreePath);
55905
+ removedFileCount += cleanup.removedFileCount;
55906
+ if (cleanup.trackedReportGitPaths.length > 0) repositoriesTrackingReports.push({
55907
+ repositoryPath: worktreePath,
55908
+ trackedReportGitPaths: cleanup.trackedReportGitPaths
55909
+ });
55910
+ } catch (error) {
55911
+ logger.warn(`[post-turn] could not clean the old report folder in ${worktreePath}: ${errorMessage(error)}`);
55912
+ }
55913
+ }
55914
+ if (removedFileCount > 0) logger.log(`[post-turn] removed ${removedFileCount} old post-turn report files that earlier versions left in your repositories`);
55915
+ if (repositoriesTrackingReports.length > 0) logger.warn(`[post-turn] ${trackedReportNoticeText(repositoriesTrackingReports.length)}\n${trackedReportCleanupCommands(repositoriesTrackingReports, platform)}`);
55916
+ return repositoriesTrackingReports;
55917
+ }
55918
+ async function recheckRepositoriesTrackingReports(repositories, logger = console) {
55919
+ const repositoriesStillTrackingReports = [];
55920
+ for (const { repositoryPath } of repositories) {
55921
+ await yieldToEventLoop();
55922
+ try {
55923
+ const trackedReportGitPaths = await listTrackedReportGitPaths(repositoryPath);
55924
+ if (trackedReportGitPaths.length > 0) repositoriesStillTrackingReports.push({
55925
+ repositoryPath,
55926
+ trackedReportGitPaths
55927
+ });
55928
+ } catch (error) {
55929
+ logger.warn(`[post-turn] could not re-check the old report folder in ${repositoryPath}: ${errorMessage(error)}`);
55930
+ }
55931
+ }
55932
+ return repositoriesStillTrackingReports;
55933
+ }
55364
55934
  var FLUSH_AT_EVENT_COUNT = 20;
55365
55935
  var FLUSH_INTERVAL_MS = 6e4;
55366
55936
  var SEND_TIMEOUT_MS = 5e3;
@@ -56086,6 +56656,7 @@ function createBackend(httpServer, options = {}) {
56086
56656
  getAllSessions: () => allLiveSessions(),
56087
56657
  sessionDataClients,
56088
56658
  getIngestLane: getCurrentIngestLane,
56659
+ getMemoryGateStatus: () => laneAssembly.memoryAdmission.gateStatus(),
56089
56660
  controlWss,
56090
56661
  dataWss,
56091
56662
  broadcastControl
@@ -56138,6 +56709,7 @@ function createBackend(httpServer, options = {}) {
56138
56709
  closeSessionDataClients,
56139
56710
  broadcastControl,
56140
56711
  spawnGate,
56712
+ memoryAdmission: laneAssembly.memoryAdmission,
56141
56713
  recordLane,
56142
56714
  sessionDocs,
56143
56715
  logger: console
@@ -56187,6 +56759,7 @@ function createBackend(httpServer, options = {}) {
56187
56759
  sessionRegistry.initialize();
56188
56760
  const reconcileProjectsOnReload = sessionRegistry.applyConfigReload;
56189
56761
  rtkInstall.maybeInstall();
56762
+ sweepOrphanedGuardrailsLaunches();
56190
56763
  laneAssembly.startRuntimeLanes();
56191
56764
  function applyCustomAgents(sourceConfig) {
56192
56765
  setCustomAgents(sourceConfig.customAgents ?? []);
@@ -56233,10 +56806,42 @@ function createBackend(httpServer, options = {}) {
56233
56806
  },
56234
56807
  logger: console
56235
56808
  });
56809
+ let repositoriesTrackingPostTurnReports = [];
56810
+ let isRecheckingPostTurnReports = false;
56811
+ function postTurnLeftoversMessage() {
56812
+ return {
56813
+ type: "post-turn-leftovers",
56814
+ repositories: repositoriesTrackingPostTurnReports.map(({ repositoryPath }) => repositoryPath),
56815
+ command: trackedReportCleanupCommands(repositoriesTrackingPostTurnReports, process.platform)
56816
+ };
56817
+ }
56818
+ function startPostTurnLeftoverCleanup() {
56819
+ cleanPostTurnLeftovers({ projects: config.projects }).then((repositories) => {
56820
+ repositoriesTrackingPostTurnReports = repositories;
56821
+ if (repositories.length > 0) broadcastControlFromWebSockets(postTurnLeftoversMessage());
56822
+ }).catch((error) => {
56823
+ console.warn(`[post-turn] old report cleanup failed: ${errorMessage(error)}`);
56824
+ });
56825
+ }
56826
+ function recheckPostTurnLeftovers() {
56827
+ if (isRecheckingPostTurnReports || repositoriesTrackingPostTurnReports.length === 0) return;
56828
+ isRecheckingPostTurnReports = true;
56829
+ const previousRepositories = repositoriesTrackingPostTurnReports;
56830
+ recheckRepositoriesTrackingReports(previousRepositories).then((repositories) => {
56831
+ if (JSON.stringify(repositories) === JSON.stringify(previousRepositories)) return;
56832
+ repositoriesTrackingPostTurnReports = repositories;
56833
+ broadcastControlFromWebSockets(postTurnLeftoversMessage());
56834
+ }).catch((error) => {
56835
+ console.warn(`[post-turn] old report re-check failed: ${errorMessage(error)}`);
56836
+ }).finally(() => {
56837
+ isRecheckingPostTurnReports = false;
56838
+ });
56839
+ }
56236
56840
  let stopConfigWatch = null;
56237
56841
  const shutdown = createBackendShutdown({
56238
56842
  configStore,
56239
56843
  cancelAutoResume: sessionRegistry.cancelAutoResume,
56844
+ cancelPostTurnLeftoverCleanup: () => httpServer.off("listening", startPostTurnLeftoverCleanup),
56240
56845
  healthInterval,
56241
56846
  getStopConfigWatch: () => stopConfigWatch,
56242
56847
  remoteAuth,
@@ -56304,6 +56909,8 @@ function createBackend(httpServer, options = {}) {
56304
56909
  buildHealthSnapshot,
56305
56910
  getUpdateStatus: updateCheck.getStatus,
56306
56911
  getUpdateJournal: updateApply.getJournal,
56912
+ getPostTurnLeftoversNotice: () => repositoriesTrackingPostTurnReports.length > 0 ? postTurnLeftoversMessage() : null,
56913
+ recheckPostTurnLeftovers,
56307
56914
  checkNow: updateCheck.checkNow,
56308
56915
  applyUpdate: updateApply.applyUpdate,
56309
56916
  isStaging: updateApply.isStaging,
@@ -56332,6 +56939,7 @@ function createBackend(httpServer, options = {}) {
56332
56939
  const { handleUpgrade } = webSockets;
56333
56940
  httpServer.on("upgrade", handleUpgrade);
56334
56941
  updateApply.startAfterListening(httpServer);
56942
+ httpServer.once("listening", startPostTurnLeftoverCleanup);
56335
56943
  stopConfigWatch = configStore.watchForChanges((newConfig) => {
56336
56944
  applyConfigReload(newConfig);
56337
56945
  broadcastControl({