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.
- usta-0.2.1/.github/workflows/publish.yml +33 -0
- {usta-0.2.0 → usta-0.2.1}/CHANGELOG.md +9 -0
- {usta-0.2.0 → usta-0.2.1}/PKG-INFO +4 -1
- {usta-0.2.0 → usta-0.2.1}/README.en.md +3 -0
- {usta-0.2.0 → usta-0.2.1}/README.md +3 -0
- {usta-0.2.0 → usta-0.2.1}/SECURITY.md +6 -0
- usta-0.2.1/docs/comparison.md +65 -0
- usta-0.2.1/docs/v0.2.1-raporu.md +63 -0
- {usta-0.2.0 → usta-0.2.1}/pyproject.toml +1 -1
- {usta-0.2.0 → usta-0.2.1}/src/usta/agent.py +23 -1
- {usta-0.2.0 → usta-0.2.1}/src/usta/api.py +2 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench.py +9 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/cli.py +18 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/runner.py +2 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/mcp_runtime.py +14 -2
- {usta-0.2.0 → usta-0.2.1}/src/usta/models.py +15 -3
- usta-0.2.1/src/usta/taint.py +95 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/tasks.py +15 -3
- {usta-0.2.0 → usta-0.2.1}/src/usta/tools.py +6 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/web/app.css +49 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/web/app.js +63 -10
- {usta-0.2.0 → usta-0.2.1}/src/usta/web/index.html +39 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/web/logic.js +1 -1
- usta-0.2.1/src/usta/web/office-logic.js +510 -0
- usta-0.2.1/src/usta/web/office.js +1660 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_benchdiag.py +3 -1
- {usta-0.2.0 → usta-0.2.1}/tests/test_mcp_runtime.py +3 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_models.py +17 -4
- usta-0.2.1/tests/test_taint.py +107 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_tools.py +9 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_web.py +78 -1
- usta-0.2.1/tests/web/office-draw.test.cjs +386 -0
- usta-0.2.1/tests/web/office.test.cjs +498 -0
- {usta-0.2.0 → usta-0.2.1}/.github/workflows/ci.yml +0 -0
- {usta-0.2.0 → usta-0.2.1}/.gitignore +0 -0
- {usta-0.2.0 → usta-0.2.1}/CONTRIBUTING.md +0 -0
- {usta-0.2.0 → usta-0.2.1}/LICENSE +0 -0
- {usta-0.2.0 → usta-0.2.1}/Makefile +0 -0
- {usta-0.2.0 → usta-0.2.1}/docs/design.md +0 -0
- {usta-0.2.0 → usta-0.2.1}/docs/implementation-audit.md +0 -0
- {usta-0.2.0 → usta-0.2.1}/docs/reliability-lab.md +0 -0
- {usta-0.2.0 → usta-0.2.1}/docs/v0.2.0-raporu.md +0 -0
- {usta-0.2.0 → usta-0.2.1}/requirements-dev.lock +0 -0
- {usta-0.2.0 → usta-0.2.1}/requirements.lock +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/__init__.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/apis.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/attachments.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/audit.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/auth.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/check/test_todo.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/repo/test_todo.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/repo/todo.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/add-feature/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cli-args/check/test_cli.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cli-args/repo/cli.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cli-args/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/check/test_cart.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/check/test_pricing.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/cart.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/pricing.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/test_cart.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/repo/test_pricing.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/cross-module-bug/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/csv-quotes/check/test_parse.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/csv-quotes/repo/parse.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/csv-quotes/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/deep-merge/check/test_config.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/deep-merge/repo/config.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/deep-merge/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/expr-eval/check/test_calc.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/expr-eval/repo/calc.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/expr-eval/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/implement-slugify/check/test_slug.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/implement-slugify/repo/slug.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/implement-slugify/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/interval-merge/check/test_intervals.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/interval-merge/repo/intervals.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/interval-merge/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/check/test_dates.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/repo/dates.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/repo/test_dates.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/leap-dates/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/lru-cache/check/test_lru.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/lru-cache/repo/lru.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/lru-cache/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/check/test_money.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/repo/money.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/repo/test_money.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/money-rounding/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/check/test_users.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/api.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/greeting.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/report.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/test_users.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/repo/users.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/multi-file-rename/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/check/test_stats.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/repo/stats.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/repo/test_stats.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/off-by-one/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/text-wrap/check/test_wrap.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/text-wrap/repo/wrap.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/text-wrap/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/check/test_find.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/repo/find.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/repo/test_find.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-case-insensitive/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/check/test_words.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/repo/test_words.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/repo/words.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-default-sep/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/check/test_greet.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/repo/greet.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/repo/test_greet.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-greeting/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/check/test_parity.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/repo/parity.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/repo/test_parity.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-is-even/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/check/test_nums.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/repo/nums.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/repo/test_nums.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-missing-return/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/check/test_names.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/repo/names.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/repo/test_names.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-strip-names/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/check/test_pct.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/repo/pct.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/repo/test_pct.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-swap-args/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/check/test_avg.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/repo/avg.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/repo/test_avg.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/tiny-wrong-operator/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/check/test_text.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/repo/test_text.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/repo/text.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/bench_tasks/word-count-fix/task.json +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/breaker.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/capability.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/context.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/fetch.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/fetch_inner.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/filetree.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/jsonproto.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/__init__.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/env.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/evaluator.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/metrics.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/report.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/scenario.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/scenarios/v1.jsonl +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/lab/scripted.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/mcp.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/mcp_tools.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/mcpcatalog.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/mcpconfig.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/memory.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/modelctl.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/modelinfo.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/outline.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/pdf_inner.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/phases.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/policy.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/portal.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/profile.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/projectcfg.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/projects.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/proxy.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/repl.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/rescue.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/sandbox.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/sessions.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/settings.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/skills.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/symbols.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/taxonomy.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/textproto.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/theme.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/verify.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/src/usta/workspace.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/add-feature/todo.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/cli-args/cli.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/cross-module-bug/cart.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/csv-quotes/parse.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/deep-merge/config.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/expr-eval/calc.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/implement-slugify/slug.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/interval-merge/intervals.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/leap-dates/dates.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/lru-cache/lru.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/money-rounding/money.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/api.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/greeting.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/report.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/multi-file-rename/users.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/off-by-one/stats.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/text-wrap/wrap.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-case-insensitive/find.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-default-sep/words.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-greeting/greet.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-is-even/parity.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-missing-return/nums.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-strip-names/names.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-swap-args/pct.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/tiny-wrong-operator/avg.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/bench_solutions/word-count-fix/text.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/conftest.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/fake_endpoint.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/fake_mcp.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/pdfutil.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/probe_mcp_src.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_agent.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_api.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_api_mcp.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_apis.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_audit.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_bench.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_breaker.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_capability.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_chat_cli.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_cli.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_context.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_fetch.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_files_skills.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_hardening2.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_hardening3.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_jsonproto.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_lab.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_mcp.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_mcp_tools.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_mcpconfig.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_memory.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_netproxy.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_no_secrets.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_outline.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_pdf.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_phased.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_phases.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_policy.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_portal.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_profile.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_projectcfg.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_projects.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_proxy.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_repl.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_rescue.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_sandbox.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_sessions.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_steer_model.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_supply_chain.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_symbols.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_tasks.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_textproto.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_theme.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_verify.py +0 -0
- {usta-0.2.0 → usta-0.2.1}/tests/test_workspace.py +0 -0
- {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.
|
|
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.
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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 ""
|