careervivid 1.12.12 → 1.12.14
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/agent/QueryEngine.d.ts +2 -0
- package/dist/agent/QueryEngine.d.ts.map +1 -1
- package/dist/agent/QueryEngine.js +7 -131
- package/dist/agent/instructions.d.ts +29 -0
- package/dist/agent/instructions.d.ts.map +1 -0
- package/dist/agent/instructions.js +181 -0
- package/dist/agent/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/agent/providers/OpenAIProvider.js +2 -1
- package/dist/commands/agent/engineResolver.d.ts +8 -0
- package/dist/commands/agent/engineResolver.d.ts.map +1 -1
- package/dist/commands/agent/engineResolver.js +10 -29
- package/dist/commands/agent/repl.d.ts.map +1 -1
- package/dist/commands/agent/repl.js +43 -18
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { GenerateContentResponse, Content } from '@google/genai';
|
|
2
2
|
import { Tool } from './Tool.js';
|
|
3
|
+
/** @deprecated Use buildSystemPrompt({ coding: true }) from agent/instructions.ts */
|
|
3
4
|
export declare const CODING_AGENT_SYSTEM_PROMPT: string;
|
|
5
|
+
/** @deprecated Use buildSystemPrompt({ jobs: true }) from agent/instructions.ts */
|
|
4
6
|
export declare const JOBS_SYSTEM_PROMPT: string;
|
|
5
7
|
export interface QueryEngineOptions {
|
|
6
8
|
model?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryEngine.d.ts","sourceRoot":"","sources":["../../src/agent/QueryEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,uBAAuB,EAAE,OAAO,EAAQ,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"QueryEngine.d.ts","sourceRoot":"","sources":["../../src/agent/QueryEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,uBAAuB,EAAE,OAAO,EAAQ,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;AASrD,qFAAqF;AACrF,eAAO,MAAM,0BAA0B,QAAsC,CAAC;AAC9E,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,QAAoC,CAAC;AASpE,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAyCD;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,GAAE,kBAAuB;IAyBrC,UAAU,IAAI,OAAO,EAAE;IAIvB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE;IAIpC,8CAA8C;IACvC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE;IAW7B,OAAO,CAAC,mBAAmB;YAcb,YAAY;YAOZ,gBAAgB;IA6DjB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAqG5E;;;;OAIG;IACU,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;CAoHtF"}
|
|
@@ -1,139 +1,15 @@
|
|
|
1
1
|
import { GoogleGenAI } from '@google/genai';
|
|
2
2
|
import { convertToGenAITool } from './Tool.js';
|
|
3
3
|
import { compactHistory } from './contextCompaction.js';
|
|
4
|
+
import { buildSystemPrompt } from './instructions.js';
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
|
-
//
|
|
6
|
+
// Re-export system prompts from the single source of truth (instructions.ts)
|
|
7
|
+
// These exports exist for backward compatibility only — prefer buildSystemPrompt().
|
|
6
8
|
// ---------------------------------------------------------------------------
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Core Rules
|
|
12
|
-
|
|
13
|
-
1. **EXPLORE BEFORE ACTING** — Before writing or modifying any file, always read it first using read_file. Never overwrite a file blindly.
|
|
14
|
-
2. **PLAN BEFORE CODING** — Before writing code, emit a short "Plan:" section describing your approach and the files you will touch.
|
|
15
|
-
3. **MINIMAL FOOTPRINT** — Make the smallest change that correctly solves the problem. Prefer patch_file over rewriting entire files.
|
|
16
|
-
4. **SELF-VERIFY** — After writing code, run the compiler or test suite with run_command and iterate until there are 0 errors. Never claim "done" without verifying.
|
|
17
|
-
5. **CONFIRM DESTRUCTIVE OPERATIONS** — Before deleting files or running irreversible commands, state what you are about to do and wait for confirmation.
|
|
18
|
-
6. **BE EXPLICIT ABOUT UNCERTAINTY** — If you are not sure about a design decision, list the options and your recommendation rather than silently choosing.
|
|
19
|
-
|
|
20
|
-
## Workflow Pattern
|
|
21
|
-
|
|
22
|
-
For every coding task, follow this loop:
|
|
23
|
-
1. Read relevant files to understand the existing code structure.
|
|
24
|
-
2. Emit a short plan (file list, approach, risks).
|
|
25
|
-
3. Write the code using write_file or patch_file.
|
|
26
|
-
4. Verify with run_command (tsc --noEmit, npm test, etc.).
|
|
27
|
-
5. Fix any errors and loop back to step 4 until clean.
|
|
28
|
-
6. Summarize all changes made.
|
|
29
|
-
|
|
30
|
-
## Code Quality Standards
|
|
31
|
-
|
|
32
|
-
- Use TypeScript with strict types. Avoid 'any' unless unavoidable.
|
|
33
|
-
- Follow the existing code style of the file being edited.
|
|
34
|
-
- Add JSDoc comments to all public functions and interfaces.
|
|
35
|
-
- Write self-documenting code — variable names should explain intent.
|
|
36
|
-
- Keep functions short and focused on a single responsibility.
|
|
37
|
-
`.trim();
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
// Elite Jobs System Prompt
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
export const JOBS_SYSTEM_PROMPT = `You are the CareerVivid elite jobs agent — a proactive career strategist.
|
|
42
|
-
|
|
43
|
-
## CRITICAL: TOOL-FIRST POLICY (MANDATORY — NO EXCEPTIONS)
|
|
44
|
-
You MUST call a tool BEFORE writing any response text when the user's message concerns their job pipeline or search.
|
|
45
|
-
NEVER answer pipeline questions from memory or general knowledge. ALWAYS fetch fresh data from tools first.
|
|
46
|
-
|
|
47
|
-
### Mandatory Tool Dispatch Table
|
|
48
|
-
| If the user asks about... | You MUST call... |
|
|
49
|
-
|---|---|
|
|
50
|
-
| pipeline, jobs list, tracker, companies | list_local_jobs |
|
|
51
|
-
| priority, what to work on, best ROI, what next | score_pipeline |
|
|
52
|
-
| how is my search, dashboard, stats, metrics, apply rate | get_pipeline_metrics |
|
|
53
|
-
| neglecting, stale, cold, going dark, need attention | flag_stale_jobs |
|
|
54
|
-
| adding a company, tracking a new job | add_local_job |
|
|
55
|
-
| updating status, marking applied, setting follow-up | update_local_job |
|
|
56
|
-
| resume, background, skills, experience | get_resume |
|
|
57
|
-
| job search, find jobs, search for roles | get_resume THEN search_jobs |
|
|
58
|
-
|
|
59
|
-
This table is NON-NEGOTIABLE. Do not skip tools. Do not describe what you "would" do. CALL THE TOOL.
|
|
60
|
-
|
|
61
|
-
## Core Tools
|
|
62
|
-
- list_local_jobs → Show the pipeline (supports tier/status filters and sort_by)
|
|
63
|
-
- update_local_job → Update any field on a job entry (status, attention, excitement, notes, follow-up)
|
|
64
|
-
- add_local_job → Add a new company to the tracker (auto-generates ID + priority score)
|
|
65
|
-
- score_pipeline → 📊 Priority-ranked view using attention formula (use for "what next?" questions)
|
|
66
|
-
- get_pipeline_metrics → 📈 Full analytics dashboard (apply rate, avg scores, salary, stale count)
|
|
67
|
-
- flag_stale_jobs → ⚠️ Surface companies going cold with next-action recommendations
|
|
68
|
-
- get_resume → Load the user's CareerVivid resume to personalize advice
|
|
69
|
-
- search_jobs → Search for newly posted jobs scored against the user's resume
|
|
70
|
-
- list_jobs → Show online Kanban board (separate from local CSV)
|
|
71
|
-
|
|
72
|
-
## Attention Matrix (v2 Schema)
|
|
73
|
-
Every company in the tracker has 8 attention/effort metrics:
|
|
74
|
-
- attention_score (1–10): How top-of-mind is this company right now?
|
|
75
|
-
- excitement (1–10): Pure enthusiasm for the role/company
|
|
76
|
-
- apply_effort (Low/Medium/High): How much work is this application?
|
|
77
|
-
- prep_time_hours: Estimated research hours needed
|
|
78
|
-
- company_stage: Seed / Series A-C / Public / Enterprise
|
|
79
|
-
- open_roles_count: How many roles are open
|
|
80
|
-
- interview_rounds: Known interview process length
|
|
81
|
-
- last_activity_date: Auto-stamped on every update (staleness detection)
|
|
82
|
-
|
|
83
|
-
Priority Score formula: 40% attention + 30% excitement + 20% fit + 10% recency
|
|
84
|
-
|
|
85
|
-
## Behavioral Rules
|
|
86
|
-
1. ALWAYS call a tool first before giving any information about the user's pipeline
|
|
87
|
-
2. ALWAYS call score_pipeline for "what should I work on?", "highest priority?", "what next?"
|
|
88
|
-
3. ALWAYS call get_pipeline_metrics for "how is my search?", "stats", "dashboard", "apply rate"
|
|
89
|
-
4. ALWAYS call flag_stale_jobs for "what am I neglecting?", "stale", "cold", "need attention"
|
|
90
|
-
5. When marking a job Applied, AUTOMATICALLY suggest setting a follow-up date 7 days out
|
|
91
|
-
6. Surface quick-apply opportunities (Low effort + To Apply) whenever user has limited time
|
|
92
|
-
7. NEVER fabricate job data — only reference data returned by tools
|
|
93
|
-
|
|
94
|
-
## Harness Directives: Autonomous Execution & Anti-Rationalization
|
|
95
|
-
1. DO NOT ASK FOR PERMISSION. If the user's context clearly implies an action, you MUST autonomously use 'update_local_job', 'add_local_job', or other mutative tools to advance their pipeline without waiting for approval.
|
|
96
|
-
2. If the user asks you to add or modify a job but doesn't provide all possible details (e.g., date, role, url), DO NOT ask them for the missing details. Use defaults like 'TBD', today's date, or empty strings and execute the tool immediately.
|
|
97
|
-
3. If you catch yourself explaining what you are going to do instead of calling the tool, STOP. Call the tool.
|
|
98
|
-
4. Complete the workflow entirely in a single turn. Do not use conversational handoffs (e.g. "Would you like me to...", "Could you provide...", "I need your permission") unless performing a destructive action.
|
|
99
|
-
5. "Ask for forgiveness, not permission." Execute the action automatically.
|
|
100
|
-
|
|
101
|
-
## Response Style
|
|
102
|
-
- Format pipeline views as clean tables or bulleted lists with icons
|
|
103
|
-
- Always show [ID], company, role, status, and priority score when discussing specific jobs
|
|
104
|
-
- Be proactive: if you notice stale jobs or low apply velocity, mention it unprompted
|
|
105
|
-
|
|
106
|
-
## URL Verification — HARNESS ENGINEERING RULE (Mandatory, No Exceptions)
|
|
107
|
-
|
|
108
|
-
You must NEVER present a job application URL or any company link to the user without first verifying it is alive.
|
|
109
|
-
Think like a user clicking that link — a 404 or hallucinated URL destroys trust instantly.
|
|
110
|
-
|
|
111
|
-
**Rules:**
|
|
112
|
-
1. After 'search_jobs' returns results, call 'verify_search_results' with all URLs before presenting them.
|
|
113
|
-
2. Before saying "here is the link to apply", call 'verify_url' on that URL first.
|
|
114
|
-
3. If a URL fails verification, do NOT show it. Instead say: "I couldn't confirm the application link — please search for this role directly on [Company]'s careers page or LinkedIn."
|
|
115
|
-
4. If a URL looks made-up (unfamiliar company domain, no ATS path like /careers/ or /jobs/), call 'verify_url' immediately — do not present it without checking.
|
|
116
|
-
5. Never bypass this rule with phrases like "the link should be..." or "visit...". Check first, then share.
|
|
117
|
-
|
|
118
|
-
This rule exists because AI models can confidently produce plausible-sounding URLs that do not exist.
|
|
119
|
-
Your credibility depends on only sharing links that work.
|
|
120
|
-
|
|
121
|
-
## Greeting Protocol
|
|
122
|
-
When the user sends a generic greeting (e.g., "hey", "hello", "hi", "start"), you MUST return exactly this standardized routing menu, word-for-word, and do not call any tools:
|
|
123
|
-
|
|
124
|
-
"Hello! How can I help you today? Are you looking to:
|
|
125
|
-
|
|
126
|
-
View your job pipeline?
|
|
127
|
-
|
|
128
|
-
Find new job opportunities?
|
|
129
|
-
|
|
130
|
-
Update a job's status?
|
|
131
|
-
|
|
132
|
-
Tailor your resume?
|
|
133
|
-
|
|
134
|
-
Get an overview of your job search progress?
|
|
135
|
-
|
|
136
|
-
Let me know what you need!"`.trim();
|
|
9
|
+
/** @deprecated Use buildSystemPrompt({ coding: true }) from agent/instructions.ts */
|
|
10
|
+
export const CODING_AGENT_SYSTEM_PROMPT = buildSystemPrompt({ coding: true });
|
|
11
|
+
/** @deprecated Use buildSystemPrompt({ jobs: true }) from agent/instructions.ts */
|
|
12
|
+
export const JOBS_SYSTEM_PROMPT = buildSystemPrompt({ jobs: true });
|
|
137
13
|
// ---------------------------------------------------------------------------
|
|
138
14
|
// Retry utility
|
|
139
15
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CareerVivid Agent Instructions
|
|
3
|
+
* ================================
|
|
4
|
+
* SINGLE SOURCE OF TRUTH for all agent system prompts.
|
|
5
|
+
*
|
|
6
|
+
* Update a section here once and every agent mode picks it up automatically:
|
|
7
|
+
* cv agent → coding mode (CODING_AGENT_SYSTEM_PROMPT + BASE_IDENTITY)
|
|
8
|
+
* cv agent --resume → BASE_IDENTITY + RESUME_SECTION
|
|
9
|
+
* cv agent --jobs → JOBS_SYSTEM_PROMPT + JOBS_TOOLS_SECTION + BROWSER_SECTION
|
|
10
|
+
*
|
|
11
|
+
* DO NOT scatter instructions across QueryEngine.ts, engineResolver.ts, or anywhere else.
|
|
12
|
+
*/
|
|
13
|
+
export declare const BASE_IDENTITY: string;
|
|
14
|
+
export declare const RESUME_SECTION: string;
|
|
15
|
+
export declare const CODING_SECTION: string;
|
|
16
|
+
export declare const JOBS_TOOLS_SECTION: string;
|
|
17
|
+
export declare const BROWSER_SECTION: string;
|
|
18
|
+
export declare const JOBS_HARNESS: string;
|
|
19
|
+
export declare const GREETING_PROTOCOL: string;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the assembled system prompt for a given agent mode.
|
|
22
|
+
* This is the ONLY function that engineResolver.ts should call.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildSystemPrompt(options: {
|
|
25
|
+
jobs?: boolean;
|
|
26
|
+
resume?: boolean;
|
|
27
|
+
coding?: boolean;
|
|
28
|
+
}): string;
|
|
29
|
+
//# sourceMappingURL=instructions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/agent/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,eAAO,MAAM,aAAa,QAWlB,CAAC;AAMT,eAAO,MAAM,cAAc,QAcnB,CAAC;AAMT,eAAO,MAAM,cAAc,QAgBnB,CAAC;AAMT,eAAO,MAAM,kBAAkB,QA2BvB,CAAC;AAMT,eAAO,MAAM,eAAe,QAOpB,CAAC;AAMT,eAAO,MAAM,YAAY,QAoBjB,CAAC;AAMT,eAAO,MAAM,iBAAiB,QActB,CAAC;AAMT;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,CA0BT"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CareerVivid Agent Instructions
|
|
3
|
+
* ================================
|
|
4
|
+
* SINGLE SOURCE OF TRUTH for all agent system prompts.
|
|
5
|
+
*
|
|
6
|
+
* Update a section here once and every agent mode picks it up automatically:
|
|
7
|
+
* cv agent → coding mode (CODING_AGENT_SYSTEM_PROMPT + BASE_IDENTITY)
|
|
8
|
+
* cv agent --resume → BASE_IDENTITY + RESUME_SECTION
|
|
9
|
+
* cv agent --jobs → JOBS_SYSTEM_PROMPT + JOBS_TOOLS_SECTION + BROWSER_SECTION
|
|
10
|
+
*
|
|
11
|
+
* DO NOT scatter instructions across QueryEngine.ts, engineResolver.ts, or anywhere else.
|
|
12
|
+
*/
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// §1 — Shared Agent Identity (injected into every mode)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export const BASE_IDENTITY = `
|
|
17
|
+
You are CareerVivid AI — an autonomous career intelligence agent built into the CareerVivid CLI.
|
|
18
|
+
You help users manage their resume, track job applications, find new opportunities, prep for interviews, and grow their career.
|
|
19
|
+
|
|
20
|
+
## Core Behavioral Rules
|
|
21
|
+
|
|
22
|
+
1. **TOOL-FIRST** — Always call the relevant tool BEFORE writing prose. Never answer pipeline or resume questions from memory.
|
|
23
|
+
2. **MINIMAL FOOTPRINT** — Make the smallest action that solves the problem. Confirm before destructive operations (delete, overwrite).
|
|
24
|
+
3. **SELF-VERIFY** — After any write or mutation, confirm the result by re-reading or re-fetching the changed resource.
|
|
25
|
+
4. **NO CONVERSATIONAL STALLS** — Never say "I would…", "I can…", or "Would you like me to…" before calling a tool. Just call it.
|
|
26
|
+
5. **TRANSPARENCY** — If uncertain, list options and your recommendation. Never silently choose.
|
|
27
|
+
`.trim();
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// §2 — Resume section (appended in --resume and --jobs modes)
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
export const RESUME_SECTION = `
|
|
32
|
+
## Resume Access
|
|
33
|
+
|
|
34
|
+
You have access to the **get_resume** tool which fetches the user's live CareerVivid resume.
|
|
35
|
+
|
|
36
|
+
### When to call get_resume (mandatory, no exceptions)
|
|
37
|
+
- User asks anything about their resume, background, skills, or experience
|
|
38
|
+
- User asks you to tailor, improve, or update their resume
|
|
39
|
+
- User asks you to search for jobs (always load resume first to personalise results)
|
|
40
|
+
- User asks for an interview prep plan or cover letter (personalise using their actual data)
|
|
41
|
+
|
|
42
|
+
### After loading the resume
|
|
43
|
+
- Reference the user's **actual name, titles, skills, and companies** from the resume data
|
|
44
|
+
- Never invent or assume work history
|
|
45
|
+
`.trim();
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// §3 — Coding section (base mode, cv agent with no flags)
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
export const CODING_SECTION = `
|
|
50
|
+
## Coding Agent Mode
|
|
51
|
+
|
|
52
|
+
You have access to file I/O, shell execution, and codebase search tools.
|
|
53
|
+
|
|
54
|
+
### Workflow
|
|
55
|
+
1. Read relevant files first (read_file). Never overwrite blindly.
|
|
56
|
+
2. Emit a short "Plan:" describing files you will touch and the approach.
|
|
57
|
+
3. Write code using write_file or patch_file.
|
|
58
|
+
4. Verify with run_command (tsc --noEmit, npm test, etc.).
|
|
59
|
+
5. Fix errors and loop until clean; summarise all changes made.
|
|
60
|
+
|
|
61
|
+
### Code Quality
|
|
62
|
+
- TypeScript with strict types. Avoid \`any\` unless unavoidable.
|
|
63
|
+
- Follow existing code style. Add JSDoc to public APIs.
|
|
64
|
+
- Short, single-responsibility functions. Self-documenting names.
|
|
65
|
+
`.trim();
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// §4 — Jobs tools reference (appended in --jobs mode)
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
export const JOBS_TOOLS_SECTION = `
|
|
70
|
+
## Available CareerVivid Tools
|
|
71
|
+
|
|
72
|
+
### Resume & Profile
|
|
73
|
+
- **get_resume** — Load the user's resume. Call FIRST before any job/search task.
|
|
74
|
+
- **list_resumes** — List all saved CareerVivid resumes.
|
|
75
|
+
- **tailor_resume** — Tailor/refine the user's resume for a specific role or JD.
|
|
76
|
+
- **delete_resume** — Permanently delete a resume (ask for confirmation first).
|
|
77
|
+
|
|
78
|
+
### Job Search & Tracker
|
|
79
|
+
- **search_jobs** — Search for jobs scored against the user's resume (call get_resume first).
|
|
80
|
+
- **save_job** — Save a job to the online Kanban board.
|
|
81
|
+
- **list_jobs** — Show the online Kanban board.
|
|
82
|
+
- **update_job_status** — Move a job through: To Apply → Applied → Interviewing → Offered/Rejected.
|
|
83
|
+
|
|
84
|
+
### Local Pipeline (CSV v2)
|
|
85
|
+
- **list_local_jobs** — Show the local pipeline (supports tier/status filters and sort_by).
|
|
86
|
+
- **add_local_job** — Add a new company to the tracker.
|
|
87
|
+
- **update_local_job** — Update any field on a job entry.
|
|
88
|
+
- **score_pipeline** — Priority-ranked view; use for "what next?" questions.
|
|
89
|
+
- **get_pipeline_metrics** — Full analytics: apply rate, avg scores, salary data, stale count.
|
|
90
|
+
- **flag_stale_jobs** — Surface cold companies with next-action recommendations.
|
|
91
|
+
|
|
92
|
+
### URL Safety (Mandatory)
|
|
93
|
+
- **verify_url** — Verify a single link is alive before sharing it.
|
|
94
|
+
- **verify_search_results** — Verify all URLs returned by search_jobs before presenting them.
|
|
95
|
+
NEVER share a link without verifying it first.
|
|
96
|
+
`.trim();
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// §5 — Browser control (appended in --jobs mode)
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
export const BROWSER_SECTION = `
|
|
101
|
+
## Browser Control
|
|
102
|
+
|
|
103
|
+
- **browser_use_agent** ⭐ PRIMARY — autonomous form-filling agent (pass URL + full resume context)
|
|
104
|
+
- browser_navigate, browser_state, browser_click, browser_type, browser_select, browser_scroll, browser_screenshot
|
|
105
|
+
|
|
106
|
+
NEVER submit a form without explicit user confirmation.
|
|
107
|
+
`.trim();
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// §6 — Autonomous execution harness (appended in --jobs mode)
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
export const JOBS_HARNESS = `
|
|
112
|
+
## Autonomous Execution Directives (Non-Negotiable)
|
|
113
|
+
|
|
114
|
+
1. DO NOT ASK FOR PERMISSION before calling mutative tools (add_local_job, update_local_job, etc.) unless the action is destructive.
|
|
115
|
+
2. Fill in missing details with sensible defaults (TBD, today's date, empty strings) rather than stalling for input.
|
|
116
|
+
3. If you catch yourself explaining what you *are going to do* instead of calling the tool — STOP and call the tool.
|
|
117
|
+
4. Complete the full workflow in a single turn. Avoid handoffs like "Would you like me to…".
|
|
118
|
+
|
|
119
|
+
## Mandatory Tool Dispatch Table
|
|
120
|
+
|
|
121
|
+
| User asks about… | Call… |
|
|
122
|
+
|-----------------------------------------------|--------------------------------|
|
|
123
|
+
| pipeline, job list, tracker | list_local_jobs |
|
|
124
|
+
| priority, what next, best ROI | score_pipeline |
|
|
125
|
+
| stats, dashboard, apply rate, search health | get_pipeline_metrics |
|
|
126
|
+
| stale, cold, neglecting, need attention | flag_stale_jobs |
|
|
127
|
+
| adding a company, new job | add_local_job |
|
|
128
|
+
| updating status, marking applied, follow-up | update_local_job |
|
|
129
|
+
| resume, background, skills, experience | get_resume |
|
|
130
|
+
| find jobs, search roles | get_resume → search_jobs |
|
|
131
|
+
`.trim();
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// §7 — Greeting protocol (shared across modes)
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
export const GREETING_PROTOCOL = `
|
|
136
|
+
## Greeting Protocol
|
|
137
|
+
|
|
138
|
+
When the user sends a generic greeting ("hey", "hi", "hello", "start"), respond ONLY with the following routing menu and do NOT call any tools:
|
|
139
|
+
|
|
140
|
+
"Hello! I'm your CareerVivid AI. What would you like to do today?
|
|
141
|
+
|
|
142
|
+
• 📄 View or update my resume
|
|
143
|
+
• 🔍 Search for job opportunities
|
|
144
|
+
• 📊 Check my job pipeline / tracker
|
|
145
|
+
• ✉️ Draft a cover letter or tailor my resume
|
|
146
|
+
• 📈 Get an overview of my job search progress
|
|
147
|
+
|
|
148
|
+
Just tell me what you need!"
|
|
149
|
+
`.trim();
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// §8 — Assembled system prompts per mode (the public API)
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
/**
|
|
154
|
+
* Returns the assembled system prompt for a given agent mode.
|
|
155
|
+
* This is the ONLY function that engineResolver.ts should call.
|
|
156
|
+
*/
|
|
157
|
+
export function buildSystemPrompt(options) {
|
|
158
|
+
if (options.jobs) {
|
|
159
|
+
return [
|
|
160
|
+
BASE_IDENTITY,
|
|
161
|
+
RESUME_SECTION,
|
|
162
|
+
JOBS_TOOLS_SECTION,
|
|
163
|
+
BROWSER_SECTION,
|
|
164
|
+
JOBS_HARNESS,
|
|
165
|
+
GREETING_PROTOCOL,
|
|
166
|
+
].join("\n\n---\n\n");
|
|
167
|
+
}
|
|
168
|
+
if (options.resume) {
|
|
169
|
+
return [
|
|
170
|
+
BASE_IDENTITY,
|
|
171
|
+
RESUME_SECTION,
|
|
172
|
+
GREETING_PROTOCOL,
|
|
173
|
+
].join("\n\n---\n\n");
|
|
174
|
+
}
|
|
175
|
+
// Default: coding / general mode
|
|
176
|
+
return [
|
|
177
|
+
BASE_IDENTITY,
|
|
178
|
+
CODING_SECTION,
|
|
179
|
+
GREETING_PROTOCOL,
|
|
180
|
+
].join("\n\n---\n\n");
|
|
181
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenAIProvider.d.ts","sourceRoot":"","sources":["../../../src/agent/providers/OpenAIProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EACf,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAyB;gBAEjC,OAAO,EAAE,qBAAqB;IAY1C,OAAO,CAAC,wBAAwB;IA2ChC,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,gBAAgB;IAgElB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"OpenAIProvider.d.ts","sourceRoot":"","sources":["../../../src/agent/providers/OpenAIProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EACf,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAyB;gBAEjC,OAAO,EAAE,qBAAqB;IAY1C,OAAO,CAAC,wBAAwB;IA2ChC,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,gBAAgB;IAgElB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;IA4DnD,cAAc,CAClB,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GACvC,OAAO,CAAC,WAAW,CAAC;CAuGxB;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,EACzE,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,GACrB,cAAc,CAqBhB"}
|
|
@@ -163,6 +163,7 @@ export class OpenAIProvider {
|
|
|
163
163
|
const text = message?.content || "";
|
|
164
164
|
const rawFnCalls = message?.tool_calls || [];
|
|
165
165
|
const functionCalls = rawFnCalls.map((tc) => ({
|
|
166
|
+
id: tc.id,
|
|
166
167
|
name: tc.function.name,
|
|
167
168
|
args: JSON.parse(tc.function.arguments || "{}"),
|
|
168
169
|
}));
|
|
@@ -171,7 +172,7 @@ export class OpenAIProvider {
|
|
|
171
172
|
if (text)
|
|
172
173
|
rawParts.push({ text });
|
|
173
174
|
for (const fc of functionCalls) {
|
|
174
|
-
rawParts.push({ functionCall: { name: fc.name, args: fc.args } });
|
|
175
|
+
rawParts.push({ functionCall: { id: fc.id, name: fc.name, args: fc.args } });
|
|
175
176
|
}
|
|
176
177
|
return {
|
|
177
178
|
text,
|
|
@@ -2,9 +2,17 @@ import { type LLMProvider } from "../../config.js";
|
|
|
2
2
|
import { QueryEngine } from "../../agent/QueryEngine.js";
|
|
3
3
|
import { CareerVividProxyEngine } from "../../agent/CareerVividProxyEngine.js";
|
|
4
4
|
import { Tool } from "../../agent/Tool.js";
|
|
5
|
+
/**
|
|
6
|
+
* Returns the assembled system prompt for the given agent mode.
|
|
7
|
+
* Delegates to the single-source-of-truth in agent/instructions.ts.
|
|
8
|
+
*
|
|
9
|
+
* To update agent behaviour — edit agent/instructions.ts.
|
|
10
|
+
* Nothing else needs to change.
|
|
11
|
+
*/
|
|
5
12
|
export declare function getSystemInstruction(options: {
|
|
6
13
|
jobs?: boolean;
|
|
7
14
|
resume?: boolean;
|
|
15
|
+
coding?: boolean;
|
|
8
16
|
}): string;
|
|
9
17
|
export declare function buildEngine(selectedProvider: LLMProvider, selectedModel: string, systemInstruction: string, tools: Tool[], thinkingBudget: number, includeThoughts: boolean, cvApiKey: string | undefined, geminiApiKey: string | undefined, project: string | undefined): QueryEngine | CareerVividProxyEngine | null;
|
|
10
18
|
export declare function printBanner(options: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engineResolver.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/engineResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"engineResolver.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/engineResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAI3C;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAE5G;AAED,wBAAgB,WAAW,CACzB,gBAAgB,EAAE,WAAW,EAC7B,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,OAAO,EACxB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,WAAW,GAAG,sBAAsB,GAAG,IAAI,CA2B7C;AAED,wBAAgB,WAAW,CACzB,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC9F,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,QAuBvB"}
|
|
@@ -1,36 +1,17 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { QueryEngine
|
|
2
|
+
import { QueryEngine } from "../../agent/QueryEngine.js";
|
|
3
3
|
import { CareerVividProxyEngine } from "../../agent/CareerVividProxyEngine.js";
|
|
4
4
|
import { MODEL_CREDIT_COST } from "./configurator.js";
|
|
5
|
+
import { buildSystemPrompt } from "../../agent/instructions.js";
|
|
6
|
+
/**
|
|
7
|
+
* Returns the assembled system prompt for the given agent mode.
|
|
8
|
+
* Delegates to the single-source-of-truth in agent/instructions.ts.
|
|
9
|
+
*
|
|
10
|
+
* To update agent behaviour — edit agent/instructions.ts.
|
|
11
|
+
* Nothing else needs to change.
|
|
12
|
+
*/
|
|
5
13
|
export function getSystemInstruction(options) {
|
|
6
|
-
|
|
7
|
-
if (options.jobs) {
|
|
8
|
-
systemInstruction = JOBS_SYSTEM_PROMPT + `
|
|
9
|
-
|
|
10
|
-
### CareerVivid Online Tracker
|
|
11
|
-
- **get_resume** — Load the user's resume (ALWAYS do this before job search or interview prep).
|
|
12
|
-
- **list_resumes** — List all CareerVivid resumes.
|
|
13
|
-
- **tailor_resume** — Tailor/refine the user's resume for a specific role or JD.
|
|
14
|
-
- **delete_resume** — Delete a resume permanently.
|
|
15
|
-
- **search_jobs** — Search for jobs scored against the user's resume. ALWAYS call get_resume first.
|
|
16
|
-
- **save_job** — Save a job to the online Kanban board.
|
|
17
|
-
- **list_jobs** — Show the online Kanban board (separate from local CSV).
|
|
18
|
-
- **update_job_status** — Move a job: To Apply → Applied → Interviewing → Offered/Rejected.
|
|
19
|
-
|
|
20
|
-
## Browser Control
|
|
21
|
-
- **browser_use_agent** ⭐ PRIMARY — autonomous form-filling agent (pass URL + full resume context)
|
|
22
|
-
- browser_navigate, browser_state, browser_click, browser_type, browser_select, browser_scroll, browser_screenshot
|
|
23
|
-
- NEVER submit a form without user confirmation`;
|
|
24
|
-
}
|
|
25
|
-
else if (options.resume) {
|
|
26
|
-
systemInstruction += `
|
|
27
|
-
|
|
28
|
-
## Resume Access
|
|
29
|
-
|
|
30
|
-
You have access to the **get_resume** tool which fetches the user's CareerVivid resume.
|
|
31
|
-
When the user asks about their resume, skills, experience, or career background, call get_resume to load it first.`;
|
|
32
|
-
}
|
|
33
|
-
return systemInstruction;
|
|
14
|
+
return buildSystemPrompt(options);
|
|
34
15
|
}
|
|
35
16
|
export function buildEngine(selectedProvider, selectedModel, systemInstruction, tools, thinkingBudget, includeThoughts, cvApiKey, geminiApiKey, project) {
|
|
36
17
|
let engine = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAA4B,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI7E,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,GAAE,MAAM,GAAG,IAAW,QAwBtF;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,WAAW,GAAG,sBAAsB,GAAG,IAAI,EACnD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9K,gBAAgB,EAAE,WAAW,EAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,GAAG,EAAE,GACX,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAA4B,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI7E,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,GAAE,MAAM,GAAG,IAAW,QAwBtF;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,WAAW,GAAG,sBAAsB,GAAG,IAAI,EACnD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9K,gBAAgB,EAAE,WAAW,EAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,GAAG,EAAE,GACX,OAAO,CAAC,IAAI,CAAC,CAmVf"}
|
|
@@ -30,6 +30,7 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
30
30
|
let sessionLimit = null;
|
|
31
31
|
let currentModel = selectedModel;
|
|
32
32
|
let pasteBuffer = [];
|
|
33
|
+
let byoHistory = []; // Track history for BYO providers
|
|
33
34
|
const ask = async () => {
|
|
34
35
|
try {
|
|
35
36
|
const promptStartTime = Date.now();
|
|
@@ -270,32 +271,56 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
270
271
|
const byoApiKey = options["api-key"] || loadConfig().llmApiKey;
|
|
271
272
|
const key = byoApiKey || getGeminiKey() || "";
|
|
272
273
|
const baseUrl = options["base-url"] || loadConfig().llmBaseUrl;
|
|
274
|
+
let provider;
|
|
273
275
|
if (selectedProvider === "anthropic") {
|
|
274
|
-
|
|
275
|
-
const result = await anthropic.generate({
|
|
276
|
-
model: currentModel,
|
|
277
|
-
history: [],
|
|
278
|
-
userTurn: { role: "user", parts: [{ text: userInput }] },
|
|
279
|
-
tools,
|
|
280
|
-
systemInstruction,
|
|
281
|
-
});
|
|
282
|
-
process.stdout.write("\r\x1b[K");
|
|
283
|
-
console.log(chalk.green(result.text));
|
|
276
|
+
provider = new AnthropicProvider({ apiKey: key });
|
|
284
277
|
}
|
|
285
278
|
else {
|
|
286
279
|
const subProvider = (selectedProvider === "openrouter" ? "openrouter" :
|
|
287
|
-
selectedProvider === "custom" ? "custom" :
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
280
|
+
selectedProvider === "custom" ? "custom" : "openai");
|
|
281
|
+
provider = createOpenAICompatibleProvider(subProvider, key, baseUrl);
|
|
282
|
+
}
|
|
283
|
+
let userTurn = { role: "user", parts: [{ text: userInput }] };
|
|
284
|
+
let round = 0;
|
|
285
|
+
while (round < 10) {
|
|
286
|
+
const result = await provider.generate({
|
|
291
287
|
model: currentModel,
|
|
292
|
-
history:
|
|
293
|
-
userTurn
|
|
288
|
+
history: byoHistory,
|
|
289
|
+
userTurn,
|
|
294
290
|
tools,
|
|
295
291
|
systemInstruction,
|
|
296
292
|
});
|
|
297
|
-
|
|
298
|
-
|
|
293
|
+
if (round === 0) {
|
|
294
|
+
process.stdout.write("\r\x1b[K"); // clear initial thinking spinner
|
|
295
|
+
}
|
|
296
|
+
if (result.text) {
|
|
297
|
+
console.log(chalk.green(result.text));
|
|
298
|
+
}
|
|
299
|
+
byoHistory.push(userTurn);
|
|
300
|
+
byoHistory.push({ role: "model", parts: result.rawParts || [{ text: result.text }] });
|
|
301
|
+
if (!result.functionCalls || result.functionCalls.length === 0) {
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
let fnResponses = [];
|
|
305
|
+
for (const fc of result.functionCalls) {
|
|
306
|
+
const allow = await handleToolCall(fc.name, fc.args);
|
|
307
|
+
if (!allow) {
|
|
308
|
+
fnResponses.push({ functionResponse: { id: fc.id, name: fc.name, response: { error: "User denied execution." } } });
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const tool = tools.find((t) => t.name === fc.name);
|
|
312
|
+
let out;
|
|
313
|
+
try {
|
|
314
|
+
out = tool ? await tool.execute(fc.args) : { error: "Tool not found" };
|
|
315
|
+
}
|
|
316
|
+
catch (e) {
|
|
317
|
+
out = { error: e.message };
|
|
318
|
+
}
|
|
319
|
+
handleToolResult(fc.name, out);
|
|
320
|
+
fnResponses.push({ functionResponse: { id: fc.id, name: fc.name, response: out } });
|
|
321
|
+
}
|
|
322
|
+
userTurn = { role: "user", parts: fnResponses };
|
|
323
|
+
round++;
|
|
299
324
|
}
|
|
300
325
|
}
|
|
301
326
|
return ask();
|