bantamkit 0.38.1__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.
Files changed (318) hide show
  1. {bantamkit-0.38.1 → bantamkit-0.38.2}/PKG-INFO +3 -3
  2. {bantamkit-0.38.1 → bantamkit-0.38.2}/README.md +2 -2
  3. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/__init__.py +1 -1
  4. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillmine.py +153 -8
  5. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillmine.py +130 -0
  6. {bantamkit-0.38.1 → bantamkit-0.38.2}/.gitignore +0 -0
  7. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/contracts/default.yaml +0 -0
  8. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/manifest.yaml +0 -0
  9. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/HISTORY.md +0 -0
  10. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/README.md +0 -0
  11. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/docs/architecture.md +0 -0
  12. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/docs/runbook.md +0 -0
  13. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/issues/142-settlement-timeout.md +0 -0
  14. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/patches/0009-retry-budget.patch +0 -0
  15. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/__init__.py +0 -0
  16. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/config.py +0 -0
  17. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/errors.py +0 -0
  18. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/posting.py +0 -0
  19. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/registry.py +0 -0
  20. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/report.py +0 -0
  21. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/retry.py +0 -0
  22. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/settle.py +0 -0
  23. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/src/ledger/validate.py +0 -0
  24. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/tests/test_posting.py +0 -0
  25. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/repo/tests/test_settle.py +0 -0
  26. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-error-contract.yaml +0 -0
  27. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-handler-map.yaml +0 -0
  28. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-patch-before-after.yaml +0 -0
  29. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-retry-attempts.yaml +0 -0
  30. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-settlement-config.yaml +0 -0
  31. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-symbol-home.yaml +0 -0
  32. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-trace-blame.yaml +0 -0
  33. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/devteam/tasks/dt-unread-key.yaml +0 -0
  34. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-in-137.yaml +0 -0
  35. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-in-359.yaml +0 -0
  36. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-in-372.yaml +0 -0
  37. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-out-11764.yaml +0 -0
  38. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-out-4137.yaml +0 -0
  39. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-large-out-8022.yaml +0 -0
  40. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-small-137.yaml +0 -0
  41. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-small-261.yaml +0 -0
  42. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/document/tasks/doc-small-388.yaml +0 -0
  43. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/fixtures/.gitkeep +0 -0
  44. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/fixtures/catalog.json +0 -0
  45. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/perturbations/task-completion.yaml +0 -0
  46. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/.gitkeep +0 -0
  47. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/extract-contact.yaml +0 -0
  48. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/extract-invoice.yaml +0 -0
  49. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/extract-order.yaml +0 -0
  50. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/extract-schedule.yaml +0 -0
  51. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/extract-versions.yaml +0 -0
  52. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/nav-prod-port.yaml +0 -0
  53. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/nav-release-bundle.yaml +0 -0
  54. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-audit-retention.yaml +0 -0
  55. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-cache-ttl.yaml +0 -0
  56. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-db-port.yaml +0 -0
  57. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-deploy.yaml +0 -0
  58. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-env-endpoint.yaml +0 -0
  59. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-oncall-rotation.yaml +0 -0
  60. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-oncall.yaml +0 -0
  61. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-org-quota.yaml +0 -0
  62. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/recall-owner.yaml +0 -0
  63. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/shop-basket-total.yaml +0 -0
  64. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/shop-cheapest.yaml +0 -0
  65. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/shop-compare.yaml +0 -0
  66. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/shop-gadget-value.yaml +0 -0
  67. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/shop-stock-total.yaml +0 -0
  68. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/evals/tasks/shop-total.yaml +0 -0
  69. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/pricing/default.json +0 -0
  70. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/profiles/default.yaml +0 -0
  71. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/profiles/patient.yaml +0 -0
  72. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/rubrics/.gitkeep +0 -0
  73. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/rubrics/code-quality.yaml +0 -0
  74. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/rubrics/grounded-completion.yaml +0 -0
  75. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/rubrics/task-completion.yaml +0 -0
  76. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/schemas/shiftwork-checkpoint.json +0 -0
  77. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/skills/.gitkeep +0 -0
  78. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/skills/file-graph.md +0 -0
  79. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/skills/memory-queries.md +0 -0
  80. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/skills/memory.md +0 -0
  81. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/.gitkeep +0 -0
  82. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/bantamkit_read.json +0 -0
  83. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/bantamkit_status.json +0 -0
  84. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/build_identity.json +0 -0
  85. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/document_list.json +0 -0
  86. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/document_read.json +0 -0
  87. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/file_graph.json +0 -0
  88. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/memory_compact.json +0 -0
  89. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/memory_dream.json +0 -0
  90. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/memory_queries.json +0 -0
  91. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/memory_recall.json +0 -0
  92. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/memory_save.json +0 -0
  93. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/repo_map.json +0 -0
  94. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/shiftwork_clock_in.json +0 -0
  95. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/shiftwork_clock_out.json +0 -0
  96. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/shiftwork_draft.json +0 -0
  97. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/shiftwork_plan.json +0 -0
  98. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/shiftwork_status.json +0 -0
  99. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/skill_audit.json +0 -0
  100. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/token_ledger.json +0 -0
  101. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/validate_json.json +0 -0
  102. {bantamkit-0.38.1 → bantamkit-0.38.2}/_assets/tools/work_plan.json +0 -0
  103. {bantamkit-0.38.1 → bantamkit-0.38.2}/hatch_build.py +0 -0
  104. {bantamkit-0.38.1 → bantamkit-0.38.2}/pyproject.toml +0 -0
  105. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/agent.py +0 -0
  106. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/assets.py +0 -0
  107. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/budget.py +0 -0
  108. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/client.py +0 -0
  109. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/contract.py +0 -0
  110. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/criticreplay.py +0 -0
  111. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/critique.py +0 -0
  112. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/docmanifest.py +0 -0
  113. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/docread.py +0 -0
  114. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/evalrun.py +0 -0
  115. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/eventlog.py +0 -0
  116. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/experiment.py +0 -0
  117. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/filegraph.py +0 -0
  118. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/hookadapter.py +0 -0
  119. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/hostinstall.py +0 -0
  120. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/jobbudget.py +0 -0
  121. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/loopguard.py +0 -0
  122. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/mcpreport.py +0 -0
  123. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/mcpserver.py +0 -0
  124. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/mcpstdio.py +0 -0
  125. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/__init__.py +0 -0
  126. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/__main__.py +0 -0
  127. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/component.py +0 -0
  128. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/divergence.py +0 -0
  129. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/dream.py +0 -0
  130. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/layers.py +0 -0
  131. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/native.py +0 -0
  132. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/queries.py +0 -0
  133. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/retrieval.py +0 -0
  134. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/skills.py +0 -0
  135. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/store.py +0 -0
  136. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/memory/usage.py +0 -0
  137. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/pdfread.py +0 -0
  138. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/pricing.py +0 -0
  139. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/profile.py +0 -0
  140. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/repomap.py +0 -0
  141. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/selfupdate.py +0 -0
  142. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/shiftwork.py +0 -0
  143. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillaudit.py +0 -0
  144. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skilldraft.py +0 -0
  145. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillfile.py +0 -0
  146. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillhost.py +0 -0
  147. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillinstall.py +0 -0
  148. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillpromote.py +0 -0
  149. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillpropose.py +0 -0
  150. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/skillreport.py +0 -0
  151. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/statusline.py +0 -0
  152. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/storage/__init__.py +0 -0
  153. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/storage/coldindex.py +0 -0
  154. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/storage/hotindex.py +0 -0
  155. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/storage/lock.py +0 -0
  156. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/storage/logsegment.py +0 -0
  157. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/storage/retention.py +0 -0
  158. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/storage/tiers.py +0 -0
  159. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/structured.py +0 -0
  160. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/textutil.py +0 -0
  161. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/tokenledger.py +0 -0
  162. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/__init__.py +0 -0
  163. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/bashturn.py +0 -0
  164. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/context.py +0 -0
  165. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/graph.py +0 -0
  166. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/log.py +0 -0
  167. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/recallfile.py +0 -0
  168. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/refs.py +0 -0
  169. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/repo.py +0 -0
  170. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/touch/summary.py +0 -0
  171. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/updatecheck.py +0 -0
  172. {bantamkit-0.38.1 → bantamkit-0.38.2}/src/bantamkit/workplan.py +0 -0
  173. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/cli_exit_status_probe.py +0 -0
  174. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/conftest.py +0 -0
  175. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/bad-crc.docx +0 -0
  176. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/charref-4301-digits.html +0 -0
  177. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/charset-table.json +0 -0
  178. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/compression-method-9.docx +0 -0
  179. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/corrupt-deflate.docx +0 -0
  180. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/encrypted-member.docx +0 -0
  181. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/encrypted-mimetype.odt +0 -0
  182. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/eszett-cell-ref.xlsx +0 -0
  183. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/internal-dtd-entity.docx +0 -0
  184. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/rfc2231-charset.eml +0 -0
  185. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/rfc822-nested-twice.eml +0 -0
  186. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/unicode-digit-shared-string.xlsx +0 -0
  187. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/docread/x-uuencode.eml +0 -0
  188. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/f8404ab-perturbation-baseline.json +0 -0
  189. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/platform-assumption-baseline.json +0 -0
  190. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/data/served-tool-surface.json +0 -0
  191. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/docread_fixtures.py +0 -0
  192. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/perturbation_baseline_harness.py +0 -0
  193. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/rbp16_effect_probe.py +0 -0
  194. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/rbp18_payload_probe.py +0 -0
  195. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_adapter.py +0 -0
  196. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_agent.py +0 -0
  197. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_agreements.py +0 -0
  198. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_amendguard.py +0 -0
  199. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_assets.py +0 -0
  200. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_bantamkit_gitignore.py +0 -0
  201. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_bantamkit_read_tool.py +0 -0
  202. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_budget.py +0 -0
  203. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_build_identity.py +0 -0
  204. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_cli_oneshot.py +0 -0
  205. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_client.py +0 -0
  206. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_compaction_corpus_survey.py +0 -0
  207. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_conformance.py +0 -0
  208. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_conformance_harness_resilience.py +0 -0
  209. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_conformance_suite_table_gate.py +0 -0
  210. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_contract_fanout.py +0 -0
  211. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_contract_patterns.py +0 -0
  212. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_contract_refs.py +0 -0
  213. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_criticreplay.py +0 -0
  214. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_critique.py +0 -0
  215. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_doc_commands_gate.py +0 -0
  216. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_docread.py +0 -0
  217. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_docread_ceilings.py +0 -0
  218. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_document_manifest_parity.py +0 -0
  219. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_document_setup.py +0 -0
  220. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_document_tasks.py +0 -0
  221. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_document_tools.py +0 -0
  222. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_encoding_gate.py +0 -0
  223. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_evalrun.py +0 -0
  224. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_eventlog.py +0 -0
  225. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_experimentation.py +0 -0
  226. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_field_program_gates.py +0 -0
  227. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_field_programs.py +0 -0
  228. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_filegraph.py +0 -0
  229. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_follow_ups.py +0 -0
  230. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_hookadapter.py +0 -0
  231. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_hostinstall.py +0 -0
  232. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_hostinstall_hooks.py +0 -0
  233. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_install_shape.py +0 -0
  234. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_investigation.py +0 -0
  235. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_jobbudget.py +0 -0
  236. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_ladder_statistics.py +0 -0
  237. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_launcher_which.py +0 -0
  238. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_layers.py +0 -0
  239. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_loopguard.py +0 -0
  240. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_mcp_endpoint.py +0 -0
  241. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_mcp_request_guard.py +0 -0
  242. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_mcpdrift.py +0 -0
  243. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_mcpreport.py +0 -0
  244. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_mcpserver.py +0 -0
  245. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_mcpstdio_frames.py +0 -0
  246. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory.py +0 -0
  247. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_compact_tool.py +0 -0
  248. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_component.py +0 -0
  249. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_divergence.py +0 -0
  250. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_dream.py +0 -0
  251. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_layers.py +0 -0
  252. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_lifecycle.py +0 -0
  253. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_native.py +0 -0
  254. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_queries.py +0 -0
  255. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_retrieval.py +0 -0
  256. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_skills.py +0 -0
  257. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_store_tripwire.py +0 -0
  258. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_strings.py +0 -0
  259. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_memory_usage.py +0 -0
  260. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_mutmatrix.py +0 -0
  261. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_nativeexport.py +0 -0
  262. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_never_block.py +0 -0
  263. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_newline_gate.py +0 -0
  264. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_packaging.py +0 -0
  265. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_pdfread.py +0 -0
  266. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_pinharness_ledger.py +0 -0
  267. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_platform_assumption_gate.py +0 -0
  268. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_pricing.py +0 -0
  269. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_readme_separation.py +0 -0
  270. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_repo_map_tool.py +0 -0
  271. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_repomap.py +0 -0
  272. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_reviews.py +0 -0
  273. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_sdk_wire_text.py +0 -0
  274. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_selfupdate.py +0 -0
  275. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_served_tool_count_records.py +0 -0
  276. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_shiftwork.py +0 -0
  277. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_shiftwork_draft.py +0 -0
  278. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillaudit.py +0 -0
  279. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillaudit_roots.py +0 -0
  280. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skilldraft.py +0 -0
  281. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillfile.py +0 -0
  282. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillhost.py +0 -0
  283. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillimprove.py +0 -0
  284. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillimprove_signals.py +0 -0
  285. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillinstall.py +0 -0
  286. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillmine_daily.py +0 -0
  287. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillpromote.py +0 -0
  288. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillpropose.py +0 -0
  289. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_skillreport.py +0 -0
  290. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_status_surface.py +0 -0
  291. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_statusline.py +0 -0
  292. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_bar.py +0 -0
  293. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_coldindex.py +0 -0
  294. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_hotindex.py +0 -0
  295. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_log.py +0 -0
  296. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_log_consumers.py +0 -0
  297. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_recall_index.py +0 -0
  298. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_retention.py +0 -0
  299. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_storage_tiers.py +0 -0
  300. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_structured.py +0 -0
  301. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_thread_exception_gate.py +0 -0
  302. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_tokenledger.py +0 -0
  303. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_tool_annotations.py +0 -0
  304. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_tool_manifest.py +0 -0
  305. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touch_bashturn.py +0 -0
  306. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touch_context.py +0 -0
  307. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touch_graph.py +0 -0
  308. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touch_log.py +0 -0
  309. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touch_recall_file.py +0 -0
  310. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touch_refs.py +0 -0
  311. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touch_summary.py +0 -0
  312. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_touches.py +0 -0
  313. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_unit_agent_id.py +0 -0
  314. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_unit_results.py +0 -0
  315. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_unknown_arguments.py +0 -0
  316. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_updatecheck.py +0 -0
  317. {bantamkit-0.38.1 → bantamkit-0.38.2}/tests/test_version_agreement.py +0 -0
  318. {bantamkit-0.38.1 → 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.1
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.1" -d wheels
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.1"
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.1" -d wheels
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.1"
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
 
@@ -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.1"
32
+ __version__ = "0.38.2"
@@ -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 `tokenize_v2` keywords of the prompt (a term in more than half of the
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 tokenize_v2
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 every `tokenize_v2` term over one source's pieces, and their count."""
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(tokenize_v2(p.prompt)))
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 tokenize_v2(piece.prompt) if df.get(t, 0) * den <= n * num)
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")
@@ -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)
File without changes