bingocode 1.1.130 → 1.1.131
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.
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(
|
|
5
|
-
"
|
|
6
|
-
"Bash(
|
|
7
|
-
"Bash(
|
|
8
|
-
"Bash(curl -sv \"https://docs.anthropic.com/en/api/messages\" --max-time 30)",
|
|
9
|
-
"Bash(curl -sv \"https://raw.githubusercontent.com/anthropics/anthropic-sdk-python/main/api.md\" --max-time 30)",
|
|
10
|
-
"Bash(curl -s \"https://raw.githubusercontent.com/anthropics/anthropic-sdk-python/refs/heads/main/README.md\" --max-time 30)",
|
|
11
|
-
"Bash(findstr /i goal)",
|
|
12
|
-
"Bash(bun run:*)",
|
|
13
|
-
"Bash(bunx tsc:*)",
|
|
14
|
-
"Bash(node_modules/.bin/tsc --noEmit --skipLibCheck)",
|
|
15
|
-
"WebFetch(domain:www.npmjs.com)",
|
|
16
|
-
"Bash(npm info:*)",
|
|
17
|
-
"Bash(npm pack:*)"
|
|
4
|
+
"Bash(dir \"F:\\\\Leanchy\\\\VirtuosAgent\\\\BingoCode\\\\src\\\\server\\\\proxy\")",
|
|
5
|
+
"Bash(dir \"F:\\\\Leanchy\\\\VirtuosAgent\\\\BingoCode\\\\src\\\\server\")",
|
|
6
|
+
"Bash(grep -rn \"stream\\\\|proxy\\\\|SSE\\\\|duplicate\\\\|repeat\\\\|render\" F:LeanchyVirtuosAgentBingoCodesrc --include=*.ts -l)",
|
|
7
|
+
"Bash(xargs grep:*)"
|
|
18
8
|
]
|
|
19
9
|
}
|
|
20
10
|
}
|
package/package.json
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
//@C:M ID=M.UI.TopToolbar;K=M;V=1.3;P=top toolbar;D=CLI;M=cli;S=ui
|
|
2
|
-
import React, { memo, useMemo } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
4
|
-
import { Chip } from './CliMenuUi.tsx';
|
|
5
|
-
import { useTheme } from '../components/design-system/ThemeProvider.js';
|
|
6
|
-
import { getGlobalConfig } from '../utils/config.ts';
|
|
7
|
-
import type { ClawdPose } from '../components/LogoV2/Clawd.tsx';
|
|
8
|
-
import { Clawd } from '../components/LogoV2/Clawd.tsx';
|
|
9
|
-
import { AnimatedClawd } from '../components/LogoV2/AnimatedClawd.tsx';
|
|
10
|
-
|
|
11
|
-
type Props = {
|
|
12
|
-
ready: boolean;
|
|
13
|
-
page: string | null;
|
|
14
|
-
animEnabled: boolean;
|
|
15
|
-
tipsEnabled: boolean;
|
|
16
|
-
ip?: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
//@C:F ID=F.UI.TopToolbar;K=F;V=1.4;P=toolbar;D=CLI;M=cli;S=ui;In=Props;Out=JSX.Element
|
|
20
|
-
export const TopToolbar: React.FC<Props> = memo(({ ready, page, animEnabled, tipsEnabled, ip }) => {
|
|
21
|
-
const [theme] = useTheme();
|
|
22
|
-
|
|
23
|
-
const { version } = useMemo(() => {
|
|
24
|
-
try {
|
|
25
|
-
const cfg = getGlobalConfig();
|
|
26
|
-
return { version: (cfg as any)?.version ?? '' };
|
|
27
|
-
} catch {
|
|
28
|
-
return { version: '' };
|
|
29
|
-
}
|
|
30
|
-
}, []);
|
|
31
|
-
|
|
32
|
-
const themeLabel = String(theme || (ready ? (getGlobalConfig()?.theme ?? 'system') : '…'));
|
|
33
|
-
const uiChipValue = `Anim ${animEnabled ? 'On' : 'Off'} · Tips ${tipsEnabled ? 'On' : 'Off'}`;
|
|
34
|
-
const uiTone = (String(theme) === 'dark') ? 'accent' : (String(theme) === 'highContrast' ? 'warning' : 'info');
|
|
35
|
-
|
|
36
|
-
const clawdPose: ClawdPose = useMemo(() => {
|
|
37
|
-
if (!ready) return 'default';
|
|
38
|
-
if (page === null) return animEnabled ? 'arms-up' : 'default';
|
|
39
|
-
return tipsEnabled ? 'look-left' : 'look-right';
|
|
40
|
-
}, [ready, page, animEnabled, tipsEnabled]);
|
|
41
|
-
|
|
42
|
-
// ── Compact mode (page !== null): single line, no logo ──────────────
|
|
43
|
-
if (page !== null) {
|
|
44
|
-
return (
|
|
45
|
-
<Box flexDirection="row" alignItems="center">
|
|
46
|
-
<Text bold>Bingo Code</Text>
|
|
47
|
-
<Box marginLeft={1}>
|
|
48
|
-
<Chip label="Theme" value={themeLabel} tone="accent" />
|
|
49
|
-
</Box>
|
|
50
|
-
<Chip label="UI" value={uiChipValue} tone={uiTone as any} />
|
|
51
|
-
{ip ? (
|
|
52
|
-
<Text color="green" dimColor> · IP: {ip}</Text>
|
|
53
|
-
) : (
|
|
54
|
-
<Text color="yellow" dimColor> · {ready ? 'Server ready' : 'Starting…'}</Text>
|
|
55
|
-
)}
|
|
56
|
-
</Box>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ── Home mode (page === null): Clawd left + 3-row right column ───────
|
|
61
|
-
return (
|
|
62
|
-
<Box flexDirection="row" alignItems="flex-start">
|
|
63
|
-
{/* Left: Clawd sprite */}
|
|
64
|
-
<Box marginRight={2}>
|
|
65
|
-
{animEnabled ? <AnimatedClawd /> : <Clawd pose={clawdPose} />}
|
|
66
|
-
</Box>
|
|
67
|
-
|
|
68
|
-
{/* Right: 3 rows */}
|
|
69
|
-
<Box flexDirection="column">
|
|
70
|
-
{/* Row 1: brand + version + chips */}
|
|
71
|
-
<Box flexDirection="row" alignItems="center">
|
|
72
|
-
<Text bold>Welcome to Bingo Code</Text>
|
|
73
|
-
{version ? <Text dimColor> v{version}</Text> : null}
|
|
74
|
-
<Box marginLeft={1}>
|
|
75
|
-
<Chip label="Theme" value={themeLabel} tone="accent" />
|
|
76
|
-
</Box>
|
|
77
|
-
<Chip label="UI" value={uiChipValue} tone={uiTone as any} />
|
|
78
|
-
</Box>
|
|
79
|
-
|
|
80
|
-
{/* Row 2: IP / server status */}
|
|
81
|
-
<Box>
|
|
82
|
-
{ip ? (
|
|
83
|
-
<Text color="green" dimColor>IP: {ip}</Text>
|
|
84
|
-
) : (
|
|
85
|
-
<Text color="yellow" dimColor>{ready ? 'Server ready' : 'Starting server…'}</Text>
|
|
86
|
-
)}
|
|
87
|
-
</Box>
|
|
88
|
-
|
|
89
|
-
{/* Row 3: keyboard shortcuts */}
|
|
90
|
-
<Box>
|
|
91
|
-
<Text dimColor>N New · R Resume · P Provider · G Theme · ? Help</Text>
|
|
92
|
-
</Box>
|
|
93
|
-
</Box>
|
|
94
|
-
</Box>
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
export default TopToolbar;
|
|
1
|
+
//@C:M ID=M.UI.TopToolbar;K=M;V=1.3;P=top toolbar;D=CLI;M=cli;S=ui
|
|
2
|
+
import React, { memo, useMemo } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { Chip } from './CliMenuUi.tsx';
|
|
5
|
+
import { useTheme } from '../components/design-system/ThemeProvider.js';
|
|
6
|
+
import { getGlobalConfig } from '../utils/config.ts';
|
|
7
|
+
import type { ClawdPose } from '../components/LogoV2/Clawd.tsx';
|
|
8
|
+
import { Clawd } from '../components/LogoV2/Clawd.tsx';
|
|
9
|
+
import { AnimatedClawd } from '../components/LogoV2/AnimatedClawd.tsx';
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
ready: boolean;
|
|
13
|
+
page: string | null;
|
|
14
|
+
animEnabled: boolean;
|
|
15
|
+
tipsEnabled: boolean;
|
|
16
|
+
ip?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
//@C:F ID=F.UI.TopToolbar;K=F;V=1.4;P=toolbar;D=CLI;M=cli;S=ui;In=Props;Out=JSX.Element
|
|
20
|
+
export const TopToolbar: React.FC<Props> = memo(({ ready, page, animEnabled, tipsEnabled, ip }) => {
|
|
21
|
+
const [theme] = useTheme();
|
|
22
|
+
|
|
23
|
+
const { version } = useMemo(() => {
|
|
24
|
+
try {
|
|
25
|
+
const cfg = getGlobalConfig();
|
|
26
|
+
return { version: (cfg as any)?.version ?? '' };
|
|
27
|
+
} catch {
|
|
28
|
+
return { version: '' };
|
|
29
|
+
}
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
const themeLabel = String(theme || (ready ? (getGlobalConfig()?.theme ?? 'system') : '…'));
|
|
33
|
+
const uiChipValue = `Anim ${animEnabled ? 'On' : 'Off'} · Tips ${tipsEnabled ? 'On' : 'Off'}`;
|
|
34
|
+
const uiTone = (String(theme) === 'dark') ? 'accent' : (String(theme) === 'highContrast' ? 'warning' : 'info');
|
|
35
|
+
|
|
36
|
+
const clawdPose: ClawdPose = useMemo(() => {
|
|
37
|
+
if (!ready) return 'default';
|
|
38
|
+
if (page === null) return animEnabled ? 'arms-up' : 'default';
|
|
39
|
+
return tipsEnabled ? 'look-left' : 'look-right';
|
|
40
|
+
}, [ready, page, animEnabled, tipsEnabled]);
|
|
41
|
+
|
|
42
|
+
// ── Compact mode (page !== null): single line, no logo ──────────────
|
|
43
|
+
if (page !== null) {
|
|
44
|
+
return (
|
|
45
|
+
<Box flexDirection="row" alignItems="center">
|
|
46
|
+
<Text bold>Bingo Code</Text>
|
|
47
|
+
<Box marginLeft={1}>
|
|
48
|
+
<Chip label="Theme" value={themeLabel} tone="accent" />
|
|
49
|
+
</Box>
|
|
50
|
+
<Chip label="UI" value={uiChipValue} tone={uiTone as any} />
|
|
51
|
+
{ip ? (
|
|
52
|
+
<Text color="green" dimColor> · IP: {ip}</Text>
|
|
53
|
+
) : (
|
|
54
|
+
<Text color="yellow" dimColor> · {ready ? 'Server ready' : 'Starting…'}</Text>
|
|
55
|
+
)}
|
|
56
|
+
</Box>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Home mode (page === null): Clawd left + 3-row right column ───────
|
|
61
|
+
return (
|
|
62
|
+
<Box flexDirection="row" alignItems="flex-start">
|
|
63
|
+
{/* Left: Clawd sprite */}
|
|
64
|
+
<Box marginRight={2}>
|
|
65
|
+
{animEnabled ? <AnimatedClawd /> : <Clawd pose={clawdPose} />}
|
|
66
|
+
</Box>
|
|
67
|
+
|
|
68
|
+
{/* Right: 3 rows */}
|
|
69
|
+
<Box flexDirection="column">
|
|
70
|
+
{/* Row 1: brand + version + chips */}
|
|
71
|
+
<Box flexDirection="row" alignItems="center">
|
|
72
|
+
<Text bold>Welcome to Bingo Code</Text>
|
|
73
|
+
{version ? <Text dimColor> v{version}</Text> : null}
|
|
74
|
+
<Box marginLeft={1}>
|
|
75
|
+
<Chip label="Theme" value={themeLabel} tone="accent" />
|
|
76
|
+
</Box>
|
|
77
|
+
<Chip label="UI" value={uiChipValue} tone={uiTone as any} />
|
|
78
|
+
</Box>
|
|
79
|
+
|
|
80
|
+
{/* Row 2: IP / server status */}
|
|
81
|
+
<Box>
|
|
82
|
+
{ip ? (
|
|
83
|
+
<Text color="green" dimColor>IP: {ip}</Text>
|
|
84
|
+
) : (
|
|
85
|
+
<Text color="yellow" dimColor>{ready ? 'Server ready' : 'Starting server…'}</Text>
|
|
86
|
+
)}
|
|
87
|
+
</Box>
|
|
88
|
+
|
|
89
|
+
{/* Row 3: keyboard shortcuts */}
|
|
90
|
+
<Box>
|
|
91
|
+
<Text dimColor>N New · R Resume · P Provider · G Theme · ? Help</Text>
|
|
92
|
+
</Box>
|
|
93
|
+
</Box>
|
|
94
|
+
</Box>
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export default TopToolbar;
|
|
@@ -298,40 +298,20 @@ function extractReasoning(delta: DeltaEx): { thinking: string; signature: string
|
|
|
298
298
|
return null
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
/**
|
|
302
|
-
* Determine what block type this chunk carries and whether it's a new block.
|
|
303
|
-
* Priority (matches LiteLLM): tool_calls > text > reasoning > ignore
|
|
304
|
-
*/
|
|
305
|
-
function detectBlockTransition(
|
|
306
|
-
delta: DeltaEx,
|
|
307
|
-
state: StreamState,
|
|
308
|
-
): { type: ContentBlockType; isNew: boolean } | null {
|
|
309
|
-
// Priority 1: Tool calls
|
|
310
|
-
if (delta.tool_calls && delta.tool_calls.length > 0) {
|
|
311
|
-
const tc = delta.tool_calls[0]
|
|
312
|
-
// A tool call with function.name signals a NEW tool block
|
|
313
|
-
const isNew = state.currentBlockType !== 'tool_use' || !!(tc.function?.name)
|
|
314
|
-
return { type: 'tool_use', isNew }
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Priority 2: Text content
|
|
318
|
-
if (delta.content != null && delta.content !== '') {
|
|
319
|
-
const isNew = state.currentBlockType !== 'text' || !state.blockStartSent
|
|
320
|
-
return { type: 'text', isNew }
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Priority 3: Reasoning/thinking
|
|
324
|
-
const reasoning = extractReasoning(delta)
|
|
325
|
-
if (reasoning) {
|
|
326
|
-
const isNew = state.currentBlockType !== 'thinking' || !state.blockStartSent
|
|
327
|
-
return { type: 'thinking', isNew }
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return null
|
|
331
|
-
}
|
|
332
|
-
|
|
333
301
|
// ─── Main chunk processing ─────────────────────────────────
|
|
334
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Process a single SSE chunk using dual-pass logic:
|
|
305
|
+
* Pass 1 — reasoning/thinking (if present)
|
|
306
|
+
* Pass 2 — text content (if present)
|
|
307
|
+
* Pass 3 — tool calls (if present; mutually exclusive with text/thinking)
|
|
308
|
+
*
|
|
309
|
+
* This avoids the single-return priority chain that caused spurious
|
|
310
|
+
* close/open cycles when providers (Gemini via OpenRouter, DeepSeek, Qwen3, …)
|
|
311
|
+
* send reasoning_content and content in the same chunk or in alternating chunks,
|
|
312
|
+
* which previously produced multiple text content_block_start events and
|
|
313
|
+
* duplicate rendering in Claude Code's Ink terminal UI.
|
|
314
|
+
*/
|
|
335
315
|
function processChunk(chunk: OpenAIChatStreamChunk, state: StreamState): void {
|
|
336
316
|
const choice = chunk.choices?.[0]
|
|
337
317
|
|
|
@@ -350,31 +330,33 @@ function processChunk(chunk: OpenAIChatStreamChunk, state: StreamState): void {
|
|
|
350
330
|
|
|
351
331
|
const delta = choice.delta as DeltaEx
|
|
352
332
|
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
333
|
+
// Tool calls are mutually exclusive with text/thinking — handle separately
|
|
334
|
+
if (delta.tool_calls && delta.tool_calls.length > 0) {
|
|
335
|
+
// Close any open text/thinking block before entering tool_use
|
|
336
|
+
if (state.currentBlockType !== 'tool_use' && state.blockStartSent && !state.blockStopSent) {
|
|
337
|
+
closeCurrentBlock(state)
|
|
338
|
+
}
|
|
339
|
+
handleToolCalls(delta, state)
|
|
340
|
+
} else {
|
|
341
|
+
// Pass 1: reasoning/thinking
|
|
342
|
+
const reasoning = extractReasoning(delta)
|
|
343
|
+
if (reasoning) {
|
|
344
|
+
// If currently in a text block, close it before opening thinking
|
|
345
|
+
if (state.currentBlockType === 'text' && state.blockStartSent && !state.blockStopSent) {
|
|
364
346
|
closeCurrentBlock(state)
|
|
365
347
|
}
|
|
348
|
+
handleThinking(delta, state)
|
|
366
349
|
}
|
|
367
350
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
break
|
|
351
|
+
// Pass 2: text content
|
|
352
|
+
// After thinking is handled, resume/open text block independently.
|
|
353
|
+
// This is the key fix: text is NOT skipped when reasoning was also present.
|
|
354
|
+
if (delta.content != null && delta.content !== '') {
|
|
355
|
+
// If currently in a thinking block, close it before opening text
|
|
356
|
+
if (state.currentBlockType === 'thinking' && state.blockStartSent && !state.blockStopSent) {
|
|
357
|
+
closeCurrentBlock(state)
|
|
358
|
+
}
|
|
359
|
+
handleText(delta, state)
|
|
378
360
|
}
|
|
379
361
|
}
|
|
380
362
|
|