KronEditor v1.1.2
🤖 PLC Agent
New: the agent asks instead of guessing. A clarifying question is now a real tool call (ask_user), not prose. It renders as a VSCode-style quick-pick above the input bar (buttons when the model supplies options, a text box when it doesn't, always with a free-text escape), pauses the run until you answer, and asks multiple questions one at a time with an n / total counter. Previously the model dumped a numbered list of questions into a chat bubble and the loop just ended — nothing was actually waiting for you.
Run state you can see and stop. The Stop control moved into the input bar (the send ➤ becomes a red ■; Esc also stops) — it used to live at the bottom of the scrolling conversation, so in a long chat it was below the fold and the agent looked unstoppable. Added a phase label + ticking elapsed timer as proof of life, and stopping is now generation-based, so a stopped run can never resume alongside a new one. Approving a proposal correctly re-arms the running indicator.
Model picker now reads a live catalogue. New POST /api/host/ai/models endpoint proxies each provider's list endpoint; the built-in lists are only an offline fallback. The Model field is a proper (the old free-text combo used its own input as the filter, so a user on claude-opus-4-8 could see only that entry and couldn't reach any other model). Non-chat models (embeddings, tts, image, moderation) are filtered out of OpenAI-shaped lists, and the Connect tab waits for the first fetch instead of showing a stale list.
New provider: DeepSeek (deepseek-chat / deepseek-reasoner). Providers are now grouped in the picker by how you authenticate (sign-in / API key / local / custom) rather than by vendor.
Provider connection fixes:
A named provider always reaches its own host — baseUrl is honored only by custom. A leftover Ollama/custom base URL used to silently redirect every provider (picking Gemini + a valid key POSTed to localhost:11434 and failed with connection refused) and sent the API key as a Bearer token to whatever host that base named.
Switching provider in Settings no longer carries apiKey/baseUrl across; credentials are stashed per provider.
Gemini: model listing uses the native /v1beta/models route (the OpenAI-compat one 404s unauthenticated), fallback list uses the -latest aliases that can't go stale, and Gemini 3's per-tool-call thought_signature is round-tripped verbatim — without it multi-turn tool use died on turn 2 with a 400.
Provider errors are condensed into one actionable sentence (friendlyProviderError); it tells a zero-quota plan apart from an ordinary rate limit, and catches Google's 400 "Please pass a valid API key" which isn't a 401.
Ladder/ST authoring correctness:
Agent-authored ladder blocks now land on the power rail. They were all emitted at y=0, parking every contact and coil ~75 px above the rail with diagonal wires.
On a mixed POU, set_st_code and set_ladder each own only their own language's rungs. Before this, whichever ran last deleted the other's work — authoring a rung then writing ST wiped the ladder.
set_ladder now validates contact/coil targets and fails loudly: non-IEC names, non-BOOL targets, unknown subTypes and comparison-ish names (EQ, GT, >=) are rejected with a routing hint instead of being silently normalized into nonsense like counter AND 10.
add_variable accepts standard FB types (TON, CTU, MC_Power, …) — the prompt told the model to do exactly this and it was rejected for every standard FB. It also refuses to create a local that shadows a global.
read_pou returns the POU's referenced globals, so the model stops re-declaring them as locals.
list_blocks documents each FB's trigger pin and power-output pin.
An explicit "write it in ladder" now outranks the content-based language heuristic — a "count to 10" request no longer silently comes back as ST.
Better weak-model handling: bare JSON arg objects are routed by shape, and POU-target errors name the POUs that actually exist.
🔧 Build & toolchain
Build & Send was broken for every armv7 BeagleBone — fixed. The bundled GCC install dir is now located by glob and passed as -B (paths.go LLVMGCCInstallDir). The sysroots name it with a "none" vendor (arm-none-linux-gnueabihf) that clang's candidate list doesn't match for arm, so every -static target build died with cannot open crtbeginT.o / unable to find library -lgcc; aarch64 worked only by luck. Version dirs are compared numerically (9.3.0 vs 10.2.1), covered by a new paths_gcc_test.go.
🔌 HAL — no more fake success
Stub _Call bodies that returned DONE = EN / VALUE = 0, ERR_ID = 0 were indistinguishable from a working read, so projects could silently run on invented data. Everything is now either real or reports ERR_ID.
BeagleBone HAL is now implemented (was almost entirely TODO): GPIO on both headers, PWM (sysfs), I2C, SPI single-byte + burst, ADC (AM335x IIO, 12-bit / 1.8 V), CAN (SocketCAN dcan0/dcan1), UART/USB device paths corrected to /dev/ttyS*, DI/DO.
The BeagleBone P8 pin map was all -1 placeholders — every P8 GPIO silently failed. It now carries the real AM335x line ids from the BBB SRM.
PWM was dead on all 12 generic-Linux SBCs. Channels are now flattened across pwmchips (sorted by index) instead of requiring one chip to expose them all — Rockchip/Amlogic boards publish one chip per PWM cell. KRON_PWM_MAX raised to 8, with HAL_Init explicitly setting fds to -1 (a static-zero fd would have made cleanup close stdin).
RPi HAL: real single-byte SPI, generic ADC via IIO (honors the driver's _scale), SocketCAN.
Jetson: ADC honestly reports ERR_ID=1 (dev kits expose no user ADC), SocketCAN added.
🧩 Board list
Raspberry Pi Pico / Pico W removed. The whole product (KronServer agent, /dev/shm IPC, hot-swap dlopen) assumes a Linux userspace — a Cortex-M board could only ever simulate, deploy was always rejected. kronhal_pico.h and the Pico pin renderer are gone. Old projects still targeting a Pico now get a clear "reselect a Linux board in Board Config" error on Build/Deploy/Hot-swap instead of silently building a wrong-arch binary.
Peripheral counts corrected against real silicon (a channel count mints blocks in the toolbox, so an optimistic number is a promise the hardware must keep): Jetson Nano's phantom CAN removed (Tegra X1 has no CAN controller), Jetson TX2 → 2 CAN, RK3588 boards (Orange Pi 5 / 5 Plus, ROCK 5B) gained the CAN they really do have via can0-m0/can1-m0 overlays, ODROID-C4 → 6 PWM + 2 header ADC inputs (