careervivid 1.12.3 → 1.12.4
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/CareerVividProxyEngine.d.ts +73 -0
- package/dist/agent/CareerVividProxyEngine.d.ts.map +1 -0
- package/dist/agent/CareerVividProxyEngine.js +320 -0
- package/dist/agent/QueryEngine.d.ts +1 -0
- package/dist/agent/QueryEngine.d.ts.map +1 -1
- package/dist/agent/QueryEngine.js +130 -2
- package/dist/agent/providers/AnthropicProvider.js +2 -2
- package/dist/agent/providers/LLMProvider.d.ts +2 -0
- package/dist/agent/providers/LLMProvider.d.ts.map +1 -1
- package/dist/agent/providers/OpenAIProvider.js +2 -2
- package/dist/agent/tools/browser.d.ts +22 -0
- package/dist/agent/tools/browser.d.ts.map +1 -0
- package/dist/agent/tools/browser.js +666 -0
- package/dist/agent/tools/coding.d.ts.map +1 -1
- package/dist/agent/tools/coding.js +25 -0
- package/dist/agent/tools/jobs.d.ts +1 -0
- package/dist/agent/tools/jobs.d.ts.map +1 -1
- package/dist/agent/tools/jobs.js +211 -0
- package/dist/agent/tools/local-tracker.d.ts +29 -0
- package/dist/agent/tools/local-tracker.d.ts.map +1 -0
- package/dist/agent/tools/local-tracker.js +777 -0
- package/dist/api.d.ts +31 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +7 -0
- package/dist/apply/adapters/ashby.d.ts +21 -0
- package/dist/apply/adapters/ashby.d.ts.map +1 -0
- package/dist/apply/adapters/ashby.js +209 -0
- package/dist/apply/adapters/generic.d.ts +24 -0
- package/dist/apply/adapters/generic.d.ts.map +1 -0
- package/dist/apply/adapters/generic.js +249 -0
- package/dist/apply/adapters/greenhouse.d.ts +31 -0
- package/dist/apply/adapters/greenhouse.d.ts.map +1 -0
- package/dist/apply/adapters/greenhouse.js +285 -0
- package/dist/apply/adapters/icims.d.ts +26 -0
- package/dist/apply/adapters/icims.d.ts.map +1 -0
- package/dist/apply/adapters/icims.js +191 -0
- package/dist/apply/adapters/lever.d.ts +16 -0
- package/dist/apply/adapters/lever.d.ts.map +1 -0
- package/dist/apply/adapters/lever.js +88 -0
- package/dist/apply/adapters/linkedin.d.ts +22 -0
- package/dist/apply/adapters/linkedin.d.ts.map +1 -0
- package/dist/apply/adapters/linkedin.js +135 -0
- package/dist/apply/browser.d.ts +56 -0
- package/dist/apply/browser.d.ts.map +1 -0
- package/dist/apply/browser.js +164 -0
- package/dist/apply/gemini-agent.d.ts +85 -0
- package/dist/apply/gemini-agent.d.ts.map +1 -0
- package/dist/apply/gemini-agent.js +319 -0
- package/dist/apply/index.d.ts +35 -0
- package/dist/apply/index.d.ts.map +1 -0
- package/dist/apply/index.js +52 -0
- package/dist/commands/agent/configurator.d.ts +22 -0
- package/dist/commands/agent/configurator.d.ts.map +1 -0
- package/dist/commands/agent/configurator.js +190 -0
- package/dist/commands/agent/engineResolver.d.ts +17 -0
- package/dist/commands/agent/engineResolver.d.ts.map +1 -0
- package/dist/commands/agent/engineResolver.js +87 -0
- package/dist/commands/agent/index.d.ts +3 -0
- package/dist/commands/agent/index.d.ts.map +1 -0
- package/dist/commands/agent/index.js +99 -0
- package/dist/commands/agent/repl.d.ts +16 -0
- package/dist/commands/agent/repl.d.ts.map +1 -0
- package/dist/commands/agent/repl.js +313 -0
- package/dist/commands/agent/toolRegistry.d.ts +7 -0
- package/dist/commands/agent/toolRegistry.d.ts.map +1 -0
- package/dist/commands/agent/toolRegistry.js +84 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +201 -95
- package/dist/commands/apply.d.ts +15 -0
- package/dist/commands/apply.d.ts.map +1 -0
- package/dist/commands/apply.js +667 -0
- package/dist/commands/eval.d.ts +17 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/eval.js +145 -0
- package/dist/commands/jobs.d.ts.map +1 -1
- package/dist/commands/jobs.js +3 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +3 -0
- package/dist/eval/index.d.ts +17 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +15 -0
- package/dist/eval/runner.d.ts +39 -0
- package/dist/eval/runner.d.ts.map +1 -0
- package/dist/eval/runner.js +349 -0
- package/dist/eval/scorer.d.ts +64 -0
- package/dist/eval/scorer.d.ts.map +1 -0
- package/dist/eval/scorer.js +245 -0
- package/dist/eval/storage/CsvDataLogger.d.ts +37 -0
- package/dist/eval/storage/CsvDataLogger.d.ts.map +1 -0
- package/dist/eval/storage/CsvDataLogger.js +162 -0
- package/dist/eval/storage/FirebaseDataLogger.d.ts +48 -0
- package/dist/eval/storage/FirebaseDataLogger.d.ts.map +1 -0
- package/dist/eval/storage/FirebaseDataLogger.js +91 -0
- package/dist/eval/storage/IDataLogger.d.ts +48 -0
- package/dist/eval/storage/IDataLogger.d.ts.map +1 -0
- package/dist/eval/storage/IDataLogger.js +18 -0
- package/dist/eval/test-cases/base-agent.d.ts +11 -0
- package/dist/eval/test-cases/base-agent.d.ts.map +1 -0
- package/dist/eval/test-cases/base-agent.js +140 -0
- package/dist/eval/test-cases/index.d.ts +19 -0
- package/dist/eval/test-cases/index.d.ts.map +1 -0
- package/dist/eval/test-cases/index.js +25 -0
- package/dist/eval/test-cases/jobs-agent.d.ts +15 -0
- package/dist/eval/test-cases/jobs-agent.d.ts.map +1 -0
- package/dist/eval/test-cases/jobs-agent.js +309 -0
- package/dist/eval/test-cases/resume-agent.d.ts +16 -0
- package/dist/eval/test-cases/resume-agent.d.ts.map +1 -0
- package/dist/eval/test-cases/resume-agent.js +137 -0
- package/dist/eval/types.d.ts +163 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +18 -0
- package/dist/index.js +3 -1
- package/package.json +8 -7
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CareerVividProxyEngine
|
|
3
|
+
*
|
|
4
|
+
* A drop-in replacement for QueryEngine that routes all Gemini API calls
|
|
5
|
+
* through the CareerVivid `agentProxy` Firebase function instead of calling
|
|
6
|
+
* Gemini directly. This allows users without a personal Gemini key to use
|
|
7
|
+
* the AI agent via their CareerVivid account credits.
|
|
8
|
+
*
|
|
9
|
+
* The proxy handles:
|
|
10
|
+
* - Authentication (via cv_live_... API key)
|
|
11
|
+
* - Credit deduction (atomically with each Gemini call)
|
|
12
|
+
* - Gemini API call (server-side, using the CareerVivid Gemini secret)
|
|
13
|
+
*
|
|
14
|
+
* The engine handles:
|
|
15
|
+
* - History management
|
|
16
|
+
* - Tool execution (locally, in the CLI)
|
|
17
|
+
* - Agentic loop (multi-turn tool calls)
|
|
18
|
+
* - Context compaction
|
|
19
|
+
*/
|
|
20
|
+
import type { Content } from "@google/genai";
|
|
21
|
+
import { Tool } from "./Tool.js";
|
|
22
|
+
export interface ProxyEngineOptions {
|
|
23
|
+
cvApiKey: string;
|
|
24
|
+
model?: string;
|
|
25
|
+
systemInstruction?: string;
|
|
26
|
+
tools?: Tool[];
|
|
27
|
+
thinkingBudget?: number;
|
|
28
|
+
includeThoughts?: boolean;
|
|
29
|
+
maxHistoryLength?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface ProxyIterationHook {
|
|
32
|
+
onStart?: () => void;
|
|
33
|
+
onResponse?: (creditInfo: {
|
|
34
|
+
creditsUsed: number;
|
|
35
|
+
creditsRemaining: number;
|
|
36
|
+
monthlyLimit: number;
|
|
37
|
+
}) => void | Promise<void>;
|
|
38
|
+
onToolCall?: (toolName: string, args: any) => Promise<boolean | void>;
|
|
39
|
+
onToolResult?: (toolName: string, result: any) => void;
|
|
40
|
+
onError?: (error: Error) => void;
|
|
41
|
+
onChunk?: (text: string) => void;
|
|
42
|
+
onThinking?: (thought: string) => void;
|
|
43
|
+
onCompacting?: () => void;
|
|
44
|
+
/** Fired when credit limit is reached — engine will stop the loop */
|
|
45
|
+
onCreditLimitReached?: (remaining: number) => void;
|
|
46
|
+
}
|
|
47
|
+
export declare class CareerVividProxyEngine {
|
|
48
|
+
private cvApiKey;
|
|
49
|
+
private model;
|
|
50
|
+
private systemInstruction;
|
|
51
|
+
private tools;
|
|
52
|
+
private toolMap;
|
|
53
|
+
private thinkingBudget;
|
|
54
|
+
private includeThoughts;
|
|
55
|
+
private maxHistoryLength;
|
|
56
|
+
private history;
|
|
57
|
+
private sessionCreditsUsed;
|
|
58
|
+
private lastKnownRemaining;
|
|
59
|
+
private monthlyLimit;
|
|
60
|
+
constructor(options: ProxyEngineOptions);
|
|
61
|
+
getHistory(): Content[];
|
|
62
|
+
setHistory(history: Content[]): void;
|
|
63
|
+
/** Credits used in this session */
|
|
64
|
+
get sessionUsed(): number;
|
|
65
|
+
/** Last known remaining credits */
|
|
66
|
+
get remaining(): number | null;
|
|
67
|
+
private callProxy;
|
|
68
|
+
private compactHistory;
|
|
69
|
+
private executeToolCalls;
|
|
70
|
+
runLoopStreaming(prompt: string, hooks?: ProxyIterationHook): Promise<string>;
|
|
71
|
+
runLoop(prompt: string, hooks?: ProxyIterationHook): Promise<string>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=CareerVividProxyEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CareerVividProxyEngine.d.ts","sourceRoot":"","sources":["../../src/agent/CareerVividProxyEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAQ,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;AAcrD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,YAAY,EAAE,MAAM,CAAC;KACtB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,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,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD;AAoCD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,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;IACjC,OAAO,CAAC,OAAO,CAAiB;IAGhC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,kBAAkB,CAAuB;IACjD,OAAO,CAAC,YAAY,CAAuB;gBAE/B,OAAO,EAAE,kBAAkB;IAWhC,UAAU,IAAI,OAAO,EAAE;IAIvB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE;IAIpC,mCAAmC;IACnC,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,mCAAmC;IACnC,IAAI,SAAS,IAAI,MAAM,GAAG,IAAI,CAE7B;YAMa,SAAS;YAqDT,cAAc;YAed,gBAAgB;IA6DjB,gBAAgB,CAC3B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC;IAQL,OAAO,CAClB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC;CAwInB"}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CareerVividProxyEngine
|
|
3
|
+
*
|
|
4
|
+
* A drop-in replacement for QueryEngine that routes all Gemini API calls
|
|
5
|
+
* through the CareerVivid `agentProxy` Firebase function instead of calling
|
|
6
|
+
* Gemini directly. This allows users without a personal Gemini key to use
|
|
7
|
+
* the AI agent via their CareerVivid account credits.
|
|
8
|
+
*
|
|
9
|
+
* The proxy handles:
|
|
10
|
+
* - Authentication (via cv_live_... API key)
|
|
11
|
+
* - Credit deduction (atomically with each Gemini call)
|
|
12
|
+
* - Gemini API call (server-side, using the CareerVivid Gemini secret)
|
|
13
|
+
*
|
|
14
|
+
* The engine handles:
|
|
15
|
+
* - History management
|
|
16
|
+
* - Tool execution (locally, in the CLI)
|
|
17
|
+
* - Agentic loop (multi-turn tool calls)
|
|
18
|
+
* - Context compaction
|
|
19
|
+
*/
|
|
20
|
+
import { convertToGenAITool } from "./Tool.js";
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
// Constants
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
const FIREBASE_REGION = "us-west1";
|
|
25
|
+
const FIREBASE_PROJECT_ID = "jastalk-firebase";
|
|
26
|
+
const PROXY_URL = `https://${FIREBASE_REGION}-${FIREBASE_PROJECT_ID}.cloudfunctions.net/agentProxy`;
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
// Retry utility
|
|
29
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
async function sleep(ms) {
|
|
31
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
32
|
+
}
|
|
33
|
+
async function retryWithBackoff(fn, maxRetries = 3, baseDelayMs = 1000) {
|
|
34
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
35
|
+
try {
|
|
36
|
+
return await fn();
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
const isRetryable = e?.status === 429 ||
|
|
40
|
+
e?.status === 503 ||
|
|
41
|
+
/rate.?limit|quota|too.?many.?requests|service.?unavailable/i.test(e?.message || "");
|
|
42
|
+
if (attempt === maxRetries || !isRetryable)
|
|
43
|
+
throw e;
|
|
44
|
+
await sleep(baseDelayMs * Math.pow(2, attempt));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throw new Error("retryWithBackoff: unreachable");
|
|
48
|
+
}
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
// CareerVividProxyEngine
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
|
+
export class CareerVividProxyEngine {
|
|
53
|
+
cvApiKey;
|
|
54
|
+
model;
|
|
55
|
+
systemInstruction;
|
|
56
|
+
tools;
|
|
57
|
+
toolMap;
|
|
58
|
+
thinkingBudget;
|
|
59
|
+
includeThoughts;
|
|
60
|
+
maxHistoryLength;
|
|
61
|
+
history = [];
|
|
62
|
+
// Running credit totals for the session
|
|
63
|
+
sessionCreditsUsed = 0;
|
|
64
|
+
lastKnownRemaining = null;
|
|
65
|
+
monthlyLimit = null;
|
|
66
|
+
constructor(options) {
|
|
67
|
+
this.cvApiKey = options.cvApiKey;
|
|
68
|
+
this.model = options.model ?? "gemini-2.5-flash";
|
|
69
|
+
this.systemInstruction = options.systemInstruction ?? "";
|
|
70
|
+
this.tools = options.tools ?? [];
|
|
71
|
+
this.toolMap = new Map(this.tools.map((t) => [t.name, t]));
|
|
72
|
+
this.thinkingBudget = options.thinkingBudget ?? 0;
|
|
73
|
+
this.includeThoughts = options.includeThoughts ?? false;
|
|
74
|
+
this.maxHistoryLength = options.maxHistoryLength ?? 40;
|
|
75
|
+
}
|
|
76
|
+
getHistory() {
|
|
77
|
+
return this.history;
|
|
78
|
+
}
|
|
79
|
+
setHistory(history) {
|
|
80
|
+
this.history = history;
|
|
81
|
+
}
|
|
82
|
+
/** Credits used in this session */
|
|
83
|
+
get sessionUsed() {
|
|
84
|
+
return this.sessionCreditsUsed;
|
|
85
|
+
}
|
|
86
|
+
/** Last known remaining credits */
|
|
87
|
+
get remaining() {
|
|
88
|
+
return this.lastKnownRemaining;
|
|
89
|
+
}
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
91
|
+
// Private helpers
|
|
92
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
93
|
+
async callProxy(contents) {
|
|
94
|
+
const genAITools = this.tools.map(convertToGenAITool);
|
|
95
|
+
const body = {
|
|
96
|
+
apiKey: this.cvApiKey,
|
|
97
|
+
model: this.model,
|
|
98
|
+
contents,
|
|
99
|
+
systemInstruction: this.systemInstruction || undefined,
|
|
100
|
+
};
|
|
101
|
+
if (genAITools.length > 0) {
|
|
102
|
+
// @google/generative-ai format: { functionDeclarations: [...] }
|
|
103
|
+
body.tools = [{ functionDeclarations: genAITools.flatMap((t) => t.functionDeclarations ?? [t]) }];
|
|
104
|
+
}
|
|
105
|
+
if (this.thinkingBudget > 0) {
|
|
106
|
+
body.thinkingBudget = this.thinkingBudget;
|
|
107
|
+
body.includeThoughts = this.includeThoughts;
|
|
108
|
+
}
|
|
109
|
+
const response = await fetch(PROXY_URL, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: { "Content-Type": "application/json" },
|
|
112
|
+
body: JSON.stringify(body),
|
|
113
|
+
});
|
|
114
|
+
if (response.status === 402) {
|
|
115
|
+
const data = await response.json();
|
|
116
|
+
return {
|
|
117
|
+
creditsUsed: 0,
|
|
118
|
+
creditsRemaining: data.creditsRemaining ?? 0,
|
|
119
|
+
monthlyLimit: data.monthlyLimit ?? 100,
|
|
120
|
+
reason: "limit_reached",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
const data = await response.json().catch(() => ({}));
|
|
125
|
+
throw new Error(data.error || `Proxy error: HTTP ${response.status}`);
|
|
126
|
+
}
|
|
127
|
+
return response.json();
|
|
128
|
+
}
|
|
129
|
+
async compactHistory() {
|
|
130
|
+
// Simple compaction: keep the last 20 turns
|
|
131
|
+
if (this.history.length > this.maxHistoryLength) {
|
|
132
|
+
const summary = {
|
|
133
|
+
role: "user",
|
|
134
|
+
parts: [
|
|
135
|
+
{
|
|
136
|
+
text: `[Context compacted — ${this.history.length} prior turns summarized to save context space. Continue the conversation normally.]`,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
this.history = [summary, ...this.history.slice(-20)];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async executeToolCalls(functionCalls, hooks) {
|
|
144
|
+
const functionResponses = [];
|
|
145
|
+
for (const call of functionCalls) {
|
|
146
|
+
const toolName = call.name || "";
|
|
147
|
+
const args = call.args || {};
|
|
148
|
+
const tool = this.toolMap.get(toolName);
|
|
149
|
+
if (!tool) {
|
|
150
|
+
functionResponses.push({
|
|
151
|
+
functionResponse: {
|
|
152
|
+
name: toolName,
|
|
153
|
+
response: {
|
|
154
|
+
error: `Tool "${toolName}" not found. Available: ${[...this.toolMap.keys()].join(", ")}`,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (hooks?.onToolCall) {
|
|
161
|
+
const allow = await hooks.onToolCall(toolName, args);
|
|
162
|
+
if (allow === false) {
|
|
163
|
+
functionResponses.push({
|
|
164
|
+
functionResponse: {
|
|
165
|
+
name: toolName,
|
|
166
|
+
response: { error: "User denied tool execution." },
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const result = await tool.execute(args);
|
|
174
|
+
if (hooks?.onToolResult)
|
|
175
|
+
hooks.onToolResult(toolName, result);
|
|
176
|
+
functionResponses.push({
|
|
177
|
+
functionResponse: { name: toolName, response: { result } },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
functionResponses.push({
|
|
182
|
+
functionResponse: {
|
|
183
|
+
name: toolName,
|
|
184
|
+
response: { error: String(err.message || err) },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return functionResponses;
|
|
190
|
+
}
|
|
191
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
192
|
+
// Public: runLoopStreaming (interface-compatible with QueryEngine)
|
|
193
|
+
// Note: The proxy does not stream — text is printed all at once after each
|
|
194
|
+
// proxy call. True streaming requires a different proxy endpoint.
|
|
195
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
196
|
+
async runLoopStreaming(prompt, hooks) {
|
|
197
|
+
return this.runLoop(prompt, hooks);
|
|
198
|
+
}
|
|
199
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
200
|
+
// Public: runLoop
|
|
201
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
202
|
+
async runLoop(prompt, hooks) {
|
|
203
|
+
this.history.push({ role: "user", parts: [{ text: prompt }] });
|
|
204
|
+
let maxIterations = 30;
|
|
205
|
+
let iterations = 0;
|
|
206
|
+
let finalAnswer = "";
|
|
207
|
+
while (iterations < maxIterations) {
|
|
208
|
+
iterations++;
|
|
209
|
+
if (hooks?.onStart)
|
|
210
|
+
hooks.onStart();
|
|
211
|
+
// Compact if needed
|
|
212
|
+
if (this.history.length > this.maxHistoryLength) {
|
|
213
|
+
if (hooks?.onCompacting)
|
|
214
|
+
hooks.onCompacting();
|
|
215
|
+
await this.compactHistory();
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const proxyResult = await retryWithBackoff(() => this.callProxy(this.history));
|
|
219
|
+
// Credit limit reached
|
|
220
|
+
if (proxyResult.reason === "limit_reached") {
|
|
221
|
+
this.lastKnownRemaining = proxyResult.creditsRemaining;
|
|
222
|
+
if (hooks?.onCreditLimitReached) {
|
|
223
|
+
hooks.onCreditLimitReached(proxyResult.creditsRemaining);
|
|
224
|
+
}
|
|
225
|
+
finalAnswer = "❌ Credit limit reached. Please upgrade or top up at careervivid.app/developer";
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
// Update credit tracking
|
|
229
|
+
this.sessionCreditsUsed += proxyResult.creditsUsed ?? 0;
|
|
230
|
+
this.lastKnownRemaining = proxyResult.creditsRemaining;
|
|
231
|
+
this.monthlyLimit = proxyResult.monthlyLimit;
|
|
232
|
+
if (hooks?.onResponse) {
|
|
233
|
+
await hooks.onResponse({
|
|
234
|
+
creditsUsed: proxyResult.creditsUsed,
|
|
235
|
+
creditsRemaining: proxyResult.creditsRemaining,
|
|
236
|
+
monthlyLimit: proxyResult.monthlyLimit,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
// Parse Gemini response
|
|
240
|
+
const candidate = proxyResult.candidates?.[0];
|
|
241
|
+
if (!candidate) {
|
|
242
|
+
finalAnswer = "No response from Gemini.";
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
const parts = candidate.content?.parts ?? [];
|
|
246
|
+
// Add model response to history
|
|
247
|
+
this.history.push({
|
|
248
|
+
role: "model",
|
|
249
|
+
parts: parts.length > 0 ? parts : [{ text: "" }],
|
|
250
|
+
});
|
|
251
|
+
// Extract thinking text
|
|
252
|
+
if (this.includeThoughts && hooks?.onThinking) {
|
|
253
|
+
const thoughtParts = parts.filter((p) => p.thought);
|
|
254
|
+
for (const part of thoughtParts) {
|
|
255
|
+
if (part.text)
|
|
256
|
+
hooks.onThinking(part.text);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Extract function calls
|
|
260
|
+
const functionCalls = parts
|
|
261
|
+
.filter((p) => p.functionCall)
|
|
262
|
+
.map((p) => ({
|
|
263
|
+
name: p.functionCall.name,
|
|
264
|
+
args: p.functionCall.args ?? {},
|
|
265
|
+
}));
|
|
266
|
+
if (functionCalls.length > 0) {
|
|
267
|
+
// Tool call turn — execute tools and loop
|
|
268
|
+
const functionResponses = await this.executeToolCalls(functionCalls, hooks);
|
|
269
|
+
this.history.push({ role: "user", parts: functionResponses });
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
// Text response — emit chunks and break
|
|
273
|
+
const textParts = parts
|
|
274
|
+
.filter((p) => p.text && !p.thought)
|
|
275
|
+
.map((p) => p.text);
|
|
276
|
+
const fullText = textParts.join("");
|
|
277
|
+
// [Harness Engineering] Prevent lazy conversational exits for Jobs Agent
|
|
278
|
+
if (this.systemInstruction && this.systemInstruction.includes("DO NOT ASK FOR PERMISSION")) {
|
|
279
|
+
const textOut = fullText;
|
|
280
|
+
const lowerText = textOut.toLowerCase();
|
|
281
|
+
const askingPermission = lowerText.includes("would you like me to") ||
|
|
282
|
+
lowerText.includes("how would you like to proceed") ||
|
|
283
|
+
lowerText.includes("should i go ahead") ||
|
|
284
|
+
lowerText.includes("shall i") ||
|
|
285
|
+
lowerText.includes("could you please provide") ||
|
|
286
|
+
lowerText.includes("i need your permission") ||
|
|
287
|
+
lowerText.includes("please provide") ||
|
|
288
|
+
lowerText.includes("could you provide");
|
|
289
|
+
if (askingPermission && iterations < maxIterations - 1) {
|
|
290
|
+
if (hooks?.onThinking)
|
|
291
|
+
hooks.onThinking("Harness intercepted permission seeking. Auto-correcting...");
|
|
292
|
+
this.history.push({
|
|
293
|
+
role: 'user',
|
|
294
|
+
parts: [{ text: "[Harness Instruction]: You halted execution to ask for permission or missing information. RE-READ YOUR HARNESS DIRECTIVES. You must autonomously execute the mutation (e.g., update_local_job or add_local_job) using defaults for missing fields. Do not wait for confirmation. Do it now." }]
|
|
295
|
+
});
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Emit as a "chunk" so the caller's onChunk handler can print it
|
|
300
|
+
if (hooks?.onChunk && fullText) {
|
|
301
|
+
hooks.onChunk(fullText);
|
|
302
|
+
}
|
|
303
|
+
finalAnswer = fullText;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
if (hooks?.onError)
|
|
308
|
+
hooks.onError(e);
|
|
309
|
+
finalAnswer = `Generation Error: ${e.message}`;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (iterations >= maxIterations) {
|
|
314
|
+
finalAnswer =
|
|
315
|
+
finalAnswer ||
|
|
316
|
+
"Max iterations (30) exceeded without a final answer.";
|
|
317
|
+
}
|
|
318
|
+
return finalAnswer;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GenerateContentResponse, Content } from '@google/genai';
|
|
2
2
|
import { Tool } from './Tool.js';
|
|
3
3
|
export declare const CODING_AGENT_SYSTEM_PROMPT: string;
|
|
4
|
+
export declare const JOBS_SYSTEM_PROMPT: string;
|
|
4
5
|
export interface QueryEngineOptions {
|
|
5
6
|
model?: string;
|
|
6
7
|
systemInstruction?: 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;AAMrD,eAAO,MAAM,0BAA0B,QA8B/B,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;AAMrD,eAAO,MAAM,0BAA0B,QA8B/B,CAAC;AAKT,eAAO,MAAM,kBAAkB,QAgFI,CAAC;AAMpC,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"}
|
|
@@ -36,6 +36,90 @@ For every coding task, follow this loop:
|
|
|
36
36
|
- Keep functions short and focused on a single responsibility.
|
|
37
37
|
`.trim();
|
|
38
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
|
+
## Greeting Protocol
|
|
107
|
+
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:
|
|
108
|
+
|
|
109
|
+
"Hello! How can I help you today? Are you looking to:
|
|
110
|
+
|
|
111
|
+
View your job pipeline?
|
|
112
|
+
|
|
113
|
+
Find new job opportunities?
|
|
114
|
+
|
|
115
|
+
Update a job's status?
|
|
116
|
+
|
|
117
|
+
Tailor your resume?
|
|
118
|
+
|
|
119
|
+
Get an overview of your job search progress?
|
|
120
|
+
|
|
121
|
+
Let me know what you need!"`.trim();
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
39
123
|
// Retry utility
|
|
40
124
|
// ---------------------------------------------------------------------------
|
|
41
125
|
const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
|
|
@@ -240,7 +324,29 @@ export class QueryEngine {
|
|
|
240
324
|
: [{ text: response.text || '' }],
|
|
241
325
|
});
|
|
242
326
|
if (!hasToolCalls) {
|
|
243
|
-
|
|
327
|
+
const textOut = response.text || '';
|
|
328
|
+
// [Harness Engineering] Prevent lazy conversational exits for Jobs Agent
|
|
329
|
+
if (this.systemInstruction.includes("DO NOT ASK FOR PERMISSION")) {
|
|
330
|
+
const lowerText = textOut.toLowerCase();
|
|
331
|
+
const askingPermission = lowerText.includes("would you like me to") ||
|
|
332
|
+
lowerText.includes("how would you like to proceed") ||
|
|
333
|
+
lowerText.includes("should i go ahead") ||
|
|
334
|
+
lowerText.includes("shall i") ||
|
|
335
|
+
lowerText.includes("could you please provide") ||
|
|
336
|
+
lowerText.includes("i need your permission") ||
|
|
337
|
+
lowerText.includes("please provide") ||
|
|
338
|
+
lowerText.includes("could you provide");
|
|
339
|
+
if (askingPermission && iterations < maxIterations - 1) {
|
|
340
|
+
if (hooks?.onThinking)
|
|
341
|
+
hooks.onThinking("Harness intercepted permission seeking. Auto-correcting...");
|
|
342
|
+
this.history.push({
|
|
343
|
+
role: 'user',
|
|
344
|
+
parts: [{ text: "[Harness Instruction]: You halted execution to ask for permission or missing information. RE-READ YOUR HARNESS DIRECTIVES. You must autonomously execute the mutation (e.g., update_local_job or add_local_job) using defaults for missing fields. Do not wait for confirmation. Do it now." }]
|
|
345
|
+
});
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
finalAnswer = textOut;
|
|
244
350
|
break;
|
|
245
351
|
}
|
|
246
352
|
const functionResponses = await this.executeToolCalls(functionCalls, hooks);
|
|
@@ -331,7 +437,29 @@ export class QueryEngine {
|
|
|
331
437
|
if (hooks?.onResponse)
|
|
332
438
|
await hooks.onResponse(undefined);
|
|
333
439
|
if (!hasToolCalls) {
|
|
334
|
-
|
|
440
|
+
const textOut = accumulatedText;
|
|
441
|
+
// [Harness Engineering] Prevent lazy conversational exits for Jobs Agent
|
|
442
|
+
if (this.systemInstruction.includes("DO NOT ASK FOR PERMISSION")) {
|
|
443
|
+
const lowerText = textOut.toLowerCase();
|
|
444
|
+
const askingPermission = lowerText.includes("would you like me to") ||
|
|
445
|
+
lowerText.includes("how would you like to proceed") ||
|
|
446
|
+
lowerText.includes("should i go ahead") ||
|
|
447
|
+
lowerText.includes("shall i") ||
|
|
448
|
+
lowerText.includes("could you please provide") ||
|
|
449
|
+
lowerText.includes("i need your permission") ||
|
|
450
|
+
lowerText.includes("please provide") ||
|
|
451
|
+
lowerText.includes("could you provide");
|
|
452
|
+
if (askingPermission && iterations < maxIterations - 1) {
|
|
453
|
+
if (hooks?.onThinking)
|
|
454
|
+
hooks.onThinking("Harness intercepted permission seeking. Auto-correcting...");
|
|
455
|
+
this.history.push({
|
|
456
|
+
role: 'user',
|
|
457
|
+
parts: [{ text: "[Harness Instruction]: You halted execution to ask for permission or missing information. RE-READ YOUR HARNESS DIRECTIVES. You must autonomously execute the mutation (e.g., update_local_job or add_local_job) using defaults for missing fields. Do not wait for confirmation. Do it now." }]
|
|
458
|
+
});
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
finalAnswer = textOut;
|
|
335
463
|
break;
|
|
336
464
|
}
|
|
337
465
|
const functionResponses = await this.executeToolCalls(accumulatedFunctionCalls, hooks);
|
|
@@ -83,7 +83,7 @@ export class AnthropicProvider {
|
|
|
83
83
|
const messages = this.toAnthropicMessages(history, userTurn);
|
|
84
84
|
const anthropicTools = tools.length > 0 ? this.toAnthropicTools(tools) : undefined;
|
|
85
85
|
const body = {
|
|
86
|
-
model: "", // set by caller
|
|
86
|
+
model: request.model || "", // set by caller
|
|
87
87
|
max_tokens: 8192,
|
|
88
88
|
system: systemInstruction,
|
|
89
89
|
messages,
|
|
@@ -136,7 +136,7 @@ export class AnthropicProvider {
|
|
|
136
136
|
const messages = this.toAnthropicMessages(history, userTurn);
|
|
137
137
|
const anthropicTools = tools.length > 0 ? this.toAnthropicTools(tools) : undefined;
|
|
138
138
|
const body = {
|
|
139
|
-
model: "",
|
|
139
|
+
model: request.model || "",
|
|
140
140
|
max_tokens: 8192,
|
|
141
141
|
system: systemInstruction,
|
|
142
142
|
messages,
|
|
@@ -21,6 +21,8 @@ export interface LLMRequest {
|
|
|
21
21
|
thinkingBudget?: number;
|
|
22
22
|
/** Whether to include thinking text */
|
|
23
23
|
includeThoughts?: boolean;
|
|
24
|
+
/** Optional model override for BYO providers */
|
|
25
|
+
model?: string;
|
|
24
26
|
}
|
|
25
27
|
export interface LLMResponse {
|
|
26
28
|
/** Text response parts from the model */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LLMProvider.d.ts","sourceRoot":"","sources":["../../../src/agent/providers/LLMProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAC;IAClB,uCAAuC;IACvC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,yBAAyB;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,eAAe,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"LLMProvider.d.ts","sourceRoot":"","sources":["../../../src/agent/providers/LLMProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAC;IAClB,uCAAuC;IACvC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,yBAAyB;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAClD,+EAA+E;IAC/E,QAAQ,EAAE,IAAI,EAAE,CAAC;IACjB,kBAAkB;IAClB,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,YAAY,GAAG,OAAO,CAAC;CAC5D;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IACnD,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAEpD;;;OAGG;IACH,cAAc,CACZ,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GACvC,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB"}
|
|
@@ -97,7 +97,7 @@ export class OpenAIProvider {
|
|
|
97
97
|
const messages = this.toOpenAIMessages(history, userTurn, systemInstruction);
|
|
98
98
|
const openAITools = tools.length > 0 ? this.toOpenAITools(tools) : undefined;
|
|
99
99
|
const body = {
|
|
100
|
-
model: "", //
|
|
100
|
+
model: request.model || "", // fallback to empty if missing
|
|
101
101
|
messages,
|
|
102
102
|
...(openAITools ? { tools: openAITools, tool_choice: "auto" } : {}),
|
|
103
103
|
};
|
|
@@ -147,7 +147,7 @@ export class OpenAIProvider {
|
|
|
147
147
|
const messages = this.toOpenAIMessages(history, userTurn, systemInstruction);
|
|
148
148
|
const openAITools = tools.length > 0 ? this.toOpenAITools(tools) : undefined;
|
|
149
149
|
const body = {
|
|
150
|
-
model: "",
|
|
150
|
+
model: request.model || "",
|
|
151
151
|
messages,
|
|
152
152
|
stream: true,
|
|
153
153
|
...(openAITools ? { tools: openAITools, tool_choice: "auto" } : {}),
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser.ts — Browser control tools for the CareerVivid Agent.
|
|
3
|
+
*
|
|
4
|
+
* These tools give the Gemini agent interactive browser control, enabling it
|
|
5
|
+
* to navigate pages, read interactive elements, fill forms, click buttons,
|
|
6
|
+
* and handle multi-page application workflows — just like Antigravity or
|
|
7
|
+
* Claude Desktop's Computer Use.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* - Uses a DEDICATED automation Chromium profile (never conflicts with real Chrome)
|
|
11
|
+
* - Sessions persist via ~/.careervivid/browser-session/ between agent runs
|
|
12
|
+
* - Each tool is a standard Tool object that plugs into QueryEngine's tool loop
|
|
13
|
+
* - DOM is simplified to a numbered Accessibility Object Model (AOM) for the LLM
|
|
14
|
+
*
|
|
15
|
+
* Why NOT the real Chrome profile:
|
|
16
|
+
* Chrome locks its profile while running. Attempting launchPersistentContext
|
|
17
|
+
* on the real profile while Chrome is open causes a SIGTRAP crash.
|
|
18
|
+
* The dedicated profile avoids this entirely and is always available.
|
|
19
|
+
*/
|
|
20
|
+
import { Tool } from "../Tool.js";
|
|
21
|
+
export declare const ALL_BROWSER_TOOLS: Tool[];
|
|
22
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAysBlC,eAAO,MAAM,iBAAiB,EAAE,IAAI,EAanC,CAAC"}
|