From 3e3fc4ecfe1b866e9b9cf8264ee63a5cf0f9f5ae Mon Sep 17 00:00:00 2001 From: Josh Purtell Date: Thu, 10 Sep 2026 16:31:47 -0400 Subject: [PATCH 1/3] Add ChatGPT Astra onboarding and free RuneBench review preview --- .gitignore | 1 + PUBLIC_EXPORT_MANIFEST.json | 322 ++++++-- README.md | 61 +- .../src-tauri/src/codex_oauth.rs | 26 +- .../src-tauri/src/model_catalog.rs | 10 + .../src-tauri/src/session/codex/event_pump.rs | 41 +- .../src-tauri/src/session/codex/home.rs | 5 +- .../src-tauri/src/session/codex/manager.rs | 11 +- .../src-tauri/tauri.package.json | 1 + .../src/renderer/src/bridge/types.ts | 1 + .../ChatgptCodexSubscriptionCard.tsx | 60 +- .../src/components/OpenRouterSetupCard.tsx | 47 ++ .../renderer/src/components/SettingsPage.tsx | 2 + .../src/renderer/src/runtime/desktopBridge.ts | 4 + .../renderer/src/runtime/modelCapabilities.ts | 6 + .../src/renderer/src/runtime/modelCatalog.ts | 1 + .../src/runtime/modelPerformanceLabels.ts | 2 + .../src/renderer/src/runtime/nativeCodex.ts | 7 +- .../src/renderer/src/runtime/sessionView.ts | 5 +- .../src/renderer/src/types/landing.ts | 7 + examples/runebench/.gitignore | 19 + examples/runebench/Dockerfile | 8 + examples/runebench/LICENSE.rs-sdk | 21 + examples/runebench/PROMPT.md | 22 + examples/runebench/README.md | 75 ++ examples/runebench/THIRD_PARTY.md | 11 + examples/runebench/build_deps.mjs | 5 + examples/runebench/build_traces.py | 37 + examples/runebench/build_web.mjs | 42 + examples/runebench/compose.yaml | 14 + examples/runebench/docker-config/config.json | 1 + examples/runebench/engine-hook.ts | 52 ++ examples/runebench/entrypoint.sh | 26 + examples/runebench/evidence_api.py | 39 + examples/runebench/example.sh | 51 ++ examples/runebench/manifest.json | 23 + examples/runebench/observe.ts | 101 +++ examples/runebench/provision.ts | 13 + examples/runebench/query.py | 149 ++++ examples/runebench/requirements.lock | 460 +++++++++++ examples/runebench/requirements.txt | 1 + examples/runebench/run.py | 181 +++++ examples/runebench/sample.py | 73 ++ examples/runebench/sample.tar.gz | Bin 0 -> 1528667 bytes examples/runebench/save-generator.ts | 750 ++++++++++++++++++ examples/runebench/scenario.json | 15 + examples/runebench/serve.py | 88 ++ examples/runebench/smoke.ts | 16 + examples/runebench/viewer.tsx | 162 ++++ package-lock.json | 130 +++ package.json | 1 + .../agent_trace.v1/EvidenceClient.tsx | 5 +- .../components/agent_trace.v1/README.md | 2 +- .../agent_trace.v1/evidenceEndpoint.ts | 8 + resources/codex/LICENSE | 201 +++++ resources/codex/NOTICE | 6 + scripts/bootstrap-deps.sh | 36 + scripts/build-tier.sh | 5 +- scripts/install.sh | 13 +- scripts/local-toolchain-env.sh | 11 + scripts/stage-codex-runtime.sh | 19 + scripts/workshop.sh | 5 +- 62 files changed, 3428 insertions(+), 89 deletions(-) create mode 100644 apps/synth_desktop/src/renderer/src/components/OpenRouterSetupCard.tsx create mode 100644 examples/runebench/.gitignore create mode 100644 examples/runebench/Dockerfile create mode 100644 examples/runebench/LICENSE.rs-sdk create mode 100644 examples/runebench/PROMPT.md create mode 100644 examples/runebench/README.md create mode 100644 examples/runebench/THIRD_PARTY.md create mode 100644 examples/runebench/build_deps.mjs create mode 100644 examples/runebench/build_traces.py create mode 100644 examples/runebench/build_web.mjs create mode 100644 examples/runebench/compose.yaml create mode 100644 examples/runebench/docker-config/config.json create mode 100644 examples/runebench/engine-hook.ts create mode 100644 examples/runebench/entrypoint.sh create mode 100644 examples/runebench/evidence_api.py create mode 100644 examples/runebench/example.sh create mode 100644 examples/runebench/manifest.json create mode 100644 examples/runebench/observe.ts create mode 100644 examples/runebench/provision.ts create mode 100644 examples/runebench/query.py create mode 100644 examples/runebench/requirements.lock create mode 100644 examples/runebench/requirements.txt create mode 100644 examples/runebench/run.py create mode 100644 examples/runebench/sample.py create mode 100644 examples/runebench/sample.tar.gz create mode 100644 examples/runebench/save-generator.ts create mode 100644 examples/runebench/scenario.json create mode 100644 examples/runebench/serve.py create mode 100644 examples/runebench/smoke.ts create mode 100644 examples/runebench/viewer.tsx create mode 100644 packages/workshop-visuals/components/agent_trace.v1/evidenceEndpoint.ts create mode 100644 resources/codex/LICENSE create mode 100644 resources/codex/NOTICE create mode 100644 scripts/bootstrap-deps.sh create mode 100644 scripts/local-toolchain-env.sh create mode 100644 scripts/stage-codex-runtime.sh diff --git a/.gitignore b/.gitignore index 9b55bf513..6b744bb18 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ work/bombadil-src/ .env.* .turbo/ work/ +runtime-distributions/codex/ apps/synth_desktop/src-tauri/generated-resources/ apps/synth_desktop/browser/runtime runtime-distributions/trace/ diff --git a/PUBLIC_EXPORT_MANIFEST.json b/PUBLIC_EXPORT_MANIFEST.json index 624d4c578..62e7f2194 100644 --- a/PUBLIC_EXPORT_MANIFEST.json +++ b/PUBLIC_EXPORT_MANIFEST.json @@ -25,10 +25,10 @@ "sha256": "67f4f45d3477325d1f91edb1a493a27215dacacb755dba0d379a4bd4d151b142" }, { - "bytes": 670, + "bytes": 699, "mode": "100644", "path": ".gitignore", - "sha256": "394215016871aab847b5a8984b3d96b4e967671891afecde7dfd3a9f5932ecd3" + "sha256": "9e93c1f315df3c0921b899953ac6ec07774ce823977b398a035ce73ceaf5c624" }, { "bytes": 3, @@ -67,10 +67,10 @@ "sha256": "49898a5b3fba843f274e014e99a57fad372bb2cebf2f827a43d6dbb3420148d0" }, { - "bytes": 7762, + "bytes": 10694, "mode": "100644", "path": "README.md", - "sha256": "e6574f787ed372c9672520951a7bd7f33dfc2ce60f7e198370c8f2017e7afd0e" + "sha256": "ef1572c89956c1e652010ec6bc8cfffb76585f84276a50bb650db045f0ef156c" }, { "bytes": 633, @@ -1051,10 +1051,10 @@ "sha256": "908ab5f929066f14fc27ae2e1bda3630c73d6bc1933d7ffc4665b7764cc69457" }, { - "bytes": 32486, + "bytes": 33386, "mode": "100644", "path": "apps/synth_desktop/src-tauri/src/codex_oauth.rs", - "sha256": "2d88189842d4f8311100f7e6baca9a2e15bae73fbb1da5a35113f78182ea747b" + "sha256": "f5b9901b4e3952063c13c2bc64935538ab3dbd49375a79467fdce38e784638de" }, { "bytes": 119, @@ -1645,10 +1645,10 @@ "sha256": "7b33991eb0340ba474e3d828ea420ac86176a251b6264aefc0e4aa3b0dd2b935" }, { - "bytes": 23521, + "bytes": 24038, "mode": "100644", "path": "apps/synth_desktop/src-tauri/src/model_catalog.rs", - "sha256": "9be5dc1e76d2e86b5c304e398a626d77c8ed3c83b61962449b2a24e621b22ec1" + "sha256": "3d60868f708a4d1414bb3ec3131d7b8c9c2d1c59397bd7fd67fdd74f61f462a2" }, { "bytes": 6111, @@ -2593,10 +2593,10 @@ "sha256": "d235803e4919c54d3b24baeacaed5899e10eb26b93956d0cebeaa57fdd9bde41" }, { - "bytes": 45592, + "bytes": 48207, "mode": "100644", "path": "apps/synth_desktop/src-tauri/src/session/codex/event_pump.rs", - "sha256": "4ff1fdfe2c70a4cf8e9e3162dc9d92fd88d9c5c70a888cd2921e5b8a40a265e0" + "sha256": "849c320de77da5cd5da6238a4af07ebc7ba936e04f4d10c828a098c4de943cf1" }, { "bytes": 47523, @@ -2611,16 +2611,16 @@ "sha256": "0e5cc452ba0589aa7983bfaf36230f66b663932d9590eb808d22f7c07cd835ee" }, { - "bytes": 53157, + "bytes": 53323, "mode": "100644", "path": "apps/synth_desktop/src-tauri/src/session/codex/home.rs", - "sha256": "1c4dde3dd33c466a18e0e491b6b5cb7868a4db9e5b39be217bc6e279baa8b370" + "sha256": "9ff949434361a3f650487283e876c86bbc63ec6f024a0f96f3b64498b87a8124" }, { - "bytes": 80126, + "bytes": 80748, "mode": "100644", "path": "apps/synth_desktop/src-tauri/src/session/codex/manager.rs", - "sha256": "cf315b1835377ea7f74b9e54383398d51746acdb6f6e3bd94b9d108a0a817763" + "sha256": "dd524d121c1c713f3a84d7560e3973cea8a448ee3cac6266ca839f37ed3f13f3" }, { "bytes": 874, @@ -3037,10 +3037,10 @@ "sha256": "eb093a67c030ede668cd59cc2a391a5eeda660c824837df6b295b7aa4e43376a" }, { - "bytes": 1269, + "bytes": 1333, "mode": "100644", "path": "apps/synth_desktop/src-tauri/tauri.package.json", - "sha256": "cc963c7e8b0e3d25479b2065e93fae4f7fe77a396c2f87e4ca9110e47bec493b" + "sha256": "73f01b5cf64f68a2f1906d4ffd6a4e7a9678bbf24837740b3fba46eddc9561b6" }, { "bytes": 2737, @@ -3625,10 +3625,10 @@ "sha256": "ad4cc64383218f3a352408a8f886df1bef808acfbc1aa65782f6861f80bdbc68" }, { - "bytes": 50648, + "bytes": 50691, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/bridge/types.ts", - "sha256": "3dd68b448d6fa3ca1163243faa3b6e0c0d5cde2b7c080a3041fab9baf4174888" + "sha256": "960e54ad12b555b79282195cb1629924b02bc7d05e5f41a92e41e17e5575ee2c" }, { "bytes": 10320, @@ -3697,10 +3697,10 @@ "sha256": "2fad9a83cd0b6aff0461106dd00fdaa3695608d89a4d9d33de7deb3b2c8a38f4" }, { - "bytes": 7735, + "bytes": 9885, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/components/ChatgptCodexSubscriptionCard.tsx", - "sha256": "be0ca980bf1beb053d3fca6000529da8f66ed8dd790c983e247973a725003ef8" + "sha256": "3c4196e5fffb84fa314a5200f33d7ddbb330ef79dc58c6f7b10946e3f33dd54f" }, { "bytes": 10392, @@ -3924,6 +3924,12 @@ "path": "apps/synth_desktop/src/renderer/src/components/OnDeviceModelsSettings.tsx", "sha256": "3f256f7bea7159bcbede5bf73b5ac0a9b32e3c82f78ac183cc9ac95a028573fd" }, + { + "bytes": 3446, + "mode": "100644", + "path": "apps/synth_desktop/src/renderer/src/components/OpenRouterSetupCard.tsx", + "sha256": "c79dd223abcc303ee4a1904b61ffd3ff15d6177bb0dae39dc8ab357cb93b0c2c" + }, { "bytes": 2621, "mode": "100644", @@ -4039,10 +4045,10 @@ "sha256": "d661ff70db1aa19a6920d39dbd91cd0e065592d3a34f62804318ec5585151e01" }, { - "bytes": 28666, + "bytes": 28758, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/components/SettingsPage.tsx", - "sha256": "ffa275ec0aaa98446626ae53f1b3eaf82aa92cdd51df30841bf40cf14067e174" + "sha256": "eb236c78d3417922c41db8d03e6f193bc6f00a5bbc7b86a15f5c6d269c5e774c" }, { "bytes": 32778, @@ -4621,10 +4627,10 @@ "sha256": "5393bae999969f16323f3ce32bda11c072ed32b909400cfc54fbc6b58b39f993" }, { - "bytes": 71315, + "bytes": 71519, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/runtime/desktopBridge.ts", - "sha256": "c070ead99014105ea85c73456304cbfa6d21b9e78af90bea11941a8623dbb79f" + "sha256": "04e680cf34e16d78165825added7404c4dd9bd1706e83326ca97459303234dfb" }, { "bytes": 1117, @@ -4675,22 +4681,22 @@ "sha256": "036fcf289f475afbc5f2f1efae2165b154aa8d7e35c9675a580100c136f61d43" }, { - "bytes": 11719, + "bytes": 12116, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/runtime/modelCapabilities.ts", - "sha256": "9c599d540a66621950d03a3adb9a8de910964ff7ded4519c8fe24f8fafe7e1d9" + "sha256": "8d54ac93caa364548398917e69f641536de3b04c088aca48505ce660494fc965" }, { - "bytes": 5354, + "bytes": 5472, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/runtime/modelCatalog.ts", - "sha256": "6c2d24e1470803322cfb3a2825af2ed3c7dcca9e55062885df408e749d151e1c" + "sha256": "c5147bdf09a33c76748e55b4e4b6211321086afcdc38d55cfbe419809b6ea7a0" }, { - "bytes": 1746, + "bytes": 1839, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/runtime/modelPerformanceLabels.ts", - "sha256": "690b3e978abc9cfe3b7657d1a570d86b000a62d72f5440d5dd350a4a36d2e441" + "sha256": "18406132b63bd04aefdbdd9070be7a6ba441ae745273ea1a7c9dbb3f243fe76c" }, { "bytes": 433, @@ -4705,10 +4711,10 @@ "sha256": "6241545b3058f07d3e0d854f0e312b5e111031956c24ba8848fa1bae2c5e0e99" }, { - "bytes": 11098, + "bytes": 11320, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/runtime/nativeCodex.ts", - "sha256": "d2542ade03b05c0bf172864a3cd2ca3df6cb05411ad0768d8b6c3a53e3b12d55" + "sha256": "3e92862dba55f019d15c44a490573b7339356b16336de160857754a5739fd795" }, { "bytes": 5206, @@ -4903,10 +4909,10 @@ "sha256": "4b7e12a6496187e9a657b495b25fd3625a301d78afebcccd94e16711a569abff" }, { - "bytes": 92358, + "bytes": 92575, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/runtime/sessionView.ts", - "sha256": "52d02c2e18994d52247814fafe7e7641366fcd6881516ee9acb2c61e4e55c2e6" + "sha256": "03ee5e98777c0221a75ef69e6aee99e687c4e7b8310a8e86876ef49cccd78f5a" }, { "bytes": 166, @@ -5053,10 +5059,10 @@ "sha256": "d73cfb7c340ea6da90f946d1a540ac7c7f575d4b75909099cc249195197716fd" }, { - "bytes": 19097, + "bytes": 19647, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/types/landing.ts", - "sha256": "3a423525c182141cc2c940a72a6d516fe42b17ff31302f99a2325dd8dcbd167f" + "sha256": "84dd73676d10d00f1a9407586498e90cf59cfa8df87142dfd9ab9311ad27923e" }, { "bytes": 1911, @@ -5172,6 +5178,180 @@ "path": "docs/sft_tinker_base_models.toml", "sha256": "079255628bef49ec64d64c8cc7402d7153382476b40529e30fd654d710b0565d" }, + { + "bytes": 278, + "mode": "100644", + "path": "examples/runebench/.gitignore", + "sha256": "e8895bc3cd0a6ce7934c08eeadf3293e2df9abc5f4484cb903be476173bf1767" + }, + { + "bytes": 832, + "mode": "100644", + "path": "examples/runebench/Dockerfile", + "sha256": "924ac39db152c5864e5a3f436d3c829d0e79f35fa1ef16d3c56d73de2cf68752" + }, + { + "bytes": 1066, + "mode": "100644", + "path": "examples/runebench/LICENSE.rs-sdk", + "sha256": "d82581c022eba07df3d8abcbaab32f8e43a5a8adc99690925d4843ed724e9f1b" + }, + { + "bytes": 1383, + "mode": "100644", + "path": "examples/runebench/PROMPT.md", + "sha256": "6dd8f878809f4e6f7ba9fb73dcea542df81283458fe18881f9aeb04a53fe4c4c" + }, + { + "bytes": 3833, + "mode": "100644", + "path": "examples/runebench/README.md", + "sha256": "0b51884e15fdda667d0bbfdf5456cdd54b7f4b4f3d8ed57b52cd8f97db84b214" + }, + { + "bytes": 606, + "mode": "100644", + "path": "examples/runebench/THIRD_PARTY.md", + "sha256": "c505b1a5bb5a6670f59c936bb7f4c4d8bc11e018155e3db9722edca1abe644bb" + }, + { + "bytes": 338, + "mode": "100644", + "path": "examples/runebench/build_deps.mjs", + "sha256": "f9f82075d8b128126d4488285e0a1701233d8dd541cdb74be2ca3e5d65f53bf0" + }, + { + "bytes": 2359, + "mode": "100644", + "path": "examples/runebench/build_traces.py", + "sha256": "03013ab29c987eea71c3950a3f9bdb8e76a253f9468b096e54a0f4c8714c12ab" + }, + { + "bytes": 4231, + "mode": "100644", + "path": "examples/runebench/build_web.mjs", + "sha256": "5fcee4dfaaecbaea07fe816b25329b7a9a9534b32cdabdb5c7a848fa8473bdd0" + }, + { + "bytes": 376, + "mode": "100644", + "path": "examples/runebench/compose.yaml", + "sha256": "6090b4bc3afb1955e41aa792c964d79e611b2da67b379bbaf79034e5557e14f6" + }, + { + "bytes": 134, + "mode": "100644", + "path": "examples/runebench/docker-config/config.json", + "sha256": "d86931ace37a780a1e0972436142aca0a214e33d3088f26bea7b3c9e85cb3e38" + }, + { + "bytes": 3672, + "mode": "100644", + "path": "examples/runebench/engine-hook.ts", + "sha256": "e2888b17bd7f5cee895fef474dd687ba9d22b86b1a3fadb706c7b4c96f691197" + }, + { + "bytes": 818, + "mode": "100644", + "path": "examples/runebench/entrypoint.sh", + "sha256": "cd07ff888a0fa949e948165b8eff4a0e839942c6d07682d5b5e82dd81bb32eaa" + }, + { + "bytes": 1569, + "mode": "100644", + "path": "examples/runebench/evidence_api.py", + "sha256": "b7cd5182a6d018e8f5c2f00a1368743b5979cbce917cbd80edaab0f69863763d" + }, + { + "bytes": 2242, + "mode": "100644", + "path": "examples/runebench/example.sh", + "sha256": "54b330b90b8961cf89c2ff40b91d5269640cc7f10900c9a97ca3a153acffec9a" + }, + { + "bytes": 825, + "mode": "100644", + "path": "examples/runebench/manifest.json", + "sha256": "6822ab6826cbefe974f052b00b81bb1cce5330052716915d7e399c7c174e8246" + }, + { + "bytes": 7191, + "mode": "100644", + "path": "examples/runebench/observe.ts", + "sha256": "76f6cd849c028b47ee3b00e6673eb792e69f945a19350b6ed25fa2f39edc5ef7" + }, + { + "bytes": 908, + "mode": "100644", + "path": "examples/runebench/provision.ts", + "sha256": "0d82a91098b1940d41a51faa7e7c191a00b9c5b8e00117bf26eea87f07940906" + }, + { + "bytes": 11868, + "mode": "100644", + "path": "examples/runebench/query.py", + "sha256": "ed5c4415bcc0f4a315d7e066015dcff7f6577331f463e123851d6c900bb6ca20" + }, + { + "bytes": 35172, + "mode": "100644", + "path": "examples/runebench/requirements.lock", + "sha256": "6c1c0bfb8140354949213be5338f3ccfc3cb7701109fcdb6cfffa722802b0590" + }, + { + "bytes": 24, + "mode": "100644", + "path": "examples/runebench/requirements.txt", + "sha256": "3943bc174edbfd5309a7a2fba82b7158247a7b44836ecaeaaa918cc6c3951829" + }, + { + "bytes": 14423, + "mode": "100644", + "path": "examples/runebench/run.py", + "sha256": "71fa8ce1fb9c31e02f8e9fd2a73726a13eeb134c88a57c3f514ab4b877f14896" + }, + { + "bytes": 3744, + "mode": "100644", + "path": "examples/runebench/sample.py", + "sha256": "2cc18b51460d6e50b1a53581fc3946dd24b352f3a0b3fc52b94cd4d014cce738" + }, + { + "bytes": 1528667, + "mode": "100644", + "path": "examples/runebench/sample.tar.gz", + "sha256": "d2d2da454fce87bb827fc927020824a717fb4c996f6b001ab2d4231ac11fbca6" + }, + { + "bytes": 22584, + "mode": "100644", + "path": "examples/runebench/save-generator.ts", + "sha256": "d872ba6447b2858b101a581ca27929fb9372ebe6627f312b2b48ce82cda2d8b1" + }, + { + "bytes": 716, + "mode": "100644", + "path": "examples/runebench/scenario.json", + "sha256": "9d5e48309804b99a56e15e5c5f0c757cf1343fa87dc9b52abaeefea678b4d364" + }, + { + "bytes": 6374, + "mode": "100644", + "path": "examples/runebench/serve.py", + "sha256": "e86f2aeaf1e8b397ad6470da86c01cb56a3610db0ad079887646176f8af809ab" + }, + { + "bytes": 1041, + "mode": "100644", + "path": "examples/runebench/smoke.ts", + "sha256": "7398e1ec6913fd68734c3efa23475c3925426b6a1e24e23cabe12acec4ee50ba" + }, + { + "bytes": 36843, + "mode": "100644", + "path": "examples/runebench/viewer.tsx", + "sha256": "5d06491437e929dd873cec5a986df073094811533a27ef3a4e04e2e60c5b5c2d" + }, { "bytes": 9, "mode": "100644", @@ -5263,16 +5443,16 @@ "sha256": "674d6b0ee1334dcf1fd4718c85fdd08117a2f05c998231ca258e8babfdbe9373" }, { - "bytes": 108191, + "bytes": 112384, "mode": "100644", "path": "package-lock.json", - "sha256": "64e9ef6e12cf3a6019c8bad59bd65e45ad58ab3d033b079acf75baa3431fceab" + "sha256": "b2f78899106c61bc0247d50c4b3ccdf4c1429be55eacf32e355be85c3513cfaf" }, { - "bytes": 1238, + "bytes": 1270, "mode": "100644", "path": "package.json", - "sha256": "fb38f8c061db2f13694328fae488ca01301f5b4adbcb7d91dddc9837d64816e2" + "sha256": "ae0e2b5b3876da4f7e807e871ede5ad4017e679c64569dab9ef36e386f7e1039" }, { "bytes": 190, @@ -5683,16 +5863,16 @@ "sha256": "fbc7b9a1cda41563685f2fd9a7e8bd5234fef2e0c54a174efc92342329885c34" }, { - "bytes": 5406, + "bytes": 5261, "mode": "100644", "path": "packages/workshop-visuals/components/agent_trace.v1/EvidenceClient.tsx", - "sha256": "9dfe893172da70cc11c91c39601bd9445847c8ae6c6edb829cd4b747232a7543" + "sha256": "9713b3eda24ba68baecff807be37b2f3f9a84e67933e4611e3be8c6dc26b8188" }, { - "bytes": 2823, + "bytes": 2900, "mode": "100644", "path": "packages/workshop-visuals/components/agent_trace.v1/README.md", - "sha256": "a1eb265adc6292d5ffcda22036d6a1566c2db2a7f595f7a64e552049266730a4" + "sha256": "edeb282612b219932ca217d7983c196236dd9bfe90556af48950104659e0a689" }, { "bytes": 3084, @@ -5724,6 +5904,12 @@ "path": "packages/workshop-visuals/components/agent_trace.v1/archive.ts", "sha256": "0488557de71377d6712043936383ad1e2c81c0db6a7d955d43b46f4cfb979101" }, + { + "bytes": 382, + "mode": "100644", + "path": "packages/workshop-visuals/components/agent_trace.v1/evidenceEndpoint.ts", + "sha256": "721a247f7f61a2d27a459a0bcd24dcf0bc287a41dfc20d9de8e9d5dd84067459" + }, { "bytes": 3222, "mode": "100644", @@ -6984,6 +7170,18 @@ "path": "pnpm-lock.yaml", "sha256": "3077e9ff7f710add160baab1c55df15c6f023069be25736229ddb44d993da52f" }, + { + "bytes": 10926, + "mode": "100644", + "path": "resources/codex/LICENSE", + "sha256": "d17f227e4df5da1600391338865ce0f3055211760a36688f816941d58232d8dc" + }, + { + "bytes": 240, + "mode": "100644", + "path": "resources/codex/NOTICE", + "sha256": "3ce7450e8b97d94d27b8c99175b21f86bd786cea3620e5d6b5a7c41e7ed35cee" + }, { "bytes": 105, "mode": "100644", @@ -6996,6 +7194,12 @@ "path": "rust-toolchain.toml", "sha256": "ebc5360fd3b6b35d0578ec1607d2a671e74fa5c3527a7fee576e15e57eed175d" }, + { + "bytes": 1966, + "mode": "100644", + "path": "scripts/bootstrap-deps.sh", + "sha256": "08fb3c0fc24f691c3c2b4a0de0179c0073e2da5bd80b431a99fef74360f5385d" + }, { "bytes": 12405, "mode": "100755", @@ -7009,10 +7213,10 @@ "sha256": "08655706e84ae98c9d9b34626ac3d9c6502698b7d6ab86c4422f5df9ff2acdce" }, { - "bytes": 5564, + "bytes": 5605, "mode": "100755", "path": "scripts/build-tier.sh", - "sha256": "2650c8750b1249e52049c390cede29ea390c7126f42053e8b98b9d7c735dbfb0" + "sha256": "cd6a190bafccd68eede834e1f94a34a9fe55af90b7cbcc1aff62b858c22ef06d" }, { "bytes": 3686, @@ -7123,10 +7327,10 @@ "sha256": "7f2cde7cb9e63e50d23f141e926048a2a0d19fe2452f648ea14233ed72b3f142" }, { - "bytes": 4500, + "bytes": 4866, "mode": "100755", "path": "scripts/install.sh", - "sha256": "99a4b87a956bae3f83734bc32da9b5b53291a881b46be8f27736791f3d561415" + "sha256": "187496966f711e55ac063c2d27bb46248fefdb410596425bace44df7838cc5c9" }, { "bytes": 506, @@ -7164,6 +7368,12 @@ "path": "scripts/laguna/test.sh", "sha256": "7ea8973bcbe4886603b9b43686cf0baaf0ed23e92d774e22e70fe195db8b3c45" }, + { + "bytes": 519, + "mode": "100644", + "path": "scripts/local-toolchain-env.sh", + "sha256": "8cc9688300a760706a3379516b4b78bb5fa703732f35ce5cf792dc9fb5224964" + }, { "bytes": 654, "mode": "100644", @@ -7218,6 +7428,12 @@ "path": "scripts/seal-adhoc-trace-import.sh", "sha256": "4cdfbcad58afb4bc1f3f4c9cf8640baddd1caae4d9011d7b6eaa189ce42117c2" }, + { + "bytes": 1033, + "mode": "100644", + "path": "scripts/stage-codex-runtime.sh", + "sha256": "b80c76ac53b44900310a2c138cdf3a4b4c8fcc4beb5aa0c6d46a745e7debd0f3" + }, { "bytes": 2813, "mode": "100755", @@ -7267,10 +7483,10 @@ "sha256": "5cb03b8ce312bacc444102bf05403214746b0ea9d7ced895e9d0b0ab3c85276d" }, { - "bytes": 2641, + "bytes": 2709, "mode": "100755", "path": "scripts/workshop.sh", - "sha256": "296e740b6451377afac1fd1bbb75b75f9a21d32d1f1810f7ed8d76c829bc2c38" + "sha256": "0cf2b85a86afb37129f384928db5a43dc75ccb6d09d85399940abecfaa9e7a08" }, { "bytes": 1843, @@ -7687,10 +7903,10 @@ "sha256": "a45a6a8cc94c53e97bf2dc719e6172ad10ae9fea1091559456396e8fa1b244ea" } ], - "policySha256": "f5ef8fbb4560ee9e401c26c94da6f6189732be76291915027bd7ee3f5f5684dd", + "policySha256": "d8f9d3188ed10080a0bf93da6ff1ebdf54063198ffd6cc384acf4de4c97f040a", "schema": "workshop.public-export-manifest.v1", "source": { - "commit": "7ef1b6fb69770e997e8e62c6d9cacffbd26d06b5", - "tree": "76ea0cabbe5a32ded58158131ae1c1e3e5a6e4b8" + "commit": "14a09348c694870ee44ced312e6eda35311a9e53", + "tree": "488e5a8b4b9d943d32c1858872323714c14257c8" } } diff --git a/README.md b/README.md index 3be69a70c..b32ff5001 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,32 @@ artifacts, container evaluations, and optimization workflows. Official downloads and checksums: [usesynth.ai/download](https://www.usesynth.ai/download). Product documentation: [docs.usesynth.ai](https://docs.usesynth.ai). +## ChatGPT, Astra, and OpenRouter + +After building, open **Settings → Models → ChatGPT subscription** and complete +the browser login. If the callback port is occupied, paste the full redirect URL +into the manual callback field. Cancel abandons that attempt. “Connected” means +signed in, not guaranteed access to every model; reconnect if the session expires. + +Select **ChatGPT → GPT-6 Astra** (`gpt-6-astra`) to use your subscription. +Alternatively, use **OpenRouter → API credits** in Settings to select an absolute +path to a private env file containing `OPENROUTER_API_KEY=your-key`. Restrict it +with `chmod 600 /absolute/path/to/private.env`; never commit it or paste the key +into the path field. Preserve existing Synth variables: this env-file setting is +shared. This setup does not import credentials into Keychain. + +Select OpenRouter Astra (`openai/gpt-6-astra`) explicitly. OpenRouter credits are +separate from ChatGPT allowance; failed ChatGPT requests never automatically +switch to a paid API. Key detection does not verify credit or model entitlement. +Astra offers Low through Max reasoning (Medium default), text/image input, a +1,050,000-token context, conservative 250,000-token auto-compaction, and Standard +service. Access still depends on the chosen account/provider. + +Source builds bundle native Codex 0.153.0, with no global Codex or Node required +when launching from Finder. Remove an obsolete `SYNTH_CODEX_BIN` override if the +runtime check fails. These changes apply to builds from this source, not older +downloaded binaries. + ## Install or build locally The v0.10 macOS app is **ad-hoc signed and not Apple-notarized**. It does not @@ -22,14 +48,20 @@ Cloud models and account features require network access and their own credentia From a source checkout on an Apple silicon Mac: ```bash -./scripts/install.sh -./scripts/workshop.sh build-and-run +./scripts/install.sh --bootstrap && ./scripts/workshop.sh build-and-run ``` -Install `uv` and provide Python 3.11 or newer as `python3` on your PATH. Use the -repository's pinned Node and Rust toolchains and the Xcode prerequisites checked -by `./scripts/install.sh --check`. That check reports missing prerequisites -without modifying local configuration. +Bootstrap installs Homebrew if absent, Node 20/npm, Python 3.12, Rust stable, +uv, jq, ripgrep, Git, and locked npm dependencies. Homebrew's +[official interactive installer](https://brew.sh) may request administrator +approval. Apple command-line tools must provide Swift 6+: if missing, the script +opens Apple's installer and asks you to finish it and rerun. Older tools require +an update or selecting Xcode 16+. Apple prompts/licenses are not automated. +Existing `.env`, shell profiles, and the global Rust default are preserved; +the build scripts locate the Homebrew tools without shell configuration. +If prerequisites are already installed, use `./scripts/install.sh` instead. +`./scripts/install.sh --check` checks prerequisites without changing local +configuration; `--dry-run` previews project setup without installing anything. The build fetches exact public Containers, Optimizers, and MLX source revisions into `work/build-sources`. No sibling repositories or release credentials are @@ -48,6 +80,23 @@ The supported source-build entrypoints are [install.sh](scripts/install.sh) and [workshop.sh](scripts/workshop.sh). Private release/test orchestration is not included in this public checkout. +## Try four-agent review + +After installing dependencies, start with the bundled RuneBench recording: + +```bash +bash examples/runebench/example.sh setup && bash examples/runebench/example.sh sample && bash examples/runebench/example.sh review +``` + +Open `http://127.0.0.1:8128/viewer`. Focus one agent, compare a teammate, +inspect messages and unsuccessful actions, and follow source records and game +frames. This recorded sample needs neither Docker nor AI credentials. +The [example guide](examples/runebench/README.md) covers a fresh, free scripted +run using Docker. Give Workshop the [starter prompt](examples/runebench/PROMPT.md) +to guide setup and native visual review. These game-specific views are an +example built with Workshop's shared inspector, not automatic interpretation +of arbitrary logs. + ## Connect an agent through MCP Start Workshop and find its **Data root** in Settings → About. A connection grants diff --git a/apps/synth_desktop/src-tauri/src/codex_oauth.rs b/apps/synth_desktop/src-tauri/src/codex_oauth.rs index 6483ab451..b4fb61c76 100644 --- a/apps/synth_desktop/src-tauri/src/codex_oauth.rs +++ b/apps/synth_desktop/src-tauri/src/codex_oauth.rs @@ -332,6 +332,7 @@ pub struct Manager { pending: Mutex>, failure: StateMutex>, authenticating: AtomicBool, + listener_task: Mutex>>, } impl Manager { @@ -368,6 +369,7 @@ impl Manager { pending: Mutex::new(None), failure: StateMutex::new(None), authenticating: AtomicBool::new(false), + listener_task: Mutex::new(None), } } @@ -376,6 +378,10 @@ impl Manager { if guard.is_some() { bail!("A ChatGPT subscription sign-in is already in progress"); } + if let Some(task) = self.listener_task.lock().await.take() { + task.abort(); + let _ = task.await; + } let verifier = random_urlsafe(32); let state = random_urlsafe(24); let challenge = URL_SAFE_NO_PAD.encode(Sha256::digest(verifier.as_bytes())); @@ -399,16 +405,15 @@ impl Manager { .failure .lock() .expect("OAuth failure state mutex poisoned") = None; - drop(guard); - let mode = match TcpListener::bind(("127.0.0.1", 1455)).await { Ok(listener) => { let manager = self.clone(); - tokio::spawn(async move { manager.accept_callback(listener).await }); + *self.listener_task.lock().await = Some(tokio::spawn(async move { manager.accept_callback(listener).await })); "auto" } Err(_) => "manual", }; + drop(guard); Ok(BeginResult { authorize_url: url.into(), mode: mode.into(), @@ -431,7 +436,8 @@ impl Manager { return; }; let mut bytes = vec![0; 16 * 1024]; - let read = stream.read(&mut bytes).await.unwrap_or(0); + let read = tokio::time::timeout(Duration::from_secs(10), stream.read(&mut bytes)) + .await.ok().and_then(Result::ok).unwrap_or(0); let request = String::from_utf8_lossy(&bytes[..read]); let target = request .lines() @@ -531,13 +537,19 @@ impl Manager { .map(str::to_owned), last_refresh_ms: now, }; + // A cancelled/replaced request must never reconnect an account after + // its token exchange returns. Serialize the check and save with cancel. + let mut current = self.pending.lock().await; + if current.as_ref().map(|value| value.state.as_str()) != Some(pending.state.as_str()) { + bail!("ChatGPT sign-in was cancelled or replaced; no credentials were stored"); + } let _refresh_lock = self.store.lock_refresh()?; self.store.save(&credential)?; *self .failure .lock() .expect("OAuth failure state mutex poisoned") = None; - *self.pending.lock().await = None; + *current = None; self.authenticating.store(false, Ordering::Release); self.status() } @@ -688,6 +700,10 @@ impl Manager { pub async fn cancel(&self) -> Result<()> { *self.pending.lock().await = None; self.authenticating.store(false, Ordering::Release); + if let Some(task) = self.listener_task.lock().await.take() { + task.abort(); + let _ = task.await; + } Ok(()) } diff --git a/apps/synth_desktop/src-tauri/src/model_catalog.rs b/apps/synth_desktop/src-tauri/src/model_catalog.rs index 5f6baba26..1d73fbf4d 100644 --- a/apps/synth_desktop/src-tauri/src/model_catalog.rs +++ b/apps/synth_desktop/src-tauri/src/model_catalog.rs @@ -287,6 +287,16 @@ fn candidates(configured: &[OpenRouterModelConfig]) -> Vec { fn builtin_candidates() -> Vec { vec![ + builtin( + "openrouter-astra", "openai/gpt-6-astra", "GPT-6 Astra", + ModelCatalogCapabilities { + input_modalities: vec!["text".into(), "image".into()], + output_modalities: vec!["text".into()], tools: true, + reasoning_control: ModelCatalogReasoningControl::Effort, + default_reasoning: Some("medium".into()), + max_context_tokens: Some(1_050_000.0), max_completion_tokens: Some(128_000.0), + }, + ), builtin( "openrouter-luna", "openai/gpt-5.6-luna", diff --git a/apps/synth_desktop/src-tauri/src/session/codex/event_pump.rs b/apps/synth_desktop/src-tauri/src/session/codex/event_pump.rs index 76ee8ba44..da5bb41cb 100644 --- a/apps/synth_desktop/src-tauri/src/session/codex/event_pump.rs +++ b/apps/synth_desktop/src-tauri/src/session/codex/event_pump.rs @@ -20,6 +20,10 @@ fn codex_child_path(binary: &Path, inherited: Option<&OsStr>) -> Result( request, persistent, } = spawn; + if matches!(request.model.as_str(), "gpt-6-astra" | "openai/gpt-6-astra") { + let mut probe = Command::new(binary); + probe.arg("--version").kill_on_drop(true); + if let Some(path) = codex_child_path(binary, std::env::var_os("PATH").as_deref())? { + probe.env("PATH", path); + } + let output = tokio::time::timeout(std::time::Duration::from_secs(20), probe.output()) + .await.context("Codex runtime version check timed out; rebuild with the pinned runtime")??; + let version = String::from_utf8_lossy(&output.stdout); + let supported = version.split_whitespace().last().and_then(|v| { + let parts: Vec = v.split('.').map(str::parse).collect::>().ok()?; + Some(parts.as_slice() >= [0, 153, 0].as_slice()) + }).unwrap_or(false); + anyhow::ensure!(output.status.success() && supported, + "Astra requires Codex 0.153.0 or newer. Rebuild Workshop or remove an outdated SYNTH_CODEX_BIN override; no alternate provider was used."); + } if persistent { return spawn_persistent_server(app, binary, session_id, home, request, pump).await; } @@ -431,6 +451,18 @@ async fn read_stdout( if persistence.codex_oauth && method == "turn/failed" { normalize_oauth_failure(&mut params); } + if method == "turn/failed" { + if let Some(record) = persistence.records.read().await.get(&session_id) { + if let Some(object) = params.as_object_mut() { + object.insert("provider".into(), json!(record.provider_title)); + object.insert("model".into(), json!(record.model)); + let message = object.get("message").and_then(Value::as_str) + .or_else(|| object.get("error").and_then(|v| v.get("message")).and_then(Value::as_str)) + .unwrap_or("Request failed; inspect provider details.").to_owned(); + object.insert("message".into(), json!(format!("{} · {}: {}", record.provider_title, record.model, message))); + } + } + } // Some app-server versions close the stream immediately after a // typed final agent message instead of sending `turn/completed`. // `phase: final_answer` is a protocol lifecycle signal, not an @@ -1019,10 +1051,17 @@ fn normalize_oauth_failure(params: &mut Value) { "codex_oauth_reauth_required", "Reconnect ChatGPT subscription in Settings → Models.", ) + } else if lower.contains("model_not_found") || lower.contains("model_not_available") + || lower.contains("not supported") || lower.contains("does not have access") { + ("codex_oauth_model_unavailable", "This model is unavailable to your ChatGPT account or workspace. Choose another ChatGPT model or ask your workspace administrator. No API provider was substituted.") } else { return; }; - *params = json!({"code": code, "message": message}); + // Keep typed turn identifiers and retry/reset metadata for recovery. + if let Some(object) = params.as_object_mut() { + object.insert("code".into(), json!(code)); + object.insert("message".into(), json!(message)); + } } diff --git a/apps/synth_desktop/src-tauri/src/session/codex/home.rs b/apps/synth_desktop/src-tauri/src/session/codex/home.rs index ab4416c19..e07ac2aa9 100644 --- a/apps/synth_desktop/src-tauri/src/session/codex/home.rs +++ b/apps/synth_desktop/src-tauri/src/session/codex/home.rs @@ -915,7 +915,9 @@ pub(crate) fn validate_start(request: &CodexSessionStartRequest) -> Result<()> { } pub(crate) fn model_context_window(model: &str) -> u64 { - if model.to_ascii_lowercase().contains("laguna-xs") { + if matches!(model, "gpt-6-astra" | "openai/gpt-6-astra") { + 1_050_000 + } else if model.to_ascii_lowercase().contains("laguna-xs") { 262_144 } else if model.to_ascii_lowercase().contains("muse-spark-1.2") { 1_048_576 @@ -932,6 +934,7 @@ pub(crate) fn auto_compact_token_limit(request: &CodexSessionStartRequest) -> u6 let requested = request.auto_compact_token_limit.unwrap_or_else(|| { let model = request.model.to_ascii_lowercase(); if model.contains("laguna-s-2.1") + || matches!(model.as_str(), "gpt-6-astra" | "openai/gpt-6-astra") || model.contains("gpt-5.6-luna") || model.contains("muse-spark-1.2") { diff --git a/apps/synth_desktop/src-tauri/src/session/codex/manager.rs b/apps/synth_desktop/src-tauri/src/session/codex/manager.rs index 31f7a878b..d6aeccca1 100644 --- a/apps/synth_desktop/src-tauri/src/session/codex/manager.rs +++ b/apps/synth_desktop/src-tauri/src/session/codex/manager.rs @@ -102,7 +102,16 @@ impl CodexManager { approvals: Arc, ) -> Self { let root = codex_root(); - let binary = PathBuf::from(env::var("SYNTH_CODEX_BIN").unwrap_or_else(|_| "codex".into())); + let binary = env::var_os("SYNTH_CODEX_BIN").map(PathBuf::from).unwrap_or_else(|| { + // Finder launches must not depend on a shell's npm/Node PATH. + let bundled = env::current_exe().ok().and_then(|exe| { + exe.parent()?.parent().map(|contents| contents.join("Resources/runtimes/codex/bin/codex")) + }); + bundled.filter(|path| path.is_file()).unwrap_or_else(|| { + let local = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex"); + if cfg!(debug_assertions) && local.is_file() { local } else { PathBuf::from("codex") } + }) + }); Self::with_paths_and_approvals( crate::session::SessionPersistence::from_core(core), root, diff --git a/apps/synth_desktop/src-tauri/tauri.package.json b/apps/synth_desktop/src-tauri/tauri.package.json index c846bcf57..7f64a9fbf 100644 --- a/apps/synth_desktop/src-tauri/tauri.package.json +++ b/apps/synth_desktop/src-tauri/tauri.package.json @@ -2,6 +2,7 @@ "$schema": "https://schema.tauri.app/config/2", "bundle": { "resources": { + "../../../runtime-distributions/codex": "runtimes/codex", "../../../services/laguna-daemon/laguna_daemon": "services/laguna-daemon/laguna_daemon", "../../../services/victoria-logs": "services/victoria-logs", "../../../packages/workshop-visuals/families": "visuals/families", diff --git a/apps/synth_desktop/src/renderer/src/bridge/types.ts b/apps/synth_desktop/src/renderer/src/bridge/types.ts index d6c592bff..2d0713321 100644 --- a/apps/synth_desktop/src/renderer/src/bridge/types.ts +++ b/apps/synth_desktop/src/renderer/src/bridge/types.ts @@ -412,6 +412,7 @@ export type SynthBackendSettings = { }; export type SynthConfigBridge = { + chooseEnvFile?(): Promise; get(): Promise; /** Rust-owned model projection; this never parses config.toml in the renderer. */ modelCatalog(): Promise; diff --git a/apps/synth_desktop/src/renderer/src/components/ChatgptCodexSubscriptionCard.tsx b/apps/synth_desktop/src/renderer/src/components/ChatgptCodexSubscriptionCard.tsx index ebb876d8d..d7ef1c3fb 100644 --- a/apps/synth_desktop/src/renderer/src/components/ChatgptCodexSubscriptionCard.tsx +++ b/apps/synth_desktop/src/renderer/src/components/ChatgptCodexSubscriptionCard.tsx @@ -22,10 +22,17 @@ export function oauthErrorMessage(reason: unknown): string { export function ChatgptCodexSubscriptionCard() { const [status, setStatus] = useState(EMPTY); const [busy, setBusy] = useState(false); + const [submitting, setSubmitting] = useState(false); + const [awaitingCallback, setAwaitingCallback] = useState(false); const [manual, setManual] = useState(false); const [redirectUrl, setRedirectUrl] = useState(""); const [error, setError] = useState(null); const pollRef = useRef(null); + const generation = useRef(0); + const stopPolling = () => { + if (pollRef.current != null) window.clearInterval(pollRef.current); + pollRef.current = null; + }; const publish = (next: CodexOauthStatus) => { setStatus(next); @@ -33,53 +40,86 @@ export function ChatgptCodexSubscriptionCard() { }; useEffect(() => { - void bridges.codexOauth?.status().then(publish).catch(() => setStatus(EMPTY)); - return () => { if (pollRef.current != null) window.clearInterval(pollRef.current); }; + const attempt = generation.current; + void bridges.codexOauth?.status().then(next => { if (attempt === generation.current) publish(next); }).catch(() => undefined); + return () => { ++generation.current; stopPolling(); }; }, []); const connect = async () => { + const attempt = ++generation.current; + stopPolling(); setBusy(true); + setAwaitingCallback(false); + setRedirectUrl(""); setError(null); try { const begin = await bridges.codexOauth!.begin(); + if (attempt !== generation.current) return; + setAwaitingCallback(true); setManual(begin.mode === "manual"); + const deadline = Date.now() + 300_000; + let polling = false; if (pollRef.current != null) window.clearInterval(pollRef.current); pollRef.current = window.setInterval(() => { + if (polling || attempt !== generation.current) return; + if (Date.now() >= deadline) { + ++generation.current; stopPolling(); setBusy(false); setAwaitingCallback(false); + setError("ChatGPT sign-in timed out. Start over to try again."); + void bridges.codexOauth?.cancel().catch(() => undefined); return; + } + polling = true; void bridges.codexOauth!.status().then((next) => { + if (attempt !== generation.current) return; publish(next); if (next.state === "ready" && pollRef.current != null) { window.clearInterval(pollRef.current); pollRef.current = null; setBusy(false); + setAwaitingCallback(false); } else if (next.state === "refresh_failed" && pollRef.current != null) { window.clearInterval(pollRef.current); pollRef.current = null; setBusy(false); + setAwaitingCallback(false); setError(next.guidance); } - }); + }).catch(reason => { + if (attempt !== generation.current) return; + stopPolling(); setBusy(false); setAwaitingCallback(false); + setError(oauthErrorMessage(reason)); + }).finally(() => { polling = false; }); }, 750); } catch (reason) { + if (attempt !== generation.current) return; setBusy(false); + setAwaitingCallback(false); setError(oauthErrorMessage(reason)); } }; const completeManual = async () => { + const attempt = ++generation.current; + stopPolling(); + setSubmitting(true); setBusy(true); setError(null); try { - publish(await bridges.codexOauth!.completeManual(redirectUrl)); + const next = await bridges.codexOauth!.completeManual(redirectUrl); + if (attempt !== generation.current) return; + publish(next); + setAwaitingCallback(false); setRedirectUrl(""); setManual(false); } catch (reason) { + if (attempt !== generation.current) return; setError(oauthErrorMessage(reason)); } finally { - setBusy(false); + if (attempt === generation.current) { setBusy(false); setSubmitting(false); } } }; const disconnect = async () => { + ++generation.current; stopPolling(); setAwaitingCallback(false); setBusy(true); setError(null); try { publish(await bridges.codexOauth!.disconnect()); } @@ -88,6 +128,7 @@ export function ChatgptCodexSubscriptionCard() { }; const cancel = async () => { + ++generation.current; setSubmitting(false); setAwaitingCallback(false); setManual(false); setRedirectUrl(""); if (pollRef.current != null) window.clearInterval(pollRef.current); pollRef.current = null; await bridges.codexOauth?.cancel().catch(() => undefined); @@ -95,6 +136,7 @@ export function ChatgptCodexSubscriptionCard() { }; const restart = async () => { + ++generation.current; setSubmitting(false); setAwaitingCallback(false); if (pollRef.current != null) window.clearInterval(pollRef.current); pollRef.current = null; await bridges.codexOauth?.cancel().catch(() => undefined); @@ -119,7 +161,7 @@ export function ChatgptCodexSubscriptionCard() {

{status.guidance}

-

Uses your Codex allowance, not API credits or Platform API access.

+

Uses your Codex allowance, not API credits or Platform API access. Connected means signed in; model access still depends on your account and workspace. No automatic fallback to a paid API provider.

@@ -131,14 +173,14 @@ export function ChatgptCodexSubscriptionCard() {
{manual ?
setRedirectUrl(event.target.value)} placeholder="http://localhost:1455/auth/callback?code=…&state=…" /> - +
: null} {error ?
Couldn’t re-sync ChatGPT. {error} Use Start over to create a fresh authorization attempt.
: null} {status.canUseModels ?
-
Available in the composerChatGPT subscription · plan allowance
3 models
+
Models in the composer · access depends on your accountChatGPT subscription · plan allowance
4 models
- {[["GPT-5.6 Sol", "gpt-5.6-sol", "Fast iteration"], ["GPT-5.6 Luna", "gpt-5.6-luna", "Everyday coding"], ["GPT-5.6 Terra", "gpt-5.6-terra", "Deep reasoning"]].map(([name, id, fit]) =>
{name}{fit}{id}
)} + {[["GPT-6 Astra", "gpt-6-astra", "Complex work · account access required"], ["GPT-5.6 Sol", "gpt-5.6-sol", "Fast iteration"], ["GPT-5.6 Luna", "gpt-5.6-luna", "Everyday coding"], ["GPT-5.6 Terra", "gpt-5.6-terra", "Deep reasoning"]].map(([name, id, fit]) =>
{name}{fit}{id}
)}
: null} diff --git a/apps/synth_desktop/src/renderer/src/components/OpenRouterSetupCard.tsx b/apps/synth_desktop/src/renderer/src/components/OpenRouterSetupCard.tsx new file mode 100644 index 000000000..3b993d830 --- /dev/null +++ b/apps/synth_desktop/src/renderer/src/components/OpenRouterSetupCard.tsx @@ -0,0 +1,47 @@ +import { useEffect, useState } from "react"; +import { bridges } from "../runtime/desktopBridge"; +import { publicError } from "../runtime/publicError"; +import type { SynthBackendSettings } from "../bridge"; +import { SettingsCard } from "./SettingsCard"; + +export function OpenRouterSetupCard() { + const [settings, setSettings] = useState(null); + const [path, setPath] = useState(""); + const [busy, setBusy] = useState(false); + const [message, setMessage] = useState(""); + useEffect(() => { + let active = true; + void bridges.config?.get().then(value => { if (active) { setSettings(value); setPath(value.envFile); } }).catch(error => { if (active) setMessage(publicError(error)); }); + return () => { active = false; }; + }, []); + const save = async () => { + if (!bridges.config || !path.trim()) return; + if (!path.trim().startsWith("/") || path.includes("=") || /[\r\n]/.test(path)) { + setMessage("Choose an absolute file path, not a key or an environment assignment."); + return; + } + setBusy(true); setMessage(""); + try { + // Read current routing before saving; never overwrite it from stale UI state. + const current = await bridges.config.get(); + const next = await bridges.config.update({ profile: current.profile, backendUrl: current.backendUrl, apiKeyEnv: current.apiKeyEnv, envFile: path.trim() }); + setSettings(next); setPath(next.envFile); + window.dispatchEvent(new CustomEvent("synth:account-changed")); + setMessage(next.openrouterApiKeyConfigured ? "OpenRouter key detected. Account credit and model access are checked when you send a message." : "No OpenRouter key found. Add OPENROUTER_API_KEY to this file, then check again."); + } catch (error) { setMessage(publicError(error)); } + finally { setBusy(false); } + }; + return +

Use your own OpenRouter key. This bills OpenRouter credits, not your ChatGPT allowance. A failed ChatGPT request never switches here automatically.

+

Create a private file containing OPENROUTER_API_KEY=your-key, restrict its permissions to your user (for example chmod 600 .env), and select its path below. Never paste a key into this path field or commit the file.

+

The native host reads the file; this setup does not import anything into Keychain. This env-file setting is shared with Synth API routing: preserve any existing variables in the selected file.

+ +
+ + +
+

{settings?.openrouterApiKeyConfigured ? `Key detected · ${settings.openrouterApiKeyFingerprint ?? "configured"}` : "OpenRouter not configured"}

+

{message}

+

Then choose API → OpenRouter → GPT-6 Astra in the composer. Metadata refresh uses the public catalog and does not run a model.

+
; +} diff --git a/apps/synth_desktop/src/renderer/src/components/SettingsPage.tsx b/apps/synth_desktop/src/renderer/src/components/SettingsPage.tsx index e3155853a..22c2f68ab 100644 --- a/apps/synth_desktop/src/renderer/src/components/SettingsPage.tsx +++ b/apps/synth_desktop/src/renderer/src/components/SettingsPage.tsx @@ -29,6 +29,7 @@ import type { DesktopPreferences } from "../preferences"; import { ProviderMark } from "./ProviderMark"; import { bridges } from "../runtime/desktopBridge"; import { ChatgptCodexSubscriptionCard } from "./ChatgptCodexSubscriptionCard"; +import { OpenRouterSetupCard } from "./OpenRouterSetupCard"; import { ContextSettings } from "./ContextSettings"; import { SecretsSettings } from "./SecretsSettings"; import { ProjectSourcesSettings } from "./ProjectSourcesSettings"; @@ -553,6 +554,7 @@ export function SettingsPage({ + diff --git a/apps/synth_desktop/src/renderer/src/runtime/desktopBridge.ts b/apps/synth_desktop/src/renderer/src/runtime/desktopBridge.ts index ace9094e5..17c543241 100644 --- a/apps/synth_desktop/src/renderer/src/runtime/desktopBridge.ts +++ b/apps/synth_desktop/src/renderer/src/runtime/desktopBridge.ts @@ -621,6 +621,10 @@ window.synthSecrets ??= isTauri }; window.synthConfig ??= isTauri ? { + chooseEnvFile: async () => { + const path = await open({ directory: false, multiple: false, title: "Choose a private OpenRouter env file" }); + return typeof path === "string" ? path : null; + }, get: () => fromGenerated(spectaCommands.synthConfigGet()), modelCatalog: () => fromGenerated(spectaCommands.modelCatalogGet()), refreshModelCatalog: () => fromGenerated(spectaCommands.modelCatalogRefresh()), diff --git a/apps/synth_desktop/src/renderer/src/runtime/modelCapabilities.ts b/apps/synth_desktop/src/renderer/src/runtime/modelCapabilities.ts index 15ae796ea..b2fbc46b5 100644 --- a/apps/synth_desktop/src/renderer/src/runtime/modelCapabilities.ts +++ b/apps/synth_desktop/src/renderer/src/runtime/modelCapabilities.ts @@ -77,6 +77,12 @@ const SERVICE_TIER_OPTIONS: ModelKnobOption[] = [ ]; const BUILTIN_MODEL_CAPABILITY_REGISTRY: ModelCapabilitySpec[] = [ + { + targetId: "chatgpt-astra", + target: { kind: "remote", models: ["gpt-6-astra"] }, + knobs: [{ id: "reasoning", label: "Thinking", testId: "reasoning-effort", storageKey: "synth.models.chatgpt-astra.reasoning", defaultValue: "medium", options: LUNA_EFFORT_OPTIONS, turnStartField: "effort" }], + reasoningDisplay: "summary", inputModalities: ["text", "image"], maxContextTokens: 1_050_000 + }, { targetId: "chatgpt-luna", target: { kind: "remote", models: ["gpt-5.6-luna"] }, diff --git a/apps/synth_desktop/src/renderer/src/runtime/modelCatalog.ts b/apps/synth_desktop/src/renderer/src/runtime/modelCatalog.ts index 66f5bf686..7323d4650 100644 --- a/apps/synth_desktop/src/renderer/src/runtime/modelCatalog.ts +++ b/apps/synth_desktop/src/renderer/src/runtime/modelCatalog.ts @@ -16,6 +16,7 @@ export const BROWSER_MODEL_CATALOG: ModelCatalog = { generatedAt: "browser-fixture", diagnostics: [], entries: [ + browserFixtureEntry("openrouter-astra", "openai/gpt-6-astra", "GPT-6 Astra", ["text", "image"], "effort", 1050000), browserFixtureEntry("openrouter-luna", "openai/gpt-5.6-luna", "GPT 5.6 Luna", ["text", "image"], "effort", 272000), browserFixtureEntry("openrouter-laguna-s", "poolside/laguna-s-2.1", "Laguna S 2.1", ["text"], "binary", 262144), browserFixtureEntry("openrouter-muse-spark", "meta/muse-spark-1.2", "Muse Spark 1.2", ["text", "image"], "effort", 1048576), diff --git a/apps/synth_desktop/src/renderer/src/runtime/modelPerformanceLabels.ts b/apps/synth_desktop/src/renderer/src/runtime/modelPerformanceLabels.ts index 53f5bc464..dbc3a71f0 100644 --- a/apps/synth_desktop/src/renderer/src/runtime/modelPerformanceLabels.ts +++ b/apps/synth_desktop/src/renderer/src/runtime/modelPerformanceLabels.ts @@ -2,6 +2,7 @@ import { CHATGPT_LUNA_MODEL, CHATGPT_SOL_MODEL, CHATGPT_TERRA_MODEL, + CHATGPT_ASTRA_MODEL, SYNTH_CLOUD_LAGUNA_S_MODEL, SYNTH_CLOUD_MUSE_SPARK_MODEL } from "../types/landing"; @@ -11,6 +12,7 @@ import { modelCatalogEntryForModel } from "./modelCatalog"; export function performanceTargetId(summary: ModelPerformanceSummary): string | null { if (summary.provider === "local-laguna") return "local-laguna"; if (summary.provider === "openai-codex-oauth") { + if (summary.modelId === CHATGPT_ASTRA_MODEL) return "chatgpt-astra"; if (summary.modelId === CHATGPT_LUNA_MODEL) return "chatgpt-luna"; if (summary.modelId === CHATGPT_SOL_MODEL) return "chatgpt-sol"; if (summary.modelId === CHATGPT_TERRA_MODEL) return "chatgpt-terra"; diff --git a/apps/synth_desktop/src/renderer/src/runtime/nativeCodex.ts b/apps/synth_desktop/src/renderer/src/runtime/nativeCodex.ts index f5d73a389..12267bab1 100644 --- a/apps/synth_desktop/src/renderer/src/runtime/nativeCodex.ts +++ b/apps/synth_desktop/src/renderer/src/runtime/nativeCodex.ts @@ -70,10 +70,13 @@ export function codexStartRequest( sessionId, workspace, baseUrl: "https://chatgpt.com/backend-api/codex", model: target.model, providerName: "openai-codex-oauth", providerTitle: "ChatGPT subscription (Codex OAuth)", providerEnvKey: "", - autoCompactTokenLimit: autoCompactTokenLimits.luna ?? 250_000, serviceTier, ...approval + autoCompactTokenLimit: target.model === "gpt-6-astra" ? autoCompactTokenLimits.astra ?? 250_000 : autoCompactTokenLimits.luna ?? 250_000, + serviceTier: target.model === "gpt-6-astra" ? "default" : serviceTier, ...approval }; } - const autoCompactTokenLimit = target.model.includes("gpt-5.6-luna") + const autoCompactTokenLimit = target.model === "openai/gpt-6-astra" + ? autoCompactTokenLimits.astra ?? 250_000 + : target.model.includes("gpt-5.6-luna") ? autoCompactTokenLimits.luna ?? 250_000 : target.model.includes("muse-spark-1.2") ? 250_000 diff --git a/apps/synth_desktop/src/renderer/src/runtime/sessionView.ts b/apps/synth_desktop/src/renderer/src/runtime/sessionView.ts index afb99ebc2..29e59b9cf 100644 --- a/apps/synth_desktop/src/renderer/src/runtime/sessionView.ts +++ b/apps/synth_desktop/src/renderer/src/runtime/sessionView.ts @@ -13,6 +13,7 @@ import { CHATGPT_LUNA_MODEL, CHATGPT_SOL_MODEL, CHATGPT_TERRA_MODEL, + CHATGPT_ASTRA_MODEL, SYNTH_CLOUD_LAGUNA_S_MODEL, SYNTH_CLOUD_LAGUNA_XS_B200_MODEL, SYNTH_CLOUD_LAGUNA_XS_H100_MODEL, @@ -105,6 +106,8 @@ export function targetIdToExecutionTarget(targetId: string, adapter: string | nu } switch (targetId) { + case "chatgpt-astra": + return { kind: "remote", provider: "openai-codex-oauth", model: CHATGPT_ASTRA_MODEL, adapter: remoteAdapter }; case "chatgpt-luna": case "chatgpt-sol": case "chatgpt-terra": @@ -165,7 +168,7 @@ export function executionTargetToUiId(target: ExecutionTarget): string { : "synth-cloud-laguna-s"; } if (target.provider === "openai-codex-oauth") { - return target.model === CHATGPT_SOL_MODEL ? "chatgpt-sol" : target.model === CHATGPT_TERRA_MODEL ? "chatgpt-terra" : "chatgpt-luna"; + return target.model === CHATGPT_ASTRA_MODEL ? "chatgpt-astra" : target.model === CHATGPT_SOL_MODEL ? "chatgpt-sol" : target.model === CHATGPT_TERRA_MODEL ? "chatgpt-terra" : "chatgpt-luna"; } if (target.targetId) { if (!modelCatalogEntry(target.targetId)) rememberHistoricalOpenRouterTarget(target, target.targetId); diff --git a/apps/synth_desktop/src/renderer/src/types/landing.ts b/apps/synth_desktop/src/renderer/src/types/landing.ts index d0f9a2c9d..e2b3cde8b 100644 --- a/apps/synth_desktop/src/renderer/src/types/landing.ts +++ b/apps/synth_desktop/src/renderer/src/types/landing.ts @@ -100,6 +100,11 @@ export function resolveDefaultTargetId( ): string { const model = preference.model.toLowerCase(); for (const provider of preference.providers) { + if (provider === "chatgpt" && availability.chatgpt && model === CHATGPT_ASTRA_MODEL) return "chatgpt-astra"; + if (provider === "openrouter" && availability.openrouter && model === CHATGPT_ASTRA_MODEL) { + const astra = targets.find(target => target.modelId === "openai/gpt-6-astra" && target.selectable !== false); + if (astra) return astra.id; + } if (provider === "chatgpt" && availability.chatgpt && model === CHATGPT_LUNA_MODEL) return "chatgpt-luna"; if (provider === "openrouter" && availability.openrouter && model === CHATGPT_LUNA_MODEL) { return targets.find((target) => target.modelId === "openai/gpt-5.6-luna" && target.selectable !== false)?.id ?? "local-laguna"; @@ -432,6 +437,7 @@ export const SYNTH_CLOUD_MUSE_SPARK_MODEL = "meta/muse-spark-1.2"; export const CHATGPT_LUNA_MODEL = "gpt-5.6-luna"; export const CHATGPT_SOL_MODEL = "gpt-5.6-sol"; export const CHATGPT_TERRA_MODEL = "gpt-5.6-terra"; +export const CHATGPT_ASTRA_MODEL = "gpt-6-astra"; export function isOpenRouterTargetId(id: string): boolean { return id.startsWith("openrouter-") || id.startsWith("openrouter:"); @@ -444,6 +450,7 @@ const BUILTIN_EXECUTION_TARGETS: ExecutionTargetOption[] = [ description: "Local · MLX · Metal · usage tracked", group: "local" }, + { id: "chatgpt-astra", label: "GPT-6 Astra", description: "ChatGPT · Codex plan allowance · account access required", group: "subscription" }, { id: "chatgpt-luna", label: "GPT-5.6 Luna", diff --git a/examples/runebench/.gitignore b/examples/runebench/.gitignore new file mode 100644 index 000000000..f3bb397c6 --- /dev/null +++ b/examples/runebench/.gitignore @@ -0,0 +1,19 @@ +.venv/ +.env +.annotation-capability +.run.lock +results/ +trace-v5/ +annotation-evidence/ +frame-cache/ +web/ +swarm.sqlite +swarm-data.json +trace-bindings.json +viewer.built.tsx +visual-request.json +visual-update.json +budget-*.json +__pycache__/ +docker-config/* +!docker-config/config.json diff --git a/examples/runebench/Dockerfile b/examples/runebench/Dockerfile new file mode 100644 index 000000000..ba85bb0ba --- /dev/null +++ b/examples/runebench/Dockerfile @@ -0,0 +1,8 @@ +FROM ghcr.io/maxbittker/rs-agent-benchmark@sha256:baa2f071f19c9e5fffe6c2003cb444a11421af33e1a3df4e666ae5db974411ba +COPY entrypoint.sh observe.ts provision.ts save-generator.ts scenario.json engine-hook.ts smoke.ts /app/ma/ +RUN cp /app/ma/engine-hook.ts /app/server/engine/src/ma-hook.ts && \ + sed -i "/await World.start();/i import { installMaClock } from './ma-hook';\ninstallMaClock(World);" /app/server/engine/src/app.ts +RUN sed -i 's/\&fps=15/\&fps=6/' /app/server/gateway/launch-bot.ts +RUN sed -i "s/protocolTimeout: 30_000/protocolTimeout: 300_000/; s/waitUntil: 'networkidle2', timeout: 60000/waitUntil: 'domcontentloaded', timeout: 300000/" /app/server/gateway/launch-bot.ts +ENV BOT_NAMES="maa mab mba mbb" NODE_TICKRATE=300 NODE_XPRATE=1 GATEWAY_URL=ws://localhost:7780 +ENTRYPOINT ["/bin/bash", "/app/ma/entrypoint.sh"] diff --git a/examples/runebench/LICENSE.rs-sdk b/examples/runebench/LICENSE.rs-sdk new file mode 100644 index 000000000..e4597acb8 --- /dev/null +++ b/examples/runebench/LICENSE.rs-sdk @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Lost City + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/runebench/PROMPT.md b/examples/runebench/PROMPT.md new file mode 100644 index 000000000..09fdb3fcd --- /dev/null +++ b/examples/runebench/PROMPT.md @@ -0,0 +1,22 @@ +Help me try this checkout's `examples/runebench` example. Read its README and +manifest, then do the setup for me without modifying existing projects. + +Start with the bundled recorded sample when available. Open it as a native +Workshop visual using the advertised visual tools, the generated +`visual-request.json`, and this checkout's visual skill. Inspect the rendered +view before saying it works. If native visual tools are unavailable, say so +explicitly; the browser viewer is a separate fallback, not native acceptance. + +Show all four agents, focus one, compare two, inspect a message and an unsuccessful +action if one was recorded, then follow an exact source record and game frame. +Do not invent a failure or message if this sample lacks one. + +Next run `bash examples/runebench/example.sh run` for a fresh 25-second scripted +episode. Check Docker first and ask before installing a system application. +This run needs no AI credentials. Rebuild the review, refresh the same visual, +and explain the recorded outcome with three exact actor/event citations. + +Do not run any paid model policies or silently substitute an API provider. After +the scripted path works, explain the optional OpenRouter mode and its enforced +aggregate budget, then wait for my approval. My ChatGPT login does not authorize +paid game agents. Never read Keychain or paste a key into a tool argument or chat. diff --git a/examples/runebench/README.md b/examples/runebench/README.md new file mode 100644 index 000000000..617993867 --- /dev/null +++ b/examples/runebench/README.md @@ -0,0 +1,75 @@ +# Four agents, one shared world + +Workshop example: **Woodland sprint**, two teams of two scripted actors chopping +trees and communicating. This adapts RuneBench; it is not the Runite Race task +or a claim about model performance. It never connects to official game servers. + +## Start here + +From the Workshop repository after `scripts/install.sh`: + +```bash +bash examples/runebench/example.sh setup +bash examples/runebench/example.sh sample +bash examples/runebench/example.sh review +``` + +Open http://127.0.0.1:8128/viewer. The bundled, checksum-verified sample is a real +September 7 recording: four actors, zero model calls. Reviewing it needs neither +Docker nor provider credentials. It is retained evidence, not a fresh run. + +For a fresh episode, stop the review server with Ctrl-C, run +`bash examples/runebench/example.sh run`, then start `review` again. +`run` is **scripted by default**, four actors, +25 seconds after readiness, three decisions per actor, zero model calls. Image +download and startup take longer than the episode. The public AMD64 game image +is about 4 GB and uses emulation on Apple Silicon. Docker Desktop or OrbStack +must be running; setup does not install or start either without your involvement. +Setup installs an isolated, hash-locked Python environment and ffmpeg if missing. + +The inspector starts on All agents: focus one actor, compare a second, follow +messages and actions, query unsuccessful chops or same-tree attempts, and open +the exact source record and recorded game frame. Recording startup gaps are +labeled. Same-tree attempts are not proof of causal interference. + +Results are retained in `results/`, using a unique directory for each run. The +runner checks all-player readiness, cross-actor capability rejection, authoritative +cutoff, rejection of late actions, and an unchanged cutoff receipt. It stops only +its own example container. A per-checkout lock prevents overlapping runs, and +an occupied controller port fails before launch. Ctrl-C stops the review server. + +## Use from a Workshop conversation + +Give Workshop [PROMPT.md](PROMPT.md). `viewer.tsx` uses Workshop's shared trace +components. `build_web.mjs` also produces `visual-request.json`, the source and +bindings for native visual creation. Treat that generated file as local/private: +it contains a local annotation capability, not a provider credential. Never +commit it or distribute it with the sample. + +## Optional model-driven run + +Only after scripted review works, explicitly authorize the provider budget: + +```bash +bash examples/runebench/example.sh run --provider openrouter --env-file /absolute/private.env +``` + +The file must contain `OPENROUTER_API_KEY=...`; keep it mode 600 and out of Git. +ChatGPT login for the Workshop conversation does not authenticate these game +policies. This example supports Luna/Terra policy IDs, not Astra game policies. +No keys enter Docker and no Keychain importer is used. OpenRouter requests disable +provider fallbacks. Default batch reservations persist with a $5/128-call ceiling; +an unknown-cost call stops further admission. Never rename a batch to evade a cap. +No paid acceptance run is included in this example's verification. + +## Reproducibility and attribution + +`manifest.json` identifies the immutable upstream image and trace schema. +Container adapter code comes from public `synth-containers==0.4.3`, not a sibling +source checkout. Visuals come from this Workshop checkout and its npm lockfile. +No private evals framework is required. `requirements.lock` pins Python packages +and hashes. Game RNG is unseeded; repeats are descriptive, not paired-seed trials. + +Built on [RuneBench](https://github.com/MaxBittker/runebench), +[rs-sdk](https://github.com/MaxBittker/rs-sdk), and LostCity. Upstream code and +game assets retain their respective notices and terms. See `THIRD_PARTY.md`. diff --git a/examples/runebench/THIRD_PARTY.md b/examples/runebench/THIRD_PARTY.md new file mode 100644 index 000000000..18df4d09c --- /dev/null +++ b/examples/runebench/THIRD_PARTY.md @@ -0,0 +1,11 @@ +# Upstream notices + +RuneBench: https://github.com/MaxBittker/runebench (README declares MIT). +rs-sdk: https://github.com/MaxBittker/rs-sdk (MIT). +The save generator is adapted from RuneBench's generator; the game SDK and +LostCity engine/client are consumed from the pinned upstream container, not +redistributed as Workshop-owned code. Their notices remain in that image. + +This is a research emulator, not an official Jagex service. Workshop does not +claim ownership of RuneScape names or game assets. Game-asset terms are separate +from Workshop's source-code license. No official game account is required. diff --git a/examples/runebench/build_deps.mjs b/examples/runebench/build_deps.mjs new file mode 100644 index 000000000..2628a3180 --- /dev/null +++ b/examples/runebench/build_deps.mjs @@ -0,0 +1,5 @@ +import { createRequire } from 'node:module'; +// Resolve from the app's locked Vite dependency, whether npm hoists it or not. +const appRequire = createRequire(new URL('../../apps/synth_desktop/package.json', import.meta.url)); +const viteRequire = createRequire(appRequire.resolve('vite')); +export const { build } = viteRequire('esbuild'); diff --git a/examples/runebench/build_traces.py b/examples/runebench/build_traces.py new file mode 100644 index 000000000..330ba88d7 --- /dev/null +++ b/examples/runebench/build_traces.py @@ -0,0 +1,37 @@ +"""Build V5 authority and shared viewer bindings from existing runs, without inference.""" +import json, sys +from pathlib import Path +HERE = Path(__file__).resolve().parent + +from synth_containers.tracing.adapters.runebench import runebench_projection +from synth_containers.tracing.validation.validator import validate_trace, validate_evidence +from synth_containers.tracing.annotation_store import AnnotationStore +from synth_containers.tracing.projections.rollout_inspector import rollout_inspector_from_sealed +from synth_containers.tracing.projections.visual import visual_from_sealed +from synth_containers.tracing.validation.rehydrate import trace_document_from_payload, evidence_bundle_from_payload + +def build(): + projections = {} + store = AnnotationStore(HERE / 'annotation-evidence') + output = HERE / 'trace-v5' + output.mkdir(exist_ok=True) + for run in sorted((HERE / 'results').iterdir()): + if not (run / 'episode/job_result.json').exists(): continue + document, evidence, packet = runebench_projection(run) + evidence = store.load(document, evidence) + packet = rollout_inspector_from_sealed(document, evidence) + findings = validate_trace(document) + validate_evidence(document, evidence)[0] + errors = [f.to_dict() for f in findings if str(f.severity) == 'error'] + if errors: raise ValueError(errors) + for name, record in [('trace', document), ('evidence', evidence), ('projection', packet)]: + (output / f'{run.name}.{name}.json').write_text(json.dumps(record.to_dict(), separators=(',', ':'))) + projections[run.name] = packet.visual.to_dict() + # Larger Craftax decision capture loads on demand through /annotations. + for key in ['craftax-retained']: + if (output / f'{key}.trace.json').exists(): + doc = trace_document_from_payload(json.loads((output / f'{key}.trace.json').read_text())) + base = evidence_bundle_from_payload(json.loads((output / f'{key}.evidence.json').read_text())) + projections[key] = visual_from_sealed(doc, store.load(doc, base)).to_dict() + (HERE / 'trace-bindings.json').write_text(json.dumps({'traceProjections': projections}, separators=(',', ':'))) + print(json.dumps({'runs': len(projections), 'items': sum(len(p['items']) for p in projections.values())})) +if __name__ == '__main__': build() diff --git a/examples/runebench/build_web.mjs b/examples/runebench/build_web.mjs new file mode 100644 index 000000000..7f4c81232 --- /dev/null +++ b/examples/runebench/build_web.mjs @@ -0,0 +1,42 @@ +import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { resolve, dirname } from 'node:path'; +import { gzipSync } from 'node:zlib'; +import { createHash } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; +import { build } from './build_deps.mjs'; + +const root = dirname(fileURLToPath(import.meta.url)); +const bindings = JSON.parse(readFileSync(resolve(root, 'trace-bindings.json'), 'utf8')); +// Keep sealed traces untouched. Enrich only the rebuildable query projection +// with loopback source links, never absolute filesystem paths. +const raw = JSON.parse(readFileSync(resolve(root, 'swarm-data.json'), 'utf8')); +if (raw.revision !== bindings.swarmData.revision) throw new Error('Query/trace projection revisions differ; rebuild the index explicitly.'); +for (const [name, result] of Object.entries(bindings.swarmData.queries)) { + const originals = raw.queries[name].rows; + result.rows.forEach((hit, index) => { + const original = originals[index]; + if (!original || hit.run_id !== original.run_id || hit.actor !== original.actor || hit.ms !== original.ms) throw new Error('Query row identity mismatch'); + const match = String(original.ref || '').match(/\/(episode\/)?events\.jsonl:(\d+)$/); + if (match) hit.evidenceUrl = `http://127.0.0.1:8128/evidence?run=${encodeURIComponent(hit.run_id)}&scope=${match[1]?'episode':'run'}&line=${match[2]}`; + }); +} +const bytes = Buffer.from(JSON.stringify(bindings)); +// Always compile authored source; do not require a destructive index rebuild +// just to pick up a UI change. +writeFileSync(resolve(root, 'viewer.built.tsx'), readFileSync(resolve(root, 'viewer.tsx'))); +const capability = execFileSync(process.env.RUNEBENCH_PYTHON || resolve(root,'.venv/bin/python'), ['-c', 'from evidence_api import capability; print(capability())'], { cwd: root, encoding: 'utf8' }).trim(); +const data = { traceArchive: { encoding: 'gzip+base64', sha256: createHash('sha256').update(bytes).digest('hex'), data: gzipSync(bytes).toString('base64') }, annotationService: { url: 'http://127.0.0.1:8128/annotations', capability } }; +mkdirSync(resolve(root, 'web'), { recursive: true }); +writeFileSync(resolve(root, 'visual-request.json'), JSON.stringify({template_id:'sourced.visual.v1',title:'RuneBench · four-agent review',content:readFileSync(resolve(root,'viewer.tsx'),'utf8'),bindings:{schemaVersion:'synth.visual-bindings.v1',inputs:[{input:'data',kind:'inline',data}]} }), {mode:0o600}); +// Optional update payload: use only an ID and revision returned by Workshop. +if (process.argv[2]) { + const revision = Number(process.argv[3]); + if (!/^vis_[a-zA-Z0-9]+$/.test(process.argv[2]) || !Number.isSafeInteger(revision) || revision < 1) throw new Error('Expected an existing visual ID and positive revision'); + const request = JSON.parse(readFileSync(resolve(root, 'visual-request.json'), 'utf8')); + delete request.template_id; + writeFileSync(resolve(root, 'visual-update.json'), JSON.stringify({...request,visual_id:process.argv[2],expected_revision:revision}), {mode:0o600}); +} +const result = await build({ stdin: { contents: `import React from 'react'; import {createRoot} from 'react-dom/client'; import Shell from './viewer.built.tsx'; createRoot(document.getElementById('root')).render();`, resolveDir: root, loader: 'tsx' }, bundle: true, write: false, platform: 'browser', format: 'iife', jsx: 'automatic', minify: true, define: { 'process.env.NODE_ENV': '"production"' }, alias: { '@synth/visuals/components/agent_trace.v1': resolve(root,'../../packages/workshop-visuals/components/agent_trace.v1/AgentTraceInspector.tsx'), 'react-dom': resolve(root,'../../node_modules/react-dom'), react: resolve(root,'../../node_modules/react') } }); +writeFileSync(resolve(root, 'web/index.html'), `RuneBench trace review
`); +console.log('Built standalone viewer with shared components and evidence binding.'); diff --git a/examples/runebench/compose.yaml b/examples/runebench/compose.yaml new file mode 100644 index 000000000..0ef1c00f9 --- /dev/null +++ b/examples/runebench/compose.yaml @@ -0,0 +1,14 @@ +services: + ma: + build: . + platform: linux/amd64 + container_name: workshop-runebench-${MA_RUN_ID:?run through example.sh} + init: true + environment: + MA_RUN_ID: ${MA_RUN_ID} + MA_EVIDENCE_KIND: ${MA_EVIDENCE_KIND:-scripted} + shm_size: 2gb + ports: + - "127.0.0.1:8126:8790" + volumes: + - ${MA_ARTIFACTS:?run through example.sh}:/logs/ma diff --git a/examples/runebench/docker-config/config.json b/examples/runebench/docker-config/config.json new file mode 100644 index 000000000..99dd6c42e --- /dev/null +++ b/examples/runebench/docker-config/config.json @@ -0,0 +1 @@ +{"auths":{},"cliPluginsExtraDirs":["/opt/homebrew/lib/docker/cli-plugins","/Applications/Docker.app/Contents/Resources/cli-plugins"]} diff --git a/examples/runebench/engine-hook.ts b/examples/runebench/engine-hook.ts new file mode 100644 index 000000000..f73245de8 --- /dev/null +++ b/examples/runebench/engine-hook.ts @@ -0,0 +1,52 @@ +import {readFileSync,writeFileSync,appendFileSync,renameSync} from 'node:fs'; +import {performance} from 'node:perf_hooks'; +const scenario=JSON.parse(readFileSync(process.env.MA_SCENARIO_FILE||'/app/ma/scenario.json','utf8')); +const logDir=process.env.MA_LOG_DIR||'/logs/ma'; +// Imported in the engine process before World.start(). Never available to policies. +export function installMaClock(world:any) { + let phase='idle', start=0, deadline=0, baseline:any=null, last:any=null, cutoff:any=null; + const atomic=(name:string,value:any)=>{writeFileSync(`${logDir}/${name}.tmp`,JSON.stringify(value));renameSync(`${logDir}/${name}.tmp`,`${logDir}/${name}`)}; + function snapshot(){ + const players=world.players.filter(Boolean); + const actors=Object.fromEntries(scenario.actors.map(a=>{ + const p=players.find((p:any)=>p.username===a.id); + return [a.id,p?{xpTenths:p.stats[8],xp:p.stats[8]/10,x:p.x,z:p.z,level:p.level,loggedIn:true}:null]; + })); + return {tick:world.currentTick,elapsedMs:start?performance.now()-start:0,at:new Date().toISOString(),actors}; + } + function finish(reason:string){ + if(phase!=='running')return; + phase='frozen'; + const scores=Object.fromEntries(scenario.actors.map(a=>[a.id,last?.actors[a.id]&&baseline?.actors[a.id]?(last.actors[a.id].xpTenths-baseline.actors[a.id].xpTenths)/10:null])); + const teams=Object.fromEntries([...new Set(scenario.actors.map(a=>a.team))].map(t=>[t,scenario.actors.filter(a=>a.team===t).every(a=>scores[a.id]!==null)?scenario.actors.filter(a=>a.team===t).reduce((s,a)=>s+scores[a.id],0):null])); + cutoff={phase,reason,rule:'last-complete-engine-tick-at-or-before-deadline',durationMs:deadline-start,freezeElapsedMs:performance.now()-start,baseline,final:last,scores,teams,valid:reason==='deadline'&&Object.values(scores).every(v=>v!==null)}; + atomic('cutoff.json',cutoff); + } + const original=world.cycle.bind(world); + world.cycle=function(){ + if(phase==='frozen')return; + if(phase==='running'&&performance.now()>=deadline){finish('deadline');return;} + original(); + if(phase==='running'){ + const s=snapshot(); + if(performance.now()<=deadline){last=s;appendFileSync(`${logDir}/engine-states.jsonl`,JSON.stringify(s)+'\n');} + else finish('deadline'); + } + }; + return Bun.serve({hostname:'127.0.0.1',port:Number(process.env.MA_CLOCK_PORT??8792),fetch:async req=>{ + const path=new URL(req.url).pathname; + if(path==='/status')return Response.json({phase,remainingMs:phase==='running'?Math.max(0,deadline-performance.now()):0,snapshot:snapshot(),cutoff}); + if(req.method==='POST'&&path==='/arm'){ + if(phase!=='idle')return new Response('Already armed',{status:409}); + const b=await req.json();const seconds=b.duration_seconds; + if(!Number.isInteger(seconds)||seconds<10||seconds>600)return new Response('Duration must be 10–600 seconds',{status:400}); + baseline=snapshot();if(Object.values(baseline.actors).some(v=>!v))return new Response('Players not ready',{status:409}); + // The pinned engine uses level/10 in its XP curve, not modern RS level/7. + if(scenario.actors.some(a=>baseline.actors[a.id].x!==a.position.x||baseline.actors[a.id].z!==a.position.z||baseline.actors[a.id].xp!==980))return new Response('Scenario save mismatch: expected start positions and level-10 Woodcutting (980 XP in this engine)',{status:409}); + start=performance.now();deadline=start+seconds*1000;baseline.elapsedMs=0;last=baseline;phase='running'; + atomic('baseline.json',baseline);return Response.json({phase,durationMs:seconds*1000,baseline}); + } + if(req.method==='POST'&&path==='/stop'){finish('operator-stop');return Response.json(cutoff);} + return new Response('Not found',{status:404}); + }}); +} diff --git a/examples/runebench/entrypoint.sh b/examples/runebench/entrypoint.sh new file mode 100644 index 000000000..94c277231 --- /dev/null +++ b/examples/runebench/entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -euo pipefail +mkdir -p /logs/ma +cd /app +bun-svc run ma/provision.ts +(cd /app/server/engine && exec bun-svc run src/app.ts) >/logs/ma/engine.log 2>&1 & +engine=$! +wait_http() { + for ((i=0;i<600;i++)); do + kill -0 "$engine" || exit 1 + if curl -fsS "$1" >/dev/null 2>&1; then return; fi + sleep 1 + done + echo "Readiness timeout: $1" >&2; exit 1 +} +wait_http http://localhost:8888 +(cd /app/server/gateway && exec bun-svc run gateway.ts) >/logs/ma/gateway.log 2>&1 & +wait_http http://localhost:7780 +idx=99 +for name in $BOT_NAMES; do + Xvfb :$idx -screen 0 800x600x24 -ac >/logs/ma/display-$name.log 2>&1 & + sleep 1 + (cd /app/server/gateway && DISPLAY=:$idx BOT_NAME=$name exec bun-svc run launch-bot.ts) >/logs/ma/client-$name.log 2>&1 & + idx=$((idx+1)) +done +exec bun-svc run /app/ma/observe.ts diff --git a/examples/runebench/evidence_api.py b/examples/runebench/evidence_api.py new file mode 100644 index 000000000..3882dd4d0 --- /dev/null +++ b/examples/runebench/evidence_api.py @@ -0,0 +1,39 @@ +"""Loopback demo adapter for Containers' canonical annotation evidence store.""" +import json +import os +import secrets +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent + +from synth_containers.tracing.annotation_store import AnnotationStore +from synth_containers.tracing.validation.rehydrate import rehydrate_trace, evidence_bundle_from_payload +from synth_containers.tracing.projections.visual import visual_from_sealed + +STORE = AnnotationStore(HERE / 'annotation-evidence') + +def capability(): + path = HERE / '.annotation-capability' + if not path.exists(): + try: + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) + with os.fdopen(fd, 'w') as f: f.write(secrets.token_urlsafe(32)) + except FileExistsError: pass + return path.read_text().strip() + +def sources(run): + if not run or any(c not in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' for c in run): + raise ValueError('Invalid run') + directory = HERE / 'trace-v5' + document = rehydrate_trace(json.loads((directory / f'{run}.trace.json').read_text())) + base = evidence_bundle_from_payload(json.loads((directory / f'{run}.evidence.json').read_text())) + return document, base + +def evidence(run, request=None): + document, base = sources(run) + if request is None: + bundle = STORE.load(document, base) + else: + bundle, _ = STORE.append(document, base, request) + return {'projection': visual_from_sealed(document, bundle).to_dict(), 'evidence_digest': bundle.content_digest} diff --git a/examples/runebench/example.sh b/examples/runebench/example.sh new file mode 100644 index 000000000..9e10b70a9 --- /dev/null +++ b/examples/runebench/example.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail +example_root="$(cd "$(dirname "$0")" && pwd)" +repo_root="$(cd "$example_root/../.." && pwd)" +cd "$example_root" +source "$repo_root/scripts/local-toolchain-env.sh" +export DOCKER_CONFIG="$example_root/docker-config" +case "${1:-help}" in + setup) + command -v uv >/dev/null || { echo 'Run scripts/install.sh --bootstrap first.' >&2; exit 1; } + node build_deps.mjs >/dev/null || { echo 'Run scripts/install.sh first.' >&2; exit 1; } + [[ -x .venv/bin/python ]] || uv venv --python 3.12 .venv + uv pip sync --python .venv/bin/python --require-hashes requirements.lock + if ! command -v ffmpeg >/dev/null; then + command -v brew >/dev/null || { echo 'Install ffmpeg before replaying game frames.' >&2; exit 1; } + brew install ffmpeg + fi + ;; + run) + [[ -x .venv/bin/python ]] || { echo 'Run bash examples/runebench/example.sh setup first.' >&2; exit 1; } + docker info >/dev/null || { echo 'Start Docker Desktop or OrbStack and retry.' >&2; exit 1; } + docker compose version >/dev/null + shift + .venv/bin/python run.py --duration 25 --calls 3 "$@" + .venv/bin/python query.py --build + node build_web.mjs + echo 'Run finished. Start review with: bash examples/runebench/example.sh review' + ;; + sample) + [[ -x .venv/bin/python ]] || { echo 'Run setup first.' >&2; exit 1; } + .venv/bin/python sample.py + .venv/bin/python query.py --build + node build_web.mjs + echo 'Sample ready. Run: bash examples/runebench/example.sh review' + ;; + rebuild) + .venv/bin/python query.py --build + node build_web.mjs + ;; + review) + [[ -f web/index.html ]] || { echo 'No review built yet. Run sample or run first.' >&2; exit 1; } + command -v ffmpeg >/dev/null || { echo 'Install ffmpeg for video frames (brew install ffmpeg).' >&2; exit 1; } + echo 'Open http://127.0.0.1:8128/viewer — Ctrl-C stops only this review server.' + .venv/bin/python serve.py + ;; + *) + echo 'Usage: bash examples/runebench/example.sh setup | sample | run [--scripted] | rebuild | review' + echo 'Default run: four scripted actors, 25 seconds, zero model calls.' + echo 'Optional paid mode: run --provider openrouter --env-file /private/path/.env' + ;; +esac diff --git a/examples/runebench/manifest.json b/examples/runebench/manifest.json new file mode 100644 index 000000000..670c54312 --- /dev/null +++ b/examples/runebench/manifest.json @@ -0,0 +1,23 @@ +{ + "schema": "workshop.example.v1", + "id": "runebench-woodland-sprint", + "upstream": "https://github.com/MaxBittker/runebench", + "sdk": "https://github.com/MaxBittker/rs-sdk", + "image": "ghcr.io/maxbittker/rs-agent-benchmark@sha256:baa2f071f19c9e5fffe6c2003cb444a11421af33e1a3df4e666ae5db974411ba", + "platform": "linux/amd64", + "actors": 4, + "defaultMode": "scripted", + "defaultDurationSeconds": 25, + "traceSchema": "synth.trace.v5", + "containersPackage": "synth-containers==0.4.3", + "viewer": "http://127.0.0.1:8128/viewer", + "visualSource": "viewer.tsx", + "visualDataBuilder": "build_web.mjs", + "sample": { + "file": "sample.tar.gz", + "sha256": "d2d2da454fce87bb827fc927020824a717fb4c996f6b001ab2d4231ac11fbca6", + "run": "20260907T182716Z-scripted", + "kind": "scripted", + "modelCalls": 0 + } +} diff --git a/examples/runebench/observe.ts b/examples/runebench/observe.ts new file mode 100644 index 000000000..744c7710d --- /dev/null +++ b/examples/runebench/observe.ts @@ -0,0 +1,101 @@ +import {BotSDK} from '/app/sdk/index'; +import {BotActions} from '/app/sdk/actions'; +import {appendFileSync,writeFileSync} from 'node:fs'; +import {randomBytes} from 'node:crypto'; +import scenario from './scenario.json'; +scenario.evidence_kind=process.env.MA_EVIDENCE_KIND||'scripted'; +const names=scenario.actors.map(a=>a.id), observers=new Map(), controllers=new Map(); +const tokens=new Map(names.map(n=>[n,randomBytes(24).toString('hex')])); +const busy=new Set(), recorders:any[]=[]; +let latest:any={ready:false,bots:{}}, clock:any={phase:'idle'}, startAt=0, sequence=0, started=false; +const seen=new Set(); +function event(kind:string,actor:string|null,payload:any){ + appendFileSync('/logs/ma/events.jsonl',JSON.stringify({schema_version:'evals.event-stream.v1',kind:'trace.raw',source:'local',occurred_at:new Date().toISOString(),run_id:process.env.MA_RUN_ID||'local-ma',sequence:String(++sequence),actor_id:actor,payload:{kind,elapsedMs:startAt?Date.now()-startAt:0,...payload}})+'\n'); +} +async function engine(path:string,body?:any){ + const r=await fetch('http://127.0.0.1:8792'+path,{method:body?'POST':'GET',headers:{'Content-Type':'application/json'},body:body?JSON.stringify(body):undefined}); + if(!r.ok)throw Error(await r.text());return r.json(); +} +function privateView(name:string){ + const s=latest.bots[name];if(!s)return null; + return {actor:scenario.actors.find(a=>a.id===name),remainingMs:clock.remainingMs,phase:clock.phase,tick:s.tick,player:s.player,skills:s.skills,inventory:s.inventory,nearbyPlayers:s.nearbyPlayers,nearbyLocs:s.nearbyLocs?.slice(0,45),gameMessages:s.gameMessages?.slice(-18),menu:s.menu,dialog:s.dialog}; +} +Bun.serve({port:8790,idleTimeout:120,fetch:async req=>{ + try{ + const path=new URL(req.url).pathname; + if(req.method==='GET'&&path==='/health')return Response.json({ready:latest.ready,names,phase:clock.phase},{status:latest.ready?200:503}); + if(req.method==='GET'&&path==='/state')return Response.json({...latest,clock,scenario}); + if(req.method==='POST'&&path==='/arm'){ + if(started||!latest.ready)return new Response('Not ready or already started',{status:409}); + started=true; + for(const name of names){ + const sdk=new BotSDK({botUsername:name,password:'test',gatewayUrl:'ws://localhost:7780',connectionMode:'control',autoLaunchBrowser:false,autoReconnect:false,actionTimeout:20000}); + await sdk.connect();controllers.set(name,{sdk,bot:new BotActions(sdk)}); + } + const b=await req.json();const result=await engine('/arm',{duration_seconds:b.duration_seconds});scenario.duration_seconds=b.duration_seconds;startAt=Date.now();clock=await engine('/status'); + event('episode.started',null,{scenario,baseline:result.baseline}); + for(const [i,name] of names.entries()){ + const recorder=Bun.spawn(['ffmpeg','-nostdin','-y','-f','x11grab','-framerate','2','-video_size','800x600','-i',`:${99+i}`,'-an','-c:v','libx264','-threads','1','-preset','ultrafast','-crf','27','-pix_fmt','yuv420p','-movflags','+frag_keyframe+empty_moov+default_base_moof',`/logs/ma/${name}.mp4`],{stdout:'ignore',stderr:Bun.file(`/logs/ma/record-${name}.log`)}); + recorders.push(recorder);event('media.started',name,{path:`${name}.mp4`,offsetMs:Date.now()-startAt}); + } + return Response.json({scenario,...result,tokens:Object.fromEntries(tokens)}); + } + if(req.method==='POST'&&path==='/stop'){const result=await engine('/stop',{});return Response.json(result);} + const match=path.match(/^\/actors\/([a-z0-9]+)\/(observation|action)$/); + if(match){ + const [,name,op]=match; + if(!tokens.has(name)||req.headers.get('authorization')!==`Bearer ${tokens.get(name)}`)return new Response('Wrong actor capability',{status:403}); + if(op==='observation'&&req.method==='GET')return Response.json(privateView(name)); + if(op==='action'&&req.method==='POST'){ + clock=await engine('/status'); + if(clock.phase!=='running'||clock.remainingMs<=0)return new Response('Episode closed',{status:409}); + if(busy.has(name))return new Response('Actor busy',{status:409}); + const a=await req.json(); + if(!['say','chop','walk','drop','wait'].includes(a.type))return new Response('Unsupported action',{status:400}); + const c=controllers.get(name)!; + const s=c.sdk.getState(); + if(a.type==='say'&&(typeof a.text!=='string'||a.text.length>80))return new Response('Chat must be at most 80 characters',{status:400}); + if(a.type==='walk'&&(!Number.isInteger(a.x)||!Number.isInteger(a.z)||Math.abs(a.x-s.player.worldX)>32||Math.abs(a.z-s.player.worldZ)>32))return new Response('Walk within 32 tiles',{status:400}); + if(a.type==='drop'&&(!Number.isInteger(a.slot)||!s.inventory.some((i:any)=>i.slot===a.slot&&/logs/i.test(i.name))))return new Response('Only logs may be dropped',{status:400}); + let tree:any; + if(a.type==='chop'){ + tree=a.x!==undefined?s.nearbyLocs.find((l:any)=>/^tree$/i.test(l.name)&&l.x===a.x&&l.z===a.z):c.sdk.findNearbyLoc(/^tree$/i); + if(!tree)return Response.json({success:false,message:'No normal tree at requested location'}); + } + busy.add(name);event('action.started',name,{action:a,target:tree?{x:tree.x,z:tree.z,id:tree.id}:null,privateObservation:privateView(name)}); + try{ + const result=a.type==='say'?await c.sdk.say(a.text):a.type==='walk'?await c.bot.walkTo(a.x,a.z,1):a.type==='chop'?await c.bot.chopTree(tree):a.type==='drop'?await c.sdk.sendDropItem(a.slot):await c.sdk.sendWait(2); + event('action.completed',name,{action:a,result});return Response.json(result); + }finally{busy.delete(name);} + } + } + return new Response('RuneBench MA: /health /state',{status:path==='/'?200:404}); + }catch(e){return Response.json({error:String(e)},{status:500});} +}}); +await Promise.all(names.map(async name=>{ + const sdk=new BotSDK({botUsername:name,password:'test',gatewayUrl:'ws://localhost:7780',connectionMode:'observe',autoLaunchBrowser:false,autoReconnect:true}); + await sdk.connect();observers.set(name,sdk); +})); +let sealed=false; +async function sample(){ + try{ + clock=await engine('/status'); + const snapshots=Object.fromEntries(names.map(name=>[name,observers.get(name)?.getState()??null])); + latest={timestamp:new Date().toISOString(),elapsedMs:startAt?Date.now()-startAt:0,ready:names.every(n=>snapshots[n]?.inGame),bots:snapshots}; + if(clock.phase==='running'){ + appendFileSync('/logs/ma/states.jsonl',JSON.stringify(latest)+'\n'); + for(const [name,s] of Object.entries(snapshots))for(const m of s?.gameMessages??[]){ + if(!m.sender)continue; + const key=`${name}|${m.sender}|${m.tick}|${m.type}|${m.text}`;if(seen.has(key))continue;seen.add(key); + event('message.observed',name,{sender:m.sender,text:m.text,clientTick:m.tick,messageType:m.type,visibility:'actor-observation'}); + } + } + writeFileSync('/logs/ma/latest.json',JSON.stringify(latest)); + if(clock.phase==='frozen'&&!sealed){ + sealed=true;event('episode.frozen',null,{cutoff:clock.cutoff}); + for(const p of recorders)p.kill('SIGINT'); + await Promise.all([...controllers.values()].map(c=>c.sdk.disconnect())); + } + }catch(e){console.error('Observer:',String(e));} +} +await sample();setInterval(sample,1000); diff --git a/examples/runebench/provision.ts b/examples/runebench/provision.ts new file mode 100644 index 000000000..43770c3da --- /dev/null +++ b/examples/runebench/provision.ts @@ -0,0 +1,13 @@ +import { generateSave, Items } from './save-generator'; +import { mkdirSync, writeFileSync } from 'node:fs'; +import scenario from './scenario.json'; +// A clean upstream image has no player directory yet. Create the engine's +// actual save root before the upstream generator chooses its fallback path. +mkdirSync('/app/server/engine/data/players/main', {recursive:true}); +for (const [i,actor] of scenario.actors.entries()) { + const name=actor.id; + if (!/^[a-z][a-z0-9]{1,11}$/.test(name)) throw Error('Invalid bot name'); + await generateSave(name, { position: actor.position, skills:{Woodcutting:10}, inventory:[{id:Items.BRONZE_AXE,count:1},{id:Items.TINDERBOX,count:1}], varps:{281:1000}, appearance:{colors:[i, i<2?4:8,0,0,0]} }); + mkdirSync(`/app/bots/${name}`,{recursive:true}); + writeFileSync(`/app/bots/${name}/bot.env`, `BOT_USERNAME=${name}\nPASSWORD=test\nSERVER=localhost\nSHOW_CHAT=true\n`); +} diff --git a/examples/runebench/query.py b/examples/runebench/query.py new file mode 100644 index 000000000..0138829e5 --- /dev/null +++ b/examples/runebench/query.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Rebuildable swarm index and a read-only query path shared by viewer and analysis.""" +import sys +import argparse, datetime as dt, hashlib, json, sqlite3, re, subprocess +from pathlib import Path +HERE=Path(__file__).resolve().parent +OUT=HERE/'results' +DB=HERE/'swarm.sqlite' +QUERIES={ + 'all':"SELECT id AS run_id, evidence_kind, status, actors, score FROM episodes ORDER BY id DESC", + 'failed_chops':"SELECT run_id, actor, ms, text, ref FROM events WHERE kind='action.completed' AND action_type='chop' AND success=0 ORDER BY run_id,ms", + 'heard_teammate':"SELECT e.run_id,e.actor,e.sender,e.ms,e.text,e.ref FROM events e JOIN actors a ON a.run_id=e.run_id AND a.id=e.actor JOIN actors s ON s.run_id=e.run_id AND s.id=lower(e.sender) WHERE e.kind='message.observed' AND e.actor<>lower(e.sender) AND a.team=s.team ORDER BY e.run_id,e.ms", + 'same_tree':"SELECT a.run_id,a.actor,b.actor AS other,a.ms,a.x,a.z,a.ref FROM events a JOIN events b ON a.run_id=b.run_id AND a.actor=3900: + frames.append({'ms':round(ms),'inventory':[{k:i.get(k) for k in ['slot','id','name','count']} for i in v.get('inventory',[])],'messages':[{k:m.get(k) for k in ['sender','text','tick']} for m in v.get('gameMessages',[])[-4:]]}) + if not baseline: + for m in v.get('gameMessages',[]): + if not m.get('sender'):continue + key=(a,m.get('sender'),m.get('tick'),m.get('text')) + if key in seen:continue + seen.add(key);add_event(a,ms,'message.observed',m,f'{job}/states.jsonl:{n}') + r['events'].sort(key=lambda e:e['ms']);r['samples'].sort(key=lambda s:s['ms']) + r['durationMs']=r['durationMs'] or max([e['ms'] for e in r['events']]+[s['ms'] for s in r['samples']]+[1]) + r['media']={a['id']:{'url':f'http://127.0.0.1:8128/media/{run.name}/{a["id"]}.mp4','offsetMs':next((e['ms'] for e in r['events'] if e['kind']=='media.started' and e['actor']==a['id']),0)} for a in actors if (job/f'{a["id"]}.mp4').exists()} + for actor,media in r['media'].items(): + log=job/f'record-{actor}.log' + m=re.search(r'Duration: N/A, start: ([0-9.]+)',log.read_text(errors='replace')) if log.exists() else None + origin=result.get('cutoff',{}).get('baseline',{}).get('at') + if m and origin: + media['offsetMs']=round(float(m[1])*1000-dt.datetime.fromisoformat(origin).timestamp()*1000) + media['clockBasis']='ffmpeg-first-input-wall-time-minus-engine-baseline' + else:media['clockBasis']='spawn-time-estimate' + runs.append(r) + db.commit();db.close() + metadata={'revision':revision.hexdigest(),'runs':runs,'queries':{k:query(v) for k,v in QUERIES.items()},'querySql':QUERIES} + (HERE/'swarm-data.json').write_text(json.dumps(metadata,separators=(',',':'))) + # Keep raw filesystem references in SQL/data; the pane only needs stable event keys. + def pane_projection(value): + if isinstance(value,dict):return {('timeline' if k=='events' else k):pane_projection(v) for k,v in value.items() if k not in ['ref','source']} + if isinstance(value,list):return [pane_projection(v) for v in value] + return value + for r in metadata['runs']: + for e in r['events']: + ref=e['ref'];file,line=ref.rsplit(':',1) + if file.endswith('events.jsonl'): + scope='episode' if Path(file).parent.name=='episode' else 'run' + e['evidence']=f'http://127.0.0.1:8128/evidence?run={r["id"]}&scope={scope}&line={line}' + pane=pane_projection(metadata) + for r in pane['runs']: + r['timeline']=[{k:v for k,v in e.items() if k not in ['result','target'] and v is not None} for e in r['timeline'] if e['kind'] in ['policy.action','policy.result','action.started','action.completed','message.observed']] + for actor,frames in r['frames'].items(): + compact=[] + for f in frames: + if not compact or f['ms']-compact[-1]['ms']>=14900: + compact.append({'ms':f['ms'],'inventory':[{k:v for k,v in i.items() if k!='id'} for i in f['inventory']],'messages':f['messages'][-2:]}) + r['frames'][actor]=compact + if r.get('cutoff'): + final=r['cutoff']['final'] + for actor,v in final['actors'].items(): + if v:r['samples'].append({'actor':actor,'ms':round(final['elapsedMs']),'xp':r['scores'][actor],'x':v['x'],'z':v['z'],'authority':'engine'}) + bindings=json.loads((HERE/'trace-bindings.json').read_text());bindings['swarmData']=pane + (HERE/'trace-bindings.json').write_text(json.dumps(bindings,separators=(',',':'))) + source=(HERE/'viewer.tsx').read_text() + if len(source.encode())>262144:raise ValueError('Pane exceeds source budget; full evidence remains in SQL') + (HERE/'viewer.built.tsx').write_text(source) + print(json.dumps({'episodes':len(runs),'events':sum(len(r['events']) for r in runs),'revision':metadata['revision'],'queries':{k:len(v['rows']) for k,v in metadata['queries'].items()}})) + +def query(sql): + db=sqlite3.connect(f'file:{DB}?mode=ro',uri=True);db.execute('PRAGMA query_only=ON');db.row_factory=sqlite3.Row + # Hard CPU budget even for user-authored read queries. + steps=0 + def guard(): + nonlocal steps;steps+=1;return 1 if steps>10000 else 0 + db.set_progress_handler(guard,1000) + allowed={sqlite3.SQLITE_SELECT,sqlite3.SQLITE_READ,sqlite3.SQLITE_FUNCTION,sqlite3.SQLITE_RECURSIVE} + db.set_authorizer(lambda op,*args:sqlite3.SQLITE_OK if op in allowed else sqlite3.SQLITE_DENY) + try: + rows=[dict(r) for r in db.execute(sql).fetchmany(1000)] + return {'queryId':hashlib.sha256(sql.encode()).hexdigest()[:12],'sql':sql,'rows':rows,'limit':1000} + finally:db.close() +if __name__=='__main__': + p=argparse.ArgumentParser();p.add_argument('--build',action='store_true');p.add_argument('--sql');p.add_argument('--query',choices=QUERIES);a=p.parse_args() + if a.build:build() + else:print(json.dumps(query(a.sql or QUERIES[a.query or 'all']),indent=2)) diff --git a/examples/runebench/requirements.lock b/examples/runebench/requirements.lock new file mode 100644 index 000000000..3b9f8f24a --- /dev/null +++ b/examples/runebench/requirements.lock @@ -0,0 +1,460 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile examples/runebench/requirements.txt --generate-hashes -o examples/runebench/requirements.lock +annotated-doc==0.0.5 \ + --hash=sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101 \ + --hash=sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb + # via fastapi +annotated-types==0.8.0 \ + --hash=sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7 \ + --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 + # via pydantic +anyio==4.15.1 \ + --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 \ + --hash=sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94 + # via + # httpx + # starlette +certifi==2026.7.22 \ + --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \ + --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55 + # via + # httpcore + # httpx + # synth-containers +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 + # via uvicorn +fastapi==0.141.1 \ + --hash=sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3 \ + --hash=sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1 + # via synth-containers +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via + # httpcore + # uvicorn +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via httpx +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via synth-containers +idna==3.19 \ + --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \ + --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 + # via + # anyio + # httpx + # synth-containers +pydantic==2.13.5 \ + --hash=sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73 \ + --hash=sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08 + # via + # fastapi + # synth-containers +pydantic-core==2.46.5 \ + --hash=sha256:013d6f3483d81e02e7c328831808f336c8596ee33b4bd4026b9ffb1e960b8942 \ + --hash=sha256:03b9666e41e35d8909852ba191a0607520f81b74eaf12ccf8737005dbb313821 \ + --hash=sha256:045ab3b6d308439e32b81cc173bba5b9018bc6ed896afd0c65b3b009b1699af5 \ + --hash=sha256:0bddb4020d8f04175865ccd17eff3040874fc11fb593f424edb452653b4b947c \ + --hash=sha256:0cdbada856a1c69a7624a64d3d9aefe79300bd6ef827b43a4f265010b9b55184 \ + --hash=sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2 \ + --hash=sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc \ + --hash=sha256:15f4a94963c95accac15b7b657bb177d3ad82bb90b0d0526d9a9b85079925db5 \ + --hash=sha256:18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0 \ + --hash=sha256:193375f3548919d3f0b60936ca113ada3e38f264f91b9b8e0508efaad57be931 \ + --hash=sha256:1a353f84de772f423b5ffb11d7ae352fbbef0f446f3c0b0af0f8236d7233606e \ + --hash=sha256:1e449def1945a462c464331254e5a44fca7c3b4f9aedf59ec2f50f8066dd8e25 \ + --hash=sha256:1e5aad1220a1192c42341c8fd4a8686657e73ab2a920c970bdc4de334fe3193d \ + --hash=sha256:200aa3dc9f8d54f0754f43247c0bad0999fdcfbfd2488384dd44f37279271fe6 \ + --hash=sha256:2471fd51c61c610e1dcf7de44d7299283661654d11264ab4802b303368d69c47 \ + --hash=sha256:24922243639cbdac66c75fcb6fd6495a9cb52b213d62f9a0d16f0310b1ff8038 \ + --hash=sha256:28a6a556cd3b6066bea827857f9d9cce027c96f776e512f544a581f9e42161f8 \ + --hash=sha256:2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b \ + --hash=sha256:2cbd9a5eff05e51c447c34dfa4632145b26b09120cf04bd0c871e44c1a5e1c9a \ + --hash=sha256:2d330aaba8621b1edcec8ae2c4050f63b84ccf6d98723a8f212e9684713abf0e \ + --hash=sha256:2d5d76654becf5efd62c9e51c3756c67b49498b0c9a40884934c40807adbd074 \ + --hash=sha256:337639ba62a11acde6ef3aeb08c8ea755f8ef1fe5e513356c0f36a2b0d7568b0 \ + --hash=sha256:347ec774390c87326a2e4929d58d3f7e8763a104d5d35f4cd595a4c952366433 \ + --hash=sha256:356c8368cbc321050b169595683a2e1d63413b1e0e2868b330af9fc14c616d3f \ + --hash=sha256:37ae34309d7bd8c0d61ab839668058f2a7962ea1fc51d105d2db228fe0618034 \ + --hash=sha256:37ea7b83c935e5b0d68c9449b82651accf78a10828b2c02b2f2d9e9496446c21 \ + --hash=sha256:3a3e26b6a8274211bddee2d0e4d0d42778f17a34510f49d2ec44b58abfc41736 \ + --hash=sha256:3aa166e99c4f2985407fb8714aebede877ecb5455cf321b606adca926d30d5a0 \ + --hash=sha256:3d2652072b2d774947ba5cf78a9e59644ac62ee572daf6dd2e1dfe905e15b2b7 \ + --hash=sha256:40375c2d05acec10323e45dfe2077ac44bc74659008614af5069034e2cfc781c \ + --hash=sha256:413a717a410d0c817ef5b786a059415550b3794e1d0c2abffd9efb93a3d9f7b4 \ + --hash=sha256:46c25dda9d092a06c08db76ffe0a197107904d0dfac653f7d5306bbcd6d6119c \ + --hash=sha256:49776eab08766a08dfff7012f8b422dcd7e25e43b316eedf0477c24fcfa84b7c \ + --hash=sha256:4d44cf99ddebf875f9b68cc267aa684c99b7b44fe63ee1cac4ec163807290069 \ + --hash=sha256:4dedce55295becb61921e386b99d4f2706045306e7fa52249a33004c837379fb \ + --hash=sha256:4f8507560a9284e1370bb048ed4282012fbef4e8d109875b95e884d228552061 \ + --hash=sha256:4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29 \ + --hash=sha256:5086029a57366b8cf81b130a43908738095c270c21a8d7f0e8bdfdb89718e2f3 \ + --hash=sha256:52e24eacdb536cade636aa90fb851835222becff8484b7001fdc78cb0290f2aa \ + --hash=sha256:53feb344243bb9510a9dec7bf3cf1b64d88a98af5dc7872a5160465f8b198c8e \ + --hash=sha256:545f26c504b27c3758439a5e6d9349931f0a04f855668d5fe323c89e82300a38 \ + --hash=sha256:54d510bac3ee52247af28ed4bb18a1e799f040ac60fd2bf5ccd4c92f1fbe786f \ + --hash=sha256:5cb482e9e84c851f4e623fe4acc1ced89168cf1fe18f7089db4548c8f5bbb65b \ + --hash=sha256:5e81740c09e310f5aa5cbd3e434a01c154d4bef93241c7877b39f211d2b78ba8 \ + --hash=sha256:5ee239d575f80b08eca11f6e20f90c4c695de7825c67eefe6091fbf20dda648e \ + --hash=sha256:5f194189415698233dd1114a093a9b56e61e2c57e11b469be3b0506f46f0771c \ + --hash=sha256:5f93c5fe914d75fbec9a49209b00da5f08e9e467d69da2b1510c81940cfd10be \ + --hash=sha256:657b40d6240c0a7b6a64b30f22d1e3aa631c7e846c621b0c0f6d1d75e2e15ea6 \ + --hash=sha256:6d30e1a4f138b8951063e9a394752a9179b51da288ffa507b1e659222f4c1793 \ + --hash=sha256:6f7b393a8b3da82f5c1fc0751e6d01ac6c55b93c18226a60bdfba4a724efafd1 \ + --hash=sha256:701b2e04b560eeb4bddf7a25ab8ca476176e34fdbd9a0e18196f0d12d4685f0b \ + --hash=sha256:771cf63ae0b1b50dd22e5f3e3549fab5f3f4ff1635d352a9e1a97fe01c7b2e64 \ + --hash=sha256:79bdfa52f843137045b2d081cc05c120ba6665d29b7559c2c47690906f39279f \ + --hash=sha256:7ac031912d54f3d83ef3b3eb98dfabc1608802e2202263d25957eeed40b94761 \ + --hash=sha256:7b0fc826b16c55e561e5d2a0c5c77b051ba1d92808118c4e4b5390f5e0cf191d \ + --hash=sha256:7c6be839a5a8312626b32029a415644a0846b420bc8b52b95b28cd92da162168 \ + --hash=sha256:816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869 \ + --hash=sha256:82a36973cf8a2ef5406f4fe2edbf8ed0c99629535d959e0b100c76a32535a111 \ + --hash=sha256:837b396ca3d7b74091ca623f6cbd8351bd42d670a79c2683e79fb089f06a2de5 \ + --hash=sha256:850a08d167dde16db8702c274f320c7be9d7da6f6dff2b58b18f9e815bd94f5b \ + --hash=sha256:8816f3d218beb4b787de5c9759c259b8fa61f9dec42dc7811f320a33771778b7 \ + --hash=sha256:892a881d5f68c2b9ea304b7a6c2c60d9343df578a311b0f86b94bc8f1ffe8129 \ + --hash=sha256:895395f8918627b04efb1ad2a4cf605387143300ba03304cd1dfa6d03f5e095e \ + --hash=sha256:8b10e3e8fd7ddc2bd915848a2768e44c15b22936f1cc54c462ad1164deb02655 \ + --hash=sha256:8e24d8f05fa2d28513d94e877e9c75ad66175376209b3977f916e240e623193c \ + --hash=sha256:8feeac04b5794e513e710af2f9c87d49f31a6dc47967bb264a1fed61a8989bec \ + --hash=sha256:9432f3598db432cb51c5b37fdbf29a60fcccc79e30d37a05022776a6bc4ab689 \ + --hash=sha256:976e1128455aa595ea04c79ccfedff1aaeab96ee013fcc916bed120c4f0ad94f \ + --hash=sha256:978e7b97d4824b5be09c69fb70507cbde3b0323fc147332ca40a94d9a6a0ebbf \ + --hash=sha256:97bf8de4d541598c94a59344eeb988a94c08ff76b5723c41f6567ec18c7892ea \ + --hash=sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9 \ + --hash=sha256:9b68938dd5b0c783d88ff8e2dcc69451b5eb936fe212d516b21b9d5567f6d464 \ + --hash=sha256:9c4b71f10dd532fb7a5cbc8f58707779e64f03a258c2bf8bfbaecfcd9970b519 \ + --hash=sha256:9f47b8a949e60f027f0aa0a6f6c7b7e9c55cbf4380d10b344e282fa4e7ab1e1b \ + --hash=sha256:a1dee1b804ff4d11c663636cf15d2ea47e9f79cd56c033fb1cbf08924842a48f \ + --hash=sha256:a2468d93d181667a7abd66e1b64bb9f76f361b0fef8faddf687456453576f5ee \ + --hash=sha256:a2a5e1d0ff29adddc9f6d6821a66302e4493f8ca898b715b6b1182c2c201ea0a \ + --hash=sha256:a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e \ + --hash=sha256:a445486499897b88a7d6c310c88ed64dd37b1b59bfd7ae9107490bbb362f47d6 \ + --hash=sha256:a91c17edf6eea2402cb5457b4c89e99bc5ed1004aa34c4adf1d4258c1a5c22c2 \ + --hash=sha256:ab4b66edffb32d9e951efb3814bd104b8367a7501b81b955cacb5726d897389f \ + --hash=sha256:aca6c767f552b21b10f774aeac128e828eafb796adfa1b666a18bf6321453c3a \ + --hash=sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f \ + --hash=sha256:b10ec717381bdbfafef34607824db4c91de69ff085e4fca3b2af91b4fa17e68a \ + --hash=sha256:b49924c73a235e969511bf2aabdff3beebf9820931f646c80274d5d780010c47 \ + --hash=sha256:b6acfb46a814762367fb7ba0828b0a17d441b92ce249a0e007474c9072662dda \ + --hash=sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0 \ + --hash=sha256:b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4 \ + --hash=sha256:b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d \ + --hash=sha256:bce57638e08ac148e5778cce7feb968307a727d66f8e2274a543d0cf0c9ad6a3 \ + --hash=sha256:c14ad3bdc85ee7f318742c457ca3968a92126d144b15721c759033bfb06296c2 \ + --hash=sha256:c1c43ad4339643d70ebb8124e1305a7dab423001eff58bb41a0f731adbc98355 \ + --hash=sha256:c3471e5c4a949c26ec00a77f01df59096aa9495877de76fd60a980f8ee6be461 \ + --hash=sha256:c583b927a8838dab890706a6fa7573fbb8b70e24000ef9f7238e2d6f6435a5ed \ + --hash=sha256:c76fe65e607be28c7fd4d56fc3c42b1583aa058ce3408b7ad0fd540171d31f9f \ + --hash=sha256:c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5 \ + --hash=sha256:cd5214352ae68f3b5e9af7768bdc5253695ee069675db3480518420b3be881f2 \ + --hash=sha256:cdbb78909f52b981d3b2d56b97328d71eb0b974c36bd77c920123a7ebb192829 \ + --hash=sha256:cdc8b74ecc48c0cb1e9607a05ec4e9e88db60a19ffcc9a1d5f9088ede40c8dc0 \ + --hash=sha256:d0a24b40877af2de4950252be9d21eaf7fb07660f3c2cae1f56c6b599ada5266 \ + --hash=sha256:d22a945598fb91236b4dd793a6e42e4f3dd7740bb5aace5ebd7d4c08d13bb575 \ + --hash=sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290 \ + --hash=sha256:d625a186a65201c23a9e3b8ed9c47e90a026e03256608cc91851c6709096844f \ + --hash=sha256:d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62 \ + --hash=sha256:e64e88d5585bea9ce95861079de72006c7fa6d3df4e3a3b65ba31eb979c15c9f \ + --hash=sha256:e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a \ + --hash=sha256:e7b891faeedeafba41b2983e5001a81b6a915b69544c7e7570d1989ce1c36ac7 \ + --hash=sha256:e80675d75ae2cd14372cb65cad5400d9347a3d3f6c13000183f22dfd027283ed \ + --hash=sha256:e9c134bb666dd54b778b9fc0d2b50cbb7f979b9e3716f26a88c9ab3b6fc1dd0f \ + --hash=sha256:eb7d8d0e5886a89a55d2eef490e272fa965a9d57c6b29a5b5088a7997ec2cad1 \ + --hash=sha256:ecb42011e12ee19cafbc312887cbf3546959fe02fbad44f272d4be5baa997615 \ + --hash=sha256:ef3fbbf161dc9351a2fe0422e51b129f9e97e42385bd0320b309c15f7d287dd8 \ + --hash=sha256:efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3 \ + --hash=sha256:f077d0b97ab11fa7dcc633fca53515f290bca8a8a633e966d5b6d1879d9ed01a \ + --hash=sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff \ + --hash=sha256:f7b0ec93a2893de856652154d73b7ba622f26fa97726487dcac373de5f4c6084 \ + --hash=sha256:fa10ef4112775900e7a0661068635eb67b2ab824fbde764de6e0e21982a93db0 \ + --hash=sha256:fc5d783bd4a2387e97b8a2d5ec781cfb92b3d893bf82370548e99db5915935d3 \ + --hash=sha256:fc8515076c11f3cfdf4fb142dcca0fe384b1230a3b5415458ac84f3e0903ec13 \ + --hash=sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9 + # via pydantic +starlette==1.6.0 \ + --hash=sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c \ + --hash=sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b + # via + # fastapi + # synth-containers +synth-containers==0.4.3 \ + --hash=sha256:d0bab327439a93fc90c77f19c464a14b9f9cefb589ae76ba56bd66b07ea4efa7 \ + --hash=sha256:eaff16ec40b6e2c9a569751f415912178f3aa0ac63396749466ec92e1d734bf5 + # via -r examples/runebench/requirements.txt +typing-extensions==4.16.0 \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 + # via + # anyio + # fastapi + # pydantic + # pydantic-core + # typing-inspection +typing-inspection==0.4.4 \ + --hash=sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47 \ + --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147 + # via + # fastapi + # pydantic +uvicorn==0.52.4 \ + --hash=sha256:73acfee47a0b133c5de13d219492d62d8a31e935f4fe6e41a232451a15379f86 \ + --hash=sha256:f86e41a149d7d05a9969337e3946a9c171c06a5d42680896daaba624aeac8da1 + # via synth-containers +websockets==17.1 \ + --hash=sha256:0014eaff8ad5b3b43feda2279f9d34bf2eaae040720b9fbbb55944b10f40b14d \ + --hash=sha256:00679b7468b4c2b12b0757118174e8eabac56bb2f579a928a104d9554a56e098 \ + --hash=sha256:00bf34b64501e3477e81fc281532ff3cbf4da26633c10b63979d5085d46602d3 \ + --hash=sha256:01dcb47deebc40b38fd4a493b9b9f4d0a704b7bec6f35e4d34085b329abce71a \ + --hash=sha256:020e271205f8ab3406d7a59cd00de6dec722315924411c421bd00642f18bad86 \ + --hash=sha256:0340bbef6bfbe16da888b3983d666a4db4954ac3253c38f13bc7aba0c7db5a2f \ + --hash=sha256:054c28db2dcec0e857e3b705d8c28012613e555b38c765d6a4f75340a4fc06a0 \ + --hash=sha256:073c5c3f7e127041fa9d34a9e29ceefee8c3cafbd267ed2927318f425144380d \ + --hash=sha256:07fa3e7c30e2c577928d359b56bf872a3e0cbcc15553eaa0907c1ee86344b56f \ + --hash=sha256:0c84bdef916556cbe1d5a43b423398be4dd3cba6522b463e53d848578b920695 \ + --hash=sha256:0c863507ada5805517ca6dff1c524dcd42942efe6304dacf06700878398d21a6 \ + --hash=sha256:0c9982938980e086da59f70d05f9418cd143401a601a0faac10fa48f7bb1cd3e \ + --hash=sha256:0de501b7f2db11e83739ac20e2d33d46da4604b829f506c24be80e7def069391 \ + --hash=sha256:0f4f50dfe2cc810fc4e2de979b35e83bf8bb4bccdc6fe472d93762ea7b1d5927 \ + --hash=sha256:1036189bd34b0bc1b10a4679321e2c7968af317efe6e8e4c1c5141c4254fb5bb \ + --hash=sha256:10ecb38ffc05e1841b619d99c725307a223ef9ad58e7b1ed33311d472dc43918 \ + --hash=sha256:12c8e2b25df59755954a04dfa09c990b96691025aaf7eafd19ed6da24b09c18d \ + --hash=sha256:12fe8984a32dbfd084e0603f1a8d740c0180cb85b3174585c54a80d2455a8394 \ + --hash=sha256:17aa424ab61620aad21b36b2240efc87b500cc496e7d0e999a5c2ae99395e886 \ + --hash=sha256:1a20136d61f9ca3a31493732762661fafc2c20e8861930214e21afc6a8a692a2 \ + --hash=sha256:1a5cf4e7bbe3ca499e6a289206cb4fcb7444b09919e129bd517f57d5fa192c13 \ + --hash=sha256:1c7ac77401227212dc6e849182feee50d57cf456ec6329ffd6979c94bb136c5c \ + --hash=sha256:1fce0f43e0d41422e0b2cad6561e1970df22f212f4c7e884967df7cf591b031c \ + --hash=sha256:259f45358c76d3b18489e3e80636cdbe807e05ecf1b10fdf1a779106d23d0c8e \ + --hash=sha256:2786cbd273ab69c22612db8a41229ddf2c158060b17b5928884bf388d07887f3 \ + --hash=sha256:29176d8b429cfa0fa443c473878d37a5c06cfd0cb36b71ba4314accc71e05906 \ + --hash=sha256:2a0162a6372110a5601cb5c9fd826635cedf69f3e110c545dd19774e040b970e \ + --hash=sha256:2a4dc6ef83f4559e0d05f313a375cb38f63c986096a9da99fe94fdd779d313e5 \ + --hash=sha256:2afb58c7ba48b329d56769f8dfd89f394efe587b65ef806bae810a484d6d3608 \ + --hash=sha256:2cce251f3e2469b99b6802b55435bcdd07123b41870f54c87b336183af9d7e68 \ + --hash=sha256:2d72879819f5145a342d0030c418702496c65a4b913ef81f5ae944dd91dd50f6 \ + --hash=sha256:32a2a68d989d6e5b74a9d5095415c51189ebae29fceb7cf2b64a1c0318a81256 \ + --hash=sha256:33e45c7ea38428e740a7f233555d71df0b875cef7fc080acebc9654475e35335 \ + --hash=sha256:34879e19bb0a3c44f9317679435aea5327fac993933a704cbf353bf1234b10c7 \ + --hash=sha256:3546ef55b3a074494106508bc6505c73825970d2d9505f7bf53882b3e88b0d1e \ + --hash=sha256:3709a1ab30b4b922027d22f68d2b61a0656a91680ac894a537624e6be7dd7f7c \ + --hash=sha256:4031152769179ab8dcdeafc7b0e58052a49117560a28671700b47b2c7b717aad \ + --hash=sha256:406b8107943a43ef4649b1e0cb0cdc052bbf08fe1c8905a623c4af9586e5cebb \ + --hash=sha256:416b4bc8789a1865a3ff643ec4ee073a5f52402d0dbeafd27b1798d5dd6b6a51 \ + --hash=sha256:43bd0c1ceb924d67f5c1a5254d8361dd9d94246e6331a726064dfa2917880780 \ + --hash=sha256:455ffeea0879d313205df1e745e5883e1feb7f31ecd26be882f5f0babd3db04f \ + --hash=sha256:469355ab1af100b9380f1afb09985019f4a4b94fa1dd0e9396db4361626d7ab8 \ + --hash=sha256:46c0331c9eaaf73a559f3a9e388466be0df96eb83d40f06f1ca6ab6613b35c82 \ + --hash=sha256:46e7a10bf04318c7b0c0273791925ae5e1cbe4a11e34aa934d2ef27862058a80 \ + --hash=sha256:47a62d6045c6eaa0d8f97bc2fb68b8cf90077a0cbfd4e83d6f2d2145611ee134 \ + --hash=sha256:4af784f3e436f65b355c117c6497320f2b5cf6a559295cb1c4c7338e335d45cc \ + --hash=sha256:4e8ffcb486c8490a34a4cef5e4409d8da5a1cb1681e5bf7d786ce5e84aa8540d \ + --hash=sha256:50903d335bfda026c2fa11dd9aed09d8cbee0c451e3a85122a9acb041b7dc69b \ + --hash=sha256:57b39dc8541cf7ed3f639da82bf7451060483967f9e733da1f8173e4095f0642 \ + --hash=sha256:581fa678ef46f4277cc8491312468e582f8ad609dbab907ba6096a08c6a0ff98 \ + --hash=sha256:5a74531ce81af587f906ab42f194032388fcff8fc7938402e5917c9147a39441 \ + --hash=sha256:5aefe78e6a3077fe22b5e64b04666a85a3eb8b934d40e8595a693adcbceb6f11 \ + --hash=sha256:5c27336b1a0ac56569493e858497870347854372395f50483725f8cdacc5a45c \ + --hash=sha256:5d4724255fb8398acd9e583b97eb2279cec20e0bd0f9a94bf75f6056ef9f13da \ + --hash=sha256:5f051f8030a51815dc00e24bd2e5f1435af095c1cc111d747ac6e2a3620d7641 \ + --hash=sha256:615746b12b26a3fd4077bc6fbeb277a1c192a45dd57b531d07ad9ed5c52a9a7a \ + --hash=sha256:617243e19a0992095956f406ee9cd3bc4ba92862d83cb1d83bb59ce574412bec \ + --hash=sha256:655a8e28010f09fd6fa317e857afab3af7647f33e41dee88fa421e92086d1090 \ + --hash=sha256:65be6bda2b537fefa4b3a5ccd6ab386533ce39dd8fe62433ec90901fdc81752d \ + --hash=sha256:67258b00302a5aaf0b267771c7014b13429abd7ea17eebc4c55bd935ff101555 \ + --hash=sha256:677014a073bcb1fbaa7e21144786864f16c08f856d66834f611eceb9006cbab8 \ + --hash=sha256:6a8e768a048c2220697477ce2e67e4345dc9f693d0ee6af53945b5e30227c6a7 \ + --hash=sha256:6da17fc94bd270f5987b10bee113461ac36a36a98b0481ddcc98056e5a90001a \ + --hash=sha256:6de9acef07e3a78e9567fcd26c29011a4da8f050b13004bbf880a0fd82a6eea5 \ + --hash=sha256:6e63c01803be425ff062b7f7fc201a74def1d49fc94a2410dd17375df75936e9 \ + --hash=sha256:6f42912fa9eb4cb7c7ec9fde9b3332ba339eb8a8811981043d4029599f3d950b \ + --hash=sha256:722ec21717eec6477bce582147a28acdfe034e604239466a6a95daedb863e774 \ + --hash=sha256:764cf7bfa149365f32b7a0fd9fed32debdac29dd06295d5635cde1745b446cd8 \ + --hash=sha256:76dd004f59115087c7b700474cb18f01325e37250032e19396c08ae41448e4b3 \ + --hash=sha256:77b37cceca17291897c3c73bd30a7c7c7909593554b5da574ec852af83c1742a \ + --hash=sha256:7a72efa3bf4fa3a6669a54420a472ad056da3973d827f10e3a536da463f926c2 \ + --hash=sha256:7e724f843fa6a0614aece65a7c73e51d0f4412ca41dccac13c3caf98e69536bb \ + --hash=sha256:8196d217eeca52b9235ee1f8a684a09885a5f953d5a31e80ef915bf2c5c94f9d \ + --hash=sha256:829dba1bc049779de9b332088c1a6a9858e96bd67e50b6b644a95e02b67836bc \ + --hash=sha256:84f25e099fdfe3b09f953d84698f729a1f7d1e99101b2787d7a28ed77b323750 \ + --hash=sha256:87f0d5e77548b0c40c8464cdb6108792e7e53f487c6400028a4ec28a8afbe5ab \ + --hash=sha256:880069d21cc33a558dcf180924a546d1ecf8ada5be3e4e70acee87019d706a24 \ + --hash=sha256:882af300d2c6a092b93767d5de03c7bb56dfb06314140c8e872d3f48e09f7b74 \ + --hash=sha256:88381602e379165b66244b2ebc29f9b23ea0851fbe63ae157f91ca324f072d6f \ + --hash=sha256:88b882764ef65147a7a5ae13168dedbe225a04e2ff4858fe543f2c402f093e9c \ + --hash=sha256:88bc5138e53903a85c354e59df7ba73ce306f7b09724cef74dba121e60a88ce2 \ + --hash=sha256:8d1b108bd8f5f6a8b90801f6db3b3858d5deca889acfdb8ac497bbb24e4b0edf \ + --hash=sha256:8f6c38cdcaf98a911d7acc25577f2f9e710f3a2fc2bde1563556784320196b51 \ + --hash=sha256:8fbf28e639544503b7d1c96452a5e5e043e4108d89b1f3fa02910603622d19db \ + --hash=sha256:96abdecbaae746851b87c3a36cb4a661df93ca3d92f114270f79228bf1d00de6 \ + --hash=sha256:98a5b2589a56a4b4f098b0a958099a4356ab904a7844f1da3841efca469af7e9 \ + --hash=sha256:9ae55d24241fc055f22aea3ac924559069848bd0ad4ea065fdd72d2194685fe8 \ + --hash=sha256:9f4a08ff7cb68c27b18e09223cc6304e01d0f82d5a240d251266dfd2e6e44729 \ + --hash=sha256:9f4c0377a83e163a303514fdfab501dbe379bdc13e5b9312a91d112658b29dce \ + --hash=sha256:9fa1ffa08c81a4f809cdab6129f8e55bee4650b9d6d3461019dda73aacd146b6 \ + --hash=sha256:a06f3b5085176763182449559e20391d7ce616a8972a9f7a33deda87ea6d4f3c \ + --hash=sha256:a62d8c424383c9dc769ff3672018df822603117e32686e567d452ed035b6fb2e \ + --hash=sha256:a7b35181a14cbfcae163b4de545d22abfd07d06c2c41ca69cfcd99251d6888ab \ + --hash=sha256:a9fe648abd1d9b89aebfa30407bfdd08a0271ec5dc7d44a4c6ccd1ce22cf562a \ + --hash=sha256:acfea4c20bf54384883ea33b1240fc1db4f52e190823a4e2b334bc3e8bfca96a \ + --hash=sha256:ad68c28a27246fed109a4409393d677b7e1388345cbbd2f5aee5c182d8506110 \ + --hash=sha256:aec00f018d34c67500ff0438dc314b40277be4a1b983cbacbf53ccf7db63e257 \ + --hash=sha256:b1bc819c6db90e8f91a38250a1ab4c058261871aa52d2fe36382eddedf146dee \ + --hash=sha256:b1c323fc3be1dc3f87f6c59458cb7d9e13dcbbf971d6c3f3e2bbaf58d3bfcdfe \ + --hash=sha256:b3b451fd2723ad3191a209afe6f3f4bc86c83e9a85bdc255353b91803ee6aa66 \ + --hash=sha256:b65d5fe48219dc2d5e158de9e6514e75600f379cc7e37108d35f31764c155566 \ + --hash=sha256:bb0efe019480a1c93e168ce96479273aaebd672fc8c350d5eed1e507ababb1b8 \ + --hash=sha256:be3f0129c5654517b2abf07dcb75bb1d9479759a4ccfb569e8293579e9fc029a \ + --hash=sha256:c3241d684a76eaaef8b2dc789afde4343cd3aad55ea81e4e8ab3605b529bae51 \ + --hash=sha256:c65da239a5ad553619804c1f9d65c1a0b3005381c6158ee14da2c7444cbd0c78 \ + --hash=sha256:c89406fa3dcd4aa8662c6406cc5c0de1790e9614d2c3aaf03ca53a8a8ccf3405 \ + --hash=sha256:ccbf3f4a9890d50b3a08ee04029fde30a03bfdeffaa19977628bf17251764e60 \ + --hash=sha256:ce0305b702b20d1e1d60a9aaace6bc89970e1753565543f310d549eab22c2435 \ + --hash=sha256:ce13c7d233239e739600a57d4a73c1192ad8259e655a4d55aa1a454242bc809d \ + --hash=sha256:cec1bb8f22abccc8d20f8ca63df9be41600c26c190f4b97ee86c675fd4a863a6 \ + --hash=sha256:d1e2f5fa2b6d01f0d85b4f223fea7ed1d504be282a02a81bd2be4817ef7a2f03 \ + --hash=sha256:d334d11398086bb5559606cb42d51c013ea7c061c7db701521392373d3c087f5 \ + --hash=sha256:d411ea5ca18ac1b12c0c94be88b60c18ca641ac43bcdfdf1c9f79d46cdbe1603 \ + --hash=sha256:d41ef69d5416fbc1d98cf96c37be6192d10fd101c3e0f8b3ddc36e09432b3c08 \ + --hash=sha256:d58159af7835fde09c462394293c0d7aaf8fb4557d8f8e5699f5e722ccae013d \ + --hash=sha256:d7b349265fad6244013eecd99df8d83c12bf3013943e431f4fadd5bffc37db42 \ + --hash=sha256:d8e83333385cac6030a5167fd18bf96cc6c58b914c308e683f05b0cf94bc8dd0 \ + --hash=sha256:d9d01e8ede41fea4f5a847dad9d628355f74905f437a5b6856d67aa66d193800 \ + --hash=sha256:d9fc873e239c5abeb150bc24dbd1a7af23a9254526383ce0a077f5e20adbeb19 \ + --hash=sha256:db9d7ee47f3ba531e278be539af39e2c7c7d28fb94897b6cd1120d63b0ef5922 \ + --hash=sha256:dc053f9e95a76213c5eb7ed95779f7daf0d2bf0e4e03073629ebfa43a033f151 \ + --hash=sha256:dc2b79afc074d2f3e64b26539350f697fe1b85ea1c49ea24eb588f247b053ce1 \ + --hash=sha256:dc5789e5ea182b77a38881383ada5347202a6c66f4857d054e075290e80b604b \ + --hash=sha256:dcb8d5f7edef7a399d322cf28d4c4e6f98dab64d301c8f50581a1080e5198142 \ + --hash=sha256:e4bd7eacb87d8cf3ed70d6392c770a0d92441f05d7d2a3efafb5bc171d5e3067 \ + --hash=sha256:e552e0037230ac16e5f568de7012041344d1b18c9feed30ec2891b8eba55af81 \ + --hash=sha256:e5f5c7a893507d0e83a80b88aefd6522f7e882cd53f9722c6f23f5a020c9557c \ + --hash=sha256:e74e41f0ad12ff1e8983e349daef79d37cc8280c743ce9d134d6c74c18dab5d6 \ + --hash=sha256:e78fd4b7b2c5086a38671c9c882c1e643385eccea360b5b1fda4a105e590087e \ + --hash=sha256:e8dc3fa6d6b7ead3f9de57895f41b116a28787548e066365d9d90f7356bcaad2 \ + --hash=sha256:ea0ed9373b880115911d9d39634bccc95b8ce590c9c42e8589f5cacc3ef3cee2 \ + --hash=sha256:edadce7a22052056fd4384543019856b34850363c9d387929f677ae01d79709c \ + --hash=sha256:eec113a5b41d124ef42ff56b0d74a6da3fd986400038eab9e58ee42a4024e837 \ + --hash=sha256:f221081107b8c48184d99f7019604486376e7ef826037e70aad6b02540732c23 \ + --hash=sha256:f3a1d577e081667dda7f8e5b4796e6e32f9713c93e2a3d930669519840a3c623 \ + --hash=sha256:f47aafd92aa28b941180e6da8a42b0f711851b14b81a5b6bb28dbbb1fa35152c \ + --hash=sha256:f4c45ee2512d3757b5e6c67c5a34e435143f2ecb7df3324f9fd888688c45c0f4 \ + --hash=sha256:f58f58b4b29bbea2a3635e2c56eff4a3adab011fe383802a9e542e31b97085fc \ + --hash=sha256:f612dc57f00c07cf4aa2673f7cbceabd654ad2457b7e639f061b794d6e11f9fd \ + --hash=sha256:f62114a54117e4948a1e414e89521f7fe1e3c2f83f2a571a06a4fc6718b0900a \ + --hash=sha256:f64e001bb7fa89b9f32cfa600bf8e9ac8ca26759d9b92ae01453ee303d9cd7b4 \ + --hash=sha256:f7233eaf441a345a5943a929fd4b5ea3278f11aed35a9ed0f3106b8cb3ca846a \ + --hash=sha256:f78a3ffb1994304db2c0c4588e4d1a518079b557054fa3bb985a6f5e50ff49a3 \ + --hash=sha256:f8e822efd54137d8cc8310eb64635ab827a4a6c72ff08691f38aa624776d8ecb \ + --hash=sha256:f98bf378d7a5be047a044a1a27c987a8f355e10e3b5754617dbe756248cbc5ce \ + --hash=sha256:fb88076df585b69c5761c387c0081aa87d7b9eb1b205a6535ca4777e25650d81 \ + --hash=sha256:fd8f47dbf2e8adb15c847215f83436de3fdb120b51fdae0fbbdf69fd97a3ad80 \ + --hash=sha256:ff3e2ba7a9f0a110b0555452e9b5a03a34e11662544e01beea15f144b48ba7b7 + # via synth-containers +zstandard==0.25.0 \ + --hash=sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64 \ + --hash=sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a \ + --hash=sha256:05353cef599a7b0b98baca9b068dd36810c3ef0f42bf282583f438caf6ddcee3 \ + --hash=sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f \ + --hash=sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6 \ + --hash=sha256:07b527a69c1e1c8b5ab1ab14e2afe0675614a09182213f21a0717b62027b5936 \ + --hash=sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431 \ + --hash=sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250 \ + --hash=sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa \ + --hash=sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f \ + --hash=sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851 \ + --hash=sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3 \ + --hash=sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9 \ + --hash=sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6 \ + --hash=sha256:19796b39075201d51d5f5f790bf849221e58b48a39a5fc74837675d8bafc7362 \ + --hash=sha256:1cd5da4d8e8ee0e88be976c294db744773459d51bb32f707a0f166e5ad5c8649 \ + --hash=sha256:1f3689581a72eaba9131b1d9bdbfe520ccd169999219b41000ede2fca5c1bfdb \ + --hash=sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5 \ + --hash=sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439 \ + --hash=sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137 \ + --hash=sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa \ + --hash=sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd \ + --hash=sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701 \ + --hash=sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0 \ + --hash=sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043 \ + --hash=sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1 \ + --hash=sha256:37daddd452c0ffb65da00620afb8e17abd4adaae6ce6310702841760c2c26860 \ + --hash=sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611 \ + --hash=sha256:3b870ce5a02d4b22286cf4944c628e0f0881b11b3f14667c1d62185a99e04f53 \ + --hash=sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b \ + --hash=sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088 \ + --hash=sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e \ + --hash=sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa \ + --hash=sha256:4b14abacf83dfb5c25eb4e4a79520de9e7e205f72c9ee7702f91233ae57d33a2 \ + --hash=sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0 \ + --hash=sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7 \ + --hash=sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf \ + --hash=sha256:51526324f1b23229001eb3735bc8c94f9c578b1bd9e867a0a646a3b17109f388 \ + --hash=sha256:53e08b2445a6bc241261fea89d065536f00a581f02535f8122eba42db9375530 \ + --hash=sha256:53f94448fe5b10ee75d246497168e5825135d54325458c4bfffbaafabcc0a577 \ + --hash=sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902 \ + --hash=sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc \ + --hash=sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98 \ + --hash=sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a \ + --hash=sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097 \ + --hash=sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea \ + --hash=sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09 \ + --hash=sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb \ + --hash=sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7 \ + --hash=sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74 \ + --hash=sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b \ + --hash=sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b \ + --hash=sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b \ + --hash=sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91 \ + --hash=sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150 \ + --hash=sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049 \ + --hash=sha256:89c4b48479a43f820b749df49cd7ba2dbc2b1b78560ecb5ab52985574fd40b27 \ + --hash=sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a \ + --hash=sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00 \ + --hash=sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd \ + --hash=sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072 \ + --hash=sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c \ + --hash=sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c \ + --hash=sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065 \ + --hash=sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512 \ + --hash=sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1 \ + --hash=sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f \ + --hash=sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2 \ + --hash=sha256:a51ff14f8017338e2f2e5dab738ce1ec3b5a851f23b18c1ae1359b1eecbee6df \ + --hash=sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab \ + --hash=sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7 \ + --hash=sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b \ + --hash=sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550 \ + --hash=sha256:b9af1fe743828123e12b41dd8091eca1074d0c1569cc42e6e1eee98027f2bbd0 \ + --hash=sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea \ + --hash=sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277 \ + --hash=sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2 \ + --hash=sha256:c2ba942c94e0691467ab901fc51b6f2085ff48f2eea77b1a48240f011e8247c7 \ + --hash=sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778 \ + --hash=sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859 \ + --hash=sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d \ + --hash=sha256:d8c56bb4e6c795fc77d74d8e8b80846e1fb8292fc0b5060cd8131d522974b751 \ + --hash=sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12 \ + --hash=sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2 \ + --hash=sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d \ + --hash=sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0 \ + --hash=sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3 \ + --hash=sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd \ + --hash=sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e \ + --hash=sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f \ + --hash=sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e \ + --hash=sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94 \ + --hash=sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708 \ + --hash=sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313 \ + --hash=sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4 \ + --hash=sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c \ + --hash=sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344 \ + --hash=sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551 \ + --hash=sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01 + # via synth-containers diff --git a/examples/runebench/requirements.txt b/examples/runebench/requirements.txt new file mode 100644 index 000000000..677bfb3dc --- /dev/null +++ b/examples/runebench/requirements.txt @@ -0,0 +1 @@ +synth-containers==0.4.3 diff --git a/examples/runebench/run.py b/examples/runebench/run.py new file mode 100644 index 000000000..c926ce0d5 --- /dev/null +++ b/examples/runebench/run.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""Bounded, concurrent MA runner. Policies receive only typed actor-local tools.""" +import argparse, concurrent.futures, datetime as dt, fcntl, hashlib, json, os, socket, subprocess, threading, time, urllib.request, urllib.error +from pathlib import Path +HERE=Path(__file__).resolve().parent +OUT=HERE/'results' +BASE='http://127.0.0.1:8126' +MODEL='openai/gpt-5.6-luna' +LOCK=threading.Lock() + +def write(p,data): + p.write_text(json.dumps(data,indent=2)) +def call(path,body=None,token=None,timeout=45): + headers={'Content-Type':'application/json'} + if token:headers['Authorization']='Bearer '+token + r=urllib.request.Request(BASE+path,data=json.dumps(body).encode() if body is not None else None,headers=headers) + with urllib.request.urlopen(r,timeout=timeout) as f:return json.load(f) +def compose(*args,env=None): + subprocess.run(['docker','compose','-p','workshop-rune-'+hashlib.sha256(str(HERE).encode()).hexdigest()[:12],'-f',str(HERE/'compose.yaml'),*args],env=env,check=True,stdout=subprocess.DEVNULL) +def utc():return dt.datetime.now(dt.timezone.utc).isoformat() +def credential(): + # Project-local env only. No Keychain, shell expansion, or credential copying into Docker. + for line in CREDENTIAL_FILE.read_text().splitlines(): + if line.strip().startswith('OPENROUTER_API_KEY='):return line.split('=',1)[1].strip().strip('"').strip("'") + raise RuntimeError('Project-local OPENROUTER_API_KEY unavailable') +def reserve(batch, model): + p=HERE/f'budget-{batch}.json' + with p.open('a+') as f: + fcntl.flock(f,fcntl.LOCK_EX);f.seek(0);s=f.read();b=json.loads(s) if s else {'maxUsd':5,'maxCalls':128,'reservedUsd':0,'calls':0,'actualUsd':0,'unknownCostCalls':0} + # Bounded 20k-byte input and 8192 output; include cache-write pricing in reservations. + amount=.20 if model.endswith('terra') else .02 + if b.get('unknownCostCalls',0) or b['calls']>=b['maxCalls'] or round(b['reservedUsd']+amount,8)>b['maxUsd']:raise RuntimeError('Aggregate budget exhausted') + b['calls']+=1;b['reservedUsd']=round(b['reservedUsd']+amount,8);f.seek(0);f.truncate();json.dump(b,f);f.flush() + return p + +def settle(p,usage): + with p.open('r+') as f: + fcntl.flock(f,fcntl.LOCK_EX);b=json.load(f) + if usage.get('cost') is None:b['unknownCostCalls']+=1 + else:b['actualUsd']+=float(usage['cost']) + f.seek(0);f.truncate();json.dump(b,f);f.flush() + +def main(): + ap=argparse.ArgumentParser();ap.add_argument('--provider',choices=['openrouter']);ap.add_argument('--env-file',type=Path);ap.add_argument('--scripted',action='store_true');ap.add_argument('--duration',type=int,default=150);ap.add_argument('--calls',type=int,default=10);ap.add_argument('--batch',default='demo-20260907');ap.add_argument('--effort',choices=['low','medium','high'],default='low');ap.add_argument('--model',choices=['luna','terra'],default='luna');args=ap.parse_args();args.scripted = args.scripted or args.provider is None + global CREDENTIAL_FILE + CREDENTIAL_FILE=args.env_file + if not args.scripted and CREDENTIAL_FILE is None:ap.error('--provider openrouter requires an explicit --env-file; never paste keys into chat') + model='openai/gpt-5.6-'+args.model + if not 10<=args.duration<=600 or not 1<=args.calls<=16:raise ValueError('Invalid duration or call cap') + if not args.batch.replace('-','').isalnum():raise ValueError('Invalid batch') + with socket.socket() as port_check: + port_check.bind(('127.0.0.1',8126)) + stamp=dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ')+('-scripted' if args.scripted else '-'+args.model+'-'+args.effort) + run=OUT/stamp;job=run/'episode';job.mkdir(parents=True,exist_ok=False) + scenario=json.loads((HERE/'scenario.json').read_text());scenario['duration_seconds']=args.duration + kind='scripted' if args.scripted else 'provider-backed' + scenario['evidence_kind']=kind + manifest={'schema_version':'runebench.ma.manifest.v1','config':{'id':'runebench-ma','scenario':scenario,'evidence_kind':kind,'model':None if args.scripted else model,'reasoning_effort':args.effort,'max_completion_tokens':8192,'max_calls_per_actor':args.calls,'batch':args.batch},'head_commit':subprocess.check_output(['git','rev-parse','HEAD'],cwd=HERE,text=True).strip(),'tree_clean':False,'source_sha256':{p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in HERE.iterdir() if p.suffix in ['.ts','.py','.json','.yaml'] and not p.name.startswith('budget-')},'evals_authority':{'head_commit':subprocess.check_output(['git','rev-parse','HEAD'],cwd=HERE,text=True).strip(),'working_tree_tracked_clean':False},'created_at':utc(),'randomization':'Fixed starting saves; engine RNG is not seeded. Repeats are not paired-seed causal trials.'} + write(run/'matrix_run_manifest.json',manifest) + env=dict(os.environ,MA_ARTIFACTS=str(job),MA_RUN_ID=stamp,MA_EVIDENCE_KIND=kind) + container_name='workshop-runebench-'+stamp + seq=0;armed_at=None + def event(kind,actor,payload): + nonlocal seq + with LOCK: + seq+=1 + record={'schema_version':'evals.event-stream.v1','kind':'trace.raw','source':'local','occurred_at':utc(),'run_id':stamp,'sequence':str(seq),'actor_id':actor,'payload':{'kind':kind,'elapsedMs':(time.monotonic()-armed_at)*1000 if armed_at else 0,**payload}} + with (run/'events.jsonl').open('a') as f:f.write(json.dumps(record)+'\n') + key=None if args.scripted else credential() + usages=[];errors=[];answered=set() + def policy(actor,token): + name=actor['id'];history=[] + prompt=f'''You control {name}, {actor['role']} on team {actor['team']} in a local RuneScape benchmark. Your team competes to gain the most total Woodcutting XP before a hard wall deadline. All players start with level 10 Woodcutting and bronze axes. XP multiplier 1, server tick 300ms. Both lead and scout can chop: lead prioritize throughput; scout locate free trees, report useful locations to your teammate, then chop too. Communicate briefly in public chat when useful, but do not spend the whole race chatting. Only your observation is supplied; other players' private state is unavailable. Nearby normal Trees are legal; do not target oaks. Crowded trees may be felled by others. If inventory is full drop logs. A chop can continue automatically until a tree is depleted. Avoid needless interruptions. Return exactly one JSON object with type (say/chop/walk/drop/wait), optional text (max 80 chars), x,z (observed tree coordinates for chop or within 32 tiles for walk), slot (logs only for drop), and reason (one concise sentence). No code. Tools: chop attempts one normal tree and waits for a log; walk navigates; wait waits 2 game ticks; say sends public chat; drop discards one inventory slot of logs. Prefer a useful action now. Teammate roster: {json.dumps([a for a in scenario['actors'] if a['team']==actor['team']])}.''' + for turn in range(args.calls): + try: + obs=call(f'/actors/{name}/observation',token=token) + if obs['phase']!='running' or obs['remainingMs']<3000:break + # Bound request size using a compact projection, retaining actor-local provenance. + compact={**obs,'nearbyLocs':obs.get('nearbyLocs',[])[:24],'gameMessages':obs.get('gameMessages',[])[-10:]} + decision_id=f'{name}-{turn}' + if args.scripted: + if turn==0:action={'type':'say','text':f'{actor["team"]} {actor["role"]} chopping nearby trees','reason':'Scripted communication probe'} + else:action={'type':'chop','reason':'Scripted lifecycle probe'} + else: + messages=[{'role':'system','content':prompt},*history[-4:],{'role':'user','content':json.dumps(compact,separators=(',',':'))}] + body={'model':model,'messages':messages,'max_completion_tokens':8192,'reasoning':{'effort':args.effort},'response_format':{'type':'json_object'},'provider':{'max_price':{'prompt':2 if args.model=='terra' else .2,'completion':12 if args.model=='terra' else 1.2},'allow_fallbacks':False}} + data=json.dumps(body).encode() + if len(data)>20000:raise RuntimeError('Request exceeds 20k-byte input bound') + bp=reserve(args.batch,model) + event('model.requested',name,{'decisionId':decision_id,'observation':compact,'inputBytes':len(data),'model':model,'reasoningEffort':args.effort,'messages':messages}) + req=urllib.request.Request('https://openrouter.ai/api/v1/chat/completions',data=data,headers={'Authorization':'Bearer '+key,'Content-Type':'application/json'}) + response=None + try: + with urllib.request.urlopen(req,timeout=min(65,max(1,obs['remainingMs']/1000))) as f:response=json.load(f) + usage=response.get('usage',{});settle(bp,usage) + except Exception: + settle(bp,{}) + raise + with LOCK:usages.append(usage);answered.add(name) + message=response['choices'][0]['message'];content=message.get('content') or '' + event('model.completed',name,{'decisionId':decision_id,'responseId':response.get('id'),'model':response.get('model'),'content':content,'reasoningEffort':args.effort,'reasoning':message.get('reasoning'),'reasoning_details':message.get('reasoning_details'),'usage':usage,'finishReason':response['choices'][0].get('finish_reason')}) + action=json.loads(content) + history.extend([{'role':'user','content':f'Turn {turn} observation received.'},{'role':'assistant','content':content}]) + action['decisionId']=decision_id + event('policy.action',name,{'decisionId':decision_id,'action':action,'evidence_kind':kind}) + result=call(f'/actors/{name}/action',action,token,timeout=40) + if args.scripted: + with LOCK:answered.add(name) + event('policy.result',name,{'decisionId':decision_id,'result':result}) + history.append({'role':'user','content':'Action result: '+json.dumps(result)[:1500]}) + time.sleep(.3) + except urllib.error.HTTPError as e: + detail=e.read(1200).decode(errors='replace') + if key:detail=detail.replace(key,'[redacted]') + event('policy.error',name,{'turn':turn,'status':e.code,'detail':detail}) + if e.code==409:break + errors.append({'actor':name,'turn':turn,'error':detail});break + except Exception as e: + message=str(e).replace(key,'[redacted]') if key else str(e) + event('policy.error',name,{'turn':turn,'error':message});errors.append({'actor':name,'turn':turn,'error':message});break + event('policy.finished',name,{}) + started=time.monotonic();result=None + try: + compose('up','-d','--build','--force-recreate',env=env) + ready_until=time.monotonic()+900 + while True: + try: + if call('/health',timeout=3)['ready']:break + except Exception:pass + state=subprocess.run(['docker','inspect','--format','{{.State.Status}}',container_name],capture_output=True,text=True,timeout=10) + if state.returncode or state.stdout.strip() in ['exited','dead']:raise RuntimeError('MA container exited before readiness; inspect episode engine/client logs') + if any('Fatal error:' in p.read_text(errors='replace') for p in job.glob('client-*.log')): + raise RuntimeError('A game browser exited before readiness; inspect episode/client-*.log') + if time.monotonic()>ready_until:raise RuntimeError('All-player readiness timed out') + time.sleep(2) + receipt=call('/arm',{'duration_seconds':args.duration},timeout=180);armed_at=time.monotonic() + isolated=False + try:call('/actors/mab/observation',token=receipt['tokens']['maa']) + except urllib.error.HTTPError as e:isolated=e.code==403 + if not isolated:raise RuntimeError('Actor capability isolation failed') + event('eval.started',None,{'evidence_kind':kind,'scenario':scenario}) + # Four trusted policy loops; models can emit only typed actions, never shell/code. + with concurrent.futures.ThreadPoolExecutor(max_workers=len(scenario['actors'])) as pool: + futures=[pool.submit(policy,a,receipt['tokens'][a['id']]) for a in scenario['actors']] + end=time.monotonic()+args.duration+10 + while time.monotonic() 9_000_000: + raise ValueError('Sample exceeds the small public-example size budget') + policy = json.loads((HERE / 'manifest.json').read_text()) + policy['sample'] = {'file': archive.name, 'sha256': hashlib.sha256(archive.read_bytes()).hexdigest(), 'run': run.name, 'kind': 'scripted', 'modelCalls': 0} + (HERE / 'manifest.json').write_text(json.dumps(policy, indent=2) + '\n') + print(f'Packaged {run.name}: {archive.stat().st_size} bytes') + +def restore(): + sample = json.loads((HERE / 'manifest.json').read_text()).get('sample') + if not sample: + raise ValueError('This checkout has no packaged sample yet') + archive = HERE / sample['file'] + if hashlib.sha256(archive.read_bytes()).hexdigest() != sample['sha256']: + raise ValueError('Sample checksum mismatch') + destination = HERE / 'results' + if (destination / sample['run']).exists(): + raise ValueError('Sample already restored; use review or rebuild instead of overwriting it') + with tarfile.open(archive, 'r:gz') as tar: + expected = {f"{sample['run']}/{name}" for name in ALLOWED} + members = tar.getmembers() + if len(members) != len(expected) or {m.name for m in members} != expected or any(not m.isfile() for m in members): + raise ValueError('Unexpected sample members') + destination.mkdir(exist_ok=True) + tar.extractall(destination, filter='data') + print(f"Restored verified sample {sample['run']}") + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--package', type=Path) + args = parser.parse_args() + package(args.package) if args.package else restore() diff --git a/examples/runebench/sample.tar.gz b/examples/runebench/sample.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..6a679b48299009ee46498b851f98814efc38df1d GIT binary patch literal 1528667 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�mW;vtX?RjB% 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 sK4oqkUSZ{ca_&bfVutD=*rG|)7A%k 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