tether-sdk 0.2.7__tar.gz → 0.2.8__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.
- {tether_sdk-0.2.7/.claude/feature-context → tether_sdk-0.2.8/.claude/gru}/active/v0-2-bugs.md +44 -11
- tether_sdk-0.2.8/.claude/gru/archive/work-on-v0-2-4.md +85 -0
- tether_sdk-0.2.8/.claude/gru.json +12 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/AGENTS.md +8 -2
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/PKG-INFO +4 -59
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/README.md +3 -58
- tether_sdk-0.2.8/docs/extending.md +52 -0
- tether_sdk-0.2.8/docs/reference/cli.md +285 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/providers.md +115 -78
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/release-notes.md +20 -1
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/troubleshooting.md +1 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/agent-interface.md +1 -19
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/mkdocs.yml +1 -0
- tether_sdk-0.2.8/tests/test_cli_docs.py +46 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_context.py +6 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_params.py +55 -0
- tether_sdk-0.2.8/tests/test_providers_docs.py +36 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_sandbox.py +53 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_tether.py +1 -1
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/__init__.py +1 -1
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/context.py +2 -1
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/providers/base.py +6 -1
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/providers/ollama.py +14 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/sandbox.py +24 -2
- tether_sdk-0.2.7/docs/extending.md +0 -107
- {tether_sdk-0.2.7/.claude/feature-context → tether_sdk-0.2.8/.claude/gru}/active/.gitkeep +0 -0
- {tether_sdk-0.2.7/.claude/feature-context → tether_sdk-0.2.8/.claude/gru}/archive/.gitkeep +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/.github/workflows/ci.yml +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/.github/workflows/delivery.yml +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/.github/workflows/docs.yml +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/.gitignore +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/LICENSE +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/README.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/grade.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/harness-bench/README.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/harness-bench/tether-adapter.patch +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/naive_agent.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/report.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/run.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/hidden/test_hidden.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ref/calc/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ref/calc/errors.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ref/calc/evaluator.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ref/calc/parser.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ref/calc/tokenizer.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ws/calc/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ws/calc/errors.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ws/calc/evaluator.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ws/calc/parser.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ws/calc/tokenizer.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ws/tests/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-hard/ws/tests/test_calc.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/hidden/test_hidden.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/ws/cli.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/ws/ledger/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/ws/ledger/parser.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/ws/ledger/report.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/ws/sample.csv +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/ws/tests/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-medium/ws/tests/test_report.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-simple/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-simple/hidden/test_hidden.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-simple/ws/tests/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-simple/ws/tests/test_textutils.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/code-simple/ws/textutils.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/codereview/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/codereview/ws/inventory.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/content/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/content/ws/.keep +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/hidden/test_hidden.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/AGENTS.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/README.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/docs/architecture.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/docs/style.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/mini/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/mini/cli.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/mini/fmt.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/mini/stats.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/mini/textio.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/tests/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/focus/ws/tests/test_stats.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/plan/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/plan/ws/README.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/plan/ws/svc/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/plan/ws/svc/client.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/plan/ws/svc/config.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/qa/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/qa/ws/.keep +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/review/goal.txt +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/bench/tasks/review/ws/paper.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/architecture.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/benchmarks.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/index.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/javascripts/mathjax.js +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/agent-interface.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/context-and-store.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/harness.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/index.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/multiplexer-and-decorator.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/sandbox-and-skills.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/reference/tools.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/releasing.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/testing.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/built-in-tools.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/cli-and-headless.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/index.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/multi-agent.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/sdk-and-custom-tools.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/security-and-sandbox.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/skills.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/docs/tutorial/workflows-and-telemetry.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/conftest.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/proxy.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_agents.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_defaults.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_faults.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_lifecycle.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_limits.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_quirks.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_sandbox.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/e2e/test_surface.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/README.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/checks.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/course.yaml +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/ruff.toml +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/skills/planner/SKILL.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/skills/searcher/SKILL.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/skills/searcher/tools.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/skills/writer/SKILL.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/examples/course/skills/writer/tools.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/pyproject.toml +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/scripts/record_golden.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/skills/researcher/SKILL.md +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/conftest.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/fixtures/dialects.jsonl +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/coding_edit_failure_modes.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/coding_edit_failure_modes.snap.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/done_nudge.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/done_nudge.snap.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/read_range_pingpong.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/read_range_pingpong.snap.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/research_web.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/research_web.snap.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/shell_wrappers.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/shell_wrappers.snap.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/waiting_input_resume.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/golden/waiting_input_resume.snap.json +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/test_anthropic.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/test_base.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/test_declarative.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/test_dialects.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/test_ollama.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/test_openai.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/providers/test_retry.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_agent_interface.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_cli.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_context_config.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_decorator.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_edit_tool.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_golden.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_harness.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_integration.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_misses.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_multiplexer.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_pipeline.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_release_hardening.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_retro.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_security_hardening.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_skills.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_store.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/test_structured_history.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/tools/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/tools/test_agent.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/tools/test_bash.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/tools/test_done.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/tools/test_edit.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/tools/test_mem.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tests/tools/test_web.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/api.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/cli.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/decorator.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/harness.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/mcp.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/multiplexer.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/pipeline.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/providers/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/providers/anthropic.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/providers/declarative.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/providers/openai.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/retro.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/schema.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/skills.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/store.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/tools/__init__.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/tools/agent.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/tools/bash.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/tools/done.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/tools/edit.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/tools/mem.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/tether/tools/web.py +0 -0
- {tether_sdk-0.2.7 → tether_sdk-0.2.8}/uv.lock +0 -0
{tether_sdk-0.2.7/.claude/feature-context → tether_sdk-0.2.8/.claude/gru}/active/v0-2-bugs.md
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# v0.2 bugs
|
|
2
2
|
- Slug: v0-2-bugs
|
|
3
3
|
- Status: active
|
|
4
|
-
- Started: 2026-10-20 Updated: 2026-10-
|
|
4
|
+
- Started: 2026-10-20 Updated: 2026-10-24 Closed: —
|
|
5
5
|
- Branches / PRs: branch `claude/keep-feature-context-resume-0ec918` (worktree); base `main` at 125b386 (release v0.2.2)
|
|
6
6
|
|
|
7
7
|
## Goal
|
|
@@ -10,6 +10,9 @@ Track and fix bugs found in Tether v0.2.x. Exact bug list not yet given by PO.
|
|
|
10
10
|
## Requests
|
|
11
11
|
- 2026-10-20: Close v0.2.2 milestone; implement v0.2.3 issues (#40 #39 #33 #24) via swe+tether(GLM), watch tether behavior and report bugs; open v0.2.4 issue for provider docs (YAML + Python, generic example, where providers.yaml lives); review docs overall and report.
|
|
12
12
|
- 2026-10-20: `/keep-feature-context new v0.2 bugs` (start log for v0.2 bugs work)
|
|
13
|
+
- 2026-10-21..24 (backfilled, close to verbatim): choose 4 issues for v0.2.5 and move the rest; fix #92 (+#89 `tether stop`), #81, #76, #86; register NVIDIA models (Nemotron 3 Ultra, Kimi K2.6, GLM-5.2, Mistral Large 3); post-release check v0.2.4 vs v0.2.5 with 3 models; viability of Harness-Bench (harness-bench.ai); sandbox explanation and an issue for 10/10; start using tether as SWE agent; release v0.2.5 and v0.2.6; file new issues and keep 4-5 issues per milestone.
|
|
14
|
+
- 2026-10-24 (backfilled): install v0.2.6 and find a smaller benchmark. Then: "Our main impossible goal is to get the highest score on Polyglot! Leave aside new features unless they help the score." Implement what is needed, release v0.2.7, run the benchmark, plan v0.2.8 from the results.
|
|
15
|
+
- 2026-10-24: PO: v0.2.7 improved or not (answer in plain words); "reading the whole file at once will fill the context faster?"; "are you logging with the keep-feature skill?" (answer: no, resumed and backfilled now).
|
|
13
16
|
|
|
14
17
|
## Decisions
|
|
15
18
|
- 2026-10-20: PO convention: v0.X.0 = feature releases (many issues OK), v0.X.Y = bug releases (small, 3-4). All 13 features now in single milestone v0.3 (v0.3.1-v0.3.3 deleted). Bug releases v0.2.4-v0.2.8 unchanged.
|
|
@@ -20,6 +23,12 @@ Track and fix bugs found in Tether v0.2.x. Exact bug list not yet given by PO.
|
|
|
20
23
|
- 2026-10-20: PO made me act as Team Leader (no product code; dispatch swe/qa/reviewer). swe delegates coding to tether; PO wants it run with GLM and observed for tether bugs.
|
|
21
24
|
- 2026-10-20: #24 -> opt-in kept + load-time warning + docs fix (maintainer lean, PO approved).
|
|
22
25
|
- 2026-10-20: Added `## Dev team specifics` to AGENTS.md (PR per task, hard stop = force push/history rewrite, store in-repo `.claude/feature-context/`).
|
|
26
|
+
- 2026-10-24 (backfilled): PO: 4-5 issues per milestone, create v0.2.X as needed; run `TETHER_E2E=1 uv run pytest e2e` before merging and put it in the PR; merges and releases only on explicit PO yes (classifier blocked one merge until PO said go).
|
|
27
|
+
- 2026-10-24: ONE focus = Polyglot-Python score (Aider polyglot, 34 Python exercises, graded by the exercise tests, test file restored before grading). Fast set = 12 exercises, 2 trials. Models: ollama/qwen3.5:9b (main), nemotron-3.5-lightning-30b-a3b on NVIDIA (second), Ultra only as an occasional ceiling check. `think=false` not adopted (79s vs 72s per run on 3 exercises, no clear gain).
|
|
28
|
+
- 2026-10-24: v0.2.7 ships only #138 and #141 (what the failure breakdown justified). Nudge issues (#132-#136) parked until the score says they matter. v0.2.8 starts with #144 (generation headroom).
|
|
29
|
+
- 2026-10-24: milestones now v0.2.8 {#144,#27,#132,#136,#137}, v0.2.9 {#145,#79,#133,#134,#135}, v0.2.10 {#60-#64}, v0.2.11 {#65,#66}; v0.3 holds the 14 feature issues (sandbox #124, Harness-Bench #123, edit tool #78/#34).
|
|
30
|
+
|
|
31
|
+
- 2026-10-24: PO: keep using tether as the swe agent with the strongest FREE model = `nvidia/nvidia/nemotron-3-ultra-550b-a55b` (docs/reference/providers.md: only free model that works; Kimi 404, GLM-5.2 EOL, GLM-5.3 as fallback; rpm 36). #144 bench running (base v0.2.7 venv vs head, polyglot-fast qwen3.5:9b x2) then e2e, then PR.
|
|
23
32
|
|
|
24
33
|
## Problems
|
|
25
34
|
- 2026-10-20: reviewer agent's own `gh pr review --comment` got blocked ('Unrequested Commit in a Connected App') and its merge condition needs a 'QA: GOOD TO GO' comment; TL posted the review+merge itself on PO instruction, works. Project has no QA layer beyond CI.
|
|
@@ -27,6 +36,11 @@ Track and fix bugs found in Tether v0.2.x. Exact bug list not yet given by PO.
|
|
|
27
36
|
- 2026-10-20 TETHER+GLM BUGS (from swe #24): (a) ses_bde86e86 glm-5.3 with --tools bash,edit: 38 steps/90k prompt tokens, read-loop alternating `sed -n 1,40p` / `40,100p` of same file, never edited; every assistant turn empty text + command only; no nudge/loop_stall recorded (= issue #39, cycle detection needed); ended `ReadTimeout` raised as tool_error/Worker error after ~5 min (httpx timeout not retried/handled). (b) ses_6176f368: without --tools bash,edit GLM had only `done`, spent 38 steps probing "unknown command", then WAITING_INPUT asking for a shell (tether default zero tools; unknown-command loops not nudged; could hint 'enabled tools: done' / tools to enable). (c) 401 shows raw HTTPStatusError, doesn't name NVIDIA_API_KEY. (d) swe worktree guard blocks eval/export/time in bash -> key loading needs a script file.
|
|
28
37
|
- 2026-10-20: swe #40 blocked: tether session ses_29d41cff (nvidia/z-ai/glm-5.3) -> 401 Unauthorized, 0 steps. NVIDIA_API_KEY missing/invalid in tether MCP server env. #24 swe confirmed same (ses_4a589057, 401, 686 ms). Both local branches empty, no PRs. Tether finding: 401 surfaces as raw HTTPStatusError, should name NVIDIA_API_KEY. Needs PO to supply key to MCP server env (not via me).
|
|
29
38
|
(none yet)
|
|
39
|
+
- 2026-10-24 (backfilled) FOUND BY POLYGLOT BASELINE (tether 0.2.6, qwen3.5:9b, 34 exercises: 14 pass, 41%; naive loop 8, 24%): 13 of 34 runs died as worker_error: 11 Ollama HTTP 500 `invalid tool call arguments for "act": unexpected end of JSON input`, 2 chat-template 500s (`CallExpression`, unknown cause, #145). post_with_retry retried the same request 3x. FIXED in #138/PR #139 (provider-agnostic `rejected_call`, hint to the model).
|
|
40
|
+
- 2026-10-24: ROOT CAUSE of step-cap runs (fast set, 24/48 hit 30 steps): fixed 2,400-char observation cap cut 6-12 KB test files to head+tail, so models re-read the same file 10+ times (45% of capped-run steps were reads, 19% edits). FIXED in #141/PR #142 (cap = max(2400, max_context_tokens*4/3)). Measured: qwen mean 0.64->0.80, re-reads 32%->19% of steps; Lightning pass 10->12/24 (8 of its 24 runs invalid, NVIDIA timeouts). Cost: prompt/step 5.3k->6.4k tokens, run total 106k->126k.
|
|
41
|
+
- 2026-10-24 OPEN (#144): cut-off tool-call hint still fires 42-45 times per 24 qwen runs (about half the runs); prompt before a cut-off median 7,553 tokens vs 5,453 normal, num_ctx 8,192: prompt fills the window, no room for the reply. The cap change did not reduce it.
|
|
42
|
+
- 2026-10-24: bench pitfalls fixed/known: naive_agent needs httpx (run bench with `uv run --with httpx`); `pgrep -f` in a wait loop matches its own shell (use PID); `bench/run.py is_infra` treats ProviderError as infra and hides real failures (report.py now splits); uv index lags PyPI (`uv tool install --refresh`); after `pkill` on bench, a cell dir can be deleted mid-run.
|
|
43
|
+
- 2026-10-24: verify gate at `done` gave only +0.08 mean on the 20 failing exercises (1 more pass): gate alone is not the lever. check-on-change prototype parked at /tmp/claude-1000/poly/check-on-change.snippet (not in repo).
|
|
30
44
|
|
|
31
45
|
## Progress log
|
|
32
46
|
- 2026-10-20: RELEASED v0.2.3: release PR #94 merged (ec32d63), GitHub Release v0.2.3 'Small-Model Robustness', delivery.yml success, PyPI tether-sdk 0.2.3 live, milestone v0.2.3 closed. Next: v0.2.4 (#27 #60 #74-#79 #81-#82 #85-#86 #88-#90 #92-#93) and v0.2.5 docs (#61-#66).
|
|
@@ -56,20 +70,39 @@ Track and fix bugs found in Tether v0.2.x. Exact bug list not yet given by PO.
|
|
|
56
70
|
- 2026-10-20: milestone v0.2.2 closed. Issue #60 created (v0.2.4, provider docs). Docs review agent launched (read-only).
|
|
57
71
|
- 2026-10-20 (backfilled): preflight ran; AGENTS.md section + `.claude/feature-context/{active,archive}` created (uncommitted).
|
|
58
72
|
- Checks: `uv run ruff check . && uv run ruff format --check .`; `uv run mypy tether/`; `uv run pytest`.
|
|
73
|
+
- 2026-10-24 (backfilled) RELEASES: v0.2.5 (#76 #81 #86 #89 #92 + NVIDIA models docs), v0.2.6 (#77 #93 #125 #127; PR #131), v0.2.7 (#138 #141 + Polyglot bench; PRs #139 #140 #142, release PR #143, merge 97cbf3e, GitHub Release v0.2.7, PyPI tether-sdk 0.2.7 live, milestone closed). All with e2e 65/65 and 100% coverage before merge.
|
|
74
|
+
- 2026-10-24: post-release check v0.2.4 vs v0.2.5 (3 models, 8 tasks): Ultra 0.943->0.946, Lightning 0.721->0.902, qwen3.5:9b 0.668->0.813. Issues filed: #123 Harness-Bench real run, #124 sandbox 10/10, #132-#137 findings.
|
|
75
|
+
- 2026-10-24: v0.2.6 vs v0.2.7 on the same 12 exercises, qwen: 3/12 pass mean 0.66 -> 9/24 pass mean 0.80 (1 vs 2 runs per exercise, modest evidence).
|
|
76
|
+
- 2026-10-24: PRs: #139 (#138 rejected tool call, +follow-up commit making it agnostic across ollama/openai/anthropic after PO asked), #140 bench, #142 cap, #143 release. Issues: #138, #141 (closed), #144 headroom (v0.2.8), #145 template 500 (v0.2.9).
|
|
77
|
+
- 2026-10-24: re-run of Lightning infra-failed cells on installed 0.2.7 running (out dir /tmp/claude-1000/poly/cap-027, log log-redo-027.txt); results not read yet.
|
|
59
78
|
|
|
60
79
|
## Next steps
|
|
61
|
-
- [x] Release v0.2.3
|
|
62
|
-
- [ ]
|
|
63
|
-
- [ ]
|
|
64
|
-
- [ ]
|
|
65
|
-
- [ ]
|
|
66
|
-
- [ ]
|
|
67
|
-
- [ ]
|
|
68
|
-
- [ ] PO to list the v0.2 bugs
|
|
69
|
-
- [ ] Commit AGENTS.md + feature-context log on this branch
|
|
80
|
+
- [x] Release v0.2.3, v0.2.4(not logged), v0.2.5, v0.2.6, v0.2.7
|
|
81
|
+
- [ ] Read the re-run of Lightning cells (cap-027) and update the v0.2.7 numbers
|
|
82
|
+
- [ ] v0.2.8: #144 headroom first (test on fast set vs cap-027: hint rate, no-action steps, prompt size, score), then #27, #132, #136, #137
|
|
83
|
+
- [ ] v0.2.9: #145 template 500 (reproduce), #79, #133, #134, #135
|
|
84
|
+
- [ ] Narrow `is_infra` in bench/run.py (known, noted in #138)
|
|
85
|
+
- [ ] Harness-Bench #123 blocked: docker bridge->host firewall (sudo) + NVIDIA key file from PO
|
|
86
|
+
- [ ] MCP server still on an old process: PO to reconnect (0.2.7 installed locally)
|
|
70
87
|
|
|
71
88
|
## Sessions
|
|
89
|
+
| 2026-10-25 | /gru takes over: store moved to .claude/gru/, AGENTS.md + .claude/gru.json updated (PR chore/gru-takeover) |
|
|
72
90
|
| Date | Session note |
|
|
73
|
-
| 2026-10-20 |
|
|
91
|
+
| 2026-10-20 | created log |
|
|
92
|
+
| 2026-10-24 | resumed, backfilled v0.2.4-v0.2.7 (log had stopped at v0.2.3) |
|
|
93
|
+
| 2026-10-24 | resumed in worktree token-usage-analysis-de8182; copied this newer log from worktree tether-v0-2-mcp-89325e (release/v0.2.7), the live one |
|
|
74
94
|
|
|
75
95
|
- 2026-10-20: PO asked: add Philosophy Gate to AGENTS.md (done, uncommitted; also fixed coverage line: CI enforces 100%). Switch swe tether model to `nvidia/nvidia/nemotron-3-ultra-550b-a55b` (exists on NVIDIA API, smoke test `done pong` passed via tether 0.2.3; no providers.yaml change needed, `nvidia` prefix covers it). Upgraded installed tether 0.2.2 -> 0.2.3 (`uv tool upgrade tether-sdk`). MCP server still on old process: needs reconnect to load 0.2.3 + key wrapper.
|
|
96
|
+
|
|
97
|
+
## Gru run 2026-10-25
|
|
98
|
+
- PRs: #146 (#144 headroom, 1/8; testing 1/4 on bench), #148 (gru takeover), #136 branch fix/136-escalate-loop-stall (worktree .claude/worktrees/gru-136, commit pending PR until codereview bench).
|
|
99
|
+
- Ledger: #136 | Tier 0 | nvidia/nemotron-3-ultra-550b-a55b | tether | ses_2a9da3da | attempt 1 | peak 8.5k ctx, 40 steps | result: code correct (consecutive counter), but FAILED max_steps; I fixed line length (E501), added e2e + release note. Acceptance test (pre-written) passes.
|
|
100
|
+
- Tether friction (ses_2a9da3da): steps 30-40 wasted because `.venv/bin/pytest` is not runnable inside the bash sandbox (venv python symlinks live under hidden /home; `which pytest` empty, `pip install` impossible). Related #76 (PATH under $HOME) but for venvs in the workspace. Also ~13 of 40 steps were `sed -n a,bp` re-reads of harness.py before the first edit (#78/#27 family), and the card's --verify gate used the same unrunnable path. Not yet filed.
|
|
101
|
+
- 2026-10-25 #144: 1/4 headroom bench: hints 29->2, nudges 98->11 (19%->2.2%), pass 11/24 vs 10, mean 0.756 vs 0.741; e2e 65 pass; PR #146 updated, awaits PO merge yes.
|
|
102
|
+
- 2026-10-25 #136: tether(Ultra) wrote correct code but hit max_steps (pytest unrunnable in sandbox). Bench codereview qwen3.5:9b 0/2 Stalled with the new wording AND with output-in-warning variant: model repeats the same empty-content call regardless of message. Not merged, no PR; findings on #136; next hypothesis: break determinism after a loop_stall (temperature bump) or think param.
|
|
103
|
+
- Next (gru pick): friction issue 'pytest/venv unrunnable in bash sandbox' (blocks tether-as-swe, evidence: ses_2a9da3da steps 30-40) -> clear improvement for dogfooding; then #145 reproduce, #27.
|
|
104
|
+
- 2026-10-25 MERGED (PO yes): #146 (#144, 1/4 headroom), #148 (gru takeover). PR #149 (sandbox venv under HOME, follow-up to #76): before exit 127 / after exit 0 for .venv/bin/pytest in bwrap; unit+bwrap tests; e2e 65 pass; awaits PO merge yes.
|
|
105
|
+
- Next picks (gru): validate #149 by re-running a small tether-as-swe card with a --verify gate on .venv/bin/pytest; then #145 (ollama chat-template 500, reproduce), #27 (folding placeholders), #136 retry with temperature bump after loop_stall. Main checkout is stale (local main diverged from origin/main by two pre-squash log commits): PO to OK `git reset --hard origin/main` there.
|
|
106
|
+
- 2026-10-25 PO: docs plan. Audit -> epic #150 (steps: CLI ref #61, providers #60+#62, site structure #151, story guides #152, troubleshooting #153, benchmarks #154, README #63, llms.txt #64, polish #65/#66), all in milestone v0.2.10 (v0.2.11 closed, empty). AGENTS.md 5.6 now states the two audiences (docs site = users/integrators, no bug history; README = evaluate/build/contribute). #145 fix on branch fix/79-stats-offenders awaiting fast-set bench then PR.
|
|
107
|
+
- 2026-10-25 Tether friction (docs cards on Nemotron Ultra): (1) ses_f6f847e5 CLI page and ses_7fb5863b providers page both ended at max steps WITHOUT calling done after the work was finished (keeps verifying); (2) rewriting a docs page it invented API facts (wrong ModelResponse fields, register_provider signature, nonexistent settings) -> docs rewrites by tether need a gru fact-check or assemble-from-source approach; (3) 5 steps in 10 min on a 156-line page (slow generation of long outputs on the free endpoint). Merged: #155 policy, #156 CLI ref (#61). PR for #60/#62 open. #145 PR waits for fast-set bench.
|
|
108
|
+
- 2026-10-25 PO: milestones reshuffled: v0.2.8 = release now (#144 #145 + sandbox venv + CLI/providers docs), v0.2.9 = docs overhaul (#150-#154, #63-#66), v0.2.10 = #27 #79 #132-#137. Merged: #146 #148 #149 #155 #156 #157 #158. Release PR v0.2.8 (version bump, notes) prepared; GitHub Release only on PO yes.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Work on v0.2.4
|
|
2
|
+
- Slug: work-on-v0-2-4
|
|
3
|
+
- Status: done
|
|
4
|
+
- Started: 2026-10-20 Updated: 2026-10-20 Closed: 2026-10-21
|
|
5
|
+
- Branches / PRs: worktree branch `claude/caveman-full-3e3119` (base main @ 8f0e0dd)
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
Work toward Tether v0.2.4 (follows v0.2.3 release, #94). Milestone v0.2.4: #88, #85, #75, #74.
|
|
9
|
+
|
|
10
|
+
## Requests
|
|
11
|
+
- 2026-10-20: `/keep-feature-context new work-on-v0.2.4` — start log for v0.2.4 work.
|
|
12
|
+
|
|
13
|
+
## Decisions
|
|
14
|
+
- 2026-10-20: SWE = tether headless, model `nvidia/nvidia/nemotron-3-ultra-550b-a55b` (provider `nvidia` in ~/.tether/providers.yaml, key NVIDIA_API_KEY from ~/.profile). Verify gates: ruff, mypy, pytest cov 100. 3 parallel worktrees (#75+#74 share harness loop so one session).
|
|
15
|
+
- 2026-10-20: Session runs with caveman (full) + ponytail (full) modes; code/commits/PRs written normal.
|
|
16
|
+
|
|
17
|
+
## Problems
|
|
18
|
+
|
|
19
|
+
## Problems
|
|
20
|
+
- 2026-10-20: 3 parallel nemotron sessions all hit NVIDIA HTTP 429 (3 retries). Run sequentially.
|
|
21
|
+
- 2026-10-20: #88 ses_c488b7de resumed: 93/80 steps (max_steps overshoot = #75), 0 edits, only reads/sed. Retry ses_eee3fb0f with exact plan: 60/60 steps, FAILED, tree clean; last cmd `edit replace ... --lines 62:62` (invalid syntax guess). Nemotron-3-ultra via tether not landing edits; likely needs `edit --help` discipline or stronger model.
|
|
22
|
+
|
|
23
|
+
## Progress log
|
|
24
|
+
- 2026-10-20: Log created. Existing related log: `active/v0-2-bugs.md`.
|
|
25
|
+
|
|
26
|
+
- 2026-10-20: Milestone v0.2.4 = #88, #85, #75, #74 (all bugs). Launched: #88 ses_c488b7de (~/worktrees v024-88, fix/88-provider-deadline); #85 ses_b5cc20de (v024-85, fix/85-stale-running); #75+#74 ses_008272e8 (v024-75-74, fix/75-74-maxsteps-done-nudge). Worktrees under .claude/worktrees/.
|
|
27
|
+
|
|
28
|
+
- 2026-10-20 FINDINGS (dogfooding nemotron-3-ultra via tether, runs ses_c488b7de/eee3fb0f/2eaaba60/760137c4):
|
|
29
|
+
1. Model NEVER used the `stdin` param (0 of 150+ turns); wrote heredocs/quoted text in `cmd` -> `edit replace` rejected 4x. FIX COMMITTED: a61b312 on branch fix/heredoc-stdin (worktree v024-88): multiplexer turns trailing heredoc in cmd into stdin (not for bash). Tests 100% cov, docs updated. After fix model did land an edit (first time).
|
|
30
|
+
2. Read thrash: model cycles ollama->anthropic->openai->base->test files (period-5). `live_reads` MAX_LIVE_READS=4 < 5 files -> LRU thrash, each re-read evicts next. `detect_cycle` only catches 2-gram x3 / 3-gram x2, window 8 -> never fires. Raising active_char_cap 2400->8000 did NOT help (27/46 repeat cmds); reverted.
|
|
31
|
+
3. NVIDIA free tier 429s even sequentially; `post_with_retry` retries 2x with 1s,2s backoff, no Retry-After -> sessions die FAILED (ses_760137c4 at 46/60).
|
|
32
|
+
4. max_steps overshoot (93/80) after `send` resume = #75; no done nudge = #74.
|
|
33
|
+
5. gh needs auth inside sandbox (GH_TOKEN env dropped) -> model cannot read issues; paste issue body into goal.
|
|
34
|
+
Ideas: detect_cycle n up to 5 w/ window 12; MAX_LIVE_READS 4->8 (or scale with budget); 429 honors Retry-After + longer backoff; completion/explore-budget nudge (#74).
|
|
35
|
+
|
|
36
|
+
- 2026-10-21 PRESET SEARCH (unscoped #88, nemotron-3-ultra, harness script scratchpad/exp.sh; all FAILED, 0 reached done):
|
|
37
|
+
| run | change | tokens | steps | result |
|
|
38
|
+
| ctrl x2 | none | 343-423k | 60 | 0 edit-tool calls, 8-20 repeats |
|
|
39
|
+
| A | skill text (bash prefix, stdin, edit flow) | 395k | 60 | no edits |
|
|
40
|
+
| B | fold-after 8, ctx 32k | 368k | 60 | no edits |
|
|
41
|
+
| C0/C1 | SEARCH/REPLACE edit tool (+skill) | 403k/422k | 60 | edited 3-4 files via bash patch scripts at step ~55, unfinished |
|
|
42
|
+
| D | C1 + 120 steps | 784k | 101 | died on 429; still re-reading at 101 |
|
|
43
|
+
| E | obs cap 8000 + fold 8 + ctx 32k + skill | 447k | 60 | 39 repeats, no edit |
|
|
44
|
+
Replay of stuck step 58: model reasoning = 'now implement' but action = `cat base.py` again; echoing reasoning_content back did NOT change it (4/4 still cat). Observations fully visible (8099 chars, not elided). Knobs (fold, cap, ctx, steps, skill text) do not fix; only SCOPED goals (file+range+exact plan) produced edits (ses_8a959866).
|
|
45
|
+
Committed on branch fix/88-provider-deadline (worktree v024-88), all 100% cov: heredoc->stdin; 429 Retry-After + 5 attempts; MAX_LIVE_READS 8; reads keyed by path+range; .py parse warning on edit; SEARCH/REPLACE blocks.
|
|
46
|
+
|
|
47
|
+
- 2026-10-21 #85 SCOPED BRIEF WORKS: brief = scope (file+methods), numbered plan, test spec, run cmd (scratchpad/goal85.txt, exp3.sh, skill C). ses_acb4e576: 40 steps, 214k tokens, correct `_reap_dead` + test, but hit max_steps before done. Gaps I fixed by hand: PermissionError (other-user pid = alive; my plan omitted it), test_agent_interface fake-RUNNING session needed pid=os.getpid(), release notes/AGENTS.md. Committed ef08560 on fix/85-reap-dead-worker (from main, clean for PR). Model diff saved scratchpad/nemotron-85.diff.
|
|
48
|
+
Lessons: scoped brief >> any knob; brief must list edge cases/existing tests that interact; give 50% more steps than the plan needs (it burned ~12 steps on test/doc fiddling via python-heredoc rewrites).
|
|
49
|
+
|
|
50
|
+
- 2026-10-21 #88 via scoped brief: ses_b251069e, 32 steps, 126k tokens, correct first pass (wait_for + test + docs). Session FAILED only from gate_error: verify gate used `uv run` but sandbox env has no uv on PATH -> use `.venv/bin/ruff|mypy|pytest` in --verify (exp3.sh updated). Rebased onto main as fix/88-attempt-deadline, PR #101. Brief template that works: scope+files, numbered plan with exact lines/strings, indentation warning, test spec naming helpers to reuse/avoid, docs bullets, run cmd, done. Skill C (SEARCH/REPLACE hint) used.
|
|
51
|
+
- 2026-10-21 #74 brief (done-nudge: is_quiet() + QUIET_NUDGE_STEPS=6 one-shot [NUDGE] appended to observation) running as ses via exp3.sh F74. #75 design pending: max_steps semantic = per-run budget; `step_limit = step_start + rec.max_steps` in execute_session (harness.py ~593) is why 46/40; need display fix in status.
|
|
52
|
+
|
|
53
|
+
- 2026-10-21 MERGED (squash, order #103,#96,#98,#101,#102,#99,#97,#100). Conflicts handled: release-notes (scratchpad/rn.py re-applies each PR's bullets onto main), AGENTS.md + tests appended by two PRs (kept both), golden snapshot drift on #97 and #100 reviewed + accepted (intended: heredoc step ok; parse warning/SEARCH-REPLACE). Gotcha: PR #97 head stuck stale after push; empty commit resynced (land.sh guards now). Gap found: golden suite did not exercise #99 -> PR #104 adds read_range_pingpong (verified: reverting #99 fails only that trace).
|
|
54
|
+
- #75 (PR #102) source change was nemotron's (scoped brief); #74 done-nudge not yet done (H74 rerun with all fixes in progress).
|
|
55
|
+
|
|
56
|
+
- 2026-10-21 DONE: #104 and #105 merged (#74 closed). Milestone v0.2.4 issues #74 #75 #85 #88 all CLOSED; main at ce72047. #74 nudge: nemotron wrote helper+loop wiring (50 steps), finished by hand (line wrap, coverage, AGENTS.md, golden trace done_nudge).
|
|
57
|
+
|
|
58
|
+
- 2026-10-21 PO REDIRECT: benchmark/baseline (Layer 2) dropped as unmaintainable (archived in scratchpad/bench-archive, branch feat/bench local only). Replaced by e2e: PR #106 `e2e/` (real CLI + fault-injecting proxy `e2e/proxy.py` in front of ollama `tether-ollama` container localhost:11435; models gemma4:e4b qwen3:8b qwen3-coder:30b qwen3.5:9b). 38 scenarios, `TETHER_E2E=1 uv run pytest e2e`, local only. Hard asserts on tether behaviour (any model), real-model success = soft warning. Mutation-checked (4 fixes reverted -> matching scenario red). PO rule added to AGENTS.md: run e2e locally before every merge, result in PR, mitigate every failure.
|
|
59
|
+
Gaps listed in docs/testing.md: sub-agents, pipelines, MCP, stats/retro/events/misses, other providers, web search, skill tools.py, --no-network, context budget #81.
|
|
60
|
+
|
|
61
|
+
- 2026-10-21 PO 'go ahead': merged #106 (e2e infra), then extended coverage in #107 -> 54 e2e scenarios (agents, pipeline, providers, MCP, web, skills tools.py, telemetry, --no-network, context budget), mutation-checked, e2e result 54 passed/91s. e2e rule in AGENTS.md satisfied (result in PR body).
|
|
62
|
+
|
|
63
|
+
## Next steps
|
|
64
|
+
- [x] #75/#74/#88/#85 all merged
|
|
65
|
+
- [ ] v0.2.4 release PR (version bump + release notes heading `## v0.2.4 (2026-10-21)`), PO OK needed
|
|
66
|
+
- [x] e2e merged (#106, #107)
|
|
67
|
+
- [ ] v0.2.4 release PR (version bump, dated release notes); PO wanted a green regression run first: e2e 54/54 is that. Needs PO go-ahead
|
|
68
|
+
- [ ] Remaining e2e gaps listed in docs/testing.md; cleanup worktrees/branches (ask PO)
|
|
69
|
+
- [ ] Clean up merged branches + worktrees (v024-*, exp-*, gold-demo) - ask PO
|
|
70
|
+
- [x] (all merged 2026-10-21 except #104) PRs opened 2026-10-21: #96 (#85 reap dead worker), #97 heredoc->stdin, #98 429 retry, #99 reads per range+window 8, #100 edit parse warning + SEARCH/REPLACE. Old local branch fix/88-provider-deadline (8 commits) superseded, unpushed.
|
|
71
|
+
- [ ] Merge order matters for docs/release-notes.md (each adds under '## v0.2.4 (unreleased)'; later PRs need trivial rebase)
|
|
72
|
+
- [ ] Watch CI on #96-#100
|
|
73
|
+
- [ ] Release notes v0.2.4 + release PR
|
|
74
|
+
|
|
75
|
+
## Sessions
|
|
76
|
+
| Date | Session note |
|
|
77
|
+
| 2026-10-20 | Created in worktree caveman-full-3e3119 |
|
|
78
|
+
|
|
79
|
+
## Wrap-up
|
|
80
|
+
- Outcome: v0.2.4 RELEASED. Release PR #108 merged (05bcf15), GitHub Release v0.2.4 'Regression Safety Net', delivery.yml success, PyPI tether-sdk 0.2.4 live, milestone v0.2.4 closed (#74 #75 #85 #88 all closed).
|
|
81
|
+
- Shipped PRs: #96 (#85), #97 heredoc->stdin, #98 429 retry, #99 reads per range + window 8, #100 edit parse warning + SEARCH/REPLACE, #101 (#88), #102 (#75), #103 golden traces, #104 ping-pong golden, #105 (#74 done nudge), #106 + #107 e2e (54 scenarios), #108 release.
|
|
82
|
+
- Final decisions: bench/baseline approach dropped as unmaintainable; regression strategy = golden traces in CI (deterministic, snapshot diffs) + local e2e behind a fault-injecting ollama proxy (assert tether behaviour, never model skill). AGENTS.md rule: run e2e locally before every merge, result in PR, mitigate every failure.
|
|
83
|
+
- Findings on using tether with nemotron-3-ultra: token knobs (fold, caps, ctx, steps, skill text) do not help on unscoped tasks in a big repo (340-780k tokens, no finish); scoped briefs (file + exact plan + test spec + edge cases) finish (#88: 32 steps/126k tokens; #85: 40/214k). On a tiny repo an unscoped task passes in 6 steps/4k tokens (repo size + exploration is the cost). Models write edits via bash heredocs/patch scripts; the `edit` tool needed tolerance (heredoc, unterminated SEARCH). Verify gates must use `.venv/bin/*` (no uv on sandbox PATH). NVIDIA free tier 429s need Retry-After + longer backoff.
|
|
84
|
+
- Follow-ups / debt: e2e gaps in docs/testing.md (pipeline verify gates/model routing, `mem pin --project`, `retro --apply`, Anthropic caching); the 'coding preset' idea became 'scoped brief template' (not shipped as a skill/file); main checkout /home/walkdead/Projects/tether is behind origin/main (not touched); PR bodies for #96..#108 mention nemotron-written pieces.
|
|
85
|
+
- Lessons: PR head can go stale after a push (empty commit resyncs; check headRefOid before trusting CI); golden suites need a trace per fix or the fix is unlocked (#99 gap); mutation-check every new test; `pkill -f` pattern can kill your own shell.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tether_repo": "igormcsouza/tether",
|
|
3
|
+
"timeout_min": 15,
|
|
4
|
+
"tiers": {
|
|
5
|
+
"0": ["tether:nvidia/nvidia/nemotron-3-ultra-550b-a55b", "tether:nvidia/z-ai/glm-5.3", "opencode/nemotron-3-ultra-free"],
|
|
6
|
+
"1": ["opencode:anthropic/claude-haiku-5-5"],
|
|
7
|
+
"2": ["opencode:anthropic/claude-sonnet-5-5"]
|
|
8
|
+
},
|
|
9
|
+
"context": {"nvidia/nvidia/nemotron-3-ultra-550b-a55b": 128000, "nvidia/z-ai/glm-5.3": 128000},
|
|
10
|
+
"sensitive": [{"path": ".github/**", "min_tier": 2}, {"path": "tether/sandbox.py", "min_tier": 2}],
|
|
11
|
+
"verified": {}
|
|
12
|
+
}
|
|
@@ -211,6 +211,10 @@ uv run mypy tether/
|
|
|
211
211
|
6. **Documentation Integrity:**
|
|
212
212
|
- Keep `README.md` and `AGENTS.md` synchronized whenever architectural interfaces or built-in tool signatures change.
|
|
213
213
|
- **Always update documentation in the same PR** when introducing a new feature, changing an existing behavior, or altering a CLI/SDK interface. This covers `README.md`, `AGENTS.md`, the MkDocs pages under `docs/` (including `docs/reference/*` for new modules), and a `docs/release-notes.md` entry. A change without matching docs is incomplete.
|
|
214
|
+
- **Two audiences, two jobs.** Every doc change is judged against the one it serves:
|
|
215
|
+
- **Docs site (`docs/`, mkdocs on GitHub Pages)** is for people who *use* tether as a tool and for agents that integrate it. It explains how tether works and how to get a job done, in plain language, with a story and a real, re-runnable example per guide (command + real output). It does **not** carry our bug history ("fixed in vX"), half-finished work, internal experiments or stale benchmarks; a known problem is a link to its GitHub issue (label `known-issue`). Contributor material (golden traces, releasing) lives in `CONTRIBUTING.md`, not in the user nav. Each page opens with who it is for and what you can do after reading it. One canonical page per topic (e.g. providers); everything else links to it, nothing is copied.
|
|
216
|
+
- **`README.md`** is for people evaluating tether, building their own harness on it, or contributing: why tether is interesting (the **current** benchmark, regenerated each release by `bench/report.py`), the architecture and *why this and not the alternative*, a short install + first run, and how to contribute. It points to the docs site for everything else and stays short (target ~150 lines).
|
|
217
|
+
- The step plan is tracked in epic #150; a docs PR names the step it completes.
|
|
214
218
|
|
|
215
219
|
## Dev team specifics
|
|
216
220
|
- Stack: Python 3.12+, package `tether/`, tests in `tests/`, docs in `docs/` (MkDocs); `uv`, ruff, mypy
|
|
@@ -218,6 +222,8 @@ uv run mypy tether/
|
|
|
218
222
|
- E2E / QA: `e2e/` scenarios (real CLI + ollama behind a fault-injecting proxy, local only); shared resource: the `tether-ollama` container (port 11435)
|
|
219
223
|
- Branches/commits/PRs: Conventional Commits (`feat(scope): ... (#N)`, `release: vX.Y.Z ...`); PR per task, squash-merged to `main`; docs updated in the same PR (see section 5.4)
|
|
220
224
|
- Delivery: PR per task
|
|
221
|
-
- Hard stops (ask first): force push / history rewrite
|
|
225
|
+
- Hard stops (ask first): force push / history rewrite; merging a PR and cutting a release need the PO's explicit yes each time
|
|
222
226
|
- Design docs: `AGENTS.md` sections 1-3, `docs/architecture.md`, `docs/reference/*`
|
|
223
|
-
-
|
|
227
|
+
- Orchestration: **`/gru` runs this repo** (Team Leader + router). Strong model plans, writes the failing acceptance tests first, reviews and decides; tether does the typing as the minion at Tier 0 on the strongest free model (`nvidia/nvidia/nemotron-3-ultra-550b-a55b`, fallback `nvidia/z-ai/glm-5.3`), escalating one tier only on evidence. Tiers, context sizes and sensitive paths: `.claude/gru.json`. tether's own failures while doing a card are logged as friction and filed as issues (with the PO's OK).
|
|
228
|
+
- Improvement loop: **every PR leaves tether a little better**, measured, not asserted: a behaviour change ships with a bench or e2e delta (Polyglot-Python fast set on `ollama/qwen3.5:9b`, `bench/`) in the PR body, reported honestly even when it is flat. After a merge, gru re-reads the next planned milestone and the other open bug issues and picks the one that clearly improves tether most (evidence from bench failure breakdowns and `tether stats` friction beats guesses); features and docs wait behind bugs that cost steps or pass rate.
|
|
229
|
+
- gru store: .claude/gru/ (committed; legacy `feature-context` logs were moved here)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tether-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: General-purpose, headless-first LLM-Native micro-harness for small language models
|
|
5
5
|
Project-URL: Homepage, https://github.com/igormcsouza/tether
|
|
6
6
|
Project-URL: Repository, https://github.com/igormcsouza/tether
|
|
@@ -286,25 +286,7 @@ Cursor, Gemini CLI, VS Code and Claude Desktop configs, a "let your harness inst
|
|
|
286
286
|
|
|
287
287
|
### Any model, no code: providers.yaml
|
|
288
288
|
|
|
289
|
-
Declare extra
|
|
290
|
-
|
|
291
|
-
Kinds: `openai`, `ollama`, `anthropic` and `command`. The `command` kind wraps any program that takes a prompt and prints JSON (a `claude -p`, `codex exec` or `gemini -p` style command), and runs `argv` directly, never through a shell. Keys are given as env var names, never values.
|
|
292
|
-
|
|
293
|
-
```yaml
|
|
294
|
-
claude-cli:
|
|
295
|
-
kind: command
|
|
296
|
-
argv: [claude, -p, --model, "{model}", --tools, "", --system-prompt, "{system}", --output-format, json, --no-session-persistence, --setting-sources, "", --disable-slash-commands, --strict-mcp-config]
|
|
297
|
-
stdin: "{conversation}"
|
|
298
|
-
reply: result
|
|
299
|
-
usage: {prompt: [usage.input_tokens, usage.cache_creation_input_tokens, usage.cache_read_input_tokens], completion: usage.output_tokens}
|
|
300
|
-
timeout: 180
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
```bash
|
|
304
|
-
tether run --model claude-cli/sonnet "fix the failing test"
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
On one small bug-fix task (v0.2.0 test), this backend used about 15x fewer tokens than plain `claude -p` (21k vs 330k). That is a single small task, not a benchmark. See [Declarative providers](docs/reference/providers.md#declarative-providers).
|
|
289
|
+
Declare extra model backends (a gateway, a remote Ollama, a `claude -p` style command) in `~/.tether/providers.yaml`. One entry per prefix, four kinds, no code. Worked examples, the Python route and every setting: [Model providers](https://igormcsouza.github.io/tether/reference/providers/).
|
|
308
290
|
|
|
309
291
|
### Python SDK Usage
|
|
310
292
|
|
|
@@ -337,46 +319,9 @@ if __name__ == "__main__":
|
|
|
337
319
|
asyncio.run(main())
|
|
338
320
|
```
|
|
339
321
|
|
|
340
|
-
### Custom
|
|
341
|
-
|
|
342
|
-
Want to route Tether through a custom transport—such as an internal corporate gateway, a custom local inference server, or a mock/replay harness for testing? Implement the 1-method `ModelProvider` protocol or register a custom URL prefix with `register_provider`:
|
|
322
|
+
### Custom providers in Python
|
|
343
323
|
|
|
344
|
-
|
|
345
|
-
import httpx
|
|
346
|
-
from tether import (
|
|
347
|
-
Harness,
|
|
348
|
-
ModelResponse,
|
|
349
|
-
extract_fallback_action,
|
|
350
|
-
register_provider,
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
class CustomGatewayProvider:
|
|
355
|
-
"""Routes Tether turns through a custom internal LLM gateway."""
|
|
356
|
-
|
|
357
|
-
def __init__(self, model_name: str) -> None:
|
|
358
|
-
self.model_name = model_name
|
|
359
|
-
|
|
360
|
-
async def generate(self, messages: list[dict], tools: list[dict]) -> ModelResponse:
|
|
361
|
-
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
362
|
-
resp = await client.post(
|
|
363
|
-
"https://llm-gateway.internal/v1/generate",
|
|
364
|
-
json={"model": self.model_name, "messages": messages, "tools": tools},
|
|
365
|
-
)
|
|
366
|
-
data = resp.json()
|
|
367
|
-
text = str(data.get("text") or "").strip()
|
|
368
|
-
return ModelResponse(
|
|
369
|
-
content=text,
|
|
370
|
-
action=extract_fallback_action(text),
|
|
371
|
-
prompt_tokens=int(data.get("total_tokens") or 0),
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
# Register as a custom `<prefix>/<model>` for Harness, Skills, and Sub-Agents:
|
|
376
|
-
register_provider("gateway", CustomGatewayProvider)
|
|
377
|
-
|
|
378
|
-
harness = Harness(model="gateway/qwen-enterprise-32b", tools=["web", "mem"])
|
|
379
|
-
```
|
|
324
|
+
Route Tether through your own transport with `register_provider(prefix, factory)` or `Harness(provider=...)`: see [Model providers](https://igormcsouza.github.io/tether/reference/providers/#add-your-own-in-python).
|
|
380
325
|
|
|
381
326
|
---
|
|
382
327
|
|
|
@@ -265,25 +265,7 @@ Cursor, Gemini CLI, VS Code and Claude Desktop configs, a "let your harness inst
|
|
|
265
265
|
|
|
266
266
|
### Any model, no code: providers.yaml
|
|
267
267
|
|
|
268
|
-
Declare extra
|
|
269
|
-
|
|
270
|
-
Kinds: `openai`, `ollama`, `anthropic` and `command`. The `command` kind wraps any program that takes a prompt and prints JSON (a `claude -p`, `codex exec` or `gemini -p` style command), and runs `argv` directly, never through a shell. Keys are given as env var names, never values.
|
|
271
|
-
|
|
272
|
-
```yaml
|
|
273
|
-
claude-cli:
|
|
274
|
-
kind: command
|
|
275
|
-
argv: [claude, -p, --model, "{model}", --tools, "", --system-prompt, "{system}", --output-format, json, --no-session-persistence, --setting-sources, "", --disable-slash-commands, --strict-mcp-config]
|
|
276
|
-
stdin: "{conversation}"
|
|
277
|
-
reply: result
|
|
278
|
-
usage: {prompt: [usage.input_tokens, usage.cache_creation_input_tokens, usage.cache_read_input_tokens], completion: usage.output_tokens}
|
|
279
|
-
timeout: 180
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
```bash
|
|
283
|
-
tether run --model claude-cli/sonnet "fix the failing test"
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
On one small bug-fix task (v0.2.0 test), this backend used about 15x fewer tokens than plain `claude -p` (21k vs 330k). That is a single small task, not a benchmark. See [Declarative providers](docs/reference/providers.md#declarative-providers).
|
|
268
|
+
Declare extra model backends (a gateway, a remote Ollama, a `claude -p` style command) in `~/.tether/providers.yaml`. One entry per prefix, four kinds, no code. Worked examples, the Python route and every setting: [Model providers](https://igormcsouza.github.io/tether/reference/providers/).
|
|
287
269
|
|
|
288
270
|
### Python SDK Usage
|
|
289
271
|
|
|
@@ -316,46 +298,9 @@ if __name__ == "__main__":
|
|
|
316
298
|
asyncio.run(main())
|
|
317
299
|
```
|
|
318
300
|
|
|
319
|
-
### Custom
|
|
320
|
-
|
|
321
|
-
Want to route Tether through a custom transport—such as an internal corporate gateway, a custom local inference server, or a mock/replay harness for testing? Implement the 1-method `ModelProvider` protocol or register a custom URL prefix with `register_provider`:
|
|
301
|
+
### Custom providers in Python
|
|
322
302
|
|
|
323
|
-
|
|
324
|
-
import httpx
|
|
325
|
-
from tether import (
|
|
326
|
-
Harness,
|
|
327
|
-
ModelResponse,
|
|
328
|
-
extract_fallback_action,
|
|
329
|
-
register_provider,
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
class CustomGatewayProvider:
|
|
334
|
-
"""Routes Tether turns through a custom internal LLM gateway."""
|
|
335
|
-
|
|
336
|
-
def __init__(self, model_name: str) -> None:
|
|
337
|
-
self.model_name = model_name
|
|
338
|
-
|
|
339
|
-
async def generate(self, messages: list[dict], tools: list[dict]) -> ModelResponse:
|
|
340
|
-
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
341
|
-
resp = await client.post(
|
|
342
|
-
"https://llm-gateway.internal/v1/generate",
|
|
343
|
-
json={"model": self.model_name, "messages": messages, "tools": tools},
|
|
344
|
-
)
|
|
345
|
-
data = resp.json()
|
|
346
|
-
text = str(data.get("text") or "").strip()
|
|
347
|
-
return ModelResponse(
|
|
348
|
-
content=text,
|
|
349
|
-
action=extract_fallback_action(text),
|
|
350
|
-
prompt_tokens=int(data.get("total_tokens") or 0),
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
# Register as a custom `<prefix>/<model>` for Harness, Skills, and Sub-Agents:
|
|
355
|
-
register_provider("gateway", CustomGatewayProvider)
|
|
356
|
-
|
|
357
|
-
harness = Harness(model="gateway/qwen-enterprise-32b", tools=["web", "mem"])
|
|
358
|
-
```
|
|
303
|
+
Route Tether through your own transport with `register_provider(prefix, factory)` or `Harness(provider=...)`: see [Model providers](https://igormcsouza.github.io/tether/reference/providers/#add-your-own-in-python).
|
|
359
304
|
|
|
360
305
|
---
|
|
361
306
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Extending Tether
|
|
2
|
+
|
|
3
|
+
Tether is designed around small, explicit protocols so you can plug in custom model gateways, local inference engines, or domain tool suites without subclassing framework base classes.
|
|
4
|
+
|
|
5
|
+
## 1. Custom Model Providers
|
|
6
|
+
|
|
7
|
+
Out of the box tether talks to Ollama, OpenAI-compatible servers and Anthropic. To reach anything else (an internal gateway, a custom inference runner, a replay harness for tests) declare it in `providers.yaml` or register a Python class with `register_provider`. Both routes, with worked examples, are on one page: [Model providers](reference/providers.md).
|
|
8
|
+
|
|
9
|
+
## 2. Custom `ToolSpec` Namespaces
|
|
10
|
+
|
|
11
|
+
For simple single-command tools, the `@tool` decorator is all you need. If you want to build a multi-subcommand namespace like `web` (`web search`, `web fetch`) or `mem` (`mem pin`, `mem search`, `mem recall`), construct a `ToolSpec` directly:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from tether import Harness, ToolSpec
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def _git_dispatch(**kwargs: object) -> str:
|
|
18
|
+
raw_args = str(kwargs.get("args", "")).strip()
|
|
19
|
+
if raw_args in ("", "status"):
|
|
20
|
+
return "On branch main — working tree clean"
|
|
21
|
+
return f"Executed git {raw_args}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
git_tool = ToolSpec(
|
|
25
|
+
name="git",
|
|
26
|
+
description="Inspect repository status and history",
|
|
27
|
+
fn=_git_dispatch,
|
|
28
|
+
params=[],
|
|
29
|
+
accepts_stdin=False,
|
|
30
|
+
is_async=True,
|
|
31
|
+
usage_override=(
|
|
32
|
+
"Usage: git <status|log|diff>\n"
|
|
33
|
+
"Inspect repository state.\n"
|
|
34
|
+
" git status : Show working tree status"
|
|
35
|
+
),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
harness = Harness(tools=[git_tool])
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Command normalizers
|
|
42
|
+
|
|
43
|
+
Models bring habits the `act` grammar does not expect (wrapping commands in `bash -lc '...'`, prefixing a vendor tag). Built-in fixes live in `tether.multiplexer.NORMALIZERS`; add your own with `tether.register_normalizer(fn)`, a deterministic `str -> str` that returns its input unchanged when it does not apply. It runs for every model after the built-ins, before the command is parsed.
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import tether
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@tether.register_normalizer
|
|
50
|
+
def drop_vendor_prefix(cmd: str) -> str:
|
|
51
|
+
return cmd.removeprefix("vendor:")
|
|
52
|
+
```
|