@docyrus/docyrus 0.0.25 → 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 +166 -152
- 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/server-loader.js +1446 -60
- 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;
|
|
@@ -3145,6 +4256,17 @@ function convertSessionEntriesToUIMessages(entries) {
|
|
|
3145
4256
|
const parts = [];
|
|
3146
4257
|
for (const block of content) {
|
|
3147
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
|
+
}
|
|
3148
4270
|
parts.push({ type: "text", text: block.text });
|
|
3149
4271
|
} else if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
3150
4272
|
parts.push({ type: "reasoning", text: block.thinking, state: "complete" });
|
|
@@ -3189,8 +4311,8 @@ var FS_IGNORE = /* @__PURE__ */ new Set([
|
|
|
3189
4311
|
".svelte-kit"
|
|
3190
4312
|
]);
|
|
3191
4313
|
function resolveSafePath(cwd, requestPath) {
|
|
3192
|
-
const resolved = (0,
|
|
3193
|
-
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;
|
|
3194
4316
|
if (resolved !== cwd && !resolved.startsWith(normalizedCwd)) {
|
|
3195
4317
|
throw new Error("Path escapes working directory");
|
|
3196
4318
|
}
|
|
@@ -3207,7 +4329,7 @@ async function buildTree(params) {
|
|
|
3207
4329
|
}
|
|
3208
4330
|
let entries;
|
|
3209
4331
|
try {
|
|
3210
|
-
entries = await (0,
|
|
4332
|
+
entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
|
|
3211
4333
|
} catch {
|
|
3212
4334
|
return [];
|
|
3213
4335
|
}
|
|
@@ -3227,8 +4349,8 @@ async function buildTree(params) {
|
|
|
3227
4349
|
if (ignore.has(entry.name)) {
|
|
3228
4350
|
continue;
|
|
3229
4351
|
}
|
|
3230
|
-
const fullPath = (0,
|
|
3231
|
-
const relativePath = (0,
|
|
4352
|
+
const fullPath = (0, import_node_path3.join)(dir, entry.name);
|
|
4353
|
+
const relativePath = (0, import_node_path3.relative)(cwd, fullPath);
|
|
3232
4354
|
if (entry.isDirectory()) {
|
|
3233
4355
|
const children = await buildTree({
|
|
3234
4356
|
dir: fullPath,
|
|
@@ -3254,7 +4376,7 @@ async function walkFiles(params) {
|
|
|
3254
4376
|
}
|
|
3255
4377
|
let entries;
|
|
3256
4378
|
try {
|
|
3257
|
-
entries = await (0,
|
|
4379
|
+
entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
|
|
3258
4380
|
} catch {
|
|
3259
4381
|
return;
|
|
3260
4382
|
}
|
|
@@ -3265,8 +4387,8 @@ async function walkFiles(params) {
|
|
|
3265
4387
|
if (ignore.has(entry.name)) {
|
|
3266
4388
|
continue;
|
|
3267
4389
|
}
|
|
3268
|
-
const fullPath = (0,
|
|
3269
|
-
const relativePath = (0,
|
|
4390
|
+
const fullPath = (0, import_node_path3.join)(dir, entry.name);
|
|
4391
|
+
const relativePath = (0, import_node_path3.relative)(cwd, fullPath);
|
|
3270
4392
|
if (entry.isDirectory()) {
|
|
3271
4393
|
await walkFiles({ dir: fullPath, cwd, pattern, ignore, maxResults, results });
|
|
3272
4394
|
} else if (entry.isFile() && pattern.test(relativePath)) {
|
|
@@ -3275,9 +4397,32 @@ async function walkFiles(params) {
|
|
|
3275
4397
|
}
|
|
3276
4398
|
}
|
|
3277
4399
|
async function createAgentServer(params) {
|
|
3278
|
-
const { port, sessionManager, modelRegistry, context, onCreateSession, onResumeSession } = params;
|
|
4400
|
+
const { port, sessionManager, modelRegistry, authRuntime, context, onCreateSession, onResumeSession } = params;
|
|
3279
4401
|
let activeSession = params.session;
|
|
4402
|
+
const pendingAskUserRequests = /* @__PURE__ */ new Map();
|
|
4403
|
+
const oauthFlowManager = new OAuthFlowManager();
|
|
3280
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
|
+
}
|
|
3281
4426
|
app.use("/*", cors({ origin: "*" }));
|
|
3282
4427
|
app.get("/api/health", (c) => {
|
|
3283
4428
|
return c.json({ ok: true });
|
|
@@ -3291,9 +4436,23 @@ async function createAgentServer(params) {
|
|
|
3291
4436
|
app.post("/api/chat", async (c) => {
|
|
3292
4437
|
const body = await c.req.json();
|
|
3293
4438
|
const messages = body.messages ?? [];
|
|
3294
|
-
|
|
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);
|
|
3295
4451
|
if (!userMessage) {
|
|
3296
|
-
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);
|
|
3297
4456
|
}
|
|
3298
4457
|
if (activeSession.isStreaming) {
|
|
3299
4458
|
await activeSession.abort();
|
|
@@ -3313,6 +4472,9 @@ async function createAgentServer(params) {
|
|
|
3313
4472
|
const bridge = createEventBridge({
|
|
3314
4473
|
messageId,
|
|
3315
4474
|
onChunk: writeChunk,
|
|
4475
|
+
onAskUser: ({ toolCallId, request }) => {
|
|
4476
|
+
pendingAskUserRequests.set(sessionId, { toolCallId, request });
|
|
4477
|
+
},
|
|
3316
4478
|
onDone: () => {
|
|
3317
4479
|
writeChunk({ type: "finish-step" });
|
|
3318
4480
|
writeChunk({ type: "finish" });
|
|
@@ -3331,7 +4493,14 @@ async function createAgentServer(params) {
|
|
|
3331
4493
|
unsubscribe();
|
|
3332
4494
|
}
|
|
3333
4495
|
});
|
|
3334
|
-
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) => {
|
|
3335
4504
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3336
4505
|
writeChunk({ type: "error", errorText: errorMessage });
|
|
3337
4506
|
writeChunk({ type: "finish-step" });
|
|
@@ -3433,9 +4602,162 @@ async function createAgentServer(params) {
|
|
|
3433
4602
|
app.get("/api/models", (c) => {
|
|
3434
4603
|
return c.json({
|
|
3435
4604
|
current: activeSession.model ? { provider: activeSession.model.provider, id: activeSession.model.id } : null,
|
|
3436
|
-
|
|
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()
|
|
3437
4621
|
});
|
|
3438
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))]
|
|
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
|
+
}
|
|
4760
|
+
});
|
|
3439
4761
|
app.post("/api/sessions/:sessionId/resume", async (c) => {
|
|
3440
4762
|
const sessionId = c.req.param("sessionId");
|
|
3441
4763
|
try {
|
|
@@ -3500,11 +4822,11 @@ async function createAgentServer(params) {
|
|
|
3500
4822
|
}
|
|
3501
4823
|
try {
|
|
3502
4824
|
const resolved = resolveSafePath(context.cwd, filePath);
|
|
3503
|
-
const fileStat = await (0,
|
|
4825
|
+
const fileStat = await (0, import_promises3.stat)(resolved);
|
|
3504
4826
|
if (!fileStat.isFile()) {
|
|
3505
4827
|
return c.json({ error: "Path is not a file" }, 400);
|
|
3506
4828
|
}
|
|
3507
|
-
const content = await (0,
|
|
4829
|
+
const content = await (0, import_promises3.readFile)(resolved, "utf-8");
|
|
3508
4830
|
return c.json({
|
|
3509
4831
|
path: filePath,
|
|
3510
4832
|
content,
|
|
@@ -3524,9 +4846,9 @@ async function createAgentServer(params) {
|
|
|
3524
4846
|
}
|
|
3525
4847
|
try {
|
|
3526
4848
|
const resolved = resolveSafePath(context.cwd, body.path);
|
|
3527
|
-
await (0,
|
|
3528
|
-
await (0,
|
|
3529
|
-
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);
|
|
3530
4852
|
return c.json({ ok: true, path: body.path, size: fileStat.size });
|
|
3531
4853
|
} catch (error) {
|
|
3532
4854
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3540,7 +4862,7 @@ async function createAgentServer(params) {
|
|
|
3540
4862
|
}
|
|
3541
4863
|
try {
|
|
3542
4864
|
const resolved = resolveSafePath(context.cwd, body.path);
|
|
3543
|
-
await (0,
|
|
4865
|
+
await (0, import_promises3.mkdir)(resolved, { recursive: true });
|
|
3544
4866
|
return c.json({ ok: true, path: body.path });
|
|
3545
4867
|
} catch (error) {
|
|
3546
4868
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3555,8 +4877,8 @@ async function createAgentServer(params) {
|
|
|
3555
4877
|
try {
|
|
3556
4878
|
const resolvedFrom = resolveSafePath(context.cwd, body.from);
|
|
3557
4879
|
const resolvedTo = resolveSafePath(context.cwd, body.to);
|
|
3558
|
-
await (0,
|
|
3559
|
-
await (0,
|
|
4880
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.join)(resolvedTo, ".."), { recursive: true });
|
|
4881
|
+
await (0, import_promises3.rename)(resolvedFrom, resolvedTo);
|
|
3560
4882
|
return c.json({ ok: true, from: body.from, to: body.to });
|
|
3561
4883
|
} catch (error) {
|
|
3562
4884
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3574,8 +4896,8 @@ async function createAgentServer(params) {
|
|
|
3574
4896
|
if (resolved === context.cwd) {
|
|
3575
4897
|
return c.json({ error: "Cannot delete working directory" }, 400);
|
|
3576
4898
|
}
|
|
3577
|
-
const fileStat = await (0,
|
|
3578
|
-
await (0,
|
|
4899
|
+
const fileStat = await (0, import_promises3.stat)(resolved);
|
|
4900
|
+
await (0, import_promises3.rm)(resolved, { recursive: fileStat.isDirectory() });
|
|
3579
4901
|
return c.json({ ok: true, path: filePath });
|
|
3580
4902
|
} catch (error) {
|
|
3581
4903
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3584,7 +4906,7 @@ async function createAgentServer(params) {
|
|
|
3584
4906
|
}
|
|
3585
4907
|
});
|
|
3586
4908
|
const CLI_EXEC = process.env.DOCYRUS_CLI_EXECUTABLE || process.execPath;
|
|
3587
|
-
const CLI_ENTRY = process.env.DOCYRUS_CLI_ENTRY || (0,
|
|
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");
|
|
3588
4910
|
const CLI_SCOPE = process.env.DOCYRUS_CLI_SCOPE;
|
|
3589
4911
|
const CLI_TIMEOUT_MS = 3e4;
|
|
3590
4912
|
function runCliCommand(args) {
|
|
@@ -3634,7 +4956,7 @@ async function createAgentServer(params) {
|
|
|
3634
4956
|
}
|
|
3635
4957
|
async function detectDevPort(cwd) {
|
|
3636
4958
|
try {
|
|
3637
|
-
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"));
|
|
3638
4960
|
const devScript = pkg.scripts?.dev;
|
|
3639
4961
|
if (devScript) {
|
|
3640
4962
|
const portFlag = devScript.match(/--port\s+(\d+)/);
|
|
@@ -3650,7 +4972,7 @@ async function createAgentServer(params) {
|
|
|
3650
4972
|
}
|
|
3651
4973
|
for (const name of ["vite.config.ts", "vite.config.mts", "vite.config.js"]) {
|
|
3652
4974
|
try {
|
|
3653
|
-
const content = await (0,
|
|
4975
|
+
const content = await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, name), "utf-8");
|
|
3654
4976
|
const portMatch = content.match(/port\s*:\s*(\d+)/);
|
|
3655
4977
|
if (portMatch) {
|
|
3656
4978
|
return Number(portMatch[1]);
|
|
@@ -3660,19 +4982,19 @@ async function createAgentServer(params) {
|
|
|
3660
4982
|
}
|
|
3661
4983
|
for (const name of ["next.config.ts", "next.config.mts", "next.config.js", "next.config.mjs"]) {
|
|
3662
4984
|
try {
|
|
3663
|
-
await (0,
|
|
4985
|
+
await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, name));
|
|
3664
4986
|
return 3e3;
|
|
3665
4987
|
} catch {
|
|
3666
4988
|
}
|
|
3667
4989
|
}
|
|
3668
4990
|
try {
|
|
3669
|
-
await (0,
|
|
4991
|
+
await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, "angular.json"));
|
|
3670
4992
|
return 4200;
|
|
3671
4993
|
} catch {
|
|
3672
4994
|
}
|
|
3673
4995
|
for (const name of ["nuxt.config.ts", "nuxt.config.js"]) {
|
|
3674
4996
|
try {
|
|
3675
|
-
await (0,
|
|
4997
|
+
await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, name));
|
|
3676
4998
|
return 3e3;
|
|
3677
4999
|
} catch {
|
|
3678
5000
|
}
|
|
@@ -3702,14 +5024,14 @@ async function createAgentServer(params) {
|
|
|
3702
5024
|
} catch {
|
|
3703
5025
|
}
|
|
3704
5026
|
try {
|
|
3705
|
-
const pkg = JSON.parse(await (0,
|
|
5027
|
+
const pkg = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, "package.json"), "utf-8"));
|
|
3706
5028
|
packageName = pkg.name ?? null;
|
|
3707
5029
|
packageVersion = pkg.version ?? null;
|
|
3708
5030
|
} catch {
|
|
3709
5031
|
}
|
|
3710
5032
|
cachedProjectInfo = {
|
|
3711
5033
|
path: cwd,
|
|
3712
|
-
folder: (0,
|
|
5034
|
+
folder: (0, import_node_path3.basename)(cwd),
|
|
3713
5035
|
repo,
|
|
3714
5036
|
packageName,
|
|
3715
5037
|
packageVersion
|
|
@@ -3940,6 +5262,18 @@ async function createAgentServer(params) {
|
|
|
3940
5262
|
process.stderr.write(` GET /api/context \u2014 server context
|
|
3941
5263
|
`);
|
|
3942
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
|
|
3943
5277
|
`);
|
|
3944
5278
|
process.stderr.write(` GET /api/fs/tree \u2014 directory tree
|
|
3945
5279
|
`);
|
|
@@ -4006,15 +5340,15 @@ function readLoaderRequest() {
|
|
|
4006
5340
|
}
|
|
4007
5341
|
async function loadPiExports() {
|
|
4008
5342
|
const piPackageDir = readRequiredEnv("PI_PACKAGE_DIR");
|
|
4009
|
-
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;
|
|
4010
5344
|
return await import(moduleUrl);
|
|
4011
5345
|
}
|
|
4012
5346
|
function resolvePackagedPiResourceRoot() {
|
|
4013
5347
|
const candidates = [
|
|
4014
|
-
(0,
|
|
4015
|
-
(0,
|
|
4016
|
-
(0,
|
|
4017
|
-
(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")
|
|
4018
5352
|
];
|
|
4019
5353
|
const resolved = candidates.find((candidate) => (0, import_node_fs.existsSync)(candidate));
|
|
4020
5354
|
if (!resolved) {
|
|
@@ -4023,13 +5357,13 @@ function resolvePackagedPiResourceRoot() {
|
|
|
4023
5357
|
return resolved;
|
|
4024
5358
|
}
|
|
4025
5359
|
function resolvePackagedExtensionPaths(resourceRoot) {
|
|
4026
|
-
const extensionsRoot = (0,
|
|
5360
|
+
const extensionsRoot = (0, import_node_path4.join)(resourceRoot, "extensions");
|
|
4027
5361
|
if (!(0, import_node_fs.existsSync)(extensionsRoot)) {
|
|
4028
5362
|
return [];
|
|
4029
5363
|
}
|
|
4030
5364
|
return (0, import_node_fs.readdirSync)(extensionsRoot, {
|
|
4031
5365
|
withFileTypes: true
|
|
4032
|
-
}).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));
|
|
4033
5367
|
}
|
|
4034
5368
|
function setProcessArgValue(flag, value) {
|
|
4035
5369
|
const existingIndex = process.argv.indexOf(flag);
|
|
@@ -4096,6 +5430,45 @@ function renderStartupSplash(version) {
|
|
|
4096
5430
|
`
|
|
4097
5431
|
);
|
|
4098
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
|
+
}
|
|
4099
5472
|
async function main() {
|
|
4100
5473
|
const request = readLoaderRequest();
|
|
4101
5474
|
const pi = await loadPiExports();
|
|
@@ -4104,16 +5477,16 @@ async function main() {
|
|
|
4104
5477
|
const version = process.env.DOCYRUS_PI_VERSION || "dev";
|
|
4105
5478
|
const resourceRoot = resolvePackagedPiResourceRoot();
|
|
4106
5479
|
const packagedExtensionPaths = resolvePackagedExtensionPaths(resourceRoot);
|
|
4107
|
-
const mcpConfigPath = (0,
|
|
5480
|
+
const mcpConfigPath = (0, import_node_path4.join)(agentDir, "mcp.json");
|
|
4108
5481
|
const hasPackagedMcpAdapter = packagedExtensionPaths.some((extensionPath) => extensionPath.includes("pi-mcp-adapter"));
|
|
4109
|
-
const envStore = new AgentEnvStore((0,
|
|
5482
|
+
const envStore = new AgentEnvStore((0, import_node_path4.join)(agentDir, "env.json"));
|
|
4110
5483
|
await envStore.hydrateProcessEnv(process.env);
|
|
4111
5484
|
if (hasPackagedMcpAdapter) {
|
|
4112
5485
|
setProcessArgValue("--mcp-config", mcpConfigPath);
|
|
4113
5486
|
}
|
|
4114
|
-
const authStorage = pi.AuthStorage.create((0,
|
|
5487
|
+
const authStorage = pi.AuthStorage.create((0, import_node_path4.join)(agentDir, "auth.json"));
|
|
4115
5488
|
const settingsManager = pi.SettingsManager.create(cwd, agentDir);
|
|
4116
|
-
const modelsJsonPath = (0,
|
|
5489
|
+
const modelsJsonPath = (0, import_node_path4.join)(agentDir, "models.json");
|
|
4117
5490
|
const modelRegistry = new pi.ModelRegistry(authStorage, modelsJsonPath);
|
|
4118
5491
|
const quietStartup = !request.verbose;
|
|
4119
5492
|
if (quietStartup) {
|
|
@@ -4140,7 +5513,7 @@ async function main() {
|
|
|
4140
5513
|
agentDir,
|
|
4141
5514
|
settingsManager,
|
|
4142
5515
|
additionalExtensionPaths: packagedExtensionPaths,
|
|
4143
|
-
systemPrompt: (0,
|
|
5516
|
+
systemPrompt: (0, import_node_path4.join)(
|
|
4144
5517
|
resourceRoot,
|
|
4145
5518
|
"prompts",
|
|
4146
5519
|
request.profile === "agent" ? "agent-system.md" : "coder-system.md"
|
|
@@ -4173,14 +5546,18 @@ Or create ${modelsJsonPath}`
|
|
|
4173
5546
|
);
|
|
4174
5547
|
}
|
|
4175
5548
|
await createAgentServer({
|
|
4176
|
-
session,
|
|
5549
|
+
session: createServerSessionAdapter({ session, extensionsResult }),
|
|
4177
5550
|
port: request.port,
|
|
4178
5551
|
sessionManager: {
|
|
4179
5552
|
list: () => pi.SessionManager.list(cwd, request.sessionDir),
|
|
4180
5553
|
open: (path) => pi.SessionManager.open(path, request.sessionDir)
|
|
4181
5554
|
},
|
|
4182
|
-
modelRegistry
|
|
4183
|
-
|
|
5555
|
+
modelRegistry,
|
|
5556
|
+
authRuntime: {
|
|
5557
|
+
authStorage,
|
|
5558
|
+
envStore,
|
|
5559
|
+
settingsManager,
|
|
5560
|
+
modelsJsonPath
|
|
4184
5561
|
},
|
|
4185
5562
|
context: {
|
|
4186
5563
|
cwd,
|
|
@@ -4191,7 +5568,7 @@ Or create ${modelsJsonPath}`
|
|
|
4191
5568
|
thinkingLevel: request.thinking ?? null
|
|
4192
5569
|
},
|
|
4193
5570
|
onCreateSession: async () => {
|
|
4194
|
-
const { session: freshSession } = await pi.createAgentSession({
|
|
5571
|
+
const { session: freshSession, extensionsResult: freshExtensionsResult } = await pi.createAgentSession({
|
|
4195
5572
|
cwd,
|
|
4196
5573
|
agentDir,
|
|
4197
5574
|
authStorage,
|
|
@@ -4203,7 +5580,10 @@ Or create ${modelsJsonPath}`
|
|
|
4203
5580
|
model: requestedModel,
|
|
4204
5581
|
thinkingLevel: request.thinking
|
|
4205
5582
|
});
|
|
4206
|
-
return
|
|
5583
|
+
return createServerSessionAdapter({
|
|
5584
|
+
session: freshSession,
|
|
5585
|
+
extensionsResult: freshExtensionsResult
|
|
5586
|
+
});
|
|
4207
5587
|
},
|
|
4208
5588
|
onResumeSession: async (sessionId) => {
|
|
4209
5589
|
const sessions = await pi.SessionManager.list(cwd, request.sessionDir);
|
|
@@ -4212,20 +5592,26 @@ Or create ${modelsJsonPath}`
|
|
|
4212
5592
|
throw new Error(`Session not found: ${sessionId}`);
|
|
4213
5593
|
}
|
|
4214
5594
|
const resumeCwd = match2.cwd || cwd;
|
|
4215
|
-
const
|
|
5595
|
+
const resumedSessionManager = pi.SessionManager.open(
|
|
5596
|
+
match2.path,
|
|
5597
|
+
request.sessionDir
|
|
5598
|
+
);
|
|
5599
|
+
const { session: resumedSession, extensionsResult: resumedExtensionsResult } = await pi.createAgentSession({
|
|
4216
5600
|
cwd: resumeCwd,
|
|
4217
5601
|
agentDir,
|
|
4218
5602
|
authStorage,
|
|
4219
5603
|
modelRegistry,
|
|
4220
5604
|
resourceLoader,
|
|
4221
5605
|
settingsManager,
|
|
4222
|
-
sessionManager,
|
|
5606
|
+
sessionManager: resumedSessionManager,
|
|
4223
5607
|
tools: buildTools(request.profile, resumeCwd, pi),
|
|
4224
5608
|
model: requestedModel,
|
|
4225
|
-
thinkingLevel: request.thinking
|
|
4226
|
-
|
|
5609
|
+
thinkingLevel: request.thinking
|
|
5610
|
+
});
|
|
5611
|
+
return createServerSessionAdapter({
|
|
5612
|
+
session: resumedSession,
|
|
5613
|
+
extensionsResult: resumedExtensionsResult
|
|
4227
5614
|
});
|
|
4228
|
-
return resumedSession;
|
|
4229
5615
|
}
|
|
4230
5616
|
});
|
|
4231
5617
|
}
|