fix: resolve all ESLint errors breaking CI validate job
- Fix import sort order in 6 files (TeamDataService, TeamProvisioningService, ChatHistory, SortableTab, LeadThoughtsGroup, MemberDraftRow) - Replace slow regex with string manipulation in MemberStatsComputer - Use RegExp.exec() instead of String.match() in toolSummary - Fix non-backtracking regex pattern in toolSummary parser - Remove unnecessary type assertions in TeamDataService and TeamProvisioningService - Convert for-loop to for-of in TeamDataService - Fix no-param-reassign in TeamDataService.sendMessage - Convert function component to arrow function in LeadThoughtsGroup - Fix nested functions depth in MentionableTextarea tip rotation - Use optional chain in teamSlice
This commit is contained in:
parent
f61077d4ee
commit
baabae0594
10 changed files with 54 additions and 40 deletions
|
|
@ -9,11 +9,17 @@ import type { FileLineStats, MemberFullStats } from '@shared/types';
|
||||||
|
|
||||||
const logger = createLogger('Service:MemberStatsComputer');
|
const logger = createLogger('Service:MemberStatsComputer');
|
||||||
|
|
||||||
const TRAILING_PUNCT = /[;.,]+$/;
|
const TRAILING_PUNCT_CHARS = new Set([';', '.', ',']);
|
||||||
const INVALID_NAMES = new Set(['null', 'undefined', 'None', 'false', 'true', '']);
|
const INVALID_NAMES = new Set(['null', 'undefined', 'None', 'false', 'true', '']);
|
||||||
|
|
||||||
|
function stripTrailingPunct(s: string): string {
|
||||||
|
let end = s.length;
|
||||||
|
while (end > 0 && TRAILING_PUNCT_CHARS.has(s[end - 1])) end--;
|
||||||
|
return end === s.length ? s : s.slice(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
export function isValidFilePath(value: string): boolean {
|
export function isValidFilePath(value: string): boolean {
|
||||||
const cleaned = value.trim().replace(TRAILING_PUNCT, '');
|
const cleaned = stripTrailingPunct(value.trim());
|
||||||
return cleaned.length > 1 && !INVALID_NAMES.has(cleaned) && cleaned.includes('/');
|
return cleaned.length > 1 && !INVALID_NAMES.has(cleaned) && cleaned.includes('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +139,7 @@ export class MemberStatsComputer {
|
||||||
// Track last known content per file for accurate Write/NotebookEdit diffs
|
// Track last known content per file for accurate Write/NotebookEdit diffs
|
||||||
const fileLastContent = new Map<string, string>();
|
const fileLastContent = new Map<string, string>();
|
||||||
|
|
||||||
const cleanPath = (fp: string): string => fp.trim().replace(TRAILING_PUNCT, '');
|
const cleanPath = (fp: string): string => stripTrailingPunct(fp.trim());
|
||||||
|
|
||||||
const trackFile = (fp: string): void => {
|
const trackFile = (fp: string): void => {
|
||||||
if (typeof fp === 'string') {
|
if (typeof fp === 'string') {
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ import type {
|
||||||
TeamTask,
|
TeamTask,
|
||||||
TeamTaskStatus,
|
TeamTaskStatus,
|
||||||
TeamTaskWithKanban,
|
TeamTaskWithKanban,
|
||||||
UpdateKanbanPatch,
|
|
||||||
ToolCallMeta,
|
ToolCallMeta,
|
||||||
|
UpdateKanbanPatch,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
|
|
||||||
const logger = createLogger('Service:TeamDataService');
|
const logger = createLogger('Service:TeamDataService');
|
||||||
|
|
@ -344,12 +344,12 @@ export class TeamDataService {
|
||||||
// Find closest anchor by timestamp (binary-search-like scan from current position)
|
// Find closest anchor by timestamp (binary-search-like scan from current position)
|
||||||
let bestAnchor = anchors[0];
|
let bestAnchor = anchors[0];
|
||||||
let bestDist = Math.abs(msgTime - bestAnchor.time);
|
let bestDist = Math.abs(msgTime - bestAnchor.time);
|
||||||
for (let a = 0; a < anchors.length; a++) {
|
for (const anchor of anchors) {
|
||||||
const dist = Math.abs(msgTime - anchors[a].time);
|
const dist = Math.abs(msgTime - anchor.time);
|
||||||
if (dist < bestDist) {
|
if (dist < bestDist) {
|
||||||
bestDist = dist;
|
bestDist = dist;
|
||||||
bestAnchor = anchors[a];
|
bestAnchor = anchor;
|
||||||
} else if (dist > bestDist && anchors[a].time > msgTime) {
|
} else if (dist > bestDist && anchor.time > msgTime) {
|
||||||
// Anchors are sorted by index (asc time) — once distance grows past the
|
// Anchors are sorted by index (asc time) — once distance grows past the
|
||||||
// message time, further anchors will only be farther.
|
// message time, further anchors will only be farther.
|
||||||
break;
|
break;
|
||||||
|
|
@ -1161,17 +1161,18 @@ export class TeamDataService {
|
||||||
|
|
||||||
async sendMessage(teamName: string, request: SendMessageRequest): Promise<SendMessageResult> {
|
async sendMessage(teamName: string, request: SendMessageRequest): Promise<SendMessageResult> {
|
||||||
// Enrich with leadSessionId so session boundary separators work
|
// Enrich with leadSessionId so session boundary separators work
|
||||||
if (!request.leadSessionId) {
|
let enrichedRequest = request;
|
||||||
|
if (!enrichedRequest.leadSessionId) {
|
||||||
try {
|
try {
|
||||||
const config = await this.configReader.getConfig(teamName);
|
const config = await this.configReader.getConfig(teamName);
|
||||||
if (config?.leadSessionId) {
|
if (config?.leadSessionId) {
|
||||||
request = { ...request, leadSessionId: config.leadSessionId };
|
enrichedRequest = { ...enrichedRequest, leadSessionId: config.leadSessionId };
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// non-critical
|
// non-critical
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.inboxWriter.sendMessage(teamName, request);
|
return this.inboxWriter.sendMessage(teamName, enrichedRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveLeadNameFromConfig(config: TeamConfig | null): string {
|
private resolveLeadNameFromConfig(config: TeamConfig | null): string {
|
||||||
|
|
@ -1528,8 +1529,8 @@ export class TeamDataService {
|
||||||
if (b.type === 'tool_use' && typeof b.name === 'string' && b.name !== 'SendMessage') {
|
if (b.type === 'tool_use' && typeof b.name === 'string' && b.name !== 'SendMessage') {
|
||||||
const input = (b.input ?? {}) as Record<string, unknown>;
|
const input = (b.input ?? {}) as Record<string, unknown>;
|
||||||
toolCallsList.push({
|
toolCallsList.push({
|
||||||
name: b.name as string,
|
name: b.name,
|
||||||
preview: extractToolPreview(b.name as string, input),
|
preview: extractToolPreview(b.name, input),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ import { getMemberColor } from '@shared/constants/memberColors';
|
||||||
import { resolveLanguageName } from '@shared/utils/agentLanguage';
|
import { resolveLanguageName } from '@shared/utils/agentLanguage';
|
||||||
import { isInboxNoiseMessage } from '@shared/utils/inboxNoise';
|
import { isInboxNoiseMessage } from '@shared/utils/inboxNoise';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { extractToolPreview, formatToolSummaryFromCalls } from '@shared/utils/toolSummary';
|
|
||||||
import { createCliAutoSuffixNameGuard } from '@shared/utils/teamMemberName';
|
import { createCliAutoSuffixNameGuard } from '@shared/utils/teamMemberName';
|
||||||
|
import { extractToolPreview, formatToolSummaryFromCalls } from '@shared/utils/toolSummary';
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
@ -2971,8 +2971,8 @@ export class TeamProvisioningService {
|
||||||
) {
|
) {
|
||||||
const input = (block.input ?? {}) as Record<string, unknown>;
|
const input = (block.input ?? {}) as Record<string, unknown>;
|
||||||
run.pendingToolCalls.push({
|
run.pendingToolCalls.push({
|
||||||
name: block.name as string,
|
name: block.name,
|
||||||
preview: extractToolPreview(block.name as string, input),
|
preview: extractToolPreview(block.name, input),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,12 @@ const SCROLL_THRESHOLD = 300;
|
||||||
/** Must match the `w-80` (320px) context panel width used in the layout below. */
|
/** Must match the `w-80` (320px) context panel width used in the layout below. */
|
||||||
const CONTEXT_PANEL_WIDTH_PX = 320;
|
const CONTEXT_PANEL_WIDTH_PX = 320;
|
||||||
|
|
||||||
|
import { formatPercentOfTotal, sumContextInjectionTokens } from '@renderer/utils/contextMath';
|
||||||
|
|
||||||
import { ChatHistoryEmptyState } from './ChatHistoryEmptyState';
|
import { ChatHistoryEmptyState } from './ChatHistoryEmptyState';
|
||||||
import { ChatHistoryItem } from './ChatHistoryItem';
|
import { ChatHistoryItem } from './ChatHistoryItem';
|
||||||
import { ChatHistoryLoadingState } from './ChatHistoryLoadingState';
|
import { ChatHistoryLoadingState } from './ChatHistoryLoadingState';
|
||||||
|
|
||||||
import { formatPercentOfTotal, sumContextInjectionTokens } from '@renderer/utils/contextMath';
|
|
||||||
import type { ContextInjection } from '@renderer/types/contextInjection';
|
import type { ContextInjection } from '@renderer/types/contextInjection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import { useCallback, useState } from 'react';
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
|
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
Bell,
|
Bell,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
||||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import {
|
import {
|
||||||
CARD_BG,
|
CARD_BG,
|
||||||
CARD_BG_ZEBRA,
|
CARD_BG_ZEBRA,
|
||||||
|
|
@ -10,7 +11,6 @@ import {
|
||||||
CARD_TEXT_LIGHT,
|
CARD_TEXT_LIGHT,
|
||||||
} from '@renderer/constants/cssVariables';
|
} from '@renderer/constants/cssVariables';
|
||||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { formatToolSummary, parseToolSummary } from '@shared/utils/toolSummary';
|
import { formatToolSummary, parseToolSummary } from '@shared/utils/toolSummary';
|
||||||
|
|
||||||
|
|
@ -102,13 +102,13 @@ function isRecentTimestamp(timestamp: string): boolean {
|
||||||
return Date.now() - t <= LIVE_WINDOW_MS;
|
return Date.now() - t <= LIVE_WINDOW_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolSummaryTooltipContent({
|
const ToolSummaryTooltipContent = ({
|
||||||
toolCalls,
|
toolCalls,
|
||||||
toolSummary,
|
toolSummary,
|
||||||
}: {
|
}: Readonly<{
|
||||||
toolCalls?: ToolCallMeta[];
|
toolCalls?: ToolCallMeta[];
|
||||||
toolSummary?: string;
|
toolSummary?: string;
|
||||||
}): JSX.Element {
|
}>): JSX.Element => {
|
||||||
if (toolCalls && toolCalls.length > 0) {
|
if (toolCalls && toolCalls.length > 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex max-h-[300px] flex-col gap-0.5 overflow-y-auto">
|
<div className="flex max-h-[300px] flex-col gap-0.5 overflow-y-auto">
|
||||||
|
|
@ -118,14 +118,14 @@ function ToolSummaryTooltipContent({
|
||||||
{toolCalls.map((tc, i) => {
|
{toolCalls.map((tc, i) => {
|
||||||
const isAgent = tc.name === 'Agent' || tc.name === 'TaskCreate';
|
const isAgent = tc.name === 'Agent' || tc.name === 'TaskCreate';
|
||||||
return (
|
return (
|
||||||
<div key={i} className={`flex items-baseline gap-2 ${isAgent ? 'mt-0.5' : ''}`}>
|
<div key={i} className={isAgent ? 'mt-0.5' : 'flex items-baseline gap-2'}>
|
||||||
<span className={`shrink-0 font-semibold ${isAgent ? 'text-violet-400' : ''}`}>
|
<span className={`shrink-0 font-semibold ${isAgent ? 'text-violet-400' : ''}`}>
|
||||||
{isAgent ? '🤖 ' : ''}
|
{isAgent ? '🤖 ' : ''}
|
||||||
{tc.name}
|
{tc.name}
|
||||||
</span>
|
</span>
|
||||||
{tc.preview && (
|
{tc.preview && (
|
||||||
<span
|
<span
|
||||||
className={`text-text-secondary ${isAgent ? 'whitespace-pre-wrap' : 'truncate'}`}
|
className={`text-text-secondary ${isAgent ? 'mt-0.5 block text-[10px]' : 'truncate'}`}
|
||||||
>
|
>
|
||||||
{tc.preview}
|
{tc.preview}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -158,7 +158,7 @@ function ToolSummaryTooltipContent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span>{toolSummary ?? ''}</span>;
|
return <span>{toolSummary ?? ''}</span>;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const LeadThoughtsGroupRow = ({
|
export const LeadThoughtsGroupRow = ({
|
||||||
group,
|
group,
|
||||||
|
|
@ -268,7 +268,7 @@ export const LeadThoughtsGroupRow = ({
|
||||||
});
|
});
|
||||||
observer.observe(el);
|
observer.observe(el);
|
||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps -- scrollRef is stable
|
}, []);
|
||||||
|
|
||||||
const handleScroll = useCallback(() => {
|
const handleScroll = useCallback(() => {
|
||||||
const el = scrollRef.current;
|
const el = scrollRef.current;
|
||||||
|
|
@ -318,7 +318,7 @@ export const LeadThoughtsGroupRow = ({
|
||||||
{totalToolSummary}
|
{totalToolSummary}
|
||||||
</span>
|
</span>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="bottom" className="font-mono text-[11px]">
|
<TooltipContent side="bottom" className="max-w-[420px] font-mono text-[11px]">
|
||||||
<ToolSummaryTooltipContent
|
<ToolSummaryTooltipContent
|
||||||
toolCalls={allToolCalls}
|
toolCalls={allToolCalls}
|
||||||
toolSummary={totalToolSummary}
|
toolSummary={totalToolSummary}
|
||||||
|
|
@ -344,7 +344,7 @@ export const LeadThoughtsGroupRow = ({
|
||||||
{chronologicalThoughts.map((thought, idx) => (
|
{chronologicalThoughts.map((thought, idx) => (
|
||||||
<div key={thought.messageId ?? idx} className="thought-expand-in">
|
<div key={thought.messageId ?? idx} className="thought-expand-in">
|
||||||
{idx > 0 && (
|
{idx > 0 && (
|
||||||
<div className="mx-auto flex w-[40%] items-center justify-center gap-[5px] py-px">
|
<div className="mx-auto flex w-2/5 items-center justify-center gap-[5px] py-px">
|
||||||
<hr
|
<hr
|
||||||
className="flex-1 border-0"
|
className="flex-1 border-0"
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -386,7 +386,11 @@ export const LeadThoughtsGroupRow = ({
|
||||||
🔧 {thought.toolSummary}
|
🔧 {thought.toolSummary}
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="top" align="start" className="font-mono text-[11px]">
|
<TooltipContent
|
||||||
|
side="top"
|
||||||
|
align="start"
|
||||||
|
className="max-w-[420px] font-mono text-[11px]"
|
||||||
|
>
|
||||||
<ToolSummaryTooltipContent
|
<ToolSummaryTooltipContent
|
||||||
toolCalls={thought.toolCalls}
|
toolCalls={thought.toolCalls}
|
||||||
toolSummary={thought.toolSummary}
|
toolSummary={thought.toolSummary}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { RoleSelect } from '@renderer/components/team/RoleSelect';
|
|
||||||
import { TeamModelSelector } from '@renderer/components/team/dialogs/TeamModelSelector';
|
import { TeamModelSelector } from '@renderer/components/team/dialogs/TeamModelSelector';
|
||||||
|
import { RoleSelect } from '@renderer/components/team/RoleSelect';
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import { Input } from '@renderer/components/ui/input';
|
import { Input } from '@renderer/components/ui/input';
|
||||||
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
||||||
|
|
|
||||||
|
|
@ -607,16 +607,18 @@ export const MentionableTextarea = React.forwardRef<HTMLTextAreaElement, Mention
|
||||||
const [tipIndex, setTipIndex] = React.useState(0);
|
const [tipIndex, setTipIndex] = React.useState(0);
|
||||||
const [tipVisible, setTipVisible] = React.useState(true);
|
const [tipVisible, setTipVisible] = React.useState(true);
|
||||||
|
|
||||||
|
const advanceTip = React.useCallback(() => {
|
||||||
|
setTipIndex((prev) => (prev + 1) % rotatingTips.length);
|
||||||
|
setTipVisible(true);
|
||||||
|
}, [rotatingTips.length]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setTipVisible(false);
|
setTipVisible(false);
|
||||||
setTimeout(() => {
|
setTimeout(advanceTip, 300);
|
||||||
setTipIndex((prev) => (prev + 1) % rotatingTips.length);
|
|
||||||
setTipVisible(true);
|
|
||||||
}, 300);
|
|
||||||
}, 10000);
|
}, 10000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [rotatingTips.length]);
|
}, [advanceTip]);
|
||||||
|
|
||||||
const resolvedHintText = hintText ?? rotatingTips[tipIndex];
|
const resolvedHintText = hintText ?? rotatingTips[tipIndex];
|
||||||
const showHintRow = showHint && (suggestions.length > 0 || enableFiles);
|
const showHintRow = showHint && (suggestions.length > 0 || enableFiles);
|
||||||
|
|
|
||||||
|
|
@ -607,7 +607,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
const prevByName = get().teamByName;
|
const prevByName = get().teamByName;
|
||||||
const existingEntry = prevByName[teamName];
|
const existingEntry = prevByName[teamName];
|
||||||
const configColor = data.config.color;
|
const configColor = data.config.color;
|
||||||
if (configColor && (!existingEntry || existingEntry.color !== configColor)) {
|
if (configColor && (!existingEntry || existingEntry?.color !== configColor)) {
|
||||||
const patched: TeamSummary = existingEntry
|
const patched: TeamSummary = existingEntry
|
||||||
? { ...existingEntry, color: configColor, displayName: data.config.name || teamName }
|
? { ...existingEntry, color: configColor, displayName: data.config.name || teamName }
|
||||||
: {
|
: {
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ export function buildToolSummary(content: Record<string, unknown>[]): string | u
|
||||||
|
|
||||||
export function parseToolSummary(summary: string | undefined): ToolSummaryData | null {
|
export function parseToolSummary(summary: string | undefined): ToolSummaryData | null {
|
||||||
if (!summary) return null;
|
if (!summary) return null;
|
||||||
const match = summary.match(/^(\d+)\s+tools?\s+\(([^)]+)\)$/);
|
const match = /^(\d+)\s+tools?\s+\(([^)]+)\)$/.exec(summary);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
const byName: Record<string, number> = {};
|
const byName: Record<string, number> = {};
|
||||||
for (const part of match[2].split(', ')) {
|
for (const part of match[2].split(', ')) {
|
||||||
const m = part.match(/^(\d+)\s+(.+)$/);
|
const m = /^(\d+)\s+(\S+(?:\s+\S+)*)$/.exec(part);
|
||||||
if (m) {
|
if (m) {
|
||||||
byName[m[2]] = parseInt(m[1], 10);
|
byName[m[2]] = parseInt(m[1], 10);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -96,9 +96,9 @@ export function extractToolPreview(
|
||||||
case 'Agent':
|
case 'Agent':
|
||||||
case 'TaskCreate':
|
case 'TaskCreate':
|
||||||
return typeof input.prompt === 'string'
|
return typeof input.prompt === 'string'
|
||||||
? truncateStr(input.prompt, 200)
|
? input.prompt
|
||||||
: typeof input.description === 'string'
|
: typeof input.description === 'string'
|
||||||
? truncateStr(input.description, 200)
|
? input.description
|
||||||
: undefined;
|
: undefined;
|
||||||
case 'WebFetch':
|
case 'WebFetch':
|
||||||
if (typeof input.url === 'string') {
|
if (typeof input.url === 'string') {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue