@junctionpanel/server 0.1.28 → 0.1.31
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/server/client/daemon-client.d.ts +42 -5
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +85 -3
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +7 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +8 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +244 -135
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/gemini-agent.d.ts +4 -1
- package/dist/server/server/agent/providers/gemini-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/gemini-agent.js +36 -8
- package/dist/server/server/agent/providers/gemini-agent.js.map +1 -1
- package/dist/server/server/agent/providers/image-attachments.d.ts +8 -0
- package/dist/server/server/agent/providers/image-attachments.d.ts.map +1 -0
- package/dist/server/server/agent/providers/image-attachments.js +47 -0
- package/dist/server/server/agent/providers/image-attachments.js.map +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +3 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/daemon-doctor.d.ts +39 -0
- package/dist/server/server/daemon-doctor.d.ts.map +1 -0
- package/dist/server/server/daemon-doctor.js +260 -0
- package/dist/server/server/daemon-doctor.js.map +1 -0
- package/dist/server/server/daemon-provider-settings.d.ts +42 -0
- package/dist/server/server/daemon-provider-settings.d.ts.map +1 -0
- package/dist/server/server/daemon-provider-settings.js +207 -0
- package/dist/server/server/daemon-provider-settings.js.map +1 -0
- package/dist/server/server/file-explorer/service.d.ts +4 -2
- package/dist/server/server/file-explorer/service.d.ts.map +1 -1
- package/dist/server/server/file-explorer/service.js +104 -2
- package/dist/server/server/file-explorer/service.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +24 -24
- package/dist/server/server/session.d.ts +10 -1
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +439 -62
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/worktree-bootstrap.d.ts +1 -0
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
- package/dist/server/server/worktree-bootstrap.js +4 -0
- package/dist/server/server/worktree-bootstrap.js.map +1 -1
- package/dist/server/shared/messages.d.ts +4245 -34
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +167 -0
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/utils/checkout-git.d.ts +23 -4
- package/dist/server/utils/checkout-git.d.ts.map +1 -1
- package/dist/server/utils/checkout-git.js +298 -79
- package/dist/server/utils/checkout-git.js.map +1 -1
- package/dist/server/utils/directory-suggestions.d.ts +4 -0
- package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
- package/dist/server/utils/directory-suggestions.js +83 -5
- package/dist/server/utils/directory-suggestions.js.map +1 -1
- package/dist/server/utils/workspace-ref-files.d.ts +31 -0
- package/dist/server/utils/workspace-ref-files.d.ts.map +1 -0
- package/dist/server/utils/workspace-ref-files.js +207 -0
- package/dist/server/utils/workspace-ref-files.js.map +1 -0
- package/dist/server/utils/worktree.d.ts +6 -3
- package/dist/server/utils/worktree.d.ts.map +1 -1
- package/dist/server/utils/worktree.js +46 -45
- package/dist/server/utils/worktree.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
-
import { watch } from 'node:fs';
|
|
2
|
+
import { existsSync, watch } from 'node:fs';
|
|
3
3
|
import { exec } from 'child_process';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
|
-
import { resolve, sep, basename, dirname } from 'path';
|
|
6
|
-
import { homedir } from 'node:os';
|
|
5
|
+
import { resolve, sep, basename, dirname, parse as parsePath } from 'path';
|
|
6
|
+
import { homedir, hostname } from 'node:os';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { serializeAgentStreamEvent, } from './messages.js';
|
|
9
9
|
import { BinaryMuxChannel, TerminalBinaryFlags, TerminalBinaryMessageType, } from '../shared/binary-mux.js';
|
|
10
10
|
import { buildConfigOverrides, buildSessionConfig, extractTimestamps, extractTimelineSnapshot, } from './persistence-hooks.js';
|
|
11
11
|
import { experimental_createMCPClient } from 'ai';
|
|
12
12
|
import { buildProviderRegistry } from './agent/provider-registry.js';
|
|
13
|
+
import { applyProviderEnv, } from './agent/provider-launch-config.js';
|
|
13
14
|
import { scheduleAgentMetadataGeneration } from './agent/agent-metadata-generator.js';
|
|
14
15
|
import { resolveEffectiveThinkingOptionId, toAgentPayload } from './agent/agent-projections.js';
|
|
15
16
|
import { appendTimelineItemIfAgentKnown, emitLiveTimelineItemIfAgentKnown, } from './agent/timeline-append.js';
|
|
@@ -20,14 +21,18 @@ import { listDirectoryEntries, readExplorerFile, getDownloadableFileInfo, } from
|
|
|
20
21
|
import { slugify, validateBranchSlug, listJunctionWorktrees, deleteJunctionWorktree, isJunctionOwnedWorktreeCwd, resolveJunctionWorktreeRootForCwd, createInRepoWorktree, restoreInRepoWorktree, } from '../utils/worktree.js';
|
|
21
22
|
import { readJunctionWorktreeMetadata } from '../utils/worktree-metadata.js';
|
|
22
23
|
import { runAsyncWorktreeBootstrap } from './worktree-bootstrap.js';
|
|
23
|
-
import { getCheckoutDiff, getCheckoutStatus, getCheckoutStatusLite, listBranchSuggestions, NotGitRepoError, MergeConflictError, MergeFromBaseConflictError, commitChanges, mergeToBase, mergeFromBase, pushCurrentBranch, createPullRequest, getPullRequestFailureLogs, getPullRequestStatus, mergePullRequest, resolveBaseRef, } from '../utils/checkout-git.js';
|
|
24
|
+
import { getCheckoutDiff, getCheckoutStatus, getCheckoutStatusLite, listBranchSuggestions, NotGitRepoError, MergeConflictError, MergeFromBaseConflictError, commitChanges, mergeToBase, mergeFromBase, pushCurrentBranch, createPullRequest, getPullRequestFailureLogs, getPullRequestStatus, listGitRemotes, mergePullRequest, resolveBaseRef, } from '../utils/checkout-git.js';
|
|
24
25
|
import { getProjectIcon } from '../utils/project-icon.js';
|
|
25
26
|
import { expandTilde } from '../utils/path.js';
|
|
26
|
-
import { searchHomeDirectories, searchWorkspaceEntries, searchGitRepositories, checkIsGitRepo } from '../utils/directory-suggestions.js';
|
|
27
|
+
import { searchHomeDirectories, searchWorkspaceEntries, searchWorkspaceEntriesAtGitRef, searchGitRepositories, checkIsGitRepo, } from '../utils/directory-suggestions.js';
|
|
27
28
|
import { cloneRepository } from '../utils/git-clone.js';
|
|
28
29
|
import { initRepository } from '../utils/git-init.js';
|
|
29
30
|
import { resolveClientMessageId } from './client-message-id.js';
|
|
30
31
|
import { deriveProjectGroupingKey, deriveProjectGroupingName } from '../shared/project-grouping.js';
|
|
32
|
+
import { resolveDaemonVersion } from './daemon-version.js';
|
|
33
|
+
import { runDaemonDoctor } from './daemon-doctor.js';
|
|
34
|
+
import { MANAGED_DAEMON_PROVIDERS, autoRouteProviderExecutable, loadDaemonProviderSettings, saveDaemonProviderExecutablePath, } from './daemon-provider-settings.js';
|
|
35
|
+
import { loadPersistedConfig } from './persisted-config.js';
|
|
31
36
|
const execAsync = promisify(exec);
|
|
32
37
|
const READ_ONLY_GIT_ENV = {
|
|
33
38
|
...process.env,
|
|
@@ -50,6 +55,7 @@ class SessionRequestError extends Error {
|
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
const SAFE_GIT_REF_PATTERN = /^[A-Za-z0-9._\/-]+$/;
|
|
58
|
+
const SAFE_GIT_REMOTE_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
53
59
|
function coerceAgentProvider(logger, value, agentId) {
|
|
54
60
|
if (isValidAgentProvider(value)) {
|
|
55
61
|
return value;
|
|
@@ -77,6 +83,19 @@ function toAgentPersistenceHandle(logger, handle) {
|
|
|
77
83
|
metadata: handle.metadata,
|
|
78
84
|
};
|
|
79
85
|
}
|
|
86
|
+
function normalizeAgentRunOptions(runOptions) {
|
|
87
|
+
if (!runOptions) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
...(runOptions.outputSchema !== undefined ? { outputSchema: runOptions.outputSchema } : {}),
|
|
92
|
+
...(runOptions.resumeFrom ? { resumeFrom: runOptions.resumeFrom } : {}),
|
|
93
|
+
...(runOptions.maxThinkingTokens !== undefined
|
|
94
|
+
? { maxThinkingTokens: runOptions.maxThinkingTokens }
|
|
95
|
+
: {}),
|
|
96
|
+
...(runOptions.extra ? { extra: runOptions.extra } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
80
99
|
/**
|
|
81
100
|
* Session represents a single connected client session.
|
|
82
101
|
* It owns all state management, orchestration logic, and message processing.
|
|
@@ -800,9 +819,15 @@ export class Session {
|
|
|
800
819
|
case 'branch_suggestions_request':
|
|
801
820
|
await this.handleBranchSuggestionsRequest(msg);
|
|
802
821
|
break;
|
|
822
|
+
case 'git_remotes_request':
|
|
823
|
+
await this.handleGitRemotesRequest(msg);
|
|
824
|
+
break;
|
|
803
825
|
case 'directory_suggestions_request':
|
|
804
826
|
await this.handleDirectorySuggestionsRequest(msg);
|
|
805
827
|
break;
|
|
828
|
+
case 'workspace_file_suggestions_request':
|
|
829
|
+
await this.handleWorkspaceFileSuggestionsRequest(msg);
|
|
830
|
+
break;
|
|
806
831
|
case 'git_clone_request':
|
|
807
832
|
await this.handleGitCloneRequest(msg);
|
|
808
833
|
break;
|
|
@@ -863,6 +888,18 @@ export class Session {
|
|
|
863
888
|
case 'list_available_providers_request':
|
|
864
889
|
await this.handleListAvailableProvidersRequest(msg);
|
|
865
890
|
break;
|
|
891
|
+
case 'run_daemon_doctor_request':
|
|
892
|
+
await this.handleRunDaemonDoctorRequest(msg);
|
|
893
|
+
break;
|
|
894
|
+
case 'get_daemon_provider_settings_request':
|
|
895
|
+
await this.handleGetDaemonProviderSettingsRequest(msg);
|
|
896
|
+
break;
|
|
897
|
+
case 'update_daemon_provider_settings_request':
|
|
898
|
+
await this.handleUpdateDaemonProviderSettingsRequest(msg);
|
|
899
|
+
break;
|
|
900
|
+
case 'auto_route_provider_request':
|
|
901
|
+
await this.handleAutoRouteProviderRequest(msg);
|
|
902
|
+
break;
|
|
866
903
|
case 'clear_agent_attention':
|
|
867
904
|
await this.handleClearAgentAttention(msg.agentId);
|
|
868
905
|
break;
|
|
@@ -1150,19 +1187,43 @@ export class Session {
|
|
|
1150
1187
|
if (!record) {
|
|
1151
1188
|
throw new Error(`Agent not found: ${agentId}`);
|
|
1152
1189
|
}
|
|
1190
|
+
const allRecords = await this.agentStorage.list();
|
|
1191
|
+
const siblingRecords = record.archivedWorktree?.cleanupState === 'deleted' && record.archivedWorktree
|
|
1192
|
+
? allRecords.filter((candidate) => {
|
|
1193
|
+
if (candidate.id === record.id || !candidate.archivedAt) {
|
|
1194
|
+
return false;
|
|
1195
|
+
}
|
|
1196
|
+
return candidate.cwd === record.cwd;
|
|
1197
|
+
})
|
|
1198
|
+
: [];
|
|
1153
1199
|
let nextRecord = {
|
|
1154
1200
|
...record,
|
|
1155
1201
|
archivedAt: null,
|
|
1156
1202
|
};
|
|
1157
1203
|
let restoredWorktree = null;
|
|
1158
1204
|
if (record.archivedWorktree?.cleanupState === 'deleted') {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1205
|
+
try {
|
|
1206
|
+
restoredWorktree = await restoreInRepoWorktree({
|
|
1207
|
+
repoRoot: record.archivedWorktree.repoRoot,
|
|
1208
|
+
baseBranch: record.archivedWorktree.baseBranch,
|
|
1209
|
+
branchName: record.archivedWorktree.branchName,
|
|
1210
|
+
worktreeSlug: record.archivedWorktree.worktreeSlug,
|
|
1211
|
+
runSetup: false,
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
catch (error) {
|
|
1215
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1216
|
+
const restoredPath = record.archivedWorktree.originalCwd;
|
|
1217
|
+
if (!message.includes('Worktree path already exists') || !existsSync(restoredPath)) {
|
|
1218
|
+
throw error;
|
|
1219
|
+
}
|
|
1220
|
+
restoredWorktree = {
|
|
1221
|
+
branchName: record.archivedWorktree.branchName,
|
|
1222
|
+
worktreePath: restoredPath,
|
|
1223
|
+
baseBranch: record.archivedWorktree.baseBranch,
|
|
1224
|
+
workspaceName: basename(restoredPath),
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1166
1227
|
nextRecord = {
|
|
1167
1228
|
...nextRecord,
|
|
1168
1229
|
cwd: restoredWorktree.worktreePath,
|
|
@@ -1174,13 +1235,34 @@ export class Session {
|
|
|
1174
1235
|
},
|
|
1175
1236
|
};
|
|
1176
1237
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1238
|
+
const recordsToRestore = [record, ...siblingRecords].map((candidate) => {
|
|
1239
|
+
const nextArchivedWorktree = restoredWorktree && candidate.archivedWorktree
|
|
1240
|
+
? {
|
|
1241
|
+
...candidate.archivedWorktree,
|
|
1242
|
+
originalCwd: restoredWorktree.worktreePath,
|
|
1243
|
+
cleanupState: 'active',
|
|
1244
|
+
cleanedUpAt: null,
|
|
1245
|
+
}
|
|
1246
|
+
: candidate.archivedWorktree;
|
|
1247
|
+
return {
|
|
1248
|
+
...candidate,
|
|
1249
|
+
archivedAt: null,
|
|
1250
|
+
cwd: restoredWorktree ? restoredWorktree.worktreePath : candidate.cwd,
|
|
1251
|
+
archivedWorktree: nextArchivedWorktree,
|
|
1252
|
+
};
|
|
1253
|
+
});
|
|
1254
|
+
for (const restoredRecord of recordsToRestore) {
|
|
1255
|
+
await this.agentStorage.upsert(restoredRecord);
|
|
1256
|
+
const liveAgent = this.agentManager.getAgent(restoredRecord.id);
|
|
1257
|
+
if (liveAgent) {
|
|
1258
|
+
this.agentManager.notifyAgentState(restoredRecord.id);
|
|
1259
|
+
}
|
|
1260
|
+
else {
|
|
1261
|
+
await this.forwardStoredAgentRecordUpdate(restoredRecord);
|
|
1262
|
+
}
|
|
1263
|
+
if (restoredRecord.id === agentId) {
|
|
1264
|
+
nextRecord = restoredRecord;
|
|
1265
|
+
}
|
|
1184
1266
|
}
|
|
1185
1267
|
this.emit({
|
|
1186
1268
|
type: 'agent_unarchived',
|
|
@@ -1363,7 +1445,7 @@ export class Session {
|
|
|
1363
1445
|
* Handle create agent request
|
|
1364
1446
|
*/
|
|
1365
1447
|
async handleCreateAgentRequest(msg) {
|
|
1366
|
-
const { config, worktreeName, requestId, initialPrompt, clientMessageId, outputSchema, git, bootstrapSetupOverride, images, labels, } = msg;
|
|
1448
|
+
const { config, worktreeName, requestId, initialPrompt, clientMessageId, outputSchema, initialRunOptions, git, bootstrapSetupOverride, generalPreferencesApplied, images, labels, } = msg;
|
|
1367
1449
|
this.sessionLogger.info({ cwd: config.cwd, provider: config.provider, worktreeName }, `Creating agent in ${config.cwd} (${config.provider})${worktreeName ? ` with worktree ${worktreeName}` : ''}`);
|
|
1368
1450
|
try {
|
|
1369
1451
|
const { sessionConfig, worktreeConfig, autoWorkspaceName } = await this.buildAgentSessionConfig(config, git, worktreeName, labels);
|
|
@@ -1387,21 +1469,27 @@ export class Session {
|
|
|
1387
1469
|
},
|
|
1388
1470
|
});
|
|
1389
1471
|
}
|
|
1390
|
-
const trimmedPrompt = initialPrompt?.trim();
|
|
1472
|
+
const trimmedPrompt = initialPrompt?.trim() ?? '';
|
|
1473
|
+
const hasInitialMessage = trimmedPrompt.length > 0 || Boolean(images?.length);
|
|
1391
1474
|
const runInitialPrompt = async () => {
|
|
1392
|
-
if (!
|
|
1475
|
+
if (!hasInitialMessage) {
|
|
1393
1476
|
return;
|
|
1394
1477
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1478
|
+
if (trimmedPrompt.length > 0) {
|
|
1479
|
+
scheduleAgentMetadataGeneration({
|
|
1480
|
+
agentManager: this.agentManager,
|
|
1481
|
+
agentId: snapshot.id,
|
|
1482
|
+
cwd: snapshot.cwd,
|
|
1483
|
+
initialPrompt: trimmedPrompt,
|
|
1484
|
+
explicitTitle: snapshot.config.title,
|
|
1485
|
+
junctionHome: this.junctionHome,
|
|
1486
|
+
logger: this.sessionLogger,
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
await this.handleSendAgentMessage(snapshot.id, trimmedPrompt, resolveClientMessageId(clientMessageId), images, normalizeAgentRunOptions({
|
|
1490
|
+
...(outputSchema ? { outputSchema } : {}),
|
|
1491
|
+
...(initialRunOptions ?? {}),
|
|
1492
|
+
}));
|
|
1405
1493
|
};
|
|
1406
1494
|
const handleInitialPromptError = (promptError) => {
|
|
1407
1495
|
this.sessionLogger.error({ err: promptError, agentId: snapshot.id }, `Failed to run initial prompt for agent ${snapshot.id}`);
|
|
@@ -1415,7 +1503,7 @@ export class Session {
|
|
|
1415
1503
|
},
|
|
1416
1504
|
});
|
|
1417
1505
|
};
|
|
1418
|
-
if (
|
|
1506
|
+
if (hasInitialMessage && !worktreeConfig) {
|
|
1419
1507
|
void runInitialPrompt().catch(handleInitialPromptError);
|
|
1420
1508
|
}
|
|
1421
1509
|
if (worktreeConfig) {
|
|
@@ -1423,6 +1511,7 @@ export class Session {
|
|
|
1423
1511
|
agentId: snapshot.id,
|
|
1424
1512
|
worktree: worktreeConfig,
|
|
1425
1513
|
setupOverride: bootstrapSetupOverride,
|
|
1514
|
+
generalPreferencesApplied,
|
|
1426
1515
|
terminalManager: this.terminalManager,
|
|
1427
1516
|
appendTimelineItem: (item) => appendTimelineItemIfAgentKnown({
|
|
1428
1517
|
agentManager: this.agentManager,
|
|
@@ -1435,7 +1524,7 @@ export class Session {
|
|
|
1435
1524
|
item,
|
|
1436
1525
|
}),
|
|
1437
1526
|
onSetupSettled: async (result) => {
|
|
1438
|
-
if (!
|
|
1527
|
+
if (!hasInitialMessage || result.setupStatus === 'failed') {
|
|
1439
1528
|
return;
|
|
1440
1529
|
}
|
|
1441
1530
|
await runInitialPrompt().catch(handleInitialPromptError);
|
|
@@ -1587,7 +1676,7 @@ export class Session {
|
|
|
1587
1676
|
this.handleAgentRunError(agentId, error, 'Failed to cancel running agent on request');
|
|
1588
1677
|
}
|
|
1589
1678
|
}
|
|
1590
|
-
async buildAgentSessionConfig(config, gitOptions, legacyWorktreeName,
|
|
1679
|
+
async buildAgentSessionConfig(config, gitOptions, legacyWorktreeName, labels) {
|
|
1591
1680
|
const cwd = expandTilde(config.cwd);
|
|
1592
1681
|
const normalized = this.normalizeGitOptions(gitOptions, legacyWorktreeName);
|
|
1593
1682
|
let repoRoot;
|
|
@@ -1608,7 +1697,26 @@ export class Session {
|
|
|
1608
1697
|
catch {
|
|
1609
1698
|
throw new Error('Selected project must be a git repository. Junction always creates a new worktree in .junction/.');
|
|
1610
1699
|
}
|
|
1611
|
-
|
|
1700
|
+
if (!normalized) {
|
|
1701
|
+
const ownership = await isJunctionOwnedWorktreeCwd(cwd, {
|
|
1702
|
+
junctionHome: this.junctionHome,
|
|
1703
|
+
});
|
|
1704
|
+
if (ownership.allowed) {
|
|
1705
|
+
const resolvedWorktree = await resolveJunctionWorktreeRootForCwd(cwd, {
|
|
1706
|
+
junctionHome: this.junctionHome,
|
|
1707
|
+
});
|
|
1708
|
+
const workspaceName = labels?.['junction:workspace']
|
|
1709
|
+
?? (resolvedWorktree ? basename(resolvedWorktree.worktreePath) : basename(cwd));
|
|
1710
|
+
return {
|
|
1711
|
+
sessionConfig: {
|
|
1712
|
+
...config,
|
|
1713
|
+
cwd,
|
|
1714
|
+
},
|
|
1715
|
+
autoWorkspaceName: workspaceName,
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
const baseBranch = normalized?.baseBranch ?? (await resolveBaseRef(repoRoot, { remoteName: normalized?.remoteName }));
|
|
1612
1720
|
if (!baseBranch) {
|
|
1613
1721
|
throw new Error('Unable to determine a base branch for worktree creation');
|
|
1614
1722
|
}
|
|
@@ -1616,6 +1724,7 @@ export class Session {
|
|
|
1616
1724
|
const createdWorktree = await createInRepoWorktree({
|
|
1617
1725
|
repoRoot,
|
|
1618
1726
|
baseBranch,
|
|
1727
|
+
remoteName: normalized?.remoteName,
|
|
1619
1728
|
runSetup: false,
|
|
1620
1729
|
});
|
|
1621
1730
|
return {
|
|
@@ -1684,6 +1793,153 @@ export class Session {
|
|
|
1684
1793
|
});
|
|
1685
1794
|
}
|
|
1686
1795
|
}
|
|
1796
|
+
resolveDaemonVersionSafe() {
|
|
1797
|
+
try {
|
|
1798
|
+
return resolveDaemonVersion(import.meta.url);
|
|
1799
|
+
}
|
|
1800
|
+
catch {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
toDaemonMetadata(input) {
|
|
1805
|
+
const homeDir = input?.homeDir ?? homedir();
|
|
1806
|
+
const rootDir = input?.rootDir ?? parsePath(homeDir).root;
|
|
1807
|
+
return {
|
|
1808
|
+
hostname: hostname(),
|
|
1809
|
+
version: this.resolveDaemonVersionSafe(),
|
|
1810
|
+
platform: process.platform,
|
|
1811
|
+
homeDir,
|
|
1812
|
+
rootDir,
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
async handleRunDaemonDoctorRequest(msg) {
|
|
1816
|
+
try {
|
|
1817
|
+
const result = await runDaemonDoctor(this.junctionHome, this.sessionLogger);
|
|
1818
|
+
this.emit({
|
|
1819
|
+
type: 'run_daemon_doctor_response',
|
|
1820
|
+
payload: {
|
|
1821
|
+
daemon: result.daemon,
|
|
1822
|
+
summary: result.summary,
|
|
1823
|
+
checks: result.checks,
|
|
1824
|
+
ranAt: result.ranAt,
|
|
1825
|
+
error: null,
|
|
1826
|
+
requestId: msg.requestId,
|
|
1827
|
+
},
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
catch (error) {
|
|
1831
|
+
this.sessionLogger.error({ err: error }, 'Failed to run daemon doctor');
|
|
1832
|
+
const snapshot = loadDaemonProviderSettings(this.junctionHome);
|
|
1833
|
+
this.emit({
|
|
1834
|
+
type: 'run_daemon_doctor_response',
|
|
1835
|
+
payload: {
|
|
1836
|
+
daemon: this.toDaemonMetadata(snapshot),
|
|
1837
|
+
summary: 'fail',
|
|
1838
|
+
checks: [],
|
|
1839
|
+
ranAt: new Date().toISOString(),
|
|
1840
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1841
|
+
requestId: msg.requestId,
|
|
1842
|
+
},
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
async handleGetDaemonProviderSettingsRequest(msg) {
|
|
1847
|
+
try {
|
|
1848
|
+
const snapshot = loadDaemonProviderSettings(this.junctionHome);
|
|
1849
|
+
this.emit({
|
|
1850
|
+
type: 'get_daemon_provider_settings_response',
|
|
1851
|
+
payload: {
|
|
1852
|
+
daemon: this.toDaemonMetadata(snapshot),
|
|
1853
|
+
providers: MANAGED_DAEMON_PROVIDERS.map((provider) => snapshot.providers[provider]),
|
|
1854
|
+
error: null,
|
|
1855
|
+
requestId: msg.requestId,
|
|
1856
|
+
},
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
catch (error) {
|
|
1860
|
+
this.sessionLogger.error({ err: error }, 'Failed to load daemon provider settings');
|
|
1861
|
+
this.emit({
|
|
1862
|
+
type: 'get_daemon_provider_settings_response',
|
|
1863
|
+
payload: {
|
|
1864
|
+
daemon: this.toDaemonMetadata(),
|
|
1865
|
+
providers: [],
|
|
1866
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1867
|
+
requestId: msg.requestId,
|
|
1868
|
+
},
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
async handleUpdateDaemonProviderSettingsRequest(msg) {
|
|
1873
|
+
try {
|
|
1874
|
+
const snapshot = saveDaemonProviderExecutablePath({
|
|
1875
|
+
junctionHome: this.junctionHome,
|
|
1876
|
+
provider: msg.provider,
|
|
1877
|
+
executablePath: msg.executablePath,
|
|
1878
|
+
});
|
|
1879
|
+
this.emit({
|
|
1880
|
+
type: 'update_daemon_provider_settings_response',
|
|
1881
|
+
payload: {
|
|
1882
|
+
daemon: this.toDaemonMetadata(snapshot),
|
|
1883
|
+
provider: snapshot.providers[msg.provider],
|
|
1884
|
+
error: null,
|
|
1885
|
+
requestId: msg.requestId,
|
|
1886
|
+
},
|
|
1887
|
+
});
|
|
1888
|
+
await this.handleRestartServerRequest(msg.requestId, 'settings_update');
|
|
1889
|
+
}
|
|
1890
|
+
catch (error) {
|
|
1891
|
+
this.sessionLogger.error({ err: error, provider: msg.provider }, 'Failed to update daemon provider settings');
|
|
1892
|
+
this.emit({
|
|
1893
|
+
type: 'update_daemon_provider_settings_response',
|
|
1894
|
+
payload: {
|
|
1895
|
+
daemon: this.toDaemonMetadata(),
|
|
1896
|
+
provider: null,
|
|
1897
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1898
|
+
requestId: msg.requestId,
|
|
1899
|
+
},
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
async handleAutoRouteProviderRequest(msg) {
|
|
1904
|
+
try {
|
|
1905
|
+
const config = loadPersistedConfig(this.junctionHome);
|
|
1906
|
+
const env = applyProviderEnv(process.env, config.agents?.providers?.[msg.provider]);
|
|
1907
|
+
const executablePath = autoRouteProviderExecutable(msg.provider, {
|
|
1908
|
+
env,
|
|
1909
|
+
platform: process.platform,
|
|
1910
|
+
});
|
|
1911
|
+
if (!executablePath) {
|
|
1912
|
+
throw new SessionRequestError('provider_not_found', `Could not automatically locate ${msg.provider} on this daemon.`);
|
|
1913
|
+
}
|
|
1914
|
+
const snapshot = saveDaemonProviderExecutablePath({
|
|
1915
|
+
junctionHome: this.junctionHome,
|
|
1916
|
+
provider: msg.provider,
|
|
1917
|
+
executablePath,
|
|
1918
|
+
});
|
|
1919
|
+
this.emit({
|
|
1920
|
+
type: 'auto_route_provider_response',
|
|
1921
|
+
payload: {
|
|
1922
|
+
daemon: this.toDaemonMetadata(snapshot),
|
|
1923
|
+
provider: snapshot.providers[msg.provider],
|
|
1924
|
+
error: null,
|
|
1925
|
+
requestId: msg.requestId,
|
|
1926
|
+
},
|
|
1927
|
+
});
|
|
1928
|
+
await this.handleRestartServerRequest(msg.requestId, 'settings_update');
|
|
1929
|
+
}
|
|
1930
|
+
catch (error) {
|
|
1931
|
+
this.sessionLogger.error({ err: error, provider: msg.provider }, 'Failed to auto-route provider executable');
|
|
1932
|
+
this.emit({
|
|
1933
|
+
type: 'auto_route_provider_response',
|
|
1934
|
+
payload: {
|
|
1935
|
+
daemon: this.toDaemonMetadata(),
|
|
1936
|
+
provider: null,
|
|
1937
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1938
|
+
requestId: msg.requestId,
|
|
1939
|
+
},
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1687
1943
|
normalizeGitOptions(gitOptions, legacyWorktreeName) {
|
|
1688
1944
|
const fallbackOptions = legacyWorktreeName
|
|
1689
1945
|
? {
|
|
@@ -1698,6 +1954,7 @@ export class Session {
|
|
|
1698
1954
|
return null;
|
|
1699
1955
|
}
|
|
1700
1956
|
const baseBranch = merged.baseBranch?.trim() || undefined;
|
|
1957
|
+
const remoteName = merged.remoteName?.trim() || undefined;
|
|
1701
1958
|
const createWorktree = Boolean(merged.createWorktree);
|
|
1702
1959
|
const createNewBranch = Boolean(merged.createNewBranch);
|
|
1703
1960
|
const normalizedBranchName = merged.newBranchName ? slugify(merged.newBranchName) : undefined;
|
|
@@ -1710,6 +1967,9 @@ export class Session {
|
|
|
1710
1967
|
if (baseBranch) {
|
|
1711
1968
|
this.assertSafeGitRef(baseBranch, 'base branch');
|
|
1712
1969
|
}
|
|
1970
|
+
if (remoteName) {
|
|
1971
|
+
this.assertSafeRemoteName(remoteName);
|
|
1972
|
+
}
|
|
1713
1973
|
if (createWorktree && !baseBranch) {
|
|
1714
1974
|
throw new Error('Base branch is required when creating a worktree');
|
|
1715
1975
|
}
|
|
@@ -1733,6 +1993,7 @@ export class Session {
|
|
|
1733
1993
|
}
|
|
1734
1994
|
return {
|
|
1735
1995
|
baseBranch,
|
|
1996
|
+
remoteName,
|
|
1736
1997
|
createNewBranch,
|
|
1737
1998
|
newBranchName: normalizedBranchName,
|
|
1738
1999
|
createWorktree,
|
|
@@ -1744,6 +2005,11 @@ export class Session {
|
|
|
1744
2005
|
throw new Error(`Invalid ${label}: ${ref}`);
|
|
1745
2006
|
}
|
|
1746
2007
|
}
|
|
2008
|
+
assertSafeRemoteName(remoteName) {
|
|
2009
|
+
if (!SAFE_GIT_REMOTE_NAME_PATTERN.test(remoteName)) {
|
|
2010
|
+
throw new Error(`Invalid remote name: ${remoteName}`);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
1747
2013
|
toCheckoutError(error) {
|
|
1748
2014
|
if (error instanceof NotGitRepoError) {
|
|
1749
2015
|
return { code: 'NOT_GIT_REPO', message: error.message };
|
|
@@ -2201,6 +2467,10 @@ export class Session {
|
|
|
2201
2467
|
const { cwd, branchName, requestId } = msg;
|
|
2202
2468
|
try {
|
|
2203
2469
|
const resolvedCwd = expandTilde(cwd);
|
|
2470
|
+
const remoteName = msg.remoteName?.trim() || undefined;
|
|
2471
|
+
if (remoteName) {
|
|
2472
|
+
this.assertSafeRemoteName(remoteName);
|
|
2473
|
+
}
|
|
2204
2474
|
// Try local branch first
|
|
2205
2475
|
try {
|
|
2206
2476
|
await execAsync(`git rev-parse --verify ${branchName}`, {
|
|
@@ -2222,26 +2492,45 @@ export class Session {
|
|
|
2222
2492
|
catch {
|
|
2223
2493
|
// Local branch doesn't exist, try remote
|
|
2224
2494
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2495
|
+
const { stdout: remoteStdout } = await execAsync('git remote', {
|
|
2496
|
+
cwd: resolvedCwd,
|
|
2497
|
+
env: READ_ONLY_GIT_ENV,
|
|
2498
|
+
});
|
|
2499
|
+
const configuredRemotes = remoteStdout
|
|
2500
|
+
.split('\n')
|
|
2501
|
+
.map((line) => line.trim())
|
|
2502
|
+
.filter((line) => line.length > 0)
|
|
2503
|
+
.sort((left, right) => left.localeCompare(right));
|
|
2504
|
+
const remoteCandidates = remoteName
|
|
2505
|
+
? [remoteName, 'origin', ...configuredRemotes]
|
|
2506
|
+
: ['origin', ...configuredRemotes];
|
|
2507
|
+
const seen = new Set();
|
|
2508
|
+
for (const candidateRemote of remoteCandidates) {
|
|
2509
|
+
const trimmedRemote = candidateRemote.trim();
|
|
2510
|
+
if (!trimmedRemote || seen.has(trimmedRemote)) {
|
|
2511
|
+
continue;
|
|
2512
|
+
}
|
|
2513
|
+
seen.add(trimmedRemote);
|
|
2514
|
+
try {
|
|
2515
|
+
await execAsync(`git rev-parse --verify ${trimmedRemote}/${branchName}`, {
|
|
2516
|
+
cwd: resolvedCwd,
|
|
2517
|
+
env: READ_ONLY_GIT_ENV,
|
|
2518
|
+
});
|
|
2519
|
+
this.emit({
|
|
2520
|
+
type: 'validate_branch_response',
|
|
2521
|
+
payload: {
|
|
2522
|
+
exists: true,
|
|
2523
|
+
resolvedRef: `${trimmedRemote}/${branchName}`,
|
|
2524
|
+
isRemote: true,
|
|
2525
|
+
error: null,
|
|
2526
|
+
requestId,
|
|
2527
|
+
},
|
|
2528
|
+
});
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
catch {
|
|
2532
|
+
// try next remote
|
|
2533
|
+
}
|
|
2245
2534
|
}
|
|
2246
2535
|
// Branch not found anywhere
|
|
2247
2536
|
this.emit({
|
|
@@ -2272,7 +2561,11 @@ export class Session {
|
|
|
2272
2561
|
const { cwd, query, limit, requestId } = msg;
|
|
2273
2562
|
try {
|
|
2274
2563
|
const resolvedCwd = expandTilde(cwd);
|
|
2275
|
-
const branches = await listBranchSuggestions(resolvedCwd, {
|
|
2564
|
+
const branches = await listBranchSuggestions(resolvedCwd, {
|
|
2565
|
+
query,
|
|
2566
|
+
limit,
|
|
2567
|
+
remoteName: msg.remoteName,
|
|
2568
|
+
});
|
|
2276
2569
|
this.emit({
|
|
2277
2570
|
type: 'branch_suggestions_response',
|
|
2278
2571
|
payload: {
|
|
@@ -2293,6 +2586,30 @@ export class Session {
|
|
|
2293
2586
|
});
|
|
2294
2587
|
}
|
|
2295
2588
|
}
|
|
2589
|
+
async handleGitRemotesRequest(msg) {
|
|
2590
|
+
const { cwd, requestId } = msg;
|
|
2591
|
+
try {
|
|
2592
|
+
const remotes = await listGitRemotes(expandTilde(cwd));
|
|
2593
|
+
this.emit({
|
|
2594
|
+
type: 'git_remotes_response',
|
|
2595
|
+
payload: {
|
|
2596
|
+
remotes,
|
|
2597
|
+
error: null,
|
|
2598
|
+
requestId,
|
|
2599
|
+
},
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
catch (error) {
|
|
2603
|
+
this.emit({
|
|
2604
|
+
type: 'git_remotes_response',
|
|
2605
|
+
payload: {
|
|
2606
|
+
remotes: [],
|
|
2607
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2608
|
+
requestId,
|
|
2609
|
+
},
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2296
2613
|
async handleDirectorySuggestionsRequest(msg) {
|
|
2297
2614
|
const { query, limit, requestId, cwd, includeFiles, includeDirectories, onlyGitRepos } = msg;
|
|
2298
2615
|
try {
|
|
@@ -2351,6 +2668,55 @@ export class Session {
|
|
|
2351
2668
|
});
|
|
2352
2669
|
}
|
|
2353
2670
|
}
|
|
2671
|
+
async handleWorkspaceFileSuggestionsRequest(msg) {
|
|
2672
|
+
const { cwd, query, limit, requestId, includeDirectories, includeFiles, ref } = msg;
|
|
2673
|
+
try {
|
|
2674
|
+
const workspaceCwd = expandTilde(cwd);
|
|
2675
|
+
if (ref) {
|
|
2676
|
+
this.assertSafeGitRef(ref, 'workspace file ref');
|
|
2677
|
+
}
|
|
2678
|
+
const entries = ref
|
|
2679
|
+
? await searchWorkspaceEntriesAtGitRef({
|
|
2680
|
+
cwd: workspaceCwd,
|
|
2681
|
+
ref,
|
|
2682
|
+
query,
|
|
2683
|
+
limit,
|
|
2684
|
+
includeDirectories,
|
|
2685
|
+
includeFiles,
|
|
2686
|
+
})
|
|
2687
|
+
: await searchWorkspaceEntries({
|
|
2688
|
+
cwd: workspaceCwd,
|
|
2689
|
+
query,
|
|
2690
|
+
limit,
|
|
2691
|
+
includeDirectories,
|
|
2692
|
+
includeFiles,
|
|
2693
|
+
});
|
|
2694
|
+
this.emit({
|
|
2695
|
+
type: 'workspace_file_suggestions_response',
|
|
2696
|
+
payload: {
|
|
2697
|
+
cwd,
|
|
2698
|
+
query,
|
|
2699
|
+
ref: ref ?? null,
|
|
2700
|
+
entries,
|
|
2701
|
+
error: null,
|
|
2702
|
+
requestId,
|
|
2703
|
+
},
|
|
2704
|
+
});
|
|
2705
|
+
}
|
|
2706
|
+
catch (error) {
|
|
2707
|
+
this.emit({
|
|
2708
|
+
type: 'workspace_file_suggestions_response',
|
|
2709
|
+
payload: {
|
|
2710
|
+
cwd,
|
|
2711
|
+
query,
|
|
2712
|
+
ref: ref ?? null,
|
|
2713
|
+
entries: [],
|
|
2714
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2715
|
+
requestId,
|
|
2716
|
+
},
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2354
2720
|
async handleGitCloneRequest(msg) {
|
|
2355
2721
|
const { url, targetDirectory, requestId } = msg;
|
|
2356
2722
|
try {
|
|
@@ -2792,6 +3158,7 @@ export class Session {
|
|
|
2792
3158
|
}
|
|
2793
3159
|
await mergeFromBase(cwd, {
|
|
2794
3160
|
baseRef: msg.baseRef,
|
|
3161
|
+
remoteName: msg.remoteName,
|
|
2795
3162
|
requireCleanTarget: msg.requireCleanTarget ?? true,
|
|
2796
3163
|
});
|
|
2797
3164
|
this.scheduleCheckoutDiffRefreshForCwd(cwd);
|
|
@@ -2820,7 +3187,7 @@ export class Session {
|
|
|
2820
3187
|
async handleCheckoutPushRequest(msg) {
|
|
2821
3188
|
const { cwd, requestId } = msg;
|
|
2822
3189
|
try {
|
|
2823
|
-
await pushCurrentBranch(cwd);
|
|
3190
|
+
await pushCurrentBranch(cwd, { remoteName: msg.remoteName });
|
|
2824
3191
|
this.emit({
|
|
2825
3192
|
type: 'checkout_push_response',
|
|
2826
3193
|
payload: {
|
|
@@ -2859,6 +3226,7 @@ export class Session {
|
|
|
2859
3226
|
title,
|
|
2860
3227
|
body,
|
|
2861
3228
|
base: msg.baseRef,
|
|
3229
|
+
remoteName: msg.remoteName,
|
|
2862
3230
|
});
|
|
2863
3231
|
this.emit({
|
|
2864
3232
|
type: 'checkout_pr_create_response',
|
|
@@ -2887,7 +3255,7 @@ export class Session {
|
|
|
2887
3255
|
async handleCheckoutPrStatusRequest(msg) {
|
|
2888
3256
|
const { cwd, requestId } = msg;
|
|
2889
3257
|
try {
|
|
2890
|
-
const prStatus = await getPullRequestStatus(cwd);
|
|
3258
|
+
const prStatus = await getPullRequestStatus(cwd, { remoteName: msg.remoteName });
|
|
2891
3259
|
this.emit({
|
|
2892
3260
|
type: 'checkout_pr_status_response',
|
|
2893
3261
|
payload: {
|
|
@@ -2915,7 +3283,7 @@ export class Session {
|
|
|
2915
3283
|
async handleCheckoutPrFailureLogsRequest(msg) {
|
|
2916
3284
|
const { cwd, requestId } = msg;
|
|
2917
3285
|
try {
|
|
2918
|
-
const result = await getPullRequestFailureLogs(cwd);
|
|
3286
|
+
const result = await getPullRequestFailureLogs(cwd, { remoteName: msg.remoteName });
|
|
2919
3287
|
this.emit({
|
|
2920
3288
|
type: 'checkout_pr_failure_logs_response',
|
|
2921
3289
|
payload: {
|
|
@@ -2945,6 +3313,7 @@ export class Session {
|
|
|
2945
3313
|
try {
|
|
2946
3314
|
await mergePullRequest(cwd, {
|
|
2947
3315
|
method: msg.method ?? 'squash',
|
|
3316
|
+
remoteName: msg.remoteName,
|
|
2948
3317
|
});
|
|
2949
3318
|
this.emit({
|
|
2950
3319
|
type: 'checkout_pr_merge_response',
|
|
@@ -3295,19 +3664,24 @@ export class Session {
|
|
|
3295
3664
|
* Handle read-only file explorer requests scoped to a workspace cwd
|
|
3296
3665
|
*/
|
|
3297
3666
|
async handleWorkspaceFileExplorerRequest(request) {
|
|
3298
|
-
const { cwd, path: requestedPath = '.', mode, requestId } = request;
|
|
3667
|
+
const { cwd, path: requestedPath = '.', mode, requestId, ref } = request;
|
|
3299
3668
|
try {
|
|
3300
3669
|
const root = expandTilde(cwd);
|
|
3670
|
+
if (ref) {
|
|
3671
|
+
this.assertSafeGitRef(ref, 'workspace file ref');
|
|
3672
|
+
}
|
|
3301
3673
|
if (mode === 'list') {
|
|
3302
3674
|
const directory = await listDirectoryEntries({
|
|
3303
3675
|
root,
|
|
3304
3676
|
relativePath: requestedPath,
|
|
3677
|
+
ref,
|
|
3305
3678
|
});
|
|
3306
3679
|
this.emit({
|
|
3307
3680
|
type: 'workspace_file_explorer_response',
|
|
3308
3681
|
payload: {
|
|
3309
3682
|
cwd,
|
|
3310
3683
|
path: directory.path,
|
|
3684
|
+
ref: ref ?? null,
|
|
3311
3685
|
mode,
|
|
3312
3686
|
directory,
|
|
3313
3687
|
file: null,
|
|
@@ -3320,12 +3694,14 @@ export class Session {
|
|
|
3320
3694
|
const file = await readExplorerFile({
|
|
3321
3695
|
root,
|
|
3322
3696
|
relativePath: requestedPath,
|
|
3697
|
+
ref,
|
|
3323
3698
|
});
|
|
3324
3699
|
this.emit({
|
|
3325
3700
|
type: 'workspace_file_explorer_response',
|
|
3326
3701
|
payload: {
|
|
3327
3702
|
cwd,
|
|
3328
3703
|
path: file.path,
|
|
3704
|
+
ref: ref ?? null,
|
|
3329
3705
|
mode,
|
|
3330
3706
|
directory: null,
|
|
3331
3707
|
file,
|
|
@@ -3342,6 +3718,7 @@ export class Session {
|
|
|
3342
3718
|
payload: {
|
|
3343
3719
|
cwd,
|
|
3344
3720
|
path: requestedPath,
|
|
3721
|
+
ref: ref ?? null,
|
|
3345
3722
|
mode,
|
|
3346
3723
|
directory: null,
|
|
3347
3724
|
file: null,
|
|
@@ -4028,7 +4405,7 @@ export class Session {
|
|
|
4028
4405
|
this.sessionLogger.error({ err: error, agentId }, 'Failed to record user message for send_agent_message_request');
|
|
4029
4406
|
}
|
|
4030
4407
|
const prompt = this.buildAgentPrompt(msg.text, msg.images);
|
|
4031
|
-
const started = this.startAgentStream(agentId, prompt);
|
|
4408
|
+
const started = this.startAgentStream(agentId, prompt, normalizeAgentRunOptions(msg.runOptions));
|
|
4032
4409
|
if (!started.ok) {
|
|
4033
4410
|
this.emit({
|
|
4034
4411
|
type: 'send_agent_message_response',
|