rovecode 0.3.2 → 0.4.0-beta.2
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/CHANGELOG.md +512 -0
- package/README.md +33 -45
- package/bin/rovecode.js +24 -0
- package/dist/cli/app-dybnr56b.js +2 -0
- package/dist/cli/ask-user-p8hq4xgj.js +2 -0
- package/dist/cli/auth-login-ewpgw5sm.js +2 -0
- package/dist/cli/auth-m8p9grty.js +2 -0
- package/dist/cli/bench-xv3ypwev.js +9 -0
- package/dist/cli/catalog-737wb2s0.js +2 -0
- package/dist/cli/cli-arhg40m0.js +2 -0
- package/dist/cli/client-cf2pxx8q.js +2 -0
- package/dist/cli/commands-3p7e4xxs.js +2 -0
- package/dist/cli/connect-3q93d7cb.js +2 -0
- package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
- package/dist/cli/context-report-hbw9zfes.js +2 -0
- package/dist/cli/count-remote-mby98cd0.js +2 -0
- package/dist/cli/design-122y0axd.js +2 -0
- package/dist/cli/dispatch-b4egzvvh.js +2 -0
- package/dist/cli/doctor-x4jkv72e.js +3 -0
- package/dist/cli/executor-ftvg6tsy.js +2 -0
- package/dist/cli/export-pdgdhkch.js +2 -0
- package/dist/cli/files-cez9a96p.js +2 -0
- package/dist/cli/gauntlet-r3xxaszc.js +2 -0
- package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
- package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
- package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
- package/dist/cli/hashline-ewg5hbe3.js +2 -0
- package/dist/cli/http-n0kehsk8.js +5 -0
- package/dist/cli/index-z5qt1s76.js +2 -0
- package/dist/cli/install-80mp63kx.js +2 -0
- package/dist/cli/loop-12twjcat.js +2 -0
- package/dist/cli/main-01pv9206.js +4 -0
- package/dist/cli/main-0jys2ccn.js +3 -0
- package/dist/cli/main-1ztz6fkj.js +10 -0
- package/dist/cli/main-23q7cmww.js +9 -0
- package/dist/cli/main-2rzbexn2.js +3 -0
- package/dist/cli/main-2wyax8k9.js +9 -0
- package/dist/cli/main-2yeveeve.js +6 -0
- package/dist/cli/main-2z3dek0b.js +3 -0
- package/dist/cli/main-2zgsknth.js +3 -0
- package/dist/cli/main-45ejth3a.js +4 -0
- package/dist/cli/main-45rn3trk.js +22 -0
- package/dist/cli/main-4p4e2w7x.js +4 -0
- package/dist/cli/main-4y0tnfpa.js +16 -0
- package/dist/cli/main-5py0rkmc.js +4 -0
- package/dist/cli/main-6dtqmbt6.js +7 -0
- package/dist/cli/main-6h9x282m.js +4 -0
- package/dist/cli/main-6vjeds42.js +3 -0
- package/dist/cli/main-78gq4bt9.js +6 -0
- package/dist/cli/main-7jd5vh3x.js +4 -0
- package/dist/cli/main-7kt6r53y.js +4 -0
- package/dist/cli/main-8c1tbazx.js +58 -0
- package/dist/cli/main-9a9rnh47.js +19 -0
- package/dist/cli/main-9ht36z12.js +3 -0
- package/dist/cli/main-a2yfvcy9.js +7 -0
- package/dist/cli/main-a3f51n0x.js +5 -0
- package/dist/cli/main-b8zq261k.js +3 -0
- package/dist/cli/main-bxtvnf6d.js +13 -0
- package/dist/cli/main-edxc3yzt.js +4 -0
- package/dist/cli/main-evgz4mp5.js +21 -0
- package/dist/cli/main-f33fc5je.js +9 -0
- package/dist/cli/main-fvnpq46y.js +12 -0
- package/dist/cli/main-gbbty4d4.js +3 -0
- package/dist/cli/main-gth53dnt.js +25 -0
- package/dist/cli/main-hqbz10aw.js +9 -0
- package/dist/cli/main-hrrvcfan.js +38 -0
- package/dist/cli/main-hzwtsb2m.js +5 -0
- package/dist/cli/main-j7ttv0sd.js +34 -0
- package/dist/cli/main-jak598k9.js +5 -0
- package/dist/cli/main-kba6zeyd.js +6 -0
- package/dist/cli/main-kwwsz6rq.js +3 -0
- package/dist/cli/main-m8vm17zq.js +3 -0
- package/dist/cli/main-mg4f96e1.js +3 -0
- package/dist/cli/main-mg9b20ac.js +18 -0
- package/dist/cli/main-mgb9ccnx.js +3 -0
- package/dist/cli/main-mjt2p7aj.js +3 -0
- package/dist/cli/main-n6qrdbmy.js +3 -0
- package/dist/cli/main-na7wse0x.js +5 -0
- package/dist/cli/main-nqveez48.js +4 -0
- package/dist/cli/main-ntqef02r.js +10 -0
- package/dist/cli/main-nvc3yjay.js +136 -0
- package/dist/cli/main-p0cfn6nr.js +16 -0
- package/dist/cli/main-qj2djy17.js +19 -0
- package/dist/cli/main-qsevpgsv.js +3 -0
- package/dist/cli/main-qvarybsp.js +3 -0
- package/dist/cli/main-rebtt91r.js +5 -0
- package/dist/cli/main-rpg7h8mb.js +3 -0
- package/dist/cli/main-rsy72qmw.js +15 -0
- package/dist/cli/main-rvetps99.js +18 -0
- package/dist/cli/main-s4bb0jav.js +3 -0
- package/dist/cli/main-s9v8k74e.js +3 -0
- package/dist/cli/main-tjvwmscs.js +3 -0
- package/dist/cli/main-tkgarpjj.js +4 -0
- package/dist/cli/main-v8y60bb2.js +3 -0
- package/dist/cli/main-vhrrq337.js +3 -0
- package/dist/cli/main-vp2dfb7s.js +4 -0
- package/dist/cli/main-vqbr22sz.js +8 -0
- package/dist/cli/main-vxnwe5xx.js +18 -0
- package/dist/cli/main-wgph00xf.js +5 -0
- package/dist/cli/main-wk2csfnj.js +5 -0
- package/dist/cli/main-wm997zjx.js +3 -0
- package/dist/cli/main-wpkyraxh.js +3 -0
- package/dist/cli/main-wqt32p5x.js +4 -0
- package/dist/cli/main-x9ct6y1a.js +3 -0
- package/dist/cli/main-xfekqh9m.js +7 -0
- package/dist/cli/main-xt9zc3n6.js +7 -0
- package/dist/cli/main-xx2z3zh5.js +4 -0
- package/dist/cli/main-y5c82rxr.js +3 -0
- package/dist/cli/main-yrjt2sqt.js +14 -0
- package/dist/cli/main-ys6zj3yr.js +3 -0
- package/dist/cli/main-ywbxshqc.js +8 -0
- package/dist/cli/main-z13755t8.js +25 -0
- package/dist/cli/main-zc7pyrbj.js +4 -0
- package/dist/cli/main.js +279 -0
- package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
- package/dist/cli/mcp-login-bthtfpt7.js +2 -0
- package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
- package/dist/cli/notify-54v5z9dz.js +2 -0
- package/dist/cli/oauth-g5gme95c.js +2 -0
- package/dist/cli/output-satndjap.js +16 -0
- package/dist/cli/profiles-sfhpbq3m.js +2 -0
- package/dist/cli/provider-config-hv3xtdt4.js +2 -0
- package/dist/cli/provider-kwzq6g84.js +2 -0
- package/dist/cli/registry-fh0hdnyn.js +2 -0
- package/dist/cli/registry-y1y8e94r.js +2 -0
- package/dist/cli/repl-t4z03mqq.js +11 -0
- package/dist/cli/resume-fqt4chg8.js +2 -0
- package/dist/cli/run-flags-rysbag9t.js +2 -0
- package/dist/cli/runtime-j19fjbsa.js +2 -0
- package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
- package/dist/cli/server-r0b6bksk.js +5 -0
- package/dist/cli/session-arg-txmn5g4x.js +2 -0
- package/dist/cli/session-ed250d9j.js +2 -0
- package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
- package/dist/cli/settings-y9rzcqx8.js +2 -0
- package/dist/cli/setup-jmbr11j0.js +2 -0
- package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
- package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
- package/dist/cli/smoke-1bg937kx.js +8 -0
- package/dist/cli/start-chat-p01cdks3.js +12 -0
- package/dist/cli/stream-4wmyaypz.js +2 -0
- package/dist/cli/task-eg4s093s.js +2 -0
- package/dist/cli/tasks-12v9rr9k.js +2 -0
- package/dist/cli/thinking-a5ngvqyh.js +2 -0
- package/dist/cli/todo-1wxpcecx.js +2 -0
- package/dist/cli/tools-2ftsya7w.js +2 -0
- package/dist/cli/tools-x1tj4fxm.js +2 -0
- package/dist/cli/trust-cmd-hccxehzb.js +2 -0
- package/dist/cli/update-check-ygt3vd7m.js +2 -0
- package/dist/cli/update-cmd-v23qhr8c.js +2 -0
- package/dist/cli/voice-g1gtck92.js +2 -0
- package/dist/cli/webfetch-0nnrjgb5.js +2 -0
- package/dist/cli/websearch-f0vr2p7d.js +2 -0
- package/dist/cli/workspace-9rq1w4ta.js +2 -0
- package/dist/lib/index.js +62 -0
- package/dist/lib/models-index.json +1 -0
- package/dist/lib/plugins.js +6 -0
- package/dist/lib/providers.js +17 -0
- package/dist/lib/public-api.js +20 -0
- package/dist/rovecode.exe +4 -0
- package/package.json +38 -11
- package/bin/rovecode.ts +0 -21
- package/src/acp/server.ts +0 -374
- package/src/cli/auth-login.ts +0 -122
- package/src/cli/connect.ts +0 -244
- package/src/cli/context-cmd.ts +0 -199
- package/src/cli/dispatch.ts +0 -82
- package/src/cli/doctor.ts +0 -362
- package/src/cli/export.ts +0 -276
- package/src/cli/help.ts +0 -293
- package/src/cli/is-tui-invocation.ts +0 -8
- package/src/cli/main.ts +0 -583
- package/src/cli/market-cmd.ts +0 -658
- package/src/cli/mcp-login.ts +0 -141
- package/src/cli/mcp-market-cmd.ts +0 -302
- package/src/cli/output.ts +0 -382
- package/src/cli/repl.ts +0 -250
- package/src/cli/repomap-root.ts +0 -14
- package/src/cli/resume.ts +0 -57
- package/src/cli/run-flags.ts +0 -43
- package/src/cli/run-limits.ts +0 -78
- package/src/cli/runtime.ts +0 -931
- package/src/cli/session-arg.ts +0 -30
- package/src/cli/sessions-cmd.ts +0 -145
- package/src/cli/setup.ts +0 -153
- package/src/cli/skills-cmd.ts +0 -194
- package/src/cli/start-chat.ts +0 -65
- package/src/cli/trust-cmd.ts +0 -52
- package/src/coding/bash.ts +0 -148
- package/src/coding/checkpoints.ts +0 -327
- package/src/coding/diff.ts +0 -138
- package/src/coding/files.ts +0 -341
- package/src/coding/hashline.ts +0 -274
- package/src/coding/lsp-gate.ts +0 -254
- package/src/coding/lsp-servers.ts +0 -147
- package/src/coding/lsp.ts +0 -283
- package/src/coding/repomap-cache.ts +0 -99
- package/src/coding/repomap-files.ts +0 -192
- package/src/coding/repomap.ts +0 -481
- package/src/core/agents.ts +0 -255
- package/src/core/compaction.ts +0 -259
- package/src/core/config.ts +0 -289
- package/src/core/context-report.ts +0 -228
- package/src/core/context.ts +0 -60
- package/src/core/count-remote.ts +0 -107
- package/src/core/execpolicy-rules.ts +0 -196
- package/src/core/execpolicy.ts +0 -385
- package/src/core/executor.ts +0 -454
- package/src/core/guardrails.ts +0 -400
- package/src/core/hooks.ts +0 -411
- package/src/core/images.ts +0 -230
- package/src/core/intro.ts +0 -266
- package/src/core/loop.ts +0 -567
- package/src/core/modes.ts +0 -372
- package/src/core/orchestrator.ts +0 -245
- package/src/core/proc-group.ts +0 -48
- package/src/core/project-trust.ts +0 -98
- package/src/core/reflection.ts +0 -165
- package/src/core/sandbox-config.ts +0 -186
- package/src/core/session-id.ts +0 -24
- package/src/core/session-images.ts +0 -73
- package/src/core/session-ops.ts +0 -183
- package/src/core/session-text.ts +0 -29
- package/src/core/session.ts +0 -469
- package/src/core/settings.ts +0 -170
- package/src/core/tasks.ts +0 -646
- package/src/core/token-scale.ts +0 -108
- package/src/core/tools.ts +0 -309
- package/src/core/trust.ts +0 -104
- package/src/core/types.ts +0 -330
- package/src/core/update-check.ts +0 -171
- package/src/core/usage.ts +0 -204
- package/src/core/validate.ts +0 -121
- package/src/core/verify-gate.ts +0 -159
- package/src/core/verify.ts +0 -236
- package/src/core/voice.ts +0 -158
- package/src/core/win-job.ts +0 -183
- package/src/core/workspace.ts +0 -184
- package/src/design/audit.ts +0 -797
- package/src/design/direction.ts +0 -190
- package/src/design/rules.ts +0 -157
- package/src/eval/bench.ts +0 -150
- package/src/eval/gauntlet-runner.ts +0 -215
- package/src/eval/gauntlet-support.ts +0 -84
- package/src/eval/gauntlet-wave3.ts +0 -269
- package/src/eval/gauntlet-wave4.ts +0 -217
- package/src/eval/gauntlet.ts +0 -253
- package/src/index.ts +0 -17
- package/src/lanes/agy.ts +0 -95
- package/src/lanes/approval.ts +0 -24
- package/src/lanes/claude.ts +0 -129
- package/src/lanes/codex.ts +0 -127
- package/src/lanes/events.ts +0 -130
- package/src/lanes/job.ts +0 -142
- package/src/lanes/opencode.ts +0 -122
- package/src/lanes/process.ts +0 -184
- package/src/lanes/progress.ts +0 -183
- package/src/lanes/registry.ts +0 -178
- package/src/lanes/runner.ts +0 -124
- package/src/lanes/types.ts +0 -112
- package/src/market/catalogs/mcp-docs.json +0 -111
- package/src/market/catalogs/plugins.json +0 -111
- package/src/market/catalogs/skills.json +0 -478
- package/src/market/clone.ts +0 -72
- package/src/market/context-cost.ts +0 -121
- package/src/market/digest.ts +0 -106
- package/src/market/index.ts +0 -22
- package/src/market/install.ts +0 -578
- package/src/market/manifest.ts +0 -187
- package/src/market/prereq.ts +0 -145
- package/src/market/registry.ts +0 -363
- package/src/market/resolve.ts +0 -111
- package/src/market/types.ts +0 -236
- package/src/market/validate.ts +0 -227
- package/src/mcp/client.ts +0 -449
- package/src/mcp/config.ts +0 -252
- package/src/mcp/local-package.ts +0 -211
- package/src/mcp/market-catalog.ts +0 -84
- package/src/mcp/market-install.ts +0 -289
- package/src/mcp/market.ts +0 -362
- package/src/mcp/oauth.ts +0 -251
- package/src/mcp/prompts-resources.ts +0 -249
- package/src/mcp/shared.ts +0 -149
- package/src/mcp/status.ts +0 -67
- package/src/mcp/tools.ts +0 -275
- package/src/mcp/transport.ts +0 -122
- package/src/mcp/trust.ts +0 -25
- package/src/memory/blocks.ts +0 -278
- package/src/memory/recall.ts +0 -355
- package/src/memory/scope.ts +0 -182
- package/src/memory/store.ts +0 -105
- package/src/memory/tools.ts +0 -99
- package/src/plugins/cli.ts +0 -119
- package/src/plugins/discover.ts +0 -108
- package/src/plugins/index.ts +0 -50
- package/src/plugins/install.ts +0 -184
- package/src/plugins/load.ts +0 -124
- package/src/plugins/manifest.ts +0 -92
- package/src/plugins/state.ts +0 -83
- package/src/providers/auth.ts +0 -408
- package/src/providers/cache.ts +0 -223
- package/src/providers/catalog-local.ts +0 -160
- package/src/providers/catalog.ts +0 -421
- package/src/providers/middleware-context.ts +0 -86
- package/src/providers/middleware.ts +0 -373
- package/src/providers/model-list.ts +0 -23
- package/src/providers/oauth/common.ts +0 -105
- package/src/providers/oauth/device-code.ts +0 -107
- package/src/providers/oauth/github-copilot.ts +0 -146
- package/src/providers/oauth/loopback.ts +0 -158
- package/src/providers/oauth/openai.ts +0 -163
- package/src/providers/oauth/openrouter.ts +0 -89
- package/src/providers/oauth/pkce.ts +0 -45
- package/src/providers/oauth/registry.ts +0 -39
- package/src/providers/oauth/seam.ts +0 -89
- package/src/providers/profile-glm53.ts +0 -111
- package/src/providers/profile-sonnet5-persona.ts +0 -65
- package/src/providers/profile-sonnet5-voice.ts +0 -23
- package/src/providers/profiles.ts +0 -156
- package/src/providers/provider-config.ts +0 -311
- package/src/providers/registry.ts +0 -333
- package/src/providers/responses.ts +0 -209
- package/src/providers/retry.ts +0 -234
- package/src/providers/router.ts +0 -294
- package/src/providers/sse.ts +0 -26
- package/src/providers/stream-errors.ts +0 -117
- package/src/providers/stream.ts +0 -566
- package/src/providers/thinking.ts +0 -189
- package/src/providers/wire-messages.ts +0 -129
- package/src/providers/wire-responses.ts +0 -79
- package/src/providers/wire-select.ts +0 -53
- package/src/server/http.ts +0 -291
- package/src/server/openapi.ts +0 -246
- package/src/sextant/card-hits.ts +0 -102
- package/src/sextant/card-keys.ts +0 -55
- package/src/sextant/context-source.ts +0 -157
- package/src/sextant/crew-cards.ts +0 -350
- package/src/sextant/draw-agents.ts +0 -273
- package/src/sextant/draw-code.ts +0 -388
- package/src/sextant/draw-context.ts +0 -222
- package/src/sextant/draw-frame.ts +0 -164
- package/src/sextant/draw-market.ts +0 -573
- package/src/sextant/draw-messages.ts +0 -386
- package/src/sextant/draw-pet.ts +0 -230
- package/src/sextant/draw-plan.ts +0 -187
- package/src/sextant/draw-tabs.ts +0 -85
- package/src/sextant/draw-util.ts +0 -65
- package/src/sextant/draw-wizard.ts +0 -378
- package/src/sextant/engine.ts +0 -230
- package/src/sextant/frame-hits.ts +0 -25
- package/src/sextant/frame.ts +0 -101
- package/src/sextant/git-status.ts +0 -197
- package/src/sextant/grid.ts +0 -59
- package/src/sextant/input.ts +0 -119
- package/src/sextant/keys.ts +0 -521
- package/src/sextant/layout.ts +0 -86
- package/src/sextant/local-commands.ts +0 -169
- package/src/sextant/market-source.ts +0 -287
- package/src/sextant/mentions.ts +0 -200
- package/src/sextant/message-hits.ts +0 -26
- package/src/sextant/model.ts +0 -387
- package/src/sextant/overlays.ts +0 -456
- package/src/sextant/panel-hits.ts +0 -38
- package/src/sextant/pet.ts +0 -399
- package/src/sextant/screen.ts +0 -324
- package/src/sextant/scroll-hits.ts +0 -66
- package/src/sextant/scrollbar.ts +0 -82
- package/src/sextant/sextant-bridge.ts +0 -174
- package/src/sextant/sextant-cards.ts +0 -142
- package/src/sextant/sextant-diff-base.ts +0 -63
- package/src/sextant/sextant-files.ts +0 -154
- package/src/sextant/sextant-frame-loop.ts +0 -335
- package/src/sextant/sextant-renderer.ts +0 -574
- package/src/sextant/sextant-repo.ts +0 -140
- package/src/sextant/theme.ts +0 -66
- package/src/sextant/tool-rows.ts +0 -189
- package/src/sextant/types.ts +0 -493
- package/src/skills/index.ts +0 -387
- package/src/skills/pack.ts +0 -220
- package/src/skills/spec.ts +0 -162
- package/src/skills/tools.ts +0 -69
- package/src/skills/versioned.ts +0 -227
- package/src/telemetry/otel-export.ts +0 -122
- package/src/telemetry/otel-lanes.ts +0 -89
- package/src/telemetry/otel-logs.ts +0 -131
- package/src/telemetry/otel-metrics.ts +0 -136
- package/src/telemetry/otel.ts +0 -397
- package/src/telemetry/otlp.ts +0 -76
- package/src/tools/ask-user.ts +0 -156
- package/src/tools/bash-bg.ts +0 -94
- package/src/tools/bash-jobs.ts +0 -237
- package/src/tools/design.ts +0 -151
- package/src/tools/evalcell.ts +0 -338
- package/src/tools/html-text.ts +0 -139
- package/src/tools/provider.ts +0 -149
- package/src/tools/task.ts +0 -250
- package/src/tools/todo.ts +0 -320
- package/src/tools/webfetch.ts +0 -332
- package/src/tools/websearch.ts +0 -359
- package/src/tui/agents-cmd.ts +0 -41
- package/src/tui/app.ts +0 -749
- package/src/tui/attach.ts +0 -127
- package/src/tui/boot-notes.ts +0 -41
- package/src/tui/builtin-prompts.ts +0 -59
- package/src/tui/checkpoints-cmd.ts +0 -70
- package/src/tui/clipboard-image.ts +0 -81
- package/src/tui/clipboard.ts +0 -78
- package/src/tui/commands.ts +0 -283
- package/src/tui/config-view.ts +0 -53
- package/src/tui/context-cmds.ts +0 -282
- package/src/tui/cost.ts +0 -108
- package/src/tui/crash-guard.ts +0 -173
- package/src/tui/focus-terminal.ts +0 -34
- package/src/tui/git-cmds.ts +0 -273
- package/src/tui/git-plain.ts +0 -58
- package/src/tui/info-cmd.ts +0 -150
- package/src/tui/input-plain.ts +0 -76
- package/src/tui/mcp-cmd.ts +0 -128
- package/src/tui/memory-note.ts +0 -77
- package/src/tui/modes-cmd.ts +0 -45
- package/src/tui/notify-seq.ts +0 -100
- package/src/tui/notify.ts +0 -318
- package/src/tui/overlays.ts +0 -97
- package/src/tui/pi-renderer.ts +0 -428
- package/src/tui/providers-cmd.ts +0 -377
- package/src/tui/reasoning-view.ts +0 -56
- package/src/tui/renderer.ts +0 -128
- package/src/tui/replay-marker.ts +0 -29
- package/src/tui/session-cmd.ts +0 -148
- package/src/tui/session-manage.ts +0 -95
- package/src/tui/sextant-attach.ts +0 -102
- package/src/tui/sextant-io.ts +0 -202
- package/src/tui/sextant-smoke.ts +0 -110
- package/src/tui/shell-cmd.ts +0 -158
- package/src/tui/smoke.ts +0 -72
- package/src/tui/staged-terminal.ts +0 -50
- package/src/tui/startup.ts +0 -12
- package/src/tui/theme.ts +0 -59
- package/src/tui/todo-label.ts +0 -7
- package/src/tui/trust-card.ts +0 -107
- package/src/tui/tui-commands.ts +0 -87
- package/tsconfig.json +0 -30
- package/vendor/pi-tui/LICENSE +0 -21
- package/vendor/pi-tui/PATCHES.md +0 -12
- package/vendor/pi-tui/PROVENANCE.md +0 -12
- package/vendor/pi-tui/README.upstream.md +0 -854
- package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
- package/vendor/pi-tui/src/autocomplete.ts +0 -827
- package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
- package/vendor/pi-tui/src/components/box.ts +0 -138
- package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
- package/vendor/pi-tui/src/components/editor.ts +0 -2364
- package/vendor/pi-tui/src/components/h-stack.ts +0 -45
- package/vendor/pi-tui/src/components/image.ts +0 -128
- package/vendor/pi-tui/src/components/input.ts +0 -448
- package/vendor/pi-tui/src/components/loader.ts +0 -93
- package/vendor/pi-tui/src/components/markdown.ts +0 -1016
- package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
- package/vendor/pi-tui/src/components/select-list.ts +0 -230
- package/vendor/pi-tui/src/components/settings-list.ts +0 -277
- package/vendor/pi-tui/src/components/spacer.ts +0 -29
- package/vendor/pi-tui/src/components/stack.ts +0 -155
- package/vendor/pi-tui/src/components/text.ts +0 -108
- package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
- package/vendor/pi-tui/src/components/v-stack.ts +0 -34
- package/vendor/pi-tui/src/editor-component.ts +0 -75
- package/vendor/pi-tui/src/fuzzy.ts +0 -138
- package/vendor/pi-tui/src/index.ts +0 -149
- package/vendor/pi-tui/src/keybindings.ts +0 -321
- package/vendor/pi-tui/src/keys.ts +0 -1402
- package/vendor/pi-tui/src/kill-ring.ts +0 -47
- package/vendor/pi-tui/src/latex.ts +0 -1381
- package/vendor/pi-tui/src/layout-node.ts +0 -52
- package/vendor/pi-tui/src/layout.ts +0 -411
- package/vendor/pi-tui/src/native-modifiers.ts +0 -60
- package/vendor/pi-tui/src/native-module-path.ts +0 -32
- package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
- package/vendor/pi-tui/src/terminal-colors.ts +0 -74
- package/vendor/pi-tui/src/terminal-image.ts +0 -701
- package/vendor/pi-tui/src/terminal.ts +0 -554
- package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
- package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
- package/vendor/pi-tui/src/tui.ts +0 -1264
- package/vendor/pi-tui/src/undo-stack.ts +0 -29
- package/vendor/pi-tui/src/utils.ts +0 -1327
- package/vendor/pi-tui/src/word-navigation.ts +0 -118
- package/vendor/pi-tui/test/test-themes.ts +0 -39
- package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
- /package/{src/providers → dist/cli}/models-index.json +0 -0
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
/** OpenAI Responses API wire (aion port #75, brought over 2026-09-07) — the second OpenAI adapter beside stream.ts's
|
|
2
|
-
* /chat/completions pair. ADR-003 seam: ONE StreamFn, errors cross as stopReason "error" / "aborted", never a throw.
|
|
3
|
-
* Hand-rolled fetch + SSE exactly like stream.ts (no `openai` npm dependency). providers/wire-select.ts decides per
|
|
4
|
-
* call which of the two wires a request takes; this module never imports stream.ts at runtime (it shares sse.ts,
|
|
5
|
-
* stream-errors.ts, usage.ts, thinking.ts, catalog.ts and wire-responses.ts with it).
|
|
6
|
-
*
|
|
7
|
-
* Request: POST <baseUrl>/responses `{ model, instructions?, input, tools?, stream: true, store: false,
|
|
8
|
-
* max_output_tokens?, reasoning? }`. `max_output_tokens` is max(cap, 16) — cap = model.maxTokens, the stream.ts rule
|
|
9
|
-
* — and is OMITTED on the ChatGPT Codex backend (a URL containing chatgpt.com/backend-api; pi's codex path never
|
|
10
|
-
* sends it), which also gets the `OpenAI-Beta: responses=experimental` + `originator: rovecode` headers.
|
|
11
|
-
* `reasoning: { effort, summary: "auto" }` is the Responses spelling of rovecode's ONE thinking dial
|
|
12
|
-
* (providers/thinking.ts thinkingPlan — the same dialect table stream.ts spreads as `reasoning_effort`; auto sends
|
|
13
|
-
* nothing, a model the catalog or the dialect marks without a reasoning mode sends nothing).
|
|
14
|
-
* NOT brought over: `include: ["reasoning.encrypted_content"]` and the reasoning item as a message part — rovecode
|
|
15
|
-
* has no reasoning MessagePart to replay it from (wire-responses.ts header), so asking the provider for encrypted
|
|
16
|
-
* content we would then discard is cost without product. Reasoning TEXT still streams live as reasoning_delta.
|
|
17
|
-
* Pattern sources, MIT — earendil-works/pi packages/ai/src/api: openai-responses.ts:290-302 (store:false,
|
|
18
|
-
* stream:true, max_output_tokens), :32-33 (the 16-token floor the API enforces), :323-332 (reasoning effort +
|
|
19
|
-
* summary "auto"); openai-codex-responses.ts:549-556 (`instructions`), :45 + :633-639 (chatgpt.com/backend-api →
|
|
20
|
-
* codex/responses), :1607-1609 + :1622 (chatgpt-account-id, originator, `OpenAI-Beta: responses=experimental`,
|
|
21
|
-
* accept text/event-stream); openai-responses-shared.ts:440,:463 (output slots keyed by output_index), :597-668
|
|
22
|
-
* (the deltas), :680-739 (output_item.done finalises a slot: message text authoritative, function_call arguments
|
|
23
|
-
* parsed), :757-759 (a stream without a terminal event is an error), :762-792 (stop mapping), :592-594 (a tool call
|
|
24
|
-
* overrides a completed stop), :742-755 (`<code>: <message>` for response.failed / error events).
|
|
25
|
-
* Pattern source, Apache-2.0 at pattern level (no code copied) — openai/codex: codex-rs/codex-api/src/sse/
|
|
26
|
-
* responses.rs:357-518 (the event set a client consumes; :843-845 fixtures pair `event:` and `data:` lines),
|
|
27
|
-
* codex-rs/model-provider-info/src/lib.rs:40 (CHATGPT_CODEX_BASE_URL), codex-rs/core/src/client.rs:688 (an
|
|
28
|
-
* `originator` header on every request — the value is `rovecode` here; codex's own OpenAI-Beta value is
|
|
29
|
-
* websocket-only, so `responses=experimental` is pi's). NOTICE entry: THIRD_PARTY_NOTICES.md, `## openai/codex`.
|
|
30
|
-
* NOT ported: `previous_response_id`, `prompt_cache_key` / `prompt_cache_retention`, `text.verbosity`,
|
|
31
|
-
* `service_tier`, `parallel_tool_calls`, `tool_choice`, strict / grammar / custom tools, developer-role items,
|
|
32
|
-
* the websocket and zstd transports, pi's retry loop (rovecode's withRetry + router classify the error turn as
|
|
33
|
-
* for chat completions) and codex's error taxonomy. Selection is static — no runtime 404 → chat re-issue.
|
|
34
|
-
*
|
|
35
|
-
* Events (`data:` payloads through sse.ts; `event:` lines are ignored — `type` names the event):
|
|
36
|
-
* `response.output_text.delta` / `response.refusal.delta` → text_delta (and the abort-salvage buffer);
|
|
37
|
-
* `response.reasoning_summary_text.delta` / `response.reasoning_text.delta` → reasoning_delta — NEVER into the
|
|
38
|
-
* text buffer, never a text part (`…summary_part.done` → one "\n\n" reasoning_delta);
|
|
39
|
-
* `response.function_call_arguments.delta` accumulates per output_index (a tool_call_delta once the slot knows
|
|
40
|
-
* its call id and name); `response.output_item.done` finalises the slot (a reasoning item finalises to nothing);
|
|
41
|
-
* `response.completed` → tool_use when a tool_call part exists, else end_turn; `response.incomplete` → length for
|
|
42
|
-
* max_output_tokens, else error naming the reason; `response.failed` / `error` → error "<code>: <message>"; no
|
|
43
|
-
* terminal event → error. Usage: `response.usage` through core/usage.ts normalizeUsage (input_tokens −
|
|
44
|
-
* input_tokens_details.cached_tokens, cacheRead; reasoning_tokens are not carried — TokenUsage has no field for
|
|
45
|
-
* them and normalizeUsage does not expose them). Non-2xx → httpErrorTurn (status + Retry-After for withRetry / the
|
|
46
|
-
* router); a thrown fetch or abort → failedTurn (an aborted turn keeps the streamed text, never a tool_call
|
|
47
|
-
* fragment). No header, token or account id ever reaches an error text or an event (oauth/common.ts hygiene rule). */
|
|
48
|
-
|
|
49
|
-
import type { AssistantTurn, Message, MessagePart, ModelRef, StopReason, StreamEvent, StreamFn, StreamOptions, TokenUsage } from "../core/types.ts";
|
|
50
|
-
import { normalizeUsage } from "../core/usage.ts";
|
|
51
|
-
import { supportsImages } from "./catalog.ts";
|
|
52
|
-
import { sseLines } from "./sse.ts";
|
|
53
|
-
import { failedTurn, fetchFirstByte, httpErrorTurn } from "./stream-errors.ts";
|
|
54
|
-
import { thinkingPlan } from "./thinking.ts";
|
|
55
|
-
import { toResponsesInput, toResponsesTools } from "./wire-responses.ts";
|
|
56
|
-
|
|
57
|
-
/** the same opts as openaiCompatStreaming — oauth/seam.ts MakeStream is unchanged */
|
|
58
|
-
export interface ResponsesAdapterOptions { baseUrl: string; apiKey: string; headers?: Record<string, string> }
|
|
59
|
-
|
|
60
|
-
/** the API rejects max_output_tokens below 16 (pi openai-responses.ts:32-33) */
|
|
61
|
-
export const RESPONSES_MIN_OUTPUT_TOKENS = 16;
|
|
62
|
-
/** the ChatGPT Codex backend: no max_output_tokens, plus the two headers below (pi openai-codex-responses.ts) */
|
|
63
|
-
export const CODEX_BACKEND = /chatgpt\.com\/backend-api/;
|
|
64
|
-
export const CODEX_HEADERS: Readonly<Record<string, string>> = { "OpenAI-Beta": "responses=experimental", originator: "rovecode" };
|
|
65
|
-
export const NO_TERMINAL_EVENT = "OpenAI Responses stream ended before a terminal response event";
|
|
66
|
-
|
|
67
|
-
export interface ResponsesRequest { url: string; headers: Record<string, string>; body: Record<string, unknown> }
|
|
68
|
-
|
|
69
|
-
/** the Responses spelling of the thinking dial: the word the OpenAI-compatible dialect would put in `reasoning_effort`
|
|
70
|
-
* (or OpenRouter's `reasoning.effort`) becomes `reasoning: { effort, summary: "auto" }`; nothing when the dial sends
|
|
71
|
-
* nothing (auto, a model without a reasoning mode, the catalog's `reasoning: false` stamp) */
|
|
72
|
-
export function responsesReasoning(model: ModelRef): { reasoning?: { effort: string; summary: "auto" } } {
|
|
73
|
-
const fields = thinkingPlan(model, "openai").fields;
|
|
74
|
-
const nested = fields.reasoning as { effort?: unknown } | undefined;
|
|
75
|
-
const word = typeof fields.reasoning_effort === "string" ? fields.reasoning_effort : typeof nested?.effort === "string" ? nested.effort : undefined;
|
|
76
|
-
return word === undefined ? {} : { reasoning: { effort: word, summary: "auto" } };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** URL, headers and body for one call (header rules); exported so the request shape is testable without fetch */
|
|
80
|
-
export function responsesRequest(opts: ResponsesAdapterOptions, model: ModelRef, messages: Message[], options?: StreamOptions): ResponsesRequest {
|
|
81
|
-
const url = opts.baseUrl.replace(/\/$/, "") + "/responses";
|
|
82
|
-
const codex = CODEX_BACKEND.test(url);
|
|
83
|
-
const { instructions, input } = toResponsesInput(messages, { vision: supportsImages(model) !== false, model }); // port #34 vision gate (stream.ts wireOptions rule)
|
|
84
|
-
const cap = model.maxTokens;
|
|
85
|
-
const body: Record<string, unknown> = {
|
|
86
|
-
model: model.model,
|
|
87
|
-
...(instructions !== undefined ? { instructions } : {}),
|
|
88
|
-
input,
|
|
89
|
-
...(options?.tools?.length ? { tools: toResponsesTools(options.tools) } : {}),
|
|
90
|
-
stream: true,
|
|
91
|
-
store: false,
|
|
92
|
-
...(cap && !codex ? { max_output_tokens: Math.max(cap, RESPONSES_MIN_OUTPUT_TOKENS) } : {}),
|
|
93
|
-
...responsesReasoning(model),
|
|
94
|
-
};
|
|
95
|
-
const headers: Record<string, string> = { "content-type": "application/json", accept: "text/event-stream", ...(codex ? CODEX_HEADERS : {}), ...opts.headers, authorization: `Bearer ${opts.apiKey}` };
|
|
96
|
-
return { url, headers, body };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ---------- SSE state ----------
|
|
100
|
-
|
|
101
|
-
type Slot = { kind: "message"; text: string } | { kind: "function_call"; callId: string; name: string; args: string } | { kind: "reasoning" };
|
|
102
|
-
interface Item { type?: string; id?: string; call_id?: string; name?: string; arguments?: string; content?: { type?: string; text?: string; refusal?: string }[]; summary?: { text?: string }[] }
|
|
103
|
-
interface Event {
|
|
104
|
-
type?: string; output_index?: number; delta?: string; arguments?: string; item?: Item; code?: string; message?: string;
|
|
105
|
-
response?: { usage?: unknown; incomplete_details?: { reason?: string } | null; error?: { code?: string; message?: string } | null };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function newSlot(item: Item | undefined): Slot | undefined {
|
|
109
|
-
if (item?.type === "message") return { kind: "message", text: "" };
|
|
110
|
-
if (item?.type === "function_call") return { kind: "function_call", callId: item.call_id ?? "", name: item.name ?? "", args: item.arguments ?? "" };
|
|
111
|
-
if (item?.type === "reasoning") return { kind: "reasoning" };
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** the part an `output_item.done` item finalises to (pi shared.ts:680-739); undefined for an empty message and for a
|
|
116
|
-
* reasoning item (no reasoning MessagePart here — header) */
|
|
117
|
-
function finishedPart(item: Item, slot: Slot | undefined, index: number): MessagePart | undefined {
|
|
118
|
-
if (item.type === "message") {
|
|
119
|
-
const text = item.content ? item.content.map((c) => (c.type === "refusal" ? c.refusal ?? "" : c.text ?? "")).join("") : slot?.kind === "message" ? slot.text : "";
|
|
120
|
-
return text ? { kind: "text", text } : undefined;
|
|
121
|
-
}
|
|
122
|
-
if (item.type === "function_call") {
|
|
123
|
-
const fc = slot?.kind === "function_call" ? slot : undefined;
|
|
124
|
-
const raw = item.arguments ?? fc?.args ?? "";
|
|
125
|
-
let args: unknown = {};
|
|
126
|
-
try { args = JSON.parse(raw || "{}"); } catch { args = { _raw: raw }; }
|
|
127
|
-
return { kind: "tool_call", id: item.call_id || fc?.callId || item.id || `fc${index}`, tool: item.name || fc?.name || "unknown", args };
|
|
128
|
-
}
|
|
129
|
-
return undefined;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** the same normalization the stream.ts adapters apply (cached share subtracted, zero fields dropped) */
|
|
133
|
-
function turnUsage(raw: unknown): TokenUsage {
|
|
134
|
-
const u = normalizeUsage(raw);
|
|
135
|
-
return { input: u.input, output: u.output, cacheRead: u.cacheRead || undefined, cacheWrite: u.cacheWrite || undefined };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ---------- the adapter ----------
|
|
139
|
-
|
|
140
|
-
export function openaiResponsesStream(opts: ResponsesAdapterOptions): StreamFn {
|
|
141
|
-
return async function* (model: ModelRef, messages: Message[], options?: StreamOptions): AsyncGenerator<StreamEvent> {
|
|
142
|
-
let turn: AssistantTurn;
|
|
143
|
-
let buffer = ""; // text deltas only — the abort salvage (failedTurn); reasoning deltas never land here
|
|
144
|
-
try {
|
|
145
|
-
const req = responsesRequest(opts, model, messages, options);
|
|
146
|
-
const res = await fetchFirstByte(req.url, { method: "POST", headers: req.headers, body: JSON.stringify(req.body), signal: options?.signal });
|
|
147
|
-
if (!res.ok || !res.body) {
|
|
148
|
-
yield { type: "turn", turn: await httpErrorTurn(res) }; // status + Retry-After recorded for withRetry (stream-errors.ts)
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
const slots = new Map<number, Slot>();
|
|
152
|
-
const parts = new Map<number, MessagePart>();
|
|
153
|
-
let usage: TokenUsage = { input: 0, output: 0 };
|
|
154
|
-
let terminal: { stop: StopReason; error?: string } | undefined;
|
|
155
|
-
for await (const line of sseLines(res.body)) {
|
|
156
|
-
const ev = JSON.parse(line) as Event;
|
|
157
|
-
const idx = ev.output_index ?? 0;
|
|
158
|
-
switch (ev.type) {
|
|
159
|
-
case "response.output_item.added": { const s = newSlot(ev.item); if (s) slots.set(idx, s); break; }
|
|
160
|
-
case "response.output_text.delta":
|
|
161
|
-
case "response.refusal.delta": {
|
|
162
|
-
if (!ev.delta) break;
|
|
163
|
-
const s: Slot = slots.get(idx) ?? { kind: "message", text: "" };
|
|
164
|
-
if (s.kind === "message") { s.text += ev.delta; slots.set(idx, s); }
|
|
165
|
-
buffer += ev.delta;
|
|
166
|
-
yield { type: "text_delta", text: ev.delta };
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
case "response.reasoning_summary_text.delta":
|
|
170
|
-
case "response.reasoning_text.delta": if (ev.delta) yield { type: "reasoning_delta", text: ev.delta }; break;
|
|
171
|
-
case "response.reasoning_summary_part.done": yield { type: "reasoning_delta", text: "\n\n" }; break;
|
|
172
|
-
case "response.function_call_arguments.delta": {
|
|
173
|
-
if (!ev.delta) break;
|
|
174
|
-
const s: Slot = slots.get(idx) ?? { kind: "function_call", callId: "", name: "", args: "" };
|
|
175
|
-
if (s.kind !== "function_call") break;
|
|
176
|
-
s.args += ev.delta;
|
|
177
|
-
slots.set(idx, s);
|
|
178
|
-
if (s.callId && s.name) yield { type: "tool_call_delta", id: s.callId, tool: s.name, argsDelta: ev.delta };
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
case "response.function_call_arguments.done": { const s = slots.get(idx); if (s?.kind === "function_call" && typeof ev.arguments === "string") s.args = ev.arguments; break; }
|
|
182
|
-
case "response.output_item.done": {
|
|
183
|
-
if (ev.item) { const p = finishedPart(ev.item, slots.get(idx), idx); if (p) parts.set(idx, p); }
|
|
184
|
-
slots.delete(idx);
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
case "response.completed":
|
|
188
|
-
case "response.incomplete": {
|
|
189
|
-
if (ev.response?.usage) usage = turnUsage(ev.response.usage);
|
|
190
|
-
const reason = ev.response?.incomplete_details?.reason;
|
|
191
|
-
terminal = ev.type === "response.completed" ? { stop: "end_turn" } : reason === "max_output_tokens" ? { stop: "length" } : { stop: "error", error: `Response incomplete: ${reason ?? "no reason given"}` };
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
case "response.failed": { const e = ev.response?.error; terminal = { stop: "error", error: `${e?.code || "unknown"}: ${e?.message || "no message"}` }; break; }
|
|
195
|
-
case "error": terminal = { stop: "error", error: `${ev.code || "unknown"}: ${ev.message || "no message"}` }; break;
|
|
196
|
-
default: break; // response.created / in_progress / content_part.* / *_part.added / *.done text echoes: nothing to do
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
if (!terminal) terminal = { stop: "error", error: NO_TERMINAL_EVENT }; // pi shared.ts:757-759 — never end_turn
|
|
200
|
-
const ordered = [...parts].sort((a, b) => a[0] - b[0]).map(([, p]) => p);
|
|
201
|
-
turn = terminal.stop === "error"
|
|
202
|
-
? { parts: [], stopReason: "error", usage, error: terminal.error }
|
|
203
|
-
: { parts: ordered, stopReason: terminal.stop === "end_turn" && ordered.some((p) => p.kind === "tool_call") ? "tool_use" : terminal.stop, usage };
|
|
204
|
-
} catch (e) {
|
|
205
|
-
turn = failedTurn(e, options?.signal, buffer); // mid-stream abort: the text already streamed survives, tool fragments do not
|
|
206
|
-
}
|
|
207
|
-
yield { type: "turn", turn };
|
|
208
|
-
};
|
|
209
|
-
}
|
package/src/providers/retry.ts
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
/** Same-model retry with exponential backoff (port #23). A StreamFn wrapper — NO second agent
|
|
2
|
-
* loop (ADR-003): bounded re-invocations of the SAME (model, messages, options) inside one
|
|
3
|
-
* stream invocation, the way the router makes one bounded pass over chain candidates. Wired
|
|
4
|
-
* INSIDE the router (`router.wrap(withRetry(stream))`, cli/runtime.ts) so retries exhaust on
|
|
5
|
-
* candidate N before the chain advances to N+1, and the router's notes stay one-per-ADVANCE.
|
|
6
|
-
*
|
|
7
|
-
* Source: gemini-cli (Apache-2.0 @0bd1d43 — research/source_snapshots/google-gemini-gemini-cli,
|
|
8
|
-
* packages/core/src/utils):
|
|
9
|
-
* - retryWithBackoff loop shape: attempt counter vs maxAttempts, delay doubling up to
|
|
10
|
-
* maxDelayMs (retry.ts:296-310, :494-501, :517-524); defaults 10 attempts / 5s initial /
|
|
11
|
-
* 30s max (retry.ts:20, :42-47).
|
|
12
|
-
* - Retryable set: 429 and 5xx; "Explicitly do not retry 400" (retry.ts:193-199); transport
|
|
13
|
-
* failures without a status are retryable (retry.ts:49-62, :174-189). Shared with the router
|
|
14
|
-
* through classifyStreamError (router.ts) — one classifier, two consumers (gemini-cli's 499
|
|
15
|
-
* is excluded there, documented deviation).
|
|
16
|
-
* - Aborts pass through untouched (retry.ts:337-340); the backoff sleep itself is abortable
|
|
17
|
-
* (delay.ts:22-48: timeout + abort listener, both cleared on settle).
|
|
18
|
-
* - A server-suggested delay is a FLOOR on the next sleep (retry.ts:472-476
|
|
19
|
-
* `Math.max(currentDelay, retryDelayMs)`); a suggestion beyond the cap is terminal — no wait,
|
|
20
|
-
* immediate fallback (googleQuotaErrors.ts:120 MAX_RETRYABLE_DELAY_SECONDS, :286-289). Here
|
|
21
|
-
* the suggestion is the HTTP Retry-After header (RFC 9110 §10.2.3: delay-seconds or
|
|
22
|
-
* HTTP-date), OpenAI's retry-after-ms, or Anthropic's ratelimit-reset timestamps, all recorded
|
|
23
|
-
* by stream-errors.ts; the cap is the per-invocation total budget and the run's own deadline.
|
|
24
|
-
* - Deviations: FULL jitter — delay = U[0,1) × min(max, base·2ⁿ) (AWS "Exponential Backoff And
|
|
25
|
-
* Jitter") instead of gemini-cli's ±30% around the current delay (retry.ts:494-495) or +20%
|
|
26
|
-
* over the server floor (:478): fewer synchronized retries, and the schedule is pinnable with
|
|
27
|
-
* an injected random. A total wall-clock cap per invocation (gemini-cli has none): with a
|
|
28
|
-
* fallback chain every candidate pays the retry budget, so it is kept short. No content-based
|
|
29
|
-
* retry (:314-328), no quota classification / fallback dialog (:342-420) — the router owns
|
|
30
|
-
* model fallback. A thrown inner stream is folded into an error turn (never-throw) but NOT
|
|
31
|
-
* retried: a seam-contract violation is a harness bug, not a provider outcome.
|
|
32
|
-
* - Per-invocation state only: attempt counter and deadline live inside one generator run;
|
|
33
|
-
* nothing is remembered across calls (unlike the router's sticky switch).
|
|
34
|
-
* - IDEMPOTENCY (2026-09-04): a retry is taken only while NOTHING has been streamed to the consumer.
|
|
35
|
-
* Once a text or reasoning delta has gone out, a failure ends the turn — with the partial text kept as
|
|
36
|
-
* the turn's parts and an error that says so — because a re-drive would print a second answer under
|
|
37
|
-
* the first (the router applies the same rule to its chain advance). Deltas from a failed attempt that
|
|
38
|
-
* streamed nothing cannot exist, so the "pass through live" rule and this one never meet.
|
|
39
|
-
*
|
|
40
|
-
* Env (retryOptionsFromEnv, read once by cli/runtime.ts):
|
|
41
|
-
* - ROVECODE_RETRY_MAX retries after the first attempt; 0 disables. Default 3 (→ 4 attempts;
|
|
42
|
-
* upstream 10 attempts — a fallback chain multiplies attempts per candidate).
|
|
43
|
-
* - ROVECODE_RETRY_BASE_MS cap of the first backoff in ms. Default 1000 (upstream 5000; full jitter
|
|
44
|
-
* halves the expected wait — ~0.5 s, 1 s, 2 s, 4 s expected).
|
|
45
|
-
* Fixed: max backoff 20 s, total budget 60 s per invocation, and never past StreamOptions.deadlineAt
|
|
46
|
-
* (the run's --max-seconds clock). */
|
|
47
|
-
|
|
48
|
-
import type { AssistantTurn, ModelRef, StreamEvent, StreamFn } from "../core/types.ts";
|
|
49
|
-
import { classifyStreamError } from "./router.ts";
|
|
50
|
-
import { httpErrorMeta, providerMessage } from "./stream-errors.ts";
|
|
51
|
-
|
|
52
|
-
export const DEFAULT_MAX_RETRIES = 3;
|
|
53
|
-
export const DEFAULT_BASE_MS = 1_000;
|
|
54
|
-
export const DEFAULT_MAX_DELAY_MS = 20_000;
|
|
55
|
-
export const DEFAULT_TOTAL_MS = 60_000;
|
|
56
|
-
|
|
57
|
-
export interface RetryNote {
|
|
58
|
-
model: ModelRef;
|
|
59
|
-
/** Attempts made so far (the one that just failed); the retry about to happen is attempt+1. */
|
|
60
|
-
attempt: number;
|
|
61
|
-
/** attempts the policy allows in total (maxRetries + 1) — for "(2/4)" */
|
|
62
|
-
maxAttempts: number;
|
|
63
|
-
delayMs: number;
|
|
64
|
-
/** The failed turn's error text, e.g. "HTTP 429: ...". */
|
|
65
|
-
reason: string;
|
|
66
|
-
/** the HTTP status when the failure was a response; undefined for a transport failure */
|
|
67
|
-
status?: number;
|
|
68
|
-
/** Parsed server wait hint (Retry-After / retry-after-ms / anthropic-ratelimit-*-reset) when the error turn carried one. */
|
|
69
|
-
retryAfterMs?: number;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** why the wrapper stopped retrying a retryable failure */
|
|
73
|
-
export type GiveUpWhy = "attempts" | "budget" | "deadline";
|
|
74
|
-
export interface GiveUpNote extends Omit<RetryNote, "delayMs"> { why: GiveUpWhy; delayMs?: number }
|
|
75
|
-
|
|
76
|
-
export interface RetryOptions {
|
|
77
|
-
/** Retries after the first attempt (ROVECODE_RETRY_MAX). 0 = never retry. */
|
|
78
|
-
maxRetries?: number;
|
|
79
|
-
/** Cap of the first backoff, doubling per attempt (ROVECODE_RETRY_BASE_MS). */
|
|
80
|
-
baseMs?: number;
|
|
81
|
-
/** Ceiling for the doubling backoff term. */
|
|
82
|
-
maxDelayMs?: number;
|
|
83
|
-
/** Wall-clock budget per invocation incl. sleeps; a wait that would end past it is not taken. */
|
|
84
|
-
totalMs?: number;
|
|
85
|
-
/** Retry visibility — a note-style callback, not a StreamEvent (grammar is shared/untouchable). */
|
|
86
|
-
onRetry?: (note: RetryNote) => void;
|
|
87
|
-
/** the last word: a retryable failure that will not be retried (attempts, budget or the run's deadline) */
|
|
88
|
-
onGiveUp?: (note: GiveUpNote) => void;
|
|
89
|
-
/** Test seams: abortable sleep, jitter source, clock (epoch ms — also anchors HTTP-date). */
|
|
90
|
-
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
91
|
-
random?: () => number;
|
|
92
|
-
now?: () => number;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Abortable sleep (delay.ts:22-48 shape) on a REF'D setTimeout — Bun unrefs AbortSignal.timeout
|
|
96
|
-
* timers, so a sleep built on one could not hold the process/test runner — plus an abort
|
|
97
|
-
* listener; whichever settles first clears the other. Resolves (never rejects) on abort: the
|
|
98
|
-
* caller re-checks signal.aborted. */
|
|
99
|
-
export function sleepMs(ms: number, signal?: AbortSignal): Promise<void> {
|
|
100
|
-
return new Promise<void>((resolve) => {
|
|
101
|
-
if (signal?.aborted) { resolve(); return; }
|
|
102
|
-
const settle = (): void => { clearTimeout(timer); signal?.removeEventListener("abort", settle); resolve(); };
|
|
103
|
-
const timer = setTimeout(settle, ms);
|
|
104
|
-
signal?.addEventListener("abort", settle, { once: true });
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** RFC 9110 §10.2.3 Retry-After: delay-seconds (non-negative; a fractional value is tolerated)
|
|
109
|
-
* or an HTTP-date, converted to ms from `now` (a past date → 0). Unparseable → undefined. */
|
|
110
|
-
export function parseRetryAfter(value: string | undefined, now: number): number | undefined {
|
|
111
|
-
const v = value?.trim();
|
|
112
|
-
if (!v) return undefined;
|
|
113
|
-
if (/^\d+(?:\.\d+)?$/.test(v)) return Math.round(Number(v) * 1000);
|
|
114
|
-
const at = Date.parse(v);
|
|
115
|
-
return Number.isNaN(at) ? undefined : Math.max(0, at - now);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** the server's wait hint for a failed turn, whichever header it used: the largest of Retry-After,
|
|
119
|
-
* retry-after-ms and the Anthropic ratelimit reset timestamps (RFC 3339) — the longest wait is the
|
|
120
|
-
* one that will actually clear the limit */
|
|
121
|
-
export function serverWaitMs(turn: AssistantTurn, now: number): number | undefined {
|
|
122
|
-
const meta = httpErrorMeta(turn);
|
|
123
|
-
if (!meta) return undefined;
|
|
124
|
-
const hints: number[] = [];
|
|
125
|
-
const ra = parseRetryAfter(meta.retryAfter, now);
|
|
126
|
-
if (ra !== undefined) hints.push(ra);
|
|
127
|
-
if (meta.retryAfterMs !== undefined && /^\d+(?:\.\d+)?$/.test(meta.retryAfterMs.trim())) hints.push(Math.round(Number(meta.retryAfterMs)));
|
|
128
|
-
if (meta.resetAt !== undefined) { const at = Date.parse(meta.resetAt); if (!Number.isNaN(at)) hints.push(Math.max(0, at - now)); }
|
|
129
|
-
return hints.length ? Math.max(...hints) : undefined;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Env knobs (header). Blank/invalid/out-of-range values fall back to the defaults;
|
|
133
|
-
* ROVECODE_RETRY_MAX=0 is honored (retry off). */
|
|
134
|
-
export function retryOptionsFromEnv(env: Record<string, string | undefined> = process.env): RetryOptions {
|
|
135
|
-
return { maxRetries: envInt(env.ROVECODE_RETRY_MAX, DEFAULT_MAX_RETRIES, 0), baseMs: envInt(env.ROVECODE_RETRY_BASE_MS, DEFAULT_BASE_MS, 1) };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function envInt(raw: string | undefined, dflt: number, min: number): number {
|
|
139
|
-
if (raw === undefined || raw.trim() === "") return dflt;
|
|
140
|
-
const n = Number(raw);
|
|
141
|
-
return Number.isFinite(n) && n >= min ? Math.floor(n) : dflt;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const errorTurn = (error: string): AssistantTurn => ({ parts: [], stopReason: "error", usage: { input: 0, output: 0 }, error });
|
|
145
|
-
|
|
146
|
-
// ---------- the human's words ----------
|
|
147
|
-
|
|
148
|
-
/** one noun phrase per failure class — what the notice and the give-up line lead with */
|
|
149
|
-
export function failureWord(status: number | undefined, error: string): string {
|
|
150
|
-
if (status === 429) return "rate limited";
|
|
151
|
-
if (status === 529 || status === 503) return "overloaded";
|
|
152
|
-
if (status !== undefined && status >= 500) return `server error (HTTP ${status})`;
|
|
153
|
-
if (status !== undefined) return `HTTP ${status}`;
|
|
154
|
-
if (/^no response from /.test(error)) return "no response";
|
|
155
|
-
return "connection failed";
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const fmtSeconds = (ms: number): string => { const s = ms / 1000; return `${s >= 10 ? Math.round(s) : Math.round(s * 10) / 10} s`; };
|
|
159
|
-
|
|
160
|
-
/** "anthropic: overloaded — retrying in 4 s (2/4)" */
|
|
161
|
-
export function describeRetry(n: RetryNote): string {
|
|
162
|
-
return `${n.model.provider}: ${failureWord(n.status, n.reason)} — retrying in ${fmtSeconds(n.delayMs)} (${n.attempt + 1}/${n.maxAttempts})`;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** "anthropic: overloaded (HTTP 529) — gave up after 4 attempts: Overloaded" */
|
|
166
|
-
export function describeGiveUp(n: GiveUpNote): string {
|
|
167
|
-
const why = n.why === "attempts" ? `gave up after ${n.attempt} attempt${n.attempt === 1 ? "" : "s"}`
|
|
168
|
-
: n.why === "deadline" ? `not retried: the run's time limit is closer than the ${fmtSeconds(n.delayMs ?? 0)} wait`
|
|
169
|
-
: `not retried: the ${fmtSeconds(n.delayMs ?? 0)} wait would pass the retry budget`;
|
|
170
|
-
const status = n.status !== undefined && !/HTTP/.test(failureWord(n.status, n.reason)) ? ` (HTTP ${n.status})` : "";
|
|
171
|
-
const msg = providerMessage(n.reason);
|
|
172
|
-
return `${n.model.provider}: ${failureWord(n.status, n.reason)}${status} — ${why}${msg ? `: ${msg}` : ""}`;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** Wrap a StreamFn: a retryable failed turn (429 / 5xx / transport, never 400, never abort, never after
|
|
176
|
-
* a delta went out) is re-driven against the SAME model with the same args after an abortable
|
|
177
|
-
* full-jitter backoff, until it succeeds, a non-retryable outcome lands, or a cap (attempts / total
|
|
178
|
-
* budget / the run's deadline) stops it — then the LAST turn is yielded untouched so the router sees
|
|
179
|
-
* the genuine provider error. Never throws (ADR-003). */
|
|
180
|
-
export function withRetry(inner: StreamFn, opts: RetryOptions = {}): StreamFn {
|
|
181
|
-
const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
182
|
-
const baseMs = opts.baseMs ?? DEFAULT_BASE_MS;
|
|
183
|
-
const maxDelayMs = opts.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
184
|
-
const totalMs = opts.totalMs ?? DEFAULT_TOTAL_MS;
|
|
185
|
-
const sleep = opts.sleep ?? sleepMs;
|
|
186
|
-
const random = opts.random ?? Math.random;
|
|
187
|
-
const now = opts.now ?? Date.now;
|
|
188
|
-
return async function* (model, messages, options): AsyncGenerator<StreamEvent> {
|
|
189
|
-
const startedAt = now();
|
|
190
|
-
for (let attempt = 1; ; attempt++) {
|
|
191
|
-
let turn: AssistantTurn | null = null;
|
|
192
|
-
let streamed = ""; // every text delta this attempt let through — the answer the consumer has already seen
|
|
193
|
-
try {
|
|
194
|
-
for await (const ev of inner(model, messages, options)) {
|
|
195
|
-
if (ev.type === "turn") turn = ev.turn;
|
|
196
|
-
else { if (ev.type === "text_delta") streamed += ev.text; else if (ev.type === "reasoning_delta") streamed ||= " "; yield ev; } // deltas pass through live (header)
|
|
197
|
-
}
|
|
198
|
-
} catch (e) {
|
|
199
|
-
yield { type: "turn", turn: errorTurn(e instanceof Error ? e.message : String(e)) }; // folded, not retried (header)
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
if (turn === null) { yield { type: "turn", turn: errorTurn("stream ended without a terminal turn") }; return; }
|
|
203
|
-
const meta = httpErrorMeta(turn);
|
|
204
|
-
const status = meta?.status ?? classifyStreamError(turn.error).status;
|
|
205
|
-
// retry.ts:337-340: aborts never retry; 400/4xx-non-429 and non-error turns stand as they are
|
|
206
|
-
if (turn.stopReason !== "error" || options?.signal?.aborted === true || !classifyStreamError(turn.error).retryable) {
|
|
207
|
-
yield { type: "turn", turn };
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (streamed.length > 0) {
|
|
211
|
-
// idempotency (header): part of the answer is on the screen — end the turn, keep that text as the
|
|
212
|
-
// turn's parts (the loop stores it and the summary shows it above the error), say why no retry
|
|
213
|
-
const kept = streamed.trim().length > 0 && turn.parts.length === 0 ? [{ kind: "text" as const, text: streamed }] : turn.parts;
|
|
214
|
-
yield { type: "turn", turn: { ...turn, parts: kept, error: `${turn.error ?? "provider stream failed"} — the connection dropped after part of the answer had arrived; not retried, a retry would repeat it` } };
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const note = { model, attempt, maxAttempts: maxRetries + 1, reason: turn.error ?? "error", ...(status !== undefined ? { status } : {}) };
|
|
218
|
-
// retries off (ROVECODE_RETRY_MAX=0): nothing was ever going to be retried, so there is no giving up to announce
|
|
219
|
-
if (attempt > maxRetries) { if (maxRetries > 0) opts.onGiveUp?.({ ...note, why: "attempts" }); yield { type: "turn", turn }; return; }
|
|
220
|
-
const retryAfterMs = serverWaitMs(turn, now());
|
|
221
|
-
const cap = Math.min(maxDelayMs, baseMs * 2 ** (attempt - 1));
|
|
222
|
-
const delayMs = Math.max(Math.round(random() * cap), retryAfterMs ?? 0); // full jitter, server floor (retry.ts:476)
|
|
223
|
-
const withHint = retryAfterMs !== undefined ? { retryAfterMs } : {};
|
|
224
|
-
// budget: a wait that would end past the deadline is not taken — the failure surfaces now
|
|
225
|
-
// and the router may advance at once (googleQuotaErrors.ts:286-289 shape)
|
|
226
|
-
if (now() - startedAt + delayMs > totalMs) { opts.onGiveUp?.({ ...note, ...withHint, delayMs, why: "budget" }); yield { type: "turn", turn }; return; }
|
|
227
|
-
// the run's own clock (--max-seconds): a wait that ends past it would only be cut off at the turn boundary
|
|
228
|
-
if (options?.deadlineAt !== undefined && now() + delayMs > options.deadlineAt) { opts.onGiveUp?.({ ...note, ...withHint, delayMs, why: "deadline" }); yield { type: "turn", turn }; return; }
|
|
229
|
-
opts.onRetry?.({ ...note, delayMs, ...withHint });
|
|
230
|
-
await sleep(delayMs, options?.signal);
|
|
231
|
-
if (options?.signal?.aborted) { yield { type: "turn", turn }; return; } // abort landed during backoff: last turn stands
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
}
|