glimmervoid 0.42.0 → 0.42.2
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/README.md +2 -2
- package/dist/bin/glimmervoid.js +8 -8
- package/dist/chunks/{adapters-D2u85MEH.js → adapters-Bga1N6Ie.js} +2 -2
- package/dist/chunks/{adapters-C8GfnK2r.js → adapters-DeqV4wUv.js} +1 -1
- package/dist/chunks/{agent-api-cli-B-1MqjKc.js → agent-api-cli-DWVeqlLu.js} +1 -1
- package/dist/chunks/{agent-api-cli-D7JPxNso.js → agent-api-cli-YvQ8WxHA.js} +1 -1
- package/dist/chunks/{agent-api-core-PwO7fPoc.js → agent-api-core-DFYLP1bF.js} +1 -1
- package/dist/chunks/config-store-CGrS8s6x.js +1 -0
- package/dist/chunks/{config-store-BxCW2IwK.js → config-store-DOGtTJ1d.js} +2 -69
- package/dist/chunks/{contracts-LXlDbl4R.js → contracts-Ds_eevZs.js} +2 -12
- package/dist/chunks/{control-messages-88sdxt-U.js → control-messages-Dgap3JRR.js} +14 -45
- package/dist/chunks/{main-DZzDU4Q4.js → main-Ywst1s6Z.js} +326 -483
- package/dist/chunks/{pair-cli-3KKNhZ22.js → pair-cli-PCXITdEd.js} +2 -2
- package/dist/chunks/{remote-config-Ce5nN0zL.js → remote-config-DeWURqqr.js} +1 -1
- package/dist/chunks/{settings-cli-BYY991YC.js → settings-cli-xpR7vk9L.js} +4 -4
- package/dist/chunks/{settings-readout-core-CWAhwZKN.js → settings-readout-core-C2mOk9Yz.js} +1 -1
- package/dist/chunks/{ssh-sign-cli-BbqYIPwh.js → ssh-sign-cli-rKiqiNem.js} +1 -1
- package/dist/client/assets/index-D9A5nTKg.js +177 -0
- package/dist/client/assets/index-NhAGtpDY.css +2 -0
- package/dist/client/index.html +2 -3
- package/dist/server/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunks/config-store-BL8LDBuM.js +0 -1
- package/dist/client/assets/index-C9wfiVqt.js +0 -176
- package/dist/client/assets/index-PnaYWtoe.css +0 -2
|
@@ -4,9 +4,9 @@ import { a as sanitizeWorktreeName, i as safePathSegment, n as equalsIgnoringCas
|
|
|
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
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
|
|
7
|
+
import { At as GithubIssueResponse, C as comparablePrKey, Ct as PLAN_RESULT_HOOK_EVENT, Dt as parseExitPlanModeHookPayload, E as WorkflowsSettings, Et as PlanRevision, F as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Ft as IssuesStatus, It as issuePullRequestStateFromGithub, Mt as IssueUpdate, Nt as IssuesSearchResponse, O as WorkflowsState, Ot as planTitle, Pt as IssuesState, S as GithubPrsFrame, St as PLAN_HOOK_OUTPUT_MAX_CHARS, T as WorkflowLabelName, Tt as PlanDecision, _ as MinedPrReviewData, _t as CoherenceJournalOutput, b as GithubPrKey, bt as PLAN_BODY_CAP_BYTES, c as BenchmarkActionRequest, d as BenchmarkJudgement, f as BenchmarkRun, ft as UpdateJournal, g as MergedPrListing, gt as TraceRecord, h as CommitComparison, ht as TraceCheckpoint, jt as IssueKey, kt as GithubIssuePullRequestsRepository, l as BenchmarkCase, m as BenchmarkSuite, mt as MAX_RAW_LINE_CHARS, n as ClientMessage, p as BenchmarkStatus, pt as SessionSetup, s as pullRequestKeyFromReference, t as CONTROL_FRAME_MAX_BYTES, tt as USAGE_INTEGER_RANGES, u as BenchmarkId, v as PrCheckoutCaseInput, vt as ChangeMap, w as WorkflowCommentBody, wt as PLAN_TOOL_NAME, x as GithubPrLightSearchNode, xt as PLAN_HOOK_EVENT, y as GithubPrDetailNode, yt as changeMapFactId } from "./control-messages-Dgap3JRR.js";
|
|
8
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-
|
|
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-DFYLP1bF.js";
|
|
10
10
|
import { n as glimmervoidHomeDir, t as decideConfigPath } from "./config-path-core-DFt-TemS.js";
|
|
11
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";
|
|
@@ -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 {
|
|
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-DOGtTJ1d.js";
|
|
25
|
+
import { T as USAGE_VENDOR_KEYS, _ 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, w as USAGE_COST_MODES, y as TASK_TITLE_SOURCE_PRIORITY } from "./contracts-Ds_eevZs.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-Bga1N6Ie.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-DeWURqqr.js";
|
|
30
|
+
import { n as configFileViewOf, u as readSettingsForAgent } from "./settings-readout-core-C2mOk9Yz.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";
|
|
@@ -3874,7 +3874,6 @@ function createBackendShutdown(dependencies) {
|
|
|
3874
3874
|
stoppers.add("telegram-outbox", () => dependencies.telegramOutbox.idle());
|
|
3875
3875
|
destroySessions([
|
|
3876
3876
|
dependencies.visionsSessions,
|
|
3877
|
-
dependencies.changeMapSessions,
|
|
3878
3877
|
dependencies.taskTitleSessions ?? /* @__PURE__ */ new Map(),
|
|
3879
3878
|
dependencies.benchmarkSessions ?? /* @__PURE__ */ new Map()
|
|
3880
3879
|
], pendingReaps);
|
|
@@ -20062,6 +20061,10 @@ var Session = class extends EventEmitter {
|
|
|
20062
20061
|
this._output.push(data);
|
|
20063
20062
|
if (this.listenerCount("data") > 0) this.emit("data", data);
|
|
20064
20063
|
}
|
|
20064
|
+
_isExitHeadedForFailure(event) {
|
|
20065
|
+
if (this.state === STATES.FAILED) return false;
|
|
20066
|
+
return TRANSITIONS[this.state]?.[event] === STATES.FAILED;
|
|
20067
|
+
}
|
|
20065
20068
|
_handleSpawnedPtyExit(guardrailsLaunch, exitCode, signal) {
|
|
20066
20069
|
this._disposeGuardrailsLaunch(guardrailsLaunch);
|
|
20067
20070
|
this._stopConversationBridge();
|
|
@@ -20081,6 +20084,11 @@ var Session = class extends EventEmitter {
|
|
|
20081
20084
|
if (pid) this._exitReap = this._reapTreeOnce(pid);
|
|
20082
20085
|
const { event, detail } = decideExitTransition(this.state, exitCode, signal, this._receivedFirstOutput);
|
|
20083
20086
|
const reason = detail.reason || null;
|
|
20087
|
+
const pendingRemoteExitReason = this._isExitHeadedForFailure(event) ? this._getRemoteLaunch()?.describeExit().catch(() => null) : void 0;
|
|
20088
|
+
if (pendingRemoteExitReason) {
|
|
20089
|
+
const remoteExitReason = await pendingRemoteExitReason;
|
|
20090
|
+
if (remoteExitReason && this._isExitHeadedForFailure(event)) this.failureReason = remoteExitReason;
|
|
20091
|
+
}
|
|
20084
20092
|
this.transition(event, detail);
|
|
20085
20093
|
if (this._recorder) {
|
|
20086
20094
|
this._recorder.writeFooter("pty_exit", exitCode);
|
|
@@ -29812,6 +29820,26 @@ function coderErrorDetail(stderr) {
|
|
|
29812
29820
|
function isCoderSignedOutError(stderr) {
|
|
29813
29821
|
return typeof stderr === "string" && stderr.includes("You are signed out or your session has expired");
|
|
29814
29822
|
}
|
|
29823
|
+
function remoteWorkRefusal({ uncommittedStatus, unpushedLog, stashList, branch, workspace }) {
|
|
29824
|
+
if (uncommittedStatus.trim()) return `Branch ${branch} in Coder workspace ${workspace} has uncommitted changes`;
|
|
29825
|
+
if (unpushedLog.trim()) return `Branch ${branch} in Coder workspace ${workspace} has commits that are not pushed`;
|
|
29826
|
+
if (stashList.trim()) return `Branch ${branch} in Coder workspace ${workspace} has stashed changes`;
|
|
29827
|
+
return null;
|
|
29828
|
+
}
|
|
29829
|
+
var describeStoppedExit = (workspace) => `Coder workspace ${workspace} stopped, by its idle stop or its stop-after limit. Restart to start it again.`;
|
|
29830
|
+
var describeDeletedExit = (workspace) => `Coder workspace ${workspace} was deleted. Restart to get a fresh workspace.`;
|
|
29831
|
+
var REMOTE_EXIT_REASON_BY_STATUS = {
|
|
29832
|
+
stopped: describeStoppedExit,
|
|
29833
|
+
stopping: describeStoppedExit,
|
|
29834
|
+
deleting: describeDeletedExit,
|
|
29835
|
+
gone: describeDeletedExit,
|
|
29836
|
+
failed: (workspace) => `Coder workspace ${workspace} failed. Restart to get a fresh workspace.`
|
|
29837
|
+
};
|
|
29838
|
+
function remoteExitReason({ workspace, status }) {
|
|
29839
|
+
const describeExit = status === null ? void 0 : REMOTE_EXIT_REASON_BY_STATUS[status];
|
|
29840
|
+
if (describeExit) return describeExit(workspace);
|
|
29841
|
+
return `The connection to Coder workspace ${workspace} was lost. Restart to reconnect.`;
|
|
29842
|
+
}
|
|
29815
29843
|
function coderNotSignedInReason(failure) {
|
|
29816
29844
|
return `The coder CLI on this host is not signed in. Run coder login, then try again. (${failure})`;
|
|
29817
29845
|
}
|
|
@@ -29904,6 +29932,10 @@ function createCoderEnvPoller({ listWorkspaces, stopWorkspace, deleteWorkspace,
|
|
|
29904
29932
|
//#endregion
|
|
29905
29933
|
//#region server/coder/coder-env-wiring.ts
|
|
29906
29934
|
var CODER_UNAVAILABLE_REASON = "Coder environments are unavailable on this host";
|
|
29935
|
+
var REMOTE_SESSIONS_OFF_REFUSAL = {
|
|
29936
|
+
ok: false,
|
|
29937
|
+
reason: "Remote Coder sessions are off. Turn on Remote Coder sessions (experimental) in Settings."
|
|
29938
|
+
};
|
|
29907
29939
|
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 }) {
|
|
29908
29940
|
let binaryProbe = null;
|
|
29909
29941
|
let isCoderAvailable = false;
|
|
@@ -29914,6 +29946,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
29914
29946
|
const lastSeenStatusByWorkspace = /* @__PURE__ */ new Map();
|
|
29915
29947
|
const provisionByProject = /* @__PURE__ */ new Map();
|
|
29916
29948
|
const failedProvisionByProject = /* @__PURE__ */ new Map();
|
|
29949
|
+
const renewalByProject = /* @__PURE__ */ new Map();
|
|
29917
29950
|
const setTimeoutFn = pollerDeps.setTimeoutFn ?? DEFAULT_TIMER_FNS.setTimeoutFn;
|
|
29918
29951
|
let ownedWorkspaces = /* @__PURE__ */ new Set([...loadOwnedWorkspaces(), ...currentMappings().map((mapping) => mapping.workspace)]);
|
|
29919
29952
|
let ownedSaveChain = Promise.resolve();
|
|
@@ -30001,18 +30034,29 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30001
30034
|
if (workspaces !== null) await pruneOwned(workspaces);
|
|
30002
30035
|
return listingJson;
|
|
30003
30036
|
}
|
|
30004
|
-
async function listWorkspaces() {
|
|
30037
|
+
async function listWorkspaces(timeoutMs) {
|
|
30005
30038
|
const workspaces = recordListing((await command([
|
|
30006
30039
|
"list",
|
|
30007
30040
|
"-o",
|
|
30008
30041
|
"json"
|
|
30009
|
-
])).stdout);
|
|
30042
|
+
], timeoutMs)).stdout);
|
|
30010
30043
|
if (workspaces === null) throw new Error("Invalid Coder workspace listing");
|
|
30011
30044
|
return workspaces;
|
|
30012
30045
|
}
|
|
30013
30046
|
function workspaceStatus(workspace) {
|
|
30014
30047
|
return lastSeenStatusByWorkspace.get(workspace) ?? null;
|
|
30015
30048
|
}
|
|
30049
|
+
async function describeRemoteExit(workspace) {
|
|
30050
|
+
const workspaces = await listWorkspaces(1e4).catch(() => null);
|
|
30051
|
+
if (workspaces === null) return remoteExitReason({
|
|
30052
|
+
workspace,
|
|
30053
|
+
status: null
|
|
30054
|
+
});
|
|
30055
|
+
return remoteExitReason({
|
|
30056
|
+
workspace,
|
|
30057
|
+
status: workspaces.find((candidate) => candidate.name === workspace)?.status ?? "gone"
|
|
30058
|
+
});
|
|
30059
|
+
}
|
|
30016
30060
|
function isConfigured() {
|
|
30017
30061
|
return Boolean(config.coder?.environments?.length) && (platform === "linux" || platform === "darwin");
|
|
30018
30062
|
}
|
|
@@ -30185,10 +30229,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30185
30229
|
};
|
|
30186
30230
|
}
|
|
30187
30231
|
async function reserve({ projectId, environmentName, branch, liveCount }) {
|
|
30188
|
-
if (config.remoteSessions !== true) return
|
|
30189
|
-
ok: false,
|
|
30190
|
-
reason: "Remote Coder sessions are off. Turn on Remote Coder sessions (experimental) in Settings."
|
|
30191
|
-
};
|
|
30232
|
+
if (config.remoteSessions !== true) return REMOTE_SESSIONS_OFF_REFUSAL;
|
|
30192
30233
|
const environment = resolveEnvironment(environmentName);
|
|
30193
30234
|
if (!environment) return {
|
|
30194
30235
|
ok: false,
|
|
@@ -30211,6 +30252,12 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30211
30252
|
ok: false,
|
|
30212
30253
|
reason: unavailableReason
|
|
30213
30254
|
};
|
|
30255
|
+
return freshMapping({
|
|
30256
|
+
environmentName,
|
|
30257
|
+
branch
|
|
30258
|
+
});
|
|
30259
|
+
}
|
|
30260
|
+
function freshMapping({ environmentName, branch }) {
|
|
30214
30261
|
const workspace = workspaceNameFromHex(randomBytes(6).toString("hex"));
|
|
30215
30262
|
if (workspace === null) return {
|
|
30216
30263
|
ok: false,
|
|
@@ -30226,6 +30273,74 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30226
30273
|
}
|
|
30227
30274
|
};
|
|
30228
30275
|
}
|
|
30276
|
+
function renew(request) {
|
|
30277
|
+
if (config.remoteSessions !== true) return Promise.resolve(REMOTE_SESSIONS_OFF_REFUSAL);
|
|
30278
|
+
const inFlightRenewal = renewalByProject.get(request.projectId);
|
|
30279
|
+
if (inFlightRenewal) return inFlightRenewal;
|
|
30280
|
+
const renewal = renewUnclaimed(request);
|
|
30281
|
+
renewalByProject.set(request.projectId, renewal);
|
|
30282
|
+
renewal.finally(() => renewalByProject.delete(request.projectId));
|
|
30283
|
+
return renewal;
|
|
30284
|
+
}
|
|
30285
|
+
async function renewUnclaimed({ projectId, repoRemoteUrl, baseBranch, liveCount }) {
|
|
30286
|
+
if (provisionByProject.has(projectId)) return {
|
|
30287
|
+
ok: true,
|
|
30288
|
+
renewed: false
|
|
30289
|
+
};
|
|
30290
|
+
const mapping = config.projects.find((project) => project.id === projectId)?.environment;
|
|
30291
|
+
if (!mapping) return {
|
|
30292
|
+
ok: false,
|
|
30293
|
+
reason: `Project ${projectId} has no Coder workspace mapping`
|
|
30294
|
+
};
|
|
30295
|
+
if (!failedProvisionByProject.has(projectId) && await isWorkspaceListed(mapping.workspace) && workspaceStatus(mapping.workspace) !== "failed") return {
|
|
30296
|
+
ok: true,
|
|
30297
|
+
renewed: false
|
|
30298
|
+
};
|
|
30299
|
+
const environment = resolveEnvironment(mapping.name);
|
|
30300
|
+
if (!environment) return {
|
|
30301
|
+
ok: false,
|
|
30302
|
+
reason: `Unknown Coder environment: ${mapping.name}`
|
|
30303
|
+
};
|
|
30304
|
+
const limitReason = liveLimitRefusal({
|
|
30305
|
+
liveCount,
|
|
30306
|
+
maxLive: environment.maxLive,
|
|
30307
|
+
environmentName: mapping.name
|
|
30308
|
+
});
|
|
30309
|
+
if (limitReason) return {
|
|
30310
|
+
ok: false,
|
|
30311
|
+
reason: limitReason
|
|
30312
|
+
};
|
|
30313
|
+
if (!await ensureAvailable()) return {
|
|
30314
|
+
ok: false,
|
|
30315
|
+
reason: unavailableReason
|
|
30316
|
+
};
|
|
30317
|
+
const fresh = freshMapping({
|
|
30318
|
+
environmentName: mapping.name,
|
|
30319
|
+
branch: mapping.branch
|
|
30320
|
+
});
|
|
30321
|
+
if (!fresh.ok) return fresh;
|
|
30322
|
+
try {
|
|
30323
|
+
await saveMapping(projectId, fresh.mapping);
|
|
30324
|
+
} catch (error) {
|
|
30325
|
+
return {
|
|
30326
|
+
ok: false,
|
|
30327
|
+
reason: `Could not save the new Coder workspace mapping: ${errorMessage(error)}`
|
|
30328
|
+
};
|
|
30329
|
+
}
|
|
30330
|
+
failedProvisionByProject.delete(projectId);
|
|
30331
|
+
provision({
|
|
30332
|
+
projectId,
|
|
30333
|
+
mapping: fresh.mapping,
|
|
30334
|
+
repoRemoteUrl,
|
|
30335
|
+
baseBranch
|
|
30336
|
+
}).then((provisioned) => {
|
|
30337
|
+
if (!provisioned.ok) log.warn(`[coder-env] fresh Coder workspace for project ${projectId} failed: ${provisioned.reason}`);
|
|
30338
|
+
});
|
|
30339
|
+
return {
|
|
30340
|
+
ok: true,
|
|
30341
|
+
renewed: true
|
|
30342
|
+
};
|
|
30343
|
+
}
|
|
30229
30344
|
function provision({ projectId, mapping, repoRemoteUrl, baseBranch }) {
|
|
30230
30345
|
const savedMapping = config.projects.find((candidate) => candidate.id === projectId)?.environment;
|
|
30231
30346
|
if (JSON.stringify(savedMapping) !== JSON.stringify(mapping)) return Promise.resolve({
|
|
@@ -30407,6 +30522,88 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30407
30522
|
}
|
|
30408
30523
|
});
|
|
30409
30524
|
}
|
|
30525
|
+
async function uncheckedRemoteWorkReason(mapping) {
|
|
30526
|
+
const environment = resolveEnvironment(mapping.name);
|
|
30527
|
+
if (!environment) throw new Error(`Unknown Coder environment: ${mapping.name}`);
|
|
30528
|
+
if (!await ensureAvailable()) throw new Error(unavailableReason);
|
|
30529
|
+
const status = (await listWorkspaces()).find((candidate) => candidate.name === mapping.workspace)?.status ?? "gone";
|
|
30530
|
+
if (status === "gone") return null;
|
|
30531
|
+
if (status !== "running") throw new Error(`Coder workspace ${mapping.workspace} is ${status}`);
|
|
30532
|
+
const checkoutDirectory = remoteCheckoutDirectory({
|
|
30533
|
+
checkoutRoot: environment.checkoutRoot,
|
|
30534
|
+
workspace: mapping.workspace
|
|
30535
|
+
});
|
|
30536
|
+
const uncommittedStatus = (await command([
|
|
30537
|
+
"ssh",
|
|
30538
|
+
mapping.workspace,
|
|
30539
|
+
"--",
|
|
30540
|
+
remoteCommand([
|
|
30541
|
+
"git",
|
|
30542
|
+
"--no-optional-locks",
|
|
30543
|
+
"-C",
|
|
30544
|
+
checkoutDirectory,
|
|
30545
|
+
"status",
|
|
30546
|
+
"--porcelain"
|
|
30547
|
+
])
|
|
30548
|
+
])).stdout;
|
|
30549
|
+
const unpushedLog = (await command([
|
|
30550
|
+
"ssh",
|
|
30551
|
+
mapping.workspace,
|
|
30552
|
+
"--",
|
|
30553
|
+
remoteCommand([
|
|
30554
|
+
"git",
|
|
30555
|
+
"-C",
|
|
30556
|
+
checkoutDirectory,
|
|
30557
|
+
"log",
|
|
30558
|
+
"--branches",
|
|
30559
|
+
"HEAD",
|
|
30560
|
+
"--not",
|
|
30561
|
+
"--remotes",
|
|
30562
|
+
"--oneline"
|
|
30563
|
+
])
|
|
30564
|
+
])).stdout;
|
|
30565
|
+
const stashList = (await command([
|
|
30566
|
+
"ssh",
|
|
30567
|
+
mapping.workspace,
|
|
30568
|
+
"--",
|
|
30569
|
+
remoteCommand([
|
|
30570
|
+
"git",
|
|
30571
|
+
"-C",
|
|
30572
|
+
checkoutDirectory,
|
|
30573
|
+
"stash",
|
|
30574
|
+
"list"
|
|
30575
|
+
])
|
|
30576
|
+
])).stdout;
|
|
30577
|
+
return remoteWorkRefusal({
|
|
30578
|
+
uncommittedStatus,
|
|
30579
|
+
unpushedLog,
|
|
30580
|
+
stashList,
|
|
30581
|
+
branch: mapping.branch,
|
|
30582
|
+
workspace: mapping.workspace
|
|
30583
|
+
});
|
|
30584
|
+
}
|
|
30585
|
+
async function remoteWorkReason(mapping) {
|
|
30586
|
+
try {
|
|
30587
|
+
return await whileWorkspaceBusy(mapping.workspace, () => uncheckedRemoteWorkReason(mapping));
|
|
30588
|
+
} catch (error) {
|
|
30589
|
+
return `Could not check remote work on ${mapping.branch} in Coder workspace ${mapping.workspace}: ${errorMessage(error)}`;
|
|
30590
|
+
}
|
|
30591
|
+
}
|
|
30592
|
+
async function settleRemoval({ projectId, discardRemoteWork }) {
|
|
30593
|
+
await provisionByProject.get(projectId);
|
|
30594
|
+
const mapping = config.projects.find((project) => project.id === projectId)?.environment;
|
|
30595
|
+
if (!mapping) return { ok: true };
|
|
30596
|
+
const keptWorkReason = discardRemoteWork ? null : await remoteWorkReason(mapping);
|
|
30597
|
+
if (keptWorkReason) return {
|
|
30598
|
+
ok: false,
|
|
30599
|
+
reason: keptWorkReason,
|
|
30600
|
+
canDiscardRemoteWork: true
|
|
30601
|
+
};
|
|
30602
|
+
release({ projectId }).then((released) => {
|
|
30603
|
+
if (!released.ok) log.warn(`[coder-env] ${released.reason}; the orphan sweep will stop ${mapping.workspace}`);
|
|
30604
|
+
});
|
|
30605
|
+
return { ok: true };
|
|
30606
|
+
}
|
|
30410
30607
|
async function uploadFile({ workspace, remotePath, contents, mode }) {
|
|
30411
30608
|
if (workspaceNameFromHex(workspace.slice(3)) !== workspace) throw new Error("Invalid Coder workspace name");
|
|
30412
30609
|
const fileMode = typeof mode === "number" ? mode.toString(8) : mode;
|
|
@@ -30436,10 +30633,13 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30436
30633
|
availableEnvironments,
|
|
30437
30634
|
reserve,
|
|
30438
30635
|
provision,
|
|
30636
|
+
renew,
|
|
30439
30637
|
resume,
|
|
30440
30638
|
release,
|
|
30639
|
+
settleRemoval,
|
|
30441
30640
|
uploadFile,
|
|
30442
|
-
workspaceStatus
|
|
30641
|
+
workspaceStatus,
|
|
30642
|
+
describeRemoteExit
|
|
30443
30643
|
};
|
|
30444
30644
|
}
|
|
30445
30645
|
var DEFAULT_INTERVAL_MS$1 = 216e5;
|
|
@@ -34590,319 +34790,6 @@ function createIngestLane({ config = null, broadcast = null, logger = console, l
|
|
|
34590
34790
|
};
|
|
34591
34791
|
}
|
|
34592
34792
|
//#endregion
|
|
34593
|
-
//#region server/change-map/core/change-narrative-core.ts
|
|
34594
|
-
var FACT_GROUPS = [
|
|
34595
|
-
"files",
|
|
34596
|
-
"subsystems",
|
|
34597
|
-
"coChangeGaps",
|
|
34598
|
-
"hotspots",
|
|
34599
|
-
"blastRadius",
|
|
34600
|
-
"untestedFiles",
|
|
34601
|
-
"collisions",
|
|
34602
|
-
"links"
|
|
34603
|
-
];
|
|
34604
|
-
function narrativeFacts(map) {
|
|
34605
|
-
return { repos: map.repos.map((repo) => ({
|
|
34606
|
-
name: repo.name,
|
|
34607
|
-
base: repo.base,
|
|
34608
|
-
totalCounts: Object.fromEntries(FACT_GROUPS.map((group) => [group, repo[group].length])),
|
|
34609
|
-
files: capFacts(repo.files, compareFactIds),
|
|
34610
|
-
subsystems: capFacts(repo.subsystems, compareFactIds).map(capSubsystemPaths),
|
|
34611
|
-
coChangeGaps: capFacts(repo.coChangeGaps, compareFactIds),
|
|
34612
|
-
hotspots: capFacts(repo.hotspots, compareFactIds),
|
|
34613
|
-
blastRadius: capFacts(repo.blastRadius, compareBlastRadius),
|
|
34614
|
-
untestedFiles: capFacts(repo.untestedFiles, compareFactIds),
|
|
34615
|
-
collisions: capFacts(repo.collisions, compareFactIds),
|
|
34616
|
-
links: capFacts(repo.links, compareLinks)
|
|
34617
|
-
})).sort((left, right) => left.name.localeCompare(right.name)) };
|
|
34618
|
-
}
|
|
34619
|
-
function capFacts(facts, compare) {
|
|
34620
|
-
return [...facts].sort(compare).slice(0, 20);
|
|
34621
|
-
}
|
|
34622
|
-
function capSubsystemPaths(subsystem) {
|
|
34623
|
-
return {
|
|
34624
|
-
...subsystem,
|
|
34625
|
-
paths: subsystem.paths.slice(0, 20),
|
|
34626
|
-
pathCount: subsystem.paths.length
|
|
34627
|
-
};
|
|
34628
|
-
}
|
|
34629
|
-
function compareFactIds(left, right) {
|
|
34630
|
-
return left.factId.localeCompare(right.factId);
|
|
34631
|
-
}
|
|
34632
|
-
function compareBlastRadius(left, right) {
|
|
34633
|
-
return right.transitiveDependentCount - left.transitiveDependentCount || compareFactIds(left, right);
|
|
34634
|
-
}
|
|
34635
|
-
function compareLinks(left, right) {
|
|
34636
|
-
return right.changedImporterCount - left.changedImporterCount || right.importerCount - left.importerCount || compareFactIds(left, right);
|
|
34637
|
-
}
|
|
34638
|
-
function factsHashInput(map) {
|
|
34639
|
-
return JSON.stringify(narrativeFacts(map), (_key, value) => {
|
|
34640
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
34641
|
-
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => {
|
|
34642
|
-
if (left < right) return -1;
|
|
34643
|
-
if (left > right) return 1;
|
|
34644
|
-
return 0;
|
|
34645
|
-
}));
|
|
34646
|
-
});
|
|
34647
|
-
}
|
|
34648
|
-
function hasNarratableFacts(map) {
|
|
34649
|
-
return map.repos.some((repo) => repo.files.length > 0);
|
|
34650
|
-
}
|
|
34651
|
-
function knownNarrativeFactIds(map) {
|
|
34652
|
-
const knownFactIds = /* @__PURE__ */ new Set();
|
|
34653
|
-
for (const repo of narrativeFacts(map).repos) for (const group of FACT_GROUPS) for (const fact of repo[group]) knownFactIds.add(fact.factId);
|
|
34654
|
-
return knownFactIds;
|
|
34655
|
-
}
|
|
34656
|
-
function buildNarrativePrompt({ facts, resultPath }) {
|
|
34657
|
-
const deliveryLines = resultPath ? ["Use no tools except Write to the exact result file path below.", "Write"] : ["Answer with the JSON as your final message and write no files.", "Return"];
|
|
34658
|
-
return [
|
|
34659
|
-
"The facts below are your only source of truth. Read nothing else.",
|
|
34660
|
-
deliveryLines[0],
|
|
34661
|
-
`${deliveryLines[1]} JSON { "claims": [{ "text": string, "factIds": string[] }] } with at most 12 claims.`,
|
|
34662
|
-
"Each claim must be one or two plain-English sentences about how a change connects to the codebase or what risk it carries.",
|
|
34663
|
-
"Every claim must cite one or more factIds copied exactly from the facts. Do not invent facts or identifiers.",
|
|
34664
|
-
"When links are present, lead with how a change in one repository reaches another through them.",
|
|
34665
|
-
`Each fact list and subsystem path list holds at most 20 entries; totalCounts and pathCount give the full sizes.`,
|
|
34666
|
-
`Keep each claim within 600 characters.`,
|
|
34667
|
-
...resultPath ? [`Result file: ${resultPath}`] : [],
|
|
34668
|
-
`Facts JSON: ${JSON.stringify(facts)}`
|
|
34669
|
-
].join("\n");
|
|
34670
|
-
}
|
|
34671
|
-
function validateNarrative({ raw, knownFactIds, factsHash, model }) {
|
|
34672
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
34673
|
-
const offeredClaims = "claims" in raw ? raw.claims : null;
|
|
34674
|
-
if (!Array.isArray(offeredClaims)) return null;
|
|
34675
|
-
const claims = [];
|
|
34676
|
-
for (const offeredClaim of offeredClaims) {
|
|
34677
|
-
if (!offeredClaim || typeof offeredClaim !== "object" || Array.isArray(offeredClaim)) continue;
|
|
34678
|
-
const text = "text" in offeredClaim ? offeredClaim.text : null;
|
|
34679
|
-
const factIds = "factIds" in offeredClaim ? offeredClaim.factIds : null;
|
|
34680
|
-
if (typeof text !== "string" || text.length > 600) continue;
|
|
34681
|
-
if (!Array.isArray(factIds)) continue;
|
|
34682
|
-
const cleanText = text.replaceAll(String.fromCharCode(8212), "-").replaceAll(String.fromCharCode(8211), "-").replaceAll(String.fromCharCode(8230), "...").trim();
|
|
34683
|
-
if (!cleanText || cleanText.length > 600) continue;
|
|
34684
|
-
const citedFactIds = [...new Set(factIds.filter((factId) => typeof factId === "string" && knownFactIds.has(factId)))];
|
|
34685
|
-
if (citedFactIds.length === 0) continue;
|
|
34686
|
-
claims.push({
|
|
34687
|
-
text: cleanText,
|
|
34688
|
-
factIds: citedFactIds
|
|
34689
|
-
});
|
|
34690
|
-
if (claims.length >= 12) break;
|
|
34691
|
-
}
|
|
34692
|
-
if (claims.length === 0) return null;
|
|
34693
|
-
return {
|
|
34694
|
-
factsHash,
|
|
34695
|
-
model,
|
|
34696
|
-
claims
|
|
34697
|
-
};
|
|
34698
|
-
}
|
|
34699
|
-
//#endregion
|
|
34700
|
-
//#region server/change-map/change-narrator.ts
|
|
34701
|
-
var MAX_CACHED_HASHES = 64;
|
|
34702
|
-
var PROMPT_FILE$2 = "change-narrative-prompt.txt";
|
|
34703
|
-
var RESULT_FILE$2 = "change-narrative-result.json";
|
|
34704
|
-
var SCHEMA_FILE = "change-narrative-schema.json";
|
|
34705
|
-
var BOOTSTRAP_PROMPT$1 = `Read ${PROMPT_FILE$2} and follow its instructions`;
|
|
34706
|
-
var CODEX_BOOTSTRAP_PROMPT = `Read ${PROMPT_FILE$2} in your cwd and answer only with the JSON`;
|
|
34707
|
-
var CODEX_OUTPUT_SCHEMA = {
|
|
34708
|
-
type: "object",
|
|
34709
|
-
additionalProperties: false,
|
|
34710
|
-
required: ["claims"],
|
|
34711
|
-
properties: { claims: {
|
|
34712
|
-
type: "array",
|
|
34713
|
-
maxItems: 12,
|
|
34714
|
-
items: {
|
|
34715
|
-
type: "object",
|
|
34716
|
-
additionalProperties: false,
|
|
34717
|
-
required: ["text", "factIds"],
|
|
34718
|
-
properties: {
|
|
34719
|
-
text: { type: "string" },
|
|
34720
|
-
factIds: {
|
|
34721
|
-
type: "array",
|
|
34722
|
-
items: { type: "string" }
|
|
34723
|
-
}
|
|
34724
|
-
}
|
|
34725
|
-
}
|
|
34726
|
-
} }
|
|
34727
|
-
};
|
|
34728
|
-
async function codexLaunch(workDir, resultPath, codexModel) {
|
|
34729
|
-
const schemaPath = path.join(workDir, SCHEMA_FILE);
|
|
34730
|
-
await fsp.writeFile(schemaPath, JSON.stringify(CODEX_OUTPUT_SCHEMA), "utf8");
|
|
34731
|
-
return {
|
|
34732
|
-
promptResultPath: null,
|
|
34733
|
-
bootstrapPrompt: CODEX_BOOTSTRAP_PROMPT,
|
|
34734
|
-
agent: "codex",
|
|
34735
|
-
extraArgs: [
|
|
34736
|
-
"exec",
|
|
34737
|
-
"--ephemeral",
|
|
34738
|
-
"--skip-git-repo-check",
|
|
34739
|
-
"--output-schema",
|
|
34740
|
-
schemaPath,
|
|
34741
|
-
"-o",
|
|
34742
|
-
resultPath,
|
|
34743
|
-
...codexModel ? ["-m", codexModel] : []
|
|
34744
|
-
],
|
|
34745
|
-
model: null
|
|
34746
|
-
};
|
|
34747
|
-
}
|
|
34748
|
-
function claudeLaunch(resultPath, model) {
|
|
34749
|
-
return {
|
|
34750
|
-
promptResultPath: resultPath,
|
|
34751
|
-
bootstrapPrompt: BOOTSTRAP_PROMPT$1,
|
|
34752
|
-
agent: void 0,
|
|
34753
|
-
extraArgs: [],
|
|
34754
|
-
model
|
|
34755
|
-
};
|
|
34756
|
-
}
|
|
34757
|
-
function makeNarrationWorkDir() {
|
|
34758
|
-
return fsp.mkdtemp(path.join(os.tmpdir(), "glimmervoid-change-narrative-"));
|
|
34759
|
-
}
|
|
34760
|
-
async function spawnNarrationWithSession({ map, factsHash, engine, model, codexModel, timeoutSeconds }, spawn, nowFn) {
|
|
34761
|
-
const workDir = await makeNarrationWorkDir();
|
|
34762
|
-
const promptPath = path.join(workDir, PROMPT_FILE$2);
|
|
34763
|
-
const resultPath = path.join(workDir, RESULT_FILE$2);
|
|
34764
|
-
const controller = new AbortController();
|
|
34765
|
-
const timeout = setTimeout(() => controller.abort(), timeoutSeconds * 1e3);
|
|
34766
|
-
timeout.unref();
|
|
34767
|
-
try {
|
|
34768
|
-
const launch = engine === "codex" ? await codexLaunch(workDir, resultPath, codexModel) : claudeLaunch(resultPath, model);
|
|
34769
|
-
await fsp.writeFile(promptPath, buildNarrativePrompt({
|
|
34770
|
-
facts: narrativeFacts(map),
|
|
34771
|
-
resultPath: launch.promptResultPath
|
|
34772
|
-
}), "utf8");
|
|
34773
|
-
await spawn({
|
|
34774
|
-
id: `change-narrative:${nowFn()}:${crypto.randomUUID()}`,
|
|
34775
|
-
name: "Change map narrative",
|
|
34776
|
-
cwd: workDir,
|
|
34777
|
-
prompt: launch.bootstrapPrompt,
|
|
34778
|
-
agent: launch.agent,
|
|
34779
|
-
extraArgs: launch.extraArgs,
|
|
34780
|
-
model: launch.model,
|
|
34781
|
-
signal: controller.signal
|
|
34782
|
-
});
|
|
34783
|
-
if (controller.signal.aborted) return null;
|
|
34784
|
-
return validateNarrative({
|
|
34785
|
-
raw: await readLaneResultFile(resultPath),
|
|
34786
|
-
knownFactIds: knownNarrativeFactIds(map),
|
|
34787
|
-
factsHash,
|
|
34788
|
-
model
|
|
34789
|
-
});
|
|
34790
|
-
} finally {
|
|
34791
|
-
clearTimeout(timeout);
|
|
34792
|
-
await fsp.rm(workDir, {
|
|
34793
|
-
recursive: true,
|
|
34794
|
-
force: true
|
|
34795
|
-
});
|
|
34796
|
-
}
|
|
34797
|
-
}
|
|
34798
|
-
function createChangeNarrator({ getConfig, spawnNarration, spawnLane = createLaneSpawn({ laneName: "change-map" }), nowFn = () => Date.now() }) {
|
|
34799
|
-
const runNarration = spawnNarration ?? ((request) => spawnNarrationWithSession(request, spawnLane, nowFn));
|
|
34800
|
-
const cachedByHash = /* @__PURE__ */ new Map();
|
|
34801
|
-
const queuedBySessionId = /* @__PURE__ */ new Map();
|
|
34802
|
-
let active = null;
|
|
34803
|
-
function narratorSettings() {
|
|
34804
|
-
const parsed = BrowserConfig.shape.changeMap.safeParse(getConfig().changeMap);
|
|
34805
|
-
if (!parsed.success) return null;
|
|
34806
|
-
return parsed.data?.narrator ?? null;
|
|
34807
|
-
}
|
|
34808
|
-
function cache(factsHash, narrative) {
|
|
34809
|
-
cachedByHash.delete(factsHash);
|
|
34810
|
-
cachedByHash.set(factsHash, narrative);
|
|
34811
|
-
if (cachedByHash.size <= MAX_CACHED_HASHES) return;
|
|
34812
|
-
const oldestHash = cachedByHash.keys().next().value;
|
|
34813
|
-
if (oldestHash) cachedByHash.delete(oldestHash);
|
|
34814
|
-
}
|
|
34815
|
-
function start(request) {
|
|
34816
|
-
const callbacksBySessionId = /* @__PURE__ */ new Map([[request.sessionId, request.onSettled]]);
|
|
34817
|
-
active = {
|
|
34818
|
-
factsHash: request.factsHash,
|
|
34819
|
-
callbacksBySessionId
|
|
34820
|
-
};
|
|
34821
|
-
Promise.resolve().then(() => runNarration(request)).then((narrative) => finish(request.factsHash, narrative), () => finish(request.factsHash, null));
|
|
34822
|
-
}
|
|
34823
|
-
function startNext() {
|
|
34824
|
-
if (active) return;
|
|
34825
|
-
for (const [sessionId, request] of queuedBySessionId) {
|
|
34826
|
-
queuedBySessionId.delete(sessionId);
|
|
34827
|
-
if (!narratorSettings()?.enabled || cachedByHash.has(request.factsHash)) {
|
|
34828
|
-
request.onSettled();
|
|
34829
|
-
continue;
|
|
34830
|
-
}
|
|
34831
|
-
start(request);
|
|
34832
|
-
break;
|
|
34833
|
-
}
|
|
34834
|
-
}
|
|
34835
|
-
function finish(factsHash, narrative) {
|
|
34836
|
-
if (!active || active.factsHash !== factsHash) return;
|
|
34837
|
-
const callbacks = [...active.callbacksBySessionId.values()];
|
|
34838
|
-
cache(factsHash, narrative);
|
|
34839
|
-
active = null;
|
|
34840
|
-
startNext();
|
|
34841
|
-
for (const callback of callbacks) callback();
|
|
34842
|
-
}
|
|
34843
|
-
function narrationFor(map, onSettled, { mayStart = true } = {}) {
|
|
34844
|
-
const config = narratorSettings();
|
|
34845
|
-
if (!config?.enabled || !hasNarratableFacts(map)) {
|
|
34846
|
-
queuedBySessionId.delete(map.sessionId);
|
|
34847
|
-
return {
|
|
34848
|
-
narrative: null,
|
|
34849
|
-
narratorState: "disabled"
|
|
34850
|
-
};
|
|
34851
|
-
}
|
|
34852
|
-
const engine = config.engine ?? "claude";
|
|
34853
|
-
const configuredModel = config.model?.trim() || "";
|
|
34854
|
-
const model = configuredModel || (engine === "claude" ? "haiku" : "codex default");
|
|
34855
|
-
const factsHash = crypto.createHash("sha256").update(JSON.stringify([
|
|
34856
|
-
factsHashInput(map),
|
|
34857
|
-
engine,
|
|
34858
|
-
configuredModel
|
|
34859
|
-
])).digest("hex");
|
|
34860
|
-
if (cachedByHash.has(factsHash)) {
|
|
34861
|
-
queuedBySessionId.delete(map.sessionId);
|
|
34862
|
-
const narrative = cachedByHash.get(factsHash) ?? null;
|
|
34863
|
-
return {
|
|
34864
|
-
narrative,
|
|
34865
|
-
narratorState: narrative ? "ready" : "failed"
|
|
34866
|
-
};
|
|
34867
|
-
}
|
|
34868
|
-
if (active?.factsHash === factsHash) {
|
|
34869
|
-
active.callbacksBySessionId.set(map.sessionId, onSettled);
|
|
34870
|
-
queuedBySessionId.delete(map.sessionId);
|
|
34871
|
-
return {
|
|
34872
|
-
narrative: null,
|
|
34873
|
-
narratorState: "pending"
|
|
34874
|
-
};
|
|
34875
|
-
}
|
|
34876
|
-
if (!mayStart) return {
|
|
34877
|
-
narrative: null,
|
|
34878
|
-
narratorState: queuedBySessionId.has(map.sessionId) ? "pending" : "disabled"
|
|
34879
|
-
};
|
|
34880
|
-
const request = {
|
|
34881
|
-
map,
|
|
34882
|
-
factsHash,
|
|
34883
|
-
sessionId: map.sessionId,
|
|
34884
|
-
onSettled,
|
|
34885
|
-
engine,
|
|
34886
|
-
model,
|
|
34887
|
-
codexModel: engine === "codex" ? configuredModel || null : null,
|
|
34888
|
-
timeoutSeconds: config.timeoutSeconds ?? 90
|
|
34889
|
-
};
|
|
34890
|
-
if (active) {
|
|
34891
|
-
queuedBySessionId.set(map.sessionId, request);
|
|
34892
|
-
return {
|
|
34893
|
-
narrative: null,
|
|
34894
|
-
narratorState: "pending"
|
|
34895
|
-
};
|
|
34896
|
-
}
|
|
34897
|
-
start(request);
|
|
34898
|
-
return {
|
|
34899
|
-
narrative: null,
|
|
34900
|
-
narratorState: "pending"
|
|
34901
|
-
};
|
|
34902
|
-
}
|
|
34903
|
-
return { narrationFor };
|
|
34904
|
-
}
|
|
34905
|
-
//#endregion
|
|
34906
34793
|
//#region server/task-title-refiner.ts
|
|
34907
34794
|
var PROMPT_FILE$1 = "task-title-prompt.txt";
|
|
34908
34795
|
var RESULT_FILE$1 = "task-title-result.json";
|
|
@@ -46275,7 +46162,6 @@ function createBackendLanes(dependencies) {
|
|
|
46275
46162
|
reviewSessions,
|
|
46276
46163
|
investigationSessions,
|
|
46277
46164
|
visionsSessions,
|
|
46278
|
-
changeMapSessions,
|
|
46279
46165
|
taskTitleSessions,
|
|
46280
46166
|
benchmarkSessions
|
|
46281
46167
|
];
|
|
@@ -46556,21 +46442,6 @@ function createBackendLanes(dependencies) {
|
|
|
46556
46442
|
...notice
|
|
46557
46443
|
});
|
|
46558
46444
|
});
|
|
46559
|
-
const changeMapSessions = /* @__PURE__ */ new Map();
|
|
46560
|
-
const changeMapNarrator = createChangeNarrator({
|
|
46561
|
-
getConfig: () => configStore.config,
|
|
46562
|
-
spawnLane: createLaneSpawn({
|
|
46563
|
-
sessions: changeMapSessions,
|
|
46564
|
-
closeSessionDataClients,
|
|
46565
|
-
hookRouter,
|
|
46566
|
-
getHookPort,
|
|
46567
|
-
spawnGate,
|
|
46568
|
-
recordLane,
|
|
46569
|
-
replayBufferKB: config.replayBufferKB,
|
|
46570
|
-
laneName: "change-map",
|
|
46571
|
-
guardrailsRefusal
|
|
46572
|
-
})
|
|
46573
|
-
});
|
|
46574
46445
|
const taskTitleSessions = /* @__PURE__ */ new Map();
|
|
46575
46446
|
const taskTitleRefiner = createTaskTitleRefiner({
|
|
46576
46447
|
getConfig: () => configStore.config,
|
|
@@ -46758,13 +46629,11 @@ function createBackendLanes(dependencies) {
|
|
|
46758
46629
|
branchGc,
|
|
46759
46630
|
coderActivity,
|
|
46760
46631
|
coderEnv,
|
|
46761
|
-
changeMapNarrator,
|
|
46762
46632
|
taskTitleRefiner,
|
|
46763
46633
|
taskTitleSessions,
|
|
46764
46634
|
current,
|
|
46765
46635
|
currentIngest,
|
|
46766
46636
|
currentVisions,
|
|
46767
|
-
changeMapSessions,
|
|
46768
46637
|
gitWorkspace,
|
|
46769
46638
|
gitWorkspaceSync,
|
|
46770
46639
|
githubStore,
|
|
@@ -49108,6 +48977,7 @@ function createSessionFactory(dependencies) {
|
|
|
49108
48977
|
}),
|
|
49109
48978
|
nodePath: environment?.nodePath ?? "node",
|
|
49110
48979
|
ensureRunning: async () => coderEnv ? coderEnv.resume({ projectId }) : refusal,
|
|
48980
|
+
describeExit: async () => coderEnv ? coderEnv.describeRemoteExit(mapping.workspace) : null,
|
|
49111
48981
|
uploadFile: async (file) => {
|
|
49112
48982
|
if (!coderEnv) throw new Error(refusal.reason);
|
|
49113
48983
|
await coderEnv.uploadFile({
|
|
@@ -49189,34 +49059,6 @@ function createSessionFactory(dependencies) {
|
|
|
49189
49059
|
};
|
|
49190
49060
|
}
|
|
49191
49061
|
//#endregion
|
|
49192
|
-
//#region server/core/diff-annotations-core.ts
|
|
49193
|
-
var DIFF_ANNOTATION_HEADER = "Operator review notes on the current worktree diff:";
|
|
49194
|
-
var DIFF_ANNOTATION_CLOSING = "Address each note, then reply with what changed.";
|
|
49195
|
-
var REMOVED_LINE_MARKER = "(removed line)";
|
|
49196
|
-
function sortAnnotations(list) {
|
|
49197
|
-
return [...list].sort((left, right) => {
|
|
49198
|
-
if (left.path !== right.path) return left.path < right.path ? -1 : 1;
|
|
49199
|
-
if (left.line !== right.line) return left.line - right.line;
|
|
49200
|
-
if (left.side === right.side) return 0;
|
|
49201
|
-
return left.side < right.side ? -1 : 1;
|
|
49202
|
-
});
|
|
49203
|
-
}
|
|
49204
|
-
function formatDiffAnnotationMessage(annotations) {
|
|
49205
|
-
const entries = sortAnnotations(annotations).flatMap((annotation) => {
|
|
49206
|
-
const note = scrubForPaste(annotation.note, DIFF_ANNOTATION_NOTE_MAX_CHARS);
|
|
49207
|
-
const path = scrubForPaste(annotation.path, 400);
|
|
49208
|
-
if (!note || !path) return [];
|
|
49209
|
-
const marker = annotation.side === "old" ? ` ${REMOVED_LINE_MARKER}` : "";
|
|
49210
|
-
return [`- ${path}:${annotation.line} (${annotation.section})${marker} ${note}`];
|
|
49211
|
-
});
|
|
49212
|
-
if (entries.length === 0) return "";
|
|
49213
|
-
return [
|
|
49214
|
-
DIFF_ANNOTATION_HEADER,
|
|
49215
|
-
...entries,
|
|
49216
|
-
DIFF_ANNOTATION_CLOSING
|
|
49217
|
-
].join("\n");
|
|
49218
|
-
}
|
|
49219
|
-
//#endregion
|
|
49220
49062
|
//#region server/github/core/github-issues-core.ts
|
|
49221
49063
|
var GITHUB_ISSUE_BODY_MAX_CHARS = 12e3;
|
|
49222
49064
|
function fencedBody(body) {
|
|
@@ -50204,7 +50046,7 @@ function changedFilesOf(scope) {
|
|
|
50204
50046
|
isCommitted: false
|
|
50205
50047
|
}));
|
|
50206
50048
|
}
|
|
50207
|
-
function createChangeMapService({ sessions,
|
|
50049
|
+
function createChangeMapService({ sessions, runGit = runGitAsync, nowFn = Date.now }) {
|
|
50208
50050
|
const specifierCache = createFileFactCache(extractImportSpecifiers, []);
|
|
50209
50051
|
const pythonSpecifierCache = createFileFactCache(extractPythonImportSpecifiers, []);
|
|
50210
50052
|
const tsconfigCache = createFileFactCache((text) => ({ config: parseTsconfigJsonc(text) }), { config: null });
|
|
@@ -50355,7 +50197,7 @@ function createChangeMapService({ sessions, narrator = null, onNarrationSettled
|
|
|
50355
50197
|
};
|
|
50356
50198
|
}
|
|
50357
50199
|
}
|
|
50358
|
-
async function assemble(session
|
|
50200
|
+
async function assemble(session) {
|
|
50359
50201
|
forgetEndedSiblings();
|
|
50360
50202
|
const scopes = await session.getChangeScopes();
|
|
50361
50203
|
const inputsByRoot = /* @__PURE__ */ new Map();
|
|
@@ -50436,44 +50278,31 @@ function createChangeMapService({ sessions, narrator = null, onNarrationSettled
|
|
|
50436
50278
|
for (const repo of repos) repo.links = [];
|
|
50437
50279
|
}
|
|
50438
50280
|
}
|
|
50439
|
-
|
|
50281
|
+
return ChangeMap.parse({
|
|
50440
50282
|
sessionId: session.id,
|
|
50441
50283
|
sig: hashText(JSON.stringify(scopes)),
|
|
50442
50284
|
generatedAt: nowFn(),
|
|
50443
|
-
repos
|
|
50444
|
-
|
|
50445
|
-
narratorState: "disabled"
|
|
50446
|
-
};
|
|
50447
|
-
const narration = narrator ? narrator.narrationFor(factsOnly, () => onNarrationSettled(session.id), { mayStart: mayStartNarration }) : null;
|
|
50448
|
-
return ChangeMap.parse(narration ? {
|
|
50449
|
-
...factsOnly,
|
|
50450
|
-
...narration
|
|
50451
|
-
} : factsOnly);
|
|
50285
|
+
repos
|
|
50286
|
+
});
|
|
50452
50287
|
}
|
|
50453
|
-
function startAssembly(session
|
|
50454
|
-
const running = assemble(session
|
|
50288
|
+
function startAssembly(session) {
|
|
50289
|
+
const running = assemble(session).finally(() => {
|
|
50455
50290
|
if (runningBySessionId.get(session.id) === running) runningBySessionId.delete(session.id);
|
|
50456
50291
|
});
|
|
50457
50292
|
runningBySessionId.set(session.id, running);
|
|
50458
50293
|
return running;
|
|
50459
50294
|
}
|
|
50460
|
-
function build(session
|
|
50295
|
+
function build(session) {
|
|
50461
50296
|
const queued = queuedBySessionId.get(session.id);
|
|
50462
|
-
if (queued)
|
|
50463
|
-
queued.mayStartNarration ||= mayStartNarration;
|
|
50464
|
-
return queued.promise;
|
|
50465
|
-
}
|
|
50297
|
+
if (queued) return queued;
|
|
50466
50298
|
const running = runningBySessionId.get(session.id);
|
|
50467
|
-
if (!running) return startAssembly(session
|
|
50468
|
-
const followUp = {
|
|
50469
|
-
|
|
50470
|
-
|
|
50471
|
-
|
|
50472
|
-
return startAssembly(session, followUp.mayStartNarration);
|
|
50473
|
-
})
|
|
50474
|
-
};
|
|
50299
|
+
if (!running) return startAssembly(session);
|
|
50300
|
+
const followUp = running.then(ignoreOutcome, ignoreOutcome).then(() => {
|
|
50301
|
+
queuedBySessionId.delete(session.id);
|
|
50302
|
+
return startAssembly(session);
|
|
50303
|
+
});
|
|
50475
50304
|
queuedBySessionId.set(session.id, followUp);
|
|
50476
|
-
return followUp
|
|
50305
|
+
return followUp;
|
|
50477
50306
|
}
|
|
50478
50307
|
return { build };
|
|
50479
50308
|
}
|
|
@@ -50755,14 +50584,14 @@ function requestValidationErrorReply(msg, message) {
|
|
|
50755
50584
|
ok: false,
|
|
50756
50585
|
error: message
|
|
50757
50586
|
}),
|
|
50758
|
-
"
|
|
50759
|
-
type: "
|
|
50587
|
+
"remove-session": () => ({
|
|
50588
|
+
type: "remove-session-result",
|
|
50760
50589
|
requestId,
|
|
50761
50590
|
ok: false,
|
|
50762
50591
|
error: message
|
|
50763
50592
|
}),
|
|
50764
|
-
"
|
|
50765
|
-
type: "
|
|
50593
|
+
"delete-hook": () => ({
|
|
50594
|
+
type: "delete-hook-result",
|
|
50766
50595
|
requestId,
|
|
50767
50596
|
ok: false,
|
|
50768
50597
|
error: message
|
|
@@ -50834,25 +50663,8 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
50834
50663
|
issue: null,
|
|
50835
50664
|
error: "Issues polling is not running."
|
|
50836
50665
|
}
|
|
50837
|
-
}), issueProjectRepos = null, serverBuild = () => null, getUsageSessions = null, getUsageReport = null, requestUsageReport = null, getPlanLimits = null, controlReplayLog = null, getRtkInstallStatus = () => null, telegramDelivery = null, resolveRtkPath = () => getRtkPath(), conversationFs = fs, conversationGit = runGitForConversationHistory, conversationProjectsDir = claudeProjectsDir(process.env, os.homedir()), readTracePage = null, readPlanRevision = null, decidePlanReview = null, sessionDocs = null, coherenceFeed = null,
|
|
50838
|
-
const changeMaps = createChangeMapService({
|
|
50839
|
-
sessions,
|
|
50840
|
-
narrator: changeMapNarrator,
|
|
50841
|
-
onNarrationSettled: (sessionId) => {
|
|
50842
|
-
broadcastChangeMap(sessionId).catch((error) => {
|
|
50843
|
-
console.warn(`[control] change-map broadcast failed: id=${sessionId}: ${errorMessage(error)}`);
|
|
50844
|
-
});
|
|
50845
|
-
}
|
|
50846
|
-
});
|
|
50847
|
-
async function broadcastChangeMap(sessionId) {
|
|
50848
|
-
const session = sessions.get(sessionId);
|
|
50849
|
-
if (!session) return;
|
|
50850
|
-
broadcastControl({
|
|
50851
|
-
type: "change-map",
|
|
50852
|
-
id: session.id,
|
|
50853
|
-
map: await changeMaps.build(session, { mayStartNarration: false })
|
|
50854
|
-
});
|
|
50855
|
-
}
|
|
50666
|
+
}), issueProjectRepos = null, serverBuild = () => null, getUsageSessions = null, getUsageReport = null, requestUsageReport = null, getPlanLimits = null, controlReplayLog = null, getRtkInstallStatus = () => null, telegramDelivery = null, resolveRtkPath = () => getRtkPath(), conversationFs = fs, conversationGit = runGitForConversationHistory, conversationProjectsDir = claudeProjectsDir(process.env, os.homedir()), readTracePage = null, readPlanRevision = null, decidePlanReview = null, sessionDocs = null, coherenceFeed = null, coderEnv = null, remoteRepoGit = null, agentConnect = null } = deps;
|
|
50667
|
+
const changeMaps = createChangeMapService({ sessions });
|
|
50856
50668
|
function buildSettingsPayload$1() {
|
|
50857
50669
|
return buildSettingsPayload({
|
|
50858
50670
|
configStore,
|
|
@@ -50950,9 +50762,56 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
50950
50762
|
console.log(`[control] Added session via UI: ${name}${skipPerms ? " (skip permissions)" : " (permission prompts)"}${remote ? ` (Coder environment ${remote.environment.name})` : ""}`);
|
|
50951
50763
|
return created.project;
|
|
50952
50764
|
}
|
|
50953
|
-
function
|
|
50765
|
+
async function remoteRepoSource(projectPath) {
|
|
50766
|
+
if (!remoteRepoGit) return {
|
|
50767
|
+
ok: false,
|
|
50768
|
+
error: "Coder environments are unavailable on this host"
|
|
50769
|
+
};
|
|
50770
|
+
const repoRemoteUrl = await remoteRepoGit.originUrl({ projectPath });
|
|
50771
|
+
if (!repoRemoteUrl) return {
|
|
50772
|
+
ok: false,
|
|
50773
|
+
error: `${projectPath} has no origin remote, so a Coder workspace cannot clone it.`
|
|
50774
|
+
};
|
|
50775
|
+
const baseBranch = configuredIntegrationBranch(config) ?? await remoteRepoGit.detectDefaultBranch({ projectPath });
|
|
50776
|
+
if (!baseBranch) return {
|
|
50777
|
+
ok: false,
|
|
50778
|
+
error: `Could not find the default branch of ${projectPath} on origin.`
|
|
50779
|
+
};
|
|
50780
|
+
return {
|
|
50781
|
+
ok: true,
|
|
50782
|
+
repoRemoteUrl,
|
|
50783
|
+
baseBranch
|
|
50784
|
+
};
|
|
50785
|
+
}
|
|
50786
|
+
async function restartSession(msg, ws, restart) {
|
|
50787
|
+
const sess = findSession(msg);
|
|
50788
|
+
if (!sess) return;
|
|
50789
|
+
const project = config.projects.find((candidate) => candidate.id === sess.id);
|
|
50790
|
+
if (sess.state !== STATES.FAILED || !project?.environment || !coderEnv) {
|
|
50791
|
+
restart(sess);
|
|
50792
|
+
return;
|
|
50793
|
+
}
|
|
50794
|
+
const source = await remoteRepoSource(project.path);
|
|
50795
|
+
if (!source.ok) {
|
|
50796
|
+
sendError(ws, source.error, { id: sess.id });
|
|
50797
|
+
return;
|
|
50798
|
+
}
|
|
50799
|
+
const renewal = await coderEnv.renew({
|
|
50800
|
+
projectId: project.id,
|
|
50801
|
+
repoRemoteUrl: source.repoRemoteUrl,
|
|
50802
|
+
baseBranch: source.baseBranch,
|
|
50803
|
+
liveCount: liveWorkspaceCount(project.environment.name, project.id)
|
|
50804
|
+
});
|
|
50805
|
+
if (!renewal.ok) {
|
|
50806
|
+
sendError(ws, renewal.reason, { id: sess.id });
|
|
50807
|
+
return;
|
|
50808
|
+
}
|
|
50809
|
+
restart(sess);
|
|
50810
|
+
}
|
|
50811
|
+
function liveWorkspaceCount(environmentName, excludedProjectId) {
|
|
50954
50812
|
return config.projects.filter((project) => {
|
|
50955
50813
|
if (project.environment?.name !== environmentName) return false;
|
|
50814
|
+
if (project.id === excludedProjectId) return false;
|
|
50956
50815
|
const session = sessions.get(project.id);
|
|
50957
50816
|
return session !== void 0 && session.state !== STATES.DORMANT;
|
|
50958
50817
|
}).length;
|
|
@@ -50972,17 +50831,12 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
50972
50831
|
refuseAddSession(msg, ws, { error: "Coder environments are unavailable on this host" });
|
|
50973
50832
|
return;
|
|
50974
50833
|
}
|
|
50975
|
-
const
|
|
50976
|
-
|
|
50977
|
-
|
|
50978
|
-
refuseAddSession(msg, ws, { error: `${projectPath} has no origin remote, so a Coder workspace cannot clone it.` });
|
|
50979
|
-
return;
|
|
50980
|
-
}
|
|
50981
|
-
const baseBranch = configuredIntegrationBranch(config) ?? await remoteRepoGit.detectDefaultBranch({ projectPath });
|
|
50982
|
-
if (!baseBranch) {
|
|
50983
|
-
refuseAddSession(msg, ws, { error: `Could not find the default branch of ${projectPath} on origin.` });
|
|
50834
|
+
const source = await remoteRepoSource(path.resolve((msg.path || "").trim()));
|
|
50835
|
+
if (!source.ok) {
|
|
50836
|
+
refuseAddSession(msg, ws, { error: source.error });
|
|
50984
50837
|
return;
|
|
50985
50838
|
}
|
|
50839
|
+
const { repoRemoteUrl, baseBranch } = source;
|
|
50986
50840
|
const id = generateProjectId();
|
|
50987
50841
|
const reserved = await coderEnv.reserve({
|
|
50988
50842
|
projectId: id,
|
|
@@ -51041,22 +50895,46 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
51041
50895
|
if (sess.state !== STATES.DORMANT) return;
|
|
51042
50896
|
sess.start();
|
|
51043
50897
|
}
|
|
51044
|
-
function
|
|
50898
|
+
async function removeSession(msg) {
|
|
51045
50899
|
const sess = findSession(msg);
|
|
51046
|
-
if (!sess) {
|
|
51047
|
-
|
|
51048
|
-
|
|
51049
|
-
}
|
|
50900
|
+
if (!sess) return {
|
|
50901
|
+
ok: false,
|
|
50902
|
+
error: "Session not found"
|
|
50903
|
+
};
|
|
51050
50904
|
if (agentSessions.get(sess.id) === sess) {
|
|
51051
50905
|
sess.destroy();
|
|
51052
50906
|
console.log(`[control] Removed spawned session via UI: ${sess.name}`);
|
|
51053
|
-
return;
|
|
50907
|
+
return { ok: true };
|
|
50908
|
+
}
|
|
50909
|
+
if (config.projects.some((project) => project.id === sess.id && project.environment) && coderEnv) {
|
|
50910
|
+
sess.killSession();
|
|
50911
|
+
const settlement = await coderEnv.settleRemoval({
|
|
50912
|
+
projectId: sess.id,
|
|
50913
|
+
discardRemoteWork: msg.discardRemoteWork === true
|
|
50914
|
+
});
|
|
50915
|
+
if (!settlement.ok) return {
|
|
50916
|
+
ok: false,
|
|
50917
|
+
error: settlement.reason,
|
|
50918
|
+
canDiscardRemoteWork: settlement.canDiscardRemoteWork
|
|
50919
|
+
};
|
|
51054
50920
|
}
|
|
51055
50921
|
const freshConfig = configStore.save((cfg) => {
|
|
51056
50922
|
cfg.projects = cfg.projects.filter((p) => p.id !== sess.id);
|
|
51057
50923
|
});
|
|
51058
50924
|
if (freshConfig) applyConfigReload(freshConfig);
|
|
51059
50925
|
console.log(`[control] Removed session via UI: ${sess.name}`);
|
|
50926
|
+
return { ok: true };
|
|
50927
|
+
}
|
|
50928
|
+
async function handleRemoveSession(msg, ws) {
|
|
50929
|
+
const removal = await removeSession(msg);
|
|
50930
|
+
if (msg.requestId) {
|
|
50931
|
+
replyTo(ws, msg, "remove-session-result", removal.ok ? {
|
|
50932
|
+
ok: true,
|
|
50933
|
+
error: null
|
|
50934
|
+
} : removal);
|
|
50935
|
+
return;
|
|
50936
|
+
}
|
|
50937
|
+
if (!removal.ok) sendError(ws, removal.error);
|
|
51060
50938
|
}
|
|
51061
50939
|
function handleRenameSession(msg, ws) {
|
|
51062
50940
|
const sess = findSession(msg);
|
|
@@ -52063,33 +51941,6 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
52063
51941
|
error: res.error || null
|
|
52064
51942
|
});
|
|
52065
51943
|
}
|
|
52066
|
-
function handleSendDiffAnnotations(msg, ws) {
|
|
52067
|
-
const reply = (payload) => replyTo(ws, msg, "send-diff-annotations-result", {
|
|
52068
|
-
ok: false,
|
|
52069
|
-
error: null,
|
|
52070
|
-
...payload
|
|
52071
|
-
});
|
|
52072
|
-
const session = findSession(msg);
|
|
52073
|
-
if (!session) {
|
|
52074
|
-
reply({ error: "Session not found" });
|
|
52075
|
-
return;
|
|
52076
|
-
}
|
|
52077
|
-
const text = formatDiffAnnotationMessage(msg.annotations ?? []);
|
|
52078
|
-
if (!text) {
|
|
52079
|
-
reply({ error: "No review notes to send" });
|
|
52080
|
-
return;
|
|
52081
|
-
}
|
|
52082
|
-
const pasted = session.pasteTextWhenReady(text);
|
|
52083
|
-
if (!pasted.ok) {
|
|
52084
|
-
reply({ error: `Could not write to "${session.name}" (${pasted.reason})` });
|
|
52085
|
-
return;
|
|
52086
|
-
}
|
|
52087
|
-
session.noteOperatorDraft();
|
|
52088
|
-
reply({
|
|
52089
|
-
ok: true,
|
|
52090
|
-
pending: pasted.deferred === true
|
|
52091
|
-
});
|
|
52092
|
-
}
|
|
52093
51944
|
async function handleRequestUsageReport(msg, ws) {
|
|
52094
51945
|
if (!requestUsageReport) {
|
|
52095
51946
|
ws.send(JSON.stringify({
|
|
@@ -52289,20 +52140,17 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
52289
52140
|
"request-hooks-report": handleRequestHooksReport,
|
|
52290
52141
|
"save-hook": handleSaveHook,
|
|
52291
52142
|
"delete-hook": handleDeleteHook,
|
|
52292
|
-
"send-diff-annotations": handleSendDiffAnnotations,
|
|
52293
52143
|
"kill": (msg) => {
|
|
52294
52144
|
const s = findSession(msg);
|
|
52295
52145
|
if (s) s.killSession();
|
|
52296
52146
|
},
|
|
52297
52147
|
"start-session": handleStartSession,
|
|
52298
|
-
"restart": (msg) => {
|
|
52299
|
-
|
|
52300
|
-
|
|
52301
|
-
|
|
52302
|
-
|
|
52303
|
-
|
|
52304
|
-
if (s) s.forceRestart({ fresh: msg.fresh === true });
|
|
52305
|
-
},
|
|
52148
|
+
"restart": (msg, ws) => restartSession(msg, ws, (s) => {
|
|
52149
|
+
s.restart({ fresh: msg.fresh === true });
|
|
52150
|
+
}),
|
|
52151
|
+
"force-restart": (msg, ws) => restartSession(msg, ws, (s) => {
|
|
52152
|
+
s.forceRestart({ fresh: msg.fresh === true });
|
|
52153
|
+
}),
|
|
52306
52154
|
"dismiss": (msg) => {
|
|
52307
52155
|
const session = findSession(msg);
|
|
52308
52156
|
if (!session) return;
|
|
@@ -52360,8 +52208,6 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
52360
52208
|
sig: null,
|
|
52361
52209
|
generatedAt: Date.now(),
|
|
52362
52210
|
repos: [],
|
|
52363
|
-
narrative: null,
|
|
52364
|
-
narratorState: "disabled",
|
|
52365
52211
|
error: `Change map could not be built: ${errorMessage(error)}`
|
|
52366
52212
|
};
|
|
52367
52213
|
});
|
|
@@ -52818,7 +52664,6 @@ function createBackendControl(dependencies) {
|
|
|
52818
52664
|
decidePlanReview: dependencies.decidePlanReview,
|
|
52819
52665
|
sessionDocs: dependencies.sessionDocs,
|
|
52820
52666
|
coherenceFeed: dependencies.coherenceFeed,
|
|
52821
|
-
changeMapNarrator: dependencies.changeMapNarrator,
|
|
52822
52667
|
coderEnv: laneAssembly.coderEnv,
|
|
52823
52668
|
remoteRepoGit: laneAssembly.gitWorkspace,
|
|
52824
52669
|
agentConnect: dependencies.agentConnect
|
|
@@ -56633,7 +56478,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
56633
56478
|
logger: console,
|
|
56634
56479
|
renameSessionFromTaskTitle
|
|
56635
56480
|
});
|
|
56636
|
-
const { allLiveSessions, branchGc, coderActivity,
|
|
56481
|
+
const { allLiveSessions, branchGc, coderActivity, gitWorkspace: assembledGitWorkspace, gitWorkspaceSync, posthog, recordLane, spawnGate, tapIngestForSession, teamReview, myPrs, issues, traceChangeBroadcast, traceWiring, uploadsWiring, usage, visionsSessions, visionsSetup } = laneAssembly;
|
|
56637
56482
|
gitWorkspace = assembledGitWorkspace;
|
|
56638
56483
|
agentApi = createAgentApiWiring({
|
|
56639
56484
|
config,
|
|
@@ -56821,7 +56666,6 @@ function createBackend(httpServer, options = {}) {
|
|
|
56821
56666
|
reviewSessions,
|
|
56822
56667
|
investigationSessions,
|
|
56823
56668
|
visionsSessions,
|
|
56824
|
-
changeMapSessions,
|
|
56825
56669
|
taskTitleSessions: laneAssembly.taskTitleSessions,
|
|
56826
56670
|
benchmarkSessions: laneAssembly.benchmarkSessions,
|
|
56827
56671
|
branchGc,
|
|
@@ -56897,7 +56741,6 @@ function createBackend(httpServer, options = {}) {
|
|
|
56897
56741
|
decidePlanReview: laneAssembly.planReview?.decide ?? null,
|
|
56898
56742
|
sessionDocs,
|
|
56899
56743
|
coherenceFeed,
|
|
56900
|
-
changeMapNarrator: laneAssembly.changeMapNarrator,
|
|
56901
56744
|
serverBuild: () => serverBuild,
|
|
56902
56745
|
telemetry,
|
|
56903
56746
|
logger: console
|