Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions packages/code/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ import { RuntimeWorkspaceCommandSandbox } from './workspace-runtime.js';
import { NativeProcessWorkspaceCommandSandbox } from './native-process.js';
import { NativeWorkspaceCommandPool } from './native-pool.js';
import { GitWorktreeWorkspaceTools, internalWorkspaceId } from './workspace-instances.js';
import { LINKED_WORKTREE_DIRECTORY, LinkedWorktreeWorkspaceTools } from './linked-worktrees.js';
import { LaneGitWorkspaceTools } from './lane-git.js';
import { LINKED_WORKTREE_DIRECTORY, LinkedWorktreeWorkspaceTools, verifyLinkedWorktree } from './linked-worktrees.js';
import { GitWorktreeManager } from './worktrees.js';
import {
WorktreeRetirementScheduler,
Expand Down Expand Up @@ -1312,6 +1313,26 @@ async function run(
});
workspaceTools = linkedWorktreeTools;
}
// Set from each registration response; stays false against a Code API that does not offer lane_git.
let laneGitNegotiated = false;
if (workspaceTools?.capabilities.operations.includes('execute_command')) {
workspaceTools = new LaneGitWorkspaceTools({
delegate: workspaceTools,
Comment thread
berry-13 marked this conversation as resolved.
isEnabled: () => laneGitNegotiated,
async resolveRoot(request, signal) {
const source = roots.find((root) => root.id === request.workspaceId);
if (!source) return undefined;
if (request.workspaceInstanceId != null) {
return await conversationWorktrees?.plannedRoot(request.workspaceId, request.workspaceInstanceId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve nested linked worktrees after conversation instances

When a request specifies both workspaceInstanceId and worktree, this early return resolves only the conversation checkout and ignores its nested linked-worktree lane. Such combined lanes are supported鈥攖he linked-worktree executor passes both values to resolveLane鈥攕o the command runs in the nested worktree while laneGit reports the parent checkout's branch and HEAD, presenting incorrect lane/PR identity.

Useful? React with 馃憤聽/ 馃憥.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not reproduce this. A request with both workspaceInstanceId and worktree is rejected before anything runs: resolveLane throws INVALID_REQUEST 'Linked worktree lanes are not available inside conversation worktrees' (linked-worktrees.ts:270). I ran LinkedWorktreeWorkspaceTools with both set and got that rejection with zero delegate calls, so no command result exists and no laneGit is produced for that combination. If nested lanes are meant to be supported later, the resolver would need to resolve the nested root then; I left it as is.

}
if (request.worktree != null) {
signal?.throwIfAborted();
return (await verifyLinkedWorktree(source.root, request.worktree, source.identity)).root;
Comment thread
berry-13 marked this conversation as resolved.
Outdated
}
return source.root;
Comment thread
berry-13 marked this conversation as resolved.
Outdated
},
});
}
const retirementSources = roots.filter((root) => root.writable);
const debugLogs =
process.env.LIBRECHAT_CODE_LOG_LEVEL?.trim().toLowerCase() === 'debug';
Expand Down Expand Up @@ -1604,8 +1625,10 @@ async function run(
});
}
: undefined,
onRegistered: fileRelaySupervisor
? async registration => {
onRegistered: async registration => {
laneGitNegotiated =
registration.supportedWorkspaceCommandResultFeatures?.includes('lane_git') === true;
Comment thread
berry-13 marked this conversation as resolved.
Outdated
if (!fileRelaySupervisor) return;
if (
registration.registrationGeneration == null ||
!Number.isSafeInteger(
Expand All @@ -1621,8 +1644,7 @@ async function run(
registration.registrationGeneration,
controller.signal,
);
}
: undefined,
},
onError: error => {
const message =
error instanceof Error
Expand Down
334 changes: 334 additions & 0 deletions packages/code/src/lane-git.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
import assert from 'node:assert/strict';
import { execFile } from 'node:child_process';
import { mkdir, mkdtemp, realpath, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
import { promisify } from 'node:util';

import { LaneGitWorkspaceTools, readLaneGit } from './lane-git.js';
import { isValidBridgeWorkspaceToolCapabilities, isWorkspaceLaneGit, isWorkspaceToolResult } from './protocol.js';
import { BridgeWorker } from './worker.js';

import type { TestContext } from 'node:test';
import type { WorkspaceExecuteCommandRequest, WorkspaceToolRequest } from './protocol.js';

const exec = promisify(execFile);
const incarnationId = 'incarnation-00000001';
const identity = ['-c', 'user.name=Test', '-c', 'user.email=test@example.com'];

async function scratch(t: TestContext): Promise<string> {
const root = await realpath(await mkdtemp(join(tmpdir(), 'lane-git-')));
t.after(() => rm(root, { recursive: true, force: true }));
return root;
}

async function repo(root: string, name: string, commit = true): Promise<string> {
const directory = join(root, name);
await mkdir(directory, { recursive: true });
await exec('git', ['init', '--initial-branch=main', directory]);
if (commit) await exec('git', ['-C', directory, ...identity, 'commit', '--allow-empty', '-m', 'initial']);
return directory;
}

async function sha(directory: string): Promise<string> {
return (await exec('git', ['-C', directory, 'rev-parse', 'HEAD'])).stdout.trim();
}

test('isolated worktree lane reports its own branch and head', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
const lane = join(root, 'lane');
await exec('git', ['-C', source, 'worktree', 'add', '-b', 'librechat/conversation-abcd1234-0123', lane]);
assert.deepEqual(await readLaneGit(lane), { branch: 'librechat/conversation-abcd1234-0123', head: await sha(lane) });
assert.equal((await readLaneGit(source))?.branch, 'main');
});

test('source checkout reports whatever is checked out', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
await exec('git', ['-C', source, 'checkout', '-b', 'feature/pr-header']);
assert.deepEqual(await readLaneGit(source), { branch: 'feature/pr-header', head: await sha(source) });
});

test('a branch renamed by the agent shows up on the next read', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
assert.equal((await readLaneGit(source))?.branch, 'main');
await exec('git', ['-C', source, 'branch', '-m', 'renamed-by-agent']);
assert.equal((await readLaneGit(source))?.branch, 'renamed-by-agent');
});

test('detached HEAD reports a null branch and the commit', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
await exec('git', ['-C', source, 'checkout', '--detach']);
assert.deepEqual(await readLaneGit(source), { branch: null, head: await sha(source) });
});

test('a repository without commits reports the branch and a null head', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source', false);
assert.deepEqual(await readLaneGit(source), { branch: 'main', head: null });
});

test('a branch name beyond the bound reports null and never forwards it', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
const long = ['a'.repeat(100), 'b'.repeat(100), 'c'.repeat(100)].join('/');
await exec('git', ['-C', source, 'checkout', '-b', long]);
assert.deepEqual(await readLaneGit(source), { branch: null, head: await sha(source) });
});

test('a directory that is not a repository reports nothing', async (t) => {
const root = await scratch(t);
assert.equal(await readLaneGit(root), undefined);
});

test('lane Git state never carries paths, remotes or credentials', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
await exec('git', ['-C', source, 'remote', 'add', 'origin', 'https://user:secret@github.com/example/app.git']);
const state = await readLaneGit(source);
assert.deepEqual(Object.keys(state ?? {}).sort(), ['branch', 'head']);
const wire = JSON.stringify(state);
assert.equal(wire.includes(root), false);
assert.equal(wire.includes('secret'), false);
assert.equal(wire.includes('github.com'), false);
});

test('wire validator accepts only bounded branch and head', () => {
const head = 'a'.repeat(40);
assert.equal(isWorkspaceLaneGit({ branch: 'main', head }), true);
assert.equal(isWorkspaceLaneGit({ branch: null, head: null }), true);
assert.equal(isWorkspaceLaneGit({ branch: 'main', head: 'b'.repeat(64) }), true);
for (const bad of [
{ branch: 'x'.repeat(257), head },
{ branch: 'bad\nname', head },
{ branch: '', head },
{ branch: 'main', head: 'A'.repeat(40) },
{ branch: 'main', head: 'a'.repeat(41) },
{ branch: 'main' },
{ branch: 'main', head, path: '/srv/repo' },
{ branch: undefined, head },
null,
[],
]) {
assert.equal(isWorkspaceLaneGit(bad), false, JSON.stringify(bad));
}
});

const commandRequest: WorkspaceExecuteCommandRequest = {
protocolVersion: 1,
operation: 'execute_command',
workspaceId: 'primary',
command: 'git checkout -b agent-branch',
};
const commandResult = {
protocolVersion: 1 as const,
operation: 'execute_command' as const,
workspaceId: 'primary',
exitCode: 0,
stdout: '',
stderr: '',
truncated: false,
timedOut: false,
};

function delegate(onRun?: () => Promise<void>) {
return {
capabilities: {
protocolVersion: 1 as const,
operations: ['read_file' as const, 'execute_command' as const],
workspaces: [{ id: 'primary' }],
},
async execute(request: WorkspaceToolRequest) {
await onRun?.();
return request.operation === 'execute_command'
? commandResult
: { protocolVersion: 1 as const, operation: 'read_file' as const, workspaceId: 'primary', path: 'a', content: '', startLine: 1, endLine: 1, truncated: false };
},
};
}

test('a finished command refreshes the lane state, including a branch the command created', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
const tools = new LaneGitWorkspaceTools({
delegate: delegate(async () => {
await exec('git', ['-C', source, 'checkout', '-b', 'agent-branch']);
}),
resolveRoot: async () => source,
});
const result = await tools.execute(commandRequest);
assert.deepEqual('laneGit' in result && result.laneGit, { branch: 'agent-branch', head: await sha(source) });
assert.equal(isWorkspaceToolResult(commandRequest, result), true);
});

test('file operations do not read Git', async () => {
let resolved = 0;
const tools = new LaneGitWorkspaceTools({
delegate: delegate(),
resolveRoot: async () => {
resolved++;
return undefined;
},
});
const result = await tools.execute({ protocolVersion: 1, operation: 'read_file', workspaceId: 'primary', path: 'a' });
assert.equal('laneGit' in result, false);
assert.equal(resolved, 0);
});

test('an unreadable lane leaves the command result untouched', async (t) => {
const root = await scratch(t);
for (const resolveRoot of [async () => root, async () => undefined, async () => Promise.reject(new Error('gone'))]) {
const result = await new LaneGitWorkspaceTools({ delegate: delegate(), resolveRoot }).execute(commandRequest);
assert.deepEqual(result, commandResult);
}
});

test('the feature is advertised only when commands are', () => {
const withCommands = new LaneGitWorkspaceTools({ delegate: delegate(), resolveRoot: async () => undefined });
assert.deepEqual(withCommands.capabilities.commandResultFeatures, ['lane_git']);
assert.equal(isValidBridgeWorkspaceToolCapabilities(withCommands.capabilities), true);
const readOnly = new LaneGitWorkspaceTools({
delegate: { ...delegate(), capabilities: { protocolVersion: 1, operations: ['read_file'], workspaces: [{ id: 'primary' }] } },
resolveRoot: async () => undefined,
});
assert.equal(readOnly.capabilities.commandResultFeatures, undefined);
assert.equal(
isValidBridgeWorkspaceToolCapabilities({ ...withCommands.capabilities, commandResultFeatures: ['other'] }),
false,
);
});

test('the probe is skipped until lane_git is negotiated, then runs', async (t) => {
const root = await scratch(t);
const source = await repo(root, 'source');
let negotiated = false;
let resolved = 0;
const tools = new LaneGitWorkspaceTools({
delegate: delegate(),
isEnabled: () => negotiated,
resolveRoot: async () => {
resolved++;
return source;
},
});
const before = await tools.execute(commandRequest);
assert.equal('laneGit' in before, false);
assert.equal(resolved, 0);
negotiated = true;
const after = await tools.execute(commandRequest);
assert.equal(resolved, 1);
assert.deepEqual('laneGit' in after && after.laneGit, { branch: 'main', head: await sha(source) });
});

// Compatibility: negotiation with old and new Code API servers.

function quarantine() {
return { async assertAvailable() {}, async arm() {}, async clear() {}, async quarantine() {} };
}

async function run(t: TestContext, supported: boolean) {
const root = await scratch(t);
const source = await repo(root, 'source');
const tools = new LaneGitWorkspaceTools({ delegate: delegate(), resolveRoot: async () => source });
const registrations: Array<{ commandResultFeatures?: string[] }> = [];
const settlements: Array<{ result?: Record<string, unknown> }> = [];
const worker = new BridgeWorker({
codeApiUrl: 'https://code.example/v1',
token: 'worker-secret',
workerId: 'vm-1',
incarnationId,
sandboxEndpoint: 'http://127.0.0.1:2000/api/v2',
capabilities: { statefulWorkspace: false, sandboxProfile: 'anthropic-srt', runtimes: [], workspaceTools: tools.capabilities },
workspaceTools: tools,
workspaceMutationQuarantine: quarantine(),
fetchImpl: async (input, init) => {
const url = String(input);
const body = init?.body ? JSON.parse(String(init.body)) : {};
if (url.endsWith('/bridge/workers/register')) {
registrations.push(body.capabilities.workspaceTools ?? {});
return Response.json({
protocolVersion: 1,
workerId: 'vm-1',
incarnationId,
registeredAt: new Date().toISOString(),
leaseTtlMs: 60_000,
supportedWorkspaceToolOperations: ['read_file', 'execute_command'],
...(supported ? { supportedWorkspaceCommandResultFeatures: ['lane_git'] } : {}),
});
}
if (url.endsWith('/settle')) settlements.push(body);
return Response.json({ protocolVersion: 1, accepted: true });
},
});
await worker.register();
await worker.executeAndSettle({
protocolVersion: 1,
assignmentId: 'assignment-1',
workerId: 'vm-1',
incarnationId,
generation: 1,
leaseToken: 'lease-token-that-is-long-enough-for-testing',
expiresAt: new Date(Date.now() + 5_000).toISOString(),
executionKind: 'workspace_tool',
workspaceId: 'primary',
request: commandRequest,
});
return { registrations, settlements };
}

test('a Code API without the feature still pairs, registers without it, and never receives laneGit', async (t) => {
const { registrations, settlements } = await run(t, false);
assert.ok(registrations.length > 0);
for (const registration of registrations) assert.equal('commandResultFeatures' in registration, false);
assert.equal(settlements.length, 1);
assert.equal(settlements[0]?.result?.exitCode, 0);
assert.equal('laneGit' in (settlements[0]?.result ?? {}), false);
});

test('a Code API that acknowledges the feature registers it and receives laneGit', async (t) => {
const { registrations, settlements } = await run(t, true);
assert.deepEqual(registrations.at(-1)?.commandResultFeatures, ['lane_git']);
assert.deepEqual(Object.keys((settlements[0]?.result?.laneGit as object) ?? {}).sort(), ['branch', 'head']);
assert.equal((settlements[0]?.result?.laneGit as { branch: string }).branch, 'main');
});

test('a worker without the field still pairs and reports status', async () => {
const executor = {
capabilities: { protocolVersion: 1 as const, operations: ['read_file' as const], workspaces: [{ id: 'primary' }] },
async execute(): Promise<never> {
throw new Error('not executed');
},
};
const registrations: unknown[] = [];
const worker = new BridgeWorker({
codeApiUrl: 'https://code.example/v1',
token: 'worker-secret',
workerId: 'vm-1',
incarnationId,
sandboxEndpoint: 'http://127.0.0.1:2000/api/v2',
capabilities: { statefulWorkspace: false, sandboxProfile: 'anthropic-srt', runtimes: [], workspaceTools: executor.capabilities },
workspaceTools: executor,
fetchImpl: async (_input, init) => {
registrations.push(JSON.parse(String(init?.body)).capabilities.workspaceTools);
return Response.json({
protocolVersion: 1,
workerId: 'vm-1',
incarnationId,
registeredAt: new Date().toISOString(),
leaseTtlMs: 60_000,
supportedWorkspaceToolOperations: ['read_file'],
});
},
});
const registration = await worker.register();
assert.equal(registration.workerId, 'vm-1');
assert.ok(registrations.length > 0);
for (const advertised of registrations) {
assert.equal(JSON.stringify(advertised).includes('commandResultFeatures'), false);
assert.equal(JSON.stringify(advertised).includes('laneGit'), false);
}
});
Loading
Loading