@kontextmind/kxm 0.7.17 → 0.7.19
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/.claude-plugin/marketplace.json +1 -1
- package/docs/configuration.md +17 -1
- package/docs/getting-started.md +4 -1
- package/docs/kxm-handbook.md +6 -2
- package/package.json +8 -6
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +484 -458
- package/plugins/kxm/dist/core.js +3 -2
- package/plugins/kxm/dist/extension.js +28841 -5273
- package/plugins/kxm/dist/mcp-server.js +21 -10
- package/plugins/kxm/dist/runtime.js +43 -39
- package/plugins/kxm/dist/server.js +43 -39
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +40 -37
- package/plugins/kxm/package.json +4 -1
- package/plugins/kxm/src/cli/hub.ts +3 -2
- package/plugins/kxm/src/cli.ts +3 -2
- package/plugins/kxm/src/config.ts +20 -0
- package/plugins/kxm/src/extension.ts +46 -3
- package/plugins/kxm/src/hub-autostart.ts +214 -0
- package/plugins/kxm/src/mcp-server.ts +3 -3
- package/plugins/kxm/src/project-name.ts +20 -0
- package/plugins/kxm/src/providers/claude-bridge/LICENSE +21 -0
- package/plugins/kxm/src/providers/claude-bridge/README.md +18 -0
- package/plugins/kxm/src/providers/claude-bridge/config.ts +53 -0
- package/plugins/kxm/src/providers/claude-bridge/convert.ts +180 -0
- package/plugins/kxm/src/providers/claude-bridge/index.ts +28 -0
- package/plugins/kxm/src/providers/claude-bridge/mcp-server.ts +59 -0
- package/plugins/kxm/src/providers/claude-bridge/models.ts +166 -0
- package/plugins/kxm/src/providers/claude-bridge/pi-compat.ts +215 -0
- package/plugins/kxm/src/providers/claude-bridge/prompt-stream.ts +89 -0
- package/plugins/kxm/src/providers/claude-bridge/provider.ts +286 -0
- package/plugins/kxm/src/providers/claude-bridge/query-state.ts +72 -0
- package/plugins/kxm/src/providers/claude-bridge/redact.ts +26 -0
- package/plugins/kxm/src/providers/claude-bridge/register.ts +124 -0
- package/plugins/kxm/src/providers/claude-bridge/stream.ts +390 -0
- package/plugins/kxm/src/providers/claude-bridge/tools.ts +140 -0
- package/plugins/kxm/src/redact.ts +3 -2
- package/scripts/build-runtime.mjs +5 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @ts-nocheck — vendored snapshot; host tsconfig is stricter than upstream.
|
|
2
|
+
// In-process MCP server that exposes pi tools to Claude Code.
|
|
3
|
+
// From pi-claude-bridge 0.7.0. Not a user-facing MCP product.
|
|
4
|
+
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import type { McpResult } from "./tools.ts";
|
|
8
|
+
|
|
9
|
+
const TOOL_USE_ID_META = "claudecode/toolUseId";
|
|
10
|
+
|
|
11
|
+
export interface McpToolDef {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: unknown;
|
|
15
|
+
handler: (toolCallId: string) => Promise<McpResult>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function assertObjectSchema(tool: McpToolDef): void {
|
|
19
|
+
const schema = tool.inputSchema as Record<string, unknown> | undefined;
|
|
20
|
+
if (!schema || schema.type !== "object") {
|
|
21
|
+
throw new Error(`${tool.name}: MCP tool parameters must be an object schema, got ${JSON.stringify(schema)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createToolServer(name: string, tools: McpToolDef[]) {
|
|
26
|
+
const server = new McpServer({ name, version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
27
|
+
const byName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
28
|
+
for (const tool of tools) assertObjectSchema(tool);
|
|
29
|
+
|
|
30
|
+
server.server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
31
|
+
tools: tools.map((tool) => ({
|
|
32
|
+
name: tool.name,
|
|
33
|
+
description: tool.description,
|
|
34
|
+
inputSchema: tool.inputSchema as Record<string, unknown>,
|
|
35
|
+
})),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request) => dispatchToolCall(byName, request));
|
|
39
|
+
|
|
40
|
+
return { type: "sdk" as const, name, instance: server };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function dispatchToolCall(
|
|
44
|
+
byName: Map<string, McpToolDef>,
|
|
45
|
+
request: { params: { name: string; _meta?: Record<string, unknown> } },
|
|
46
|
+
) {
|
|
47
|
+
const tool = byName.get(request.params.name);
|
|
48
|
+
if (!tool) throw new Error(`Unknown tool: ${request.params.name}`);
|
|
49
|
+
const toolCallId = request.params._meta?.[TOOL_USE_ID_META];
|
|
50
|
+
if (typeof toolCallId !== "string") {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`${tool.name}: tools/call is missing _meta["${TOOL_USE_ID_META}"] — cannot pair the result with its tool call`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const { content, isError } = await tool.handler(toolCallId);
|
|
56
|
+
return { content, isError };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { TOOL_USE_ID_META };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Canonical selection + display order for the model picker.
|
|
2
|
+
// Extracted from pi-claude-bridge 0.7.0; catalog is static (no pi-ai getModels).
|
|
3
|
+
|
|
4
|
+
import type { Model, ProviderModelConfig, ThinkingLevel, ThinkingLevelMap } from "./pi-compat.ts";
|
|
5
|
+
|
|
6
|
+
export const PROVIDER_ID = "claude-bridge";
|
|
7
|
+
export const PROVIDER_NAME = "Claude Bridge";
|
|
8
|
+
export const CLAUDE_BRIDGE_API = "claude-bridge";
|
|
9
|
+
|
|
10
|
+
export const MODEL_IDS_IN_ORDER = [
|
|
11
|
+
"claude-fable-5-1",
|
|
12
|
+
"claude-fable-5",
|
|
13
|
+
"claude-opus-5",
|
|
14
|
+
"claude-opus-4-8",
|
|
15
|
+
"claude-opus-4-7",
|
|
16
|
+
"claude-opus-4-6",
|
|
17
|
+
"claude-sonnet-5",
|
|
18
|
+
"claude-sonnet-4-6",
|
|
19
|
+
"claude-haiku-4-5",
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
export type ClaudeBridgeModelId = (typeof MODEL_IDS_IN_ORDER)[number];
|
|
23
|
+
|
|
24
|
+
const DISPLAY_NAMES: Record<ClaudeBridgeModelId, string> = {
|
|
25
|
+
"claude-fable-5-1": "Claude Fable 5.1",
|
|
26
|
+
"claude-fable-5": "Claude Fable 5",
|
|
27
|
+
"claude-opus-5": "Claude Opus 5",
|
|
28
|
+
"claude-opus-4-8": "Claude Opus 4.8",
|
|
29
|
+
"claude-opus-4-7": "Claude Opus 4.7",
|
|
30
|
+
"claude-opus-4-6": "Claude Opus 4.6",
|
|
31
|
+
"claude-sonnet-5": "Claude Sonnet 5",
|
|
32
|
+
"claude-sonnet-4-6": "Claude Sonnet 4.6",
|
|
33
|
+
"claude-haiku-4-5": "Claude Haiku 4.5",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Per-model effort overrides (pi-ai 0.72+ shipped these for later Opus). */
|
|
37
|
+
const THINKING_LEVEL_MAPS: Partial<Record<ClaudeBridgeModelId, ThinkingLevelMap>> = {
|
|
38
|
+
"claude-opus-4-8": { xhigh: "xhigh", max: "max" },
|
|
39
|
+
"claude-opus-4-7": { xhigh: "xhigh", max: "max" },
|
|
40
|
+
"claude-opus-5": { xhigh: "xhigh", max: "max" },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type LongContextSettings = {
|
|
44
|
+
plan: "pro" | "max";
|
|
45
|
+
longContextExtraUsage: boolean;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type ClaudeCodeRuntimeModel = {
|
|
49
|
+
cliModelId: string;
|
|
50
|
+
contextWindow: number;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const TWO_HUNDRED_K_CONTEXT = 200_000;
|
|
54
|
+
const ONE_M_CONTEXT = 1_000_000;
|
|
55
|
+
|
|
56
|
+
export const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } as const;
|
|
57
|
+
|
|
58
|
+
export const DEFAULT_LONG_CONTEXT: LongContextSettings = {
|
|
59
|
+
plan: "pro",
|
|
60
|
+
longContextExtraUsage: false,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Pi reasoning levels → Claude Agent SDK effort levels. */
|
|
64
|
+
export const REASONING_TO_EFFORT: Record<string, string> = {
|
|
65
|
+
minimal: "low",
|
|
66
|
+
low: "low",
|
|
67
|
+
medium: "medium",
|
|
68
|
+
high: "high",
|
|
69
|
+
xhigh: "max",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export function resolveEffort(
|
|
73
|
+
reasoning: ThinkingLevel | undefined,
|
|
74
|
+
thinkingLevelMap?: ThinkingLevelMap,
|
|
75
|
+
): string | undefined {
|
|
76
|
+
if (!reasoning) return undefined;
|
|
77
|
+
const mapped = thinkingLevelMap?.[reasoning];
|
|
78
|
+
if (typeof mapped === "string") return mapped;
|
|
79
|
+
return REASONING_TO_EFFORT[reasoning];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function resolveClaudeCodeRuntimeModel(
|
|
83
|
+
modelId: string,
|
|
84
|
+
settings: LongContextSettings,
|
|
85
|
+
): ClaudeCodeRuntimeModel {
|
|
86
|
+
switch (modelId) {
|
|
87
|
+
case "claude-opus-5":
|
|
88
|
+
return { cliModelId: "claude-opus-5[1m]", contextWindow: ONE_M_CONTEXT };
|
|
89
|
+
case "claude-opus-4-8":
|
|
90
|
+
return { cliModelId: "claude-opus-4-8[1m]", contextWindow: ONE_M_CONTEXT };
|
|
91
|
+
case "claude-opus-4-7":
|
|
92
|
+
return { cliModelId: "claude-opus-4-7", contextWindow: ONE_M_CONTEXT };
|
|
93
|
+
case "claude-opus-4-6": {
|
|
94
|
+
const useOneM = settings.plan === "max" || settings.longContextExtraUsage;
|
|
95
|
+
return {
|
|
96
|
+
cliModelId: useOneM ? "claude-opus-4-6[1m]" : "claude-opus-4-6",
|
|
97
|
+
contextWindow: useOneM ? ONE_M_CONTEXT : TWO_HUNDRED_K_CONTEXT,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case "claude-fable-5-1":
|
|
101
|
+
return { cliModelId: "claude-fable-5-1[1m]", contextWindow: ONE_M_CONTEXT };
|
|
102
|
+
case "claude-fable-5":
|
|
103
|
+
return { cliModelId: "claude-fable-5[1m]", contextWindow: ONE_M_CONTEXT };
|
|
104
|
+
case "claude-sonnet-5":
|
|
105
|
+
return { cliModelId: "claude-sonnet-5[1m]", contextWindow: ONE_M_CONTEXT };
|
|
106
|
+
case "claude-sonnet-4-6":
|
|
107
|
+
return {
|
|
108
|
+
cliModelId: settings.longContextExtraUsage ? "claude-sonnet-4-6[1m]" : "claude-sonnet-4-6",
|
|
109
|
+
contextWindow: settings.longContextExtraUsage ? ONE_M_CONTEXT : TWO_HUNDRED_K_CONTEXT,
|
|
110
|
+
};
|
|
111
|
+
case "claude-haiku-4-5":
|
|
112
|
+
return { cliModelId: "claude-haiku-4-5", contextWindow: TWO_HUNDRED_K_CONTEXT };
|
|
113
|
+
default:
|
|
114
|
+
return { cliModelId: modelId, contextWindow: TWO_HUNDRED_K_CONTEXT };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function claudeCodeModelId(model: { id: string }, settings: LongContextSettings): string {
|
|
119
|
+
return resolveClaudeCodeRuntimeModel(model.id, settings).cliModelId;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function resolveModel<T extends { id: string }>(models: T[], input: string): T | undefined {
|
|
123
|
+
const lower = input.toLowerCase();
|
|
124
|
+
return models.find((m) => m.id === lower || m.id.includes(lower));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function applyLongContext<T extends { id: string; name: string; contextWindow?: number | null }>(
|
|
128
|
+
models: T[],
|
|
129
|
+
settings: LongContextSettings,
|
|
130
|
+
): T[] {
|
|
131
|
+
return models.map((m) => {
|
|
132
|
+
const { contextWindow } = resolveClaudeCodeRuntimeModel(m.id, settings);
|
|
133
|
+
const name =
|
|
134
|
+
contextWindow > TWO_HUNDRED_K_CONTEXT && !/\b1M\b/i.test(m.name) ? `${m.name} 1M` : m.name;
|
|
135
|
+
return contextWindow === m.contextWindow && name === m.name ? m : { ...m, contextWindow, name };
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function staticModel(id: ClaudeBridgeModelId): ProviderModelConfig {
|
|
140
|
+
const runtime = resolveClaudeCodeRuntimeModel(id, DEFAULT_LONG_CONTEXT);
|
|
141
|
+
const name = DISPLAY_NAMES[id];
|
|
142
|
+
const thinkingLevelMap = THINKING_LEVEL_MAPS[id];
|
|
143
|
+
return {
|
|
144
|
+
id,
|
|
145
|
+
name: runtime.contextWindow > TWO_HUNDRED_K_CONTEXT && !/\b1M\b/i.test(name) ? `${name} 1M` : name,
|
|
146
|
+
reasoning: true,
|
|
147
|
+
input: ["text", "image"],
|
|
148
|
+
cost: { ...ZERO_COST },
|
|
149
|
+
contextWindow: runtime.contextWindow,
|
|
150
|
+
maxTokens: 32_000,
|
|
151
|
+
...(thinkingLevelMap ? { thinkingLevelMap } : {}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const CLAUDE_BRIDGE_MODELS: ProviderModelConfig[] = MODEL_IDS_IN_ORDER.map(staticModel);
|
|
156
|
+
|
|
157
|
+
export function registeredClaudeBridgeModels(
|
|
158
|
+
settings: LongContextSettings = DEFAULT_LONG_CONTEXT,
|
|
159
|
+
): Model[] {
|
|
160
|
+
return applyLongContext(CLAUDE_BRIDGE_MODELS, settings).map((model) => ({
|
|
161
|
+
...model,
|
|
162
|
+
api: CLAUDE_BRIDGE_API,
|
|
163
|
+
provider: PROVIDER_ID,
|
|
164
|
+
baseUrl: PROVIDER_ID,
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural Pi stream types used by the vendored claude-bridge provider.
|
|
3
|
+
* No @earendil-works/pi-ai import (nested copy is forbidden).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type Api = string;
|
|
7
|
+
export type ProviderId = string;
|
|
8
|
+
export type ThinkingLevel = "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
9
|
+
export type ModelThinkingLevel = "off" | ThinkingLevel;
|
|
10
|
+
export type ThinkingLevelMap = Partial<Record<ModelThinkingLevel, string | null>>;
|
|
11
|
+
|
|
12
|
+
export type ModelCostRates = {
|
|
13
|
+
input: number;
|
|
14
|
+
output: number;
|
|
15
|
+
cacheRead: number;
|
|
16
|
+
cacheWrite: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ModelCost = ModelCostRates;
|
|
20
|
+
|
|
21
|
+
export type ProviderModelConfig = {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
api?: Api;
|
|
25
|
+
baseUrl?: string;
|
|
26
|
+
reasoning: boolean;
|
|
27
|
+
thinkingLevelMap?: ThinkingLevelMap;
|
|
28
|
+
input: Array<"text" | "image">;
|
|
29
|
+
cost: ModelCost;
|
|
30
|
+
contextWindow: number;
|
|
31
|
+
maxTokens: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type Model<TApi extends Api = Api> = ProviderModelConfig & {
|
|
35
|
+
api: TApi;
|
|
36
|
+
provider: ProviderId;
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type TextContent = {
|
|
41
|
+
type: "text";
|
|
42
|
+
text: string;
|
|
43
|
+
textSignature?: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type ThinkingContent = {
|
|
47
|
+
type: "thinking";
|
|
48
|
+
thinking: string;
|
|
49
|
+
thinkingSignature?: string;
|
|
50
|
+
redacted?: boolean;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type ImageContent = {
|
|
54
|
+
type: "image";
|
|
55
|
+
data: string;
|
|
56
|
+
mimeType: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type ToolCall = {
|
|
60
|
+
type: "toolCall";
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
arguments: Record<string, unknown>;
|
|
64
|
+
thoughtSignature?: string;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type Usage = {
|
|
68
|
+
input: number;
|
|
69
|
+
output: number;
|
|
70
|
+
cacheRead: number;
|
|
71
|
+
cacheWrite: number;
|
|
72
|
+
reasoning?: number;
|
|
73
|
+
totalTokens: number;
|
|
74
|
+
cost: {
|
|
75
|
+
input: number;
|
|
76
|
+
output: number;
|
|
77
|
+
cacheRead: number;
|
|
78
|
+
cacheWrite: number;
|
|
79
|
+
total: number;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export type StopReason = "pending" | "stop" | "length" | "toolUse" | "error" | "aborted" | "deferred";
|
|
84
|
+
|
|
85
|
+
export type UserMessage = {
|
|
86
|
+
role: "user";
|
|
87
|
+
content: string | Array<TextContent | ImageContent>;
|
|
88
|
+
timestamp: number;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type AssistantMessage = {
|
|
92
|
+
role: "assistant";
|
|
93
|
+
content: Array<TextContent | ThinkingContent | ToolCall>;
|
|
94
|
+
api: Api;
|
|
95
|
+
provider: ProviderId;
|
|
96
|
+
model: string;
|
|
97
|
+
usage: Usage;
|
|
98
|
+
stopReason: StopReason;
|
|
99
|
+
errorMessage?: string;
|
|
100
|
+
timestamp: number;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type ToolResultMessage = {
|
|
104
|
+
role: "toolResult";
|
|
105
|
+
toolCallId: string;
|
|
106
|
+
toolName?: string;
|
|
107
|
+
content: string | Array<TextContent | ImageContent>;
|
|
108
|
+
isError?: boolean;
|
|
109
|
+
timestamp?: number;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export type Message = UserMessage | AssistantMessage | ToolResultMessage;
|
|
113
|
+
|
|
114
|
+
export type Tool = {
|
|
115
|
+
name: string;
|
|
116
|
+
description: string;
|
|
117
|
+
parameters: unknown;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type Context = {
|
|
121
|
+
systemPrompt?: string;
|
|
122
|
+
messages: Message[];
|
|
123
|
+
tools?: Tool[];
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type AssistantMessageEvent =
|
|
127
|
+
| { type: "start"; partial: AssistantMessage }
|
|
128
|
+
| { type: "text_start"; contentIndex: number; partial: AssistantMessage }
|
|
129
|
+
| { type: "text_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
130
|
+
| { type: "text_end"; contentIndex: number; content: string; partial: AssistantMessage }
|
|
131
|
+
| { type: "thinking_start"; contentIndex: number; partial: AssistantMessage }
|
|
132
|
+
| { type: "thinking_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
133
|
+
| { type: "thinking_end"; contentIndex: number; content: string; partial: AssistantMessage }
|
|
134
|
+
| { type: "toolcall_start"; contentIndex: number; partial: AssistantMessage }
|
|
135
|
+
| { type: "toolcall_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
136
|
+
| { type: "toolcall_end"; contentIndex: number; toolCall: ToolCall; partial: AssistantMessage }
|
|
137
|
+
| { type: "done"; reason: StopReason; message: AssistantMessage }
|
|
138
|
+
| { type: "error"; reason: StopReason; error: AssistantMessage };
|
|
139
|
+
|
|
140
|
+
export class AssistantMessageEventStream {
|
|
141
|
+
queue: AssistantMessageEvent[] = [];
|
|
142
|
+
waiting: Array<(result: { value?: AssistantMessageEvent; done: boolean }) => void> = [];
|
|
143
|
+
done = false;
|
|
144
|
+
finalResultPromise: Promise<AssistantMessage>;
|
|
145
|
+
private resolveFinalResult!: (message: AssistantMessage) => void;
|
|
146
|
+
|
|
147
|
+
constructor() {
|
|
148
|
+
this.finalResultPromise = new Promise((resolve) => {
|
|
149
|
+
this.resolveFinalResult = resolve;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
push(event: AssistantMessageEvent): void {
|
|
154
|
+
if (this.done) return;
|
|
155
|
+
if (event.type === "done") {
|
|
156
|
+
this.done = true;
|
|
157
|
+
this.resolveFinalResult(event.message);
|
|
158
|
+
} else if (event.type === "error") {
|
|
159
|
+
this.done = true;
|
|
160
|
+
this.resolveFinalResult(event.error);
|
|
161
|
+
}
|
|
162
|
+
const waiter = this.waiting.shift();
|
|
163
|
+
if (waiter) waiter({ value: event, done: false });
|
|
164
|
+
else this.queue.push(event);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
end(result?: AssistantMessage): void {
|
|
168
|
+
this.done = true;
|
|
169
|
+
if (result !== undefined) this.resolveFinalResult(result);
|
|
170
|
+
while (this.waiting.length > 0) {
|
|
171
|
+
this.waiting.shift()?.({ done: true });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async *[Symbol.asyncIterator](): AsyncGenerator<AssistantMessageEvent> {
|
|
176
|
+
while (true) {
|
|
177
|
+
if (this.queue.length > 0) {
|
|
178
|
+
yield this.queue.shift()!;
|
|
179
|
+
} else if (this.done) {
|
|
180
|
+
return;
|
|
181
|
+
} else {
|
|
182
|
+
const next = await new Promise<{ value?: AssistantMessageEvent; done: boolean }>((resolve) => {
|
|
183
|
+
this.waiting.push(resolve);
|
|
184
|
+
});
|
|
185
|
+
if (next.done) return;
|
|
186
|
+
if (next.value) yield next.value;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
result(): Promise<AssistantMessage> {
|
|
192
|
+
return this.finalResultPromise;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function createAssistantMessageEventStream(): AssistantMessageEventStream {
|
|
197
|
+
return new AssistantMessageEventStream();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function emptyUsage(): Usage {
|
|
201
|
+
return {
|
|
202
|
+
input: 0,
|
|
203
|
+
output: 0,
|
|
204
|
+
cacheRead: 0,
|
|
205
|
+
cacheWrite: 0,
|
|
206
|
+
totalTokens: 0,
|
|
207
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export type SimpleStreamOptions = {
|
|
212
|
+
signal?: AbortSignal;
|
|
213
|
+
cwd?: string;
|
|
214
|
+
reasoning?: ThinkingLevel;
|
|
215
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Long-lived streaming-input prompt for query().
|
|
2
|
+
// Vendored from pi-claude-bridge 0.7.0 (provider path only).
|
|
3
|
+
|
|
4
|
+
export type PromptUserMessage = {
|
|
5
|
+
type: "user";
|
|
6
|
+
message: { role: "user"; content: unknown };
|
|
7
|
+
parent_tool_use_id: null;
|
|
8
|
+
priority?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface PromptStream {
|
|
12
|
+
stream: AsyncGenerator<PromptUserMessage>;
|
|
13
|
+
push: (msg: PromptUserMessage) => Promise<void>;
|
|
14
|
+
end: () => void;
|
|
15
|
+
fail: (error: Error) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function makePromptStream(): PromptStream {
|
|
19
|
+
type Item = { msg: PromptUserMessage; resolve: () => void; reject: (e: Error) => void };
|
|
20
|
+
const queue: Item[] = [];
|
|
21
|
+
let inflight: Item | null = null;
|
|
22
|
+
let wake: (() => void) | null = null;
|
|
23
|
+
let done = false;
|
|
24
|
+
let failure: Error | null = null;
|
|
25
|
+
|
|
26
|
+
const kick = () => {
|
|
27
|
+
wake?.();
|
|
28
|
+
wake = null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
async function* gen(): AsyncGenerator<PromptUserMessage> {
|
|
32
|
+
try {
|
|
33
|
+
while (true) {
|
|
34
|
+
while (queue.length === 0 && !done && !failure) {
|
|
35
|
+
await new Promise<void>((resolve) => {
|
|
36
|
+
wake = resolve;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (failure) throw failure;
|
|
40
|
+
const item = queue.shift();
|
|
41
|
+
if (!item) return;
|
|
42
|
+
inflight = item;
|
|
43
|
+
try {
|
|
44
|
+
yield item.msg;
|
|
45
|
+
item.resolve();
|
|
46
|
+
} finally {
|
|
47
|
+
item.reject(new Error("prompt stream closed"));
|
|
48
|
+
inflight = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} finally {
|
|
52
|
+
done = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
stream: gen(),
|
|
58
|
+
push: (msg) =>
|
|
59
|
+
failure || done
|
|
60
|
+
? Promise.reject(failure ?? new Error("prompt stream closed"))
|
|
61
|
+
: new Promise<void>((resolve, reject) => {
|
|
62
|
+
queue.push({ msg, resolve, reject });
|
|
63
|
+
kick();
|
|
64
|
+
}),
|
|
65
|
+
end: () => {
|
|
66
|
+
done = true;
|
|
67
|
+
kick();
|
|
68
|
+
},
|
|
69
|
+
fail: (error) => {
|
|
70
|
+
if (failure) return;
|
|
71
|
+
failure = error;
|
|
72
|
+
queue.splice(0).forEach((item) => item.reject(error));
|
|
73
|
+
inflight?.reject(error);
|
|
74
|
+
kick();
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function userMessage(
|
|
80
|
+
content: unknown,
|
|
81
|
+
priority?: string,
|
|
82
|
+
): PromptUserMessage {
|
|
83
|
+
return {
|
|
84
|
+
type: "user",
|
|
85
|
+
message: { role: "user", content },
|
|
86
|
+
parent_tool_use_id: null,
|
|
87
|
+
...(priority ? { priority } : {}),
|
|
88
|
+
};
|
|
89
|
+
}
|