@bubblebrain-ai/bubble 0.0.15 → 0.0.17

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.
Files changed (51) hide show
  1. package/README.md +24 -0
  2. package/dist/agent/discovery-barrier.d.ts +21 -0
  3. package/dist/agent/discovery-barrier.js +173 -0
  4. package/dist/agent/internal-reminder-sanitizer.d.ts +9 -0
  5. package/dist/agent/internal-reminder-sanitizer.js +198 -0
  6. package/dist/agent/task-classifier.js +23 -5
  7. package/dist/agent.js +215 -30
  8. package/dist/context/budget.js +15 -0
  9. package/dist/context/projector.js +4 -3
  10. package/dist/debug-trace.js +14 -0
  11. package/dist/feishu/serve.js +1 -0
  12. package/dist/main.js +2 -0
  13. package/dist/model-catalog.d.ts +3 -0
  14. package/dist/model-catalog.js +44 -0
  15. package/dist/model-config.d.ts +3 -0
  16. package/dist/model-config.js +3 -0
  17. package/dist/model-pricing.d.ts +3 -2
  18. package/dist/model-pricing.js +8 -0
  19. package/dist/network/chatgpt-transport.d.ts +16 -0
  20. package/dist/network/chatgpt-transport.js +240 -0
  21. package/dist/oauth/openai-codex.d.ts +7 -2
  22. package/dist/oauth/openai-codex.js +7 -4
  23. package/dist/orchestrator/default-hooks.js +13 -2
  24. package/dist/orchestrator/hooks.d.ts +2 -0
  25. package/dist/prompt/compose.js +1 -1
  26. package/dist/prompt/reminders.js +3 -3
  27. package/dist/prompt/runtime.js +1 -0
  28. package/dist/provider-anthropic.d.ts +77 -0
  29. package/dist/provider-anthropic.js +544 -0
  30. package/dist/provider-openai-codex.d.ts +3 -0
  31. package/dist/provider-openai-codex.js +11 -2
  32. package/dist/provider-registry.d.ts +2 -0
  33. package/dist/provider-registry.js +29 -3
  34. package/dist/provider-transform.d.ts +1 -1
  35. package/dist/provider-transform.js +23 -0
  36. package/dist/provider.d.ts +4 -1
  37. package/dist/provider.js +119 -40
  38. package/dist/reasoning-debug.js +4 -1
  39. package/dist/session-log.js +17 -2
  40. package/dist/slash-commands/commands.js +4 -2
  41. package/dist/stats/usage.d.ts +4 -0
  42. package/dist/stats/usage.js +48 -11
  43. package/dist/tools/glob.js +3 -0
  44. package/dist/tools/grep.js +7 -0
  45. package/dist/tui/run.js +22 -12
  46. package/dist/tui-ink/app.js +3 -0
  47. package/dist/tui-ink/message-list.js +6 -3
  48. package/dist/tui-opentui/app.js +3 -0
  49. package/dist/tui-opentui/message-list.js +6 -3
  50. package/dist/types.d.ts +14 -1
  51. package/package.json +2 -1
package/dist/tui/run.js CHANGED
@@ -9,6 +9,8 @@ import { homedir } from "node:os";
9
9
  import { AgentAbortError } from "../agent.js";
10
10
  import { AgentRunInputQueue } from "../agent/input-controller.js";
11
11
  import { debugReasoningStream, summarizeDebugText } from "../reasoning-debug.js";
12
+ import { isHiddenToolMetadata } from "../agent/discovery-barrier.js";
13
+ import { sanitizeInternalReminderBlocks } from "../agent/internal-reminder-sanitizer.js";
12
14
  import { summarizeAgentEventForTrace, summarizeTraceError, summarizeTraceValue, traceEvent, } from "../debug-trace.js";
13
15
  import { BUILTIN_PROVIDERS, decodeModel, displayModel, isUserVisibleProvider } from "../provider-registry.js";
14
16
  import { calculateUsageCost } from "../model-pricing.js";
@@ -60,6 +62,7 @@ const PROVIDER_PRIORITY = new Map([
60
62
  ["zai", 5],
61
63
  ["zai-coding-plan", 6],
62
64
  ["kimi-for-coding", 7],
65
+ ["stepfun", 8],
63
66
  ]);
64
67
  const DEFAULT_THEME = {
65
68
  primary: "#fab283",
@@ -5075,6 +5078,7 @@ function OpenTuiApp(props) {
5075
5078
  "zhipuai-coding-plan": "Coding Plan",
5076
5079
  "zai-coding-plan": "Coding Plan",
5077
5080
  "kimi-for-coding": "Coding Plan",
5081
+ stepfun: "Step Plan API key",
5078
5082
  local: "OpenAI-compatible local endpoint",
5079
5083
  };
5080
5084
  return descriptions[providerId] ?? "API key";
@@ -6581,7 +6585,7 @@ function OpenTuiApp(props) {
6581
6585
  completionTokens: usage.completionTokens,
6582
6586
  reasoningTokens: usage.reasoningTokens,
6583
6587
  turns: usage.turns,
6584
- costText: cost ? `${formatCurrency(cost.cost)} spent${cost.estimated ? " est." : ""}` : "cost unavailable",
6588
+ costText: cost ? `${formatCurrency(cost.cost, cost.currency)} spent${cost.estimated ? " est." : ""}` : "cost unavailable",
6585
6589
  };
6586
6590
  }
6587
6591
  function sidebarMcpStates() {
@@ -6950,12 +6954,14 @@ function renderUserMessage(message, index) {
6950
6954
  }, h("box", { paddingTop: 1, paddingBottom: 1, paddingLeft: 2, backgroundColor: theme.backgroundPanel, flexShrink: 0, flexDirection: "column" }, ...userChildren));
6951
6955
  }
6952
6956
  function renderAssistantMessage(message, syntaxStyle, subtleSyntaxStyle, showThinking = true, verboseTrace = false, width = 80) {
6957
+ const visibleReasoning = showThinking
6958
+ ? sanitizeInternalReminderBlocks(message.reasoning ?? "").trim()
6959
+ : "";
6953
6960
  const modelSwitch = parseModelSwitchMessage(message.content);
6954
- if (modelSwitch && !message.reasoning?.trim() && !(message.toolCalls?.length)) {
6961
+ if (modelSwitch && !visibleReasoning && !(message.toolCalls?.length)) {
6955
6962
  return renderModelSwitchMessage(modelSwitch);
6956
6963
  }
6957
6964
  const children = [];
6958
- const visibleReasoning = showThinking ? message.reasoning?.trim() : "";
6959
6965
  const parts = message.parts ?? [];
6960
6966
  const hasParts = parts.length > 0;
6961
6967
  if (message.status && !visibleReasoning && !message.content.trim() && !(message.toolCalls?.length) && !hasParts) {
@@ -8433,12 +8439,14 @@ function pickerTitle(kind, providerId) {
8433
8439
  function getModelPickerReasoningLevels(providerId, modelId) {
8434
8440
  // Only expand into one picker row per effort for models that genuinely have a
8435
8441
  // reasoning-effort spectrum: OpenAI's reasoning models (codex gpt-5.x:
8436
- // off/minimal/low/medium/high/xhigh) and DeepSeek's v4 models. Other providers
8442
+ // off/minimal/low/medium/high/xhigh), DeepSeek's v4 models, and StepFun
8443
+ // Step Plan models. Other providers
8437
8444
  // (e.g. GLM, Moonshot/Kimi) only have a thinking on/off toggle, not an effort
8438
8445
  // control, so they stay as a single row.
8439
8446
  const isOpenAIReasoning = providerId === "openai" || providerId === "openai-codex";
8440
8447
  const isDeepseekReasoning = providerId === "deepseek" && (modelId === "deepseek-v4-flash" || modelId === "deepseek-v4-pro");
8441
- if (!isOpenAIReasoning && !isDeepseekReasoning)
8448
+ const isStepFunReasoning = providerId === "stepfun";
8449
+ if (!isOpenAIReasoning && !isDeepseekReasoning && !isStepFunReasoning)
8442
8450
  return [];
8443
8451
  const levels = getAvailableThinkingLevels(providerId, modelId);
8444
8452
  // gpt-4o and friends report only ["off"] — keep those as a single row too.
@@ -8451,8 +8459,9 @@ function displayModelWithThinking(model, thinkingLevel) {
8451
8459
  if (!providerId)
8452
8460
  return displayModel(model);
8453
8461
  // Use the same scoping as the picker: only models with a real reasoning-effort
8454
- // spectrum (OpenAI codex gpt-5.x, deepseek v4) get the "(level)" suffix. The
8455
- // on/off thinking toggle on GLM / Moonshot(Kimi) is not an effort control.
8462
+ // spectrum (OpenAI codex gpt-5.x, deepseek v4, StepFun Step Plan) get the
8463
+ // "(level)" suffix. The on/off thinking toggle on GLM / Moonshot(Kimi) is
8464
+ // not an effort control.
8456
8465
  const levels = getModelPickerReasoningLevels(providerId, modelId);
8457
8466
  if (levels.length > 1 && thinkingLevel !== "off") {
8458
8467
  return `${displayModel(model)} (${thinkingLevel})`;
@@ -8649,6 +8658,8 @@ function reconstructDisplayMessages(agentMessages) {
8649
8658
  }
8650
8659
  catch { }
8651
8660
  const toolResult = agentMessages.find((candidate) => candidate.role === "tool" && candidate.toolCallId === tc.id);
8661
+ if (isHiddenToolMetadata(toolResult ? toolResult.metadata : undefined))
8662
+ continue;
8652
8663
  toolCalls.push({
8653
8664
  id: tc.id,
8654
8665
  name: tc.name,
@@ -9389,12 +9400,11 @@ function formatCompactNumber(value) {
9389
9400
  return `${(value / 1_000).toFixed(1)}K`;
9390
9401
  return String(value);
9391
9402
  }
9392
- function formatCurrency(value) {
9403
+ function formatCurrency(value, currency = "USD") {
9393
9404
  if (value < 0.0001)
9394
- return "$0.0000";
9395
- if (value < 1)
9396
- return `$${value.toFixed(4)}`;
9397
- return `$${value.toFixed(2)}`;
9405
+ return currency === "USD" ? "$0.0000" : "CNY 0.0000";
9406
+ const amount = value < 1 ? value.toFixed(4) : value.toFixed(2);
9407
+ return currency === "USD" ? `$${amount}` : `CNY ${amount}`;
9398
9408
  }
9399
9409
  function sidebarStatusColor(kind) {
9400
9410
  if (kind === "connected")
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { Box, Text, useApp, useInput } from "ink";
4
4
  import { AgentAbortError } from "../agent.js";
5
+ import { isHiddenToolMetadata } from "../agent/discovery-barrier.js";
5
6
  import { registry as slashRegistry } from "../slash-commands/index.js";
6
7
  import { UserConfig, maskKey } from "../config.js";
7
8
  import { createPastedContentMarker, InputBox, isCtrlCInput, shouldCollapsePastedContent, } from "./input-box.js";
@@ -82,6 +83,8 @@ function reconstructDisplayMessages(agentMessages) {
82
83
  args = {};
83
84
  }
84
85
  const toolResult = agentMessages.find((tm) => tm.role === "tool" && tm.toolCallId === tc.id);
86
+ if (isHiddenToolMetadata(toolResult ? toolResult.metadata : undefined))
87
+ continue;
85
88
  toolCalls.push({
86
89
  id: tc.id,
87
90
  name: tc.name,
@@ -7,6 +7,7 @@ import { MarkdownContent, StreamingMarkdown } from "./markdown.js";
7
7
  import { buildTraceGroups, formatTracePath, traceGroupLabel } from "./trace-groups.js";
8
8
  import { EDIT_COLLAPSED_DIFF_LINES, formatEditSuccessSummary, getEditDiffDetails } from "./edit-diff.js";
9
9
  import { formatSubagentRoute } from "../agent/subagent-route-format.js";
10
+ import { sanitizeInternalReminderBlocks } from "../agent/internal-reminder-sanitizer.js";
10
11
  export function MessageList({ messages, streamingContent, streamingReasoning, streamingTools, streamingParts, terminalColumns, verboseTrace, pendingApproval, nowTick, welcomeBanner, }) {
11
12
  const hasStreaming = !!(streamingContent ||
12
13
  streamingReasoning ||
@@ -44,22 +45,24 @@ function MessageItem({ message, terminalColumns, verboseTrace, showExpandHint, n
44
45
  if (message.syntheticKind === "ui_compact_summary") {
45
46
  return _jsx(CompactionSummaryBlock, { message: message });
46
47
  }
48
+ const visibleReasoning = sanitizeInternalReminderBlocks(message.reasoning ?? "").trim();
47
49
  const hasVisibleAssistantContent = !!message.content ||
48
50
  (message.toolCalls?.length ?? 0) > 0 ||
49
51
  (message.parts?.length ?? 0) > 0 ||
50
- (!!message.reasoning && verboseTrace);
52
+ (!!visibleReasoning && verboseTrace);
51
53
  if (!hasVisibleAssistantContent)
52
54
  return null;
53
- return (_jsxs(Box, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: [message.reasoning && verboseTrace && _jsx(ReasoningTraceBlock, { reasoning: message.reasoning }), message.parts && message.parts.length > 0 ? (_jsx(MessageParts, { parts: message.parts, terminalColumns: terminalColumns, verboseTrace: verboseTrace, pendingApproval: undefined, showExpandHint: showExpandHint, nowTick: nowTick })) : (_jsxs(_Fragment, { children: [message.toolCalls && (_jsx(ToolsPart, { toolCalls: message.toolCalls, terminalColumns: terminalColumns, verboseTrace: verboseTrace, pendingApproval: undefined, showExpandHint: showExpandHint, nowTick: nowTick })), message.content && _jsx(MarkdownContent, { content: message.content })] })), verboseTrace && message.toolCalls && message.toolCalls.length > 0 && (_jsx(TurnDigest, { toolCalls: message.toolCalls })), message.taskElapsedMs !== undefined && (_jsx(TaskDurationLine, { elapsedMs: message.taskElapsedMs }))] }));
55
+ return (_jsxs(Box, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: [visibleReasoning && verboseTrace && _jsx(ReasoningTraceBlock, { reasoning: visibleReasoning }), message.parts && message.parts.length > 0 ? (_jsx(MessageParts, { parts: message.parts, terminalColumns: terminalColumns, verboseTrace: verboseTrace, pendingApproval: undefined, showExpandHint: showExpandHint, nowTick: nowTick })) : (_jsxs(_Fragment, { children: [message.toolCalls && (_jsx(ToolsPart, { toolCalls: message.toolCalls, terminalColumns: terminalColumns, verboseTrace: verboseTrace, pendingApproval: undefined, showExpandHint: showExpandHint, nowTick: nowTick })), message.content && _jsx(MarkdownContent, { content: message.content })] })), verboseTrace && message.toolCalls && message.toolCalls.length > 0 && (_jsx(TurnDigest, { toolCalls: message.toolCalls })), message.taskElapsedMs !== undefined && (_jsx(TaskDurationLine, { elapsedMs: message.taskElapsedMs }))] }));
54
56
  }
55
57
  function StreamingMessage({ content, reasoning, tools, parts, terminalColumns, verboseTrace, pendingApproval, nowTick, }) {
56
58
  const deferredContent = React.useDeferredValue(content);
57
59
  const deferredReasoning = React.useDeferredValue(reasoning);
58
60
  const deferredParts = React.useDeferredValue(parts);
61
+ const visibleReasoning = sanitizeInternalReminderBlocks(deferredReasoning).trim();
59
62
  const visibleParts = deferredParts.length > 0
60
63
  ? deferredParts
61
64
  : fallbackStreamingParts(deferredContent, tools);
62
- return (_jsxs(Box, { flexDirection: "column", children: [deferredReasoning && verboseTrace && (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(ReasoningTraceBlock, { reasoning: deferredReasoning }) })), visibleParts.length > 0 && (
65
+ return (_jsxs(Box, { flexDirection: "column", children: [visibleReasoning && verboseTrace && (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(ReasoningTraceBlock, { reasoning: visibleReasoning }) })), visibleParts.length > 0 && (
63
66
  // marginTop intentionally 0: this Box only mounts on the first non-empty
64
67
  // streaming frame, so a marginTop=1 here would visibly insert a blank
65
68
  // line under the user message right at that moment (the "spinner sits
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentui/reac
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { useKeyboard, useRenderer } from "@opentui/react";
5
5
  import { AgentAbortError } from "../agent.js";
6
+ import { isHiddenToolMetadata } from "../agent/discovery-barrier.js";
6
7
  import { registry as slashRegistry } from "../slash-commands/index.js";
7
8
  import { UserConfig, maskKey } from "../config.js";
8
9
  import { createPastedContentMarker, InputBox, shouldCollapsePastedContent, } from "./input-box.js";
@@ -96,6 +97,8 @@ function reconstructDisplayMessages(agentMessages) {
96
97
  args = {};
97
98
  }
98
99
  const toolResult = agentMessages.find((tm) => tm.role === "tool" && tm.toolCallId === tc.id);
100
+ if (isHiddenToolMetadata(toolResult ? toolResult.metadata : undefined))
101
+ continue;
99
102
  toolCalls.push({
100
103
  id: tc.id,
101
104
  name: tc.name,
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentui/reac
2
2
  import { useTheme } from "./theme.js";
3
3
  import { MarkdownContent, StreamingMarkdown } from "./markdown.js";
4
4
  import { EDIT_COLLAPSED_DIFF_LINES, getEditDiffDetails } from "./edit-diff.js";
5
+ import { sanitizeInternalReminderBlocks } from "../agent/internal-reminder-sanitizer.js";
5
6
  /**
6
7
  * Scrollback-style message list following opencode visual rules:
7
8
  *
@@ -29,18 +30,20 @@ function MessageItem({ message, terminalColumns, verboseTrace, }) {
29
30
  if (message.syntheticKind === "ui_compact_summary") {
30
31
  return _jsx(CompactionSummaryBlock, { message: message, theme: theme });
31
32
  }
33
+ const visibleReasoning = sanitizeInternalReminderBlocks(message.reasoning ?? "").trim();
32
34
  const hasVisible = !!message.content ||
33
35
  (message.toolCalls?.length ?? 0) > 0 ||
34
36
  (message.parts?.length ?? 0) > 0 ||
35
- (!!message.reasoning && verboseTrace);
37
+ (!!visibleReasoning && verboseTrace);
36
38
  if (!hasVisible)
37
39
  return null;
38
- return (_jsxs("box", { style: { marginTop: 1, marginBottom: 1, flexDirection: "column" }, children: [message.reasoning && verboseTrace && _jsx(ReasoningBlock, { reasoning: message.reasoning, theme: theme }), message.parts && message.parts.length > 0 ? (_jsx(MessageParts, { parts: message.parts, terminalColumns: terminalColumns, verboseTrace: verboseTrace, theme: theme })) : (_jsxs(_Fragment, { children: [message.toolCalls && _jsx(ToolsPart, { toolCalls: message.toolCalls, terminalColumns: terminalColumns, verboseTrace: verboseTrace, theme: theme }), message.content && _jsx(MarkdownContent, { content: message.content, terminalColumns: terminalColumns })] }))] }));
40
+ return (_jsxs("box", { style: { marginTop: 1, marginBottom: 1, flexDirection: "column" }, children: [visibleReasoning && verboseTrace && _jsx(ReasoningBlock, { reasoning: visibleReasoning, theme: theme }), message.parts && message.parts.length > 0 ? (_jsx(MessageParts, { parts: message.parts, terminalColumns: terminalColumns, verboseTrace: verboseTrace, theme: theme })) : (_jsxs(_Fragment, { children: [message.toolCalls && _jsx(ToolsPart, { toolCalls: message.toolCalls, terminalColumns: terminalColumns, verboseTrace: verboseTrace, theme: theme }), message.content && _jsx(MarkdownContent, { content: message.content, terminalColumns: terminalColumns })] }))] }));
39
41
  }
40
42
  function StreamingMessage({ content, reasoning, tools, parts, terminalColumns, verboseTrace, }) {
41
43
  const theme = useTheme();
44
+ const visibleReasoning = sanitizeInternalReminderBlocks(reasoning).trim();
42
45
  const visibleParts = parts.length > 0 ? parts : fallbackStreamingParts(content, tools);
43
- return (_jsxs("box", { style: { flexDirection: "column", marginTop: 1 }, children: [reasoning && verboseTrace && _jsx(ReasoningBlock, { reasoning: reasoning, theme: theme }), visibleParts.length > 0 && (_jsx(MessageParts, { parts: visibleParts, terminalColumns: terminalColumns, verboseTrace: verboseTrace, streaming: true, theme: theme }))] }));
46
+ return (_jsxs("box", { style: { flexDirection: "column", marginTop: 1 }, children: [visibleReasoning && verboseTrace && _jsx(ReasoningBlock, { reasoning: visibleReasoning, theme: theme }), visibleParts.length > 0 && (_jsx(MessageParts, { parts: visibleParts, terminalColumns: terminalColumns, verboseTrace: verboseTrace, streaming: true, theme: theme }))] }));
44
47
  }
45
48
  function fallbackStreamingParts(content, tools) {
46
49
  const out = [];
package/dist/types.d.ts CHANGED
@@ -14,6 +14,14 @@ export interface ImageContent {
14
14
  export type ContentPart = TextContent | ImageContent;
15
15
  export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
16
16
  export type ReasoningEffort = ThinkingLevel;
17
+ export type ProviderRawContentBlock = Record<string, unknown> & {
18
+ type: string;
19
+ };
20
+ export interface AssistantProviderMetadata {
21
+ anthropic?: {
22
+ contentBlocks?: ProviderRawContentBlock[];
23
+ };
24
+ }
17
25
  export interface UserMessage {
18
26
  role: "user";
19
27
  content: string | ContentPart[];
@@ -23,6 +31,7 @@ export interface AssistantMessage {
23
31
  content: string;
24
32
  reasoning?: string;
25
33
  toolCalls?: ToolCall[];
34
+ providerMetadata?: AssistantProviderMetadata;
26
35
  /** Model metadata captured for local usage statistics. */
27
36
  model?: string;
28
37
  providerId?: string;
@@ -102,7 +111,7 @@ export interface ParsedToolCall extends ToolCall {
102
111
  }
103
112
  export type ToolResultStatus = "success" | "no_match" | "partial" | "timeout" | "blocked" | "cancelled" | "command_error";
104
113
  export interface ToolResultMetadata {
105
- kind?: "search" | "read" | "write" | "edit" | "patch" | "shell" | "server" | "web" | "security" | "lsp" | "question" | "subagent";
114
+ kind?: "search" | "read" | "write" | "edit" | "patch" | "shell" | "server" | "web" | "security" | "lsp" | "question" | "subagent" | "internal";
106
115
  path?: string;
107
116
  pattern?: string;
108
117
  matches?: number;
@@ -239,6 +248,10 @@ export type StreamChunk = {
239
248
  } | {
240
249
  type: "reasoning_delta";
241
250
  content: string;
251
+ } | {
252
+ type: "provider_content_block";
253
+ provider: "anthropic";
254
+ block: ProviderRawContentBlock;
242
255
  } | {
243
256
  type: "tool_call";
244
257
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bubblebrain-ai/bubble",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "A terminal coding agent",
5
5
  "type": "module",
6
6
  "engines": {
@@ -44,6 +44,7 @@
44
44
  "solid-js": "^1.9.12",
45
45
  "string-width": "^8.2.1",
46
46
  "typescript-language-server": "^5.1.3",
47
+ "undici": "^6.26.0",
47
48
  "vscode-jsonrpc": "^8.2.1",
48
49
  "vscode-langservers-extracted": "^4.10.0"
49
50
  },