glitool 2.0.3 → 2.0.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.js +14 -1
- package/dist/llm/factory.js +9 -5
- package/dist/monitor.js +9 -0
- package/dist/ui/StatusBar.js +11 -1
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -19,8 +19,8 @@ import { runPlanningAgent } from "./agents/planningAgent.js";
|
|
|
19
19
|
import { runDebugger } from "./agents/debugger.js";
|
|
20
20
|
import { runRefactorer } from "./agents/refactorer.js";
|
|
21
21
|
import { runGitAgent } from "./agents/git-agent.js";
|
|
22
|
-
import { ToolMessage } from "@langchain/core/messages";
|
|
23
22
|
import { makeLlm, startNewRequest } from './llm/factory.js';
|
|
23
|
+
import { emit } from './monitor.js';
|
|
24
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
25
25
|
const __dirname = dirname(__filename);
|
|
26
26
|
loadEnv({ path: join(os.homedir(), '.glitool', '.env') });
|
|
@@ -195,7 +195,9 @@ function extractTarget(args) {
|
|
|
195
195
|
}
|
|
196
196
|
export async function chat(userInput, onToolCall, onStatus, onToken, onEscalation, onUsage, onStageEvent) {
|
|
197
197
|
startNewRequest();
|
|
198
|
+
emit('user_prompt', { text: userInput });
|
|
198
199
|
const decision = await route(userInput, sessionMessages.slice(-6));
|
|
200
|
+
emit('router', { domain: decision.domain, tier: decision.tier, model: decision.recommendedModel, reason: decision.reason });
|
|
199
201
|
logRouting(userInput, decision);
|
|
200
202
|
const cleanedInput = decision.source === 'explicit' ? stripExplicitPrefix(userInput) : userInput;
|
|
201
203
|
sessionMessages.push(new HumanMessage(cleanedInput));
|
|
@@ -206,6 +208,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
206
208
|
return shortcut;
|
|
207
209
|
}
|
|
208
210
|
if (decision.domain === 'planning') {
|
|
211
|
+
emit('agent', { name: 'planning' });
|
|
209
212
|
onStatus?.('Planning...');
|
|
210
213
|
const result = await runPlanningAgent(cleanedInput, (inputTokens, outputTokens) => {
|
|
211
214
|
onUsage?.(inputTokens + outputTokens, estimateCost('gpt-5.4', inputTokens, outputTokens));
|
|
@@ -215,6 +218,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
215
218
|
return result;
|
|
216
219
|
}
|
|
217
220
|
if (decision.domain === 'review') {
|
|
221
|
+
emit('agent', { name: 'reviewer' });
|
|
218
222
|
onStageEvent?.({ type: 'stage_start', stage: 'reviewer' });
|
|
219
223
|
const result = await runReviewer(cleanedInput, (name, args) => {
|
|
220
224
|
onStageEvent?.({ type: 'tool', stage: 'reviewer', tool: name, target: extractTarget(args) });
|
|
@@ -226,6 +230,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
226
230
|
return result;
|
|
227
231
|
}
|
|
228
232
|
if (decision.domain === 'debugging') {
|
|
233
|
+
emit('agent', { name: 'debugger' });
|
|
229
234
|
onStageEvent?.({ type: 'stage_start', stage: 'debugger' });
|
|
230
235
|
const result = await runDebugger(cleanedInput, (name, args) => {
|
|
231
236
|
onStageEvent?.({ type: 'tool', stage: 'debugger', tool: name, target: extractTarget(args) });
|
|
@@ -237,6 +242,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
237
242
|
return result;
|
|
238
243
|
}
|
|
239
244
|
if (decision.domain === 'refactoring') {
|
|
245
|
+
emit('agent', { name: 'refactorer' });
|
|
240
246
|
onStageEvent?.({ type: 'stage_start', stage: 'refactorer' });
|
|
241
247
|
const result = await runRefactorer(cleanedInput, (name, args) => {
|
|
242
248
|
onStageEvent?.({ type: 'tool', stage: 'refactorer', tool: name, target: extractTarget(args) });
|
|
@@ -248,6 +254,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
248
254
|
return result;
|
|
249
255
|
}
|
|
250
256
|
if (decision.domain === 'git') {
|
|
257
|
+
emit('agent', { name: 'git' });
|
|
251
258
|
onStageEvent?.({ type: 'stage_start', stage: 'git_agent' });
|
|
252
259
|
const result = await runGitAgent(cleanedInput, (name, args) => {
|
|
253
260
|
onStageEvent?.({ type: 'tool', stage: 'git_agent', tool: name, target: extractTarget(args) });
|
|
@@ -259,6 +266,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
259
266
|
return result;
|
|
260
267
|
}
|
|
261
268
|
if (decision.domain === 'coding') {
|
|
269
|
+
emit('agent', { name: 'coder' });
|
|
262
270
|
const graphResult = await runAgentGraph(cleanedInput, buildSystemPrompt(), onToolCall, onStatus ?? (() => { }), decision, onStageEvent // ← add this
|
|
263
271
|
);
|
|
264
272
|
if (graphResult.escalated && onEscalation) {
|
|
@@ -275,6 +283,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
275
283
|
return graphResult.finalOutput;
|
|
276
284
|
}
|
|
277
285
|
}
|
|
286
|
+
emit('agent', { name: 'chat' });
|
|
278
287
|
const simpleAgent = createReactAgent({
|
|
279
288
|
llm: createLlm(decision.recommendedModel),
|
|
280
289
|
tools,
|
|
@@ -307,12 +316,14 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
307
316
|
}
|
|
308
317
|
if (event === 'on_tool_start') {
|
|
309
318
|
onToolCall(eventName, data.input);
|
|
319
|
+
emit('tool_call', { name: eventName, input: data.input });
|
|
310
320
|
}
|
|
311
321
|
if (event === 'on_chat_model_end') {
|
|
312
322
|
const usage = data.output?.usage_metadata;
|
|
313
323
|
if (usage) {
|
|
314
324
|
totalInputTokens += usage.input_tokens ?? 0;
|
|
315
325
|
totalOutputTokens += usage.output_tokens ?? 0;
|
|
326
|
+
emit('llm_call', { tokens_in: usage.input_tokens ?? 0, tokens_out: usage.output_tokens ?? 0 });
|
|
316
327
|
}
|
|
317
328
|
if (!finalResponse) {
|
|
318
329
|
const output = data.output;
|
|
@@ -331,5 +342,7 @@ export async function chat(userInput, onToolCall, onStatus, onToken, onEscalatio
|
|
|
331
342
|
onUsage(totalInputTokens + totalOutputTokens, estimateCost(model, totalInputTokens, totalOutputTokens));
|
|
332
343
|
}
|
|
333
344
|
saveSession(sessionMessages);
|
|
345
|
+
emit('response', { text: finalResponse });
|
|
346
|
+
emit('done', { total_tokens: totalInputTokens + totalOutputTokens });
|
|
334
347
|
return finalResponse;
|
|
335
348
|
}
|
package/dist/llm/factory.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ChatOpenAI } from '@langchain/openai';
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { getAuthToken, getOrCreateAnonId } from '../auth.js';
|
|
4
|
-
|
|
4
|
+
function backendUrl() {
|
|
5
|
+
return process.env.GLITOOL_BACKEND ?? 'https://api.glit.in';
|
|
6
|
+
}
|
|
5
7
|
let currentRequestId = null;
|
|
6
8
|
export function startNewRequest() {
|
|
7
9
|
currentRequestId = randomUUID();
|
|
@@ -12,15 +14,16 @@ function requestIdHeader() {
|
|
|
12
14
|
}
|
|
13
15
|
export function makeLlm(model, extras = {}) {
|
|
14
16
|
if (process.env.OPENAI_API_KEY) {
|
|
15
|
-
return new ChatOpenAI({ model, apiKey: process.env.OPENAI_API_KEY, ...extras });
|
|
17
|
+
return new ChatOpenAI({ model, apiKey: process.env.OPENAI_API_KEY, streaming: true, ...extras });
|
|
16
18
|
}
|
|
17
19
|
const token = getAuthToken();
|
|
18
20
|
if (token) {
|
|
19
21
|
return new ChatOpenAI({
|
|
20
22
|
model,
|
|
21
23
|
apiKey: token,
|
|
24
|
+
streaming: true,
|
|
22
25
|
configuration: {
|
|
23
|
-
baseURL: `${
|
|
26
|
+
baseURL: `${backendUrl()}/v1`,
|
|
24
27
|
defaultHeaders: requestIdHeader(),
|
|
25
28
|
},
|
|
26
29
|
...extras,
|
|
@@ -29,8 +32,9 @@ export function makeLlm(model, extras = {}) {
|
|
|
29
32
|
return new ChatOpenAI({
|
|
30
33
|
model,
|
|
31
34
|
apiKey: 'anon',
|
|
35
|
+
streaming: true,
|
|
32
36
|
configuration: {
|
|
33
|
-
baseURL: `${
|
|
37
|
+
baseURL: `${backendUrl()}/v1`,
|
|
34
38
|
defaultHeaders: { 'X-Anon-ID': getOrCreateAnonId(), ...requestIdHeader() },
|
|
35
39
|
},
|
|
36
40
|
...extras,
|
|
@@ -46,7 +50,7 @@ export function makeInternalLlm(model, extras = {}) {
|
|
|
46
50
|
model,
|
|
47
51
|
apiKey: token ?? 'anon',
|
|
48
52
|
configuration: {
|
|
49
|
-
baseURL: `${
|
|
53
|
+
baseURL: `${backendUrl()}/v1`,
|
|
50
54
|
defaultHeaders: {
|
|
51
55
|
...anonHeaders,
|
|
52
56
|
'X-Glitool-Internal': 'true',
|
package/dist/monitor.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function emit(type, data = {}) {
|
|
2
|
+
if (!process.env.GLITOOL_DEV_MONITOR)
|
|
3
|
+
return;
|
|
4
|
+
fetch('http://localhost:4000/event', {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7
|
+
body: JSON.stringify({ type, ...data, t: new Date().toISOString() }),
|
|
8
|
+
}).catch(() => { });
|
|
9
|
+
}
|
package/dist/ui/StatusBar.js
CHANGED
|
@@ -28,6 +28,16 @@ function formatCost(c) {
|
|
|
28
28
|
export const StatusBar = ({ state, detail, tier, anonLeft, model, tokens, cost }) => {
|
|
29
29
|
const dotColor = STATE_COLOR[state];
|
|
30
30
|
const stateLabel = STATE_LABEL[state];
|
|
31
|
+
// Animate dot when working
|
|
32
|
+
const SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
|
|
33
|
+
const [frame, setFrame] = React.useState(0);
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
if (state !== 'working')
|
|
36
|
+
return;
|
|
37
|
+
const id = setInterval(() => setFrame(f => (f + 1) % SPINNER_FRAMES.length), 150);
|
|
38
|
+
return () => clearInterval(id);
|
|
39
|
+
}, [state]);
|
|
40
|
+
const dotChar = state === 'working' ? SPINNER_FRAMES[frame] : symbols.statusDot;
|
|
31
41
|
const leftParts = [stateLabel];
|
|
32
42
|
if (detail)
|
|
33
43
|
leftParts.push(detail);
|
|
@@ -40,5 +50,5 @@ export const StatusBar = ({ state, detail, tier, anonLeft, model, tokens, cost }
|
|
|
40
50
|
}
|
|
41
51
|
rightParts.push(model);
|
|
42
52
|
rightParts.push(formatTokens(tokens));
|
|
43
|
-
return (_jsxs(Box, { borderStyle: "single", borderColor: colors.line, paddingX: 1, justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: dotColor, children:
|
|
53
|
+
return (_jsxs(Box, { borderStyle: "single", borderColor: colors.line, paddingX: 1, justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: dotColor, children: dotChar }), _jsxs(Text, { color: colors.ink2, children: [" ", leftParts.join(' . ')] })] }), _jsx(Box, { children: _jsx(Text, { color: colors.muted, children: rightParts.join(' · ') }) })] }));
|
|
44
54
|
};
|