app
Altium - the Altium Bridge
Public Made by Adomby adom
Adom Bridge for Altium Designer: a resident DelphiScript agent gives the AI eval, the 1,100-command process launcher, all 8 EDA surfaces with screenshots, compile/ERC, DRC, OutJob outputs, native SchLib/PcbLib authoring with STEP 3D bodies, and dialog handling by PID.
← Commit history
v0.1.32: licence gate respects the selected instance
7 files changed
+30−11
CHANGELOG.md+4@@ -1,5 +1,9 @@ # Changelog +## 0.1.32 — 2026-09-01+- **Instance-awareness now reaches the licence gate.** `main_window()` returned "the first Altium main window" regardless of the selected instance, and `licence_state()` → `_need_licensed()` are built on it — so with two Altiums running, every verb targeting one instance was judged against the OTHER instance's licence. Measured live: `altium_license_status {instance:"develop"}` correctly reported `Trial`, while `altium_agent_start {instance:"develop"}` was refused `not_licensed` because Designer held no seat.+- Verified with a real second instance (Altium Designer Develop): the layer distinguishes `AD26/Designer` from `ADDevelop/Develop`, gives each its own agent workspace, and REFUSES a verb with `ambiguous_instance` when no selector is given — rather than acting inside the other AI thread's Altium.+ ## 0.1.31 — 2026-09-01 - **Systemic fix: `ad_callback` now unwraps ab's reply envelope.** ab's direct API answers `{success, status, error, output}` with the verb's REAL result JSON-encoded as a STRING in `output`. The Python bridge never unwrapped it, so EVERY field read back from an ab callback — `hwnd`, `windows`, `processes`, `controls` — was silently `None`. Calls that only ACTED (click, kill) worked, which is why this hid for so long; anything that tried to READ a result quietly saw nothing. - Found because 0.1.30's background-capture helper decided a minimized window was not minimized: it was reading `rect` out of an envelope it could not see into. The Rust bridge has always unwrapped (`ab.rs::unwrap_output`) — this brings Python in line.
adom-bridge-altium-manifest.json+4−4@@ -1,9 +1,9 @@ { "name": "altium",- "version": "0.1.31",- "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.31/adom-bridge-altium-v0.1.31.zip",- "sha256": "ff4f7b392d17e964d9cb1501edc45f85240a95928bf02f60209d7f749cda254c",- "size": 129960,+ "version": "0.1.32",+ "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.32/adom-bridge-altium-v0.1.32.zip",+ "sha256": "d382722088301629b1c1d11f3bd6f574d3370861fb25d891a6cf7ecf32d883fc",+ "size": 130236, "verbPrefixes": [ "altium_" ],
adom-bridge-altium-v0.1.32.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "altium", "displayName": "Altium Designer",- "version": "0.1.31",+ "version": "0.1.32", "description": "Drive Altium Designer from Adom: a resident DelphiScript agent (eval + the 1,100-command process launcher), the X2 -R command line, UIA over the VCL shell, dialogs by PID, screenshots of every Altium window, and Altium 365.", "homepage": "https://wiki.adom.inc/adom/altium-bridge", "docs": "https://wiki.adom.inc/adom/altium-bridge",
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "altium-bridge",- "version": "0.1.31",+ "version": "0.1.32", "type": "app", "description": "Skills for your container so your AI knows how to drive the Altium Designer bridge (altium_* verbs). The bridge runtime itself is the release zip; Adom Bridge loads that.", "tags": [
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.1.31+0.1.32
src/handlers/altium_detect.py+19−4@@ -234,10 +234,25 @@ def altium_windows() -> list[dict]: def main_window() -> dict | None:- for w in altium_windows():- if w["kind"] == "main":- return w- return None+ """The main window of the SELECTED instance, or the only one.++ Instance-awareness has to reach this far down: `licence_state()` is built on it, and+ `_need_licensed()` is built on that. With two Altiums running, returning "the first main window"+ meant every licence gate judged the WRONG instance — a verb targeting Develop was refused+ because Designer held no seat (measured 2026-09-01, right after the instance layer landed)."""+ wins = [w for w in altium_windows() if w["kind"] == "main"]+ if not wins:+ return None+ try:+ from handlers import agent as _agent+ sel = _agent.current_instance()+ except Exception:+ sel = None+ if sel and sel.get("pid"):+ for w in wins:+ if w.get("pid") == sel["pid"]:+ return w+ return wins[0] def licence_state() -> dict: