glimmervoid 0.41.0 → 0.42.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 +13 -13
- package/dist/chunks/{adapters-JDV45XpZ.js → adapters-C8GfnK2r.js} +1 -1
- package/dist/chunks/{adapters-yVOK5me4.js → adapters-D2u85MEH.js} +30 -15
- package/dist/chunks/{agent-api-cli-BSY2jR0P.js → agent-api-cli-B-1MqjKc.js} +1 -1
- package/dist/chunks/{agent-api-cli-1xk9WryE.js → agent-api-cli-D7JPxNso.js} +10 -5
- package/dist/chunks/{agent-api-core-syEPHs7j.js → agent-api-core-PwO7fPoc.js} +19 -5
- package/dist/chunks/{agent-setup-cli-py1gUMvJ.js → agent-setup-cli-C9qDV2g5.js} +1 -1
- package/dist/chunks/config-store-BL8LDBuM.js +1 -0
- package/dist/chunks/{config-store-BEaaXTd5.js → config-store-BxCW2IwK.js} +2 -2
- package/dist/chunks/{contracts-V_hfT23R.js → contracts-LXlDbl4R.js} +2 -2
- package/dist/chunks/{control-messages-Ci4g6gD1.js → control-messages-88sdxt-U.js} +9 -2
- package/dist/chunks/{git-version-D_iXgYTZ.js → git-version-DqagvnAZ.js} +1 -1
- package/dist/chunks/{grok-BUGgQVE3.js → grok-DwrAcNHC.js} +1 -1
- package/dist/chunks/{main-Bbh8ky2x.js → main-DZzDU4Q4.js} +403 -123
- package/dist/chunks/{pair-cli-DkItUGpT.js → pair-cli-3KKNhZ22.js} +2 -2
- package/dist/chunks/{remote-config-DFwmHWed.js → remote-config-Ce5nN0zL.js} +1 -1
- package/dist/chunks/{session-C8IpFmGQ.js → session-3eSP3t1i.js} +8 -2
- package/dist/chunks/{session-routes-nySS7DUE.js → session-routes-B2oSzmQn.js} +1 -1
- package/dist/chunks/{settings-cli-CaYd3bUH.js → settings-cli-BYY991YC.js} +5 -5
- package/dist/chunks/{settings-readout-core-Du7n-zyD.js → settings-readout-core-CWAhwZKN.js} +2 -2
- package/dist/chunks/{ssh-sign-cli-BLzmQ3Vh.js → ssh-sign-cli-BbqYIPwh.js} +2 -2
- package/dist/client/assets/index-C9wfiVqt.js +176 -0
- package/dist/client/assets/index-PnaYWtoe.css +2 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/config-store-DE_r4DP8.js +0 -1
- package/dist/client/assets/index-D7QFo-gy.js +0 -176
- package/dist/client/assets/index-DKnOk67Z.css +0 -2
|
@@ -2,13 +2,13 @@ import { i as sanitizeOneLine, n as requireExecutableSpawnHelper, r as firstLine
|
|
|
2
2
|
import { a as isMissingFileError, i as errorText$1, r as errorMessage, t as errorCode } from "./text-DoirglWV.js";
|
|
3
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 { A as SIGN_URL_ENV, At as
|
|
5
|
+
import { $t as STATES, A as SIGN_URL_ENV, At as TeamReviewStatus, B as LinkedPr, Bt as shortSha, Ct as ReviewThreadId, Dt as TeamReviewCompareFiles, E as SESSION_DOC_BODY_CAP_BYTES, Ft as decisionAsIfApprovalRequired, G as MyPrKeepMergeableRequest, H as MyPrAutoRebase, Ht as NODE_ID_RE, I as SshSignRequest, J as MyPrMergeMethod, L as TASK_TITLE_CONTROL_CHARACTERS, Lt as hasStandingViewerApproval, Mt as TeamReviewThreadResult, Nt as TeamReviewThreadsRepository, O as SESSION_NAME_PATTERN, Ot as TeamReviewResolveResponse, Pt as canApproveAfterComment, Q as MyPrMergeWhenReadyRequest, Qt as RESTARTABLE_STATES, R as TaskTitle, S as ConversationQuestion, St as ReviewResult, Tt as TeamReviewActionRequest, U as MyPrCheckContext, Ut as REPO_SLUG_RE, V as MyPr, Vt as GH_SEGMENT, W as MyPrCheckContextsResponse, Wt as repoParts, Xt as KILLABLE_STATES, Y as MyPrMergeRequest, Yt as BADGE_LABELS, Z as MyPrMergeStateResponse, Zt as MERGEABLE_LIVE_STATES, _ as CONVERSATION_TOOL_RESULT_HOOK_EVENT, _t as QueuedReview, bt as ReviewDraft, dt as EarlierReview, ft as FindingSeverity, g as CONVERSATION_PERMISSION_HOOK_EVENT, gt as PrDetail, h as CONVERSATION_APPROVAL_SUMMARY_MAX_CHARS, ht as PostingPlan, it as MyPrsState, j as SSH_SIGN_BODY_CAP_BYTES, jt as TeamReviewThreadCommentsResponse, k as SESSION_NAME_RULE_TEXT, kt as TeamReviewStateEntry, lt as CommitSha, m as COHERENCE_HOOKS_INACTIVE, mt as GithubReviewState, n as AGENT_URL_ENV, nt as MyPrThreadsResponse, q as MyPrLaneEntry, r as ASK_USER_QUESTION_TOOL_NAME, rt as MyPrsSnapshot, tt as MyPrThreadNode, ut as DECIDING_REVIEW_STATES, w as RefinedTaskTitle, wt as ReviewVerdict, xt as ReviewFinding, yt as ReviewComment, zt as HEX_SHA_RE } from "./session-3eSP3t1i.js";
|
|
6
6
|
import { n as execFileAsync, o as spawn, r as execFileSync, t as execFile } from "./child-process-safe-D4hyVIXj.js";
|
|
7
|
-
import { At as planTitle, C as GithubPrsFrame, Ct as PLAN_HOOK_EVENT, D as WorkflowsSettings, Dt as PlanDecision, E as WorkflowLabelName, Et as PLAN_TOOL_NAME, Ft as IssuesSearchResponse, It as IssuesState, L as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Lt as IssuesStatus, Mt as GithubIssueResponse, Nt as IssueKey, Ot as PlanRevision, Pt as IssueUpdate, Rt as issuePullRequestStateFromGithub, S as GithubPrLightSearchNode, St as PLAN_BODY_CAP_BYTES, T as WorkflowCommentBody, Tt as PLAN_RESULT_HOOK_EVENT, _ as MergedPrListing, _t as TraceCheckpoint, b as GithubPrDetailNode, bt as ChangeMap, c as pullRequestKeyFromReference, d as BenchmarkId, f as BenchmarkJudgement, g as CommitComparison, gt as MAX_RAW_LINE_CHARS, h as BenchmarkSuite, ht as SessionSetup, i as DIFF_ANNOTATION_NOTE_MAX_CHARS, jt as GithubIssuePullRequestsRepository, k as WorkflowsState, kt as parseExitPlanModeHookPayload, l as BenchmarkActionRequest, m as BenchmarkStatus, mt as UpdateJournal, n as ClientMessage, p as BenchmarkRun, rt as USAGE_INTEGER_RANGES, t as CONTROL_FRAME_MAX_BYTES, u as BenchmarkCase, v as MinedPrReviewData, vt as TraceRecord, w as comparablePrKey, wt as PLAN_HOOK_OUTPUT_MAX_CHARS, x as GithubPrKey, xt as changeMapFactId, y as PrCheckoutCaseInput, yt as CoherenceJournalOutput } from "./control-messages-
|
|
8
|
-
import { a as permitsHelpCommand, r as describeSessionLimits } from "./session-routes-
|
|
9
|
-
import { a as decideSpawnAllowance, c as
|
|
7
|
+
import { At as planTitle, C as GithubPrsFrame, Ct as PLAN_HOOK_EVENT, D as WorkflowsSettings, Dt as PlanDecision, E as WorkflowLabelName, Et as PLAN_TOOL_NAME, Ft as IssuesSearchResponse, It as IssuesState, L as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Lt as IssuesStatus, Mt as GithubIssueResponse, Nt as IssueKey, Ot as PlanRevision, Pt as IssueUpdate, Rt as issuePullRequestStateFromGithub, S as GithubPrLightSearchNode, St as PLAN_BODY_CAP_BYTES, T as WorkflowCommentBody, Tt as PLAN_RESULT_HOOK_EVENT, _ as MergedPrListing, _t as TraceCheckpoint, b as GithubPrDetailNode, bt as ChangeMap, c as pullRequestKeyFromReference, d as BenchmarkId, f as BenchmarkJudgement, g as CommitComparison, gt as MAX_RAW_LINE_CHARS, h as BenchmarkSuite, ht as SessionSetup, i as DIFF_ANNOTATION_NOTE_MAX_CHARS, jt as GithubIssuePullRequestsRepository, k as WorkflowsState, kt as parseExitPlanModeHookPayload, l as BenchmarkActionRequest, m as BenchmarkStatus, mt as UpdateJournal, n as ClientMessage, p as BenchmarkRun, rt as USAGE_INTEGER_RANGES, t as CONTROL_FRAME_MAX_BYTES, u as BenchmarkCase, v as MinedPrReviewData, vt as TraceRecord, w as comparablePrKey, wt as PLAN_HOOK_OUTPUT_MAX_CHARS, x as GithubPrKey, xt as changeMapFactId, y as PrCheckoutCaseInput, yt as CoherenceJournalOutput } from "./control-messages-88sdxt-U.js";
|
|
8
|
+
import { a as permitsHelpCommand, r as describeSessionLimits } from "./session-routes-B2oSzmQn.js";
|
|
9
|
+
import { a as decideSpawnAllowance, c as describeKeptChildAttention, d as describeUnpushedCloseRefusal, h as projectReviews, i as decideAgentRequest, l as describePushLanding, m as projectMyPrs, n as REFUSAL_REASON, o as deriveChildSessionName, p as parseAgentVerb, s as describeChildEndNote, u as describeRoleRefusal } from "./agent-api-core-PwO7fPoc.js";
|
|
10
10
|
import { n as glimmervoidHomeDir, t as decideConfigPath } from "./config-path-core-DFt-TemS.js";
|
|
11
|
-
import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-
|
|
11
|
+
import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-DqagvnAZ.js";
|
|
12
12
|
import { n as checkGuardrailsDependencies, t as allowGuardedSpawn } from "./guardrails-deps-BjPzk1rE.js";
|
|
13
13
|
import { t as HOOK_URL_ENV } from "./hook-relay-core-DA_bz2KA.js";
|
|
14
14
|
import { t as HOOK_VIA_GUARDRAILS_PROXY_ENV } from "./guardrails-proxy-core-BI3rdY-m.js";
|
|
@@ -21,13 +21,13 @@ import { n as DEFAULT_MAX_MS, r as nextBackoffMs, t as DEFAULT_BASE_MS } from ".
|
|
|
21
21
|
import { n as unrefTimer, t as DEFAULT_TIMER_FNS } from "./timer-deps-u2DAQLok.js";
|
|
22
22
|
import { n as shellQuote } from "./hook-command-core-qTNK-xMF.js";
|
|
23
23
|
import { C as parseCgroupBytes, S as memoryReserveBytes, T as parseMemAvailableBytes, _ as createSpawnGate, a as jsonStateLoadError, b as cgroupLimitBytes, 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 isQueueAdmissionTimeout, w as parseCgroupV2Path, x as headroomFromReadings, y as cgroupAncestorPaths } from "./json-file-DFCTvPjj.js";
|
|
24
|
-
import { $ as DEFAULT_BRANCH_GC_PREFIXES, B as buildExceptionProperties, F as TELEMETRY_BATCH_URL, G as exceptionFingerprint, H as decideIsDevInstall, I as TELEMETRY_FLAGS_URL, J as resolveProjectToken, K as nodeMajorVersion, L as adapterBucket, P as FIRST_RUN_NOTICE, R as buildAiGenerationEvents, U as decideRemoteTelemetryState, V as classifySessionExit, W as decideTelemetryConsent, Z as stableConfigKey, a as generateProjectId, at as worktreeIntegrationTips, b as validateSettingsBlock, ct as isSameDirectoryPath, d as assetForPlatform, et as matchedPrefix, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as ensureProjectIds, it as usablePrefixes, m as installTargetPath, n as DEFAULT_CONFIG, nt as planWorktreeGc, o as glimmervoidHomeDir$1, ot as canonicalizePath, p as findEscapingArchiveMember, q as parsePosthogReleaseId, r as createConfigStore, rt as sessionIdFromBranch, st as comparableDirectoryPath, t as ABORT_CONFIG_SAVE, tt as planBranchGc, v as mergeSettingsBlock, y as settingsBlockSpecFor, z as buildBrowserExceptionProperties } from "./config-store-
|
|
25
|
-
import { E as USAGE_VENDOR_KEYS, T as USAGE_COST_MODES, _ as PersistedTaskTitle, a as parseDataClientMessage, b as configIssueMessage, c as PendingCrashReport, d as TelemetryState, g as ConfigUpdate, i as parseDataAttachHistory, l as TELEMETRY_EVENT_SCHEMAS, m as BrowserConfig, n as HookEnvelope, o as DashboardTelemetryReport, r as DEEP_HISTORY_SCROLLBACK_LINES, t as HTTP_URL_RE, v as RUNTIME_CONFIG_SCALAR_KEYS, y as TASK_TITLE_SOURCE_PRIORITY } from "./contracts-
|
|
26
|
-
import { A as dedupeCodexFiles, B as shouldPersistWarehouse, C as shortSha$1, D as codexSessionIdFromPath, E as codexRootCandidates, F as passOutcome, H as claudeCode, I as projectDirCandidates, L as resolveProjectsDirs, M as grokHomes, N as grokRootCandidates, O as continuationDelayMs, P as isUsageFile, R as scopedClaudeRoots, S as parseResolvedSha, T as codexHomes, V as splitLines$1, _ as isCheckFresh, a as describeAgentResolvability, b as parseRegistryDeprecation, c as isKnownAgentId, d as resolveAdapter, f as setCustomAgents, g as decideUpdateStatus, h as decideInstallFlavor, i as customAgentFingerprint, j as expandTilde, k as decideFileRead, l as listAgentIds, m as readHostCodexVersion, n as cachedAgentResolvability, o as getAdapter, p as codex, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resetCommandCache, v as normalizeSha, w as codexFallbackRoots, x as parseRegistryLatest, y as normalizeUpdateChannel, z as shouldEvaluateDespiteIoFailures } from "./adapters-
|
|
24
|
+
import { $ as DEFAULT_BRANCH_GC_PREFIXES, B as buildExceptionProperties, F as TELEMETRY_BATCH_URL, G as exceptionFingerprint, H as decideIsDevInstall, I as TELEMETRY_FLAGS_URL, J as resolveProjectToken, K as nodeMajorVersion, L as adapterBucket, P as FIRST_RUN_NOTICE, R as buildAiGenerationEvents, U as decideRemoteTelemetryState, V as classifySessionExit, W as decideTelemetryConsent, Z as stableConfigKey, a as generateProjectId, at as worktreeIntegrationTips, b as validateSettingsBlock, ct as isSameDirectoryPath, d as assetForPlatform, et as matchedPrefix, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as ensureProjectIds, it as usablePrefixes, m as installTargetPath, n as DEFAULT_CONFIG, nt as planWorktreeGc, o as glimmervoidHomeDir$1, ot as canonicalizePath, p as findEscapingArchiveMember, q as parsePosthogReleaseId, r as createConfigStore, rt as sessionIdFromBranch, st as comparableDirectoryPath, t as ABORT_CONFIG_SAVE, tt as planBranchGc, v as mergeSettingsBlock, y as settingsBlockSpecFor, z as buildBrowserExceptionProperties } from "./config-store-BxCW2IwK.js";
|
|
25
|
+
import { E as USAGE_VENDOR_KEYS, T as USAGE_COST_MODES, _ as PersistedTaskTitle, a as parseDataClientMessage, b as configIssueMessage, c as PendingCrashReport, d as TelemetryState, g as ConfigUpdate, i as parseDataAttachHistory, l as TELEMETRY_EVENT_SCHEMAS, m as BrowserConfig, n as HookEnvelope, o as DashboardTelemetryReport, r as DEEP_HISTORY_SCROLLBACK_LINES, t as HTTP_URL_RE, v as RUNTIME_CONFIG_SCALAR_KEYS, y as TASK_TITLE_SOURCE_PRIORITY } from "./contracts-LXlDbl4R.js";
|
|
26
|
+
import { A as dedupeCodexFiles, B as shouldPersistWarehouse, C as shortSha$1, D as codexSessionIdFromPath, E as codexRootCandidates, F as passOutcome, H as claudeCode, I as projectDirCandidates, L as resolveProjectsDirs, M as grokHomes, N as grokRootCandidates, O as continuationDelayMs, P as isUsageFile, R as scopedClaudeRoots, S as parseResolvedSha, T as codexHomes, V as splitLines$1, _ as isCheckFresh, a as describeAgentResolvability, b as parseRegistryDeprecation, c as isKnownAgentId, d as resolveAdapter, f as setCustomAgents, g as decideUpdateStatus, h as decideInstallFlavor, i as customAgentFingerprint, j as expandTilde, k as decideFileRead, l as listAgentIds, m as readHostCodexVersion, n as cachedAgentResolvability, o as getAdapter, p as codex, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resetCommandCache, v as normalizeSha, w as codexFallbackRoots, x as parseRegistryLatest, y as normalizeUpdateChannel, z as shouldEvaluateDespiteIoFailures } from "./adapters-D2u85MEH.js";
|
|
27
27
|
import { t as HOOK_TOOLS } from "./hook-tools-Dl7-r2Eb.js";
|
|
28
28
|
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";
|
|
29
|
-
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 { n as configFileViewOf, u as readSettingsForAgent } from "./settings-readout-core-
|
|
29
|
+
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-Ce5nN0zL.js";
|
|
30
|
+
import { n as configFileViewOf, u as readSettingsForAgent } from "./settings-readout-core-CWAhwZKN.js";
|
|
31
31
|
import { _ as escapeMarkup, g as createLaneLog, n as createVisionsSetup } from "./visions-setup-DB2P8JUL.js";
|
|
32
32
|
import { t as getRtkPath } from "./rtk-resolver-BDHi6RMv.js";
|
|
33
33
|
import { createRequire } from "node:module";
|
|
@@ -2040,6 +2040,7 @@ function projectSessionCard(source, { id, name }) {
|
|
|
2040
2040
|
guardrailsReleased: source.isGuardrailsReleased === true,
|
|
2041
2041
|
resumeSessionId: source.resumeSessionId || null,
|
|
2042
2042
|
...source.isWorkspace ? { workspace: true } : {},
|
|
2043
|
+
...source.remoteWorkspace ? { remoteWorkspace: source.remoteWorkspace } : {},
|
|
2043
2044
|
...source.linkedPrs?.length ? { linkedPrs: source.linkedPrs } : {},
|
|
2044
2045
|
...source.ownerId ? { ownerId: source.ownerId } : {}
|
|
2045
2046
|
};
|
|
@@ -2065,6 +2066,7 @@ function projectSessionSnapshots(source) {
|
|
|
2065
2066
|
ownerId: source.ownerId ?? null,
|
|
2066
2067
|
isWorktree: source.isWorktree,
|
|
2067
2068
|
isWorkspace: source.isWorkspace,
|
|
2069
|
+
remoteWorkspace: source.remoteWorkspace ?? null,
|
|
2068
2070
|
isGuarded: source.isGuarded === true,
|
|
2069
2071
|
isGuardrailsReleased: source.isGuardrailsReleased === true,
|
|
2070
2072
|
resumeSessionId: source.resumeSessionId,
|
|
@@ -2082,6 +2084,7 @@ function projectSessionSnapshots(source) {
|
|
|
2082
2084
|
mergeReason: source.mergeReason,
|
|
2083
2085
|
worktreeNotice: source.worktreeNotice,
|
|
2084
2086
|
failureReason: source.failureReason ?? null,
|
|
2087
|
+
unseenOutcome: source.unseenOutcome ?? null,
|
|
2085
2088
|
effectiveBase: source.effectiveBase ?? null,
|
|
2086
2089
|
linkedPrs: source.linkedPrs,
|
|
2087
2090
|
coherenceHooks: source.coherenceHooks ?? null,
|
|
@@ -2561,6 +2564,7 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2561
2564
|
const parentsWithChannelCleanup = /* @__PURE__ */ new WeakSet();
|
|
2562
2565
|
const sessionsWithOperatorWatch = /* @__PURE__ */ new WeakSet();
|
|
2563
2566
|
const lastOperatorInputAtBySession = /* @__PURE__ */ new WeakMap();
|
|
2567
|
+
const childIdsToDiscard = /* @__PURE__ */ new Set();
|
|
2564
2568
|
function forgetLink(childId) {
|
|
2565
2569
|
const link = linksByChildId.get(childId);
|
|
2566
2570
|
if (link) for (const timer of link.retryTimersBySide.values()) clearTimeoutFn(timer);
|
|
@@ -2695,6 +2699,45 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2695
2699
|
parent.off("teardown", closeChildWithParent);
|
|
2696
2700
|
};
|
|
2697
2701
|
}
|
|
2702
|
+
async function keepOrDiscardChildWorktree(child, worktree, isParentRemoved) {
|
|
2703
|
+
const isDiscardRequested = childIdsToDiscard.delete(child.id);
|
|
2704
|
+
if (!worktree) return false;
|
|
2705
|
+
if (isDiscardRequested || isParentRemoved) {
|
|
2706
|
+
await child.discardWorktree();
|
|
2707
|
+
return false;
|
|
2708
|
+
}
|
|
2709
|
+
return !await child.discardWorktreeIfClean();
|
|
2710
|
+
}
|
|
2711
|
+
async function settleChildWorktree(parentId, child, childName, startAttempt, worktreeAtStart) {
|
|
2712
|
+
try {
|
|
2713
|
+
await startAttempt.catch(() => {});
|
|
2714
|
+
await child._awaitPendingReaps();
|
|
2715
|
+
const worktree = child.getWorktreeCarry();
|
|
2716
|
+
const registeredParent = listAllSessions().find((session) => session.id === parentId);
|
|
2717
|
+
const isKept = await keepOrDiscardChildWorktree(child, worktree, !registeredParent);
|
|
2718
|
+
if (registeredParent && worktree && isKept) {
|
|
2719
|
+
logger.warn(`[${AGENT_LANE_TAG} ${childName}] kept a worktree with unpushed work: ${worktree.worktreeDir}`);
|
|
2720
|
+
registeredParent.noteAttention(describeKeptChildAttention(childName, worktree));
|
|
2721
|
+
post(child.id, "toParent", {
|
|
2722
|
+
author: "glimmervoid",
|
|
2723
|
+
text: describeChildEndNote(childName, {
|
|
2724
|
+
kind: "kept",
|
|
2725
|
+
worktree
|
|
2726
|
+
}),
|
|
2727
|
+
at: now()
|
|
2728
|
+
});
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2731
|
+
const kind = worktree || worktreeAtStart ? "discarded" : "none";
|
|
2732
|
+
post(child.id, "toParent", {
|
|
2733
|
+
author: "glimmervoid",
|
|
2734
|
+
text: describeChildEndNote(childName, { kind }),
|
|
2735
|
+
at: now()
|
|
2736
|
+
});
|
|
2737
|
+
} catch (error) {
|
|
2738
|
+
logger.warn(`[${AGENT_LANE_TAG} ${childName}] could not settle the worktree: ${errorMessage(error)}`);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2698
2741
|
function post(childId, side, entry) {
|
|
2699
2742
|
const channel = channelsByChildId.get(childId);
|
|
2700
2743
|
if (!channel) return;
|
|
@@ -2796,6 +2839,8 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2796
2839
|
unguarded: !parent.isGuarded
|
|
2797
2840
|
};
|
|
2798
2841
|
let child = null;
|
|
2842
|
+
let worktreeAtStart = null;
|
|
2843
|
+
let startAttempt = Promise.resolve();
|
|
2799
2844
|
let released = false;
|
|
2800
2845
|
let detachFromParent = () => {};
|
|
2801
2846
|
const releaseChildSlot = () => {
|
|
@@ -2808,6 +2853,7 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2808
2853
|
id,
|
|
2809
2854
|
session: name
|
|
2810
2855
|
});
|
|
2856
|
+
if (child) settleChildWorktree(parent.id, child, name, startAttempt, worktreeAtStart);
|
|
2811
2857
|
};
|
|
2812
2858
|
const abandonChild = (reason) => {
|
|
2813
2859
|
logger.warn(`[${AGENT_LANE_TAG} ${name}] ${reason}`);
|
|
@@ -2843,7 +2889,7 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2843
2889
|
});
|
|
2844
2890
|
spawned.on("exit", releaseChildSlot);
|
|
2845
2891
|
spawned.on("teardown", releaseChildSlot);
|
|
2846
|
-
|
|
2892
|
+
startAttempt = spawnGate.run(() => {
|
|
2847
2893
|
if (spawned._destroyed) return void 0;
|
|
2848
2894
|
broadcastControl({
|
|
2849
2895
|
type: "session-added",
|
|
@@ -2854,7 +2900,9 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2854
2900
|
});
|
|
2855
2901
|
return spawned.start();
|
|
2856
2902
|
});
|
|
2903
|
+
await startAttempt;
|
|
2857
2904
|
if (!spawned.hasLivePty) return abandonChild("the child never reached a live terminal");
|
|
2905
|
+
worktreeAtStart = spawned.getWorktreeCarry();
|
|
2858
2906
|
return {
|
|
2859
2907
|
ok: true,
|
|
2860
2908
|
sessionId: id,
|
|
@@ -2981,7 +3029,7 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2981
3029
|
error: outcome.error
|
|
2982
3030
|
}
|
|
2983
3031
|
};
|
|
2984
|
-
const note = `this session owns ${outcome.name} until the task is done: glimmervoid read ${outcome.sessionId} reads its messages, glimmervoid send ${outcome.sessionId} <text> steers it, and glimmervoid close ${outcome.sessionId} ends it`;
|
|
3032
|
+
const note = `this session owns ${outcome.name} until the task is done: glimmervoid read ${outcome.sessionId} reads its messages, glimmervoid send ${outcome.sessionId} <text> steers it, and glimmervoid close ${outcome.sessionId} ends it and discards its worktree, refusing while it holds work that is not on the integration branch unless you add --discard`;
|
|
2985
3033
|
return {
|
|
2986
3034
|
status: 200,
|
|
2987
3035
|
body: {
|
|
@@ -3050,11 +3098,16 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
3050
3098
|
}
|
|
3051
3099
|
};
|
|
3052
3100
|
}
|
|
3053
|
-
function close(session, { child }) {
|
|
3101
|
+
async function close(session, { child, discard = false }) {
|
|
3054
3102
|
const owned = findChildOf(session, child);
|
|
3055
3103
|
if (!owned.ok) return refusal(404, owned.error);
|
|
3056
3104
|
const childSession = agentSessions.get(owned.channel.childId);
|
|
3057
|
-
|
|
3105
|
+
const alreadyEnded = refusal(409, `${owned.channel.childName} has already ended`);
|
|
3106
|
+
if (!childSession) return alreadyEnded;
|
|
3107
|
+
const worktree = childSession.getWorktreeCarry();
|
|
3108
|
+
if (!discard && worktree && await childSession.hasUnmergedWork()) return refusal(409, describeUnpushedCloseRefusal(owned.channel.childName, owned.channel.childId, worktree));
|
|
3109
|
+
if (agentSessions.get(owned.channel.childId) !== childSession) return alreadyEnded;
|
|
3110
|
+
if (discard) childIdsToDiscard.add(childSession.id);
|
|
3058
3111
|
childSession.destroy();
|
|
3059
3112
|
return {
|
|
3060
3113
|
status: 200,
|
|
@@ -4113,7 +4166,7 @@ function explainNotification(to, gate, event, opts) {
|
|
|
4113
4166
|
};
|
|
4114
4167
|
}
|
|
4115
4168
|
if (to === STATES.RUNNING) {
|
|
4116
|
-
if (event === "user_input" ||
|
|
4169
|
+
if (event === "user_input" || !opts || !opts.hookSeen) {
|
|
4117
4170
|
gate.reset();
|
|
4118
4171
|
return {
|
|
4119
4172
|
category: null,
|
|
@@ -5371,10 +5424,10 @@ function createSessionEventWiring(dependencies) {
|
|
|
5371
5424
|
session.on("agent-attention", ({ note }) => {
|
|
5372
5425
|
pendingAgentNote = pendingAgentNote === null ? note : `${pendingAgentNote} | ${note}`;
|
|
5373
5426
|
});
|
|
5374
|
-
session.on("user-prompt", () => {
|
|
5427
|
+
session.on("user-prompt", ({ isSelfWake }) => {
|
|
5375
5428
|
markTurnWorking();
|
|
5376
5429
|
pendingAgentNote = null;
|
|
5377
|
-
notifyGate.reset();
|
|
5430
|
+
if (!isSelfWake) notifyGate.reset();
|
|
5378
5431
|
releaseDeferredAcknowledge();
|
|
5379
5432
|
});
|
|
5380
5433
|
session.on("waiting-input-acknowledged", () => dependencies.notificationManager.acknowledge(session.id));
|
|
@@ -5405,6 +5458,7 @@ function createSessionEventWiring(dependencies) {
|
|
|
5405
5458
|
relay("wakeup-change", "session-wakeup");
|
|
5406
5459
|
session.on("wakeup-change", persistWasWorking);
|
|
5407
5460
|
relay("prompt-kind-change", "session-prompt");
|
|
5461
|
+
relay("unseen-outcome-change", "session-unseen");
|
|
5408
5462
|
relay("conversation-change", "session-conversation");
|
|
5409
5463
|
session.on("merge-status", ({ mergeStatus, reason, parked }) => {
|
|
5410
5464
|
dependencies.broadcastControl({
|
|
@@ -6569,6 +6623,18 @@ function formatTouchedRanges(ranges) {
|
|
|
6569
6623
|
return (Array.isArray(ranges) ? ranges : []).map((range) => range.start === range.end ? `${range.start}` : `${range.start}-${range.end}`).join(", ");
|
|
6570
6624
|
}
|
|
6571
6625
|
//#endregion
|
|
6626
|
+
//#region shared/fnv-hash.ts
|
|
6627
|
+
var FNV_OFFSET_BASIS = 2166136261;
|
|
6628
|
+
var FNV_PRIME = 16777619;
|
|
6629
|
+
function fnv1a32(text) {
|
|
6630
|
+
let hash = FNV_OFFSET_BASIS;
|
|
6631
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
6632
|
+
hash ^= text.charCodeAt(index);
|
|
6633
|
+
hash = Math.imul(hash, FNV_PRIME) >>> 0;
|
|
6634
|
+
}
|
|
6635
|
+
return hash;
|
|
6636
|
+
}
|
|
6637
|
+
//#endregion
|
|
6572
6638
|
//#region server/visions/core/visions-dispatch-core.ts
|
|
6573
6639
|
var DEFAULT_QUIET_MS = 3e4;
|
|
6574
6640
|
var DEFAULT_COOLDOWN_MS = 3e5;
|
|
@@ -6647,12 +6713,7 @@ function resolveVisionsConfig(raw) {
|
|
|
6647
6713
|
}
|
|
6648
6714
|
function hashText$1(text) {
|
|
6649
6715
|
const value = typeof text === "string" ? text : "";
|
|
6650
|
-
|
|
6651
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
6652
|
-
hash ^= value.charCodeAt(index);
|
|
6653
|
-
hash = Math.imul(hash, 16777619) >>> 0;
|
|
6654
|
-
}
|
|
6655
|
-
return `${value.length.toString(36)}-${hash.toString(36)}`;
|
|
6716
|
+
return `${value.length.toString(36)}-${fnv1a32(value).toString(36)}`;
|
|
6656
6717
|
}
|
|
6657
6718
|
function createDispatchState(dispatchTimes = []) {
|
|
6658
6719
|
return {
|
|
@@ -12813,7 +12874,13 @@ function seedTaskTitleSources({ persistedTitle, resumeSessionId, customTitle, in
|
|
|
12813
12874
|
customTitle
|
|
12814
12875
|
};
|
|
12815
12876
|
}
|
|
12816
|
-
var
|
|
12877
|
+
var envelopePattern = (tag) => new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?</${tag}\\s*>`, "gi");
|
|
12878
|
+
var TASK_NOTIFICATION_ENVELOPE_PATTERN = envelopePattern("task-notification");
|
|
12879
|
+
var PROMPT_ENVELOPE_PATTERNS = [
|
|
12880
|
+
envelopePattern("pasted_content"),
|
|
12881
|
+
envelopePattern("system-reminder"),
|
|
12882
|
+
TASK_NOTIFICATION_ENVELOPE_PATTERN
|
|
12883
|
+
];
|
|
12817
12884
|
var MIN_SUBSTANTIVE_WORDS = 3;
|
|
12818
12885
|
var CONTINUATION_PROMPTS = new Set([
|
|
12819
12886
|
"yes please continue",
|
|
@@ -12833,6 +12900,10 @@ function cleanTaskPrompt(prompt) {
|
|
|
12833
12900
|
for (const pattern of PROMPT_ENVELOPE_PATTERNS) cleanedPrompt = cleanedPrompt.replace(pattern, " ");
|
|
12834
12901
|
return cleanedPrompt.replace(/\s+/g, " ").trim();
|
|
12835
12902
|
}
|
|
12903
|
+
function isSelfWakePrompt(prompt) {
|
|
12904
|
+
if (typeof prompt !== "string") return false;
|
|
12905
|
+
return prompt.replace(TASK_NOTIFICATION_ENVELOPE_PATTERN, " ").trim() === "" && prompt.trim() !== "";
|
|
12906
|
+
}
|
|
12836
12907
|
function isSubstantivePrompt(prompt) {
|
|
12837
12908
|
if (typeof prompt !== "string") return false;
|
|
12838
12909
|
const cleanedPrompt = cleanTaskPrompt(prompt);
|
|
@@ -13391,6 +13462,7 @@ function coderCreateArgs({ workspace, template, stopAfter, parameters = {} }) {
|
|
|
13391
13462
|
"--stop-after",
|
|
13392
13463
|
stopAfter,
|
|
13393
13464
|
...parameterArgs,
|
|
13465
|
+
"--use-parameter-defaults",
|
|
13394
13466
|
"--yes"
|
|
13395
13467
|
];
|
|
13396
13468
|
}
|
|
@@ -14464,6 +14536,13 @@ var ENTRY_HOOKS = {
|
|
|
14464
14536
|
session.emit("session-done", { name: session.name });
|
|
14465
14537
|
}
|
|
14466
14538
|
};
|
|
14539
|
+
var UNSEEN_OUTCOME_ON_ENTRY = /* @__PURE__ */ new Map([
|
|
14540
|
+
[STATES.COMPLETE, "finished"],
|
|
14541
|
+
[STATES.FAILED, "failed"],
|
|
14542
|
+
[STATES.INITIALIZING, null],
|
|
14543
|
+
[STATES.DORMANT, null],
|
|
14544
|
+
[STATES.DONE, null]
|
|
14545
|
+
]);
|
|
14467
14546
|
var EXIT_HOOKS = { [STATES.WAITING](session) {
|
|
14468
14547
|
session.emit("attention-cleared", { name: session.name });
|
|
14469
14548
|
} };
|
|
@@ -15882,7 +15961,8 @@ function createSessionHookLifecycle(options) {
|
|
|
15882
15961
|
const args = injection.buildHookArgs({
|
|
15883
15962
|
bypassHookTrust: isTrustBypassed,
|
|
15884
15963
|
hookTools: options.hookTools,
|
|
15885
|
-
userHooks: glimmervoidSuppliedHooks()
|
|
15964
|
+
userHooks: glimmervoidSuppliedHooks(),
|
|
15965
|
+
observeToolCalls: options.observeToolCalls
|
|
15886
15966
|
});
|
|
15887
15967
|
if (!args) {
|
|
15888
15968
|
console.warn(`[session:${options.name}] hook injection skipped: the relay path cannot be expressed for ${options.agentId} - falling back to OSC title only`);
|
|
@@ -18343,6 +18423,7 @@ var Session = class extends EventEmitter {
|
|
|
18343
18423
|
state;
|
|
18344
18424
|
stateSince;
|
|
18345
18425
|
failureReason;
|
|
18426
|
+
unseenOutcome;
|
|
18346
18427
|
ephemeral;
|
|
18347
18428
|
agentId;
|
|
18348
18429
|
usageVendor;
|
|
@@ -18450,6 +18531,7 @@ var Session = class extends EventEmitter {
|
|
|
18450
18531
|
this.state = STATES.DORMANT;
|
|
18451
18532
|
this.stateSince = Date.now();
|
|
18452
18533
|
this.failureReason = null;
|
|
18534
|
+
this.unseenOutcome = null;
|
|
18453
18535
|
this._receivedFirstOutput = false;
|
|
18454
18536
|
this._killPollTimer = null;
|
|
18455
18537
|
this._killReapTimer = null;
|
|
@@ -18690,13 +18772,8 @@ var Session = class extends EventEmitter {
|
|
|
18690
18772
|
get recentTaskPrompts() {
|
|
18691
18773
|
return [...this._recentTaskPrompts];
|
|
18692
18774
|
}
|
|
18693
|
-
setPendingTaskTitle(prompt) {
|
|
18694
|
-
this._taskTitleSources.pendingPromptTitle = extractPromptTaskTitle(prompt);
|
|
18695
|
-
this._updateTaskTitle();
|
|
18696
|
-
}
|
|
18697
18775
|
applyTaskTitleRefinement(refinement) {
|
|
18698
18776
|
if (refinement.action === "replace") this._taskTitleSources.refinedTitle = refinement.title;
|
|
18699
|
-
this._taskTitleSources.pendingPromptTitle = null;
|
|
18700
18777
|
this._updateTaskTitle();
|
|
18701
18778
|
}
|
|
18702
18779
|
_updateTaskTitle() {
|
|
@@ -18823,7 +18900,8 @@ var Session = class extends EventEmitter {
|
|
|
18823
18900
|
this.emit("user-prompt", {
|
|
18824
18901
|
state: this.state,
|
|
18825
18902
|
stateSince: this.stateSince,
|
|
18826
|
-
ts: Date.now()
|
|
18903
|
+
ts: Date.now(),
|
|
18904
|
+
isSelfWake: isSelfWakePrompt(raw.payload?.prompt)
|
|
18827
18905
|
});
|
|
18828
18906
|
}
|
|
18829
18907
|
if (raw && raw.signal === "ready" && raw.source === "hook") this.backgroundTracking.applyBackgroundTasks(raw.payload);
|
|
@@ -19147,6 +19225,9 @@ var Session = class extends EventEmitter {
|
|
|
19147
19225
|
get isWorkspace() {
|
|
19148
19226
|
return this.worktreeLifecycle.snapshot().isWorkspace;
|
|
19149
19227
|
}
|
|
19228
|
+
get remoteWorkspace() {
|
|
19229
|
+
return this._getRemoteLaunch()?.workspace ?? null;
|
|
19230
|
+
}
|
|
19150
19231
|
getWorktreeCarry() {
|
|
19151
19232
|
return this.worktreeLifecycle.getCarry();
|
|
19152
19233
|
}
|
|
@@ -19268,6 +19349,7 @@ var Session = class extends EventEmitter {
|
|
|
19268
19349
|
ownerId: this.ownerId,
|
|
19269
19350
|
isWorktree: this.isWorktree,
|
|
19270
19351
|
isWorkspace: this.isWorkspace,
|
|
19352
|
+
remoteWorkspace: this.remoteWorkspace,
|
|
19271
19353
|
isGuarded: this.isGuarded,
|
|
19272
19354
|
isGuardrailsReleased: this.isGuardrailsReleased,
|
|
19273
19355
|
resumeSessionId: this._resumeSessionId,
|
|
@@ -19285,6 +19367,7 @@ var Session = class extends EventEmitter {
|
|
|
19285
19367
|
mergeReason: this.mergeReason,
|
|
19286
19368
|
worktreeNotice: this.worktreeNotice,
|
|
19287
19369
|
failureReason: this.failureReason,
|
|
19370
|
+
unseenOutcome: this.unseenOutcome,
|
|
19288
19371
|
effectiveBase: this.worktreeLifecycle.snapshot().effectiveBase,
|
|
19289
19372
|
linkedPrs: this.linkedPrs,
|
|
19290
19373
|
coherenceHooks: this.coherenceHooks,
|
|
@@ -19329,6 +19412,8 @@ var Session = class extends EventEmitter {
|
|
|
19329
19412
|
this.state = to;
|
|
19330
19413
|
if (event === "spawn_fail") this.failureReason = describeSpawnFailure(detail);
|
|
19331
19414
|
if (to === STATES.INITIALIZING) this.failureReason = null;
|
|
19415
|
+
const unseenOutcomeOnEntry = UNSEEN_OUTCOME_ON_ENTRY.get(to);
|
|
19416
|
+
if (unseenOutcomeOnEntry !== void 0 && !isCompactionRestoreEvent(event)) this._setUnseenOutcome(unseenOutcomeOnEntry);
|
|
19332
19417
|
if (to === STATES.IDLE || to === STATES.COMPLETE) this._observeCompaction("settled");
|
|
19333
19418
|
const entryHook = ENTRY_HOOKS[to];
|
|
19334
19419
|
if (entryHook && !isCompactionRestoreEvent(event)) entryHook(this);
|
|
@@ -20176,9 +20261,16 @@ var Session = class extends EventEmitter {
|
|
|
20176
20261
|
this._armTimer("_killPollTimer", KILL_POLL_INTERVAL_MS, poll);
|
|
20177
20262
|
}
|
|
20178
20263
|
dismiss() {
|
|
20179
|
-
|
|
20264
|
+
const hadUnseenOutcome = this.unseenOutcome !== null;
|
|
20265
|
+
this._setUnseenOutcome(null);
|
|
20266
|
+
if (!DISMISSIBLE_STATES.has(this.state)) return hadUnseenOutcome;
|
|
20180
20267
|
return this.transition("user_dismiss");
|
|
20181
20268
|
}
|
|
20269
|
+
_setUnseenOutcome(nextUnseenOutcome) {
|
|
20270
|
+
if (this.unseenOutcome === nextUnseenOutcome) return;
|
|
20271
|
+
this.unseenOutcome = nextUnseenOutcome;
|
|
20272
|
+
this.emit("unseen-outcome-change", { unseenOutcome: nextUnseenOutcome });
|
|
20273
|
+
}
|
|
20182
20274
|
_teardownPending() {
|
|
20183
20275
|
return this._pendingRestart || this._finishing;
|
|
20184
20276
|
}
|
|
@@ -23737,25 +23829,30 @@ function createTeamReviewPoller(deps) {
|
|
|
23737
23829
|
}
|
|
23738
23830
|
return false;
|
|
23739
23831
|
}
|
|
23740
|
-
function
|
|
23741
|
-
const departedPosted = Object.entries(state).filter(([key, entry]) => !candidateKeys.has(key) && !entry.inFlight && entry.draft?.status === "posted");
|
|
23742
|
-
store.setWatchedKeys("team-review-departed", departedPosted.flatMap(([, entry]) => entry.draft ? [comparablePrKey(entry.draft)] : []));
|
|
23832
|
+
function closedEntryKeys() {
|
|
23743
23833
|
const prs = store.getSnapshot().prs;
|
|
23744
|
-
return new Set(
|
|
23745
|
-
const pr =
|
|
23746
|
-
return pr !== void 0 && pr.state !== "OPEN";
|
|
23747
|
-
})
|
|
23834
|
+
return new Set(Object.keys(state).filter((key) => {
|
|
23835
|
+
const pr = prs[key.toLowerCase()];
|
|
23836
|
+
return !state[key]?.inFlight && pr !== void 0 && pr.state !== "OPEN";
|
|
23837
|
+
}));
|
|
23838
|
+
}
|
|
23839
|
+
function watchDeparted(candidateKeys) {
|
|
23840
|
+
const departedKeys = Object.keys(state).filter((key) => !candidateKeys.has(key) && !state[key]?.inFlight);
|
|
23841
|
+
store.setWatchedKeys("team-review-departed", departedKeys.map((key) => key.toLowerCase()));
|
|
23748
23842
|
}
|
|
23749
|
-
async function pruneDeparted(candidateKeys) {
|
|
23843
|
+
async function pruneDeparted(candidateKeys, isComplete) {
|
|
23750
23844
|
let isDirty = false;
|
|
23751
|
-
|
|
23845
|
+
watchDeparted(candidateKeys);
|
|
23846
|
+
const closedKeys = closedEntryKeys();
|
|
23752
23847
|
for (const [key, entry] of Object.entries(state)) {
|
|
23753
|
-
|
|
23848
|
+
const isClosed = closedKeys.has(key);
|
|
23849
|
+
if (candidateKeys.has(key) || !isComplete && !isClosed) continue;
|
|
23850
|
+
if (entry.resumable) {
|
|
23754
23851
|
await discardResumable(entry.resumable);
|
|
23755
23852
|
entry.resumable = null;
|
|
23756
23853
|
isDirty = true;
|
|
23757
23854
|
}
|
|
23758
|
-
if (!shouldPruneEntry(entry,
|
|
23855
|
+
if (!shouldPruneEntry(entry, false, now(), isClosed)) continue;
|
|
23759
23856
|
delete state[key];
|
|
23760
23857
|
isDirty = true;
|
|
23761
23858
|
}
|
|
@@ -23996,6 +24093,10 @@ function createTeamReviewPoller(deps) {
|
|
|
23996
24093
|
const planned = headsToReview(candidates);
|
|
23997
24094
|
if (planned.isDirty) loop.track(persist());
|
|
23998
24095
|
emitStatus();
|
|
24096
|
+
if (closedEntryKeys().size > 0) {
|
|
24097
|
+
loop.tickSoon();
|
|
24098
|
+
return;
|
|
24099
|
+
}
|
|
23999
24100
|
if (planned.queue.length === 0 || !canStartReview()) return;
|
|
24000
24101
|
const queueSignature = queuedHeadsSignature(planned.queue);
|
|
24001
24102
|
if (queueSignature === lastRebuildQueueSignature) return;
|
|
@@ -24049,7 +24150,7 @@ function createTeamReviewPoller(deps) {
|
|
|
24049
24150
|
retryAfterMs: rateLimitWaitMs
|
|
24050
24151
|
};
|
|
24051
24152
|
}
|
|
24052
|
-
const isPruned =
|
|
24153
|
+
const isPruned = await pruneDeparted(new Set(candidates.map((candidate) => candidate.key)), isComplete);
|
|
24053
24154
|
if (isPruned) watchAskedFor();
|
|
24054
24155
|
const planned = headsToReview(candidates);
|
|
24055
24156
|
const isStarted = await startReviewsUnlessGuardrailsRefused(planned.queue);
|
|
@@ -29579,7 +29680,9 @@ var GLIMMERVOID_WORKSPACE_RE = /^gv-[0-9a-f]{12}$/;
|
|
|
29579
29680
|
var IDLE_STOP_STATES = /* @__PURE__ */ new Set([
|
|
29580
29681
|
STATES.IDLE,
|
|
29581
29682
|
STATES.COMPLETE,
|
|
29582
|
-
STATES.DORMANT
|
|
29683
|
+
STATES.DORMANT,
|
|
29684
|
+
STATES.FAILED,
|
|
29685
|
+
STATES.DONE
|
|
29583
29686
|
]);
|
|
29584
29687
|
var CODER_ENVIRONMENT_DEFAULTS = Object.freeze({
|
|
29585
29688
|
idleStopMinutes: 30,
|
|
@@ -29664,19 +29767,24 @@ function shouldIdleStop({ sessionState, idleSinceMs, nowMs, idleStopMinutes }) {
|
|
|
29664
29767
|
if (!IDLE_STOP_STATES.has(sessionState) || idleSinceMs === null) return false;
|
|
29665
29768
|
return nowMs - idleSinceMs >= idleStopMinutes * 6e4;
|
|
29666
29769
|
}
|
|
29667
|
-
function planReconcile({ workspaces, mappings, busyWorkspaces, nowMs, deleteAfterStoppedDays }) {
|
|
29770
|
+
function planReconcile({ workspaces, mappings, busyWorkspaces, ownedWorkspaces, nowMs, deleteAfterStoppedDays }) {
|
|
29668
29771
|
const mappedNames = new Set(mappings.map((mapping) => mapping.workspace));
|
|
29669
29772
|
const listedNames = new Set(workspaces.map((workspace) => workspace.name));
|
|
29670
29773
|
const presentMappings = mappings.filter((mapping) => !busyWorkspaces.has(mapping.workspace)).filter((mapping) => listedNames.has(mapping.workspace));
|
|
29671
|
-
const unmapped = workspaces.filter((workspace) => !mappedNames.has(workspace.name) && !busyWorkspaces.has(workspace.name) && workspace.status !== "deleting");
|
|
29774
|
+
const unmapped = workspaces.filter((workspace) => !mappedNames.has(workspace.name) && !busyWorkspaces.has(workspace.name) && workspace.status !== "deleting" && ownedWorkspaces.has(workspace.name));
|
|
29672
29775
|
const isStale = (workspace) => workspace.stoppedAtMs !== null && nowMs - workspace.stoppedAtMs >= deleteAfterStoppedDays * DAY_MS;
|
|
29673
29776
|
return {
|
|
29674
29777
|
resume: presentMappings.filter((mapping) => mapping.wasActive),
|
|
29675
29778
|
stop: presentMappings.filter((mapping) => !mapping.wasActive),
|
|
29676
29779
|
delete: unmapped.filter(isStale).map((workspace) => workspace.name),
|
|
29677
|
-
|
|
29780
|
+
stopUnmapped: unmapped.filter((workspace) => workspace.status === "running").map((workspace) => workspace.name)
|
|
29678
29781
|
};
|
|
29679
29782
|
}
|
|
29783
|
+
function retainedOwnedWorkspaces({ ownedWorkspaces, workspaces, mappings, busyWorkspaces }) {
|
|
29784
|
+
const listedNames = new Set(workspaces.map((workspace) => workspace.name));
|
|
29785
|
+
const mappedNames = new Set(mappings.map((mapping) => mapping.workspace));
|
|
29786
|
+
return new Set([...ownedWorkspaces].filter((workspace) => listedNames.has(workspace) || mappedNames.has(workspace) || busyWorkspaces.has(workspace)));
|
|
29787
|
+
}
|
|
29680
29788
|
function failedProvisionCleanup({ didCreate, didStart }) {
|
|
29681
29789
|
if (didCreate) return "delete";
|
|
29682
29790
|
if (didStart) return "stop";
|
|
@@ -29690,13 +29798,34 @@ function liveLimitRefusal({ liveCount, maxLive, environmentName }) {
|
|
|
29690
29798
|
if (liveCount < maxLive) return null;
|
|
29691
29799
|
return `${environmentName} already has ${liveCount} of ${maxLive} live workspaces; close one first`;
|
|
29692
29800
|
}
|
|
29801
|
+
var CODER_ERROR_DETAIL_LIMIT = 200;
|
|
29693
29802
|
function remoteCheckoutDirectory({ checkoutRoot, workspace }) {
|
|
29694
29803
|
return path.posix.join(checkoutRoot, workspace);
|
|
29695
29804
|
}
|
|
29805
|
+
function coderErrorDetail(stderr) {
|
|
29806
|
+
if (typeof stderr !== "string") return null;
|
|
29807
|
+
const lines = stderr.split("\n").map((line) => line.trim());
|
|
29808
|
+
const errorLineIndex = lines.findLastIndex((line) => line.startsWith("error:"));
|
|
29809
|
+
if (errorLineIndex === -1) return null;
|
|
29810
|
+
return lines.slice(errorLineIndex).filter(Boolean).join(" ").slice(6).trim().slice(0, CODER_ERROR_DETAIL_LIMIT) || null;
|
|
29811
|
+
}
|
|
29812
|
+
function isCoderSignedOutError(stderr) {
|
|
29813
|
+
return typeof stderr === "string" && stderr.includes("You are signed out or your session has expired");
|
|
29814
|
+
}
|
|
29815
|
+
function coderNotSignedInReason(failure) {
|
|
29816
|
+
return `The coder CLI on this host is not signed in. Run coder login, then try again. (${failure})`;
|
|
29817
|
+
}
|
|
29696
29818
|
//#endregion
|
|
29697
29819
|
//#region server/coder/coder-env-poller.ts
|
|
29698
|
-
function createCoderEnvPoller({ listWorkspaces, stopWorkspace, deleteWorkspace, sessionsForIdleCheck, mappings, busyWorkspaces, isReconcilePending, deleteAfterStoppedDays, onReconciled, now = Date.now, log = console, ...loopOptions }) {
|
|
29820
|
+
function createCoderEnvPoller({ listWorkspaces, stopWorkspace, deleteWorkspace, sessionsForIdleCheck, mappings, busyWorkspaces, ownedWorkspaces, isReconcilePending, deleteAfterStoppedDays, onReconciled, now = Date.now, log = console, ...loopOptions }) {
|
|
29699
29821
|
const stoppedIdlePeriods = /* @__PURE__ */ new Map();
|
|
29822
|
+
async function mutateUnmappedWorkspace(mutate, workspace) {
|
|
29823
|
+
try {
|
|
29824
|
+
await mutate(workspace);
|
|
29825
|
+
} catch (error) {
|
|
29826
|
+
log.warn(`[coder-env] ${errorMessage(error)}; skipping ${workspace}`);
|
|
29827
|
+
}
|
|
29828
|
+
}
|
|
29700
29829
|
async function runTick() {
|
|
29701
29830
|
try {
|
|
29702
29831
|
const workspaces = parseGlimmervoidWorkspaces(await listWorkspaces());
|
|
@@ -29704,20 +29833,24 @@ function createCoderEnvPoller({ listWorkspaces, stopWorkspace, deleteWorkspace,
|
|
|
29704
29833
|
if (loop.isStopped()) return;
|
|
29705
29834
|
const workspaceByName = new Map(workspaces.map((workspace) => [workspace.name, workspace]));
|
|
29706
29835
|
const stoppedThisTick = /* @__PURE__ */ new Set();
|
|
29836
|
+
const plan = planReconcile({
|
|
29837
|
+
workspaces,
|
|
29838
|
+
mappings: mappings(),
|
|
29839
|
+
busyWorkspaces: busyWorkspaces(),
|
|
29840
|
+
ownedWorkspaces: ownedWorkspaces(),
|
|
29841
|
+
nowMs: now(),
|
|
29842
|
+
deleteAfterStoppedDays: deleteAfterStoppedDays()
|
|
29843
|
+
});
|
|
29844
|
+
for (const workspace of plan.delete) {
|
|
29845
|
+
if (loop.isStopped()) return;
|
|
29846
|
+
await mutateUnmappedWorkspace(deleteWorkspace, workspace);
|
|
29847
|
+
}
|
|
29848
|
+
for (const workspace of plan.stopUnmapped) {
|
|
29849
|
+
if (loop.isStopped()) return;
|
|
29850
|
+
log.warn(`[coder-env] stopping unmapped workspace ${workspace}`);
|
|
29851
|
+
await mutateUnmappedWorkspace(stopWorkspace, workspace);
|
|
29852
|
+
}
|
|
29707
29853
|
if (isReconcilePending()) {
|
|
29708
|
-
const staleAfterDays = deleteAfterStoppedDays();
|
|
29709
|
-
const plan = planReconcile({
|
|
29710
|
-
workspaces,
|
|
29711
|
-
mappings: mappings(),
|
|
29712
|
-
busyWorkspaces: busyWorkspaces(),
|
|
29713
|
-
nowMs: now(),
|
|
29714
|
-
deleteAfterStoppedDays: staleAfterDays
|
|
29715
|
-
});
|
|
29716
|
-
if (plan.keptUnmapped.length > 0) log.warn(`[coder-env] keeping unmapped workspaces until stopped for ${staleAfterDays} days: ${plan.keptUnmapped.join(", ")}`);
|
|
29717
|
-
for (const workspace of plan.delete) {
|
|
29718
|
-
if (loop.isStopped()) return;
|
|
29719
|
-
await deleteWorkspace(workspace);
|
|
29720
|
-
}
|
|
29721
29854
|
for (const mapping of plan.stop) {
|
|
29722
29855
|
if (loop.isStopped()) return;
|
|
29723
29856
|
const status = workspaceByName.get(mapping.workspace)?.status;
|
|
@@ -29770,15 +29903,50 @@ function createCoderEnvPoller({ listWorkspaces, stopWorkspace, deleteWorkspace,
|
|
|
29770
29903
|
}
|
|
29771
29904
|
//#endregion
|
|
29772
29905
|
//#region server/coder/coder-env-wiring.ts
|
|
29773
|
-
|
|
29906
|
+
var CODER_UNAVAILABLE_REASON = "Coder environments are unavailable on this host";
|
|
29907
|
+
function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {}, loadOwnedWorkspaces, saveOwnedWorkspaces, runCoder: executeCoder, env = process.env, platform = process.platform, fetch: fetchReady = globalThis.fetch, createPoller = createCoderEnvPoller, now = Date.now, log = console, ...pollerDeps }) {
|
|
29774
29908
|
let binaryProbe = null;
|
|
29775
29909
|
let isCoderAvailable = false;
|
|
29910
|
+
let unavailableReason = CODER_UNAVAILABLE_REASON;
|
|
29911
|
+
const signedOutErrors = /* @__PURE__ */ new WeakSet();
|
|
29776
29912
|
let hasReconciled = false;
|
|
29777
29913
|
const operationCountByWorkspace = /* @__PURE__ */ new Map();
|
|
29778
29914
|
const lastSeenStatusByWorkspace = /* @__PURE__ */ new Map();
|
|
29779
29915
|
const provisionByProject = /* @__PURE__ */ new Map();
|
|
29780
29916
|
const failedProvisionByProject = /* @__PURE__ */ new Map();
|
|
29781
29917
|
const setTimeoutFn = pollerDeps.setTimeoutFn ?? DEFAULT_TIMER_FNS.setTimeoutFn;
|
|
29918
|
+
let ownedWorkspaces = /* @__PURE__ */ new Set([...loadOwnedWorkspaces(), ...currentMappings().map((mapping) => mapping.workspace)]);
|
|
29919
|
+
let ownedSaveChain = Promise.resolve();
|
|
29920
|
+
function currentMappings() {
|
|
29921
|
+
return config.projects.flatMap((project) => project.id && project.environment ? [{
|
|
29922
|
+
projectId: project.id,
|
|
29923
|
+
workspace: project.environment.workspace,
|
|
29924
|
+
wasActive: project.wasActive === true
|
|
29925
|
+
}] : []);
|
|
29926
|
+
}
|
|
29927
|
+
function busyWorkspaces() {
|
|
29928
|
+
return new Set(operationCountByWorkspace.keys());
|
|
29929
|
+
}
|
|
29930
|
+
function saveOwned() {
|
|
29931
|
+
const save = ownedSaveChain.then(() => saveOwnedWorkspaces([...ownedWorkspaces]));
|
|
29932
|
+
ownedSaveChain = save.catch(() => {});
|
|
29933
|
+
return save;
|
|
29934
|
+
}
|
|
29935
|
+
async function pruneOwned(workspaces) {
|
|
29936
|
+
const retained = retainedOwnedWorkspaces({
|
|
29937
|
+
ownedWorkspaces,
|
|
29938
|
+
workspaces,
|
|
29939
|
+
mappings: currentMappings(),
|
|
29940
|
+
busyWorkspaces: busyWorkspaces()
|
|
29941
|
+
});
|
|
29942
|
+
if (retained.size === ownedWorkspaces.size) return;
|
|
29943
|
+
ownedWorkspaces = retained;
|
|
29944
|
+
try {
|
|
29945
|
+
await saveOwned();
|
|
29946
|
+
} catch (error) {
|
|
29947
|
+
log.warn(`[coder-env] could not save owned Coder workspaces: ${errorMessage(error)}`);
|
|
29948
|
+
}
|
|
29949
|
+
}
|
|
29782
29950
|
async function runCoder(args, { timeoutMs, input = "", env: commandEnv }) {
|
|
29783
29951
|
try {
|
|
29784
29952
|
const options = {
|
|
@@ -29797,10 +29965,22 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29797
29965
|
encoding: "utf8",
|
|
29798
29966
|
maxBuffer: 16777216
|
|
29799
29967
|
});
|
|
29800
|
-
} catch {
|
|
29801
|
-
|
|
29968
|
+
} catch (error) {
|
|
29969
|
+
const stderr = error instanceof Error && "stderr" in error ? error.stderr : null;
|
|
29970
|
+
const detail = coderErrorDetail(stderr);
|
|
29971
|
+
const failure = detail ? `Coder ${args[0]} failed: ${detail}` : `Coder ${args[0]} failed`;
|
|
29972
|
+
if (!isCoderSignedOutError(stderr)) throw new Error(failure);
|
|
29973
|
+
throw markSignedOut(failure);
|
|
29802
29974
|
}
|
|
29803
29975
|
}
|
|
29976
|
+
function markSignedOut(failure) {
|
|
29977
|
+
binaryProbe = null;
|
|
29978
|
+
isCoderAvailable = false;
|
|
29979
|
+
unavailableReason = coderNotSignedInReason(failure);
|
|
29980
|
+
const signedOutError = new Error(unavailableReason);
|
|
29981
|
+
signedOutErrors.add(signedOutError);
|
|
29982
|
+
return signedOutError;
|
|
29983
|
+
}
|
|
29804
29984
|
function command(args, timeoutMs = 3e4) {
|
|
29805
29985
|
return runCoder(args, { timeoutMs });
|
|
29806
29986
|
}
|
|
@@ -29817,7 +29997,8 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29817
29997
|
"-o",
|
|
29818
29998
|
"json"
|
|
29819
29999
|
])).stdout;
|
|
29820
|
-
recordListing(listingJson);
|
|
30000
|
+
const workspaces = recordListing(listingJson);
|
|
30001
|
+
if (workspaces !== null) await pruneOwned(workspaces);
|
|
29821
30002
|
return listingJson;
|
|
29822
30003
|
}
|
|
29823
30004
|
async function listWorkspaces() {
|
|
@@ -29838,12 +30019,20 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29838
30019
|
async function probeBinary() {
|
|
29839
30020
|
try {
|
|
29840
30021
|
await command(["version"], 5e3);
|
|
29841
|
-
isCoderAvailable = true;
|
|
29842
|
-
return true;
|
|
29843
30022
|
} catch {
|
|
30023
|
+
unavailableReason = CODER_UNAVAILABLE_REASON;
|
|
29844
30024
|
log.warn("[coder-env] coder binary is unavailable");
|
|
29845
30025
|
return false;
|
|
29846
30026
|
}
|
|
30027
|
+
try {
|
|
30028
|
+
await command(["whoami"], 15e3);
|
|
30029
|
+
} catch (error) {
|
|
30030
|
+
if (!(error instanceof Error && signedOutErrors.has(error))) unavailableReason = `${CODER_UNAVAILABLE_REASON}: ${errorMessage(error)}`;
|
|
30031
|
+
log.warn(`[coder-env] ${errorMessage(error)}`);
|
|
30032
|
+
return false;
|
|
30033
|
+
}
|
|
30034
|
+
isCoderAvailable = true;
|
|
30035
|
+
return true;
|
|
29847
30036
|
}
|
|
29848
30037
|
async function ensureAvailable() {
|
|
29849
30038
|
if (!isConfigured()) return false;
|
|
@@ -29887,12 +30076,9 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29887
30076
|
"--yes"
|
|
29888
30077
|
]);
|
|
29889
30078
|
},
|
|
29890
|
-
mappings:
|
|
29891
|
-
|
|
29892
|
-
|
|
29893
|
-
wasActive: project.wasActive === true
|
|
29894
|
-
}] : []),
|
|
29895
|
-
busyWorkspaces: () => new Set(operationCountByWorkspace.keys()),
|
|
30079
|
+
mappings: currentMappings,
|
|
30080
|
+
busyWorkspaces,
|
|
30081
|
+
ownedWorkspaces: () => ownedWorkspaces,
|
|
29896
30082
|
isReconcilePending: () => !hasReconciled,
|
|
29897
30083
|
deleteAfterStoppedDays: () => Math.max(...(config.coder?.environments ?? []).map((environment) => resolveCoderEnvironment(environment).deleteAfterStoppedDays)),
|
|
29898
30084
|
onReconciled: async (plan) => {
|
|
@@ -29938,8 +30124,16 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29938
30124
|
}
|
|
29939
30125
|
async function waitForReady(workspaceName, environment) {
|
|
29940
30126
|
const startedAtMs = now();
|
|
30127
|
+
let consecutiveListFailures = 0;
|
|
29941
30128
|
for (;;) {
|
|
29942
|
-
const workspace = (await listWorkspaces()
|
|
30129
|
+
const workspace = (await listWorkspaces().then((listed) => {
|
|
30130
|
+
consecutiveListFailures = 0;
|
|
30131
|
+
return listed;
|
|
30132
|
+
}, (listError) => {
|
|
30133
|
+
consecutiveListFailures += 1;
|
|
30134
|
+
if (consecutiveListFailures >= 3) throw listError;
|
|
30135
|
+
return [];
|
|
30136
|
+
})).find((candidate) => candidate.name === workspaceName);
|
|
29943
30137
|
let isReadyUrlUp = null;
|
|
29944
30138
|
if (environment.readyUrl) {
|
|
29945
30139
|
isReadyUrlUp = false;
|
|
@@ -29967,6 +30161,9 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29967
30161
|
])).stdout);
|
|
29968
30162
|
if (reason) throw new Error(reason);
|
|
29969
30163
|
}
|
|
30164
|
+
async function isWorkspaceListed(workspace) {
|
|
30165
|
+
return (await listWorkspaces().catch(() => null))?.some((candidate) => candidate.name === workspace) ?? true;
|
|
30166
|
+
}
|
|
29970
30167
|
async function failAttempt({ workspace, didCreate, didStart, reason }) {
|
|
29971
30168
|
const cleanup = failedProvisionCleanup({
|
|
29972
30169
|
didCreate,
|
|
@@ -29978,9 +30175,9 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29978
30175
|
cleanup,
|
|
29979
30176
|
workspace,
|
|
29980
30177
|
"--yes"
|
|
29981
|
-
]);
|
|
30178
|
+
], 12e5);
|
|
29982
30179
|
} catch {
|
|
29983
|
-
reasons.push(`Coder cleanup ${cleanup} failed for ${workspace}`);
|
|
30180
|
+
if (await isWorkspaceListed(workspace)) reasons.push(`Coder cleanup ${cleanup} failed for ${workspace}`);
|
|
29984
30181
|
}
|
|
29985
30182
|
return {
|
|
29986
30183
|
ok: false,
|
|
@@ -30012,7 +30209,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30012
30209
|
};
|
|
30013
30210
|
if (!await ensureAvailable()) return {
|
|
30014
30211
|
ok: false,
|
|
30015
|
-
reason:
|
|
30212
|
+
reason: unavailableReason
|
|
30016
30213
|
};
|
|
30017
30214
|
const workspace = workspaceNameFromHex(randomBytes(6).toString("hex"));
|
|
30018
30215
|
if (workspace === null) return {
|
|
@@ -30057,7 +30254,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30057
30254
|
try {
|
|
30058
30255
|
const environment = resolveEnvironment(mapping.name);
|
|
30059
30256
|
if (!environment) throw new Error(`Unknown Coder environment: ${mapping.name}`);
|
|
30060
|
-
if (!await ensureAvailable()) throw new Error(
|
|
30257
|
+
if (!await ensureAvailable()) throw new Error(unavailableReason);
|
|
30061
30258
|
const createArgs = coderCreateArgs({
|
|
30062
30259
|
workspace,
|
|
30063
30260
|
template: environment.template,
|
|
@@ -30069,8 +30266,14 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30069
30266
|
checkoutRoot: environment.checkoutRoot,
|
|
30070
30267
|
workspace
|
|
30071
30268
|
});
|
|
30269
|
+
ownedWorkspaces.add(workspace);
|
|
30270
|
+
await saveOwned().catch((saveError) => {
|
|
30271
|
+
throw new Error(`Could not record Coder workspace ${workspace} as owned by this host: ${errorMessage(saveError)}`);
|
|
30272
|
+
});
|
|
30072
30273
|
didCreate = true;
|
|
30073
|
-
await command(createArgs, 12e5)
|
|
30274
|
+
await command(createArgs, 12e5).catch(async (createError) => {
|
|
30275
|
+
if (!(await listWorkspaces().catch(() => [])).some((candidate) => candidate.name === workspace)) throw createError;
|
|
30276
|
+
});
|
|
30074
30277
|
await waitForReady(workspace, environment);
|
|
30075
30278
|
await checkSharing(workspace);
|
|
30076
30279
|
const nodeVersion = await command([
|
|
@@ -30144,7 +30347,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30144
30347
|
};
|
|
30145
30348
|
if (!await ensureAvailable()) return {
|
|
30146
30349
|
ok: false,
|
|
30147
|
-
reason:
|
|
30350
|
+
reason: unavailableReason
|
|
30148
30351
|
};
|
|
30149
30352
|
return whileWorkspaceBusy(mapping.workspace, () => startWorkspace({
|
|
30150
30353
|
mapping,
|
|
@@ -30185,7 +30388,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30185
30388
|
if (!mapping) return { ok: true };
|
|
30186
30389
|
if (!await ensureAvailable()) return {
|
|
30187
30390
|
ok: false,
|
|
30188
|
-
reason:
|
|
30391
|
+
reason: unavailableReason
|
|
30189
30392
|
};
|
|
30190
30393
|
return whileWorkspaceBusy(mapping.workspace, async () => {
|
|
30191
30394
|
try {
|
|
@@ -30209,7 +30412,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30209
30412
|
const fileMode = typeof mode === "number" ? mode.toString(8) : mode;
|
|
30210
30413
|
if (!/^[0-7]{3,4}$/.test(fileMode)) throw new Error("Invalid remote file mode");
|
|
30211
30414
|
if (!path.posix.isAbsolute(remotePath)) throw new Error("Remote upload path must be absolute");
|
|
30212
|
-
if (!await ensureAvailable()) throw new Error(
|
|
30415
|
+
if (!await ensureAvailable()) throw new Error(unavailableReason);
|
|
30213
30416
|
await runCoder([
|
|
30214
30417
|
"ssh",
|
|
30215
30418
|
workspace,
|
|
@@ -30853,6 +31056,12 @@ function createBranchGcWiring({ config, gitWorkspace, liveSessionIds, liveWorktr
|
|
|
30853
31056
|
};
|
|
30854
31057
|
}
|
|
30855
31058
|
//#endregion
|
|
31059
|
+
//#region shared/contracts/coder-owned-workspaces.ts
|
|
31060
|
+
var CoderOwnedWorkspaces = z.object({
|
|
31061
|
+
version: z.literal(1),
|
|
31062
|
+
workspaces: z.array(z.string().regex(/^gv-[0-9a-f]{12}$/))
|
|
31063
|
+
});
|
|
31064
|
+
//#endregion
|
|
30856
31065
|
//#region shared/display-text.ts
|
|
30857
31066
|
var SECONDS_PER_MINUTE = 60;
|
|
30858
31067
|
var HOURS_PER_DAY = 24;
|
|
@@ -34767,9 +34976,6 @@ function createTaskTitleRefiner({ getConfig, spawnLane, renameSession, nowFn = (
|
|
|
34767
34976
|
function canRefine(session) {
|
|
34768
34977
|
return !isStopped && !session._destroyed && !session.ephemeral && !session.customTitle && settings().enabled;
|
|
34769
34978
|
}
|
|
34770
|
-
function clearPending(session) {
|
|
34771
|
-
session.setPendingTaskTitle(null);
|
|
34772
|
-
}
|
|
34773
34979
|
function clearRecheck(session) {
|
|
34774
34980
|
const state = statesBySession.get(session);
|
|
34775
34981
|
if (!state?.recheckTimer) return;
|
|
@@ -34804,11 +35010,8 @@ function createTaskTitleRefiner({ getConfig, spawnLane, renameSession, nowFn = (
|
|
|
34804
35010
|
for (const [session, request] of queuedBySession) {
|
|
34805
35011
|
queuedBySession.delete(session);
|
|
34806
35012
|
if (session.taskTitlePromptRevision !== request.revision) continue;
|
|
34807
|
-
if (!canRefine(session))
|
|
34808
|
-
|
|
34809
|
-
continue;
|
|
34810
|
-
}
|
|
34811
|
-
if (session.state !== STATES.IDLE && session.state !== STATES.COMPLETE) {
|
|
35013
|
+
if (!canRefine(session)) continue;
|
|
35014
|
+
if (request.currentTitle !== null && session.state !== STATES.IDLE && session.state !== STATES.COMPLETE) {
|
|
34812
35015
|
scheduleRecheck(session);
|
|
34813
35016
|
continue;
|
|
34814
35017
|
}
|
|
@@ -34863,7 +35066,6 @@ function createTaskTitleRefiner({ getConfig, spawnLane, renameSession, nowFn = (
|
|
|
34863
35066
|
clearRecheck(session);
|
|
34864
35067
|
if (!canRefine(session)) {
|
|
34865
35068
|
queuedBySession.delete(session);
|
|
34866
|
-
clearPending(session);
|
|
34867
35069
|
return;
|
|
34868
35070
|
}
|
|
34869
35071
|
const decision = decisionFor(session);
|
|
@@ -34891,13 +35093,9 @@ function createTaskTitleRefiner({ getConfig, spawnLane, renameSession, nowFn = (
|
|
|
34891
35093
|
detach
|
|
34892
35094
|
};
|
|
34893
35095
|
statesBySession.set(session, state);
|
|
34894
|
-
function onPrompt(
|
|
35096
|
+
function onPrompt() {
|
|
34895
35097
|
state.substantivePromptsSinceRefinement++;
|
|
34896
|
-
if (
|
|
34897
|
-
clearPending(session);
|
|
34898
|
-
return;
|
|
34899
|
-
}
|
|
34900
|
-
session.setPendingTaskTitle(prompt);
|
|
35098
|
+
if (session.refinedTaskTitle === null) onTurnEnd(session);
|
|
34901
35099
|
}
|
|
34902
35100
|
function onReset() {
|
|
34903
35101
|
clearRecheck(session);
|
|
@@ -34911,7 +35109,6 @@ function createTaskTitleRefiner({ getConfig, spawnLane, renameSession, nowFn = (
|
|
|
34911
35109
|
clearRecheck(session);
|
|
34912
35110
|
queuedBySession.delete(session);
|
|
34913
35111
|
if (active?.request.session === session) active.controller.abort();
|
|
34914
|
-
clearPending(session);
|
|
34915
35112
|
}
|
|
34916
35113
|
function detach() {
|
|
34917
35114
|
session.off("task-prompt", onPrompt);
|
|
@@ -34930,10 +35127,7 @@ function createTaskTitleRefiner({ getConfig, spawnLane, renameSession, nowFn = (
|
|
|
34930
35127
|
}
|
|
34931
35128
|
async function stop() {
|
|
34932
35129
|
isStopped = true;
|
|
34933
|
-
for (const
|
|
34934
|
-
state.detach();
|
|
34935
|
-
clearPending(session);
|
|
34936
|
-
}
|
|
35130
|
+
for (const state of statesBySession.values()) state.detach();
|
|
34937
35131
|
queuedBySession.clear();
|
|
34938
35132
|
active?.controller.abort();
|
|
34939
35133
|
await active?.settled;
|
|
@@ -41312,17 +41506,19 @@ function resetsAtMs(value) {
|
|
|
41312
41506
|
if (numeric > SECONDS_CEILING) return Math.round(numeric);
|
|
41313
41507
|
return Math.round(numeric * 1e3);
|
|
41314
41508
|
}
|
|
41315
|
-
function
|
|
41316
|
-
|
|
41317
|
-
const
|
|
41318
|
-
|
|
41319
|
-
const resetsAt = resetsAtMs(window.resets_at);
|
|
41320
|
-
if (pct === null && resetsAt === null) return null;
|
|
41509
|
+
function rateLimitWindow(usedPct, resetsAt) {
|
|
41510
|
+
const pct = roundPct(usedPct);
|
|
41511
|
+
const resetsAtMsValue = resetsAtMs(resetsAt);
|
|
41512
|
+
if (pct === null && resetsAtMsValue === null) return null;
|
|
41321
41513
|
return {
|
|
41322
41514
|
pct,
|
|
41323
|
-
resetsAtMs:
|
|
41515
|
+
resetsAtMs: resetsAtMsValue
|
|
41324
41516
|
};
|
|
41325
41517
|
}
|
|
41518
|
+
function normalizeWindow(raw) {
|
|
41519
|
+
if (!isRecord(raw)) return null;
|
|
41520
|
+
return rateLimitWindow(raw.used_percentage, raw.resets_at);
|
|
41521
|
+
}
|
|
41326
41522
|
function normalizeRateLimits(raw) {
|
|
41327
41523
|
if (!isRecord(raw)) return null;
|
|
41328
41524
|
const limits = raw;
|
|
@@ -41362,14 +41558,19 @@ function shouldBroadcastPlanLimits(previous, next) {
|
|
|
41362
41558
|
if (nextSignature === null) return false;
|
|
41363
41559
|
return nextSignature !== planLimitsSignature(previous);
|
|
41364
41560
|
}
|
|
41365
|
-
|
|
41561
|
+
var PLAN_LIMITS_SOURCE_BY_VENDOR = Object.freeze({
|
|
41562
|
+
claude: "statusline",
|
|
41563
|
+
codex: "rollout"
|
|
41564
|
+
});
|
|
41565
|
+
function buildPlanLimitsMessage(snapshot, vendor = "claude") {
|
|
41366
41566
|
if (!snapshot?.rateLimits) return null;
|
|
41367
41567
|
return {
|
|
41368
41568
|
type: "plan-limits",
|
|
41369
41569
|
ts: snapshot.ts,
|
|
41370
41570
|
fiveHour: snapshot.rateLimits.fiveHour,
|
|
41371
41571
|
sevenDay: snapshot.rateLimits.sevenDay,
|
|
41372
|
-
source:
|
|
41572
|
+
source: PLAN_LIMITS_SOURCE_BY_VENDOR[vendor],
|
|
41573
|
+
vendor
|
|
41373
41574
|
};
|
|
41374
41575
|
}
|
|
41375
41576
|
function selectOfficialSessionCost(costMode, recordedCostUSD) {
|
|
@@ -41728,6 +41929,39 @@ function parseCodexUsageLine(line, state = createCodexUsageState(), { sessionId
|
|
|
41728
41929
|
vendor: "codex"
|
|
41729
41930
|
});
|
|
41730
41931
|
}
|
|
41932
|
+
var RATE_LIMITS_MARKER = "\"rate_limits\"";
|
|
41933
|
+
var CODEX_PLAN_LIMIT_ID = "codex";
|
|
41934
|
+
var FIVE_HOUR_WINDOW_MINUTES = 300;
|
|
41935
|
+
var SEVEN_DAY_WINDOW_MINUTES = 10080;
|
|
41936
|
+
function codexPlanWindows(rawWindows) {
|
|
41937
|
+
const windowsByMinutes = /* @__PURE__ */ new Map();
|
|
41938
|
+
for (const rawWindow of rawWindows) {
|
|
41939
|
+
if (!isRecord(rawWindow)) continue;
|
|
41940
|
+
windowsByMinutes.set(numberOr(rawWindow.window_minutes, 0), rateLimitWindow(rawWindow.used_percent, rawWindow.resets_at));
|
|
41941
|
+
}
|
|
41942
|
+
return {
|
|
41943
|
+
fiveHour: windowsByMinutes.get(FIVE_HOUR_WINDOW_MINUTES) ?? null,
|
|
41944
|
+
sevenDay: windowsByMinutes.get(SEVEN_DAY_WINDOW_MINUTES) ?? null
|
|
41945
|
+
};
|
|
41946
|
+
}
|
|
41947
|
+
function parseCodexRateLimitsLine(line) {
|
|
41948
|
+
if (typeof line !== "string" || !line.includes(RATE_LIMITS_MARKER)) return null;
|
|
41949
|
+
const raw = parseJsonLine(line);
|
|
41950
|
+
if (!raw) return null;
|
|
41951
|
+
const parsed = raw;
|
|
41952
|
+
if (parsed.payload?.type !== "token_count") return null;
|
|
41953
|
+
const rateLimits = parsed.payload.rate_limits;
|
|
41954
|
+
if (!isRecord(rateLimits)) return null;
|
|
41955
|
+
if (rateLimits.limit_id != null && rateLimits.limit_id !== CODEX_PLAN_LIMIT_ID) return null;
|
|
41956
|
+
const ts = Date.parse(String(parsed.timestamp));
|
|
41957
|
+
if (!Number.isFinite(ts)) return null;
|
|
41958
|
+
const windows = codexPlanWindows([rateLimits.primary, rateLimits.secondary]);
|
|
41959
|
+
if (!windows.fiveHour && !windows.sevenDay) return null;
|
|
41960
|
+
return {
|
|
41961
|
+
rateLimits: windows,
|
|
41962
|
+
ts
|
|
41963
|
+
};
|
|
41964
|
+
}
|
|
41731
41965
|
function codexDedupIdentity(entry) {
|
|
41732
41966
|
if (!entry) return null;
|
|
41733
41967
|
return [
|
|
@@ -42193,6 +42427,7 @@ function createUsageScanner(deps = {}) {
|
|
|
42193
42427
|
let lastPartial = false;
|
|
42194
42428
|
let lastOutcome = null;
|
|
42195
42429
|
let lastIoFailures = 0;
|
|
42430
|
+
let latestCodexRateLimitSnapshot = null;
|
|
42196
42431
|
let activePass = null;
|
|
42197
42432
|
let pendingForce = false;
|
|
42198
42433
|
let pendingScopeDirs = null;
|
|
@@ -42333,6 +42568,14 @@ function createUsageScanner(deps = {}) {
|
|
|
42333
42568
|
markDirty();
|
|
42334
42569
|
return false;
|
|
42335
42570
|
}
|
|
42571
|
+
function noteCodexRateLimits(snapshot) {
|
|
42572
|
+
if (!snapshot) return;
|
|
42573
|
+
if (latestCodexRateLimitSnapshot && snapshot.ts < latestCodexRateLimitSnapshot.ts) return;
|
|
42574
|
+
latestCodexRateLimitSnapshot = snapshot;
|
|
42575
|
+
}
|
|
42576
|
+
function latestCodexRateLimits() {
|
|
42577
|
+
return latestCodexRateLimitSnapshot;
|
|
42578
|
+
}
|
|
42336
42579
|
function ingestVendorLine(parsed, file) {
|
|
42337
42580
|
if (!parsed) return 0;
|
|
42338
42581
|
return storeEntry(stripIngestFields(priceEntry({
|
|
@@ -42342,6 +42585,7 @@ function createUsageScanner(deps = {}) {
|
|
|
42342
42585
|
}
|
|
42343
42586
|
function ingestLine({ line, file, vendor, vendorState, dirs: claudeRoots, lineOrdinal }) {
|
|
42344
42587
|
if (vendor === "codex") {
|
|
42588
|
+
noteCodexRateLimits(parseCodexRateLimitsLine(line));
|
|
42345
42589
|
const parsedCodexEntry = parseCodexUsageLine(line, vendorState ?? createCodexUsageState());
|
|
42346
42590
|
if (!parsedCodexEntry) return 0;
|
|
42347
42591
|
return ingestVendorLine({
|
|
@@ -42903,6 +43147,7 @@ function createUsageScanner(deps = {}) {
|
|
|
42903
43147
|
sessionTotals,
|
|
42904
43148
|
stats,
|
|
42905
43149
|
budgetSpend,
|
|
43150
|
+
latestCodexRateLimits,
|
|
42906
43151
|
_entriesForTest: () => entries.map((entry) => entry)
|
|
42907
43152
|
};
|
|
42908
43153
|
Object.defineProperty(api, "_entriesForTest", { enumerable: false });
|
|
@@ -43019,6 +43264,7 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
|
|
|
43019
43264
|
let ioFailureStreak = 0;
|
|
43020
43265
|
let ioFailureStreakId = 0;
|
|
43021
43266
|
let planLimits = null;
|
|
43267
|
+
let codexPlanLimits = null;
|
|
43022
43268
|
const officialCostByClaudeId = /* @__PURE__ */ new Map();
|
|
43023
43269
|
let budgetFiredState = {
|
|
43024
43270
|
daily: {},
|
|
@@ -43119,13 +43365,16 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
|
|
|
43119
43365
|
passInFlight = false;
|
|
43120
43366
|
}
|
|
43121
43367
|
if (stopped) return result;
|
|
43122
|
-
if (result)
|
|
43123
|
-
|
|
43124
|
-
|
|
43125
|
-
|
|
43126
|
-
|
|
43127
|
-
|
|
43128
|
-
|
|
43368
|
+
if (result) {
|
|
43369
|
+
ingestCodexRateLimits(scanner.latestCodexRateLimits());
|
|
43370
|
+
laneLog.debugNote(() => "pass complete", () => ({
|
|
43371
|
+
files: result.files,
|
|
43372
|
+
entries: result.entries,
|
|
43373
|
+
newEntries: result.newEntries,
|
|
43374
|
+
durationMs: result.durationMs,
|
|
43375
|
+
outcome: result.outcome
|
|
43376
|
+
}));
|
|
43377
|
+
}
|
|
43129
43378
|
if (result?.outcome === "io-failed") {
|
|
43130
43379
|
ioFailureStreak += 1;
|
|
43131
43380
|
laneLog.warnOnce(`scan-io:${ioFailureStreakId}`, "scan pass lost files to io errors", {
|
|
@@ -43267,8 +43516,18 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
|
|
|
43267
43516
|
const message = buildPlanLimitsMessage(snapshot);
|
|
43268
43517
|
if (message) broadcast(message);
|
|
43269
43518
|
}
|
|
43270
|
-
function
|
|
43519
|
+
function ingestCodexRateLimits(snapshot) {
|
|
43520
|
+
if (stopped || !cfg.enabled || !cfg.planLimits || !snapshot) return;
|
|
43521
|
+
if (codexPlanLimits && snapshot.ts < codexPlanLimits.ts) return;
|
|
43522
|
+
const changed = shouldBroadcastPlanLimits(codexPlanLimits, snapshot);
|
|
43523
|
+
codexPlanLimits = snapshot;
|
|
43524
|
+
if (!changed) return;
|
|
43525
|
+
const message = buildPlanLimitsMessage(snapshot, "codex");
|
|
43526
|
+
if (message) broadcast(message);
|
|
43527
|
+
}
|
|
43528
|
+
function getPlanLimitsMessage(vendor = "claude") {
|
|
43271
43529
|
if (!cfg.enabled || !cfg.planLimits) return null;
|
|
43530
|
+
if (vendor === "codex") return buildPlanLimitsMessage(codexPlanLimits, "codex");
|
|
43272
43531
|
return buildPlanLimitsMessage(planLimits);
|
|
43273
43532
|
}
|
|
43274
43533
|
function loadBudgetState() {
|
|
@@ -46041,8 +46300,29 @@ function createBackendLanes(dependencies) {
|
|
|
46041
46300
|
log: logger
|
|
46042
46301
|
});
|
|
46043
46302
|
const laneTelemetry = telemetry;
|
|
46303
|
+
const coderOwnedWorkspacesPath = path.join(glimmervoidHomeDir$1(), "coder-owned-workspaces.json");
|
|
46044
46304
|
const coderEnv = createCoderEnvWiring({
|
|
46045
46305
|
config,
|
|
46306
|
+
loadOwnedWorkspaces: () => {
|
|
46307
|
+
const outcome = loadJsonStateFileSync({
|
|
46308
|
+
filePath: coderOwnedWorkspacesPath,
|
|
46309
|
+
fsSync: fs,
|
|
46310
|
+
parse: (raw) => CoderOwnedWorkspaces.safeParse(raw).data?.workspaces ?? null,
|
|
46311
|
+
quarantine: false,
|
|
46312
|
+
includeNotDir: false
|
|
46313
|
+
});
|
|
46314
|
+
if (outcome.status === "loaded") return outcome.value;
|
|
46315
|
+
if (outcome.status === "missing") return [];
|
|
46316
|
+
logger.warn(`[coder-env] ignoring ${coderOwnedWorkspacesPath}: ${errorMessage(jsonStateLoadError(outcome))}`);
|
|
46317
|
+
return [];
|
|
46318
|
+
},
|
|
46319
|
+
saveOwnedWorkspaces: (workspaces) => writeJsonAtomic(coderOwnedWorkspacesPath, {
|
|
46320
|
+
version: 1,
|
|
46321
|
+
workspaces
|
|
46322
|
+
}, {
|
|
46323
|
+
mode: 384,
|
|
46324
|
+
mkdir: true
|
|
46325
|
+
}),
|
|
46046
46326
|
persistMapping: (projectId, mapping) => persistSessionField(configStore, config, projectId, "environment", mapping),
|
|
46047
46327
|
sessionsForIdleCheck: () => {
|
|
46048
46328
|
const environmentsByName = new Map((config.coder?.environments ?? []).map((environment) => [environment.name, resolveCoderEnvironment(environment)]));
|
|
@@ -52449,8 +52729,8 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
52449
52729
|
if (usageSessions) ws.send(JSON.stringify(usageSessions));
|
|
52450
52730
|
const usageReport = typeof getUsageReport === "function" ? getUsageReport() : null;
|
|
52451
52731
|
if (usageReport) ws.send(JSON.stringify(usageReport));
|
|
52452
|
-
const
|
|
52453
|
-
if (planLimits) ws.send(JSON.stringify(planLimits));
|
|
52732
|
+
const planLimitsMessages = typeof getPlanLimits === "function" ? getPlanLimits() : [];
|
|
52733
|
+
for (const planLimits of planLimitsMessages) if (planLimits) ws.send(JSON.stringify(planLimits));
|
|
52454
52734
|
const since = parseSinceParam(req?.url);
|
|
52455
52735
|
if (controlReplayLog && since !== null) {
|
|
52456
52736
|
const { entries, evicted } = controlReplayLog.entriesSince(since);
|
|
@@ -52532,7 +52812,7 @@ function createBackendControl(dependencies) {
|
|
|
52532
52812
|
getUsageSessions: () => usage.getSessionsMessage(),
|
|
52533
52813
|
getUsageReport: () => usage.getCachedReport(),
|
|
52534
52814
|
requestUsageReport: (args) => usage.requestReport(args),
|
|
52535
|
-
getPlanLimits: () => usage.getPlanLimitsMessage(),
|
|
52815
|
+
getPlanLimits: () => [usage.getPlanLimitsMessage("claude"), usage.getPlanLimitsMessage("codex")],
|
|
52536
52816
|
readTracePage: dependencies.readTracePage,
|
|
52537
52817
|
readPlanRevision: dependencies.readPlanRevision,
|
|
52538
52818
|
decidePlanReview: dependencies.decidePlanReview,
|