@letta-ai/letta-code 0.23.5 → 0.23.7
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/letta.js +1092 -137
- package/package.json +1 -1
- package/skills/scheduling-tasks/SKILL.md +1 -1
package/letta.js
CHANGED
|
@@ -3269,7 +3269,7 @@ var package_default;
|
|
|
3269
3269
|
var init_package = __esm(() => {
|
|
3270
3270
|
package_default = {
|
|
3271
3271
|
name: "@letta-ai/letta-code",
|
|
3272
|
-
version: "0.23.
|
|
3272
|
+
version: "0.23.7",
|
|
3273
3273
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
3274
3274
|
type: "module",
|
|
3275
3275
|
bin: {
|
|
@@ -8391,7 +8391,7 @@ var init_models2 = __esm(() => {
|
|
|
8391
8391
|
}
|
|
8392
8392
|
},
|
|
8393
8393
|
{
|
|
8394
|
-
id: "opus",
|
|
8394
|
+
id: "opus-4.6-high",
|
|
8395
8395
|
handle: "anthropic/claude-opus-4-6",
|
|
8396
8396
|
label: "Opus 4.6",
|
|
8397
8397
|
description: "Anthropic's (legacy) best model (high reasoning)",
|
|
@@ -8459,7 +8459,7 @@ var init_models2 = __esm(() => {
|
|
|
8459
8459
|
}
|
|
8460
8460
|
},
|
|
8461
8461
|
{
|
|
8462
|
-
id: "opus
|
|
8462
|
+
id: "opus",
|
|
8463
8463
|
handle: "anthropic/claude-opus-4-7",
|
|
8464
8464
|
label: "Opus 4.7",
|
|
8465
8465
|
description: "Anthropic's best model (med reasoning)",
|
|
@@ -8487,20 +8487,6 @@ var init_models2 = __esm(() => {
|
|
|
8487
8487
|
parallel_tool_calls: true
|
|
8488
8488
|
}
|
|
8489
8489
|
},
|
|
8490
|
-
{
|
|
8491
|
-
id: "opus-4.7-medium",
|
|
8492
|
-
handle: "anthropic/claude-opus-4-7",
|
|
8493
|
-
label: "Opus 4.7",
|
|
8494
|
-
description: "Opus 4.7 (med reasoning)",
|
|
8495
|
-
updateArgs: {
|
|
8496
|
-
context_window: 200000,
|
|
8497
|
-
max_output_tokens: 128000,
|
|
8498
|
-
reasoning_effort: "medium",
|
|
8499
|
-
enable_reasoner: true,
|
|
8500
|
-
max_reasoning_tokens: 12000,
|
|
8501
|
-
parallel_tool_calls: true
|
|
8502
|
-
}
|
|
8503
|
-
},
|
|
8504
8490
|
{
|
|
8505
8491
|
id: "opus-4.7-high",
|
|
8506
8492
|
handle: "anthropic/claude-opus-4-7",
|
|
@@ -10261,6 +10247,11 @@ function getModelInfoForLlmConfig(modelHandle, llmConfig) {
|
|
|
10261
10247
|
const match = candidates.find((m) => m.updateArgs?.reasoning_effort === effort);
|
|
10262
10248
|
if (match)
|
|
10263
10249
|
return match;
|
|
10250
|
+
if (effort === "max") {
|
|
10251
|
+
const legacyXHighMatch = candidates.find((m) => m.updateArgs?.reasoning_effort === "xhigh");
|
|
10252
|
+
if (legacyXHighMatch)
|
|
10253
|
+
return legacyXHighMatch;
|
|
10254
|
+
}
|
|
10264
10255
|
}
|
|
10265
10256
|
if (llmConfig?.enable_reasoner === false) {
|
|
10266
10257
|
const match = candidates.find((m) => m.updateArgs?.enable_reasoner === false);
|
|
@@ -10377,7 +10368,8 @@ var init_model = __esm(() => {
|
|
|
10377
10368
|
"low",
|
|
10378
10369
|
"medium",
|
|
10379
10370
|
"high",
|
|
10380
|
-
"xhigh"
|
|
10371
|
+
"xhigh",
|
|
10372
|
+
"max"
|
|
10381
10373
|
];
|
|
10382
10374
|
RESUME_REFRESH_FIELDS = [
|
|
10383
10375
|
"max_output_tokens",
|
|
@@ -37858,6 +37850,11 @@ function getModelInfoForLlmConfig2(modelHandle, llmConfig) {
|
|
|
37858
37850
|
const match = candidates.find((m) => m.updateArgs?.reasoning_effort === effort);
|
|
37859
37851
|
if (match)
|
|
37860
37852
|
return match;
|
|
37853
|
+
if (effort === "max") {
|
|
37854
|
+
const legacyXHighMatch = candidates.find((m) => m.updateArgs?.reasoning_effort === "xhigh");
|
|
37855
|
+
if (legacyXHighMatch)
|
|
37856
|
+
return legacyXHighMatch;
|
|
37857
|
+
}
|
|
37861
37858
|
}
|
|
37862
37859
|
if (llmConfig?.enable_reasoner === false) {
|
|
37863
37860
|
const match = candidates.find((m) => m.updateArgs?.enable_reasoner === false);
|
|
@@ -37974,7 +37971,8 @@ var init_model2 = __esm(() => {
|
|
|
37974
37971
|
"low",
|
|
37975
37972
|
"medium",
|
|
37976
37973
|
"high",
|
|
37977
|
-
"xhigh"
|
|
37974
|
+
"xhigh",
|
|
37975
|
+
"max"
|
|
37978
37976
|
];
|
|
37979
37977
|
RESUME_REFRESH_FIELDS2 = [
|
|
37980
37978
|
"max_output_tokens",
|
|
@@ -38252,6 +38250,9 @@ __export(exports_modify, {
|
|
|
38252
38250
|
updateAgentLLMConfig: () => updateAgentLLMConfig2,
|
|
38253
38251
|
recompileAgentSystemPrompt: () => recompileAgentSystemPrompt
|
|
38254
38252
|
});
|
|
38253
|
+
function supportsDistinctAnthropicXHighEffort2(modelHandle) {
|
|
38254
|
+
return modelHandle.includes("claude-opus-4-7");
|
|
38255
|
+
}
|
|
38255
38256
|
function buildModelSettings2(modelHandle, updateArgs) {
|
|
38256
38257
|
const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
|
|
38257
38258
|
const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
|
|
@@ -38285,10 +38286,13 @@ function buildModelSettings2(modelHandle, updateArgs) {
|
|
|
38285
38286
|
parallel_tool_calls: true
|
|
38286
38287
|
};
|
|
38287
38288
|
const effort = updateArgs?.reasoning_effort;
|
|
38289
|
+
const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort2(modelHandle);
|
|
38288
38290
|
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
38289
38291
|
anthropicSettings.effort = effort;
|
|
38290
38292
|
} else if (effort === "xhigh") {
|
|
38291
|
-
anthropicSettings.effort = "max";
|
|
38293
|
+
anthropicSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
|
|
38294
|
+
} else if (effort === "max") {
|
|
38295
|
+
anthropicSettings.effort = effort;
|
|
38292
38296
|
}
|
|
38293
38297
|
if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
|
|
38294
38298
|
anthropicSettings.thinking = {
|
|
@@ -38341,10 +38345,13 @@ function buildModelSettings2(modelHandle, updateArgs) {
|
|
|
38341
38345
|
parallel_tool_calls: true
|
|
38342
38346
|
};
|
|
38343
38347
|
const effort = updateArgs?.reasoning_effort;
|
|
38348
|
+
const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort2(modelHandle);
|
|
38344
38349
|
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
38345
38350
|
bedrockSettings.effort = effort;
|
|
38346
38351
|
} else if (effort === "xhigh") {
|
|
38347
|
-
bedrockSettings.effort = "max";
|
|
38352
|
+
bedrockSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
|
|
38353
|
+
} else if (effort === "max") {
|
|
38354
|
+
bedrockSettings.effort = effort;
|
|
38348
38355
|
}
|
|
38349
38356
|
if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
|
|
38350
38357
|
bedrockSettings.thinking = {
|
|
@@ -39802,6 +39809,9 @@ function normalizeLoadedAccount(account) {
|
|
|
39802
39809
|
if (next.channel === "telegram" && (next.displayName === "Telegram bot" || next.displayName === "Migrated Telegram bot") || next.channel === "slack" && (next.displayName === "Slack app" || next.displayName === "Migrated Slack app")) {
|
|
39803
39810
|
next.displayName = undefined;
|
|
39804
39811
|
}
|
|
39812
|
+
if (next.channel === "slack") {
|
|
39813
|
+
next.defaultPermissionMode = next.defaultPermissionMode ?? "default";
|
|
39814
|
+
}
|
|
39805
39815
|
return next;
|
|
39806
39816
|
}
|
|
39807
39817
|
function makeDefaultLegacyAccount(channelId) {
|
|
@@ -39836,6 +39846,7 @@ function makeDefaultLegacyAccount(channelId) {
|
|
|
39836
39846
|
dmPolicy: config.dmPolicy,
|
|
39837
39847
|
allowedUsers: [...config.allowedUsers],
|
|
39838
39848
|
agentId: null,
|
|
39849
|
+
defaultPermissionMode: "default",
|
|
39839
39850
|
createdAt: now,
|
|
39840
39851
|
updatedAt: now
|
|
39841
39852
|
};
|
|
@@ -40127,6 +40138,357 @@ var init_types = __esm(() => {
|
|
|
40127
40138
|
SUPPORTED_CHANNEL_IDS = ["telegram", "slack"];
|
|
40128
40139
|
});
|
|
40129
40140
|
|
|
40141
|
+
// src/channels/interactive.ts
|
|
40142
|
+
function normalizeWhitespace(text) {
|
|
40143
|
+
return text.replace(/\s+/g, " ").trim();
|
|
40144
|
+
}
|
|
40145
|
+
function isAffirmativeResponse(text) {
|
|
40146
|
+
const normalized = normalizeWhitespace(text).toLowerCase();
|
|
40147
|
+
return [
|
|
40148
|
+
"approve",
|
|
40149
|
+
"approved",
|
|
40150
|
+
"allow",
|
|
40151
|
+
"yes",
|
|
40152
|
+
"y",
|
|
40153
|
+
"ok",
|
|
40154
|
+
"okay",
|
|
40155
|
+
"continue",
|
|
40156
|
+
"go ahead",
|
|
40157
|
+
"looks good",
|
|
40158
|
+
"lgtm",
|
|
40159
|
+
"sgtm",
|
|
40160
|
+
"ship it"
|
|
40161
|
+
].includes(normalized);
|
|
40162
|
+
}
|
|
40163
|
+
function isNegativeResponse(text) {
|
|
40164
|
+
const normalized = normalizeWhitespace(text).toLowerCase();
|
|
40165
|
+
return [
|
|
40166
|
+
"deny",
|
|
40167
|
+
"denied",
|
|
40168
|
+
"reject",
|
|
40169
|
+
"rejected",
|
|
40170
|
+
"no",
|
|
40171
|
+
"n",
|
|
40172
|
+
"cancel",
|
|
40173
|
+
"skip",
|
|
40174
|
+
"keep planning"
|
|
40175
|
+
].includes(normalized);
|
|
40176
|
+
}
|
|
40177
|
+
function stripApprovalPrefix(text) {
|
|
40178
|
+
return normalizeWhitespace(text.replace(/^(approve|allow|yes|y|ok|okay|deny|reject|no|n)\s*[:-]?\s*/i, ""));
|
|
40179
|
+
}
|
|
40180
|
+
function summarizeControlRequestInput(input) {
|
|
40181
|
+
const serialized = JSON.stringify(input, null, 2);
|
|
40182
|
+
if (!serialized || serialized === "{}") {
|
|
40183
|
+
return null;
|
|
40184
|
+
}
|
|
40185
|
+
if (serialized.length <= 1200) {
|
|
40186
|
+
return serialized;
|
|
40187
|
+
}
|
|
40188
|
+
return `${serialized.slice(0, 1197).trimEnd()}...`;
|
|
40189
|
+
}
|
|
40190
|
+
function summarizePlanPreview(planContent) {
|
|
40191
|
+
const normalized = planContent.trim();
|
|
40192
|
+
if (!normalized) {
|
|
40193
|
+
return "";
|
|
40194
|
+
}
|
|
40195
|
+
const maxLength = 1800;
|
|
40196
|
+
if (normalized.length <= maxLength) {
|
|
40197
|
+
return normalized;
|
|
40198
|
+
}
|
|
40199
|
+
return `${normalized.slice(0, maxLength).trimEnd()}
|
|
40200
|
+
|
|
40201
|
+
[Plan preview truncated for channel delivery.]`;
|
|
40202
|
+
}
|
|
40203
|
+
function buildQuestionPrompt(question, index) {
|
|
40204
|
+
const lines = [
|
|
40205
|
+
`${index + 1}. ${question.question ?? `Question ${index + 1}`}`
|
|
40206
|
+
];
|
|
40207
|
+
const options = question.options ?? [];
|
|
40208
|
+
options.forEach((option, optionIndex) => {
|
|
40209
|
+
const label = option.label?.trim() || `Option ${optionIndex + 1}`;
|
|
40210
|
+
const description = option.description?.trim();
|
|
40211
|
+
lines.push(description ? ` ${optionIndex + 1}) ${label} — ${description}` : ` ${optionIndex + 1}) ${label}`);
|
|
40212
|
+
});
|
|
40213
|
+
if (question.multiSelect) {
|
|
40214
|
+
lines.push(" Choose one or more options. Separate multiple answers with commas.");
|
|
40215
|
+
}
|
|
40216
|
+
return lines;
|
|
40217
|
+
}
|
|
40218
|
+
function matchQuestionOption(question, text) {
|
|
40219
|
+
const trimmed = normalizeWhitespace(text);
|
|
40220
|
+
const options = question.options ?? [];
|
|
40221
|
+
if (!trimmed || options.length === 0) {
|
|
40222
|
+
return trimmed;
|
|
40223
|
+
}
|
|
40224
|
+
const numberMatch = trimmed.match(/^(\d+)$/);
|
|
40225
|
+
if (numberMatch?.[1]) {
|
|
40226
|
+
const option = options[Number(numberMatch[1]) - 1];
|
|
40227
|
+
if (option?.label?.trim()) {
|
|
40228
|
+
return option.label.trim();
|
|
40229
|
+
}
|
|
40230
|
+
}
|
|
40231
|
+
const exactLabel = options.find((option) => option.label && normalizeWhitespace(option.label).toLowerCase() === trimmed.toLowerCase());
|
|
40232
|
+
if (exactLabel?.label?.trim()) {
|
|
40233
|
+
return exactLabel.label.trim();
|
|
40234
|
+
}
|
|
40235
|
+
return trimmed;
|
|
40236
|
+
}
|
|
40237
|
+
function matchQuestionAnswer(question, text) {
|
|
40238
|
+
if (!question.multiSelect) {
|
|
40239
|
+
return matchQuestionOption(question, text);
|
|
40240
|
+
}
|
|
40241
|
+
const normalized = normalizeWhitespace(text);
|
|
40242
|
+
if (!normalized) {
|
|
40243
|
+
return normalized;
|
|
40244
|
+
}
|
|
40245
|
+
const selections = normalized.replace(/\band\b/gi, ",").split(/\s*(?:,|\/|;)\s*/).map((entry) => normalizeWhitespace(entry)).filter(Boolean);
|
|
40246
|
+
if (selections.length <= 1) {
|
|
40247
|
+
return matchQuestionOption(question, normalized);
|
|
40248
|
+
}
|
|
40249
|
+
const matchedSelections = Array.from(new Set(selections.map((selection) => matchQuestionOption(question, selection)).filter(Boolean)));
|
|
40250
|
+
return matchedSelections.length > 0 ? matchedSelections.join(", ") : normalized;
|
|
40251
|
+
}
|
|
40252
|
+
function parseNumberedAnswers(rawText, questions) {
|
|
40253
|
+
const matches = Array.from(rawText.matchAll(/(?:^|\n)\s*(\d+)[).:-]\s*(.+?)(?=(?:\n\s*\d+[).:-]\s*)|$)/gs));
|
|
40254
|
+
if (matches.length === 0) {
|
|
40255
|
+
return null;
|
|
40256
|
+
}
|
|
40257
|
+
const answers = {};
|
|
40258
|
+
for (const match of matches) {
|
|
40259
|
+
const questionIndex = Number(match[1]) - 1;
|
|
40260
|
+
const question = questions[questionIndex];
|
|
40261
|
+
const answerText = match[2]?.trim();
|
|
40262
|
+
if (!question?.question || !answerText) {
|
|
40263
|
+
continue;
|
|
40264
|
+
}
|
|
40265
|
+
answers[question.question] = matchQuestionAnswer(question, answerText);
|
|
40266
|
+
}
|
|
40267
|
+
return Object.keys(answers).length > 0 ? answers : null;
|
|
40268
|
+
}
|
|
40269
|
+
function buildAllowResponse(requestId, decision) {
|
|
40270
|
+
return {
|
|
40271
|
+
request_id: requestId,
|
|
40272
|
+
decision
|
|
40273
|
+
};
|
|
40274
|
+
}
|
|
40275
|
+
function buildDenyResponse(requestId, message) {
|
|
40276
|
+
return {
|
|
40277
|
+
request_id: requestId,
|
|
40278
|
+
decision: {
|
|
40279
|
+
behavior: "deny",
|
|
40280
|
+
message
|
|
40281
|
+
}
|
|
40282
|
+
};
|
|
40283
|
+
}
|
|
40284
|
+
function getAskUserQuestionInput(input) {
|
|
40285
|
+
return input;
|
|
40286
|
+
}
|
|
40287
|
+
function formatAskUserQuestionPrompt(event) {
|
|
40288
|
+
const input = getAskUserQuestionInput(event.input);
|
|
40289
|
+
const questions = (input.questions ?? []).filter((question) => normalizeWhitespace(question.question ?? ""));
|
|
40290
|
+
const lines = [
|
|
40291
|
+
"The agent needs an answer before it can continue.",
|
|
40292
|
+
"",
|
|
40293
|
+
...questions.flatMap((question, index) => buildQuestionPrompt(question, index)),
|
|
40294
|
+
""
|
|
40295
|
+
];
|
|
40296
|
+
if (questions.length <= 1) {
|
|
40297
|
+
const singleQuestion = questions[0];
|
|
40298
|
+
lines.push(singleQuestion?.multiSelect ? "Reply with one or more option numbers/labels separated by commas, or just send a freeform answer in your next message." : "Reply with an option number/label, or just send a freeform answer in your next message.");
|
|
40299
|
+
} else {
|
|
40300
|
+
lines.push("Reply with numbered lines, for example:", "1: your answer", "2: your answer", "", "You can also use option numbers or option labels. For multi-select questions, separate multiple answers with commas.");
|
|
40301
|
+
}
|
|
40302
|
+
return lines.join(`
|
|
40303
|
+
`);
|
|
40304
|
+
}
|
|
40305
|
+
function formatEnterPlanModePrompt() {
|
|
40306
|
+
return [
|
|
40307
|
+
"The agent wants to enter plan mode before making changes.",
|
|
40308
|
+
"",
|
|
40309
|
+
"Reply `approve` to let it plan first, or reply `deny` to skip planning and continue normally."
|
|
40310
|
+
].join(`
|
|
40311
|
+
`);
|
|
40312
|
+
}
|
|
40313
|
+
function formatExitPlanModePrompt(event) {
|
|
40314
|
+
const lines = [
|
|
40315
|
+
"The agent is ready to leave plan mode and start implementing."
|
|
40316
|
+
];
|
|
40317
|
+
if (event.planContent?.trim()) {
|
|
40318
|
+
lines.push("", "Proposed plan:", summarizePlanPreview(event.planContent));
|
|
40319
|
+
if (event.planFilePath?.trim()) {
|
|
40320
|
+
lines.push("", `Plan file: ${event.planFilePath.trim()}`);
|
|
40321
|
+
}
|
|
40322
|
+
}
|
|
40323
|
+
lines.push("", "Reply `approve` to accept the plan and start coding.", "Reply with feedback instead if you want the agent to keep planning.");
|
|
40324
|
+
return lines.join(`
|
|
40325
|
+
`);
|
|
40326
|
+
}
|
|
40327
|
+
function formatGenericToolApprovalPrompt(event) {
|
|
40328
|
+
const inputSummary = summarizeControlRequestInput(event.input);
|
|
40329
|
+
const lines = [`The agent wants approval to run \`${event.toolName}\`.`];
|
|
40330
|
+
if (inputSummary) {
|
|
40331
|
+
lines.push("", "Tool input:", inputSummary);
|
|
40332
|
+
}
|
|
40333
|
+
lines.push("", "Reply `approve` to allow it.", "Reply with feedback instead if you want to deny it.");
|
|
40334
|
+
return lines.join(`
|
|
40335
|
+
`);
|
|
40336
|
+
}
|
|
40337
|
+
function formatChannelControlRequestPrompt(event) {
|
|
40338
|
+
switch (event.kind) {
|
|
40339
|
+
case "ask_user_question":
|
|
40340
|
+
return formatAskUserQuestionPrompt(event);
|
|
40341
|
+
case "enter_plan_mode":
|
|
40342
|
+
return formatEnterPlanModePrompt();
|
|
40343
|
+
case "exit_plan_mode":
|
|
40344
|
+
return formatExitPlanModePrompt(event);
|
|
40345
|
+
case "generic_tool_approval":
|
|
40346
|
+
return formatGenericToolApprovalPrompt(event);
|
|
40347
|
+
default: {
|
|
40348
|
+
const exhaustiveCheck = event.kind;
|
|
40349
|
+
return exhaustiveCheck;
|
|
40350
|
+
}
|
|
40351
|
+
}
|
|
40352
|
+
}
|
|
40353
|
+
function parseAskUserQuestionResponse(event, rawText) {
|
|
40354
|
+
const input = getAskUserQuestionInput(event.input);
|
|
40355
|
+
const questions = (input.questions ?? []).filter((question) => normalizeWhitespace(question.question ?? ""));
|
|
40356
|
+
if (questions.length === 0) {
|
|
40357
|
+
return {
|
|
40358
|
+
type: "reprompt",
|
|
40359
|
+
message: "I couldn't find the original question payload. Please ask the agent to try again."
|
|
40360
|
+
};
|
|
40361
|
+
}
|
|
40362
|
+
if (questions.length === 1) {
|
|
40363
|
+
const [question] = questions;
|
|
40364
|
+
if (!question?.question) {
|
|
40365
|
+
return {
|
|
40366
|
+
type: "reprompt",
|
|
40367
|
+
message: "I couldn't find the original question text. Please ask the agent to try again."
|
|
40368
|
+
};
|
|
40369
|
+
}
|
|
40370
|
+
const answer = matchQuestionAnswer(question, rawText);
|
|
40371
|
+
return {
|
|
40372
|
+
type: "response",
|
|
40373
|
+
response: buildAllowResponse(event.requestId, {
|
|
40374
|
+
behavior: "allow",
|
|
40375
|
+
updated_input: {
|
|
40376
|
+
...event.input,
|
|
40377
|
+
answers: {
|
|
40378
|
+
...input.answers ?? {},
|
|
40379
|
+
[question.question]: answer
|
|
40380
|
+
}
|
|
40381
|
+
}
|
|
40382
|
+
})
|
|
40383
|
+
};
|
|
40384
|
+
}
|
|
40385
|
+
const numberedAnswers = parseNumberedAnswers(rawText, questions);
|
|
40386
|
+
if (!numberedAnswers) {
|
|
40387
|
+
return {
|
|
40388
|
+
type: "reprompt",
|
|
40389
|
+
message: `Please answer with numbered lines so I can map each reply to the right question.
|
|
40390
|
+
Example:
|
|
40391
|
+
1: your answer
|
|
40392
|
+
2: your answer`
|
|
40393
|
+
};
|
|
40394
|
+
}
|
|
40395
|
+
const missingQuestions = questions.filter((question) => question.question && !Object.hasOwn(numberedAnswers, question.question));
|
|
40396
|
+
if (missingQuestions.length > 0) {
|
|
40397
|
+
return {
|
|
40398
|
+
type: "reprompt",
|
|
40399
|
+
message: `I still need answers for: ${missingQuestions.map((question) => question.question).join(", ")}`
|
|
40400
|
+
};
|
|
40401
|
+
}
|
|
40402
|
+
return {
|
|
40403
|
+
type: "response",
|
|
40404
|
+
response: buildAllowResponse(event.requestId, {
|
|
40405
|
+
behavior: "allow",
|
|
40406
|
+
updated_input: {
|
|
40407
|
+
...event.input,
|
|
40408
|
+
answers: {
|
|
40409
|
+
...input.answers ?? {},
|
|
40410
|
+
...numberedAnswers
|
|
40411
|
+
}
|
|
40412
|
+
}
|
|
40413
|
+
})
|
|
40414
|
+
};
|
|
40415
|
+
}
|
|
40416
|
+
function parseEnterPlanModeResponse(event, rawText) {
|
|
40417
|
+
if (isAffirmativeResponse(rawText)) {
|
|
40418
|
+
return {
|
|
40419
|
+
type: "response",
|
|
40420
|
+
response: buildAllowResponse(event.requestId, {
|
|
40421
|
+
behavior: "allow"
|
|
40422
|
+
})
|
|
40423
|
+
};
|
|
40424
|
+
}
|
|
40425
|
+
if (isNegativeResponse(rawText)) {
|
|
40426
|
+
return {
|
|
40427
|
+
type: "response",
|
|
40428
|
+
response: buildDenyResponse(event.requestId, "User chose to skip plan mode and continue implementing directly.")
|
|
40429
|
+
};
|
|
40430
|
+
}
|
|
40431
|
+
return {
|
|
40432
|
+
type: "reprompt",
|
|
40433
|
+
message: "Reply `approve` to let the agent enter plan mode, or `deny` to skip planning."
|
|
40434
|
+
};
|
|
40435
|
+
}
|
|
40436
|
+
function parseExitPlanModeResponse(event, rawText) {
|
|
40437
|
+
if (isAffirmativeResponse(rawText)) {
|
|
40438
|
+
return {
|
|
40439
|
+
type: "response",
|
|
40440
|
+
response: buildAllowResponse(event.requestId, {
|
|
40441
|
+
behavior: "allow"
|
|
40442
|
+
})
|
|
40443
|
+
};
|
|
40444
|
+
}
|
|
40445
|
+
const feedback = stripApprovalPrefix(rawText);
|
|
40446
|
+
return {
|
|
40447
|
+
type: "response",
|
|
40448
|
+
response: buildDenyResponse(event.requestId, feedback || "Please keep planning and revise the proposal.")
|
|
40449
|
+
};
|
|
40450
|
+
}
|
|
40451
|
+
function parseGenericToolApprovalResponse(event, rawText) {
|
|
40452
|
+
if (isAffirmativeResponse(rawText)) {
|
|
40453
|
+
const message = stripApprovalPrefix(rawText);
|
|
40454
|
+
return {
|
|
40455
|
+
type: "response",
|
|
40456
|
+
response: buildAllowResponse(event.requestId, {
|
|
40457
|
+
behavior: "allow",
|
|
40458
|
+
...message ? { message } : {}
|
|
40459
|
+
})
|
|
40460
|
+
};
|
|
40461
|
+
}
|
|
40462
|
+
const feedback = stripApprovalPrefix(rawText);
|
|
40463
|
+
return {
|
|
40464
|
+
type: "response",
|
|
40465
|
+
response: buildDenyResponse(event.requestId, feedback || "Denied by channel user.")
|
|
40466
|
+
};
|
|
40467
|
+
}
|
|
40468
|
+
function parseChannelControlRequestResponse(event, rawText) {
|
|
40469
|
+
const trimmed = rawText.trim();
|
|
40470
|
+
if (!trimmed) {
|
|
40471
|
+
return {
|
|
40472
|
+
type: "reprompt",
|
|
40473
|
+
message: formatChannelControlRequestPrompt(event)
|
|
40474
|
+
};
|
|
40475
|
+
}
|
|
40476
|
+
switch (event.kind) {
|
|
40477
|
+
case "ask_user_question":
|
|
40478
|
+
return parseAskUserQuestionResponse(event, trimmed);
|
|
40479
|
+
case "enter_plan_mode":
|
|
40480
|
+
return parseEnterPlanModeResponse(event, trimmed);
|
|
40481
|
+
case "exit_plan_mode":
|
|
40482
|
+
return parseExitPlanModeResponse(event, trimmed);
|
|
40483
|
+
case "generic_tool_approval":
|
|
40484
|
+
return parseGenericToolApprovalResponse(event, trimmed);
|
|
40485
|
+
default: {
|
|
40486
|
+
const exhaustiveCheck = event.kind;
|
|
40487
|
+
return exhaustiveCheck;
|
|
40488
|
+
}
|
|
40489
|
+
}
|
|
40490
|
+
}
|
|
40491
|
+
|
|
40130
40492
|
// src/channels/telegram/media.ts
|
|
40131
40493
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
40132
40494
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -40656,14 +41018,27 @@ function getPackageManagerExecutable(packageManager) {
|
|
|
40656
41018
|
}
|
|
40657
41019
|
return packageManager;
|
|
40658
41020
|
}
|
|
41021
|
+
function resolveInstallPlatform() {
|
|
41022
|
+
return platformOverride ?? process.platform;
|
|
41023
|
+
}
|
|
40659
41024
|
function getInstallArgs(packageManager, installPackages) {
|
|
41025
|
+
const noBinLinks = resolveInstallPlatform() === "win32" && packageManager !== "bun";
|
|
40660
41026
|
switch (packageManager) {
|
|
40661
41027
|
case "bun":
|
|
40662
41028
|
return ["add", "--no-save", ...installPackages];
|
|
40663
41029
|
case "pnpm":
|
|
40664
|
-
return [
|
|
41030
|
+
return [
|
|
41031
|
+
"add",
|
|
41032
|
+
...noBinLinks ? ["--no-bin-links"] : [],
|
|
41033
|
+
...installPackages
|
|
41034
|
+
];
|
|
40665
41035
|
case "npm":
|
|
40666
|
-
return [
|
|
41036
|
+
return [
|
|
41037
|
+
"install",
|
|
41038
|
+
"--no-save",
|
|
41039
|
+
...noBinLinks ? ["--no-bin-links"] : [],
|
|
41040
|
+
...installPackages
|
|
41041
|
+
];
|
|
40667
41042
|
}
|
|
40668
41043
|
}
|
|
40669
41044
|
async function installChannelRuntime(channelId) {
|
|
@@ -41097,6 +41472,13 @@ function createTelegramAdapter(config) {
|
|
|
41097
41472
|
} : undefined;
|
|
41098
41473
|
await telegramBot.api.sendMessage(chatId, text, reply_parameters ? { reply_parameters } : {});
|
|
41099
41474
|
},
|
|
41475
|
+
async handleControlRequestEvent(event) {
|
|
41476
|
+
const telegramBot = await ensureBot();
|
|
41477
|
+
const reply_parameters = event.source.messageId || event.source.threadId ? {
|
|
41478
|
+
message_id: Number(event.source.threadId ?? event.source.messageId)
|
|
41479
|
+
} : undefined;
|
|
41480
|
+
await telegramBot.api.sendMessage(event.source.chatId, formatChannelControlRequestPrompt(event), reply_parameters ? { reply_parameters } : {});
|
|
41481
|
+
},
|
|
41100
41482
|
onMessage: undefined
|
|
41101
41483
|
};
|
|
41102
41484
|
return adapter;
|
|
@@ -41296,6 +41678,14 @@ var init_plugin = __esm(() => {
|
|
|
41296
41678
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
41297
41679
|
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
|
|
41298
41680
|
import { basename as basename3, extname as extname2, join as join12 } from "node:path";
|
|
41681
|
+
function mapSlackThreadMessage(message) {
|
|
41682
|
+
return {
|
|
41683
|
+
text: resolveSlackThreadMessageText(message),
|
|
41684
|
+
userId: isNonEmptyString(message.user) ? message.user : undefined,
|
|
41685
|
+
botId: isNonEmptyString(message.bot_id) ? message.bot_id : undefined,
|
|
41686
|
+
ts: isNonEmptyString(message.ts) ? message.ts : undefined
|
|
41687
|
+
};
|
|
41688
|
+
}
|
|
41299
41689
|
function asRecord(value) {
|
|
41300
41690
|
return value && typeof value === "object" ? value : null;
|
|
41301
41691
|
}
|
|
@@ -41569,12 +41959,31 @@ async function resolveSlackThreadHistory(params) {
|
|
|
41569
41959
|
const nextCursor = response.response_metadata?.next_cursor;
|
|
41570
41960
|
cursor = typeof nextCursor === "string" && nextCursor.trim().length > 0 ? nextCursor.trim() : undefined;
|
|
41571
41961
|
} while (cursor);
|
|
41572
|
-
return retained.map(
|
|
41573
|
-
|
|
41574
|
-
|
|
41575
|
-
|
|
41576
|
-
|
|
41577
|
-
|
|
41962
|
+
return retained.map(mapSlackThreadMessage);
|
|
41963
|
+
} catch {
|
|
41964
|
+
return [];
|
|
41965
|
+
}
|
|
41966
|
+
}
|
|
41967
|
+
async function resolveSlackChannelHistory(params) {
|
|
41968
|
+
const maxMessages = params.limit ?? 20;
|
|
41969
|
+
if (!Number.isFinite(maxMessages) || maxMessages <= 0) {
|
|
41970
|
+
return [];
|
|
41971
|
+
}
|
|
41972
|
+
const fetchLimit = Math.min(Math.max(maxMessages * 3, maxMessages), 100);
|
|
41973
|
+
try {
|
|
41974
|
+
const response = await params.client.conversations.history({
|
|
41975
|
+
channel: params.channelId,
|
|
41976
|
+
latest: params.beforeTs,
|
|
41977
|
+
inclusive: false,
|
|
41978
|
+
limit: fetchLimit
|
|
41979
|
+
});
|
|
41980
|
+
const retained = (response.messages ?? []).filter((message) => {
|
|
41981
|
+
if (message.ts === params.beforeTs) {
|
|
41982
|
+
return false;
|
|
41983
|
+
}
|
|
41984
|
+
return Boolean(resolveSlackThreadMessageText(message));
|
|
41985
|
+
}).slice(0, fetchLimit).reverse();
|
|
41986
|
+
return retained.slice(-maxMessages).map(mapSlackThreadMessage);
|
|
41578
41987
|
} catch {
|
|
41579
41988
|
return [];
|
|
41580
41989
|
}
|
|
@@ -41744,6 +42153,13 @@ function buildSlackThreadLabel(msg, starterText) {
|
|
|
41744
42153
|
}
|
|
41745
42154
|
return roomLabel ? `Slack thread${roomLabel}` : `Slack thread ${msg.chatId}`;
|
|
41746
42155
|
}
|
|
42156
|
+
function buildSlackChannelContextLabel(msg) {
|
|
42157
|
+
if (msg.chatType !== "channel") {
|
|
42158
|
+
return;
|
|
42159
|
+
}
|
|
42160
|
+
const roomLabel = isNonEmptyString2(msg.chatLabel) && msg.chatLabel !== msg.chatId ? ` in ${msg.chatLabel}` : "";
|
|
42161
|
+
return roomLabel ? `Slack channel context${roomLabel} before thread start` : `Slack channel context before thread start`;
|
|
42162
|
+
}
|
|
41747
42163
|
async function resolveSlackAccountDisplayName(botToken, appToken) {
|
|
41748
42164
|
const bolt = await loadSlackBoltModule();
|
|
41749
42165
|
const App2 = resolveSlackAppConstructor(bolt);
|
|
@@ -42222,22 +42638,42 @@ function createSlackAdapter(config) {
|
|
|
42222
42638
|
});
|
|
42223
42639
|
rememberMessageThread(response.ts, options?.replyToMessageId ?? response.ts ?? null);
|
|
42224
42640
|
},
|
|
42641
|
+
async handleControlRequestEvent(event) {
|
|
42642
|
+
await ensureApp();
|
|
42643
|
+
const slackClient = await ensureWriteClient();
|
|
42644
|
+
const response = await slackClient.chat.postMessage({
|
|
42645
|
+
channel: event.source.chatId,
|
|
42646
|
+
text: formatChannelControlRequestPrompt(event),
|
|
42647
|
+
...event.source.threadId ?? event.source.messageId ? { thread_ts: event.source.threadId ?? event.source.messageId } : {}
|
|
42648
|
+
});
|
|
42649
|
+
rememberMessageThread(response.ts, event.source.threadId ?? event.source.messageId ?? response.ts ?? null);
|
|
42650
|
+
},
|
|
42225
42651
|
async prepareInboundMessage(msg, options) {
|
|
42226
|
-
if (!options?.isFirstRouteTurn || msg.channel !== "slack" || msg.chatType !== "channel" || !isNonEmptyString2(msg.threadId) || !isNonEmptyString2(msg.messageId)
|
|
42652
|
+
if (!options?.isFirstRouteTurn || msg.channel !== "slack" || msg.chatType !== "channel" || !isNonEmptyString2(msg.threadId) || !isNonEmptyString2(msg.messageId)) {
|
|
42653
|
+
return msg;
|
|
42654
|
+
}
|
|
42655
|
+
const shouldHydrateExistingThreadContext = msg.threadId !== msg.messageId;
|
|
42656
|
+
const shouldHydrateChannelBootstrapContext = msg.isMention === true && msg.threadId === msg.messageId;
|
|
42657
|
+
if (!shouldHydrateExistingThreadContext && !shouldHydrateChannelBootstrapContext) {
|
|
42227
42658
|
return msg;
|
|
42228
42659
|
}
|
|
42229
42660
|
const slackApp = await ensureApp();
|
|
42230
|
-
const starter = await resolveSlackThreadStarter({
|
|
42661
|
+
const starter = shouldHydrateExistingThreadContext ? await resolveSlackThreadStarter({
|
|
42231
42662
|
channelId: msg.chatId,
|
|
42232
42663
|
threadTs: msg.threadId,
|
|
42233
42664
|
client: slackApp.client
|
|
42234
|
-
});
|
|
42235
|
-
const history = await resolveSlackThreadHistory({
|
|
42665
|
+
}) : null;
|
|
42666
|
+
const history = shouldHydrateExistingThreadContext ? await resolveSlackThreadHistory({
|
|
42236
42667
|
channelId: msg.chatId,
|
|
42237
42668
|
threadTs: msg.threadId,
|
|
42238
42669
|
client: slackApp.client,
|
|
42239
42670
|
currentMessageTs: msg.messageId,
|
|
42240
42671
|
limit: INITIAL_SLACK_THREAD_HISTORY_LIMIT
|
|
42672
|
+
}) : await resolveSlackChannelHistory({
|
|
42673
|
+
channelId: msg.chatId,
|
|
42674
|
+
beforeTs: msg.messageId,
|
|
42675
|
+
client: slackApp.client,
|
|
42676
|
+
limit: INITIAL_SLACK_THREAD_HISTORY_LIMIT
|
|
42241
42677
|
});
|
|
42242
42678
|
if (!starter && history.length === 0) {
|
|
42243
42679
|
return msg;
|
|
@@ -42266,7 +42702,7 @@ function createSlackAdapter(config) {
|
|
|
42266
42702
|
return {
|
|
42267
42703
|
...msg,
|
|
42268
42704
|
threadContext: {
|
|
42269
|
-
label: buildSlackThreadLabel(msg, starter?.text),
|
|
42705
|
+
label: shouldHydrateExistingThreadContext ? buildSlackThreadLabel(msg, starter?.text) : buildSlackChannelContextLabel(msg),
|
|
42270
42706
|
...starter ? {
|
|
42271
42707
|
starter: {
|
|
42272
42708
|
messageId: starter.ts,
|
|
@@ -42436,6 +42872,7 @@ DM Policy — who can message this app directly?
|
|
|
42436
42872
|
botToken,
|
|
42437
42873
|
appToken,
|
|
42438
42874
|
agentId: null,
|
|
42875
|
+
defaultPermissionMode: "default",
|
|
42439
42876
|
dmPolicy: policy,
|
|
42440
42877
|
allowedUsers,
|
|
42441
42878
|
createdAt: now,
|
|
@@ -43278,6 +43715,14 @@ function buildChannelTurnSource(route, msg) {
|
|
|
43278
43715
|
conversationId: route.conversationId
|
|
43279
43716
|
};
|
|
43280
43717
|
}
|
|
43718
|
+
function getChannelApprovalScopeKey(params) {
|
|
43719
|
+
return [
|
|
43720
|
+
params.channel,
|
|
43721
|
+
params.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID,
|
|
43722
|
+
params.chatId,
|
|
43723
|
+
params.threadId ?? ""
|
|
43724
|
+
].join(":");
|
|
43725
|
+
}
|
|
43281
43726
|
function getChannelRegistry() {
|
|
43282
43727
|
return instance;
|
|
43283
43728
|
}
|
|
@@ -43295,7 +43740,10 @@ class ChannelRegistry {
|
|
|
43295
43740
|
ready = false;
|
|
43296
43741
|
messageHandler = null;
|
|
43297
43742
|
eventHandler = null;
|
|
43743
|
+
approvalResponseHandler = null;
|
|
43298
43744
|
buffer = [];
|
|
43745
|
+
pendingControlRequestsById = new Map;
|
|
43746
|
+
pendingControlRequestIdByScope = new Map;
|
|
43299
43747
|
constructor() {
|
|
43300
43748
|
if (instance) {
|
|
43301
43749
|
throw new Error("ChannelRegistry is a singleton — use getChannelRegistry()");
|
|
@@ -43365,9 +43813,57 @@ class ChannelRegistry {
|
|
|
43365
43813
|
setMessageHandler(handler) {
|
|
43366
43814
|
this.messageHandler = handler;
|
|
43367
43815
|
}
|
|
43816
|
+
setApprovalResponseHandler(handler) {
|
|
43817
|
+
this.approvalResponseHandler = handler;
|
|
43818
|
+
}
|
|
43368
43819
|
setEventHandler(handler) {
|
|
43369
43820
|
this.eventHandler = handler;
|
|
43370
43821
|
}
|
|
43822
|
+
async registerPendingControlRequest(event) {
|
|
43823
|
+
const scopeKey = getChannelApprovalScopeKey({
|
|
43824
|
+
channel: event.source.channel,
|
|
43825
|
+
accountId: event.source.accountId,
|
|
43826
|
+
chatId: event.source.chatId,
|
|
43827
|
+
threadId: event.source.threadId
|
|
43828
|
+
});
|
|
43829
|
+
const existingRequestId = this.pendingControlRequestIdByScope.get(scopeKey);
|
|
43830
|
+
if (existingRequestId) {
|
|
43831
|
+
this.clearPendingControlRequest(existingRequestId);
|
|
43832
|
+
}
|
|
43833
|
+
this.pendingControlRequestsById.set(event.requestId, event);
|
|
43834
|
+
this.pendingControlRequestIdByScope.set(scopeKey, event.requestId);
|
|
43835
|
+
const adapter = this.getAdapter(event.source.channel, event.source.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID);
|
|
43836
|
+
if (!adapter) {
|
|
43837
|
+
return;
|
|
43838
|
+
}
|
|
43839
|
+
try {
|
|
43840
|
+
if (adapter.handleControlRequestEvent) {
|
|
43841
|
+
await adapter.handleControlRequestEvent(event);
|
|
43842
|
+
return;
|
|
43843
|
+
}
|
|
43844
|
+
await adapter.sendDirectReply(event.source.chatId, formatChannelControlRequestPrompt(event), {
|
|
43845
|
+
replyToMessageId: event.source.threadId ?? event.source.messageId
|
|
43846
|
+
});
|
|
43847
|
+
} catch (error) {
|
|
43848
|
+
console.error(`[Channels] Failed to deliver control request prompt for ${event.source.channel}/${event.source.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID}:`, error instanceof Error ? error.message : error);
|
|
43849
|
+
}
|
|
43850
|
+
}
|
|
43851
|
+
clearPendingControlRequest(requestId) {
|
|
43852
|
+
const pending = this.pendingControlRequestsById.get(requestId);
|
|
43853
|
+
if (!pending) {
|
|
43854
|
+
return;
|
|
43855
|
+
}
|
|
43856
|
+
this.pendingControlRequestsById.delete(requestId);
|
|
43857
|
+
const scopeKey = getChannelApprovalScopeKey({
|
|
43858
|
+
channel: pending.source.channel,
|
|
43859
|
+
accountId: pending.source.accountId,
|
|
43860
|
+
chatId: pending.source.chatId,
|
|
43861
|
+
threadId: pending.source.threadId
|
|
43862
|
+
});
|
|
43863
|
+
if (this.pendingControlRequestIdByScope.get(scopeKey) === requestId) {
|
|
43864
|
+
this.pendingControlRequestIdByScope.delete(scopeKey);
|
|
43865
|
+
}
|
|
43866
|
+
}
|
|
43371
43867
|
setReady() {
|
|
43372
43868
|
this.ready = true;
|
|
43373
43869
|
this.flushBuffer();
|
|
@@ -43459,6 +43955,7 @@ class ChannelRegistry {
|
|
|
43459
43955
|
this.ready = false;
|
|
43460
43956
|
this.messageHandler = null;
|
|
43461
43957
|
this.eventHandler = null;
|
|
43958
|
+
this.approvalResponseHandler = null;
|
|
43462
43959
|
}
|
|
43463
43960
|
async stopAll() {
|
|
43464
43961
|
for (const adapter of Array.from(this.adapters.values())) {
|
|
@@ -43469,13 +43966,63 @@ class ChannelRegistry {
|
|
|
43469
43966
|
this.ready = false;
|
|
43470
43967
|
this.messageHandler = null;
|
|
43471
43968
|
this.eventHandler = null;
|
|
43969
|
+
this.approvalResponseHandler = null;
|
|
43970
|
+
this.pendingControlRequestsById.clear();
|
|
43971
|
+
this.pendingControlRequestIdByScope.clear();
|
|
43472
43972
|
instance = null;
|
|
43473
43973
|
}
|
|
43974
|
+
async tryHandlePendingControlRequest(adapter, msg) {
|
|
43975
|
+
const scopeKey = getChannelApprovalScopeKey({
|
|
43976
|
+
channel: msg.channel,
|
|
43977
|
+
accountId: msg.accountId,
|
|
43978
|
+
chatId: msg.chatId,
|
|
43979
|
+
threadId: msg.threadId
|
|
43980
|
+
});
|
|
43981
|
+
const requestId = this.pendingControlRequestIdByScope.get(scopeKey);
|
|
43982
|
+
if (!requestId) {
|
|
43983
|
+
return false;
|
|
43984
|
+
}
|
|
43985
|
+
const pending = this.pendingControlRequestsById.get(requestId);
|
|
43986
|
+
if (!pending) {
|
|
43987
|
+
this.pendingControlRequestIdByScope.delete(scopeKey);
|
|
43988
|
+
return false;
|
|
43989
|
+
}
|
|
43990
|
+
const parsed = parseChannelControlRequestResponse(pending, msg.text);
|
|
43991
|
+
if (parsed.type === "reprompt") {
|
|
43992
|
+
await adapter.sendDirectReply(msg.chatId, parsed.message, {
|
|
43993
|
+
replyToMessageId: msg.threadId ?? msg.messageId
|
|
43994
|
+
});
|
|
43995
|
+
return true;
|
|
43996
|
+
}
|
|
43997
|
+
if (!this.approvalResponseHandler) {
|
|
43998
|
+
await adapter.sendDirectReply(msg.chatId, "I’m reconnecting to Letta Code right now, so I couldn’t use that reply yet. Please send it again in a moment.", {
|
|
43999
|
+
replyToMessageId: msg.threadId ?? msg.messageId
|
|
44000
|
+
});
|
|
44001
|
+
return true;
|
|
44002
|
+
}
|
|
44003
|
+
const handled = await this.approvalResponseHandler({
|
|
44004
|
+
runtime: {
|
|
44005
|
+
agent_id: pending.source.agentId,
|
|
44006
|
+
conversation_id: pending.source.conversationId
|
|
44007
|
+
},
|
|
44008
|
+
response: parsed.response
|
|
44009
|
+
});
|
|
44010
|
+
this.clearPendingControlRequest(requestId);
|
|
44011
|
+
if (!handled) {
|
|
44012
|
+
await adapter.sendDirectReply(msg.chatId, "That approval prompt expired before I could use your reply. Please ask the agent to try again.", {
|
|
44013
|
+
replyToMessageId: msg.threadId ?? msg.messageId
|
|
44014
|
+
});
|
|
44015
|
+
}
|
|
44016
|
+
return true;
|
|
44017
|
+
}
|
|
43474
44018
|
async handleInboundMessage(msg) {
|
|
43475
44019
|
const accountId = msg.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
43476
44020
|
const adapter = this.getAdapter(msg.channel, accountId);
|
|
43477
44021
|
if (!adapter)
|
|
43478
44022
|
return;
|
|
44023
|
+
if (await this.tryHandlePendingControlRequest(adapter, msg)) {
|
|
44024
|
+
return;
|
|
44025
|
+
}
|
|
43479
44026
|
const config = getChannelAccount(msg.channel, accountId);
|
|
43480
44027
|
if (!config)
|
|
43481
44028
|
return;
|
|
@@ -43558,6 +44105,16 @@ class ChannelRegistry {
|
|
|
43558
44105
|
updatedAt: now
|
|
43559
44106
|
};
|
|
43560
44107
|
addRoute(msg.channel, route);
|
|
44108
|
+
if (config.defaultPermissionMode !== "default") {
|
|
44109
|
+
this.eventHandler?.({
|
|
44110
|
+
type: "slack_conversation_created",
|
|
44111
|
+
channelId: "slack",
|
|
44112
|
+
accountId: config.accountId,
|
|
44113
|
+
agentId: config.agentId,
|
|
44114
|
+
conversationId,
|
|
44115
|
+
defaultPermissionMode: config.defaultPermissionMode
|
|
44116
|
+
});
|
|
44117
|
+
}
|
|
43561
44118
|
return route;
|
|
43562
44119
|
}
|
|
43563
44120
|
async ensureSlackRoute(adapter, msg, config) {
|
|
@@ -52520,6 +53077,7 @@ function createConversationRuntime(listener, agentId, conversationId) {
|
|
|
52520
53077
|
key: runtimeKey,
|
|
52521
53078
|
agentId: normalizedAgentId,
|
|
52522
53079
|
conversationId: normalizedConversationId,
|
|
53080
|
+
activeChannelTurnSources: null,
|
|
52523
53081
|
messageQueue: Promise.resolve(),
|
|
52524
53082
|
pendingApprovalResolvers: new Map,
|
|
52525
53083
|
recoveredApprovalState: null,
|
|
@@ -74741,7 +75299,12 @@ async function executeTool(name, args, options) {
|
|
|
74741
75299
|
enhancedArgs = { ...enhancedArgs, signal: options.signal };
|
|
74742
75300
|
}
|
|
74743
75301
|
if (options?.onOutput) {
|
|
74744
|
-
enhancedArgs = {
|
|
75302
|
+
enhancedArgs = {
|
|
75303
|
+
...enhancedArgs,
|
|
75304
|
+
onOutput: (chunk, stream2) => {
|
|
75305
|
+
options.onOutput?.(scrubSecretsFromString(chunk), stream2);
|
|
75306
|
+
}
|
|
75307
|
+
};
|
|
74745
75308
|
}
|
|
74746
75309
|
enhancedArgs = substituteSecretsInArgs(enhancedArgs);
|
|
74747
75310
|
}
|
|
@@ -75241,6 +75804,7 @@ function toAccountSnapshot(account) {
|
|
|
75241
75804
|
hasBotToken: account.botToken.trim().length > 0,
|
|
75242
75805
|
hasAppToken: account.appToken.trim().length > 0,
|
|
75243
75806
|
agentId: account.agentId,
|
|
75807
|
+
defaultPermissionMode: account.defaultPermissionMode ?? "default",
|
|
75244
75808
|
createdAt: account.createdAt,
|
|
75245
75809
|
updatedAt: account.updatedAt
|
|
75246
75810
|
};
|
|
@@ -75273,6 +75837,7 @@ function createAccountFromPatch(channelId, accountId, patch) {
|
|
|
75273
75837
|
botToken: patch.botToken ?? "",
|
|
75274
75838
|
appToken: patch.appToken ?? "",
|
|
75275
75839
|
agentId: patch.agentId ?? null,
|
|
75840
|
+
defaultPermissionMode: patch.defaultPermissionMode ?? "default",
|
|
75276
75841
|
dmPolicy: patch.dmPolicy ?? "open",
|
|
75277
75842
|
allowedUsers: patch.allowedUsers ?? [],
|
|
75278
75843
|
createdAt: now,
|
|
@@ -75300,6 +75865,7 @@ function mergeAccountPatch(existing, patch) {
|
|
|
75300
75865
|
botToken: patch.botToken ?? existing.botToken,
|
|
75301
75866
|
appToken: patch.appToken ?? existing.appToken,
|
|
75302
75867
|
agentId: patch.agentId ?? existing.agentId,
|
|
75868
|
+
defaultPermissionMode: patch.defaultPermissionMode ?? existing.defaultPermissionMode ?? "default",
|
|
75303
75869
|
dmPolicy: patch.dmPolicy ?? existing.dmPolicy,
|
|
75304
75870
|
allowedUsers: patch.allowedUsers ?? existing.allowedUsers,
|
|
75305
75871
|
updatedAt: nextUpdatedAt
|
|
@@ -77195,13 +77761,58 @@ function writeCronFile(data) {
|
|
|
77195
77761
|
writeFileSync14(tmp, JSON.stringify(data, null, 2), { flush: true });
|
|
77196
77762
|
renameSync2(tmp, path20);
|
|
77197
77763
|
}
|
|
77198
|
-
function
|
|
77764
|
+
function readLinuxProcessIdentity(pid) {
|
|
77765
|
+
try {
|
|
77766
|
+
const stat5 = readFileSync15(`/proc/${pid}/stat`, "utf8");
|
|
77767
|
+
const endCommand = stat5.lastIndexOf(")");
|
|
77768
|
+
if (endCommand === -1) {
|
|
77769
|
+
return null;
|
|
77770
|
+
}
|
|
77771
|
+
const fields = stat5.slice(endCommand + 2).trim().split(/\s+/);
|
|
77772
|
+
const startTicks = fields[19] ?? null;
|
|
77773
|
+
if (!startTicks) {
|
|
77774
|
+
return null;
|
|
77775
|
+
}
|
|
77776
|
+
let bootId = null;
|
|
77777
|
+
try {
|
|
77778
|
+
bootId = readFileSync15("/proc/sys/kernel/random/boot_id", "utf8").trim() || null;
|
|
77779
|
+
} catch {}
|
|
77780
|
+
return { startTicks, bootId };
|
|
77781
|
+
} catch {
|
|
77782
|
+
return null;
|
|
77783
|
+
}
|
|
77784
|
+
}
|
|
77785
|
+
function readProcessIdentity(pid) {
|
|
77786
|
+
if (readProcessIdentityOverride) {
|
|
77787
|
+
return readProcessIdentityOverride(pid);
|
|
77788
|
+
}
|
|
77789
|
+
return readLinuxProcessIdentity(pid);
|
|
77790
|
+
}
|
|
77791
|
+
function captureProcessIdentity(pid) {
|
|
77792
|
+
const identity = readProcessIdentity(pid);
|
|
77793
|
+
return {
|
|
77794
|
+
process_start_ticks: identity?.startTicks ?? null,
|
|
77795
|
+
boot_id: identity?.bootId ?? null
|
|
77796
|
+
};
|
|
77797
|
+
}
|
|
77798
|
+
function isProcessAlive(pid, owner) {
|
|
77199
77799
|
try {
|
|
77200
77800
|
process.kill(pid, 0);
|
|
77201
|
-
return true;
|
|
77202
77801
|
} catch {
|
|
77203
77802
|
return false;
|
|
77204
77803
|
}
|
|
77804
|
+
if (owner) {
|
|
77805
|
+
const identity = readProcessIdentity(pid);
|
|
77806
|
+
if (identity) {
|
|
77807
|
+
if (owner.boot_id && identity.bootId && owner.boot_id !== identity.bootId) {
|
|
77808
|
+
return false;
|
|
77809
|
+
}
|
|
77810
|
+
if (owner.process_start_ticks && identity.startTicks && owner.process_start_ticks !== identity.startTicks) {
|
|
77811
|
+
return false;
|
|
77812
|
+
}
|
|
77813
|
+
}
|
|
77814
|
+
}
|
|
77815
|
+
return true;
|
|
77205
77816
|
}
|
|
77206
77817
|
function readLockOwner(lockDir) {
|
|
77207
77818
|
try {
|
|
@@ -77224,7 +77835,7 @@ function isLockStale(lockDir) {
|
|
|
77224
77835
|
return true;
|
|
77225
77836
|
}
|
|
77226
77837
|
}
|
|
77227
|
-
const pidDead = !isProcessAlive(owner.pid);
|
|
77838
|
+
const pidDead = !isProcessAlive(owner.pid, owner);
|
|
77228
77839
|
const isOld = Date.now() - owner.acquired_at > LOCK_STALE_AGE_MS;
|
|
77229
77840
|
return pidDead && isOld;
|
|
77230
77841
|
}
|
|
@@ -77243,7 +77854,8 @@ function acquireLock() {
|
|
|
77243
77854
|
const owner = {
|
|
77244
77855
|
pid: process.pid,
|
|
77245
77856
|
token,
|
|
77246
|
-
acquired_at: Date.now()
|
|
77857
|
+
acquired_at: Date.now(),
|
|
77858
|
+
...captureProcessIdentity(process.pid)
|
|
77247
77859
|
};
|
|
77248
77860
|
writeLockOwner(lockDir, owner);
|
|
77249
77861
|
return {
|
|
@@ -77336,7 +77948,7 @@ function addTask(input) {
|
|
|
77336
77948
|
prompt: input.prompt,
|
|
77337
77949
|
status: "active",
|
|
77338
77950
|
created_at: now.toISOString(),
|
|
77339
|
-
expires_at:
|
|
77951
|
+
expires_at: null,
|
|
77340
77952
|
last_fired_at: null,
|
|
77341
77953
|
fire_count: 0,
|
|
77342
77954
|
cancel_reason: null,
|
|
@@ -77348,7 +77960,7 @@ function addTask(input) {
|
|
|
77348
77960
|
data.tasks.push(task2);
|
|
77349
77961
|
writeCronFile(data);
|
|
77350
77962
|
let warning;
|
|
77351
|
-
if (!data.scheduler_owner || !isProcessAlive(data.scheduler_owner.pid)) {
|
|
77963
|
+
if (!data.scheduler_owner || !isProcessAlive(data.scheduler_owner.pid, data.scheduler_owner)) {
|
|
77352
77964
|
warning = "No letta server is currently running. This task will only execute when a WS listener is active.";
|
|
77353
77965
|
}
|
|
77354
77966
|
return { task: task2, warning };
|
|
@@ -77396,15 +78008,17 @@ function claimSchedulerLease() {
|
|
|
77396
78008
|
const data = readCronFile();
|
|
77397
78009
|
const token = randomBytes(4).toString("hex");
|
|
77398
78010
|
if (data.scheduler_owner) {
|
|
77399
|
-
const
|
|
77400
|
-
|
|
78011
|
+
const existingOwner = data.scheduler_owner;
|
|
78012
|
+
const { pid, token: existingToken } = existingOwner;
|
|
78013
|
+
if (isProcessAlive(pid, existingOwner)) {
|
|
77401
78014
|
throw new Error(`Scheduler lease held by PID ${pid} (token ${existingToken}). Cannot claim.`);
|
|
77402
78015
|
}
|
|
77403
78016
|
}
|
|
77404
78017
|
data.scheduler_owner = {
|
|
77405
78018
|
pid: process.pid,
|
|
77406
78019
|
token,
|
|
77407
|
-
started_at: new Date().toISOString()
|
|
78020
|
+
started_at: new Date().toISOString(),
|
|
78021
|
+
...captureProcessIdentity(process.pid)
|
|
77408
78022
|
};
|
|
77409
78023
|
writeCronFile(data);
|
|
77410
78024
|
return token;
|
|
@@ -77464,10 +78078,9 @@ function getCronFileMtime() {
|
|
|
77464
78078
|
return 0;
|
|
77465
78079
|
}
|
|
77466
78080
|
}
|
|
77467
|
-
var CRON_FILE_NAME = "crons.json", LOCK_DIR_NAME = "crons.lock", LOCK_TOKEN_FILE = "owner.json", LOCK_TIMEOUT_MS = 5000, LOCK_RETRY_MS = 50, LOCK_STALE_AGE_MS = 30000, MAX_ACTIVE_TASKS_PER_AGENT = 50, TASK_ID_BYTES = 4,
|
|
78081
|
+
var CRON_FILE_NAME = "crons.json", LOCK_DIR_NAME = "crons.lock", LOCK_TOKEN_FILE = "owner.json", LOCK_TIMEOUT_MS = 5000, LOCK_RETRY_MS = 50, LOCK_STALE_AGE_MS = 30000, MAX_ACTIVE_TASKS_PER_AGENT = 50, TASK_ID_BYTES = 4, GC_AGE_MS, readProcessIdentityOverride = null;
|
|
77468
78082
|
var init_cronFile = __esm(() => {
|
|
77469
78083
|
init_parseInterval();
|
|
77470
|
-
DEFAULT_TTL_MS = 3 * 24 * 60 * 60 * 1000;
|
|
77471
78084
|
GC_AGE_MS = 24 * 60 * 60 * 1000;
|
|
77472
78085
|
});
|
|
77473
78086
|
|
|
@@ -81751,6 +82364,18 @@ var MIN_SPLIT_LENGTH = 1500;
|
|
|
81751
82364
|
function isInteractiveApprovalTool(toolName) {
|
|
81752
82365
|
return INTERACTIVE_APPROVAL_TOOLS.has(toolName);
|
|
81753
82366
|
}
|
|
82367
|
+
function getInteractiveApprovalKind(toolName) {
|
|
82368
|
+
switch (toolName) {
|
|
82369
|
+
case "AskUserQuestion":
|
|
82370
|
+
return "ask_user_question";
|
|
82371
|
+
case "EnterPlanMode":
|
|
82372
|
+
return "enter_plan_mode";
|
|
82373
|
+
case "ExitPlanMode":
|
|
82374
|
+
return "exit_plan_mode";
|
|
82375
|
+
default:
|
|
82376
|
+
return null;
|
|
82377
|
+
}
|
|
82378
|
+
}
|
|
81754
82379
|
function isHeadlessAutoAllowTool(toolName) {
|
|
81755
82380
|
return HEADLESS_AUTO_ALLOW_TOOLS.has(toolName);
|
|
81756
82381
|
}
|
|
@@ -84514,6 +85139,25 @@ function normalizeInterruptOutputLines(value) {
|
|
|
84514
85139
|
const combinedLength = filtered.reduce((sum, entry) => sum + entry.length, 0);
|
|
84515
85140
|
return combinedLength <= INTERRUPT_TOOL_RETURN_MAX_CHARS ? filtered : undefined;
|
|
84516
85141
|
}
|
|
85142
|
+
function appendStreamingOutputWithCap(current, chunk) {
|
|
85143
|
+
if (chunk.length === 0) {
|
|
85144
|
+
return current;
|
|
85145
|
+
}
|
|
85146
|
+
const next = `${current}${chunk}`;
|
|
85147
|
+
if (next.length <= STREAMING_TOOL_OUTPUT_MAX_CHARS) {
|
|
85148
|
+
return next;
|
|
85149
|
+
}
|
|
85150
|
+
return next.slice(next.length - STREAMING_TOOL_OUTPUT_MAX_CHARS);
|
|
85151
|
+
}
|
|
85152
|
+
function normalizeStreamingOutputLines(text) {
|
|
85153
|
+
if (text.length === 0) {
|
|
85154
|
+
return;
|
|
85155
|
+
}
|
|
85156
|
+
const lines = text.replace(/\r\n/g, `
|
|
85157
|
+
`).split(`
|
|
85158
|
+
`).filter((line) => line.length > 0);
|
|
85159
|
+
return lines.length > 0 ? lines : undefined;
|
|
85160
|
+
}
|
|
84517
85161
|
function asToolReturnStatus(value) {
|
|
84518
85162
|
if (value === "success" || value === "error") {
|
|
84519
85163
|
return value;
|
|
@@ -84727,6 +85371,54 @@ function emitToolExecutionFinishedEvents(socket, runtime, params) {
|
|
|
84727
85371
|
});
|
|
84728
85372
|
}
|
|
84729
85373
|
}
|
|
85374
|
+
function createToolExecutionOutputEmitter(socket, runtime, params) {
|
|
85375
|
+
const outputByToolCallId = new Map;
|
|
85376
|
+
return (toolCallId, chunk, isStderr = false) => {
|
|
85377
|
+
if (!toolCallId || chunk.length === 0) {
|
|
85378
|
+
return;
|
|
85379
|
+
}
|
|
85380
|
+
const existing = outputByToolCallId.get(toolCallId);
|
|
85381
|
+
const outputState = existing ?? {
|
|
85382
|
+
messageId: `message-tool-return-stream-${toolCallId}`,
|
|
85383
|
+
stdout: "",
|
|
85384
|
+
stderr: ""
|
|
85385
|
+
};
|
|
85386
|
+
if (isStderr) {
|
|
85387
|
+
outputState.stderr = appendStreamingOutputWithCap(outputState.stderr, chunk);
|
|
85388
|
+
} else {
|
|
85389
|
+
outputState.stdout = appendStreamingOutputWithCap(outputState.stdout, chunk);
|
|
85390
|
+
}
|
|
85391
|
+
outputByToolCallId.set(toolCallId, outputState);
|
|
85392
|
+
const stdout = normalizeStreamingOutputLines(outputState.stdout);
|
|
85393
|
+
const stderr = normalizeStreamingOutputLines(outputState.stderr);
|
|
85394
|
+
const toolReturn = [stdout?.join(`
|
|
85395
|
+
`), stderr?.join(`
|
|
85396
|
+
`)].filter((part) => typeof part === "string" && part.length > 0).join(`
|
|
85397
|
+
`);
|
|
85398
|
+
emitCanonicalMessageDelta(socket, runtime, {
|
|
85399
|
+
type: "message",
|
|
85400
|
+
message_type: "tool_return_message",
|
|
85401
|
+
id: outputState.messageId,
|
|
85402
|
+
date: new Date().toISOString(),
|
|
85403
|
+
run_id: params.runId ?? runtime.activeRunId ?? undefined,
|
|
85404
|
+
status: "success",
|
|
85405
|
+
tool_call_id: toolCallId,
|
|
85406
|
+
tool_return: toolReturn,
|
|
85407
|
+
tool_returns: [
|
|
85408
|
+
{
|
|
85409
|
+
tool_call_id: toolCallId,
|
|
85410
|
+
status: "success",
|
|
85411
|
+
tool_return: toolReturn,
|
|
85412
|
+
...stdout ? { stdout } : {},
|
|
85413
|
+
...stderr ? { stderr } : {}
|
|
85414
|
+
}
|
|
85415
|
+
]
|
|
85416
|
+
}, {
|
|
85417
|
+
agent_id: params.agentId,
|
|
85418
|
+
conversation_id: params.conversationId
|
|
85419
|
+
});
|
|
85420
|
+
};
|
|
85421
|
+
}
|
|
84730
85422
|
function getInterruptApprovalsForEmission(runtime, params) {
|
|
84731
85423
|
if (params.lastExecutionResults && params.lastExecutionResults.length > 0) {
|
|
84732
85424
|
return params.lastExecutionResults;
|
|
@@ -84843,7 +85535,7 @@ function stashRecoveredApprovalInterrupts(runtime, recovered) {
|
|
|
84843
85535
|
clearRecoveredApprovalState(runtime);
|
|
84844
85536
|
return true;
|
|
84845
85537
|
}
|
|
84846
|
-
var INTERRUPT_TOOL_RETURN_MAX_CHARS;
|
|
85538
|
+
var INTERRUPT_TOOL_RETURN_MAX_CHARS, STREAMING_TOOL_OUTPUT_MAX_CHARS;
|
|
84847
85539
|
var init_interrupts = __esm(async () => {
|
|
84848
85540
|
init_approval_result_normalization();
|
|
84849
85541
|
init_constants();
|
|
@@ -84855,6 +85547,7 @@ var init_interrupts = __esm(async () => {
|
|
|
84855
85547
|
init_protocol_outbound()
|
|
84856
85548
|
]);
|
|
84857
85549
|
INTERRUPT_TOOL_RETURN_MAX_CHARS = LIMITS.BASH_OUTPUT_CHARS;
|
|
85550
|
+
STREAMING_TOOL_OUTPUT_MAX_CHARS = LIMITS.BASH_OUTPUT_CHARS;
|
|
84858
85551
|
});
|
|
84859
85552
|
|
|
84860
85553
|
// src/websocket/listener/permissionMode.ts
|
|
@@ -87695,6 +88388,11 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
|
|
|
87695
88388
|
agentId: recovered.agentId,
|
|
87696
88389
|
conversationId: recovered.conversationId
|
|
87697
88390
|
});
|
|
88391
|
+
const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
|
|
88392
|
+
runId: runtime.activeRunId ?? undefined,
|
|
88393
|
+
agentId: recovered.agentId,
|
|
88394
|
+
conversationId: recovered.conversationId
|
|
88395
|
+
});
|
|
87698
88396
|
const recoveryAbortController = new AbortController;
|
|
87699
88397
|
runtime.activeAbortController = recoveryAbortController;
|
|
87700
88398
|
const preparedToolContext = await prepareToolExecutionContextForScope({
|
|
@@ -87709,6 +88407,7 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
|
|
|
87709
88407
|
try {
|
|
87710
88408
|
const approvalResults = await executeApprovalBatch(decisions, undefined, {
|
|
87711
88409
|
abortSignal: recoveryAbortController.signal,
|
|
88410
|
+
onStreamingOutput: emitToolExecutionOutput,
|
|
87712
88411
|
toolContextId: preparedToolContext.preparedToolContext.contextId,
|
|
87713
88412
|
workingDirectory,
|
|
87714
88413
|
parentScope: recovered.agentId && recovered.conversationId ? {
|
|
@@ -87979,9 +88678,15 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
|
|
|
87979
88678
|
agentId: runtime.agentId ?? undefined,
|
|
87980
88679
|
conversationId: recoveryConversationId
|
|
87981
88680
|
});
|
|
88681
|
+
const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
|
|
88682
|
+
runId: runtime.activeRunId ?? undefined,
|
|
88683
|
+
agentId: runtime.agentId ?? undefined,
|
|
88684
|
+
conversationId: recoveryConversationId
|
|
88685
|
+
});
|
|
87982
88686
|
try {
|
|
87983
88687
|
const approvalResults = await executeApprovalBatch(decisions, undefined, {
|
|
87984
88688
|
abortSignal,
|
|
88689
|
+
onStreamingOutput: emitToolExecutionOutput,
|
|
87985
88690
|
toolContextId: preparedToolContext.preparedToolContext.contextId,
|
|
87986
88691
|
workingDirectory: recoveryWorkingDirectory,
|
|
87987
88692
|
parentScope: runtime.agentId && runtime.conversationId ? {
|
|
@@ -88327,7 +89032,46 @@ var init_send = __esm(async () => {
|
|
|
88327
89032
|
});
|
|
88328
89033
|
|
|
88329
89034
|
// src/websocket/listener/turn-approval.ts
|
|
89035
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
88330
89036
|
import WebSocket2 from "ws";
|
|
89037
|
+
function getChannelApprovalSourceScopeKey(source) {
|
|
89038
|
+
return [
|
|
89039
|
+
source.channel,
|
|
89040
|
+
source.accountId ?? "",
|
|
89041
|
+
source.chatId,
|
|
89042
|
+
source.threadId ?? ""
|
|
89043
|
+
].join(":");
|
|
89044
|
+
}
|
|
89045
|
+
function resolveChannelApprovalSource(runtime) {
|
|
89046
|
+
const sources = runtime.activeChannelTurnSources ?? [];
|
|
89047
|
+
if (sources.length === 0) {
|
|
89048
|
+
return null;
|
|
89049
|
+
}
|
|
89050
|
+
const sourcesByScope = new Map;
|
|
89051
|
+
for (const source of sources) {
|
|
89052
|
+
sourcesByScope.set(getChannelApprovalSourceScopeKey(source), source);
|
|
89053
|
+
}
|
|
89054
|
+
if (sourcesByScope.size !== 1) {
|
|
89055
|
+
return null;
|
|
89056
|
+
}
|
|
89057
|
+
return [...sourcesByScope.values()].at(-1) ?? null;
|
|
89058
|
+
}
|
|
89059
|
+
async function maybeReadPlanPreview(toolName, turnPermissionModeState) {
|
|
89060
|
+
if (toolName !== "ExitPlanMode" || !turnPermissionModeState.planFilePath) {
|
|
89061
|
+
return {};
|
|
89062
|
+
}
|
|
89063
|
+
try {
|
|
89064
|
+
const planContent = await readFile11(turnPermissionModeState.planFilePath, "utf8");
|
|
89065
|
+
return {
|
|
89066
|
+
planFilePath: turnPermissionModeState.planFilePath,
|
|
89067
|
+
planContent
|
|
89068
|
+
};
|
|
89069
|
+
} catch {
|
|
89070
|
+
return {
|
|
89071
|
+
planFilePath: turnPermissionModeState.planFilePath
|
|
89072
|
+
};
|
|
89073
|
+
}
|
|
89074
|
+
}
|
|
88331
89075
|
async function handleApprovalStop(params) {
|
|
88332
89076
|
const {
|
|
88333
89077
|
approvals,
|
|
@@ -88465,6 +89209,18 @@ async function handleApprovalStop(params) {
|
|
|
88465
89209
|
agent_id: agentId,
|
|
88466
89210
|
conversation_id: conversationId
|
|
88467
89211
|
};
|
|
89212
|
+
const registry = getChannelRegistry();
|
|
89213
|
+
const channelSource = resolveChannelApprovalSource(runtime);
|
|
89214
|
+
if (registry && channelSource) {
|
|
89215
|
+
await registry.registerPendingControlRequest({
|
|
89216
|
+
requestId,
|
|
89217
|
+
kind: getInteractiveApprovalKind(ac.approval.toolName) ?? "generic_tool_approval",
|
|
89218
|
+
source: channelSource,
|
|
89219
|
+
toolName: ac.approval.toolName,
|
|
89220
|
+
input: ac.parsedArgs,
|
|
89221
|
+
...await maybeReadPlanPreview(ac.approval.toolName, turnPermissionModeState)
|
|
89222
|
+
});
|
|
89223
|
+
}
|
|
88468
89224
|
let responseBody;
|
|
88469
89225
|
try {
|
|
88470
89226
|
responseBody = await requestApprovalOverWS(runtime, socket, requestId, controlRequest);
|
|
@@ -88473,6 +89229,8 @@ async function handleApprovalStop(params) {
|
|
|
88473
89229
|
return interruptTermination();
|
|
88474
89230
|
}
|
|
88475
89231
|
throw error;
|
|
89232
|
+
} finally {
|
|
89233
|
+
registry?.clearPendingControlRequest(requestId);
|
|
88476
89234
|
}
|
|
88477
89235
|
if (shouldInterrupt()) {
|
|
88478
89236
|
return interruptTermination();
|
|
@@ -88550,6 +89308,11 @@ async function handleApprovalStop(params) {
|
|
|
88550
89308
|
agentId,
|
|
88551
89309
|
conversationId
|
|
88552
89310
|
});
|
|
89311
|
+
const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
|
|
89312
|
+
runId: executionRunId,
|
|
89313
|
+
agentId,
|
|
89314
|
+
conversationId
|
|
89315
|
+
});
|
|
88553
89316
|
if (shouldInterrupt()) {
|
|
88554
89317
|
return interruptTermination();
|
|
88555
89318
|
}
|
|
@@ -88568,6 +89331,7 @@ async function handleApprovalStop(params) {
|
|
|
88568
89331
|
const executionResults = await executeApprovalBatch(decisions, undefined, {
|
|
88569
89332
|
toolContextId: turnToolContextId ?? undefined,
|
|
88570
89333
|
abortSignal: abortController.signal,
|
|
89334
|
+
onStreamingOutput: emitToolExecutionOutput,
|
|
88571
89335
|
workingDirectory: turnWorkingDirectory,
|
|
88572
89336
|
parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
|
|
88573
89337
|
onFileWrite
|
|
@@ -88669,6 +89433,7 @@ async function handleApprovalStop(params) {
|
|
|
88669
89433
|
};
|
|
88670
89434
|
}
|
|
88671
89435
|
var init_turn_approval = __esm(async () => {
|
|
89436
|
+
init_registry();
|
|
88672
89437
|
init_diffPreview();
|
|
88673
89438
|
init_interactivePolicy();
|
|
88674
89439
|
init_skill_injection();
|
|
@@ -90512,6 +91277,7 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
|
|
|
90512
91277
|
}
|
|
90513
91278
|
let turnError;
|
|
90514
91279
|
let didThrow = false;
|
|
91280
|
+
runtime.activeChannelTurnSources = channelTurnSources;
|
|
90515
91281
|
try {
|
|
90516
91282
|
await processQueuedTurn(queuedTurn, dequeuedBatch);
|
|
90517
91283
|
} catch (error) {
|
|
@@ -90519,6 +91285,7 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
|
|
|
90519
91285
|
turnError = error instanceof Error ? error.message : String(error);
|
|
90520
91286
|
throw error;
|
|
90521
91287
|
} finally {
|
|
91288
|
+
runtime.activeChannelTurnSources = null;
|
|
90522
91289
|
if (channelTurnSources.length > 0) {
|
|
90523
91290
|
await dispatchChannelTurnLifecycleEvent({
|
|
90524
91291
|
type: "finished",
|
|
@@ -90596,11 +91363,6 @@ function refreshTaskCache(state) {
|
|
|
90596
91363
|
}
|
|
90597
91364
|
}
|
|
90598
91365
|
function shouldFireTask(task2, now) {
|
|
90599
|
-
if (task2.recurring && task2.expires_at) {
|
|
90600
|
-
if (new Date(task2.expires_at).getTime() <= now.getTime()) {
|
|
90601
|
-
return false;
|
|
90602
|
-
}
|
|
90603
|
-
}
|
|
90604
91366
|
if (!task2.recurring && task2.scheduled_for) {
|
|
90605
91367
|
const scheduledMs = new Date(task2.scheduled_for).getTime() + task2.jitter_offset_ms;
|
|
90606
91368
|
return scheduledMs <= now.getTime();
|
|
@@ -90640,16 +91402,6 @@ function fireCronTask(task2, now, socket, opts, processQueuedTurn) {
|
|
|
90640
91402
|
});
|
|
90641
91403
|
}
|
|
90642
91404
|
}
|
|
90643
|
-
function handleExpiredRecurring(task2, now) {
|
|
90644
|
-
if (!task2.recurring || !task2.expires_at)
|
|
90645
|
-
return;
|
|
90646
|
-
if (new Date(task2.expires_at).getTime() <= now.getTime()) {
|
|
90647
|
-
updateTask(task2.id, (t) => {
|
|
90648
|
-
t.status = "cancelled";
|
|
90649
|
-
t.cancel_reason = "expired";
|
|
90650
|
-
});
|
|
90651
|
-
}
|
|
90652
|
-
}
|
|
90653
91405
|
function handleMissedOneShot(task2, now) {
|
|
90654
91406
|
if (task2.recurring || !task2.scheduled_for)
|
|
90655
91407
|
return false;
|
|
@@ -90678,9 +91430,6 @@ function tick2(state, socket, opts, processQueuedTurn) {
|
|
|
90678
91430
|
}
|
|
90679
91431
|
refreshTaskCache(state);
|
|
90680
91432
|
for (const task2 of state.cachedTasks) {
|
|
90681
|
-
if (task2.status !== "active")
|
|
90682
|
-
continue;
|
|
90683
|
-
handleExpiredRecurring(task2, now);
|
|
90684
91433
|
if (task2.status !== "active")
|
|
90685
91434
|
continue;
|
|
90686
91435
|
if (handleMissedOneShot(task2, now))
|
|
@@ -91144,6 +91893,12 @@ function isListInDirectoryCommand(value) {
|
|
|
91144
91893
|
const c = value;
|
|
91145
91894
|
return c.type === "list_in_directory" && typeof c.path === "string";
|
|
91146
91895
|
}
|
|
91896
|
+
function isGetTreeCommand(value) {
|
|
91897
|
+
if (!value || typeof value !== "object")
|
|
91898
|
+
return false;
|
|
91899
|
+
const c = value;
|
|
91900
|
+
return c.type === "get_tree" && typeof c.path === "string" && typeof c.depth === "number" && typeof c.request_id === "string";
|
|
91901
|
+
}
|
|
91147
91902
|
function isReadFileCommand(value) {
|
|
91148
91903
|
if (!value || typeof value !== "object")
|
|
91149
91904
|
return false;
|
|
@@ -91470,7 +92225,7 @@ function parseServerMessage(data) {
|
|
|
91470
92225
|
try {
|
|
91471
92226
|
const raw = typeof data === "string" ? data : data.toString();
|
|
91472
92227
|
const parsed = JSON.parse(raw);
|
|
91473
|
-
if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed) || isSearchFilesCommand(parsed) || isListInDirectoryCommand(parsed) || isReadFileCommand(parsed) || isWriteFileCommand(parsed) || isWatchFileCommand(parsed) || isUnwatchFileCommand(parsed) || isEditFileCommand(parsed) || isFileOpsCommand(parsed) || isListMemoryCommand(parsed) || isMemoryHistoryCommand(parsed) || isMemoryFileAtRefCommand(parsed) || isEnableMemfsCommand(parsed) || isListModelsCommand(parsed) || isUpdateModelCommand(parsed) || isCronListCommand(parsed) || isCronAddCommand(parsed) || isCronGetCommand(parsed) || isCronDeleteCommand(parsed) || isCronDeleteAllCommand(parsed) || isSkillEnableCommand(parsed) || isSkillDisableCommand(parsed) || isCreateAgentCommand(parsed) || isGetReflectionSettingsCommand(parsed) || isSetReflectionSettingsCommand(parsed) || isChannelsListCommand(parsed) || isChannelAccountsListCommand(parsed) || isChannelAccountCreateCommand(parsed) || isChannelAccountUpdateCommand(parsed) || isChannelAccountBindCommand(parsed) || isChannelAccountUnbindCommand(parsed) || isChannelAccountDeleteCommand(parsed) || isChannelAccountStartCommand(parsed) || isChannelAccountStopCommand(parsed) || isChannelGetConfigCommand(parsed) || isChannelSetConfigCommand(parsed) || isChannelStartCommand(parsed) || isChannelStopCommand(parsed) || isChannelPairingsListCommand(parsed) || isChannelPairingBindCommand(parsed) || isChannelRoutesListCommand(parsed) || isChannelTargetsListCommand(parsed) || isChannelTargetBindCommand(parsed) || isChannelRouteUpdateCommand(parsed) || isChannelRouteRemoveCommand(parsed) || isExecuteCommandCommand(parsed) || isSearchBranchesCommand(parsed) || isCheckoutBranchCommand(parsed)) {
|
|
92228
|
+
if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed) || isSearchFilesCommand(parsed) || isListInDirectoryCommand(parsed) || isGetTreeCommand(parsed) || isReadFileCommand(parsed) || isWriteFileCommand(parsed) || isWatchFileCommand(parsed) || isUnwatchFileCommand(parsed) || isEditFileCommand(parsed) || isFileOpsCommand(parsed) || isListMemoryCommand(parsed) || isMemoryHistoryCommand(parsed) || isMemoryFileAtRefCommand(parsed) || isEnableMemfsCommand(parsed) || isListModelsCommand(parsed) || isUpdateModelCommand(parsed) || isCronListCommand(parsed) || isCronAddCommand(parsed) || isCronGetCommand(parsed) || isCronDeleteCommand(parsed) || isCronDeleteAllCommand(parsed) || isSkillEnableCommand(parsed) || isSkillDisableCommand(parsed) || isCreateAgentCommand(parsed) || isGetReflectionSettingsCommand(parsed) || isSetReflectionSettingsCommand(parsed) || isChannelsListCommand(parsed) || isChannelAccountsListCommand(parsed) || isChannelAccountCreateCommand(parsed) || isChannelAccountUpdateCommand(parsed) || isChannelAccountBindCommand(parsed) || isChannelAccountUnbindCommand(parsed) || isChannelAccountDeleteCommand(parsed) || isChannelAccountStartCommand(parsed) || isChannelAccountStopCommand(parsed) || isChannelGetConfigCommand(parsed) || isChannelSetConfigCommand(parsed) || isChannelStartCommand(parsed) || isChannelStopCommand(parsed) || isChannelPairingsListCommand(parsed) || isChannelPairingBindCommand(parsed) || isChannelRoutesListCommand(parsed) || isChannelTargetsListCommand(parsed) || isChannelTargetBindCommand(parsed) || isChannelRouteUpdateCommand(parsed) || isChannelRouteRemoveCommand(parsed) || isExecuteCommandCommand(parsed) || isSearchBranchesCommand(parsed) || isCheckoutBranchCommand(parsed)) {
|
|
91474
92229
|
return parsed;
|
|
91475
92230
|
}
|
|
91476
92231
|
const invalidInput = getInvalidInputReason(parsed);
|
|
@@ -91843,18 +92598,20 @@ function formatToolsetStatusMessageForModelUpdate(params) {
|
|
|
91843
92598
|
}
|
|
91844
92599
|
return "Manual toolset override remains active: " + formatToolsetName(toolsetPreference) + ".";
|
|
91845
92600
|
}
|
|
91846
|
-
function formatEffortSuffix(updateArgs) {
|
|
92601
|
+
function formatEffortSuffix(modelLabel, updateArgs) {
|
|
91847
92602
|
if (!updateArgs)
|
|
91848
92603
|
return "";
|
|
91849
92604
|
const effort = updateArgs.reasoning_effort;
|
|
91850
92605
|
if (typeof effort !== "string" || effort.length === 0)
|
|
91851
92606
|
return "";
|
|
92607
|
+
const xhighLabel = modelLabel.includes("Opus 4.7") ? "Extra-High" : "Max";
|
|
91852
92608
|
const labels = {
|
|
91853
92609
|
none: "No Reasoning",
|
|
91854
92610
|
low: "Low",
|
|
91855
92611
|
medium: "Medium",
|
|
91856
92612
|
high: "High",
|
|
91857
|
-
xhigh:
|
|
92613
|
+
xhigh: xhighLabel,
|
|
92614
|
+
max: "Max"
|
|
91858
92615
|
};
|
|
91859
92616
|
return ` (${labels[effort] ?? effort})`;
|
|
91860
92617
|
}
|
|
@@ -91867,7 +92624,7 @@ function buildModelUpdateStatusMessage(params) {
|
|
|
91867
92624
|
toolsetPreference,
|
|
91868
92625
|
updateArgs
|
|
91869
92626
|
} = params;
|
|
91870
|
-
let message = `Model updated to ${modelLabel}${formatEffortSuffix(updateArgs)}.`;
|
|
92627
|
+
let message = `Model updated to ${modelLabel}${formatEffortSuffix(modelLabel, updateArgs)}.`;
|
|
91871
92628
|
if (toolsetError) {
|
|
91872
92629
|
message += ` Warning: toolset switch failed (${toolsetError}).`;
|
|
91873
92630
|
return { message, level: "warning" };
|
|
@@ -92432,6 +93189,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
92432
93189
|
has_bot_token: snapshot.hasBotToken,
|
|
92433
93190
|
has_app_token: snapshot.hasAppToken,
|
|
92434
93191
|
agent_id: snapshot.agentId,
|
|
93192
|
+
default_permission_mode: snapshot.defaultPermissionMode,
|
|
92435
93193
|
created_at: snapshot.createdAt,
|
|
92436
93194
|
updated_at: snapshot.updatedAt
|
|
92437
93195
|
};
|
|
@@ -92525,6 +93283,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
92525
93283
|
appToken: "app_token" in parsed.account ? parsed.account.app_token : undefined,
|
|
92526
93284
|
mode: "mode" in parsed.account ? parsed.account.mode : undefined,
|
|
92527
93285
|
agentId: "agent_id" in parsed.account ? parsed.account.agent_id : undefined,
|
|
93286
|
+
defaultPermissionMode: "default_permission_mode" in parsed.account ? parsed.account.default_permission_mode : undefined,
|
|
92528
93287
|
dmPolicy: parsed.account.dm_policy,
|
|
92529
93288
|
allowedUsers: parsed.account.allowed_users
|
|
92530
93289
|
}, {
|
|
@@ -92565,6 +93324,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
92565
93324
|
appToken: "app_token" in parsed.patch ? parsed.patch.app_token : undefined,
|
|
92566
93325
|
mode: "mode" in parsed.patch ? parsed.patch.mode : undefined,
|
|
92567
93326
|
agentId: "agent_id" in parsed.patch ? parsed.patch.agent_id : undefined,
|
|
93327
|
+
defaultPermissionMode: "default_permission_mode" in parsed.patch ? parsed.patch.default_permission_mode : undefined,
|
|
92568
93328
|
dmPolicy: parsed.patch.dm_policy,
|
|
92569
93329
|
allowedUsers: parsed.patch.allowed_users
|
|
92570
93330
|
});
|
|
@@ -93306,17 +94066,35 @@ function wireChannelIngress(listener, socket, opts, processQueuedTurn) {
|
|
|
93306
94066
|
}
|
|
93307
94067
|
scheduleQueuePump(conversationRuntime, socket, opts, processQueuedTurn);
|
|
93308
94068
|
});
|
|
94069
|
+
registry.setApprovalResponseHandler(async ({ runtime, response }) => handleApprovalResponseInput(listener, {
|
|
94070
|
+
runtime,
|
|
94071
|
+
response,
|
|
94072
|
+
socket,
|
|
94073
|
+
opts,
|
|
94074
|
+
processQueuedTurn
|
|
94075
|
+
}));
|
|
93309
94076
|
registry.setEventHandler((event) => {
|
|
93310
|
-
|
|
93311
|
-
emitChannelPairingsUpdated(socket, event.channelId);
|
|
93312
|
-
emitChannelsUpdated(socket, event.channelId);
|
|
93313
|
-
return;
|
|
93314
|
-
}
|
|
93315
|
-
emitChannelTargetsUpdated(socket, event.channelId);
|
|
93316
|
-
emitChannelsUpdated(socket, event.channelId);
|
|
94077
|
+
handleChannelRegistryEvent(event, socket, listener);
|
|
93317
94078
|
});
|
|
93318
94079
|
registry.setReady();
|
|
93319
94080
|
}
|
|
94081
|
+
function handleChannelRegistryEvent(event, socket, runtime) {
|
|
94082
|
+
if (event.type === "pairings_updated") {
|
|
94083
|
+
emitChannelPairingsUpdated(socket, event.channelId);
|
|
94084
|
+
emitChannelsUpdated(socket, event.channelId);
|
|
94085
|
+
return;
|
|
94086
|
+
}
|
|
94087
|
+
if (event.type === "targets_updated") {
|
|
94088
|
+
emitChannelTargetsUpdated(socket, event.channelId);
|
|
94089
|
+
emitChannelsUpdated(socket, event.channelId);
|
|
94090
|
+
return;
|
|
94091
|
+
}
|
|
94092
|
+
const permissionModeState = getOrCreateConversationPermissionModeStateRef(runtime, event.agentId, event.conversationId);
|
|
94093
|
+
permissionModeState.mode = event.defaultPermissionMode;
|
|
94094
|
+
permissionModeState.planFilePath = null;
|
|
94095
|
+
permissionModeState.modeBeforePlan = null;
|
|
94096
|
+
persistPermissionModeMapForRuntime(runtime);
|
|
94097
|
+
}
|
|
93320
94098
|
function stampInboundUserMessageOtids(incoming) {
|
|
93321
94099
|
let didChange = false;
|
|
93322
94100
|
const messages = incoming.messages.map((payload) => {
|
|
@@ -93725,6 +94503,51 @@ async function startListenerClient(opts) {
|
|
|
93725
94503
|
telemetry.init();
|
|
93726
94504
|
await connectWithRetry(runtime, opts);
|
|
93727
94505
|
}
|
|
94506
|
+
async function listDirectoryHybrid(absDir, indexRoot3, includeFiles) {
|
|
94507
|
+
let indexedNames;
|
|
94508
|
+
const indexedFolders = [];
|
|
94509
|
+
const indexedFiles = [];
|
|
94510
|
+
if (indexRoot3 !== undefined) {
|
|
94511
|
+
const relPath = path23.relative(indexRoot3, absDir);
|
|
94512
|
+
if (!relPath.startsWith("..")) {
|
|
94513
|
+
const indexed = searchFileIndex({
|
|
94514
|
+
searchDir: relPath || ".",
|
|
94515
|
+
pattern: "",
|
|
94516
|
+
deep: false,
|
|
94517
|
+
maxResults: 1e4
|
|
94518
|
+
});
|
|
94519
|
+
indexedNames = new Set;
|
|
94520
|
+
for (const entry of indexed) {
|
|
94521
|
+
const name = entry.path.split(path23.sep).pop() ?? entry.path;
|
|
94522
|
+
indexedNames.add(name);
|
|
94523
|
+
if (entry.type === "dir") {
|
|
94524
|
+
indexedFolders.push(name);
|
|
94525
|
+
} else {
|
|
94526
|
+
indexedFiles.push(name);
|
|
94527
|
+
}
|
|
94528
|
+
}
|
|
94529
|
+
}
|
|
94530
|
+
}
|
|
94531
|
+
const { readdir: readdir7 } = await import("node:fs/promises");
|
|
94532
|
+
const entries = await readdir7(absDir, { withFileTypes: true });
|
|
94533
|
+
const extraFolders = [];
|
|
94534
|
+
const extraFiles = [];
|
|
94535
|
+
for (const e of entries) {
|
|
94536
|
+
if (DIR_LISTING_IGNORED_NAMES.has(e.name))
|
|
94537
|
+
continue;
|
|
94538
|
+
if (indexedNames?.has(e.name))
|
|
94539
|
+
continue;
|
|
94540
|
+
if (e.isDirectory()) {
|
|
94541
|
+
extraFolders.push(e.name);
|
|
94542
|
+
} else if (includeFiles) {
|
|
94543
|
+
extraFiles.push(e.name);
|
|
94544
|
+
}
|
|
94545
|
+
}
|
|
94546
|
+
return {
|
|
94547
|
+
folders: [...indexedFolders, ...extraFolders].sort((a, b) => a.localeCompare(b)),
|
|
94548
|
+
files: includeFiles ? [...indexedFiles, ...extraFiles].sort((a, b) => a.localeCompare(b)) : []
|
|
94549
|
+
};
|
|
94550
|
+
}
|
|
93728
94551
|
async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now()) {
|
|
93729
94552
|
if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
|
|
93730
94553
|
return;
|
|
@@ -94045,33 +94868,21 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
94045
94868
|
console.log(`[Listen] Received list_in_directory command: path=${parsed.path}`);
|
|
94046
94869
|
runDetachedListenerTask("list_in_directory", async () => {
|
|
94047
94870
|
try {
|
|
94048
|
-
|
|
94871
|
+
let indexRoot3;
|
|
94872
|
+
try {
|
|
94873
|
+
await ensureFileIndex2();
|
|
94874
|
+
indexRoot3 = getIndexRoot();
|
|
94875
|
+
} catch {}
|
|
94049
94876
|
console.log(`[Listen] Reading directory: ${parsed.path}`);
|
|
94050
|
-
const
|
|
94051
|
-
console.log(`[Listen] Directory read success, ${entries.length} entries`);
|
|
94052
|
-
const IGNORED_NAMES = new Set([
|
|
94053
|
-
".DS_Store",
|
|
94054
|
-
".git",
|
|
94055
|
-
".gitignore",
|
|
94056
|
-
"Thumbs.db"
|
|
94057
|
-
]);
|
|
94058
|
-
const sortedEntries = entries.filter((e) => !IGNORED_NAMES.has(e.name)).sort((a, b) => a.name.localeCompare(b.name));
|
|
94059
|
-
const allFolders = [];
|
|
94060
|
-
const allFiles = [];
|
|
94061
|
-
for (const e of sortedEntries) {
|
|
94062
|
-
if (e.isDirectory()) {
|
|
94063
|
-
allFolders.push(e.name);
|
|
94064
|
-
} else if (parsed.include_files) {
|
|
94065
|
-
allFiles.push(e.name);
|
|
94066
|
-
}
|
|
94067
|
-
}
|
|
94877
|
+
const { folders: allFolders, files: allFiles } = await listDirectoryHybrid(parsed.path, indexRoot3, !!parsed.include_files);
|
|
94068
94878
|
const total = allFolders.length + allFiles.length;
|
|
94069
94879
|
const offset = parsed.offset ?? 0;
|
|
94070
94880
|
const limit2 = parsed.limit ?? total;
|
|
94071
94881
|
const combined = [...allFolders, ...allFiles];
|
|
94072
94882
|
const page = combined.slice(offset, offset + limit2);
|
|
94073
|
-
const
|
|
94074
|
-
const
|
|
94883
|
+
const folderSet = new Set(allFolders);
|
|
94884
|
+
const folders = page.filter((name) => folderSet.has(name));
|
|
94885
|
+
const files = page.filter((name) => !folderSet.has(name));
|
|
94075
94886
|
const response = {
|
|
94076
94887
|
type: "list_in_directory_response",
|
|
94077
94888
|
path: parsed.path,
|
|
@@ -94102,12 +94913,77 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
94102
94913
|
});
|
|
94103
94914
|
return;
|
|
94104
94915
|
}
|
|
94916
|
+
if (isGetTreeCommand(parsed)) {
|
|
94917
|
+
console.log(`[Listen] Received get_tree command: path=${parsed.path}, depth=${parsed.depth}`);
|
|
94918
|
+
runDetachedListenerTask("get_tree", async () => {
|
|
94919
|
+
try {
|
|
94920
|
+
const results = [];
|
|
94921
|
+
let hasMoreDepth = false;
|
|
94922
|
+
let indexRoot3;
|
|
94923
|
+
try {
|
|
94924
|
+
await ensureFileIndex2();
|
|
94925
|
+
indexRoot3 = getIndexRoot();
|
|
94926
|
+
} catch {}
|
|
94927
|
+
const queue = [[parsed.path, "", 0]];
|
|
94928
|
+
let qi = 0;
|
|
94929
|
+
while (qi < queue.length) {
|
|
94930
|
+
const item = queue[qi++];
|
|
94931
|
+
if (!item)
|
|
94932
|
+
break;
|
|
94933
|
+
const [absDir, relDir, depth] = item;
|
|
94934
|
+
if (depth >= parsed.depth) {
|
|
94935
|
+
if (depth === parsed.depth && relDir !== "") {
|
|
94936
|
+
hasMoreDepth = true;
|
|
94937
|
+
}
|
|
94938
|
+
continue;
|
|
94939
|
+
}
|
|
94940
|
+
let listing;
|
|
94941
|
+
try {
|
|
94942
|
+
listing = await listDirectoryHybrid(absDir, indexRoot3, true);
|
|
94943
|
+
} catch {
|
|
94944
|
+
continue;
|
|
94945
|
+
}
|
|
94946
|
+
for (const name of listing.folders) {
|
|
94947
|
+
const entryRel = relDir === "" ? name : `${relDir}/${name}`;
|
|
94948
|
+
results.push({ path: entryRel, type: "dir" });
|
|
94949
|
+
queue.push([path23.join(absDir, name), entryRel, depth + 1]);
|
|
94950
|
+
}
|
|
94951
|
+
for (const name of listing.files) {
|
|
94952
|
+
const entryRel = relDir === "" ? name : `${relDir}/${name}`;
|
|
94953
|
+
results.push({ path: entryRel, type: "file" });
|
|
94954
|
+
}
|
|
94955
|
+
}
|
|
94956
|
+
console.log(`[Listen] Sending get_tree_response: ${results.length} entries, has_more_depth=${hasMoreDepth}`);
|
|
94957
|
+
safeSocketSend(socket, {
|
|
94958
|
+
type: "get_tree_response",
|
|
94959
|
+
path: parsed.path,
|
|
94960
|
+
request_id: parsed.request_id,
|
|
94961
|
+
entries: results,
|
|
94962
|
+
has_more_depth: hasMoreDepth,
|
|
94963
|
+
success: true
|
|
94964
|
+
}, "listener_get_tree_send_failed", "listener_get_tree");
|
|
94965
|
+
} catch (err) {
|
|
94966
|
+
trackListenerError("listener_get_tree_failed", err, "listener_file_browser");
|
|
94967
|
+
console.error(`[Listen] get_tree error: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
94968
|
+
safeSocketSend(socket, {
|
|
94969
|
+
type: "get_tree_response",
|
|
94970
|
+
path: parsed.path,
|
|
94971
|
+
request_id: parsed.request_id,
|
|
94972
|
+
entries: [],
|
|
94973
|
+
has_more_depth: false,
|
|
94974
|
+
success: false,
|
|
94975
|
+
error: err instanceof Error ? err.message : "Failed to get tree"
|
|
94976
|
+
}, "listener_get_tree_send_failed", "listener_get_tree");
|
|
94977
|
+
}
|
|
94978
|
+
});
|
|
94979
|
+
return;
|
|
94980
|
+
}
|
|
94105
94981
|
if (isReadFileCommand(parsed)) {
|
|
94106
94982
|
console.log(`[Listen] Received read_file command: path=${parsed.path}, request_id=${parsed.request_id}`);
|
|
94107
94983
|
runDetachedListenerTask("read_file", async () => {
|
|
94108
94984
|
try {
|
|
94109
|
-
const { readFile:
|
|
94110
|
-
const content = await
|
|
94985
|
+
const { readFile: readFile12 } = await import("node:fs/promises");
|
|
94986
|
+
const content = await readFile12(parsed.path, "utf-8");
|
|
94111
94987
|
console.log(`[Listen] read_file success: ${parsed.path} (${content.length} bytes)`);
|
|
94112
94988
|
safeSocketSend(socket, {
|
|
94113
94989
|
type: "read_file_response",
|
|
@@ -94137,10 +95013,10 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
94137
95013
|
try {
|
|
94138
95014
|
const { edit: edit2 } = await Promise.resolve().then(() => (init_Edit2(), exports_Edit));
|
|
94139
95015
|
const { write: write2 } = await Promise.resolve().then(() => (init_Write2(), exports_Write));
|
|
94140
|
-
const { readFile:
|
|
95016
|
+
const { readFile: readFile12 } = await import("node:fs/promises");
|
|
94141
95017
|
let currentContent = null;
|
|
94142
95018
|
try {
|
|
94143
|
-
currentContent = await
|
|
95019
|
+
currentContent = await readFile12(parsed.path, "utf-8");
|
|
94144
95020
|
} catch (readErr) {
|
|
94145
95021
|
const e = readErr;
|
|
94146
95022
|
if (e.code !== "ENOENT")
|
|
@@ -94162,6 +95038,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
94162
95038
|
}
|
|
94163
95039
|
}
|
|
94164
95040
|
console.log(`[Listen] write_file success: ${parsed.path} (${parsed.content.length} bytes)`);
|
|
95041
|
+
refreshFileIndex();
|
|
94165
95042
|
safeSocketSend(socket, {
|
|
94166
95043
|
type: "write_file_response",
|
|
94167
95044
|
request_id: parsed.request_id,
|
|
@@ -94247,7 +95124,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
94247
95124
|
console.log(`[Listen] Received edit_file command: file_path=${parsed.file_path}, request_id=${parsed.request_id}`);
|
|
94248
95125
|
runDetachedListenerTask("edit_file", async () => {
|
|
94249
95126
|
try {
|
|
94250
|
-
const { readFile:
|
|
95127
|
+
const { readFile: readFile12 } = await import("node:fs/promises");
|
|
94251
95128
|
const { edit: edit2 } = await Promise.resolve().then(() => (init_Edit2(), exports_Edit));
|
|
94252
95129
|
console.log(`[Listen] Executing edit: old_string="${parsed.old_string.slice(0, 50)}${parsed.old_string.length > 50 ? "..." : ""}"`);
|
|
94253
95130
|
const result = await edit2({
|
|
@@ -94258,9 +95135,12 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
94258
95135
|
expected_replacements: parsed.expected_replacements
|
|
94259
95136
|
});
|
|
94260
95137
|
console.log(`[Listen] edit_file success: ${result.replacements} replacement(s) at line ${result.startLine}`);
|
|
95138
|
+
if (result.replacements > 0) {
|
|
95139
|
+
refreshFileIndex();
|
|
95140
|
+
}
|
|
94261
95141
|
if (result.replacements > 0) {
|
|
94262
95142
|
try {
|
|
94263
|
-
const contentAfter = await
|
|
95143
|
+
const contentAfter = await readFile12(parsed.file_path, "utf-8");
|
|
94264
95144
|
safeSocketSend(socket, {
|
|
94265
95145
|
type: "file_ops",
|
|
94266
95146
|
path: parsed.file_path,
|
|
@@ -94892,7 +95772,7 @@ function createLegacyTestRuntime() {
|
|
|
94892
95772
|
}
|
|
94893
95773
|
return bridge;
|
|
94894
95774
|
}
|
|
94895
|
-
var channelsServiceLoaderOverride = null, WIKI_LINK_REGEX, __listenClientTestUtils;
|
|
95775
|
+
var channelsServiceLoaderOverride = null, WIKI_LINK_REGEX, DIR_LISTING_IGNORED_NAMES, __listenClientTestUtils;
|
|
94896
95776
|
var init_client4 = __esm(async () => {
|
|
94897
95777
|
init_available_models();
|
|
94898
95778
|
init_client2();
|
|
@@ -94939,6 +95819,7 @@ var init_client4 = __esm(async () => {
|
|
|
94939
95819
|
init_protocol_outbound()
|
|
94940
95820
|
]);
|
|
94941
95821
|
WIKI_LINK_REGEX = /\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g;
|
|
95822
|
+
DIR_LISTING_IGNORED_NAMES = new Set([".DS_Store", ".git", "Thumbs.db"]);
|
|
94942
95823
|
__listenClientTestUtils = {
|
|
94943
95824
|
setChannelsServiceLoaderForTests: (loader) => {
|
|
94944
95825
|
channelsServiceLoaderOverride = loader;
|
|
@@ -94994,6 +95875,7 @@ var init_client4 = __esm(async () => {
|
|
|
94994
95875
|
handleListMemoryCommand,
|
|
94995
95876
|
isDetachedChannelsCommand,
|
|
94996
95877
|
handleChannelsProtocolCommand,
|
|
95878
|
+
handleChannelRegistryEvent,
|
|
94997
95879
|
handleSkillCommand,
|
|
94998
95880
|
handleCreateAgentCommand,
|
|
94999
95881
|
handleReflectionSettingsCommand,
|
|
@@ -95173,7 +96055,7 @@ __export(exports_skills2, {
|
|
|
95173
96055
|
GLOBAL_SKILLS_DIR: () => GLOBAL_SKILLS_DIR2
|
|
95174
96056
|
});
|
|
95175
96057
|
import { existsSync as existsSync28 } from "node:fs";
|
|
95176
|
-
import { readdir as readdir8, readFile as
|
|
96058
|
+
import { readdir as readdir8, readFile as readFile12, realpath as realpath4, stat as stat7 } from "node:fs/promises";
|
|
95177
96059
|
import { dirname as dirname13, join as join35 } from "node:path";
|
|
95178
96060
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
95179
96061
|
function getBundledSkillsPath2() {
|
|
@@ -95304,7 +96186,7 @@ async function findSkillFiles2(currentPath, rootPath, skills, errors, source, vi
|
|
|
95304
96186
|
}
|
|
95305
96187
|
}
|
|
95306
96188
|
async function parseSkillFile2(filePath, rootPath, source) {
|
|
95307
|
-
const content = await
|
|
96189
|
+
const content = await readFile12(filePath, "utf-8");
|
|
95308
96190
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
95309
96191
|
const normalizedRoot = rootPath.endsWith("/") ? rootPath.slice(0, -1) : rootPath;
|
|
95310
96192
|
const relativePath = filePath.slice(normalizedRoot.length + 1);
|
|
@@ -95363,7 +96245,7 @@ __export(exports_fs, {
|
|
|
95363
96245
|
writeJsonFile: () => writeJsonFile,
|
|
95364
96246
|
writeFile: () => writeFile10,
|
|
95365
96247
|
readJsonFile: () => readJsonFile,
|
|
95366
|
-
readFile: () =>
|
|
96248
|
+
readFile: () => readFile13,
|
|
95367
96249
|
mkdir: () => mkdir9,
|
|
95368
96250
|
exists: () => exists2
|
|
95369
96251
|
});
|
|
@@ -95374,7 +96256,7 @@ import {
|
|
|
95374
96256
|
mkdirSync as mkdirSync21
|
|
95375
96257
|
} from "node:fs";
|
|
95376
96258
|
import { dirname as dirname14 } from "node:path";
|
|
95377
|
-
async function
|
|
96259
|
+
async function readFile13(path24) {
|
|
95378
96260
|
return fsReadFileSync2(path24, { encoding: "utf-8" });
|
|
95379
96261
|
}
|
|
95380
96262
|
async function writeFile10(path24, content) {
|
|
@@ -95391,7 +96273,7 @@ async function mkdir9(path24, options) {
|
|
|
95391
96273
|
mkdirSync21(path24, options);
|
|
95392
96274
|
}
|
|
95393
96275
|
async function readJsonFile(path24) {
|
|
95394
|
-
const text = await
|
|
96276
|
+
const text = await readFile13(path24);
|
|
95395
96277
|
return JSON.parse(text);
|
|
95396
96278
|
}
|
|
95397
96279
|
async function writeJsonFile(path24, data, options) {
|
|
@@ -97230,7 +98112,7 @@ __export(exports_import, {
|
|
|
97230
98112
|
extractSkillsFromAf: () => extractSkillsFromAf
|
|
97231
98113
|
});
|
|
97232
98114
|
import { createReadStream } from "node:fs";
|
|
97233
|
-
import { chmod, mkdir as mkdir10, readFile as
|
|
98115
|
+
import { chmod, mkdir as mkdir10, readFile as readFile14, writeFile as writeFile11 } from "node:fs/promises";
|
|
97234
98116
|
import { dirname as dirname15, resolve as resolve28 } from "node:path";
|
|
97235
98117
|
async function importAgentFromFile(options) {
|
|
97236
98118
|
const client = await getClient();
|
|
@@ -97263,7 +98145,7 @@ async function importAgentFromFile(options) {
|
|
|
97263
98145
|
}
|
|
97264
98146
|
async function extractSkillsFromAf(afPath, destDir) {
|
|
97265
98147
|
const extracted = [];
|
|
97266
|
-
const content = await
|
|
98148
|
+
const content = await readFile14(afPath, "utf-8");
|
|
97267
98149
|
const afData = JSON.parse(content);
|
|
97268
98150
|
if (!afData.skills || !Array.isArray(afData.skills)) {
|
|
97269
98151
|
return [];
|
|
@@ -100147,10 +101029,10 @@ var init_headless = __esm(async () => {
|
|
|
100147
101029
|
init_toolset()
|
|
100148
101030
|
]);
|
|
100149
101031
|
PROVIDER_FALLBACK_MAP = {
|
|
100150
|
-
opus: "bedrock-opus-4.6",
|
|
100151
101032
|
"opus-4.6-no-reasoning": "bedrock-opus-4.6",
|
|
100152
101033
|
"opus-4.6-low": "bedrock-opus-4.6",
|
|
100153
101034
|
"opus-4.6-medium": "bedrock-opus-4.6",
|
|
101035
|
+
"opus-4.6-high": "bedrock-opus-4.6",
|
|
100154
101036
|
"opus-4.6-xhigh": "bedrock-opus-4.6",
|
|
100155
101037
|
sonnet: "bedrock-sonnet-4.6",
|
|
100156
101038
|
"sonnet-1m": "bedrock-sonnet-4.6",
|
|
@@ -124160,7 +125042,7 @@ __export(exports_custom, {
|
|
|
124160
125042
|
COMMANDS_DIR: () => COMMANDS_DIR
|
|
124161
125043
|
});
|
|
124162
125044
|
import { existsSync as existsSync36 } from "node:fs";
|
|
124163
|
-
import { readdir as readdir10, readFile as
|
|
125045
|
+
import { readdir as readdir10, readFile as readFile15 } from "node:fs/promises";
|
|
124164
125046
|
import { basename as basename11, dirname as dirname17, join as join44 } from "node:path";
|
|
124165
125047
|
async function getCustomCommands() {
|
|
124166
125048
|
if (cachedCommands !== null) {
|
|
@@ -124219,7 +125101,7 @@ async function findCommandFiles(currentPath, rootPath, commands2, source2) {
|
|
|
124219
125101
|
} catch (_error) {}
|
|
124220
125102
|
}
|
|
124221
125103
|
async function parseCommandFile(filePath, rootPath, source2) {
|
|
124222
|
-
const content = await
|
|
125104
|
+
const content = await readFile15(filePath, "utf-8");
|
|
124223
125105
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
124224
125106
|
const id = basename11(filePath, ".md");
|
|
124225
125107
|
const relativePath = dirname17(filePath).slice(rootPath.length);
|
|
@@ -127008,6 +127890,8 @@ function formatReasoningLabel(effort) {
|
|
|
127008
127890
|
if (effort === "none")
|
|
127009
127891
|
return null;
|
|
127010
127892
|
if (effort === "xhigh")
|
|
127893
|
+
return "xhigh";
|
|
127894
|
+
if (effort === "max")
|
|
127011
127895
|
return "max";
|
|
127012
127896
|
if (effort === "minimal")
|
|
127013
127897
|
return "minimal";
|
|
@@ -128046,6 +128930,8 @@ function getReasoningEffortTag(effort) {
|
|
|
128046
128930
|
if (effort === "none")
|
|
128047
128931
|
return null;
|
|
128048
128932
|
if (effort === "xhigh")
|
|
128933
|
+
return "xhigh";
|
|
128934
|
+
if (effort === "max")
|
|
128049
128935
|
return "max";
|
|
128050
128936
|
if (effort === "minimal")
|
|
128051
128937
|
return "minimal";
|
|
@@ -135673,10 +136559,12 @@ var init_MessageSearch = __esm(async () => {
|
|
|
135673
136559
|
});
|
|
135674
136560
|
|
|
135675
136561
|
// src/cli/components/ModelReasoningSelector.tsx
|
|
135676
|
-
function formatEffortLabel(effort) {
|
|
136562
|
+
function formatEffortLabel(effort, hasDistinctMaxTier) {
|
|
135677
136563
|
if (effort === "none")
|
|
135678
136564
|
return "Off";
|
|
135679
136565
|
if (effort === "xhigh")
|
|
136566
|
+
return hasDistinctMaxTier ? "Extra-High" : "Max";
|
|
136567
|
+
if (effort === "max")
|
|
135680
136568
|
return "Max";
|
|
135681
136569
|
if (effort === "minimal")
|
|
135682
136570
|
return "Minimal";
|
|
@@ -135703,6 +136591,7 @@ function ModelReasoningSelector({
|
|
|
135703
136591
|
}, [options, initialModelId]);
|
|
135704
136592
|
const selectedOption = options[selectedIndex] ?? options[0];
|
|
135705
136593
|
const effortOptions = import_react82.useMemo(() => options.filter((option) => option.effort !== "none"), [options]);
|
|
136594
|
+
const hasDistinctMaxTier = import_react82.useMemo(() => options.some((option) => option.effort === "max"), [options]);
|
|
135706
136595
|
const totalBars = Math.max(effortOptions.length, 1);
|
|
135707
136596
|
const selectedBars = import_react82.useMemo(() => {
|
|
135708
136597
|
if (!selectedOption)
|
|
@@ -135741,7 +136630,7 @@ function ModelReasoningSelector({
|
|
|
135741
136630
|
setSelectedIndex((prev) => (prev + 1) % options.length);
|
|
135742
136631
|
}
|
|
135743
136632
|
});
|
|
135744
|
-
const effortLabel = selectedOption ? formatEffortLabel(selectedOption.effort) : "Medium";
|
|
136633
|
+
const effortLabel = selectedOption ? formatEffortLabel(selectedOption.effort, hasDistinctMaxTier) : "Medium";
|
|
135745
136634
|
const selectedText = selectedBars > 0 ? EFFORT_BLOCK.repeat(selectedBars) : "";
|
|
135746
136635
|
const remainingBars = totalBars > selectedBars ? EFFORT_BLOCK.repeat(totalBars - selectedBars) : "";
|
|
135747
136636
|
return /* @__PURE__ */ jsx_dev_runtime59.jsxDEV(Box_default, {
|
|
@@ -136944,7 +137833,7 @@ var init_PersonalitySelector = __esm(async () => {
|
|
|
136944
137833
|
});
|
|
136945
137834
|
|
|
136946
137835
|
// src/utils/aws-credentials.ts
|
|
136947
|
-
import { readFile as
|
|
137836
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
136948
137837
|
import { homedir as homedir36 } from "node:os";
|
|
136949
137838
|
import { join as join48 } from "node:path";
|
|
136950
137839
|
async function parseAwsCredentials() {
|
|
@@ -136952,11 +137841,11 @@ async function parseAwsCredentials() {
|
|
|
136952
137841
|
const configPath = join48(homedir36(), ".aws", "config");
|
|
136953
137842
|
const profiles = new Map;
|
|
136954
137843
|
try {
|
|
136955
|
-
const content = await
|
|
137844
|
+
const content = await readFile16(credentialsPath, "utf-8");
|
|
136956
137845
|
parseIniFile(content, profiles, false);
|
|
136957
137846
|
} catch {}
|
|
136958
137847
|
try {
|
|
136959
|
-
const content = await
|
|
137848
|
+
const content = await readFile16(configPath, "utf-8");
|
|
136960
137849
|
parseIniFile(content, profiles, true);
|
|
136961
137850
|
} catch {}
|
|
136962
137851
|
return Array.from(profiles.values());
|
|
@@ -143621,7 +144510,7 @@ var exports_export = {};
|
|
|
143621
144510
|
__export(exports_export, {
|
|
143622
144511
|
packageSkills: () => packageSkills
|
|
143623
144512
|
});
|
|
143624
|
-
import { readdir as readdir11, readFile as
|
|
144513
|
+
import { readdir as readdir11, readFile as readFile17 } from "node:fs/promises";
|
|
143625
144514
|
import { relative as relative17, resolve as resolve32 } from "node:path";
|
|
143626
144515
|
async function packageSkills(agentId, skillsDir) {
|
|
143627
144516
|
const skills = [];
|
|
@@ -143642,7 +144531,7 @@ async function packageSkills(agentId, skillsDir) {
|
|
|
143642
144531
|
const skillDir = resolve32(baseDir, entry.name);
|
|
143643
144532
|
const skillMdPath = resolve32(skillDir, "SKILL.md");
|
|
143644
144533
|
try {
|
|
143645
|
-
await
|
|
144534
|
+
await readFile17(skillMdPath, "utf-8");
|
|
143646
144535
|
} catch {
|
|
143647
144536
|
console.warn(`Skipping invalid skill ${entry.name}: missing SKILL.md`);
|
|
143648
144537
|
continue;
|
|
@@ -143674,7 +144563,7 @@ async function readSkillFiles(skillDir) {
|
|
|
143674
144563
|
if (entry.isDirectory()) {
|
|
143675
144564
|
await walk(fullPath);
|
|
143676
144565
|
} else {
|
|
143677
|
-
const content = await
|
|
144566
|
+
const content = await readFile17(fullPath, "utf-8");
|
|
143678
144567
|
const relativePath = relative17(skillDir, fullPath).replace(/\\/g, "/");
|
|
143679
144568
|
files[relativePath] = content;
|
|
143680
144569
|
}
|
|
@@ -143828,12 +144717,12 @@ function deriveReasoningEffort(modelSettings, llmConfig) {
|
|
|
143828
144717
|
const effort = modelSettings.effort;
|
|
143829
144718
|
if (effort === "low" || effort === "medium" || effort === "high")
|
|
143830
144719
|
return effort;
|
|
143831
|
-
if (effort === "max")
|
|
143832
|
-
return
|
|
144720
|
+
if (effort === "xhigh" || effort === "max")
|
|
144721
|
+
return effort;
|
|
143833
144722
|
}
|
|
143834
144723
|
}
|
|
143835
144724
|
const re = llmConfig?.reasoning_effort;
|
|
143836
|
-
if (re === "none" || re === "minimal" || re === "low" || re === "medium" || re === "high" || re === "xhigh")
|
|
144725
|
+
if (re === "none" || re === "minimal" || re === "low" || re === "medium" || re === "high" || re === "xhigh" || re === "max")
|
|
143837
144726
|
return re;
|
|
143838
144727
|
if (llmConfig?.enable_reasoner === false)
|
|
143839
144728
|
return "none";
|
|
@@ -143842,7 +144731,7 @@ function deriveReasoningEffort(modelSettings, llmConfig) {
|
|
|
143842
144731
|
function inferReasoningEffortFromModelPreset(modelId, modelHandle) {
|
|
143843
144732
|
const modelInfo = (modelId ? getModelInfo2(modelId) : null) ?? (modelHandle ? getModelInfo2(modelHandle) : null);
|
|
143844
144733
|
const presetEffort = modelInfo?.updateArgs?.reasoning_effort;
|
|
143845
|
-
if (presetEffort === "none" || presetEffort === "minimal" || presetEffort === "low" || presetEffort === "medium" || presetEffort === "high" || presetEffort === "xhigh") {
|
|
144734
|
+
if (presetEffort === "none" || presetEffort === "minimal" || presetEffort === "low" || presetEffort === "medium" || presetEffort === "high" || presetEffort === "xhigh" || presetEffort === "max") {
|
|
143846
144735
|
return presetEffort;
|
|
143847
144736
|
}
|
|
143848
144737
|
return null;
|
|
@@ -143883,7 +144772,8 @@ function getErrorHintForStopReason(stopReason, currentModelId, modelEndpointType
|
|
|
143883
144772
|
}
|
|
143884
144773
|
const isAutoModel = currentModelId?.startsWith("auto") ?? false;
|
|
143885
144774
|
const statusInfo = modelEndpointType && !isAutoModel ? PROVIDER_STATUS_PAGES[modelEndpointType] : undefined;
|
|
143886
|
-
const
|
|
144775
|
+
const isOpus46 = currentModelId?.startsWith("opus-4.6") ?? false;
|
|
144776
|
+
const hasBedrockOpus = isOpus46 && modelEndpointType === "anthropic" && getModelInfo2("bedrock-opus-4.6");
|
|
143887
144777
|
const modelSwapSuffix = hasBedrockOpus ? " (e.g. Opus 4.6 via Amazon Bedrock)" : "";
|
|
143888
144778
|
if (statusInfo) {
|
|
143889
144779
|
return [
|
|
@@ -151231,7 +152121,7 @@ ${SYSTEM_REMINDER_CLOSE}
|
|
|
151231
152121
|
const modelHandle = model.handle ?? model.id;
|
|
151232
152122
|
const modelUpdateArgs = model.updateArgs;
|
|
151233
152123
|
const rawReasoningEffort = modelUpdateArgs?.reasoning_effort;
|
|
151234
|
-
const reasoningLevel = typeof rawReasoningEffort === "string" ? rawReasoningEffort === "none" ? "no" : rawReasoningEffort === "xhigh" ? "max" : rawReasoningEffort : modelUpdateArgs?.enable_reasoner === false ? "no" : null;
|
|
152124
|
+
const reasoningLevel = typeof rawReasoningEffort === "string" ? rawReasoningEffort === "none" ? "no" : rawReasoningEffort === "xhigh" ? model.label.includes("Opus 4.7") ? "extra-high" : "max" : rawReasoningEffort : modelUpdateArgs?.enable_reasoner === false ? "no" : null;
|
|
151235
152125
|
const selectedContextWindow = model.updateArgs?.context_window;
|
|
151236
152126
|
const reasoningTierOptions = getReasoningTierOptionsForHandle3(modelHandle, selectedContextWindow);
|
|
151237
152127
|
if (!opts?.skipReasoningPrompt && activeOverlay === "model" && reasoningTierOptions.length > 1) {
|
|
@@ -152007,7 +152897,16 @@ ${guidance}`);
|
|
|
152007
152897
|
}).filter((m) => Boolean(m.effort));
|
|
152008
152898
|
if (tiers.length < 2)
|
|
152009
152899
|
return;
|
|
152010
|
-
const
|
|
152900
|
+
const anthropicXHighEffort = modelHandle.includes("claude-opus-4-7") ? "xhigh" : "max";
|
|
152901
|
+
const order = [
|
|
152902
|
+
"none",
|
|
152903
|
+
"minimal",
|
|
152904
|
+
"low",
|
|
152905
|
+
"medium",
|
|
152906
|
+
"high",
|
|
152907
|
+
"xhigh",
|
|
152908
|
+
"max"
|
|
152909
|
+
];
|
|
152011
152910
|
const rank = (effort) => {
|
|
152012
152911
|
const idx = order.indexOf(effort);
|
|
152013
152912
|
return idx >= 0 ? idx : 999;
|
|
@@ -152044,12 +152943,11 @@ ${guidance}`);
|
|
|
152044
152943
|
};
|
|
152045
152944
|
}
|
|
152046
152945
|
if (ms.provider_type === "anthropic" || ms.provider_type === "bedrock") {
|
|
152047
|
-
const anthropicEffort = next.effort === "xhigh" ? "max" : next.effort;
|
|
152048
152946
|
return {
|
|
152049
152947
|
...prev,
|
|
152050
152948
|
model_settings: {
|
|
152051
152949
|
...ms,
|
|
152052
|
-
effort:
|
|
152950
|
+
effort: next.effort === "xhigh" ? anthropicXHighEffort : next.effort
|
|
152053
152951
|
}
|
|
152054
152952
|
};
|
|
152055
152953
|
}
|
|
@@ -154443,7 +155341,7 @@ __export(exports_import2, {
|
|
|
154443
155341
|
extractSkillsFromAf: () => extractSkillsFromAf2
|
|
154444
155342
|
});
|
|
154445
155343
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
154446
|
-
import { chmod as chmod2, mkdir as mkdir11, readFile as
|
|
155344
|
+
import { chmod as chmod2, mkdir as mkdir11, readFile as readFile18, writeFile as writeFile12 } from "node:fs/promises";
|
|
154447
155345
|
import { dirname as dirname20, resolve as resolve33 } from "node:path";
|
|
154448
155346
|
async function importAgentFromFile2(options) {
|
|
154449
155347
|
const client = await getClient();
|
|
@@ -154476,7 +155374,7 @@ async function importAgentFromFile2(options) {
|
|
|
154476
155374
|
}
|
|
154477
155375
|
async function extractSkillsFromAf2(afPath, destDir) {
|
|
154478
155376
|
const extracted = [];
|
|
154479
|
-
const content = await
|
|
155377
|
+
const content = await readFile18(afPath, "utf-8");
|
|
154480
155378
|
const afData = JSON.parse(content);
|
|
154481
155379
|
if (!afData.skills || !Array.isArray(afData.skills)) {
|
|
154482
155380
|
return [];
|
|
@@ -155490,6 +156388,9 @@ init_openai_codex_provider();
|
|
|
155490
156388
|
init_debug();
|
|
155491
156389
|
init_available_models();
|
|
155492
156390
|
init_client2();
|
|
156391
|
+
function supportsDistinctAnthropicXHighEffort(modelHandle) {
|
|
156392
|
+
return modelHandle.includes("claude-opus-4-7");
|
|
156393
|
+
}
|
|
155493
156394
|
function buildModelSettings(modelHandle, updateArgs) {
|
|
155494
156395
|
const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
|
|
155495
156396
|
const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
|
|
@@ -155523,10 +156424,13 @@ function buildModelSettings(modelHandle, updateArgs) {
|
|
|
155523
156424
|
parallel_tool_calls: true
|
|
155524
156425
|
};
|
|
155525
156426
|
const effort = updateArgs?.reasoning_effort;
|
|
156427
|
+
const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort(modelHandle);
|
|
155526
156428
|
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
155527
156429
|
anthropicSettings.effort = effort;
|
|
155528
156430
|
} else if (effort === "xhigh") {
|
|
155529
|
-
anthropicSettings.effort = "max";
|
|
156431
|
+
anthropicSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
|
|
156432
|
+
} else if (effort === "max") {
|
|
156433
|
+
anthropicSettings.effort = effort;
|
|
155530
156434
|
}
|
|
155531
156435
|
if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
|
|
155532
156436
|
anthropicSettings.thinking = {
|
|
@@ -155579,10 +156483,13 @@ function buildModelSettings(modelHandle, updateArgs) {
|
|
|
155579
156483
|
parallel_tool_calls: true
|
|
155580
156484
|
};
|
|
155581
156485
|
const effort = updateArgs?.reasoning_effort;
|
|
156486
|
+
const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort(modelHandle);
|
|
155582
156487
|
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
155583
156488
|
bedrockSettings.effort = effort;
|
|
155584
156489
|
} else if (effort === "xhigh") {
|
|
155585
|
-
bedrockSettings.effort = "max";
|
|
156490
|
+
bedrockSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
|
|
156491
|
+
} else if (effort === "max") {
|
|
156492
|
+
bedrockSettings.effort = effort;
|
|
155586
156493
|
}
|
|
155587
156494
|
if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
|
|
155588
156495
|
bedrockSettings.thinking = {
|
|
@@ -158410,8 +159317,14 @@ Usage:
|
|
|
158410
159317
|
letta channels route list [--channel <ch>] Show routing table
|
|
158411
159318
|
letta channels route add [options] Add a route
|
|
158412
159319
|
letta channels route remove [options] Remove a route
|
|
159320
|
+
letta channels bind [options] Bind a Slack app to an agent
|
|
158413
159321
|
letta channels pair [options] Approve pairing + bind to agent
|
|
158414
159322
|
|
|
159323
|
+
Bind options (Slack only):
|
|
159324
|
+
--channel slack Required
|
|
159325
|
+
--account-id <id> Channel account ID (optional; inferred when only one account exists)
|
|
159326
|
+
--agent <id> Agent ID (defaults to LETTA_AGENT_ID)
|
|
159327
|
+
|
|
158415
159328
|
Route add options:
|
|
158416
159329
|
--channel <name> Channel name (e.g. "telegram")
|
|
158417
159330
|
--account-id <id> Channel account ID (required when multiple accounts exist)
|
|
@@ -158705,6 +159618,46 @@ async function handlePair(values) {
|
|
|
158705
159618
|
}
|
|
158706
159619
|
return result.success ? 0 : 1;
|
|
158707
159620
|
}
|
|
159621
|
+
function handleBind(values) {
|
|
159622
|
+
const channelId = values.channel;
|
|
159623
|
+
const accountId = values["account-id"];
|
|
159624
|
+
const agentId = getAgentId3(values.agent);
|
|
159625
|
+
if (!channelId) {
|
|
159626
|
+
console.error("Error: --channel is required.");
|
|
159627
|
+
return 1;
|
|
159628
|
+
}
|
|
159629
|
+
if (channelId !== "slack") {
|
|
159630
|
+
console.error(`"bind" is only supported for Slack. Telegram binding is route-scoped — use "pair" or "route add" instead.`);
|
|
159631
|
+
return 1;
|
|
159632
|
+
}
|
|
159633
|
+
if (!agentId) {
|
|
159634
|
+
console.error("Error: --agent is required (or set LETTA_AGENT_ID env var).");
|
|
159635
|
+
return 1;
|
|
159636
|
+
}
|
|
159637
|
+
let resolvedAccountId;
|
|
159638
|
+
try {
|
|
159639
|
+
resolvedAccountId = resolveSelectedAccountId(channelId, accountId);
|
|
159640
|
+
} catch (error) {
|
|
159641
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
159642
|
+
return 1;
|
|
159643
|
+
}
|
|
159644
|
+
const account = getChannelAccount(channelId, resolvedAccountId);
|
|
159645
|
+
if (!account) {
|
|
159646
|
+
console.error(`Account "${resolvedAccountId}" not found for channel "${channelId}".`);
|
|
159647
|
+
return 1;
|
|
159648
|
+
}
|
|
159649
|
+
account.agentId = agentId;
|
|
159650
|
+
account.updatedAt = new Date().toISOString();
|
|
159651
|
+
upsertChannelAccount(channelId, account);
|
|
159652
|
+
console.log(JSON.stringify({
|
|
159653
|
+
success: true,
|
|
159654
|
+
channel: channelId,
|
|
159655
|
+
accountId: resolvedAccountId,
|
|
159656
|
+
agentId
|
|
159657
|
+
}, null, 2));
|
|
159658
|
+
console.warn("Note: If a listener is running, restart it for the binding to take effect.");
|
|
159659
|
+
return 0;
|
|
159660
|
+
}
|
|
158708
159661
|
async function runChannelsSubcommand(argv) {
|
|
158709
159662
|
const { values, positionals } = parseChannelsArgs(argv);
|
|
158710
159663
|
if (values.help) {
|
|
@@ -158745,6 +159698,8 @@ async function runChannelsSubcommand(argv) {
|
|
|
158745
159698
|
return 1;
|
|
158746
159699
|
}
|
|
158747
159700
|
}
|
|
159701
|
+
case "bind":
|
|
159702
|
+
return handleBind(values);
|
|
158748
159703
|
case "pair":
|
|
158749
159704
|
return await handlePair(values);
|
|
158750
159705
|
default:
|
|
@@ -158752,7 +159707,7 @@ async function runChannelsSubcommand(argv) {
|
|
|
158752
159707
|
printUsage3();
|
|
158753
159708
|
return 0;
|
|
158754
159709
|
}
|
|
158755
|
-
console.error(`Unknown channels action: "${action}". Use: install, configure, status, route, pair`);
|
|
159710
|
+
console.error(`Unknown channels action: "${action}". Use: install, configure, status, route, bind, pair`);
|
|
158756
159711
|
return 1;
|
|
158757
159712
|
}
|
|
158758
159713
|
}
|
|
@@ -163877,4 +164832,4 @@ Error during initialization: ${message}`);
|
|
|
163877
164832
|
}
|
|
163878
164833
|
main();
|
|
163879
164834
|
|
|
163880
|
-
//# debugId=
|
|
164835
|
+
//# debugId=13707EE7EA6B933364756E2164756E21
|