ashlr-plugin 1.36.4
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/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +39 -0
- package/.claude-plugin/plugin.json +36 -0
- package/.codex/agents/README.md +9 -0
- package/.codex/agents/ashlr-explorer.md +36 -0
- package/.codex/agents/ashlr-worker.md +38 -0
- package/.codex-plugin/plugin.json +45 -0
- package/.mcp.json +14 -0
- package/LICENSE +21 -0
- package/README.md +311 -0
- package/agents/ashlr-code.md +69 -0
- package/agents/ashlr-explore.md +44 -0
- package/agents/ashlr-plan.md +54 -0
- package/bench/fixtures/notebook-large.ipynb +660 -0
- package/bench/fixtures/notebook-small.ipynb +39 -0
- package/bench/fixtures/tasklist-closed.json +62 -0
- package/bench/fixtures/tasklist-large-mixed.json +27 -0
- package/bench/fixtures/tasklist-sprint-open.json +79 -0
- package/bench/fixtures/websearch-bun-runtime.json +32 -0
- package/bench/fixtures/websearch-react-hooks-patterns.json +32 -0
- package/bench/fixtures/websearch-typescript-generics.json +38 -0
- package/bench/refs/node-sdk/.refrev +1 -0
- package/bench/refs/node-sdk/README.md +17 -0
- package/bench/refs/node-sdk/src/agent/agent.ts +163 -0
- package/bench/refs/node-sdk/src/agent/create-agent-ui-stream.ts +83 -0
- package/bench/refs/node-sdk/src/agent/tool-loop-agent.ts +238 -0
- package/bench/refs/node-sdk/src/embed/embed-many.ts +409 -0
- package/bench/refs/node-sdk/src/embed/embed-result.ts +50 -0
- package/bench/refs/node-sdk/src/embed/embed.ts +257 -0
- package/bench/refs/node-sdk/src/error/invalid-argument-error.ts +34 -0
- package/bench/refs/node-sdk/src/error/invalid-tool-input-error.ts +33 -0
- package/bench/refs/node-sdk/src/error/no-object-generated-error.ts +70 -0
- package/bench/refs/node-sdk/src/generate-image/generate-image-result.ts +42 -0
- package/bench/refs/node-sdk/src/generate-image/generate-image.ts +362 -0
- package/bench/refs/node-sdk/src/generate-object/generate-object-result.ts +67 -0
- package/bench/refs/node-sdk/src/generate-object/generate-object.ts +501 -0
- package/bench/refs/node-sdk/src/generate-object/inject-json-instruction.ts +30 -0
- package/bench/refs/node-sdk/src/generate-object/output-strategy.ts +415 -0
- package/bench/refs/node-sdk/src/generate-object/repair-text.ts +12 -0
- package/bench/refs/node-sdk/src/generate-object/stream-object.ts +960 -0
- package/bench/refs/node-sdk/src/generate-speech/generate-speech.ts +191 -0
- package/bench/refs/node-sdk/src/generate-text/generate-text.ts +1484 -0
- package/bench/refs/node-sdk/src/generate-text/stream-text.ts +2708 -0
- package/bench/refs/node-sdk/src/generate-text/tool-call.ts +59 -0
- package/bench/refs/node-sdk/src/generate-text/tool-result.ts +36 -0
- package/bench/refs/node-sdk/src/telemetry/create-telemetry-dispatcher.ts +146 -0
- package/bench/refs/node-sdk/src/telemetry/telemetry-registry.ts +15 -0
- package/bench/refs/node-sdk/src/telemetry/telemetry.ts +223 -0
- package/bench/refs/node-sdk/src/util/async-iterable-stream.ts +94 -0
- package/bench/refs/node-sdk/src/util/consume-stream.ts +31 -0
- package/bench/refs/node-sdk/src/util/create-stitchable-stream.ts +112 -0
- package/bench/refs/node-sdk/src/util/download/download.ts +74 -0
- package/bench/refs/node-sdk/src/util/error-handler.ts +1 -0
- package/bench/refs/node-sdk/src/util/fix-json.ts +401 -0
- package/bench/refs/node-sdk/src/util/merge-objects.ts +79 -0
- package/bench/refs/python-lib/.refrev +1 -0
- package/bench/refs/python-lib/README.md +18 -0
- package/bench/refs/python-lib/pandas/core/_numba/executor.py +249 -0
- package/bench/refs/python-lib/pandas/core/_numba/extensions.py +586 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/mean_.py +198 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/min_max_.py +178 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/sum_.py +255 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/var_.py +251 -0
- package/bench/refs/python-lib/pandas/core/accessor.py +520 -0
- package/bench/refs/python-lib/pandas/core/api.py +138 -0
- package/bench/refs/python-lib/pandas/core/array_algos/masked_accumulations.py +97 -0
- package/bench/refs/python-lib/pandas/core/array_algos/masked_reductions.py +252 -0
- package/bench/refs/python-lib/pandas/core/array_algos/putmask.py +150 -0
- package/bench/refs/python-lib/pandas/core/array_algos/quantile.py +232 -0
- package/bench/refs/python-lib/pandas/core/array_algos/replace.py +159 -0
- package/bench/refs/python-lib/pandas/core/array_algos/take.py +609 -0
- package/bench/refs/python-lib/pandas/core/arraylike.py +534 -0
- package/bench/refs/python-lib/pandas/core/arrays/_arrow_string_mixins.py +434 -0
- package/bench/refs/python-lib/pandas/core/arrays/_mixins.py +630 -0
- package/bench/refs/python-lib/pandas/core/arrays/_ranges.py +276 -0
- package/bench/refs/python-lib/pandas/core/arrays/_utils.py +77 -0
- package/bench/refs/python-lib/pandas/core/arrays/arrow/accessors.py +521 -0
- package/bench/refs/python-lib/pandas/core/arrays/arrow/extension_types.py +174 -0
- package/bench/refs/python-lib/pandas/core/arrays/boolean.py +454 -0
- package/bench/refs/python-lib/pandas/core/arrays/floating.py +192 -0
- package/bench/refs/python-lib/pandas/core/arrays/integer.py +296 -0
- package/bench/refs/python-lib/pandas/core/arrays/masked.py +2140 -0
- package/bench/refs/python-lib/pandas/core/arrays/numeric.py +305 -0
- package/bench/refs/python-lib/pandas/core/arrays/numpy_.py +701 -0
- package/bench/refs/python-lib/pandas/core/arrays/period.py +1586 -0
- package/bench/refs/python-lib/pandas/core/arrays/sparse/accessor.py +505 -0
- package/bench/refs/python-lib/pandas/core/arrays/sparse/scipy_sparse.py +208 -0
- package/bench/refs/rust-project/.refrev +1 -0
- package/bench/refs/rust-project/README.md +17 -0
- package/bench/refs/rust-project/tokio/src/doc/os.rs +68 -0
- package/bench/refs/rust-project/tokio/src/fs/dir_builder.rs +129 -0
- package/bench/refs/rust-project/tokio/src/fs/file.rs +1113 -0
- package/bench/refs/rust-project/tokio/src/fs/mocks.rs +176 -0
- package/bench/refs/rust-project/tokio/src/fs/mod.rs +324 -0
- package/bench/refs/rust-project/tokio/src/fs/open_options.rs +856 -0
- package/bench/refs/rust-project/tokio/src/fs/read.rs +80 -0
- package/bench/refs/rust-project/tokio/src/fs/read_dir.rs +357 -0
- package/bench/refs/rust-project/tokio/src/fs/write.rs +99 -0
- package/bench/refs/rust-project/tokio/src/future/maybe_done.rs +123 -0
- package/bench/refs/rust-project/tokio/src/future/try_join.rs +82 -0
- package/bench/refs/rust-project/tokio/src/io/async_buf_read.rs +117 -0
- package/bench/refs/rust-project/tokio/src/io/async_read.rs +133 -0
- package/bench/refs/rust-project/tokio/src/io/async_seek.rs +96 -0
- package/bench/refs/rust-project/tokio/src/io/async_write.rs +402 -0
- package/bench/refs/rust-project/tokio/src/io/blocking.rs +339 -0
- package/bench/refs/rust-project/tokio/src/io/interest.rs +345 -0
- package/bench/refs/rust-project/tokio/src/io/join.rs +130 -0
- package/bench/refs/rust-project/tokio/src/io/mod.rs +308 -0
- package/bench/refs/rust-project/tokio/src/io/poll_evented.rs +319 -0
- package/bench/refs/rust-project/tokio/src/io/read_buf.rs +344 -0
- package/bench/refs/rust-project/tokio/src/io/ready.rs +322 -0
- package/bench/refs/rust-project/tokio/src/io/split.rs +166 -0
- package/bench/refs/rust-project/tokio/src/io/stderr.rs +148 -0
- package/bench/refs/rust-project/tokio/src/io/stdin.rs +98 -0
- package/bench/refs/rust-project/tokio/src/io/stdio_common.rs +222 -0
- package/bench/refs/rust-project/tokio/src/io/stdout.rs +197 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_reader.rs +311 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_stream.rs +207 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_writer.rs +310 -0
- package/bench/refs/rust-project/tokio/src/io/util/chain.rs +144 -0
- package/commands/_spawn-patterns.json +32 -0
- package/commands/ashlr-allow.md +21 -0
- package/commands/ashlr-benchmark.md +53 -0
- package/commands/ashlr-brief.md +97 -0
- package/commands/ashlr-budget.md +76 -0
- package/commands/ashlr-compact.md +54 -0
- package/commands/ashlr-dashboard.md +37 -0
- package/commands/ashlr-doctor.md +56 -0
- package/commands/ashlr-eco-mode.md +92 -0
- package/commands/ashlr-genome-init.md +24 -0
- package/commands/ashlr-genome-keygen.md +36 -0
- package/commands/ashlr-genome-loop.md +118 -0
- package/commands/ashlr-genome-push.md +36 -0
- package/commands/ashlr-genome-rewrap.md +48 -0
- package/commands/ashlr-genome-team-init.md +67 -0
- package/commands/ashlr-handoff.md +23 -0
- package/commands/ashlr-help.md +86 -0
- package/commands/ashlr-hook-timings.md +30 -0
- package/commands/ashlr-leaderboard.md +50 -0
- package/commands/ashlr-ollama-setup.md +39 -0
- package/commands/ashlr-orchestrate-status.md +72 -0
- package/commands/ashlr-orchestrate.md +83 -0
- package/commands/ashlr-parallelize.md +63 -0
- package/commands/ashlr-report-crash.md +33 -0
- package/commands/ashlr-resume.md +72 -0
- package/commands/ashlr-savings.md +24 -0
- package/commands/ashlr-settings.md +50 -0
- package/commands/ashlr-spawn.md +61 -0
- package/commands/ashlr-start.md +60 -0
- package/commands/ashlr-status.md +64 -0
- package/commands/ashlr-team-invite.md +47 -0
- package/commands/ashlr-tier.md +122 -0
- package/commands/ashlr-tour.md +60 -0
- package/commands/ashlr-update.md +155 -0
- package/commands/ashlr-upgrade.md +96 -0
- package/commands/goal.md +58 -0
- package/commands/loop.md +40 -0
- package/docs/CNAME +1 -0
- package/docs/PRO_TIER.md +104 -0
- package/docs/README.md +21 -0
- package/docs/architecture.md +415 -0
- package/docs/ashlr-pipe.md +161 -0
- package/docs/assets/logo.svg +19 -0
- package/docs/assets/og.png +0 -0
- package/docs/assets/og.svg +41 -0
- package/docs/benchmarks-v2.json +355 -0
- package/docs/benchmarks.json +102 -0
- package/docs/benchmarks.md +211 -0
- package/docs/billing.md +185 -0
- package/docs/cloud-genome.md +155 -0
- package/docs/competitive-wozcode.md +92 -0
- package/docs/deploy.md +325 -0
- package/docs/email-templates.md +113 -0
- package/docs/embed-tuning.md +103 -0
- package/docs/genome.md +120 -0
- package/docs/github-oauth-onboarding.md +127 -0
- package/docs/hero-video-script.md +129 -0
- package/docs/index.html +2262 -0
- package/docs/install-prompt.md +78 -0
- package/docs/install-windows.md +132 -0
- package/docs/install.ps1 +171 -0
- package/docs/install.sh +146 -0
- package/docs/launch/README.md +114 -0
- package/docs/launch/hn.md +49 -0
- package/docs/launch/reddit.md +45 -0
- package/docs/launch/x.md +65 -0
- package/docs/launch-post.md +112 -0
- package/docs/legal.md +78 -0
- package/docs/llm-providers.md +99 -0
- package/docs/multi-host-mcp.md +217 -0
- package/docs/multi-turn-architecture.md +108 -0
- package/docs/operations/deployment.md +108 -0
- package/docs/operations/error-monitoring.md +70 -0
- package/docs/operations/weekly-digest.md +70 -0
- package/docs/operations.md +494 -0
- package/docs/platform-support.md +133 -0
- package/docs/policy-packs.md +188 -0
- package/docs/press-kit/fact-sheet.md +78 -0
- package/docs/press-kit/founder-bio.md +3 -0
- package/docs/press-kit/logo.svg +33 -0
- package/docs/press-kit/screenshots.md +146 -0
- package/docs/pricing.md +251 -0
- package/docs/pro-backend-architecture.md +523 -0
- package/docs/producthunt.md +52 -0
- package/docs/router-migration.md +110 -0
- package/docs/session-log-schema.md +68 -0
- package/docs/social.md +95 -0
- package/docs/team-genome.md +528 -0
- package/docs/telemetry.md +194 -0
- package/docs/test-isolation.md +100 -0
- package/docs/upgrade.md +159 -0
- package/hooks/_hook-errors.ts +87 -0
- package/hooks/_hook-perf-emit.ts +113 -0
- package/hooks/_hook-timing-ledger.ts +1133 -0
- package/hooks/_nudge-throttle.ts +127 -0
- package/hooks/_recent-blocks.ts +127 -0
- package/hooks/audit-upload.ts +86 -0
- package/hooks/codex-hooks.json +171 -0
- package/hooks/commit-attribution.ts +179 -0
- package/hooks/edit-batching-nudge.ts +154 -0
- package/hooks/genome-scribe-hook.ts +219 -0
- package/hooks/hooks.json +294 -0
- package/hooks/orient-nudge-hook.ts +242 -0
- package/hooks/policy-enforce.ts +214 -0
- package/hooks/post-tool-use-embedding.ts +93 -0
- package/hooks/post-tool-use-genome.sh +37 -0
- package/hooks/post-tool-use-genome.ts +69 -0
- package/hooks/posttooluse-correlate.ts +122 -0
- package/hooks/posttooluse-dedupe-output.ts +241 -0
- package/hooks/posttooluse-genome-refresh.ts +223 -0
- package/hooks/posttooluse-native-nudge.ts +205 -0
- package/hooks/posttooluse-stale-result.ts +277 -0
- package/hooks/pretooluse-bash.ts +63 -0
- package/hooks/pretooluse-budget-guard.ts +318 -0
- package/hooks/pretooluse-common.ts +985 -0
- package/hooks/pretooluse-eco-router.ts +196 -0
- package/hooks/pretooluse-edit.sh +93 -0
- package/hooks/pretooluse-edit.ts +197 -0
- package/hooks/pretooluse-glob.ts +70 -0
- package/hooks/pretooluse-grep.sh +85 -0
- package/hooks/pretooluse-grep.ts +96 -0
- package/hooks/pretooluse-notebookedit.ts +121 -0
- package/hooks/pretooluse-read.sh +118 -0
- package/hooks/pretooluse-read.ts +118 -0
- package/hooks/pretooluse-task.ts +130 -0
- package/hooks/pretooluse-webfetch.ts +66 -0
- package/hooks/pretooluse-websearch.ts +85 -0
- package/hooks/pretooluse-write.ts +128 -0
- package/hooks/pulse-emit.ts +197 -0
- package/hooks/session-end-consolidate.sh +35 -0
- package/hooks/session-end-consolidate.ts +188 -0
- package/hooks/session-end-stats-gc.ts +80 -0
- package/hooks/session-log-append.sh +94 -0
- package/hooks/session-log-append.ts +116 -0
- package/hooks/session-start.ts +993 -0
- package/hooks/sessionend-hook-health-nudge.ts +451 -0
- package/hooks/sessionend-savings-nudge.ts +318 -0
- package/hooks/sessionend-status-line-nudge.ts +296 -0
- package/hooks/sessionstart-brief.ts +140 -0
- package/hooks/sessionstart-cost-refactor.ts +95 -0
- package/hooks/sessionstart-efficient.ts +110 -0
- package/hooks/sessionstart-genome-author.ts +93 -0
- package/hooks/sessionstart-lean-tools.ts +98 -0
- package/hooks/sessionstart-search.ts +97 -0
- package/hooks/stop-accounting.ts +152 -0
- package/hooks/subagent-stop-rollup.ts +162 -0
- package/hooks/userpromptsubmit-brief-trigger.ts +156 -0
- package/package.json +56 -0
- package/ports/README.md +108 -0
- package/ports/cursor/mcp.json +11 -0
- package/ports/goose/recipe.yaml +26 -0
- package/scripts/ashlr-mcp.ts +40 -0
- package/scripts/auto-update.ts +177 -0
- package/scripts/baseline-scan.ts +831 -0
- package/scripts/bench-orchestrate.ts +377 -0
- package/scripts/benchmark-refs.ts +292 -0
- package/scripts/bootstrap.mjs +100 -0
- package/scripts/brief-eval.ts +234 -0
- package/scripts/bump-version.ts +82 -0
- package/scripts/bun-resolve.d.mts +11 -0
- package/scripts/bun-resolve.mjs +59 -0
- package/scripts/calibrate-grep.ts +467 -0
- package/scripts/capture-grep-workload.ts +260 -0
- package/scripts/check-tier.ts +61 -0
- package/scripts/check-version-sync.ts +97 -0
- package/scripts/cli-orchestrate-status.ts +386 -0
- package/scripts/cli-orchestrate.ts +303 -0
- package/scripts/cli.ts +478 -0
- package/scripts/cloud-smoke-test.ts +313 -0
- package/scripts/coach-report.ts +373 -0
- package/scripts/context-db-migrate.ts +137 -0
- package/scripts/context-status.ts +43 -0
- package/scripts/demo-run.ts +255 -0
- package/scripts/deploy-smoke.sh +140 -0
- package/scripts/doctor.ts +992 -0
- package/scripts/embed-file-worker.ts +104 -0
- package/scripts/embed-tune.ts +308 -0
- package/scripts/errors-report.ts +428 -0
- package/scripts/find-test-leak.ts +69 -0
- package/scripts/generate-badge.ts +357 -0
- package/scripts/genome-auto-consolidate.ts +533 -0
- package/scripts/genome-auto-propose.ts +578 -0
- package/scripts/genome-cloud-pull.ts +363 -0
- package/scripts/genome-cloud-push.ts +382 -0
- package/scripts/genome-cloud-sync.ts +587 -0
- package/scripts/genome-commit-watcher.ts +295 -0
- package/scripts/genome-init.ts +986 -0
- package/scripts/genome-key.ts +233 -0
- package/scripts/genome-keygen.ts +168 -0
- package/scripts/genome-link.ts +59 -0
- package/scripts/genome-refresh-worker.ts +364 -0
- package/scripts/genome-rewrap.ts +90 -0
- package/scripts/genome-synthesizer.ts +679 -0
- package/scripts/genome-team-init.ts +312 -0
- package/scripts/handoff-pack.ts +438 -0
- package/scripts/hook-bootstrap.mjs +75 -0
- package/scripts/hook-timing-reader.ts +371 -0
- package/scripts/hook-timings-report.ts +554 -0
- package/scripts/install-genome-hooks.ts +196 -0
- package/scripts/install-onnx-model.ts +154 -0
- package/scripts/install-permissions.ts +276 -0
- package/scripts/install-status-line.ts +150 -0
- package/scripts/mcp-entrypoint.sh +78 -0
- package/scripts/mcp-entrypoint.ts +130 -0
- package/scripts/mdx-angle-bracket-lint.ts +152 -0
- package/scripts/measure-cold-start.ts +126 -0
- package/scripts/migrate-stats-to-sqlite.ts +86 -0
- package/scripts/ollama-setup.ts +498 -0
- package/scripts/onboarding-wizard.ts +1691 -0
- package/scripts/orchestrate-emit-telemetry.ts +177 -0
- package/scripts/orchestrate-expand.ts +517 -0
- package/scripts/orchestrate-run.ts +744 -0
- package/scripts/print-legend.ts +50 -0
- package/scripts/product-metadata.ts +38 -0
- package/scripts/prune-old-memories.ts +208 -0
- package/scripts/publish.sh +88 -0
- package/scripts/read-calibration.ts +122 -0
- package/scripts/report-crash.ts +226 -0
- package/scripts/run-benchmark.ts +750 -0
- package/scripts/run-root-tests.ts +28 -0
- package/scripts/savings-dashboard.ts +1258 -0
- package/scripts/savings-report-extras.ts +447 -0
- package/scripts/savings-status-line.ts +873 -0
- package/scripts/session-event-emit.ts +266 -0
- package/scripts/session-greet.ts +686 -0
- package/scripts/session-log-report.ts +551 -0
- package/scripts/session-resume.ts +614 -0
- package/scripts/set-hook-mode.ts +83 -0
- package/scripts/smoke-cross-platform.ts +117 -0
- package/scripts/smoke-realtime.ts +257 -0
- package/scripts/smoke-tools.ts +21 -0
- package/scripts/stats-cloud-pull.ts +213 -0
- package/scripts/stats-cloud-sync.ts +261 -0
- package/scripts/telemetry-flush.ts +160 -0
- package/scripts/telemetry-status.ts +266 -0
- package/scripts/timing-budget.ts +35 -0
- package/scripts/ui-animation.ts +120 -0
- package/scripts/ui.ts +431 -0
- package/scripts/upgrade-flow.ts +775 -0
- package/scripts/weekly-digest.ts +394 -0
- package/servers/_accounting.ts +78 -0
- package/servers/_ask-router.ts +84 -0
- package/servers/_ast-chunker.ts +434 -0
- package/servers/_ast-helpers.ts +183 -0
- package/servers/_ast-languages.ts +153 -0
- package/servers/_ast-refactor/_shared.ts +221 -0
- package/servers/_ast-refactor/cross-file-rename.ts +560 -0
- package/servers/_ast-refactor/extract-function.ts +357 -0
- package/servers/_ast-refactor/file-local-rename.ts +172 -0
- package/servers/_ast-refactor.ts +41 -0
- package/servers/_ast-skeleton.ts +107 -0
- package/servers/_bash-summarizers-registry.ts +1096 -0
- package/servers/_crash-dump.ts +210 -0
- package/servers/_cwd-clamp.ts +342 -0
- package/servers/_edit-log.ts +28 -0
- package/servers/_edit-match.ts +236 -0
- package/servers/_edit-validate.ts +196 -0
- package/servers/_embed-calibration.ts +123 -0
- package/servers/_embedding-cache.ts +401 -0
- package/servers/_embedding-model.ts +329 -0
- package/servers/_events.ts +113 -0
- package/servers/_genome-cache.ts +154 -0
- package/servers/_genome-commits.ts +172 -0
- package/servers/_genome-crypto-v2.ts +242 -0
- package/servers/_genome-crypto.ts +259 -0
- package/servers/_genome-discoveries.ts +140 -0
- package/servers/_genome-embed-populator.ts +216 -0
- package/servers/_genome-freshness.ts +134 -0
- package/servers/_genome-issues.ts +171 -0
- package/servers/_genome-live.ts +263 -0
- package/servers/_genome-prs.ts +188 -0
- package/servers/_genome-search.ts +479 -0
- package/servers/_genome-sync.ts +297 -0
- package/servers/_history-tracker.ts +279 -0
- package/servers/_http-helpers.ts +151 -0
- package/servers/_identity-hash.ts +151 -0
- package/servers/_llm-providers/anthropic.ts +132 -0
- package/servers/_llm-providers/cloud.ts +195 -0
- package/servers/_llm-providers/index.ts +350 -0
- package/servers/_llm-providers/local.ts +127 -0
- package/servers/_llm-providers/onnx.ts +393 -0
- package/servers/_llm-providers/types.ts +27 -0
- package/servers/_manifest-v2.ts +447 -0
- package/servers/_mcp-host.ts +93 -0
- package/servers/_nudge-events.ts +451 -0
- package/servers/_orchestrate-executor.ts +316 -0
- package/servers/_prefetch.ts +435 -0
- package/servers/_pricing.ts +256 -0
- package/servers/_pro.ts +381 -0
- package/servers/_read-cache.ts +44 -0
- package/servers/_router-handlers.ts +46 -0
- package/servers/_router.ts +55 -0
- package/servers/_run-with-timeout.ts +173 -0
- package/servers/_savings-render.ts +214 -0
- package/servers/_stats-sqlite.ts +640 -0
- package/servers/_stats.ts +1183 -0
- package/servers/_status-line-cells.ts +469 -0
- package/servers/_streaks.ts +162 -0
- package/servers/_summarize.ts +208 -0
- package/servers/_task-graph-render.ts +217 -0
- package/servers/_task-graph.ts +545 -0
- package/servers/_telemetry.ts +719 -0
- package/servers/_test-parsers.ts +530 -0
- package/servers/_test-watch.ts +626 -0
- package/servers/_text-helpers.ts +37 -0
- package/servers/_token-measure.ts +160 -0
- package/servers/_tool-base.ts +248 -0
- package/servers/_weekly-digest.ts +391 -0
- package/servers/ask-server-handlers.ts +49 -0
- package/servers/ask-server.ts +174 -0
- package/servers/bash-server-handlers.ts +138 -0
- package/servers/bash-server.ts +1136 -0
- package/servers/bench.ts +186 -0
- package/servers/diff-semantic-server-handlers.ts +39 -0
- package/servers/diff-semantic-server.ts +566 -0
- package/servers/diff-server-handlers.ts +317 -0
- package/servers/diff-server.ts +20 -0
- package/servers/edit-server-handlers.ts +31 -0
- package/servers/edit-server.ts +209 -0
- package/servers/edit-structural-server-handlers.ts +322 -0
- package/servers/efficiency-server-handlers.ts +161 -0
- package/servers/efficiency-server.ts +1135 -0
- package/servers/flush-server-handlers.ts +21 -0
- package/servers/flush-server.ts +19 -0
- package/servers/genome-server-handlers.ts +118 -0
- package/servers/genome-server.ts +397 -0
- package/servers/github-server-handlers.ts +187 -0
- package/servers/github-server.ts +856 -0
- package/servers/glob-server-handlers.ts +233 -0
- package/servers/glob-server.ts +20 -0
- package/servers/grep-server-handlers.ts +61 -0
- package/servers/grep-server.ts +564 -0
- package/servers/http-server-handlers.ts +36 -0
- package/servers/http-server.ts +149 -0
- package/servers/logs-server-handlers.ts +42 -0
- package/servers/logs-server.ts +359 -0
- package/servers/ls-server-handlers.ts +166 -0
- package/servers/ls-server.ts +20 -0
- package/servers/multi-edit-server-handlers.ts +51 -0
- package/servers/multi-edit-server.ts +422 -0
- package/servers/notebook-edit-server-handlers.ts +34 -0
- package/servers/notebook-edit-server.ts +181 -0
- package/servers/orient-server-handlers.ts +52 -0
- package/servers/orient-server.ts +436 -0
- package/servers/pipe-server-handlers.ts +68 -0
- package/servers/pipe-server.ts +300 -0
- package/servers/read-server-handlers.ts +42 -0
- package/servers/read-server.ts +206 -0
- package/servers/rename-file-server-handlers.ts +67 -0
- package/servers/rename-file-server.ts +699 -0
- package/servers/savings-server-handlers.ts +27 -0
- package/servers/savings-server.ts +54 -0
- package/servers/search-replace-regex-server-handlers.ts +96 -0
- package/servers/search-replace-regex-server.ts +751 -0
- package/servers/sql-server-handlers.ts +58 -0
- package/servers/sql-server.ts +505 -0
- package/servers/task-server-handlers.ts +358 -0
- package/servers/task-server.ts +24 -0
- package/servers/test-server-handlers.ts +351 -0
- package/servers/test-server.ts +20 -0
- package/servers/tree-server-handlers.ts +503 -0
- package/servers/tree-server.ts +20 -0
- package/servers/webfetch-server-handlers.ts +166 -0
- package/servers/webfetch-server.ts +20 -0
- package/servers/websearch-server-handlers.ts +215 -0
- package/servers/websearch-server.ts +19 -0
- package/servers/write-server-handlers.ts +27 -0
- package/servers/write-server.ts +97 -0
- package/skills/ashlr-brief/SKILL.md +80 -0
- package/skills/ashlr-codex-doctor/SKILL.md +21 -0
- package/skills/ashlr-codex-efficient-tools/SKILL.md +17 -0
- package/skills/ashlr-codex-genome-refresh/SKILL.md +20 -0
- package/skills/ashlr-codex-parallel-explore/SKILL.md +20 -0
- package/skills/ashlr-codex-savings/SKILL.md +17 -0
- package/skills/ashlr-cost-refactor/SKILL.md +50 -0
- package/skills/ashlr-efficient/SKILL.md +64 -0
- package/skills/ashlr-genome-author/SKILL.md +43 -0
- package/skills/ashlr-lean-tools/SKILL.md +43 -0
- package/skills/ashlr-search/SKILL.md +46 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM provider dispatcher and public summarizeIfLarge facade.
|
|
3
|
+
*
|
|
4
|
+
* Provider hierarchy (ASHLR_LLM_PROVIDER=auto, which is the default):
|
|
5
|
+
* anthropic (ANTHROPIC_API_KEY or ~/.claude/.credentials.json)
|
|
6
|
+
* → cloud (~/.ashlr/pro-token, calls hosted ashlr Haiku proxy)
|
|
7
|
+
* → onnx (onnxruntime-node + bundled model — currently stubbed)
|
|
8
|
+
* → local (LM Studio / Ollama at ASHLR_LLM_URL)
|
|
9
|
+
* → none (falls back to snipCompact truncation)
|
|
10
|
+
*
|
|
11
|
+
* Anthropic-direct keeps priority over cloud because it's cheaper for users
|
|
12
|
+
* who already have their own key. Cloud serves Pro-but-no-key users.
|
|
13
|
+
*
|
|
14
|
+
* Override with:
|
|
15
|
+
* ASHLR_LLM_PROVIDER=anthropic — use Anthropic only (no fallback)
|
|
16
|
+
* ASHLR_LLM_PROVIDER=cloud — use cloud proxy only
|
|
17
|
+
* ASHLR_LLM_PROVIDER=onnx — use ONNX only
|
|
18
|
+
* ASHLR_LLM_PROVIDER=local — use local LLM only (preserves old behavior)
|
|
19
|
+
* ASHLR_LLM_PROVIDER=off — disable LLM summarization entirely
|
|
20
|
+
*
|
|
21
|
+
* Cost telemetry: every summarize() call records inTokens/outTokens/cost
|
|
22
|
+
* via logEvent("llm_summarize_provider_used") and recordSaving for
|
|
23
|
+
* LLM cost accounting.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { createHash } from "crypto";
|
|
27
|
+
import { existsSync } from "fs";
|
|
28
|
+
import { mkdir, readFile, stat, writeFile } from "fs/promises";
|
|
29
|
+
import { homedir } from "os";
|
|
30
|
+
import { dirname, join } from "path";
|
|
31
|
+
|
|
32
|
+
import { logEvent } from "../_events.ts";
|
|
33
|
+
import { bumpSummarization } from "../_stats.ts";
|
|
34
|
+
import { costForLLM } from "../_pricing.ts";
|
|
35
|
+
import { anthropicProvider, _resetAnthropicAvailabilityCache } from "./anthropic.ts";
|
|
36
|
+
import { onnxProvider } from "./onnx.ts";
|
|
37
|
+
import { localProvider, _resetLocalAvailabilityCache } from "./local.ts";
|
|
38
|
+
import { cloudProvider, _resetCloudAvailabilityCache } from "./cloud.ts";
|
|
39
|
+
import type { LlmProvider, ProviderName } from "./types.ts";
|
|
40
|
+
|
|
41
|
+
export type { LlmProvider, ProviderName };
|
|
42
|
+
export { _resetAnthropicAvailabilityCache, _resetLocalAvailabilityCache, _resetCloudAvailabilityCache };
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// None provider — signals "no LLM available"; caller falls back to snipCompact
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
const noneProvider: LlmProvider = {
|
|
49
|
+
name: "none",
|
|
50
|
+
async isAvailable() { return false; },
|
|
51
|
+
async summarize() {
|
|
52
|
+
throw new Error("No LLM provider available; caller should fall back to snipCompact");
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Provider selection
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Select the best available provider given the ASHLR_LLM_PROVIDER env var.
|
|
62
|
+
* Returns noneProvider when nothing is available — never throws.
|
|
63
|
+
*/
|
|
64
|
+
export async function selectProvider(
|
|
65
|
+
override?: string,
|
|
66
|
+
): Promise<LlmProvider> {
|
|
67
|
+
const setting = (override ?? process.env.ASHLR_LLM_PROVIDER ?? "auto")
|
|
68
|
+
.toLowerCase()
|
|
69
|
+
.trim() as ProviderName;
|
|
70
|
+
|
|
71
|
+
if (setting === "off") return noneProvider;
|
|
72
|
+
|
|
73
|
+
if (setting === "anthropic") return anthropicProvider;
|
|
74
|
+
if (setting === "cloud") return cloudProvider;
|
|
75
|
+
if (setting === "onnx") return onnxProvider;
|
|
76
|
+
if (setting === "local") return localProvider;
|
|
77
|
+
|
|
78
|
+
// "auto" — try each in order, return first available.
|
|
79
|
+
// EXCEPTION: if the user explicitly set ASHLR_LLM_URL, respect that as a
|
|
80
|
+
// strong "use local" signal and prefer local over Anthropic. Without that
|
|
81
|
+
// env var, Anthropic-direct is preferred (best quality, cheaper for user).
|
|
82
|
+
// Cloud fills the gap for Pro users who have a pro-token but no Anthropic key.
|
|
83
|
+
if (process.env.ASHLR_LLM_URL) {
|
|
84
|
+
if (await localProvider.isAvailable()) return localProvider;
|
|
85
|
+
}
|
|
86
|
+
if (await anthropicProvider.isAvailable()) return anthropicProvider;
|
|
87
|
+
if (await cloudProvider.isAvailable()) return cloudProvider;
|
|
88
|
+
if (await onnxProvider.isAvailable()) return onnxProvider;
|
|
89
|
+
if (await localProvider.isAvailable()) return localProvider;
|
|
90
|
+
|
|
91
|
+
return noneProvider;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Cache (shared with _summarize.ts facade)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
99
|
+
const PROMPT_VERSION = 1;
|
|
100
|
+
|
|
101
|
+
// One-shot guard so `[ashlr] WARN: local LLM unreachable` only prints once
|
|
102
|
+
// per process, not on every summarize call when the misconfiguration persists.
|
|
103
|
+
let _localUnreachableWarned = false;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Footer appended to the snipCompact fallback when no LLM provider is
|
|
107
|
+
* available. Names the concrete env vars / setup commands so users have a
|
|
108
|
+
* one-line path to enabling the smart-summary route — the previous "LLM
|
|
109
|
+
* unreachable" text was true but unactionable.
|
|
110
|
+
*/
|
|
111
|
+
const LLM_FALLBACK_FOOTER =
|
|
112
|
+
"[ashlr · no LLM provider · using snipCompact fallback]\n" +
|
|
113
|
+
" enable smart summaries by configuring one of:\n" +
|
|
114
|
+
" ANTHROPIC_API_KEY=… (cloud, best quality)\n" +
|
|
115
|
+
" /ashlr-ollama-setup (local, free, no key)\n" +
|
|
116
|
+
" ASHLR_LLM_URL=http://…/v1 (any OpenAI-compatible endpoint)";
|
|
117
|
+
|
|
118
|
+
function home(): string { return process.env.HOME ?? homedir(); }
|
|
119
|
+
function cacheDir(): string { return join(home(), ".ashlr", "summary-cache"); }
|
|
120
|
+
|
|
121
|
+
function sha256(s: string): string {
|
|
122
|
+
return createHash("sha256").update(s).digest("hex");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function readCache(path: string): Promise<string | null> {
|
|
126
|
+
if (!existsSync(path)) return null;
|
|
127
|
+
try {
|
|
128
|
+
const s = await stat(path);
|
|
129
|
+
if (Date.now() - s.mtimeMs > CACHE_TTL_MS) return null;
|
|
130
|
+
return await readFile(path, "utf-8");
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function writeCache(path: string, content: string): Promise<void> {
|
|
137
|
+
await mkdir(dirname(path), { recursive: true });
|
|
138
|
+
await writeFile(path, content, "utf-8");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Fallback truncation (preserved from original _summarize.ts)
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
function snipFallback(raw: string): string {
|
|
146
|
+
if (raw.length <= 2000) return raw;
|
|
147
|
+
return raw.slice(0, 800) + "\n\n[... " + (raw.length - 1600) + " bytes elided ...]\n\n" + raw.slice(-800);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function bypassHint(rawBytes: number, summaryBytes: number): string {
|
|
151
|
+
const ratio = rawBytes > 0 ? (rawBytes / summaryBytes).toFixed(1) : "?";
|
|
152
|
+
return `[ashlr summary · ${rawBytes.toLocaleString()} → ${summaryBytes.toLocaleString()} bytes · ${ratio}× reduction · pass bypassSummary:true to see full output]`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// SummarizeOpts + SummarizeResult (mirrored from _summarize.ts public API)
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
export interface SummarizeOpts {
|
|
160
|
+
toolName: string;
|
|
161
|
+
systemPrompt: string;
|
|
162
|
+
thresholdBytes?: number;
|
|
163
|
+
bypass?: boolean;
|
|
164
|
+
timeoutMs?: number;
|
|
165
|
+
/** Legacy: override the LLM endpoint URL (OpenAI-compat). Passed through to
|
|
166
|
+
* local provider only for backward compatibility with existing tests. */
|
|
167
|
+
endpointOverride?: string;
|
|
168
|
+
/** Override the provider name (for testing). Bypasses env var. */
|
|
169
|
+
providerOverride?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface SummarizeResult {
|
|
173
|
+
text: string;
|
|
174
|
+
summarized: boolean;
|
|
175
|
+
wasCached: boolean;
|
|
176
|
+
fellBack: boolean;
|
|
177
|
+
outputBytes: number;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Public entry point
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
const DEFAULT_THRESHOLD_BYTES = 16_384;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Summarize `rawText` if it exceeds the threshold. Falls back to snipCompact
|
|
188
|
+
* when no LLM provider is available or when the provider throws.
|
|
189
|
+
*
|
|
190
|
+
* This is the implementation that `servers/_summarize.ts` re-exports, keeping
|
|
191
|
+
* the public API stable for all callers.
|
|
192
|
+
*/
|
|
193
|
+
export async function summarizeIfLarge(
|
|
194
|
+
rawText: string,
|
|
195
|
+
opts: SummarizeOpts,
|
|
196
|
+
): Promise<SummarizeResult> {
|
|
197
|
+
const threshold = opts.thresholdBytes ?? DEFAULT_THRESHOLD_BYTES;
|
|
198
|
+
const rawBytes = Buffer.byteLength(rawText, "utf-8");
|
|
199
|
+
|
|
200
|
+
if (rawBytes <= threshold) {
|
|
201
|
+
await logEvent("tool_noop", { tool: opts.toolName, reason: "below-threshold" });
|
|
202
|
+
return { text: rawText, summarized: false, wasCached: false, fellBack: false, outputBytes: rawBytes };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (opts.bypass) {
|
|
206
|
+
await logEvent("tool_noop", { tool: opts.toolName, reason: "bypassed" });
|
|
207
|
+
return {
|
|
208
|
+
text: rawText + "\n\n[ashlr · summarization bypassed (bypassSummary:true)]",
|
|
209
|
+
summarized: false,
|
|
210
|
+
wasCached: false,
|
|
211
|
+
fellBack: false,
|
|
212
|
+
outputBytes: rawBytes,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Cache check
|
|
217
|
+
const cacheKey = sha256(opts.toolName + "::" + PROMPT_VERSION + "::" + rawText);
|
|
218
|
+
const cachePath = join(cacheDir(), `${cacheKey}.txt`);
|
|
219
|
+
const cached = await readCache(cachePath);
|
|
220
|
+
if (cached) {
|
|
221
|
+
await bumpStat("cacheHits");
|
|
222
|
+
const out = cached + "\n" + bypassHint(rawBytes, Buffer.byteLength(cached, "utf-8"));
|
|
223
|
+
return { text: out, summarized: true, wasCached: true, fellBack: false, outputBytes: Buffer.byteLength(out, "utf-8") };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await bumpStat("calls");
|
|
227
|
+
|
|
228
|
+
// If endpointOverride is provided, use the legacy local-provider path directly
|
|
229
|
+
// (preserves backward compat with existing tests that pass endpointOverride).
|
|
230
|
+
if (opts.endpointOverride) {
|
|
231
|
+
const summary = await callLegacyEndpoint(rawText, opts);
|
|
232
|
+
if (summary == null) {
|
|
233
|
+
await logEvent("tool_fallback", { tool: opts.toolName, reason: "llm-unreachable" });
|
|
234
|
+
const fallback = snipFallback(rawText) + "\n\n" + LLM_FALLBACK_FOOTER;
|
|
235
|
+
return { text: fallback, summarized: false, wasCached: false, fellBack: true, outputBytes: Buffer.byteLength(fallback, "utf-8") };
|
|
236
|
+
}
|
|
237
|
+
await writeCache(cachePath, summary).catch(() => undefined);
|
|
238
|
+
const out = summary + "\n" + bypassHint(rawBytes, Buffer.byteLength(summary, "utf-8"));
|
|
239
|
+
return { text: out, summarized: true, wasCached: false, fellBack: false, outputBytes: Buffer.byteLength(out, "utf-8") };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// New provider dispatch path
|
|
243
|
+
const provider = await selectProvider(opts.providerOverride);
|
|
244
|
+
const t0 = Date.now();
|
|
245
|
+
let summary: string | null = null;
|
|
246
|
+
let inTokens = 0;
|
|
247
|
+
let outTokens = 0;
|
|
248
|
+
let latencyMs = 0;
|
|
249
|
+
let fellBackToSnipCompact = false;
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const result = await provider.summarize(rawText, opts.systemPrompt);
|
|
253
|
+
summary = result.output;
|
|
254
|
+
inTokens = result.inTokens;
|
|
255
|
+
outTokens = result.outTokens;
|
|
256
|
+
latencyMs = result.latencyMs;
|
|
257
|
+
} catch {
|
|
258
|
+
latencyMs = Date.now() - t0;
|
|
259
|
+
fellBackToSnipCompact = true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Emit telemetry event
|
|
263
|
+
const llmCostUsd = costForLLM(provider.name, inTokens, outTokens);
|
|
264
|
+
await logEvent("tool_call", {
|
|
265
|
+
tool: opts.toolName,
|
|
266
|
+
extra: {
|
|
267
|
+
event: "llm_summarize_provider_used",
|
|
268
|
+
provider: provider.name,
|
|
269
|
+
latency_ms: latencyMs,
|
|
270
|
+
in_tokens: inTokens,
|
|
271
|
+
out_tokens: outTokens,
|
|
272
|
+
fellBackToSnipCompact,
|
|
273
|
+
llmCostUsd,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
if (summary == null) {
|
|
278
|
+
await logEvent("tool_fallback", { tool: opts.toolName, reason: "llm-unreachable" });
|
|
279
|
+
// Per v1.22 review: when the SELECTED provider is local AND it failed,
|
|
280
|
+
// surface a one-line stderr warning that names the URL the user
|
|
281
|
+
// configured. Without this, a typo in ASHLR_LLM_URL silently degrades
|
|
282
|
+
// every summarization to truncation with the only signal buried inside
|
|
283
|
+
// tool output. Only logged once per process to avoid spam.
|
|
284
|
+
if (provider.name === "local" && !_localUnreachableWarned) {
|
|
285
|
+
_localUnreachableWarned = true;
|
|
286
|
+
const url = process.env.ASHLR_LLM_URL ?? "http://localhost:1234/v1";
|
|
287
|
+
process.stderr.write(
|
|
288
|
+
`[ashlr] WARN: local LLM at ${url} unreachable; falling back to truncation. ` +
|
|
289
|
+
`Verify the endpoint, or unset ASHLR_LLM_URL to let auto-selection prefer Anthropic.\n`,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
const fallback = snipFallback(rawText) + "\n\n" + LLM_FALLBACK_FOOTER;
|
|
293
|
+
return { text: fallback, summarized: false, wasCached: false, fellBack: true, outputBytes: Buffer.byteLength(fallback, "utf-8") };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
await writeCache(cachePath, summary).catch(() => undefined);
|
|
297
|
+
const out = summary + "\n" + bypassHint(rawBytes, Buffer.byteLength(summary, "utf-8"));
|
|
298
|
+
return { text: out, summarized: true, wasCached: false, fellBack: false, outputBytes: Buffer.byteLength(out, "utf-8") };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// Legacy endpoint path (backward compat for endpointOverride callers/tests)
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
async function callLegacyEndpoint(rawText: string, opts: SummarizeOpts): Promise<string | null> {
|
|
306
|
+
const url = opts.endpointOverride!;
|
|
307
|
+
const apiKey =
|
|
308
|
+
process.env.ASHLR_LLM_KEY ??
|
|
309
|
+
(process.env.ASHLR_PRO_TOKEN && !process.env.ASHLR_LLM_URL
|
|
310
|
+
? process.env.ASHLR_PRO_TOKEN
|
|
311
|
+
: "local-llm");
|
|
312
|
+
const model = process.env.ASHLR_LLM_MODEL ?? "qwen/qwen3-coder-30b@8bit";
|
|
313
|
+
const timeoutMs = opts.timeoutMs ?? 5_000;
|
|
314
|
+
|
|
315
|
+
const ctl = new AbortController();
|
|
316
|
+
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
317
|
+
try {
|
|
318
|
+
const res = await fetch(`${url}/chat/completions`, {
|
|
319
|
+
method: "POST",
|
|
320
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
321
|
+
signal: ctl.signal,
|
|
322
|
+
body: JSON.stringify({
|
|
323
|
+
model,
|
|
324
|
+
stream: false,
|
|
325
|
+
max_tokens: 800,
|
|
326
|
+
temperature: 0.1,
|
|
327
|
+
messages: [
|
|
328
|
+
{ role: "system", content: opts.systemPrompt },
|
|
329
|
+
{ role: "user", content: rawText },
|
|
330
|
+
],
|
|
331
|
+
}),
|
|
332
|
+
});
|
|
333
|
+
clearTimeout(t);
|
|
334
|
+
if (!res.ok) return null;
|
|
335
|
+
const j = (await res.json()) as { choices?: Array<{ message?: { content?: string } }> };
|
|
336
|
+
const text = j.choices?.[0]?.message?.content?.trim();
|
|
337
|
+
return text && text.length > 0 ? text : null;
|
|
338
|
+
} catch {
|
|
339
|
+
clearTimeout(t);
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ---------------------------------------------------------------------------
|
|
345
|
+
// Internal stat bump helper
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
|
|
348
|
+
async function bumpStat(field: "calls" | "cacheHits"): Promise<void> {
|
|
349
|
+
try { await bumpSummarization(field); } catch { /* best-effort */ }
|
|
350
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local LLM provider — LM Studio / Ollama via OpenAI-compat API.
|
|
3
|
+
*
|
|
4
|
+
* Preserves the existing _summarize.ts local-LLM logic verbatim:
|
|
5
|
+
* - ASHLR_LLM_URL (default: http://localhost:1234/v1)
|
|
6
|
+
* - ASHLR_LLM_KEY (default: "local-llm")
|
|
7
|
+
* - ASHLR_LLM_MODEL (default: "qwen/qwen3-coder-30b@8bit")
|
|
8
|
+
*
|
|
9
|
+
* Availability: pings GET /models at the configured URL; result is cached for
|
|
10
|
+
* 60 seconds so repeated isAvailable() calls don't spam the local server.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { LlmProvider, LlmSummarizeResult } from "./types.ts";
|
|
14
|
+
|
|
15
|
+
function localEndpoint(): string {
|
|
16
|
+
// Mirror the llmEndpoint() logic from the old _summarize.ts — pro-token
|
|
17
|
+
// auto-routing uses the ashlr cloud; explicit ASHLR_LLM_URL wins; default
|
|
18
|
+
// is LM Studio at localhost:1234.
|
|
19
|
+
if (process.env.ASHLR_PRO_TOKEN && !process.env.ASHLR_LLM_URL) {
|
|
20
|
+
const base = process.env.ASHLR_API_URL ?? "https://api.ashlr.ai";
|
|
21
|
+
return `${base}/llm`;
|
|
22
|
+
}
|
|
23
|
+
return process.env.ASHLR_LLM_URL ?? "http://localhost:1234/v1";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Availability cache: { available, expiresAt }
|
|
27
|
+
let _availCache: { available: boolean; expiresAt: number } | null = null;
|
|
28
|
+
const AVAIL_CACHE_MS = 60_000;
|
|
29
|
+
|
|
30
|
+
export const localProvider: LlmProvider = {
|
|
31
|
+
name: "local",
|
|
32
|
+
|
|
33
|
+
async isAvailable(): Promise<boolean> {
|
|
34
|
+
// Explicit user config wins — if ASHLR_LLM_URL is set, trust the user
|
|
35
|
+
// knows their endpoint works. This both (a) respects intent and
|
|
36
|
+
// (b) avoids requiring test stubs to mock the probe endpoint
|
|
37
|
+
// (`/models`) just to be eligible for the chat path. Same rule for
|
|
38
|
+
// ASHLR_PRO_TOKEN (cloud-routed).
|
|
39
|
+
if (process.env.ASHLR_LLM_URL || process.env.ASHLR_PRO_TOKEN) return true;
|
|
40
|
+
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
if (_availCache && now < _availCache.expiresAt) return _availCache.available;
|
|
43
|
+
|
|
44
|
+
const url = localEndpoint();
|
|
45
|
+
const ctl = new AbortController();
|
|
46
|
+
const t = setTimeout(() => ctl.abort(), 2_000);
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(`${url}/models`, {
|
|
50
|
+
signal: ctl.signal,
|
|
51
|
+
headers: { Authorization: `Bearer ${process.env.ASHLR_LLM_KEY ?? "local-llm"}` },
|
|
52
|
+
});
|
|
53
|
+
clearTimeout(t);
|
|
54
|
+
const available = res.ok;
|
|
55
|
+
_availCache = { available, expiresAt: now + AVAIL_CACHE_MS };
|
|
56
|
+
return available;
|
|
57
|
+
} catch {
|
|
58
|
+
clearTimeout(t);
|
|
59
|
+
_availCache = { available: false, expiresAt: now + AVAIL_CACHE_MS };
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
async summarize(
|
|
65
|
+
text: string,
|
|
66
|
+
prompt: string,
|
|
67
|
+
opts?: { maxTokens?: number },
|
|
68
|
+
): Promise<LlmSummarizeResult> {
|
|
69
|
+
const url = localEndpoint();
|
|
70
|
+
const apiKey = process.env.ASHLR_LLM_KEY ?? (
|
|
71
|
+
process.env.ASHLR_PRO_TOKEN && !process.env.ASHLR_LLM_URL
|
|
72
|
+
? process.env.ASHLR_PRO_TOKEN
|
|
73
|
+
: "local-llm"
|
|
74
|
+
);
|
|
75
|
+
const model = process.env.ASHLR_LLM_MODEL ?? "qwen/qwen3-coder-30b@8bit";
|
|
76
|
+
const maxTokens = opts?.maxTokens ?? 800;
|
|
77
|
+
|
|
78
|
+
const ctl = new AbortController();
|
|
79
|
+
const timer = setTimeout(() => ctl.abort(), 5_000);
|
|
80
|
+
|
|
81
|
+
const t0 = Date.now();
|
|
82
|
+
let res: Response;
|
|
83
|
+
try {
|
|
84
|
+
res = await fetch(`${url}/chat/completions`, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
signal: ctl.signal,
|
|
87
|
+
headers: {
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
Authorization: `Bearer ${apiKey}`,
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
model,
|
|
93
|
+
stream: false,
|
|
94
|
+
max_tokens: maxTokens,
|
|
95
|
+
temperature: 0.1,
|
|
96
|
+
messages: [
|
|
97
|
+
{ role: "system", content: prompt },
|
|
98
|
+
{ role: "user", content: text },
|
|
99
|
+
],
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
} finally {
|
|
103
|
+
clearTimeout(timer);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!res.ok) throw new Error(`Local LLM error ${res.status}`);
|
|
107
|
+
|
|
108
|
+
const latencyMs = Date.now() - t0;
|
|
109
|
+
const j = (await res.json()) as {
|
|
110
|
+
choices?: Array<{ message?: { content?: string } }>;
|
|
111
|
+
usage?: { prompt_tokens?: number; completion_tokens?: number };
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const output = j.choices?.[0]?.message?.content?.trim() ?? "";
|
|
115
|
+
if (!output) throw new Error("Local LLM: empty response");
|
|
116
|
+
|
|
117
|
+
const inTokens = j.usage?.prompt_tokens ?? 0;
|
|
118
|
+
const outTokens = j.usage?.completion_tokens ?? 0;
|
|
119
|
+
|
|
120
|
+
return { output, inTokens, outTokens, latencyMs };
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** Test hook: reset the availability cache so tests can toggle endpoints. */
|
|
125
|
+
export function _resetLocalAvailabilityCache(): void {
|
|
126
|
+
_availCache = null;
|
|
127
|
+
}
|