Files
NetMesh/components/ai/toolArtifacts/terminalToolArtifact.ts

82 lines
2.4 KiB
TypeScript
Raw Permalink Normal View History

import { normalizeArtifactToolName } from './toolArtifactNames';
import { parseResultPayload } from './toolArtifactResultPayload';
export type TerminalToolArtifact =
| {
kind: 'terminal.context';
sessionId: string;
label?: string;
range: string;
totalLines: number;
startLine: number;
endLine: number;
returnedLines: number;
hasMoreBefore: boolean;
hasMoreAfter: boolean;
source?: string;
preview: string;
}
| {
kind: 'error';
message: string;
};
const TERMINAL_ARTIFACT_TOOL_NAMES = new Set([
'terminal_read_context',
]);
function readString(value: unknown): string | undefined {
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
}
function readNumber(value: unknown): number | undefined {
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
}
function readBoolean(value: unknown): boolean {
return value === true;
}
export function parseTerminalToolArtifact(
toolName: string,
result: unknown,
): TerminalToolArtifact | null {
const normalizedToolName = normalizeArtifactToolName(toolName);
if (!normalizedToolName || !TERMINAL_ARTIFACT_TOOL_NAMES.has(normalizedToolName)) return null;
const payload = parseResultPayload(result);
if (!payload) return null;
if (payload.ok === false || payload.isError === true || typeof payload.error === 'string') {
return {
kind: 'error',
message: readString(payload.error) ?? 'Terminal context read failed.',
};
}
const sessionId = readString(payload.sessionId);
const content = typeof payload.content === 'string' ? payload.content : '';
const totalLines = readNumber(payload.totalLines);
const startLine = readNumber(payload.startLine);
const endLine = readNumber(payload.endLine);
const returnedLines = readNumber(payload.returnedLines);
if (!sessionId || totalLines == null || startLine == null || endLine == null || returnedLines == null) {
return null;
}
return {
kind: 'terminal.context',
sessionId,
label: readString(payload.label),
range: readString(payload.range) ?? 'viewport',
totalLines,
startLine,
endLine,
returnedLines,
hasMoreBefore: readBoolean(payload.hasMoreBefore),
hasMoreAfter: readBoolean(payload.hasMoreAfter),
source: readString(payload.source),
preview: content.split('\n').slice(0, 6).join('\n'),
};
}