Skip to content

Commit 26d1ab8

Browse files
authored
🌿 feat: Report Lane Git Branch And Head On Command Results (#311)
* feat: Report Lane Git Branch And Head On Command Results The worker reads the checked-out branch and HEAD of the lane a command ran in and attaches them to execute_command results as laneGit. Code API validates the value and forwards it only when CODEAPI_BRIDGE_LANE_GIT is enabled and the worker advertised lane_git. * fix: Address Review Findings on Lane Git Reporting Compare a retried settlement with the stored one before replica-local lane Git sanitization, and skip the Git probe until lane_git is negotiated at registration. * fix: Bound Lane Git Probe and Pass Flag Through Compose Cap the post-command Git probe to the budget the command left, reserving settlement time, and pass CODEAPI_BRIDGE_LANE_GIT to the api and service-worker compose services. * fix: Harden Lane Git Probe Bounds and Reported Values Bound root resolution with the probe limit, enable the probe from the worker's active registration, require HEAD to peel to a commit, and reject Unicode control and format characters in branch names. * fix: Trust Only Probed Lane Git and Use the Worker Deadline Assign the lane Git worker on the normal startup path, budget the probe from the worker's assignment deadline, and drop any laneGit supplied by the command executor. * fix: Reuse Settlement Reads and Preserve Branch Whitespace Fold lane Git sanitization into the settle path's existing reads instead of a separate pre-pass, and strip only Git's trailing newline from probe output. * fix: Report Null for Non-UTF-8 Branch Names Read the lane branch as bytes and decode it strictly, so a ref name that is not valid UTF-8 is reported as null instead of a lossy copy. * fix: Revalidate Source Git Metadata Before Probing Probe a source or conversation checkout only while its .git is its own plain directory, so a command that rewrites .git to point at another repository cannot make the host-side probe report that repository's branch and head. * fix: Reject Linked Refs Before Probing Source Git Walk refs/ and require every entry to be a plain file or directory before the host-side probe runs, so a ref symlinked to another checkout cannot be followed. * fix: Guard Linked Lane Refs and Report Full Branch Names Check a linked lane's metadata HEAD and the shared refs before probing, read the branch from the full symbolic ref instead of --short, and avoid Dirent.parentPath so the refs walk works on Node 20.11. * fix: Stop the Git Refs Walk When the Probe Aborts Pass the probe signal into the source and linked worktree metadata checks and check it between directory operations, so an abandoned walk does not keep running after the command result is returned. * fix: Report Lane Git Only From a Stable Branch Read Read the branch before and after the head and omit laneGit when it changed in between, so a concurrent checkout cannot pair one branch with another branch's commit. * fix: Own Object Storage and Pin the Head Before Probing Require a link-free refs tree and object store with no alternates or reftable before the host-side probe, ignore replacement refs, and read the head from the branch ref the first read named. * fix: Disable Transports and Lazy Fetch in the Lane Git Probe Run the probe with no allowed protocols and lazy fetch disabled, so a rewritten .git/config cannot make a missing object trigger a fetch or a transport command on the host.
1 parent 4401420 commit 26d1ab8

18 files changed

Lines changed: 1681 additions & 14 deletions

‎docker-compose.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ services:
1414
- CODEAPI_BRIDGE_AUTH_MODE=${CODEAPI_BRIDGE_AUTH_MODE:-paired}
1515
- CODEAPI_BRIDGE_DYNAMIC_WORKERS=${CODEAPI_BRIDGE_DYNAMIC_WORKERS:-true}
1616
- CODEAPI_BRIDGE_MAX_WORKSPACE_LEASE_SLOTS=${CODEAPI_BRIDGE_MAX_WORKSPACE_LEASE_SLOTS:-1}
17+
- CODEAPI_BRIDGE_LANE_GIT=${CODEAPI_BRIDGE_LANE_GIT:-false}
1718
- CODEAPI_BRIDGE_WORKER_ID=${CODEAPI_BRIDGE_WORKER_ID:-}
1819
- CODEAPI_BRIDGE_RECOVERY_SERVER_ID=${CODEAPI_BRIDGE_RECOVERY_SERVER_ID:-}
1920
- CODEAPI_BRIDGE_ENROLLMENT_TTL_SECONDS=${CODEAPI_BRIDGE_ENROLLMENT_TTL_SECONDS:-0}
@@ -69,6 +70,7 @@ services:
6970
- CODEAPI_BRIDGE_AUTH_MODE=${CODEAPI_BRIDGE_AUTH_MODE:-paired}
7071
- CODEAPI_BRIDGE_DYNAMIC_WORKERS=${CODEAPI_BRIDGE_DYNAMIC_WORKERS:-true}
7172
- CODEAPI_BRIDGE_MAX_WORKSPACE_LEASE_SLOTS=${CODEAPI_BRIDGE_MAX_WORKSPACE_LEASE_SLOTS:-1}
73+
- CODEAPI_BRIDGE_LANE_GIT=${CODEAPI_BRIDGE_LANE_GIT:-false}
7274
- CODEAPI_BRIDGE_WORKER_ID=${CODEAPI_BRIDGE_WORKER_ID:-}
7375
- CODEAPI_BRIDGE_RECOVERY_SERVER_ID=${CODEAPI_BRIDGE_RECOVERY_SERVER_ID:-}
7476
- CODEAPI_BRIDGE_ENROLLMENT_TTL_SECONDS=${CODEAPI_BRIDGE_ENROLLMENT_TTL_SECONDS:-0}

‎packages/code/src/cli.ts‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ import { RuntimeWorkspaceCommandSandbox } from './workspace-runtime.js';
4141
import { NativeProcessWorkspaceCommandSandbox } from './native-process.js';
4242
import { NativeWorkspaceCommandPool } from './native-pool.js';
4343
import { GitWorktreeWorkspaceTools, internalWorkspaceId } from './workspace-instances.js';
44-
import { LINKED_WORKTREE_DIRECTORY, LinkedWorktreeWorkspaceTools } from './linked-worktrees.js';
44+
import { LaneGitWorkspaceTools, ownsGitMetadata, ownsLinkedWorktreeMetadata } from './lane-git.js';
45+
import { LINKED_WORKTREE_DIRECTORY, LinkedWorktreeWorkspaceTools, verifyLinkedWorktree } from './linked-worktrees.js';
4546
import { GitWorktreeManager } from './worktrees.js';
4647
import {
4748
WorktreeRetirementScheduler,
@@ -1312,6 +1313,28 @@ async function run(
13121313
});
13131314
workspaceTools = linkedWorktreeTools;
13141315
}
1316+
// Probe Git only while the active registration advertises lane_git, which the worker decides.
1317+
let laneGitWorker: BridgeWorker | undefined;
1318+
if (workspaceTools?.capabilities.operations.includes('execute_command')) {
1319+
workspaceTools = new LaneGitWorkspaceTools({
1320+
delegate: workspaceTools,
1321+
isEnabled: () => laneGitWorker?.commandResultFeatureActive('lane_git') === true,
1322+
async resolveRoot(request, signal) {
1323+
const source = roots.find((root) => root.id === request.workspaceId);
1324+
if (!source) return undefined;
1325+
if (request.workspaceInstanceId != null) {
1326+
const root = await conversationWorktrees?.plannedRoot(request.workspaceId, request.workspaceInstanceId);
1327+
return root != null && (await ownsGitMetadata(root, signal)) ? root : undefined;
1328+
}
1329+
if (request.worktree != null) {
1330+
signal?.throwIfAborted();
1331+
const lane = await verifyLinkedWorktree(source.root, request.worktree, source.identity);
1332+
return (await ownsLinkedWorktreeMetadata(lane.commonGitDir, request.worktree, signal)) ? lane.root : undefined;
1333+
}
1334+
return (await ownsGitMetadata(source.root, signal)) ? source.root : undefined;
1335+
},
1336+
});
1337+
}
13151338
const retirementSources = roots.filter((root) => root.writable);
13161339
const debugLogs =
13171340
process.env.LIBRECHAT_CODE_LOG_LEVEL?.trim().toLowerCase() === 'debug';
@@ -1633,6 +1656,7 @@ async function run(
16331656
);
16341657
},
16351658
});
1659+
laneGitWorker = worker;
16361660
if (runtimeSessionId !== undefined) {
16371661
await worker.refreshCredential(controller.signal);
16381662
await worker.register(controller.signal);

‎packages/code/src/environment.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,13 @@ export class EnvironmentWorkspaceTools implements WorkspaceToolExecutor {
237237
async execute(
238238
request: WorkspaceToolRequest,
239239
signal?: AbortSignal,
240+
context?: { deadlineAtMs?: number },
240241
): Promise<WorkspaceToolResult> {
241242
if (
242243
request.operation !== 'execute_command' ||
243244
!request.environmentAction
244245
) {
245-
return this.delegate.execute(request, signal);
246+
return this.delegate.execute(request, signal, context);
246247
}
247248
const environment = this.environments.get(request.workspaceId);
248249
const action = environment?.definition.actions?.find(
@@ -271,6 +272,7 @@ export class EnvironmentWorkspaceTools implements WorkspaceToolExecutor {
271272
cwd: '.',
272273
},
273274
signal,
275+
context,
274276
);
275277
}
276278
}

0 commit comments

Comments
 (0)