@docyrus/docyrus 0.0.24 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -1
- package/agent-loader.js +249 -45
- package/agent-loader.js.map +3 -3
- package/main.js +278 -196
- package/main.js.map +4 -4
- package/package.json +2 -1
- package/resources/pi-agent/extensions/architect.ts +18 -34
- package/resources/pi-agent/extensions/plan.ts +1197 -0
- package/resources/pi-agent/shared/askUserProtocol.ts +248 -0
- package/resources/pi-agent/skills/diffity-diff/SKILL.md +28 -0
- package/resources/pi-agent/skills/diffity-resolve/SKILL.md +69 -0
- package/resources/pi-agent/skills/diffity-review/SKILL.md +175 -0
- package/server-loader.js +1680 -73
- package/server-loader.js.map +4 -4
package/server-loader.js
CHANGED
|
@@ -692,7 +692,7 @@ var init_dist = __esm({
|
|
|
692
692
|
// src/server/server-loader.ts
|
|
693
693
|
var import_node_fs = require("node:fs");
|
|
694
694
|
var import_node_url = require("node:url");
|
|
695
|
-
var
|
|
695
|
+
var import_node_path4 = require("node:path");
|
|
696
696
|
var import_picocolors = __toESM(require_picocolors());
|
|
697
697
|
|
|
698
698
|
// src/agent/envStore.ts
|
|
@@ -795,8 +795,8 @@ var AgentEnvStore = class {
|
|
|
795
795
|
|
|
796
796
|
// src/server/agentServer.ts
|
|
797
797
|
var import_node_child_process = require("node:child_process");
|
|
798
|
-
var
|
|
799
|
-
var
|
|
798
|
+
var import_promises3 = require("node:fs/promises");
|
|
799
|
+
var import_node_path3 = require("node:path");
|
|
800
800
|
|
|
801
801
|
// ../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/compose.js
|
|
802
802
|
var compose = (middleware, onError, onNotFound) => {
|
|
@@ -2936,10 +2936,212 @@ var cors = (options) => {
|
|
|
2936
2936
|
};
|
|
2937
2937
|
};
|
|
2938
2938
|
|
|
2939
|
+
// resources/pi-agent/shared/askUserProtocol.ts
|
|
2940
|
+
var ASK_USER_TAG = "ask_user";
|
|
2941
|
+
var ASK_USER_OPEN = `<${ASK_USER_TAG}>`;
|
|
2942
|
+
var ASK_USER_CLOSE = `</${ASK_USER_TAG}>`;
|
|
2943
|
+
var ASK_USER_RESPONSE_TAG = "ask_user_response";
|
|
2944
|
+
var ASK_USER_RESPONSE_OPEN = `<${ASK_USER_RESPONSE_TAG}>`;
|
|
2945
|
+
var ASK_USER_RESPONSE_CLOSE = `</${ASK_USER_RESPONSE_TAG}>`;
|
|
2946
|
+
function hashString(value) {
|
|
2947
|
+
let hash = 0;
|
|
2948
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
2949
|
+
hash = (hash << 5) - hash + value.charCodeAt(index);
|
|
2950
|
+
hash |= 0;
|
|
2951
|
+
}
|
|
2952
|
+
return Math.abs(hash).toString(36);
|
|
2953
|
+
}
|
|
2954
|
+
function isRecord(value) {
|
|
2955
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2956
|
+
}
|
|
2957
|
+
function isNonEmptyString(value) {
|
|
2958
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2959
|
+
}
|
|
2960
|
+
function normalizeTrimmedString(value) {
|
|
2961
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
2962
|
+
}
|
|
2963
|
+
function normalizeQuestionType(value) {
|
|
2964
|
+
return value === "text" || value === "textarea" || value === "boolean" || value === "singleSelect" || value === "multiSelect" ? value : void 0;
|
|
2965
|
+
}
|
|
2966
|
+
function normalizeDefaultValue(value) {
|
|
2967
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
2968
|
+
return value;
|
|
2969
|
+
}
|
|
2970
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
2971
|
+
return [...value];
|
|
2972
|
+
}
|
|
2973
|
+
return void 0;
|
|
2974
|
+
}
|
|
2975
|
+
function normalizeOptions(value) {
|
|
2976
|
+
if (!Array.isArray(value)) {
|
|
2977
|
+
return void 0;
|
|
2978
|
+
}
|
|
2979
|
+
const options = [];
|
|
2980
|
+
for (const item of value) {
|
|
2981
|
+
if (!isRecord(item) || !isNonEmptyString(item.label) || !isNonEmptyString(item.value)) {
|
|
2982
|
+
return void 0;
|
|
2983
|
+
}
|
|
2984
|
+
options.push({
|
|
2985
|
+
label: item.label.trim(),
|
|
2986
|
+
value: item.value.trim()
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
return options;
|
|
2990
|
+
}
|
|
2991
|
+
function normalizeAskUserQuestion(value) {
|
|
2992
|
+
if (!isRecord(value)) {
|
|
2993
|
+
return void 0;
|
|
2994
|
+
}
|
|
2995
|
+
const id = normalizeTrimmedString(value.id);
|
|
2996
|
+
const label = normalizeTrimmedString(value.label);
|
|
2997
|
+
const type = normalizeQuestionType(value.type);
|
|
2998
|
+
if (!id || !label || !type) {
|
|
2999
|
+
return void 0;
|
|
3000
|
+
}
|
|
3001
|
+
const question = {
|
|
3002
|
+
id,
|
|
3003
|
+
label,
|
|
3004
|
+
type
|
|
3005
|
+
};
|
|
3006
|
+
const description = normalizeTrimmedString(value.description);
|
|
3007
|
+
if (description) {
|
|
3008
|
+
question.description = description;
|
|
3009
|
+
}
|
|
3010
|
+
if (typeof value.required === "boolean") {
|
|
3011
|
+
question.required = value.required;
|
|
3012
|
+
}
|
|
3013
|
+
const options = normalizeOptions(value.options);
|
|
3014
|
+
if (type === "singleSelect" || type === "multiSelect") {
|
|
3015
|
+
if (!options || options.length === 0) {
|
|
3016
|
+
return void 0;
|
|
3017
|
+
}
|
|
3018
|
+
question.options = options;
|
|
3019
|
+
}
|
|
3020
|
+
const placeholder = normalizeTrimmedString(value.placeholder);
|
|
3021
|
+
if (placeholder) {
|
|
3022
|
+
question.placeholder = placeholder;
|
|
3023
|
+
}
|
|
3024
|
+
const defaultValue = normalizeDefaultValue(value.defaultValue);
|
|
3025
|
+
if (defaultValue !== void 0) {
|
|
3026
|
+
question.defaultValue = defaultValue;
|
|
3027
|
+
}
|
|
3028
|
+
return question;
|
|
3029
|
+
}
|
|
3030
|
+
function normalizeAskUserRequest(value) {
|
|
3031
|
+
if (!isRecord(value)) {
|
|
3032
|
+
return void 0;
|
|
3033
|
+
}
|
|
3034
|
+
const title = normalizeTrimmedString(value.title);
|
|
3035
|
+
const message = normalizeTrimmedString(value.message);
|
|
3036
|
+
if (!title || !message || !Array.isArray(value.questions) || value.questions.length === 0) {
|
|
3037
|
+
return void 0;
|
|
3038
|
+
}
|
|
3039
|
+
const questions = [];
|
|
3040
|
+
for (const question of value.questions) {
|
|
3041
|
+
const normalized = normalizeAskUserQuestion(question);
|
|
3042
|
+
if (!normalized) {
|
|
3043
|
+
return void 0;
|
|
3044
|
+
}
|
|
3045
|
+
questions.push(normalized);
|
|
3046
|
+
}
|
|
3047
|
+
return {
|
|
3048
|
+
title,
|
|
3049
|
+
message,
|
|
3050
|
+
questions
|
|
3051
|
+
};
|
|
3052
|
+
}
|
|
3053
|
+
function normalizeAskUserResponse(value) {
|
|
3054
|
+
if (!isRecord(value)) {
|
|
3055
|
+
return void 0;
|
|
3056
|
+
}
|
|
3057
|
+
const action = value.action;
|
|
3058
|
+
if (action !== "submit" && action !== "cancel" && action !== "decline") {
|
|
3059
|
+
return void 0;
|
|
3060
|
+
}
|
|
3061
|
+
const answersRecord = isRecord(value.answers) ? value.answers : {};
|
|
3062
|
+
const answers = {};
|
|
3063
|
+
for (const [key, answerValue] of Object.entries(answersRecord)) {
|
|
3064
|
+
const normalized = normalizeDefaultValue(answerValue);
|
|
3065
|
+
if (normalized !== void 0) {
|
|
3066
|
+
answers[key] = normalized;
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
return {
|
|
3070
|
+
action,
|
|
3071
|
+
answers
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
function createAskUserToolCallId(request) {
|
|
3075
|
+
return `ask_user_${hashString(JSON.stringify(request))}`;
|
|
3076
|
+
}
|
|
3077
|
+
function parseAskUserRequestFromText(text) {
|
|
3078
|
+
const trimmed = text.trim();
|
|
3079
|
+
if (!trimmed.startsWith(ASK_USER_OPEN) || !trimmed.endsWith(ASK_USER_CLOSE)) {
|
|
3080
|
+
return void 0;
|
|
3081
|
+
}
|
|
3082
|
+
const body = trimmed.slice(ASK_USER_OPEN.length, trimmed.length - ASK_USER_CLOSE.length).trim();
|
|
3083
|
+
if (!body) {
|
|
3084
|
+
return void 0;
|
|
3085
|
+
}
|
|
3086
|
+
try {
|
|
3087
|
+
return normalizeAskUserRequest(JSON.parse(body));
|
|
3088
|
+
} catch {
|
|
3089
|
+
return void 0;
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
function formatAskUserResponsePrompt(response) {
|
|
3093
|
+
return [
|
|
3094
|
+
"The user submitted structured clarification answers.",
|
|
3095
|
+
"",
|
|
3096
|
+
`${ASK_USER_RESPONSE_OPEN}`,
|
|
3097
|
+
JSON.stringify(response, null, 2),
|
|
3098
|
+
`${ASK_USER_RESPONSE_CLOSE}`,
|
|
3099
|
+
"",
|
|
3100
|
+
"Continue the planning workflow using these answers."
|
|
3101
|
+
].join("\n");
|
|
3102
|
+
}
|
|
3103
|
+
function parseAskUserResponseFromToolOutput(output) {
|
|
3104
|
+
return normalizeAskUserResponse(output);
|
|
3105
|
+
}
|
|
3106
|
+
|
|
2939
3107
|
// src/server/eventBridge.ts
|
|
2940
3108
|
function createEventBridge(params) {
|
|
2941
|
-
const { messageId, onChunk, onDone, onError } = params;
|
|
3109
|
+
const { messageId, onChunk, onDone, onError, onAskUser } = params;
|
|
2942
3110
|
const activeToolCalls = /* @__PURE__ */ new Map();
|
|
3111
|
+
let activeTextBuffer = null;
|
|
3112
|
+
function flushBufferedTextToStream(close) {
|
|
3113
|
+
if (!activeTextBuffer) {
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
onChunk({ type: "text-start", id: messageId });
|
|
3117
|
+
if (activeTextBuffer.text.length > 0) {
|
|
3118
|
+
onChunk({ type: "text-delta", id: messageId, delta: activeTextBuffer.text });
|
|
3119
|
+
}
|
|
3120
|
+
if (close) {
|
|
3121
|
+
onChunk({ type: "text-end", id: messageId });
|
|
3122
|
+
activeTextBuffer = null;
|
|
3123
|
+
return;
|
|
3124
|
+
}
|
|
3125
|
+
activeTextBuffer = { text: "", mode: "plain" };
|
|
3126
|
+
}
|
|
3127
|
+
function maybeEmitAskUser(text) {
|
|
3128
|
+
const request = parseAskUserRequestFromText(text);
|
|
3129
|
+
if (!request) {
|
|
3130
|
+
return false;
|
|
3131
|
+
}
|
|
3132
|
+
const toolCallId = createAskUserToolCallId(request);
|
|
3133
|
+
onAskUser?.({ toolCallId, request });
|
|
3134
|
+
onChunk({ type: "tool-input-start", toolCallId, toolName: "ask_user", dynamic: true, title: request.title });
|
|
3135
|
+
onChunk({
|
|
3136
|
+
type: "tool-input-available",
|
|
3137
|
+
toolCallId,
|
|
3138
|
+
toolName: "ask_user",
|
|
3139
|
+
input: request,
|
|
3140
|
+
dynamic: true,
|
|
3141
|
+
title: request.title
|
|
3142
|
+
});
|
|
3143
|
+
return true;
|
|
3144
|
+
}
|
|
2943
3145
|
function handleEvent(event) {
|
|
2944
3146
|
switch (event.type) {
|
|
2945
3147
|
case "message_update": {
|
|
@@ -2963,15 +3165,42 @@ function createEventBridge(params) {
|
|
|
2963
3165
|
}
|
|
2964
3166
|
switch (assistantEvent.type) {
|
|
2965
3167
|
case "text_start": {
|
|
2966
|
-
|
|
3168
|
+
activeTextBuffer = { text: "", mode: "pending" };
|
|
2967
3169
|
break;
|
|
2968
3170
|
}
|
|
2969
3171
|
case "text_delta": {
|
|
2970
|
-
|
|
3172
|
+
if (!activeTextBuffer) {
|
|
3173
|
+
onChunk({ type: "text-start", id: messageId });
|
|
3174
|
+
onChunk({ type: "text-delta", id: messageId, delta: assistantEvent.delta ?? "" });
|
|
3175
|
+
activeTextBuffer = { text: "", mode: "plain" };
|
|
3176
|
+
break;
|
|
3177
|
+
}
|
|
3178
|
+
if (activeTextBuffer.mode === "plain") {
|
|
3179
|
+
onChunk({ type: "text-delta", id: messageId, delta: assistantEvent.delta ?? "" });
|
|
3180
|
+
break;
|
|
3181
|
+
}
|
|
3182
|
+
activeTextBuffer.text += assistantEvent.delta ?? "";
|
|
3183
|
+
if ("<ask_user>".startsWith(activeTextBuffer.text) || activeTextBuffer.text.startsWith("<ask_user>")) {
|
|
3184
|
+
break;
|
|
3185
|
+
}
|
|
3186
|
+
flushBufferedTextToStream(false);
|
|
2971
3187
|
break;
|
|
2972
3188
|
}
|
|
2973
3189
|
case "text_end": {
|
|
2974
|
-
|
|
3190
|
+
if (!activeTextBuffer) {
|
|
3191
|
+
onChunk({ type: "text-end", id: messageId });
|
|
3192
|
+
break;
|
|
3193
|
+
}
|
|
3194
|
+
if (activeTextBuffer.mode === "plain") {
|
|
3195
|
+
onChunk({ type: "text-end", id: messageId });
|
|
3196
|
+
activeTextBuffer = null;
|
|
3197
|
+
break;
|
|
3198
|
+
}
|
|
3199
|
+
if (!maybeEmitAskUser(activeTextBuffer.text)) {
|
|
3200
|
+
flushBufferedTextToStream(true);
|
|
3201
|
+
} else {
|
|
3202
|
+
activeTextBuffer = null;
|
|
3203
|
+
}
|
|
2975
3204
|
break;
|
|
2976
3205
|
}
|
|
2977
3206
|
case "thinking_start": {
|
|
@@ -3050,6 +3279,854 @@ function createEventBridge(params) {
|
|
|
3050
3279
|
return { handleEvent };
|
|
3051
3280
|
}
|
|
3052
3281
|
|
|
3282
|
+
// src/agent/modelsConfig.ts
|
|
3283
|
+
var import_promises2 = require("node:fs/promises");
|
|
3284
|
+
var import_node_path2 = require("node:path");
|
|
3285
|
+
function createDefaultState2() {
|
|
3286
|
+
return {
|
|
3287
|
+
providers: {}
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
async function readModelsConfig(filePath) {
|
|
3291
|
+
try {
|
|
3292
|
+
const raw2 = await (0, import_promises2.readFile)(filePath, "utf8");
|
|
3293
|
+
const parsed = JSON.parse(raw2);
|
|
3294
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.providers !== "object" || parsed.providers === null) {
|
|
3295
|
+
return createDefaultState2();
|
|
3296
|
+
}
|
|
3297
|
+
return {
|
|
3298
|
+
providers: { ...parsed.providers }
|
|
3299
|
+
};
|
|
3300
|
+
} catch (error) {
|
|
3301
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
3302
|
+
return createDefaultState2();
|
|
3303
|
+
}
|
|
3304
|
+
return createDefaultState2();
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
async function writeModelsConfig(filePath, state) {
|
|
3308
|
+
const directory = (0, import_node_path2.dirname)(filePath);
|
|
3309
|
+
await (0, import_promises2.mkdir)(directory, {
|
|
3310
|
+
recursive: true,
|
|
3311
|
+
mode: 448
|
|
3312
|
+
});
|
|
3313
|
+
await (0, import_promises2.writeFile)(filePath, `${JSON.stringify(state, null, 2)}
|
|
3314
|
+
`, {
|
|
3315
|
+
encoding: "utf8",
|
|
3316
|
+
mode: 384
|
|
3317
|
+
});
|
|
3318
|
+
await (0, import_promises2.chmod)(filePath, 384);
|
|
3319
|
+
}
|
|
3320
|
+
async function upsertModelsProvider(filePath, providerId, providerConfig) {
|
|
3321
|
+
const state = await readModelsConfig(filePath);
|
|
3322
|
+
await writeModelsConfig(filePath, {
|
|
3323
|
+
providers: {
|
|
3324
|
+
...state.providers,
|
|
3325
|
+
[providerId]: providerConfig
|
|
3326
|
+
}
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
async function removeModelsProvider(filePath, providerId) {
|
|
3330
|
+
const state = await readModelsConfig(filePath);
|
|
3331
|
+
const nextProviders = { ...state.providers };
|
|
3332
|
+
delete nextProviders[providerId];
|
|
3333
|
+
await writeModelsConfig(filePath, {
|
|
3334
|
+
providers: nextProviders
|
|
3335
|
+
});
|
|
3336
|
+
}
|
|
3337
|
+
function createCustomOpenAiProviderConfig(params) {
|
|
3338
|
+
return {
|
|
3339
|
+
baseUrl: params.baseUrl,
|
|
3340
|
+
apiKey: "env:CUSTOM_OPENAI_API_KEY",
|
|
3341
|
+
api: "openai-completions",
|
|
3342
|
+
models: [
|
|
3343
|
+
{
|
|
3344
|
+
id: params.modelId,
|
|
3345
|
+
name: params.modelId,
|
|
3346
|
+
reasoning: false,
|
|
3347
|
+
input: ["text"],
|
|
3348
|
+
contextWindow: 128e3,
|
|
3349
|
+
maxTokens: 16384,
|
|
3350
|
+
cost: {
|
|
3351
|
+
input: 0,
|
|
3352
|
+
output: 0,
|
|
3353
|
+
cacheRead: 0,
|
|
3354
|
+
cacheWrite: 0
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
]
|
|
3358
|
+
};
|
|
3359
|
+
}
|
|
3360
|
+
function createAzureProviderConfig(params) {
|
|
3361
|
+
const providerConfig = {
|
|
3362
|
+
baseUrl: params.baseUrl,
|
|
3363
|
+
apiKey: "env:AZURE_OPENAI_API_KEY"
|
|
3364
|
+
};
|
|
3365
|
+
if (params.useCustomModel) {
|
|
3366
|
+
providerConfig.api = "azure-openai-responses";
|
|
3367
|
+
providerConfig.models = [
|
|
3368
|
+
{
|
|
3369
|
+
id: params.modelId,
|
|
3370
|
+
name: params.modelId,
|
|
3371
|
+
reasoning: false,
|
|
3372
|
+
input: ["text"],
|
|
3373
|
+
contextWindow: 128e3,
|
|
3374
|
+
maxTokens: 16384,
|
|
3375
|
+
cost: {
|
|
3376
|
+
input: 0,
|
|
3377
|
+
output: 0,
|
|
3378
|
+
cacheRead: 0,
|
|
3379
|
+
cacheWrite: 0
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
];
|
|
3383
|
+
}
|
|
3384
|
+
return providerConfig;
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
// src/agent/authFlows.ts
|
|
3388
|
+
function trimOrUndefined(value) {
|
|
3389
|
+
const trimmed = value?.trim();
|
|
3390
|
+
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
3391
|
+
}
|
|
3392
|
+
async function saveGenericApiKey(params) {
|
|
3393
|
+
params.authStorage.set(params.providerId, {
|
|
3394
|
+
type: "api_key",
|
|
3395
|
+
key: params.apiKey.trim()
|
|
3396
|
+
});
|
|
3397
|
+
}
|
|
3398
|
+
async function saveCustomOpenAiConfig(params) {
|
|
3399
|
+
params.authStorage.set("custom-openai", {
|
|
3400
|
+
type: "api_key",
|
|
3401
|
+
key: params.apiKey.trim()
|
|
3402
|
+
});
|
|
3403
|
+
await params.envStore.removeMany([
|
|
3404
|
+
"CUSTOM_OPENAI_API_KEY"
|
|
3405
|
+
]);
|
|
3406
|
+
await params.envStore.setMany({
|
|
3407
|
+
CUSTOM_OPENAI_API_KEY: params.apiKey.trim()
|
|
3408
|
+
});
|
|
3409
|
+
await upsertModelsProvider(params.modelsJsonPath, "custom-openai", createCustomOpenAiProviderConfig({
|
|
3410
|
+
baseUrl: params.baseUrl.trim(),
|
|
3411
|
+
modelId: params.modelId.trim()
|
|
3412
|
+
}));
|
|
3413
|
+
params.settingsManager.setDefaultModelAndProvider("custom-openai", params.modelId.trim());
|
|
3414
|
+
}
|
|
3415
|
+
async function saveAzureConfig(params) {
|
|
3416
|
+
const modelId = params.modelId.trim();
|
|
3417
|
+
const useCustomModel = params.useCustomModel ?? false;
|
|
3418
|
+
params.authStorage.set("azure-openai-responses", {
|
|
3419
|
+
type: "api_key",
|
|
3420
|
+
key: params.apiKey.trim()
|
|
3421
|
+
});
|
|
3422
|
+
await params.envStore.removeMany([
|
|
3423
|
+
"AZURE_OPENAI_API_KEY",
|
|
3424
|
+
"AZURE_OPENAI_API_VERSION",
|
|
3425
|
+
"AZURE_OPENAI_DEPLOYMENT_NAME_MAP"
|
|
3426
|
+
]);
|
|
3427
|
+
const envValues = {
|
|
3428
|
+
AZURE_OPENAI_API_KEY: params.apiKey.trim()
|
|
3429
|
+
};
|
|
3430
|
+
const trimmedApiVersion = trimOrUndefined(params.apiVersion);
|
|
3431
|
+
if (trimmedApiVersion) {
|
|
3432
|
+
envValues.AZURE_OPENAI_API_VERSION = trimmedApiVersion;
|
|
3433
|
+
}
|
|
3434
|
+
const trimmedDeploymentName = trimOrUndefined(params.deploymentName);
|
|
3435
|
+
if (trimmedDeploymentName && trimmedDeploymentName !== modelId) {
|
|
3436
|
+
envValues.AZURE_OPENAI_DEPLOYMENT_NAME_MAP = JSON.stringify({
|
|
3437
|
+
[modelId]: trimmedDeploymentName
|
|
3438
|
+
});
|
|
3439
|
+
}
|
|
3440
|
+
await params.envStore.setMany(envValues);
|
|
3441
|
+
await upsertModelsProvider(params.modelsJsonPath, "azure-openai-responses", createAzureProviderConfig({
|
|
3442
|
+
baseUrl: params.baseUrl.trim(),
|
|
3443
|
+
modelId,
|
|
3444
|
+
useCustomModel
|
|
3445
|
+
}));
|
|
3446
|
+
params.settingsManager.setDefaultModelAndProvider("azure-openai-responses", modelId);
|
|
3447
|
+
}
|
|
3448
|
+
async function saveBedrockConfig(params) {
|
|
3449
|
+
params.authStorage.set("amazon-bedrock", {
|
|
3450
|
+
type: "api_key",
|
|
3451
|
+
key: "aws-configured"
|
|
3452
|
+
});
|
|
3453
|
+
await params.envStore.removeMany([
|
|
3454
|
+
"AWS_PROFILE",
|
|
3455
|
+
"AWS_ACCESS_KEY_ID",
|
|
3456
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
3457
|
+
"AWS_SESSION_TOKEN",
|
|
3458
|
+
"AWS_REGION",
|
|
3459
|
+
"AWS_DEFAULT_REGION",
|
|
3460
|
+
"AWS_BEARER_TOKEN_BEDROCK"
|
|
3461
|
+
]);
|
|
3462
|
+
const envValues = {
|
|
3463
|
+
AWS_REGION: params.region.trim(),
|
|
3464
|
+
AWS_DEFAULT_REGION: params.region.trim()
|
|
3465
|
+
};
|
|
3466
|
+
const profile = trimOrUndefined(params.profile);
|
|
3467
|
+
if (profile) {
|
|
3468
|
+
envValues.AWS_PROFILE = profile;
|
|
3469
|
+
}
|
|
3470
|
+
const accessKeyId = trimOrUndefined(params.accessKeyId);
|
|
3471
|
+
const secretAccessKey = trimOrUndefined(params.secretAccessKey);
|
|
3472
|
+
if (accessKeyId && secretAccessKey) {
|
|
3473
|
+
envValues.AWS_ACCESS_KEY_ID = accessKeyId;
|
|
3474
|
+
envValues.AWS_SECRET_ACCESS_KEY = secretAccessKey;
|
|
3475
|
+
}
|
|
3476
|
+
const sessionToken = trimOrUndefined(params.sessionToken);
|
|
3477
|
+
if (sessionToken) {
|
|
3478
|
+
envValues.AWS_SESSION_TOKEN = sessionToken;
|
|
3479
|
+
}
|
|
3480
|
+
await params.envStore.setMany(envValues);
|
|
3481
|
+
params.settingsManager.setDefaultModelAndProvider("amazon-bedrock", params.modelId.trim());
|
|
3482
|
+
}
|
|
3483
|
+
async function clearProviderConfig(params) {
|
|
3484
|
+
params.authStorage.remove(params.providerId);
|
|
3485
|
+
if (params.providerId === "custom-openai") {
|
|
3486
|
+
await params.envStore.removeMany([
|
|
3487
|
+
"CUSTOM_OPENAI_API_KEY"
|
|
3488
|
+
]);
|
|
3489
|
+
await removeModelsProvider(params.modelsJsonPath, "custom-openai");
|
|
3490
|
+
}
|
|
3491
|
+
if (params.providerId === "azure-openai-responses") {
|
|
3492
|
+
await params.envStore.removeMany([
|
|
3493
|
+
"AZURE_OPENAI_API_KEY",
|
|
3494
|
+
"AZURE_OPENAI_API_VERSION",
|
|
3495
|
+
"AZURE_OPENAI_DEPLOYMENT_NAME_MAP"
|
|
3496
|
+
]);
|
|
3497
|
+
await removeModelsProvider(params.modelsJsonPath, "azure-openai-responses");
|
|
3498
|
+
}
|
|
3499
|
+
if (params.providerId === "amazon-bedrock") {
|
|
3500
|
+
await params.envStore.removeMany([
|
|
3501
|
+
"AWS_PROFILE",
|
|
3502
|
+
"AWS_ACCESS_KEY_ID",
|
|
3503
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
3504
|
+
"AWS_SESSION_TOKEN",
|
|
3505
|
+
"AWS_REGION",
|
|
3506
|
+
"AWS_DEFAULT_REGION",
|
|
3507
|
+
"AWS_BEARER_TOKEN_BEDROCK"
|
|
3508
|
+
]);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3512
|
+
// src/agent/providerCatalog.ts
|
|
3513
|
+
var OAUTH_ONLY_PROVIDER_IDS = /* @__PURE__ */ new Set([
|
|
3514
|
+
"github-copilot",
|
|
3515
|
+
"openai-codex",
|
|
3516
|
+
"google-gemini-cli",
|
|
3517
|
+
"google-antigravity"
|
|
3518
|
+
]);
|
|
3519
|
+
var PROVIDER_LABELS = {
|
|
3520
|
+
anthropic: "Anthropic (Claude)",
|
|
3521
|
+
openai: "OpenAI",
|
|
3522
|
+
google: "Google (Gemini)",
|
|
3523
|
+
"google-vertex": "Google Vertex AI",
|
|
3524
|
+
"azure-openai-responses": "Azure OpenAI",
|
|
3525
|
+
"amazon-bedrock": "Amazon Bedrock",
|
|
3526
|
+
xai: "xAI (Grok)",
|
|
3527
|
+
groq: "Groq",
|
|
3528
|
+
cerebras: "Cerebras",
|
|
3529
|
+
openrouter: "OpenRouter",
|
|
3530
|
+
"vercel-ai-gateway": "Vercel AI Gateway",
|
|
3531
|
+
zai: "ZAI",
|
|
3532
|
+
mistral: "Mistral",
|
|
3533
|
+
minimax: "MiniMax",
|
|
3534
|
+
"minimax-cn": "MiniMax CN",
|
|
3535
|
+
huggingface: "Hugging Face",
|
|
3536
|
+
opencode: "OpenCode",
|
|
3537
|
+
"opencode-go": "OpenCode Go",
|
|
3538
|
+
"kimi-coding": "Kimi Coding",
|
|
3539
|
+
"custom-openai": "Custom OpenAI-Compatible",
|
|
3540
|
+
"google-gemini-cli": "Google Gemini CLI",
|
|
3541
|
+
"google-antigravity": "Google Antigravity",
|
|
3542
|
+
"openai-codex": "OpenAI Codex",
|
|
3543
|
+
"github-copilot": "GitHub Copilot"
|
|
3544
|
+
};
|
|
3545
|
+
var PROVIDER_HINTS = {
|
|
3546
|
+
anthropic: "recommended",
|
|
3547
|
+
"custom-openai": "custom base URL + API key",
|
|
3548
|
+
"azure-openai-responses": "API key + base URL/resource + deployment",
|
|
3549
|
+
"amazon-bedrock": "AWS profile or access key pair",
|
|
3550
|
+
"openai-codex": "browser auth",
|
|
3551
|
+
"github-copilot": "browser auth",
|
|
3552
|
+
"google-gemini-cli": "browser auth",
|
|
3553
|
+
"google-antigravity": "browser auth"
|
|
3554
|
+
};
|
|
3555
|
+
function humanizeProviderId(providerId) {
|
|
3556
|
+
return providerId.split("-").map((part) => {
|
|
3557
|
+
if (!part) {
|
|
3558
|
+
return part;
|
|
3559
|
+
}
|
|
3560
|
+
if (part.length <= 3) {
|
|
3561
|
+
return part.toUpperCase();
|
|
3562
|
+
}
|
|
3563
|
+
return part[0].toUpperCase() + part.slice(1);
|
|
3564
|
+
}).join(" ");
|
|
3565
|
+
}
|
|
3566
|
+
function toLabel(providerId) {
|
|
3567
|
+
return PROVIDER_LABELS[providerId] || humanizeProviderId(providerId);
|
|
3568
|
+
}
|
|
3569
|
+
function toHint(providerId) {
|
|
3570
|
+
return PROVIDER_HINTS[providerId];
|
|
3571
|
+
}
|
|
3572
|
+
function compareProviderOptions(a, b) {
|
|
3573
|
+
return a.label.localeCompare(b.label);
|
|
3574
|
+
}
|
|
3575
|
+
function getBrowserAuthProviderOptions(oauthProviders) {
|
|
3576
|
+
return oauthProviders.map((provider) => ({
|
|
3577
|
+
id: provider.id,
|
|
3578
|
+
label: provider.name || toLabel(provider.id),
|
|
3579
|
+
hint: toHint(provider.id),
|
|
3580
|
+
flow: "oauth"
|
|
3581
|
+
})).sort(compareProviderOptions);
|
|
3582
|
+
}
|
|
3583
|
+
function getApiKeyProviderOptions(providerIds) {
|
|
3584
|
+
const baseProviders = providerIds.filter((providerId) => !OAUTH_ONLY_PROVIDER_IDS.has(providerId)).map((providerId) => {
|
|
3585
|
+
let flow = "generic-api-key";
|
|
3586
|
+
if (providerId === "azure-openai-responses") {
|
|
3587
|
+
flow = "azure-openai-responses";
|
|
3588
|
+
} else if (providerId === "amazon-bedrock") {
|
|
3589
|
+
flow = "amazon-bedrock";
|
|
3590
|
+
}
|
|
3591
|
+
return {
|
|
3592
|
+
id: providerId,
|
|
3593
|
+
label: toLabel(providerId),
|
|
3594
|
+
hint: toHint(providerId),
|
|
3595
|
+
flow
|
|
3596
|
+
};
|
|
3597
|
+
});
|
|
3598
|
+
baseProviders.push({
|
|
3599
|
+
id: "custom-openai",
|
|
3600
|
+
label: toLabel("custom-openai"),
|
|
3601
|
+
hint: toHint("custom-openai"),
|
|
3602
|
+
flow: "custom-openai"
|
|
3603
|
+
});
|
|
3604
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
3605
|
+
for (const provider of baseProviders) {
|
|
3606
|
+
deduped.set(provider.id, provider);
|
|
3607
|
+
}
|
|
3608
|
+
return Array.from(deduped.values()).sort(compareProviderOptions);
|
|
3609
|
+
}
|
|
3610
|
+
function getProviderLabel(providerId) {
|
|
3611
|
+
return toLabel(providerId);
|
|
3612
|
+
}
|
|
3613
|
+
function getProviderHint(providerId) {
|
|
3614
|
+
return toHint(providerId);
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
// src/agent/authInteractivity.ts
|
|
3618
|
+
function buildFieldUiHint(field) {
|
|
3619
|
+
return {
|
|
3620
|
+
component: field.component,
|
|
3621
|
+
...field.component === "password" ? { secret: true } : {},
|
|
3622
|
+
...field.placeholder ? { placeholder: field.placeholder } : {},
|
|
3623
|
+
...field.options ? { options: field.options } : {},
|
|
3624
|
+
...field.dependsOn ? { dependsOn: field.dependsOn } : {}
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
function buildProperty(field) {
|
|
3628
|
+
return {
|
|
3629
|
+
type: "string",
|
|
3630
|
+
title: field.title,
|
|
3631
|
+
...field.description ? { description: field.description } : {},
|
|
3632
|
+
...field.options ? { enum: field.options.map((option) => option.value) } : {},
|
|
3633
|
+
...field.defaultValue ? { default: field.defaultValue } : {}
|
|
3634
|
+
};
|
|
3635
|
+
}
|
|
3636
|
+
function toSelectOptions(values) {
|
|
3637
|
+
return values.map((value) => ({ label: value, value }));
|
|
3638
|
+
}
|
|
3639
|
+
function getProviderFormFields(params) {
|
|
3640
|
+
const provider = params.provider;
|
|
3641
|
+
switch (provider.flow) {
|
|
3642
|
+
case "custom-openai":
|
|
3643
|
+
return [
|
|
3644
|
+
{
|
|
3645
|
+
name: "baseUrl",
|
|
3646
|
+
title: "Custom OpenAI base URL",
|
|
3647
|
+
required: true,
|
|
3648
|
+
component: "text",
|
|
3649
|
+
placeholder: "https://my-proxy.example.com/v1"
|
|
3650
|
+
},
|
|
3651
|
+
{
|
|
3652
|
+
name: "apiKey",
|
|
3653
|
+
title: `${provider.label} API key`,
|
|
3654
|
+
required: true,
|
|
3655
|
+
component: "password"
|
|
3656
|
+
},
|
|
3657
|
+
{
|
|
3658
|
+
name: "modelId",
|
|
3659
|
+
title: "Model ID",
|
|
3660
|
+
required: true,
|
|
3661
|
+
component: "text",
|
|
3662
|
+
placeholder: "gpt-4o"
|
|
3663
|
+
}
|
|
3664
|
+
];
|
|
3665
|
+
case "azure-openai-responses":
|
|
3666
|
+
return [
|
|
3667
|
+
{
|
|
3668
|
+
name: "apiKey",
|
|
3669
|
+
title: "Azure OpenAI API key",
|
|
3670
|
+
required: true,
|
|
3671
|
+
component: "password"
|
|
3672
|
+
},
|
|
3673
|
+
{
|
|
3674
|
+
name: "configMode",
|
|
3675
|
+
title: "Azure OpenAI configuration",
|
|
3676
|
+
required: true,
|
|
3677
|
+
defaultValue: "base-url",
|
|
3678
|
+
component: "select",
|
|
3679
|
+
options: [
|
|
3680
|
+
{ label: "Use a base URL", value: "base-url" },
|
|
3681
|
+
{ label: "Use an Azure resource name", value: "resource-name" }
|
|
3682
|
+
]
|
|
3683
|
+
},
|
|
3684
|
+
{
|
|
3685
|
+
name: "baseUrl",
|
|
3686
|
+
title: "Azure OpenAI base URL",
|
|
3687
|
+
required: true,
|
|
3688
|
+
component: "text",
|
|
3689
|
+
placeholder: "https://my-resource.openai.azure.com/openai/v1",
|
|
3690
|
+
dependsOn: { field: "configMode", equals: "base-url" }
|
|
3691
|
+
},
|
|
3692
|
+
{
|
|
3693
|
+
name: "resourceName",
|
|
3694
|
+
title: "Azure resource name",
|
|
3695
|
+
required: true,
|
|
3696
|
+
component: "text",
|
|
3697
|
+
placeholder: "my-resource",
|
|
3698
|
+
dependsOn: { field: "configMode", equals: "resource-name" }
|
|
3699
|
+
},
|
|
3700
|
+
{
|
|
3701
|
+
name: "modelId",
|
|
3702
|
+
title: "Model ID",
|
|
3703
|
+
required: true,
|
|
3704
|
+
component: params.modelIdsByProvider[provider.id]?.length ? "select" : "text",
|
|
3705
|
+
options: params.modelIdsByProvider[provider.id]?.length ? toSelectOptions(params.modelIdsByProvider[provider.id]) : void 0,
|
|
3706
|
+
defaultValue: params.modelIdsByProvider[provider.id]?.[0] || "gpt-4.1"
|
|
3707
|
+
},
|
|
3708
|
+
{
|
|
3709
|
+
name: "deploymentName",
|
|
3710
|
+
title: "Deployment name",
|
|
3711
|
+
component: "text",
|
|
3712
|
+
placeholder: "gpt-4.1"
|
|
3713
|
+
},
|
|
3714
|
+
{
|
|
3715
|
+
name: "apiVersion",
|
|
3716
|
+
title: "API version",
|
|
3717
|
+
component: "text",
|
|
3718
|
+
defaultValue: "2025-03-01-preview",
|
|
3719
|
+
placeholder: "2025-03-01-preview"
|
|
3720
|
+
}
|
|
3721
|
+
];
|
|
3722
|
+
case "amazon-bedrock":
|
|
3723
|
+
return [
|
|
3724
|
+
{
|
|
3725
|
+
name: "authMode",
|
|
3726
|
+
title: "Amazon Bedrock authentication",
|
|
3727
|
+
required: true,
|
|
3728
|
+
defaultValue: "profile",
|
|
3729
|
+
component: "select",
|
|
3730
|
+
options: [
|
|
3731
|
+
{ label: "Use an AWS profile", value: "profile" },
|
|
3732
|
+
{ label: "Paste AWS access keys", value: "access-keys" }
|
|
3733
|
+
]
|
|
3734
|
+
},
|
|
3735
|
+
{
|
|
3736
|
+
name: "profile",
|
|
3737
|
+
title: "AWS profile name",
|
|
3738
|
+
required: true,
|
|
3739
|
+
defaultValue: "default",
|
|
3740
|
+
component: "text",
|
|
3741
|
+
placeholder: "default",
|
|
3742
|
+
dependsOn: { field: "authMode", equals: "profile" }
|
|
3743
|
+
},
|
|
3744
|
+
{
|
|
3745
|
+
name: "accessKeyId",
|
|
3746
|
+
title: "AWS access key ID",
|
|
3747
|
+
required: true,
|
|
3748
|
+
component: "text",
|
|
3749
|
+
dependsOn: { field: "authMode", equals: "access-keys" }
|
|
3750
|
+
},
|
|
3751
|
+
{
|
|
3752
|
+
name: "secretAccessKey",
|
|
3753
|
+
title: "AWS secret access key",
|
|
3754
|
+
required: true,
|
|
3755
|
+
component: "password",
|
|
3756
|
+
dependsOn: { field: "authMode", equals: "access-keys" }
|
|
3757
|
+
},
|
|
3758
|
+
{
|
|
3759
|
+
name: "sessionToken",
|
|
3760
|
+
title: "AWS session token",
|
|
3761
|
+
component: "password",
|
|
3762
|
+
dependsOn: { field: "authMode", equals: "access-keys" }
|
|
3763
|
+
},
|
|
3764
|
+
{
|
|
3765
|
+
name: "region",
|
|
3766
|
+
title: "AWS region",
|
|
3767
|
+
required: true,
|
|
3768
|
+
defaultValue: "us-east-1",
|
|
3769
|
+
component: "text",
|
|
3770
|
+
placeholder: "us-east-1"
|
|
3771
|
+
},
|
|
3772
|
+
{
|
|
3773
|
+
name: "modelId",
|
|
3774
|
+
title: "Bedrock model ID",
|
|
3775
|
+
required: true,
|
|
3776
|
+
component: params.modelIdsByProvider[provider.id]?.length ? "select" : "text",
|
|
3777
|
+
options: params.modelIdsByProvider[provider.id]?.length ? toSelectOptions(params.modelIdsByProvider[provider.id]) : void 0,
|
|
3778
|
+
defaultValue: params.modelIdsByProvider[provider.id]?.[0] || "anthropic.claude-3-7-sonnet-20250219-v1:0"
|
|
3779
|
+
}
|
|
3780
|
+
];
|
|
3781
|
+
default:
|
|
3782
|
+
return [
|
|
3783
|
+
{
|
|
3784
|
+
name: "apiKey",
|
|
3785
|
+
title: `${provider.label} API key`,
|
|
3786
|
+
required: true,
|
|
3787
|
+
component: "password"
|
|
3788
|
+
}
|
|
3789
|
+
];
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
function buildProviderFormContract(params) {
|
|
3793
|
+
if (params.provider.flow === "oauth") {
|
|
3794
|
+
return null;
|
|
3795
|
+
}
|
|
3796
|
+
const fields = getProviderFormFields(params);
|
|
3797
|
+
const properties = Object.fromEntries(fields.map((field) => [field.name, buildProperty(field)]));
|
|
3798
|
+
const required = fields.filter((field) => field.required).map((field) => field.name);
|
|
3799
|
+
const fieldHints = Object.fromEntries(fields.map((field) => [field.name, buildFieldUiHint(field)]));
|
|
3800
|
+
return {
|
|
3801
|
+
inputSchema: {
|
|
3802
|
+
type: "object",
|
|
3803
|
+
properties,
|
|
3804
|
+
required
|
|
3805
|
+
},
|
|
3806
|
+
uiHints: {
|
|
3807
|
+
submitLabel: "Save credentials",
|
|
3808
|
+
order: fields.map((field) => field.name),
|
|
3809
|
+
fields: fieldHints
|
|
3810
|
+
}
|
|
3811
|
+
};
|
|
3812
|
+
}
|
|
3813
|
+
function getOAuthDescriptor(params, provider) {
|
|
3814
|
+
return {
|
|
3815
|
+
id: provider.id,
|
|
3816
|
+
label: provider.name || getProviderLabel(provider.id),
|
|
3817
|
+
hint: getProviderHint(provider.id),
|
|
3818
|
+
flow: "oauth",
|
|
3819
|
+
authMode: "oauth",
|
|
3820
|
+
configured: params.authStorage.hasAuth(provider.id),
|
|
3821
|
+
canLogin: true,
|
|
3822
|
+
canLogout: params.authStorage.hasAuth(provider.id),
|
|
3823
|
+
inputSchema: null,
|
|
3824
|
+
uiHints: null,
|
|
3825
|
+
oauth: { usesCallbackServer: provider.usesCallbackServer }
|
|
3826
|
+
};
|
|
3827
|
+
}
|
|
3828
|
+
function getApiKeyDescriptor(params, provider) {
|
|
3829
|
+
const contract = buildProviderFormContract({
|
|
3830
|
+
provider,
|
|
3831
|
+
modelIdsByProvider: params.modelIdsByProvider
|
|
3832
|
+
});
|
|
3833
|
+
return {
|
|
3834
|
+
id: provider.id,
|
|
3835
|
+
label: provider.label,
|
|
3836
|
+
hint: provider.hint,
|
|
3837
|
+
flow: provider.flow,
|
|
3838
|
+
authMode: "api-key",
|
|
3839
|
+
configured: params.authStorage.hasAuth(provider.id),
|
|
3840
|
+
canLogin: true,
|
|
3841
|
+
canLogout: params.authStorage.hasAuth(provider.id),
|
|
3842
|
+
inputSchema: contract?.inputSchema ?? null,
|
|
3843
|
+
uiHints: contract?.uiHints ?? null,
|
|
3844
|
+
oauth: null
|
|
3845
|
+
};
|
|
3846
|
+
}
|
|
3847
|
+
function buildProviderCatalog(params) {
|
|
3848
|
+
const oauthProviders = getBrowserAuthProviderOptions(params.browserProviders).map(
|
|
3849
|
+
(provider) => getOAuthDescriptor(
|
|
3850
|
+
params,
|
|
3851
|
+
params.browserProviders.find((item) => item.id === provider.id) ?? { id: provider.id, name: provider.label }
|
|
3852
|
+
)
|
|
3853
|
+
);
|
|
3854
|
+
const apiKeyProviders = getApiKeyProviderOptions(params.apiKeyProviderIds).map((provider) => getApiKeyDescriptor(params, provider));
|
|
3855
|
+
return [...oauthProviders, ...apiKeyProviders].sort((left, right) => left.label.localeCompare(right.label));
|
|
3856
|
+
}
|
|
3857
|
+
function listProviderOptions(params) {
|
|
3858
|
+
return [
|
|
3859
|
+
...getBrowserAuthProviderOptions(params.browserProviders),
|
|
3860
|
+
...getApiKeyProviderOptions(params.apiKeyProviderIds)
|
|
3861
|
+
].sort((left, right) => left.label.localeCompare(right.label));
|
|
3862
|
+
}
|
|
3863
|
+
function findProviderOption(params) {
|
|
3864
|
+
return listProviderOptions({
|
|
3865
|
+
browserProviders: params.browserProviders,
|
|
3866
|
+
apiKeyProviderIds: params.apiKeyProviderIds
|
|
3867
|
+
}).find((provider) => provider.id === params.providerId);
|
|
3868
|
+
}
|
|
3869
|
+
function trimOrUndefined2(value) {
|
|
3870
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
3871
|
+
}
|
|
3872
|
+
function validateProviderLoginInput(params) {
|
|
3873
|
+
const fields = getProviderFormFields({
|
|
3874
|
+
provider: params.provider,
|
|
3875
|
+
modelIdsByProvider: params.modelIdsByProvider
|
|
3876
|
+
});
|
|
3877
|
+
const values = {};
|
|
3878
|
+
for (const field of fields) {
|
|
3879
|
+
if (field.dependsOn) {
|
|
3880
|
+
const dependsOnValue = values[field.dependsOn.field] ?? trimOrUndefined2(params.value[field.dependsOn.field]);
|
|
3881
|
+
if (dependsOnValue !== field.dependsOn.equals) {
|
|
3882
|
+
continue;
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
const rawValue = params.value[field.name];
|
|
3886
|
+
const normalized = trimOrUndefined2(rawValue);
|
|
3887
|
+
if (field.required && !normalized) {
|
|
3888
|
+
return { ok: false, error: `Missing required field: ${field.name}` };
|
|
3889
|
+
}
|
|
3890
|
+
if (normalized) {
|
|
3891
|
+
values[field.name] = normalized;
|
|
3892
|
+
} else if (field.defaultValue) {
|
|
3893
|
+
values[field.name] = field.defaultValue;
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
return { ok: true, value: values };
|
|
3897
|
+
}
|
|
3898
|
+
async function applyProviderLogin(params) {
|
|
3899
|
+
const provider = params.provider;
|
|
3900
|
+
switch (provider.flow) {
|
|
3901
|
+
case "custom-openai": {
|
|
3902
|
+
await saveCustomOpenAiConfig({
|
|
3903
|
+
...params,
|
|
3904
|
+
apiKey: params.input.apiKey,
|
|
3905
|
+
baseUrl: params.input.baseUrl,
|
|
3906
|
+
modelId: params.input.modelId
|
|
3907
|
+
});
|
|
3908
|
+
return { providerId: provider.id, preferredModelId: params.input.modelId };
|
|
3909
|
+
}
|
|
3910
|
+
case "azure-openai-responses": {
|
|
3911
|
+
const baseUrl = params.input.baseUrl ?? (params.input.resourceName ? `https://${params.input.resourceName}.openai.azure.com/openai/v1` : void 0);
|
|
3912
|
+
if (!baseUrl) {
|
|
3913
|
+
throw new Error("Azure login requires either baseUrl or resourceName.");
|
|
3914
|
+
}
|
|
3915
|
+
const builtInModelIds = new Set(params.modelIdsByProvider[provider.id] ?? []);
|
|
3916
|
+
await saveAzureConfig({
|
|
3917
|
+
...params,
|
|
3918
|
+
apiKey: params.input.apiKey,
|
|
3919
|
+
baseUrl,
|
|
3920
|
+
modelId: params.input.modelId,
|
|
3921
|
+
deploymentName: params.input.deploymentName,
|
|
3922
|
+
apiVersion: params.input.apiVersion,
|
|
3923
|
+
useCustomModel: !builtInModelIds.has(params.input.modelId)
|
|
3924
|
+
});
|
|
3925
|
+
return { providerId: provider.id, preferredModelId: params.input.modelId };
|
|
3926
|
+
}
|
|
3927
|
+
case "amazon-bedrock": {
|
|
3928
|
+
const authMode = params.input.authMode ?? "profile";
|
|
3929
|
+
await saveBedrockConfig({
|
|
3930
|
+
...params,
|
|
3931
|
+
region: params.input.region,
|
|
3932
|
+
modelId: params.input.modelId,
|
|
3933
|
+
profile: authMode === "profile" ? params.input.profile : void 0,
|
|
3934
|
+
accessKeyId: authMode === "access-keys" ? params.input.accessKeyId : void 0,
|
|
3935
|
+
secretAccessKey: authMode === "access-keys" ? params.input.secretAccessKey : void 0,
|
|
3936
|
+
sessionToken: authMode === "access-keys" ? params.input.sessionToken : void 0
|
|
3937
|
+
});
|
|
3938
|
+
return { providerId: provider.id, preferredModelId: params.input.modelId };
|
|
3939
|
+
}
|
|
3940
|
+
default: {
|
|
3941
|
+
await saveGenericApiKey({
|
|
3942
|
+
...params,
|
|
3943
|
+
providerId: provider.id,
|
|
3944
|
+
apiKey: params.input.apiKey
|
|
3945
|
+
});
|
|
3946
|
+
return { providerId: provider.id };
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
async function refreshSessionAfterCredentialChange(params) {
|
|
3951
|
+
params.modelRegistry.refresh();
|
|
3952
|
+
const availableModels = params.modelRegistry.getAvailable();
|
|
3953
|
+
const preferredModel = params.preferredProviderId && params.preferredModelId ? availableModels.find((model) => model.provider === params.preferredProviderId && model.id === params.preferredModelId) : params.preferredProviderId ? availableModels.find((model) => model.provider === params.preferredProviderId) : void 0;
|
|
3954
|
+
const currentModel = params.session.model;
|
|
3955
|
+
const currentModelStillAvailable = currentModel ? availableModels.some((model) => model.provider === currentModel.provider && model.id === currentModel.id) : false;
|
|
3956
|
+
if (currentModel && !currentModelStillAvailable) {
|
|
3957
|
+
if (preferredModel) {
|
|
3958
|
+
await params.session.setModel(preferredModel);
|
|
3959
|
+
return;
|
|
3960
|
+
}
|
|
3961
|
+
if (availableModels[0]) {
|
|
3962
|
+
await params.session.setModel(availableModels[0]);
|
|
3963
|
+
}
|
|
3964
|
+
return;
|
|
3965
|
+
}
|
|
3966
|
+
if (!currentModel && preferredModel) {
|
|
3967
|
+
await params.session.setModel(preferredModel);
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
if (!currentModel && availableModels[0]) {
|
|
3971
|
+
await params.session.setModel(availableModels[0]);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
function serializeModels(params) {
|
|
3975
|
+
return params.models.map((model) => ({
|
|
3976
|
+
provider: model.provider,
|
|
3977
|
+
providerLabel: getProviderLabel(model.provider),
|
|
3978
|
+
id: model.id,
|
|
3979
|
+
name: typeof model.name === "string" && model.name.length > 0 ? model.name : model.id,
|
|
3980
|
+
reasoning: model.reasoning === true,
|
|
3981
|
+
contextWindow: typeof model.contextWindow === "number" ? model.contextWindow : null,
|
|
3982
|
+
maxTokens: typeof model.maxTokens === "number" ? model.maxTokens : null,
|
|
3983
|
+
configured: params.authStorage.hasAuth(model.provider)
|
|
3984
|
+
}));
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3987
|
+
// src/server/oauthFlowManager.ts
|
|
3988
|
+
var import_node_crypto = require("node:crypto");
|
|
3989
|
+
function normalizeError(error) {
|
|
3990
|
+
return error instanceof Error ? error.message : String(error);
|
|
3991
|
+
}
|
|
3992
|
+
function buildPromptStep(params) {
|
|
3993
|
+
return {
|
|
3994
|
+
type: params.type,
|
|
3995
|
+
stepToken: params.state.stepToken,
|
|
3996
|
+
providerId: params.state.providerId,
|
|
3997
|
+
message: params.prompt.message,
|
|
3998
|
+
inputSchema: {
|
|
3999
|
+
type: "object",
|
|
4000
|
+
properties: {
|
|
4001
|
+
value: {
|
|
4002
|
+
type: "string",
|
|
4003
|
+
title: params.type === "manual-code" ? "Verification Code" : "Value",
|
|
4004
|
+
...params.prompt.placeholder ? { default: params.prompt.placeholder } : {}
|
|
4005
|
+
}
|
|
4006
|
+
},
|
|
4007
|
+
required: params.prompt.allowEmpty ? [] : ["value"]
|
|
4008
|
+
},
|
|
4009
|
+
uiHints: {
|
|
4010
|
+
submitLabel: "Continue",
|
|
4011
|
+
order: ["value"],
|
|
4012
|
+
fields: {
|
|
4013
|
+
value: {
|
|
4014
|
+
component: "text",
|
|
4015
|
+
...params.prompt.placeholder ? { placeholder: params.prompt.placeholder } : {}
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
};
|
|
4020
|
+
}
|
|
4021
|
+
function buildAuthStep(params) {
|
|
4022
|
+
return {
|
|
4023
|
+
type: "open-url",
|
|
4024
|
+
stepToken: params.state.stepToken,
|
|
4025
|
+
providerId: params.state.providerId,
|
|
4026
|
+
url: params.authInfo.url,
|
|
4027
|
+
instructions: params.authInfo.instructions,
|
|
4028
|
+
usesCallbackServer: params.state.usesCallbackServer,
|
|
4029
|
+
pending: true
|
|
4030
|
+
};
|
|
4031
|
+
}
|
|
4032
|
+
var OAuthFlowManager = class {
|
|
4033
|
+
flows = /* @__PURE__ */ new Map();
|
|
4034
|
+
emitStep(state, step) {
|
|
4035
|
+
state.currentStep = step;
|
|
4036
|
+
if (state.waitingForStep) {
|
|
4037
|
+
const waiting = state.waitingForStep;
|
|
4038
|
+
state.waitingForStep = void 0;
|
|
4039
|
+
waiting.resolve(step);
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
state.queuedSteps.push(step);
|
|
4043
|
+
}
|
|
4044
|
+
nextStep(state) {
|
|
4045
|
+
const queued = state.queuedSteps.shift();
|
|
4046
|
+
if (queued) {
|
|
4047
|
+
return Promise.resolve(queued);
|
|
4048
|
+
}
|
|
4049
|
+
if (state.currentStep) {
|
|
4050
|
+
return Promise.resolve(state.currentStep);
|
|
4051
|
+
}
|
|
4052
|
+
return new Promise((resolve2) => {
|
|
4053
|
+
state.waitingForStep = { resolve: resolve2 };
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
async start(params) {
|
|
4057
|
+
const state = {
|
|
4058
|
+
stepToken: (0, import_node_crypto.randomUUID)(),
|
|
4059
|
+
providerId: params.providerId,
|
|
4060
|
+
usesCallbackServer: params.usesCallbackServer,
|
|
4061
|
+
pendingInput: void 0,
|
|
4062
|
+
currentStep: void 0,
|
|
4063
|
+
queuedSteps: [],
|
|
4064
|
+
completed: false
|
|
4065
|
+
};
|
|
4066
|
+
this.flows.set(state.stepToken, state);
|
|
4067
|
+
void params.authStorage.login(params.providerId, {
|
|
4068
|
+
onAuth: (info) => {
|
|
4069
|
+
this.emitStep(state, buildAuthStep({ state, authInfo: info }));
|
|
4070
|
+
},
|
|
4071
|
+
onPrompt: async (prompt) => {
|
|
4072
|
+
const step = buildPromptStep({ state, type: "prompt", prompt });
|
|
4073
|
+
this.emitStep(state, step);
|
|
4074
|
+
return await new Promise((resolve2) => {
|
|
4075
|
+
state.pendingInput = { type: "prompt", resolve: resolve2 };
|
|
4076
|
+
});
|
|
4077
|
+
},
|
|
4078
|
+
onManualCodeInput: async () => {
|
|
4079
|
+
const step = buildPromptStep({
|
|
4080
|
+
state,
|
|
4081
|
+
type: "manual-code",
|
|
4082
|
+
prompt: {
|
|
4083
|
+
message: "Enter the verification code from the provider."
|
|
4084
|
+
}
|
|
4085
|
+
});
|
|
4086
|
+
this.emitStep(state, step);
|
|
4087
|
+
return await new Promise((resolve2) => {
|
|
4088
|
+
state.pendingInput = { type: "manual-code", resolve: resolve2 };
|
|
4089
|
+
});
|
|
4090
|
+
},
|
|
4091
|
+
onProgress: (_message) => {
|
|
4092
|
+
}
|
|
4093
|
+
}).then(() => {
|
|
4094
|
+
state.completed = true;
|
|
4095
|
+
this.emitStep(state, {
|
|
4096
|
+
type: "complete",
|
|
4097
|
+
stepToken: state.stepToken,
|
|
4098
|
+
providerId: state.providerId
|
|
4099
|
+
});
|
|
4100
|
+
}).catch((error) => {
|
|
4101
|
+
state.completed = true;
|
|
4102
|
+
this.emitStep(state, {
|
|
4103
|
+
type: "error",
|
|
4104
|
+
stepToken: state.stepToken,
|
|
4105
|
+
providerId: state.providerId,
|
|
4106
|
+
error: normalizeError(error)
|
|
4107
|
+
});
|
|
4108
|
+
});
|
|
4109
|
+
return await this.nextStep(state);
|
|
4110
|
+
}
|
|
4111
|
+
async continue(params) {
|
|
4112
|
+
const state = this.flows.get(params.stepToken);
|
|
4113
|
+
if (!state) {
|
|
4114
|
+
throw new Error(`Unknown OAuth step token: ${params.stepToken}`);
|
|
4115
|
+
}
|
|
4116
|
+
if (state.pendingInput) {
|
|
4117
|
+
const nextValue = params.value?.trim();
|
|
4118
|
+
state.pendingInput.resolve(nextValue ?? "");
|
|
4119
|
+
state.pendingInput = void 0;
|
|
4120
|
+
state.currentStep = void 0;
|
|
4121
|
+
}
|
|
4122
|
+
const step = await this.nextStep(state);
|
|
4123
|
+
if (step.type === "complete" || step.type === "error") {
|
|
4124
|
+
this.flows.delete(state.stepToken);
|
|
4125
|
+
}
|
|
4126
|
+
return step;
|
|
4127
|
+
}
|
|
4128
|
+
};
|
|
4129
|
+
|
|
3053
4130
|
// src/server/agentServer.ts
|
|
3054
4131
|
function generateMessageId() {
|
|
3055
4132
|
return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -3069,6 +4146,40 @@ function extractLastUserText(messages) {
|
|
|
3069
4146
|
}
|
|
3070
4147
|
return void 0;
|
|
3071
4148
|
}
|
|
4149
|
+
function extractAskUserToolResponse(messages) {
|
|
4150
|
+
for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
|
|
4151
|
+
const message = messages[messageIndex];
|
|
4152
|
+
if (message.role !== "assistant") {
|
|
4153
|
+
continue;
|
|
4154
|
+
}
|
|
4155
|
+
for (let partIndex = message.parts.length - 1; partIndex >= 0; partIndex -= 1) {
|
|
4156
|
+
const part = message.parts[partIndex];
|
|
4157
|
+
if (part.type === "dynamic-tool" && part.toolName === "ask_user" && part.state === "output-available" && typeof part.toolCallId === "string") {
|
|
4158
|
+
const response = parseAskUserResponseFromToolOutput(part.output);
|
|
4159
|
+
if (response) {
|
|
4160
|
+
return {
|
|
4161
|
+
toolCallId: part.toolCallId,
|
|
4162
|
+
response
|
|
4163
|
+
};
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
return void 0;
|
|
4169
|
+
}
|
|
4170
|
+
function normalizeSlashCommands(commands) {
|
|
4171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4172
|
+
return commands.filter((command) => typeof command.name === "string" && command.name.trim().length > 0).sort((left, right) => left.name.localeCompare(right.name)).filter((command) => {
|
|
4173
|
+
if (seen.has(command.name)) {
|
|
4174
|
+
return false;
|
|
4175
|
+
}
|
|
4176
|
+
seen.add(command.name);
|
|
4177
|
+
return true;
|
|
4178
|
+
}).map((command) => ({
|
|
4179
|
+
name: command.name,
|
|
4180
|
+
description: command.description?.trim() || null
|
|
4181
|
+
}));
|
|
4182
|
+
}
|
|
3072
4183
|
async function waitForIdle(session, timeoutMs = 3e4) {
|
|
3073
4184
|
if (!session.isStreaming) {
|
|
3074
4185
|
return;
|
|
@@ -3092,6 +4203,96 @@ async function waitForIdle(session, timeoutMs = 3e4) {
|
|
|
3092
4203
|
}
|
|
3093
4204
|
});
|
|
3094
4205
|
}
|
|
4206
|
+
function convertSessionEntriesToUIMessages(entries) {
|
|
4207
|
+
const messages = [];
|
|
4208
|
+
const toolResults = /* @__PURE__ */ new Map();
|
|
4209
|
+
for (const entry of entries) {
|
|
4210
|
+
const rec = entry;
|
|
4211
|
+
if (rec.type !== "message") {
|
|
4212
|
+
continue;
|
|
4213
|
+
}
|
|
4214
|
+
const msg = rec.message;
|
|
4215
|
+
if (!msg || msg.role !== "toolResult") {
|
|
4216
|
+
continue;
|
|
4217
|
+
}
|
|
4218
|
+
const toolCallId = msg.toolCallId;
|
|
4219
|
+
if (toolCallId) {
|
|
4220
|
+
const content = msg.content;
|
|
4221
|
+
const textParts = Array.isArray(content) ? content.filter((b) => b.type === "text").map((b) => b.text).join("\n") : typeof content === "string" ? content : "";
|
|
4222
|
+
toolResults.set(toolCallId, {
|
|
4223
|
+
output: textParts,
|
|
4224
|
+
isError: msg.isError ?? false
|
|
4225
|
+
});
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
for (const entry of entries) {
|
|
4229
|
+
const rec = entry;
|
|
4230
|
+
if (rec.type !== "message") {
|
|
4231
|
+
continue;
|
|
4232
|
+
}
|
|
4233
|
+
const msg = rec.message;
|
|
4234
|
+
if (!msg) {
|
|
4235
|
+
continue;
|
|
4236
|
+
}
|
|
4237
|
+
const entryId = rec.id ?? `entry_${messages.length}`;
|
|
4238
|
+
if (msg.role === "user") {
|
|
4239
|
+
const content = msg.content;
|
|
4240
|
+
let text = "";
|
|
4241
|
+
if (typeof content === "string") {
|
|
4242
|
+
text = content;
|
|
4243
|
+
} else if (Array.isArray(content)) {
|
|
4244
|
+
text = content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
4245
|
+
}
|
|
4246
|
+
if (text) {
|
|
4247
|
+
messages.push({ id: entryId, role: "user", parts: [{ type: "text", text }] });
|
|
4248
|
+
}
|
|
4249
|
+
continue;
|
|
4250
|
+
}
|
|
4251
|
+
if (msg.role === "assistant") {
|
|
4252
|
+
const content = msg.content;
|
|
4253
|
+
if (!Array.isArray(content)) {
|
|
4254
|
+
continue;
|
|
4255
|
+
}
|
|
4256
|
+
const parts = [];
|
|
4257
|
+
for (const block of content) {
|
|
4258
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
4259
|
+
const askUserRequest = parseAskUserRequestFromText(block.text);
|
|
4260
|
+
if (askUserRequest) {
|
|
4261
|
+
parts.push({
|
|
4262
|
+
type: "dynamic-tool",
|
|
4263
|
+
toolCallId: createAskUserToolCallId(askUserRequest),
|
|
4264
|
+
toolName: "ask_user",
|
|
4265
|
+
state: "input-available",
|
|
4266
|
+
input: askUserRequest
|
|
4267
|
+
});
|
|
4268
|
+
continue;
|
|
4269
|
+
}
|
|
4270
|
+
parts.push({ type: "text", text: block.text });
|
|
4271
|
+
} else if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
4272
|
+
parts.push({ type: "reasoning", text: block.thinking, state: "complete" });
|
|
4273
|
+
} else if (block.type === "toolCall") {
|
|
4274
|
+
const toolCallId = block.id;
|
|
4275
|
+
const toolName = block.name;
|
|
4276
|
+
const input = block.arguments ?? {};
|
|
4277
|
+
const result = toolResults.get(toolCallId);
|
|
4278
|
+
parts.push({
|
|
4279
|
+
type: "dynamic-tool",
|
|
4280
|
+
toolCallId,
|
|
4281
|
+
toolName,
|
|
4282
|
+
input,
|
|
4283
|
+
state: result ? result.isError ? "output-error" : "output-available" : "input-available",
|
|
4284
|
+
...result && !result.isError ? { output: result.output } : {},
|
|
4285
|
+
...result && result.isError ? { errorText: String(result.output) } : {}
|
|
4286
|
+
});
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
if (parts.length > 0) {
|
|
4290
|
+
messages.push({ id: entryId, role: "assistant", parts });
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
return messages;
|
|
4295
|
+
}
|
|
3095
4296
|
var FS_IGNORE = /* @__PURE__ */ new Set([
|
|
3096
4297
|
"node_modules",
|
|
3097
4298
|
".git",
|
|
@@ -3110,8 +4311,8 @@ var FS_IGNORE = /* @__PURE__ */ new Set([
|
|
|
3110
4311
|
".svelte-kit"
|
|
3111
4312
|
]);
|
|
3112
4313
|
function resolveSafePath(cwd, requestPath) {
|
|
3113
|
-
const resolved = (0,
|
|
3114
|
-
const normalizedCwd = cwd.endsWith(
|
|
4314
|
+
const resolved = (0, import_node_path3.resolve)(cwd, requestPath);
|
|
4315
|
+
const normalizedCwd = cwd.endsWith(import_node_path3.sep) ? cwd : cwd + import_node_path3.sep;
|
|
3115
4316
|
if (resolved !== cwd && !resolved.startsWith(normalizedCwd)) {
|
|
3116
4317
|
throw new Error("Path escapes working directory");
|
|
3117
4318
|
}
|
|
@@ -3128,7 +4329,7 @@ async function buildTree(params) {
|
|
|
3128
4329
|
}
|
|
3129
4330
|
let entries;
|
|
3130
4331
|
try {
|
|
3131
|
-
entries = await (0,
|
|
4332
|
+
entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
|
|
3132
4333
|
} catch {
|
|
3133
4334
|
return [];
|
|
3134
4335
|
}
|
|
@@ -3148,8 +4349,8 @@ async function buildTree(params) {
|
|
|
3148
4349
|
if (ignore.has(entry.name)) {
|
|
3149
4350
|
continue;
|
|
3150
4351
|
}
|
|
3151
|
-
const fullPath = (0,
|
|
3152
|
-
const relativePath = (0,
|
|
4352
|
+
const fullPath = (0, import_node_path3.join)(dir, entry.name);
|
|
4353
|
+
const relativePath = (0, import_node_path3.relative)(cwd, fullPath);
|
|
3153
4354
|
if (entry.isDirectory()) {
|
|
3154
4355
|
const children = await buildTree({
|
|
3155
4356
|
dir: fullPath,
|
|
@@ -3175,7 +4376,7 @@ async function walkFiles(params) {
|
|
|
3175
4376
|
}
|
|
3176
4377
|
let entries;
|
|
3177
4378
|
try {
|
|
3178
|
-
entries = await (0,
|
|
4379
|
+
entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
|
|
3179
4380
|
} catch {
|
|
3180
4381
|
return;
|
|
3181
4382
|
}
|
|
@@ -3186,8 +4387,8 @@ async function walkFiles(params) {
|
|
|
3186
4387
|
if (ignore.has(entry.name)) {
|
|
3187
4388
|
continue;
|
|
3188
4389
|
}
|
|
3189
|
-
const fullPath = (0,
|
|
3190
|
-
const relativePath = (0,
|
|
4390
|
+
const fullPath = (0, import_node_path3.join)(dir, entry.name);
|
|
4391
|
+
const relativePath = (0, import_node_path3.relative)(cwd, fullPath);
|
|
3191
4392
|
if (entry.isDirectory()) {
|
|
3192
4393
|
await walkFiles({ dir: fullPath, cwd, pattern, ignore, maxResults, results });
|
|
3193
4394
|
} else if (entry.isFile() && pattern.test(relativePath)) {
|
|
@@ -3196,9 +4397,32 @@ async function walkFiles(params) {
|
|
|
3196
4397
|
}
|
|
3197
4398
|
}
|
|
3198
4399
|
async function createAgentServer(params) {
|
|
3199
|
-
const { port, sessionManager, modelRegistry, context, onResumeSession } = params;
|
|
4400
|
+
const { port, sessionManager, modelRegistry, authRuntime, context, onCreateSession, onResumeSession } = params;
|
|
3200
4401
|
let activeSession = params.session;
|
|
4402
|
+
const pendingAskUserRequests = /* @__PURE__ */ new Map();
|
|
4403
|
+
const oauthFlowManager = new OAuthFlowManager();
|
|
3201
4404
|
const app = new Hono2();
|
|
4405
|
+
function getModelIdsByProvider() {
|
|
4406
|
+
const values = {};
|
|
4407
|
+
for (const model of modelRegistry.getAll()) {
|
|
4408
|
+
if (!values[model.provider]) {
|
|
4409
|
+
values[model.provider] = [];
|
|
4410
|
+
}
|
|
4411
|
+
values[model.provider].push(model.id);
|
|
4412
|
+
}
|
|
4413
|
+
for (const providerId of Object.keys(values)) {
|
|
4414
|
+
values[providerId] = [...new Set(values[providerId])].sort((left, right) => left.localeCompare(right));
|
|
4415
|
+
}
|
|
4416
|
+
return values;
|
|
4417
|
+
}
|
|
4418
|
+
function getProviderCatalog() {
|
|
4419
|
+
return buildProviderCatalog({
|
|
4420
|
+
authStorage: authRuntime.authStorage,
|
|
4421
|
+
browserProviders: authRuntime.authStorage.getOAuthProviders(),
|
|
4422
|
+
apiKeyProviderIds: [...new Set(modelRegistry.getAll().map((model) => model.provider))].sort((left, right) => left.localeCompare(right)),
|
|
4423
|
+
modelIdsByProvider: getModelIdsByProvider()
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
3202
4426
|
app.use("/*", cors({ origin: "*" }));
|
|
3203
4427
|
app.get("/api/health", (c) => {
|
|
3204
4428
|
return c.json({ ok: true });
|
|
@@ -3212,9 +4436,23 @@ async function createAgentServer(params) {
|
|
|
3212
4436
|
app.post("/api/chat", async (c) => {
|
|
3213
4437
|
const body = await c.req.json();
|
|
3214
4438
|
const messages = body.messages ?? [];
|
|
3215
|
-
|
|
4439
|
+
if (body.sessionId) {
|
|
4440
|
+
try {
|
|
4441
|
+
activeSession = await onResumeSession(body.sessionId);
|
|
4442
|
+
} catch (error) {
|
|
4443
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
4444
|
+
return c.json({ error: `Failed to resume session: ${msg}` }, 400);
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
const sessionId = body.sessionId?.trim() || activeSession.id?.trim() || "active";
|
|
4448
|
+
const askUserResponse = extractAskUserToolResponse(messages);
|
|
4449
|
+
const pendingAskUser = pendingAskUserRequests.get(sessionId);
|
|
4450
|
+
const userMessage = askUserResponse ? pendingAskUser && pendingAskUser.toolCallId === askUserResponse.toolCallId ? formatAskUserResponsePrompt(askUserResponse.response) : void 0 : extractLastUserText(messages);
|
|
3216
4451
|
if (!userMessage) {
|
|
3217
|
-
return c.json({ error: "No user message found" }, 400);
|
|
4452
|
+
return c.json({ error: askUserResponse ? "No matching pending ask_user request found" : "No user message found" }, 400);
|
|
4453
|
+
}
|
|
4454
|
+
if (askUserResponse) {
|
|
4455
|
+
pendingAskUserRequests.delete(sessionId);
|
|
3218
4456
|
}
|
|
3219
4457
|
if (activeSession.isStreaming) {
|
|
3220
4458
|
await activeSession.abort();
|
|
@@ -3234,6 +4472,9 @@ async function createAgentServer(params) {
|
|
|
3234
4472
|
const bridge = createEventBridge({
|
|
3235
4473
|
messageId,
|
|
3236
4474
|
onChunk: writeChunk,
|
|
4475
|
+
onAskUser: ({ toolCallId, request }) => {
|
|
4476
|
+
pendingAskUserRequests.set(sessionId, { toolCallId, request });
|
|
4477
|
+
},
|
|
3237
4478
|
onDone: () => {
|
|
3238
4479
|
writeChunk({ type: "finish-step" });
|
|
3239
4480
|
writeChunk({ type: "finish" });
|
|
@@ -3252,7 +4493,14 @@ async function createAgentServer(params) {
|
|
|
3252
4493
|
unsubscribe();
|
|
3253
4494
|
}
|
|
3254
4495
|
});
|
|
3255
|
-
activeSession.prompt(userMessage).
|
|
4496
|
+
activeSession.prompt(userMessage).then(() => {
|
|
4497
|
+
if (!activeSession.isStreaming) {
|
|
4498
|
+
unsubscribe();
|
|
4499
|
+
writeChunk({ type: "finish-step" });
|
|
4500
|
+
writeChunk({ type: "finish" });
|
|
4501
|
+
controller.close();
|
|
4502
|
+
}
|
|
4503
|
+
}).catch((error) => {
|
|
3256
4504
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3257
4505
|
writeChunk({ type: "error", errorText: errorMessage });
|
|
3258
4506
|
writeChunk({ type: "finish-step" });
|
|
@@ -3291,6 +4539,31 @@ async function createAgentServer(params) {
|
|
|
3291
4539
|
return c.json({ error: message }, 500);
|
|
3292
4540
|
}
|
|
3293
4541
|
});
|
|
4542
|
+
app.post("/api/sessions", async (c) => {
|
|
4543
|
+
try {
|
|
4544
|
+
if (activeSession.isStreaming) {
|
|
4545
|
+
await activeSession.abort();
|
|
4546
|
+
await waitForIdle(activeSession);
|
|
4547
|
+
}
|
|
4548
|
+
activeSession = await onCreateSession();
|
|
4549
|
+
const sessionId = activeSession.id?.trim();
|
|
4550
|
+
if (!sessionId) {
|
|
4551
|
+
return c.json({ error: "Created session is missing an id" }, 500);
|
|
4552
|
+
}
|
|
4553
|
+
const sessions = await sessionManager.list();
|
|
4554
|
+
const match2 = sessions.find((session) => session.id === sessionId);
|
|
4555
|
+
return c.json({
|
|
4556
|
+
ok: true,
|
|
4557
|
+
sessionId,
|
|
4558
|
+
sessionName: match2?.name ?? null,
|
|
4559
|
+
cwd: match2?.cwd ?? context.cwd,
|
|
4560
|
+
model: activeSession.model ? { provider: activeSession.model.provider, id: activeSession.model.id } : null
|
|
4561
|
+
});
|
|
4562
|
+
} catch (error) {
|
|
4563
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4564
|
+
return c.json({ error: message }, 500);
|
|
4565
|
+
}
|
|
4566
|
+
});
|
|
3294
4567
|
app.get("/api/sessions/:sessionId/messages", async (c) => {
|
|
3295
4568
|
const sessionId = c.req.param("sessionId");
|
|
3296
4569
|
try {
|
|
@@ -3301,14 +4574,15 @@ async function createAgentServer(params) {
|
|
|
3301
4574
|
}
|
|
3302
4575
|
const opened = sessionManager.open(match2.path);
|
|
3303
4576
|
const sessionContext = opened.buildSessionContext();
|
|
3304
|
-
const
|
|
4577
|
+
const rawEntries = opened.getBranch();
|
|
4578
|
+
const messages = convertSessionEntriesToUIMessages(rawEntries);
|
|
3305
4579
|
return c.json({
|
|
3306
4580
|
sessionId: opened.getSessionId(),
|
|
3307
4581
|
sessionName: opened.getSessionName() ?? null,
|
|
3308
4582
|
cwd: opened.getCwd(),
|
|
3309
4583
|
model: sessionContext.model ?? null,
|
|
3310
4584
|
thinkingLevel: sessionContext.thinkingLevel ?? null,
|
|
3311
|
-
|
|
4585
|
+
messages
|
|
3312
4586
|
});
|
|
3313
4587
|
} catch (error) {
|
|
3314
4588
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3328,8 +4602,161 @@ async function createAgentServer(params) {
|
|
|
3328
4602
|
app.get("/api/models", (c) => {
|
|
3329
4603
|
return c.json({
|
|
3330
4604
|
current: activeSession.model ? { provider: activeSession.model.provider, id: activeSession.model.id } : null,
|
|
3331
|
-
|
|
4605
|
+
thinkingLevel: activeSession.thinkingLevel,
|
|
4606
|
+
availableThinkingLevels: activeSession.getAvailableThinkingLevels(),
|
|
4607
|
+
available: serializeModels({
|
|
4608
|
+
models: modelRegistry.getAvailable(),
|
|
4609
|
+
authStorage: authRuntime.authStorage
|
|
4610
|
+
})
|
|
4611
|
+
});
|
|
4612
|
+
});
|
|
4613
|
+
app.get("/api/slash-commands", (c) => {
|
|
4614
|
+
return c.json({
|
|
4615
|
+
commands: normalizeSlashCommands(activeSession.listCommands())
|
|
4616
|
+
});
|
|
4617
|
+
});
|
|
4618
|
+
app.get("/api/auth/providers", (c) => {
|
|
4619
|
+
return c.json({
|
|
4620
|
+
providers: getProviderCatalog()
|
|
4621
|
+
});
|
|
4622
|
+
});
|
|
4623
|
+
app.post("/api/auth/providers/:providerId/login", async (c) => {
|
|
4624
|
+
const providerId = c.req.param("providerId");
|
|
4625
|
+
const provider = findProviderOption({
|
|
4626
|
+
providerId,
|
|
4627
|
+
browserProviders: authRuntime.authStorage.getOAuthProviders(),
|
|
4628
|
+
apiKeyProviderIds: [...new Set(modelRegistry.getAll().map((model) => model.provider))]
|
|
4629
|
+
});
|
|
4630
|
+
if (!provider) {
|
|
4631
|
+
return c.json({ error: `Unknown provider: ${providerId}` }, 404);
|
|
4632
|
+
}
|
|
4633
|
+
if (provider.flow === "oauth") {
|
|
4634
|
+
const step = await oauthFlowManager.start({
|
|
4635
|
+
authStorage: authRuntime.authStorage,
|
|
4636
|
+
providerId,
|
|
4637
|
+
usesCallbackServer: authRuntime.authStorage.getOAuthProviders().find((item) => item.id === providerId)?.usesCallbackServer
|
|
4638
|
+
});
|
|
4639
|
+
return c.json({ step });
|
|
4640
|
+
}
|
|
4641
|
+
const body = await c.req.json();
|
|
4642
|
+
const validated = validateProviderLoginInput({
|
|
4643
|
+
provider,
|
|
4644
|
+
modelIdsByProvider: getModelIdsByProvider(),
|
|
4645
|
+
value: body
|
|
4646
|
+
});
|
|
4647
|
+
if (!validated.ok) {
|
|
4648
|
+
return c.json({ error: validated.error }, 400);
|
|
4649
|
+
}
|
|
4650
|
+
try {
|
|
4651
|
+
const result = await applyProviderLogin({
|
|
4652
|
+
...authRuntime,
|
|
4653
|
+
provider,
|
|
4654
|
+
input: validated.value,
|
|
4655
|
+
modelIdsByProvider: getModelIdsByProvider()
|
|
4656
|
+
});
|
|
4657
|
+
await refreshSessionAfterCredentialChange({
|
|
4658
|
+
session: activeSession,
|
|
4659
|
+
modelRegistry,
|
|
4660
|
+
preferredProviderId: result.providerId,
|
|
4661
|
+
preferredModelId: result.preferredModelId
|
|
4662
|
+
});
|
|
4663
|
+
return c.json({
|
|
4664
|
+
ok: true,
|
|
4665
|
+
providerId,
|
|
4666
|
+
current: activeSession.model ? { provider: activeSession.model.provider, id: activeSession.model.id } : null,
|
|
4667
|
+
thinkingLevel: activeSession.thinkingLevel
|
|
4668
|
+
});
|
|
4669
|
+
} catch (error) {
|
|
4670
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4671
|
+
return c.json({ error: message }, 400);
|
|
4672
|
+
}
|
|
4673
|
+
});
|
|
4674
|
+
app.post("/api/auth/providers/:providerId/oauth/continue", async (c) => {
|
|
4675
|
+
const providerId = c.req.param("providerId");
|
|
4676
|
+
const body = await c.req.json();
|
|
4677
|
+
if (!body.stepToken) {
|
|
4678
|
+
return c.json({ error: "Missing required field: stepToken" }, 400);
|
|
4679
|
+
}
|
|
4680
|
+
try {
|
|
4681
|
+
const value = typeof body.values?.value === "string" ? body.values.value : void 0;
|
|
4682
|
+
const step = await oauthFlowManager.continue({
|
|
4683
|
+
stepToken: body.stepToken,
|
|
4684
|
+
value
|
|
4685
|
+
});
|
|
4686
|
+
if (step.type === "complete") {
|
|
4687
|
+
modelRegistry.refresh();
|
|
4688
|
+
await refreshSessionAfterCredentialChange({
|
|
4689
|
+
session: activeSession,
|
|
4690
|
+
modelRegistry,
|
|
4691
|
+
preferredProviderId: providerId
|
|
4692
|
+
});
|
|
4693
|
+
}
|
|
4694
|
+
return c.json({ step });
|
|
4695
|
+
} catch (error) {
|
|
4696
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4697
|
+
return c.json({ error: message }, 400);
|
|
4698
|
+
}
|
|
4699
|
+
});
|
|
4700
|
+
app.post("/api/auth/providers/:providerId/logout", async (c) => {
|
|
4701
|
+
const providerId = c.req.param("providerId");
|
|
4702
|
+
const provider = findProviderOption({
|
|
4703
|
+
providerId,
|
|
4704
|
+
browserProviders: authRuntime.authStorage.getOAuthProviders(),
|
|
4705
|
+
apiKeyProviderIds: [...new Set(modelRegistry.getAll().map((model) => model.provider))]
|
|
3332
4706
|
});
|
|
4707
|
+
if (!provider) {
|
|
4708
|
+
return c.json({ error: `Unknown provider: ${providerId}` }, 404);
|
|
4709
|
+
}
|
|
4710
|
+
try {
|
|
4711
|
+
if (provider.flow === "oauth") {
|
|
4712
|
+
authRuntime.authStorage.logout(providerId);
|
|
4713
|
+
} else {
|
|
4714
|
+
await clearProviderConfig({
|
|
4715
|
+
...authRuntime,
|
|
4716
|
+
providerId
|
|
4717
|
+
});
|
|
4718
|
+
}
|
|
4719
|
+
modelRegistry.refresh();
|
|
4720
|
+
await refreshSessionAfterCredentialChange({
|
|
4721
|
+
session: activeSession,
|
|
4722
|
+
modelRegistry
|
|
4723
|
+
});
|
|
4724
|
+
return c.json({
|
|
4725
|
+
ok: true,
|
|
4726
|
+
providerId,
|
|
4727
|
+
current: activeSession.model ? { provider: activeSession.model.provider, id: activeSession.model.id } : null,
|
|
4728
|
+
thinkingLevel: activeSession.thinkingLevel
|
|
4729
|
+
});
|
|
4730
|
+
} catch (error) {
|
|
4731
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4732
|
+
return c.json({ error: message }, 400);
|
|
4733
|
+
}
|
|
4734
|
+
});
|
|
4735
|
+
app.post("/api/models/select", async (c) => {
|
|
4736
|
+
const body = await c.req.json();
|
|
4737
|
+
const provider = body.provider?.trim();
|
|
4738
|
+
const modelId = body.modelId?.trim();
|
|
4739
|
+
if (!provider || !modelId) {
|
|
4740
|
+
return c.json({ error: "Missing required fields: provider, modelId" }, 400);
|
|
4741
|
+
}
|
|
4742
|
+
const model = modelRegistry.find(provider, modelId);
|
|
4743
|
+
if (!model) {
|
|
4744
|
+
return c.json({ error: `Model not found: ${provider}/${modelId}` }, 404);
|
|
4745
|
+
}
|
|
4746
|
+
try {
|
|
4747
|
+
await activeSession.setModel(model);
|
|
4748
|
+
if (body.thinkingLevel) {
|
|
4749
|
+
activeSession.setThinkingLevel(body.thinkingLevel);
|
|
4750
|
+
}
|
|
4751
|
+
return c.json({
|
|
4752
|
+
current: activeSession.model ? { provider: activeSession.model.provider, id: activeSession.model.id } : null,
|
|
4753
|
+
thinkingLevel: activeSession.thinkingLevel,
|
|
4754
|
+
availableThinkingLevels: activeSession.getAvailableThinkingLevels()
|
|
4755
|
+
});
|
|
4756
|
+
} catch (error) {
|
|
4757
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4758
|
+
return c.json({ error: message }, 400);
|
|
4759
|
+
}
|
|
3333
4760
|
});
|
|
3334
4761
|
app.post("/api/sessions/:sessionId/resume", async (c) => {
|
|
3335
4762
|
const sessionId = c.req.param("sessionId");
|
|
@@ -3395,11 +4822,11 @@ async function createAgentServer(params) {
|
|
|
3395
4822
|
}
|
|
3396
4823
|
try {
|
|
3397
4824
|
const resolved = resolveSafePath(context.cwd, filePath);
|
|
3398
|
-
const fileStat = await (0,
|
|
4825
|
+
const fileStat = await (0, import_promises3.stat)(resolved);
|
|
3399
4826
|
if (!fileStat.isFile()) {
|
|
3400
4827
|
return c.json({ error: "Path is not a file" }, 400);
|
|
3401
4828
|
}
|
|
3402
|
-
const content = await (0,
|
|
4829
|
+
const content = await (0, import_promises3.readFile)(resolved, "utf-8");
|
|
3403
4830
|
return c.json({
|
|
3404
4831
|
path: filePath,
|
|
3405
4832
|
content,
|
|
@@ -3419,9 +4846,9 @@ async function createAgentServer(params) {
|
|
|
3419
4846
|
}
|
|
3420
4847
|
try {
|
|
3421
4848
|
const resolved = resolveSafePath(context.cwd, body.path);
|
|
3422
|
-
await (0,
|
|
3423
|
-
await (0,
|
|
3424
|
-
const fileStat = await (0,
|
|
4849
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.join)(resolved, ".."), { recursive: true });
|
|
4850
|
+
await (0, import_promises3.writeFile)(resolved, body.content, "utf-8");
|
|
4851
|
+
const fileStat = await (0, import_promises3.stat)(resolved);
|
|
3425
4852
|
return c.json({ ok: true, path: body.path, size: fileStat.size });
|
|
3426
4853
|
} catch (error) {
|
|
3427
4854
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3435,7 +4862,7 @@ async function createAgentServer(params) {
|
|
|
3435
4862
|
}
|
|
3436
4863
|
try {
|
|
3437
4864
|
const resolved = resolveSafePath(context.cwd, body.path);
|
|
3438
|
-
await (0,
|
|
4865
|
+
await (0, import_promises3.mkdir)(resolved, { recursive: true });
|
|
3439
4866
|
return c.json({ ok: true, path: body.path });
|
|
3440
4867
|
} catch (error) {
|
|
3441
4868
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3450,8 +4877,8 @@ async function createAgentServer(params) {
|
|
|
3450
4877
|
try {
|
|
3451
4878
|
const resolvedFrom = resolveSafePath(context.cwd, body.from);
|
|
3452
4879
|
const resolvedTo = resolveSafePath(context.cwd, body.to);
|
|
3453
|
-
await (0,
|
|
3454
|
-
await (0,
|
|
4880
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.join)(resolvedTo, ".."), { recursive: true });
|
|
4881
|
+
await (0, import_promises3.rename)(resolvedFrom, resolvedTo);
|
|
3455
4882
|
return c.json({ ok: true, from: body.from, to: body.to });
|
|
3456
4883
|
} catch (error) {
|
|
3457
4884
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3469,8 +4896,8 @@ async function createAgentServer(params) {
|
|
|
3469
4896
|
if (resolved === context.cwd) {
|
|
3470
4897
|
return c.json({ error: "Cannot delete working directory" }, 400);
|
|
3471
4898
|
}
|
|
3472
|
-
const fileStat = await (0,
|
|
3473
|
-
await (0,
|
|
4899
|
+
const fileStat = await (0, import_promises3.stat)(resolved);
|
|
4900
|
+
await (0, import_promises3.rm)(resolved, { recursive: fileStat.isDirectory() });
|
|
3474
4901
|
return c.json({ ok: true, path: filePath });
|
|
3475
4902
|
} catch (error) {
|
|
3476
4903
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3478,6 +4905,42 @@ async function createAgentServer(params) {
|
|
|
3478
4905
|
return c.json({ error: message }, status);
|
|
3479
4906
|
}
|
|
3480
4907
|
});
|
|
4908
|
+
const CLI_EXEC = process.env.DOCYRUS_CLI_EXECUTABLE || process.execPath;
|
|
4909
|
+
const CLI_ENTRY = process.env.DOCYRUS_CLI_ENTRY || (0, import_node_path3.resolve)(process.argv[1] ? (0, import_node_path3.join)(process.argv[1], "..") : __dirname, "main.js");
|
|
4910
|
+
const CLI_SCOPE = process.env.DOCYRUS_CLI_SCOPE;
|
|
4911
|
+
const CLI_TIMEOUT_MS = 3e4;
|
|
4912
|
+
function runCliCommand(args) {
|
|
4913
|
+
return new Promise((resolveResult) => {
|
|
4914
|
+
const scopeArgs = CLI_SCOPE ? ["--scope", CLI_SCOPE] : [];
|
|
4915
|
+
const proc = (0, import_node_child_process.spawn)(CLI_EXEC, [CLI_ENTRY, ...scopeArgs, ...args, "--json"], {
|
|
4916
|
+
cwd: context.cwd,
|
|
4917
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4918
|
+
timeout: CLI_TIMEOUT_MS
|
|
4919
|
+
});
|
|
4920
|
+
let stdout = "";
|
|
4921
|
+
let stderr = "";
|
|
4922
|
+
proc.stdout?.on("data", (chunk) => {
|
|
4923
|
+
stdout += chunk.toString();
|
|
4924
|
+
});
|
|
4925
|
+
proc.stderr?.on("data", (chunk) => {
|
|
4926
|
+
stderr += chunk.toString();
|
|
4927
|
+
});
|
|
4928
|
+
proc.on("close", (code) => {
|
|
4929
|
+
if (code !== 0) {
|
|
4930
|
+
resolveResult({ ok: false, error: stderr.trim() || `exit code ${code}` });
|
|
4931
|
+
return;
|
|
4932
|
+
}
|
|
4933
|
+
try {
|
|
4934
|
+
resolveResult({ ok: true, data: JSON.parse(stdout) });
|
|
4935
|
+
} catch {
|
|
4936
|
+
resolveResult({ ok: true, data: stdout.trim() });
|
|
4937
|
+
}
|
|
4938
|
+
});
|
|
4939
|
+
proc.on("error", (err) => {
|
|
4940
|
+
resolveResult({ ok: false, error: err.message });
|
|
4941
|
+
});
|
|
4942
|
+
});
|
|
4943
|
+
}
|
|
3481
4944
|
let devProcess = null;
|
|
3482
4945
|
let devUrl = null;
|
|
3483
4946
|
const DEV_URL_PATTERN = /https?:\/\/(?:localhost|127\.0\.0\.1):\d+/;
|
|
@@ -3493,7 +4956,7 @@ async function createAgentServer(params) {
|
|
|
3493
4956
|
}
|
|
3494
4957
|
async function detectDevPort(cwd) {
|
|
3495
4958
|
try {
|
|
3496
|
-
const pkg = JSON.parse(await (0,
|
|
4959
|
+
const pkg = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, "package.json"), "utf-8"));
|
|
3497
4960
|
const devScript = pkg.scripts?.dev;
|
|
3498
4961
|
if (devScript) {
|
|
3499
4962
|
const portFlag = devScript.match(/--port\s+(\d+)/);
|
|
@@ -3509,7 +4972,7 @@ async function createAgentServer(params) {
|
|
|
3509
4972
|
}
|
|
3510
4973
|
for (const name of ["vite.config.ts", "vite.config.mts", "vite.config.js"]) {
|
|
3511
4974
|
try {
|
|
3512
|
-
const content = await (0,
|
|
4975
|
+
const content = await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, name), "utf-8");
|
|
3513
4976
|
const portMatch = content.match(/port\s*:\s*(\d+)/);
|
|
3514
4977
|
if (portMatch) {
|
|
3515
4978
|
return Number(portMatch[1]);
|
|
@@ -3519,32 +4982,76 @@ async function createAgentServer(params) {
|
|
|
3519
4982
|
}
|
|
3520
4983
|
for (const name of ["next.config.ts", "next.config.mts", "next.config.js", "next.config.mjs"]) {
|
|
3521
4984
|
try {
|
|
3522
|
-
await (0,
|
|
4985
|
+
await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, name));
|
|
3523
4986
|
return 3e3;
|
|
3524
4987
|
} catch {
|
|
3525
4988
|
}
|
|
3526
4989
|
}
|
|
3527
4990
|
try {
|
|
3528
|
-
await (0,
|
|
4991
|
+
await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, "angular.json"));
|
|
3529
4992
|
return 4200;
|
|
3530
4993
|
} catch {
|
|
3531
4994
|
}
|
|
3532
4995
|
for (const name of ["nuxt.config.ts", "nuxt.config.js"]) {
|
|
3533
4996
|
try {
|
|
3534
|
-
await (0,
|
|
4997
|
+
await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, name));
|
|
3535
4998
|
return 3e3;
|
|
3536
4999
|
} catch {
|
|
3537
5000
|
}
|
|
3538
5001
|
}
|
|
3539
5002
|
return null;
|
|
3540
5003
|
}
|
|
5004
|
+
let cachedProjectInfo = null;
|
|
5005
|
+
async function getProjectInfo() {
|
|
5006
|
+
if (cachedProjectInfo) {
|
|
5007
|
+
return cachedProjectInfo;
|
|
5008
|
+
}
|
|
5009
|
+
const cwd = context.cwd;
|
|
5010
|
+
let repo = null;
|
|
5011
|
+
let packageName = null;
|
|
5012
|
+
let packageVersion = null;
|
|
5013
|
+
try {
|
|
5014
|
+
repo = await new Promise((res, rej) => {
|
|
5015
|
+
(0, import_node_child_process.execFile)("git", ["remote", "get-url", "origin"], { cwd }, (err, stdout) => {
|
|
5016
|
+
if (err) {
|
|
5017
|
+
return rej(err);
|
|
5018
|
+
}
|
|
5019
|
+
const url = stdout.trim();
|
|
5020
|
+
const match2 = url.match(/\/([^/]+?)(?:\.git)?$/);
|
|
5021
|
+
res(match2 ? match2[1] : url);
|
|
5022
|
+
});
|
|
5023
|
+
});
|
|
5024
|
+
} catch {
|
|
5025
|
+
}
|
|
5026
|
+
try {
|
|
5027
|
+
const pkg = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, "package.json"), "utf-8"));
|
|
5028
|
+
packageName = pkg.name ?? null;
|
|
5029
|
+
packageVersion = pkg.version ?? null;
|
|
5030
|
+
} catch {
|
|
5031
|
+
}
|
|
5032
|
+
cachedProjectInfo = {
|
|
5033
|
+
path: cwd,
|
|
5034
|
+
folder: (0, import_node_path3.basename)(cwd),
|
|
5035
|
+
repo,
|
|
5036
|
+
packageName,
|
|
5037
|
+
packageVersion
|
|
5038
|
+
};
|
|
5039
|
+
return cachedProjectInfo;
|
|
5040
|
+
}
|
|
3541
5041
|
app.get("/api/env/status", async (c) => {
|
|
5042
|
+
const [authResult, project] = await Promise.all([
|
|
5043
|
+
runCliCommand(["auth", "who"]),
|
|
5044
|
+
getProjectInfo()
|
|
5045
|
+
]);
|
|
5046
|
+
const env = authResult.ok ? authResult.data : null;
|
|
3542
5047
|
if (devProcess && devProcess.exitCode === null && devUrl) {
|
|
3543
5048
|
const httpStatus = await probeUrl(devUrl);
|
|
3544
5049
|
return c.json({
|
|
3545
5050
|
status: httpStatus !== null ? "running" : "starting",
|
|
3546
5051
|
url: devUrl,
|
|
3547
5052
|
managed: true,
|
|
5053
|
+
project,
|
|
5054
|
+
env,
|
|
3548
5055
|
...httpStatus !== null ? { httpStatus } : {}
|
|
3549
5056
|
});
|
|
3550
5057
|
}
|
|
@@ -3553,7 +5060,7 @@ async function createAgentServer(params) {
|
|
|
3553
5060
|
devProcess = null;
|
|
3554
5061
|
const stoppedUrl = devUrl;
|
|
3555
5062
|
devUrl = null;
|
|
3556
|
-
return c.json({ status: "stopped", url: stoppedUrl, exitCode, managed: true });
|
|
5063
|
+
return c.json({ status: "stopped", url: stoppedUrl, exitCode, managed: true, project, env });
|
|
3557
5064
|
}
|
|
3558
5065
|
const explicitUrl = c.req.query("url");
|
|
3559
5066
|
const explicitPort = c.req.query("port");
|
|
@@ -3574,10 +5081,12 @@ async function createAgentServer(params) {
|
|
|
3574
5081
|
status: httpStatus !== null ? "running" : "stopped",
|
|
3575
5082
|
url: probeTarget,
|
|
3576
5083
|
httpStatus: httpStatus ?? void 0,
|
|
3577
|
-
managed: false
|
|
5084
|
+
managed: false,
|
|
5085
|
+
project,
|
|
5086
|
+
env
|
|
3578
5087
|
});
|
|
3579
5088
|
}
|
|
3580
|
-
return c.json({ status: "stopped", url: null, managed: false });
|
|
5089
|
+
return c.json({ status: "stopped", url: null, managed: false, project, env });
|
|
3581
5090
|
});
|
|
3582
5091
|
app.post("/api/env/serve", (c) => {
|
|
3583
5092
|
if (devProcess && devProcess.exitCode === null) {
|
|
@@ -3637,10 +5146,6 @@ async function createAgentServer(params) {
|
|
|
3637
5146
|
devUrl = null;
|
|
3638
5147
|
return c.json({ status: "stopped", url: stoppedUrl, pid });
|
|
3639
5148
|
});
|
|
3640
|
-
const CLI_EXEC = process.env.DOCYRUS_CLI_EXECUTABLE || process.execPath;
|
|
3641
|
-
const CLI_ENTRY = process.env.DOCYRUS_CLI_ENTRY;
|
|
3642
|
-
const CLI_SCOPE = process.env.DOCYRUS_CLI_SCOPE;
|
|
3643
|
-
const CLI_TIMEOUT_MS = 3e4;
|
|
3644
5149
|
const BOOLEAN_CLI_FLAGS = /* @__PURE__ */ new Set(["json", "verbose", "global", "noAuth", "expand", "i"]);
|
|
3645
5150
|
function buildCliArgs(pathSegments, query, body) {
|
|
3646
5151
|
const args = [...pathSegments, "--json"];
|
|
@@ -3679,9 +5184,6 @@ async function createAgentServer(params) {
|
|
|
3679
5184
|
return args;
|
|
3680
5185
|
}
|
|
3681
5186
|
app.all("/api/cli/*", async (c) => {
|
|
3682
|
-
if (!CLI_ENTRY) {
|
|
3683
|
-
return c.json({ error: "CLI entry path not available (DOCYRUS_CLI_ENTRY not set)" }, 500);
|
|
3684
|
-
}
|
|
3685
5187
|
const pathSegments = c.req.path.replace(/^\/api\/cli\/?/, "").split("/").filter(Boolean);
|
|
3686
5188
|
if (pathSegments.length === 0) {
|
|
3687
5189
|
return c.json({ error: "No command specified. Usage: /api/cli/<command>/[subcommand]/[args...]" }, 400);
|
|
@@ -3737,12 +5239,10 @@ async function createAgentServer(params) {
|
|
|
3737
5239
|
});
|
|
3738
5240
|
});
|
|
3739
5241
|
const { serve: serve2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
port
|
|
3743
|
-
}, (info) => {
|
|
5242
|
+
const MAX_PORT_RETRIES = 10;
|
|
5243
|
+
function printBanner(actualPort) {
|
|
3744
5244
|
process.stderr.write(`
|
|
3745
|
-
Agent server listening on http://localhost:${
|
|
5245
|
+
Agent server listening on http://localhost:${actualPort}
|
|
3746
5246
|
|
|
3747
5247
|
`);
|
|
3748
5248
|
process.stderr.write(` POST /api/chat \u2014 send chat messages (SSE UIMessage stream)
|
|
@@ -3752,6 +5252,8 @@ async function createAgentServer(params) {
|
|
|
3752
5252
|
process.stderr.write(` GET /api/status \u2014 session status
|
|
3753
5253
|
`);
|
|
3754
5254
|
process.stderr.write(` GET /api/sessions \u2014 list sessions
|
|
5255
|
+
`);
|
|
5256
|
+
process.stderr.write(` POST /api/sessions \u2014 create a new session
|
|
3755
5257
|
`);
|
|
3756
5258
|
process.stderr.write(` GET /api/sessions/:sessionId/messages \u2014 session messages
|
|
3757
5259
|
`);
|
|
@@ -3760,6 +5262,18 @@ async function createAgentServer(params) {
|
|
|
3760
5262
|
process.stderr.write(` GET /api/context \u2014 server context
|
|
3761
5263
|
`);
|
|
3762
5264
|
process.stderr.write(` GET /api/models \u2014 available models
|
|
5265
|
+
`);
|
|
5266
|
+
process.stderr.write(` GET /api/auth/providers \u2014 auth provider catalog
|
|
5267
|
+
`);
|
|
5268
|
+
process.stderr.write(` POST /api/auth/providers/:id/login \u2014 provider login/start login flow
|
|
5269
|
+
`);
|
|
5270
|
+
process.stderr.write(` POST /api/auth/providers/:id/oauth/continue \u2014 continue OAuth flow
|
|
5271
|
+
`);
|
|
5272
|
+
process.stderr.write(` POST /api/auth/providers/:id/logout \u2014 provider logout
|
|
5273
|
+
`);
|
|
5274
|
+
process.stderr.write(` POST /api/models/select \u2014 select model and optional thinking level
|
|
5275
|
+
`);
|
|
5276
|
+
process.stderr.write(` GET /api/slash-commands \u2014 slash command list
|
|
3763
5277
|
`);
|
|
3764
5278
|
process.stderr.write(` GET /api/fs/tree \u2014 directory tree
|
|
3765
5279
|
`);
|
|
@@ -3784,7 +5298,33 @@ async function createAgentServer(params) {
|
|
|
3784
5298
|
process.stderr.write(` * /api/cli/** \u2014 proxy any docyrus CLI command
|
|
3785
5299
|
|
|
3786
5300
|
`);
|
|
3787
|
-
}
|
|
5301
|
+
}
|
|
5302
|
+
for (let attempt = 0; attempt < MAX_PORT_RETRIES; attempt++) {
|
|
5303
|
+
const candidatePort = port + attempt;
|
|
5304
|
+
try {
|
|
5305
|
+
await new Promise((resolveStart, rejectStart) => {
|
|
5306
|
+
const server = serve2({ fetch: app.fetch, port: candidatePort }, () => {
|
|
5307
|
+
printBanner(candidatePort);
|
|
5308
|
+
resolveStart();
|
|
5309
|
+
});
|
|
5310
|
+
server.on("error", (err) => {
|
|
5311
|
+
if (err.code === "EADDRINUSE") {
|
|
5312
|
+
process.stderr.write(` Port ${candidatePort} in use, trying ${candidatePort + 1}...
|
|
5313
|
+
`);
|
|
5314
|
+
rejectStart(err);
|
|
5315
|
+
} else {
|
|
5316
|
+
throw err;
|
|
5317
|
+
}
|
|
5318
|
+
});
|
|
5319
|
+
});
|
|
5320
|
+
return;
|
|
5321
|
+
} catch (err) {
|
|
5322
|
+
const isAddrInUse = err instanceof Error && "code" in err && err.code === "EADDRINUSE";
|
|
5323
|
+
if (!isAddrInUse || attempt === MAX_PORT_RETRIES - 1) {
|
|
5324
|
+
throw new Error(`All ports ${port}\u2013${port + MAX_PORT_RETRIES - 1} are in use`);
|
|
5325
|
+
}
|
|
5326
|
+
}
|
|
5327
|
+
}
|
|
3788
5328
|
}
|
|
3789
5329
|
|
|
3790
5330
|
// src/server/server-loader.ts
|
|
@@ -3800,15 +5340,15 @@ function readLoaderRequest() {
|
|
|
3800
5340
|
}
|
|
3801
5341
|
async function loadPiExports() {
|
|
3802
5342
|
const piPackageDir = readRequiredEnv("PI_PACKAGE_DIR");
|
|
3803
|
-
const moduleUrl = (0, import_node_url.pathToFileURL)((0,
|
|
5343
|
+
const moduleUrl = (0, import_node_url.pathToFileURL)((0, import_node_path4.join)(piPackageDir, "dist", "index.js")).href;
|
|
3804
5344
|
return await import(moduleUrl);
|
|
3805
5345
|
}
|
|
3806
5346
|
function resolvePackagedPiResourceRoot() {
|
|
3807
5347
|
const candidates = [
|
|
3808
|
-
(0,
|
|
3809
|
-
(0,
|
|
3810
|
-
(0,
|
|
3811
|
-
(0,
|
|
5348
|
+
(0, import_node_path4.resolve)(process.cwd(), "apps/api-cli/resources/pi-agent"),
|
|
5349
|
+
(0, import_node_path4.resolve)(__dirname, "../resources/pi-agent"),
|
|
5350
|
+
(0, import_node_path4.resolve)(__dirname, "resources/pi-agent"),
|
|
5351
|
+
(0, import_node_path4.resolve)(process.cwd(), "dist/apps/api-cli/resources/pi-agent")
|
|
3812
5352
|
];
|
|
3813
5353
|
const resolved = candidates.find((candidate) => (0, import_node_fs.existsSync)(candidate));
|
|
3814
5354
|
if (!resolved) {
|
|
@@ -3817,13 +5357,13 @@ function resolvePackagedPiResourceRoot() {
|
|
|
3817
5357
|
return resolved;
|
|
3818
5358
|
}
|
|
3819
5359
|
function resolvePackagedExtensionPaths(resourceRoot) {
|
|
3820
|
-
const extensionsRoot = (0,
|
|
5360
|
+
const extensionsRoot = (0, import_node_path4.join)(resourceRoot, "extensions");
|
|
3821
5361
|
if (!(0, import_node_fs.existsSync)(extensionsRoot)) {
|
|
3822
5362
|
return [];
|
|
3823
5363
|
}
|
|
3824
5364
|
return (0, import_node_fs.readdirSync)(extensionsRoot, {
|
|
3825
5365
|
withFileTypes: true
|
|
3826
|
-
}).filter((entry) => entry.isDirectory() || entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))).map((entry) => (0,
|
|
5366
|
+
}).filter((entry) => entry.isDirectory() || entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))).map((entry) => (0, import_node_path4.join)(extensionsRoot, entry.name)).sort((left, right) => left.localeCompare(right));
|
|
3827
5367
|
}
|
|
3828
5368
|
function setProcessArgValue(flag, value) {
|
|
3829
5369
|
const existingIndex = process.argv.indexOf(flag);
|
|
@@ -3890,6 +5430,45 @@ function renderStartupSplash(version) {
|
|
|
3890
5430
|
`
|
|
3891
5431
|
);
|
|
3892
5432
|
}
|
|
5433
|
+
function createServerSessionAdapter(params) {
|
|
5434
|
+
return {
|
|
5435
|
+
id: params.session.id,
|
|
5436
|
+
get isStreaming() {
|
|
5437
|
+
return params.session.isStreaming;
|
|
5438
|
+
},
|
|
5439
|
+
get model() {
|
|
5440
|
+
return params.session.model;
|
|
5441
|
+
},
|
|
5442
|
+
get thinkingLevel() {
|
|
5443
|
+
return params.session.thinkingLevel;
|
|
5444
|
+
},
|
|
5445
|
+
subscribe(listener) {
|
|
5446
|
+
return params.session.subscribe(listener);
|
|
5447
|
+
},
|
|
5448
|
+
prompt(text) {
|
|
5449
|
+
return params.session.prompt(text);
|
|
5450
|
+
},
|
|
5451
|
+
abort() {
|
|
5452
|
+
return params.session.abort();
|
|
5453
|
+
},
|
|
5454
|
+
setModel(model) {
|
|
5455
|
+
return params.session.setModel(model);
|
|
5456
|
+
},
|
|
5457
|
+
setThinkingLevel(level) {
|
|
5458
|
+
return params.session.setThinkingLevel(level);
|
|
5459
|
+
},
|
|
5460
|
+
getAvailableThinkingLevels() {
|
|
5461
|
+
return params.session.getAvailableThinkingLevels();
|
|
5462
|
+
},
|
|
5463
|
+
supportsThinking() {
|
|
5464
|
+
return params.session.supportsThinking();
|
|
5465
|
+
},
|
|
5466
|
+
listCommands() {
|
|
5467
|
+
const getCommands = params.extensionsResult.runtime.getCommands;
|
|
5468
|
+
return typeof getCommands === "function" ? getCommands() : [];
|
|
5469
|
+
}
|
|
5470
|
+
};
|
|
5471
|
+
}
|
|
3893
5472
|
async function main() {
|
|
3894
5473
|
const request = readLoaderRequest();
|
|
3895
5474
|
const pi = await loadPiExports();
|
|
@@ -3898,16 +5477,16 @@ async function main() {
|
|
|
3898
5477
|
const version = process.env.DOCYRUS_PI_VERSION || "dev";
|
|
3899
5478
|
const resourceRoot = resolvePackagedPiResourceRoot();
|
|
3900
5479
|
const packagedExtensionPaths = resolvePackagedExtensionPaths(resourceRoot);
|
|
3901
|
-
const mcpConfigPath = (0,
|
|
5480
|
+
const mcpConfigPath = (0, import_node_path4.join)(agentDir, "mcp.json");
|
|
3902
5481
|
const hasPackagedMcpAdapter = packagedExtensionPaths.some((extensionPath) => extensionPath.includes("pi-mcp-adapter"));
|
|
3903
|
-
const envStore = new AgentEnvStore((0,
|
|
5482
|
+
const envStore = new AgentEnvStore((0, import_node_path4.join)(agentDir, "env.json"));
|
|
3904
5483
|
await envStore.hydrateProcessEnv(process.env);
|
|
3905
5484
|
if (hasPackagedMcpAdapter) {
|
|
3906
5485
|
setProcessArgValue("--mcp-config", mcpConfigPath);
|
|
3907
5486
|
}
|
|
3908
|
-
const authStorage = pi.AuthStorage.create((0,
|
|
5487
|
+
const authStorage = pi.AuthStorage.create((0, import_node_path4.join)(agentDir, "auth.json"));
|
|
3909
5488
|
const settingsManager = pi.SettingsManager.create(cwd, agentDir);
|
|
3910
|
-
const modelsJsonPath = (0,
|
|
5489
|
+
const modelsJsonPath = (0, import_node_path4.join)(agentDir, "models.json");
|
|
3911
5490
|
const modelRegistry = new pi.ModelRegistry(authStorage, modelsJsonPath);
|
|
3912
5491
|
const quietStartup = !request.verbose;
|
|
3913
5492
|
if (quietStartup) {
|
|
@@ -3934,7 +5513,7 @@ async function main() {
|
|
|
3934
5513
|
agentDir,
|
|
3935
5514
|
settingsManager,
|
|
3936
5515
|
additionalExtensionPaths: packagedExtensionPaths,
|
|
3937
|
-
systemPrompt: (0,
|
|
5516
|
+
systemPrompt: (0, import_node_path4.join)(
|
|
3938
5517
|
resourceRoot,
|
|
3939
5518
|
"prompts",
|
|
3940
5519
|
request.profile === "agent" ? "agent-system.md" : "coder-system.md"
|
|
@@ -3967,14 +5546,18 @@ Or create ${modelsJsonPath}`
|
|
|
3967
5546
|
);
|
|
3968
5547
|
}
|
|
3969
5548
|
await createAgentServer({
|
|
3970
|
-
session,
|
|
5549
|
+
session: createServerSessionAdapter({ session, extensionsResult }),
|
|
3971
5550
|
port: request.port,
|
|
3972
5551
|
sessionManager: {
|
|
3973
5552
|
list: () => pi.SessionManager.list(cwd, request.sessionDir),
|
|
3974
5553
|
open: (path) => pi.SessionManager.open(path, request.sessionDir)
|
|
3975
5554
|
},
|
|
3976
|
-
modelRegistry
|
|
3977
|
-
|
|
5555
|
+
modelRegistry,
|
|
5556
|
+
authRuntime: {
|
|
5557
|
+
authStorage,
|
|
5558
|
+
envStore,
|
|
5559
|
+
settingsManager,
|
|
5560
|
+
modelsJsonPath
|
|
3978
5561
|
},
|
|
3979
5562
|
context: {
|
|
3980
5563
|
cwd,
|
|
@@ -3984,6 +5567,24 @@ Or create ${modelsJsonPath}`
|
|
|
3984
5567
|
sessionDir: request.sessionDir ?? null,
|
|
3985
5568
|
thinkingLevel: request.thinking ?? null
|
|
3986
5569
|
},
|
|
5570
|
+
onCreateSession: async () => {
|
|
5571
|
+
const { session: freshSession, extensionsResult: freshExtensionsResult } = await pi.createAgentSession({
|
|
5572
|
+
cwd,
|
|
5573
|
+
agentDir,
|
|
5574
|
+
authStorage,
|
|
5575
|
+
modelRegistry,
|
|
5576
|
+
resourceLoader,
|
|
5577
|
+
settingsManager,
|
|
5578
|
+
sessionManager,
|
|
5579
|
+
tools: buildTools(request.profile, cwd, pi),
|
|
5580
|
+
model: requestedModel,
|
|
5581
|
+
thinkingLevel: request.thinking
|
|
5582
|
+
});
|
|
5583
|
+
return createServerSessionAdapter({
|
|
5584
|
+
session: freshSession,
|
|
5585
|
+
extensionsResult: freshExtensionsResult
|
|
5586
|
+
});
|
|
5587
|
+
},
|
|
3987
5588
|
onResumeSession: async (sessionId) => {
|
|
3988
5589
|
const sessions = await pi.SessionManager.list(cwd, request.sessionDir);
|
|
3989
5590
|
const match2 = sessions.find((s) => s.id === sessionId);
|
|
@@ -3991,20 +5592,26 @@ Or create ${modelsJsonPath}`
|
|
|
3991
5592
|
throw new Error(`Session not found: ${sessionId}`);
|
|
3992
5593
|
}
|
|
3993
5594
|
const resumeCwd = match2.cwd || cwd;
|
|
3994
|
-
const
|
|
5595
|
+
const resumedSessionManager = pi.SessionManager.open(
|
|
5596
|
+
match2.path,
|
|
5597
|
+
request.sessionDir
|
|
5598
|
+
);
|
|
5599
|
+
const { session: resumedSession, extensionsResult: resumedExtensionsResult } = await pi.createAgentSession({
|
|
3995
5600
|
cwd: resumeCwd,
|
|
3996
5601
|
agentDir,
|
|
3997
5602
|
authStorage,
|
|
3998
5603
|
modelRegistry,
|
|
3999
5604
|
resourceLoader,
|
|
4000
5605
|
settingsManager,
|
|
4001
|
-
sessionManager,
|
|
5606
|
+
sessionManager: resumedSessionManager,
|
|
4002
5607
|
tools: buildTools(request.profile, resumeCwd, pi),
|
|
4003
5608
|
model: requestedModel,
|
|
4004
|
-
thinkingLevel: request.thinking
|
|
4005
|
-
|
|
5609
|
+
thinkingLevel: request.thinking
|
|
5610
|
+
});
|
|
5611
|
+
return createServerSessionAdapter({
|
|
5612
|
+
session: resumedSession,
|
|
5613
|
+
extensionsResult: resumedExtensionsResult
|
|
4006
5614
|
});
|
|
4007
|
-
return resumedSession;
|
|
4008
5615
|
}
|
|
4009
5616
|
});
|
|
4010
5617
|
}
|