@agentforge/patterns 0.16.88 → 0.16.90

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
@@ -1140,32 +1140,17 @@ var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planne
1140
1140
  var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
1141
1141
  var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
1142
1142
 
1143
- // src/plan-execute/serialization.ts
1143
+ // src/plan-execute/model-response.ts
1144
1144
  function hasTextContentPart(value) {
1145
1145
  return typeof value === "object" && value !== null && "text" in value && typeof value.text === "string";
1146
1146
  }
1147
- function stringifyWithFallback(value, fallbackLabel) {
1147
+ function stringifyModelContent(value) {
1148
1148
  try {
1149
1149
  const serialized = JSON.stringify(value);
1150
- if (serialized === void 0) {
1151
- return "undefined";
1152
- }
1153
- return serialized;
1154
- } catch (error) {
1155
- const reason = error instanceof Error ? error.message : String(error);
1156
- return `[${fallbackLabel}: ${reason}]`;
1157
- }
1158
- }
1159
- function toJsonSafeValue(value) {
1160
- try {
1161
- const serialized = JSON.stringify(value);
1162
- if (serialized === void 0) {
1163
- return void 0;
1164
- }
1165
- return JSON.parse(serialized);
1150
+ return serialized === void 0 ? "undefined" : serialized;
1166
1151
  } catch (error) {
1167
1152
  const reason = error instanceof Error ? error.message : String(error);
1168
- return `[Unserializable step result: ${reason}]`;
1153
+ return `[Unserializable model content: ${reason}]`;
1169
1154
  }
1170
1155
  }
1171
1156
  function normalizeModelContent(content) {
@@ -1183,10 +1168,15 @@ function normalizeModelContent(content) {
1183
1168
  return textParts.join("\n");
1184
1169
  }
1185
1170
  }
1186
- return stringifyWithFallback(content, "Unserializable model content");
1171
+ return stringifyModelContent(content);
1187
1172
  }
1188
- function serializePlanExecuteResult(result) {
1189
- return stringifyWithFallback(result, "Unserializable step result");
1173
+ function parseModelResponse(content, context) {
1174
+ const normalized = normalizeModelContent(content);
1175
+ try {
1176
+ return JSON.parse(normalized);
1177
+ } catch (parseError) {
1178
+ throw new Error(`Failed to parse ${context} from LLM response: ${parseError}`);
1179
+ }
1190
1180
  }
1191
1181
 
1192
1182
  // src/plan-execute/planner-node.ts
@@ -1215,19 +1205,13 @@ function createPlannerNode(config) {
1215
1205
  new import_messages2.HumanMessage(userPrompt)
1216
1206
  ];
1217
1207
  const response = await model.invoke(messages);
1218
- const content = normalizeModelContent(response.content);
1219
- let plan;
1220
- try {
1221
- const parsed = JSON.parse(content);
1222
- plan = {
1223
- steps: parsed.steps.slice(0, maxSteps),
1224
- goal: parsed.goal || state.input || "",
1225
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1226
- confidence: parsed.confidence
1227
- };
1228
- } catch (parseError) {
1229
- throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
1230
- }
1208
+ const parsed = parseModelResponse(response.content, "plan");
1209
+ const plan = {
1210
+ steps: parsed.steps.slice(0, maxSteps),
1211
+ goal: parsed.goal || state.input || "",
1212
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1213
+ confidence: parsed.confidence
1214
+ };
1231
1215
  plannerLogger.info("Plan created", {
1232
1216
  stepCount: plan.steps.length,
1233
1217
  goal: plan.goal.substring(0, 100),
@@ -1418,6 +1402,37 @@ function createExecutorNode(config) {
1418
1402
 
1419
1403
  // src/plan-execute/replanner-node.ts
1420
1404
  var import_messages3 = require("@langchain/core/messages");
1405
+
1406
+ // src/plan-execute/serialization.ts
1407
+ function stringifyWithFallback(value, fallbackLabel) {
1408
+ try {
1409
+ const serialized = JSON.stringify(value);
1410
+ if (serialized === void 0) {
1411
+ return "undefined";
1412
+ }
1413
+ return serialized;
1414
+ } catch (error) {
1415
+ const reason = error instanceof Error ? error.message : String(error);
1416
+ return `[${fallbackLabel}: ${reason}]`;
1417
+ }
1418
+ }
1419
+ function toJsonSafeValue(value) {
1420
+ try {
1421
+ const serialized = JSON.stringify(value);
1422
+ if (serialized === void 0) {
1423
+ return void 0;
1424
+ }
1425
+ return JSON.parse(serialized);
1426
+ } catch (error) {
1427
+ const reason = error instanceof Error ? error.message : String(error);
1428
+ return `[Unserializable step result: ${reason}]`;
1429
+ }
1430
+ }
1431
+ function serializePlanExecuteResult(result) {
1432
+ return stringifyWithFallback(result, "Unserializable step result");
1433
+ }
1434
+
1435
+ // src/plan-execute/replanner-node.ts
1421
1436
  function createReplannerNode(config) {
1422
1437
  const {
1423
1438
  model,
@@ -1455,13 +1470,7 @@ function createReplannerNode(config) {
1455
1470
  new import_messages3.HumanMessage(userPrompt)
1456
1471
  ];
1457
1472
  const response = await model.invoke(messages);
1458
- const content = normalizeModelContent(response.content);
1459
- let decision;
1460
- try {
1461
- decision = JSON.parse(content);
1462
- } catch (parseError) {
1463
- throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
1464
- }
1473
+ const decision = parseModelResponse(response.content, "replan decision");
1465
1474
  if (decision.shouldReplan) {
1466
1475
  replannerLogger.info("Replanning triggered", {
1467
1476
  reason: decision.reason,
package/dist/index.d.cts CHANGED
@@ -2973,249 +2973,68 @@ type MultiAgentStateType = {
2973
2973
  error?: string;
2974
2974
  };
2975
2975
 
2976
- /**
2977
- * Type Definitions for Multi-Agent Coordination Pattern
2978
- *
2979
- * This module defines TypeScript types for the Multi-Agent pattern.
2980
- *
2981
- * @module patterns/multi-agent/types
2982
- */
2983
-
2984
- type WorkerTool = Tool<never, unknown>;
2985
- /**
2986
- * Runtime config passed to worker execution functions.
2987
- *
2988
- * Includes LangGraph's RunnableConfig while remaining open to caller-defined
2989
- * keys used by integrations.
2990
- */
2991
- type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
2992
- /**
2993
- * Configuration for the supervisor node
2994
- */
2995
2976
  interface SupervisorConfig {
2996
- /**
2997
- * Language model for routing decisions (used for LLM-based routing)
2998
- */
2999
2977
  model?: BaseChatModel;
3000
- /**
3001
- * Routing strategy to use
3002
- */
3003
2978
  strategy: RoutingStrategy;
3004
- /**
3005
- * System prompt for the supervisor (LLM-based routing only)
3006
- */
3007
2979
  systemPrompt?: string;
3008
- /**
3009
- * Custom routing function (for rule-based routing)
3010
- */
3011
2980
  routingFn?: (state: MultiAgentStateType) => Promise<RoutingDecision>;
3012
- /**
3013
- * Whether to include verbose logging
3014
- */
3015
2981
  verbose?: boolean;
3016
- /**
3017
- * Maximum number of routing iterations
3018
- */
3019
2982
  maxIterations?: number;
3020
2983
  /**
3021
- * Maximum number of tool call retries before requiring routing decision
3022
- *
3023
- * Prevents infinite loops where the supervisor keeps calling tools without making a routing decision.
3024
- *
2984
+ * Maximum number of tool call retries before requiring a routing decision.
2985
+ * Prevents infinite loops where the supervisor keeps calling tools without
2986
+ * making a routing decision.
3025
2987
  * @default 3
3026
2988
  */
3027
2989
  maxToolRetries?: number;
3028
2990
  }
3029
- /**
3030
- * Configuration for a worker agent node
3031
- */
2991
+
2992
+ type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
2993
+ type WorkerTool = Tool<never, unknown>;
3032
2994
  interface WorkerConfig {
3033
- /**
3034
- * Unique identifier for this worker
3035
- */
3036
2995
  id: string;
3037
- /**
3038
- * Worker capabilities
3039
- */
3040
2996
  capabilities: WorkerCapabilities;
3041
- /**
3042
- * Language model for the worker
3043
- */
3044
2997
  model?: BaseChatModel;
3045
- /**
3046
- * Available tools for this worker
3047
- */
3048
2998
  tools?: WorkerTool[];
3049
- /**
3050
- * System prompt for the worker
3051
- */
3052
2999
  systemPrompt?: string;
3053
- /**
3054
- * Whether to include verbose logging
3055
- */
3056
3000
  verbose?: boolean;
3057
3001
  /**
3058
- * Custom execution function
3059
- *
3060
- * If provided, this function will be used to execute tasks for this worker.
3061
- * Takes precedence over the `agent` property.
3062
- *
3063
- * The config parameter contains LangGraph runtime configuration including
3064
- * thread_id for checkpointing, which is required for interrupt functionality.
3002
+ * Custom execution function. When provided, this takes precedence over
3003
+ * the `agent` property.
3065
3004
  */
3066
3005
  executeFn?: (state: MultiAgentStateType, config?: WorkerExecutionConfig) => Promise<Partial<MultiAgentStateType>>;
3067
3006
  /**
3068
- * ReAct agent instance
3069
- *
3070
- * If provided, the Multi-Agent pattern will automatically wrap this ReAct agent
3071
- * to work as a worker. The agent should be a compiled LangGraph StateGraph
3072
- * (e.g., created with `createReActAgent()`).
3073
- *
3074
- * Note: `executeFn` takes precedence over `agent` if both are provided.
3075
- *
3076
- * @example
3077
- * ```typescript
3078
- * const hrAgent = createReActAgent({ model, tools, systemPrompt });
3079
- *
3080
- * const system = createMultiAgentSystem({
3081
- * workers: [{
3082
- * id: 'hr',
3083
- * capabilities: { skills: ['hr'], ... },
3084
- * agent: hrAgent, // Automatically wrapped!
3085
- * }]
3086
- * });
3087
- * ```
3007
+ * ReAct agent instance. `executeFn` takes precedence when both are set.
3088
3008
  */
3089
3009
  agent?: CompiledStateGraph<unknown, unknown>;
3090
3010
  }
3091
- /**
3092
- * Configuration for the aggregator node
3093
- */
3011
+
3094
3012
  interface AggregatorConfig {
3095
- /**
3096
- * Language model for aggregation (optional)
3097
- */
3098
3013
  model?: BaseChatModel;
3099
- /**
3100
- * System prompt for aggregation
3101
- */
3102
3014
  systemPrompt?: string;
3103
- /**
3104
- * Custom aggregation function
3105
- */
3106
3015
  aggregateFn?: (state: MultiAgentStateType) => Promise<string>;
3107
- /**
3108
- * Whether to include verbose logging
3109
- */
3110
3016
  verbose?: boolean;
3111
3017
  }
3112
- /**
3113
- * Configuration for the multi-agent system
3114
- */
3115
3018
  interface MultiAgentSystemConfig {
3116
- /**
3117
- * Supervisor configuration
3118
- */
3119
3019
  supervisor: SupervisorConfig;
3120
- /**
3121
- * Worker configurations
3122
- */
3123
3020
  workers: WorkerConfig[];
3124
- /**
3125
- * Aggregator configuration (optional)
3126
- */
3127
3021
  aggregator?: AggregatorConfig;
3128
- /**
3129
- * Maximum iterations for the entire system
3130
- */
3131
3022
  maxIterations?: number;
3132
- /**
3133
- * Whether to include verbose logging
3134
- */
3135
3023
  verbose?: boolean;
3136
3024
  /**
3137
- * Optional checkpointer for state persistence
3138
- * Required for human-in-the-loop workflows (askHuman tool), interrupts, and conversation continuity
3139
- *
3140
- * **Worker Checkpoint Namespaces:**
3141
- * When worker agents are configured with `checkpointer: true`, they automatically use
3142
- * separate checkpoint namespaces to enable proper handling of nested graph interrupts.
3143
- *
3144
- * The namespace format is: `{parent_thread_id}:worker:{workerId}`
3145
- *
3146
- * For example, if the parent thread ID is `thread_abc123` and the worker ID is `hr`,
3147
- * the worker's checkpoint namespace will be `thread_abc123:worker:hr`.
3148
- *
3149
- * This allows worker agents to use the `askHuman` tool without causing infinite loops,
3150
- * as each worker's state is saved and resumed independently.
3151
- *
3152
- * @example
3153
- * Basic usage with checkpointer:
3154
- * ```typescript
3155
- * import { MemorySaver } from '@langchain/langgraph';
3156
- *
3157
- * const checkpointer = new MemorySaver();
3158
- * const system = createMultiAgentSystem({
3159
- * supervisor: { strategy: 'skill-based', model },
3160
- * workers: [...],
3161
- * checkpointer
3162
- * });
3163
- * ```
3164
- *
3165
- * @example
3166
- * Worker agents with nested graph interrupts:
3167
- * ```typescript
3168
- * import { MemorySaver } from '@langchain/langgraph';
3169
- * import { createReActAgent } from '@agentforge/patterns';
3170
- * import { createAskHumanTool } from '@agentforge/tools';
3171
- *
3172
- * // Create worker agent with checkpointer: true
3173
- * const hrAgent = createReActAgent({
3174
- * model,
3175
- * tools: [createAskHumanTool(), ...hrTools],
3176
- * checkpointer: true // Use parent's checkpointer with separate namespace
3177
- * });
3178
- *
3179
- * // Create multi-agent system with checkpointer
3180
- * const system = createMultiAgentSystem({
3181
- * supervisor: { strategy: 'skill-based', model },
3182
- * workers: [{
3183
- * id: 'hr',
3184
- * capabilities: { skills: ['hr'], ... },
3185
- * agent: hrAgent
3186
- * }],
3187
- * checkpointer: new MemorySaver()
3188
- * });
3189
- *
3190
- * // When hrAgent calls askHuman, it will use checkpoint namespace:
3191
- * // thread_abc123:worker:hr
3192
- * ```
3025
+ * Optional checkpointer for state persistence and human-in-the-loop flows.
3026
+ * Worker agents use isolated namespaces in the form
3027
+ * `{parent_thread_id}:worker:{workerId}` so nested interrupts can resume
3028
+ * without looping through the parent graph.
3193
3029
  */
3194
3030
  checkpointer?: BaseCheckpointSaver;
3195
3031
  }
3196
- /**
3197
- * Node type for multi-agent graph
3198
- */
3032
+
3199
3033
  type MultiAgentNode = 'supervisor' | 'aggregator' | string;
3200
- /**
3201
- * Route type for multi-agent graph
3202
- */
3203
3034
  type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string | string[];
3204
- /**
3205
- * Router function type
3206
- */
3207
3035
  type MultiAgentRouter = (state: MultiAgentStateType) => MultiAgentRoute;
3208
- /**
3209
- * Routing strategy implementation
3210
- */
3211
3036
  interface RoutingStrategyImpl {
3212
- /**
3213
- * Name of the strategy
3214
- */
3215
3037
  name: RoutingStrategy;
3216
- /**
3217
- * Execute the routing strategy
3218
- */
3219
3038
  route: (state: MultiAgentStateType, config: SupervisorConfig) => Promise<RoutingDecision>;
3220
3039
  }
3221
3040
 
package/dist/index.d.ts CHANGED
@@ -2973,249 +2973,68 @@ type MultiAgentStateType = {
2973
2973
  error?: string;
2974
2974
  };
2975
2975
 
2976
- /**
2977
- * Type Definitions for Multi-Agent Coordination Pattern
2978
- *
2979
- * This module defines TypeScript types for the Multi-Agent pattern.
2980
- *
2981
- * @module patterns/multi-agent/types
2982
- */
2983
-
2984
- type WorkerTool = Tool<never, unknown>;
2985
- /**
2986
- * Runtime config passed to worker execution functions.
2987
- *
2988
- * Includes LangGraph's RunnableConfig while remaining open to caller-defined
2989
- * keys used by integrations.
2990
- */
2991
- type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
2992
- /**
2993
- * Configuration for the supervisor node
2994
- */
2995
2976
  interface SupervisorConfig {
2996
- /**
2997
- * Language model for routing decisions (used for LLM-based routing)
2998
- */
2999
2977
  model?: BaseChatModel;
3000
- /**
3001
- * Routing strategy to use
3002
- */
3003
2978
  strategy: RoutingStrategy;
3004
- /**
3005
- * System prompt for the supervisor (LLM-based routing only)
3006
- */
3007
2979
  systemPrompt?: string;
3008
- /**
3009
- * Custom routing function (for rule-based routing)
3010
- */
3011
2980
  routingFn?: (state: MultiAgentStateType) => Promise<RoutingDecision>;
3012
- /**
3013
- * Whether to include verbose logging
3014
- */
3015
2981
  verbose?: boolean;
3016
- /**
3017
- * Maximum number of routing iterations
3018
- */
3019
2982
  maxIterations?: number;
3020
2983
  /**
3021
- * Maximum number of tool call retries before requiring routing decision
3022
- *
3023
- * Prevents infinite loops where the supervisor keeps calling tools without making a routing decision.
3024
- *
2984
+ * Maximum number of tool call retries before requiring a routing decision.
2985
+ * Prevents infinite loops where the supervisor keeps calling tools without
2986
+ * making a routing decision.
3025
2987
  * @default 3
3026
2988
  */
3027
2989
  maxToolRetries?: number;
3028
2990
  }
3029
- /**
3030
- * Configuration for a worker agent node
3031
- */
2991
+
2992
+ type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
2993
+ type WorkerTool = Tool<never, unknown>;
3032
2994
  interface WorkerConfig {
3033
- /**
3034
- * Unique identifier for this worker
3035
- */
3036
2995
  id: string;
3037
- /**
3038
- * Worker capabilities
3039
- */
3040
2996
  capabilities: WorkerCapabilities;
3041
- /**
3042
- * Language model for the worker
3043
- */
3044
2997
  model?: BaseChatModel;
3045
- /**
3046
- * Available tools for this worker
3047
- */
3048
2998
  tools?: WorkerTool[];
3049
- /**
3050
- * System prompt for the worker
3051
- */
3052
2999
  systemPrompt?: string;
3053
- /**
3054
- * Whether to include verbose logging
3055
- */
3056
3000
  verbose?: boolean;
3057
3001
  /**
3058
- * Custom execution function
3059
- *
3060
- * If provided, this function will be used to execute tasks for this worker.
3061
- * Takes precedence over the `agent` property.
3062
- *
3063
- * The config parameter contains LangGraph runtime configuration including
3064
- * thread_id for checkpointing, which is required for interrupt functionality.
3002
+ * Custom execution function. When provided, this takes precedence over
3003
+ * the `agent` property.
3065
3004
  */
3066
3005
  executeFn?: (state: MultiAgentStateType, config?: WorkerExecutionConfig) => Promise<Partial<MultiAgentStateType>>;
3067
3006
  /**
3068
- * ReAct agent instance
3069
- *
3070
- * If provided, the Multi-Agent pattern will automatically wrap this ReAct agent
3071
- * to work as a worker. The agent should be a compiled LangGraph StateGraph
3072
- * (e.g., created with `createReActAgent()`).
3073
- *
3074
- * Note: `executeFn` takes precedence over `agent` if both are provided.
3075
- *
3076
- * @example
3077
- * ```typescript
3078
- * const hrAgent = createReActAgent({ model, tools, systemPrompt });
3079
- *
3080
- * const system = createMultiAgentSystem({
3081
- * workers: [{
3082
- * id: 'hr',
3083
- * capabilities: { skills: ['hr'], ... },
3084
- * agent: hrAgent, // Automatically wrapped!
3085
- * }]
3086
- * });
3087
- * ```
3007
+ * ReAct agent instance. `executeFn` takes precedence when both are set.
3088
3008
  */
3089
3009
  agent?: CompiledStateGraph<unknown, unknown>;
3090
3010
  }
3091
- /**
3092
- * Configuration for the aggregator node
3093
- */
3011
+
3094
3012
  interface AggregatorConfig {
3095
- /**
3096
- * Language model for aggregation (optional)
3097
- */
3098
3013
  model?: BaseChatModel;
3099
- /**
3100
- * System prompt for aggregation
3101
- */
3102
3014
  systemPrompt?: string;
3103
- /**
3104
- * Custom aggregation function
3105
- */
3106
3015
  aggregateFn?: (state: MultiAgentStateType) => Promise<string>;
3107
- /**
3108
- * Whether to include verbose logging
3109
- */
3110
3016
  verbose?: boolean;
3111
3017
  }
3112
- /**
3113
- * Configuration for the multi-agent system
3114
- */
3115
3018
  interface MultiAgentSystemConfig {
3116
- /**
3117
- * Supervisor configuration
3118
- */
3119
3019
  supervisor: SupervisorConfig;
3120
- /**
3121
- * Worker configurations
3122
- */
3123
3020
  workers: WorkerConfig[];
3124
- /**
3125
- * Aggregator configuration (optional)
3126
- */
3127
3021
  aggregator?: AggregatorConfig;
3128
- /**
3129
- * Maximum iterations for the entire system
3130
- */
3131
3022
  maxIterations?: number;
3132
- /**
3133
- * Whether to include verbose logging
3134
- */
3135
3023
  verbose?: boolean;
3136
3024
  /**
3137
- * Optional checkpointer for state persistence
3138
- * Required for human-in-the-loop workflows (askHuman tool), interrupts, and conversation continuity
3139
- *
3140
- * **Worker Checkpoint Namespaces:**
3141
- * When worker agents are configured with `checkpointer: true`, they automatically use
3142
- * separate checkpoint namespaces to enable proper handling of nested graph interrupts.
3143
- *
3144
- * The namespace format is: `{parent_thread_id}:worker:{workerId}`
3145
- *
3146
- * For example, if the parent thread ID is `thread_abc123` and the worker ID is `hr`,
3147
- * the worker's checkpoint namespace will be `thread_abc123:worker:hr`.
3148
- *
3149
- * This allows worker agents to use the `askHuman` tool without causing infinite loops,
3150
- * as each worker's state is saved and resumed independently.
3151
- *
3152
- * @example
3153
- * Basic usage with checkpointer:
3154
- * ```typescript
3155
- * import { MemorySaver } from '@langchain/langgraph';
3156
- *
3157
- * const checkpointer = new MemorySaver();
3158
- * const system = createMultiAgentSystem({
3159
- * supervisor: { strategy: 'skill-based', model },
3160
- * workers: [...],
3161
- * checkpointer
3162
- * });
3163
- * ```
3164
- *
3165
- * @example
3166
- * Worker agents with nested graph interrupts:
3167
- * ```typescript
3168
- * import { MemorySaver } from '@langchain/langgraph';
3169
- * import { createReActAgent } from '@agentforge/patterns';
3170
- * import { createAskHumanTool } from '@agentforge/tools';
3171
- *
3172
- * // Create worker agent with checkpointer: true
3173
- * const hrAgent = createReActAgent({
3174
- * model,
3175
- * tools: [createAskHumanTool(), ...hrTools],
3176
- * checkpointer: true // Use parent's checkpointer with separate namespace
3177
- * });
3178
- *
3179
- * // Create multi-agent system with checkpointer
3180
- * const system = createMultiAgentSystem({
3181
- * supervisor: { strategy: 'skill-based', model },
3182
- * workers: [{
3183
- * id: 'hr',
3184
- * capabilities: { skills: ['hr'], ... },
3185
- * agent: hrAgent
3186
- * }],
3187
- * checkpointer: new MemorySaver()
3188
- * });
3189
- *
3190
- * // When hrAgent calls askHuman, it will use checkpoint namespace:
3191
- * // thread_abc123:worker:hr
3192
- * ```
3025
+ * Optional checkpointer for state persistence and human-in-the-loop flows.
3026
+ * Worker agents use isolated namespaces in the form
3027
+ * `{parent_thread_id}:worker:{workerId}` so nested interrupts can resume
3028
+ * without looping through the parent graph.
3193
3029
  */
3194
3030
  checkpointer?: BaseCheckpointSaver;
3195
3031
  }
3196
- /**
3197
- * Node type for multi-agent graph
3198
- */
3032
+
3199
3033
  type MultiAgentNode = 'supervisor' | 'aggregator' | string;
3200
- /**
3201
- * Route type for multi-agent graph
3202
- */
3203
3034
  type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string | string[];
3204
- /**
3205
- * Router function type
3206
- */
3207
3035
  type MultiAgentRouter = (state: MultiAgentStateType) => MultiAgentRoute;
3208
- /**
3209
- * Routing strategy implementation
3210
- */
3211
3036
  interface RoutingStrategyImpl {
3212
- /**
3213
- * Name of the strategy
3214
- */
3215
3037
  name: RoutingStrategy;
3216
- /**
3217
- * Execute the routing strategy
3218
- */
3219
3038
  route: (state: MultiAgentStateType, config: SupervisorConfig) => Promise<RoutingDecision>;
3220
3039
  }
3221
3040
 
package/dist/index.js CHANGED
@@ -1042,32 +1042,17 @@ var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planne
1042
1042
  var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
1043
1043
  var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
1044
1044
 
1045
- // src/plan-execute/serialization.ts
1045
+ // src/plan-execute/model-response.ts
1046
1046
  function hasTextContentPart(value) {
1047
1047
  return typeof value === "object" && value !== null && "text" in value && typeof value.text === "string";
1048
1048
  }
1049
- function stringifyWithFallback(value, fallbackLabel) {
1049
+ function stringifyModelContent(value) {
1050
1050
  try {
1051
1051
  const serialized = JSON.stringify(value);
1052
- if (serialized === void 0) {
1053
- return "undefined";
1054
- }
1055
- return serialized;
1056
- } catch (error) {
1057
- const reason = error instanceof Error ? error.message : String(error);
1058
- return `[${fallbackLabel}: ${reason}]`;
1059
- }
1060
- }
1061
- function toJsonSafeValue(value) {
1062
- try {
1063
- const serialized = JSON.stringify(value);
1064
- if (serialized === void 0) {
1065
- return void 0;
1066
- }
1067
- return JSON.parse(serialized);
1052
+ return serialized === void 0 ? "undefined" : serialized;
1068
1053
  } catch (error) {
1069
1054
  const reason = error instanceof Error ? error.message : String(error);
1070
- return `[Unserializable step result: ${reason}]`;
1055
+ return `[Unserializable model content: ${reason}]`;
1071
1056
  }
1072
1057
  }
1073
1058
  function normalizeModelContent(content) {
@@ -1085,10 +1070,15 @@ function normalizeModelContent(content) {
1085
1070
  return textParts.join("\n");
1086
1071
  }
1087
1072
  }
1088
- return stringifyWithFallback(content, "Unserializable model content");
1073
+ return stringifyModelContent(content);
1089
1074
  }
1090
- function serializePlanExecuteResult(result) {
1091
- return stringifyWithFallback(result, "Unserializable step result");
1075
+ function parseModelResponse(content, context) {
1076
+ const normalized = normalizeModelContent(content);
1077
+ try {
1078
+ return JSON.parse(normalized);
1079
+ } catch (parseError) {
1080
+ throw new Error(`Failed to parse ${context} from LLM response: ${parseError}`);
1081
+ }
1092
1082
  }
1093
1083
 
1094
1084
  // src/plan-execute/planner-node.ts
@@ -1117,19 +1107,13 @@ function createPlannerNode(config) {
1117
1107
  new HumanMessage2(userPrompt)
1118
1108
  ];
1119
1109
  const response = await model.invoke(messages);
1120
- const content = normalizeModelContent(response.content);
1121
- let plan;
1122
- try {
1123
- const parsed = JSON.parse(content);
1124
- plan = {
1125
- steps: parsed.steps.slice(0, maxSteps),
1126
- goal: parsed.goal || state.input || "",
1127
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1128
- confidence: parsed.confidence
1129
- };
1130
- } catch (parseError) {
1131
- throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
1132
- }
1110
+ const parsed = parseModelResponse(response.content, "plan");
1111
+ const plan = {
1112
+ steps: parsed.steps.slice(0, maxSteps),
1113
+ goal: parsed.goal || state.input || "",
1114
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1115
+ confidence: parsed.confidence
1116
+ };
1133
1117
  plannerLogger.info("Plan created", {
1134
1118
  stepCount: plan.steps.length,
1135
1119
  goal: plan.goal.substring(0, 100),
@@ -1320,6 +1304,37 @@ function createExecutorNode(config) {
1320
1304
 
1321
1305
  // src/plan-execute/replanner-node.ts
1322
1306
  import { HumanMessage as HumanMessage3, SystemMessage as SystemMessage3 } from "@langchain/core/messages";
1307
+
1308
+ // src/plan-execute/serialization.ts
1309
+ function stringifyWithFallback(value, fallbackLabel) {
1310
+ try {
1311
+ const serialized = JSON.stringify(value);
1312
+ if (serialized === void 0) {
1313
+ return "undefined";
1314
+ }
1315
+ return serialized;
1316
+ } catch (error) {
1317
+ const reason = error instanceof Error ? error.message : String(error);
1318
+ return `[${fallbackLabel}: ${reason}]`;
1319
+ }
1320
+ }
1321
+ function toJsonSafeValue(value) {
1322
+ try {
1323
+ const serialized = JSON.stringify(value);
1324
+ if (serialized === void 0) {
1325
+ return void 0;
1326
+ }
1327
+ return JSON.parse(serialized);
1328
+ } catch (error) {
1329
+ const reason = error instanceof Error ? error.message : String(error);
1330
+ return `[Unserializable step result: ${reason}]`;
1331
+ }
1332
+ }
1333
+ function serializePlanExecuteResult(result) {
1334
+ return stringifyWithFallback(result, "Unserializable step result");
1335
+ }
1336
+
1337
+ // src/plan-execute/replanner-node.ts
1323
1338
  function createReplannerNode(config) {
1324
1339
  const {
1325
1340
  model,
@@ -1357,13 +1372,7 @@ function createReplannerNode(config) {
1357
1372
  new HumanMessage3(userPrompt)
1358
1373
  ];
1359
1374
  const response = await model.invoke(messages);
1360
- const content = normalizeModelContent(response.content);
1361
- let decision;
1362
- try {
1363
- decision = JSON.parse(content);
1364
- } catch (parseError) {
1365
- throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
1366
- }
1375
+ const decision = parseModelResponse(response.content, "replan decision");
1367
1376
  if (decision.shouldReplan) {
1368
1377
  replannerLogger.info("Replanning triggered", {
1369
1378
  reason: decision.reason,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.16.88",
3
+ "version": "0.16.90",
4
4
  "description": "Production-ready agent workflow patterns for TypeScript including ReAct and Planner-Executor, built on LangGraph.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -41,13 +41,13 @@
41
41
  "url": "https://github.com/TVScoundrel/agentforge/issues"
42
42
  },
43
43
  "dependencies": {
44
- "@agentforge/core": "0.16.88",
44
+ "@agentforge/core": "0.16.90",
45
45
  "@langchain/core": "^1.1.17",
46
46
  "@langchain/langgraph": "^1.1.2",
47
47
  "zod": "^3.23.8"
48
48
  },
49
49
  "devDependencies": {
50
- "@agentforge/testing": "0.16.88",
50
+ "@agentforge/testing": "0.16.90",
51
51
  "@eslint/js": "^9.17.0",
52
52
  "@types/node": "^22.10.2",
53
53
  "eslint": "^9.17.0",