From c0d417856ffd272c1d583303a299a2444454c7b2 Mon Sep 17 00:00:00 2001 From: hudsonaikins-crown Date: Fri, 13 Nov 2026 00:12:34 -0500 Subject: [PATCH] Prepare v0.5.1: restore SimBrief generation, report cancelled turns Pin playwright to 1.64.1: the 1.65.0 driver needs libatomic.so.1, absent from python:3.12-slim, so v0.5.0 failed every Generate before the click. turn_status now reports cancelled after cancel_trip instead of the withdrawn approval_required result. CI builds the container image and starts the Playwright driver inside it. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 16 ++++++++++ CHANGELOG.md | 17 +++++++++++ pyproject.toml | 4 +-- src/ifdispatcher/remote.py | 14 ++++++++- tests/test_trip_progress.py | 20 +++++++++++++ uv.lock | 58 +++++++++++++++++++++++++++++-------- 6 files changed, 114 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d092b9c..a122357 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,3 +46,19 @@ jobs: - run: npx wrangler types --strict-vars=false --check - run: npm run typecheck - run: npm test + + image: + # Starts the Playwright driver inside the real container image. Unit tests mock the + # browser, so a driver that cannot run in python:3.12-slim (v0.5.0: playwright 1.65.0 + # needed libatomic.so.1) only shows up here. + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.ref || github.sha }} + persist-credentials: false + - run: docker build --platform linux/amd64 -t ifd-ci . + - run: >- + docker run --rm --platform linux/amd64 ifd-ci uv run --locked --no-dev python -c + 'from playwright.sync_api import sync_playwright; + p = sync_playwright().start(); p.stop(); print("playwright driver ok")' diff --git a/CHANGELOG.md b/CHANGELOG.md index b41bf99..5e027ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,22 @@ A running server reports its version at `/health`. ## [Unreleased] +## [0.5.1] - 2026-11-13 + +### Fixed + +- SimBrief generation works again. v0.5.0 shipped playwright 1.65.0, whose driver needs + `libatomic.so.1`, which the `python:3.12-slim` image lacks, so every Generate failed within + seconds before the click. Playwright is pinned to 1.64.1, and production ran v0.4.7 again + until this release. +- `turn_status` reports `cancelled` after `cancel_trip` instead of repeating the withdrawn + `approval_required` result and its approve instruction. + +### Changed + +- CI builds the container image and starts the Playwright driver inside it, so a dependency + that cannot run in the deployed image fails before release. + ## [0.5.0] - 2026-11-13 ### Added @@ -326,6 +342,7 @@ First public release, and the first version live on the self-hosted Cloudflare d of blocking it permanently ([#3](https://github.com/IntelIP/InfiniteFlightAgent/pull/3)). [Unreleased]: https://github.com/IntelIP/InfiniteFlightAgent/compare/v0.4.6...HEAD +[0.5.1]: https://github.com/IntelIP/InfiniteFlightAgent/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/IntelIP/InfiniteFlightAgent/compare/v0.4.7...v0.5.0 [0.4.7]: https://github.com/IntelIP/InfiniteFlightAgent/compare/v0.4.6...v0.4.7 [0.4.6]: https://github.com/IntelIP/InfiniteFlightAgent/compare/v0.4.5...v0.4.6 diff --git a/pyproject.toml b/pyproject.toml index 3e76483..3630423 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "infinite-flight-dispatcher" -version = "0.5.0" +version = "0.5.1" description = "AI dispatcher for Infinite Flight: real flights, SimBrief plans, and takeoff N1 estimates over MCP" requires-python = ">=3.11" license = "GPL-3.0-only" @@ -15,7 +15,7 @@ dependencies = [ "langgraph-checkpoint-sqlite>=3.1.3", "pyjwt[crypto]>=2.15.1", "browserbase>=1.23.0", - "playwright>=1.64.1", + "playwright==1.64.1", # 1.65.0 driver needs libatomic.so.1, absent from python:3.12-slim "psycopg[binary]>=3.3.6", "psycopg-pool>=3.2", "langgraph-checkpoint-postgres>=3.2.0", diff --git a/src/ifdispatcher/remote.py b/src/ifdispatcher/remote.py index 3902c78..6d4d8fa 100644 --- a/src/ifdispatcher/remote.py +++ b/src/ifdispatcher/remote.py @@ -254,8 +254,20 @@ async def turn_status(thread_id: str) -> dict[str, Any]: """Result of the latest plan_flight or approve_dispatch call on thread_id after it returned status running. Returns running again (call later), or the reply / approval request. """ + finished = thread_id in _turns and _turns[thread_id].done() + record = progress.read(thread_id) if finished or thread_id not in _turns else None + if finished and record and record.get("status") == "cancelled": + # The cached result predates cancel_trip (e.g. a withdrawn approval_required); + # report the cancellation instead of asking for an approval that is refused. + return { + "status": "cancelled", + "thread_id": thread_id, + "turn": progress.turn_view(record, live=False), + "next": "This turn was cancelled; any pending approval was withdrawn and cannot be " + "approved. Call trip_progress for kept legs, or send plan_flight to start again.", + } if thread_id not in _turns: - turn = progress.turn_view(progress.read(thread_id), live=False) + turn = progress.turn_view(record, live=False) if turn: return { "status": turn["status"], diff --git a/tests/test_trip_progress.py b/tests/test_trip_progress.py index 2c78165..0504bbf 100644 --- a/tests/test_trip_progress.py +++ b/tests/test_trip_progress.py @@ -280,6 +280,26 @@ def test_cancel_pending_approval_keeps_completed_legs_and_refuses_approval(store assert store.read_meta("T1", "1", 1)["briefing"] == "**SPAWN:** gate" +def test_turn_status_reports_cancel_instead_of_stale_approval(store, monkeypatch): + """Observed live on v0.5.0: turn_status kept returning the finished approval_required + result after cancel_trip withdrew that approval.""" + from concurrent.futures import Future + + prepare(store, leg="1") + state = pending_state(store) + monkeypatch.setattr(remote, "_get_agent", lambda: NoModel(state)) + done = Future() + done.set_result({"status": "approval_required", "thread_id": "thread-s"}) + monkeypatch.setattr(remote, "_turns", {"thread-s": done}) + + assert asyncio.run(remote.turn_status("thread-s"))["status"] == "approval_required" + assert asyncio.run(remote.cancel_trip("thread-s"))["status"] == "cancelled" + status = asyncio.run(remote.turn_status("thread-s")) + assert status["status"] == "cancelled" + assert "cannot be approved" in status["next"] + assert status["turn"]["status"] == "cancelled" + + def test_trip_progress_not_found_and_records_approval_history(store, monkeypatch): monkeypatch.setattr(remote, "_get_agent", lambda: NoModel(empty_state())) assert remote.trip_progress("missing")["status"] == "not_found" diff --git a/uv.lock b/uv.lock index 5f71733..5a6db91 100644 --- a/uv.lock +++ b/uv.lock @@ -720,7 +720,7 @@ wheels = [ [[package]] name = "infinite-flight-dispatcher" -version = "0.5.0" +version = "0.5.1" source = { editable = "." } dependencies = [ { name = "boto3" }, @@ -757,7 +757,7 @@ requires-dist = [ { name = "langgraph-checkpoint-sqlite", specifier = ">=3.1.3" }, { name = "langsmith", specifier = "==0.14.17" }, { name = "mcp", specifier = "==1.27.0" }, - { name = "playwright", specifier = ">=1.64.1" }, + { name = "playwright", specifier = "==1.64.1" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.3.6" }, { name = "psycopg-pool", specifier = ">=3.2" }, { name = "pydantic", specifier = ">=2.11,<3" }, @@ -1334,21 +1334,22 @@ wheels = [ [[package]] name = "playwright" -version = "1.65.0" +version = "1.64.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet" }, + { name = "pyee", version = "13.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "pyee", version = "14.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/26/bfd17cb22beaab2c41ae68b245338ca25b83bffd809fa8d4117d648002d6/playwright-1.65.0.tar.gz", hash = "sha256:81f6a4c56e25d2e0f66232c3548649b17697ee3211e222aae78d4ecafad51918", size = 10224005, upload-time = "2026-11-09T21:10:01.425Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/8c/e98c8ac52e9187bff759893a4009bf468dbd56255dc34ad8f53c8e77f10c/playwright-1.65.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:78d2bd6127463552ddd890516e5eb4cc805559af474aa5ccf5b62e907972ce16", size = 50319011, upload-time = "2026-11-09T21:09:30.436Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5d/213c3f278d4f3b110d61b4082222c52b9f7187d3f4ca3dde2d217e0d752f/playwright-1.65.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:229d6b184de3da7bf292cc38a63ee82aefb5c9f0a4233b1142f3ffb1cbd410ec", size = 48808710, upload-time = "2026-11-09T21:09:34.587Z" }, - { url = "https://files.pythonhosted.org/packages/73/76/ed4f88c6cc56a31b21c9820df74b65d1fef8f0500b8bc85c5f994a3b9d36/playwright-1.65.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:17e8e46065aa1a2e2aa3c3efde37788fecd0e5e645c7020780ebe684305118ca", size = 50319014, upload-time = "2026-11-09T21:09:38.488Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5c/fddffe92237452d23b19c4703ca8a2ddffc3da9eaf4424b0f019dd5608f3/playwright-1.65.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:e8e4d4a177bef2c2f2a17019febadded1c84cd1499b80f7100bb30ff20df7bad", size = 53470748, upload-time = "2026-11-09T21:09:42.382Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ff/952586cd6d9ec43739d66fa6bfebfd92656ab03e97bc54659b23c23ad069/playwright-1.65.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9072e6394fec07e56e17fec0ca271a7847dda4716db9d716291d1f7344f199bb", size = 53142506, upload-time = "2026-11-09T21:09:46.401Z" }, - { url = "https://files.pythonhosted.org/packages/fe/21/af53bd727a8dfb7289c9f5c9477f84aa3dd8d9e7786ed68b65c9e2625ec3/playwright-1.65.0-py3-none-win32.whl", hash = "sha256:d9efef7b809020325d3d1bb34eb05c93865199274319249c9f6a77cd17fd8573", size = 42664070, upload-time = "2026-11-09T21:09:50.532Z" }, - { url = "https://files.pythonhosted.org/packages/8c/0d/32e96514b48fabd16ece31bae11afba5e18cf425123fb7c36c6b8bd8db8f/playwright-1.65.0-py3-none-win_amd64.whl", hash = "sha256:ab22611318e8747477c949c81b055ee6e224ecedf66c2a794209c7fd0210a743", size = 42664080, upload-time = "2026-11-09T21:09:54.425Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d6/2a75d6eeda7b23e1b241ef898ed53bc3ea134ff52d2c959c770dce843e3c/playwright-1.65.0-py3-none-win_arm64.whl", hash = "sha256:67f0b1a80d0d2d4349f67c1ccfeceaa067f1d85aaac32d4d81dd3e971a96ddbb", size = 38367903, upload-time = "2026-11-09T21:09:58.28Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c8/cc677856d89a0c077673b40ab915ed5597bf1347b205cabe70121c431ca9/playwright-1.64.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:29453ff76e25c17d0b45658cacd6b92e9eec46e8db21e134fb3b7143df50f013", size = 44508971, upload-time = "2026-10-22T16:29:58.12Z" }, + { url = "https://files.pythonhosted.org/packages/72/4f/572bbbe60a5787ca61a84bad934c3ce19c08c2bc9c0e9781bf0d99cad1a6/playwright-1.64.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:59f7b041284ce8d1b72098ae50ca48a2d83692eec25154ab5ba79d4b6b4fe8a9", size = 43155232, upload-time = "2026-10-22T16:30:02.415Z" }, + { url = "https://files.pythonhosted.org/packages/44/fa/468f313a56d33a6420705147c8d47bef558f087856edf38c5b13eef8aa6c/playwright-1.64.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:6a6f99adad1ae40e6ee62efe89785db861ce7ddd57651521cb31894183cc4ae3", size = 44508977, upload-time = "2026-10-22T16:30:07.632Z" }, + { url = "https://files.pythonhosted.org/packages/15/b9/e481a34572933b5cf179d5637c98da488c732ee339dff5b73a0624d2e42b/playwright-1.64.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:577b8d88778c245cfbd145ae17cf53044f084b1b46c55c69081d4069d56057e2", size = 48471931, upload-time = "2026-10-22T16:30:12.831Z" }, + { url = "https://files.pythonhosted.org/packages/0c/11/545f25e47ec54e1e8a2892ef9994fc2b298f909ab37b88578e3a1f87ac2e/playwright-1.64.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a29fda44725e11b0b625c4bff6458a1f6dbee97b88c9788d12e18a8344458c2d", size = 48150994, upload-time = "2026-10-22T16:30:18.297Z" }, + { url = "https://files.pythonhosted.org/packages/33/87/8ff7af99387ee72ae9b46a66ece7a0c0aa63f73320ecf6dd3dd087fa840a/playwright-1.64.1-py3-none-win32.whl", hash = "sha256:d3c48b31da426c2f3562d0919cb9ea4e5415f6dbd29d7211fa4d66cca24fbd2f", size = 38801828, upload-time = "2026-10-22T16:30:23.154Z" }, + { url = "https://files.pythonhosted.org/packages/96/0a/64db6851aa1852a4f76be28eea71d4e2bf9f3ce493ee2e66939426729046/playwright-1.64.1-py3-none-win_amd64.whl", hash = "sha256:5e240a79464434c9260957b9ef59a5808e02e96e3fcabdc717fd7485260b9730", size = 38801836, upload-time = "2026-10-22T16:30:28.092Z" }, + { url = "https://files.pythonhosted.org/packages/c5/4b/d7ddde9467a5fcfe6a0d3c954732332e98a4f4a9025fd42c8d091c16e5f9/playwright-1.64.1-py3-none-win_arm64.whl", hash = "sha256:983ccd085453ea5a7588193657fc349a6ffbf7bf7ba663fd58dc81e4aa3af9d9", size = 34693875, upload-time = "2026-10-22T16:30:32.724Z" }, ] [[package]] @@ -1648,6 +1649,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/88/4131c51ff87b7cd7f82a1194d0c0a2b7ca68c72d9da04c3b7e69ab91a79f/pydantic_settings-2.16.2-py3-none-any.whl", hash = "sha256:8d523e5c129b097686bde019bd6109bff53f4c8c3f63f1ddf9933ffa42e5ab4f", size = 81994, upload-time = "2026-10-28T11:14:26.716Z" }, ] +[[package]] +name = "pyee" +version = "13.0.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655, upload-time = "2026-02-14T21:12:28.044Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/b4d4827c93ef43c01f599ef31453ccc1c132b353284fc6c87d535c233129/pyee-13.0.1-py3-none-any.whl", hash = "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228", size = 15659, upload-time = "2026-02-14T21:12:26.263Z" }, +] + +[[package]] +name = "pyee" +version = "14.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten'", +] +dependencies = [ + { name = "typing-extensions", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/f1/fdedc2c75c3e31a330659c85e5793bb18b3397981fbf0844c6dee5b18926/pyee-14.0.0.tar.gz", hash = "sha256:76dd0f4314ecd27f02dc73589dea7fd3853f9b6176d8ef9b122860657e3602de", size = 98760, upload-time = "2026-08-13T04:26:11.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/12/5347938b1f9a6453f0dbdfcc3e2388a1320ef9b9ec17fbefbc4ab647ea98/pyee-14.0.0-py3-none-any.whl", hash = "sha256:3ac2d3229a9677f7de2c33d7f52fe25b638a46b19c413fea2edc8c6d0a644e4d", size = 15553, upload-time = "2026-08-13T04:26:09.916Z" }, +] + [[package]] name = "pygments" version = "2.21.0"