fourmis-agents-sdk 0.2.6 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-loop.d.ts +3 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +23 -9
- package/dist/agents/index.js +40 -12
- package/dist/agents/tools.js +40 -12
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +658 -28
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +666 -28
- package/dist/memory/index.d.ts +42 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +319 -0
- package/dist/memory/memory-handler.d.ts +49 -0
- package/dist/memory/memory-handler.d.ts.map +1 -0
- package/dist/memory/memory-handler.js +233 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +17 -3
- package/dist/providers/registry.js +17 -3
- package/dist/providers/types.d.ts +6 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/skills/frontmatter.d.ts +15 -0
- package/dist/skills/frontmatter.d.ts.map +1 -0
- package/dist/skills/frontmatter.js +51 -0
- package/dist/skills/index.d.ts +8 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +289 -0
- package/dist/skills/skills.d.ts +78 -0
- package/dist/skills/skills.d.ts.map +1 -0
- package/dist/skills/skills.js +287 -0
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/system-prompt.d.ts +2 -0
- package/dist/utils/system-prompt.d.ts.map +1 -1
- package/dist/utils/system-prompt.js +279 -3
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -501,7 +501,8 @@ async function* agentLoop(prompt, options) {
|
|
|
501
501
|
hooks,
|
|
502
502
|
mcpClient,
|
|
503
503
|
previousMessages,
|
|
504
|
-
sessionLogger
|
|
504
|
+
sessionLogger,
|
|
505
|
+
nativeMemoryTool
|
|
505
506
|
} = options;
|
|
506
507
|
const startTime = Date.now();
|
|
507
508
|
let apiTimeMs = 0;
|
|
@@ -555,13 +556,15 @@ async function* agentLoop(prompt, options) {
|
|
|
555
556
|
let assistantTextParts = [];
|
|
556
557
|
let toolCalls = [];
|
|
557
558
|
let turnUsage = emptyTokenUsage();
|
|
559
|
+
const nativeTools = nativeMemoryTool ? [nativeMemoryTool.definition] : undefined;
|
|
558
560
|
try {
|
|
559
561
|
const chunks = provider.chat({
|
|
560
562
|
model,
|
|
561
563
|
messages,
|
|
562
564
|
tools: toolDefs.length > 0 ? toolDefs : undefined,
|
|
563
565
|
systemPrompt,
|
|
564
|
-
signal
|
|
566
|
+
signal,
|
|
567
|
+
nativeTools
|
|
565
568
|
});
|
|
566
569
|
for await (const chunk of chunks) {
|
|
567
570
|
switch (chunk.type) {
|
|
@@ -719,13 +722,24 @@ async function* agentLoop(prompt, options) {
|
|
|
719
722
|
input: toolInput,
|
|
720
723
|
uuid: uuid()
|
|
721
724
|
};
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
725
|
+
let result;
|
|
726
|
+
if (call.name === "memory" && nativeMemoryTool) {
|
|
727
|
+
try {
|
|
728
|
+
const content = await nativeMemoryTool.execute(toolInput);
|
|
729
|
+
result = { content, isError: content.startsWith("Error:") };
|
|
730
|
+
} catch (err) {
|
|
731
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
732
|
+
result = { content: `Error: ${message}`, isError: true };
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
const toolCtx = {
|
|
736
|
+
cwd,
|
|
737
|
+
signal,
|
|
738
|
+
sessionId,
|
|
739
|
+
env
|
|
740
|
+
};
|
|
741
|
+
result = await tools.execute(call.name, toolInput, toolCtx);
|
|
742
|
+
}
|
|
729
743
|
if (debug) {
|
|
730
744
|
console.error(`[debug] Tool ${call.name}: ${result.isError ? "ERROR" : "OK"} (${result.content.length} chars)`);
|
|
731
745
|
}
|
|
@@ -1040,15 +1054,29 @@ class AnthropicAdapter {
|
|
|
1040
1054
|
} else if (request.systemPrompt) {
|
|
1041
1055
|
params.system = request.systemPrompt;
|
|
1042
1056
|
}
|
|
1057
|
+
const allTools = [];
|
|
1043
1058
|
if (tools && tools.length > 0) {
|
|
1044
|
-
|
|
1059
|
+
allTools.push(...tools);
|
|
1060
|
+
}
|
|
1061
|
+
if (request.nativeTools && request.nativeTools.length > 0) {
|
|
1062
|
+
allTools.push(...request.nativeTools);
|
|
1063
|
+
}
|
|
1064
|
+
if (allTools.length > 0) {
|
|
1065
|
+
params.tools = allTools;
|
|
1045
1066
|
}
|
|
1046
1067
|
if (request.temperature !== undefined) {
|
|
1047
1068
|
params.temperature = request.temperature;
|
|
1048
1069
|
}
|
|
1049
|
-
const
|
|
1070
|
+
const hasMemoryTool = request.nativeTools?.some((t) => t.type === "memory_20250818");
|
|
1071
|
+
const requestOptions = {
|
|
1050
1072
|
signal: request.signal
|
|
1051
|
-
}
|
|
1073
|
+
};
|
|
1074
|
+
if (hasMemoryTool) {
|
|
1075
|
+
requestOptions.headers = {
|
|
1076
|
+
"anthropic-beta": "context-management-2025-06-27"
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
const stream = this.client.messages.stream(params, requestOptions);
|
|
1052
1080
|
const toolInputBuffers = new Map;
|
|
1053
1081
|
for await (const event of stream) {
|
|
1054
1082
|
switch (event.type) {
|
|
@@ -2888,9 +2916,277 @@ class SettingsManager {
|
|
|
2888
2916
|
}
|
|
2889
2917
|
}
|
|
2890
2918
|
|
|
2919
|
+
// src/skills/frontmatter.ts
|
|
2920
|
+
import { parse } from "yaml";
|
|
2921
|
+
function normalizeNewlines(value) {
|
|
2922
|
+
return value.replace(/\r\n/g, `
|
|
2923
|
+
`).replace(/\r/g, `
|
|
2924
|
+
`);
|
|
2925
|
+
}
|
|
2926
|
+
function extractFrontmatter(content) {
|
|
2927
|
+
const normalized = normalizeNewlines(content);
|
|
2928
|
+
if (!normalized.startsWith("---")) {
|
|
2929
|
+
return { yamlString: null, body: normalized };
|
|
2930
|
+
}
|
|
2931
|
+
const endIndex = normalized.indexOf(`
|
|
2932
|
+
---`, 3);
|
|
2933
|
+
if (endIndex === -1) {
|
|
2934
|
+
return { yamlString: null, body: normalized };
|
|
2935
|
+
}
|
|
2936
|
+
return {
|
|
2937
|
+
yamlString: normalized.slice(4, endIndex),
|
|
2938
|
+
body: normalized.slice(endIndex + 4).trim()
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
function parseFrontmatter(content) {
|
|
2942
|
+
const { yamlString, body } = extractFrontmatter(content);
|
|
2943
|
+
if (!yamlString) {
|
|
2944
|
+
return { frontmatter: {}, body };
|
|
2945
|
+
}
|
|
2946
|
+
const parsed = parse(yamlString);
|
|
2947
|
+
return { frontmatter: parsed ?? {}, body };
|
|
2948
|
+
}
|
|
2949
|
+
function stripFrontmatter(content) {
|
|
2950
|
+
return parseFrontmatter(content).body;
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// src/skills/skills.ts
|
|
2954
|
+
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync4, realpathSync, statSync } from "fs";
|
|
2955
|
+
import { homedir as homedir4 } from "os";
|
|
2956
|
+
import { basename, dirname as dirname3, isAbsolute, join as join4, resolve } from "path";
|
|
2957
|
+
var MAX_NAME_LENGTH = 64;
|
|
2958
|
+
var MAX_DESCRIPTION_LENGTH = 1024;
|
|
2959
|
+
var CONFIG_DIR_NAME = ".claude";
|
|
2960
|
+
function shouldIgnore(name) {
|
|
2961
|
+
return name.startsWith(".") || name === "node_modules";
|
|
2962
|
+
}
|
|
2963
|
+
function validateName(name, parentDirName) {
|
|
2964
|
+
const errors = [];
|
|
2965
|
+
if (name !== parentDirName) {
|
|
2966
|
+
errors.push(`name "${name}" does not match parent directory "${parentDirName}"`);
|
|
2967
|
+
}
|
|
2968
|
+
if (name.length > MAX_NAME_LENGTH) {
|
|
2969
|
+
errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);
|
|
2970
|
+
}
|
|
2971
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
2972
|
+
errors.push(`name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)`);
|
|
2973
|
+
}
|
|
2974
|
+
if (name.startsWith("-") || name.endsWith("-")) {
|
|
2975
|
+
errors.push(`name must not start or end with a hyphen`);
|
|
2976
|
+
}
|
|
2977
|
+
if (name.includes("--")) {
|
|
2978
|
+
errors.push(`name must not contain consecutive hyphens`);
|
|
2979
|
+
}
|
|
2980
|
+
return errors;
|
|
2981
|
+
}
|
|
2982
|
+
function validateDescription(description) {
|
|
2983
|
+
const errors = [];
|
|
2984
|
+
if (!description || description.trim() === "") {
|
|
2985
|
+
errors.push("description is required");
|
|
2986
|
+
} else if (description.length > MAX_DESCRIPTION_LENGTH) {
|
|
2987
|
+
errors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);
|
|
2988
|
+
}
|
|
2989
|
+
return errors;
|
|
2990
|
+
}
|
|
2991
|
+
function loadSkillsFromDir(options) {
|
|
2992
|
+
return loadSkillsFromDirInternal(options.dir, options.source, true);
|
|
2993
|
+
}
|
|
2994
|
+
function loadSkillsFromDirInternal(dir, source, includeRootFiles) {
|
|
2995
|
+
const skills = [];
|
|
2996
|
+
const diagnostics = [];
|
|
2997
|
+
if (!existsSync4(dir)) {
|
|
2998
|
+
return { skills, diagnostics };
|
|
2999
|
+
}
|
|
3000
|
+
try {
|
|
3001
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
3002
|
+
for (const entry of entries) {
|
|
3003
|
+
if (shouldIgnore(entry.name)) {
|
|
3004
|
+
continue;
|
|
3005
|
+
}
|
|
3006
|
+
const fullPath = join4(dir, entry.name);
|
|
3007
|
+
let isDirectory = entry.isDirectory();
|
|
3008
|
+
let isFile = entry.isFile();
|
|
3009
|
+
if (entry.isSymbolicLink()) {
|
|
3010
|
+
try {
|
|
3011
|
+
const stats = statSync(fullPath);
|
|
3012
|
+
isDirectory = stats.isDirectory();
|
|
3013
|
+
isFile = stats.isFile();
|
|
3014
|
+
} catch {
|
|
3015
|
+
continue;
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
if (isDirectory) {
|
|
3019
|
+
const subResult = loadSkillsFromDirInternal(fullPath, source, false);
|
|
3020
|
+
skills.push(...subResult.skills);
|
|
3021
|
+
diagnostics.push(...subResult.diagnostics);
|
|
3022
|
+
continue;
|
|
3023
|
+
}
|
|
3024
|
+
if (!isFile)
|
|
3025
|
+
continue;
|
|
3026
|
+
const isRootMd = includeRootFiles && entry.name.endsWith(".md");
|
|
3027
|
+
const isSkillMd = !includeRootFiles && entry.name === "SKILL.md";
|
|
3028
|
+
if (!isRootMd && !isSkillMd)
|
|
3029
|
+
continue;
|
|
3030
|
+
const result = loadSkillFromFile(fullPath, source);
|
|
3031
|
+
if (result.skill) {
|
|
3032
|
+
skills.push(result.skill);
|
|
3033
|
+
}
|
|
3034
|
+
diagnostics.push(...result.diagnostics);
|
|
3035
|
+
}
|
|
3036
|
+
} catch {}
|
|
3037
|
+
return { skills, diagnostics };
|
|
3038
|
+
}
|
|
3039
|
+
function loadSkillFromFile(filePath, source) {
|
|
3040
|
+
const diagnostics = [];
|
|
3041
|
+
try {
|
|
3042
|
+
const rawContent = readFileSync4(filePath, "utf-8");
|
|
3043
|
+
const { frontmatter } = parseFrontmatter(rawContent);
|
|
3044
|
+
const skillDir = dirname3(filePath);
|
|
3045
|
+
const parentDirName = basename(skillDir);
|
|
3046
|
+
const descErrors = validateDescription(frontmatter.description);
|
|
3047
|
+
for (const error of descErrors) {
|
|
3048
|
+
diagnostics.push({ type: "warning", message: error, path: filePath });
|
|
3049
|
+
}
|
|
3050
|
+
const name = frontmatter.name || parentDirName;
|
|
3051
|
+
const nameErrors = validateName(name, parentDirName);
|
|
3052
|
+
for (const error of nameErrors) {
|
|
3053
|
+
diagnostics.push({ type: "warning", message: error, path: filePath });
|
|
3054
|
+
}
|
|
3055
|
+
if (!frontmatter.description || frontmatter.description.trim() === "") {
|
|
3056
|
+
return { skill: null, diagnostics };
|
|
3057
|
+
}
|
|
3058
|
+
return {
|
|
3059
|
+
skill: {
|
|
3060
|
+
name,
|
|
3061
|
+
description: frontmatter.description,
|
|
3062
|
+
filePath,
|
|
3063
|
+
baseDir: skillDir,
|
|
3064
|
+
source,
|
|
3065
|
+
disableModelInvocation: frontmatter["disable-model-invocation"] === true
|
|
3066
|
+
},
|
|
3067
|
+
diagnostics
|
|
3068
|
+
};
|
|
3069
|
+
} catch (error) {
|
|
3070
|
+
const message = error instanceof Error ? error.message : "failed to parse skill file";
|
|
3071
|
+
diagnostics.push({ type: "warning", message, path: filePath });
|
|
3072
|
+
return { skill: null, diagnostics };
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
function normalizePath(input) {
|
|
3076
|
+
const trimmed = input.trim();
|
|
3077
|
+
if (trimmed === "~")
|
|
3078
|
+
return homedir4();
|
|
3079
|
+
if (trimmed.startsWith("~/"))
|
|
3080
|
+
return join4(homedir4(), trimmed.slice(2));
|
|
3081
|
+
if (trimmed.startsWith("~"))
|
|
3082
|
+
return join4(homedir4(), trimmed.slice(1));
|
|
3083
|
+
return trimmed;
|
|
3084
|
+
}
|
|
3085
|
+
function resolveSkillPath(p, cwd) {
|
|
3086
|
+
const normalized = normalizePath(p);
|
|
3087
|
+
return isAbsolute(normalized) ? normalized : resolve(cwd, normalized);
|
|
3088
|
+
}
|
|
3089
|
+
function loadSkills(options = {}) {
|
|
3090
|
+
const { cwd = process.cwd(), skillPaths = [], includeDefaults = true } = options;
|
|
3091
|
+
const skillMap = new Map;
|
|
3092
|
+
const realPathSet = new Set;
|
|
3093
|
+
const allDiagnostics = [];
|
|
3094
|
+
const collisionDiagnostics = [];
|
|
3095
|
+
function addSkills(result) {
|
|
3096
|
+
allDiagnostics.push(...result.diagnostics);
|
|
3097
|
+
for (const skill of result.skills) {
|
|
3098
|
+
let realPath;
|
|
3099
|
+
try {
|
|
3100
|
+
realPath = realpathSync(skill.filePath);
|
|
3101
|
+
} catch {
|
|
3102
|
+
realPath = skill.filePath;
|
|
3103
|
+
}
|
|
3104
|
+
if (realPathSet.has(realPath))
|
|
3105
|
+
continue;
|
|
3106
|
+
const existing = skillMap.get(skill.name);
|
|
3107
|
+
if (existing) {
|
|
3108
|
+
collisionDiagnostics.push({
|
|
3109
|
+
type: "collision",
|
|
3110
|
+
message: `name "${skill.name}" collision`,
|
|
3111
|
+
path: skill.filePath,
|
|
3112
|
+
collision: {
|
|
3113
|
+
resourceType: "skill",
|
|
3114
|
+
name: skill.name,
|
|
3115
|
+
winnerPath: existing.filePath,
|
|
3116
|
+
loserPath: skill.filePath
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
} else {
|
|
3120
|
+
skillMap.set(skill.name, skill);
|
|
3121
|
+
realPathSet.add(realPath);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
if (includeDefaults) {
|
|
3126
|
+
const userSkillsDir = join4(homedir4(), CONFIG_DIR_NAME, "skills");
|
|
3127
|
+
const projectSkillsDir = resolve(cwd, CONFIG_DIR_NAME, "skills");
|
|
3128
|
+
addSkills(loadSkillsFromDirInternal(userSkillsDir, "user", true));
|
|
3129
|
+
addSkills(loadSkillsFromDirInternal(projectSkillsDir, "project", true));
|
|
3130
|
+
}
|
|
3131
|
+
for (const rawPath of skillPaths) {
|
|
3132
|
+
const resolvedPath = resolveSkillPath(rawPath, cwd);
|
|
3133
|
+
if (!existsSync4(resolvedPath)) {
|
|
3134
|
+
allDiagnostics.push({ type: "warning", message: "skill path does not exist", path: resolvedPath });
|
|
3135
|
+
continue;
|
|
3136
|
+
}
|
|
3137
|
+
try {
|
|
3138
|
+
const stats = statSync(resolvedPath);
|
|
3139
|
+
if (stats.isDirectory()) {
|
|
3140
|
+
addSkills(loadSkillsFromDirInternal(resolvedPath, "path", true));
|
|
3141
|
+
} else if (stats.isFile() && resolvedPath.endsWith(".md")) {
|
|
3142
|
+
const result = loadSkillFromFile(resolvedPath, "path");
|
|
3143
|
+
if (result.skill) {
|
|
3144
|
+
addSkills({ skills: [result.skill], diagnostics: result.diagnostics });
|
|
3145
|
+
} else {
|
|
3146
|
+
allDiagnostics.push(...result.diagnostics);
|
|
3147
|
+
}
|
|
3148
|
+
} else {
|
|
3149
|
+
allDiagnostics.push({ type: "warning", message: "skill path is not a markdown file", path: resolvedPath });
|
|
3150
|
+
}
|
|
3151
|
+
} catch (error) {
|
|
3152
|
+
const message = error instanceof Error ? error.message : "failed to read skill path";
|
|
3153
|
+
allDiagnostics.push({ type: "warning", message, path: resolvedPath });
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
return {
|
|
3157
|
+
skills: Array.from(skillMap.values()),
|
|
3158
|
+
diagnostics: [...allDiagnostics, ...collisionDiagnostics]
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3161
|
+
function escapeXml(str) {
|
|
3162
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3163
|
+
}
|
|
3164
|
+
function formatSkillsForPrompt(skills) {
|
|
3165
|
+
const visibleSkills = skills.filter((s) => !s.disableModelInvocation);
|
|
3166
|
+
if (visibleSkills.length === 0) {
|
|
3167
|
+
return "";
|
|
3168
|
+
}
|
|
3169
|
+
const lines = [
|
|
3170
|
+
"The following skills provide specialized instructions for specific tasks.",
|
|
3171
|
+
"Use the read tool to load a skill's file when the task matches its description.",
|
|
3172
|
+
"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
|
|
3173
|
+
"",
|
|
3174
|
+
"<available_skills>"
|
|
3175
|
+
];
|
|
3176
|
+
for (const skill of visibleSkills) {
|
|
3177
|
+
lines.push(" <skill>");
|
|
3178
|
+
lines.push(` <name>${escapeXml(skill.name)}</name>`);
|
|
3179
|
+
lines.push(` <description>${escapeXml(skill.description)}</description>`);
|
|
3180
|
+
lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
|
|
3181
|
+
lines.push(" </skill>");
|
|
3182
|
+
}
|
|
3183
|
+
lines.push("</available_skills>");
|
|
3184
|
+
return lines.join(`
|
|
3185
|
+
`);
|
|
3186
|
+
}
|
|
2891
3187
|
// src/utils/system-prompt.ts
|
|
2892
|
-
import { readFileSync as
|
|
2893
|
-
import { join as
|
|
3188
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3189
|
+
import { join as join5 } from "path";
|
|
2894
3190
|
var CORE_IDENTITY = `You are an AI coding agent. You help users with software engineering tasks by reading, writing, and modifying code. You have access to tools that let you interact with the filesystem and execute commands.
|
|
2895
3191
|
|
|
2896
3192
|
You are highly capable and can help users complete complex tasks that would otherwise be too difficult or time-consuming.`;
|
|
@@ -2961,6 +3257,14 @@ Working directory: ${context.cwd}`);
|
|
|
2961
3257
|
${instructions}`);
|
|
2962
3258
|
}
|
|
2963
3259
|
}
|
|
3260
|
+
if (context.skills && context.skills.length > 0) {
|
|
3261
|
+
const skillsPrompt = formatSkillsForPrompt(context.skills);
|
|
3262
|
+
if (skillsPrompt) {
|
|
3263
|
+
sections.push(`# Skills
|
|
3264
|
+
|
|
3265
|
+
${skillsPrompt}`);
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
2964
3268
|
if (context.customPrompt) {
|
|
2965
3269
|
sections.push(context.customPrompt);
|
|
2966
3270
|
}
|
|
@@ -2971,7 +3275,7 @@ ${instructions}`);
|
|
|
2971
3275
|
function readProjectInstructions(cwd) {
|
|
2972
3276
|
for (const name of ["CLAUDE.md", "AGENTS.md"]) {
|
|
2973
3277
|
try {
|
|
2974
|
-
const content =
|
|
3278
|
+
const content = readFileSync5(join5(cwd, name), "utf-8").trim();
|
|
2975
3279
|
if (content)
|
|
2976
3280
|
return content;
|
|
2977
3281
|
} catch {}
|
|
@@ -3462,7 +3766,7 @@ class TaskManager {
|
|
|
3462
3766
|
if (!block) {
|
|
3463
3767
|
return `Task "${id}" is still running.`;
|
|
3464
3768
|
}
|
|
3465
|
-
const timeoutPromise = new Promise((
|
|
3769
|
+
const timeoutPromise = new Promise((resolve2) => setTimeout(resolve2, timeoutMs));
|
|
3466
3770
|
await Promise.race([task.promise, timeoutPromise]);
|
|
3467
3771
|
if (task.status === "running") {
|
|
3468
3772
|
return `Task "${id}" is still running (timed out after ${timeoutMs}ms).`;
|
|
@@ -3484,21 +3788,21 @@ class TaskManager {
|
|
|
3484
3788
|
}
|
|
3485
3789
|
|
|
3486
3790
|
// src/utils/session-store.ts
|
|
3487
|
-
import { readFileSync as
|
|
3488
|
-
import { join as
|
|
3489
|
-
import { homedir as
|
|
3791
|
+
import { readFileSync as readFileSync6, appendFileSync, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
3792
|
+
import { join as join6 } from "path";
|
|
3793
|
+
import { homedir as homedir5 } from "os";
|
|
3490
3794
|
function sanitizeCwd(cwd) {
|
|
3491
3795
|
return cwd.replace(/[/.]/g, "-");
|
|
3492
3796
|
}
|
|
3493
3797
|
function sessionsDir(cwd) {
|
|
3494
|
-
return
|
|
3798
|
+
return join6(homedir5(), ".claude", "projects", sanitizeCwd(cwd));
|
|
3495
3799
|
}
|
|
3496
3800
|
function ensureDir(dir) {
|
|
3497
3801
|
mkdirSync3(dir, { recursive: true });
|
|
3498
3802
|
}
|
|
3499
3803
|
function logMessage(dir, sessionId, entry) {
|
|
3500
3804
|
ensureDir(dir);
|
|
3501
|
-
const filePath =
|
|
3805
|
+
const filePath = join6(dir, `${sessionId}.jsonl`);
|
|
3502
3806
|
appendFileSync(filePath, JSON.stringify(entry) + `
|
|
3503
3807
|
`);
|
|
3504
3808
|
}
|
|
@@ -3535,10 +3839,10 @@ function createSessionLogger(cwd, sessionId, model) {
|
|
|
3535
3839
|
function findLatestSession(cwd) {
|
|
3536
3840
|
const dir = sessionsDir(cwd);
|
|
3537
3841
|
try {
|
|
3538
|
-
const files =
|
|
3539
|
-
const filePath =
|
|
3842
|
+
const files = readdirSync2(dir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
3843
|
+
const filePath = join6(dir, f);
|
|
3540
3844
|
try {
|
|
3541
|
-
return { name: f, mtime:
|
|
3845
|
+
return { name: f, mtime: statSync2(filePath).mtimeMs };
|
|
3542
3846
|
} catch {
|
|
3543
3847
|
return null;
|
|
3544
3848
|
}
|
|
@@ -3552,10 +3856,10 @@ function findLatestSession(cwd) {
|
|
|
3552
3856
|
}
|
|
3553
3857
|
function loadSessionMessages(cwd, sessionId) {
|
|
3554
3858
|
const dir = sessionsDir(cwd);
|
|
3555
|
-
const filePath =
|
|
3859
|
+
const filePath = join6(dir, `${sessionId}.jsonl`);
|
|
3556
3860
|
let lines;
|
|
3557
3861
|
try {
|
|
3558
|
-
lines =
|
|
3862
|
+
lines = readFileSync6(filePath, "utf-8").trim().split(`
|
|
3559
3863
|
`).filter(Boolean);
|
|
3560
3864
|
} catch {
|
|
3561
3865
|
return [];
|
|
@@ -3586,6 +3890,307 @@ function loadSessionMessages(cwd, sessionId) {
|
|
|
3586
3890
|
return messages;
|
|
3587
3891
|
}
|
|
3588
3892
|
|
|
3893
|
+
// src/memory/memory-handler.ts
|
|
3894
|
+
import { readdir, stat, readFile, writeFile, rm, rename, mkdir as mkdir2 } from "fs/promises";
|
|
3895
|
+
import { join as join7, resolve as resolve2, relative as relative2 } from "path";
|
|
3896
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3897
|
+
function createMemoryHandler(memoryDir) {
|
|
3898
|
+
const absMemoryDir = resolve2(memoryDir);
|
|
3899
|
+
function resolvePath2(logicalPath) {
|
|
3900
|
+
let cleaned = logicalPath;
|
|
3901
|
+
if (cleaned.startsWith("/memories")) {
|
|
3902
|
+
cleaned = cleaned.slice("/memories".length);
|
|
3903
|
+
}
|
|
3904
|
+
if (cleaned.startsWith("/")) {
|
|
3905
|
+
cleaned = cleaned.slice(1);
|
|
3906
|
+
}
|
|
3907
|
+
if (cleaned.includes("..") || cleaned.includes("%2e") || cleaned.includes("%2E")) {
|
|
3908
|
+
throw new Error(`Path traversal detected: ${logicalPath}`);
|
|
3909
|
+
}
|
|
3910
|
+
const absPath = cleaned === "" ? absMemoryDir : resolve2(absMemoryDir, cleaned);
|
|
3911
|
+
const rel = relative2(absMemoryDir, absPath);
|
|
3912
|
+
if (rel.startsWith("..") || resolve2(absPath) !== absPath && !absPath.startsWith(absMemoryDir)) {
|
|
3913
|
+
throw new Error(`Path traversal detected: ${logicalPath}`);
|
|
3914
|
+
}
|
|
3915
|
+
return absPath;
|
|
3916
|
+
}
|
|
3917
|
+
function toLogicalPath(absPath) {
|
|
3918
|
+
const rel = relative2(absMemoryDir, absPath);
|
|
3919
|
+
return rel === "" ? "/memories" : `/memories/${rel}`;
|
|
3920
|
+
}
|
|
3921
|
+
function formatSize(bytes) {
|
|
3922
|
+
if (bytes < 1024)
|
|
3923
|
+
return `${bytes}`;
|
|
3924
|
+
if (bytes < 1024 * 1024)
|
|
3925
|
+
return `${(bytes / 1024).toFixed(1)}K`;
|
|
3926
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
|
|
3927
|
+
}
|
|
3928
|
+
async function listDir(dirPath, depth = 0) {
|
|
3929
|
+
const lines = [];
|
|
3930
|
+
const dirStat = await stat(dirPath);
|
|
3931
|
+
if (depth === 0) {
|
|
3932
|
+
lines.push(`${formatSize(dirStat.size)} ${toLogicalPath(dirPath)}`);
|
|
3933
|
+
}
|
|
3934
|
+
if (depth >= 2)
|
|
3935
|
+
return lines;
|
|
3936
|
+
try {
|
|
3937
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
3938
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
3939
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
3940
|
+
continue;
|
|
3941
|
+
const entryPath = join7(dirPath, entry.name);
|
|
3942
|
+
const entryStat = await stat(entryPath);
|
|
3943
|
+
lines.push(`${formatSize(entryStat.size)} ${toLogicalPath(entryPath)}`);
|
|
3944
|
+
if (entry.isDirectory()) {
|
|
3945
|
+
const subLines = await listDir(entryPath, depth + 1);
|
|
3946
|
+
lines.push(...subLines.slice(depth === 0 ? 0 : 0));
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
} catch {}
|
|
3950
|
+
return lines;
|
|
3951
|
+
}
|
|
3952
|
+
function formatFileContent(content, viewRange) {
|
|
3953
|
+
const lines = content.split(`
|
|
3954
|
+
`);
|
|
3955
|
+
if (lines.length > 999999) {
|
|
3956
|
+
throw new Error(`File exceeds maximum line limit of 999,999 lines.`);
|
|
3957
|
+
}
|
|
3958
|
+
let start = 0;
|
|
3959
|
+
let end = lines.length;
|
|
3960
|
+
if (viewRange && viewRange.length >= 2) {
|
|
3961
|
+
start = Math.max(0, viewRange[0] - 1);
|
|
3962
|
+
end = Math.min(lines.length, viewRange[1]);
|
|
3963
|
+
}
|
|
3964
|
+
const formatted = [];
|
|
3965
|
+
for (let i = start;i < end; i++) {
|
|
3966
|
+
const lineNum = String(i + 1).padStart(6, " ");
|
|
3967
|
+
formatted.push(`${lineNum} ${lines[i]}`);
|
|
3968
|
+
}
|
|
3969
|
+
return formatted.join(`
|
|
3970
|
+
`);
|
|
3971
|
+
}
|
|
3972
|
+
async function handleView(cmd) {
|
|
3973
|
+
const absPath = resolvePath2(cmd.path);
|
|
3974
|
+
if (!existsSync5(absPath)) {
|
|
3975
|
+
return `The path ${cmd.path} does not exist. Please provide a valid path.`;
|
|
3976
|
+
}
|
|
3977
|
+
const s = await stat(absPath);
|
|
3978
|
+
if (s.isDirectory()) {
|
|
3979
|
+
const lines = await listDir(absPath);
|
|
3980
|
+
return `Here're the files and directories up to 2 levels deep in ${cmd.path}, excluding hidden items and node_modules:
|
|
3981
|
+
${lines.join(`
|
|
3982
|
+
`)}`;
|
|
3983
|
+
}
|
|
3984
|
+
const content = await readFile(absPath, "utf-8");
|
|
3985
|
+
const formatted = formatFileContent(content, cmd.view_range);
|
|
3986
|
+
return `Here's the content of ${cmd.path} with line numbers:
|
|
3987
|
+
${formatted}`;
|
|
3988
|
+
}
|
|
3989
|
+
async function handleCreate(cmd) {
|
|
3990
|
+
const absPath = resolvePath2(cmd.path);
|
|
3991
|
+
if (existsSync5(absPath)) {
|
|
3992
|
+
return `Error: File ${cmd.path} already exists`;
|
|
3993
|
+
}
|
|
3994
|
+
const parentDir = resolve2(absPath, "..");
|
|
3995
|
+
await mkdir2(parentDir, { recursive: true });
|
|
3996
|
+
await writeFile(absPath, cmd.file_text, "utf-8");
|
|
3997
|
+
return `File created successfully at: ${cmd.path}`;
|
|
3998
|
+
}
|
|
3999
|
+
async function handleStrReplace(cmd) {
|
|
4000
|
+
const absPath = resolvePath2(cmd.path);
|
|
4001
|
+
if (!existsSync5(absPath)) {
|
|
4002
|
+
return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
|
|
4003
|
+
}
|
|
4004
|
+
const s = await stat(absPath);
|
|
4005
|
+
if (s.isDirectory()) {
|
|
4006
|
+
return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
|
|
4007
|
+
}
|
|
4008
|
+
const content = await readFile(absPath, "utf-8");
|
|
4009
|
+
const lines = content.split(`
|
|
4010
|
+
`);
|
|
4011
|
+
const matchingLines = [];
|
|
4012
|
+
let searchPos = 0;
|
|
4013
|
+
let occurrences = 0;
|
|
4014
|
+
while (true) {
|
|
4015
|
+
const idx = content.indexOf(cmd.old_str, searchPos);
|
|
4016
|
+
if (idx === -1)
|
|
4017
|
+
break;
|
|
4018
|
+
occurrences++;
|
|
4019
|
+
const lineNum = content.substring(0, idx).split(`
|
|
4020
|
+
`).length;
|
|
4021
|
+
matchingLines.push(lineNum);
|
|
4022
|
+
searchPos = idx + cmd.old_str.length;
|
|
4023
|
+
}
|
|
4024
|
+
if (occurrences === 0) {
|
|
4025
|
+
return `No replacement was performed, old_str \`${cmd.old_str}\` did not appear verbatim in ${cmd.path}.`;
|
|
4026
|
+
}
|
|
4027
|
+
if (occurrences > 1) {
|
|
4028
|
+
return `No replacement was performed. Multiple occurrences of old_str \`${cmd.old_str}\` in lines: ${matchingLines.join(", ")}. Please ensure it is unique`;
|
|
4029
|
+
}
|
|
4030
|
+
const newContent = content.replace(cmd.old_str, cmd.new_str);
|
|
4031
|
+
await writeFile(absPath, newContent, "utf-8");
|
|
4032
|
+
const newLines = newContent.split(`
|
|
4033
|
+
`);
|
|
4034
|
+
const replaceLine = matchingLines[0];
|
|
4035
|
+
const snippetStart = Math.max(0, replaceLine - 3);
|
|
4036
|
+
const snippetEnd = Math.min(newLines.length, replaceLine + 3);
|
|
4037
|
+
const snippet = newLines.slice(snippetStart, snippetEnd).map((line, i) => `${String(snippetStart + i + 1).padStart(6, " ")} ${line}`).join(`
|
|
4038
|
+
`);
|
|
4039
|
+
return `The memory file has been edited.
|
|
4040
|
+
${snippet}`;
|
|
4041
|
+
}
|
|
4042
|
+
async function handleInsert(cmd) {
|
|
4043
|
+
const absPath = resolvePath2(cmd.path);
|
|
4044
|
+
if (!existsSync5(absPath)) {
|
|
4045
|
+
return `Error: The path ${cmd.path} does not exist`;
|
|
4046
|
+
}
|
|
4047
|
+
const s = await stat(absPath);
|
|
4048
|
+
if (s.isDirectory()) {
|
|
4049
|
+
return `Error: The path ${cmd.path} does not exist`;
|
|
4050
|
+
}
|
|
4051
|
+
const content = await readFile(absPath, "utf-8");
|
|
4052
|
+
const lines = content.split(`
|
|
4053
|
+
`);
|
|
4054
|
+
if (cmd.insert_line < 0 || cmd.insert_line > lines.length) {
|
|
4055
|
+
return `Error: Invalid \`insert_line\` parameter: ${cmd.insert_line}. It should be within the range of lines of the file: [0, ${lines.length}]`;
|
|
4056
|
+
}
|
|
4057
|
+
const insertLines = cmd.insert_text.split(`
|
|
4058
|
+
`);
|
|
4059
|
+
lines.splice(cmd.insert_line, 0, ...insertLines);
|
|
4060
|
+
await writeFile(absPath, lines.join(`
|
|
4061
|
+
`), "utf-8");
|
|
4062
|
+
return `The file ${cmd.path} has been edited.`;
|
|
4063
|
+
}
|
|
4064
|
+
async function handleDelete(cmd) {
|
|
4065
|
+
const absPath = resolvePath2(cmd.path);
|
|
4066
|
+
if (!existsSync5(absPath)) {
|
|
4067
|
+
return `Error: The path ${cmd.path} does not exist`;
|
|
4068
|
+
}
|
|
4069
|
+
await rm(absPath, { recursive: true, force: true });
|
|
4070
|
+
return `Successfully deleted ${cmd.path}`;
|
|
4071
|
+
}
|
|
4072
|
+
async function handleRename(cmd) {
|
|
4073
|
+
const oldAbs = resolvePath2(cmd.old_path);
|
|
4074
|
+
const newAbs = resolvePath2(cmd.new_path);
|
|
4075
|
+
if (!existsSync5(oldAbs)) {
|
|
4076
|
+
return `Error: The path ${cmd.old_path} does not exist`;
|
|
4077
|
+
}
|
|
4078
|
+
if (existsSync5(newAbs)) {
|
|
4079
|
+
return `Error: The destination ${cmd.new_path} already exists`;
|
|
4080
|
+
}
|
|
4081
|
+
const parentDir = resolve2(newAbs, "..");
|
|
4082
|
+
await mkdir2(parentDir, { recursive: true });
|
|
4083
|
+
await rename(oldAbs, newAbs);
|
|
4084
|
+
return `Successfully renamed ${cmd.old_path} to ${cmd.new_path}`;
|
|
4085
|
+
}
|
|
4086
|
+
async function execute(cmd) {
|
|
4087
|
+
if (!existsSync5(absMemoryDir)) {
|
|
4088
|
+
await mkdir2(absMemoryDir, { recursive: true });
|
|
4089
|
+
}
|
|
4090
|
+
switch (cmd.command) {
|
|
4091
|
+
case "view":
|
|
4092
|
+
return handleView(cmd);
|
|
4093
|
+
case "create":
|
|
4094
|
+
return handleCreate(cmd);
|
|
4095
|
+
case "str_replace":
|
|
4096
|
+
return handleStrReplace(cmd);
|
|
4097
|
+
case "insert":
|
|
4098
|
+
return handleInsert(cmd);
|
|
4099
|
+
case "delete":
|
|
4100
|
+
return handleDelete(cmd);
|
|
4101
|
+
case "rename":
|
|
4102
|
+
return handleRename(cmd);
|
|
4103
|
+
default:
|
|
4104
|
+
return `Error: Unknown command: ${cmd.command}`;
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
return { execute, resolvePath: resolvePath2, toLogicalPath };
|
|
4108
|
+
}
|
|
4109
|
+
// src/memory/index.ts
|
|
4110
|
+
function createNativeMemoryTool(config) {
|
|
4111
|
+
const handler = createMemoryHandler(config.path);
|
|
4112
|
+
return {
|
|
4113
|
+
definition: { type: "memory_20250818", name: "memory" },
|
|
4114
|
+
execute: (cmd) => handler.execute(cmd)
|
|
4115
|
+
};
|
|
4116
|
+
}
|
|
4117
|
+
function createMemoryTool(config) {
|
|
4118
|
+
const handler = createMemoryHandler(config.path);
|
|
4119
|
+
return {
|
|
4120
|
+
name: "memory",
|
|
4121
|
+
description: `Manage persistent memory files. Supports 6 commands:
|
|
4122
|
+
` + `- view: Show directory listing or file contents (path, optional view_range)
|
|
4123
|
+
` + `- create: Create a new file (path, file_text)
|
|
4124
|
+
` + `- str_replace: Replace text in a file (path, old_str, new_str)
|
|
4125
|
+
` + `- insert: Insert text at a line number (path, insert_line, insert_text)
|
|
4126
|
+
` + `- delete: Delete a file or directory (path)
|
|
4127
|
+
` + `- rename: Rename/move a file or directory (old_path, new_path)
|
|
4128
|
+
|
|
4129
|
+
` + `All paths should start with /memories/. Example: /memories/notes.txt
|
|
4130
|
+
|
|
4131
|
+
` + "IMPORTANT: Always view your memory directory before starting any task.",
|
|
4132
|
+
inputSchema: {
|
|
4133
|
+
type: "object",
|
|
4134
|
+
properties: {
|
|
4135
|
+
command: {
|
|
4136
|
+
type: "string",
|
|
4137
|
+
enum: ["view", "create", "str_replace", "insert", "delete", "rename"],
|
|
4138
|
+
description: "The memory operation to perform"
|
|
4139
|
+
},
|
|
4140
|
+
path: {
|
|
4141
|
+
type: "string",
|
|
4142
|
+
description: "Path to the file or directory (starts with /memories/)"
|
|
4143
|
+
},
|
|
4144
|
+
file_text: {
|
|
4145
|
+
type: "string",
|
|
4146
|
+
description: "Content for the 'create' command"
|
|
4147
|
+
},
|
|
4148
|
+
old_str: {
|
|
4149
|
+
type: "string",
|
|
4150
|
+
description: "Text to find for 'str_replace' command"
|
|
4151
|
+
},
|
|
4152
|
+
new_str: {
|
|
4153
|
+
type: "string",
|
|
4154
|
+
description: "Replacement text for 'str_replace' command"
|
|
4155
|
+
},
|
|
4156
|
+
insert_line: {
|
|
4157
|
+
type: "number",
|
|
4158
|
+
description: "Line number for 'insert' command"
|
|
4159
|
+
},
|
|
4160
|
+
insert_text: {
|
|
4161
|
+
type: "string",
|
|
4162
|
+
description: "Text to insert for 'insert' command"
|
|
4163
|
+
},
|
|
4164
|
+
old_path: {
|
|
4165
|
+
type: "string",
|
|
4166
|
+
description: "Source path for 'rename' command"
|
|
4167
|
+
},
|
|
4168
|
+
new_path: {
|
|
4169
|
+
type: "string",
|
|
4170
|
+
description: "Destination path for 'rename' command"
|
|
4171
|
+
},
|
|
4172
|
+
view_range: {
|
|
4173
|
+
type: "array",
|
|
4174
|
+
items: { type: "number" },
|
|
4175
|
+
description: "Optional [start, end] line range for 'view' command"
|
|
4176
|
+
}
|
|
4177
|
+
},
|
|
4178
|
+
required: ["command"]
|
|
4179
|
+
},
|
|
4180
|
+
async execute(input) {
|
|
4181
|
+
try {
|
|
4182
|
+
const cmd = input;
|
|
4183
|
+
const result = await handler.execute(cmd);
|
|
4184
|
+
const isError = result.startsWith("Error:");
|
|
4185
|
+
return { content: result, isError };
|
|
4186
|
+
} catch (err) {
|
|
4187
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4188
|
+
return { content: `Error: ${message}`, isError: true };
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4193
|
+
|
|
3589
4194
|
// src/api.ts
|
|
3590
4195
|
var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
|
|
3591
4196
|
var DEFAULT_MAX_TURNS = 10;
|
|
@@ -3600,10 +4205,25 @@ function query(params) {
|
|
|
3600
4205
|
const model = options.model ?? DEFAULT_MODEL;
|
|
3601
4206
|
const toolNames = resolveToolNames(options.tools);
|
|
3602
4207
|
const registry = buildToolRegistry(toolNames, undefined, options.disallowedTools);
|
|
4208
|
+
let skills = [];
|
|
4209
|
+
{
|
|
4210
|
+
const skillsResult = loadSkills({
|
|
4211
|
+
cwd: options.cwd,
|
|
4212
|
+
skillPaths: options.skillPaths,
|
|
4213
|
+
includeDefaults: options.includeDefaultSkills !== false
|
|
4214
|
+
});
|
|
4215
|
+
skills = skillsResult.skills;
|
|
4216
|
+
if (options.debug && skillsResult.diagnostics.length > 0) {
|
|
4217
|
+
for (const d of skillsResult.diagnostics) {
|
|
4218
|
+
console.warn(`[skills] ${d.type}: ${d.message} (${d.path})`);
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
3603
4222
|
const systemPrompt = options.systemPrompt ?? buildSystemPrompt({
|
|
3604
4223
|
tools: registry.list(),
|
|
3605
4224
|
cwd: options.cwd,
|
|
3606
|
-
customPrompt: options.appendSystemPrompt
|
|
4225
|
+
customPrompt: options.appendSystemPrompt,
|
|
4226
|
+
skills
|
|
3607
4227
|
});
|
|
3608
4228
|
let settingsManager;
|
|
3609
4229
|
let mergedPermissions = options.permissions;
|
|
@@ -3682,6 +4302,15 @@ function query(params) {
|
|
|
3682
4302
|
registry.register(createTaskOutputTool(taskManager));
|
|
3683
4303
|
registry.register(createTaskStopTool(taskManager));
|
|
3684
4304
|
}
|
|
4305
|
+
let nativeMemoryTool;
|
|
4306
|
+
if (options.memoryPath) {
|
|
4307
|
+
const memoryConfig = { path: options.memoryPath };
|
|
4308
|
+
if (providerName === "anthropic") {
|
|
4309
|
+
nativeMemoryTool = createNativeMemoryTool(memoryConfig);
|
|
4310
|
+
} else {
|
|
4311
|
+
registry.register(createMemoryTool(memoryConfig));
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
3685
4314
|
const generator = agentLoop(prompt, {
|
|
3686
4315
|
provider,
|
|
3687
4316
|
model,
|
|
@@ -3699,7 +4328,8 @@ function query(params) {
|
|
|
3699
4328
|
hooks: hookManager,
|
|
3700
4329
|
mcpClient,
|
|
3701
4330
|
previousMessages,
|
|
3702
|
-
sessionLogger
|
|
4331
|
+
sessionLogger,
|
|
4332
|
+
nativeMemoryTool
|
|
3703
4333
|
});
|
|
3704
4334
|
return createQuery(generator, abortController);
|
|
3705
4335
|
}
|
|
@@ -3729,10 +4359,18 @@ function createMcpServer(opts) {
|
|
|
3729
4359
|
};
|
|
3730
4360
|
}
|
|
3731
4361
|
export {
|
|
4362
|
+
stripFrontmatter,
|
|
3732
4363
|
registerProvider,
|
|
3733
4364
|
query,
|
|
4365
|
+
parseFrontmatter,
|
|
3734
4366
|
tool as mcpTool,
|
|
4367
|
+
loadSkillsFromDir,
|
|
4368
|
+
loadSkills,
|
|
3735
4369
|
getProvider,
|
|
4370
|
+
formatSkillsForPrompt,
|
|
4371
|
+
createNativeMemoryTool,
|
|
4372
|
+
createMemoryTool,
|
|
4373
|
+
createMemoryHandler,
|
|
3736
4374
|
createMcpServer,
|
|
3737
4375
|
WriteTool,
|
|
3738
4376
|
ToolRegistry,
|