@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/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 import_node_path3 = require("node:path");
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 import_promises2 = require("node:fs/promises");
799
- var import_node_path2 = require("node:path");
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
- onChunk({ type: "text-start", id: messageId });
3168
+ activeTextBuffer = { text: "", mode: "pending" };
2967
3169
  break;
2968
3170
  }
2969
3171
  case "text_delta": {
2970
- onChunk({ type: "text-delta", id: messageId, delta: assistantEvent.delta ?? "" });
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
- onChunk({ type: "text-end", id: messageId });
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, import_node_path2.resolve)(cwd, requestPath);
3193
- const normalizedCwd = cwd.endsWith(import_node_path2.sep) ? cwd : cwd + import_node_path2.sep;
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, import_promises2.readdir)(dir, { withFileTypes: true });
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, import_node_path2.join)(dir, entry.name);
3231
- const relativePath = (0, import_node_path2.relative)(cwd, fullPath);
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, import_promises2.readdir)(dir, { withFileTypes: true });
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, import_node_path2.join)(dir, entry.name);
3269
- const relativePath = (0, import_node_path2.relative)(cwd, fullPath);
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
- const userMessage = extractLastUserText(messages);
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).catch((error) => {
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
- available: modelRegistry.getAvailable()
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, import_promises2.stat)(resolved);
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, import_promises2.readFile)(resolved, "utf-8");
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, import_promises2.mkdir)((0, import_node_path2.join)(resolved, ".."), { recursive: true });
3528
- await (0, import_promises2.writeFile)(resolved, body.content, "utf-8");
3529
- const fileStat = await (0, import_promises2.stat)(resolved);
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, import_promises2.mkdir)(resolved, { recursive: true });
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, import_promises2.mkdir)((0, import_node_path2.join)(resolvedTo, ".."), { recursive: true });
3559
- await (0, import_promises2.rename)(resolvedFrom, resolvedTo);
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, import_promises2.stat)(resolved);
3578
- await (0, import_promises2.rm)(resolved, { recursive: fileStat.isDirectory() });
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, import_node_path2.resolve)(process.argv[1] ? (0, import_node_path2.join)(process.argv[1], "..") : __dirname, "main.js");
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, import_promises2.readFile)((0, import_node_path2.join)(cwd, "package.json"), "utf-8"));
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, import_promises2.readFile)((0, import_node_path2.join)(cwd, name), "utf-8");
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, import_promises2.stat)((0, import_node_path2.join)(cwd, name));
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, import_promises2.stat)((0, import_node_path2.join)(cwd, "angular.json"));
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, import_promises2.stat)((0, import_node_path2.join)(cwd, name));
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, import_promises2.readFile)((0, import_node_path2.join)(cwd, "package.json"), "utf-8"));
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, import_node_path2.basename)(cwd),
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, import_node_path3.join)(piPackageDir, "dist", "index.js")).href;
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, import_node_path3.resolve)(process.cwd(), "apps/api-cli/resources/pi-agent"),
4015
- (0, import_node_path3.resolve)(__dirname, "../resources/pi-agent"),
4016
- (0, import_node_path3.resolve)(__dirname, "resources/pi-agent"),
4017
- (0, import_node_path3.resolve)(process.cwd(), "dist/apps/api-cli/resources/pi-agent")
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, import_node_path3.join)(resourceRoot, "extensions");
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, import_node_path3.join)(extensionsRoot, entry.name)).sort((left, right) => left.localeCompare(right));
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, import_node_path3.join)(agentDir, "mcp.json");
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, import_node_path3.join)(agentDir, "env.json"));
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, import_node_path3.join)(agentDir, "auth.json"));
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, import_node_path3.join)(agentDir, "models.json");
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, import_node_path3.join)(
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
- getAvailable: () => modelRegistry.getAvailable()
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 freshSession;
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 { session: resumedSession } = await pi.createAgentSession({
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
- resumeSessionId: sessionId
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
  }