bantamkit 0.38.0__tar.gz → 0.38.2__tar.gz
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.
- {bantamkit-0.38.0 → bantamkit-0.38.2}/PKG-INFO +3 -3
- {bantamkit-0.38.0 → bantamkit-0.38.2}/README.md +2 -2
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/bantamkit_status.json +8 -2
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/__init__.py +1 -1
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/mcpserver.py +24 -7
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillmine.py +153 -8
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/storage/tiers.py +50 -8
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/served-tool-surface.json +8 -2
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillmine.py +130 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_status_surface.py +32 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_tiers.py +70 -1
- {bantamkit-0.38.0 → bantamkit-0.38.2}/.gitignore +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/contracts/default.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/manifest.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/HISTORY.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/README.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/docs/architecture.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/docs/runbook.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/issues/142-settlement-timeout.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/patches/0009-retry-budget.patch +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/__init__.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/config.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/errors.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/posting.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/registry.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/report.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/retry.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/settle.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/validate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/tests/test_posting.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/repo/tests/test_settle.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-error-contract.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-handler-map.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-patch-before-after.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-retry-attempts.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-settlement-config.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-symbol-home.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-trace-blame.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-unread-key.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-in-137.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-in-359.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-in-372.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-out-11764.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-out-4137.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-out-8022.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-small-137.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-small-261.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-small-388.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/fixtures/.gitkeep +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/fixtures/catalog.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/perturbations/task-completion.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/.gitkeep +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/extract-contact.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/extract-invoice.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/extract-order.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/extract-schedule.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/extract-versions.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/nav-prod-port.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/nav-release-bundle.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-audit-retention.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-cache-ttl.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-db-port.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-deploy.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-env-endpoint.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-oncall-rotation.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-oncall.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-org-quota.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/recall-owner.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/shop-basket-total.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/shop-cheapest.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/shop-compare.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/shop-gadget-value.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/shop-stock-total.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/evals/tasks/shop-total.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/pricing/default.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/profiles/default.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/profiles/patient.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/rubrics/.gitkeep +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/rubrics/code-quality.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/rubrics/grounded-completion.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/rubrics/task-completion.yaml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/schemas/shiftwork-checkpoint.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/skills/.gitkeep +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/skills/file-graph.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/skills/memory-queries.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/skills/memory.md +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/.gitkeep +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/bantamkit_read.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/build_identity.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/document_list.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/document_read.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/file_graph.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/memory_compact.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/memory_dream.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/memory_queries.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/memory_recall.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/memory_save.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/repo_map.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/shiftwork_clock_in.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/shiftwork_clock_out.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/shiftwork_draft.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/shiftwork_plan.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/shiftwork_status.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/skill_audit.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/token_ledger.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/validate_json.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/_assets/tools/work_plan.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/hatch_build.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/pyproject.toml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/agent.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/assets.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/budget.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/client.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/contract.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/criticreplay.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/critique.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/docmanifest.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/docread.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/evalrun.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/eventlog.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/experiment.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/filegraph.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/hookadapter.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/hostinstall.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/jobbudget.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/loopguard.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/mcpreport.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/mcpstdio.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/__init__.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/__main__.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/component.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/divergence.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/dream.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/layers.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/native.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/queries.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/retrieval.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/skills.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/store.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/memory/usage.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/pdfread.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/pricing.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/profile.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/repomap.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/selfupdate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/shiftwork.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillaudit.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skilldraft.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillfile.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillhost.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillinstall.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillpromote.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillpropose.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/skillreport.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/statusline.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/storage/__init__.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/storage/coldindex.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/storage/hotindex.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/storage/lock.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/storage/logsegment.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/storage/retention.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/structured.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/textutil.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/tokenledger.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/__init__.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/bashturn.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/context.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/graph.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/log.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/recallfile.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/refs.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/repo.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/touch/summary.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/updatecheck.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/src/bantamkit/workplan.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/cli_exit_status_probe.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/conftest.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/bad-crc.docx +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/charref-4301-digits.html +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/charset-table.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/compression-method-9.docx +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/corrupt-deflate.docx +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/encrypted-member.docx +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/encrypted-mimetype.odt +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/eszett-cell-ref.xlsx +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/internal-dtd-entity.docx +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/rfc2231-charset.eml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/rfc822-nested-twice.eml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/unicode-digit-shared-string.xlsx +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/docread/x-uuencode.eml +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/f8404ab-perturbation-baseline.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/data/platform-assumption-baseline.json +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/docread_fixtures.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/perturbation_baseline_harness.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/rbp16_effect_probe.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/rbp18_payload_probe.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_adapter.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_agent.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_agreements.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_amendguard.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_assets.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_bantamkit_gitignore.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_bantamkit_read_tool.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_budget.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_build_identity.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_cli_oneshot.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_client.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_compaction_corpus_survey.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_conformance.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_conformance_harness_resilience.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_conformance_suite_table_gate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_contract_fanout.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_contract_patterns.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_contract_refs.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_criticreplay.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_critique.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_doc_commands_gate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_docread.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_docread_ceilings.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_document_manifest_parity.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_document_setup.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_document_tasks.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_document_tools.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_encoding_gate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_evalrun.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_eventlog.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_experimentation.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_field_program_gates.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_field_programs.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_filegraph.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_follow_ups.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_hookadapter.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_hostinstall.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_hostinstall_hooks.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_install_shape.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_investigation.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_jobbudget.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_ladder_statistics.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_launcher_which.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_layers.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_loopguard.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_mcp_endpoint.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_mcp_request_guard.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_mcpdrift.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_mcpreport.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_mcpserver.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_mcpstdio_frames.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_compact_tool.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_component.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_divergence.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_dream.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_layers.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_lifecycle.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_native.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_queries.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_retrieval.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_skills.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_store_tripwire.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_strings.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_memory_usage.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_mutmatrix.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_nativeexport.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_never_block.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_newline_gate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_packaging.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_pdfread.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_pinharness_ledger.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_platform_assumption_gate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_pricing.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_readme_separation.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_repo_map_tool.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_repomap.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_reviews.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_sdk_wire_text.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_selfupdate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_served_tool_count_records.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_shiftwork.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_shiftwork_draft.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillaudit.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillaudit_roots.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skilldraft.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillfile.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillhost.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillimprove.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillimprove_signals.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillinstall.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillmine_daily.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillpromote.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillpropose.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_skillreport.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_statusline.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_bar.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_coldindex.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_hotindex.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_log.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_log_consumers.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_recall_index.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_storage_retention.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_structured.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_thread_exception_gate.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_tokenledger.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_tool_annotations.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_tool_manifest.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touch_bashturn.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touch_context.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touch_graph.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touch_log.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touch_recall_file.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touch_refs.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touch_summary.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_touches.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_unit_agent_id.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_unit_results.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_unknown_arguments.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_updatecheck.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_version_agreement.py +0 -0
- {bantamkit-0.38.0 → bantamkit-0.38.2}/tests/test_workplan.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bantamkit
|
|
3
|
-
Version: 0.38.
|
|
3
|
+
Version: 0.38.2
|
|
4
4
|
Summary: Persistent memory for coding agents: an MCP server for Claude Code, Claude Desktop, Cursor and VS Code Copilot, plus a Python library that lifts small-model agents. Install once, run offline.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Ink01101011/bantamkit
|
|
6
6
|
Project-URL: Repository, https://github.com/Ink01101011/bantamkit
|
|
@@ -144,9 +144,9 @@ CPU architecture and Python minor version** (some wheels, such as `pydantic_core
|
|
|
144
144
|
one platform only), copy `wheels/` across, and install from it:
|
|
145
145
|
|
|
146
146
|
```bash
|
|
147
|
-
python -m pip download "bantamkit[mcp]==0.38.
|
|
147
|
+
python -m pip download "bantamkit[mcp]==0.38.2" -d wheels
|
|
148
148
|
python -m venv <env>
|
|
149
|
-
<env>/bin/pip install --no-index --find-links wheels "bantamkit[mcp]==0.38.
|
|
149
|
+
<env>/bin/pip install --no-index --find-links wheels "bantamkit[mcp]==0.38.2"
|
|
150
150
|
<env>/bin/bantamkit-mcp --install cursor
|
|
151
151
|
```
|
|
152
152
|
|
|
@@ -113,9 +113,9 @@ CPU architecture and Python minor version** (some wheels, such as `pydantic_core
|
|
|
113
113
|
one platform only), copy `wheels/` across, and install from it:
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
|
-
python -m pip download "bantamkit[mcp]==0.38.
|
|
116
|
+
python -m pip download "bantamkit[mcp]==0.38.2" -d wheels
|
|
117
117
|
python -m venv <env>
|
|
118
|
-
<env>/bin/pip install --no-index --find-links wheels "bantamkit[mcp]==0.38.
|
|
118
|
+
<env>/bin/pip install --no-index --find-links wheels "bantamkit[mcp]==0.38.2"
|
|
119
119
|
<env>/bin/bantamkit-mcp --install cursor
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bantamkit_status",
|
|
3
3
|
"title": "bantamkit status",
|
|
4
|
-
"description": "Is bantamkit working, and which bantamkit? Returns a short report a person can read in the transcript: `bantamkit Active 🟢` or `bantamkit Degraded 🟠` on the first line, then the version and `build_id` of the code answering, the surfaces it serves, the memory store it is bound to with its index size against its budget, whether the event log is on, and — when degraded — every condition in full with what to do about it. Call it when the user asks whether the server is alive or working, when a bantamkit tool answered oddly and you need to know whether the server is the reason, or when a reply arrived carrying a `bantamkit degraded` footer and you want the rest of it. Every fact is observed at call time from the server's own state; nothing is a heartbeat or a cached verdict.
|
|
4
|
+
"description": "Is bantamkit working, and which bantamkit? Returns a short report a person can read in the transcript: `bantamkit Active 🟢` or `bantamkit Degraded 🟠` on the first line, then the version and `build_id` of the code answering, the surfaces it serves, the memory store it is bound to with its index size against its budget, whether the event log is on, and — when degraded — every condition in full with what to do about it. Call it when the user asks whether the server is alive or working, when a bantamkit tool answered oddly and you need to know whether the server is the reason, or when a reply arrived carrying a `bantamkit degraded` footer and you want the rest of it. Every fact is observed at call time from the server's own state; nothing is a heartbeat or a cached verdict. Pass `detail: true` to list every usage and touch ledger on its own line instead of one summary line per family. Writes nothing, and never echoes another call's arguments. build_identity answers only the `build_id` fingerprint, as JSON, when all you need is to compare two installs; shiftwork_status reports a job, not the server.",
|
|
5
5
|
"surfaces": [
|
|
6
6
|
"mcp"
|
|
7
7
|
],
|
|
8
8
|
"parameters": {
|
|
9
|
-
"properties": {
|
|
9
|
+
"properties": {
|
|
10
|
+
"detail": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"default": false,
|
|
13
|
+
"description": "true lists every usage and touch ledger on its own line; the default folds each family into one summary line."
|
|
14
|
+
}
|
|
15
|
+
},
|
|
10
16
|
"type": "object",
|
|
11
17
|
"title": "bantamkit_statusArguments",
|
|
12
18
|
"additionalProperties": false
|
|
@@ -29,4 +29,4 @@ from bantamkit.structured import StructuredOutputError, extract_json, structured
|
|
|
29
29
|
# file as its dynamic version source, so the wheel's metadata and the string the MCP
|
|
30
30
|
# server advertises are the same committed bytes, and neither is a function of when
|
|
31
31
|
# someone last ran `pip`.
|
|
32
|
-
__version__ = "0.38.
|
|
32
|
+
__version__ = "0.38.2"
|
|
@@ -667,6 +667,13 @@ def build_identity() -> dict[str, Any]:
|
|
|
667
667
|
#: operator who read `bantamkit_status` in a footer must find it in their prompt menu
|
|
668
668
|
#: without translating, and a model that read it in a prompt must find the tool.
|
|
669
669
|
STATUS_NAME = "bantamkit_status"
|
|
670
|
+
#: job78: the one argument `bantamkit_status` takes. The advertised schema is the asset's
|
|
671
|
+
#: (`assets/tools/bantamkit_status.json`, which carries this same sentence); this is what
|
|
672
|
+
#: the signature enforces.
|
|
673
|
+
STATUS_DETAIL_DESCRIPTION = (
|
|
674
|
+
"true lists every usage and touch ledger on its own line; "
|
|
675
|
+
"the default folds each family into one summary line."
|
|
676
|
+
)
|
|
670
677
|
|
|
671
678
|
#: What the prompt says after the report. The report is the payload; this is the one line
|
|
672
679
|
#: that tells the model what the person wanted with it, and it asks for a RELAY rather
|
|
@@ -1004,6 +1011,8 @@ def status_report(
|
|
|
1004
1011
|
prompts: int,
|
|
1005
1012
|
templates: int,
|
|
1006
1013
|
sampling: bool = False,
|
|
1014
|
+
*,
|
|
1015
|
+
detail: bool = False,
|
|
1007
1016
|
) -> str:
|
|
1008
1017
|
"""The whole answer `bantamkit_status` returns — prose, for a person, in a transcript.
|
|
1009
1018
|
|
|
@@ -1018,8 +1027,9 @@ def status_report(
|
|
|
1018
1027
|
the question this tool exists to answer: two endpoints registered under one name is
|
|
1019
1028
|
the situation RB-P84 filed, and a version string cannot tell them apart.
|
|
1020
1029
|
|
|
1021
|
-
NO ARGUMENT VALUE CAN REACH THIS.
|
|
1022
|
-
|
|
1030
|
+
NO ARGUMENT VALUE CAN REACH THIS. Every input above is server state, except `detail`
|
|
1031
|
+
(job78), a bool that only chooses between the two forms of the `logs:` lines and is
|
|
1032
|
+
never printed; `test_status_surface.py` asserts the absence positively with a sentinel.
|
|
1023
1033
|
"""
|
|
1024
1034
|
identity = build_identity()
|
|
1025
1035
|
build = identity["build_id"]
|
|
@@ -1061,7 +1071,9 @@ def status_report(
|
|
|
1061
1071
|
# #204 (job69 J69-7): the capability tiers, every hot index, every log and the last
|
|
1062
1072
|
# retention run -- AFTER the update line, so every line above keeps its number. Read
|
|
1063
1073
|
# here, on demand, and never on the per-call footer path.
|
|
1064
|
-
*storage_tiers.status_lines(
|
|
1074
|
+
*storage_tiers.status_lines(
|
|
1075
|
+
memory, Path.home(), storage_tiers.current_cwd(), detail=detail
|
|
1076
|
+
),
|
|
1065
1077
|
# #199 Task 6: read-repair and the usage ledger, AFTER the storage block for the same
|
|
1066
1078
|
# reason the block sits after the update line -- every line above keeps its number.
|
|
1067
1079
|
f"read-repaired: {memory.repaired} this process",
|
|
@@ -1325,9 +1337,10 @@ class _Tool(SDKTool if SDKTool is not None else object): # type: ignore[misc]
|
|
|
1325
1337
|
side effect, also the 0.37.2 behaviour: the SDK logs a `ToolError` at INFO, not with
|
|
1326
1338
|
`logger.exception`, so a crash prints no traceback to stderr under the default logging.
|
|
1327
1339
|
|
|
1328
|
-
Adopting the SDK's crash-hiding policy -- dropping the suffix --
|
|
1329
|
-
|
|
1330
|
-
|
|
1340
|
+
Adopting the SDK's crash-hiding policy -- dropping the suffix -- was considered in #274
|
|
1341
|
+
and declined on 2026-11-05: keeping this override is the project's policy, written down
|
|
1342
|
+
in docs/porting.md ("A tool crash answers `Error executing tool <name>: <exc>` in both
|
|
1343
|
+
runtimes"). Reversing it is a change to BOTH runtimes and their conformance cases.
|
|
1331
1344
|
"""
|
|
1332
1345
|
|
|
1333
1346
|
async def run(
|
|
@@ -1858,7 +1871,10 @@ def build_server(memory: Memory, log: EventLog | None = None) -> Any:
|
|
|
1858
1871
|
)
|
|
1859
1872
|
return _noted_dict(identity)
|
|
1860
1873
|
|
|
1861
|
-
def bantamkit_status(
|
|
1874
|
+
def bantamkit_status(
|
|
1875
|
+
ctx: Context,
|
|
1876
|
+
detail: Annotated[bool, Field(description=STATUS_DETAIL_DESCRIPTION)] = False,
|
|
1877
|
+
) -> str:
|
|
1862
1878
|
"""The one surface every host renders, answering "is this thing working".
|
|
1863
1879
|
|
|
1864
1880
|
NO FOOTER ON THIS ONE, and the omission is the design rather than an oversight:
|
|
@@ -1881,6 +1897,7 @@ def build_server(memory: Memory, log: EventLog | None = None) -> Any:
|
|
|
1881
1897
|
SERVED_PROMPTS,
|
|
1882
1898
|
SERVED_RESOURCE_TEMPLATES,
|
|
1883
1899
|
_sampling_declared(ctx),
|
|
1900
|
+
detail=detail,
|
|
1884
1901
|
)
|
|
1885
1902
|
|
|
1886
1903
|
def bantamkit_read(
|
|
@@ -22,12 +22,19 @@ lacks a string `ts` or a known `status`) is skipped.
|
|
|
22
22
|
- `repo` (R7): the realpath of the root's COMMON git dir — a linked worktree's `commondir`
|
|
23
23
|
is followed — so the worktrees of one repository share one key; `""` outside a repo.
|
|
24
24
|
|
|
25
|
-
The **signature** is `
|
|
25
|
+
The **signature** is `tokenize_label` keywords of the prompt (a term in more than half of the
|
|
26
26
|
source's pieces is dropped, then the `KEYWORDS_TOP` most frequent, ties in code-point
|
|
27
27
|
order), the tool-name n-grams for `NGRAM_SIZES`, and the touched paths. **Similarity** is a
|
|
28
28
|
weighted Jaccard over the three, each component whose union is empty left out of both sums,
|
|
29
29
|
answered as an exact reduced fraction `(num, den)`; `meets` compares on integers only, so the
|
|
30
|
-
two runtimes cannot disagree at the `SIM_MIN` boundary.
|
|
30
|
+
two runtimes cannot disagree at the `SIM_MIN` boundary. `tokenize_label` is the miner's own
|
|
31
|
+
tokenizer (#289), not recall's `tokenize_v2`: recall's code-point bigrams split Thai vowels and
|
|
32
|
+
tone marks (`ทำต่อ` became `ทํ ํา าต ต่ ่อ`) and let filler words such as `แล้ว` and `ทำ` decide
|
|
33
|
+
the clusters. It groups a base character with its combining marks and following vowels
|
|
34
|
+
(`ะ า ำ ๅ` and Lao `ະ າ ຳ`, which are Lo, not marks) and a leading vowel with the character
|
|
35
|
+
after it, drops `LABEL_STOPWORDS`, and forms bigrams of those units; spaced-script
|
|
36
|
+
words are kept whole exactly as `tokenize_v2` keeps them. `tokenize_v2` is unchanged, because
|
|
37
|
+
recall is measured on it.
|
|
31
38
|
|
|
32
39
|
**Clusters** (Task 5): leader clustering per source, the X1 bars on successful members only,
|
|
33
40
|
twice the bar for the installer, recurring steps, pitfalls from failed members, and the X3
|
|
@@ -47,7 +54,9 @@ list, or the interrupt marker; a last turn has none). The `MinerCache` keeps eac
|
|
|
47
54
|
offset and open turn, so an unchanged file is not read and a grown one is read from where the
|
|
48
55
|
last run stopped. Measured shapes (job73 C6 Step 0) added `bash-*`, `task-notification` and
|
|
49
56
|
`local-command-caveat` to the cut list: a `!` command's output and a background task's result
|
|
50
|
-
arrive as non-meta user records.
|
|
57
|
+
arrive as non-meta user records. #289 added `ide_opened_file`, `ide_selection`,
|
|
58
|
+
`ide_diagnostics` and `browser_instruction`: the IDE and the Chrome extension write their
|
|
59
|
+
notices into the user's record, and a prompt made only of them is empty and starts no piece.
|
|
51
60
|
|
|
52
61
|
`runtime-ts/src/skillmine.ts` is the port;
|
|
53
62
|
`tools/conformance/suites/skill-miner.mjs` compares the two.
|
|
@@ -61,6 +70,7 @@ import json
|
|
|
61
70
|
import math
|
|
62
71
|
import os
|
|
63
72
|
import re
|
|
73
|
+
import unicodedata
|
|
64
74
|
import zlib
|
|
65
75
|
from collections import Counter
|
|
66
76
|
from collections.abc import Iterable, Mapping
|
|
@@ -68,7 +78,7 @@ from dataclasses import dataclass, field
|
|
|
68
78
|
from pathlib import Path
|
|
69
79
|
from typing import Any
|
|
70
80
|
|
|
71
|
-
from bantamkit.memory.retrieval import
|
|
81
|
+
from bantamkit.memory.retrieval import _nospace, _wordchar
|
|
72
82
|
from bantamkit.touch.log import AGENT_ID
|
|
73
83
|
|
|
74
84
|
SOURCE_SHIFTWORK = "shiftwork"
|
|
@@ -408,13 +418,141 @@ def _read_bundle(p: str) -> dict[str, Any] | None:
|
|
|
408
418
|
# ---------------------------------------------------------------------------- signature
|
|
409
419
|
|
|
410
420
|
|
|
421
|
+
#: #289: Thai filler words dropped from a label; each is matched as a unit sequence.
|
|
422
|
+
LABEL_STOPWORDS = (
|
|
423
|
+
"ครับ",
|
|
424
|
+
"ค่ะ",
|
|
425
|
+
"แล้ว",
|
|
426
|
+
"ต่อ",
|
|
427
|
+
"ทำ",
|
|
428
|
+
"ให้",
|
|
429
|
+
"ที่",
|
|
430
|
+
"และ",
|
|
431
|
+
"ได้",
|
|
432
|
+
"หน่อย",
|
|
433
|
+
"ด้วย",
|
|
434
|
+
"เลย",
|
|
435
|
+
"นี้",
|
|
436
|
+
"ไหม",
|
|
437
|
+
"อีก",
|
|
438
|
+
"ก่อน",
|
|
439
|
+
"เป็น",
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _is_leading_vowel(cp: int) -> bool:
|
|
444
|
+
return 0x0E40 <= cp <= 0x0E44 or 0x0EC0 <= cp <= 0x0EC4
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
#: Thai/Lao following vowels (SARA A, AA, AM, LAKKHANGYAO; Lao A, AA, AM): general category
|
|
448
|
+
#: Lo, yet they close the syllable before them, so a unit takes them the way it takes a mark
|
|
449
|
+
#: (#289 amendment A1).
|
|
450
|
+
_FOLLOWING_VOWELS = frozenset({0x0E30, 0x0E32, 0x0E33, 0x0E45, 0x0EB0, 0x0EB2, 0x0EB3})
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _label_fold(text: str) -> str:
|
|
454
|
+
"""NFKC, lower-cased, SARA AM recomposed (NFKC splits it into NIKHAHIT + SARA AA)."""
|
|
455
|
+
s = unicodedata.normalize("NFKC", text).lower()
|
|
456
|
+
return s.replace("\u0e4d\u0e32", "\u0e33").replace("\u0ecd\u0eb2", "\u0eb3")
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _label_units(run: str) -> list[str]:
|
|
460
|
+
"""A no-space run as units: a mark or a following vowel joins the unit before it, a
|
|
461
|
+
leading vowel the one after. Each unit is gathered as a list of parts and joined once, so
|
|
462
|
+
a long run of joining characters stays linear (`out[-1] += ch` copied the unit each time)."""
|
|
463
|
+
units: list[list[str]] = []
|
|
464
|
+
pend: list[str] = []
|
|
465
|
+
for ch in run:
|
|
466
|
+
joins = ord(ch) in _FOLLOWING_VOWELS or unicodedata.category(ch) in ("Mn", "Mc")
|
|
467
|
+
if joins and units and not pend:
|
|
468
|
+
units[-1].append(ch)
|
|
469
|
+
elif _is_leading_vowel(ord(ch)):
|
|
470
|
+
pend.append(ch)
|
|
471
|
+
else:
|
|
472
|
+
pend.append(ch)
|
|
473
|
+
units.append(pend)
|
|
474
|
+
pend = []
|
|
475
|
+
if pend:
|
|
476
|
+
units.append(pend)
|
|
477
|
+
return ["".join(u) for u in units]
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
_STOP_UNITS: tuple[tuple[str, ...], ...] = tuple(
|
|
481
|
+
sorted(
|
|
482
|
+
(tuple(_label_units(_label_fold(w))) for w in LABEL_STOPWORDS),
|
|
483
|
+
key=len,
|
|
484
|
+
reverse=True,
|
|
485
|
+
)
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def _label_run(run: str, out: list[str]) -> None:
|
|
490
|
+
units = _label_units(run)
|
|
491
|
+
seg: list[str] = []
|
|
492
|
+
|
|
493
|
+
def emit() -> None:
|
|
494
|
+
if len(seg) == 1:
|
|
495
|
+
out.append(seg[0])
|
|
496
|
+
else:
|
|
497
|
+
out.extend(seg[i] + seg[i + 1] for i in range(len(seg) - 1))
|
|
498
|
+
seg.clear()
|
|
499
|
+
|
|
500
|
+
i = 0
|
|
501
|
+
while i < len(units):
|
|
502
|
+
for stop in _STOP_UNITS:
|
|
503
|
+
if tuple(units[i : i + len(stop)]) == stop:
|
|
504
|
+
emit()
|
|
505
|
+
i += len(stop)
|
|
506
|
+
break
|
|
507
|
+
else:
|
|
508
|
+
seg.append(units[i])
|
|
509
|
+
i += 1
|
|
510
|
+
emit()
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def tokenize_label(text: str) -> list[str]:
|
|
514
|
+
"""The miner's tokenizer (#289): `tokenize_v2`'s scan, but a no-space run is cut into
|
|
515
|
+
units (a base with its marks and following vowels, a leading vowel with what follows),
|
|
516
|
+
`LABEL_STOPWORDS` end a segment and emit nothing, and a segment emits its one unit or its
|
|
517
|
+
unit bigrams. A bigram of units can still span a syllable boundary, and a stopword's unit
|
|
518
|
+
sequence also matches inside a longer word (`ต่อย` -> `ย`): there is no segmenter."""
|
|
519
|
+
s = _label_fold(text)
|
|
520
|
+
out: list[str] = []
|
|
521
|
+
word: list[str] = []
|
|
522
|
+
run: list[str] = []
|
|
523
|
+
|
|
524
|
+
def flush_word() -> None:
|
|
525
|
+
if word:
|
|
526
|
+
out.append("".join(word))
|
|
527
|
+
word.clear()
|
|
528
|
+
|
|
529
|
+
def flush_run() -> None:
|
|
530
|
+
if run:
|
|
531
|
+
_label_run("".join(run), out)
|
|
532
|
+
run.clear()
|
|
533
|
+
|
|
534
|
+
for ch in s:
|
|
535
|
+
if _nospace(ord(ch)):
|
|
536
|
+
flush_word()
|
|
537
|
+
run.append(ch)
|
|
538
|
+
elif _wordchar(ch):
|
|
539
|
+
flush_run()
|
|
540
|
+
word.append(ch)
|
|
541
|
+
else:
|
|
542
|
+
flush_word()
|
|
543
|
+
flush_run()
|
|
544
|
+
flush_word()
|
|
545
|
+
flush_run()
|
|
546
|
+
return out
|
|
547
|
+
|
|
548
|
+
|
|
411
549
|
def doc_freq(pieces: Iterable[Piece]) -> tuple[Counter[str], int]:
|
|
412
|
-
"""Document frequency of
|
|
550
|
+
"""Document frequency of each `tokenize_label` term over one source's pieces, and the count."""
|
|
413
551
|
df: Counter[str] = Counter()
|
|
414
552
|
n = 0
|
|
415
553
|
for p in pieces:
|
|
416
554
|
n += 1
|
|
417
|
-
df.update(set(
|
|
555
|
+
df.update(set(tokenize_label(p.prompt)))
|
|
418
556
|
return df, n
|
|
419
557
|
|
|
420
558
|
|
|
@@ -425,7 +563,7 @@ def ngrams(tools: Iterable[str]) -> frozenset[str]:
|
|
|
425
563
|
|
|
426
564
|
def signature(piece: Piece, df: Mapping[str, int], n: int) -> Signature:
|
|
427
565
|
num, den = KEYWORD_DF_MAX
|
|
428
|
-
counts = Counter(t for t in
|
|
566
|
+
counts = Counter(t for t in tokenize_label(piece.prompt) if df.get(t, 0) * den <= n * num)
|
|
429
567
|
ranked = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))
|
|
430
568
|
return Signature(
|
|
431
569
|
keywords=frozenset(t for t, _ in ranked[:KEYWORDS_TOP]),
|
|
@@ -842,7 +980,10 @@ CORRECTIONS = (
|
|
|
842
980
|
INTERRUPT = "[Request interrupted by user"
|
|
843
981
|
#: R3: spans the harness writes into a user record; none of them is the user's own words.
|
|
844
982
|
#: `bash-*` and `task-notification` are measured additions (C6 Step 0): a `!` command's
|
|
845
|
-
#: output and a background task's result arrive as non-meta user records.
|
|
983
|
+
#: output and a background task's result arrive as non-meta user records. The IDE and
|
|
984
|
+
#: browser tags are #289's, measured 2026-11-06 over the maintainer's 798 session pieces:
|
|
985
|
+
#: `ide_opened_file` was carried by 67, `browser_instruction` by 58, `ide_selection` by 20;
|
|
986
|
+
#: `ide_diagnostics` occurs 151 times in transcripts (0 in pieces) and is cut defensively.
|
|
846
987
|
CUT_TAGS = (
|
|
847
988
|
"system-reminder",
|
|
848
989
|
"command-name",
|
|
@@ -855,6 +996,10 @@ CUT_TAGS = (
|
|
|
855
996
|
"bash-stdout",
|
|
856
997
|
"bash-stderr",
|
|
857
998
|
"task-notification",
|
|
999
|
+
"ide_opened_file",
|
|
1000
|
+
"ide_selection",
|
|
1001
|
+
"ide_diagnostics",
|
|
1002
|
+
"browser_instruction",
|
|
858
1003
|
)
|
|
859
1004
|
#: R4: the tools whose input names a file the agent touched.
|
|
860
1005
|
PATH_TOOLS = ("Read", "Edit", "MultiEdit", "Write", "NotebookEdit")
|
|
@@ -14,8 +14,10 @@ THE STATUS LINES, appended after the update line so every earlier line keeps its
|
|
|
14
14
|
storage: core on; hooks on|off; host data on|off
|
|
15
15
|
index facts <layer>: <n> doc(s), <bytes> bytes, persisted|in-memory (one per memory layer
|
|
16
16
|
whose facts/ exists)
|
|
17
|
-
logs:
|
|
18
|
-
|
|
17
|
+
logs: hook-log <bytes> (<n> rotated, <n> skipped) (when it exists)
|
|
18
|
+
logs: usage/* <n> file(s) <bytes> (<n> rotated, <n> skipped) (one per family that
|
|
19
|
+
logs: touch/* <n> file(s) <bytes> (<n> rotated, <n> skipped) has a counted log;
|
|
20
|
+
or `logs: none`)
|
|
19
21
|
retention: [off; ]last ran <iso>|never; refused <n>
|
|
20
22
|
|
|
21
23
|
`<layer>` is the layer's KIND (`project`, `extra`, `profile`), never a grant's name -- the rule
|
|
@@ -24,6 +26,15 @@ recall scores from an index built per call, and this is where that is reported.
|
|
|
24
26
|
the index holds on disk (head plus every shard file present). A log's `bytes` is its live file
|
|
25
27
|
plus its rotated segments; `skipped` is what `LogSegment.read` could not parse.
|
|
26
28
|
|
|
29
|
+
THE TWO FORMS OF THE `logs:` LINES (job78). By default every `usage/<id>` ledger is folded
|
|
30
|
+
into ONE `usage/*` line and every `touch/<id>` log into ONE `touch/*` line, in that order
|
|
31
|
+
after `hook-log`: `<n>` counts the family's logs, `file` when it is 1 and `files` otherwise,
|
|
32
|
+
and the bytes, rotated and skipped figures are the sums of the per-log numbers. A family
|
|
33
|
+
with no counted log prints no line. With `detail` (the `bantamkit_status` argument of that
|
|
34
|
+
name) every log gets its own line instead, `logs: <name> <bytes> (<n> rotated, <n>
|
|
35
|
+
skipped)` with `<name>` one of `hook-log`, `usage/<id>`, `touch/<id>` -- the only form
|
|
36
|
+
before job78, which on a long-lived home printed thousands of usage lines.
|
|
37
|
+
|
|
27
38
|
`runtime-ts/src/storage/tiers.ts` is the port; `tools/conformance/suites/storage-tiers.mjs`
|
|
28
39
|
compares the two over an MCP-only host profile (no hook, no native directory).
|
|
29
40
|
"""
|
|
@@ -150,8 +161,9 @@ def _logs(base: Path) -> list[tuple[str, Path]]:
|
|
|
150
161
|
return found
|
|
151
162
|
|
|
152
163
|
|
|
153
|
-
def
|
|
154
|
-
|
|
164
|
+
def _log_counts(home: Path | str) -> list[tuple[str, int, int, int]]:
|
|
165
|
+
"""`(name, held, rotated, skipped)` per counted log, in `_logs` order."""
|
|
166
|
+
out: list[tuple[str, int, int, int]] = []
|
|
155
167
|
for name, path in _logs(Path(home) / ".bantamkit"):
|
|
156
168
|
seg = LogSegment(path)
|
|
157
169
|
segments = seg.segments()
|
|
@@ -159,7 +171,32 @@ def log_lines(home: Path | str) -> list[str]:
|
|
|
159
171
|
continue
|
|
160
172
|
held = _size(path) + sum(_size(s) for s in segments)
|
|
161
173
|
seg.read()
|
|
162
|
-
out.append(
|
|
174
|
+
out.append((name, held, len(segments), seg.skipped))
|
|
175
|
+
return out
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def log_lines(home: Path | str, detail: bool = False) -> list[str]:
|
|
179
|
+
"""The `logs:` lines: per family by default, per log with `detail` (module docstring)."""
|
|
180
|
+
counts = _log_counts(home)
|
|
181
|
+
if detail:
|
|
182
|
+
out = [f"logs: {n} {held} ({r} rotated, {s} skipped)" for n, held, r, s in counts]
|
|
183
|
+
return out or ["logs: none"]
|
|
184
|
+
out = [
|
|
185
|
+
f"logs: {n} {held} ({r} rotated, {s} skipped)"
|
|
186
|
+
for n, held, r, s in counts
|
|
187
|
+
if n == "hook-log"
|
|
188
|
+
]
|
|
189
|
+
for family in ("usage", "touch"):
|
|
190
|
+
members = [c for c in counts if c[0].startswith(f"{family}/")]
|
|
191
|
+
if not members:
|
|
192
|
+
continue
|
|
193
|
+
held = sum(c[1] for c in members)
|
|
194
|
+
rotated = sum(c[2] for c in members)
|
|
195
|
+
skipped = sum(c[3] for c in members)
|
|
196
|
+
out.append(
|
|
197
|
+
f"logs: {family}/* {_plural(len(members), 'file')} {held} "
|
|
198
|
+
f"({rotated} rotated, {skipped} skipped)"
|
|
199
|
+
)
|
|
163
200
|
return out or ["logs: none"]
|
|
164
201
|
|
|
165
202
|
|
|
@@ -196,12 +233,17 @@ def retention_line(home: Path | str) -> str:
|
|
|
196
233
|
return f"retention: {prefix}last ran {ran}; refused {refused}"
|
|
197
234
|
|
|
198
235
|
|
|
199
|
-
def status_lines(
|
|
200
|
-
|
|
236
|
+
def status_lines(
|
|
237
|
+
memory: Any, home: Path | str, cwd: Path | str | None, *, detail: bool = False
|
|
238
|
+
) -> list[str]:
|
|
239
|
+
"""Every storage line of `bantamkit_status`, in order. Never raises.
|
|
240
|
+
|
|
241
|
+
`detail` is the tool's argument: one `logs:` line per log instead of per family.
|
|
242
|
+
"""
|
|
201
243
|
return [
|
|
202
244
|
tier_line(tiers(home, cwd)),
|
|
203
245
|
*index_lines(memory),
|
|
204
|
-
*log_lines(home),
|
|
246
|
+
*log_lines(home, detail),
|
|
205
247
|
touch_line(home),
|
|
206
248
|
retention_line(home),
|
|
207
249
|
]
|
|
@@ -23,9 +23,15 @@
|
|
|
23
23
|
"openWorldHint": false,
|
|
24
24
|
"readOnlyHint": true
|
|
25
25
|
},
|
|
26
|
-
"description": "Is bantamkit working, and which bantamkit? Returns a short report a person can read in the transcript: `bantamkit Active 🟢` or `bantamkit Degraded 🟠` on the first line, then the version and `build_id` of the code answering, the surfaces it serves, the memory store it is bound to with its index size against its budget, whether the event log is on, and — when degraded — every condition in full with what to do about it. Call it when the user asks whether the server is alive or working, when a bantamkit tool answered oddly and you need to know whether the server is the reason, or when a reply arrived carrying a `bantamkit degraded` footer and you want the rest of it. Every fact is observed at call time from the server's own state; nothing is a heartbeat or a cached verdict.
|
|
26
|
+
"description": "Is bantamkit working, and which bantamkit? Returns a short report a person can read in the transcript: `bantamkit Active 🟢` or `bantamkit Degraded 🟠` on the first line, then the version and `build_id` of the code answering, the surfaces it serves, the memory store it is bound to with its index size against its budget, whether the event log is on, and — when degraded — every condition in full with what to do about it. Call it when the user asks whether the server is alive or working, when a bantamkit tool answered oddly and you need to know whether the server is the reason, or when a reply arrived carrying a `bantamkit degraded` footer and you want the rest of it. Every fact is observed at call time from the server's own state; nothing is a heartbeat or a cached verdict. Pass `detail: true` to list every usage and touch ledger on its own line instead of one summary line per family. Writes nothing, and never echoes another call's arguments. build_identity answers only the `build_id` fingerprint, as JSON, when all you need is to compare two installs; shiftwork_status reports a job, not the server.",
|
|
27
27
|
"inputSchema": {
|
|
28
|
-
"properties": {
|
|
28
|
+
"properties": {
|
|
29
|
+
"detail": {
|
|
30
|
+
"type": "boolean",
|
|
31
|
+
"default": false,
|
|
32
|
+
"description": "true lists every usage and touch ledger on its own line; the default folds each family into one summary line."
|
|
33
|
+
}
|
|
34
|
+
},
|
|
29
35
|
"type": "object",
|
|
30
36
|
"title": "bantamkit_statusArguments",
|
|
31
37
|
"additionalProperties": false
|
|
@@ -1012,3 +1012,133 @@ def test_a_successful_commit_carries_its_subject_and_a_failed_one_none():
|
|
|
1012
1012
|
del old["commits"] # a cache entry written before the field: read as no commits
|
|
1013
1013
|
assert skillmine._piece_from(old).commits == ()
|
|
1014
1014
|
assert skillmine._piece_from({**piece.as_dict(), "commits": [1]}) is None
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
# ---------------------------------------------------------------------------- #289
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
_IDE_NOTICE = (
|
|
1021
|
+
"<ide_opened_file>The user opened the file /x/a.py in the IDE. This may or may not be "
|
|
1022
|
+
"related to the current task.</ide_opened_file>"
|
|
1023
|
+
"<browser_instruction>Claude in Chrome browser automation is available.</browser_instruction>"
|
|
1024
|
+
)
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
@pytest.mark.parametrize(
|
|
1028
|
+
("text", "want"),
|
|
1029
|
+
[
|
|
1030
|
+
("ทำต่อ", []),
|
|
1031
|
+
("merge แล้ว tag ต่อ", ["merge", "tag"]),
|
|
1032
|
+
("อนุมัติแล้ว", ["อนุ", "นุมั", "มัติ"]),
|
|
1033
|
+
("กลับไปใช้ แผนเดิม", ["กลั", "ลับ", "บไป", "ไปใช้", "แผน", "นเดิ", "เดิม"]),
|
|
1034
|
+
("เข้าใจแล้วครับ ทำต่อเลย", ["เข้าใจ"]),
|
|
1035
|
+
("จำนวน", ["จำน", "นว", "วน"]),
|
|
1036
|
+
("ทำงาน", ["งาน"]),
|
|
1037
|
+
("ประชุม", ["ประ", "ระชุ", "ชุม"]),
|
|
1038
|
+
("สร้างไฟล์ใหม่", ["สร้า", "ร้าง", "งไฟ", "ไฟล์", "ล์ให", "ใหม่"]),
|
|
1039
|
+
# Known limit (#289 A1): a stopword's unit sequence matches inside a longer word
|
|
1040
|
+
# ("ต่อ" in "ต่อย"); there is no dictionary segmenter, by ruling.
|
|
1041
|
+
("ต่อย", ["ย"]),
|
|
1042
|
+
],
|
|
1043
|
+
)
|
|
1044
|
+
def test_tokenize_label_vectors(text: str, want: list[str]) -> None:
|
|
1045
|
+
assert skillmine.tokenize_label(text) == want
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
def test_tokenize_label_matches_tokenize_v2_on_ascii() -> None:
|
|
1049
|
+
from bantamkit.memory.retrieval import tokenize_v2
|
|
1050
|
+
|
|
1051
|
+
for text in ["Fix the parser in src/parse.py, then run 2 tests!", "merge PR #12 & tag v0.38"]:
|
|
1052
|
+
assert skillmine.tokenize_label(text) == tokenize_v2(text)
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
_FOLLOWING_VOWELS = "\u0e30\u0e32\u0e33\u0e45\u0eb0\u0eb2\u0eb3"
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
def test_tokenize_label_no_token_starts_with_a_mark_or_following_vowel() -> None:
|
|
1059
|
+
import unicodedata
|
|
1060
|
+
|
|
1061
|
+
texts = [
|
|
1062
|
+
"ทำต่อ",
|
|
1063
|
+
"merge แล้ว tag ต่อ",
|
|
1064
|
+
"อนุมัติแล้ว",
|
|
1065
|
+
"กลับไปใช้ แผนเดิม",
|
|
1066
|
+
"เข้าใจแล้วครับ ทำต่อเลย",
|
|
1067
|
+
"จำนวน",
|
|
1068
|
+
"ประชุม",
|
|
1069
|
+
"สร้างไฟล์ใหม่",
|
|
1070
|
+
"ສະບາຍດີ ຂອບໃຈຫຼາຍໆ",
|
|
1071
|
+
]
|
|
1072
|
+
toks = [t for x in texts for t in skillmine.tokenize_label(x)]
|
|
1073
|
+
assert toks
|
|
1074
|
+
assert all(unicodedata.category(t[0]) not in ("Mn", "Mc") for t in toks)
|
|
1075
|
+
assert all(t[0] not in _FOLLOWING_VOWELS for t in toks)
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
def test_label_units_attach_following_vowels() -> None:
|
|
1079
|
+
assert skillmine._label_units("ทำ") == ["ทำ"]
|
|
1080
|
+
assert skillmine._label_units("เข้าใจ") == ["เข้า", "ใจ"]
|
|
1081
|
+
assert skillmine._label_units("ประ") == ["ป", "ระ"]
|
|
1082
|
+
# A following vowel with no unit before it starts its own unit.
|
|
1083
|
+
assert skillmine._label_units("\u0e32ก") == ["\u0e32", "ก"]
|
|
1084
|
+
# One pending behind a leading vowel is not attached across it.
|
|
1085
|
+
assert skillmine._label_units("เาก") == ["เา", "ก"]
|
|
1086
|
+
assert skillmine.tokenize_label("เาก") == ["เาก"]
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
def test_label_units_lakkhangyao_and_lao_sara_a() -> None:
|
|
1090
|
+
# U+0E45 LAKKHANGYAO joins the unit before it, like the mark U+0E35 after ษ.
|
|
1091
|
+
assert skillmine._label_units("ฤๅษี") == ["ฤๅ", "ษี"]
|
|
1092
|
+
assert skillmine.tokenize_label("ฤๅษี") == ["ฤๅษี"]
|
|
1093
|
+
# U+0EB0 Lao SARA A joins its base.
|
|
1094
|
+
assert skillmine._label_units("ຈະ") == ["ຈະ"]
|
|
1095
|
+
assert skillmine.tokenize_label("ຈະ") == ["ຈະ"]
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
@pytest.mark.parametrize(
|
|
1099
|
+
"run",
|
|
1100
|
+
["ก" + "\u0e48" * 10**6, "ก" + "\u0e32" * 10**6, "เ" + "\u0e48" * 10**6],
|
|
1101
|
+
ids=["mark", "following-vowel", "behind-leading-vowel"],
|
|
1102
|
+
)
|
|
1103
|
+
def test_label_units_linear_on_long_joining_runs(run: str) -> None:
|
|
1104
|
+
# `out[-1] += ch` copied the unit per joining character: ~13.7 s at 1M chars (26 s behind a
|
|
1105
|
+
# leading vowel). Linear it is ~0.3 s, so a 3 s bound fails the old code and not a slow box.
|
|
1106
|
+
import time
|
|
1107
|
+
|
|
1108
|
+
t0 = time.perf_counter()
|
|
1109
|
+
units = skillmine._label_units(run)
|
|
1110
|
+
assert time.perf_counter() - t0 < 3.0
|
|
1111
|
+
assert "".join(units) == run
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
def test_tokenize_v2_unchanged_for_recall() -> None:
|
|
1115
|
+
from bantamkit.memory.retrieval import tokenize_v2
|
|
1116
|
+
|
|
1117
|
+
assert tokenize_v2("ทำต่อ") == ["ทํ", "ํา", "าต", "ต่", "่อ"]
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
def test_signature_uses_tokenize_label() -> None:
|
|
1121
|
+
p = Piece(id="t", source="session", ts="", repo="", prompt="อนุมัติแล้ว ทำต่อ", ok=True)
|
|
1122
|
+
df, n = doc_freq([p])
|
|
1123
|
+
assert set(df) == {"อนุ", "นุมั", "มัติ"}
|
|
1124
|
+
assert signature(p, {}, 1).keywords == frozenset({"อนุ", "นุมั", "มัติ"})
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
def test_clean_prompt_cuts_ide_and_browser_notices() -> None:
|
|
1128
|
+
assert clean_prompt(_IDE_NOTICE) == ""
|
|
1129
|
+
assert clean_prompt("<ide_selection>sel</ide_selection> fix it") == "fix it"
|
|
1130
|
+
assert clean_prompt("<ide_diagnostics>d</ide_diagnostics>") == ""
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
def test_ide_notice_only_prompt_yields_no_piece_or_keyword(tmp_path: Path) -> None:
|
|
1134
|
+
home, repo = _home(tmp_path)
|
|
1135
|
+
t = _T(SID, str(repo))
|
|
1136
|
+
t.prompt(_ts(1), [{"type": "text", "text": _IDE_NOTICE}])
|
|
1137
|
+
t.call(_ts(2), "Read", {"file_path": str(repo / "src" / "parse.py")}, "t1")
|
|
1138
|
+
t.prompt(_ts(3), _IDE_NOTICE)
|
|
1139
|
+
t.call(_ts(4), "Read", {"file_path": str(repo / "src" / "parse.py")}, "t2")
|
|
1140
|
+
t.write(home / ".claude" / "projects" / "-work-repo" / f"{SID}.jsonl")
|
|
1141
|
+
pieces = _run(home)
|
|
1142
|
+
assert pieces == []
|
|
1143
|
+
for word in ("ide", "opened", "browser", "chrome"):
|
|
1144
|
+
assert word not in json.dumps(pieces)
|
|
@@ -480,6 +480,38 @@ def test_the_status_report_itself_never_carries_the_footer(tmp_path):
|
|
|
480
480
|
assert report.endswith(".")
|
|
481
481
|
|
|
482
482
|
|
|
483
|
+
def test_the_detail_argument_chooses_the_form_of_the_logs_lines(tmp_path, record_home):
|
|
484
|
+
"""job78: `detail` reaches the storage block through the tool, and only there.
|
|
485
|
+
|
|
486
|
+
Two usage ledgers: the default report folds them into one `usage/*` line, `detail: true`
|
|
487
|
+
prints one line each, and every other line of the report is the same in both.
|
|
488
|
+
"""
|
|
489
|
+
usage = record_home / ".bantamkit" / "usage"
|
|
490
|
+
usage.mkdir()
|
|
491
|
+
(usage / "aa.jsonl").write_bytes(b'{"ts":"1"}\n')
|
|
492
|
+
(usage / "bb.jsonl").write_bytes(b'{"ts":"2"}\nbroken\n')
|
|
493
|
+
server = build_server(make_memory(tmp_path))
|
|
494
|
+
|
|
495
|
+
async def scenario():
|
|
496
|
+
async with Client(server) as c:
|
|
497
|
+
plain = (await c.call_tool(STATUS_NAME, {})).content[0].text
|
|
498
|
+
off = (await c.call_tool(STATUS_NAME, {"detail": False})).content[0].text
|
|
499
|
+
on = (await c.call_tool(STATUS_NAME, {"detail": True})).content[0].text
|
|
500
|
+
return plain, off, on
|
|
501
|
+
|
|
502
|
+
plain, off, on = run(scenario())
|
|
503
|
+
assert plain == off
|
|
504
|
+
summary = [ln for ln in plain.split("\n") if ln.startswith("logs: ")]
|
|
505
|
+
detailed = [ln for ln in on.split("\n") if ln.startswith("logs: ")]
|
|
506
|
+
assert summary == ["logs: usage/* 2 files 29 (0 rotated, 1 skipped)"]
|
|
507
|
+
assert detailed == [
|
|
508
|
+
"logs: usage/aa 11 (0 rotated, 0 skipped)",
|
|
509
|
+
"logs: usage/bb 18 (0 rotated, 1 skipped)",
|
|
510
|
+
]
|
|
511
|
+
rest = [ln for ln in plain.split("\n") if not ln.startswith("logs: ")]
|
|
512
|
+
assert rest == [ln for ln in on.split("\n") if not ln.startswith("logs: ")]
|
|
513
|
+
|
|
514
|
+
|
|
483
515
|
def test_no_argument_value_reaches_the_status_report_or_the_footer(tmp_path):
|
|
484
516
|
"""NON-VACUITY 3: the absence, asserted POSITIVELY, against a sentinel.
|
|
485
517
|
|