deepagents 1.7.4 → 1.7.6

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