@ebowwa/coder 0.7.66 → 0.7.68
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/index.js +212 -32
- package/dist/index.js.map +156 -0
- package/dist/interfaces/ui/terminal/cli/bootstrap.js.map +141 -0
- package/dist/interfaces/ui/terminal/cli/index.js +205 -25
- package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
- package/dist/native/index.darwin-arm64.node +0 -0
- package/native/index.darwin-arm64.node +0 -0
- package/package.json +2 -2
- package/packages/src/core/api-client-impl.ts +156 -24
- package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +42 -13
- package/packages/src/interfaces/ui/terminal/cli/interactive/scroll-handler.ts +284 -0
- package/packages/src/native/index.ts +1 -0
|
@@ -7378,6 +7378,69 @@ function convertToolsToOpenAIFormat(tools) {
|
|
|
7378
7378
|
}
|
|
7379
7379
|
}));
|
|
7380
7380
|
}
|
|
7381
|
+
function convertMessagesToOpenAIFormat(messages) {
|
|
7382
|
+
const result = [];
|
|
7383
|
+
for (const msg of messages) {
|
|
7384
|
+
if (msg.role === "assistant") {
|
|
7385
|
+
const toolCalls = [];
|
|
7386
|
+
const textParts = [];
|
|
7387
|
+
for (const block of msg.content) {
|
|
7388
|
+
if (block.type === "text") {
|
|
7389
|
+
textParts.push(block.text);
|
|
7390
|
+
} else if (block.type === "tool_use") {
|
|
7391
|
+
toolCalls.push({
|
|
7392
|
+
id: block.id,
|
|
7393
|
+
type: "function",
|
|
7394
|
+
function: {
|
|
7395
|
+
name: block.name,
|
|
7396
|
+
arguments: JSON.stringify(block.input)
|
|
7397
|
+
}
|
|
7398
|
+
});
|
|
7399
|
+
} else if (block.type === "thinking" || block.type === "redacted_thinking") {}
|
|
7400
|
+
}
|
|
7401
|
+
const openAIMsg = {
|
|
7402
|
+
role: "assistant",
|
|
7403
|
+
content: textParts.join(`
|
|
7404
|
+
`) || null
|
|
7405
|
+
};
|
|
7406
|
+
if (toolCalls.length > 0) {
|
|
7407
|
+
openAIMsg.tool_calls = toolCalls;
|
|
7408
|
+
}
|
|
7409
|
+
result.push(openAIMsg);
|
|
7410
|
+
} else if (msg.role === "user") {
|
|
7411
|
+
const textParts = [];
|
|
7412
|
+
const toolResults = [];
|
|
7413
|
+
for (const block of msg.content) {
|
|
7414
|
+
if (block.type === "text") {
|
|
7415
|
+
textParts.push(block.text);
|
|
7416
|
+
} else if (block.type === "tool_result") {
|
|
7417
|
+
const contentStr = typeof block.content === "string" ? block.content : block.content.map((c) => c.type === "text" ? c.text : JSON.stringify(c)).join(`
|
|
7418
|
+
`);
|
|
7419
|
+
toolResults.push({
|
|
7420
|
+
tool_use_id: block.tool_use_id,
|
|
7421
|
+
content: contentStr,
|
|
7422
|
+
is_error: block.is_error
|
|
7423
|
+
});
|
|
7424
|
+
}
|
|
7425
|
+
}
|
|
7426
|
+
if (textParts.length > 0) {
|
|
7427
|
+
result.push({
|
|
7428
|
+
role: "user",
|
|
7429
|
+
content: textParts.join(`
|
|
7430
|
+
`)
|
|
7431
|
+
});
|
|
7432
|
+
}
|
|
7433
|
+
for (const tr of toolResults) {
|
|
7434
|
+
result.push({
|
|
7435
|
+
role: "tool",
|
|
7436
|
+
tool_call_id: tr.tool_use_id,
|
|
7437
|
+
content: tr.content
|
|
7438
|
+
});
|
|
7439
|
+
}
|
|
7440
|
+
}
|
|
7441
|
+
}
|
|
7442
|
+
return result;
|
|
7443
|
+
}
|
|
7381
7444
|
function calculateCost2(model, usage) {
|
|
7382
7445
|
return calculateCost(model, usage);
|
|
7383
7446
|
}
|
|
@@ -7771,20 +7834,6 @@ async function createMessageStream(messages, options) {
|
|
|
7771
7834
|
signal
|
|
7772
7835
|
} = options;
|
|
7773
7836
|
const startTime = Date.now();
|
|
7774
|
-
const cachedMessages = buildCachedMessages(messages, cacheConfig);
|
|
7775
|
-
const cachedSystemPrompt = buildSystemPrompt(systemPrompt, cacheConfig);
|
|
7776
|
-
const request = {
|
|
7777
|
-
model,
|
|
7778
|
-
max_tokens: maxTokens,
|
|
7779
|
-
messages: cachedMessages.map((m) => ({
|
|
7780
|
-
role: m.role,
|
|
7781
|
-
content: m.content
|
|
7782
|
-
})),
|
|
7783
|
-
stream: true
|
|
7784
|
-
};
|
|
7785
|
-
if (cachedSystemPrompt) {
|
|
7786
|
-
request.system = cachedSystemPrompt;
|
|
7787
|
-
}
|
|
7788
7837
|
const providerInfo = resolveProvider(model);
|
|
7789
7838
|
let apiEndpoint;
|
|
7790
7839
|
let headers;
|
|
@@ -7814,6 +7863,37 @@ async function createMessageStream(messages, options) {
|
|
|
7814
7863
|
"anthropic-version": "2023-06-01"
|
|
7815
7864
|
};
|
|
7816
7865
|
}
|
|
7866
|
+
const cachedMessages = buildCachedMessages(messages, cacheConfig);
|
|
7867
|
+
const cachedSystemPrompt = buildSystemPrompt(systemPrompt, cacheConfig);
|
|
7868
|
+
let requestMessages;
|
|
7869
|
+
if (apiFormat === "openai") {
|
|
7870
|
+
const openAIMessages = convertMessagesToOpenAIFormat(cachedMessages);
|
|
7871
|
+
if (cachedSystemPrompt) {
|
|
7872
|
+
const systemText = typeof cachedSystemPrompt === "string" ? cachedSystemPrompt : cachedSystemPrompt.map((b) => b.text).join(`
|
|
7873
|
+
|
|
7874
|
+
`);
|
|
7875
|
+
requestMessages = [
|
|
7876
|
+
{ role: "system", content: systemText },
|
|
7877
|
+
...openAIMessages
|
|
7878
|
+
];
|
|
7879
|
+
} else {
|
|
7880
|
+
requestMessages = openAIMessages;
|
|
7881
|
+
}
|
|
7882
|
+
} else {
|
|
7883
|
+
requestMessages = cachedMessages.map((m) => ({
|
|
7884
|
+
role: m.role,
|
|
7885
|
+
content: m.content
|
|
7886
|
+
}));
|
|
7887
|
+
}
|
|
7888
|
+
const request = {
|
|
7889
|
+
model,
|
|
7890
|
+
max_tokens: maxTokens,
|
|
7891
|
+
messages: requestMessages,
|
|
7892
|
+
stream: true
|
|
7893
|
+
};
|
|
7894
|
+
if (cachedSystemPrompt && apiFormat === "anthropic") {
|
|
7895
|
+
request.system = cachedSystemPrompt;
|
|
7896
|
+
}
|
|
7817
7897
|
if (tools && tools.length > 0) {
|
|
7818
7898
|
if (apiFormat === "openai") {
|
|
7819
7899
|
request.tools = convertToolsToOpenAIFormat(tools);
|
|
@@ -11249,6 +11329,84 @@ var init_spinner_frames = __esm(() => {
|
|
|
11249
11329
|
];
|
|
11250
11330
|
});
|
|
11251
11331
|
|
|
11332
|
+
// packages/src/interfaces/ui/terminal/cli/interactive/scroll-handler.ts
|
|
11333
|
+
function handleScrollEvent(event, currentOffset, totalMessages, config = {}) {
|
|
11334
|
+
const { pageScrollAmount, lineScrollAmount } = { ...DEFAULT_SCROLL_CONFIG, ...config };
|
|
11335
|
+
const maxScroll = Math.max(0, Math.max(totalMessages - 1, 10));
|
|
11336
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11337
|
+
console.error("[ScrollHandler] Event:", {
|
|
11338
|
+
code: event.code,
|
|
11339
|
+
shift: event.shift,
|
|
11340
|
+
ctrl: event.ctrl,
|
|
11341
|
+
alt: event.alt,
|
|
11342
|
+
is_special: event.is_special
|
|
11343
|
+
});
|
|
11344
|
+
}
|
|
11345
|
+
if (KeyEvents.isPageUp(event)) {
|
|
11346
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11347
|
+
console.error("[ScrollHandler] PageUp detected, scrolling up");
|
|
11348
|
+
}
|
|
11349
|
+
return { handled: true, newOffset: Math.min(currentOffset + pageScrollAmount, maxScroll) };
|
|
11350
|
+
}
|
|
11351
|
+
if (KeyEvents.isPageDown(event)) {
|
|
11352
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11353
|
+
console.error("[ScrollHandler] PageDown detected, scrolling down");
|
|
11354
|
+
}
|
|
11355
|
+
return { handled: true, newOffset: Math.max(0, currentOffset - pageScrollAmount) };
|
|
11356
|
+
}
|
|
11357
|
+
if (KeyEvents.isHome(event)) {
|
|
11358
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11359
|
+
console.error("[ScrollHandler] Home detected, resetting to bottom");
|
|
11360
|
+
}
|
|
11361
|
+
return { handled: true, newOffset: 0 };
|
|
11362
|
+
}
|
|
11363
|
+
if (KeyEvents.isUp(event) && event.alt) {
|
|
11364
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11365
|
+
console.error("[ScrollHandler] Alt+Up detected, scrolling up");
|
|
11366
|
+
}
|
|
11367
|
+
return { handled: true, newOffset: Math.min(currentOffset + lineScrollAmount, maxScroll) };
|
|
11368
|
+
}
|
|
11369
|
+
if (KeyEvents.isDown(event) && event.alt) {
|
|
11370
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11371
|
+
console.error("[ScrollHandler] Alt+Down detected, scrolling down");
|
|
11372
|
+
}
|
|
11373
|
+
return { handled: true, newOffset: Math.max(0, currentOffset - lineScrollAmount) };
|
|
11374
|
+
}
|
|
11375
|
+
if (KeyEvents.isUp(event) && event.ctrl) {
|
|
11376
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11377
|
+
console.error("[ScrollHandler] Ctrl+Up detected, scrolling up");
|
|
11378
|
+
}
|
|
11379
|
+
return { handled: true, newOffset: Math.min(currentOffset + lineScrollAmount, maxScroll) };
|
|
11380
|
+
}
|
|
11381
|
+
if (KeyEvents.isDown(event) && event.ctrl) {
|
|
11382
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11383
|
+
console.error("[ScrollHandler] Ctrl+Down detected, scrolling down");
|
|
11384
|
+
}
|
|
11385
|
+
return { handled: true, newOffset: Math.max(0, currentOffset - lineScrollAmount) };
|
|
11386
|
+
}
|
|
11387
|
+
if (KeyEvents.isUp(event) && event.shift) {
|
|
11388
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11389
|
+
console.error("[ScrollHandler] Shift+Up detected, scrolling up");
|
|
11390
|
+
}
|
|
11391
|
+
return { handled: true, newOffset: Math.min(currentOffset + lineScrollAmount, maxScroll) };
|
|
11392
|
+
}
|
|
11393
|
+
if (KeyEvents.isDown(event) && event.shift) {
|
|
11394
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
11395
|
+
console.error("[ScrollHandler] Shift+Down detected, scrolling down");
|
|
11396
|
+
}
|
|
11397
|
+
return { handled: true, newOffset: Math.max(0, currentOffset - lineScrollAmount) };
|
|
11398
|
+
}
|
|
11399
|
+
return { handled: false, newOffset: currentOffset };
|
|
11400
|
+
}
|
|
11401
|
+
var DEFAULT_SCROLL_CONFIG;
|
|
11402
|
+
var init_scroll_handler = __esm(() => {
|
|
11403
|
+
init_input_handler();
|
|
11404
|
+
DEFAULT_SCROLL_CONFIG = {
|
|
11405
|
+
pageScrollAmount: 3,
|
|
11406
|
+
lineScrollAmount: 1
|
|
11407
|
+
};
|
|
11408
|
+
});
|
|
11409
|
+
|
|
11252
11410
|
// packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts
|
|
11253
11411
|
import process3 from "process";
|
|
11254
11412
|
function createInitialState() {
|
|
@@ -11399,6 +11557,23 @@ class InteractiveRunner {
|
|
|
11399
11557
|
this.state = { ...this.state, inputValue: "", cursorPos: 0 };
|
|
11400
11558
|
return true;
|
|
11401
11559
|
}
|
|
11560
|
+
if (process3.env.CODER_DEBUG_SCROLL === "1") {
|
|
11561
|
+
console.error("[InteractiveRunner] Key event:", {
|
|
11562
|
+
code: event.code,
|
|
11563
|
+
shift: event.shift,
|
|
11564
|
+
ctrl: event.ctrl,
|
|
11565
|
+
alt: event.alt,
|
|
11566
|
+
is_special: event.is_special
|
|
11567
|
+
});
|
|
11568
|
+
}
|
|
11569
|
+
const scrollResult = handleScrollEvent(event, this.state.scrollOffset, this.messageStore.messages.length);
|
|
11570
|
+
if (process3.env.CODER_DEBUG_SCROLL === "1") {
|
|
11571
|
+
console.error("[InteractiveRunner] Scroll result:", scrollResult);
|
|
11572
|
+
}
|
|
11573
|
+
if (scrollResult.handled) {
|
|
11574
|
+
this.state = { ...this.state, scrollOffset: scrollResult.newOffset };
|
|
11575
|
+
return true;
|
|
11576
|
+
}
|
|
11402
11577
|
if (KeyEvents.isUp(event)) {
|
|
11403
11578
|
return this._handleHistoryUp();
|
|
11404
11579
|
}
|
|
@@ -11696,18 +11871,19 @@ class InteractiveRunner {
|
|
|
11696
11871
|
})) : [];
|
|
11697
11872
|
return {
|
|
11698
11873
|
messages: renderMessages,
|
|
11699
|
-
inputValue,
|
|
11700
|
-
cursorPos,
|
|
11701
|
-
statusText,
|
|
11702
|
-
isLoading,
|
|
11703
|
-
streamingText,
|
|
11874
|
+
input_value: inputValue,
|
|
11875
|
+
cursor_pos: cursorPos,
|
|
11876
|
+
status_text: statusText,
|
|
11877
|
+
is_loading: isLoading,
|
|
11878
|
+
streaming_text: streamingText,
|
|
11704
11879
|
model: this.props.model,
|
|
11705
|
-
|
|
11706
|
-
helpText,
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
searchResults,
|
|
11710
|
-
|
|
11880
|
+
show_help: helpMode,
|
|
11881
|
+
help_text: helpText,
|
|
11882
|
+
search_mode: sessionSelectMode,
|
|
11883
|
+
search_query: "",
|
|
11884
|
+
search_results: searchResults,
|
|
11885
|
+
search_selected: 0,
|
|
11886
|
+
scroll_offset: this.state.scrollOffset
|
|
11711
11887
|
};
|
|
11712
11888
|
}
|
|
11713
11889
|
_getHelpText(section) {
|
|
@@ -11809,6 +11985,7 @@ var init_interactive_runner = __esm(() => {
|
|
|
11809
11985
|
init_native();
|
|
11810
11986
|
init_spinner_frames();
|
|
11811
11987
|
init_input_handler();
|
|
11988
|
+
init_scroll_handler();
|
|
11812
11989
|
init_types();
|
|
11813
11990
|
});
|
|
11814
11991
|
|
|
@@ -34325,3 +34502,6 @@ if (checkPtyWrapper2()) {
|
|
|
34325
34502
|
process5.exit(1);
|
|
34326
34503
|
});
|
|
34327
34504
|
}
|
|
34505
|
+
|
|
34506
|
+
//# debugId=7897AF5E6C87CE9064756E2164756E21
|
|
34507
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebowwa/coder",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.68",
|
|
4
4
|
"description": "AI-powered terminal coding assistant",
|
|
5
5
|
"author": "ebowwa",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "bun run build:native && bun run build:ts && bun run link",
|
|
27
|
-
"build:ts": "bun build ./packages/src/index.ts ./packages/src/interfaces/ui/terminal/cli/bootstrap.ts --outdir ./dist --target bun --external @ebowwa/coder-native && cp -r native dist/ && mkdir -p dist/interfaces/ui/terminal && cp -r native dist/interfaces/ui/terminal/ && mv dist/interfaces/ui/terminal/cli/bootstrap.js dist/interfaces/ui/terminal/cli/index.js",
|
|
27
|
+
"build:ts": "bun build ./packages/src/index.ts ./packages/src/interfaces/ui/terminal/cli/bootstrap.ts --outdir ./dist --target bun --external @ebowwa/coder-native --sourcemap && cp -r native dist/ && mkdir -p dist/interfaces/ui/terminal && cp -r native dist/interfaces/ui/terminal/ && mv dist/interfaces/ui/terminal/cli/bootstrap.js dist/interfaces/ui/terminal/cli/index.js",
|
|
28
28
|
"build:native": "cd packages/rust && cargo build --release --target-dir ./target && cd ../.. && bun run build:napi-artifacts",
|
|
29
29
|
"build:napi-artifacts": "napi build --platform --release --target-dir packages/rust/target --manifest-path packages/rust/Cargo.toml --output-dir native --js-package-name @ebowwa/coder-native --const-enum --strip --dts index.d.ts",
|
|
30
30
|
"link": "bun link || npm link",
|
|
@@ -63,6 +63,115 @@ function convertToolsToOpenAIFormat(tools: APITool[]): unknown[] {
|
|
|
63
63
|
}));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* OpenAI-format message types
|
|
68
|
+
*/
|
|
69
|
+
interface OpenAIToolCall {
|
|
70
|
+
id: string;
|
|
71
|
+
type: "function";
|
|
72
|
+
function: {
|
|
73
|
+
name: string;
|
|
74
|
+
arguments: string;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface OpenAIMessage {
|
|
79
|
+
role: "user" | "assistant" | "tool" | "system";
|
|
80
|
+
content: string | null;
|
|
81
|
+
tool_calls?: OpenAIToolCall[];
|
|
82
|
+
tool_call_id?: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert Anthropic-style messages to OpenAI-style messages
|
|
88
|
+
*
|
|
89
|
+
* Key conversions:
|
|
90
|
+
* 1. Assistant messages with tool_use → add tool_calls array
|
|
91
|
+
* 2. User messages with tool_result → separate role: "tool" messages
|
|
92
|
+
*
|
|
93
|
+
* This is required because OpenAI-format APIs (Zhipu, etc.) don't understand
|
|
94
|
+
* Anthropic's tool_result content block type.
|
|
95
|
+
*/
|
|
96
|
+
function convertMessagesToOpenAIFormat(messages: Message[]): OpenAIMessage[] {
|
|
97
|
+
const result: OpenAIMessage[] = [];
|
|
98
|
+
|
|
99
|
+
for (const msg of messages) {
|
|
100
|
+
if (msg.role === "assistant") {
|
|
101
|
+
// Assistant message - check for tool_use blocks
|
|
102
|
+
const toolCalls: OpenAIToolCall[] = [];
|
|
103
|
+
const textParts: string[] = [];
|
|
104
|
+
|
|
105
|
+
for (const block of msg.content) {
|
|
106
|
+
if (block.type === "text") {
|
|
107
|
+
textParts.push(block.text);
|
|
108
|
+
} else if (block.type === "tool_use") {
|
|
109
|
+
toolCalls.push({
|
|
110
|
+
id: block.id,
|
|
111
|
+
type: "function",
|
|
112
|
+
function: {
|
|
113
|
+
name: block.name,
|
|
114
|
+
arguments: JSON.stringify(block.input),
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
} else if (block.type === "thinking" || block.type === "redacted_thinking") {
|
|
118
|
+
// Skip thinking blocks in OpenAI format (not supported)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const openAIMsg: OpenAIMessage = {
|
|
123
|
+
role: "assistant",
|
|
124
|
+
content: textParts.join("\n") || null,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
if (toolCalls.length > 0) {
|
|
128
|
+
openAIMsg.tool_calls = toolCalls;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
result.push(openAIMsg);
|
|
132
|
+
} else if (msg.role === "user") {
|
|
133
|
+
// User message - check for tool_result blocks
|
|
134
|
+
const textParts: string[] = [];
|
|
135
|
+
const toolResults: { tool_use_id: string; content: string; is_error?: boolean }[] = [];
|
|
136
|
+
|
|
137
|
+
for (const block of msg.content) {
|
|
138
|
+
if (block.type === "text") {
|
|
139
|
+
textParts.push(block.text);
|
|
140
|
+
} else if (block.type === "tool_result") {
|
|
141
|
+
// Extract content as string
|
|
142
|
+
const contentStr = typeof block.content === "string"
|
|
143
|
+
? block.content
|
|
144
|
+
: block.content.map(c => c.type === "text" ? c.text : JSON.stringify(c)).join("\n");
|
|
145
|
+
toolResults.push({
|
|
146
|
+
tool_use_id: block.tool_use_id,
|
|
147
|
+
content: contentStr,
|
|
148
|
+
is_error: block.is_error,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Add text content as user message if present
|
|
154
|
+
if (textParts.length > 0) {
|
|
155
|
+
result.push({
|
|
156
|
+
role: "user",
|
|
157
|
+
content: textParts.join("\n"),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Add each tool result as a separate "tool" role message
|
|
162
|
+
for (const tr of toolResults) {
|
|
163
|
+
result.push({
|
|
164
|
+
role: "tool",
|
|
165
|
+
tool_call_id: tr.tool_use_id,
|
|
166
|
+
content: tr.content,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
66
175
|
export interface StreamOptions {
|
|
67
176
|
apiKey: string;
|
|
68
177
|
model?: string;
|
|
@@ -647,30 +756,7 @@ export async function createMessageStream(
|
|
|
647
756
|
|
|
648
757
|
const startTime = Date.now();
|
|
649
758
|
|
|
650
|
-
//
|
|
651
|
-
const cachedMessages = buildCachedMessages(messages, cacheConfig);
|
|
652
|
-
|
|
653
|
-
// Build system prompt with cache control
|
|
654
|
-
const cachedSystemPrompt = buildSystemPrompt(systemPrompt, cacheConfig);
|
|
655
|
-
|
|
656
|
-
// Build request
|
|
657
|
-
const request: APIRequest = {
|
|
658
|
-
model,
|
|
659
|
-
max_tokens: maxTokens,
|
|
660
|
-
messages: cachedMessages.map((m) => ({
|
|
661
|
-
role: m.role,
|
|
662
|
-
content: m.content,
|
|
663
|
-
})),
|
|
664
|
-
stream: true,
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
if (cachedSystemPrompt) {
|
|
668
|
-
request.system = cachedSystemPrompt;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// Tools will be set after determining API format (for format conversion)
|
|
672
|
-
|
|
673
|
-
// Resolve provider based on model name
|
|
759
|
+
// Resolve provider FIRST to determine API format
|
|
674
760
|
const providerInfo = resolveProvider(model);
|
|
675
761
|
|
|
676
762
|
// Determine API endpoint and headers based on provider
|
|
@@ -710,6 +796,52 @@ export async function createMessageStream(
|
|
|
710
796
|
};
|
|
711
797
|
}
|
|
712
798
|
|
|
799
|
+
// Build cached messages
|
|
800
|
+
const cachedMessages = buildCachedMessages(messages, cacheConfig);
|
|
801
|
+
|
|
802
|
+
// Build system prompt with cache control
|
|
803
|
+
const cachedSystemPrompt = buildSystemPrompt(systemPrompt, cacheConfig);
|
|
804
|
+
|
|
805
|
+
// Build request with format-appropriate message conversion
|
|
806
|
+
let requestMessages: unknown;
|
|
807
|
+
if (apiFormat === "openai") {
|
|
808
|
+
// Convert to OpenAI format (handles tool_use and tool_result properly)
|
|
809
|
+
const openAIMessages = convertMessagesToOpenAIFormat(cachedMessages);
|
|
810
|
+
|
|
811
|
+
// Add system prompt as first message for OpenAI format
|
|
812
|
+
if (cachedSystemPrompt) {
|
|
813
|
+
const systemText = typeof cachedSystemPrompt === "string"
|
|
814
|
+
? cachedSystemPrompt
|
|
815
|
+
: cachedSystemPrompt.map(b => b.text).join("\n\n");
|
|
816
|
+
requestMessages = [
|
|
817
|
+
{ role: "system", content: systemText },
|
|
818
|
+
...openAIMessages,
|
|
819
|
+
];
|
|
820
|
+
} else {
|
|
821
|
+
requestMessages = openAIMessages;
|
|
822
|
+
}
|
|
823
|
+
} else {
|
|
824
|
+
// Keep Anthropic format
|
|
825
|
+
requestMessages = cachedMessages.map((m) => ({
|
|
826
|
+
role: m.role,
|
|
827
|
+
content: m.content,
|
|
828
|
+
}));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Build request
|
|
832
|
+
const request: APIRequest = {
|
|
833
|
+
model,
|
|
834
|
+
max_tokens: maxTokens,
|
|
835
|
+
messages: requestMessages as Message[],
|
|
836
|
+
stream: true,
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
if (cachedSystemPrompt && apiFormat === "anthropic") {
|
|
840
|
+
// Anthropic format uses separate system field
|
|
841
|
+
// OpenAI format already has system as first message
|
|
842
|
+
request.system = cachedSystemPrompt;
|
|
843
|
+
}
|
|
844
|
+
|
|
713
845
|
// Set tools with format conversion if needed
|
|
714
846
|
if (tools && tools.length > 0) {
|
|
715
847
|
if (apiFormat === "openai") {
|
|
@@ -23,6 +23,7 @@ import type { InputEvent, NativeRendererType } from "../../../../../native/index
|
|
|
23
23
|
import { spinnerFrames } from "../../shared/spinner-frames.js";
|
|
24
24
|
import { MessageStoreImpl } from "./message-store.js";
|
|
25
25
|
import { InputManagerImpl, KeyEvents, inputEventToNativeKeyEvent } from "./input-handler.js";
|
|
26
|
+
import { handleScrollEvent } from "./scroll-handler.js";
|
|
26
27
|
import type {
|
|
27
28
|
InteractiveRunnerProps,
|
|
28
29
|
InteractiveState,
|
|
@@ -269,7 +270,34 @@ export class InteractiveRunner {
|
|
|
269
270
|
return true;
|
|
270
271
|
}
|
|
271
272
|
|
|
272
|
-
//
|
|
273
|
+
// Chat history scroll - handle all scroll keys via scroll handler
|
|
274
|
+
// Debug: log event details
|
|
275
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
276
|
+
console.error("[InteractiveRunner] Key event:", {
|
|
277
|
+
code: event.code,
|
|
278
|
+
shift: event.shift,
|
|
279
|
+
ctrl: event.ctrl,
|
|
280
|
+
alt: event.alt,
|
|
281
|
+
is_special: event.is_special,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const scrollResult = handleScrollEvent(
|
|
286
|
+
event,
|
|
287
|
+
this.state.scrollOffset,
|
|
288
|
+
this.messageStore.messages.length
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
if (process.env.CODER_DEBUG_SCROLL === "1") {
|
|
292
|
+
console.error("[InteractiveRunner] Scroll result:", scrollResult);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (scrollResult.handled) {
|
|
296
|
+
this.state = { ...this.state, scrollOffset: scrollResult.newOffset };
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// History navigation (plain Up/Down without shift)
|
|
273
301
|
if (KeyEvents.isUp(event)) {
|
|
274
302
|
return this._handleHistoryUp();
|
|
275
303
|
}
|
|
@@ -682,21 +710,22 @@ export class InteractiveRunner {
|
|
|
682
710
|
content: `${s.messageCount} messages`,
|
|
683
711
|
})) : [];
|
|
684
712
|
|
|
685
|
-
// NativeRenderer expects
|
|
713
|
+
// NativeRenderer expects snake_case field names (NAPI-RS keeps Rust naming)
|
|
686
714
|
return {
|
|
687
715
|
messages: renderMessages,
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
716
|
+
input_value: inputValue,
|
|
717
|
+
cursor_pos: cursorPos,
|
|
718
|
+
status_text: statusText,
|
|
719
|
+
is_loading: isLoading,
|
|
720
|
+
streaming_text: streamingText,
|
|
693
721
|
model: this.props.model,
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
722
|
+
show_help: helpMode,
|
|
723
|
+
help_text: helpText,
|
|
724
|
+
search_mode: sessionSelectMode,
|
|
725
|
+
search_query: "",
|
|
726
|
+
search_results: searchResults,
|
|
727
|
+
search_selected: 0,
|
|
728
|
+
scroll_offset: this.state.scrollOffset,
|
|
700
729
|
};
|
|
701
730
|
}
|
|
702
731
|
|