Setting the file. One moment.
Executor · Swarm · langchain-ai/langchain-skills · Skills Docs
ContentsBack to the top of the page scripts/ executor.ts
TypeScript · 160 lines · 5 KB
:
{
14 swarmTask ?: ( args : {
15 description : string ;
16 subagent_type ?: string ;
17 response_schema ?: Record < string , unknown >;
18 mode ?: "agent" | "invoke" ;
19 }) => Promise < string >;
20 };
21
22 /**
23 * Column names that must not be overwritten by structured output merging.
24 */
25 const RESERVED_COLUMNS = new Set ([ "id" , "file" ]);
26
27 /**
28 * Call the PTC `swarm_task` tool.
29 *
30 * @internal Exported for testing — not part of the public API.
31 * @param args - Task arguments forwarded to the swarm task tool.
32 * @returns The subagent's response as a string.
33 * @throws Error if the `swarm_task` PTC tool is not configured.
34 */
35 export async function callTask ( args : {
36 description : string ;
37 subagent_type ?: string ;
38 response_schema ?: Record < string , unknown >;
39 mode ?: "agent" | "invoke" ;
40 }) : Promise < string > {
41 if ( typeof tools.swarmTask !== "function" ) {
42 throw new Error (
43 "Swarm requires a 'swarm_task' tool in the PTC configuration." ,
44 );
45 }
46 return tools. swarmTask (args);
47 }
48
49 /**
50 * Dispatch an array of task specs to subagents with bounded concurrency.
51 *
52 * Spawns up to `concurrency` workers that pull from the task queue.
53 * Each worker calls the task function and records the result (or error)
54 * at the same index as the input spec, preserving order.
55 *
56 * @param tasks - Task specs to dispatch.
57 * @param options - Dispatch options (currently just `concurrency`).
58 * @returns Results in the same order as the input tasks.
59 */
60 export async function dispatch (
61 tasks : TaskSpec [],
62 options : { concurrency : number },
63 ) : Promise < TaskResult []> {
64 const results = new Array < TaskResult >(tasks. length );
65
66 let idx = 0 ;
67 async function worker () : Promise < void > {
68 while (idx < tasks. length ) {
69 const i = idx ++ ;
70 const spec = tasks[i];
71 try {
72 const output = await callTask ({
73 description: spec.prompt,
74 ... (spec.subagentType != null && {
75 subagent_type: spec.subagentType,
76 }),
77 ... (spec.responseSchema != null && {
78 response_schema: spec.responseSchema,
79 }),
80 ... (spec.mode != null && { mode: spec.mode }),
81 });
82 results[i] = {
83 id: spec.id,
84 status: "completed" ,
85 result: String (output),
86 };
87 } catch ( err : unknown ) {
88 const msg =
89 err != null && typeof (err as Error ).message === "string"
90 ? (err as Error ).message
91 : String (err);
92 results[i] = { id: spec.id, status: "failed" , error: msg };
93 }
94 }
95 }
96
97 const workers : Promise < void >[] = [];
98 for ( let w = 0 ; w < Math. min (options.concurrency, tasks. length ); w ++ ) {
99 workers. push ( worker ());
100 }
101 await Promise . all (workers);
102
103 return results;
104 }
105
106 /**
107 * Group failed task results by error message.
108 *
109 * Produces deduplicated failure groups sorted by count descending,
110 * each containing the shared error message, the count of affected
111 * rows, and the full list of affected row IDs.
112 *
113 * @param results - Array of task results (may include completed results).
114 * @returns Deduplicated failure groups, sorted by count descending.
115 */
116 export function deduplicateFailures ( results : TaskResult []) : FailureGroup [] {
117 const groups = new Map < string , string []>();
118
119 for ( const r of results) {
120 if (r.status !== "failed" || ! r.error) {
121 continue ;
122 }
123
124 const ids = groups. get (r.error);
125 if (ids) {
126 ids. push (r.id);
127 } else {
128 groups. set (r.error, [r.id]);
129 }
130 }
131
132 const out : FailureGroup [] = [];
133 for ( const [ error , ids ] of groups) {
134 out. push ({ error, count: ids. length , ids });
135 }
136 out. sort (( a , b ) => b.count - a.count);
137
138 return out;
139 }
140
141 /**
142 * Merge a subagent result into a table row.
143 *
144 * Each property of the parsed structured output is spread onto the
145 * row as a top-level column — except reserved columns (`id`, `file`)
146 * which are never overwritten.
147 *
148 * @param row - The table row to update (mutated in place).
149 * @param value - The subagent's parsed structured output.
150 */
151 export function mergeResult (
152 row : Record < string , unknown >,
153 value : Record < string , unknown >,
154 ) : void {
155 for ( const [ k , v ] of Object. entries (value)) {
156 if ( ! RESERVED_COLUMNS . has (k)) {
157 row[k] = v;
158 }
159 }
160 }