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.
- package/dist/bin/glimmervoid.js +14 -14
- package/dist/chunks/{adapters-BmfVPxlH.js → adapters-B-IkWl-r.js} +19 -4
- package/dist/chunks/{adapters-CqFFmM2R.js → adapters-Dt3vwxwY.js} +1 -1
- package/dist/chunks/{agent-api-cli-BTHEyJKS.js → agent-api-cli-3hryuewm.js} +1 -1
- package/dist/chunks/{agent-api-cli-CBgOKXoO.js → agent-api-cli-BcZ9DLCF.js} +3 -3
- package/dist/chunks/{agent-api-core-DzKhakCv.js → agent-api-core--8WtU1xU.js} +3 -12
- package/dist/chunks/{agent-setup-cli-C8DGxlA-.js → agent-setup-cli-BixDoCkF.js} +2 -2
- package/dist/chunks/{config-BYc2z1Oc.js → config-BHBZsCBc.js} +9 -8
- package/dist/chunks/config-store-DYj3wYgw.js +1 -0
- package/dist/chunks/{config-store-B7MpiYxG.js → config-store-DbfMbz4j.js} +41 -64
- package/dist/chunks/{contracts-C6ipN_zw.js → contracts-u40eJhaf.js} +20 -14
- package/dist/chunks/{git-version-6QFnKbWa.js → git-version-BpnTd8sU.js} +1 -1
- package/dist/chunks/{grok-CZ6kP_vD.js → grok-xFs6zIu4.js} +1 -1
- package/dist/chunks/{json-file-BaxGcB2R.js → json-file-CIJf0Cel.js} +190 -9
- package/dist/chunks/{knowledge-graph-cli-DApeMzA8.js → knowledge-graph-cli-DcspNqhz.js} +1 -1
- package/dist/chunks/{loopback-post-DJ6iYWuL.js → loopback-post-DZK7YibD.js} +1 -1
- package/dist/chunks/{main-BUfjroXE.js → main-w01-P1oZ.js} +1048 -440
- package/dist/chunks/{pair-cli-GZ9t1mIO.js → pair-cli-BdoyrDCr.js} +2 -2
- package/dist/chunks/{remote-config-D2Is89Kj.js → remote-config-61zJ0hME.js} +2 -2
- package/dist/chunks/{session-BZbariv5.js → session-8Pv0y1vz.js} +1 -1
- package/dist/chunks/{session-routes-C4t8O8yg.js → session-routes-D-GZC-Sy.js} +1 -1
- package/dist/chunks/{settings-cli-DQOjmjev.js → settings-cli-DKJsm3AP.js} +3 -3
- package/dist/chunks/{ssh-sign-cli-LIBP8ppO.js → ssh-sign-cli-Dc2gOCwE.js} +2 -2
- package/dist/client/assets/index-BfqmOGgc.js +275 -0
- package/dist/client/assets/index-DxABp5yJ.css +1 -0
- package/dist/client/index.html +10 -3
- package/dist/server/index.js +1 -1
- package/dist/session/command-hook-relay.js +1 -1
- package/dist/session/hook-relay.js +1 -1
- package/dist/session/statusline-relay.js +1 -1
- package/dist/session/visions-relay.js +7 -7
- package/package.json +1 -1
- package/scripts/recover-handoff.mjs +1 -1
- package/dist/chunks/config-store-CsUoy88Q.js +0 -1
- package/dist/client/assets/index-B_Qt7fbZ.css +0 -1
- 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-
|
|
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-
|
|
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-
|
|
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
|
|
21
|
-
import { s as GLIMMERVOID_SECRET_KEYS } from "./grok-
|
|
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
|
|
24
|
-
import { $ as
|
|
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-
|
|
26
|
-
import { $ as
|
|
27
|
-
import { A as
|
|
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-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
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
|
-
...
|
|
4856
|
+
...parent.agentSpawnBudget(),
|
|
4749
4857
|
isFactoryWorker: parent.factoryRole === "worker",
|
|
4750
4858
|
isEphemeral: parent.ephemeral
|
|
4751
4859
|
});
|
|
4752
|
-
if (
|
|
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
|
-
|
|
5455
|
-
if (
|
|
5456
|
-
|
|
5457
|
-
|
|
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
|
|
5460
|
-
const
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
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
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
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
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
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
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
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$
|
|
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$
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
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 + (
|
|
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
|
|
12778
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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:
|
|
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 ||
|
|
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
|
-
|
|
14733
|
-
|
|
14734
|
-
|
|
14735
|
-
|
|
14736
|
-
|
|
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
|
-
|
|
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
|
|
14853
|
-
|
|
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
|
-
|
|
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
|
|
23241
|
-
|
|
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
|
-
|
|
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: {
|
|
23750
|
+
env: {
|
|
23751
|
+
...plan.launchEnv,
|
|
23752
|
+
...bridgeSocketLauncherEnv(os.tmpdir(), process.platform)
|
|
23753
|
+
},
|
|
23369
23754
|
...startDir === void 0 ? {} : { cwd: startDir },
|
|
23370
|
-
dispose:
|
|
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(
|
|
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
|
-
|
|
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,
|
|
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:
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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
|
|
38854
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38882
|
-
const content = await fs.promises.readFile(
|
|
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:
|
|
39227
|
+
format: "html",
|
|
38888
39228
|
content
|
|
38889
39229
|
};
|
|
38890
39230
|
} catch (err) {
|
|
38891
|
-
if (isMissingFileError(err, { includeNotDir: false }))
|
|
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
|
|
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({
|
|
44822
|
-
|
|
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
|
-
|
|
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 (
|
|
44834
|
-
|
|
45248
|
+
if (force) pendingForce = true;
|
|
45249
|
+
if (!force && scopeDirs) pendingScopeDirs = [...pendingScopeDirs ?? [], ...scopeDirs];
|
|
44835
45250
|
return activePass;
|
|
44836
45251
|
}
|
|
44837
|
-
activePass = runPassChain({
|
|
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({
|
|
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
|
-
|
|
45551
|
+
nudge.schedule();
|
|
45117
45552
|
return;
|
|
45118
45553
|
}
|
|
45119
|
-
runPassAndPush({
|
|
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
|
|
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({
|