glimmervoid 0.32.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/glimmervoid.js +10 -9
- package/dist/chunks/{adapters-CoC18rdt.js → adapters-BS_7o8fj.js} +3 -3
- package/dist/chunks/{adapters-BU74M4QZ.js → adapters-Bn-dQ02i.js} +1 -1
- package/dist/chunks/{agent-api-cli-V_BkYhBI.js → agent-api-cli-BR0GVDn6.js} +11 -2
- package/dist/chunks/{agent-api-core-B3YI8SYs.js → agent-api-core-DUiWHSHg.js} +10 -1
- package/dist/chunks/{agent-setup-cli-D4956BfN.js → agent-setup-cli-BqYcwfOs.js} +1 -1
- package/dist/chunks/{config-CZ4H51D0.js → config-B6BkTOcE.js} +1 -1
- package/dist/chunks/config-store-C8Ry8ZFD.js +2 -0
- package/dist/chunks/{config-store-BVtdHkDK.js → config-store-aZpWeX92.js} +2 -2
- package/dist/chunks/{contracts-DXujp-Zn.js → contracts-DzDg-Z9I.js} +42 -2
- package/dist/chunks/{grok-C4lzPFEg.js → grok-7Z5KcgcY.js} +1 -1
- package/dist/chunks/{knowledge-graph-cli-Bjy9_kv5.js → knowledge-graph-cli-D5Gf87Y7.js} +1 -1
- package/dist/chunks/{main-DSbpYn2Y.js → main-BG6iHwoY.js} +834 -410
- package/dist/chunks/{pair-cli-CKn-wUw9.js → pair-cli-DQkrt_-s.js} +2 -2
- package/dist/chunks/{remote-config-DP44Ybt9.js → remote-config-Da4C7tMG.js} +1 -1
- package/dist/chunks/{session-B8fJb_Or.js → session-Dtkp9o3r.js} +11 -2
- package/dist/client/assets/index-BxU3Ww0L.css +1 -0
- package/dist/client/assets/index-Co4S7tnh.js +254 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +1 -1
- package/dist/session/sandbox-open-launcher.js +41 -2
- package/package.json +1 -1
- package/dist/chunks/config-store-Cp62jN1O.js +0 -2
- package/dist/client/assets/index-C_XKsDFx.css +0 -1
- package/dist/client/assets/index-CoqWjYxP.js +0 -254
|
@@ -2,7 +2,7 @@ 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 isPathInside, c as sanitizeWorktreeName, n as caseFoldedPathKey, o as isSameDirectoryPath, r as comparableDirectoryPath, s as safePathSegment, t as canonicalizePath } from "./paths-CeJl2Vcg.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-BPoxENmK.js";
|
|
5
|
-
import {
|
|
5
|
+
import { D as MERGEABLE_LIVE_STATES, E as KILLABLE_STATES, O as RESTARTABLE_STATES, S as isSamePromptQuestion, T as toolDetailLine, a as AgentDispatchRequest, b as TASK_TITLE_CONTROL_CHARACTERS, c as AgentTicketRequest, h as SESSION_DOC_BODY_CAP_BYTES, k as STATES, l as ORCHESTRATOR_AGENT_VERBS, m as RefinedTaskTitle, n as AGENT_URL_ENV, r as ASK_USER_QUESTION_TOOL_NAME, x as TaskTitle } from "./session-Dtkp9o3r.js";
|
|
6
6
|
import { n as execFileAsync, o as spawn, r as execFileSync, t as execFile } from "./child-process-safe-DDELk9XC.js";
|
|
7
7
|
import { n as glimmervoidHomeDir } from "./config-path-core-DFt-TemS.js";
|
|
8
8
|
import { n as checkSandboxDependencies, t as allowSandboxedSpawn } from "./sandbox-deps-DuaNFq7n.js";
|
|
@@ -16,15 +16,15 @@ import { i as SANE_YOLO_FILES } from "./rtk-hook-core-BmEz2aq9.js";
|
|
|
16
16
|
import { _ as lineStartOffsets, a as projectForUri, b as replacedSpanOfWholeTextChange, c as visionsLaneReloadSignature, d as applyDidOpen, f as createDocStore, g as lineOfOffset, h as getDoc, i as isUriInProjects, l as applyDidChange, m as formatRange, n as applyEditorNotification, o as resolveVisionsScopeProjects, p as detectBlankLineBoundary, r as createEditorState, s as scopePathsOf, t as ACTIVITY_METHOD, u as applyDidClose, v as listDocs, x as uriOfParams, y as offsetOfPosition } from "./ingest-editor-core-vR6Wv6bP.js";
|
|
17
17
|
import { n as DEFAULT_MAX_MS, r as nextBackoffMs, t as DEFAULT_BASE_MS } from "./backoff-DjVpfHk7.js";
|
|
18
18
|
import { n as unrefTimer, t as DEFAULT_TIMER_FNS } from "./timer-deps-u2DAQLok.js";
|
|
19
|
-
import { a as decideTicketAction, i as decideSpawnAllowance, l as isOrchestratorAgentVerb, n as REFUSAL_REASON, o as deriveChildSessionName, r as decideAgentRequest, s as describePushLanding, t as PUSH_UNAVAILABLE_REASON, u as parseAgentVerb } from "./agent-api-core-
|
|
20
|
-
import { s as GLIMMERVOID_SECRET_KEYS } from "./grok-
|
|
19
|
+
import { a as decideTicketAction, i as decideSpawnAllowance, l as isOrchestratorAgentVerb, n as REFUSAL_REASON, o as deriveChildSessionName, r as decideAgentRequest, s as describePushLanding, t as PUSH_UNAVAILABLE_REASON, u as parseAgentVerb } from "./agent-api-core-DUiWHSHg.js";
|
|
20
|
+
import { s as GLIMMERVOID_SECRET_KEYS } from "./grok-7Z5KcgcY.js";
|
|
21
21
|
import { _ as isQueueAdmissionTimeout, a as jsonStateLoadError, c as loadedJsonValue, d as writeJsonAtomicSync, f as writeTextAtomic, g as createSpawnGate, h as createSerialQueue, i as createJsonStateWriter, m as createKeyedSerialQueue, n as appendJsonLines, o as loadJsonStateFile, p as writeTextAtomicSync, r as createJsonStateStore, s as loadJsonStateFileSync, t as appendJsonLine, u as writeJsonAtomic } from "./json-file-oEH9vt-0.js";
|
|
22
|
-
import { $ as TeamReviewCompareFiles, A as MyPrSearchResponse, At as CoherenceWorkInspect, B as PostingPlan, C as MyPrMergeMethod, Ct as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, D as MyPrMergeWhenReadyRequest, E as MyPrMergeStateResponse, F as CommitSha, G as ReviewDraft, H as QueuedReview, I as DECIDING_REVIEW_STATES, J as ReviewThreadId, K as ReviewFinding, L as FindingSeverity, M as MyPrThreadsResponse, N as MyPrsState, Ot as CoherenceTicketCreated, P as MyPrsStatus, R as GithubReviewDecision, Tt as CoherenceOrient, U as ReviewChecksState, V as PrDetail, W as ReviewComment, X as SearchedPr, Y as ReviewVerdict, Z as TeamReviewActionRequest, _ as WorkflowSearchNode, a as ConfigUpdate, at as TeamReviewThreadsRepository, b as MyPrAutoRebase, bt as USAGE_COST_MODES, c as TASK_TITLE_SOURCE_PRIORITY, ct as hasStandingViewerApproval, d as BRANCH_GC_CONTROL_NUMERIC_KEYS, dt as shortSha$1, et as TeamReviewResolveResponse, ft as GH_SEGMENT, g as WorkflowLabelName, h as WorkflowCommentBody, ht as repoParts, it as TeamReviewThreadResult, j as MyPrThreadNode, k as MyPrSearchNode, kt as CoherenceWorkCreated, l as configIssueMessage, lt as FULL_SHA_RE, m as DEFAULT_FACTORY_PROTECTED_PATHS, mt as REPO_SLUG_RE, nt as TeamReviewStatus, o as PersistedTaskTitle, ot as canApproveAfterComment, p as DEFAULT_FACTORY_CHECKS, pt as NODE_ID_RE, q as ReviewResult, r as BrowserConfig, rt as TeamReviewThreadCommentsResponse, s as RUNTIME_CONFIG_SCALAR_KEYS, st as decisionAsIfApprovalRequired, tt as TeamReviewStateEntry, u as BRANCH_GC_CONTROL_BOOLEAN_KEYS, ut as HEX_SHA_RE, v as WorkflowsSettings, w as MyPrMergeRequest, wt as USAGE_INTEGER_RANGES, x as MyPrKeepMergeableRequest, xt as USAGE_VENDOR_KEYS, y as WorkflowsState, yt as USAGE_BUDGET_KEYS, z as GithubReviewState } from "./config-
|
|
23
|
-
import { $ as sessionIdFromBranch, A as snapshotEvents, B as classifySessionExit, C as createIngestStore, D as resolveIngestConfig, E as publishEvent, F as adapterBucket, G as nodeMajorVersion, H as decideRemoteTelemetryState, I as buildAiGenerationEvents, J as stableConfigKey, K as parsePosthogReleaseId, L as buildBrowserExceptionProperties, M as FIRST_RUN_NOTICE, N as TELEMETRY_BATCH_URL, O as ringStats, P as TELEMETRY_FLAGS_URL, Q as planWorktreeGc, R as buildExceptionProperties, S as buildContextDigest, T as latestSeq, U as decideTelemetryConsent, V as decideIsDevInstall, W as exceptionFingerprint, X as matchedPrefix, Y as DEFAULT_BRANCH_GC_PREFIXES, Z as planBranchGc, _ as INGEST_SPEC, a as ensureProjectIds, b as DEFAULT_DIGEST_BUDGET_CHARS, d as assetForPlatform, et as usablePrefixes, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as createConfigStore, j as localDayKey, k as scrubText, m as installTargetPath, n as DEFAULT_CONFIG, o as generateProjectId, p as findEscapingArchiveMember, q as resolveProjectToken, s as glimmervoidHomeDir$1, t as ABORT_CONFIG_SAVE, tt as worktreeIntegrationTips, v as mergeSettingsBlock, w as enabledSourceNames, x as SOURCE_DEFAULTS, y as validateSettingsBlock, z as buildFactoryDecisionEvent } from "./config-store-
|
|
24
|
-
import { $ as FactoryHarnessVerdict, A as BenchmarkActionRequest, B as PrCheckoutCaseInput, C as IssueKey, D as IssuesStatus, E as IssuesState, F as BenchmarkStatus, G as TelemetryState, H as PendingCrashReport, I as BenchmarkSuite, J as FACTORY_ERROR_MAX_CHARS, K as FACTORY_DECISION_OUTPUT_MAX_BYTES, L as CommitComparison, M as BenchmarkId, N as BenchmarkJudgement, O as ChangeMap, P as BenchmarkRun, Q as FactoryHarnessStateLine, R as MergedPrListing, S as GithubIssueResponse, T as IssuesSearchResponse, U as TELEMETRY_EVENT_SCHEMAS, X as FactoryConsequences, Y as FACTORY_PROTECTED_PATH_FLOOR, Z as FactoryControlRequest, _ as PlanDecision, a as ClientMessage, at as FactoryQueueIntentRequest, b as planTitle, c as UpdateJournal, ct as FactoryWorkerEvent, d as TraceRecord, et as FactoryHogQLResponse, f as PLAN_BODY_CAP_BYTES, g as PLAN_TOOL_NAME, h as PLAN_RESULT_HOOK_EVENT, i as CONTROL_FRAME_MAX_BYTES, it as FactoryProjectState, j as BenchmarkCase, k as changeMapFactId, l as MAX_RAW_LINE_CHARS, lt as isSingleFactoryPathSegment, m as PLAN_HOOK_OUTPUT_MAX_CHARS, n as HookEnvelope, nt as FactoryIssueQueryRow, o as DIFF_ANNOTATION_NOTE_MAX_CHARS, ot as FactoryReviewVerdict, p as PLAN_HOOK_EVENT, q as FactoryDecisionLogLine, r as parseDataClientMessage, rt as FactoryLaneState, s as InstallFlavor, st as FactoryReviewerOutput, t as HTTP_URL_RE, tt as FactoryIssue, u as TraceCheckpoint, v as PlanRevision, w as IssueUpdate, x as GithubIssuePullRequestsRepository, y as parseExitPlanModeHookPayload, z as MinedPrReviewData } from "./contracts-
|
|
25
|
-
import { a as describeAgentResolvability, c as isKnownAgentId, d as setCustomAgents, f as claudeCode, i as customAgentFingerprint, l as listAgentIds, n as cachedAgentResolvability, o as getAdapter, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resolveAdapter } from "./adapters-
|
|
22
|
+
import { $ as TeamReviewCompareFiles, A as MyPrSearchResponse, At as CoherenceWorkInspect, B as PostingPlan, C as MyPrMergeMethod, Ct as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, D as MyPrMergeWhenReadyRequest, E as MyPrMergeStateResponse, F as CommitSha, G as ReviewDraft, H as QueuedReview, I as DECIDING_REVIEW_STATES, J as ReviewThreadId, K as ReviewFinding, L as FindingSeverity, M as MyPrThreadsResponse, N as MyPrsState, Ot as CoherenceTicketCreated, P as MyPrsStatus, R as GithubReviewDecision, Tt as CoherenceOrient, U as ReviewChecksState, V as PrDetail, W as ReviewComment, X as SearchedPr, Y as ReviewVerdict, Z as TeamReviewActionRequest, _ as WorkflowSearchNode, a as ConfigUpdate, at as TeamReviewThreadsRepository, b as MyPrAutoRebase, bt as USAGE_COST_MODES, c as TASK_TITLE_SOURCE_PRIORITY, ct as hasStandingViewerApproval, d as BRANCH_GC_CONTROL_NUMERIC_KEYS, dt as shortSha$1, et as TeamReviewResolveResponse, ft as GH_SEGMENT, g as WorkflowLabelName, h as WorkflowCommentBody, ht as repoParts, it as TeamReviewThreadResult, j as MyPrThreadNode, k as MyPrSearchNode, kt as CoherenceWorkCreated, l as configIssueMessage, lt as FULL_SHA_RE, m as DEFAULT_FACTORY_PROTECTED_PATHS, mt as REPO_SLUG_RE, nt as TeamReviewStatus, o as PersistedTaskTitle, ot as canApproveAfterComment, p as DEFAULT_FACTORY_CHECKS, pt as NODE_ID_RE, q as ReviewResult, r as BrowserConfig, rt as TeamReviewThreadCommentsResponse, s as RUNTIME_CONFIG_SCALAR_KEYS, st as decisionAsIfApprovalRequired, tt as TeamReviewStateEntry, u as BRANCH_GC_CONTROL_BOOLEAN_KEYS, ut as HEX_SHA_RE, v as WorkflowsSettings, w as MyPrMergeRequest, wt as USAGE_INTEGER_RANGES, x as MyPrKeepMergeableRequest, xt as USAGE_VENDOR_KEYS, y as WorkflowsState, yt as USAGE_BUDGET_KEYS, z as GithubReviewState } from "./config-B6BkTOcE.js";
|
|
23
|
+
import { $ as sessionIdFromBranch, A as snapshotEvents, B as classifySessionExit, C as createIngestStore, D as resolveIngestConfig, E as publishEvent, F as adapterBucket, G as nodeMajorVersion, H as decideRemoteTelemetryState, I as buildAiGenerationEvents, J as stableConfigKey, K as parsePosthogReleaseId, L as buildBrowserExceptionProperties, M as FIRST_RUN_NOTICE, N as TELEMETRY_BATCH_URL, O as ringStats, P as TELEMETRY_FLAGS_URL, Q as planWorktreeGc, R as buildExceptionProperties, S as buildContextDigest, T as latestSeq, U as decideTelemetryConsent, V as decideIsDevInstall, W as exceptionFingerprint, X as matchedPrefix, Y as DEFAULT_BRANCH_GC_PREFIXES, Z as planBranchGc, _ as INGEST_SPEC, a as ensureProjectIds, b as DEFAULT_DIGEST_BUDGET_CHARS, d as assetForPlatform, et as usablePrefixes, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as createConfigStore, j as localDayKey, k as scrubText, m as installTargetPath, n as DEFAULT_CONFIG, o as generateProjectId, p as findEscapingArchiveMember, q as resolveProjectToken, s as glimmervoidHomeDir$1, t as ABORT_CONFIG_SAVE, tt as worktreeIntegrationTips, v as mergeSettingsBlock, w as enabledSourceNames, x as SOURCE_DEFAULTS, y as validateSettingsBlock, z as buildFactoryDecisionEvent } from "./config-store-aZpWeX92.js";
|
|
24
|
+
import { $ as FactoryHarnessVerdict, A as BenchmarkActionRequest, B as PrCheckoutCaseInput, C as IssueKey, D as IssuesStatus, E as IssuesState, F as BenchmarkStatus, G as TelemetryState, H as PendingCrashReport, I as BenchmarkSuite, J as FACTORY_ERROR_MAX_CHARS, K as FACTORY_DECISION_OUTPUT_MAX_BYTES, L as CommitComparison, M as BenchmarkId, N as BenchmarkJudgement, O as ChangeMap, P as BenchmarkRun, Q as FactoryHarnessStateLine, R as MergedPrListing, S as GithubIssueResponse, T as IssuesSearchResponse, U as TELEMETRY_EVENT_SCHEMAS, X as FactoryConsequences, Y as FACTORY_PROTECTED_PATH_FLOOR, Z as FactoryControlRequest, _ as PlanDecision, a as ClientMessage, at as FactoryQueueIntentRequest, b as planTitle, c as UpdateJournal, ct as FactoryWorkerEvent, d as TraceRecord, et as FactoryHogQLResponse, f as PLAN_BODY_CAP_BYTES, g as PLAN_TOOL_NAME, h as PLAN_RESULT_HOOK_EVENT, i as CONTROL_FRAME_MAX_BYTES, it as FactoryProjectState, j as BenchmarkCase, k as changeMapFactId, l as MAX_RAW_LINE_CHARS, lt as isSingleFactoryPathSegment, m as PLAN_HOOK_OUTPUT_MAX_CHARS, n as HookEnvelope, nt as FactoryIssueQueryRow, o as DIFF_ANNOTATION_NOTE_MAX_CHARS, ot as FactoryReviewVerdict, p as PLAN_HOOK_EVENT, q as FactoryDecisionLogLine, r as parseDataClientMessage, rt as FactoryLaneState, s as InstallFlavor, st as FactoryReviewerOutput, t as HTTP_URL_RE, tt as FactoryIssue, u as TraceCheckpoint, v as PlanRevision, w as IssueUpdate, x as GithubIssuePullRequestsRepository, y as parseExitPlanModeHookPayload, z as MinedPrReviewData } from "./contracts-DzDg-Z9I.js";
|
|
25
|
+
import { a as describeAgentResolvability, c as isKnownAgentId, d as setCustomAgents, f as claudeCode, i as customAgentFingerprint, l as listAgentIds, n as cachedAgentResolvability, o as getAdapter, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resolveAdapter } from "./adapters-BS_7o8fj.js";
|
|
26
26
|
import { t as HOOK_TOOLS } from "./hook-tools-UX2fmt81.js";
|
|
27
|
-
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-
|
|
27
|
+
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-Da4C7tMG.js";
|
|
28
28
|
import { _ as escapeMarkup, g as createLaneLog, n as createVisionsSetup } from "./visions-setup-CfgNHiG3.js";
|
|
29
29
|
import { t as getRtkPath } from "./rtk-resolver-jJxy_Mrt.js";
|
|
30
30
|
import { createRequire } from "node:module";
|
|
@@ -2019,7 +2019,7 @@ function judgeRole({ vendor, cwd, hookPort, readablePaths, agentSessionId }) {
|
|
|
2019
2019
|
readableWithinDenied: [...readablePaths]
|
|
2020
2020
|
};
|
|
2021
2021
|
}
|
|
2022
|
-
function workRole({ vendor, cwd, checkout, readablePaths, agentSessionId }) {
|
|
2022
|
+
function workRole({ vendor, cwd, checkout, readablePaths, writablePaths = [], agentSessionId }) {
|
|
2023
2023
|
return {
|
|
2024
2024
|
vendors: [vendor],
|
|
2025
2025
|
vendorHome: {
|
|
@@ -2028,7 +2028,7 @@ function workRole({ vendor, cwd, checkout, readablePaths, agentSessionId }) {
|
|
|
2028
2028
|
agentSessionId
|
|
2029
2029
|
},
|
|
2030
2030
|
network: { access: "open" },
|
|
2031
|
-
writableRoots: [],
|
|
2031
|
+
writableRoots: [...writablePaths],
|
|
2032
2032
|
lockedCheckouts: [checkout],
|
|
2033
2033
|
commitCheckouts: [],
|
|
2034
2034
|
readDenied: [],
|
|
@@ -2218,7 +2218,7 @@ function registerEphemeralSession({ map, id, sess, closeSessionDataClients, logP
|
|
|
2218
2218
|
//#region server/agent-api-wiring.ts
|
|
2219
2219
|
var AGENT_LANE_TAG = "agent-spawn";
|
|
2220
2220
|
var SPAWN_FAILED_MESSAGE = "could not spawn the session";
|
|
2221
|
-
function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoardSessions, makeSession, wireSessionEvents, closeSessionDataClients, broadcastControl, spawnGate, recordLane = null, logger = console }) {
|
|
2221
|
+
function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoardSessions, makeSession, wireSessionEvents, closeSessionDataClients, broadcastControl, spawnGate, sessionDocs, recordLane = null, logger = console }) {
|
|
2222
2222
|
async function spawn(parent, request) {
|
|
2223
2223
|
const budget = parent.agentSpawnBudget();
|
|
2224
2224
|
const allowance = decideSpawnAllowance(budget);
|
|
@@ -2364,6 +2364,21 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
2364
2364
|
body: reply
|
|
2365
2365
|
};
|
|
2366
2366
|
}
|
|
2367
|
+
if (parsed.verb === "edit") {
|
|
2368
|
+
const reply = await sessionDocs.surface(session, parsed.request);
|
|
2369
|
+
if (!reply.ok) return {
|
|
2370
|
+
status: 400,
|
|
2371
|
+
body: reply
|
|
2372
|
+
};
|
|
2373
|
+
return {
|
|
2374
|
+
status: 200,
|
|
2375
|
+
body: {
|
|
2376
|
+
ok: true,
|
|
2377
|
+
path: reply.doc.path,
|
|
2378
|
+
name: reply.doc.name
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2367
2382
|
const outcome = await spawn(session, parsed.request);
|
|
2368
2383
|
if (!outcome.ok) return {
|
|
2369
2384
|
status: outcome.status,
|
|
@@ -4965,12 +4980,6 @@ var FINDING_HEADER_SOURCE = String.raw`\*\*(?:\[([^\]\n]+)\]\s+)?(${SEVERITY_ALT
|
|
|
4965
4980
|
function findingHeader$1(reviewer, severity) {
|
|
4966
4981
|
return `**[${reviewer}] ${severity}**`;
|
|
4967
4982
|
}
|
|
4968
|
-
function findingSeveritiesIn(body) {
|
|
4969
|
-
return [...body.matchAll(new RegExp(FINDING_HEADER_SOURCE, "g"))].flatMap((match) => {
|
|
4970
|
-
const severity = FindingSeverity.safeParse(match[2]);
|
|
4971
|
-
return severity.success ? [severity.data] : [];
|
|
4972
|
-
});
|
|
4973
|
-
}
|
|
4974
4983
|
function parseLeadingFindingHeader(body) {
|
|
4975
4984
|
const match = new RegExp(String.raw`^${FINDING_HEADER_SOURCE}\s*(?:\r?\n|$)`).exec(body);
|
|
4976
4985
|
if (!match) return null;
|
|
@@ -7085,10 +7094,49 @@ function isCommentable(comment, commentable) {
|
|
|
7085
7094
|
if (commentable === null) return true;
|
|
7086
7095
|
return isLineCommentable(commentable, comment.path, comment.side, comment.line);
|
|
7087
7096
|
}
|
|
7088
|
-
function
|
|
7097
|
+
function locationKey$1(location) {
|
|
7098
|
+
return `${location.path}:${location.line}:${location.side}`;
|
|
7099
|
+
}
|
|
7100
|
+
function lineKey(location) {
|
|
7101
|
+
return `${location.path}:${location.line}`;
|
|
7102
|
+
}
|
|
7103
|
+
function keepStrongestFinding(strongestByLocation, location, finding) {
|
|
7104
|
+
const strongest = strongestByLocation.get(location);
|
|
7105
|
+
if (strongest && FindingSeverity.options.indexOf(strongest.severity) <= FindingSeverity.options.indexOf(finding.severity)) return;
|
|
7106
|
+
strongestByLocation.set(location, finding);
|
|
7107
|
+
}
|
|
7108
|
+
function strongestFindingByLocation(findings) {
|
|
7109
|
+
const strongestByLocation = /* @__PURE__ */ new Map();
|
|
7110
|
+
for (const finding of findings) {
|
|
7111
|
+
if (finding.line === null) continue;
|
|
7112
|
+
keepStrongestFinding(strongestByLocation, locationKey$1({
|
|
7113
|
+
path: finding.path,
|
|
7114
|
+
line: finding.line,
|
|
7115
|
+
side: finding.side
|
|
7116
|
+
}), finding);
|
|
7117
|
+
keepStrongestFinding(strongestByLocation, lineKey({
|
|
7118
|
+
path: finding.path,
|
|
7119
|
+
line: finding.line
|
|
7120
|
+
}), finding);
|
|
7121
|
+
}
|
|
7122
|
+
return strongestByLocation;
|
|
7123
|
+
}
|
|
7124
|
+
function labeledComment(comment, finding) {
|
|
7125
|
+
if (!finding) return comment;
|
|
7126
|
+
const content = withoutAutomatedNote(comment.body);
|
|
7127
|
+
const agentHeader = parseLeadingFindingHeader(content);
|
|
7128
|
+
const commentText = agentHeader ? content.slice(agentHeader.length).trim() : content;
|
|
7129
|
+
return {
|
|
7130
|
+
...comment,
|
|
7131
|
+
body: `${findingHeader(finding)}\n\n${commentText}`
|
|
7132
|
+
};
|
|
7133
|
+
}
|
|
7134
|
+
function renderPostingPlan(plan, findings, commentable) {
|
|
7089
7135
|
const comments = [];
|
|
7090
7136
|
const foldedSections = [];
|
|
7091
|
-
|
|
7137
|
+
const findingAtLocation = strongestFindingByLocation(findings);
|
|
7138
|
+
for (const plannedComment of plan.comments) {
|
|
7139
|
+
const comment = labeledComment(plannedComment, findingAtLocation.get(locationKey$1(plannedComment)) ?? findingAtLocation.get(lineKey(plannedComment)));
|
|
7092
7140
|
if (isCommentable(comment, commentable)) {
|
|
7093
7141
|
comments.push({
|
|
7094
7142
|
...comment,
|
|
@@ -7104,23 +7152,7 @@ function renderPostingPlan(plan, commentable) {
|
|
|
7104
7152
|
};
|
|
7105
7153
|
}
|
|
7106
7154
|
function readyDraft({ candidate, tier, reasons, result, reviewedBase, commentable = null, posting = null }) {
|
|
7107
|
-
const rendered = posting ? renderPostingPlan(posting, commentable) : renderReview(result, commentable);
|
|
7108
|
-
const severityByLocation = /* @__PURE__ */ new Map();
|
|
7109
|
-
for (const finding of result.findings) {
|
|
7110
|
-
if (finding.line === null) continue;
|
|
7111
|
-
const location = `${finding.path}:${finding.line}:${finding.side}`;
|
|
7112
|
-
const previousSeverity = severityByLocation.get(location);
|
|
7113
|
-
if (previousSeverity && FindingSeverity.options.indexOf(previousSeverity) <= FindingSeverity.options.indexOf(finding.severity)) continue;
|
|
7114
|
-
severityByLocation.set(location, finding.severity);
|
|
7115
|
-
}
|
|
7116
|
-
const comments = rendered.comments.map((comment) => {
|
|
7117
|
-
if (findingSeveritiesIn(comment.body).length > 0) return comment;
|
|
7118
|
-
const severity = severityByLocation.get(`${comment.path}:${comment.line}:${comment.side}`);
|
|
7119
|
-
return severity ? {
|
|
7120
|
-
...comment,
|
|
7121
|
-
severity
|
|
7122
|
-
} : comment;
|
|
7123
|
-
});
|
|
7155
|
+
const rendered = posting ? renderPostingPlan(posting, result.findings, commentable) : renderReview(result, commentable);
|
|
7124
7156
|
return {
|
|
7125
7157
|
...draftBase(candidate, tier, reasons, result.head),
|
|
7126
7158
|
...reviewedBase ? { reviewedBase } : {},
|
|
@@ -7128,7 +7160,7 @@ function readyDraft({ candidate, tier, reasons, result, reviewedBase, commentabl
|
|
|
7128
7160
|
summary: result.summary,
|
|
7129
7161
|
...result.assessment ? { assessment: result.assessment } : {},
|
|
7130
7162
|
body: rendered.body,
|
|
7131
|
-
comments,
|
|
7163
|
+
comments: rendered.comments,
|
|
7132
7164
|
status: "ready"
|
|
7133
7165
|
};
|
|
7134
7166
|
}
|
|
@@ -7295,10 +7327,13 @@ function buildReviewPrompt({ candidate, detail, tier, reasons, checkoutPath, rep
|
|
|
7295
7327
|
`- commit_id must be exactly ${head}.`,
|
|
7296
7328
|
"- comments holds one entry per finding anchored to a line of the diff; each path is non-empty, each line is a",
|
|
7297
7329
|
` positive integer on that side of the diff, side is one of ${sides}, and each body is non-empty.`,
|
|
7298
|
-
"- body holds only the findings that cannot anchor to a diff line, one short bullet each
|
|
7299
|
-
"
|
|
7300
|
-
"
|
|
7301
|
-
"
|
|
7330
|
+
"- body holds only the findings that cannot anchor to a diff line, one short bullet each, in the form",
|
|
7331
|
+
" - **[<reviewer>] <severity>** `path:line`: the problem and the fix",
|
|
7332
|
+
" with the reviewer and severity that finding has in the report.",
|
|
7333
|
+
" Nothing else: no summary, no recap of the change, no praise, no verdict or its reasoning, no follow-up preamble,",
|
|
7334
|
+
" no list of what was checked or could not be run. With every finding inline, body is the empty string.",
|
|
7335
|
+
"- Keep each comment to the problem and the fix, in a few sentences, with no label or heading. Glimmervoid marks the",
|
|
7336
|
+
" review as automated and labels each comment with its finding's reviewer and severity itself.",
|
|
7302
7337
|
"If this file is missing or invalid, Glimmervoid renders the draft from the report findings instead.",
|
|
7303
7338
|
"",
|
|
7304
7339
|
"Untrusted data:",
|
|
@@ -11847,9 +11882,358 @@ function createSessionOutput(options) {
|
|
|
11847
11882
|
};
|
|
11848
11883
|
}
|
|
11849
11884
|
//#endregion
|
|
11885
|
+
//#region server/core/ingest-shell-core.ts
|
|
11886
|
+
var SOURCE$4 = "shellHistory";
|
|
11887
|
+
var SHELLS = Object.freeze([
|
|
11888
|
+
"powershell",
|
|
11889
|
+
"fish",
|
|
11890
|
+
"bash",
|
|
11891
|
+
"zsh"
|
|
11892
|
+
]);
|
|
11893
|
+
var ZERO_SETUP_SHELLS = Object.freeze(["powershell", "fish"]);
|
|
11894
|
+
var ZERO_SETUP_SHELLS_ON_MACOS = Object.freeze(["zsh", "fish"]);
|
|
11895
|
+
var PSREADLINE_SUFFIX = "_history.txt";
|
|
11896
|
+
var MAX_COMMAND_CHARS = 4e3;
|
|
11897
|
+
var TRIVIAL_COMMANDS = /* @__PURE__ */ new Set([
|
|
11898
|
+
"",
|
|
11899
|
+
"l",
|
|
11900
|
+
"ls",
|
|
11901
|
+
"ll",
|
|
11902
|
+
"la",
|
|
11903
|
+
"dir",
|
|
11904
|
+
"cd",
|
|
11905
|
+
"cd ..",
|
|
11906
|
+
"cd -",
|
|
11907
|
+
"cd ~",
|
|
11908
|
+
"cd /",
|
|
11909
|
+
"pwd",
|
|
11910
|
+
"cls",
|
|
11911
|
+
"clear",
|
|
11912
|
+
"exit",
|
|
11913
|
+
"q",
|
|
11914
|
+
"history"
|
|
11915
|
+
]);
|
|
11916
|
+
function defaultShellsFor(platform) {
|
|
11917
|
+
return platform === "darwin" ? ZERO_SETUP_SHELLS_ON_MACOS : ZERO_SETUP_SHELLS;
|
|
11918
|
+
}
|
|
11919
|
+
function normalizeShells(raw, platform) {
|
|
11920
|
+
if (!Array.isArray(raw) || raw.length === 0) return {
|
|
11921
|
+
shells: [...defaultShellsFor(platform)],
|
|
11922
|
+
rejected: []
|
|
11923
|
+
};
|
|
11924
|
+
const shells = [];
|
|
11925
|
+
const rejected = [];
|
|
11926
|
+
for (const entry of raw) {
|
|
11927
|
+
const name = typeof entry === "string" ? entry.trim().toLowerCase() : "";
|
|
11928
|
+
const shell = name === "pwsh" || name === "powershell.exe" ? "powershell" : name;
|
|
11929
|
+
if (SHELLS.includes(shell)) {
|
|
11930
|
+
if (!shells.includes(shell)) shells.push(shell);
|
|
11931
|
+
continue;
|
|
11932
|
+
}
|
|
11933
|
+
const label = typeof entry === "string" ? entry.trim() : String(entry);
|
|
11934
|
+
if (rejected.includes(label)) continue;
|
|
11935
|
+
rejected.push(label);
|
|
11936
|
+
}
|
|
11937
|
+
return {
|
|
11938
|
+
shells,
|
|
11939
|
+
rejected
|
|
11940
|
+
};
|
|
11941
|
+
}
|
|
11942
|
+
function resolveHomeDir(env, homeDir) {
|
|
11943
|
+
return rawTextOr(env?.HOME, null) || rawTextOr(env?.USERPROFILE, null) || homeDir;
|
|
11944
|
+
}
|
|
11945
|
+
function dataHome(env, homeDir) {
|
|
11946
|
+
return rawTextOr(env?.XDG_DATA_HOME, null) || path.join(resolveHomeDir(env, homeDir), ".local", "share");
|
|
11947
|
+
}
|
|
11948
|
+
function configHome(env, homeDir) {
|
|
11949
|
+
return rawTextOr(env?.XDG_CONFIG_HOME, null) || path.join(resolveHomeDir(env, homeDir), ".config");
|
|
11950
|
+
}
|
|
11951
|
+
function powershellLocations(env, platform, homeDir) {
|
|
11952
|
+
if (platform === "win32") {
|
|
11953
|
+
const appData = rawTextOr(env?.APPDATA, null) || path.join(resolveHomeDir(env, homeDir), "AppData", "Roaming");
|
|
11954
|
+
return [{
|
|
11955
|
+
shell: "powershell",
|
|
11956
|
+
dir: path.join(appData, "Microsoft", "Windows", "PowerShell", "PSReadLine"),
|
|
11957
|
+
suffix: PSREADLINE_SUFFIX,
|
|
11958
|
+
name: null
|
|
11959
|
+
}];
|
|
11960
|
+
}
|
|
11961
|
+
return [{
|
|
11962
|
+
shell: "powershell",
|
|
11963
|
+
dir: path.join(dataHome(env, homeDir), "powershell", "PSReadLine"),
|
|
11964
|
+
suffix: PSREADLINE_SUFFIX,
|
|
11965
|
+
name: null
|
|
11966
|
+
}];
|
|
11967
|
+
}
|
|
11968
|
+
function fishLocations(env, homeDir) {
|
|
11969
|
+
return [{
|
|
11970
|
+
shell: "fish",
|
|
11971
|
+
dir: path.join(dataHome(env, homeDir), "fish"),
|
|
11972
|
+
suffix: null,
|
|
11973
|
+
name: "fish_history"
|
|
11974
|
+
}, {
|
|
11975
|
+
shell: "fish",
|
|
11976
|
+
dir: path.join(configHome(env, homeDir), "fish"),
|
|
11977
|
+
suffix: null,
|
|
11978
|
+
name: "fish_history"
|
|
11979
|
+
}];
|
|
11980
|
+
}
|
|
11981
|
+
function fileLocation(shell, filePath) {
|
|
11982
|
+
return {
|
|
11983
|
+
shell,
|
|
11984
|
+
dir: path.dirname(filePath),
|
|
11985
|
+
suffix: null,
|
|
11986
|
+
name: path.basename(filePath)
|
|
11987
|
+
};
|
|
11988
|
+
}
|
|
11989
|
+
function histFileLocation(wanted, env) {
|
|
11990
|
+
const posixShells = wanted.filter((shell) => shell === "bash" || shell === "zsh");
|
|
11991
|
+
if (posixShells.length !== 1) return [];
|
|
11992
|
+
const histFile = rawTextOr(env?.HISTFILE, null);
|
|
11993
|
+
if (!histFile) return [];
|
|
11994
|
+
return [fileLocation(posixShells[0], histFile)];
|
|
11995
|
+
}
|
|
11996
|
+
function defaultHistFiles(shell, env, homeDir) {
|
|
11997
|
+
const home = resolveHomeDir(env, homeDir);
|
|
11998
|
+
if (shell === "bash") return [fileLocation("bash", path.join(home, ".bash_history"))];
|
|
11999
|
+
return [fileLocation("zsh", path.join(home, ".zsh_history")), fileLocation("zsh", path.join(home, ".histfile"))];
|
|
12000
|
+
}
|
|
12001
|
+
function locationKey(location) {
|
|
12002
|
+
return `${path.resolve(location.dir)}|${location.name || location.suffix}`;
|
|
12003
|
+
}
|
|
12004
|
+
function historyLocations({ shells = null, env = {}, platform = process.platform, homeDir = null } = {}) {
|
|
12005
|
+
const wanted = normalizeShells(shells, platform).shells;
|
|
12006
|
+
const locations = [...histFileLocation(wanted, env)];
|
|
12007
|
+
for (const shell of wanted) {
|
|
12008
|
+
if (shell === "powershell") locations.push(...powershellLocations(env, platform, homeDir));
|
|
12009
|
+
if (shell === "fish") locations.push(...fishLocations(env, homeDir));
|
|
12010
|
+
if (shell === "bash" || shell === "zsh") locations.push(...defaultHistFiles(shell, env, homeDir));
|
|
12011
|
+
}
|
|
12012
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12013
|
+
const deduped = [];
|
|
12014
|
+
for (const location of locations) {
|
|
12015
|
+
const key = locationKey(location);
|
|
12016
|
+
if (seen.has(key)) continue;
|
|
12017
|
+
seen.add(key);
|
|
12018
|
+
deduped.push(location);
|
|
12019
|
+
}
|
|
12020
|
+
return deduped;
|
|
12021
|
+
}
|
|
12022
|
+
function matchesLocation(location, filename) {
|
|
12023
|
+
if (typeof filename !== "string" || !filename) return false;
|
|
12024
|
+
if (location?.name) return filename === location.name;
|
|
12025
|
+
const suffix = location?.suffix;
|
|
12026
|
+
if (!suffix) return false;
|
|
12027
|
+
if (filename.length <= suffix.length) return false;
|
|
12028
|
+
return filename.toLowerCase().endsWith(suffix.toLowerCase());
|
|
12029
|
+
}
|
|
12030
|
+
function createParseState() {
|
|
12031
|
+
return {
|
|
12032
|
+
pending: [],
|
|
12033
|
+
pendingTs: null,
|
|
12034
|
+
entry: null
|
|
12035
|
+
};
|
|
12036
|
+
}
|
|
12037
|
+
function withinContinuationBounds(pending) {
|
|
12038
|
+
if (pending.length >= 64) return false;
|
|
12039
|
+
let chars = 0;
|
|
12040
|
+
for (const line of pending) chars += line.length + 1;
|
|
12041
|
+
return chars < MAX_COMMAND_CHARS;
|
|
12042
|
+
}
|
|
12043
|
+
function parsePowershell(lines, state) {
|
|
12044
|
+
const commands = [];
|
|
12045
|
+
let pending = state.pending;
|
|
12046
|
+
for (const line of lines) {
|
|
12047
|
+
const continued = line.endsWith("`");
|
|
12048
|
+
pending = [...pending, continued ? line.slice(0, -1) : line];
|
|
12049
|
+
if (continued && withinContinuationBounds(pending)) continue;
|
|
12050
|
+
commands.push({
|
|
12051
|
+
text: pending.join("\n"),
|
|
12052
|
+
ts: null
|
|
12053
|
+
});
|
|
12054
|
+
pending = [];
|
|
12055
|
+
}
|
|
12056
|
+
return {
|
|
12057
|
+
commands,
|
|
12058
|
+
state: {
|
|
12059
|
+
...state,
|
|
12060
|
+
pending
|
|
12061
|
+
}
|
|
12062
|
+
};
|
|
12063
|
+
}
|
|
12064
|
+
var FISH_ENTRY = /^- cmd:\s?([\s\S]*)$/;
|
|
12065
|
+
var FISH_WHEN = /^\s+when:\s*(\d+)/;
|
|
12066
|
+
function unescapeFish(text) {
|
|
12067
|
+
let out = "";
|
|
12068
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
12069
|
+
const char = text[index];
|
|
12070
|
+
if (char !== "\\") {
|
|
12071
|
+
out += char;
|
|
12072
|
+
continue;
|
|
12073
|
+
}
|
|
12074
|
+
const next = text[index + 1];
|
|
12075
|
+
if (next === "n") {
|
|
12076
|
+
out += "\n";
|
|
12077
|
+
index += 1;
|
|
12078
|
+
continue;
|
|
12079
|
+
}
|
|
12080
|
+
if (next === "\\") {
|
|
12081
|
+
out += "\\";
|
|
12082
|
+
index += 1;
|
|
12083
|
+
continue;
|
|
12084
|
+
}
|
|
12085
|
+
out += char;
|
|
12086
|
+
}
|
|
12087
|
+
return out;
|
|
12088
|
+
}
|
|
12089
|
+
function parseFish(lines, state) {
|
|
12090
|
+
const commands = [];
|
|
12091
|
+
let entry = state.entry;
|
|
12092
|
+
for (const line of lines) {
|
|
12093
|
+
const started = FISH_ENTRY.exec(line);
|
|
12094
|
+
if (started) {
|
|
12095
|
+
if (entry) commands.push(entry);
|
|
12096
|
+
entry = {
|
|
12097
|
+
text: unescapeFish(started[1]),
|
|
12098
|
+
ts: null
|
|
12099
|
+
};
|
|
12100
|
+
continue;
|
|
12101
|
+
}
|
|
12102
|
+
if (!entry) continue;
|
|
12103
|
+
const when = FISH_WHEN.exec(line);
|
|
12104
|
+
if (!when) continue;
|
|
12105
|
+
commands.push({
|
|
12106
|
+
text: entry.text,
|
|
12107
|
+
ts: Number(when[1]) * 1e3
|
|
12108
|
+
});
|
|
12109
|
+
entry = null;
|
|
12110
|
+
}
|
|
12111
|
+
return {
|
|
12112
|
+
commands,
|
|
12113
|
+
state: {
|
|
12114
|
+
...state,
|
|
12115
|
+
entry
|
|
12116
|
+
}
|
|
12117
|
+
};
|
|
12118
|
+
}
|
|
12119
|
+
var BASH_TIMESTAMP = /^#(\d{9,})\s*$/;
|
|
12120
|
+
function parseBash(lines, state) {
|
|
12121
|
+
const commands = [];
|
|
12122
|
+
let pendingTs = state.pendingTs;
|
|
12123
|
+
for (const line of lines) {
|
|
12124
|
+
const stamped = BASH_TIMESTAMP.exec(line);
|
|
12125
|
+
if (stamped) {
|
|
12126
|
+
pendingTs = Number(stamped[1]) * 1e3;
|
|
12127
|
+
continue;
|
|
12128
|
+
}
|
|
12129
|
+
if (!line.trim()) continue;
|
|
12130
|
+
commands.push({
|
|
12131
|
+
text: line,
|
|
12132
|
+
ts: pendingTs
|
|
12133
|
+
});
|
|
12134
|
+
pendingTs = null;
|
|
12135
|
+
}
|
|
12136
|
+
return {
|
|
12137
|
+
commands,
|
|
12138
|
+
state: {
|
|
12139
|
+
...state,
|
|
12140
|
+
pendingTs
|
|
12141
|
+
}
|
|
12142
|
+
};
|
|
12143
|
+
}
|
|
12144
|
+
var ZSH_EXTENDED = /^:\s(\d+):(\d+);([\s\S]*)$/;
|
|
12145
|
+
function parseZsh(lines, state) {
|
|
12146
|
+
const commands = [];
|
|
12147
|
+
let pending = state.pending;
|
|
12148
|
+
let pendingTs = state.pendingTs;
|
|
12149
|
+
for (const line of lines) {
|
|
12150
|
+
const starting = pending.length === 0;
|
|
12151
|
+
const extended = starting ? ZSH_EXTENDED.exec(line) : null;
|
|
12152
|
+
if (starting) pendingTs = extended ? Number(extended[1]) * 1e3 : null;
|
|
12153
|
+
const text = extended ? extended[3] : line;
|
|
12154
|
+
const continued = text.endsWith("\\");
|
|
12155
|
+
pending = [...pending, continued ? text.slice(0, -1) : text];
|
|
12156
|
+
if (continued && withinContinuationBounds(pending)) continue;
|
|
12157
|
+
const joined = pending.join("\n");
|
|
12158
|
+
pending = [];
|
|
12159
|
+
if (joined.trim()) commands.push({
|
|
12160
|
+
text: joined,
|
|
12161
|
+
ts: pendingTs
|
|
12162
|
+
});
|
|
12163
|
+
pendingTs = null;
|
|
12164
|
+
}
|
|
12165
|
+
return {
|
|
12166
|
+
commands,
|
|
12167
|
+
state: {
|
|
12168
|
+
...state,
|
|
12169
|
+
pending,
|
|
12170
|
+
pendingTs
|
|
12171
|
+
}
|
|
12172
|
+
};
|
|
12173
|
+
}
|
|
12174
|
+
var PARSERS = Object.freeze({
|
|
12175
|
+
powershell: parsePowershell,
|
|
12176
|
+
fish: parseFish,
|
|
12177
|
+
bash: parseBash,
|
|
12178
|
+
zsh: parseZsh
|
|
12179
|
+
});
|
|
12180
|
+
function parseHistoryLines({ shell, lines = [], state = null } = {}) {
|
|
12181
|
+
const current = state || createParseState();
|
|
12182
|
+
const parser = shell ? PARSERS[shell] : void 0;
|
|
12183
|
+
if (!parser || !Array.isArray(lines) || lines.length === 0) return {
|
|
12184
|
+
commands: [],
|
|
12185
|
+
state: current
|
|
12186
|
+
};
|
|
12187
|
+
return parser(lines, current);
|
|
12188
|
+
}
|
|
12189
|
+
function foldForCompare(text) {
|
|
12190
|
+
return String(text == null ? "" : text).replace(/\s+/g, " ").trim();
|
|
12191
|
+
}
|
|
12192
|
+
function isTrivialCommand(text) {
|
|
12193
|
+
if (typeof text !== "string") return true;
|
|
12194
|
+
return TRIVIAL_COMMANDS.has(text.trim().toLowerCase());
|
|
12195
|
+
}
|
|
12196
|
+
function decideCommandEvent({ shell = "", command = null, previous = null, now = 0 } = {}) {
|
|
12197
|
+
const text = typeof command?.text === "string" ? command.text : "";
|
|
12198
|
+
const compared = foldForCompare(text);
|
|
12199
|
+
if (!compared) return {
|
|
12200
|
+
event: null,
|
|
12201
|
+
previous
|
|
12202
|
+
};
|
|
12203
|
+
if (isTrivialCommand(compared)) return {
|
|
12204
|
+
event: null,
|
|
12205
|
+
previous: compared
|
|
12206
|
+
};
|
|
12207
|
+
if (compared === previous) return {
|
|
12208
|
+
event: null,
|
|
12209
|
+
previous: compared
|
|
12210
|
+
};
|
|
12211
|
+
const stamped = Number(command?.ts);
|
|
12212
|
+
const lineCount = text.split("\n").length;
|
|
12213
|
+
return {
|
|
12214
|
+
event: {
|
|
12215
|
+
source: SOURCE$4,
|
|
12216
|
+
kind: "command",
|
|
12217
|
+
ts: Number.isFinite(stamped) && stamped > 0 ? Math.floor(stamped) : now,
|
|
12218
|
+
scope: {
|
|
12219
|
+
root: null,
|
|
12220
|
+
sessionId: null
|
|
12221
|
+
},
|
|
12222
|
+
summary: `${shell}: ${text}`,
|
|
12223
|
+
detail: lineCount > 1 ? {
|
|
12224
|
+
shell,
|
|
12225
|
+
lines: lineCount
|
|
12226
|
+
} : { shell }
|
|
12227
|
+
},
|
|
12228
|
+
previous: compared
|
|
12229
|
+
};
|
|
12230
|
+
}
|
|
12231
|
+
//#endregion
|
|
11850
12232
|
//#region server/hook-tools.ts
|
|
11851
12233
|
var writtenHomes = /* @__PURE__ */ new Set();
|
|
11852
12234
|
var warnedTools = /* @__PURE__ */ new Set();
|
|
12235
|
+
var ensuredDataDirs = /* @__PURE__ */ new Set();
|
|
12236
|
+
var RTK_DATA_FOLDER = "rtk";
|
|
11853
12237
|
function saneYoloHomeDir() {
|
|
11854
12238
|
return path.join(glimmervoidHomeDir(os.homedir(), process.env), "sane-yolo");
|
|
11855
12239
|
}
|
|
@@ -11872,6 +12256,37 @@ function writeSaneYoloPolicy(homeDir) {
|
|
|
11872
12256
|
}
|
|
11873
12257
|
writtenHomes.add(homeDir);
|
|
11874
12258
|
}
|
|
12259
|
+
function rtkDataDir(platform = process.platform, env = process.env, homeDir = os.homedir()) {
|
|
12260
|
+
if (platform === "darwin") return path.join(homeDir, "Library", "Application Support", RTK_DATA_FOLDER);
|
|
12261
|
+
if (platform !== "linux") return null;
|
|
12262
|
+
const dataHomeDir = dataHome(env, homeDir);
|
|
12263
|
+
return path.isAbsolute(dataHomeDir) ? path.join(dataHomeDir, RTK_DATA_FOLDER) : null;
|
|
12264
|
+
}
|
|
12265
|
+
function ensureDataDir(dataDir) {
|
|
12266
|
+
if (dataDir === null || ensuredDataDirs.has(dataDir)) return dataDir;
|
|
12267
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
12268
|
+
ensuredDataDirs.add(dataDir);
|
|
12269
|
+
return dataDir;
|
|
12270
|
+
}
|
|
12271
|
+
function rtkHookTool(binPath, warn) {
|
|
12272
|
+
try {
|
|
12273
|
+
const dataDir = ensureDataDir(rtkDataDir());
|
|
12274
|
+
return dataDir === null ? {
|
|
12275
|
+
id: "rtk",
|
|
12276
|
+
binPath
|
|
12277
|
+
} : {
|
|
12278
|
+
id: "rtk",
|
|
12279
|
+
binPath,
|
|
12280
|
+
dataDir
|
|
12281
|
+
};
|
|
12282
|
+
} catch (error) {
|
|
12283
|
+
warnOnce("rtkDataDir", `[rtk] The rtk data folder could not be created: ${String(error)}. Sandboxed sessions will run rtk without savings tracking.`, warn);
|
|
12284
|
+
return {
|
|
12285
|
+
id: "rtk",
|
|
12286
|
+
binPath
|
|
12287
|
+
};
|
|
12288
|
+
}
|
|
12289
|
+
}
|
|
11875
12290
|
function warnOnce(toolId, message, warn) {
|
|
11876
12291
|
if (warnedTools.has(toolId)) return;
|
|
11877
12292
|
warnedTools.add(toolId);
|
|
@@ -11881,10 +12296,7 @@ function resolveHookTools(config, { skipPermissions, warn = console.warn }) {
|
|
|
11881
12296
|
const hookTools = [];
|
|
11882
12297
|
if (config.rtk) {
|
|
11883
12298
|
const rtkPath = getRtkPath();
|
|
11884
|
-
if (rtkPath) hookTools.push(
|
|
11885
|
-
id: "rtk",
|
|
11886
|
-
binPath: rtkPath
|
|
11887
|
-
});
|
|
12299
|
+
if (rtkPath) hookTools.push(rtkHookTool(rtkPath, warn));
|
|
11888
12300
|
if (!rtkPath) warnOnce("rtk", "[rtk] config.rtk is true, but no rtk binary was found. Sessions will spawn without rtk hooks.", warn);
|
|
11889
12301
|
}
|
|
11890
12302
|
if (config.saneYolo === false || !skipPermissions) return hookTools;
|
|
@@ -11940,6 +12352,10 @@ function createSessionHookLifecycle(options) {
|
|
|
11940
12352
|
...isSaneYoloActive ? [saneYoloHomeDir()] : []
|
|
11941
12353
|
];
|
|
11942
12354
|
}
|
|
12355
|
+
function injectedWritablePaths(isSaneYoloActive) {
|
|
12356
|
+
if (token === null) return [];
|
|
12357
|
+
return injectedHookTools(isSaneYoloActive).flatMap((tool) => tool.dataDir === void 0 ? [] : [tool.dataDir]);
|
|
12358
|
+
}
|
|
11943
12359
|
function cleanup() {
|
|
11944
12360
|
if (!token && !settingsHandle) return;
|
|
11945
12361
|
if (options.hookRouter) try {
|
|
@@ -12140,7 +12556,8 @@ function createSessionHookLifecycle(options) {
|
|
|
12140
12556
|
hasInjection: () => token !== null,
|
|
12141
12557
|
hasSettings: () => settingsHandle !== null,
|
|
12142
12558
|
isRequiredSandboxMissing: () => options.settingsSandbox !== null && settingsHandle === null,
|
|
12143
|
-
injectedReadablePaths
|
|
12559
|
+
injectedReadablePaths,
|
|
12560
|
+
injectedWritablePaths
|
|
12144
12561
|
};
|
|
12145
12562
|
}
|
|
12146
12563
|
//#endregion
|
|
@@ -19618,13 +20035,14 @@ var Session = class extends EventEmitter {
|
|
|
19618
20035
|
cwd: this.effectiveCwd(),
|
|
19619
20036
|
hookPort: this._hooks.hasInjection() ? this._hooks.listenerPort() : null,
|
|
19620
20037
|
readablePaths: this._hooks.injectedReadablePaths(this._isSaneYoloActive),
|
|
20038
|
+
writablePaths: this._hooks.injectedWritablePaths(this._isSaneYoloActive),
|
|
19621
20039
|
agentSessionId
|
|
19622
20040
|
});
|
|
19623
20041
|
if (!prepared.ok) throw new Error(prepared.reason);
|
|
19624
20042
|
this._disposeSandboxLaunch(this._sandboxLaunch);
|
|
19625
20043
|
this._sandboxLaunch = prepared.launch;
|
|
19626
20044
|
return {
|
|
19627
|
-
...await prepared.launch.wrap(command.file, command.args),
|
|
20045
|
+
...await prepared.launch.wrap(command.file, command.args, { hasTerminal: true }),
|
|
19628
20046
|
env: prepared.launch.env,
|
|
19629
20047
|
cwd: prepared.launch.cwd
|
|
19630
20048
|
};
|
|
@@ -20395,10 +20813,13 @@ async function prepareSandboxLaunch({ role, launchId, tempDir, enterDir }) {
|
|
|
20395
20813
|
return {
|
|
20396
20814
|
ok: true,
|
|
20397
20815
|
launch: {
|
|
20398
|
-
wrap: async (file, args) => {
|
|
20816
|
+
wrap: async (file, args, { hasTerminal }) => {
|
|
20399
20817
|
const rebuilt = await buildPlanFromCurrentDisk();
|
|
20400
20818
|
if (!rebuilt.ok) throw new Error(rebuilt.reason);
|
|
20401
|
-
await writeJsonAtomic(policyPath,
|
|
20819
|
+
await writeJsonAtomic(policyPath, {
|
|
20820
|
+
...rebuilt.plan.policy,
|
|
20821
|
+
allowPty: hasTerminal && rebuilt.plan.launcher === "open-network"
|
|
20822
|
+
});
|
|
20402
20823
|
placeholderPathsAbsentAtWrap = await absentPaths(sandboxPlaceholderPaths(realRole));
|
|
20403
20824
|
await createMissingMountPoints(lockedClaudeMountPoints(realRole));
|
|
20404
20825
|
return {
|
|
@@ -23821,7 +24242,7 @@ var prepareSandboxedCheckRunner = async ({ scratchParent, writablePaths, readabl
|
|
|
23821
24242
|
const runCheck = async (argv, { timeout, signal, ...execOptions }) => {
|
|
23822
24243
|
const [checkCommand, ...checkArgs] = argv;
|
|
23823
24244
|
if (checkCommand === void 0) throw new Error("Factory check command is empty");
|
|
23824
|
-
const wrapped = await launch.wrap(checkCommand, checkArgs);
|
|
24245
|
+
const wrapped = await launch.wrap(checkCommand, checkArgs, { hasTerminal: false });
|
|
23825
24246
|
const timeoutSignal = AbortSignal.timeout(timeout);
|
|
23826
24247
|
try {
|
|
23827
24248
|
return await execFileAsync(wrapped.file, wrapped.args, {
|
|
@@ -25250,8 +25671,11 @@ async function existingRealPath(candidate) {
|
|
|
25250
25671
|
return null;
|
|
25251
25672
|
}
|
|
25252
25673
|
}
|
|
25253
|
-
async function
|
|
25254
|
-
return (await Promise.all(
|
|
25674
|
+
async function existingRealPaths(candidates) {
|
|
25675
|
+
return (await Promise.all(candidates.filter((candidate) => path.isAbsolute(candidate)).map(existingRealPath))).filter((realPath) => realPath !== null);
|
|
25676
|
+
}
|
|
25677
|
+
function readableToolFolders(homeDir) {
|
|
25678
|
+
return existingRealPaths(LANE_READABLE_HOME_FOLDERS.map((folder) => path.join(homeDir, folder)));
|
|
25255
25679
|
}
|
|
25256
25680
|
async function readLaneCommonGitDir(cwd) {
|
|
25257
25681
|
const { stdout } = await runHardenedGit([
|
|
@@ -25329,7 +25753,8 @@ async function resolveWorkLane(input, { hookToolConfig = {}, resolveSaneYoloHook
|
|
|
25329
25753
|
cwd: facts.cwd,
|
|
25330
25754
|
checkout,
|
|
25331
25755
|
agentSessionId: facts.agentSessionId,
|
|
25332
|
-
readablePaths: [...facts.readablePaths, ...toolFolders]
|
|
25756
|
+
readablePaths: [...facts.readablePaths, ...toolFolders],
|
|
25757
|
+
writablePaths: await existingRealPaths(facts.writablePaths)
|
|
25333
25758
|
}),
|
|
25334
25759
|
launchId: facts.launchId
|
|
25335
25760
|
});
|
|
@@ -29948,7 +30373,7 @@ function pickStaleByMtime(entriesByKey, { maxTracked = 256 } = {}) {
|
|
|
29948
30373
|
}
|
|
29949
30374
|
//#endregion
|
|
29950
30375
|
//#region server/core/ingest-agent-core.ts
|
|
29951
|
-
var SOURCE$
|
|
30376
|
+
var SOURCE$2 = "agentLogs";
|
|
29952
30377
|
var MAX_RAW_CHARS = 4e3;
|
|
29953
30378
|
var TOOL_TARGET_KEYS = Object.freeze([
|
|
29954
30379
|
"file_path",
|
|
@@ -30015,7 +30440,7 @@ function turnEvent({ ts, root, sessionId, vendor, text }) {
|
|
|
30015
30440
|
const summary = boundRaw(text).trim();
|
|
30016
30441
|
if (!summary) return null;
|
|
30017
30442
|
return {
|
|
30018
|
-
source: SOURCE$
|
|
30443
|
+
source: SOURCE$2,
|
|
30019
30444
|
kind: "agent-turn",
|
|
30020
30445
|
ts,
|
|
30021
30446
|
scope: {
|
|
@@ -30030,7 +30455,7 @@ function toolEvent({ ts, root, sessionId, vendor, name, target }) {
|
|
|
30030
30455
|
const tool = boundRaw(textOr(name, null) || "tool");
|
|
30031
30456
|
const suffix = target ? ` ${target}` : "";
|
|
30032
30457
|
return {
|
|
30033
|
-
source: SOURCE$
|
|
30458
|
+
source: SOURCE$2,
|
|
30034
30459
|
kind: "agent-tool",
|
|
30035
30460
|
ts,
|
|
30036
30461
|
scope: {
|
|
@@ -31824,353 +32249,6 @@ function createGitIngest({ publish, sourceConfig = {}, reposProvider = null, log
|
|
|
31824
32249
|
};
|
|
31825
32250
|
}
|
|
31826
32251
|
//#endregion
|
|
31827
|
-
//#region server/core/ingest-shell-core.ts
|
|
31828
|
-
var SOURCE$1 = "shellHistory";
|
|
31829
|
-
var SHELLS = Object.freeze([
|
|
31830
|
-
"powershell",
|
|
31831
|
-
"fish",
|
|
31832
|
-
"bash",
|
|
31833
|
-
"zsh"
|
|
31834
|
-
]);
|
|
31835
|
-
var ZERO_SETUP_SHELLS = Object.freeze(["powershell", "fish"]);
|
|
31836
|
-
var ZERO_SETUP_SHELLS_ON_MACOS = Object.freeze(["zsh", "fish"]);
|
|
31837
|
-
var PSREADLINE_SUFFIX = "_history.txt";
|
|
31838
|
-
var MAX_COMMAND_CHARS = 4e3;
|
|
31839
|
-
var TRIVIAL_COMMANDS = /* @__PURE__ */ new Set([
|
|
31840
|
-
"",
|
|
31841
|
-
"l",
|
|
31842
|
-
"ls",
|
|
31843
|
-
"ll",
|
|
31844
|
-
"la",
|
|
31845
|
-
"dir",
|
|
31846
|
-
"cd",
|
|
31847
|
-
"cd ..",
|
|
31848
|
-
"cd -",
|
|
31849
|
-
"cd ~",
|
|
31850
|
-
"cd /",
|
|
31851
|
-
"pwd",
|
|
31852
|
-
"cls",
|
|
31853
|
-
"clear",
|
|
31854
|
-
"exit",
|
|
31855
|
-
"q",
|
|
31856
|
-
"history"
|
|
31857
|
-
]);
|
|
31858
|
-
function defaultShellsFor(platform) {
|
|
31859
|
-
return platform === "darwin" ? ZERO_SETUP_SHELLS_ON_MACOS : ZERO_SETUP_SHELLS;
|
|
31860
|
-
}
|
|
31861
|
-
function normalizeShells(raw, platform) {
|
|
31862
|
-
if (!Array.isArray(raw) || raw.length === 0) return {
|
|
31863
|
-
shells: [...defaultShellsFor(platform)],
|
|
31864
|
-
rejected: []
|
|
31865
|
-
};
|
|
31866
|
-
const shells = [];
|
|
31867
|
-
const rejected = [];
|
|
31868
|
-
for (const entry of raw) {
|
|
31869
|
-
const name = typeof entry === "string" ? entry.trim().toLowerCase() : "";
|
|
31870
|
-
const shell = name === "pwsh" || name === "powershell.exe" ? "powershell" : name;
|
|
31871
|
-
if (SHELLS.includes(shell)) {
|
|
31872
|
-
if (!shells.includes(shell)) shells.push(shell);
|
|
31873
|
-
continue;
|
|
31874
|
-
}
|
|
31875
|
-
const label = typeof entry === "string" ? entry.trim() : String(entry);
|
|
31876
|
-
if (rejected.includes(label)) continue;
|
|
31877
|
-
rejected.push(label);
|
|
31878
|
-
}
|
|
31879
|
-
return {
|
|
31880
|
-
shells,
|
|
31881
|
-
rejected
|
|
31882
|
-
};
|
|
31883
|
-
}
|
|
31884
|
-
function resolveHomeDir(env, homeDir) {
|
|
31885
|
-
return rawTextOr(env?.HOME, null) || rawTextOr(env?.USERPROFILE, null) || homeDir;
|
|
31886
|
-
}
|
|
31887
|
-
function dataHome(env, homeDir) {
|
|
31888
|
-
return rawTextOr(env?.XDG_DATA_HOME, null) || path.join(resolveHomeDir(env, homeDir), ".local", "share");
|
|
31889
|
-
}
|
|
31890
|
-
function configHome(env, homeDir) {
|
|
31891
|
-
return rawTextOr(env?.XDG_CONFIG_HOME, null) || path.join(resolveHomeDir(env, homeDir), ".config");
|
|
31892
|
-
}
|
|
31893
|
-
function powershellLocations(env, platform, homeDir) {
|
|
31894
|
-
if (platform === "win32") {
|
|
31895
|
-
const appData = rawTextOr(env?.APPDATA, null) || path.join(resolveHomeDir(env, homeDir), "AppData", "Roaming");
|
|
31896
|
-
return [{
|
|
31897
|
-
shell: "powershell",
|
|
31898
|
-
dir: path.join(appData, "Microsoft", "Windows", "PowerShell", "PSReadLine"),
|
|
31899
|
-
suffix: PSREADLINE_SUFFIX,
|
|
31900
|
-
name: null
|
|
31901
|
-
}];
|
|
31902
|
-
}
|
|
31903
|
-
return [{
|
|
31904
|
-
shell: "powershell",
|
|
31905
|
-
dir: path.join(dataHome(env, homeDir), "powershell", "PSReadLine"),
|
|
31906
|
-
suffix: PSREADLINE_SUFFIX,
|
|
31907
|
-
name: null
|
|
31908
|
-
}];
|
|
31909
|
-
}
|
|
31910
|
-
function fishLocations(env, homeDir) {
|
|
31911
|
-
return [{
|
|
31912
|
-
shell: "fish",
|
|
31913
|
-
dir: path.join(dataHome(env, homeDir), "fish"),
|
|
31914
|
-
suffix: null,
|
|
31915
|
-
name: "fish_history"
|
|
31916
|
-
}, {
|
|
31917
|
-
shell: "fish",
|
|
31918
|
-
dir: path.join(configHome(env, homeDir), "fish"),
|
|
31919
|
-
suffix: null,
|
|
31920
|
-
name: "fish_history"
|
|
31921
|
-
}];
|
|
31922
|
-
}
|
|
31923
|
-
function fileLocation(shell, filePath) {
|
|
31924
|
-
return {
|
|
31925
|
-
shell,
|
|
31926
|
-
dir: path.dirname(filePath),
|
|
31927
|
-
suffix: null,
|
|
31928
|
-
name: path.basename(filePath)
|
|
31929
|
-
};
|
|
31930
|
-
}
|
|
31931
|
-
function histFileLocation(wanted, env) {
|
|
31932
|
-
const posixShells = wanted.filter((shell) => shell === "bash" || shell === "zsh");
|
|
31933
|
-
if (posixShells.length !== 1) return [];
|
|
31934
|
-
const histFile = rawTextOr(env?.HISTFILE, null);
|
|
31935
|
-
if (!histFile) return [];
|
|
31936
|
-
return [fileLocation(posixShells[0], histFile)];
|
|
31937
|
-
}
|
|
31938
|
-
function defaultHistFiles(shell, env, homeDir) {
|
|
31939
|
-
const home = resolveHomeDir(env, homeDir);
|
|
31940
|
-
if (shell === "bash") return [fileLocation("bash", path.join(home, ".bash_history"))];
|
|
31941
|
-
return [fileLocation("zsh", path.join(home, ".zsh_history")), fileLocation("zsh", path.join(home, ".histfile"))];
|
|
31942
|
-
}
|
|
31943
|
-
function locationKey(location) {
|
|
31944
|
-
return `${path.resolve(location.dir)}|${location.name || location.suffix}`;
|
|
31945
|
-
}
|
|
31946
|
-
function historyLocations({ shells = null, env = {}, platform = process.platform, homeDir = null } = {}) {
|
|
31947
|
-
const wanted = normalizeShells(shells, platform).shells;
|
|
31948
|
-
const locations = [...histFileLocation(wanted, env)];
|
|
31949
|
-
for (const shell of wanted) {
|
|
31950
|
-
if (shell === "powershell") locations.push(...powershellLocations(env, platform, homeDir));
|
|
31951
|
-
if (shell === "fish") locations.push(...fishLocations(env, homeDir));
|
|
31952
|
-
if (shell === "bash" || shell === "zsh") locations.push(...defaultHistFiles(shell, env, homeDir));
|
|
31953
|
-
}
|
|
31954
|
-
const seen = /* @__PURE__ */ new Set();
|
|
31955
|
-
const deduped = [];
|
|
31956
|
-
for (const location of locations) {
|
|
31957
|
-
const key = locationKey(location);
|
|
31958
|
-
if (seen.has(key)) continue;
|
|
31959
|
-
seen.add(key);
|
|
31960
|
-
deduped.push(location);
|
|
31961
|
-
}
|
|
31962
|
-
return deduped;
|
|
31963
|
-
}
|
|
31964
|
-
function matchesLocation(location, filename) {
|
|
31965
|
-
if (typeof filename !== "string" || !filename) return false;
|
|
31966
|
-
if (location?.name) return filename === location.name;
|
|
31967
|
-
const suffix = location?.suffix;
|
|
31968
|
-
if (!suffix) return false;
|
|
31969
|
-
if (filename.length <= suffix.length) return false;
|
|
31970
|
-
return filename.toLowerCase().endsWith(suffix.toLowerCase());
|
|
31971
|
-
}
|
|
31972
|
-
function createParseState() {
|
|
31973
|
-
return {
|
|
31974
|
-
pending: [],
|
|
31975
|
-
pendingTs: null,
|
|
31976
|
-
entry: null
|
|
31977
|
-
};
|
|
31978
|
-
}
|
|
31979
|
-
function withinContinuationBounds(pending) {
|
|
31980
|
-
if (pending.length >= 64) return false;
|
|
31981
|
-
let chars = 0;
|
|
31982
|
-
for (const line of pending) chars += line.length + 1;
|
|
31983
|
-
return chars < MAX_COMMAND_CHARS;
|
|
31984
|
-
}
|
|
31985
|
-
function parsePowershell(lines, state) {
|
|
31986
|
-
const commands = [];
|
|
31987
|
-
let pending = state.pending;
|
|
31988
|
-
for (const line of lines) {
|
|
31989
|
-
const continued = line.endsWith("`");
|
|
31990
|
-
pending = [...pending, continued ? line.slice(0, -1) : line];
|
|
31991
|
-
if (continued && withinContinuationBounds(pending)) continue;
|
|
31992
|
-
commands.push({
|
|
31993
|
-
text: pending.join("\n"),
|
|
31994
|
-
ts: null
|
|
31995
|
-
});
|
|
31996
|
-
pending = [];
|
|
31997
|
-
}
|
|
31998
|
-
return {
|
|
31999
|
-
commands,
|
|
32000
|
-
state: {
|
|
32001
|
-
...state,
|
|
32002
|
-
pending
|
|
32003
|
-
}
|
|
32004
|
-
};
|
|
32005
|
-
}
|
|
32006
|
-
var FISH_ENTRY = /^- cmd:\s?([\s\S]*)$/;
|
|
32007
|
-
var FISH_WHEN = /^\s+when:\s*(\d+)/;
|
|
32008
|
-
function unescapeFish(text) {
|
|
32009
|
-
let out = "";
|
|
32010
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
32011
|
-
const char = text[index];
|
|
32012
|
-
if (char !== "\\") {
|
|
32013
|
-
out += char;
|
|
32014
|
-
continue;
|
|
32015
|
-
}
|
|
32016
|
-
const next = text[index + 1];
|
|
32017
|
-
if (next === "n") {
|
|
32018
|
-
out += "\n";
|
|
32019
|
-
index += 1;
|
|
32020
|
-
continue;
|
|
32021
|
-
}
|
|
32022
|
-
if (next === "\\") {
|
|
32023
|
-
out += "\\";
|
|
32024
|
-
index += 1;
|
|
32025
|
-
continue;
|
|
32026
|
-
}
|
|
32027
|
-
out += char;
|
|
32028
|
-
}
|
|
32029
|
-
return out;
|
|
32030
|
-
}
|
|
32031
|
-
function parseFish(lines, state) {
|
|
32032
|
-
const commands = [];
|
|
32033
|
-
let entry = state.entry;
|
|
32034
|
-
for (const line of lines) {
|
|
32035
|
-
const started = FISH_ENTRY.exec(line);
|
|
32036
|
-
if (started) {
|
|
32037
|
-
if (entry) commands.push(entry);
|
|
32038
|
-
entry = {
|
|
32039
|
-
text: unescapeFish(started[1]),
|
|
32040
|
-
ts: null
|
|
32041
|
-
};
|
|
32042
|
-
continue;
|
|
32043
|
-
}
|
|
32044
|
-
if (!entry) continue;
|
|
32045
|
-
const when = FISH_WHEN.exec(line);
|
|
32046
|
-
if (!when) continue;
|
|
32047
|
-
commands.push({
|
|
32048
|
-
text: entry.text,
|
|
32049
|
-
ts: Number(when[1]) * 1e3
|
|
32050
|
-
});
|
|
32051
|
-
entry = null;
|
|
32052
|
-
}
|
|
32053
|
-
return {
|
|
32054
|
-
commands,
|
|
32055
|
-
state: {
|
|
32056
|
-
...state,
|
|
32057
|
-
entry
|
|
32058
|
-
}
|
|
32059
|
-
};
|
|
32060
|
-
}
|
|
32061
|
-
var BASH_TIMESTAMP = /^#(\d{9,})\s*$/;
|
|
32062
|
-
function parseBash(lines, state) {
|
|
32063
|
-
const commands = [];
|
|
32064
|
-
let pendingTs = state.pendingTs;
|
|
32065
|
-
for (const line of lines) {
|
|
32066
|
-
const stamped = BASH_TIMESTAMP.exec(line);
|
|
32067
|
-
if (stamped) {
|
|
32068
|
-
pendingTs = Number(stamped[1]) * 1e3;
|
|
32069
|
-
continue;
|
|
32070
|
-
}
|
|
32071
|
-
if (!line.trim()) continue;
|
|
32072
|
-
commands.push({
|
|
32073
|
-
text: line,
|
|
32074
|
-
ts: pendingTs
|
|
32075
|
-
});
|
|
32076
|
-
pendingTs = null;
|
|
32077
|
-
}
|
|
32078
|
-
return {
|
|
32079
|
-
commands,
|
|
32080
|
-
state: {
|
|
32081
|
-
...state,
|
|
32082
|
-
pendingTs
|
|
32083
|
-
}
|
|
32084
|
-
};
|
|
32085
|
-
}
|
|
32086
|
-
var ZSH_EXTENDED = /^:\s(\d+):(\d+);([\s\S]*)$/;
|
|
32087
|
-
function parseZsh(lines, state) {
|
|
32088
|
-
const commands = [];
|
|
32089
|
-
let pending = state.pending;
|
|
32090
|
-
let pendingTs = state.pendingTs;
|
|
32091
|
-
for (const line of lines) {
|
|
32092
|
-
const starting = pending.length === 0;
|
|
32093
|
-
const extended = starting ? ZSH_EXTENDED.exec(line) : null;
|
|
32094
|
-
if (starting) pendingTs = extended ? Number(extended[1]) * 1e3 : null;
|
|
32095
|
-
const text = extended ? extended[3] : line;
|
|
32096
|
-
const continued = text.endsWith("\\");
|
|
32097
|
-
pending = [...pending, continued ? text.slice(0, -1) : text];
|
|
32098
|
-
if (continued && withinContinuationBounds(pending)) continue;
|
|
32099
|
-
const joined = pending.join("\n");
|
|
32100
|
-
pending = [];
|
|
32101
|
-
if (joined.trim()) commands.push({
|
|
32102
|
-
text: joined,
|
|
32103
|
-
ts: pendingTs
|
|
32104
|
-
});
|
|
32105
|
-
pendingTs = null;
|
|
32106
|
-
}
|
|
32107
|
-
return {
|
|
32108
|
-
commands,
|
|
32109
|
-
state: {
|
|
32110
|
-
...state,
|
|
32111
|
-
pending,
|
|
32112
|
-
pendingTs
|
|
32113
|
-
}
|
|
32114
|
-
};
|
|
32115
|
-
}
|
|
32116
|
-
var PARSERS = Object.freeze({
|
|
32117
|
-
powershell: parsePowershell,
|
|
32118
|
-
fish: parseFish,
|
|
32119
|
-
bash: parseBash,
|
|
32120
|
-
zsh: parseZsh
|
|
32121
|
-
});
|
|
32122
|
-
function parseHistoryLines({ shell, lines = [], state = null } = {}) {
|
|
32123
|
-
const current = state || createParseState();
|
|
32124
|
-
const parser = shell ? PARSERS[shell] : void 0;
|
|
32125
|
-
if (!parser || !Array.isArray(lines) || lines.length === 0) return {
|
|
32126
|
-
commands: [],
|
|
32127
|
-
state: current
|
|
32128
|
-
};
|
|
32129
|
-
return parser(lines, current);
|
|
32130
|
-
}
|
|
32131
|
-
function foldForCompare(text) {
|
|
32132
|
-
return String(text == null ? "" : text).replace(/\s+/g, " ").trim();
|
|
32133
|
-
}
|
|
32134
|
-
function isTrivialCommand(text) {
|
|
32135
|
-
if (typeof text !== "string") return true;
|
|
32136
|
-
return TRIVIAL_COMMANDS.has(text.trim().toLowerCase());
|
|
32137
|
-
}
|
|
32138
|
-
function decideCommandEvent({ shell = "", command = null, previous = null, now = 0 } = {}) {
|
|
32139
|
-
const text = typeof command?.text === "string" ? command.text : "";
|
|
32140
|
-
const compared = foldForCompare(text);
|
|
32141
|
-
if (!compared) return {
|
|
32142
|
-
event: null,
|
|
32143
|
-
previous
|
|
32144
|
-
};
|
|
32145
|
-
if (isTrivialCommand(compared)) return {
|
|
32146
|
-
event: null,
|
|
32147
|
-
previous: compared
|
|
32148
|
-
};
|
|
32149
|
-
if (compared === previous) return {
|
|
32150
|
-
event: null,
|
|
32151
|
-
previous: compared
|
|
32152
|
-
};
|
|
32153
|
-
const stamped = Number(command?.ts);
|
|
32154
|
-
const lineCount = text.split("\n").length;
|
|
32155
|
-
return {
|
|
32156
|
-
event: {
|
|
32157
|
-
source: SOURCE$1,
|
|
32158
|
-
kind: "command",
|
|
32159
|
-
ts: Number.isFinite(stamped) && stamped > 0 ? Math.floor(stamped) : now,
|
|
32160
|
-
scope: {
|
|
32161
|
-
root: null,
|
|
32162
|
-
sessionId: null
|
|
32163
|
-
},
|
|
32164
|
-
summary: `${shell}: ${text}`,
|
|
32165
|
-
detail: lineCount > 1 ? {
|
|
32166
|
-
shell,
|
|
32167
|
-
lines: lineCount
|
|
32168
|
-
} : { shell }
|
|
32169
|
-
},
|
|
32170
|
-
previous: compared
|
|
32171
|
-
};
|
|
32172
|
-
}
|
|
32173
|
-
//#endregion
|
|
32174
32252
|
//#region server/ingest-shell-history.ts
|
|
32175
32253
|
var DEFAULT_POLL_MS = 2e3;
|
|
32176
32254
|
var DEFAULT_DISCOVER_MS = 3e4;
|
|
@@ -34117,7 +34195,7 @@ async function containedPathForMissingFile(candidate, realRoot) {
|
|
|
34117
34195
|
};
|
|
34118
34196
|
}
|
|
34119
34197
|
}
|
|
34120
|
-
async function openContainedFile(candidate, root, allowsMissingFile = false) {
|
|
34198
|
+
async function openContainedFile(candidate, root, allowsMissingFile = false, opensForWriting = false) {
|
|
34121
34199
|
let realRoot;
|
|
34122
34200
|
try {
|
|
34123
34201
|
realRoot = await fs.promises.realpath(root);
|
|
@@ -34144,7 +34222,8 @@ async function openContainedFile(candidate, root, allowsMissingFile = false) {
|
|
|
34144
34222
|
};
|
|
34145
34223
|
let handle = null;
|
|
34146
34224
|
try {
|
|
34147
|
-
|
|
34225
|
+
const accessFlag = opensForWriting ? fs.constants.O_RDWR : fs.constants.O_RDONLY;
|
|
34226
|
+
handle = await fs.promises.open(realCandidate, accessFlag | fs.constants.O_NONBLOCK);
|
|
34148
34227
|
const stat = await handle.stat();
|
|
34149
34228
|
if (stat.isFile()) return {
|
|
34150
34229
|
ok: true,
|
|
@@ -47445,6 +47524,22 @@ function requestValidationErrorReply(msg, message) {
|
|
|
47445
47524
|
requestId,
|
|
47446
47525
|
ok: false,
|
|
47447
47526
|
error: message
|
|
47527
|
+
}),
|
|
47528
|
+
"session-doc-read": () => ({
|
|
47529
|
+
type: "session-doc-response",
|
|
47530
|
+
requestId,
|
|
47531
|
+
id: typeof msg?.id === "string" ? msg.id : "",
|
|
47532
|
+
path: typeof msg?.path === "string" ? msg.path : "",
|
|
47533
|
+
ok: false,
|
|
47534
|
+
error: message
|
|
47535
|
+
}),
|
|
47536
|
+
"session-doc-save": () => ({
|
|
47537
|
+
type: "session-doc-save-result",
|
|
47538
|
+
requestId,
|
|
47539
|
+
id: typeof msg?.id === "string" ? msg.id : "",
|
|
47540
|
+
path: typeof msg?.path === "string" ? msg.path : "",
|
|
47541
|
+
ok: false,
|
|
47542
|
+
error: message
|
|
47448
47543
|
})
|
|
47449
47544
|
};
|
|
47450
47545
|
const requestType = typeof msg?.type === "string" ? msg.type : "";
|
|
@@ -47475,7 +47570,7 @@ function parseSinceParam(url) {
|
|
|
47475
47570
|
return Number.isFinite(n) ? n : null;
|
|
47476
47571
|
}
|
|
47477
47572
|
function registerControlHandlers(controlWss, deps) {
|
|
47478
|
-
const { sessions, agentSessions = /* @__PURE__ */ new Map(), config, configStore, broadcastControl, generateProjectId, applyConfigReload, applySettingsReload, requestShutdown, requestRestart, handleClientFocus, buildHealthSnapshot, getUpdateStatus, getUpdateJournal = null, checkNow = null, applyUpdate = null, isStaging = null, noteRestartRequested = null, getPosthogStatus, posthogReportsDir = null, posthogSetIssueStatus = null, posthogArchiveInvestigation = null, getTeamReviewStatus = null, getMyPrsStatus = null, getIssuesStatus = null, issues = null, teamReview = null, myPrs = null, getBenchmarkStatus = null, getFactoryState = null, benchmarks = null, factory = null, createGithubClient = createPrGh, issueProjectRepos = null, serverBuild = () => null, getUsageSessions = null, getUsageReport = null, requestUsageReport = null, getPlanLimits = null, controlReplayLog = null, getRtkInstallStatus = () => null, resolveRtkPath = () => getRtkPath(), conversationFs = fs, conversationGit = runGitForConversationHistory, conversationProjectsDir = claudeProjectsDir(process.env, os.homedir()), readTracePage = null, readPlanRevision = null, decidePlanReview = null, changeMapNarrator = null } = deps;
|
|
47573
|
+
const { sessions, agentSessions = /* @__PURE__ */ new Map(), config, configStore, broadcastControl, generateProjectId, applyConfigReload, applySettingsReload, requestShutdown, requestRestart, handleClientFocus, buildHealthSnapshot, getUpdateStatus, getUpdateJournal = null, checkNow = null, applyUpdate = null, isStaging = null, noteRestartRequested = null, getPosthogStatus, posthogReportsDir = null, posthogSetIssueStatus = null, posthogArchiveInvestigation = null, getTeamReviewStatus = null, getMyPrsStatus = null, getIssuesStatus = null, issues = null, teamReview = null, myPrs = null, getBenchmarkStatus = null, getFactoryState = null, benchmarks = null, factory = null, createGithubClient = createPrGh, issueProjectRepos = null, serverBuild = () => null, getUsageSessions = null, getUsageReport = null, requestUsageReport = null, getPlanLimits = null, controlReplayLog = null, getRtkInstallStatus = () => null, resolveRtkPath = () => getRtkPath(), conversationFs = fs, conversationGit = runGitForConversationHistory, conversationProjectsDir = claudeProjectsDir(process.env, os.homedir()), readTracePage = null, readPlanRevision = null, decidePlanReview = null, sessionDocs = null, changeMapNarrator = null } = deps;
|
|
47479
47574
|
const changeMaps = createChangeMapService({
|
|
47480
47575
|
sessions,
|
|
47481
47576
|
narrator: changeMapNarrator,
|
|
@@ -48801,6 +48896,58 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
48801
48896
|
message: refusal
|
|
48802
48897
|
}));
|
|
48803
48898
|
},
|
|
48899
|
+
"session-doc-read": async (message, ws) => {
|
|
48900
|
+
const reply = {
|
|
48901
|
+
type: "session-doc-response",
|
|
48902
|
+
requestId: message.requestId ?? null,
|
|
48903
|
+
id: message.id,
|
|
48904
|
+
path: message.path
|
|
48905
|
+
};
|
|
48906
|
+
const session = findSession(message);
|
|
48907
|
+
if (!sessionDocs || !session) return ws.send(JSON.stringify({
|
|
48908
|
+
...reply,
|
|
48909
|
+
ok: false,
|
|
48910
|
+
error: "Session not found"
|
|
48911
|
+
}));
|
|
48912
|
+
try {
|
|
48913
|
+
ws.send(JSON.stringify({
|
|
48914
|
+
...reply,
|
|
48915
|
+
...await sessionDocs.read(session, message.path)
|
|
48916
|
+
}));
|
|
48917
|
+
} catch (error) {
|
|
48918
|
+
ws.send(JSON.stringify({
|
|
48919
|
+
...reply,
|
|
48920
|
+
ok: false,
|
|
48921
|
+
error: `Read failed: ${errorMessage(error)}`
|
|
48922
|
+
}));
|
|
48923
|
+
}
|
|
48924
|
+
},
|
|
48925
|
+
"session-doc-save": async (message, ws) => {
|
|
48926
|
+
const reply = {
|
|
48927
|
+
type: "session-doc-save-result",
|
|
48928
|
+
requestId: message.requestId ?? null,
|
|
48929
|
+
id: message.id,
|
|
48930
|
+
path: message.path
|
|
48931
|
+
};
|
|
48932
|
+
const session = findSession(message);
|
|
48933
|
+
if (!sessionDocs || !session) return ws.send(JSON.stringify({
|
|
48934
|
+
...reply,
|
|
48935
|
+
ok: false,
|
|
48936
|
+
error: "Session not found"
|
|
48937
|
+
}));
|
|
48938
|
+
try {
|
|
48939
|
+
ws.send(JSON.stringify({
|
|
48940
|
+
...reply,
|
|
48941
|
+
...await sessionDocs.save(session, message.path, message.text, message.baseHash)
|
|
48942
|
+
}));
|
|
48943
|
+
} catch (error) {
|
|
48944
|
+
ws.send(JSON.stringify({
|
|
48945
|
+
...reply,
|
|
48946
|
+
ok: false,
|
|
48947
|
+
error: `Save failed: ${errorMessage(error)}`
|
|
48948
|
+
}));
|
|
48949
|
+
}
|
|
48950
|
+
},
|
|
48804
48951
|
"shutdown": handleShutdown,
|
|
48805
48952
|
"restart-server": handleRestart,
|
|
48806
48953
|
"update-check": handleUpdateCheck,
|
|
@@ -48950,6 +49097,7 @@ function createBackendControl(dependencies) {
|
|
|
48950
49097
|
readTracePage: dependencies.readTracePage,
|
|
48951
49098
|
readPlanRevision: dependencies.readPlanRevision,
|
|
48952
49099
|
decidePlanReview: dependencies.decidePlanReview,
|
|
49100
|
+
sessionDocs: dependencies.sessionDocs,
|
|
48953
49101
|
changeMapNarrator: dependencies.changeMapNarrator
|
|
48954
49102
|
});
|
|
48955
49103
|
const sendLaneSnapshotOnConnect = (laneName, readLane, refuseRemote) => {
|
|
@@ -48967,6 +49115,7 @@ function createBackendControl(dependencies) {
|
|
|
48967
49115
|
};
|
|
48968
49116
|
sendLaneSnapshotOnConnect("visions", laneAssembly.currentVisions, false);
|
|
48969
49117
|
sendLaneSnapshotOnConnect("ingest", laneAssembly.currentIngest, true);
|
|
49118
|
+
sendLaneSnapshotOnConnect("session-docs", () => dependencies.sessionDocs, false);
|
|
48970
49119
|
}
|
|
48971
49120
|
//#endregion
|
|
48972
49121
|
//#region server/core/update-apply-core.ts
|
|
@@ -50706,9 +50855,6 @@ async function sharedFoldersOf({ checkout, gitDir }, worktreeShare) {
|
|
|
50706
50855
|
const sharedEntries = worktreeShare.filter((entry) => !UNSHARED_WORKTREE_ENTRIES.includes(entry));
|
|
50707
50856
|
return (await Promise.all(sharedEntries.map((entry) => sharedFolderBehind(path.join(checkout, entry), path.resolve(path.join(repositoryRoot, entry)))))).filter((sharedFolder) => sharedFolder !== null);
|
|
50708
50857
|
}
|
|
50709
|
-
async function existingRealPaths(candidates) {
|
|
50710
|
-
return (await Promise.all(candidates.filter((candidate) => path.isAbsolute(candidate)).map(existingRealPath))).filter((realPath) => realPath !== null);
|
|
50711
|
-
}
|
|
50712
50858
|
function createSessionSandbox({ getSettings, uploadsRoot, sandboxRefusal = allowSandboxedSpawn, prepareSandbox = prepareSandboxLaunch, trustClaudeFolder, platform = process.platform, homeDir = os.homedir(), env = process.env, readGithubToken = () => readGithubCliToken(env), log = console }) {
|
|
50713
50859
|
const expandHome = (candidates) => candidates.map((candidate) => expandTilde(candidate.trim(), env, homeDir)).filter(Boolean);
|
|
50714
50860
|
const githubCliConfigDir = () => rawTextOr(env.GH_CONFIG_DIR, null) ?? path.join(rawTextOr(env.XDG_CONFIG_HOME, null) ?? path.join(homeDir, ".config"), "gh");
|
|
@@ -50728,7 +50874,7 @@ function createSessionSandbox({ getSettings, uploadsRoot, sandboxRefusal = allow
|
|
|
50728
50874
|
writableRoots: [
|
|
50729
50875
|
...startFolderRoots,
|
|
50730
50876
|
...sharedFolders,
|
|
50731
|
-
...await existingRealPaths(expandHome(settings.writablePaths))
|
|
50877
|
+
...await existingRealPaths([...expandHome(settings.writablePaths), ...facts.writablePaths])
|
|
50732
50878
|
],
|
|
50733
50879
|
readDenied: expandHome(settings.unreadablePaths).filter((unreadablePath) => path.isAbsolute(unreadablePath)),
|
|
50734
50880
|
readablePaths: [...facts.readablePaths, ...await existingRealPaths([sessionUploads, githubCliConfigDir()])]
|
|
@@ -51811,6 +51957,281 @@ function createTelemetry(options) {
|
|
|
51811
51957
|
};
|
|
51812
51958
|
}
|
|
51813
51959
|
//#endregion
|
|
51960
|
+
//#region server/core/session-docs-core.ts
|
|
51961
|
+
var NUL_CHARACTER = String.fromCharCode(0);
|
|
51962
|
+
var DOC_REFUSAL_COPY = Object.freeze({
|
|
51963
|
+
missing: "no file exists at that path, so write it first and surface it again",
|
|
51964
|
+
unreadable: "the file cannot be read",
|
|
51965
|
+
"outside-root": "the path is outside this session's checkout",
|
|
51966
|
+
"not-a-regular-file": "the path is not a regular file",
|
|
51967
|
+
"root-unresolvable": "this session's checkout cannot be resolved",
|
|
51968
|
+
"too-large": `the file is over ${SESSION_DOC_BODY_CAP_BYTES / 1024} KiB`,
|
|
51969
|
+
binary: "the file is not UTF-8 text",
|
|
51970
|
+
"not-surfaced": "that file was not surfaced by this session",
|
|
51971
|
+
"changed-on-disk": "the file changed on disk since it was opened, so reload it before saving",
|
|
51972
|
+
"hard-linked": "the file has other hard links, so it may live outside this session's checkout"
|
|
51973
|
+
});
|
|
51974
|
+
function describeDocRefusal(reason) {
|
|
51975
|
+
return DOC_REFUSAL_COPY[reason];
|
|
51976
|
+
}
|
|
51977
|
+
function hashDocBytes(bytes) {
|
|
51978
|
+
return crypto.createHash("sha256").update(bytes).digest("hex");
|
|
51979
|
+
}
|
|
51980
|
+
function countOccurrences(text, needle) {
|
|
51981
|
+
return text.split(needle).length - 1;
|
|
51982
|
+
}
|
|
51983
|
+
function decodeDocBytes(bytes) {
|
|
51984
|
+
if (bytes.byteLength > 262144) return {
|
|
51985
|
+
ok: false,
|
|
51986
|
+
reason: "too-large"
|
|
51987
|
+
};
|
|
51988
|
+
let text;
|
|
51989
|
+
try {
|
|
51990
|
+
text = new TextDecoder("utf-8", {
|
|
51991
|
+
fatal: true,
|
|
51992
|
+
ignoreBOM: true
|
|
51993
|
+
}).decode(bytes);
|
|
51994
|
+
} catch {
|
|
51995
|
+
return {
|
|
51996
|
+
ok: false,
|
|
51997
|
+
reason: "binary"
|
|
51998
|
+
};
|
|
51999
|
+
}
|
|
52000
|
+
if (text.includes(NUL_CHARACTER)) return {
|
|
52001
|
+
ok: false,
|
|
52002
|
+
reason: "binary"
|
|
52003
|
+
};
|
|
52004
|
+
const crlfCount = countOccurrences(text, "\r\n");
|
|
52005
|
+
const usesCrlf = crlfCount > 0 && crlfCount === countOccurrences(text, "\n");
|
|
52006
|
+
if (!usesCrlf) return {
|
|
52007
|
+
ok: true,
|
|
52008
|
+
text,
|
|
52009
|
+
usesCrlf
|
|
52010
|
+
};
|
|
52011
|
+
return {
|
|
52012
|
+
ok: true,
|
|
52013
|
+
text: text.replaceAll("\r\n", "\n"),
|
|
52014
|
+
usesCrlf
|
|
52015
|
+
};
|
|
52016
|
+
}
|
|
52017
|
+
function encodeDocText(text, usesCrlf) {
|
|
52018
|
+
if (text.includes(NUL_CHARACTER)) return {
|
|
52019
|
+
ok: false,
|
|
52020
|
+
reason: "binary"
|
|
52021
|
+
};
|
|
52022
|
+
const lineFeedText = text.replaceAll("\r\n", "\n");
|
|
52023
|
+
const content = usesCrlf ? lineFeedText.replaceAll("\n", "\r\n") : lineFeedText;
|
|
52024
|
+
if (Buffer.byteLength(content, "utf8") > 262144) return {
|
|
52025
|
+
ok: false,
|
|
52026
|
+
reason: "too-large"
|
|
52027
|
+
};
|
|
52028
|
+
return {
|
|
52029
|
+
ok: true,
|
|
52030
|
+
content
|
|
52031
|
+
};
|
|
52032
|
+
}
|
|
52033
|
+
function withSurfacedDoc(docs, surfaced, maxDocs) {
|
|
52034
|
+
return [surfaced, ...docs.filter((doc) => doc.path !== surfaced.path)].slice(0, maxDocs);
|
|
52035
|
+
}
|
|
52036
|
+
//#endregion
|
|
52037
|
+
//#region server/session-docs-wiring.ts
|
|
52038
|
+
async function readAtMostCapPlusOneByte(handle) {
|
|
52039
|
+
const buffer = Buffer.alloc(SESSION_DOC_BODY_CAP_BYTES + 1);
|
|
52040
|
+
let filledBytes = 0;
|
|
52041
|
+
while (filledBytes < buffer.byteLength) {
|
|
52042
|
+
const { bytesRead } = await handle.read(buffer, filledBytes, buffer.byteLength - filledBytes, filledBytes);
|
|
52043
|
+
if (bytesRead === 0) break;
|
|
52044
|
+
filledBytes += bytesRead;
|
|
52045
|
+
}
|
|
52046
|
+
return buffer.subarray(0, filledBytes);
|
|
52047
|
+
}
|
|
52048
|
+
async function readOpenedDoc(file) {
|
|
52049
|
+
if (file.stat.size > 262144) return {
|
|
52050
|
+
ok: false,
|
|
52051
|
+
reason: "too-large"
|
|
52052
|
+
};
|
|
52053
|
+
try {
|
|
52054
|
+
return {
|
|
52055
|
+
ok: true,
|
|
52056
|
+
loaded: {
|
|
52057
|
+
file,
|
|
52058
|
+
bytes: await readAtMostCapPlusOneByte(file.handle)
|
|
52059
|
+
}
|
|
52060
|
+
};
|
|
52061
|
+
} catch {
|
|
52062
|
+
return {
|
|
52063
|
+
ok: false,
|
|
52064
|
+
reason: "unreadable"
|
|
52065
|
+
};
|
|
52066
|
+
}
|
|
52067
|
+
}
|
|
52068
|
+
async function refusalForOpenedInode(file, root) {
|
|
52069
|
+
if (file.stat.nlink > 1) return "hard-linked";
|
|
52070
|
+
const kernelPath = await fs.promises.readlink(`/proc/self/fd/${file.handle.fd}`).catch(() => null);
|
|
52071
|
+
if (kernelPath === null) return null;
|
|
52072
|
+
const realRoot = await fs.promises.realpath(root).catch(() => null);
|
|
52073
|
+
if (realRoot === null) return "root-unresolvable";
|
|
52074
|
+
if (!isPathInside(realRoot, kernelPath, { allowEqual: false })) return "outside-root";
|
|
52075
|
+
return null;
|
|
52076
|
+
}
|
|
52077
|
+
async function openVerifiedDoc(candidate, root, opensForWriting) {
|
|
52078
|
+
const opened = opensForWriting ? await openContainedFile(candidate, root, false, true) : await openContainedFile(candidate, root);
|
|
52079
|
+
if (!opened.ok) return opened;
|
|
52080
|
+
const refusal = await refusalForOpenedInode(opened.file, root);
|
|
52081
|
+
if (refusal === null) return opened;
|
|
52082
|
+
await opened.file.handle.close().catch(() => {});
|
|
52083
|
+
return {
|
|
52084
|
+
ok: false,
|
|
52085
|
+
reason: refusal
|
|
52086
|
+
};
|
|
52087
|
+
}
|
|
52088
|
+
async function loadContainedDoc(candidate, root) {
|
|
52089
|
+
const opened = await openVerifiedDoc(candidate, root, false);
|
|
52090
|
+
if (!opened.ok) return opened;
|
|
52091
|
+
try {
|
|
52092
|
+
return await readOpenedDoc(opened.file);
|
|
52093
|
+
} finally {
|
|
52094
|
+
await opened.file.handle.close().catch(() => {});
|
|
52095
|
+
}
|
|
52096
|
+
}
|
|
52097
|
+
async function overwriteThroughHandle(handle, bytes) {
|
|
52098
|
+
let writtenBytes = 0;
|
|
52099
|
+
while (writtenBytes < bytes.byteLength) {
|
|
52100
|
+
const { bytesWritten } = await handle.write(bytes, writtenBytes, bytes.byteLength - writtenBytes, writtenBytes);
|
|
52101
|
+
writtenBytes += bytesWritten;
|
|
52102
|
+
}
|
|
52103
|
+
await handle.truncate(bytes.byteLength);
|
|
52104
|
+
await handle.sync();
|
|
52105
|
+
}
|
|
52106
|
+
async function overwriteIfUnchanged(file, text, baseHash) {
|
|
52107
|
+
const outcome = await readOpenedDoc(file);
|
|
52108
|
+
if (!outcome.ok) return {
|
|
52109
|
+
ok: false,
|
|
52110
|
+
error: describeDocRefusal(outcome.reason),
|
|
52111
|
+
conflict: false
|
|
52112
|
+
};
|
|
52113
|
+
if (hashDocBytes(outcome.loaded.bytes) !== baseHash) return {
|
|
52114
|
+
ok: false,
|
|
52115
|
+
error: describeDocRefusal("changed-on-disk"),
|
|
52116
|
+
conflict: true
|
|
52117
|
+
};
|
|
52118
|
+
const current = decodeDocBytes(outcome.loaded.bytes);
|
|
52119
|
+
const encoded = encodeDocText(text, current.ok && current.usesCrlf);
|
|
52120
|
+
if (!encoded.ok) return {
|
|
52121
|
+
ok: false,
|
|
52122
|
+
error: describeDocRefusal(encoded.reason),
|
|
52123
|
+
conflict: false
|
|
52124
|
+
};
|
|
52125
|
+
const encodedBytes = Buffer.from(encoded.content, "utf8");
|
|
52126
|
+
await overwriteThroughHandle(file.handle, encodedBytes);
|
|
52127
|
+
return {
|
|
52128
|
+
ok: true,
|
|
52129
|
+
hash: hashDocBytes(encodedBytes)
|
|
52130
|
+
};
|
|
52131
|
+
}
|
|
52132
|
+
async function nameWithinRoot(root, realPath) {
|
|
52133
|
+
const realRoot = await fs.promises.realpath(root).catch(() => root);
|
|
52134
|
+
return path.relative(realRoot, realPath);
|
|
52135
|
+
}
|
|
52136
|
+
function createSessionDocsWiring({ broadcastControl, now = Date.now }) {
|
|
52137
|
+
const docsBySessionId = /* @__PURE__ */ new Map();
|
|
52138
|
+
const saveQueue = createKeyedSerialQueue();
|
|
52139
|
+
function broadcastDocs(sessionId) {
|
|
52140
|
+
broadcastControl({
|
|
52141
|
+
type: "session-docs-changed",
|
|
52142
|
+
id: sessionId,
|
|
52143
|
+
docs: docsBySessionId.get(sessionId) ?? []
|
|
52144
|
+
});
|
|
52145
|
+
}
|
|
52146
|
+
function forgetSession(sessionId) {
|
|
52147
|
+
if (!docsBySessionId.delete(sessionId)) return;
|
|
52148
|
+
broadcastDocs(sessionId);
|
|
52149
|
+
}
|
|
52150
|
+
function isSurfaced(session, docPath) {
|
|
52151
|
+
return (docsBySessionId.get(session.id) ?? []).some((doc) => doc.path === docPath);
|
|
52152
|
+
}
|
|
52153
|
+
async function surface(session, request) {
|
|
52154
|
+
const root = session.effectiveCwd();
|
|
52155
|
+
const outcome = await loadContainedDoc(path.resolve(root, request.path), root);
|
|
52156
|
+
if (!outcome.ok) return {
|
|
52157
|
+
ok: false,
|
|
52158
|
+
error: describeDocRefusal(outcome.reason)
|
|
52159
|
+
};
|
|
52160
|
+
const decoded = decodeDocBytes(outcome.loaded.bytes);
|
|
52161
|
+
if (!decoded.ok) return {
|
|
52162
|
+
ok: false,
|
|
52163
|
+
error: describeDocRefusal(decoded.reason)
|
|
52164
|
+
};
|
|
52165
|
+
const realPath = outcome.loaded.file.realPath;
|
|
52166
|
+
const doc = {
|
|
52167
|
+
path: realPath,
|
|
52168
|
+
name: await nameWithinRoot(root, realPath),
|
|
52169
|
+
surfacedAt: now()
|
|
52170
|
+
};
|
|
52171
|
+
if (!docsBySessionId.has(session.id)) session.once("teardown", () => forgetSession(session.id));
|
|
52172
|
+
docsBySessionId.set(session.id, withSurfacedDoc(docsBySessionId.get(session.id) ?? [], doc, 20));
|
|
52173
|
+
broadcastDocs(session.id);
|
|
52174
|
+
return {
|
|
52175
|
+
ok: true,
|
|
52176
|
+
doc
|
|
52177
|
+
};
|
|
52178
|
+
}
|
|
52179
|
+
async function read(session, docPath) {
|
|
52180
|
+
if (!isSurfaced(session, docPath)) return {
|
|
52181
|
+
ok: false,
|
|
52182
|
+
error: describeDocRefusal("not-surfaced")
|
|
52183
|
+
};
|
|
52184
|
+
const outcome = await loadContainedDoc(docPath, session.effectiveCwd());
|
|
52185
|
+
if (!outcome.ok) return {
|
|
52186
|
+
ok: false,
|
|
52187
|
+
error: describeDocRefusal(outcome.reason)
|
|
52188
|
+
};
|
|
52189
|
+
const decoded = decodeDocBytes(outcome.loaded.bytes);
|
|
52190
|
+
if (!decoded.ok) return {
|
|
52191
|
+
ok: false,
|
|
52192
|
+
error: describeDocRefusal(decoded.reason)
|
|
52193
|
+
};
|
|
52194
|
+
return {
|
|
52195
|
+
ok: true,
|
|
52196
|
+
text: decoded.text,
|
|
52197
|
+
hash: hashDocBytes(outcome.loaded.bytes)
|
|
52198
|
+
};
|
|
52199
|
+
}
|
|
52200
|
+
async function writeIfUnchanged(session, docPath, text, baseHash) {
|
|
52201
|
+
const opened = await openVerifiedDoc(docPath, session.effectiveCwd(), true);
|
|
52202
|
+
if (!opened.ok) return {
|
|
52203
|
+
ok: false,
|
|
52204
|
+
error: describeDocRefusal(opened.reason),
|
|
52205
|
+
conflict: false
|
|
52206
|
+
};
|
|
52207
|
+
try {
|
|
52208
|
+
return await overwriteIfUnchanged(opened.file, text, baseHash);
|
|
52209
|
+
} finally {
|
|
52210
|
+
await opened.file.handle.close().catch(() => {});
|
|
52211
|
+
}
|
|
52212
|
+
}
|
|
52213
|
+
async function save(session, docPath, text, baseHash) {
|
|
52214
|
+
if (!isSurfaced(session, docPath)) return {
|
|
52215
|
+
ok: false,
|
|
52216
|
+
error: describeDocRefusal("not-surfaced"),
|
|
52217
|
+
conflict: false
|
|
52218
|
+
};
|
|
52219
|
+
return saveQueue.run(docPath, () => writeIfUnchanged(session, docPath, text, baseHash));
|
|
52220
|
+
}
|
|
52221
|
+
function snapshotMessage() {
|
|
52222
|
+
return {
|
|
52223
|
+
type: "session-docs-snapshot",
|
|
52224
|
+
sessions: Object.fromEntries(docsBySessionId)
|
|
52225
|
+
};
|
|
52226
|
+
}
|
|
52227
|
+
return {
|
|
52228
|
+
surface,
|
|
52229
|
+
read,
|
|
52230
|
+
save,
|
|
52231
|
+
snapshotMessage
|
|
52232
|
+
};
|
|
52233
|
+
}
|
|
52234
|
+
//#endregion
|
|
51814
52235
|
//#region server/backend.ts
|
|
51815
52236
|
var AGENT_INGRESS_HOST = "127.0.0.1";
|
|
51816
52237
|
function createBackend(httpServer, options = {}) {
|
|
@@ -51916,6 +52337,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
51916
52337
|
});
|
|
51917
52338
|
const { controlWss, dataWss, controlReplayLog, sessionDataClients, broadcastControl: broadcastControlFromWebSockets, broadcastLocalControl, closeSessionDataClients } = webSockets;
|
|
51918
52339
|
broadcastControl = broadcastControlFromWebSockets;
|
|
52340
|
+
const sessionDocs = createSessionDocsWiring({ broadcastControl: broadcastControlFromWebSockets });
|
|
51919
52341
|
const sessions = /* @__PURE__ */ new Map();
|
|
51920
52342
|
const agentSessions = /* @__PURE__ */ new Map();
|
|
51921
52343
|
const { buildHealthSnapshot, healthInterval } = createBackendHealth({
|
|
@@ -51975,6 +52397,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
51975
52397
|
broadcastControl,
|
|
51976
52398
|
spawnGate,
|
|
51977
52399
|
recordLane,
|
|
52400
|
+
sessionDocs,
|
|
51978
52401
|
logger: console
|
|
51979
52402
|
});
|
|
51980
52403
|
const wireSessionEvents = createSessionEventWiring({
|
|
@@ -52157,6 +52580,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
52157
52580
|
readTracePage: traceWiring?.readTracePage ?? null,
|
|
52158
52581
|
readPlanRevision: laneAssembly.planReview?.readPlanRevision ?? null,
|
|
52159
52582
|
decidePlanReview: laneAssembly.planReview?.decide ?? null,
|
|
52583
|
+
sessionDocs,
|
|
52160
52584
|
changeMapNarrator: laneAssembly.changeMapNarrator,
|
|
52161
52585
|
serverBuild: () => serverBuild,
|
|
52162
52586
|
telemetry,
|