← Commit history

Bridge SDK: add the v1.9.79 timeouts block contract (per-verb HTTP budgets)

John Lauer ·dbc61f1c4b ·4mo ago ·parent b73c94b
3 files changed +27−3
README.md+9−1
@@ -55,6 +55,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - [ ] `spawn.kind` ∈ `python|node|exe|external-http`, with **`entrypoint`** (NOT `process`/`cmd`/`args`); `healthEndpoint` is **inside `spawn`**; `port:0` lets AD pick. *(→ bridge.json)* - [ ] `updateManifestUrl` → YOUR page's manifest (durable auto-update); `docs` → your own page; **`homepage` is a `wiki.adom.inc` URL, NEVER `github.com`.** - [ ] `detect` declared iff you drive a HOST APP (KiCad/Fusion/Altium…); omitted if you bring your own runtime (pup). `prewarm.assets` declared for heavy downloads (e.g. `chrome-for-testing`). *(→ Cold-start tiers)*+- [ ] **`timeouts`** declared if ANY verb can run longer than 60s (a big export/render/cloud-open) — with a matching **`statusVerb`** to poll. Third-party bridges were 60s-capped before v1.9.79; this is how you raise it. *(→ bridge.json)*  **Runtime** (Node/Python bridges) *(→ Runtime contract)*: - [ ] You bind **`ADOM_BIND_HOST`** (loopback default), never `0.0.0.0`.@@ -106,6 +107,12 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "statusVerb": "blender_status",           // verb a caller polls after a long op times out   "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json", +  "timeouts": {                             // v1.9.79 — per-verb HTTP budgets (seconds); see note below+    "default": 60,                          //   fallback for any verb not matched (absent → AD's global 60s)+    "verbs": { "render": 600, "export_step": 360 },        // exact BARE verb names (no prefix)+    "prefixes": [ { "prefix": "export_", "seconds": 120 } ] // fallback rules, first match wins+  },+   // v1.9.47 cold-start (see "Cold-start tiers" below):   "detect": {                               // Tier 2 — how AD DETECTS your HOST APP. AD NEVER installs it.     "hostApp": "Blender",                   // omit the whole `detect` block if your bridge brings its own runtime@@ -123,6 +130,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - **`docs`** — point at YOUR dedicated wiki page. AD derives your **skillPkg** (`adom/<slug>`) and your card hero from this. Don't point it at a shared/structural page. - **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-desktop` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-desktop` is a stale default to fix.) - **`detect` / `prewarm`** — the v1.9.47 cold-start declarations (full rules in "Cold-start tiers" below). `detect` = your HOST APP (AD detects, never installs); `prewarm` = your heavy RUNTIME assets (AD prewarms/auto-installs). A node bridge that ships its deps still works without `prewarm`; declare `prewarm.assets` only for big *downloaded* assets like a browser.+- **`timeouts`** (v1.9.79) — per-verb HTTP budgets (seconds) AD honors so a SLOW verb (a big export, a cloud open, a long render) isn't cut off before it finishes. Resolution: exact `verbs` (BARE names, no prefix) → first matching `prefixes` → `default` → AD's global 60s. A timeout is **NON-terminal**: AD returns `{stillRunning:true, statusVerb, timeoutSeconds}` and the caller polls your `statusVerb` (so ALSO declare `statusVerb`). **⚠ Before v1.9.79 a third-party bridge was hard-capped at 60s with no way to raise it** — if any of your verbs can run longer, declare `timeouts` (this is the fix). AD reads it with ZERO AD change; the two bundled EDA bridges keep a labeled built-in table only until they declare their own.  --- @@ -324,7 +332,7 @@ These eight patterns are what make a cloud AI actually succeed with your bridge  - **Naming:** every verb is `<name>_<verb>` (`blender_render`, `blender_status`). AD routes any verb with your prefix to you. - **Request:** AD POSTs `{ "command":"<verb>", "args":{…} }` to your port. **Response:** JSON with at least `success` (bool); human/AI output in `output`, errors in `error` (+ an `errorCode` for machine-actionable failures). AD relays everything else verbatim — so ALWAYS add `_hint`/`_next`/`related`/`pitfalls`.-- **Long-running verbs:** return promptly with a job id + set `statusVerb`; the caller polls instead of blocking. On a timeout AD auto-returns `{stillRunning:true, statusVerb, timeoutSeconds}`.+- **Long-running verbs:** return promptly with a job id + set `statusVerb`; the caller polls instead of blocking. On a timeout AD auto-returns `{stillRunning:true, statusVerb, timeoutSeconds}`. If a verb genuinely blocks past 60s (a synchronous export/render), **also declare a `timeouts` block** (v1.9.79) so AD's HTTP budget matches — otherwise AD cuts the connection at 60s (third-party bridges were hard-capped there before v1.9.79). - **`<prefix>describe`:** return your full verb catalog (`{verbs:[{name,summary,input,output,timeoutSeconds,statusVerb,longRunning,example,hint,related,pitfalls}]}`). AD caches it (`~/.adom/bridge-verbs/<name>.json`) and shows it in the GUI Verbs tab — but remember, `describe` is read rarely; the per-call hints are what carry the AI. - **Keyboard input: route through AD's `desktop_press_key` — do NOT reimplement chords.** AD's key verb already does full modifier chords (`"shift+s"`, `"ctrl+shift+p"`, `"alt+f4"` — modifiers held via SendInput, key tapped, modifiers released in reverse), foregrounds the target window first (`{window|titleContains|hwnd}`) so CEF/Qt apps like Fusion receive it, and every bridge inherits it via the `/command` desktop passthrough (`{"command":"press_key", ...}` on the desktop namespace). A bridge-local `*_send_key` that parses only single keys (the Fusion bridge's `fusion_send_key` circa v1.6.x — `"shift+s"` → "Unknown key") duplicates AD's work and drifts; keep such a verb only for SendMessage-to-exact-hwnd cases AD's focus-first model can't cover, and say so in its `describe`. - **Host-app-optional verbs (v1.9.76):** declare `detect.hostAppOptionalVerbs` (bare names, e.g. `["readiness","describe"]`) in `bridge.json` so AD forwards those verbs to your running bridge even when your host app is absent — AD's built-in read-only set {readiness, describe, get_app_state, status} is always forwarded. And declare **`detect.installVerb`** (e.g. `"kicad_upgrade"`) so `bridge_readiness` recommends YOUR installer over the generic winget fallback.
SKILL.md+9−1
@@ -55,6 +55,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - [ ] `spawn.kind` ∈ `python|node|exe|external-http`, with **`entrypoint`** (NOT `process`/`cmd`/`args`); `healthEndpoint` is **inside `spawn`**; `port:0` lets AD pick. *(→ bridge.json)* - [ ] `updateManifestUrl` → YOUR page's manifest (durable auto-update); `docs` → your own page; **`homepage` is a `wiki.adom.inc` URL, NEVER `github.com`.** - [ ] `detect` declared iff you drive a HOST APP (KiCad/Fusion/Altium…); omitted if you bring your own runtime (pup). `prewarm.assets` declared for heavy downloads (e.g. `chrome-for-testing`). *(→ Cold-start tiers)*+- [ ] **`timeouts`** declared if ANY verb can run longer than 60s (a big export/render/cloud-open) — with a matching **`statusVerb`** to poll. Third-party bridges were 60s-capped before v1.9.79; this is how you raise it. *(→ bridge.json)*  **Runtime** (Node/Python bridges) *(→ Runtime contract)*: - [ ] You bind **`ADOM_BIND_HOST`** (loopback default), never `0.0.0.0`.@@ -106,6 +107,12 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "statusVerb": "blender_status",           // verb a caller polls after a long op times out   "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json", +  "timeouts": {                             // v1.9.79 — per-verb HTTP budgets (seconds); see note below+    "default": 60,                          //   fallback for any verb not matched (absent → AD's global 60s)+    "verbs": { "render": 600, "export_step": 360 },        // exact BARE verb names (no prefix)+    "prefixes": [ { "prefix": "export_", "seconds": 120 } ] // fallback rules, first match wins+  },+   // v1.9.47 cold-start (see "Cold-start tiers" below):   "detect": {                               // Tier 2 — how AD DETECTS your HOST APP. AD NEVER installs it.     "hostApp": "Blender",                   // omit the whole `detect` block if your bridge brings its own runtime@@ -123,6 +130,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - **`docs`** — point at YOUR dedicated wiki page. AD derives your **skillPkg** (`adom/<slug>`) and your card hero from this. Don't point it at a shared/structural page. - **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-desktop` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-desktop` is a stale default to fix.) - **`detect` / `prewarm`** — the v1.9.47 cold-start declarations (full rules in "Cold-start tiers" below). `detect` = your HOST APP (AD detects, never installs); `prewarm` = your heavy RUNTIME assets (AD prewarms/auto-installs). A node bridge that ships its deps still works without `prewarm`; declare `prewarm.assets` only for big *downloaded* assets like a browser.+- **`timeouts`** (v1.9.79) — per-verb HTTP budgets (seconds) AD honors so a SLOW verb (a big export, a cloud open, a long render) isn't cut off before it finishes. Resolution: exact `verbs` (BARE names, no prefix) → first matching `prefixes` → `default` → AD's global 60s. A timeout is **NON-terminal**: AD returns `{stillRunning:true, statusVerb, timeoutSeconds}` and the caller polls your `statusVerb` (so ALSO declare `statusVerb`). **⚠ Before v1.9.79 a third-party bridge was hard-capped at 60s with no way to raise it** — if any of your verbs can run longer, declare `timeouts` (this is the fix). AD reads it with ZERO AD change; the two bundled EDA bridges keep a labeled built-in table only until they declare their own.  --- @@ -324,7 +332,7 @@ These eight patterns are what make a cloud AI actually succeed with your bridge  - **Naming:** every verb is `<name>_<verb>` (`blender_render`, `blender_status`). AD routes any verb with your prefix to you. - **Request:** AD POSTs `{ "command":"<verb>", "args":{…} }` to your port. **Response:** JSON with at least `success` (bool); human/AI output in `output`, errors in `error` (+ an `errorCode` for machine-actionable failures). AD relays everything else verbatim — so ALWAYS add `_hint`/`_next`/`related`/`pitfalls`.-- **Long-running verbs:** return promptly with a job id + set `statusVerb`; the caller polls instead of blocking. On a timeout AD auto-returns `{stillRunning:true, statusVerb, timeoutSeconds}`.+- **Long-running verbs:** return promptly with a job id + set `statusVerb`; the caller polls instead of blocking. On a timeout AD auto-returns `{stillRunning:true, statusVerb, timeoutSeconds}`. If a verb genuinely blocks past 60s (a synchronous export/render), **also declare a `timeouts` block** (v1.9.79) so AD's HTTP budget matches — otherwise AD cuts the connection at 60s (third-party bridges were hard-capped there before v1.9.79). - **`<prefix>describe`:** return your full verb catalog (`{verbs:[{name,summary,input,output,timeoutSeconds,statusVerb,longRunning,example,hint,related,pitfalls}]}`). AD caches it (`~/.adom/bridge-verbs/<name>.json`) and shows it in the GUI Verbs tab — but remember, `describe` is read rarely; the per-call hints are what carry the AI. - **Keyboard input: route through AD's `desktop_press_key` — do NOT reimplement chords.** AD's key verb already does full modifier chords (`"shift+s"`, `"ctrl+shift+p"`, `"alt+f4"` — modifiers held via SendInput, key tapped, modifiers released in reverse), foregrounds the target window first (`{window|titleContains|hwnd}`) so CEF/Qt apps like Fusion receive it, and every bridge inherits it via the `/command` desktop passthrough (`{"command":"press_key", ...}` on the desktop namespace). A bridge-local `*_send_key` that parses only single keys (the Fusion bridge's `fusion_send_key` circa v1.6.x — `"shift+s"` → "Unknown key") duplicates AD's work and drifts; keep such a verb only for SendMessage-to-exact-hwnd cases AD's focus-first model can't cover, and say so in its `describe`. - **Host-app-optional verbs (v1.9.76):** declare `detect.hostAppOptionalVerbs` (bare names, e.g. `["readiness","describe"]`) in `bridge.json` so AD forwards those verbs to your running bridge even when your host app is absent — AD's built-in read-only set {readiness, describe, get_app_state, status} is always forwarded. And declare **`detect.installVerb`** (e.g. `"kicad_upgrade"`) so `bridge_readiness` recommends YOUR installer over the generic winget fallback.
bridges-SDK.md+9−1
@@ -55,6 +55,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - [ ] `spawn.kind` ∈ `python|node|exe|external-http`, with **`entrypoint`** (NOT `process`/`cmd`/`args`); `healthEndpoint` is **inside `spawn`**; `port:0` lets AD pick. *(→ bridge.json)* - [ ] `updateManifestUrl` → YOUR page's manifest (durable auto-update); `docs` → your own page; **`homepage` is a `wiki.adom.inc` URL, NEVER `github.com`.** - [ ] `detect` declared iff you drive a HOST APP (KiCad/Fusion/Altium…); omitted if you bring your own runtime (pup). `prewarm.assets` declared for heavy downloads (e.g. `chrome-for-testing`). *(→ Cold-start tiers)*+- [ ] **`timeouts`** declared if ANY verb can run longer than 60s (a big export/render/cloud-open) — with a matching **`statusVerb`** to poll. Third-party bridges were 60s-capped before v1.9.79; this is how you raise it. *(→ bridge.json)*  **Runtime** (Node/Python bridges) *(→ Runtime contract)*: - [ ] You bind **`ADOM_BIND_HOST`** (loopback default), never `0.0.0.0`.@@ -106,6 +107,12 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "statusVerb": "blender_status",           // verb a caller polls after a long op times out   "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json", +  "timeouts": {                             // v1.9.79 — per-verb HTTP budgets (seconds); see note below+    "default": 60,                          //   fallback for any verb not matched (absent → AD's global 60s)+    "verbs": { "render": 600, "export_step": 360 },        // exact BARE verb names (no prefix)+    "prefixes": [ { "prefix": "export_", "seconds": 120 } ] // fallback rules, first match wins+  },+   // v1.9.47 cold-start (see "Cold-start tiers" below):   "detect": {                               // Tier 2 — how AD DETECTS your HOST APP. AD NEVER installs it.     "hostApp": "Blender",                   // omit the whole `detect` block if your bridge brings its own runtime@@ -123,6 +130,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - **`docs`** — point at YOUR dedicated wiki page. AD derives your **skillPkg** (`adom/<slug>`) and your card hero from this. Don't point it at a shared/structural page. - **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-desktop` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-desktop` is a stale default to fix.) - **`detect` / `prewarm`** — the v1.9.47 cold-start declarations (full rules in "Cold-start tiers" below). `detect` = your HOST APP (AD detects, never installs); `prewarm` = your heavy RUNTIME assets (AD prewarms/auto-installs). A node bridge that ships its deps still works without `prewarm`; declare `prewarm.assets` only for big *downloaded* assets like a browser.+- **`timeouts`** (v1.9.79) — per-verb HTTP budgets (seconds) AD honors so a SLOW verb (a big export, a cloud open, a long render) isn't cut off before it finishes. Resolution: exact `verbs` (BARE names, no prefix) → first matching `prefixes` → `default` → AD's global 60s. A timeout is **NON-terminal**: AD returns `{stillRunning:true, statusVerb, timeoutSeconds}` and the caller polls your `statusVerb` (so ALSO declare `statusVerb`). **⚠ Before v1.9.79 a third-party bridge was hard-capped at 60s with no way to raise it** — if any of your verbs can run longer, declare `timeouts` (this is the fix). AD reads it with ZERO AD change; the two bundled EDA bridges keep a labeled built-in table only until they declare their own.  --- @@ -324,7 +332,7 @@ These eight patterns are what make a cloud AI actually succeed with your bridge  - **Naming:** every verb is `<name>_<verb>` (`blender_render`, `blender_status`). AD routes any verb with your prefix to you. - **Request:** AD POSTs `{ "command":"<verb>", "args":{…} }` to your port. **Response:** JSON with at least `success` (bool); human/AI output in `output`, errors in `error` (+ an `errorCode` for machine-actionable failures). AD relays everything else verbatim — so ALWAYS add `_hint`/`_next`/`related`/`pitfalls`.-- **Long-running verbs:** return promptly with a job id + set `statusVerb`; the caller polls instead of blocking. On a timeout AD auto-returns `{stillRunning:true, statusVerb, timeoutSeconds}`.+- **Long-running verbs:** return promptly with a job id + set `statusVerb`; the caller polls instead of blocking. On a timeout AD auto-returns `{stillRunning:true, statusVerb, timeoutSeconds}`. If a verb genuinely blocks past 60s (a synchronous export/render), **also declare a `timeouts` block** (v1.9.79) so AD's HTTP budget matches — otherwise AD cuts the connection at 60s (third-party bridges were hard-capped there before v1.9.79). - **`<prefix>describe`:** return your full verb catalog (`{verbs:[{name,summary,input,output,timeoutSeconds,statusVerb,longRunning,example,hint,related,pitfalls}]}`). AD caches it (`~/.adom/bridge-verbs/<name>.json`) and shows it in the GUI Verbs tab — but remember, `describe` is read rarely; the per-call hints are what carry the AI. - **Keyboard input: route through AD's `desktop_press_key` — do NOT reimplement chords.** AD's key verb already does full modifier chords (`"shift+s"`, `"ctrl+shift+p"`, `"alt+f4"` — modifiers held via SendInput, key tapped, modifiers released in reverse), foregrounds the target window first (`{window|titleContains|hwnd}`) so CEF/Qt apps like Fusion receive it, and every bridge inherits it via the `/command` desktop passthrough (`{"command":"press_key", ...}` on the desktop namespace). A bridge-local `*_send_key` that parses only single keys (the Fusion bridge's `fusion_send_key` circa v1.6.x — `"shift+s"` → "Unknown key") duplicates AD's work and drifts; keep such a verb only for SendMessage-to-exact-hwnd cases AD's focus-first model can't cover, and say so in its `describe`. - **Host-app-optional verbs (v1.9.76):** declare `detect.hostAppOptionalVerbs` (bare names, e.g. `["readiness","describe"]`) in `bridge.json` so AD forwards those verbs to your running bridge even when your host app is absent — AD's built-in read-only set {readiness, describe, get_app_state, status} is always forwarded. And declare **`detect.installVerb`** (e.g. `"kicad_upgrade"`) so `bridge_readiness` recommends YOUR installer over the generic winget fallback.