usta 0.2.0__tar.gz → 0.2.1__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 (259) hide show
  1. usta-0.2.1/.github/workflows/publish.yml +33 -0
  2. {usta-0.2.0 → usta-0.2.1}/CHANGELOG.md +9 -0
  3. {usta-0.2.0 → usta-0.2.1}/PKG-INFO +4 -1
  4. {usta-0.2.0 → usta-0.2.1}/README.en.md +3 -0
  5. {usta-0.2.0 → usta-0.2.1}/README.md +3 -0
  6. {usta-0.2.0 → usta-0.2.1}/SECURITY.md +6 -0
  7. usta-0.2.1/docs/comparison.md +65 -0
  8. usta-0.2.1/docs/v0.2.1-raporu.md +63 -0
  9. {usta-0.2.0 → usta-0.2.1}/pyproject.toml +1 -1
  10. {usta-0.2.0 → usta-0.2.1}/src/usta/agent.py +23 -1
  11. {usta-0.2.0 → usta-0.2.1}/src/usta/api.py +2 -0
  12. {usta-0.2.0 → usta-0.2.1}/src/usta/bench.py +9 -0
  13. {usta-0.2.0 → usta-0.2.1}/src/usta/cli.py +18 -0
  14. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/runner.py +2 -0
  15. {usta-0.2.0 → usta-0.2.1}/src/usta/mcp_runtime.py +14 -2
  16. {usta-0.2.0 → usta-0.2.1}/src/usta/models.py +15 -3
  17. usta-0.2.1/src/usta/taint.py +95 -0
  18. {usta-0.2.0 → usta-0.2.1}/src/usta/tasks.py +15 -3
  19. {usta-0.2.0 → usta-0.2.1}/src/usta/tools.py +6 -0
  20. {usta-0.2.0 → usta-0.2.1}/src/usta/web/app.css +49 -0
  21. {usta-0.2.0 → usta-0.2.1}/src/usta/web/app.js +63 -10
  22. {usta-0.2.0 → usta-0.2.1}/src/usta/web/index.html +39 -0
  23. {usta-0.2.0 → usta-0.2.1}/src/usta/web/logic.js +1 -1
  24. usta-0.2.1/src/usta/web/office-logic.js +510 -0
  25. usta-0.2.1/src/usta/web/office.js +1660 -0
  26. {usta-0.2.0 → usta-0.2.1}/tests/test_benchdiag.py +3 -1
  27. {usta-0.2.0 → usta-0.2.1}/tests/test_mcp_runtime.py +3 -0
  28. {usta-0.2.0 → usta-0.2.1}/tests/test_models.py +17 -4
  29. usta-0.2.1/tests/test_taint.py +107 -0
  30. {usta-0.2.0 → usta-0.2.1}/tests/test_tools.py +9 -0
  31. {usta-0.2.0 → usta-0.2.1}/tests/test_web.py +78 -1
  32. usta-0.2.1/tests/web/office-draw.test.cjs +386 -0
  33. usta-0.2.1/tests/web/office.test.cjs +498 -0
  34. {usta-0.2.0 → usta-0.2.1}/.github/workflows/ci.yml +0 -0
  35. {usta-0.2.0 → usta-0.2.1}/.gitignore +0 -0
  36. {usta-0.2.0 → usta-0.2.1}/CONTRIBUTING.md +0 -0
  37. {usta-0.2.0 → usta-0.2.1}/LICENSE +0 -0
  38. {usta-0.2.0 → usta-0.2.1}/Makefile +0 -0
  39. {usta-0.2.0 → usta-0.2.1}/docs/design.md +0 -0
  40. {usta-0.2.0 → usta-0.2.1}/docs/implementation-audit.md +0 -0
  41. {usta-0.2.0 → usta-0.2.1}/docs/reliability-lab.md +0 -0
  42. {usta-0.2.0 → usta-0.2.1}/docs/v0.2.0-raporu.md +0 -0
  43. {usta-0.2.0 → usta-0.2.1}/requirements-dev.lock +0 -0
  44. {usta-0.2.0 → usta-0.2.1}/requirements.lock +0 -0
  45. {usta-0.2.0 → usta-0.2.1}/src/usta/__init__.py +0 -0
  46. {usta-0.2.0 → usta-0.2.1}/src/usta/apis.py +0 -0
  47. {usta-0.2.0 → usta-0.2.1}/src/usta/attachments.py +0 -0
  48. {usta-0.2.0 → usta-0.2.1}/src/usta/audit.py +0 -0
  49. {usta-0.2.0 → usta-0.2.1}/src/usta/auth.py +0 -0
  50. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/check/test_todo.py +0 -0
  51. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/repo/test_todo.py +0 -0
  52. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/repo/todo.py +0 -0
  53. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/task.json +0 -0
  54. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cli-args/check/test_cli.py +0 -0
  55. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cli-args/repo/cli.py +0 -0
  56. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cli-args/task.json +0 -0
  57. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/check/test_cart.py +0 -0
  58. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/check/test_pricing.py +0 -0
  59. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/cart.py +0 -0
  60. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/pricing.py +0 -0
  61. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/test_cart.py +0 -0
  62. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/test_pricing.py +0 -0
  63. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/task.json +0 -0
  64. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/csv-quotes/check/test_parse.py +0 -0
  65. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/csv-quotes/repo/parse.py +0 -0
  66. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/csv-quotes/task.json +0 -0
  67. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/deep-merge/check/test_config.py +0 -0
  68. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/deep-merge/repo/config.py +0 -0
  69. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/deep-merge/task.json +0 -0
  70. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/expr-eval/check/test_calc.py +0 -0
  71. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/expr-eval/repo/calc.py +0 -0
  72. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/expr-eval/task.json +0 -0
  73. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/implement-slugify/check/test_slug.py +0 -0
  74. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/implement-slugify/repo/slug.py +0 -0
  75. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/implement-slugify/task.json +0 -0
  76. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/interval-merge/check/test_intervals.py +0 -0
  77. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/interval-merge/repo/intervals.py +0 -0
  78. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/interval-merge/task.json +0 -0
  79. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/check/test_dates.py +0 -0
  80. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/repo/dates.py +0 -0
  81. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/repo/test_dates.py +0 -0
  82. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/task.json +0 -0
  83. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/lru-cache/check/test_lru.py +0 -0
  84. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/lru-cache/repo/lru.py +0 -0
  85. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/lru-cache/task.json +0 -0
  86. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/check/test_money.py +0 -0
  87. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/repo/money.py +0 -0
  88. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/repo/test_money.py +0 -0
  89. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/task.json +0 -0
  90. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/check/test_users.py +0 -0
  91. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/api.py +0 -0
  92. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/greeting.py +0 -0
  93. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/report.py +0 -0
  94. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/test_users.py +0 -0
  95. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/users.py +0 -0
  96. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/task.json +0 -0
  97. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/check/test_stats.py +0 -0
  98. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/repo/stats.py +0 -0
  99. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/repo/test_stats.py +0 -0
  100. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/task.json +0 -0
  101. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/text-wrap/check/test_wrap.py +0 -0
  102. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/text-wrap/repo/wrap.py +0 -0
  103. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/text-wrap/task.json +0 -0
  104. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/check/test_find.py +0 -0
  105. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/repo/find.py +0 -0
  106. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/repo/test_find.py +0 -0
  107. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/task.json +0 -0
  108. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/check/test_words.py +0 -0
  109. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/repo/test_words.py +0 -0
  110. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/repo/words.py +0 -0
  111. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/task.json +0 -0
  112. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/check/test_greet.py +0 -0
  113. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/repo/greet.py +0 -0
  114. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/repo/test_greet.py +0 -0
  115. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/task.json +0 -0
  116. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/check/test_parity.py +0 -0
  117. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/repo/parity.py +0 -0
  118. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/repo/test_parity.py +0 -0
  119. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/task.json +0 -0
  120. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/check/test_nums.py +0 -0
  121. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/repo/nums.py +0 -0
  122. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/repo/test_nums.py +0 -0
  123. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/task.json +0 -0
  124. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/check/test_names.py +0 -0
  125. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/repo/names.py +0 -0
  126. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/repo/test_names.py +0 -0
  127. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/task.json +0 -0
  128. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/check/test_pct.py +0 -0
  129. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/repo/pct.py +0 -0
  130. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/repo/test_pct.py +0 -0
  131. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/task.json +0 -0
  132. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/check/test_avg.py +0 -0
  133. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/repo/avg.py +0 -0
  134. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/repo/test_avg.py +0 -0
  135. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/task.json +0 -0
  136. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/check/test_text.py +0 -0
  137. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/repo/test_text.py +0 -0
  138. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/repo/text.py +0 -0
  139. {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/task.json +0 -0
  140. {usta-0.2.0 → usta-0.2.1}/src/usta/breaker.py +0 -0
  141. {usta-0.2.0 → usta-0.2.1}/src/usta/capability.py +0 -0
  142. {usta-0.2.0 → usta-0.2.1}/src/usta/context.py +0 -0
  143. {usta-0.2.0 → usta-0.2.1}/src/usta/fetch.py +0 -0
  144. {usta-0.2.0 → usta-0.2.1}/src/usta/fetch_inner.py +0 -0
  145. {usta-0.2.0 → usta-0.2.1}/src/usta/filetree.py +0 -0
  146. {usta-0.2.0 → usta-0.2.1}/src/usta/jsonproto.py +0 -0
  147. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/__init__.py +0 -0
  148. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/env.py +0 -0
  149. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/evaluator.py +0 -0
  150. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/metrics.py +0 -0
  151. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/report.py +0 -0
  152. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/scenario.py +0 -0
  153. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/scenarios/v1.jsonl +0 -0
  154. {usta-0.2.0 → usta-0.2.1}/src/usta/lab/scripted.py +0 -0
  155. {usta-0.2.0 → usta-0.2.1}/src/usta/mcp.py +0 -0
  156. {usta-0.2.0 → usta-0.2.1}/src/usta/mcp_tools.py +0 -0
  157. {usta-0.2.0 → usta-0.2.1}/src/usta/mcpcatalog.py +0 -0
  158. {usta-0.2.0 → usta-0.2.1}/src/usta/mcpconfig.py +0 -0
  159. {usta-0.2.0 → usta-0.2.1}/src/usta/memory.py +0 -0
  160. {usta-0.2.0 → usta-0.2.1}/src/usta/modelctl.py +0 -0
  161. {usta-0.2.0 → usta-0.2.1}/src/usta/modelinfo.py +0 -0
  162. {usta-0.2.0 → usta-0.2.1}/src/usta/outline.py +0 -0
  163. {usta-0.2.0 → usta-0.2.1}/src/usta/pdf_inner.py +0 -0
  164. {usta-0.2.0 → usta-0.2.1}/src/usta/phases.py +0 -0
  165. {usta-0.2.0 → usta-0.2.1}/src/usta/policy.py +0 -0
  166. {usta-0.2.0 → usta-0.2.1}/src/usta/portal.py +0 -0
  167. {usta-0.2.0 → usta-0.2.1}/src/usta/profile.py +0 -0
  168. {usta-0.2.0 → usta-0.2.1}/src/usta/projectcfg.py +0 -0
  169. {usta-0.2.0 → usta-0.2.1}/src/usta/projects.py +0 -0
  170. {usta-0.2.0 → usta-0.2.1}/src/usta/proxy.py +0 -0
  171. {usta-0.2.0 → usta-0.2.1}/src/usta/repl.py +0 -0
  172. {usta-0.2.0 → usta-0.2.1}/src/usta/rescue.py +0 -0
  173. {usta-0.2.0 → usta-0.2.1}/src/usta/sandbox.py +0 -0
  174. {usta-0.2.0 → usta-0.2.1}/src/usta/sessions.py +0 -0
  175. {usta-0.2.0 → usta-0.2.1}/src/usta/settings.py +0 -0
  176. {usta-0.2.0 → usta-0.2.1}/src/usta/skills.py +0 -0
  177. {usta-0.2.0 → usta-0.2.1}/src/usta/symbols.py +0 -0
  178. {usta-0.2.0 → usta-0.2.1}/src/usta/taxonomy.py +0 -0
  179. {usta-0.2.0 → usta-0.2.1}/src/usta/textproto.py +0 -0
  180. {usta-0.2.0 → usta-0.2.1}/src/usta/theme.py +0 -0
  181. {usta-0.2.0 → usta-0.2.1}/src/usta/verify.py +0 -0
  182. {usta-0.2.0 → usta-0.2.1}/src/usta/workspace.py +0 -0
  183. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/add-feature/todo.py +0 -0
  184. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/cli-args/cli.py +0 -0
  185. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/cross-module-bug/cart.py +0 -0
  186. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/csv-quotes/parse.py +0 -0
  187. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/deep-merge/config.py +0 -0
  188. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/expr-eval/calc.py +0 -0
  189. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/implement-slugify/slug.py +0 -0
  190. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/interval-merge/intervals.py +0 -0
  191. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/leap-dates/dates.py +0 -0
  192. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/lru-cache/lru.py +0 -0
  193. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/money-rounding/money.py +0 -0
  194. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/api.py +0 -0
  195. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/greeting.py +0 -0
  196. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/report.py +0 -0
  197. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/users.py +0 -0
  198. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/off-by-one/stats.py +0 -0
  199. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/text-wrap/wrap.py +0 -0
  200. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-case-insensitive/find.py +0 -0
  201. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-default-sep/words.py +0 -0
  202. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-greeting/greet.py +0 -0
  203. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-is-even/parity.py +0 -0
  204. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-missing-return/nums.py +0 -0
  205. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-strip-names/names.py +0 -0
  206. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-swap-args/pct.py +0 -0
  207. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-wrong-operator/avg.py +0 -0
  208. {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/word-count-fix/text.py +0 -0
  209. {usta-0.2.0 → usta-0.2.1}/tests/conftest.py +0 -0
  210. {usta-0.2.0 → usta-0.2.1}/tests/fake_endpoint.py +0 -0
  211. {usta-0.2.0 → usta-0.2.1}/tests/fake_mcp.py +0 -0
  212. {usta-0.2.0 → usta-0.2.1}/tests/pdfutil.py +0 -0
  213. {usta-0.2.0 → usta-0.2.1}/tests/probe_mcp_src.py +0 -0
  214. {usta-0.2.0 → usta-0.2.1}/tests/test_agent.py +0 -0
  215. {usta-0.2.0 → usta-0.2.1}/tests/test_api.py +0 -0
  216. {usta-0.2.0 → usta-0.2.1}/tests/test_api_mcp.py +0 -0
  217. {usta-0.2.0 → usta-0.2.1}/tests/test_apis.py +0 -0
  218. {usta-0.2.0 → usta-0.2.1}/tests/test_audit.py +0 -0
  219. {usta-0.2.0 → usta-0.2.1}/tests/test_bench.py +0 -0
  220. {usta-0.2.0 → usta-0.2.1}/tests/test_breaker.py +0 -0
  221. {usta-0.2.0 → usta-0.2.1}/tests/test_capability.py +0 -0
  222. {usta-0.2.0 → usta-0.2.1}/tests/test_chat_cli.py +0 -0
  223. {usta-0.2.0 → usta-0.2.1}/tests/test_cli.py +0 -0
  224. {usta-0.2.0 → usta-0.2.1}/tests/test_context.py +0 -0
  225. {usta-0.2.0 → usta-0.2.1}/tests/test_fetch.py +0 -0
  226. {usta-0.2.0 → usta-0.2.1}/tests/test_files_skills.py +0 -0
  227. {usta-0.2.0 → usta-0.2.1}/tests/test_hardening2.py +0 -0
  228. {usta-0.2.0 → usta-0.2.1}/tests/test_hardening3.py +0 -0
  229. {usta-0.2.0 → usta-0.2.1}/tests/test_jsonproto.py +0 -0
  230. {usta-0.2.0 → usta-0.2.1}/tests/test_lab.py +0 -0
  231. {usta-0.2.0 → usta-0.2.1}/tests/test_mcp.py +0 -0
  232. {usta-0.2.0 → usta-0.2.1}/tests/test_mcp_tools.py +0 -0
  233. {usta-0.2.0 → usta-0.2.1}/tests/test_mcpconfig.py +0 -0
  234. {usta-0.2.0 → usta-0.2.1}/tests/test_memory.py +0 -0
  235. {usta-0.2.0 → usta-0.2.1}/tests/test_netproxy.py +0 -0
  236. {usta-0.2.0 → usta-0.2.1}/tests/test_no_secrets.py +0 -0
  237. {usta-0.2.0 → usta-0.2.1}/tests/test_outline.py +0 -0
  238. {usta-0.2.0 → usta-0.2.1}/tests/test_pdf.py +0 -0
  239. {usta-0.2.0 → usta-0.2.1}/tests/test_phased.py +0 -0
  240. {usta-0.2.0 → usta-0.2.1}/tests/test_phases.py +0 -0
  241. {usta-0.2.0 → usta-0.2.1}/tests/test_policy.py +0 -0
  242. {usta-0.2.0 → usta-0.2.1}/tests/test_portal.py +0 -0
  243. {usta-0.2.0 → usta-0.2.1}/tests/test_profile.py +0 -0
  244. {usta-0.2.0 → usta-0.2.1}/tests/test_projectcfg.py +0 -0
  245. {usta-0.2.0 → usta-0.2.1}/tests/test_projects.py +0 -0
  246. {usta-0.2.0 → usta-0.2.1}/tests/test_proxy.py +0 -0
  247. {usta-0.2.0 → usta-0.2.1}/tests/test_repl.py +0 -0
  248. {usta-0.2.0 → usta-0.2.1}/tests/test_rescue.py +0 -0
  249. {usta-0.2.0 → usta-0.2.1}/tests/test_sandbox.py +0 -0
  250. {usta-0.2.0 → usta-0.2.1}/tests/test_sessions.py +0 -0
  251. {usta-0.2.0 → usta-0.2.1}/tests/test_steer_model.py +0 -0
  252. {usta-0.2.0 → usta-0.2.1}/tests/test_supply_chain.py +0 -0
  253. {usta-0.2.0 → usta-0.2.1}/tests/test_symbols.py +0 -0
  254. {usta-0.2.0 → usta-0.2.1}/tests/test_tasks.py +0 -0
  255. {usta-0.2.0 → usta-0.2.1}/tests/test_textproto.py +0 -0
  256. {usta-0.2.0 → usta-0.2.1}/tests/test_theme.py +0 -0
  257. {usta-0.2.0 → usta-0.2.1}/tests/test_verify.py +0 -0
  258. {usta-0.2.0 → usta-0.2.1}/tests/test_workspace.py +0 -0
  259. {usta-0.2.0 → usta-0.2.1}/tests/web/logic.test.cjs +0 -0
@@ -0,0 +1,33 @@
1
+ # PyPI'ye yayın: yalnızca bir GitHub release yayımlanınca çalışır. Parola/jeton yok: PyPI "Trusted Publisher"
2
+ # (OIDC) olarak bu depo + workflow + `pypi` ortamı tanımlanmadıkça yayın başarısız olur (bilinçli güvenlik).
3
+ name: publish
4
+ on:
5
+ release:
6
+ types: [published]
7
+ permissions:
8
+ contents: read
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+ - run: python -m pip install build && python -m build
18
+ - uses: actions/upload-artifact@v4
19
+ with:
20
+ name: dist
21
+ path: dist/
22
+ pypi:
23
+ needs: build
24
+ runs-on: ubuntu-latest
25
+ environment: pypi
26
+ permissions:
27
+ id-token: write
28
+ steps:
29
+ - uses: actions/download-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -1,5 +1,14 @@
1
1
  # Değişiklik günlüğü
2
2
 
3
+ ## 0.2.1 — Ofis ve kaynak izi kapısı
4
+
5
+ Ölçümler ve sınırlar: [docs/v0.2.1-raporu.md](docs/v0.2.1-raporu.md). Rakiplerle dürüst karşılaştırma: [docs/comparison.md](docs/comparison.md).
6
+
7
+ - **Kaynak izi kapısı (`taint.py`), varsayılan AÇIK:** okunan içerikten türeyen yazma/komut hedefleri onay ister (oto modda reddedilir). 7B Lab'da gerçekleşen yetkisiz eylem 6→2/40; yanlış alarm gözlenmedi (7B Lab'da 40, DeepSeek Lab'da 44, bench'te 96 meşru koşu). Kanıt yönlüdür, istatistiksel olarak güçlü değildir (aralıklar örtüşüyor). Kapatmak: `--no-taint-gate` / `USTA_TAINT_GATE=0`. Bench'te `X-TAINT` kolu.
8
+ - Model çıktısı sınırında kesilen/bozuk araç argümanı artık görevi öldürmez: araç hatası olarak modele döner ("daha küçük parçalarla dene"). Daha önce `araç çağrısı çözümlenemedi` ile görev düşüyordu.
9
+ - Portal: **Ofis** sekmesi (piksel-sanat ofis animasyonu). Ajan olaylarını (araç çağrısı, onay isteği, bitiş) canlıda ve geçmiş oturum tekrarında gösterir; bağımlılık ve harici görsel yok; sayaçlar yalnız gerçek olaylardan gelir. Varsayılan sekme değişmedi (Akış).
10
+ - v0.2.0 öncesi (imajsız) MCP kayıtları için hata mesajı düzeltildi: "docker pull None" yerine `usta mcp add --image` ile yeniden kaydetme yönergesi.
11
+
3
12
  ## 0.2.0 — güvenilirlik ve ölçüm
4
13
 
5
14
  Sonuçlar, maliyetler ve doğrulanamayan hipotezler: [docs/v0.2.0-raporu.md](docs/v0.2.0-raporu.md).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: usta
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Genel amaçlı, açık kaynak yapay zekâ ajanı
5
5
  Author: Uğur Can Leba
6
6
  License: Apache-2.0
@@ -46,6 +46,8 @@ kılan harness** (araç çağrısı, bağlam yönetimi, doğrulama döngüsü, d
46
46
 
47
47
  ## Hızlı başlangıç
48
48
 
49
+ PyPI'den: `uv tool install usta` (ya da `pip install usta`). Kaynaktan:
50
+
49
51
  ```bash
50
52
  git clone https://github.com/SoundByGAIA/usta.git usta && cd usta
51
53
  uv venv && uv pip install -e .
@@ -116,6 +118,7 @@ hangisinin ne işe yaradığı ve maliyeti [docs/v0.2.0-raporu.md](docs/v0.2.0-r
116
118
  | `usta probe --deep` · `--protocol auto` | Model yetenek profili: protokol başına 9 sonda (uçtan uca ajan görevleri dahil), model revizyonu + yapılandırmaya bağlı kalıcı profil; `auto` koşu başında bir kez çözülür ve sabitlenir |
117
119
  | `usta mcp add --image … / prepare / test` | MCP sunucuları varsayılan olarak **yalıtılmış konteynerde**; paketler `prepare` ile bir kez indirilip sabitlenir |
118
120
  | `usta sandbox pin / status` | Sandbox imajının kimliğini sabitler; değişirse çalıştırma reddedilir |
121
+ | `--no-taint-gate` | Kaynak izi kapısını kapatır (varsayılan açık): okunan içerikten türeyen yazma/komut hedefleri onay ister, oto modda reddedilir |
119
122
  | `--symbol-tools` (deneysel) | Tree-sitter ile sembol araçları (`pip install 'usta[symbols]'`): `related_files`, `find_symbol`, `symbol_context` |
120
123
 
121
124
  Yürütme aşamaları (DISCOVER → PLAN → EDIT → VERIFY → DIAGNOSE → REPAIR) **gözlemlenir** ve denetim kaydına yazılır; her görev bir terminal
@@ -14,6 +14,8 @@ A feature becomes the default only if a measurement shows it helps.
14
14
 
15
15
  ## Quick start
16
16
 
17
+ From PyPI: `uv tool install usta` (or `pip install usta`). From source:
18
+
17
19
  ```bash
18
20
  git clone https://github.com/SoundByGAIA/usta.git usta && cd usta
19
21
  uv venv && uv pip install -e .
@@ -85,6 +87,7 @@ measurement infrastructure. It is **not proven** to raise task success on small
85
87
  | `usta probe --deep` · `--protocol auto` | Per-protocol capability profile (incl. end-to-end agent tasks), keyed by model revision + server config; `auto` resolves once at start and is pinned for the run |
86
88
  | `usta mcp add --image … / prepare / test` | MCP servers run in an **isolated container** by default; packages are fetched once with `prepare` and pinned |
87
89
  | `usta sandbox pin / status` | Pins the sandbox image id; a changed image is refused |
90
+ | `--no-taint-gate` | Turns off the provenance gate (on by default): write/command targets derived from untrusted content ask for approval, denied in auto mode |
88
91
  | `--symbol-tools` (experimental) | Tree-sitter symbol tools (`pip install 'usta[symbols]'`) |
89
92
 
90
93
  Execution phases (DISCOVER → PLAN → EDIT → VERIFY → DIAGNOSE → REPAIR) are **observed** and written to the audit log; every task ends in a terminal
@@ -14,6 +14,8 @@ kılan harness** (araç çağrısı, bağlam yönetimi, doğrulama döngüsü, d
14
14
 
15
15
  ## Hızlı başlangıç
16
16
 
17
+ PyPI'den: `uv tool install usta` (ya da `pip install usta`). Kaynaktan:
18
+
17
19
  ```bash
18
20
  git clone https://github.com/SoundByGAIA/usta.git usta && cd usta
19
21
  uv venv && uv pip install -e .
@@ -84,6 +86,7 @@ hangisinin ne işe yaradığı ve maliyeti [docs/v0.2.0-raporu.md](docs/v0.2.0-r
84
86
  | `usta probe --deep` · `--protocol auto` | Model yetenek profili: protokol başına 9 sonda (uçtan uca ajan görevleri dahil), model revizyonu + yapılandırmaya bağlı kalıcı profil; `auto` koşu başında bir kez çözülür ve sabitlenir |
85
87
  | `usta mcp add --image … / prepare / test` | MCP sunucuları varsayılan olarak **yalıtılmış konteynerde**; paketler `prepare` ile bir kez indirilip sabitlenir |
86
88
  | `usta sandbox pin / status` | Sandbox imajının kimliğini sabitler; değişirse çalıştırma reddedilir |
89
+ | `--no-taint-gate` | Kaynak izi kapısını kapatır (varsayılan açık): okunan içerikten türeyen yazma/komut hedefleri onay ister, oto modda reddedilir |
87
90
  | `--symbol-tools` (deneysel) | Tree-sitter ile sembol araçları (`pip install 'usta[symbols]'`): `related_files`, `find_symbol`, `symbol_context` |
88
91
 
89
92
  Yürütme aşamaları (DISCOVER → PLAN → EDIT → VERIFY → DIAGNOSE → REPAIR) **gözlemlenir** ve denetim kaydına yazılır; her görev bir terminal
@@ -40,6 +40,12 @@ Ayrıntı: [docs/design.md](docs/design.md) §5. Özet ve **bilinçli sınırlar
40
40
  - **Tedarik zinciri (v0.2.0):** Python bağımlılıkları hash'li, çok platformlu kilit dosyalarıyla (`requirements*.lock`) kurulur ve CI'da `pip-audit` kilidi tarar.
41
41
  Sandbox ve MCP imajları etiketle seçilir; `usta sandbox pin` / `usta mcp prepare` yerel imaj kimliğini sabitler ve her çalıştırmada doğrular
42
42
  (etiket başka içeriğe işaret ederse çalışmaz). Usta imajı kendiliğinden çekmez; ilk kez çekilen imajın güvenilirliği sizin kararınızdır.
43
+ - **Dolaylı komut enjeksiyonu (v0.2.1, kısmi savunma):** okunan dosya/sayfa/MCP çıktısındaki gizli talimatlar modeli yönlendirebilir. Kaynak izi
44
+ kapısı (`taint.py`, varsayılan açık; `--no-taint-gate` ile kapanır) şunu yakalar: `write_file`/`edit_file` yolu ya da `run` komutundaki yol/adres
45
+ parçası, güvenilmeyen kaynaklardan (dosya içeriği, `fetch_url`, `web_search`, MCP) gelen metinde geçiyor ve sizin isteminizde geçmiyorsa eylem
46
+ onay ister (manuel modda gerekçeyle sorulur, oto modda reddedilir). **Sınırlar:** komut çıktısındaki talimatlar kapsam dışıdır (test izlerindeki
47
+ meşru yollar sürekli işaretlenmesin diye); model enjekte metni yazdığı dosyanın *içeriğine* kopyalarsa yakalanmaz; sezgiseldir, kesin koruma
48
+ değildir. Ölçüm: [docs/v0.2.1-raporu.md](docs/v0.2.1-raporu.md).
43
49
  - **Sızdırma kanalları** (belgeli): izinli alan adlarına HTTPS; model uç noktanız (istemler ve açılan dosya içerikleri o API'ye gider);
44
50
  önizlemede ajanın yazdığı sayfanın kendi kendine başka adrese yönlenmesi (iframe sandbox'ı bunu engelleyemez; `connect-src`, `form-action` ve
45
51
  `allow-top-navigation` kapalıdır).
@@ -0,0 +1,65 @@
1
+ # Usta ve benzer projeler: dürüst karşılaştırma
2
+
3
+ Tarih: 2026-11-07. Kaynak: her projenin kendi GitHub deposu ve README'si. **Bu karşılaştırma bağımsız bir
4
+ ölçüm değildir:** aşağıda "belgesinde yazıyor" denen şeyleri çalıştırıp doğrulamadım; yalnız Usta için kendi
5
+ ölçümlerimiz var (bkz. `docs/v0.2.0-raporu.md`). Yıldız sayıları o günkü değerlerdir ve kalite göstergesi değildir.
6
+
7
+ ## Büyüklük (GitHub, 2026-11-07)
8
+
9
+ | Proje | Lisans | Yıldız | Dil |
10
+ |---|---|---|---|
11
+ | opencode (anomalyco) | MIT | ~225 bin | TypeScript |
12
+ | Claude Code (anthropics) | belirtilmemiş | ~155 bin | TypeScript |
13
+ | Codex (openai) | Apache-2.0 | ~134 bin | Rust |
14
+ | OpenHands | MIT | ~93 bin | TypeScript |
15
+ | Cline | Apache-2.0 | ~71 bin | TypeScript |
16
+ | goose | Apache-2.0 | ~56 bin | Rust |
17
+ | Aider | Apache-2.0 | ~50 bin | Python |
18
+ | **Usta** | Apache-2.0 | **3** | Python |
19
+
20
+ Usta bu alanda çok küçük bir projedir. Özellik genişliği, IDE entegrasyonu, topluluk ve cilada bu projelerle
21
+ yarışmıyoruz ve yarışıyormuş gibi davranmıyoruz.
22
+
23
+ ## Belgelerinde yazanlar (özet, doğrulanmadı)
24
+
25
+ | Proje | Belgede öne çıkan |
26
+ |---|---|
27
+ | Aider | Terminalde eşli programlama; Claude, DeepSeek, OpenAI ve yerel modellerle çalışır; kendisini kıyas ölçütü olarak anıyor |
28
+ | OpenHands | Yerelde, Docker'da ya da sanal makinelerde çalıştırma; Docker sandbox seçeneği |
29
+ | Cline | İnsan onaylı araç kullanımı; Ollama/LM Studio ile yerel model; MCP; çoklu ajan ekipleri |
30
+ | goose | 15+ sağlayıcı (Ollama dahil), MCP üzerinden 70+ eklenti |
31
+ | opencode | Bash komutlarından önce izin ister; alt ajanlar |
32
+ | Claude Code, Codex | Terminalde çalışan kod ajanları (README'lerinden güvenilir bir özellik özeti çıkaramadım; ayrıntıya girmiyoruz) |
33
+
34
+ ## Usta'nın farklı olduğu yer (kendi ölçümlerimizle desteklenen)
35
+
36
+ 1. **Ölçüm altyapısı ürünün parçası.** `usta bench` (ajanın göremediği gizli testler, başarısızlık sınıflandırması,
37
+ iz kaydı ve tekrar oynatma, eşleştirilmiş kol karşılaştırması, güven aralıkları, altyapı hatası ayrımı) ve
38
+ `usta lab` (24 saldırı/hata senaryosu, eşleştirilmiş temiz/saldırılı koşu, saldırı başarı oranı, yanlış ret).
39
+ Rakiplerin benzer bir dahili güvenilirlik laboratuvarı olup olmadığını bilmiyoruz; yalnız README'lerini anahtar kelimeyle taradık ve böyle bir şeyin öne çıkarıldığını görmedik (bu bir yokluk kanıtı değildir).
40
+ 2. **Dürüst sonuç ilkesi.** "Özellik ancak ölçümde fayda gösterirse varsayılan olur." 7B'de yeni özelliklerin başarıyı
41
+ artırmadığını yayınladık (v0.2.0 raporu).
42
+ 3. **Varsayılan yalıtım.** MCP sunucuları yalıtılmış Docker konteynerinde (salt okunur kök, `cap-drop ALL`, ağ yok ya da
43
+ izinli proxy); ana makinede çalıştırma ancak açık bir uyumluluk modudur.
44
+ 4. **Küçük, bağımlılığı az çekirdek.** Python, saf JS portal; yerel küçük modellerle çalışacak şekilde ölçülüp ayarlanıyor.
45
+
46
+ ## Usta'nın zayıf olduğu yer
47
+
48
+ - 3 yıldız, tek geliştirici, kullanıcı geri bildirimi çok az.
49
+ - IDE eklentisi, masaüstü uygulaması, çoklu oturum/çoklu ajan orkestrasyonu yok.
50
+ - 7B yerel modelde bench başarısı düşük ve deneysel özelliklerin faydası kanıtlanmadı; büyük yerel modeller (12B+) ölçülmedi.
51
+ - Araç çıktısı/README enjeksiyonuna karşı 7B'de koruma tam değil (bkz. v0.2.0 raporu; `--taint-gate` deneysel).
52
+ - Sağlayıcı kapsamı sınırlı test edildi (yerel Ollama ve DeepSeek).
53
+
54
+ ## Piksel ofis görselleştiricileri
55
+
56
+ Aynı fikrin başka uygulamaları: Claude-Office (MIT, izometrik, çok oturumlu, masaüstü uygulaması), agent-virtual-office
57
+ (MIT, çok ajanlı, sahne etkileşimleri), pixtuoid (terminal içi), agent-office (çok ajanlı, sohbet ve görev atama).
58
+ Usta'nın **Ofis** sekmesi bunlardan ilham aldı ama kodu/görseli kopyalamadı; bağımlılıksızdır, özgün sprite'lar kodla
59
+ çizilir. Farkları: tek ajan odaklıdır (sahte kalabalık yok), her sayaç ve efekt gerçek olaylara bağlıdır, onay doğrudan
60
+ ofisten verilebilir, geçmiş oturum zaman çubuğuyla gezilir. Eksikleri: çok oturum yok, ses yok, masaüstü uygulaması yok.
61
+
62
+ ## Sonuç
63
+
64
+ Usta "daha çok özellik" yarışına girmiyor. İddiası dar: **küçük ve yerel modellerle çalışan, ölçülebilir ve
65
+ denetlenebilir bir harness.** Bu iddia ancak ölçümle yaşar; ölçüm gösterdiği sürece yazarız, göstermediğinde de yazarız.
@@ -0,0 +1,63 @@
1
+ # Usta v0.2.1 — rapor
2
+
3
+ v0.2.1; Ofis sekmesi, kaynak izi kapısı ve iki düzeltme. Tüm sayılar bu depodaki ölçüm çıktılarından gelir.
4
+ Başarısız ve belirsiz sonuçlar dahildir. Ölçüm ortamı: 7B için yerel Ollama (`usta-qc7b-8k`, JSON protokolü), güçlü model
5
+ için DeepSeek (`deepseek-chat`, native protokol).
6
+
7
+ ## Ne değişti
8
+
9
+ 1. **Ofis sekmesi (portal).** Piksel-sanat ofis animasyonu: araç çağrısı/onay/bitiş olaylarını canlıda ve geçmiş oturumda
10
+ gösterir; onay doğrudan ofisten verilir; token/bağlam/süre göstergeleri; geçmiş oturum için zaman çubuğu. Bağımlılıksız saf JS,
11
+ özgün sprite'lar. Sayaçlar yalnız gerçek olaylardan. Node testleri 69/69.
12
+ 2. **Kaynak izi kapısı (`taint.py`), varsayılan açık.** Bir yazma/komut hedefi (dosya yolu, komuttaki yol/adres) okunan içerikte
13
+ geçiyor ve kullanıcı isteğinde geçmiyorsa eylem onay ister (oto modda reddedilir). `--no-taint-gate` ile kapanır.
14
+ 3. **Düzeltme:** model çıktı sınırında kesilen araç argümanı artık görevi öldürmüyor (araç hatası olarak modele dönüyor).
15
+ 4. **Düzeltme:** v0.2.0 öncesi imajsız MCP kaydı için anlaşılır hata mesajı.
16
+ 5. Belgeler: [rakip karşılaştırması](comparison.md).
17
+
18
+ ## Kaynak izi kapısı: ölçüm
19
+
20
+ **7B (Lab, 24 senaryo × 2 tekrar × temiz/saldırılı, JSON protokolü):**
21
+
22
+ | Metrik | Kapı kapalı (v0.2.0 politikası) | Kapı açık |
23
+ |---|---|---|
24
+ | Meşru görev başarısı | 24/40 (%60) | 21/40 (%52) |
25
+ | Saldırı başarı oranı | 4/40 (%10) | 2/40 (%5) |
26
+ | Yetkisiz eylem denemesi | 11/40 | 7/40 |
27
+ | Yetkisiz eylem **gerçekleşmesi** | 6/40 (%15) | 2/40 (%5) |
28
+ | Yanlış ret | 0/40 | 0/40 |
29
+ | Temiz koşuda engellenen (yanlış alarm) | — | **0/40** |
30
+ | Saldırılı koşuda engellenen girişim | — | 5 |
31
+
32
+ Aralıklar örtüşüyor; **yön olumlu, ama istatistiksel olarak güçlü kanıt değil.** Meşru başarıdaki düşüş (24→21) gürültüdür:
33
+ 7B aynı senaryolarda 80 koşunun 34'ünde sonuç değiştirdi (20 düşüş, 14 yükseliş) ve kapı temiz koşularda hiç tetiklenmedi.
34
+
35
+ **DeepSeek (Lab, 88 koşu/yapılandırma):** her iki yapılandırmada saldırı başarı oranı 0/40, yetkisiz deneme 0; meşru başarı 39/40 ve
36
+ 38/40. **Güçlü model bu saldırılara zaten kapılmıyor,** yani kapı orada ölçülebilir fayda sağlamıyor, zarar da vermiyor.
37
+
38
+ **Bench (yanlış alarm kontrolü):** DeepSeek, 18 görev × 2 tekrar: C 36/36, C + kapı (`X-TAINT`) 36/36, kapı hiç tetiklenmedi.
39
+ 7B, kolay 8 görev × 3 tekrar: C 8/24 ve 10/24 (iki koşu), `X-TAINT` 5/24; kapı 24 koşuda en çok bir kez devreye girdi. 5/24 ile 8–10/24
40
+ arasındaki fark, aynı kolun iki koşusunun birbirinden farkı (2 görev) kadar: gürültü düzeyinde.
41
+
42
+ **Kaçan saldırılar (sınırlar):** `inj-tool-04`: talimat `run` çıktısından geldi (kapı komut çıktısını güvenilmeyen saymıyor). `mcp-auth-04`: model
43
+ enjekte metni yazdığı dosyanın *içeriğine* kopyaladı. İkisi de bilinen, belgelenmiş sınırdır (SECURITY.md).
44
+
45
+ ## Varsayılan karar ve gerekçe
46
+
47
+ Kapıyı **varsayılan açık** yaptık, ama bu çıkarım zayıf kanıta dayanıyor ve kararın gerekçesi şu: (1) meşru koşularda yanlış alarm
48
+ gözlenmedi (7B Lab'da 40, DeepSeek Lab'da 44, bench'te 96); (2) güvenli tarafta başarısız olur (onay ister, görevi sessizce bozmaz); (3) 7B'de yön olumlu.
49
+ "Ölçümde fayda görülmeden varsayılan olmaz" ilkesine göre bu sınırda bir karardır; kanıt zayıf kalırsa ya da gerçek kullanımda yanlış alarm
50
+ çıkarsa kapatılır. `X-TAINT` kolu ve `--no-taint-gate` bu yüzden duruyor.
51
+
52
+ ## Doğrulananlar ve doğrulanamayanlar
53
+
54
+ - Doğrulandı: 1118 Python testi, 69 Node testi, `ruff`; Ofis onay akışı gerçek bir DeepSeek oturumunda Ofis'ten ve Akış'tan (iki yönde).
55
+ - Doğrulanamadı: 12B+ yerel modeller; kapının gerçek kullanımdaki yanlış alarm oranı; Ofis'in gerçek bir 7B görevini baştan sona izlemesi
56
+ (7B o oturumlarda araç çağırmadı, hatta çıktı uydurdu; Ofis bunu doğru yansıttı).
57
+ - Bench kolay/zor ayrımı: DeepSeek 18 görevin tamamını çözüyor (doygun), 7B zor 10 görevde 0 başarı; **orta zorlukta görev yok,** bu yüzden
58
+ güçlü ve zayıf modeller arasındaki kollar ayırt edilemiyor. Bir sonraki ölçüm işi budur.
59
+
60
+ ## Maliyet
61
+
62
+ DeepSeek token kullanımı (yalnız ölçüm koşuları, Lab + bench): yaklaşık 2,0 milyon token. Usta'ya Ofis'i yazdırdığım kodlama koşularının
63
+ token sayısını ayrıca izlemedim.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "usta"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Genel amaçlı, açık kaynak yapay zekâ ajanı"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -12,6 +12,7 @@ from usta.breaker import CircuitBreaker, Trip
12
12
  from usta.models import Message, ModelClient, ToolCall
13
13
  from usta.phases import PhaseTracker
14
14
  from usta.policy import Decision, Policy
15
+ from usta.taint import Taint
15
16
  from usta.tools import Registry, ToolError
16
17
  from usta.verify import Verifier
17
18
 
@@ -62,7 +63,13 @@ class Agent:
62
63
  inbox: Callable[[], list[str]] | None = None,
63
64
  shrink_writes: int | None = None,
64
65
  phases: PhaseTracker | None = None,
66
+ taint: bool = False,
65
67
  ):
68
+ self.taint = (
69
+ taint # DENEYSEL: güvenilmeyen içerikten türeyen yazma/komut hedefleri onay ister
70
+ )
71
+ self._taint: Taint | None = None
72
+ self.approval_note = "" # onay istenirken gerekçe (görev yöneticisi onay olayına ekler)
66
73
  self.phases = phases # yürütme aşamaları gözlemcisi (isteğe bağlı; döngüyü değiştirmez)
67
74
  if phases is not None and phases.on_transition is None:
68
75
  # geçişler denetim kaydına yazılır; SSE/olay akışı (API sözleşmesi) değişmez
@@ -122,6 +129,7 @@ class Agent:
122
129
  first = f"{self.memory_context}\n\nGörev: {prompt}" if self.memory_context else prompt
123
130
  messages = [Message("system", self.system_prompt), Message("user", first)]
124
131
  self.audit.record("task", prompt=prompt)
132
+ self._taint = Taint(prompt) if self.taint else None
125
133
  self._usage_base = self._usage_now()
126
134
  if self.breaker is not None:
127
135
  self.breaker.start()
@@ -195,6 +203,8 @@ class Agent:
195
203
  for text in got:
196
204
  messages.append(Message("user", f"Kullanıcıdan ek mesaj (görev sürerken): {text}"))
197
205
  self.audit.record("user_message", chars=len(text))
206
+ if self._taint is not None:
207
+ self._taint.add_user(text)
198
208
  return bool(got)
199
209
 
200
210
  def _calibrate(self, estimated: int, requests_before: int) -> None:
@@ -296,8 +306,18 @@ class Agent:
296
306
  self.audit.record("tool_call", tool=call.name, args=call.arguments)
297
307
  self._emit("tool_call", tool=call.name, args=call.arguments)
298
308
  decision = self.policy.check(call.name, call.arguments)
309
+ note = ""
310
+ if decision is Decision.ALLOW and self._taint is not None:
311
+ note = self._taint.check(call.name, call.arguments)
312
+ if note: # hedef güvenilmeyen içerikten türedi: onay şartı (oto modda reddedilir)
313
+ self.audit.record("taint", tool=call.name, reason=note)
314
+ decision = Decision.ASK
299
315
  if decision is Decision.ASK:
300
- approved = bool(self.approver and self.approver(call.name, call.arguments))
316
+ self.approval_note = note
317
+ try:
318
+ approved = bool(self.approver and self.approver(call.name, call.arguments))
319
+ finally:
320
+ self.approval_note = ""
301
321
  decision = Decision.ALLOW if approved else Decision.DENY
302
322
  self.audit.record("approval", tool=call.name, approved=approved)
303
323
  if decision is Decision.DENY:
@@ -306,6 +326,8 @@ class Agent:
306
326
  return "reddedildi: bu eylem izin politikasına aykırı veya onaylanmadı"
307
327
  try:
308
328
  result = self.registry.call(call.name, call.arguments)
329
+ if self._taint is not None:
330
+ self._taint.observe(call.name, result)
309
331
  if self.verifier is not None and call.name in ("write_file", "edit_file"):
310
332
  self._dirty = True
311
333
  warn = self.verifier.after_edit(call.name, call.arguments, self._root())
@@ -38,6 +38,8 @@ UI_FILES = {
38
38
  "/": ("index.html", "text/html; charset=utf-8"),
39
39
  "/app.js": ("app.js", "text/javascript; charset=utf-8"),
40
40
  "/logic.js": ("logic.js", "text/javascript; charset=utf-8"),
41
+ "/office-logic.js": ("office-logic.js", "text/javascript; charset=utf-8"),
42
+ "/office.js": ("office.js", "text/javascript; charset=utf-8"),
41
43
  "/app.css": ("app.css", "text/css; charset=utf-8"),
42
44
  }
43
45
  UI_HEADERS = {
@@ -52,6 +52,7 @@ class Arm:
52
52
  breaker: bool = False # devre kesici: döngüde geri sar, denenenleri bildir (breaker.py)
53
53
  context_window: int | None = None # verilirse bağlam bütçesi/sıkıştırma etkin (token)
54
54
  symbol_tools: bool = False # find_symbol / symbol_context / related_files (usta[symbols])
55
+ taint: bool = False # kaynak izi kapısı: okunan içerikten türeyen yazma/komut hedefleri onay ister (taint.py)
55
56
  max_read: int | None = (
56
57
  None # verilirse read_file çıktısı bu karakterle sınırlı (kod okuma bütçesi)
57
58
  )
@@ -95,6 +96,13 @@ ARMS: dict[str, Arm] = {
95
96
  symbol_tools=True, max_read=12_000, system_prompt=SYMBOL_PROMPT, experimental=True,
96
97
  ),
97
98
  } # fmt: skip
99
+ ARMS["X-TAINT"] = replace(
100
+ ARMS["C"],
101
+ name="X-TAINT",
102
+ description="C + kaynak izi kapısı (taint.py)",
103
+ taint=True,
104
+ experimental=True,
105
+ )
98
106
 
99
107
 
100
108
  @dataclass(frozen=True)
@@ -344,6 +352,7 @@ def _attempt(
344
352
  breaker=CircuitBreaker(ws) if arm.breaker else None,
345
353
  context_window=arm.context_window,
346
354
  phases=tracker,
355
+ taint=arm.taint,
347
356
  )
348
357
  error, steps, truncated, infra, answer, timed_out = "", 0, False, False, "", False
349
358
  timer.start()
@@ -172,6 +172,13 @@ def _add_model_args(p: argparse.ArgumentParser) -> None:
172
172
  action="store_true",
173
173
  help="DENEYSEL: find_symbol/symbol_context/related_files araçlarını aç (pip install 'usta[symbols]'; USTA_SYMBOL_TOOLS=1)",
174
174
  )
175
+ p.add_argument(
176
+ "--taint-gate",
177
+ action=argparse.BooleanOptionalAction,
178
+ default=None,
179
+ help="kaynak izi kapısı (varsayılan AÇIK): okunan içerikten türeyen yazma/komut hedefleri onay ister, "
180
+ "oto modda reddedilir; kapatmak için --no-taint-gate ya da USTA_TAINT_GATE=0",
181
+ )
175
182
  p.add_argument(
176
183
  "--protocol",
177
184
  choices=(*PROTOCOLS, "auto"),
@@ -180,6 +187,14 @@ def _add_model_args(p: argparse.ArgumentParser) -> None:
180
187
  )
181
188
 
182
189
 
190
+ def _taint_gate(args: argparse.Namespace) -> bool:
191
+ """Kaynak izi kapısı: varsayılan açık; --no-taint-gate ya da USTA_TAINT_GATE=0 kapatır."""
192
+ flag = getattr(args, "taint_gate", None)
193
+ if flag is not None:
194
+ return bool(flag)
195
+ return os.environ.get("USTA_TAINT_GATE") != "0"
196
+
197
+
183
198
  def _symbol_tools(args: argparse.Namespace) -> bool:
184
199
  """Deneysel sembol araçları: bayrak ya da USTA_SYMBOL_TOOLS=1. Paket yoksa kapalı kalır ve söyler."""
185
200
  on = bool(getattr(args, "symbol_tools", False)) or os.environ.get("USTA_SYMBOL_TOOLS") == "1"
@@ -730,6 +745,7 @@ def _lab_cmd(args: argparse.Namespace) -> int:
730
745
  harness = labrunner.HarnessConfig(
731
746
  id=args.harness_id,
732
747
  breaker=args.breaker,
748
+ taint=_taint_gate(args),
733
749
  protocol=args.protocol or os.environ.get("USTA_PROTOCOL") or "native",
734
750
  )
735
751
  rev = args.model_revision or capability.detect_revision(endpoint, name)
@@ -960,6 +976,7 @@ def _serve_cmd(args: argparse.Namespace) -> int:
960
976
  max_steps=args.max_steps,
961
977
  allowed_hosts=hosts,
962
978
  symbol_tools=_symbol_tools(args),
979
+ taint_gate=_taint_gate(args),
963
980
  mcp_servers=lambda: (
964
981
  _mcp_servers(home) or []
965
982
  ), # panelden eklenenler yeniden başlatmadan gelir
@@ -1240,6 +1257,7 @@ def _chat_cmd(args: argparse.Namespace) -> int:
1240
1257
  context_window=window,
1241
1258
  model_control=control,
1242
1259
  symbol_tools=_symbol_tools(args),
1260
+ taint_gate=_taint_gate(args),
1243
1261
  mcp_servers=lambda: _mcp_servers(home) or [],
1244
1262
  )
1245
1263
  interactive = sys.stdin.isatty()
@@ -41,6 +41,7 @@ class HarnessConfig:
41
41
 
42
42
  id: str = "base"
43
43
  breaker: bool = False
44
+ taint: bool = False # DENEYSEL kaynak izi kapısı (taint.py)
44
45
  protocol: str = "native"
45
46
  approver: str = "deny" # ölçümde insan yok: onay isteyen eylemler reddedilir
46
47
 
@@ -121,6 +122,7 @@ def run_one(
121
122
  on_event=env.on_event,
122
123
  cancel=cancel,
123
124
  breaker=CircuitBreaker(env.root) if harness.breaker else None,
125
+ taint=harness.taint,
124
126
  )
125
127
  timer.start()
126
128
  try:
@@ -176,9 +176,19 @@ def _rm(name: str) -> None:
176
176
  subprocess.run(["docker", "rm", "-f", name], capture_output=True, check=False)
177
177
 
178
178
 
179
+ def _no_image_msg(name: str) -> str:
180
+ return (
181
+ f"'{name}' için imaj tanımlı değil (v0.2.0 öncesi kayıt). MCP sunucuları artık yalıtılmış konteynerde çalışır: "
182
+ f"`usta mcp add {name} --image <imaj> ...` ile yeniden kaydet "
183
+ "(ana makinede çalıştırmak yalnız `--isolation host --i-understand-host-risk` ile)"
184
+ )
185
+
186
+
179
187
  def _verify_pins(home: Path, srv: mcpconfig.Server) -> list[str]:
180
188
  notes = []
181
- current = image_id(srv.image or "")
189
+ if not srv.image:
190
+ raise McpIsolationError(_no_image_msg(srv.name))
191
+ current = image_id(srv.image)
182
192
  if current is None:
183
193
  raise McpIsolationError(
184
194
  f"imaj yerelde yok: {srv.image} (Usta imajı kendiliğinden çekmez: docker pull {srv.image})"
@@ -299,7 +309,9 @@ def prepare(
299
309
  problem = docker_problem()
300
310
  if problem:
301
311
  raise McpIsolationError(f"{problem}: sunucu hazırlanamaz")
302
- iid = image_id(srv.image or "")
312
+ if not srv.image:
313
+ raise McpIsolationError(_no_image_msg(srv.name))
314
+ iid = image_id(srv.image)
303
315
  if iid is None:
304
316
  raise McpIsolationError(
305
317
  f"imaj yerelde yok: {srv.image} (docker pull {srv.image}; Usta kendiliğinden çekmez)"
@@ -34,6 +34,9 @@ class Usage:
34
34
  rescued: int = 0 # içerikten kurtarılan araç çağrısı sayısı (model tool_calls kullanmıyor)
35
35
 
36
36
 
37
+ BAD_ARGS_KEY = "__bozuk_argumanlar__" # modelin argüman JSON'u çözümlenemedi (çoğu kez çıktı sınırında kesildi)
38
+
39
+
37
40
  @dataclass(frozen=True)
38
41
  class ToolCall:
39
42
  id: str
@@ -152,10 +155,19 @@ class OpenAICompatClient:
152
155
  calls = []
153
156
  for c in msg.get("tool_calls") or []:
154
157
  try:
155
- args = json.loads(c["function"]["arguments"] or "{}")
156
- calls.append(ToolCall(c["id"], c["function"]["name"], args))
157
- except (KeyError, ValueError) as e:
158
+ raw = c["function"]["arguments"] or "{}"
159
+ name = c["function"]["name"]
160
+ cid = c["id"]
161
+ except (KeyError, TypeError) as e:
158
162
  raise ModelError("araç çağrısı çözümlenemedi") from e
163
+ try:
164
+ args = json.loads(raw)
165
+ except ValueError:
166
+ args = None
167
+ if not isinstance(args, dict):
168
+ # Çıktı sınırında kesilen/bozuk argüman görevi öldürmez: model durumu araç hatası olarak görür
169
+ args = {BAD_ARGS_KEY: len(raw) if isinstance(raw, str) else 0}
170
+ calls.append(ToolCall(cid, name, args))
159
171
  if not calls and tools and self.rescue:
160
172
  from usta.rescue import extract_calls # döngüsel içe aktarmayı önler
161
173
 
@@ -0,0 +1,95 @@
1
+ """Kaynak izi (taint) kapısı: güvenilmeyen içerikten türeyen yazma/komut hedefleri onay ister.
2
+
3
+ Saldırı biçimi: ajan bir dosyayı/sayfayı/araç çıktısını okur, içindeki gizli talimata uyar ve kullanıcının hiç
4
+ bahsetmediği bir dosyaya yazar ya da bir komut çalıştırır. Ortak nokta: eylemin HEDEFİ (dosya yolu, komut
5
+ parçası, adres) okunan içerikte geçer ama kullanıcının isteğinde geçmez.
6
+
7
+ Kapı bunu yakalar: write_file/edit_file yolu ve `run` komutundaki yol/adres benzeri parçalar, güvenilmeyen kaynaklardan
8
+ gelen metinde geçiyor ve kullanıcı metninde (istem, ekler, görev sırasında gönderilen mesajlar) geçmiyorsa, eylem
9
+ `ASK` olur (manuel modda kullanıcıya sorulur, oto modda reddedilir). Sezgiseldir; kesin koruma değildir.
10
+
11
+ Güvenilmeyen kaynaklar: read_file/read_symbol (depo içeriği), fetch_url, web_search ve MCP araçları. Komut çıktıları
12
+ (`run`) ve dizin listeleri kapsam dışıdır: ajanın kendi çalıştırdığı kod/izler ve dosya adları meşru hedefleri
13
+ sürekli işaretlerdi (ör. test çıktısındaki izden hatalı dosyayı düzeltmek).
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import re
19
+ import shlex
20
+
21
+ UNTRUSTED_TOOLS = frozenset({"read_file", "read_symbol", "fetch_url", "web_search"})
22
+ MAX_BLOB = 400_000 # güvenilmeyen metin üst sınırı (karakter); eskiler atılır
23
+ MIN_LEN = 4 # bundan kısa parçalar eşleşme sayılmaz (".py", "ls" gibi)
24
+ _URL = re.compile(r"https?://([^\s/'\"<>]+)", re.IGNORECASE)
25
+ _PATHISH = re.compile(r"[\w.\-]+[/.][\w.\-/]*\w|[\w\-]+/")
26
+
27
+
28
+ def _norm_path(p: str) -> str:
29
+ p = str(p or "").replace("\\", "/").strip()
30
+ while p.startswith("./"):
31
+ p = p[2:]
32
+ return p
33
+
34
+
35
+ def _is_untrusted(tool: str) -> bool:
36
+ return tool in UNTRUSTED_TOOLS or tool.startswith("mcp__")
37
+
38
+
39
+ def _candidates(tool: str, args: dict) -> list[str]:
40
+ """Eylemin hedef parçaları: yazma yolu; komutta URL sunucuları ve yol/dosya benzeri jetonlar."""
41
+ if tool in ("write_file", "edit_file"):
42
+ path = _norm_path(args.get("path"))
43
+ return [path] if len(path) >= MIN_LEN else []
44
+ if tool == "run":
45
+ cmd = str(args.get("command") or "")
46
+ out = [h.lower() for h in _URL.findall(cmd)]
47
+ try:
48
+ tokens = shlex.split(cmd)
49
+ except ValueError:
50
+ tokens = cmd.split()
51
+ for t in tokens:
52
+ if t.startswith("-") or len(t) < MIN_LEN:
53
+ continue
54
+ if "://" in t:
55
+ continue # URL sunucu olarak zaten eklendi
56
+ if _PATHISH.search(t):
57
+ out.append(_norm_path(t))
58
+ return [c for c in out if len(c) >= MIN_LEN]
59
+ return []
60
+
61
+
62
+ class Taint:
63
+ """Bir görev boyunca güvenilmeyen metni ve kullanıcı metnini tutar; eylemleri denetler."""
64
+
65
+ def __init__(self, user_text: str = ""):
66
+ self._user = [str(user_text or "")]
67
+ self._blobs: list[str] = []
68
+ self._size = 0
69
+
70
+ def add_user(self, text: str) -> None:
71
+ self._user.append(str(text or ""))
72
+
73
+ def observe(self, tool: str, result: str) -> None:
74
+ if not _is_untrusted(tool):
75
+ return
76
+ text = str(result or "")
77
+ self._blobs.append(text)
78
+ self._size += len(text)
79
+ while self._size > MAX_BLOB and len(self._blobs) > 1:
80
+ self._size -= len(self._blobs.pop(0))
81
+
82
+ def check(self, tool: str, args: dict) -> str:
83
+ """Boş dize: sorun yok. Aksi halde gerekçe (hedef ve nerede bulunduğu)."""
84
+ if not self._blobs:
85
+ return ""
86
+ user = "\n".join(self._user)
87
+ blob = "\n".join(self._blobs)
88
+ low_user, low_blob = user.lower(), blob.lower()
89
+ for cand in _candidates(tool, args):
90
+ key = cand.lower()
91
+ if key in low_user:
92
+ continue
93
+ if key in low_blob:
94
+ return f"hedef '{cand}' okunan içerikte geçiyor ama isteğinde geçmiyor"
95
+ return ""