deepagents 1.7.4 → 1.7.5

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/index.cjs CHANGED
@@ -1856,6 +1856,7 @@ function createMemoryMiddleware(options) {
1856
1856
  const MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024;
1857
1857
  const MAX_SKILL_NAME_LENGTH = 64;
1858
1858
  const MAX_SKILL_DESCRIPTION_LENGTH = 1024;
1859
+ const MAX_SKILL_COMPATIBILITY_LENGTH = 500;
1859
1860
  /**
1860
1861
  * Zod schema for a single skill metadata entry.
1861
1862
  */
@@ -1943,6 +1944,22 @@ Remember: Skills are tools to make you more capable and consistent. When in doub
1943
1944
  `;
1944
1945
  /**
1945
1946
  * Validate skill name per Agent Skills specification.
1947
+ *
1948
+ * Constraints per Agent Skills specification:
1949
+ *
1950
+ * - 1-64 characters
1951
+ * - Unicode lowercase alphanumeric and hyphens only (`a-z` and `-`).
1952
+ * - Must not start or end with `-`
1953
+ * - Must not contain consecutive `--`
1954
+ * - Must match the parent directory name containing the `SKILL.md` file
1955
+ *
1956
+ * Unicode lowercase alphanumeric means any lowercase or decimal digit, which
1957
+ * covers accented Latin characters (e.g., `'café'`, `'über-tool'`) and other
1958
+ * scripts.
1959
+ *
1960
+ * @param name - The skill name from YAML frontmatter
1961
+ * @param directoryName - The parent directory name
1962
+ * @returns `{ valid, error }` tuple. Error is empty string if valid.
1946
1963
  */
1947
1964
  function validateSkillName$1(name, directoryName) {
1948
1965
  if (!name) return {
@@ -1953,10 +1970,18 @@ function validateSkillName$1(name, directoryName) {
1953
1970
  valid: false,
1954
1971
  error: "name exceeds 64 characters"
1955
1972
  };
1956
- if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) return {
1973
+ if (name.startsWith("-") || name.endsWith("-") || name.includes("--")) return {
1957
1974
  valid: false,
1958
1975
  error: "name must be lowercase alphanumeric with single hyphens only"
1959
1976
  };
1977
+ for (const c of name) {
1978
+ if (c === "-") continue;
1979
+ if (/\p{Ll}/u.test(c) || /\p{Nd}/u.test(c)) continue;
1980
+ return {
1981
+ valid: false,
1982
+ error: "name must be lowercase alphanumeric with single hyphens only"
1983
+ };
1984
+ }
1960
1985
  if (name !== directoryName) return {
1961
1986
  valid: false,
1962
1987
  error: `name '${name}' must match directory name '${directoryName}'`
@@ -1967,7 +1992,52 @@ function validateSkillName$1(name, directoryName) {
1967
1992
  };
1968
1993
  }
1969
1994
  /**
1970
- * Parse YAML frontmatter from SKILL.md content.
1995
+ * Validate and normalize the metadata field from YAML frontmatter.
1996
+ *
1997
+ * YAML parsing can return any type for the `metadata` key. This ensures the
1998
+ * value in {@link SkillMetadata} is always a `Record<string, string>` by
1999
+ * coercing via `String()` and rejecting non-object inputs.
2000
+ *
2001
+ * @param raw - Raw value from `frontmatterData.metadata`.
2002
+ * @param skillPath - Path to the `SKILL.md` file (for warning messages).
2003
+ * @returns A validated `Record<string, string>`.
2004
+ */
2005
+ function validateMetadata(raw, skillPath) {
2006
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
2007
+ if (raw) console.warn(`Ignoring non-object metadata in ${skillPath} (got ${typeof raw})`);
2008
+ return {};
2009
+ }
2010
+ const result = {};
2011
+ for (const [k, v] of Object.entries(raw)) result[String(k)] = String(v);
2012
+ return result;
2013
+ }
2014
+ /**
2015
+ * Build a parenthetical annotation string from optional skill fields.
2016
+ *
2017
+ * Combines license and compatibility into a comma-separated string for
2018
+ * display in the system prompt skill listing.
2019
+ *
2020
+ * @param skill - Skill metadata to extract annotations from.
2021
+ * @returns Annotation string like `'License: MIT, Compatibility: Python 3.10+'`,
2022
+ * or empty string if neither field is set.
2023
+ */
2024
+ function formatSkillAnnotations(skill) {
2025
+ const parts = [];
2026
+ if (skill.license) parts.push(`License: ${skill.license}`);
2027
+ if (skill.compatibility) parts.push(`Compatibility: ${skill.compatibility}`);
2028
+ return parts.join(", ");
2029
+ }
2030
+ /**
2031
+ * Parse YAML frontmatter from `SKILL.md` content.
2032
+ *
2033
+ * Extracts metadata per Agent Skills specification from YAML frontmatter
2034
+ * delimited by `---` markers at the start of the content.
2035
+ *
2036
+ * @param content - Content of the `SKILL.md` file
2037
+ * @param skillPath - Path to the `SKILL.md` file (for error messages and metadata)
2038
+ * @param directoryName - Name of the parent directory containing the skill
2039
+ * @returns `SkillMetadata` if parsing succeeds, `null` if parsing fails or
2040
+ * validation errors occur
1971
2041
  */
1972
2042
  function parseSkillMetadataFromContent(content, skillPath, directoryName) {
1973
2043
  if (content.length > MAX_SKILL_FILE_SIZE) {
@@ -1991,28 +2061,36 @@ function parseSkillMetadataFromContent(content, skillPath, directoryName) {
1991
2061
  console.warn(`Skipping ${skillPath}: frontmatter is not a mapping`);
1992
2062
  return null;
1993
2063
  }
1994
- const name = frontmatterData.name;
1995
- const description = frontmatterData.description;
2064
+ const name = String(frontmatterData.name ?? "").trim();
2065
+ const description = String(frontmatterData.description ?? "").trim();
1996
2066
  if (!name || !description) {
1997
2067
  console.warn(`Skipping ${skillPath}: missing required 'name' or 'description'`);
1998
2068
  return null;
1999
2069
  }
2000
- const validation = validateSkillName$1(String(name), directoryName);
2070
+ const validation = validateSkillName$1(name, directoryName);
2001
2071
  if (!validation.valid) console.warn(`Skill '${name}' in ${skillPath} does not follow Agent Skills specification: ${validation.error}. Consider renaming for spec compliance.`);
2002
- let descriptionStr = String(description).trim();
2072
+ let descriptionStr = description;
2003
2073
  if (descriptionStr.length > MAX_SKILL_DESCRIPTION_LENGTH) {
2004
2074
  console.warn(`Description exceeds ${MAX_SKILL_DESCRIPTION_LENGTH} characters in ${skillPath}, truncating`);
2005
2075
  descriptionStr = descriptionStr.slice(0, MAX_SKILL_DESCRIPTION_LENGTH);
2006
2076
  }
2007
- const allowedToolsStr = frontmatterData["allowed-tools"];
2008
- const allowedTools = allowedToolsStr ? allowedToolsStr.split(" ") : [];
2077
+ const rawTools = frontmatterData["allowed-tools"];
2078
+ let allowedTools;
2079
+ if (rawTools) if (Array.isArray(rawTools)) allowedTools = rawTools.map((t) => String(t).trim()).filter(Boolean);
2080
+ else allowedTools = String(rawTools).split(/\s+/).filter(Boolean);
2081
+ else allowedTools = [];
2082
+ let compatibilityStr = String(frontmatterData.compatibility ?? "").trim() || null;
2083
+ if (compatibilityStr && compatibilityStr.length > MAX_SKILL_COMPATIBILITY_LENGTH) {
2084
+ console.warn(`Compatibility exceeds ${MAX_SKILL_COMPATIBILITY_LENGTH} characters in ${skillPath}, truncating`);
2085
+ compatibilityStr = compatibilityStr.slice(0, MAX_SKILL_COMPATIBILITY_LENGTH);
2086
+ }
2009
2087
  return {
2010
- name: String(name),
2088
+ name,
2011
2089
  description: descriptionStr,
2012
2090
  path: skillPath,
2013
- metadata: frontmatterData.metadata || {},
2014
- license: typeof frontmatterData.license === "string" ? frontmatterData.license.trim() || null : null,
2015
- compatibility: typeof frontmatterData.compatibility === "string" ? frontmatterData.compatibility.trim() || null : null,
2091
+ metadata: validateMetadata(frontmatterData.metadata ?? {}, skillPath),
2092
+ license: String(frontmatterData.license ?? "").trim() || null,
2093
+ compatibility: compatibilityStr,
2016
2094
  allowedTools
2017
2095
  };
2018
2096
  }
@@ -2076,7 +2154,10 @@ function formatSkillsList(skills, sources) {
2076
2154
  if (skills.length === 0) return `(No skills available yet. You can create skills in ${sources.map((s) => `\`${s}\``).join(" or ")})`;
2077
2155
  const lines = [];
2078
2156
  for (const skill of skills) {
2079
- lines.push(`- **${skill.name}**: ${skill.description}`);
2157
+ const annotations = formatSkillAnnotations(skill);
2158
+ let descLine = `- **${skill.name}**: ${skill.description}`;
2159
+ if (annotations) descLine += ` (${annotations})`;
2160
+ lines.push(descLine);
2080
2161
  if (skill.allowedTools && skill.allowedTools.length > 0) lines.push(` → Allowed tools: ${skill.allowedTools.join(", ")}`);
2081
2162
  lines.push(` → Read \`${skill.path}\` for full instructions`);
2082
2163
  }
@@ -2197,9 +2278,25 @@ function createSkillsMiddleware(options) {
2197
2278
  * from `langchain` directly.
2198
2279
  */
2199
2280
  /**
2281
+ * Zod schema for a summarization event that tracks what was summarized and
2282
+ * where the cutoff is.
2283
+ *
2284
+ * Instead of rewriting LangGraph state with `RemoveMessage(REMOVE_ALL_MESSAGES)`,
2285
+ * the middleware stores this event and uses it to reconstruct the effective message
2286
+ * list on subsequent calls.
2287
+ */
2288
+ const SummarizationEventSchema = zod.z.object({
2289
+ cutoffIndex: zod.z.number(),
2290
+ summaryMessage: zod.z.instanceof(langchain.HumanMessage),
2291
+ filePath: zod.z.string().nullable()
2292
+ });
2293
+ /**
2200
2294
  * State schema for summarization middleware.
2201
2295
  */
2202
- const SummarizationStateSchema = zod.z.object({ _summarizationSessionId: zod.z.string().optional() });
2296
+ const SummarizationStateSchema = zod.z.object({
2297
+ _summarizationSessionId: zod.z.string().optional(),
2298
+ _summarizationEvent: SummarizationEventSchema.optional()
2299
+ });
2203
2300
 
2204
2301
  //#endregion
2205
2302
  //#region src/backends/store.ts