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
package/src/core/config.ts
DELETED
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project context inheritance (port #8): auto-import instruction files from
|
|
3
|
-
* other coding-agent "harnesses" (Claude, Gemini, Cursor, Copilot, plain
|
|
4
|
-
* AGENTS.md) so rovecode projects don't need to duplicate repo conventions.
|
|
5
|
-
*
|
|
6
|
-
* Pattern + precedence order modeled on oh-my-pi's context-file discovery
|
|
7
|
-
* (research/source_snapshots/can1357-oh-my-pi):
|
|
8
|
-
* - docs/context-files.md, "Other supported context conventions" and
|
|
9
|
-
* "Load order and shadowing" tables — the provider-priority idea
|
|
10
|
-
* (higher priority wins at a shared scope) and the per-tool path
|
|
11
|
-
* conventions: AGENTS.md, CLAUDE.md / .claude/CLAUDE.md, GEMINI.md,
|
|
12
|
-
* .cursor/rules/*.mdc + legacy .cursorrules, .github/copilot-instructions.md.
|
|
13
|
-
* - packages/coding-agent/src/discovery/builtin.ts `getAncestorDirs` — the
|
|
14
|
-
* cwd-upward ancestor walk with an optional inclusive stop directory.
|
|
15
|
-
* - packages/coding-agent/src/discovery/cursor.ts — `.cursor/rules/*.mdc`
|
|
16
|
-
* carries MDC frontmatter that must be separated from the rule body.
|
|
17
|
-
*
|
|
18
|
-
* Discovery + precedence (rovecode's documented spec):
|
|
19
|
-
* - Ancestor walk: cwd UPWARD via dirname until parent === current (the
|
|
20
|
-
* filesystem root). The walk additionally stops — INCLUSIVELY — at the
|
|
21
|
-
* first directory containing `.git` (file or directory; worktrees use a
|
|
22
|
-
* file), so a repository never inherits context from outside itself.
|
|
23
|
-
* `opts.stopAt` bounds the walk at an explicit dir (also inclusive).
|
|
24
|
-
* - Precedence: NEARER directories first (nearest wins); within one
|
|
25
|
-
* directory, family order rovecode > agents > claude > gemini > cursor >
|
|
26
|
-
* copilot (the harvest list in `buildCandidates`). Earlier position wins
|
|
27
|
-
* dedupe and total-cap priority.
|
|
28
|
-
* - Shadowing (dedupe by depth): the same relative path (e.g. `AGENTS.md`)
|
|
29
|
-
* found in a nearer directory completely shadows the farther one — the
|
|
30
|
-
* farther file is never read and never listed.
|
|
31
|
-
* - Blank files (empty or whitespace-only, after MDC frontmatter stripping)
|
|
32
|
-
* are skipped entirely: no section, no dedupe registration, no shadowing.
|
|
33
|
-
* - Byte-identical content across surviving candidates is included once;
|
|
34
|
-
* the earlier (higher-precedence) occurrence wins and later duplicates
|
|
35
|
-
* never reach `sources` — unlike total-cap drops, which keep a stub.
|
|
36
|
-
*
|
|
37
|
-
* Budgets (rovecode-specific; OMP has no cap on context-file loading):
|
|
38
|
-
* - `maxPerFileChars` caps each file's content. Truncation is fence-safe:
|
|
39
|
-
* cut at the last newline inside the window and close an odd ``` fence
|
|
40
|
-
* count so the following sections aren't swallowed by an open code block.
|
|
41
|
-
* - `maxTotalChars` budgets the FULL rendered section string — the
|
|
42
|
-
* `## From <path>` header included, so `text.length` never exceeds it.
|
|
43
|
-
* - `maxFiles` bounds how many files are included; further existing
|
|
44
|
-
* candidate files are counted in `skippedFiles` but never read.
|
|
45
|
-
*
|
|
46
|
-
* Freshness: callers snapshot the result once per runtime (cli/runtime.ts)
|
|
47
|
-
* so the system prompt stays byte-stable for prompt caching (port #5).
|
|
48
|
-
* Mid-session edits to config files are intentionally not picked up —
|
|
49
|
-
* restart rovecode (a new runtime) to refresh.
|
|
50
|
-
*
|
|
51
|
-
* Remaining deliberate deviations from OMP: no provider-priority shadowing
|
|
52
|
-
* table (content dedupe + path shadowing instead); `.cursor/rules/*.mdc`
|
|
53
|
-
* harvested unconditionally as plain text (frontmatter stripped, never
|
|
54
|
-
* parsed/acted on) and restricted to `*.mdc`; the character/file budgets are
|
|
55
|
-
* rovecode's own.
|
|
56
|
-
*/
|
|
57
|
-
|
|
58
|
-
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
59
|
-
import { join, dirname, resolve } from "node:path";
|
|
60
|
-
|
|
61
|
-
export interface ContextSource {
|
|
62
|
-
/** Display path relative to cwd, "/"-separated with one "../" segment per
|
|
63
|
-
* ancestor level — used verbatim in the rendered "## From <path>" header. */
|
|
64
|
-
path: string;
|
|
65
|
-
family: "rovecode" | "agents" | "claude" | "gemini" | "cursor" | "copilot";
|
|
66
|
-
chars: number;
|
|
67
|
-
truncated: boolean;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface ProjectContext {
|
|
71
|
-
text: string;
|
|
72
|
-
sources: ContextSource[];
|
|
73
|
-
/** Existing candidate files that were NOT read because the `maxFiles`
|
|
74
|
-
* bound was already reached (surfaced in /status, never silent). */
|
|
75
|
-
skippedFiles: number;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface LoadOptions {
|
|
79
|
-
/** Per-file cap in characters. Default 8000. */
|
|
80
|
-
maxPerFileChars?: number;
|
|
81
|
-
/** Total cap on the rendered text (headers included). Default 24000. */
|
|
82
|
-
maxTotalChars?: number;
|
|
83
|
-
/** Max files included across the walk. Default 24. */
|
|
84
|
-
maxFiles?: number;
|
|
85
|
-
/** Inclusive upper bound for the ancestor walk (tests/embedders). */
|
|
86
|
-
stopAt?: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const DEFAULT_MAX_PER_FILE_CHARS = 8000;
|
|
90
|
-
const DEFAULT_MAX_TOTAL_CHARS = 24000;
|
|
91
|
-
const DEFAULT_MAX_FILES = 24;
|
|
92
|
-
const TRUNCATION_MARKER = "…[truncated]";
|
|
93
|
-
|
|
94
|
-
type Family = ContextSource["family"];
|
|
95
|
-
|
|
96
|
-
interface Candidate {
|
|
97
|
-
/** Path relative to its directory, "/"-separated regardless of host OS. */
|
|
98
|
-
relPath: string;
|
|
99
|
-
family: Family;
|
|
100
|
-
/** Strip a leading MDC frontmatter block before treating this as content. */
|
|
101
|
-
mdc: boolean;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Harvest list for ONE directory, in family precedence order (first =
|
|
105
|
-
* highest): rovecode > agents > claude > gemini > cursor > copilot. */
|
|
106
|
-
function buildCandidates(dir: string): Candidate[] {
|
|
107
|
-
const candidates: Candidate[] = [
|
|
108
|
-
{ relPath: ".rovecode/ROVECODE.md", family: "rovecode", mdc: false },
|
|
109
|
-
{ relPath: "ROVECODE.md", family: "rovecode", mdc: false },
|
|
110
|
-
{ relPath: "AGENTS.md", family: "agents", mdc: false },
|
|
111
|
-
{ relPath: "CLAUDE.md", family: "claude", mdc: false },
|
|
112
|
-
{ relPath: ".claude/CLAUDE.md", family: "claude", mdc: false },
|
|
113
|
-
{ relPath: "GEMINI.md", family: "gemini", mdc: false },
|
|
114
|
-
{ relPath: ".cursorrules", family: "cursor", mdc: false },
|
|
115
|
-
];
|
|
116
|
-
for (const name of listCursorRuleFiles(dir)) {
|
|
117
|
-
candidates.push({ relPath: `.cursor/rules/${name}`, family: "cursor", mdc: true });
|
|
118
|
-
}
|
|
119
|
-
candidates.push({ relPath: ".github/copilot-instructions.md", family: "copilot", mdc: false });
|
|
120
|
-
return candidates;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** cwd upward: dirname until parent === current (filesystem root), stopping
|
|
124
|
-
* inclusively at `stopAt` or at the first dir containing `.git` (file or
|
|
125
|
-
* directory — git worktrees use a file). OMP `getAncestorDirs` pattern. */
|
|
126
|
-
function ancestorDirs(cwd: string, stopAt?: string): string[] {
|
|
127
|
-
const dirs: string[] = [];
|
|
128
|
-
const stop = stopAt === undefined ? null : resolve(stopAt);
|
|
129
|
-
let current = resolve(cwd);
|
|
130
|
-
for (;;) {
|
|
131
|
-
dirs.push(current);
|
|
132
|
-
if (stop !== null && current === stop) break;
|
|
133
|
-
if (hasGitMarker(current)) break; // repo root — inclusive, never above
|
|
134
|
-
const parent = dirname(current);
|
|
135
|
-
if (parent === current) break; // filesystem root
|
|
136
|
-
current = parent;
|
|
137
|
-
}
|
|
138
|
-
return dirs;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** `.git` presence, or false on any fs error (permissions, bad path, …). */
|
|
142
|
-
function hasGitMarker(dir: string): boolean {
|
|
143
|
-
try {
|
|
144
|
-
return existsSync(join(dir, ".git"));
|
|
145
|
-
} catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/** `.cursor/rules/*.mdc`, sorted by filename. A missing/unreadable directory
|
|
151
|
-
* yields no entries — silent skip, never a throw. */
|
|
152
|
-
function listCursorRuleFiles(dir: string): string[] {
|
|
153
|
-
try {
|
|
154
|
-
const entries = readdirSync(join(dir, ".cursor", "rules"), { withFileTypes: true });
|
|
155
|
-
return entries
|
|
156
|
-
.filter((e) => e.isFile() && e.name.endsWith(".mdc"))
|
|
157
|
-
.map((e) => e.name)
|
|
158
|
-
.sort();
|
|
159
|
-
} catch {
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** Read a file's content, or null on any failure (missing, unreadable, is a
|
|
165
|
-
* directory, …) — every failure mode is a silent skip, never a throw. */
|
|
166
|
-
function tryReadFile(absPath: string): string | null {
|
|
167
|
-
try {
|
|
168
|
-
return readFileSync(absPath, "utf8");
|
|
169
|
-
} catch {
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** existsSync that can never throw (used only for `skippedFiles` counting). */
|
|
175
|
-
function fileExists(absPath: string): boolean {
|
|
176
|
-
try {
|
|
177
|
-
return existsSync(absPath);
|
|
178
|
-
} catch {
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** Strip a leading `---\n...\n---` MDC frontmatter block, if present. */
|
|
184
|
-
function stripMdcFrontmatter(content: string): string {
|
|
185
|
-
const lines = content.split("\n");
|
|
186
|
-
if ((lines[0] ?? "").trim() !== "---") return content;
|
|
187
|
-
let endIdx = -1;
|
|
188
|
-
for (let i = 1; i < lines.length; i++) {
|
|
189
|
-
if ((lines[i] ?? "").trim() === "---") {
|
|
190
|
-
endIdx = i;
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (endIdx === -1) return content;
|
|
195
|
-
return lines.slice(endIdx + 1).join("\n").replace(/^\n+/, "");
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Fence-safe truncation: cut at the last newline inside the window (whole
|
|
199
|
-
* lines only; hard cut when the window has no interior newline), close an
|
|
200
|
-
* odd ``` fence count so following sections aren't swallowed by an open
|
|
201
|
-
* code block, then append the marker. */
|
|
202
|
-
function truncateSafely(full: string, maxChars: number): string {
|
|
203
|
-
const window = full.slice(0, maxChars);
|
|
204
|
-
const nl = window.lastIndexOf("\n");
|
|
205
|
-
const cut = nl > 0 ? window.slice(0, nl) : window;
|
|
206
|
-
const fences = cut.split("\n").filter((l) => l.trimStart().startsWith("```")).length;
|
|
207
|
-
return fences % 2 === 1 ? `${cut}${TRUNCATION_MARKER}\n\`\`\`` : cut + TRUNCATION_MARKER;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
interface KeptFile {
|
|
211
|
-
/** cwd-relative display path: "../" per ancestor level + relPath. */
|
|
212
|
-
displayPath: string;
|
|
213
|
-
family: Family;
|
|
214
|
-
/** Content after per-file truncation (includes the marker when truncated). */
|
|
215
|
-
content: string;
|
|
216
|
-
truncated: boolean;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Load and merge instruction files from every supported harness convention
|
|
221
|
-
* found in `cwd` and its ancestors (see module doc for the walk, precedence,
|
|
222
|
-
* shadowing, and budget rules). Deterministic: identical tree contents always
|
|
223
|
-
* produce the same `text` and `sources`, in the same order.
|
|
224
|
-
*/
|
|
225
|
-
export function loadProjectContext(cwd: string, opts?: LoadOptions): ProjectContext {
|
|
226
|
-
const maxPerFileChars = opts?.maxPerFileChars ?? DEFAULT_MAX_PER_FILE_CHARS;
|
|
227
|
-
const maxTotalChars = opts?.maxTotalChars ?? DEFAULT_MAX_TOTAL_CHARS;
|
|
228
|
-
const maxFiles = opts?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
229
|
-
|
|
230
|
-
const seenContent = new Set<string>();
|
|
231
|
-
const shadowed = new Set<string>(); // relPaths claimed by a nearer non-blank file
|
|
232
|
-
const kept: KeptFile[] = [];
|
|
233
|
-
let skippedFiles = 0;
|
|
234
|
-
|
|
235
|
-
const dirs = ancestorDirs(cwd, opts?.stopAt);
|
|
236
|
-
for (let depth = 0; depth < dirs.length; depth++) {
|
|
237
|
-
const dir = dirs[depth]!;
|
|
238
|
-
for (const candidate of buildCandidates(dir)) {
|
|
239
|
-
if (shadowed.has(candidate.relPath)) continue; // nearest wins (dedupe by depth)
|
|
240
|
-
const absPath = join(dir, candidate.relPath);
|
|
241
|
-
if (kept.length >= maxFiles) {
|
|
242
|
-
// file-count bound (HIGH-3): count existing candidates, never read them
|
|
243
|
-
if (fileExists(absPath)) skippedFiles++;
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
const raw = tryReadFile(absPath);
|
|
247
|
-
if (raw === null) continue; // missing or unreadable — skip silently
|
|
248
|
-
|
|
249
|
-
const full = candidate.mdc ? stripMdcFrontmatter(raw) : raw;
|
|
250
|
-
if (full.trim() === "") continue; // blank: no section/dedupe/shadow (LOW-6)
|
|
251
|
-
shadowed.add(candidate.relPath);
|
|
252
|
-
|
|
253
|
-
if (seenContent.has(full)) continue; // byte-identical dupe: earlier one won
|
|
254
|
-
seenContent.add(full);
|
|
255
|
-
|
|
256
|
-
let content = full;
|
|
257
|
-
let truncated = false;
|
|
258
|
-
if (full.length > maxPerFileChars) {
|
|
259
|
-
content = truncateSafely(full, maxPerFileChars);
|
|
260
|
-
truncated = true;
|
|
261
|
-
}
|
|
262
|
-
kept.push({
|
|
263
|
-
displayPath: "../".repeat(depth) + candidate.relPath,
|
|
264
|
-
family: candidate.family, content, truncated,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Total cap enforced in precedence order over the FULL section string —
|
|
270
|
-
// header included, so `text.length <= maxTotalChars` always holds. A file
|
|
271
|
-
// that would exceed the budget is dropped from `text` but stays listed in
|
|
272
|
-
// `sources` as a chars:0/truncated:true stub so callers can see what was
|
|
273
|
-
// cut, and why the surviving text is short.
|
|
274
|
-
const sources: ContextSource[] = [];
|
|
275
|
-
const sections: string[] = [];
|
|
276
|
-
let total = 0;
|
|
277
|
-
for (const file of kept) {
|
|
278
|
-
const section = `\n\n## From ${file.displayPath}\n${file.content}`;
|
|
279
|
-
if (total + section.length <= maxTotalChars) {
|
|
280
|
-
sources.push({ path: file.displayPath, family: file.family, chars: file.content.length, truncated: file.truncated });
|
|
281
|
-
sections.push(section);
|
|
282
|
-
total += section.length;
|
|
283
|
-
} else {
|
|
284
|
-
sources.push({ path: file.displayPath, family: file.family, chars: 0, truncated: true });
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return { text: sections.join(""), sources, skippedFiles };
|
|
289
|
-
}
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/** What is actually in the context window, item by item — and how far our arithmetic is from what
|
|
2
|
-
* the provider says it charged for.
|
|
3
|
-
*
|
|
4
|
-
* Two numbers exist for every turn and they are NOT the same thing:
|
|
5
|
-
* - the ESTIMATE: o200k over every part we are about to send. Ours, synchronous, available before
|
|
6
|
-
* a request and for a session that never ran. An estimate for anything that is not an OpenAI
|
|
7
|
-
* tokenizer — Anthropic's is not public, and its 4.7-generation tokenizer produces materially
|
|
8
|
-
* more tokens for the same text, so the estimate reads LOW there.
|
|
9
|
-
* - the REPORTED prompt: what the provider itself counted, i.e. `input + cacheRead + cacheWrite`
|
|
10
|
-
* of a turn's usage. Cache reads and writes are part of the prompt the model saw; leaving them
|
|
11
|
-
* out is the most common way a context meter reads far too low on an agentic session, where
|
|
12
|
-
* almost the whole prompt is a cache read.
|
|
13
|
-
* drift() compares them at the same point in the transcript, so "how wrong is our meter for this
|
|
14
|
-
* provider" becomes a measured number instead of a belief. Beyond DRIFT_TOLERANCE it is worth
|
|
15
|
-
* saying out loud: it means compaction fires at the wrong time.
|
|
16
|
-
*
|
|
17
|
-
* Nothing here reads the network or the disk, and nothing throws: a transcript with no usage at all
|
|
18
|
-
* still produces a report, with `drift` simply absent. */
|
|
19
|
-
|
|
20
|
-
import { partsTokenText } from "./loop.ts";
|
|
21
|
-
import type { Message, MessagePart } from "./types.ts";
|
|
22
|
-
import { tokenScaleFor } from "./token-scale.ts";
|
|
23
|
-
import { contextHealth, costUsdTiered, countTokens, type NormalizedUsage, type PricingRow } from "./usage.ts";
|
|
24
|
-
import { ratesFor } from "../providers/catalog.ts";
|
|
25
|
-
import type { PriceTier } from "../providers/catalog-local.ts";
|
|
26
|
-
|
|
27
|
-
/** past this the estimate is misleading enough to name — the compaction trigger reads the estimate */
|
|
28
|
-
export const DRIFT_TOLERANCE = 0.05;
|
|
29
|
-
|
|
30
|
-
/** the budget for a model whose window we do not know — the old flat value, kept as the fallback */
|
|
31
|
-
export const DEFAULT_CONTEXT_BUDGET = 200_000;
|
|
32
|
-
/** never plan for less history than this, however small the window says it is */
|
|
33
|
-
export const MIN_CONTEXT_BUDGET = 32_000;
|
|
34
|
-
/** room left beside the history for the system prompt, the tool schemas and the indexes */
|
|
35
|
-
export const PROMPT_OVERHEAD_TOKENS = 24_000;
|
|
36
|
-
/** assumed answer room when the catalog states no output limit */
|
|
37
|
-
const ASSUMED_OUTPUT = 32_000;
|
|
38
|
-
|
|
39
|
-
/** How much history a run may carry before compaction. A flat 200k spends a fifth of a 1M window and
|
|
40
|
-
* overflows a 128k one, so it is derived: the window minus what the answer and the fixed prompt need.
|
|
41
|
-
* An explicit override wins (ROVECODE_CONTEXT_BUDGET), an unknown window keeps the old default, and a
|
|
42
|
-
* window too small to hold the floor gets a proportional share rather than a budget larger than itself. */
|
|
43
|
-
export function contextBudgetFor(opts: { window?: number; maxOutput?: number; override?: number; scale?: number }): number {
|
|
44
|
-
const { window, maxOutput, override } = opts;
|
|
45
|
-
if (override !== undefined && Number.isFinite(override) && override > 0) return Math.floor(override);
|
|
46
|
-
if (!window || !Number.isFinite(window) || window <= 0) return DEFAULT_CONTEXT_BUDGET;
|
|
47
|
-
const reserve = (Number.isFinite(maxOutput) && (maxOutput ?? 0) > 0 ? (maxOutput as number) : ASSUMED_OUTPUT) + PROMPT_OVERHEAD_TOKENS;
|
|
48
|
-
const room = window - reserve;
|
|
49
|
-
const raw = room < MIN_CONTEXT_BUDGET ? Math.max(1, Math.floor(window * 0.6)) : Math.floor(room);
|
|
50
|
-
// The budget is compared against an estimate, so a model whose tokenizer counts more than the
|
|
51
|
-
// estimator must get a smaller budget — dividing here is exactly equivalent to inflating every
|
|
52
|
-
// estimate at every call site, and there is one of it. See core/token-scale.ts for the measurements.
|
|
53
|
-
const scale = opts.scale !== undefined && Number.isFinite(opts.scale) && opts.scale > 0 ? opts.scale : 1;
|
|
54
|
-
return Math.max(1, Math.floor(raw / scale));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface ContextSlice {
|
|
58
|
-
label: string;
|
|
59
|
-
tokens: number;
|
|
60
|
-
/** of the estimate, 0..1 — 0 when the estimate is 0 */
|
|
61
|
-
share: number;
|
|
62
|
-
/** what the reader should know about this row, when a number alone would mislead */
|
|
63
|
-
note?: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface ContextDrift {
|
|
67
|
-
/** our estimate of the prompt at the last turn that reported usage */
|
|
68
|
-
estimated: number;
|
|
69
|
-
/** what that turn says it was given: input + cacheRead + cacheWrite */
|
|
70
|
-
reported: number;
|
|
71
|
-
/** reported − estimated; positive means we are UNDER-counting the real window */
|
|
72
|
-
delta: number;
|
|
73
|
-
/** |delta| / reported, 0 when reported is 0 */
|
|
74
|
-
fraction: number;
|
|
75
|
-
beyondTolerance: boolean;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface ContextReport {
|
|
79
|
-
model: { provider: string; model: string };
|
|
80
|
-
/** the catalog's context window for the current model, when it knows one */
|
|
81
|
-
window?: number;
|
|
82
|
-
/** o200k over the whole transcript — what the next request would carry */
|
|
83
|
-
estimated: number;
|
|
84
|
-
/** the estimate corrected towards this model's own tokenizer; equals `estimated` when unmeasured.
|
|
85
|
-
* The window rows below are computed from THIS, because it is the number the provider will use. */
|
|
86
|
-
corrected: number;
|
|
87
|
-
/** the correction that was applied, and where its number came from */
|
|
88
|
-
scale: { factor: number; measured: boolean; note: string };
|
|
89
|
-
slices: ContextSlice[];
|
|
90
|
-
/** window − corrected, floored at 0; absent when the window is unknown */
|
|
91
|
-
remaining?: number;
|
|
92
|
-
/** corrected / window; absent when the window is unknown */
|
|
93
|
-
fraction?: number;
|
|
94
|
-
nearLimit?: boolean;
|
|
95
|
-
drift?: ContextDrift;
|
|
96
|
-
/** summed over every turn that reported usage */
|
|
97
|
-
totals: NormalizedUsage;
|
|
98
|
-
/** USD over the turns that could be priced, and how many could not */
|
|
99
|
-
costUsd?: number;
|
|
100
|
-
unpricedTurns: number;
|
|
101
|
-
/** images carry tokens we do not estimate — say how many rather than pretend they are free */
|
|
102
|
-
images: number;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface ReportInput {
|
|
106
|
-
messages: readonly Message[];
|
|
107
|
-
/** the current model, used for the window and for turns with no origin */
|
|
108
|
-
current: { provider: string; model: string };
|
|
109
|
-
/** window + pricing for a model, however the caller gets them (catalog, overlay, a test double) */
|
|
110
|
-
lookup: (ref: { provider: string; model: string }) => { contextWindow?: number; pricing?: PricingRow; tier?: PriceTier } | undefined;
|
|
111
|
-
/** the system prompt that will be sent, when the caller has it */
|
|
112
|
-
system?: string;
|
|
113
|
-
/** the serialized tool schemas that will be sent, when the caller has them */
|
|
114
|
-
toolSchemas?: string;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const tokensOf = (text: string): number => (text ? countTokens(text) : 0);
|
|
118
|
-
|
|
119
|
-
function partTokens(parts: readonly MessagePart[], kind: MessagePart["kind"]): number {
|
|
120
|
-
const only = parts.filter((p) => p.kind === kind);
|
|
121
|
-
return only.length === 0 ? 0 : tokensOf(partsTokenText(only as MessagePart[]));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Roles are grouped the way a reader thinks about them, not the way the wire does: what I sent, what
|
|
125
|
-
* the model said, what the tools were asked, what they answered. Tool traffic dominates an agentic
|
|
126
|
-
* session and hiding it inside "assistant" is what makes a context meter useless. */
|
|
127
|
-
export function contextReport(input: ReportInput): ContextReport {
|
|
128
|
-
const { messages, current, lookup } = input;
|
|
129
|
-
const info = lookup(current);
|
|
130
|
-
|
|
131
|
-
const userText = messages.filter((m) => m.role === "user");
|
|
132
|
-
const assistantText = messages.filter((m) => m.role === "assistant");
|
|
133
|
-
const systemMsgs = messages.filter((m) => m.role === "system");
|
|
134
|
-
|
|
135
|
-
const slices: ContextSlice[] = [];
|
|
136
|
-
const push = (label: string, tokens: number, note?: string) => {
|
|
137
|
-
if (tokens > 0) slices.push({ label, tokens, share: 0, ...(note ? { note } : {}) });
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
push("system prompt", tokensOf(input.system ?? "") + partTokens(systemMsgs.flatMap((m) => m.parts), "text"));
|
|
141
|
-
push("tool schemas", tokensOf(input.toolSchemas ?? ""), input.toolSchemas ? undefined : "not supplied");
|
|
142
|
-
push("your messages", partTokens(userText.flatMap((m) => m.parts), "text"));
|
|
143
|
-
push("assistant replies", partTokens(assistantText.flatMap((m) => m.parts), "text"));
|
|
144
|
-
const allParts = messages.flatMap((m) => m.parts);
|
|
145
|
-
push("tool calls", partTokens(allParts, "tool_call"));
|
|
146
|
-
push("tool results", partTokens(allParts, "tool_result"));
|
|
147
|
-
|
|
148
|
-
const estimated = slices.reduce((n, s) => n + s.tokens, 0);
|
|
149
|
-
for (const s of slices) s.share = estimated > 0 ? s.tokens / estimated : 0;
|
|
150
|
-
|
|
151
|
-
const totals: NormalizedUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
152
|
-
let cost = 0;
|
|
153
|
-
let priced = 0;
|
|
154
|
-
let unpricedTurns = 0;
|
|
155
|
-
for (const m of messages) {
|
|
156
|
-
const u = m.usage;
|
|
157
|
-
if (!u) continue;
|
|
158
|
-
const n: NormalizedUsage = { input: u.input, output: u.output, cacheRead: u.cacheRead ?? 0, cacheWrite: u.cacheWrite ?? 0 };
|
|
159
|
-
totals.input += n.input; totals.output += n.output; totals.cacheRead += n.cacheRead; totals.cacheWrite += n.cacheWrite;
|
|
160
|
-
if (n.input === 0 && n.output === 0 && n.cacheRead === 0 && n.cacheWrite === 0) continue;
|
|
161
|
-
const info = lookup(m.origin ?? current);
|
|
162
|
-
// The prompt this turn carried decides the rate on a tiered model: xAI and Google bill a prompt
|
|
163
|
-
// over 200k at the upper rate, and xAI applies it to the whole request. Pricing a tiered turn at
|
|
164
|
-
// the base rate printed roughly HALF the real cost, with no caveat saying so — the TUI's /cost had
|
|
165
|
-
// this right and this report did not, which is the worst arrangement of the two.
|
|
166
|
-
const c = info?.pricing
|
|
167
|
-
? costUsdTiered(n, ratesFor({ ...(m.origin ?? current), pricing: info.pricing, ...(info.tier ? { tier: info.tier } : {}) }, n.input + n.cacheRead + n.cacheWrite))
|
|
168
|
-
: undefined;
|
|
169
|
-
if (c === undefined) unpricedTurns += 1;
|
|
170
|
-
else { cost += c; priced += 1; }
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// The window rows are what a reader acts on, so they are computed from the corrected estimate: on
|
|
174
|
-
// Claude 5 o200k reads up to 1.58x low, and a meter that says 60% of a window already at 95% is worse
|
|
175
|
-
// than no meter. `estimated` stays raw beside it so the correction is visible, never silent.
|
|
176
|
-
const sc = tokenScaleFor(current);
|
|
177
|
-
const corrected = Math.ceil(estimated * sc.scale);
|
|
178
|
-
const report: ContextReport = {
|
|
179
|
-
model: current,
|
|
180
|
-
estimated,
|
|
181
|
-
corrected,
|
|
182
|
-
scale: { factor: sc.scale, measured: sc.measured, note: sc.note },
|
|
183
|
-
slices,
|
|
184
|
-
totals,
|
|
185
|
-
unpricedTurns,
|
|
186
|
-
images: allParts.filter((p) => p.kind === "image").length,
|
|
187
|
-
...(priced > 0 ? { costUsd: cost } : {}),
|
|
188
|
-
};
|
|
189
|
-
if (info?.contextWindow) {
|
|
190
|
-
const health = contextHealth(corrected, info.contextWindow);
|
|
191
|
-
report.window = info.contextWindow;
|
|
192
|
-
report.fraction = health.fraction;
|
|
193
|
-
report.nearLimit = health.nearLimit;
|
|
194
|
-
report.remaining = Math.max(0, info.contextWindow - corrected);
|
|
195
|
-
}
|
|
196
|
-
// the two rows the provider counted and the transcript never stored — see drift()
|
|
197
|
-
const fixedTokens = (slices.find((x) => x.label === "system prompt")?.tokens ?? 0) + (slices.find((x) => x.label === "tool schemas")?.tokens ?? 0);
|
|
198
|
-
const d = drift(messages, fixedTokens);
|
|
199
|
-
if (d) report.drift = d;
|
|
200
|
-
return report;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** Our estimate against the provider's own count, measured at the last turn that reported one.
|
|
204
|
-
* The comparison point matters: a turn's usage describes the prompt BEFORE that turn, so the
|
|
205
|
-
* estimate is taken over everything up to it, exclusive. Returns undefined when no turn reported
|
|
206
|
-
* a prompt (a fresh session, or a provider that sends no usage).
|
|
207
|
-
*
|
|
208
|
-
* `fixedTokens` is the system prompt plus the tool schemas. They must be included or the comparison
|
|
209
|
-
* is not a comparison: neither is ever stored in a transcript — the system message is appended to the
|
|
210
|
-
* wire payload and never to history, and tool schemas are a separate wire field entirely — while the
|
|
211
|
-
* provider's `reported` count is of a request that always carried both. Leaving them out made every
|
|
212
|
-
* session look like it drifted by roughly the size of the fixed prompt, which on a fresh session is
|
|
213
|
-
* most of it, and did so even for OpenAI models where the estimator is exact by construction. That is
|
|
214
|
-
* a false signal on the one line whose whole job is to say whether the meter can be trusted. */
|
|
215
|
-
export function drift(messages: readonly Message[], fixedTokens = 0): ContextDrift | undefined {
|
|
216
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
217
|
-
const u = messages[i]?.usage;
|
|
218
|
-
if (!u) continue;
|
|
219
|
-
const reported = u.input + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0);
|
|
220
|
-
if (reported <= 0) continue;
|
|
221
|
-
const before = messages.slice(0, i);
|
|
222
|
-
const estimated = fixedTokens + (before.length === 0 ? 0 : tokensOf(before.map((m) => partsTokenText(m.parts)).join("\n")));
|
|
223
|
-
const delta = reported - estimated;
|
|
224
|
-
const fraction = reported > 0 ? Math.abs(delta) / reported : 0;
|
|
225
|
-
return { estimated, reported, delta, fraction, beyondTolerance: fraction > DRIFT_TOLERANCE };
|
|
226
|
-
}
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
package/src/core/context.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/** Token accounting + ordered context assembly (ADR-007). */
|
|
2
|
-
|
|
3
|
-
export interface ContextChunk {
|
|
4
|
-
name: string; // "system" | "skills" | "repo-map" | "files" | "history" | "reminder"
|
|
5
|
-
text: string;
|
|
6
|
-
/** drop first under pressure; history is compacted not dropped */
|
|
7
|
-
priority: number; // higher = keep longer
|
|
8
|
-
tokens: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Cheap estimator: ~4 chars/token, clamped. Accurate enough for budgeting;
|
|
12
|
-
* providers report exact usage which we feed back via usage accounting. */
|
|
13
|
-
export function estimateTokens(text: string): number {
|
|
14
|
-
return Math.ceil(text.length / 4);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface AssemblyResult {
|
|
18
|
-
chunks: ContextChunk[]; // kept, in prompt order
|
|
19
|
-
dropped: ContextChunk[]; // non-history chunks evicted to fit budget (lowest priority first)
|
|
20
|
-
totalTokens: number; // tokens of kept chunks
|
|
21
|
-
overBudget: boolean; // true when even kept chunks exceed budget
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Order: system > files > repo-map > skills > history > reminder (aider ChatChunks ordering).
|
|
25
|
-
* History is never dropped here (compaction owns it); non-history chunks are
|
|
26
|
-
* evicted lowest-priority-first until the budget holds — system last-dropped. */
|
|
27
|
-
export function assembleContext(chunks: ContextChunk[], budgetTokens: number): AssemblyResult {
|
|
28
|
-
const ordered = [...chunks].sort((a, b) => b.priority - a.priority);
|
|
29
|
-
const nonHistory = ordered.filter((c) => c.name !== "history");
|
|
30
|
-
const dropped: ContextChunk[] = [];
|
|
31
|
-
const kept = [...nonHistory];
|
|
32
|
-
const hist = ordered.find((c) => c.name === "history");
|
|
33
|
-
const histTokens = hist?.tokens ?? 0;
|
|
34
|
-
while (kept.length > 0 && kept.reduce((n, c) => n + c.tokens, 0) + histTokens > budgetTokens) {
|
|
35
|
-
// evict lowest-priority kept chunk (tail after priority sort = system last)
|
|
36
|
-
const victim = kept.pop()!;
|
|
37
|
-
dropped.push(victim);
|
|
38
|
-
}
|
|
39
|
-
const total = kept.reduce((n, c) => n + c.tokens, 0) + histTokens;
|
|
40
|
-
const chunksOut = [...kept]; // prompt order: high priority first
|
|
41
|
-
if (hist) chunksOut.push(hist);
|
|
42
|
-
return { chunks: chunksOut, dropped, totalTokens: total, overBudget: total > budgetTokens };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Head/tail summarization plan (aider history.py:41): keep recent tail under
|
|
46
|
-
* half budget, summarize older head. Pure — callers do the LLM summarize. */
|
|
47
|
-
export function planCompaction(history: Message4Plan[], budgetTokens: number): { keep: Message4Plan[]; summarize: Message4Plan[] } {
|
|
48
|
-
const total = history.reduce((n, m) => n + m.tokens, 0);
|
|
49
|
-
if (total <= budgetTokens * 0.8) return { keep: history, summarize: [] };
|
|
50
|
-
const half = Math.floor(budgetTokens / 2);
|
|
51
|
-
let acc = 0; let cut = history.length;
|
|
52
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
53
|
-
acc += history[i]!.tokens;
|
|
54
|
-
if (acc > half) { cut = i + 1; break; }
|
|
55
|
-
cut = i;
|
|
56
|
-
}
|
|
57
|
-
return { keep: history.slice(cut), summarize: history.slice(0, cut) };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface Message4Plan { id: string; tokens: number; text: string }
|