From 190eb6b162da8333107b8d6840ffdfd756d75f5e Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Tue, 12 May 2026 14:04:17 -0500 Subject: [PATCH] docs(web): refresh v0.8.32 site state --- README.md | 173 ++++++++++++++----------------- V086_BRIEF.md | 141 ------------------------- web/app/api/cron/route.ts | 2 + web/lib/community-agent-tasks.ts | 27 +++-- web/lib/community-agent.ts | 16 ++- web/lib/content-watch.ts | 12 ++- web/lib/deepseek.ts | 22 +++- web/lib/facts.generated.ts | 11 +- web/lib/kv.ts | 4 + web/scripts/check-kv-id.mjs | 11 +- 10 files changed, 155 insertions(+), 264 deletions(-) delete mode 100644 V086_BRIEF.md diff --git a/README.md b/README.md index 751ef8e0..f28cd0fe 100644 --- a/README.md +++ b/README.md @@ -225,106 +225,85 @@ deepseek --provider ollama --model deepseek-coder:1.3b --- -## What's New In v0.8.29 +## What's New In v0.8.32 -A maintenance release anchored by a v0.8.27 / v0.8.28 regression fix -plus 25 community PRs. [Full changelog](CHANGELOG.md). +A "more useful tools" release expanding the tool surface for real-world +workflows. Five new tools, ten community PRs targeting model-protocol bugs +and UX papercuts, and a snapshot cap that stops giant workspaces from +hanging the TUI on first turn. [Full changelog](CHANGELOG.md). -- **Scroll demon, gone for good** (#1085 regression). Parallel sub- - agents running `exec_shell` would scroll the alt-screen out from - under ratatui's diff renderer, leaving a blank band growing above - the header. Three layers of defence now: a `tracing-subscriber` - writing to `~/.deepseek/logs/tui-YYYY-MM-DD.log`, an fd-level - `dup2` stderr redirect for the alt-screen lifetime (Unix), and - module-level `#![deny(clippy::print_stdout, clippy::print_stderr)]` - on the TUI runtime modules. New `eprintln!`s inside `tools/`, - `core/`, `tui/`, `network_policy.rs`, or `runtime_threads.rs` now - fail CI. -- **Ctrl+R session restore is workspace-scoped** (#1395, PR #1397 from - **@linzhiqin2003**) — previously listed every saved session on disk, - which meant Project A's history could leak into Project B. -- **Runtime version visible in the header.** A discreet `v0.8.29` - chip sits in the header's right cluster alongside the provider / - effort / Live / context chips. Drops first under tight terminal - width. -- **MCP HTTP transport honors HTTP(S)_PROXY** (#1408 from - **@hlx98007**) — corporate / Clash / Shadowsocks proxies now apply - to MCP HTTP connections, matching every other tool on the box. - `NO_PROXY` honored. -- **MCP discovery survives malformed items** (#1410 from - **@Liu-Vince**) — one bad tool / resource / prompt entry no - longer drops the whole page; the malformed entry is skipped and - the rest of the catalogue surfaces normally. -- **MCP SSE accepts CRLF-framed endpoint events** (#1309, PR #1358 - from **@reidliu41**) — FastMCP / uvicorn streams no longer time - out waiting for LF-only event separators. -- **Composer ignores leaked mouse-report bytes** (#1418, PR #1421 - from **@reidliu41**) — terminal chains that leak `[<35;44;18M` - style mouse reports into stdin no longer fill the input area. -- **Footer chips respect the available width** (#1357, PR #1417 from - **@Wenjunyun123**) — long cache / aux chips drop before crowding - the left status line or composer area on narrow terminals. -- **Note management commands** (PR #1407 from **@reidliu41**) — - `/note add`, `/note list`, and friends for persistent maintainer - notes inside the TUI. -- **`/init`-style global AGENTS.md merges with project AGENTS.md** - (#1157, PR #1399 from **@linzhiqin2003**) — your `~/.deepseek/ - AGENTS.md` baseline now layers under the workspace's own - AGENTS.md instead of being shadowed. -- **Language directive: thinking matches the user's message language** - (#1118, PR #1398 from **@linzhiqin2003**) — `reasoning_content` - follows the latest user message language, not the project context's - inferred `lang`. -- **Web search filters spam-stuffed SERPs** (#964, PR #1396 from - **@linzhiqin2003**) — Bing / DDG fallback paths drop the - generated-content / SEO-farm domains that were poisoning quick - lookups. -- **Auto routing recognises CJK debug / search keywords** (PRs #1401 - and #1402 from **@linzhiqin2003**) — `--model auto` and the - reasoning-effort picker correctly route Chinese / Japanese - technical queries instead of falling through to the generic - baseline. -- **Deferred tools hydrate schemas before first execution** (#1419, - PR #1429 from **@SamhandsomeLee**) — `edit_file` and other - deferred tools now load, show their expected fields, and ask the - model to retry instead of executing guessed argument names. -- **DeepSeek aliases replay thinking-mode tool turns** (PR #1428 - from **@Beltran12138**) — `deepseek-chat` and - `deepseek-reasoner` now get the same `reasoning_content` replay - treatment as explicit V4 model IDs, avoiding second-turn 400s - after tool calls. -- **Skill completions stay under `/skill`** (#1437, PR #1442 from - **@reidliu41**) — large local skill collections no longer crowd - the root slash-command menu. -- **`edit_file` rejects no-op replacements** (PR #1460 from - **@xiluoduyu**) — identical `search` / `replace` values now fail - validation instead of returning an empty diff. -- **Windows terminal layout gets width-stable glyphs** (#1314, - PR #1465 from **@CrepuscularIRIS**) — header and file-tree icons - no longer rely on SMP emoji that cmd / PowerShell can mismeasure. -- **Ghostty uses low-motion rendering by default** (#1445, PR #1468 - from **@CrepuscularIRIS**) — affected terminals avoid animation - flicker without manual config. -- **Docker buildx provenance EPERM failures get a hint** (#1449, - PR #1469 from **@CrepuscularIRIS**) — macOS shell output points at - the provenance flag when that restricted metadata write fails. -- **Windows CMD mouse-wheel fallback scrolls the transcript** - (#1443, PR #1471 from **@CrepuscularIRIS**) — wheel events mapped - to Up / Down no longer cycle composer history when mouse capture - is off. -- **Sync-to-CNB workflow hardened** — explicit `permissions: - contents: read`, narrowed trigger to `main` + `v*` tags (no longer - mirrors feature branches), `actions/checkout` bumped v3 → v4. -- **+438 LOC of new test coverage** for `error_taxonomy`, - `parse_pages_arg`, web-search precedence, and - `sanitize_stream_chunk` control-byte filtering (PRs #1403-#1406 - from **@linzhiqin2003**). +- **Five new tools.** `read_file` now extracts PDFs in pure Rust — no + Poppler install required. `pandoc_convert` moves documents between 11 + formats (Markdown, HTML, DOCX, EPUB, LaTeX…). `image_ocr` runs local + tesseract on screenshots and scanned documents. `image_analyze` sends + images to a vision model for natural-language description (opt-in only). + `js_execution` mirrors `code_execution` for Node.js snippets. +- **Two more providers.** AtlasCloud joins as a first-class provider + (`provider = "atlascloud"`) with the same config-surface shape as the + existing NVIDIA NIM / Fireworks rows. `web_search` supports Tavily and + Bocha as configurable backends for regions where DuckDuckGo is + unreliable. +- **Prompt-cache survives mid-session edits** (PR #1345 from + **@Duducoco**). Moving `instructions`, user memory, and session goal + below the volatile-content boundary means the KV prefix cache no longer + breaks every time you edit your memory file — skills and context + management instructions stay hot regardless of how often you run + `/memory`. +- **vLLM thinking toggle actually works now** (PR #1480 from + **@h3c-hexin**). `reasoning_effort = "off"` on vLLM providers now emits + the OpenAI `chat_template_kwargs.enable_thinking` extension instead of + the silently-ignored Anthropic-native field. Measured improvement on + Qwen3: TTFT from ~13s → ~270ms. +- **Kitty keyboard protocol on Windows** (PR #1483 from + **@CrepuscularIRIS / autoghclaw**). `Shift+Enter` now inserts a + newline instead of submitting in VSCode and Windows Terminal — + previously indistinguishable from plain Enter on Windows. +- **Tool-result retrieval namespace unified** (#1541). Wire-dedup refs + and disk-spillover refs now share a lookup path — `retrieve_tool_result` + accepts SHA refs, bare hex hashes, `art_` aliases, and absolute + paths, with error messages that list every accepted form. +- **Snapshots skip giant workspaces** (#1552). A 2 GB ceiling on + non-excluded workspace content prevents first-turn `git add -A` from + hanging the TUI on multi-hundred-GB project directories. Configurable + via `[snapshots] max_workspace_gb`; set to `0` to restore unbounded + behaviour. +- **`deepseek update` refreshes both binaries** (PR #1492 from + **@NorethSea**). The updater now enumerates colocated binaries (both + the dispatcher and the TUI runtime), downloads and verifies every + release asset, and writes the sibling first so a partial failure can't + leave the launcher updated while the TUI stays stale. +- **Approval modal collapses to a one-line banner** (PR #1455 from + **@tiger-dog**). Tab toggles between the full takeover card and a + bottom-line summary — the transcript stays visible while you decide. +- **`@`-mention truncation no longer splits CJK codepoints** (PR #1495 + from **@CrepuscularIRIS / autoghclaw**). Files larger than 128 KB + used to truncate mid-codepoint; the truncator now rounds down to the + last valid UTF-8 boundary. +- **Startup empty-state shows the build version**, active model with a + `/model` hint, and current working directory (PR #1444 from + **@reidliu41**). +- **`/change` slash command** displays the latest CHANGELOG section + inside the TUI (PR #1416 from **@zhuangbiaowei**). +- **Toast overlay no longer renders on top of the composer** (PR #1485 + from **@MeAiRobot**). Approval toasts now clamp to the gap between + the composer and footer. +- **TUI no longer freezes during long-running shell jobs** (PR #1494 + from **@CrepuscularIRIS / autoghclaw**). The job panel's refresh path + now reads only the tail bytes under the mutex lock instead of cloning + the entire stdout buffer every 2.5 seconds. +- **Markdown renderer no longer eats underscores in identifiers** (PR + #1455 from **@tiger-dog**). `deepseek_tui` and `foo_bar_baz` no longer + render half-italic. +- **`/sessions` picker highlights the selected row** more strongly in + dark terminals (PR #1493 from **@reidliu41**), and no longer shows + `` as the session title (PR #1498 from **@wdw8276**). -Thanks to **@linzhiqin2003** (10 landings this cycle), -**@reidliu41** (5 landings), **@CrepuscularIRIS** (4 landings), -**@SamhandsomeLee**, **@Beltran12138**, **@Wenjunyun123**, -**@hlx98007**, **@Liu-Vince**, **@xiluoduyu**, and -**@shenxiaodaosanhua** for the bug report. +Thanks to **@CrepuscularIRIS** (4 landings), **@reidliu41** (2 landings), +**@tiger-dog** (2 landings), **@Duducoco**, **@h3c-hexin**, +**@NorethSea**, **@MeAiRobot**, **@zhuangbiaowei**, **@wdw8276**, +**@MMMarcinho**, **@SamhandsomeLee**, **@sandofree**, +**@lucaszhu-hue**, **@muyuliyan**, **@Oliver-ZPLiu**, **@czf0718**, +**@jieshu666**, and **@YaYII**. --- diff --git a/V086_BRIEF.md b/V086_BRIEF.md deleted file mode 100644 index 1e445dab..00000000 --- a/V086_BRIEF.md +++ /dev/null @@ -1,141 +0,0 @@ -# v0.8.6 Backlog — Work Brief for an AI Agent - -This is a structured brief for another AI (Claude Opus, DeepSeek V4, or similar) to -understand the full v0.8.6 scope and begin implementation. The repo is -`github.com/Hmbown/DeepSeek-TUI` — Rust workspace, TUI coding agent for DeepSeek V4. - -**Branch**: create `feat/v0.8.6` from `main` (current HEAD at v0.8.5 tag). -**All 23 issues are tagged `v0.8.6`** and live in the repo's GitHub Issues. -**Zero open issues outside this list** — the board is clean. - -## Project Context - -DeepSeek TUI is a terminal-native coding agent. Key architectural points: -- **Dispatcher binary** (`deepseek`) delegates to the TUI binary (`deepseek-tui`) -- **Crate map**: `crates/tui` is the main crate; `crates/cli` handles CLI entry; - `crates/config`, `crates/core`, `crates/tools` etc. are sub-crates -- **Engine pattern**: `core/engine.rs` runs the agent loop, processes tool calls -- **TUI**: ratatui-based, alt-screen, composer at bottom, sidebar at right -- **Config**: `~/.deepseek/config.toml`, profiles, providers, settings -- **Key files to read first**: `docs/ARCHITECTURE.md`, `crates/tui/src/main.rs`, - `crates/tui/src/tui/app.rs`, `crates/tui/src/core/engine.rs` - -Read `AGENTS.md` and `CLAUDE.md` in the repo root for build/test commands. - ---- - -## v0.8.6 Issues — Grouped by Theme - -### Group A: UX Polish — Transcript & Clipboard (5 issues) - -| # | Title | TL;DR | -|---|-------|-------| -| 380 | Inline diff highlighting | Color +/- in apply_patch/edit_file results | -| 379 | Smart clipboard Ctrl+Y | Copy focused cell to system clipboard | -| 375 | Right-click context menu | Per-cell menu: Copy, Open in editor, Re-run, Hide | -| 374 | Clickable file:line | OSC-8 hyperlinks on path:line in tool output | -| 376 | Native-copy escape | Hold Shift to bypass alt-screen for terminal selection | - -### Group B: Workspace UX — Navigation & Visibility (4 issues) - -| # | Title | TL;DR | -|---|-------|-------| -| 394 | File-tree pane | Ctrl+E toggles left-side workspace navigator | -| 395 | Cycle-boundary visualization | Inline dividers between coherence cycles | -| 396 | Per-turn cache hit chip | Footer shows cache hit % after each turn | -| 388 | Crash-recovery prompt | On restart, offer to restore interrupted turn | - -### Group C: Session & History (3 issues) - -| # | Title | TL;DR | -|---|-------|-------| -| 383 | /edit — revise and resubmit | Pull last message into composer, re-run turn | -| 384 | /undo — revert last patch | Surgical undo of apply_patch/edit_file/write_file | -| 385 | /diff — session changes | Show git diff since session start | - -### Group D: Tools & Intelligence (4 issues) - -| # | Title | TL;DR | -|---|-------|-------| -| 389 | Inline LSP diagnostics | Show rust-analyzer errors after each patch | -| 386 | /init — bootstrap AGENTS.md | Auto-detect project type, write starter AGENTS.md | -| 391 | User-defined slash commands | ~/.deepseek/commands/.md templates | -| 392 | /model auto | Heuristic Pro-vs-Flash routing per turn | - -### Group E: Infrastructure & Sharing (4 issues) - -| # | Title | TL;DR | -|---|-------|-------| -| 390 | /profile — hot-switch config | Switch config profiles in-session without restart | -| 393 | /share — session URL | Export session as static HTML, upload to gist/S3 | -| 387 | In-app self-update | deepseek update fetches + replaces binary | -| 397 | Goal mode | Stated objective, token budget, self-verification tools | - -### Group F: Quality & Fixes (3 issues) - -| # | Title | TL;DR | -|---|-------|-------| -| 382 | Collapse Steer/Queue/Immediate | One mental model — everything queues, Ctrl+Enter steers | -| 373 | Sidebar Tasks panel ignores shell jobs | Wire shell jobs into Tasks panel | -| 377 | Shrink App state | Group ~200 fields into typed sub-states | -| 378 | Docs: tighten README + ARCHITECTURE | External-reader polish pass | - ---- - -## Suggested Implementation Order - -### Wave 1: Foundation (start here) -1. **#377 (refactor App state)** — do this FIRST. Group fields into sub-state structs - before adding more fields. Every subsequent feature touches App. -2. **#382 (collapse Steer/Queue)** — UX clarity fix, low implementation risk. -3. **#373 (Tasks panel shell jobs)** — bugfix, low risk. - -### Wave 2: Transcript UX -4. **#380 (inline diff highlighting)** — parser pass on tool output, visible value. -5. **#374 (clickable file:line)** — OSC-8 hyperlinks, high discoverability. -6. **#379 (smart clipboard Ctrl+Y)** — small feature, big ergonomic win. -7. **#375 (right-click context menu)** — depends on mouse event plumbing. -8. **#376 (native-copy escape)** — terminal selection fix. - -### Wave 3: Session tools -9. **#383 (/edit)** — requires engine truncation path. -10. **#384 (/undo)** — depends on snapshot infra. -11. **#385 (/diff)** — uses snapshot repo, depends on #380 for rendering. -12. **#388 (crash-recovery prompt)** — uses existing checkpoint infra. - -### Wave 4: Intelligence -13. **#386 (/init)** — project detection + AGENTS.md generation. -14. **#389 (LSP diagnostics)** — polls existing LSP client, low-maintenance. -15. **#391 (user-defined commands)** — skills loader reuse. -16. **#392 (/model auto)** — heuristic router, DeepSeek-specific. - -### Wave 5: Visibility & sharing -17. **#394 (file-tree pane)** — workspace navigator. -18. **#395 (cycle-boundary visualization)** — coherence cycle dividers. -19. **#396 (cache hit chip)** — footer chip, simple addition. -20. **#393 (/share)** — HTML export, gist/S3 backend. -21. **#387 (self-update)** — binary fetch + verify + replace. - -### Wave 6: Docs & goal mode -22. **#378 (docs polish)** — README + ARCHITECTURE refresh. -23. **#397 (Goal mode)** — largest feature, last (benefits from all previous work). - ---- - -## Working Patterns - -- **PR-per-issue** (or small clusters). Each merged PR closes one issue. -- **Decomposition first**: read the issue body, identify the files that need to change, - create a `checklist_write`, then implement. -- **Test gates**: `cargo test --workspace --all-features` must pass before each PR. -- **Lint gates**: `cargo clippy --workspace --all-targets --all-features -- -D warnings` clean. -- **Format**: `cargo fmt --all` before commit. -- **GitHub**: push to `feat/v0.8.6`, create PRs to `main`. Use `gh` CLI. -- **No open issues** except the v0.8.6 list — if new issues emerge, create them but don't block. - -## Key Resources - -- Repo: `https://github.com/Hmbown/DeepSeek-TUI` -- Architecture: `docs/ARCHITECTURE.md` -- Config reference: `docs/CONFIGURATION.md` -- CLI: `gh issue list --label v0.8.6 --json number,title,body` for full issue text diff --git a/web/app/api/cron/route.ts b/web/app/api/cron/route.ts index 34db6a62..47c0143f 100644 --- a/web/app/api/cron/route.ts +++ b/web/app/api/cron/route.ts @@ -57,6 +57,8 @@ export async function GET(req: Request) { const agentEnv: AgentEnv = { CURATED_KV: env.CURATED_KV, DEEPSEEK_API_KEY: env.DEEPSEEK_API_KEY, + DEEPSEEK_BASE_URL: env.DEEPSEEK_BASE_URL ?? process.env.DEEPSEEK_BASE_URL, + DEEPSEEK_MODEL: env.DEEPSEEK_MODEL ?? process.env.DEEPSEEK_MODEL, GITHUB_TOKEN: env.GITHUB_TOKEN, CRON_SECRET: env.CRON_SECRET, GITHUB_REPO: env.GITHUB_REPO, diff --git a/web/lib/community-agent-tasks.ts b/web/lib/community-agent-tasks.ts index 19ccc13f..00c96d78 100644 --- a/web/lib/community-agent-tasks.ts +++ b/web/lib/community-agent-tasks.ts @@ -12,6 +12,7 @@ import { hasFreshDraft, logUsage, type AgentDraft, + type DeepSeekEnv, } from "@/lib/community-agent"; export interface AgentEnv { @@ -22,6 +23,8 @@ export interface AgentEnv { delete(key: string): Promise; }; DEEPSEEK_API_KEY?: string; + DEEPSEEK_BASE_URL?: string; + DEEPSEEK_MODEL?: string; GITHUB_TOKEN?: string; CRON_SECRET?: string; GITHUB_REPO?: string; @@ -29,6 +32,13 @@ export interface AgentEnv { MAINTAINER_GITHUB_PAT?: string; } +function dsEnv(env: AgentEnv): DeepSeekEnv { + return { + baseUrl: env.DEEPSEEK_BASE_URL ?? process.env.DEEPSEEK_BASE_URL, + model: env.DEEPSEEK_MODEL ?? process.env.DEEPSEEK_MODEL, + }; +} + export async function runCurate(env: AgentEnv): Promise> { if (!env.DEEPSEEK_API_KEY) { return { skipped: true, reason: "DEEPSEEK_API_KEY not set" }; @@ -38,7 +48,7 @@ export async function runCurate(env: AgentEnv): Promise> fetchRepoStats(env.GITHUB_TOKEN), fetchFeed(env.GITHUB_TOKEN, 30), ]); - const dispatch = await curate(env.DEEPSEEK_API_KEY, stats, feed); + const dispatch = await curate(env.DEEPSEEK_API_KEY, stats, feed, dsEnv(env)); await putDispatch(dispatch); return { ok: true, headline: dispatch.headline }; } catch (e) { @@ -84,7 +94,8 @@ export async function runTriage(env: AgentEnv): Promise> const { content, usage } = await agentChat( [{ role: "system", content: TRIAGE_PROMPT }, { role: "user", content: JSON.stringify(payload) }], env.DEEPSEEK_API_KEY!, - true + true, + dsEnv(env) ); const parsed = JSON.parse(content) as { bodyEn: string; bodyZh: string }; const draft: AgentDraft = { @@ -166,7 +177,8 @@ export async function runPrReview(env: AgentEnv): Promise> const { content, usage } = await agentChat( [{ role: "system", content: STALE_PROMPT }, { role: "user", content: JSON.stringify(payload) }], env.DEEPSEEK_API_KEY!, - true + true, + dsEnv(env) ); const parsed = JSON.parse(content) as { bodyEn: string; bodyZh: string }; const draft: AgentDraft = { @@ -290,7 +303,8 @@ export async function runDupes(env: AgentEnv): Promise> const { content, usage } = await agentChat( [{ role: "system", content: DUPES_PROMPT }, { role: "user", content: JSON.stringify({ issues: openIssues }) }], env.DEEPSEEK_API_KEY!, - true + true, + dsEnv(env) ); const parsed = JSON.parse(content) as { suggestions?: { targetNumber: number; duplicateNumber: number; reason: string; bodyEn: string; bodyZh: string }[] }; @@ -373,7 +387,8 @@ export async function runDigest(env: AgentEnv): Promise> const { content, usage } = await agentChat( [{ role: "system", content: DIGEST_PROMPT }, { role: "user", content: JSON.stringify(payload) }], env.DEEPSEEK_API_KEY!, - true + true, + dsEnv(env) ); const parsed = JSON.parse(content) as { titleEn: string; titleZh: string; summaryEn: string; summaryZh: string; sections: { heading: string; items: string[] }[] }; diff --git a/web/lib/community-agent.ts b/web/lib/community-agent.ts index 481c2954..38662403 100644 --- a/web/lib/community-agent.ts +++ b/web/lib/community-agent.ts @@ -41,13 +41,19 @@ export interface UsageLog { outputTokens: number; } +export interface DeepSeekEnv { + baseUrl?: string; + model?: string; +} + export async function agentChat( messages: ChatMessage[], apiKey: string, - jsonMode = false + jsonMode = false, + dsEnv?: DeepSeekEnv ): Promise<{ content: string; usage: { input: number; output: number } }> { - const base = process.env.DEEPSEEK_BASE_URL ?? FALLBACK_BASE; - const model = process.env.DEEPSEEK_MODEL ?? FALLBACK_MODEL; + const base = dsEnv?.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? FALLBACK_BASE; + const model = dsEnv?.model ?? process.env.DEEPSEEK_MODEL ?? FALLBACK_MODEL; const res = await fetch(`${base}/v1/chat/completions`, { method: "POST", headers: { @@ -185,6 +191,8 @@ interface KVNamespace { export interface CommunityAgentEnv { CURATED_KV?: KVNamespace; DEEPSEEK_API_KEY?: string; + DEEPSEEK_BASE_URL?: string; + DEEPSEEK_MODEL?: string; GITHUB_TOKEN?: string; CRON_SECRET?: string; GITHUB_REPO?: string; @@ -200,6 +208,8 @@ export async function getAgentEnv(): Promise { } catch { return { DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY, + DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL, + DEEPSEEK_MODEL: process.env.DEEPSEEK_MODEL, GITHUB_TOKEN: process.env.GITHUB_TOKEN, CRON_SECRET: process.env.CRON_SECRET, GITHUB_REPO: process.env.GITHUB_REPO, diff --git a/web/lib/content-watch.ts b/web/lib/content-watch.ts index 28385679..c724d3f0 100644 --- a/web/lib/content-watch.ts +++ b/web/lib/content-watch.ts @@ -13,7 +13,7 @@ * Both surface as drafts in CURATED_KV under `draft:linkcheck:<...>` and * `draft:semantic-drift:<...>`, picked up by the existing /admin listing. */ -import { agentChat, saveDraft, type AgentDraft, VOICE_CONSTRAINTS } from "./community-agent"; +import { agentChat, saveDraft, type AgentDraft, type DeepSeekEnv, VOICE_CONSTRAINTS } from "./community-agent"; interface KVNamespace { get(k: string): Promise; @@ -25,9 +25,18 @@ interface KVNamespace { interface WatchEnv { CURATED_KV?: KVNamespace; DEEPSEEK_API_KEY?: string; + DEEPSEEK_BASE_URL?: string; + DEEPSEEK_MODEL?: string; GITHUB_TOKEN?: string; } +function dsEnv(env: WatchEnv): DeepSeekEnv { + return { + baseUrl: env.DEEPSEEK_BASE_URL ?? process.env.DEEPSEEK_BASE_URL, + model: env.DEEPSEEK_MODEL ?? process.env.DEEPSEEK_MODEL, + }; +} + // --- Link checker --- // Targets to probe daily. For registries that block bot HEAD/GET (npm, crates.io) @@ -255,6 +264,7 @@ ${docsText}`; ], env.DEEPSEEK_API_KEY, true, + dsEnv(env), ); } catch (e) { return { ok: false, drafted: 0, reason: `LLM call failed: ${e}` }; diff --git a/web/lib/deepseek.ts b/web/lib/deepseek.ts index 6b97d5f1..b110f66f 100644 --- a/web/lib/deepseek.ts +++ b/web/lib/deepseek.ts @@ -12,9 +12,19 @@ interface ChatResponse { choices: { message: { content: string } }[]; } -export async function chat(messages: ChatMessage[], apiKey: string, jsonMode = false): Promise { - const base = process.env.DEEPSEEK_BASE_URL ?? FALLBACK_BASE; - const model = process.env.DEEPSEEK_MODEL ?? FALLBACK_MODEL; +export interface DeepSeekEnv { + baseUrl?: string; + model?: string; +} + +export async function chat( + messages: ChatMessage[], + apiKey: string, + jsonMode = false, + dsEnv?: DeepSeekEnv +): Promise { + const base = dsEnv?.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? FALLBACK_BASE; + const model = dsEnv?.model ?? process.env.DEEPSEEK_MODEL ?? FALLBACK_MODEL; const res = await fetch(`${base}/v1/chat/completions`, { method: "POST", headers: { @@ -64,7 +74,8 @@ Rules: export async function curate( apiKey: string, stats: RepoStats, - feed: FeedItem[] + feed: FeedItem[], + dsEnv?: DeepSeekEnv ): Promise { const trimmedFeed = feed.slice(0, 25).map((f) => ({ kind: f.kind, @@ -96,7 +107,8 @@ export async function curate( { role: "user", content: JSON.stringify(userPayload, null, 2) }, ], apiKey, - true + true, + dsEnv ); const parsed = JSON.parse(raw) as Omit; diff --git a/web/lib/facts.generated.ts b/web/lib/facts.generated.ts index 8c7ff726..e42fc4f1 100644 --- a/web/lib/facts.generated.ts +++ b/web/lib/facts.generated.ts @@ -18,13 +18,8 @@ export interface RepoFacts { } export const FACTS: RepoFacts = { -<<<<<<< Updated upstream - "generatedAt": "2026-05-12T03:14:50.815Z", - "version": "0.8.30", -======= - "generatedAt": "2026-05-10T15:06:44.698Z", - "version": "0.8.27", ->>>>>>> Stashed changes + "generatedAt": "2026-05-12T19:02:49.213Z", + "version": "0.8.32", "crates": [ "agent", "app-server", @@ -95,7 +90,7 @@ export const FACTS: RepoFacts = { ], "defaultModel": "deepseek-v4-pro", "nodeEngines": ">=18", - "toolCount": 62, + "toolCount": 64, "license": "MIT", "latestRelease": null }; diff --git a/web/lib/kv.ts b/web/lib/kv.ts index 7d8619fd..f2bfcd4c 100644 --- a/web/lib/kv.ts +++ b/web/lib/kv.ts @@ -16,6 +16,8 @@ interface KVNamespace { interface CloudflareEnv { CURATED_KV?: KVNamespace; DEEPSEEK_API_KEY?: string; + DEEPSEEK_BASE_URL?: string; + DEEPSEEK_MODEL?: string; GITHUB_TOKEN?: string; CRON_SECRET?: string; GITHUB_REPO?: string; @@ -29,6 +31,8 @@ export async function getEnv(): Promise { } catch { return { DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY, + DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL, + DEEPSEEK_MODEL: process.env.DEEPSEEK_MODEL, GITHUB_TOKEN: process.env.GITHUB_TOKEN, CRON_SECRET: process.env.CRON_SECRET, GITHUB_REPO: process.env.GITHUB_REPO, diff --git a/web/scripts/check-kv-id.mjs b/web/scripts/check-kv-id.mjs index 8e2b5ae1..3667a382 100644 --- a/web/scripts/check-kv-id.mjs +++ b/web/scripts/check-kv-id.mjs @@ -14,10 +14,15 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); const cfgPath = join(__dirname, "..", "wrangler.jsonc"); const raw = readFileSync(cfgPath, "utf-8"); -// Parse JSONC (strip comments, trailing commas) +// Parse JSONC (strip comments, trailing commas). +// Use a two-pass approach to avoid mangling URLs: first strip +// line comments that look like comments (preceded by whitespace +// or comma, not part of ://), then strip block comments. const stripped = raw - .replace(/\/\/.*$/gm, "") // line comments - .replace(/\/\*[\s\S]*?\*\//g, ""); // block comments + .replace(/(^|[,\s])\/\/[^\n]*/gm, "$1") // line comments (skips :// in URLs) + .replace(/\/\*[\s\S]*?\*\//g, "") // block comments + .replace(/,\s*}/g, "}") // trailing commas + .replace(/,\s*]/g, "]"); const cfg = JSON.parse(stripped); const nss = cfg.kv_namespaces;