elanous 0.2.38 → 0.2.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/1115.434ae2ecb0ad986d.js +149 -0
- package/apps/pwa/out/_next/static/chunks/1121.667cec0bcdbb3fdb.js +36 -0
- package/apps/pwa/out/_next/static/chunks/1178.1aa9cad0e7695427.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1197-54da59d250a6e476.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5203.51e5d8b5f1dcfe53.js → 1219.7e17964e0e7cd8e5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/1245.1cfd13b26dbd71d6.js +287 -0
- package/apps/pwa/out/_next/static/chunks/1494-f93046ff5c5a124d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1609.2b312797599b8e45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1661.0dd330f3d89f73f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/167-0c121e4b47e0f9da.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1700.cdc2bf8cc685e221.js +90 -0
- package/apps/pwa/out/_next/static/chunks/1703-78e46acad29f3cfc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7573.2376e13c9c21f1f8.js → 1765.679b29625c8e88d4.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{3528.e4b8ab70d339f965.js → 1800.f7ff51dc4978a958.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1971-7efb628c471dc5a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1999-8de7b7bb36801913.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2073.689dcb2680113d76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2198-557d49184104d165.js +1 -0
- package/apps/pwa/out/_next/static/chunks/221-440ce67fa9f5c839.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8879.ac06755bcf3e44a1.js → 2263.e09b6edea33bad2d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2427-8ed8e98624cbd664.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2457-725c5827bc003795.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2491-2718b15a5ccb75a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2527-5c636a43c7cc5dde.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2692-1027cb16fe4ccf49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2743.5d3f76948be32b34.js +41 -0
- package/apps/pwa/out/_next/static/chunks/2773.0e80b9d325fb5635.js +32 -0
- package/apps/pwa/out/_next/static/chunks/2873-0f3cb56c8e3528ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9fe1b809.738da85c8b17e3fb.js → 2fdf2931.b431eee4fb468bc3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{eaad0cba.771200940c529df7.js → 2fe2c302.41b517f88d6fe502.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3097.864e92bdaa2f4d15.js +79 -0
- package/apps/pwa/out/_next/static/chunks/3136-d6870ba63300a93e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3173-0289113d434e83b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3230-49298c3b73ef2268.js +1 -0
- package/apps/pwa/out/_next/static/chunks/325-dd9208a1a0ab7ce4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3283.528f430dd043fb4e.js +74 -0
- package/apps/pwa/out/_next/static/chunks/3295.cc8cb13b8f0dae1a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3688-86e3b88abf18cb3b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3804-7220467a5473a8df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3808.3e1e247ce7bdf889.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b7478fef.5917716286213fcc.js → 385e4574.e9886ab32759eda3.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/3927.b5473ad109da44c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3934-98ea94832e194796.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4133.2056bb4d78f621af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4156.ba4de4a3664f04b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4297-239ca82f293faab0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4320-dcc5a7734126f4c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4434.94b4c552a2d91e69.js +148 -0
- package/apps/pwa/out/_next/static/chunks/{8911.bf5e712acb25fb6c.js → 4527.a69f2894d1bc9f4a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4579.03e4092f73950965.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{5307.d7328020227880c2.js → 4667.97e31aa38c95c475.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/{6142.913b44e260a3f925.js → 4702.4a4adc1f16d7c93b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4815.481501c57b3568d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/487-d16e698ef14662d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4888-7a38bf522c9cc6fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5034-30e429d6bb086881.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5040-35c494e60dadd97d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/50c6ab1e.81a96e419350909d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5154.4b6a836179356f30.js +94 -0
- package/apps/pwa/out/_next/static/chunks/{8487.291666583bebe640.js → 5159.115f1c2ca471386c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5228-a8200df486278f2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/530.d26346e18089640a.js +115 -0
- package/apps/pwa/out/_next/static/chunks/5379.97af106d09566f69.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{89d6c04f-6b8ab341748e468a.js → 53aeb4ee-51c92f8d683f5220.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3291.f6c1005387d514bd.js → 5471.1e611302ed3a7c53.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5479-b6372e762719f92f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5534-699c5cbcbc96c5d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3086.fb14b60ce98b53d3.js → 5574.f35accf4f132b29d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5701.9c4e8f250312eeee.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{1632.7d87f998e714bede.js → 5744.b9415e18ad623100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5300.677c97e0393742e6.js → 5892.2526d962339662d8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5937.c2df9e3447e1f667.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5941.8210cc93c9202c0a.js +286 -0
- package/apps/pwa/out/_next/static/chunks/{7161-8f22db3ec5ed005e.js → 5947-135e26537d80a6b9.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/{3124-561f7f037943cde2.js → 5964-7e21164a8f2cf0ce.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5975.4004f820ae3cecd5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6142-7ccd48795d7abd55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6160.bf9177b8c1bfb243.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6398.71ba0690c8027555.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6492.2bd5c10edc1a064e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6552.a3840aa7da35ad68.js +62 -0
- package/apps/pwa/out/_next/static/chunks/6736.1e36d3ac5fa5e41d.js +24 -0
- package/apps/pwa/out/_next/static/chunks/6874.d5cf7eb8edb2cabe.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6900.3ccbe069bdd4556a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/71327eb4.add50b365f94bf3a.js +122 -0
- package/apps/pwa/out/_next/static/chunks/7136.deca3e3a2ddaa374.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7139-ee9f8ed2677a40b3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.738b777642a02233.js → 7191.fdc955f7d96064a4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7423-e0aa5d9e2962120e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7453.afc1ea7e05d56a8c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7503-5e6a3d7fe67e4a0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7761-476fd7bf768abd34.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7975.eafb12097a0a9e62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1026-44010f405704050c.js → 8050-9b364bdef8f71927.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8152.64e253c35cccfaae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8190.620f0231b3924bcb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/83184dcd-0ee6247ff41eb3f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8448.45b6b1f1a99eeb35.js +104 -0
- package/apps/pwa/out/_next/static/chunks/8464-e24f10451b658aac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8477.e4c944bb3aa5d2f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8710.ca385257d5ca422e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8830.627ccf294ec2465c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8880.64c445592f04acaf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8981.c706964c8ba27aff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5638.107314dc479e2f37.js → 9048.212083de42d25be3.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9053.bd0fffaff8d3f836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9161-c66e723d7ec9d682.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9185.1ecdf9b65c0bb684.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9356.fccd5f58086b4ff5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9446.dc0406109371d329.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9506.dcf25d467a42627e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8019.21ca554a821dad01.js → 9507.7026657a2f47eb64.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/9541.eb8e2f8480c7feca.js +93 -0
- package/apps/pwa/out/_next/static/chunks/9577.ed7582186efa5482.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9579.4a8169600fff2058.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9642-ff2e039eb62e3b92.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9659-8f2323348113ad80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9724.4c5623af2db43153.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{6493.27ad77de7aac7966.js → 9834.63ab2bbc3153172e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3389.012c70767a872f9b.js → 9869.81a1e24ca682d5a0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8994d93e.33ba491f95345fc2.js → a0a01613.6d1cca322238d7fe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{de8b930d.d94c0f42acf807fc.js → addc2b32.8c4ff8f0aefe987d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-8d9d76ea322d855c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed2c4e59f9f1fe21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-2dc99e03c5d76ca0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b5a3a788330e920f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-866aac5ff226a7f3.js → page-14949db884bfa672.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ea76c85b36388a97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-01716d20af62d325.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-62baf6881a64b1e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f89ba590c2af8afa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-22f8b0f78d9637e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-817f9d7e6820be80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-ffb9bc0d2650249a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-f6d0e64873c0f629.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-8bfa0d4ba23a209d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-f5060462249e32c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-8d9d76ea322d855c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2049f8b1ef79117.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1446c5fa93907f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-b48a7a07c5204156.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-e974b8c4ca47614c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-8a919b869bd28594.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-d31b8de6bfcd72ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-64765f817909db29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-d128c8569a36ee9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-b2679d357e9b9270.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-6fabec98a9e7b269.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-048f30149dcf1349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-9113181d6b5a2725.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-40f9e19149024298.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-1e7a755a13227611.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3c8f8b67a203cf07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-18032f5437215062.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-da3b739192fd0ee3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d332545571bd6fd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-5237aa5b3f3b8ec5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a142dfb0331a05f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d16e56c3989c147e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6b6593600869e2cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-8d9d76ea322d855c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-29e8b8a8396b55cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-b3414d856a1a7e2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c3affa52b7ce961b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f21dddf9f95b6fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-f5a30a24745bf088.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-8c8569a09b4d4e34.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-2ce32722c0243483.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4767b0d9c0a98c6c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-116ee1339c387a75.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-35e8c50ca4ef7ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-52e17113356bb891.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7458ae05ed2ac1a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/b2271e54.c1de3347976e90b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b7459770.6539b11860eb8a9c.js → c0c1a699.0ba79f97005965d4.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/framework-ad6c9dd524ad6ef8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-2ddd1f2b44d6aa59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-e3d82bdc358d20c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-71d5aa3896e1655f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-03ffc54e00438546.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-56bb89af5a611d0b.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.mjs +6 -1
- package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
- package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
- package/graphs/demo/persona-marketer/fixtures/marketer-scenario.yaml +10 -0
- package/graphs/demo/persona-marketer/persona-marketer.yaml +25 -0
- package/graphs/demo/persona-marketer/recipes.yaml +19 -0
- package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
- package/graphs/demo/persona-teacher/recipes.yaml +17 -0
- package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
- package/graphs/overlays/stop-heal-ladder.yaml +23 -2
- package/graphs/release/release-loop.yaml +3 -1
- package/graphs/units/gate/gate.yaml +20 -0
- package/graphs/units/git/git.yaml +26 -0
- package/graphs/units/implement/implement.yaml +19 -0
- package/graphs/units/landing/landing.yaml +18 -0
- package/graphs/units/review/review.yaml +20 -0
- package/graphs/units/verbs/recipes.yaml +3 -0
- package/graphs/units/verbs/research.yaml +20 -0
- package/graphs/video/explainer-line.yaml +4 -1
- package/graphs/work/research-report.yaml +11 -1
- package/package.json +1 -1
- package/scripts/dead-code/report.ts +169 -0
- package/scripts/disk-steward.ts +46 -0
- package/scripts/fleet/node-setup.sh +50 -0
- package/scripts/graph-units/verbs/research.ts +55 -0
- package/scripts/lib/morning-report-options.ts +3 -4
- package/scripts/lib/tui-regress-evidence.ts +56 -0
- package/scripts/llm-cost-ledger.ts +179 -8
- package/scripts/measure-nl-routing-corpus.ts +10 -2
- package/scripts/morning-report.ts +5 -1
- package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
- package/scripts/public-copy-lint.ts +163 -19
- package/scripts/public-export.ts +15 -7
- package/scripts/ratchets/train-side-invariants.sh +0 -1
- package/scripts/release-loop/export-check-node.ts +21 -3
- package/scripts/release-loop/gate-autoheal.ts +117 -0
- package/scripts/release-loop/gate-baseline-receipt.ts +8 -1
- package/scripts/release-loop/gate-diff.ts +22 -3
- package/scripts/release-loop/gate-node.ts +583 -64
- package/scripts/release-loop/gate-shard-dispatch.ts +128 -0
- package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
- package/scripts/release-loop/gate-shard-receipts.ts +89 -0
- package/scripts/release-loop/gate-triage-node.ts +70 -10
- package/scripts/release-loop/gate-wall.ts +5 -5
- package/scripts/release-loop/preflight.ts +6 -2
- package/scripts/release-loop/resume-release.ts +267 -20
- package/scripts/release-loop/resume.ts +48 -0
- package/scripts/release-loop/traffic-node.ts +6 -3
- package/scripts/release-loop/unattended-release.ts +4 -2
- package/scripts/release-loop/version-node.ts +7 -2
- package/scripts/release-story/draft.ts +1 -1
- package/scripts/research-bench/README.md +53 -0
- package/scripts/research-bench/questions.json +66 -0
- package/scripts/research-bench/rubric.md +33 -0
- package/scripts/research-bench/run.ts +523 -0
- package/scripts/shadow-authoring-probe.ts +20 -0
- package/scripts/tui-regress.ts +85 -10
- package/scripts/ux-sim.ts +19 -2
- package/src/acp/core-turn-bridge.ts +94 -4
- package/src/agent/autonomous-tools.ts +10 -3
- package/src/agent/monad-agent-turn.ts +4 -1
- package/src/agent/registry.ts +26 -2
- package/src/agent/shared-app-tools.ts +1 -1
- package/src/agent/surface-ux/build.ts +12 -3
- package/src/agent/surface-ux/types.ts +1 -1
- package/src/agent/task-notification.ts +75 -6
- package/src/agent/types.ts +3 -0
- package/src/agent-mission/driver.ts +29 -3
- package/src/agent-mission/mission-cli.ts +2 -1
- package/src/agent-substrate/review-context-pack.ts +365 -0
- package/src/agent-substrate/self-review-cli.ts +291 -20
- package/src/ask-user-question/index.ts +6 -0
- package/src/ask-user-question/inline-resolver.ts +49 -0
- package/src/auto-research/question-ladder-contract.ts +22 -3
- package/src/auto-research/research-turn.ts +359 -17
- package/src/autopilot/agent-loop.ts +1 -1
- package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
- package/src/autopilot/pty-control-loop.ts +8 -1
- package/src/boot/daemon-tools/index.ts +4 -3
- package/src/boot/daemon-tools/self-implement.ts +312 -7
- package/src/boot/daemon-tools/types.ts +2 -0
- package/src/budget/llm-cost.ts +44 -5
- package/src/budget/shadow-arms.ts +52 -0
- package/src/budget/unified-usage.ts +17 -1
- package/src/capture/tui-self-report.ts +14 -0
- package/src/chat/index.ts +33 -7
- package/src/chat/pickers/state.ts +7 -1
- package/src/cli/agent-cli.ts +43 -8
- package/src/cli/intake-cli.ts +22 -1
- package/src/cli/lesson-cli.ts +19 -3
- package/src/cli/log-event-names.ts +6 -0
- package/src/cli/mcp-login.ts +8 -18
- package/src/cli/msg-cli.ts +57 -1
- package/src/cli/pod-cli.ts +17 -2
- package/src/cli/pr-land-heavy-remote.ts +15 -9
- package/src/cli/pty-takeover-cli.ts +67 -7
- package/src/cli/release-cli.ts +79 -7
- package/src/cli/repo-cli.ts +2 -1
- package/src/cli/sim-cli.ts +7 -1
- package/src/cli/tasks-cli.ts +41 -13
- package/src/codex/models.ts +30 -15
- package/src/control-plane/ledger.ts +21 -4
- package/src/core-turn/run-core-turn.ts +47 -3
- package/src/core-turn/run-goal-loop.ts +240 -10
- package/src/core-turn/types.ts +9 -0
- package/src/dashboard/acp-boot.ts +72 -3
- package/src/dashboard/chat/acp-chat.ts +2 -2
- package/src/dashboard/classic-sigquit.ts +16 -0
- package/src/dashboard/index.ts +327 -27
- package/src/dashboard/input/chat-main-interaction-opts.ts +3 -0
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +7 -2
- package/src/dashboard/input/chat-main-slash-command.ts +7 -0
- package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
- package/src/dashboard/input/dashboard-priority-key-route.ts +21 -5
- package/src/dashboard/input/external-input-host.ts +2 -0
- package/src/dashboard/sidetab-runtime.ts +228 -0
- package/src/dashboard/slash-runtime/ask-clarification.ts +7 -1
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -8
- package/src/dashboard/slash-runtime/help-from-registry.ts +2 -2
- package/src/dashboard/term-slash-runtime.ts +16 -18
- package/src/dashboard/turn-preamble.ts +14 -1
- package/src/disk-steward/judge.ts +73 -0
- package/src/disk-steward/ledger.ts +122 -0
- package/src/disk-steward/steward.ts +373 -0
- package/src/domains/guardian-tick.ts +191 -2
- package/src/domains/knowledge.ts +1 -1
- package/src/esc-abort-gate.ts +33 -1
- package/src/execution-loop/launch-gate.ts +206 -1
- package/src/graph-runner/graph-cli.ts +27 -4
- package/src/graph-runner/in-process-registry.ts +37 -0
- package/src/graph-runner/runner.ts +63 -11
- package/src/graph-wizard/generate.ts +16 -7
- package/src/graph-wizard/mission-compose.ts +201 -0
- package/src/grok/agent-search.ts +8 -2
- package/src/grok/credential.ts +5 -0
- package/src/grounding/ref-index.ts +30 -0
- package/src/grounding/ref-vector.ts +61 -0
- package/src/grounding/sources-cli.ts +11 -3
- package/src/grounding/sources.ts +15 -1
- package/src/harness/harness-cli-command.ts +305 -23
- package/src/harness/harness-incidents.ts +312 -7
- package/src/harness/harness-pod-dispatch.ts +7 -1
- package/src/harness/harness-space.ts +15 -1
- package/src/harness/progress-worker-entry.ts +59 -0
- package/src/harness/progress-worker-runtime.ts +11 -0
- package/src/index.ts +35 -5
- package/src/input/external-input-queue.ts +10 -0
- package/src/instance/dev-project.ts +25 -0
- package/src/intake-plane/collect-telegram-saved.ts +36 -1
- package/src/intake-plane/intake-to-tasks.ts +246 -3
- package/src/intelligence-map/model-alias.ts +6 -5
- package/src/intelligence-map/model-catalog.ts +59 -2
- package/src/lessons/friction-ledger.ts +154 -0
- package/src/live-supply/collector.ts +149 -0
- package/src/live-supply/probes.ts +52 -0
- package/src/live-supply/snapshot.ts +40 -0
- package/src/llm/model-defaults.ts +4 -4
- package/src/llm.ts +473 -87
- package/src/loops/orchestrator/tick.ts +56 -0
- package/src/mcp/mcp-oauth.ts +3 -1
- package/src/model-tier/llm-tier-map.ts +9 -9
- package/src/model-tier/turn-tier.ts +1 -1
- package/src/msg/msg-mirror-github.ts +80 -0
- package/src/msg/msg-mirror-policy.ts +61 -0
- package/src/msg/msg-mirror-runner.ts +219 -0
- package/src/msg/msg-mirror-state.ts +33 -0
- package/src/msg/msg-store.ts +11 -0
- package/src/nexus/api/harness-api.ts +2 -1
- package/src/output-truncation.ts +41 -12
- package/src/project/project-store.ts +48 -1
- package/src/pty/snapshot.py +132 -0
- package/src/pty-shell/pty-control-ipc.ts +29 -2
- package/src/pty-shell/pty-manifest.ts +59 -3
- package/src/pty-shell/registry.ts +2 -0
- package/src/pty-shell/tui-run-state.ts +80 -3
- package/src/release-loop/gate-host-reservation.ts +157 -4
- package/src/release-loop/gate-shard-ledger.ts +68 -0
- package/src/release-loop/merged-pr-checklist.ts +25 -2
- package/src/release-loop/rehearsal-shard-plan.ts +6 -1
- package/src/release-loop/rehearsal.ts +27 -0
- package/src/release-loop/release-schedule.ts +90 -1
- package/src/research/bloom-exemplars.ts +135 -0
- package/src/research/edge-decision.ts +124 -15
- package/src/research/gap-followup.ts +249 -0
- package/src/research/lenses/counter-evidence.ts +100 -0
- package/src/research/lenses/working-backwards.ts +89 -0
- package/src/research/plugin-discovery.ts +242 -0
- package/src/research-bridge/invoke.ts +4 -2
- package/src/research-bridge/types.ts +2 -0
- package/src/self-dev/ask-launch-flow.ts +73 -131
- package/src/self-dev/dev-cli.ts +19 -17
- package/src/self-dev/dev-pipeline.ts +8 -4
- package/src/self-dev/graph-front-nodes.ts +23 -0
- package/src/self-dev/graph-journey-route.ts +10 -2
- package/src/self-dev/launch-preflight-text.ts +2 -2
- package/src/self-dev/launch-preflight.ts +47 -11
- package/src/self-dev/orchestrate-cli.ts +1 -0
- package/src/self-dev/orchestrate.ts +15 -0
- package/src/self-dev/run-supervisor.ts +204 -3
- package/src/self-dev/run-types.ts +3 -0
- package/src/self-implement/device-first.ts +8 -2
- package/src/self-implement/doctor-visit.ts +76 -58
- package/src/self-implement/gate-artifacts.ts +16 -0
- package/src/self-implement/gate-baseline.ts +75 -4
- package/src/self-implement/gate-cli.ts +368 -30
- package/src/self-implement/gate-receipt-registry.ts +180 -0
- package/src/self-implement/gate-remote.ts +165 -24
- package/src/self-implement/goal-author-clarification.ts +205 -32
- package/src/self-implement/goal-author-decompose.ts +24 -15
- package/src/self-implement/goal-author.ts +86 -10
- package/src/self-implement/graph-stage-units.ts +172 -0
- package/src/self-implement/graph-templates.ts +162 -1
- package/src/self-implement/graph-variant.ts +37 -4
- package/src/self-implement/host-regate.ts +124 -49
- package/src/self-implement/impl-findings-feedback.ts +188 -0
- package/src/self-implement/knowhow-pack.ts +227 -10
- package/src/self-implement/off-diff-evidence.ts +124 -0
- package/src/self-implement/orchestrator.ts +320 -28
- package/src/self-implement/run-ledger.ts +110 -14
- package/src/self-implement/run-stop.ts +33 -2
- package/src/self-implement/running-runs.ts +35 -12
- package/src/self-implement/seams.ts +6 -3
- package/src/self-implement/self-implement-cli.ts +40 -6
- package/src/self-implement/stop-autoheal-operational.ts +43 -0
- package/src/self-implement/stop-autoheal.ts +15 -6
- package/src/session/chat.ts +66 -15
- package/src/session/request-size-guard.ts +123 -0
- package/src/session-runtime/provider-circuit.ts +140 -0
- package/src/shell-runner/file-engine.ts +1 -0
- package/src/shell-runner/types.ts +4 -0
- package/src/sidetab-core/model.ts +312 -0
- package/src/skills/tools/schedule-wakeup.ts +23 -1
- package/src/skills/tools/shell.ts +51 -5
- package/src/steward/directive-audit.ts +354 -0
- package/src/surface/child-process.ts +31 -0
- package/src/surface/tui-stderr-owner.ts +48 -0
- package/src/task-agent/cover.ts +30 -12
- package/src/task-agent/judge.ts +56 -0
- package/src/task-agent/parent-host.ts +14 -1
- package/src/task-agent/postland-card.ts +35 -2
- package/src/task-agent/rejudge-on-head.ts +46 -2
- package/src/task-agent/repair-cycle.ts +1 -1
- package/src/task-agent/task-hand.ts +133 -25
- package/src/task-agent/tier-split.ts +105 -21
- package/src/task-orchestrator/surfaces/pod-command-job.ts +330 -2
- package/src/task-orchestrator/surfaces/pod-config-snapshot.ts +123 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +70 -2
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
- package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +11 -8
- package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +74 -6
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +2 -1
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +56 -15
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +298 -45
- package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
- package/src/tool-runtime/skill-exec-runtime.ts +95 -5
- package/src/tui/entry.ts +25 -5
- package/src/tui/inline-choice.ts +188 -0
- package/src/tui/sidetab/list.ts +36 -165
- package/src/tui/sidetab/nl-tool.ts +2 -136
- package/src/tui/sidetab/peek.ts +52 -0
- package/src/tui/slash/term.ts +12 -1
- package/src/tui-client/dashboard-session.ts +10 -1
- package/src/user-config.ts +45 -9
- package/src/ux-sim/classic-tui-seat-scenarios.ts +1 -7
- package/src/ux-sim/fake-land-queue.ts +88 -0
- package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
- package/src/ux-sim/fault-pod-child-result.ts +42 -0
- package/src/ux-sim/gate-shard-host-reroute.ts +82 -0
- package/src/ux-sim/goal-interim-trace.ts +24 -0
- package/src/ux-sim/next-tui-seat-scenarios.ts +8 -13
- package/src/ux-sim/release-sim-rig.ts +262 -0
- package/src/ux-sim/seminar-rehearsal.ts +109 -0
- package/src/ux-sim/subagent-tools-device.ts +2 -2
- package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +4 -1
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/explainer.ts +54 -2
- package/src/web-search/grok.ts +32 -6
- package/src/web-search/index.ts +8 -7
- package/src/workflow-runtime/node-catalog.ts +53 -4
- package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +0 -27
- package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +0 -148
- package/apps/pwa/out/_next/static/chunks/2104.70e086d5174a4736.js +0 -1
- package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/214.186a430da28338f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +0 -93
- package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +0 -104
- package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +0 -24
- package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +0 -1
- package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +0 -36
- package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +0 -287
- package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +0 -94
- package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4199-f7c6eb40cd0322f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +0 -32
- package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +0 -90
- package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +0 -79
- package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +0 -74
- package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +0 -286
- package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +0 -1
- package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +0 -8
- package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +0 -62
- package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7488.966b116789f3d41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7524.ce973525d30b3d52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7628.6b16863ac3972333.js +0 -34
- package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7954.4f7a8832663ee979.js +0 -115
- package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +0 -1
- package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +0 -149
- package/apps/pwa/out/_next/static/chunks/9223.cbcc88e0e9950426.js +0 -41
- package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +0 -226
- package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +0 -24
- package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9581.da63ddf41acfad46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +0 -2
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-291ab19fe2acb173.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6b140f77593576eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/bb6987f9.3647e1a23b0e3217.js +0 -122
- package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +0 -1
- package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
- /package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_ssgManifest.js +0 -0
package/src/llm.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Auto-routes based on model name prefix, or picks the first available provider.
|
|
4
4
|
|
|
5
5
|
import { wellFormedLLMMessages } from './llm-wellformed.js';
|
|
6
|
+
import { truncateOutput } from './output-truncation.js';
|
|
6
7
|
import { createHash, randomUUID } from 'node:crypto';
|
|
7
8
|
import { testFakeLlmEnabled, testFakeLlmProvider } from './ux-sim/fake-llm.js';
|
|
8
9
|
import { realpathSync } from 'node:fs';
|
|
@@ -269,6 +270,10 @@ export interface LLMMessage {
|
|
|
269
270
|
role: 'system' | 'user' | 'assistant';
|
|
270
271
|
/** Plain string for text-only messages; ContentBlock[] when images are mixed in. */
|
|
271
272
|
content: string | ContentBlock[];
|
|
273
|
+
/** Codex Responses message phase, preserved when replaying a stateless turn. */
|
|
274
|
+
phase?: 'commentary' | 'partial_answer' | 'final_answer';
|
|
275
|
+
/** Opaque Codex stateless reasoning items, before this assistant message. */
|
|
276
|
+
codexReasoningItems?: Array<{ type: 'reasoning'; encrypted_content: string; summary: unknown[]; id?: string }>;
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
/** JSONSchema-shaped tool definition sent to the provider (no handler). */
|
|
@@ -284,6 +289,21 @@ function toolUseNames(messages: readonly LLMMessage[]): string[] {
|
|
|
284
289
|
: []);
|
|
285
290
|
}
|
|
286
291
|
|
|
292
|
+
/** Return paths for legacy tuple-form `items` schemas without logging schema
|
|
293
|
+
* values. Provider 400s often omit the rejected tool name, so these paths let
|
|
294
|
+
* the next TUI recurrence identify the source contract without logging args. */
|
|
295
|
+
function collectTupleItemsSchemaPaths(schema: unknown, path = 'parameters'): string[] {
|
|
296
|
+
if (Array.isArray(schema)) {
|
|
297
|
+
return schema.flatMap((value, index) => collectTupleItemsSchemaPaths(value, `${path}[${index}]`));
|
|
298
|
+
}
|
|
299
|
+
if (!schema || typeof schema !== 'object') return [];
|
|
300
|
+
return Object.entries(schema as Record<string, unknown>).flatMap(([key, value]) => {
|
|
301
|
+
const childPath = `${path}.${key}`;
|
|
302
|
+
const tuplePath = key === 'items' && Array.isArray(value) ? [childPath] : [];
|
|
303
|
+
return [...tuplePath, ...collectTupleItemsSchemaPaths(value, childPath)];
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
287
307
|
function mapProviderToolNamesInMessages(
|
|
288
308
|
messages: readonly LLMMessage[],
|
|
289
309
|
mapName: (name: string) => string,
|
|
@@ -340,7 +360,10 @@ function restoreProviderToolCallNames(
|
|
|
340
360
|
* (currently Anthropic only) — consumers that don't care should
|
|
341
361
|
* ignore it; `textOnly()` filters it out. */
|
|
342
362
|
export type LLMStreamEvent =
|
|
343
|
-
| { type: 'text'; delta: string }
|
|
363
|
+
| { type: 'text'; delta: string; phase?: 'commentary' | 'final' }
|
|
364
|
+
| { type: 'text_phase'; phase: 'commentary' | 'final' }
|
|
365
|
+
| { type: 'turn_end'; endTurn: boolean }
|
|
366
|
+
| { type: 'codex_reasoning_item'; item: { type: 'reasoning'; encrypted_content: string; summary: unknown[]; id?: string } }
|
|
344
367
|
| {
|
|
345
368
|
type: 'tool_call';
|
|
346
369
|
id: string;
|
|
@@ -415,6 +438,27 @@ export interface LLMOpts {
|
|
|
415
438
|
streamIdleTimeoutMs?: number;
|
|
416
439
|
/** Optional per-request usage telemetry from the provider stream. */
|
|
417
440
|
onUsage?(usage: LLMUsage): void;
|
|
441
|
+
/** Optional request-size policy. Providers that expose a serialized request
|
|
442
|
+
* can compact the normalized conversation before sending it. The callback
|
|
443
|
+
* is opt-in; providers without a serializer hook preserve their wire path. */
|
|
444
|
+
requestSizeGuard?: {
|
|
445
|
+
maxChars: number;
|
|
446
|
+
compactMessages: (
|
|
447
|
+
messages: LLMMessage[],
|
|
448
|
+
info: { provider: string; requestChars: number; maxChars: number },
|
|
449
|
+
) => Promise<{ messages: LLMMessage[]; reason: string } | null>;
|
|
450
|
+
onMeasured?: (info: {
|
|
451
|
+
provider: string;
|
|
452
|
+
requestChars: number;
|
|
453
|
+
maxChars: number;
|
|
454
|
+
compacted: boolean;
|
|
455
|
+
reason: string;
|
|
456
|
+
previousChars?: number;
|
|
457
|
+
}) => void;
|
|
458
|
+
};
|
|
459
|
+
/** Optional cap for text tool results accumulated into the conversation history.
|
|
460
|
+
* Oversized results are spilled to a local file and represented by head/tail. */
|
|
461
|
+
toolResultInlineLimitBytes?: number;
|
|
418
462
|
/** Optional model role attached to this request's usage observation. */
|
|
419
463
|
usageRole?: ModelRole;
|
|
420
464
|
/** Optional per-call metering phase; absent calls keep the existing ledger shape. */
|
|
@@ -476,6 +520,9 @@ export interface LLMOpts {
|
|
|
476
520
|
* 그러면 종결 신호 뒤 빈 턴은 이 스트림에 쓴 답이 없어도 재촉·`[NO FINAL SYNTHESIS]` 없이 빈 답으로 끝낸다.
|
|
477
521
|
* 비우면(기본) 종전대로 — 답이 «어디에도» 없을 때의 경고는 그대로 보인다. */
|
|
478
522
|
terminalPriorAnswerVisible?: boolean;
|
|
523
|
+
/** Goal-loop only: a validated completion ends the provider loop after tool results settle.
|
|
524
|
+
* Omitted for ordinary terminal tools, which may need a further answer turn. */
|
|
525
|
+
terminalCompletionAccepted?: (name: string, args: Record<string, unknown>) => boolean;
|
|
479
526
|
/** Prompt-caching toggle. Honored by Anthropic (attaches
|
|
480
527
|
* cache_control markers on system + tools + 2nd-to-last history
|
|
481
528
|
* message) and OpenAI (passes stream_options.include_usage so
|
|
@@ -1628,7 +1675,8 @@ type ResponsesContentBlock =
|
|
|
1628
1675
|
* codex answer began with "tool_result 가 없다" because, from the
|
|
1629
1676
|
* model's perspective, no tool result ever arrived. */
|
|
1630
1677
|
type ResponsesInputItem =
|
|
1631
|
-
| { role: string; content: ResponsesContentBlock[] }
|
|
1678
|
+
| { role: string; content: ResponsesContentBlock[]; phase?: 'commentary' | 'partial_answer' | 'final_answer' }
|
|
1679
|
+
| { type: 'reasoning'; encrypted_content: string; summary: unknown[]; id?: string }
|
|
1632
1680
|
| { type: 'function_call'; name: string; arguments: string; call_id: string }
|
|
1633
1681
|
// Image-pipeline P3 (2026-05-05) — `output` accepts string OR a
|
|
1634
1682
|
// ContentItem[] mirroring the user-message content block shape.
|
|
@@ -1655,6 +1703,8 @@ type ResponsesInputItem =
|
|
|
1655
1703
|
* Codex variants — current gpt-5 family all true per allowlist). */
|
|
1656
1704
|
export interface MessagesToResponsesInputOpts {
|
|
1657
1705
|
acceptToolImages?: boolean;
|
|
1706
|
+
/** Server-stored continuation already contains prior reasoning items. */
|
|
1707
|
+
replayCodexReasoning?: boolean;
|
|
1658
1708
|
acceptUserMessageImages?: boolean;
|
|
1659
1709
|
onOrphanToolResult?: (callId: string) => void;
|
|
1660
1710
|
}
|
|
@@ -1827,6 +1877,9 @@ export function messagesToResponsesInput(
|
|
|
1827
1877
|
continue;
|
|
1828
1878
|
}
|
|
1829
1879
|
|
|
1880
|
+
// Stateless Codex requires the opaque reasoning item in its original
|
|
1881
|
+
// position before the corresponding assistant message/tool calls.
|
|
1882
|
+
if (m.role === 'assistant' && opts.replayCodexReasoning !== false && m.codexReasoningItems) input.push(...m.codexReasoningItems);
|
|
1830
1883
|
// Assemble the final content block list: text first (matches
|
|
1831
1884
|
// how users usually write prompts — "describe this image" then
|
|
1832
1885
|
// attachment), images second. Responses API is order-sensitive
|
|
@@ -1844,7 +1897,7 @@ export function messagesToResponsesInput(
|
|
|
1844
1897
|
// representation — emitting an empty Message wrapper would just
|
|
1845
1898
|
// add a stray empty-text block to the conversation history.
|
|
1846
1899
|
if (content.length > 0) {
|
|
1847
|
-
input.push({ role: m.role, content });
|
|
1900
|
+
input.push({ role: m.role, content, ...(m.role === 'assistant' && m.phase ? { phase: m.phase } : {}) });
|
|
1848
1901
|
}
|
|
1849
1902
|
input.push(...trailingItems);
|
|
1850
1903
|
}
|
|
@@ -2175,6 +2228,41 @@ function mapReasoningLevelToGeminiThinking(
|
|
|
2175
2228
|
/** Stream against /responses (Codex, OpenAI Responses API). Exported
|
|
2176
2229
|
* so tests can exercise header/body shape without spinning up the
|
|
2177
2230
|
* full makeCodexProvider path. */
|
|
2231
|
+
function buildCodexResponsesWireBody(
|
|
2232
|
+
body: {
|
|
2233
|
+
model: string;
|
|
2234
|
+
instructions: string;
|
|
2235
|
+
input: unknown[];
|
|
2236
|
+
tools?: unknown[];
|
|
2237
|
+
tool_choice?: unknown;
|
|
2238
|
+
reasoning?: { effort?: 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'; summary?: 'auto' | 'concise' | 'detailed' };
|
|
2239
|
+
store?: boolean;
|
|
2240
|
+
previousResponseId?: string;
|
|
2241
|
+
},
|
|
2242
|
+
promptCache: boolean,
|
|
2243
|
+
): Record<string, unknown> {
|
|
2244
|
+
const cacheKey = promptCache ? computePromptCacheKey(body.model, body.instructions) : undefined;
|
|
2245
|
+
return {
|
|
2246
|
+
...body,
|
|
2247
|
+
stream: true,
|
|
2248
|
+
store: body.store === true,
|
|
2249
|
+
...(body.tool_choice ? { tool_choice: body.tool_choice } : {}),
|
|
2250
|
+
...(body.previousResponseId ? { previous_response_id: body.previousResponseId } : {}),
|
|
2251
|
+
...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
|
|
2252
|
+
...(body.reasoning ? {
|
|
2253
|
+
reasoning: body.reasoning,
|
|
2254
|
+
include: ['reasoning.encrypted_content'],
|
|
2255
|
+
} : {}),
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
function serializeCodexResponsesWireBody(
|
|
2260
|
+
body: Parameters<typeof buildCodexResponsesWireBody>[0],
|
|
2261
|
+
promptCache: boolean,
|
|
2262
|
+
): string {
|
|
2263
|
+
return JSON.stringify(buildCodexResponsesWireBody(body, promptCache));
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2178
2266
|
export async function* streamCodexResponsesEvents(
|
|
2179
2267
|
url: string,
|
|
2180
2268
|
bearer: string,
|
|
@@ -2221,6 +2309,9 @@ export async function* streamCodexResponsesEvents(
|
|
|
2221
2309
|
/** Opt-out hook (tests). When false, skips the
|
|
2222
2310
|
* `prompt_cache_key` body field; production always passes true. */
|
|
2223
2311
|
promptCache?: boolean;
|
|
2312
|
+
/** Exact body string already serialized by the request-size preflight.
|
|
2313
|
+
* Passing it through ensures the measured bytes are the bytes fetched. */
|
|
2314
|
+
serializedWireBody?: string;
|
|
2224
2315
|
/** Callback fired when the SSE stream emits `response.created`
|
|
2225
2316
|
* carrying the new response's id. Used by makeCodexProvider to
|
|
2226
2317
|
* capture the id and thread `previousResponseId` on the next
|
|
@@ -2274,32 +2365,16 @@ export async function* streamCodexResponsesEvents(
|
|
|
2274
2365
|
// value codex_cli_rs ships) so the backend
|
|
2275
2366
|
// applies the same tool/SSE conventions
|
|
2276
2367
|
const promptCache = extra.promptCache !== false;
|
|
2277
|
-
|
|
2278
|
-
//
|
|
2279
|
-
const wireBody
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
// to send only the delta input items. Default still false so any
|
|
2285
|
-
// caller that doesn't opt in (tests building bodies directly)
|
|
2286
|
-
// sees the legacy wire shape.
|
|
2368
|
+
// TUI-REQUEST-SIZE-COMPACT — the exact serializer is shared with the
|
|
2369
|
+
// preflight guard, so the size decision and fetch body cannot drift.
|
|
2370
|
+
const wireBody = buildCodexResponsesWireBody(body, promptCache);
|
|
2371
|
+
const wireJson = extra.serializedWireBody ?? JSON.stringify(wireBody);
|
|
2372
|
+
debug.log('llm.request', 'request-size', {
|
|
2373
|
+
provider: 'openai-codex',
|
|
2374
|
+
requestChars: wireJson.length,
|
|
2287
2375
|
store: body.store === true,
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
|
|
2291
|
-
// Reasoning opt-in (gpt-5 family). When the caller sets
|
|
2292
|
-
// body.reasoning we mirror it onto the wire AND append the
|
|
2293
|
-
// include directive that lets multi-turn requests thread the
|
|
2294
|
-
// model's encrypted reasoning context. Omit both when absent
|
|
2295
|
-
// so non-reasoning models / opt-out users see the legacy
|
|
2296
|
-
// wire shape unchanged.
|
|
2297
|
-
...(body.reasoning ? {
|
|
2298
|
-
reasoning: body.reasoning,
|
|
2299
|
-
include: ['reasoning.encrypted_content'],
|
|
2300
|
-
} : {}),
|
|
2301
|
-
};
|
|
2302
|
-
const wireJson = JSON.stringify(wireBody);
|
|
2376
|
+
previousResponseId: body.previousResponseId ? `${body.previousResponseId.slice(0, 12)}…` : null,
|
|
2377
|
+
});
|
|
2303
2378
|
const response = await fetchApiWithRetry(
|
|
2304
2379
|
url,
|
|
2305
2380
|
{
|
|
@@ -2328,6 +2403,10 @@ export async function* streamCodexResponsesEvents(
|
|
|
2328
2403
|
debug.log('llm.response.status', `codex ${response.status}`, {
|
|
2329
2404
|
url, status: response.status, elapsedMs: Date.now() - startedAt,
|
|
2330
2405
|
});
|
|
2406
|
+
let messagePhase: 'commentary' | 'final' | undefined;
|
|
2407
|
+
const codexTextPhase = (value: unknown): 'commentary' | 'final' | undefined =>
|
|
2408
|
+
value === 'commentary' || value === 'partial_answer' ? 'commentary'
|
|
2409
|
+
: value === 'final_answer' ? 'final' : undefined;
|
|
2331
2410
|
try {
|
|
2332
2411
|
for await (const line of sseLineStream(response.body!)) {
|
|
2333
2412
|
if (!line.startsWith('data: ')) continue;
|
|
@@ -2347,15 +2426,32 @@ export async function* streamCodexResponsesEvents(
|
|
|
2347
2426
|
extra.onResponseCreated(id);
|
|
2348
2427
|
}
|
|
2349
2428
|
}
|
|
2429
|
+
if (type === 'response.output_item.added' || type === 'response.output_item.done') {
|
|
2430
|
+
if (parsed.item?.type === 'message') {
|
|
2431
|
+
const phase = codexTextPhase(parsed.item.phase);
|
|
2432
|
+
if (type === 'response.output_item.added') messagePhase = phase;
|
|
2433
|
+
if (phase) {
|
|
2434
|
+
messagePhase = phase;
|
|
2435
|
+
yield { type: 'text_phase', phase };
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2350
2439
|
// Text tokens — both `response.output_text.delta` (new naming) and
|
|
2351
2440
|
// raw `output_text.delta` (older streams).
|
|
2352
2441
|
if (type.endsWith('output_text.delta')) {
|
|
2353
2442
|
const delta = parsed.delta || '';
|
|
2354
|
-
if (delta) yield { type: 'text', delta };
|
|
2443
|
+
if (delta) yield { type: 'text', delta, ...(messagePhase ? { phase: messagePhase } : {}) };
|
|
2355
2444
|
}
|
|
2356
2445
|
// Tool calls come in as `response.output_item.done` with item.type = 'function_call'.
|
|
2357
2446
|
if (type === 'response.output_item.done') {
|
|
2358
2447
|
const item = parsed.item;
|
|
2448
|
+
if (item?.type === 'reasoning' && typeof item.encrypted_content === 'string' && item.encrypted_content) {
|
|
2449
|
+
yield { type: 'codex_reasoning_item', item: {
|
|
2450
|
+
type: 'reasoning', encrypted_content: item.encrypted_content,
|
|
2451
|
+
summary: Array.isArray(item.summary) ? item.summary : [],
|
|
2452
|
+
...(typeof item.id === 'string' ? { id: item.id } : {}),
|
|
2453
|
+
} };
|
|
2454
|
+
}
|
|
2359
2455
|
if (item && item.type === 'function_call') {
|
|
2360
2456
|
let args: Record<string, unknown> = {};
|
|
2361
2457
|
try { args = item.arguments ? JSON.parse(item.arguments) : {}; } catch { /* empty */ }
|
|
@@ -2410,6 +2506,9 @@ export async function* streamCodexResponsesEvents(
|
|
|
2410
2506
|
// hit rate for codex regardless. See store=true wave (next+1)
|
|
2411
2507
|
// for the volume-side counterpart.
|
|
2412
2508
|
if (type === 'response.completed') {
|
|
2509
|
+
if (typeof parsed.response?.end_turn === 'boolean') {
|
|
2510
|
+
yield { type: 'turn_end', endTurn: parsed.response.end_turn };
|
|
2511
|
+
}
|
|
2413
2512
|
const u = parsed.response?.usage;
|
|
2414
2513
|
if (u && typeof u === 'object') {
|
|
2415
2514
|
const cached = u.input_tokens_details?.cached_tokens
|
|
@@ -4474,6 +4573,13 @@ function explainAndLogFallbackCandidates(
|
|
|
4474
4573
|
|
|
4475
4574
|
/** 스트림 중 끊김 재시도 상한 — codex `DEFAULT_STREAM_MAX_RETRIES`(5) 와 같은 값. */
|
|
4476
4575
|
const STREAM_DISCONNECT_MAX_RETRIES = 5;
|
|
4576
|
+
class StreamIdleTimeoutError extends Error {
|
|
4577
|
+
constructor(readonly idleMs: number) {
|
|
4578
|
+
super(`stream idle timeout after ${idleMs}ms`);
|
|
4579
|
+
this.name = 'StreamIdleTimeoutError';
|
|
4580
|
+
Object.assign(this, { code: 'ETIMEDOUT' });
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4477
4583
|
const STREAM_IDLE_TIMEOUT_MAX_RETRIES = 1;
|
|
4478
4584
|
let streamDisconnectRetryDelayMs: (attempt: number) => number = backoffMs;
|
|
4479
4585
|
|
|
@@ -4799,6 +4905,20 @@ type ProviderTextResult =
|
|
|
4799
4905
|
| { ok: true; full: string; usage: ObservedUsage | 'unmeasured' }
|
|
4800
4906
|
| { ok: false; full: string; error: unknown };
|
|
4801
4907
|
|
|
4908
|
+
function resolveStreamIdleTimeout(opts: LLMOpts, provider: LLMProvider, model: string): { ms: number; source: 'call' | 'config' | 'default' } {
|
|
4909
|
+
if (opts.streamIdleTimeoutMs !== undefined
|
|
4910
|
+
&& Number.isFinite(opts.streamIdleTimeoutMs) && opts.streamIdleTimeoutMs > 0) {
|
|
4911
|
+
return { ms: opts.streamIdleTimeoutMs, source: 'call' };
|
|
4912
|
+
}
|
|
4913
|
+
let configured: number | undefined;
|
|
4914
|
+
try {
|
|
4915
|
+
configured = (require('./user-config.js') as typeof import('./user-config.js')).getUserConfig().llm.streamIdleTimeoutMs;
|
|
4916
|
+
} catch { /* config lookup is fail-soft; preserve provider defaults */ }
|
|
4917
|
+
if (configured !== undefined) return { ms: configured, source: 'config' };
|
|
4918
|
+
const longIdle = usesLongReasoningIdle(getModelFamily(model), provider.name);
|
|
4919
|
+
return { ms: longIdle ? STREAM_IDLE_TIMEOUT_MS_REASONING : STREAM_IDLE_TIMEOUT_MS, source: 'default' };
|
|
4920
|
+
}
|
|
4921
|
+
|
|
4802
4922
|
function accumulateUsage(usage: ObservedUsage | undefined, received: LLMUsage): ObservedUsage {
|
|
4803
4923
|
const total = { ...usage };
|
|
4804
4924
|
for (const field of ['inputTokens', 'outputTokens', 'cacheReadInputTokens', 'cacheCreationInputTokens', 'reportedCostUsd'] as const) {
|
|
@@ -4836,10 +4956,8 @@ async function consumeProviderText(
|
|
|
4836
4956
|
// Preserve the provider or callback error that caused this close.
|
|
4837
4957
|
}
|
|
4838
4958
|
};
|
|
4839
|
-
const
|
|
4840
|
-
|
|
4841
|
-
? STREAM_IDLE_TIMEOUT_MS_REASONING : STREAM_IDLE_TIMEOUT_MS
|
|
4842
|
-
);
|
|
4959
|
+
const idleTimeout = resolveStreamIdleTimeout(opts, provider, opts.model ?? provider.defaultModel);
|
|
4960
|
+
const idleMs = idleTimeout.ms;
|
|
4843
4961
|
|
|
4844
4962
|
while (true) {
|
|
4845
4963
|
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
@@ -4860,10 +4978,9 @@ async function consumeProviderText(
|
|
|
4860
4978
|
if (step.idle) {
|
|
4861
4979
|
debug.log('llm', 'stream-idle-timeout', {
|
|
4862
4980
|
provider: provider.name, model: opts.model ?? provider.defaultModel,
|
|
4863
|
-
idleMs, receivedChars: full.length, site: 'stream-llm',
|
|
4981
|
+
idleMs, idleMsSource: idleTimeout.source, receivedChars: full.length, site: 'stream-llm',
|
|
4864
4982
|
});
|
|
4865
|
-
const error = new
|
|
4866
|
-
error.name = 'StreamIdleTimeoutError';
|
|
4983
|
+
const error = new StreamIdleTimeoutError(idleMs);
|
|
4867
4984
|
// An async generator can leave return() pending behind a stalled next().
|
|
4868
4985
|
void closeIterator();
|
|
4869
4986
|
return { ok: false, full, error };
|
|
@@ -4945,6 +5062,7 @@ export async function streamLLM(
|
|
|
4945
5062
|
textChars: full.length,
|
|
4946
5063
|
usage: consumed.usage,
|
|
4947
5064
|
});
|
|
5065
|
+
(await import('./session-runtime/provider-circuit.js')).recordProviderSuccess(activeProvider.name, Date.now());
|
|
4948
5066
|
return full;
|
|
4949
5067
|
}
|
|
4950
5068
|
|
|
@@ -4962,6 +5080,7 @@ export async function streamLLM(
|
|
|
4962
5080
|
status: err.status,
|
|
4963
5081
|
kind: err.kind,
|
|
4964
5082
|
}, { level: 'warn' });
|
|
5083
|
+
(await import('./session-runtime/provider-circuit.js')).recordProviderFailure(activeProvider.name, 'overload-failover', Date.now());
|
|
4965
5084
|
activeProvider = switched;
|
|
4966
5085
|
modelOverride = undefined;
|
|
4967
5086
|
attemptedProviders.add(activeProvider.name);
|
|
@@ -4998,6 +5117,7 @@ export async function streamLLM(
|
|
|
4998
5117
|
tools: opts.tools?.map((tool) => tool.name) ?? [],
|
|
4999
5118
|
...(opts.model ? { modelOverrideDropped: opts.model } : {}),
|
|
5000
5119
|
}, { level: 'warn' });
|
|
5120
|
+
(await import('./session-runtime/provider-circuit.js')).recordProviderFailure(activeProvider.name, verdict.category, Date.now());
|
|
5001
5121
|
activeProvider = fallback;
|
|
5002
5122
|
modelOverride = undefined;
|
|
5003
5123
|
attemptedProviders.add(activeProvider.name);
|
|
@@ -5123,7 +5243,7 @@ export function resolveDefaultProvider(model?: string): LLMProvider {
|
|
|
5123
5243
|
export interface StreamWithToolsHandlers {
|
|
5124
5244
|
/** Streaming text delta — invoked for every text chunk across all turns. */
|
|
5125
5245
|
onText(delta: string, fullAcrossTurns: string): void;
|
|
5126
|
-
/**
|
|
5246
|
+
/** Transient user-visible status emitted by stream recovery (not model text). */
|
|
5127
5247
|
onStatus?(line: string): void;
|
|
5128
5248
|
/** Dispatch a tool call. Returning undefined/null throws in the loop so
|
|
5129
5249
|
* callers should always return a JSON-serializable value (can be an
|
|
@@ -7390,13 +7510,33 @@ function resultText(result: unknown): string {
|
|
|
7390
7510
|
}
|
|
7391
7511
|
|
|
7392
7512
|
/** skill_exec returns structured host metadata, but its model-facing tool result is the answer body. */
|
|
7393
|
-
function toolResultContentForCall(toolName: string, result: unknown): string {
|
|
7513
|
+
function toolResultContentForCall(toolName: string, result: unknown, inlineLimitBytes?: number): string {
|
|
7514
|
+
let content: string;
|
|
7394
7515
|
if (toolName === 'skill_exec' && result && typeof result === 'object'
|
|
7395
7516
|
&& typeof (result as { output?: unknown }).output === 'string') {
|
|
7396
|
-
|
|
7397
|
-
|
|
7517
|
+
content = (result as { ok?: unknown }).ok === false
|
|
7518
|
+
? JSON.stringify(result)
|
|
7519
|
+
: (result as { output: string }).output;
|
|
7520
|
+
} else {
|
|
7521
|
+
content = typeof result === 'string' ? result : JSON.stringify(result);
|
|
7522
|
+
}
|
|
7523
|
+
if (!Number.isFinite(inlineLimitBytes) || inlineLimitBytes === undefined || inlineLimitBytes <= 0) return content;
|
|
7524
|
+
const safeToolName = toolName.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 48) || 'tool';
|
|
7525
|
+
const capped = truncateOutput(content, {
|
|
7526
|
+
toolName: `tool-result-${safeToolName}`,
|
|
7527
|
+
inlineLimit: inlineLimitBytes,
|
|
7528
|
+
headBytes: Math.floor(inlineLimitBytes * 0.6),
|
|
7529
|
+
tailBytes: Math.floor(inlineLimitBytes * 0.2),
|
|
7530
|
+
});
|
|
7531
|
+
if (capped.spilled) {
|
|
7532
|
+
debug.log('llm.router', 'tool-result.output-spilled', {
|
|
7533
|
+
tool: safeToolName,
|
|
7534
|
+
originalBytes: capped.originalBytes,
|
|
7535
|
+
inlineLimitBytes,
|
|
7536
|
+
savedPath: capped.savedPath,
|
|
7537
|
+
});
|
|
7398
7538
|
}
|
|
7399
|
-
return
|
|
7539
|
+
return capped.output;
|
|
7400
7540
|
}
|
|
7401
7541
|
|
|
7402
7542
|
function detectExecutionFailure(result: unknown): boolean {
|
|
@@ -8336,12 +8476,42 @@ export async function streamLLMWithTools(
|
|
|
8336
8476
|
skipProviders?: readonly string[];
|
|
8337
8477
|
} = {},
|
|
8338
8478
|
): Promise<string> {
|
|
8479
|
+
// Fixed providers keep the original synchronous launch path. Load the circuit before
|
|
8480
|
+
// selection only when it can change this turn's initial provider.
|
|
8481
|
+
const circuit = !opts.provider && !opts.initialProvider
|
|
8482
|
+
? await import('./session-runtime/provider-circuit.js')
|
|
8483
|
+
: undefined;
|
|
8339
8484
|
const provider = opts.provider ?? opts.initialProvider ?? resolveDefaultProvider(opts.model);
|
|
8340
8485
|
const initial = finalizeStreamingProviderModel(provider, opts.model ?? provider.defaultModel);
|
|
8341
8486
|
let activeProvider = initial.provider;
|
|
8342
8487
|
let activeModel = initial.model;
|
|
8488
|
+
let circuitBlockedInitialProvider: string | undefined;
|
|
8489
|
+
// SURVIVAL-PROVIDER-CIRCUIT (S2): 명시·고정 공급자(`provider`·`initialProvider`)가 없고
|
|
8490
|
+
// 회로가 열려(open) 있으면, 체인 해석(`remainingFallbackProviderNames` 순서)의 첫
|
|
8491
|
+
// closed(없으면 half-open) 공급자로 이번 턴을 연다. 모두 open 이면 원래 1순위를 그대로
|
|
8492
|
+
// 쓴다(«갈 곳 없음 = 멈춤»이 아니다) — `circuit-all-open` 관측만 남긴다.
|
|
8493
|
+
if (circuit && circuit.circuitState(activeProvider.name, Date.now()) === 'open') {
|
|
8494
|
+
const chainNames = [activeProvider.name, ...remainingFallbackProviderNames(new Set(opts.skipProviders ?? []), opts)]
|
|
8495
|
+
.filter((name) => providerByName(name)?.available());
|
|
8496
|
+
const pickedName = circuit.pickInitialProvider(chainNames, Date.now());
|
|
8497
|
+
const picked = pickedName && pickedName !== activeProvider.name ? providerByName(pickedName) : undefined;
|
|
8498
|
+
if (picked && picked.available()) {
|
|
8499
|
+
circuitBlockedInitialProvider = activeProvider.name;
|
|
8500
|
+
debug.log('tui.survival', 'turn-opened-on-fallback', { blocked: activeProvider.name, opened: picked.name });
|
|
8501
|
+
handlers.onStatus?.(`⚡ ${activeProvider.name} 회로 열림 — 이번 턴은 ${picked.name}`);
|
|
8502
|
+
const pickedInitial = finalizeStreamingProviderModel(picked, picked.defaultModel);
|
|
8503
|
+
activeProvider = pickedInitial.provider;
|
|
8504
|
+
activeModel = pickedInitial.model;
|
|
8505
|
+
} else {
|
|
8506
|
+
debug.log('tui.survival', 'circuit-all-open', { blocked: activeProvider.name });
|
|
8507
|
+
}
|
|
8508
|
+
}
|
|
8343
8509
|
opts.onResolvedProvider?.(activeProvider.name, activeModel);
|
|
8344
|
-
const attemptedProviders = new Set<string>([
|
|
8510
|
+
const attemptedProviders = new Set<string>([
|
|
8511
|
+
activeProvider.name,
|
|
8512
|
+
...(circuitBlockedInitialProvider ? [circuitBlockedInitialProvider] : []),
|
|
8513
|
+
...(opts.skipProviders ?? []),
|
|
8514
|
+
]);
|
|
8345
8515
|
let blockedProviders: ProviderFallbackAttempt[] = [];
|
|
8346
8516
|
const effectiveModel = activeModel;
|
|
8347
8517
|
const modelFamily = getModelFamily(effectiveModel);
|
|
@@ -8361,7 +8531,7 @@ export async function streamLLMWithTools(
|
|
|
8361
8531
|
tools: opts.tools?.map(t => t.name),
|
|
8362
8532
|
toolsAvailable: !!provider.streamChat && (opts.tools?.length ?? 0) > 0,
|
|
8363
8533
|
}, { level: 'info' });
|
|
8364
|
-
if (!provider.streamChat ||
|
|
8534
|
+
if (!provider.streamChat || ((opts.tools?.length ?? 0) === 0 && provider.name !== 'openai-codex')) {
|
|
8365
8535
|
// No tool support or no tools requested — delegate to plain streamLLM.
|
|
8366
8536
|
debug.log('llm.router', 'streamLLMWithTools.fallback', {
|
|
8367
8537
|
reason: !provider.streamChat ? 'provider-no-streamChat' : 'no-tools',
|
|
@@ -8522,6 +8692,8 @@ export async function streamLLMWithTools(
|
|
|
8522
8692
|
}
|
|
8523
8693
|
// OR-DEEPSEEK-TUNE — 마지막 턴의 «도구 없는» 추론(deepseek/* 만 채워진다). 매 턴 시작에 비운다.
|
|
8524
8694
|
let turnReasoningDetails: Array<Record<string, unknown>> | undefined;
|
|
8695
|
+
let turnCodexReasoningItems: LLMMessage['codexReasoningItems'];
|
|
8696
|
+
let turnCodexTextPhase: LLMMessage['phase'];
|
|
8525
8697
|
const emitTurnComplete = (finalAssistantText?: string): void => {
|
|
8526
8698
|
if (finalAssistantText && finalAssistantText.length > 0) {
|
|
8527
8699
|
history.push({
|
|
@@ -8530,18 +8702,23 @@ export async function streamLLMWithTools(
|
|
|
8530
8702
|
type: 'text', text: finalAssistantText,
|
|
8531
8703
|
...(turnReasoningDetails ? { providerMeta: { reasoningDetails: turnReasoningDetails } } : {}),
|
|
8532
8704
|
}],
|
|
8705
|
+
...(turnCodexReasoningItems?.length ? { codexReasoningItems: turnCodexReasoningItems } : {}),
|
|
8706
|
+
...(turnCodexTextPhase ? { phase: turnCodexTextPhase } : {}),
|
|
8533
8707
|
});
|
|
8534
8708
|
}
|
|
8535
8709
|
handlers.onTurnComplete?.(history.slice(initialHistoryLen));
|
|
8536
8710
|
};
|
|
8537
8711
|
let fullText = '';
|
|
8538
8712
|
let sawToolRound = false;
|
|
8713
|
+
let codexContinuationNeeded = false;
|
|
8539
8714
|
// TUI-GOAL-LOOP-FRICTION — 종결 신호 도구(LLMOpts.terminalToolNames). 비면 아래 분기는 전부 꺼진다.
|
|
8540
8715
|
const terminalToolNames: ReadonlySet<string> = new Set(opts.terminalToolNames ?? []);
|
|
8541
8716
|
/** 이 스트림에서 종결 신호를 한 번이라도 디스패치했나. */
|
|
8542
8717
|
let terminalSignalDispatched = false;
|
|
8718
|
+
let completionNeedsFinalTurn = false;
|
|
8543
8719
|
/** 도구 라운드에 실린 마지막 «비지 않은» 답 텍스트(종결 신호 턴 마감·빈 턴 포기에서 쓴다). */
|
|
8544
8720
|
let lastToolRoundText = '';
|
|
8721
|
+
let lastToolRoundTextPhase: 'commentary' | 'final' | undefined;
|
|
8545
8722
|
const agentBatchTickIntervalMs = opts.agentBatchTickIntervalMs ?? BATCH_TICK_INTERVAL_MS;
|
|
8546
8723
|
|
|
8547
8724
|
// Empty-turn guard: smaller models (gpt-5.4-mini, haiku) sometimes
|
|
@@ -8715,7 +8892,7 @@ export async function streamLLMWithTools(
|
|
|
8715
8892
|
let loopTermination: 'budget-exhausted' | 'aborted' = 'budget-exhausted';
|
|
8716
8893
|
let terminalTurn = maxTurns;
|
|
8717
8894
|
|
|
8718
|
-
for (let turn = 0; turn < maxTurns; turn++) {
|
|
8895
|
+
for (let turn = 0; turn < maxTurns || ((completionNeedsFinalTurn || codexContinuationNeeded) && turn === maxTurns); turn++) {
|
|
8719
8896
|
// ★ /cancel 범용화(대표 2026-07-12) — abort 신호가 오면 tool 루프를 즉시 중단한다. 이게 없으면
|
|
8720
8897
|
// fetch 는 abort 돼도 루프가 다음 turn 으로 계속 돌아 tool 반복(grep 폭주 등)이 멈추지 않는다.
|
|
8721
8898
|
if (opts.signal?.aborted) {
|
|
@@ -8748,6 +8925,11 @@ export async function streamLLMWithTools(
|
|
|
8748
8925
|
}
|
|
8749
8926
|
}
|
|
8750
8927
|
let turnText = '';
|
|
8928
|
+
let turnTextPhase: 'commentary' | 'final' | undefined;
|
|
8929
|
+
let turnEnd: boolean | undefined;
|
|
8930
|
+
codexContinuationNeeded = false;
|
|
8931
|
+
turnCodexReasoningItems = undefined;
|
|
8932
|
+
turnCodexTextPhase = undefined;
|
|
8751
8933
|
// TUI-TURN-NARRATION — 이 라운드에 받은 추론 요약 글자 수(내용은 남기지 않는다).
|
|
8752
8934
|
let turnReasoningChars = 0;
|
|
8753
8935
|
turnReasoningDetails = undefined;
|
|
@@ -8763,7 +8945,7 @@ export async function streamLLMWithTools(
|
|
|
8763
8945
|
turn, historyLen: history.length,
|
|
8764
8946
|
}, { level: 'info' });
|
|
8765
8947
|
|
|
8766
|
-
//
|
|
8948
|
+
// Turn context backstop — with the codex/unlimited tool-loop cap
|
|
8767
8949
|
// removed, a long tool-heavy turn (esp. mission build) grows `history`
|
|
8768
8950
|
// unbounded until it overflows the model context window and the provider
|
|
8769
8951
|
// throws. ref codex / opencode bound exploration by COMPACTION, not a
|
|
@@ -8847,13 +9029,14 @@ export async function streamLLMWithTools(
|
|
|
8847
9029
|
// revert to auto (see above).
|
|
8848
9030
|
const buildTurnOpts = () => {
|
|
8849
9031
|
const remainingFallbacks = opts.provider ? 0 : remainingFallbackProviderNames(attemptedProviders, opts).length;
|
|
8850
|
-
const base = { ...opts, model: activeModel, tools: activeTools, remainingFallbacks };
|
|
9032
|
+
const base = { ...opts, model: activeModel, tools: completionNeedsFinalTurn ? [] : activeTools, remainingFallbacks };
|
|
8851
9033
|
return turn === 0 ? base : { ...base, toolChoice: undefined };
|
|
8852
9034
|
};
|
|
8853
9035
|
let turnOpts = buildTurnOpts();
|
|
8854
9036
|
// reasoning-heavy family(codex/claude/gemini/grok/local)는 침묵 추론 구간이 길어 idle watchdog 를
|
|
8855
9037
|
// family-aware 로 상향(2026-07-19 goal-exec → 아크4 전-family). fast/mini(gpt·other)만 45s 유지.
|
|
8856
|
-
const
|
|
9038
|
+
const idleTimeout = resolveStreamIdleTimeout(opts, activeProvider, activeModel);
|
|
9039
|
+
const streamIdleTimeoutMs = idleTimeout.ms;
|
|
8857
9040
|
// ⚠️ hang 진단(2026-07-17) — codex SSE 스트림이 완료 신호 없이 안 끝나는 케이스 확정용.
|
|
8858
9041
|
// consume-start 후 consume-end 가 없으면 = for-await(streamChat SSE) hang. lastEvType 로 어느
|
|
8859
9042
|
// 이벤트 뒤에서 멈추는지(usage/text/tool_call) 특정. 무장할 상한도 남겨 실험 런을 구별한다.
|
|
@@ -8862,6 +9045,7 @@ export async function streamLLMWithTools(
|
|
|
8862
9045
|
provider: activeProvider.name,
|
|
8863
9046
|
model: activeModel,
|
|
8864
9047
|
idleMs: streamIdleTimeoutMs,
|
|
9048
|
+
idleMsSource: idleTimeout.source,
|
|
8865
9049
|
...(modelFamily !== 'other' ? { modelFamily } : {}),
|
|
8866
9050
|
});
|
|
8867
9051
|
let evCount = 0;
|
|
@@ -8870,6 +9054,7 @@ export async function streamLLMWithTools(
|
|
|
8870
9054
|
// 다시 건다(codex `stream_max_retries` 동형). 이 턴에서 받은 tool_call·image 가 있으면 재시도하지 않는다.
|
|
8871
9055
|
let streamDisconnectRetries = 0;
|
|
8872
9056
|
let streamIdleTimeoutRetries = 0;
|
|
9057
|
+
let streamIdleRetryPartial = '';
|
|
8873
9058
|
let streamEmittedImage = false;
|
|
8874
9059
|
// 끊긴 조각을 이력(turnText·fullText)에서 «조용히» 되감는다 — 화면엔 지우라고 알리지 않는다
|
|
8875
9060
|
// (TUI 의 빈 델타는 «커밋»이다). 이미 보인 글과 겹치는 재시도 본문은 `retryDisplay` 가 막는다.
|
|
@@ -8887,6 +9072,7 @@ export async function streamLLMWithTools(
|
|
|
8887
9072
|
if (turnText.length > 0) {
|
|
8888
9073
|
fullText = fullText.slice(0, fullText.length - turnText.length);
|
|
8889
9074
|
turnText = '';
|
|
9075
|
+
turnTextPhase = undefined;
|
|
8890
9076
|
}
|
|
8891
9077
|
turnReasoningChars = 0;
|
|
8892
9078
|
retryDisplay.arm();
|
|
@@ -8898,6 +9084,19 @@ export async function streamLLMWithTools(
|
|
|
8898
9084
|
// 이벤트 대기를 idle timeout 과 race — STREAM_IDLE_TIMEOUT_MS 무이벤트면 스트림 정리·탈출(받은
|
|
8899
9085
|
// 텍스트로 진행). 35분 무한 hang → 자동 복구. 원인 규명은 consume-end 부재/idle-timeout 로그가.
|
|
8900
9086
|
let streamIter: AsyncIterator<LLMStreamEvent> | undefined;
|
|
9087
|
+
let streamAttemptAbort: AbortController | undefined;
|
|
9088
|
+
let streamAttemptAbortCleanup: (() => void) | undefined;
|
|
9089
|
+
const releaseStreamAttempt = (abort = false, reason?: unknown): void => {
|
|
9090
|
+
if (abort) streamAttemptAbort?.abort(reason);
|
|
9091
|
+
streamAttemptAbortCleanup?.();
|
|
9092
|
+
streamAttemptAbortCleanup = undefined;
|
|
9093
|
+
streamAttemptAbort = undefined;
|
|
9094
|
+
};
|
|
9095
|
+
let providerToolSchemaDiagnostics: Record<string, {
|
|
9096
|
+
originalName: string;
|
|
9097
|
+
providerName: string;
|
|
9098
|
+
tupleItemsPaths: Record<string, string>;
|
|
9099
|
+
}> | undefined;
|
|
8901
9100
|
while (true) {
|
|
8902
9101
|
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
8903
9102
|
let step: { idle: true } | { idle: false; r: IteratorResult<LLMStreamEvent, void> };
|
|
@@ -8927,8 +9126,22 @@ export async function streamLLMWithTools(
|
|
|
8927
9126
|
? [turnOpts.toolChoice.name]
|
|
8928
9127
|
: []),
|
|
8929
9128
|
]);
|
|
9129
|
+
const attemptAbort = new AbortController();
|
|
9130
|
+
const forwardExternalAbort = () => attemptAbort.abort(opts.signal?.reason);
|
|
9131
|
+
if (opts.signal?.aborted) forwardExternalAbort();
|
|
9132
|
+
else opts.signal?.addEventListener('abort', forwardExternalAbort, { once: true });
|
|
9133
|
+
streamAttemptAbort = attemptAbort;
|
|
9134
|
+
streamAttemptAbortCleanup = () => opts.signal?.removeEventListener('abort', forwardExternalAbort);
|
|
9135
|
+
providerToolSchemaDiagnostics = Object.fromEntries((turnOpts.tools ?? []).map((tool, index) => [String(index), {
|
|
9136
|
+
originalName: tool.name,
|
|
9137
|
+
providerName: nameMap.toProviderName(tool.name),
|
|
9138
|
+
tupleItemsPaths: Object.fromEntries(
|
|
9139
|
+
collectTupleItemsSchemaPaths(tool.parameters).map((path, pathIndex) => [String(pathIndex), path]),
|
|
9140
|
+
),
|
|
9141
|
+
}]));
|
|
8930
9142
|
const providerOpts: LLMOpts = {
|
|
8931
9143
|
...turnOpts,
|
|
9144
|
+
signal: attemptAbort.signal,
|
|
8932
9145
|
tools: turnOpts.tools?.map((tool) => ({ ...tool, name: nameMap.toProviderName(tool.name) })),
|
|
8933
9146
|
toolChoice: turnOpts.toolChoice && typeof turnOpts.toolChoice === 'object'
|
|
8934
9147
|
? { name: nameMap.toProviderName(turnOpts.toolChoice.name) }
|
|
@@ -8944,21 +9157,25 @@ export async function streamLLMWithTools(
|
|
|
8944
9157
|
streamIter.next().then((r) => ({ idle: false as const, r })),
|
|
8945
9158
|
new Promise<{ idle: true }>((res) => { idleTimer = setTimeout(() => res({ idle: true }), streamIdleTimeoutMs); }),
|
|
8946
9159
|
]);
|
|
8947
|
-
if (step.idle)
|
|
8948
|
-
const error = new Error(`stream idle timeout after ${streamIdleTimeoutMs}ms`);
|
|
8949
|
-
error.name = 'StreamIdleTimeoutError';
|
|
8950
|
-
Object.assign(error, { code: 'ETIMEDOUT' });
|
|
8951
|
-
throw error;
|
|
8952
|
-
}
|
|
9160
|
+
if (step.idle) throw new StreamIdleTimeoutError(streamIdleTimeoutMs);
|
|
8953
9161
|
} catch (err) {
|
|
8954
9162
|
if (idleTimer) clearTimeout(idleTimer);
|
|
8955
|
-
|
|
9163
|
+
releaseStreamAttempt(true, err);
|
|
9164
|
+
if (err instanceof ApiHttpError && err.status === 400 && providerToolSchemaDiagnostics) {
|
|
9165
|
+
debug.log('llm.tool-schema', 'provider-request-rejected', {
|
|
9166
|
+
provider: activeProvider.name,
|
|
9167
|
+
model: activeModel,
|
|
9168
|
+
turn,
|
|
9169
|
+
status: err.status,
|
|
9170
|
+
tools: providerToolSchemaDiagnostics,
|
|
9171
|
+
}, { level: 'warn' });
|
|
9172
|
+
}
|
|
9173
|
+
if (err instanceof StreamIdleTimeoutError) {
|
|
8956
9174
|
debug.log('llm.stream', 'idle-timeout', {
|
|
8957
|
-
turn, evCount, lastEvType, idleMs:
|
|
9175
|
+
turn, evCount, lastEvType, idleMs: err.idleMs, idleMsSource: idleTimeout.source,
|
|
8958
9176
|
textChars: turnText.length, pendingCalls: pendingCalls.length,
|
|
8959
9177
|
}, { level: 'warn' });
|
|
8960
|
-
//
|
|
8961
|
-
// Detach cleanup so it cannot keep the user turn busy forever.
|
|
9178
|
+
// Provider iterators can queue return() behind a pending next(). Detach cleanup.
|
|
8962
9179
|
const timedOutIterator = streamIter;
|
|
8963
9180
|
if (timedOutIterator?.return) {
|
|
8964
9181
|
try { void Promise.resolve(timedOutIterator.return(undefined)).catch(() => {}); } catch { /* fail-soft cleanup */ }
|
|
@@ -8971,18 +9188,29 @@ export async function streamLLMWithTools(
|
|
|
8971
9188
|
attempt: streamIdleTimeoutRetries, maxRetries: STREAM_IDLE_TIMEOUT_MAX_RETRIES,
|
|
8972
9189
|
delayMs: 0, statusLine: '스트림 무응답 · 재시도',
|
|
8973
9190
|
}, { level: 'warn' });
|
|
8974
|
-
handlers.onStatus?.(`스트림 무응답 · 재시도 ${streamIdleTimeoutRetries}/${STREAM_IDLE_TIMEOUT_MAX_RETRIES}`);
|
|
9191
|
+
try { handlers.onStatus?.(`스트림 무응답 · 재시도 ${streamIdleTimeoutRetries}/${STREAM_IDLE_TIMEOUT_MAX_RETRIES}`); } catch { /* UI notification must not block recovery */ }
|
|
9192
|
+
streamIdleRetryPartial = turnText;
|
|
8975
9193
|
rewindDisconnectedStream();
|
|
8976
9194
|
streamIter = undefined;
|
|
8977
9195
|
continue;
|
|
8978
9196
|
}
|
|
8979
|
-
//
|
|
8980
|
-
|
|
8981
|
-
if (
|
|
8982
|
-
if (opts.provider
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
9197
|
+
// Do not replay or advance providers after a tool side effect or image escaped.
|
|
9198
|
+
if (pendingCalls.length > 0 || streamEmittedImage) break;
|
|
9199
|
+
if (opts.signal?.aborted) throw err;
|
|
9200
|
+
if (opts.provider && (turnText.length > 0 || streamIdleRetryPartial.length > 0)) {
|
|
9201
|
+
// A pinned provider cannot advance to fallback. Keep already shown partial text
|
|
9202
|
+
// and complete the turn instead of rewinding it and failing the whole response.
|
|
9203
|
+
if (turnText.length === 0 && streamIdleRetryPartial.length > 0) {
|
|
9204
|
+
turnText = streamIdleRetryPartial;
|
|
9205
|
+
fullText += streamIdleRetryPartial;
|
|
9206
|
+
}
|
|
9207
|
+
try { handlers.onStatus?.('스트림 무응답 · 재시도 소진 (부분 응답 유지)'); } catch { /* status is fail-soft */ }
|
|
9208
|
+
debug.log('llm.stream', 'idle-timeout-pinned-partial-preserved', { turn, evCount, lastEvType, textChars: turnText.length }, { level: 'warn' });
|
|
9209
|
+
break;
|
|
9210
|
+
}
|
|
9211
|
+
if (opts.provider) throw err;
|
|
9212
|
+
rewindDisconnectedStream();
|
|
9213
|
+
streamIter = undefined;
|
|
8986
9214
|
}
|
|
8987
9215
|
if (err instanceof OverloadFailoverError && !opts.provider) {
|
|
8988
9216
|
const switched = activeProvider.name === 'openai-codex'
|
|
@@ -8990,6 +9218,7 @@ export async function streamLLMWithTools(
|
|
|
8990
9218
|
: providerByName('grok');
|
|
8991
9219
|
if (switched && !attemptedProviders.has(switched.name)
|
|
8992
9220
|
&& (activeProvider.name === 'openai-codex' || switched.available()) && evCount === 0) {
|
|
9221
|
+
(circuit ?? await import('./session-runtime/provider-circuit.js')).recordProviderFailure(activeProvider.name, 'overload-failover', Date.now());
|
|
8993
9222
|
debug.log('llm.router', 'overload-failover', {
|
|
8994
9223
|
turn,
|
|
8995
9224
|
blockedProvider: activeProvider.name,
|
|
@@ -9004,13 +9233,13 @@ export async function streamLLMWithTools(
|
|
|
9004
9233
|
activeModel = finalized.model;
|
|
9005
9234
|
attemptedProviders.add(activeProvider.name);
|
|
9006
9235
|
streamDisconnectRetries = 0;
|
|
9236
|
+
streamIdleTimeoutRetries = 0;
|
|
9007
9237
|
turnOpts = buildTurnOpts();
|
|
9008
9238
|
streamIter = undefined;
|
|
9009
9239
|
continue;
|
|
9010
9240
|
}
|
|
9011
9241
|
}
|
|
9012
|
-
if (!(err instanceof
|
|
9013
|
-
&& evCount > 0 && isStreamDisconnectRetryable(err, pendingCalls.length, streamEmittedImage) && !opts.signal?.aborted) {
|
|
9242
|
+
if (!(err instanceof StreamIdleTimeoutError) && evCount > 0 && isStreamDisconnectRetryable(err, pendingCalls.length, streamEmittedImage) && !opts.signal?.aborted) {
|
|
9014
9243
|
const retryData = {
|
|
9015
9244
|
provider: activeProvider.name,
|
|
9016
9245
|
model: activeModel,
|
|
@@ -9054,6 +9283,7 @@ export async function streamLLMWithTools(
|
|
|
9054
9283
|
remainingProviders: remaining,
|
|
9055
9284
|
attempted: blockedProviders,
|
|
9056
9285
|
});
|
|
9286
|
+
(circuit ?? await import('./session-runtime/provider-circuit.js')).recordProviderFailure(activeProvider.name, verdict.category, Date.now());
|
|
9057
9287
|
blockedProviders = [...verdict.attempts];
|
|
9058
9288
|
const fallback = verdict.action === 'advance' ? providerByName(verdict.nextProvider) : undefined;
|
|
9059
9289
|
if (!fallback) {
|
|
@@ -9086,24 +9316,36 @@ export async function streamLLMWithTools(
|
|
|
9086
9316
|
attemptedProviders.add(activeProvider.name);
|
|
9087
9317
|
// 재시도 상한은 «공급자마다» — 넘어간 공급자도 스트림 도중 끊김이면 다시 5회까지 건다.
|
|
9088
9318
|
streamDisconnectRetries = 0;
|
|
9319
|
+
streamIdleTimeoutRetries = 0;
|
|
9089
9320
|
turnOpts = buildTurnOpts();
|
|
9090
9321
|
streamIter = undefined;
|
|
9091
9322
|
continue;
|
|
9092
9323
|
}
|
|
9093
9324
|
if (idleTimer) clearTimeout(idleTimer);
|
|
9094
|
-
if (step.
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9325
|
+
if (step.r.done) {
|
|
9326
|
+
(circuit ?? await import('./session-runtime/provider-circuit.js')).recordProviderSuccess(activeProvider.name, Date.now());
|
|
9327
|
+
releaseStreamAttempt();
|
|
9328
|
+
break;
|
|
9098
9329
|
}
|
|
9099
|
-
if (step.r.done) break;
|
|
9100
9330
|
const ev = step.r.value;
|
|
9101
9331
|
evCount++;
|
|
9102
9332
|
lastEvType = ev.type;
|
|
9333
|
+
// One same-provider idle retry is allowed per turn; progress clears the stale partial marker,
|
|
9334
|
+
// but does not reset that retry budget.
|
|
9335
|
+
if (streamIdleTimeoutRetries > 0) streamIdleRetryPartial = '';
|
|
9103
9336
|
if (ev.type === 'text') {
|
|
9104
9337
|
turnText += ev.delta;
|
|
9338
|
+
if (ev.phase) turnTextPhase = ev.phase;
|
|
9339
|
+
if (ev.phase) turnCodexTextPhase = ev.phase === 'final' ? 'final_answer' : 'commentary';
|
|
9105
9340
|
fullText += ev.delta;
|
|
9106
9341
|
emitRetryDisplayStep(retryDisplay.onText(ev.delta, turnText));
|
|
9342
|
+
} else if (ev.type === 'text_phase') {
|
|
9343
|
+
turnTextPhase = ev.phase;
|
|
9344
|
+
turnCodexTextPhase = ev.phase === 'final' ? 'final_answer' : 'commentary';
|
|
9345
|
+
} else if (ev.type === 'turn_end') {
|
|
9346
|
+
turnEnd = ev.endTurn;
|
|
9347
|
+
} else if (ev.type === 'codex_reasoning_item') {
|
|
9348
|
+
(turnCodexReasoningItems ??= []).push(ev.item);
|
|
9107
9349
|
} else if (ev.type === 'reasoning_details') {
|
|
9108
9350
|
turnReasoningDetails = ev.details;
|
|
9109
9351
|
} else if (ev.type === 'tool_call') {
|
|
@@ -9153,6 +9395,8 @@ export async function streamLLMWithTools(
|
|
|
9153
9395
|
...(ev.revisedPrompt !== undefined ? { revisedPrompt: ev.revisedPrompt } : {}),
|
|
9154
9396
|
});
|
|
9155
9397
|
} else if (ev.type === 'usage') {
|
|
9398
|
+
// Provider parsers normalize inputTokens to uncached input; cache
|
|
9399
|
+
// reads/writes are separate fields (prompt-cache/types.ts contract).
|
|
9156
9400
|
// ⭐ 주 에이전트 턴의 토큰·비용을 «게이트 없이» 남긴다(2026-09-24 실측: 이 줄이 없어 3시간 `llm.usage`
|
|
9157
9401
|
// 18행이 전부 곁가지 두 자리였고, 하니스 런 비용의 거의 전부가 관측 밖이었다). 곁가지와 같은 모양 ·
|
|
9158
9402
|
// 모르는 모델은 금액 대신 `cost.kind='unknown'`. runId 는 debug.log 가 붙인다. 실패해도 턴은 계속.
|
|
@@ -9230,6 +9474,26 @@ export async function streamLLMWithTools(
|
|
|
9230
9474
|
}
|
|
9231
9475
|
}
|
|
9232
9476
|
|
|
9477
|
+
// Codex can explicitly mark a response as unfinished even when it emitted
|
|
9478
|
+
// text and no tools. Keep that text in history with its phase, then ask
|
|
9479
|
+
// again. The extra turn is bounded to one past maxTurns.
|
|
9480
|
+
if (pendingCalls.length === 0 && turnEnd === false) {
|
|
9481
|
+
if (turnText || turnCodexReasoningItems?.length) history.push({
|
|
9482
|
+
role: 'assistant', content: turnText,
|
|
9483
|
+
...(turnTextPhase ? { phase: turnTextPhase === 'final' ? 'final_answer' as const : 'commentary' as const } : {}),
|
|
9484
|
+
...(turnCodexReasoningItems?.length ? { codexReasoningItems: turnCodexReasoningItems } : {}),
|
|
9485
|
+
});
|
|
9486
|
+
if (turnText) {
|
|
9487
|
+
clearVisibleAssistantTextForToolRound(handlers, sawToolRound);
|
|
9488
|
+
sawToolRound = true;
|
|
9489
|
+
}
|
|
9490
|
+
codexContinuationNeeded = true;
|
|
9491
|
+
debug.log('llm.router', 'codex.end-turn-false-continue', { turn, phase: turnTextPhase ?? 'unknown', textChars: turnText.length });
|
|
9492
|
+
// The one grace turn is also unfinished. Fall through to the existing
|
|
9493
|
+
// budget notice; never present a phase-marked progress item as final.
|
|
9494
|
+
if (turn >= maxTurns) break;
|
|
9495
|
+
continue;
|
|
9496
|
+
}
|
|
9233
9497
|
// No tool calls + non-empty text = the model gave its final answer.
|
|
9234
9498
|
if (pendingCalls.length === 0 && turnText.length > 0) {
|
|
9235
9499
|
const visibleText = finalizeVisibleAssistantText(fullText, turnText, sawToolRound);
|
|
@@ -9267,7 +9531,7 @@ export async function streamLLMWithTools(
|
|
|
9267
9531
|
// TUI-H1-GOALLOOP-EMPTY-TAIL — 답이 이 스트림 «앞»에 이미 보였어도 같다(opts.terminalPriorAnswerVisible —
|
|
9268
9532
|
// 예: Bash → «끝» → 재주입 → update_goal 만 → 빈 턴). 이 스트림에 답이 없으면 빈 문자열로 끝내고 화면은
|
|
9269
9533
|
// 건드리지 않는다(onText 생략 · emitTurnComplete 는 빈 답을 이력에 안 싣는다). 마감은 호출자(goal loop) 몫.
|
|
9270
|
-
if (terminalSignalDispatched && (lastToolRoundText.trim().length > 0 || opts.terminalPriorAnswerVisible === true)) {
|
|
9534
|
+
if (!completionNeedsFinalTurn && terminalSignalDispatched && (lastToolRoundText.trim().length > 0 || opts.terminalPriorAnswerVisible === true)) {
|
|
9271
9535
|
const answer = lastToolRoundText.trim().length > 0 ? lastToolRoundText : '';
|
|
9272
9536
|
debug.log('goal.loop', 'empty-turn-after-terminal-signal', {
|
|
9273
9537
|
turn, emptyRetries, answerChars: answer.length,
|
|
@@ -9311,7 +9575,10 @@ export async function streamLLMWithTools(
|
|
|
9311
9575
|
clearVisibleAssistantTextForToolRound(handlers, sawToolRound);
|
|
9312
9576
|
}
|
|
9313
9577
|
sawToolRound = true;
|
|
9314
|
-
if (turnText.trim().length > 0)
|
|
9578
|
+
if (turnText.trim().length > 0) {
|
|
9579
|
+
lastToolRoundText = turnText;
|
|
9580
|
+
lastToolRoundTextPhase = turnTextPhase;
|
|
9581
|
+
}
|
|
9315
9582
|
// TUI-GOAL-LOOP-FRICTION(#46) — 이 턴의 호출이 «종결 신호뿐»이면 예산·후속 단계 거부를 걸지 않는다.
|
|
9316
9583
|
const terminalOnlyTurn = terminalToolNames.size > 0
|
|
9317
9584
|
&& pendingCalls.length > 0
|
|
@@ -9360,7 +9627,10 @@ export async function streamLLMWithTools(
|
|
|
9360
9627
|
...(call.providerMeta !== undefined ? { providerMeta: call.providerMeta } : {}),
|
|
9361
9628
|
});
|
|
9362
9629
|
}
|
|
9363
|
-
history.push({ role: 'assistant', content: assistantBlocks
|
|
9630
|
+
history.push({ role: 'assistant', content: assistantBlocks,
|
|
9631
|
+
...(turnTextPhase ? { phase: turnTextPhase === 'final' ? 'final_answer' as const : 'commentary' as const } : {}),
|
|
9632
|
+
...(turnCodexReasoningItems?.length ? { codexReasoningItems: turnCodexReasoningItems } : {}),
|
|
9633
|
+
});
|
|
9364
9634
|
|
|
9365
9635
|
// Final-synthesis window: in the last FINAL_SYNTHESIS_TURNS of the
|
|
9366
9636
|
// budget, refuse to execute tool calls. Each pending call gets a
|
|
@@ -11114,6 +11384,11 @@ export async function streamLLMWithTools(
|
|
|
11114
11384
|
}
|
|
11115
11385
|
const dispatchIndex = callsToDispatch.findIndex((candidate) => candidate.id === call.id);
|
|
11116
11386
|
const { result, isError } = dispatchedResults[dispatchIndex]!;
|
|
11387
|
+
const toolResultInlineLimitBytes = opts.toolResultInlineLimitBytes === undefined
|
|
11388
|
+
? undefined
|
|
11389
|
+
: activeProvider.name === 'openai-codex'
|
|
11390
|
+
? Math.min(opts.toolResultInlineLimitBytes, 10 * 1024)
|
|
11391
|
+
: opts.toolResultInlineLimitBytes;
|
|
11117
11392
|
const imageBearing = maybeImageBearingResult(result);
|
|
11118
11393
|
if (imageBearing) {
|
|
11119
11394
|
// Image-bearing tool result (Phase 1, 2026-05-05) — see
|
|
@@ -11126,7 +11401,7 @@ export async function streamLLMWithTools(
|
|
|
11126
11401
|
tool_use_id: call.id,
|
|
11127
11402
|
content: [
|
|
11128
11403
|
{ type: 'image', mediaType: imageBearing.mediaType, base64: imageBearing.dataB64 },
|
|
11129
|
-
{ type: 'text', text: JSON.stringify(imageBearing.rest) },
|
|
11404
|
+
{ type: 'text', text: toolResultContentForCall(call.name, JSON.stringify(imageBearing.rest), toolResultInlineLimitBytes) },
|
|
11130
11405
|
],
|
|
11131
11406
|
...(isError ? { isError: true } : {}),
|
|
11132
11407
|
});
|
|
@@ -11134,7 +11409,7 @@ export async function streamLLMWithTools(
|
|
|
11134
11409
|
resultBlocks.push({
|
|
11135
11410
|
type: 'tool_result',
|
|
11136
11411
|
tool_use_id: call.id,
|
|
11137
|
-
content: toolResultContentForCall(call.name, result),
|
|
11412
|
+
content: toolResultContentForCall(call.name, result, toolResultInlineLimitBytes),
|
|
11138
11413
|
...(isError ? { isError: true } : {}),
|
|
11139
11414
|
});
|
|
11140
11415
|
}
|
|
@@ -11183,10 +11458,43 @@ export async function streamLLMWithTools(
|
|
|
11183
11458
|
}
|
|
11184
11459
|
// 신호뿐인 턴 · 섞인 턴(정상 디스패치 · 부분 거부 · 통째 거부의 수신 확인) 모두 — 종결 신호가 이 턴에 «받아들여졌다».
|
|
11185
11460
|
if (!hardStopText && pendingCalls.some(isTerminalCall)) terminalSignalDispatched = true;
|
|
11461
|
+
// A goal completion with evidence has already been acknowledged by dispatch. The
|
|
11462
|
+
// across-turn goal loop owns final acceptance (test evidence / rejected calls),
|
|
11463
|
+
// but no further provider request is needed to make that decision. Keep the
|
|
11464
|
+
// tool-result blocks in onTurnComplete so it can inspect the original call.
|
|
11465
|
+
const hasAcceptedCompletion = !hardStopText && opts.terminalCompletionAccepted && pendingCalls.some((call) =>
|
|
11466
|
+
isTerminalCall(call) && resultBlocks.some((block) => block.type === 'tool_result'
|
|
11467
|
+
&& block.tool_use_id === call.id && !block.isError)
|
|
11468
|
+
&& opts.terminalCompletionAccepted!(call.name, call.args));
|
|
11469
|
+
const hasVisibleAnswer = lastToolRoundText.trim().length > 0 || opts.terminalPriorAnswerVisible === true;
|
|
11470
|
+
// A completion tool can follow a commentary/progress segment on any provider.
|
|
11471
|
+
// Its successful ack does not turn that segment into a final user answer.
|
|
11472
|
+
const completionTextIsProgress = lastToolRoundTextPhase === 'commentary'
|
|
11473
|
+
|| (lastToolRoundTextPhase !== 'final' && lastToolRoundText.trim().length <= 240
|
|
11474
|
+
&& /(?:하겠습니다|드리겠습니다|할게요|할 예정입니다|확인하겠|살펴보겠|작성하겠|정리하겠|I(?:'ll| will)\b|\bnext[,]?\s+I\b)/i.test(lastToolRoundText));
|
|
11475
|
+
if (hasAcceptedCompletion && completionTextIsProgress) {
|
|
11476
|
+
completionNeedsFinalTurn = true;
|
|
11477
|
+
history.push({ role: LOOP_SELF_NOTE_ROLE, content: 'The goal completion was accepted. Write your final answer to the user now as plain text. Do not call tools or describe what you will do next.' });
|
|
11478
|
+
debug.log('goal.loop', 'completion-await-final', { turn, progressChars: lastToolRoundText.length, phase: lastToolRoundTextPhase ?? 'unknown' }, { level: 'info' });
|
|
11479
|
+
}
|
|
11480
|
+
if (hasAcceptedCompletion && hasVisibleAnswer && !completionNeedsFinalTurn) {
|
|
11481
|
+
const answer = lastToolRoundText.trim().length > 0 ? lastToolRoundText : '';
|
|
11482
|
+
const visiblePriorAnswer = opts.terminalPriorAnswerVisible === true;
|
|
11483
|
+
debug.log('goal.loop', 'accepted-terminal-completion-finish', {
|
|
11484
|
+
turn, answerChars: answer.length, priorAnswerVisible: visiblePriorAnswer,
|
|
11485
|
+
tools: pendingCalls.map((call) => call.name),
|
|
11486
|
+
}, { level: 'info' });
|
|
11487
|
+
// Every text delta was already streamed above (and fullText accumulates
|
|
11488
|
+
// all rounds). A second onText here would violate the cumulative-full
|
|
11489
|
+
// contract and duplicate text shown before this terminal tool call.
|
|
11490
|
+
const finalAnswer = answer || (opts.terminalPriorAnswerVisible ? fullText.trim() : '');
|
|
11491
|
+
emitTurnComplete(finalAnswer);
|
|
11492
|
+
return finalAnswer;
|
|
11493
|
+
}
|
|
11186
11494
|
// TUI-GOAL-LOOP-FRICTION(#63) — 종결 신호뿐인 턴 ⊕ 같은 턴에 쓴 답 = 이 턴이 최종 답이다.
|
|
11187
11495
|
// 다음 모델 턴을 열면 할 말이 없는 모델이 빈 턴을 되풀이하고 `[NO FINAL SYNTHESIS]` 가 붙는다(10-15 22:36Z 표본).
|
|
11188
11496
|
// 이 턴에 답이 없으면 종전처럼 다음 턴을 연다(앞 라운드 글은 «하겠습니다» 류 서술일 수 있다 — 모델이 답을 쓰게).
|
|
11189
|
-
if (terminalOnlyTurn && !hardStopText && !postToolResultUserNote && turnText.trim().length > 0) {
|
|
11497
|
+
if (terminalOnlyTurn && !completionNeedsFinalTurn && !hardStopText && !postToolResultUserNote && turnText.trim().length > 0) {
|
|
11190
11498
|
const answer = turnText;
|
|
11191
11499
|
debug.log('goal.loop', 'terminal-signal-finish', {
|
|
11192
11500
|
turn, maxTurns, tools: pendingCalls.map((call) => call.name), answerChars: answer.length,
|
|
@@ -11400,6 +11708,7 @@ export async function streamLLMWithTools(
|
|
|
11400
11708
|
let synthesizedText: string | null = null;
|
|
11401
11709
|
const shouldForceSynthesisOnBudget =
|
|
11402
11710
|
loopTermination === 'budget-exhausted'
|
|
11711
|
+
&& !codexContinuationNeeded
|
|
11403
11712
|
&& !forceSynthesisAttempted
|
|
11404
11713
|
&& (modelFamily === 'gemini'
|
|
11405
11714
|
|| applyCodexBehaviorGuard('force-synthesis', effectiveModel, codexGuardCandidate, true));
|
|
@@ -11486,11 +11795,11 @@ import {
|
|
|
11486
11795
|
} from './oauth/codex.js';
|
|
11487
11796
|
import { extractChatGPTClaims } from './oauth/jwt.js';
|
|
11488
11797
|
|
|
11489
|
-
// ⭐ 2026-
|
|
11798
|
+
// ⭐ 2026-10-28 — current Codex default is GPT-6.1 Terra.
|
|
11490
11799
|
// ⛔ 이 상수는 «최후 폴백»이다 — 정상 경로는 `llm-tier-map` 의 사다리를 탄다.
|
|
11491
|
-
//
|
|
11492
|
-
//
|
|
11493
|
-
export const CODEX_DEFAULT_MODEL = 'gpt-6-
|
|
11800
|
+
// Codex CLI 0.164.0: `codex exec -m gpt-6.1-terra` returned successfully on 2026-10-28.
|
|
11801
|
+
// Elanous ACP Codex app-server also returned on this model; Elanous's better-tier effort remains medium.
|
|
11802
|
+
export const CODEX_DEFAULT_MODEL = 'gpt-6.1-terra';
|
|
11494
11803
|
export const CODEX_DEFAULT_URL = 'https://api.openai.com/v1/chat/completions';
|
|
11495
11804
|
|
|
11496
11805
|
/** Build an OpenAI-compatible provider (grok / openai) using the
|
|
@@ -11867,6 +12176,8 @@ function makeCodexProvider(cfg: UCLLMConfig): LLMProvider {
|
|
|
11867
12176
|
const responsesUrl = cfgBase
|
|
11868
12177
|
? (cfgBase.endsWith('/responses') ? cfgBase : `${cfgBase.replace(/\/$/, '')}/responses`)
|
|
11869
12178
|
: `${CODEX_API_BASE_URL}/responses`;
|
|
12179
|
+
const endpointSupportsStore = !responsesUrl.includes('chatgpt.com/backend-api');
|
|
12180
|
+
const useStore = endpointSupportsStore && cfg.codexStore !== false;
|
|
11870
12181
|
// Image-pipeline P3 (2026-05-05) — gate the function_call_output
|
|
11871
12182
|
// ContentItem[] wire on the active model. gpt-5 family supports
|
|
11872
12183
|
// image_url in tool results; legacy gpt-4 / non-vision routes
|
|
@@ -11886,6 +12197,7 @@ function makeCodexProvider(cfg: UCLLMConfig): LLMProvider {
|
|
|
11886
12197
|
const { instructions, input } = messagesToResponsesInput(messages, {
|
|
11887
12198
|
acceptToolImages: codexAcceptToolImages,
|
|
11888
12199
|
acceptUserMessageImages: codexAcceptUserImages,
|
|
12200
|
+
replayCodexReasoning: !useStore,
|
|
11889
12201
|
onOrphanToolResult: (callId) => {
|
|
11890
12202
|
try { debug.log('llm.codex', 'orphan-tool-output-dropped', { callId }); }
|
|
11891
12203
|
catch { /* observation must not block the request */ }
|
|
@@ -11935,12 +12247,17 @@ function makeCodexProvider(cfg: UCLLMConfig): LLMProvider {
|
|
|
11935
12247
|
// wave for ChatGPT regardless of cfg.codexStore — the
|
|
11936
12248
|
// infrastructure stays in place for Azure/API-key paths and
|
|
11937
12249
|
// for any future ChatGPT-side policy change.
|
|
11938
|
-
const endpointSupportsStore = !responsesUrl.includes('chatgpt.com/backend-api');
|
|
11939
|
-
const useStore = endpointSupportsStore && cfg.codexStore !== false;
|
|
11940
12250
|
const delta = useStore ? getIncrementalItems(lastInput, input) : null;
|
|
11941
|
-
|
|
12251
|
+
// A stored continuation already has old reasoning. A full rebase has
|
|
12252
|
+
// no previous_response_id, so it must replay those opaque items.
|
|
12253
|
+
const wireInput = delta ?? (useStore
|
|
12254
|
+
? messagesToResponsesInput(messages, {
|
|
12255
|
+
acceptToolImages: codexAcceptToolImages,
|
|
12256
|
+
acceptUserMessageImages: codexAcceptUserImages,
|
|
12257
|
+
}).input
|
|
12258
|
+
: input);
|
|
11942
12259
|
const wirePrevId = (delta && lastResponseId) ? lastResponseId : undefined;
|
|
11943
|
-
|
|
12260
|
+
let requestBody = {
|
|
11944
12261
|
model: opts.model || model,
|
|
11945
12262
|
instructions,
|
|
11946
12263
|
input: wireInput,
|
|
@@ -11954,8 +12271,77 @@ function makeCodexProvider(cfg: UCLLMConfig): LLMProvider {
|
|
|
11954
12271
|
// can disable via llm.codexStore=false.
|
|
11955
12272
|
store: useStore,
|
|
11956
12273
|
...(wirePrevId ? { previousResponseId: wirePrevId } : {}),
|
|
11957
|
-
}
|
|
12274
|
+
};
|
|
12275
|
+
const requestGuard = opts.requestSizeGuard;
|
|
12276
|
+
const promptCache = opts.promptCache !== false;
|
|
12277
|
+
let serializedWireBody: string | undefined;
|
|
12278
|
+
if (requestGuard) {
|
|
12279
|
+
serializedWireBody = serializeCodexResponsesWireBody(requestBody, promptCache);
|
|
12280
|
+
let requestChars = serializedWireBody.length;
|
|
12281
|
+
const initialRequestChars = requestChars;
|
|
12282
|
+
let requestCompacted = false;
|
|
12283
|
+
let requestCompactReason = requestChars > requestGuard.maxChars
|
|
12284
|
+
? 'store-threading-skipped'
|
|
12285
|
+
: 'under-cap';
|
|
12286
|
+
if (requestChars > requestGuard.maxChars && !useStore) {
|
|
12287
|
+
try {
|
|
12288
|
+
const compacted = await requestGuard.compactMessages(messages, {
|
|
12289
|
+
provider: 'openai-codex', requestChars, maxChars: requestGuard.maxChars,
|
|
12290
|
+
});
|
|
12291
|
+
if (compacted) {
|
|
12292
|
+
const compactInput = messagesToResponsesInput(compacted.messages, {
|
|
12293
|
+
acceptToolImages: codexAcceptToolImages,
|
|
12294
|
+
acceptUserMessageImages: codexAcceptUserImages,
|
|
12295
|
+
// Compaction runs only when store threading is off, matching
|
|
12296
|
+
// the original non-threaded wire input policy.
|
|
12297
|
+
replayCodexReasoning: !useStore,
|
|
12298
|
+
});
|
|
12299
|
+
const compactBody = { ...requestBody, instructions: compactInput.instructions, input: compactInput.input };
|
|
12300
|
+
const compactWireBody = serializeCodexResponsesWireBody(compactBody, promptCache);
|
|
12301
|
+
const compactChars = compactWireBody.length;
|
|
12302
|
+
if (compactChars < requestChars) {
|
|
12303
|
+
messages.splice(0, messages.length, ...compacted.messages);
|
|
12304
|
+
requestBody = compactBody;
|
|
12305
|
+
serializedWireBody = compactWireBody;
|
|
12306
|
+
requestChars = compactChars;
|
|
12307
|
+
requestCompacted = true;
|
|
12308
|
+
requestCompactReason = compactChars > requestGuard.maxChars
|
|
12309
|
+
? `compaction-still-over-cap:${compacted.reason}`
|
|
12310
|
+
: compacted.reason;
|
|
12311
|
+
} else {
|
|
12312
|
+
requestCompactReason = 'compaction-no-reduction';
|
|
12313
|
+
}
|
|
12314
|
+
} else {
|
|
12315
|
+
requestCompactReason = 'compaction-no-op';
|
|
12316
|
+
}
|
|
12317
|
+
} catch (error) {
|
|
12318
|
+
requestCompactReason = 'compaction-error';
|
|
12319
|
+
debug.log('llm.request', 'request-size-compact-error', {
|
|
12320
|
+
provider: 'openai-codex',
|
|
12321
|
+
requestChars,
|
|
12322
|
+
maxChars: requestGuard.maxChars,
|
|
12323
|
+
message: error instanceof Error ? error.message : String(error),
|
|
12324
|
+
}, { level: 'warn' });
|
|
12325
|
+
}
|
|
12326
|
+
}
|
|
12327
|
+
requestGuard.onMeasured?.({
|
|
12328
|
+
provider: 'openai-codex', requestChars, maxChars: requestGuard.maxChars,
|
|
12329
|
+
compacted: requestCompacted, reason: requestCompactReason,
|
|
12330
|
+
...(requestCompacted ? { previousChars: initialRequestChars } : {}),
|
|
12331
|
+
});
|
|
12332
|
+
debug.log('llm.request', 'request-size-compact', {
|
|
12333
|
+
provider: 'openai-codex',
|
|
12334
|
+
beforeChars: initialRequestChars,
|
|
12335
|
+
afterChars: requestChars,
|
|
12336
|
+
maxChars: requestGuard.maxChars,
|
|
12337
|
+
compacted: requestCompacted,
|
|
12338
|
+
reason: requestCompactReason,
|
|
12339
|
+
});
|
|
12340
|
+
}
|
|
12341
|
+
yield* streamCodexResponsesEvents(responsesUrl, tokens.accessToken, requestBody, opts.signal, {
|
|
11958
12342
|
accountId,
|
|
12343
|
+
promptCache,
|
|
12344
|
+
...(serializedWireBody ? { serializedWireBody } : {}),
|
|
11959
12345
|
// Capture the new response id from the SSE stream so the
|
|
11960
12346
|
// next call can thread it. If the backend skips the event
|
|
11961
12347
|
// lastResponseId stays undefined and we fall back to full
|