Skip to content

feat: Add job-apply-agent AgentKit#76

Open
namansinghrana wants to merge 3 commits intoLamatic:mainfrom
namansinghrana:feat/job-apply-agent
Open

feat: Add job-apply-agent AgentKit#76
namansinghrana wants to merge 3 commits intoLamatic:mainfrom
namansinghrana:feat/job-apply-agent

Conversation

@namansinghrana
Copy link

@namansinghrana namansinghrana commented Mar 20, 2026

What This Kit Does

ApplyBud is an AI-powered job application agent that evaluates job postings
against a candidate's resume, scores the match (0–100), and automatically
generates a tailored professional cover letter for any role scoring 70 or above.
Results are returned ranked by match score with qualified jobs first.

Providers & Prerequisites

  • Groq — model: groq/llama-3.3-70b-versatile (requires Groq API key via Lamatic credential named ApplyBud)
  • Lamatic — flow must be deployed with Edge enabled
  • Node.js 18+

How to Run Locally

  1. cd kits/agentic/job-apply-agent
  2. npm install
  3. cp .env.example .env.local and fill in values
  4. npm run dev

Live Preview

https://job-apply-agent.vercel.app

Lamatic Flow

Flow ID: 56e18477-5451-4b67-9fc5-5014be67dc7c

Summary

New AgentKit: ApplyBud - Job Application Agent

Core Functionality:

  • Evaluates job postings against a candidate's resume and assigns match scores (0–100)
  • Generates tailored professional cover letters for roles scoring 70 or above
  • Returns results ranked by match score with qualified jobs prioritized

Kit Components:

  • Frontend: Next.js 14 application with TypeScript
    • Resume input textarea (plain text only)
    • Dynamic job URL input (supports up to 10 URLs)
    • Results display with job cards showing match scores, skills, and cover letters (copy-to-clipboard enabled)
  • Backend: Server action for input validation and orchestration
  • Integration: Lamatic SDK client for flow execution

Flow Configuration:

  • 13-node orchestration workflow (job-apply-flow)
  • Steps: resume parsing → job fetching/cleaning → per-job analysis → match scoring → conditional cover letter generation → result bundling
  • Uses Groq LLM (groq/llama-3.3-70b-versatile) for analysis

Configuration & Setup:

  • Requires Groq API key via Lamatic credential named ApplyBud
  • Environment variables: LAMATIC_API_KEY, LAMATIC_PROJECT_ID, LAMATIC_FLOW_ID, LAMATIC_API_URL
  • Flow must be deployed with Edge enabled
  • Local development: npm install.env.local setup → npm run dev

Tech Stack:

  • Next.js 14, TypeScript, React, Lamatic SDK, Groq LLM

Live Preview: https://job-apply-agent.vercel.app

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

This PR introduces a complete Job Apply Agent kit within the agentic kits directory. It includes environment configuration, Next.js UI components for resume and job URL input, server-side orchestration and Lamatic API integration, comprehensive documentation, and a multi-step flow configuration for resume parsing, job analysis, matching, and conditional cover-letter generation.

Changes

Cohort / File(s) Summary
Configuration & Setup
.env.example, .gitignore, package.json, tsconfig.json, next-env.d.ts
Foundational project files including npm dependencies (Next.js 14, React, Lamatic SDK), TypeScript strict config, and dev/build scripts.
Agent Configuration
config.json
Declares kit metadata, required environment variables (API key, project/flow IDs), input schema (resume, job URLs), and output schema (candidate, totals, ranked results with scores and cover letters).
Documentation
README.md, flows/job-apply-flow/README.md
Main kit README covering setup, flow import via Lamatic dashboard, UI/GraphQL usage, architecture diagram, and tech stack; flow-specific README detailing 13 cooperating nodes and configuration requirements.
React Components & Pages
app/layout.tsx, app/page.tsx, components/ResumeInput.tsx, components/JobUrlInput.tsx, components/JobResults.tsx
Client-side UI for resume textarea input, dynamic job URL list management, result display with match scores, skill matching, expandable cover letters, and copy-to-clipboard functionality.
Core Business Logic
actions/orchestrate.ts, lib/lamatic-client.ts
Input validation (minimum resume length, valid URLs), orchestration bridge to Lamatic flow execution, error handling, and response parsing; Lamatic client initialization and flow invocation.
Flow Orchestration Configuration
flows/job-apply-flow/config.json, flows/job-apply-flow/inputs.json, flows/job-apply-flow/meta.json
Complex flow definition with 13 nodes for resume parsing, job fetching/cleaning, per-job LLM analysis, match scoring, conditional cover-letter generation (score ≥ 70), and result bundling; model selector inputs; workflow metadata.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Browser
    participant Page as Next.js Page
    participant Orch as orchestrate Action
    participant Lamatic as Lamatic API
    participant Flow as Job-Apply Flow<br/>(13 nodes)
    
    User->>Page: Submit resume + job URLs
    Page->>Orch: call orchestrate(input)
    Orch->>Orch: Validate resume & URLs
    Orch->>Lamatic: executeFlow(flowId, {resume, job_urls})
    Lamatic->>Flow: Trigger flow execution
    Flow->>Flow: Parse resume
    Flow->>Flow: Fetch & clean job pages
    Flow->>Flow: Loop: analyze each job (LLM)
    Flow->>Flow: Score & match skills
    alt Score >= 70
        Flow->>Flow: Generate cover letter (LLM)
    else Score < 70
        Flow->>Flow: Skip cover letter
    end
    Flow->>Flow: Bundle results, sort by qualification
    Flow-->>Lamatic: Return ApplyBudResponse
    Lamatic-->>Orch: Return response with results
    Orch-->>Page: Return OrchestrateResult
    Page->>Page: Render JobResults with<br/>match scores & cover letters
    Page-->>User: Display ranked opportunities
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Fix : Config Removal #42: Modifies Lamatic environment variable configuration scheme; this PR uses LAMATIC_API_KEY, LAMATIC_PROJECT_ID, and LAMATIC_API_URL in .env.example and client initialization, while that PR switches to base64-encoded LAMATIC_CONFIG_* env keys, indicating a potential configuration compatibility concern.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Add job-apply-agent AgentKit' accurately and specifically summarizes the main change: introducing a new job application agent kit to the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can disable the changed files summary in the walkthrough.

Disable the reviews.changed_files_summary setting to disable the changed files summary in the walkthrough.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (3)
kits/agentic/job-apply-agent/app/page.tsx (1)

93-350: Consider extracting styles to a CSS module for maintainability.

The inline styled-jsx block spans ~250 lines. For a component of this size, a separate CSS module (page.module.css) would improve readability and make styles easier to maintain/reuse.

kits/agentic/job-apply-agent/actions/orchestrate.ts (1)

33-40: Consider restricting URLs to HTTP/HTTPS protocols.

The current URL validation accepts any syntactically valid URL, including potentially problematic protocols like file://, javascript:, or data:. While the URLs are likely sent to the Lamatic flow for fetching, restricting to HTTP(S) provides defense-in-depth.

Suggested fix
   const validUrls = input.job_urls.filter((url) => {
     try {
-      new URL(url);
-      return true;
+      const parsed = new URL(url);
+      return parsed.protocol === "http:" || parsed.protocol === "https:";
     } catch {
       return false;
     }
   });
kits/agentic/job-apply-agent/components/JobResults.tsx (1)

17-21: Add error handling for clipboard API.

navigator.clipboard.writeText can reject if clipboard permissions are denied or in non-secure contexts. Consider wrapping in try-catch to prevent unhandled promise rejection and provide user feedback on failure.

Suggested fix
   const copyToClipboard = async (text: string, index: number) => {
-    await navigator.clipboard.writeText(text);
-    setCopiedIndex(index);
-    setTimeout(() => setCopiedIndex(null), 2000);
+    try {
+      await navigator.clipboard.writeText(text);
+      setCopiedIndex(index);
+      setTimeout(() => setCopiedIndex(null), 2000);
+    } catch {
+      // Fallback or user notification could be added here
+      console.error("Failed to copy to clipboard");
+    }
   };

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 031592d0-8f83-44f8-b26d-53b02b38e3bf

📥 Commits

Reviewing files that changed from the base of the PR and between e1ceb47 and 760d2f3.

⛔ Files ignored due to path filters (1)
  • kits/agentic/job-apply-agent/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • kits/agentic/job-apply-agent/.env.example
  • kits/agentic/job-apply-agent/.gitignore
  • kits/agentic/job-apply-agent/README.md
  • kits/agentic/job-apply-agent/actions/orchestrate.ts
  • kits/agentic/job-apply-agent/app/layout.tsx
  • kits/agentic/job-apply-agent/app/page.tsx
  • kits/agentic/job-apply-agent/components/JobResults.tsx
  • kits/agentic/job-apply-agent/components/JobUrlInput.tsx
  • kits/agentic/job-apply-agent/components/ResumeInput.tsx
  • kits/agentic/job-apply-agent/config.json
  • kits/agentic/job-apply-agent/flows/job-apply-flow/README.md
  • kits/agentic/job-apply-agent/flows/job-apply-flow/config.json
  • kits/agentic/job-apply-agent/flows/job-apply-flow/inputs.json
  • kits/agentic/job-apply-agent/flows/job-apply-flow/meta.json
  • kits/agentic/job-apply-agent/lib/lamatic-client.ts
  • kits/agentic/job-apply-agent/next-env.d.ts
  • kits/agentic/job-apply-agent/package.json
  • kits/agentic/job-apply-agent/tsconfig.json

Comment on lines +1 to +4
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace template metadata with product metadata.

At Line 2 and Line 3, the app still uses boilerplate values (Next.js, Generated by Next.js), which is inconsistent with this kit and affects browser title/share previews.

Suggested metadata update
 export const metadata = {
-  title: 'Next.js',
-  description: 'Generated by Next.js',
+  title: 'ApplyBud — Job Apply Agent',
+  description: 'Analyze job links against a resume, rank matches, and auto-generate tailored cover letters for high-fit roles.',
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}
export const metadata = {
title: 'ApplyBud — Job Apply Agent',
description: 'Analyze job links against a resume, rank matches, and auto-generate tailored cover letters for high-fit roles.',
}

Comment on lines +29 to +50
{urls.map((url, index) => (
<div key={index} className="url-row">
<input
type="url"
value={url}
onChange={(e) => updateUrl(index, e.target.value)}
disabled={disabled}
placeholder="https://jobs.example.com/role-title-12345"
/>
{urls.length > 1 && (
<button
type="button"
onClick={() => removeUrl(index)}
disabled={disabled}
className="remove-btn"
aria-label="Remove URL"
>
</button>
)}
</div>
))}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Using array index as key can cause input state issues when removing items.

When a URL is removed from the middle of the list, React reconciles based on index, which can cause the wrong input values to appear in remaining fields (stale DOM state). Consider using a stable unique ID per URL entry.

Suggested fix using stable IDs
+"use client";
+
+import { useId } from "react";
+
 interface JobUrlInputProps {
-  urls: string[];
-  onChange: (urls: string[]) => void;
+  urls: { id: string; value: string }[];
+  onChange: (urls: { id: string; value: string }[]) => void;
   disabled?: boolean;
 }

Alternatively, if changing the data shape is not desirable, generate a unique ID when adding:

+let urlIdCounter = 0;
+const generateId = () => `url-${++urlIdCounter}`;
+
 export function JobUrlInput({ urls, onChange, disabled }: JobUrlInputProps) {
-  const addUrl = () => onChange([...urls, ""]);
+  const addUrl = () => onChange([...urls, { id: generateId(), value: "" }]);

Comment on lines +31 to +45
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": { "type": "string" },
"job_title": { "type": "string" },
"company": { "type": "string" },
"match_score": { "type": "number" },
"qualified": { "type": "boolean" },
"matched_skills": { "type": "string" },
"cover_letter": { "type": "string" }
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Output schema missing seniority field.

The JobResult interface in lib/lamatic-client.ts includes a seniority: string field (used in JobResults.tsx at line 57-59), but it's not declared in the output schema here. This creates a mismatch between the documented schema and the actual response shape.

Suggested fix
         "properties": {
           "url": { "type": "string" },
           "job_title": { "type": "string" },
           "company": { "type": "string" },
+          "seniority": { "type": "string" },
           "match_score": { "type": "number" },
           "qualified": { "type": "boolean" },
           "matched_skills": { "type": "string" },
           "cover_letter": { "type": "string" }
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": { "type": "string" },
"job_title": { "type": "string" },
"company": { "type": "string" },
"match_score": { "type": "number" },
"qualified": { "type": "boolean" },
"matched_skills": { "type": "string" },
"cover_letter": { "type": "string" }
}
}
}
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": { "type": "string" },
"job_title": { "type": "string" },
"company": { "type": "string" },
"seniority": { "type": "string" },
"match_score": { "type": "number" },
"qualified": { "type": "boolean" },
"matched_skills": { "type": "string" },
"cover_letter": { "type": "string" }
}
}
}

Comment on lines +84 to +87
"values": {
"id": "codeNode_487",
"code": "var urls = {{triggerNode_1.output.job_urls}};\nvar out = [];\nvar i = 0;\nwhile (i < urls.length) {\n var url = urls[i];\n var text = \"\";\n var status = \"ok\";\n try {\n var response = await fetch(url);\n var html = await response.text();\n var clean = html;\n var s1 = clean.indexOf('<script');\n while (s1 > -1) {\n var e1 = clean.indexOf('<' + '/script>', s1);\n if (e1 === -1) { break; }\n clean = clean.substring(0, s1) + ' ' + clean.substring(e1 + 9);\n s1 = clean.indexOf('<script');\n }\n var s2 = clean.indexOf('<style');\n while (s2 > -1) {\n var e2 = clean.indexOf('<' + '/style>', s2);\n if (e2 === -1) { break; }\n clean = clean.substring(0, s2) + ' ' + clean.substring(e2 + 8);\n s2 = clean.indexOf('<style');\n }\n var t1 = clean.indexOf('<');\n while (t1 > -1) {\n var t2 = clean.indexOf('>', t1);\n if (t2 === -1) { break; }\n clean = clean.substring(0, t1) + ' ' + clean.substring(t2 + 1);\n t1 = clean.indexOf('<');\n }\n var words = clean.split(' ');\n var filtered = [];\n var w = 0;\n while (w < words.length) {\n var word = words[w].trim();\n if (word.length > 0) { filtered.push(word); }\n w = w + 1;\n }\n text = filtered.join(' ');\n if (text.length > 6000) { text = text.substring(0, 6000); }\n } catch (e) {\n status = \"error\";\n text = \"\";\n }\n out.push({ url: url, jd_text: text, status: status });\n i = i + 1;\n}\nreturn { job_pages: out };",
"nodeName": "Fetch Job Pages"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Restrict and bound outbound job-page fetches.

Line 86 fetches arbitrary user-supplied URLs with no scheme/private-network validation, no timeout, and no response.ok check. In a public flow this is an SSRF vector, and slow or non-2xx targets are still treated as valid job descriptions.

Comment on lines +183 to +186
"values": {
"id": "codeNode_575",
"code": "var candidateSkills = {{InstructorLLMNode_426.output.skills}};\nvar jobRequiredRaw = {{InstructorLLMNode_219.output.required_skills}};\nvar jobPreferredRaw = {{InstructorLLMNode_219.output.preferred_skills}};\nvar jobTitle = {{InstructorLLMNode_219.output.job_title}};\nvar jobCompany = {{InstructorLLMNode_219.output.company}};\nvar jobSeniority = {{InstructorLLMNode_219.output.seniority}};\n\nvar jobRequired = jobRequiredRaw.split(',');\nvar jobPreferred = jobPreferredRaw.split(',');\nvar candidateList = candidateSkills.split(',');\n\nvar reqMatches = 0;\nvar prefMatches = 0;\nvar matchedSkills = [];\nvar i = 0;\n\nwhile (i < jobRequired.length) {\n var req = jobRequired[i].toLowerCase().trim();\n var j = 0;\n while (j < candidateList.length) {\n var cs = candidateList[j].toLowerCase().trim();\n if (cs === req || cs.indexOf(req) > -1 || req.indexOf(cs) > -1) {\n reqMatches = reqMatches + 1;\n matchedSkills.push(candidateList[j].trim());\n break;\n }\n j = j + 1;\n }\n i = i + 1;\n}\n\ni = 0;\nwhile (i < jobPreferred.length) {\n var pref = jobPreferred[i].toLowerCase().trim();\n var k = 0;\n while (k < candidateList.length) {\n var cs2 = candidateList[k].toLowerCase().trim();\n if (cs2 === pref || cs2.indexOf(pref) > -1 || pref.indexOf(cs2) > -1) {\n prefMatches = prefMatches + 1;\n matchedSkills.push(candidateList[k].trim());\n break;\n }\n k = k + 1;\n }\n i = i + 1;\n}\n\nvar reqScore = jobRequired.length > 0 ? (reqMatches / jobRequired.length) * 70 : 35;\nvar prefScore = jobPreferred.length > 0 ? (prefMatches / jobPreferred.length) * 30 : 15;\nvar score = Math.round(reqScore + prefScore);\n\nreturn {\n job_title: jobTitle,\n company: jobCompany,\n seniority: jobSeniority,\n score: score,\n qualified: score >= 70,\n matched_skills: matchedSkills.join(', ')\n};",
"nodeName": "Match Scorer"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Normalize LLM skill fields before scoring.

Line 185 assumes skills, required_skills, and preferred_skills are always populated comma-delimited strings. If any of them is missing, .split(",") throws; if any is "", the indexOf("") checks count it as a match and can award a perfect score to jobs with no extracted skills.

Comment on lines +360 to +363
"values": {
"id": "codeNode_621",
"code": "var loopData = {{forLoopNode_787.output}};\nvar loopResults = loopData.loopOutput;\nvar out = [];\nvar i = 0;\n\nwhile (i < loopResults.length) {\n var iter = loopResults[i];\n var scorer = iter.codeNode_575;\n var coverNode = iter.LLMNode_122;\n\n var scorerOutput = {};\n if (scorer && scorer.output && scorer.status === 'success') {\n scorerOutput = scorer.output;\n }\n\n var coverLetter = null;\n if (coverNode && coverNode.output && coverNode.output.generatedResponse) {\n coverLetter = coverNode.output.generatedResponse;\n }\n\n var jobUrl = '';\n if (loopData.currentValue && loopData.currentValue.url) {\n jobUrl = loopData.currentValue.url;\n }\n\n var item = {\n url: jobUrl,\n job_title: scorerOutput.job_title,\n company: scorerOutput.company,\n seniority: scorerOutput.seniority,\n match_score: scorerOutput.score,\n qualified: scorerOutput.qualified,\n matched_skills: scorerOutput.matched_skills,\n cover_letter: coverLetter\n };\n out.push(item);\n i = i + 1;\n}\n\nvar sorted = out.sort(function(a, b) {\n if (a.qualified !== b.qualified) { return a.qualified ? -1 : 1; }\n return b.match_score - a.match_score;\n});\n\nvar qualCount = 0;\nvar q = 0;\nwhile (q < sorted.length) {\n if (sorted[q].qualified === true) { qualCount = qualCount + 1; }\n q = q + 1;\n}\n\nreturn {\n results: sorted,\n total: sorted.length,\n qualified_count: qualCount,\n candidate: {{InstructorLLMNode_426.output.name}}\n};",
"nodeName": "Bundle Results"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bundle each result with its own job URL.

Line 362 reads loopData.currentValue.url inside the aggregation loop. That is shared loop state, so every bundled item will inherit the last processed URL (or "") instead of its own posting URL. Carry the URL through the iteration output or index back into codeNode_487.output.job_pages[i].

Comment on lines +3 to +8
"description": "",
"tags": [],
"testInput": "",
"githubUrl": "",
"documentationUrl": "",
"deployUrl": ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Populate flow metadata fields before publishing.

At Line 3 through Line 8, the metadata is effectively blank. This leaves the flow card/documentation entry incomplete and harder to consume.

Suggested metadata patch
 {
   "name": "ApplyBud",
-  "description": "",
-  "tags": [],
-  "testInput": "",
-  "githubUrl": "",
-  "documentationUrl": "",
-  "deployUrl": ""
+  "description": "AI-powered job application agent that scores resume-job fit and generates cover letters for qualified roles.",
+  "tags": ["jobs", "resume", "cover-letter", "agentic"],
+  "testInput": "{\"resume\":\"<paste_resume_text>\",\"job_urls\":[\"https://example.com/job-1\"]}",
+  "githubUrl": "https://github.com/Lamatic/AgentKit/pull/76",
+  "documentationUrl": "https://github.com/Lamatic/AgentKit/tree/main/kits/agentic/job-apply-agent",
+  "deployUrl": "https://job-apply-agent.vercel.app"
 }

Comment on lines +3 to +7
const client = new Lamatic({
apiKey: process.env.LAMATIC_API_KEY!,
projectId: process.env.LAMATIC_PROJECT_ID!,
endpoint: process.env.LAMATIC_API_URL!,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the Lamatic endpoint requirement explicit.

Lines 3-7 hard-require process.env.LAMATIC_API_URL, but kits/agentic/job-apply-agent/config.json:11-15 does not list it in requiredEnvVars. The kit can pass config validation and still instantiate Lamatic with an undefined endpoint. Either declare the env var in the kit manifest or validate it here before constructing the client.

Comment on lines +37 to +40
const response = await client.executeFlow(flowId, {
resume: input.resume,
job_urls: input.job_urls.join(","),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Pass job_urls as an array, not a CSV string.

Line 39 flattens job_urls into one comma-delimited string, but kits/agentic/job-apply-agent/flows/job-apply-flow/config.json:19 declares the trigger input as [string], and kits/agentic/job-apply-agent/flows/job-apply-flow/config.json:86 iterates one URL per element. Multiple postings will be collapsed into one invalid fetch target, so the core multi-job path breaks.

Suggested patch
   const response = await client.executeFlow(flowId, {
     resume: input.resume,
-    job_urls: input.job_urls.join(","),
+    job_urls: input.job_urls,
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await client.executeFlow(flowId, {
resume: input.resume,
job_urls: input.job_urls.join(","),
});
const response = await client.executeFlow(flowId, {
resume: input.resume,
job_urls: input.job_urls,
});


## Model

All AI nodes use `groq/llama-3.1-8b-instant` via Groq.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Model name inconsistency with PR description.

The README states the model is groq/llama-3.1-8b-instant, but the PR description mentions groq/llama-3.3-70b-versatile. Please verify and align the documentation with the actual model configured in the flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants