v2.4.0-unsloth-studio-lift: port 3 Unsloth Studio AGPL-3.0 modules

Batch 1 — tool-call-parser.ts: replaces xml-parser.ts with a port of
Unsloth's tool_call_parser.py. Adds balanced-brace JSON scanner,
single-param fast path, hasToolSignal/stripToolMarkup/parseToolCallsFromText
exports, and stream-finalization stripping at all three final-write sites
(error-handler, finalizeCompletion, executeToolPhase). Anthropic <invoke>
shape preserved. 75+12 tests.

Batch 2 — web/html-to-md.ts: parse5 tree-walking HTML-to-Markdown converter
ported from Unsloth's _html_to_md.py. Replaces web_fetch's regex stripHtml
with structured markdown output (headings, links, lists, tables, code blocks,
blockquotes, entity decoding). 29 tests.

Batch 3 — llama-args-validator.ts: port of llama_server_args.py deny-list
validator. Wired into AGENTS.md frontmatter parser — llama_extra_args field
validated at load time, rejects managed flags (model identity, networking,
auth/TLS, server UI). No runtime consumer yet (llama-swap boundary). 76 tests.

All three files carry SPDX-License-Identifier: AGPL-3.0-only headers.
LICENSE flipped to AGPL-3.0-only in prior commit (a938cf1).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 23:30:50 +00:00
parent a938cf1d42
commit 90a6761b07
17 changed files with 1672 additions and 311 deletions

View File

@@ -9,6 +9,7 @@ import * as modelContext from '../model-context.js';
import { maybeFlagForCompaction } from './payload.js';
import { insertParts, partsFromAssistantMessage } from './parts.js';
import type { PartInsert } from './parts.js';
import { stripToolMarkup } from './tool-call-parser.js';
import type { InferenceContext, StreamResult, TurnArgs } from './turn.js';
export async function handleAbortOrError(
@@ -21,6 +22,7 @@ export async function handleAbortOrError(
const isAbort = err instanceof Error && err.name === 'AbortError';
const finalStatus = isAbort ? 'cancelled' : 'failed';
const errMsg = err instanceof Error ? err.message : String(err);
accumulated = stripToolMarkup(accumulated, { final: true });
// v1.8.2: persist a structured error metadata blob on genuine failures so
// the bubble can render the reason on reload without re-deriving from the
// (one-shot) WS error frame. User-initiated abort skips this — there's no
@@ -101,7 +103,8 @@ export async function finalizeCompletion(
session: Session
): Promise<void> {
const { sessionId, chatId, assistantMessageId } = args;
const { content, finishReason, promptTokens, completionTokens } = result;
const content = stripToolMarkup(result.content, { final: true });
const { finishReason, promptTokens, completionTokens } = result;
// v1.11.3: see executeToolPhase for the rationale.
const mctx = await modelContext.getModelContext(session.model);