@tachu/extensions 1.0.0-alpha.1
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.
- package/CHANGELOG.md +119 -0
- package/LICENSE +201 -0
- package/README.md +1104 -0
- package/README_ZH.md +1082 -0
- package/dist/backends/file.d.ts +18 -0
- package/dist/backends/file.d.ts.map +1 -0
- package/dist/backends/file.js +85 -0
- package/dist/backends/file.js.map +1 -0
- package/dist/backends/index.d.ts +4 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +4 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/terminal.d.ts +18 -0
- package/dist/backends/terminal.d.ts.map +1 -0
- package/dist/backends/terminal.js +81 -0
- package/dist/backends/terminal.js.map +1 -0
- package/dist/backends/web.d.ts +18 -0
- package/dist/backends/web.d.ts.map +1 -0
- package/dist/backends/web.js +55 -0
- package/dist/backends/web.js.map +1 -0
- package/dist/common/net.d.ts +39 -0
- package/dist/common/net.d.ts.map +1 -0
- package/dist/common/net.js +177 -0
- package/dist/common/net.js.map +1 -0
- package/dist/common/path.d.ts +51 -0
- package/dist/common/path.d.ts.map +1 -0
- package/dist/common/path.js +76 -0
- package/dist/common/path.js.map +1 -0
- package/dist/common/process.d.ts +19 -0
- package/dist/common/process.d.ts.map +1 -0
- package/dist/common/process.js +67 -0
- package/dist/common/process.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/sse-adapter.d.ts +82 -0
- package/dist/mcp/sse-adapter.d.ts.map +1 -0
- package/dist/mcp/sse-adapter.js +201 -0
- package/dist/mcp/sse-adapter.js.map +1 -0
- package/dist/mcp/stdio-adapter.d.ts +85 -0
- package/dist/mcp/stdio-adapter.d.ts.map +1 -0
- package/dist/mcp/stdio-adapter.js +203 -0
- package/dist/mcp/stdio-adapter.js.map +1 -0
- package/dist/memory/fs-memory-system.d.ts +147 -0
- package/dist/memory/fs-memory-system.d.ts.map +1 -0
- package/dist/memory/fs-memory-system.js +266 -0
- package/dist/memory/fs-memory-system.js.map +1 -0
- package/dist/memory/index.d.ts +2 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/observability/index.d.ts +3 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +3 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/jsonl-emitter.d.ts +58 -0
- package/dist/observability/jsonl-emitter.d.ts.map +1 -0
- package/dist/observability/jsonl-emitter.js +96 -0
- package/dist/observability/jsonl-emitter.js.map +1 -0
- package/dist/observability/otel-emitter.d.ts +52 -0
- package/dist/observability/otel-emitter.d.ts.map +1 -0
- package/dist/observability/otel-emitter.js +143 -0
- package/dist/observability/otel-emitter.js.map +1 -0
- package/dist/providers/anthropic.d.ts +73 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +521 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/mock.d.ts +81 -0
- package/dist/providers/mock.d.ts.map +1 -0
- package/dist/providers/mock.js +160 -0
- package/dist/providers/mock.js.map +1 -0
- package/dist/providers/openai.d.ts +95 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +529 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/qwen.d.ts +145 -0
- package/dist/providers/qwen.d.ts.map +1 -0
- package/dist/providers/qwen.js +669 -0
- package/dist/providers/qwen.js.map +1 -0
- package/dist/rules/index.d.ts +9 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +15 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/no-hallucination.md +11 -0
- package/dist/rules/no-sensitive-output.md +11 -0
- package/dist/rules/prefer-concise-response.md +11 -0
- package/dist/rules/require-tool-verification.md +11 -0
- package/dist/safety/default-gate.d.ts +112 -0
- package/dist/safety/default-gate.d.ts.map +1 -0
- package/dist/safety/default-gate.js +188 -0
- package/dist/safety/default-gate.js.map +1 -0
- package/dist/safety/index.d.ts +2 -0
- package/dist/safety/index.d.ts.map +1 -0
- package/dist/safety/index.js +2 -0
- package/dist/safety/index.js.map +1 -0
- package/dist/tools/_shared/web-client.d.ts +18 -0
- package/dist/tools/_shared/web-client.d.ts.map +1 -0
- package/dist/tools/_shared/web-client.js +46 -0
- package/dist/tools/_shared/web-client.js.map +1 -0
- package/dist/tools/apply-patch/descriptor.md +27 -0
- package/dist/tools/apply-patch/executor.d.ts +19 -0
- package/dist/tools/apply-patch/executor.d.ts.map +1 -0
- package/dist/tools/apply-patch/executor.js +193 -0
- package/dist/tools/apply-patch/executor.js.map +1 -0
- package/dist/tools/fetch-url/descriptor.md +44 -0
- package/dist/tools/fetch-url/executor.d.ts +28 -0
- package/dist/tools/fetch-url/executor.d.ts.map +1 -0
- package/dist/tools/fetch-url/executor.js +115 -0
- package/dist/tools/fetch-url/executor.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +286 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-dir/descriptor.md +29 -0
- package/dist/tools/list-dir/executor.d.ts +22 -0
- package/dist/tools/list-dir/executor.d.ts.map +1 -0
- package/dist/tools/list-dir/executor.js +48 -0
- package/dist/tools/list-dir/executor.js.map +1 -0
- package/dist/tools/read-file/descriptor.md +28 -0
- package/dist/tools/read-file/executor.d.ts +15 -0
- package/dist/tools/read-file/executor.d.ts.map +1 -0
- package/dist/tools/read-file/executor.js +22 -0
- package/dist/tools/read-file/executor.js.map +1 -0
- package/dist/tools/run-shell/descriptor.md +39 -0
- package/dist/tools/run-shell/executor.d.ts +20 -0
- package/dist/tools/run-shell/executor.d.ts.map +1 -0
- package/dist/tools/run-shell/executor.js +76 -0
- package/dist/tools/run-shell/executor.js.map +1 -0
- package/dist/tools/search-code/descriptor.md +31 -0
- package/dist/tools/search-code/executor.d.ts +23 -0
- package/dist/tools/search-code/executor.d.ts.map +1 -0
- package/dist/tools/search-code/executor.js +122 -0
- package/dist/tools/search-code/executor.js.map +1 -0
- package/dist/tools/shared.d.ts +47 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +27 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/web-fetch/descriptor.md +198 -0
- package/dist/tools/web-fetch/errors.d.ts +32 -0
- package/dist/tools/web-fetch/errors.d.ts.map +1 -0
- package/dist/tools/web-fetch/errors.js +91 -0
- package/dist/tools/web-fetch/errors.js.map +1 -0
- package/dist/tools/web-fetch/executor.d.ts +10 -0
- package/dist/tools/web-fetch/executor.d.ts.map +1 -0
- package/dist/tools/web-fetch/executor.js +191 -0
- package/dist/tools/web-fetch/executor.js.map +1 -0
- package/dist/tools/web-fetch/index.d.ts +4 -0
- package/dist/tools/web-fetch/index.d.ts.map +1 -0
- package/dist/tools/web-fetch/index.js +3 -0
- package/dist/tools/web-fetch/index.js.map +1 -0
- package/dist/tools/web-fetch/types.d.ts +157 -0
- package/dist/tools/web-fetch/types.d.ts.map +1 -0
- package/dist/tools/web-fetch/types.js +7 -0
- package/dist/tools/web-fetch/types.js.map +1 -0
- package/dist/tools/web-search/descriptor.md +89 -0
- package/dist/tools/web-search/errors.d.ts +33 -0
- package/dist/tools/web-search/errors.d.ts.map +1 -0
- package/dist/tools/web-search/errors.js +45 -0
- package/dist/tools/web-search/errors.js.map +1 -0
- package/dist/tools/web-search/executor.d.ts +10 -0
- package/dist/tools/web-search/executor.d.ts.map +1 -0
- package/dist/tools/web-search/executor.js +185 -0
- package/dist/tools/web-search/executor.js.map +1 -0
- package/dist/tools/web-search/index.d.ts +4 -0
- package/dist/tools/web-search/index.d.ts.map +1 -0
- package/dist/tools/web-search/index.js +3 -0
- package/dist/tools/web-search/index.js.map +1 -0
- package/dist/tools/web-search/types.d.ts +86 -0
- package/dist/tools/web-search/types.d.ts.map +1 -0
- package/dist/tools/web-search/types.js +7 -0
- package/dist/tools/web-search/types.js.map +1 -0
- package/dist/tools/write-file/descriptor.md +30 -0
- package/dist/tools/write-file/executor.d.ts +16 -0
- package/dist/tools/write-file/executor.d.ts.map +1 -0
- package/dist/tools/write-file/executor.js +18 -0
- package/dist/tools/write-file/executor.js.map +1 -0
- package/dist/transformers/document-to-text.d.ts +23 -0
- package/dist/transformers/document-to-text.d.ts.map +1 -0
- package/dist/transformers/document-to-text.js +69 -0
- package/dist/transformers/document-to-text.js.map +1 -0
- package/dist/transformers/image-to-text.d.ts +38 -0
- package/dist/transformers/image-to-text.d.ts.map +1 -0
- package/dist/transformers/image-to-text.js +82 -0
- package/dist/transformers/image-to-text.js.map +1 -0
- package/dist/transformers/index.d.ts +3 -0
- package/dist/transformers/index.d.ts.map +1 -0
- package/dist/transformers/index.js +3 -0
- package/dist/transformers/index.js.map +1 -0
- package/dist/vector/index.d.ts +3 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/index.js +3 -0
- package/dist/vector/index.js.map +1 -0
- package/dist/vector/local-fs.d.ts +76 -0
- package/dist/vector/local-fs.d.ts.map +1 -0
- package/dist/vector/local-fs.js +153 -0
- package/dist/vector/local-fs.js.map +1 -0
- package/dist/vector/qdrant.d.ts +85 -0
- package/dist/vector/qdrant.d.ts.map +1 -0
- package/dist/vector/qdrant.js +208 -0
- package/dist/vector/qdrant.js.map +1 -0
- package/package.json +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,1104 @@
|
|
|
1
|
+
# Tachu
|
|
2
|
+
|
|
3
|
+
**An agentic engine under active development — the *Harness* that aims to turn any LLM into a reliable, observable Agent.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@tachu/core)
|
|
6
|
+
[](#project-status)
|
|
7
|
+
[](#license)
|
|
8
|
+
[](https://bun.sh)
|
|
9
|
+
[](https://www.typescriptlang.org)
|
|
10
|
+
|
|
11
|
+
> **⚠️ Project Status — Alpha.** The 9-phase pipeline, registry, prompt assembler, CLI, OpenAI / Anthropic / Qwen adapters, MCP adapters, vector stores and observability emitters are wired up and individually tested. **Phase 3 (Intent Analysis) is a real LLM call**, so `tachu chat` / `tachu run` produce real conversational replies. **Phase 5 (Planning) and Phase 8 (Result Validation) are still stubbed** — requests classified as *complex* will not yet receive an LLM-generated multi-step plan or semantic validation. See [Project Status](#project-status) and [Roadmap](#roadmap) for the per-feature breakdown. Do **not** use this in production yet. Install via the `@alpha` dist-tag.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What is Tachu?
|
|
16
|
+
|
|
17
|
+
Tachu aims to be an **agentic engine you can build a real product on** — not a toy demo, not a thin wrapper. It is the *Harness* in the equation **Agent = Model + Harness**: it provides the structural skeleton (protocol, lifecycle, safety, memory, orchestration) so that any LLM becomes a reliable, observable Agent.
|
|
18
|
+
|
|
19
|
+
The engine is intentionally **domain-agnostic**: it knows nothing about your business logic, your users, or your domain vocabulary. Instead, it defines a small set of core abstractions (Rules, Skills, Tools, Agents) through which your business fills in all the intelligence. Tachu is designed to handle the hard parts — 9-phase execution pipeline, dual-plane semantic matching, context window management, token-precise prompt assembly, structured retry/fallback, cancellation propagation, and end-to-end observability.
|
|
20
|
+
|
|
21
|
+
Tachu ships as a Bun-native TypeScript monorepo with three packages: the zero-dependency engine core (`@tachu/core`), an official extensions library (`@tachu/extensions`), and a fully-featured CLI program (`@tachu/cli`) that doubles as the reference implementation.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Project Status
|
|
26
|
+
|
|
27
|
+
**Current release:** `1.0.0-alpha.1` on the `alpha` dist-tag.
|
|
28
|
+
|
|
29
|
+
This is the **first public alpha** — most infrastructure is in place, but several LLM-backed stages are still stubbed. The table below is the single source of truth; every claim made elsewhere in this README must be cross-checked against it.
|
|
30
|
+
|
|
31
|
+
| Capability | Status | Notes |
|
|
32
|
+
|-----------|--------|-------|
|
|
33
|
+
| 9-phase pipeline skeleton (types, orchestrator, state machine, hooks) | ✅ Implemented | `packages/core/src/engine` |
|
|
34
|
+
| Descriptor Registry (Rules / Skills / Tools / Agents) | ✅ Implemented | Markdown + YAML frontmatter loader, semantic indexing, startup validation |
|
|
35
|
+
| Prompt assembler (tiktoken, KV-cache-friendly ordering) | ✅ Implemented | `packages/core/src/prompt` |
|
|
36
|
+
| Task scheduler, DAG validator, retry/fallback bookkeeping | ✅ Implemented | `packages/core/src/engine/scheduler.ts` |
|
|
37
|
+
| Session / Memory / Runtime-state / Safety / Model-router / Provider / Observability / Hooks modules | ✅ Implemented | `packages/core/src/modules` |
|
|
38
|
+
| OpenAI / Anthropic / Mock Provider adapters | ✅ Implemented | streaming, function calling, tool schemas |
|
|
39
|
+
| `apiKey` / `baseURL` / `organization` / `timeoutMs` configuration (env var / `tachu.config.ts` / CLI flags) | ✅ Implemented | Azure OpenAI / LiteLLM / OpenRouter / self-hosted gateways supported |
|
|
40
|
+
| 7 built-in tools + Terminal / File / Web backends | ✅ Implemented | `packages/extensions/src/{tools,backends}` |
|
|
41
|
+
| MCP stdio + SSE adapters | ✅ Implemented | `packages/extensions/src/mcp` |
|
|
42
|
+
| `LocalFsVectorStore` (file-backed) + `QdrantVectorStore` (REST) | ✅ Implemented | |
|
|
43
|
+
| OTel / JSONL / Console emitters | ✅ Implemented | |
|
|
44
|
+
| `tachu init` / `tachu run` / `tachu chat` CLI surface, streaming renderer, session persistence, Ctrl+C semantics | ✅ Implemented | |
|
|
45
|
+
| **CLI terminal Markdown rendering** | ✅ **Implemented** | `marked` + `marked-terminal` + `cli-highlight` stack. Applied to the final assistant reply in `tachu chat` / `tachu run --output text` when stdout is a TTY; automatically disables under `NO_COLOR` / non-TTY / `--no-color`. Explicit control via `--markdown` / `--no-markdown` on `tachu run`. Dedicated `renderMarkdownToAnsi` wrapper (`packages/cli/src/renderer/markdown.ts`) with 11 unit tests in `markdown.test.ts`. |
|
|
46
|
+
| **Phase 3 — Intent Analysis (LLM call, pure classification)** | ✅ **Implemented** | Routes through `ModelRouter.resolve("intent")` → registered `ProviderAdapter`. This phase is **pure classification** — `IntentResult = { complexity, intent, contextRelevance, relevantContext? }` only; the system prompt explicitly forbids `directAnswer` / `answer` / `reply` fields and the final user-facing answer is delegated to the `direct-answer` Sub-flow (Phase 7). Ships **5 few-shot examples** (greeting / creative short / write-code / write-lesson-plan / real multi-tool complex); complexity is anchored on *"does this need real tools / external resources?"*, not output length — single-turn long-form creative asks (write code, TDD lesson plans, essays, translations) stay `simple`. Bounded history window (up to 10 recent `MemorySystem` entries) and 30 s per-call timeout composed with the phase-level abort signal. Robust JSON extraction (plain / fenced / embedded); when the LLM ignores the JSON protocol but returns meaningful text, the text is accepted as the `intent` summary and the request still flows to `direct-answer`. Heuristic `intent = input.slice(0, 200)` fallback is used only on *no usable content* (provider unregistered, network / timeout error, cancellation, empty response). 10 dedicated tests in `intent.test.ts`. |
|
|
47
|
+
| **Phase 5 — Task Planning (fallback contract)** | ✅ **Implemented** | Enforces `plans[0].tasks.length >= 1`. Rules: (1) `simple` intent → single `direct-answer` sub-flow task; (2) `complex` + matched tools → first-N tool tasks (unchanged); (3) `complex` + no matching tool → single `direct-answer` sub-flow task with `warn: true` (the sub-flow honestly discloses the lack of tool matching); (4) defensive post-guard catches upstream regressions that leave `tasks` empty. Real LLM-backed planner (producing ranked multi-step plans for tool-chain complex requests) is still scheduled for a later alpha. |
|
|
48
|
+
| **`direct-answer` built-in Sub-flow (Phase 7)** | ✅ **Implemented** | `packages/core/src/engine/subflows/direct-answer.ts`. Resolves `capabilityMapping.intent` (fallback to `fast-cheap`), composes system + ≤10 memory-history entries + user prompt, calls `ProviderAdapter.chat()` with a 60 s per-call timeout merged with the phase abort signal. System prompt mandates **natural-language Markdown**, forbids JSON wrappers / `"已识别请求:…"` templates / 4-space indented code blocks, and supports a `warn: true` flag for honest tool-missing disclaimers. Emits `llm_call_start` / `llm_call_end` observability events under `phase: "direct-answer"`. Non-overridable: `DescriptorRegistry` registers `direct-answer` as a reserved name and rejects business registration / unregistration with `RegistryError.reservedName`. See [ADR 0001](docs/adr/decisions/0001-direct-answer-as-builtin-subflow.md). |
|
|
49
|
+
| **Phase 8 — Result Validation (LLM call)** | 🟡 **Stub** | Only checks whether any step `failed`; no semantic validation. Scheduled for a follow-up alpha. |
|
|
50
|
+
| **Phase 9 — Output Assembly** | ✅ **Implemented** | Content selector: `taskResults['task-direct-answer']` → `{intent, taskResults}` structured JSON (tool-chain success, reshape pending real Phase 5/8) → honest-fallback plain-language message with recognized intent + internal diagnosis + *"rephrase as simple"* suggestion (validation failed). Internal state JSON is never leaked to end users. 6 dedicated tests in `output.test.ts`. |
|
|
51
|
+
| Real-world smoke tests against OpenAI / Anthropic / Azure | 🔴 Not yet | Adapters are unit-tested with mocks; we have not yet published a signed-off end-to-end record. |
|
|
52
|
+
| Production hardening (SLO, error budgets, failure injection, signed provenance) | 🔴 Not yet | v1 target. |
|
|
53
|
+
|
|
54
|
+
Legend: ✅ implemented and tested · 🟡 stub / placeholder present, real implementation in progress · 🔴 not yet started.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Key Features
|
|
59
|
+
|
|
60
|
+
> Features marked **(stub)** are wired end-to-end but do not yet call an LLM — see the [Project Status](#project-status) table.
|
|
61
|
+
|
|
62
|
+
- **9-Phase Execution Pipeline** — session management → safety → intent analysis (pure classification) → pre-check → planning (fallback contract) → DAG validation → execution → result validation *(stub)* → output normalization; each phase is a typed, hookable stage, and every request — simple or complex — flows through all nine phases with uniform Rules / Hooks / Observability / budget accounting
|
|
63
|
+
- **`direct-answer` built-in Sub-flow** — answers to simple requests (and to complex requests with no matching tool) are produced by a first-class engine-internal Sub-flow running in Phase 7, not baked into the intent prompt. See [ADR 0001](docs/adr/decisions/0001-direct-answer-as-builtin-subflow.md).
|
|
64
|
+
- **Dual-Plane Matching** — semantic discovery (vector similarity) + deterministic execution gate (scopes, whitelist, approval) for every Rule, Skill, Tool, and Agent
|
|
65
|
+
- **Four Core Abstractions** — declare Rules, Skills, Tools, and Agents as Markdown + YAML frontmatter descriptors; the engine resolves, activates, and orchestrates them automatically
|
|
66
|
+
- **OpenAI & Anthropic Adapters** — streaming, function calling, configurable `baseURL` / `organization` / `timeoutMs`; works with Azure OpenAI, LiteLLM, OpenRouter, or any self-hosted gateway
|
|
67
|
+
- **MCP Integration** — connect any MCP server (stdio or SSE) via `McpToolAdapter`; MCP tools become first-class engine Tools
|
|
68
|
+
- **Token-Precise Prompt Assembly** — tiktoken-based exact token counting; KV-cache-friendly prompt layout; automatic context compression (Head-Middle-Tail strategy)
|
|
69
|
+
- **Structured Memory** — session context window with configurable limits; archive-before-summarize guarantee; vector recall for long-term memory
|
|
70
|
+
- **OpenTelemetry Observability** — every phase entry/exit, LLM call, tool call, retry, and fallback emits a structured `EngineEvent`; OTel and JSONL emitters included
|
|
71
|
+
- **Interactive CLI** — `tachu chat` / `tachu run` / `tachu init` with full parameter sets, streaming render, session persistence, and Ctrl+C cancellation
|
|
72
|
+
- **Terminal Markdown rendering** — final assistant replies are rendered via `marked` + `marked-terminal` + `cli-highlight`; headings, bold / italic, lists, block quotes, links, tables and fenced code blocks (with syntax highlighting) are all supported. Automatically disabled under `NO_COLOR` / non-TTY / `--no-color`; explicitly controllable with `--markdown` / `--no-markdown` on `tachu run`.
|
|
73
|
+
- **Fail-Closed Safety Baseline** — loop protection, budget circuit-breaker, and basic input validation are hardwired into the engine and cannot be disabled
|
|
74
|
+
- **Qdrant & LocalFs Vector Stores** — plug in Qdrant for multi-process deployments or use the file-backed store for local/single-process setups
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Vision
|
|
79
|
+
|
|
80
|
+
> *太初有道,万物之始。以声明式描述符创造 Agent 万物。*
|
|
81
|
+
> *In the beginning was the Tao — all things arise from it. With declarative descriptors, conjure Agent capability from nothing.*
|
|
82
|
+
|
|
83
|
+
The long-term vision of Tachu is a universal Agent framework where **the engine provides the skeleton and business provides the blood**: any organization can build production-grade agentic systems on top of a stable, observable, auditable foundation without re-solving the hard problems of safety, context management, retry logic, and multi-provider orchestration every time.
|
|
84
|
+
|
|
85
|
+
Tachu is built around three convictions:
|
|
86
|
+
|
|
87
|
+
1. **The Harness is the hard part.** Model intelligence is commoditized; reliable orchestration is not. Tachu invests deeply in the engine infrastructure so application developers can focus on domain logic.
|
|
88
|
+
2. **Declaration over implementation.** Rules, Skills, Tools, and Agents are declared as plain Markdown files. The engine resolves them. No framework-specific boilerplate.
|
|
89
|
+
3. **Observable by default.** Every internal event is structured and emittable. Production systems need complete traces — Tachu provides them without opt-in instrumentation.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Core Abstractions
|
|
94
|
+
|
|
95
|
+
Tachu's four core abstractions are **co-equal and orthogonal** — each independently registered, independently activated, and composable across all engine phases.
|
|
96
|
+
|
|
97
|
+
| Abstraction | Nature | Activation Gate | Effect |
|
|
98
|
+
|-------------|--------|-----------------|--------|
|
|
99
|
+
| **Rules** | Constraints & guidance | Semantic discovery → direct activation | Injected into LLM System Prompt at each scoped phase |
|
|
100
|
+
| **Skills** | Knowledge & instructions | Semantic discovery → direct activation | Injected into LLM context when activated |
|
|
101
|
+
| **Tools** | Atomic executable operations | Semantic discovery → **mandatory gate** (scopes → whitelist → approval) | Executed with full side-effect tracking |
|
|
102
|
+
| **Agents** | Natural-language-driven execution units | Semantic discovery → activatable | Recursively use engine capabilities; all Tool calls pass through the Tool gate |
|
|
103
|
+
|
|
104
|
+
All four share a **common descriptor schema** (Markdown + YAML frontmatter):
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
name: unique-name # required, unique within type
|
|
108
|
+
description: ... # natural language (used for semantic discovery)
|
|
109
|
+
tags: [tag1, tag2] # for filtering and classification
|
|
110
|
+
trigger: { type: always } # activation condition
|
|
111
|
+
requires:
|
|
112
|
+
- { kind: tool, name: read-file } # explicit dependency references
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Dual-Plane Matching Model
|
|
116
|
+
|
|
117
|
+
Every core abstraction is activated through a two-phase process:
|
|
118
|
+
|
|
119
|
+
```mermaid
|
|
120
|
+
graph LR
|
|
121
|
+
Input[Context Input] --> Discovery[Semantic Discovery Plane]
|
|
122
|
+
Discovery --> Index[(Vector Index)]
|
|
123
|
+
Index --> Candidates[Candidate Set]
|
|
124
|
+
Candidates --> Gate[Deterministic Execution Gate]
|
|
125
|
+
Gate -- scopes / whitelist / approval --> Execution[Execution Plane]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- **Semantic discovery plane**: `description` is vectorized on registration; at runtime, the current context is matched against the index to produce a candidate set
|
|
129
|
+
- **Deterministic execution gate**: final activation requires passing a deterministic gate (explicit references, whitelist checks, permission scopes, approval checks)
|
|
130
|
+
|
|
131
|
+
Rules and Skills pass through the gate freely (no side effects). Tools always pass through the full gate. Agents activate freely but all Tool calls they trigger still pass through the Tool gate.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Architecture Overview
|
|
136
|
+
|
|
137
|
+
### Three-Layer Structure
|
|
138
|
+
|
|
139
|
+
Tachu is published as three layers:
|
|
140
|
+
|
|
141
|
+
```mermaid
|
|
142
|
+
graph TD
|
|
143
|
+
subgraph "Business Layer"
|
|
144
|
+
A[Business Rules / Domain Tools / Custom Adapters / Domain Skills / Agents / Plan Templates]
|
|
145
|
+
end
|
|
146
|
+
subgraph "Extensions Library — @tachu/extensions"
|
|
147
|
+
B[OpenAI & Anthropic Adapters / 7 Common Tools / Terminal+File+Web Backends / Qdrant+LocalFs VectorStore / MCP Adapter / OTel+JSONL Emitters / 4 Common Rules]
|
|
148
|
+
end
|
|
149
|
+
subgraph "Engine Core — @tachu/core"
|
|
150
|
+
C[Protocol Definitions / 9-Phase Pipeline / Lifecycle Hooks / Session / Memory / Safety / Model Router / Runtime State]
|
|
151
|
+
end
|
|
152
|
+
A --> B
|
|
153
|
+
B --> C
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Layer | Package | Responsibility |
|
|
157
|
+
|-------|---------|----------------|
|
|
158
|
+
| Engine Core | `@tachu/core` | Protocol interfaces, 9-phase pipeline skeleton, 8 core modules, Registry, Prompt assembler, VectorStore interface + built-in light implementation |
|
|
159
|
+
| Extensions Library | `@tachu/extensions` | Official concrete implementations: Provider adapters, Tools, Backends, VectorStore adapters, OTel/JSONL emitters, common Rules |
|
|
160
|
+
| Business / CLI | `@tachu/cli` or your code | Assembles core + extensions into a working Agent; provides domain Rules/Skills/Tools/Agents |
|
|
161
|
+
|
|
162
|
+
### 9-Phase Execution Pipeline
|
|
163
|
+
|
|
164
|
+
Every request processed by the engine flows through exactly 9 phases:
|
|
165
|
+
|
|
166
|
+
```mermaid
|
|
167
|
+
graph TD
|
|
168
|
+
Start([Business Request]) --> S1[Phase 1: Session Management]
|
|
169
|
+
S1 --> S2[Phase 2: Minimum Safety Check]
|
|
170
|
+
S2 --> S3[Phase 3: Intent Analysis — LLM]
|
|
171
|
+
S3 -- simple --> S9[Phase 9: Output Normalization]
|
|
172
|
+
S3 -- complex --> S4[Phase 4: Pre-Check]
|
|
173
|
+
S4 --> S5[Phase 5: Task Planning]
|
|
174
|
+
S5 -- Plan mode --> PlanLoop{Plan Review Loop}
|
|
175
|
+
S5 -- Template match --> S6
|
|
176
|
+
S5 -- Dynamic split --> S6[Phase 6: DAG Validation]
|
|
177
|
+
PlanLoop -- confirmed --> S6
|
|
178
|
+
S6 --> S7[Phase 7: Sub-task Execution]
|
|
179
|
+
S7 --> S8[Phase 8: Result Validation — LLM]
|
|
180
|
+
S8 -- pass --> S9
|
|
181
|
+
S8 -- fail --> Retry{Retry / Re-plan}
|
|
182
|
+
Retry -- within limits --> S5
|
|
183
|
+
Retry -- exhausted --> S9
|
|
184
|
+
S9 --> End([Output])
|
|
185
|
+
|
|
186
|
+
style S2 fill:#ffeaa7,stroke:#fdcb6e
|
|
187
|
+
style S7 fill:#dfe6e9,stroke:#b2bec3
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
| # | Phase | LLM Call | Key Output |
|
|
191
|
+
|---|-------|----------|------------|
|
|
192
|
+
| 1 | Session Management | No | Session context loaded |
|
|
193
|
+
| 2 | Minimum Safety Check | No | Pass / reject |
|
|
194
|
+
| 3 | Intent Analysis | **Yes** | `IntentResult` (simple/complex, context relevance) |
|
|
195
|
+
| 4 | Pre-Check | No | Resource availability, deep safety validation |
|
|
196
|
+
| 5 | Task Planning | **Yes** | `PlanningResult` (ranked plans + DAG) |
|
|
197
|
+
| 6 | DAG Validation | No | Cycle detection, node integrity (deterministic) |
|
|
198
|
+
| 7 | Sub-task Execution | Per sub-task | `TaskResult[]` (parallel where possible) |
|
|
199
|
+
| 8 | Result Validation | **Yes** | `ValidationResult` (pass / execution_issue / planning_issue) |
|
|
200
|
+
| 9 | Output Normalization | No | `EngineOutput` (typed, with steps, metadata, artifacts) |
|
|
201
|
+
|
|
202
|
+
**Key pipeline properties:**
|
|
203
|
+
|
|
204
|
+
- **Full-path safety gating** — Phase 2 runs on every request, including fast-path simple responses
|
|
205
|
+
- **Context guard** — Phase 3 decides whether session history is relevant; irrelevant history is not forwarded
|
|
206
|
+
- **Three-strikes limit** — Task-level retries are bounded at 3 (configurable); system-level retries at 2
|
|
207
|
+
- **Last-message-wins** — A new request on the same session cancels the current execution via `AbortController`
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Installation
|
|
212
|
+
|
|
213
|
+
Tachu requires [Bun](https://bun.sh) as the runtime.
|
|
214
|
+
|
|
215
|
+
> **Install via the `@alpha` dist-tag** (or an exact version) until Tachu reaches stable.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# Install the engine core (alpha)
|
|
219
|
+
bun add @tachu/core@alpha
|
|
220
|
+
|
|
221
|
+
# Install the extensions library (providers, tools, backends, vector stores)
|
|
222
|
+
bun add @tachu/extensions@alpha
|
|
223
|
+
|
|
224
|
+
# Install and use the CLI globally
|
|
225
|
+
bun add -g @tachu/cli@alpha
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
After global installation, verify with:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
tachu --version # expect 1.0.0-alpha.1 or newer
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Quick Start
|
|
237
|
+
|
|
238
|
+
### CLI
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
# 1. Initialize a new project workspace
|
|
242
|
+
tachu init --template minimal --provider openai
|
|
243
|
+
|
|
244
|
+
# 2. Set your API key (used by the OpenAI provider adapter)
|
|
245
|
+
export OPENAI_API_KEY=sk-...
|
|
246
|
+
|
|
247
|
+
# 3. Run a single prompt
|
|
248
|
+
tachu run "Summarize the last 5 git commits in this repository"
|
|
249
|
+
|
|
250
|
+
# 4. Start an interactive chat session
|
|
251
|
+
tachu chat
|
|
252
|
+
|
|
253
|
+
# Resume the most recent session
|
|
254
|
+
tachu chat --resume
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Programmatic (TypeScript)
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { Engine } from '@tachu/core';
|
|
261
|
+
import { OpenAIProviderAdapter } from '@tachu/extensions/providers';
|
|
262
|
+
import { FileBackend, TerminalBackend } from '@tachu/extensions/backends';
|
|
263
|
+
import type { EngineConfig, InputEnvelope, ExecutionContext } from '@tachu/core';
|
|
264
|
+
|
|
265
|
+
const config: EngineConfig = {
|
|
266
|
+
registry: {
|
|
267
|
+
descriptorPaths: ['.tachu'],
|
|
268
|
+
enableVectorIndexing: false,
|
|
269
|
+
},
|
|
270
|
+
runtime: {
|
|
271
|
+
planMode: false,
|
|
272
|
+
maxConcurrency: 4,
|
|
273
|
+
defaultTaskTimeoutMs: 120_000,
|
|
274
|
+
failFast: false,
|
|
275
|
+
},
|
|
276
|
+
memory: {
|
|
277
|
+
contextTokenLimit: 8000,
|
|
278
|
+
compressionThreshold: 0.8,
|
|
279
|
+
headKeep: 4,
|
|
280
|
+
tailKeep: 12,
|
|
281
|
+
archivePath: '.tachu/archive.jsonl',
|
|
282
|
+
vectorIndexLimit: 10_000,
|
|
283
|
+
},
|
|
284
|
+
budget: {
|
|
285
|
+
maxTokens: 50_000,
|
|
286
|
+
maxToolCalls: 50,
|
|
287
|
+
maxWallTimeMs: 300_000,
|
|
288
|
+
},
|
|
289
|
+
safety: {
|
|
290
|
+
maxInputSizeBytes: 1_000_000,
|
|
291
|
+
maxRecursionDepth: 10,
|
|
292
|
+
workspaceRoot: process.cwd(),
|
|
293
|
+
promptInjectionPatterns: [],
|
|
294
|
+
},
|
|
295
|
+
models: {
|
|
296
|
+
capabilityMapping: {
|
|
297
|
+
'high-reasoning': { provider: 'openai', model: 'gpt-4o' },
|
|
298
|
+
'fast-cheap': { provider: 'openai', model: 'gpt-4o-mini' },
|
|
299
|
+
},
|
|
300
|
+
providerFallbackOrder: ['openai'],
|
|
301
|
+
},
|
|
302
|
+
observability: { enabled: true, maskSensitiveData: true },
|
|
303
|
+
hooks: { writeHookTimeout: 5000, failureBehavior: 'continue' },
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const engine = new Engine(config);
|
|
307
|
+
|
|
308
|
+
// Register a provider
|
|
309
|
+
engine.useProvider(new OpenAIProviderAdapter({ apiKey: process.env.OPENAI_API_KEY! }));
|
|
310
|
+
|
|
311
|
+
// Stream results
|
|
312
|
+
const input: InputEnvelope = {
|
|
313
|
+
content: 'Write a TypeScript function that debounces async operations',
|
|
314
|
+
metadata: { modality: 'text' },
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const context: ExecutionContext = {
|
|
318
|
+
requestId: crypto.randomUUID(),
|
|
319
|
+
sessionId: 'session-001',
|
|
320
|
+
traceId: crypto.randomUUID(),
|
|
321
|
+
principal: { userId: 'user-001' },
|
|
322
|
+
budget: { maxTokens: 20_000, maxDurationMs: 60_000 },
|
|
323
|
+
scopes: ['read', 'write'],
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
for await (const chunk of engine.runStream(input, context)) {
|
|
327
|
+
if (chunk.type === 'delta') process.stdout.write(chunk.content);
|
|
328
|
+
if (chunk.type === 'done') console.log('\n\nCompleted:', chunk.output.status);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Package Layout
|
|
335
|
+
|
|
336
|
+
### Package Summary
|
|
337
|
+
|
|
338
|
+
| Package | Description | Key Exports |
|
|
339
|
+
|---------|-------------|-------------|
|
|
340
|
+
| `@tachu/core` | Zero-dependency engine core | `Engine`, `Registry`, `PromptAssembler`, all interfaces and types |
|
|
341
|
+
| `@tachu/extensions` | Official implementations | `OpenAIProviderAdapter`, `AnthropicProviderAdapter`, `McpToolAdapter`, `QdrantVectorStore`, `OtelEmitter`, backends, tools, rules |
|
|
342
|
+
| `@tachu/cli` | Production CLI program | `tachu chat`, `tachu run`, `tachu init` |
|
|
343
|
+
|
|
344
|
+
### Dependency Relationship
|
|
345
|
+
|
|
346
|
+
```mermaid
|
|
347
|
+
graph LR
|
|
348
|
+
cli["@tachu/cli"]
|
|
349
|
+
extensions["@tachu/extensions"]
|
|
350
|
+
core["@tachu/core"]
|
|
351
|
+
|
|
352
|
+
cli --> extensions
|
|
353
|
+
cli --> core
|
|
354
|
+
extensions --> core
|
|
355
|
+
|
|
356
|
+
style core fill:#74b9ff,stroke:#0984e3
|
|
357
|
+
style extensions fill:#a29bfe,stroke:#6c5ce7
|
|
358
|
+
style cli fill:#fd79a8,stroke:#e84393
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Core Package Internal Structure
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
@tachu/core / src/
|
|
365
|
+
├── types/ # All TypeScript interfaces: descriptors, context, I/O, config
|
|
366
|
+
├── engine/ # Engine entry class, phase handlers, orchestrator, scheduler
|
|
367
|
+
├── registry/ # Registry: register/lookup/startup validation for all 4 abstractions
|
|
368
|
+
├── modules/ # 8 core modules (session, memory, runtime-state, model-router,
|
|
369
|
+
│ # provider, safety, observability, hooks)
|
|
370
|
+
├── prompt/ # PromptAssembler: token budgeting, KV-cache-friendly ordering
|
|
371
|
+
└── vector/ # VectorStore interface + built-in lightweight implementation
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Providers & Integrations
|
|
377
|
+
|
|
378
|
+
### LLM Providers
|
|
379
|
+
|
|
380
|
+
| Provider | Package | Streaming | Function Calling | Notes |
|
|
381
|
+
|----------|---------|-----------|-----------------|-------|
|
|
382
|
+
| OpenAI | `@tachu/extensions/providers` | ✅ | ✅ | GPT-4o, GPT-4o-mini, and all listable models |
|
|
383
|
+
| Anthropic | `@tachu/extensions/providers` | ✅ | ✅ | Claude 3.5 Sonnet and all listable models |
|
|
384
|
+
| Mock | `@tachu/extensions/providers` | ✅ | ✅ | For testing; configurable responses |
|
|
385
|
+
|
|
386
|
+
Provider fallback is configured via `models.providerFallbackOrder`. When a system-level error occurs (timeout, API error), the engine automatically switches to the next provider in the list without re-planning.
|
|
387
|
+
|
|
388
|
+
### Provider Connection Configuration
|
|
389
|
+
|
|
390
|
+
Every built-in provider accepts `apiKey`, `baseURL`, `organization` (OpenAI-only), `project` (OpenAI-only), and `timeoutMs`. Supply them at any of three levels (later wins):
|
|
391
|
+
|
|
392
|
+
1. **Environment variables** (recommended for secrets):
|
|
393
|
+
|
|
394
|
+
| Variable | Provider | Purpose |
|
|
395
|
+
|----------|----------|---------|
|
|
396
|
+
| `OPENAI_API_KEY` | OpenAI | Credential fallback when `apiKey` is not set |
|
|
397
|
+
| `OPENAI_BASE_URL` | OpenAI | SDK-level baseURL override (honored by `openai` SDK) |
|
|
398
|
+
| `ANTHROPIC_API_KEY` | Anthropic | Credential fallback when `apiKey` is not set |
|
|
399
|
+
| `ANTHROPIC_BASE_URL` | Anthropic | SDK-level baseURL override (honored by `@anthropic-ai/sdk`) |
|
|
400
|
+
|
|
401
|
+
2. **`tachu.config.ts` — `providers` block** (recommended for non-secret connection metadata):
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
const config: EngineConfig = {
|
|
405
|
+
// ...other fields
|
|
406
|
+
providers: {
|
|
407
|
+
openai: {
|
|
408
|
+
// apiKey stays in env; keep this file commit-safe
|
|
409
|
+
baseURL: 'https://your-gateway.example.com/v1',
|
|
410
|
+
organization: 'org-xxxx',
|
|
411
|
+
timeoutMs: 60_000,
|
|
412
|
+
},
|
|
413
|
+
anthropic: {
|
|
414
|
+
baseURL: 'https://your-gateway.example.com/anthropic',
|
|
415
|
+
timeoutMs: 60_000,
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
3. **CLI flags** (highest priority; great for one-off overrides):
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
tachu run "..." --provider openai \
|
|
425
|
+
--api-base https://gateway.example.com/v1 \
|
|
426
|
+
--api-key sk-dev \
|
|
427
|
+
--organization org-xxxx
|
|
428
|
+
|
|
429
|
+
tachu chat --provider anthropic \
|
|
430
|
+
--api-base https://gateway.example.com/anthropic
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Flags apply to whichever provider the request ends up using — either the one supplied via `--provider`, or the one resolved from your `capabilityMapping`. CLI flags never touch the `mock` provider.
|
|
434
|
+
|
|
435
|
+
Typical use cases: Azure OpenAI, self-hosted LiteLLM / OpenRouter / Kong gateways, organization-wide egress proxies, and air-gapped environments.
|
|
436
|
+
|
|
437
|
+
### MCP (Model Context Protocol)
|
|
438
|
+
|
|
439
|
+
Tachu ships two official adapters for MCP (`McpStdioAdapter` / `McpSseAdapter`, built on `@modelcontextprotocol/sdk`) and the CLI wires them into `DescriptorRegistry` and the `TaskExecutor`—declare an `mcpServers` block in `tachu.config.ts`, and the CLI auto-discovers tools, routes calls, and disconnects on exit.
|
|
440
|
+
|
|
441
|
+
**Declarative config (recommended; field names align with the OpenAI Agents SDK and the common MCP client convention)**
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
// tachu.config.ts
|
|
445
|
+
const config: EngineConfig = {
|
|
446
|
+
// ... other fields
|
|
447
|
+
mcpServers: {
|
|
448
|
+
// Local stdio process (standard MCP stdio transport)
|
|
449
|
+
fs: {
|
|
450
|
+
command: 'npx',
|
|
451
|
+
args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
|
|
452
|
+
env: { ...process.env },
|
|
453
|
+
},
|
|
454
|
+
// Remote SSE service (standard MCP SSE transport)
|
|
455
|
+
remoteKb: {
|
|
456
|
+
url: 'https://mcp.example.com/sse/',
|
|
457
|
+
headers: { Authorization: `Bearer ${process.env.MCP_TOKEN ?? ''}` },
|
|
458
|
+
timeoutMs: 50_000,
|
|
459
|
+
connectTimeoutMs: 10_000,
|
|
460
|
+
// Optional tachu extensions
|
|
461
|
+
// description: 'Project documentation search (example)',
|
|
462
|
+
// keywords: ['docs', '文档'],
|
|
463
|
+
// expandOnKeywordMatch: true,
|
|
464
|
+
// allowTools: ['getStatus'],
|
|
465
|
+
// denyTools: ['dangerousOp'],
|
|
466
|
+
// requiresApproval: true,
|
|
467
|
+
// disabled: false,
|
|
468
|
+
// tags: ['example'],
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Semantics:
|
|
475
|
+
|
|
476
|
+
- **Namespacing** — remote tools are registered as `<serverId>__<originalName>` (e.g. `remoteKb__getStatus`) so multiple servers coexist without name clashes
|
|
477
|
+
- **Fault isolation** — any server failing to connect / list tools only emits a single stderr warning; other servers and the main flow keep running
|
|
478
|
+
- **Timeouts & cancellation** — `adapter.connect()` is wrapped by `connectTimeoutMs`; `ToolExecutionContext.abortSignal` is forwarded to `adapter.executeTool({ signal })`, so Ctrl+C / budget breaches propagate through the protocol layer
|
|
479
|
+
- **Approval gating** — MCP tool `requiresApproval` is OR-ed with the per-server `requiresApproval` and the tool-loop global flag; the CLI's default `y/N` prompt handles it uniformly
|
|
480
|
+
- **Lifecycle** — `tachu run` / `tachu chat` always call `engine.dispose()` then `mounted.disconnectAll()` in a `finally` branch; disconnect failures only emit warnings
|
|
481
|
+
- **LLM-facing `description`** — when provided, the per-server `description` is prefixed to every tool's description as `[<serverId>: <description>] <original>`, so the planner can route more accurately even without reading the full JSON schema
|
|
482
|
+
- **Keyword-gated tools (prompt-size optimization)** — a server with `expandOnKeywordMatch: true` and non-empty `keywords` is *not* registered at startup. `tachu run <prompt>` and each `you>` turn in `tachu chat` evaluate the user input against each server's keywords (case-insensitive substring match; structured input is `JSON.stringify`'d first) and only register tools from servers whose keywords hit. Use this to keep dozens of niche tools out of the default prompt while still making them reachable on demand — the schema validator refuses `expandOnKeywordMatch: true` without keywords
|
|
483
|
+
|
|
484
|
+
**SDK usage (when you bypass the CLI and assemble the engine yourself)**
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
import { McpSseAdapter, McpStdioAdapter } from '@tachu/extensions';
|
|
488
|
+
|
|
489
|
+
const sse = new McpSseAdapter({
|
|
490
|
+
url: 'https://mcp.example.com/sse/',
|
|
491
|
+
serverId: 'remoteKb',
|
|
492
|
+
headers: { Authorization: 'Bearer ...' },
|
|
493
|
+
defaultTimeoutMs: 50_000,
|
|
494
|
+
});
|
|
495
|
+
await sse.connect('https://mcp.example.com/sse/');
|
|
496
|
+
const tools = await sse.listTools();
|
|
497
|
+
for (const tool of tools) await engine.registry.register(tool);
|
|
498
|
+
|
|
499
|
+
const stdio = new McpStdioAdapter({
|
|
500
|
+
command: 'npx',
|
|
501
|
+
args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
|
|
502
|
+
serverId: 'fs',
|
|
503
|
+
});
|
|
504
|
+
await stdio.connect('');
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
If you want the same "one block of config, auto-wired" experience inside a custom host, reuse `@tachu/cli`'s `mountMcpServers(config.mcpServers, { cwd })` / `setupMcpServersFromConfig(config, registry, { cwd })`—they return `{ descriptors, executors, disconnectAll }` that you can feed into `createEngine({ extraToolExecutors })`.
|
|
508
|
+
|
|
509
|
+
### Vector Stores
|
|
510
|
+
|
|
511
|
+
| Adapter | Package | Use Case |
|
|
512
|
+
|---------|---------|----------|
|
|
513
|
+
| `InMemoryVectorStore` | `@tachu/core` | Development / testing; built-in, zero dependencies |
|
|
514
|
+
| `LocalFsVectorStore` | `@tachu/extensions/vector` | Single-process production; file-backed persistence |
|
|
515
|
+
| `QdrantVectorStore` | `@tachu/extensions/vector` | Multi-process production; full Qdrant REST API support |
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
import { QdrantVectorStore } from '@tachu/extensions/vector';
|
|
519
|
+
|
|
520
|
+
const vectorStore = new QdrantVectorStore({
|
|
521
|
+
url: 'http://localhost:6333',
|
|
522
|
+
collectionName: 'tachu-descriptors',
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
engine.useVectorStore(vectorStore);
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Observability Emitters
|
|
529
|
+
|
|
530
|
+
| Emitter | Package | Output |
|
|
531
|
+
|---------|---------|--------|
|
|
532
|
+
| `OtelEmitter` | `@tachu/extensions/emitters` | OpenTelemetry spans via `@opentelemetry/api` |
|
|
533
|
+
| `JsonlEmitter` | `@tachu/extensions/emitters` | Append-only JSONL file |
|
|
534
|
+
| `ConsoleEmitter` | `@tachu/extensions/emitters` | Structured console output (development) |
|
|
535
|
+
|
|
536
|
+
### Execution Backends
|
|
537
|
+
|
|
538
|
+
| Backend | Package | Description |
|
|
539
|
+
|---------|---------|-------------|
|
|
540
|
+
| `TerminalBackend` | `@tachu/extensions/backends` | Shell command execution in a sandboxed terminal |
|
|
541
|
+
| `FileBackend` | `@tachu/extensions/backends` | File system read/write operations |
|
|
542
|
+
| `WebBackend` | `@tachu/extensions/backends` | HTTP requests to external APIs / web resources |
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## Design Principles
|
|
547
|
+
|
|
548
|
+
Tachu is built on seven core engineering principles drawn from its architecture:
|
|
549
|
+
|
|
550
|
+
1. **Dual-Plane Matching** — All four core abstractions are discovered semantically (vector similarity) but activated deterministically (scopes, whitelist, approval). Semantic discovery is advisory; execution gates are authoritative.
|
|
551
|
+
|
|
552
|
+
2. **Full-Path Safety Gating** — The minimum safety check (Phase 2) runs on *every* request path, including the fast path for simple questions. Safety is never traded for performance.
|
|
553
|
+
|
|
554
|
+
3. **Three-Strikes Retry Limit** — Both the task-level retry loop and the system-level retry loop are strictly bounded. Unlimited retry is not allowed. When limits are exhausted, the engine outputs step-level completion status rather than a generic failure.
|
|
555
|
+
|
|
556
|
+
4. **KV-Cache-Friendly Prompt Assembly** — The System Prompt is assembled in a stable order (hard rules → soft preferences → skills → tool definitions) so that the prefix remains unchanged across turns, maximizing KV cache reuse and reducing LLM cost.
|
|
557
|
+
|
|
558
|
+
5. **Last-Message-Wins Cancellation** — When a new message arrives in the same session, the current execution is cancelled via `AbortController` and the new input is processed in the existing context. This guarantees coherent context while avoiding stale work.
|
|
559
|
+
|
|
560
|
+
6. **Engine Agnostic of Business Permissions** — The engine only evaluates coarse-grained `scopes` from the execution context at the Tool gate. Fine-grained business authorization is the responsibility of Tool implementations or dedicated authorization Tools.
|
|
561
|
+
|
|
562
|
+
7. **Fail-Closed Safety Baseline** — Loop protection, budget circuit-breaking, and basic input validation are hardwired into the engine core and *cannot* be disabled by configuration. Even with a completely empty business configuration, the engine does not run unconstrained.
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## Configuration
|
|
567
|
+
|
|
568
|
+
The engine is configured via a `tachu.config.ts` file at the project root (generated by `tachu init`):
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
import type { EngineConfig } from '@tachu/core';
|
|
572
|
+
|
|
573
|
+
const config: EngineConfig = {
|
|
574
|
+
// Descriptor registry: where Rules/Skills/Tools/Agents are loaded from
|
|
575
|
+
registry: {
|
|
576
|
+
descriptorPaths: ['.tachu'],
|
|
577
|
+
enableVectorIndexing: false, // set true to auto-index descriptors at startup
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
// Runtime behaviour
|
|
581
|
+
runtime: {
|
|
582
|
+
planMode: false, // when true, only plan but never execute tasks
|
|
583
|
+
maxConcurrency: 4, // max parallel sub-tasks
|
|
584
|
+
defaultTaskTimeoutMs: 120_000, // single-task default timeout (ms)
|
|
585
|
+
failFast: false, // any sub-task failure aborts the run
|
|
586
|
+
},
|
|
587
|
+
|
|
588
|
+
// Context window & memory
|
|
589
|
+
memory: {
|
|
590
|
+
contextTokenLimit: 8000, // context window token limit
|
|
591
|
+
compressionThreshold: 0.8, // trigger compression at 80% capacity
|
|
592
|
+
headKeep: 4, // earliest messages preserved during compression
|
|
593
|
+
tailKeep: 12, // latest messages preserved during compression
|
|
594
|
+
archivePath: '.tachu/archive.jsonl',
|
|
595
|
+
vectorIndexLimit: 10_000, // max entries in the built-in vector index
|
|
596
|
+
},
|
|
597
|
+
|
|
598
|
+
// Budget constraints (per execution)
|
|
599
|
+
budget: {
|
|
600
|
+
maxTokens: 50_000, // total token budget per execution
|
|
601
|
+
maxToolCalls: 50, // max tool calls per execution
|
|
602
|
+
maxWallTimeMs: 300_000, // 5-minute wall-time limit
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
// Safety baseline (hardwired minimum; add business policies via SafetyModule.registerPolicy)
|
|
606
|
+
safety: {
|
|
607
|
+
maxInputSizeBytes: 1_000_000,
|
|
608
|
+
maxRecursionDepth: 10,
|
|
609
|
+
workspaceRoot: process.cwd(), // file backend root (path-traversal guard)
|
|
610
|
+
promptInjectionPatterns: [], // optional regex strings; matches emit warnings only
|
|
611
|
+
},
|
|
612
|
+
|
|
613
|
+
// Model routing
|
|
614
|
+
models: {
|
|
615
|
+
capabilityMapping: {
|
|
616
|
+
'high-reasoning': { provider: 'openai', model: 'gpt-4o' },
|
|
617
|
+
'fast-cheap': { provider: 'openai', model: 'gpt-4o-mini' },
|
|
618
|
+
'vision': { provider: 'openai', model: 'gpt-4o' },
|
|
619
|
+
},
|
|
620
|
+
providerFallbackOrder: ['openai', 'anthropic'],
|
|
621
|
+
},
|
|
622
|
+
|
|
623
|
+
// Observability (events emitted to ObservabilityEmitter)
|
|
624
|
+
observability: {
|
|
625
|
+
enabled: true,
|
|
626
|
+
maskSensitiveData: true, // auto-mask PII in event payloads
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
// Hooks
|
|
630
|
+
hooks: {
|
|
631
|
+
writeHookTimeout: 5_000, // ms; mutating hooks exceeding this are skipped
|
|
632
|
+
failureBehavior: 'continue', // 'abort' to fail the run on any hook error
|
|
633
|
+
},
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
export default config;
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
All fields have sensible defaults. `tachu init` generates this file pre-filled for your chosen provider.
|
|
640
|
+
|
|
641
|
+
> **Schema reference**: see detailed-design §14.1 for the full `EngineConfig` interface (and the historical-vs-v1 changelog). Earlier drafts that used `retry / planning / agent / context / execution / storage` keys are deprecated and will fail `validateConfig` with `VALIDATION_INVALID_CONFIG`.
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
## CLI Reference
|
|
646
|
+
|
|
647
|
+
### `tachu init`
|
|
648
|
+
|
|
649
|
+
Initialize a new Tachu project workspace.
|
|
650
|
+
|
|
651
|
+
```
|
|
652
|
+
tachu init [options]
|
|
653
|
+
|
|
654
|
+
Options:
|
|
655
|
+
--template <name> Scaffold template: minimal | full (default: minimal)
|
|
656
|
+
--force Overwrite existing files without prompting
|
|
657
|
+
--path <dir> Target directory (default: CWD)
|
|
658
|
+
--provider <name> Default provider: openai | anthropic | mock (default: mock)
|
|
659
|
+
--no-examples Skip generating example rule/tool descriptors
|
|
660
|
+
-h, --help Show help
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
Generates `.tachu/` directory skeleton + `tachu.config.ts` + `.gitignore` entries.
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
### `tachu run <prompt>`
|
|
668
|
+
|
|
669
|
+
Execute a single prompt and stream the result to stdout.
|
|
670
|
+
|
|
671
|
+
```
|
|
672
|
+
tachu run <prompt> [options]
|
|
673
|
+
|
|
674
|
+
Arguments:
|
|
675
|
+
<prompt> The prompt text (or pipe via stdin)
|
|
676
|
+
|
|
677
|
+
Options:
|
|
678
|
+
--session <id> Use a specific session ID
|
|
679
|
+
--resume Resume the most recent session
|
|
680
|
+
--model <name> Override the high-reasoning model
|
|
681
|
+
--provider <name> Override the default provider
|
|
682
|
+
--api-base <url> Override provider baseURL (gateway / Azure / LiteLLM)
|
|
683
|
+
--api-key <key> Override provider apiKey (env var still recommended)
|
|
684
|
+
--organization <id> Override OpenAI organization ID
|
|
685
|
+
--input <file> Read prompt from a file
|
|
686
|
+
--json Parse prompt as JSON (structured input)
|
|
687
|
+
--output <fmt> Output format: text | json | markdown (default: text)
|
|
688
|
+
--markdown Enable terminal Markdown rendering for --output text
|
|
689
|
+
(default: on when stdout is a TTY and NO_COLOR is unset)
|
|
690
|
+
--no-markdown Disable terminal Markdown rendering (force raw text)
|
|
691
|
+
--no-validation Skip Phase 8 result validation
|
|
692
|
+
--plan-mode Enable Plan mode (pause after Phase 5 for approval)
|
|
693
|
+
--verbose, -v Verbose output (phase transitions, each phase line has `(Nms)` duration appended)
|
|
694
|
+
--debug Debug mode: implies --verbose and streams every engine observability
|
|
695
|
+
event (phase / llm / tool / MCP) to stderr, color-coded.
|
|
696
|
+
Safe for `-o json` pipelines (stdout is not polluted).
|
|
697
|
+
--no-color Disable ANSI color output (also respects NO_COLOR env var;
|
|
698
|
+
implies --no-markdown since Markdown rendering is color-based)
|
|
699
|
+
--timeout <ms> Wall-time limit (overrides budget.maxWallTimeMs)
|
|
700
|
+
-h, --help Show help
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
### `tachu chat`
|
|
706
|
+
|
|
707
|
+
Start an interactive multi-turn chat session.
|
|
708
|
+
|
|
709
|
+
```
|
|
710
|
+
tachu chat [options]
|
|
711
|
+
|
|
712
|
+
Options:
|
|
713
|
+
--session <id> Use a specific session ID
|
|
714
|
+
--resume Resume the most recent session
|
|
715
|
+
--history List all sessions and exit (no interactive prompt)
|
|
716
|
+
--export <file> Export a session to Markdown and exit
|
|
717
|
+
--model <name> Override the high-reasoning model
|
|
718
|
+
--provider <name> Override the default provider
|
|
719
|
+
--api-base <url> Override provider baseURL (gateway / Azure / LiteLLM)
|
|
720
|
+
--api-key <key> Override provider apiKey (env var still recommended)
|
|
721
|
+
--organization <id> Override OpenAI organization ID
|
|
722
|
+
--plan-mode Enable Plan mode
|
|
723
|
+
--verbose, -v Verbose output (phase lines carry `(Nms)` duration)
|
|
724
|
+
--debug Debug mode: implies --verbose and streams observability events to stderr.
|
|
725
|
+
Also prints per-turn MCP gated-group activation summary.
|
|
726
|
+
--no-color Disable color output
|
|
727
|
+
-h, --help Show help
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
**Built-in interactive commands** (prefix with `/`):
|
|
731
|
+
|
|
732
|
+
| Command | Description |
|
|
733
|
+
|---------|-------------|
|
|
734
|
+
| `/exit` | Save session and quit |
|
|
735
|
+
| `/reset` | Clear the current session's memory |
|
|
736
|
+
| `/new` | Start a new session |
|
|
737
|
+
| `/list` | List all saved sessions |
|
|
738
|
+
| `/load <id>` | Switch to a specific session |
|
|
739
|
+
| `/save` | Manually persist the current session |
|
|
740
|
+
| `/export <path>` | Export session to a Markdown file |
|
|
741
|
+
| `/history` | Show this session's message history |
|
|
742
|
+
| `/stats` | Show token usage, tool calls, and remaining budget |
|
|
743
|
+
| `/help` | Show all commands |
|
|
744
|
+
|
|
745
|
+
**Ctrl+C behaviour:**
|
|
746
|
+
- First press: cancel the current LLM/Tool call (return to prompt, session intact)
|
|
747
|
+
- Second press within 1 second: save session and exit gracefully
|
|
748
|
+
- Third press: force exit
|
|
749
|
+
|
|
750
|
+
**Session persistence contract:**
|
|
751
|
+
|
|
752
|
+
`tachu chat` uses the `FsMemorySystem` from `@tachu/extensions` by default. Each conversation is written to `<cwd>/.tachu/memory/<session-id>.jsonl` on every `append` (append-only for crash safety). `--resume` and `--session <id>` hydrate the full history from that file on startup, then the engine continues inside the very same `MemorySystem` — so the LLM sees the complete prior context.
|
|
753
|
+
|
|
754
|
+
- `persistence` is controlled via `memory.persistence` in `tachu.config.ts` (`"fs"` default / `"memory"` for SDK-embedded use)
|
|
755
|
+
- `persistDir` defaults to `.tachu/memory`
|
|
756
|
+
- Legacy `tachu.config.ts` sessions that still embedded `messages` inside the session JSON are auto-migrated into the new `jsonl` layout on first resume (one-time, idempotent)
|
|
757
|
+
- `/history`, `/export <path>`, `/stats`, `/reset`, `/clear`, `/new`, `/load <id>` all operate against this single source of truth
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
## Extension Guide
|
|
762
|
+
|
|
763
|
+
Tachu is extended by creating Markdown descriptor files in the `.tachu/` directory. No code changes are required for Rules, Skills, and Tools — only Agents need executable functions registered separately.
|
|
764
|
+
|
|
765
|
+
### Custom Rule
|
|
766
|
+
|
|
767
|
+
```markdown
|
|
768
|
+
<!-- .tachu/rules/no-external-calls.md -->
|
|
769
|
+
---
|
|
770
|
+
name: no-external-calls
|
|
771
|
+
description: Prevent the agent from making external network calls without explicit approval
|
|
772
|
+
type: rule
|
|
773
|
+
scope: [execution]
|
|
774
|
+
tags: [security, network]
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
Do not make HTTP requests, DNS lookups, or any other external network calls unless
|
|
778
|
+
the tool being invoked has `requiresApproval: true` and the user has confirmed.
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Custom Skill
|
|
782
|
+
|
|
783
|
+
```markdown
|
|
784
|
+
<!-- .tachu/skills/git-workflow/SKILL.md -->
|
|
785
|
+
---
|
|
786
|
+
name: git-workflow
|
|
787
|
+
description: Git branching, commit, and PR workflow knowledge for this repository
|
|
788
|
+
tags: [development, git]
|
|
789
|
+
requires:
|
|
790
|
+
- { kind: tool, name: run-command }
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## Git Workflow
|
|
794
|
+
|
|
795
|
+
This repository follows trunk-based development with short-lived feature branches.
|
|
796
|
+
|
|
797
|
+
### Branch Naming
|
|
798
|
+
- Feature: `feat/<ticket>-<short-desc>`
|
|
799
|
+
- Fix: `fix/<ticket>-<short-desc>`
|
|
800
|
+
|
|
801
|
+
### Commit Convention
|
|
802
|
+
Use Conventional Commits: `type(scope): subject`
|
|
803
|
+
...
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
### Custom Tool
|
|
807
|
+
|
|
808
|
+
```markdown
|
|
809
|
+
<!-- .tachu/tools/query-db.md -->
|
|
810
|
+
---
|
|
811
|
+
name: query-db
|
|
812
|
+
description: Execute a read-only SQL query against the application database
|
|
813
|
+
sideEffect: readonly
|
|
814
|
+
idempotent: true
|
|
815
|
+
requiresApproval: false
|
|
816
|
+
timeout: 10000
|
|
817
|
+
inputSchema:
|
|
818
|
+
type: object
|
|
819
|
+
properties:
|
|
820
|
+
sql: { type: string, description: "SQL SELECT statement" }
|
|
821
|
+
limit: { type: number, description: "Max rows to return", default: 100 }
|
|
822
|
+
required: [sql]
|
|
823
|
+
execute: queryDatabase
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
Executes a parameterized read-only SQL query. Results are returned as a JSON array.
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
Register the execution function in your `engine-factory.ts`:
|
|
830
|
+
|
|
831
|
+
```typescript
|
|
832
|
+
engine.registry.registerExecutor('queryDatabase', async (input, ctx) => {
|
|
833
|
+
const { sql, limit = 100 } = input as { sql: string; limit?: number };
|
|
834
|
+
return db.query(sql).limit(limit).execute();
|
|
835
|
+
});
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
### Custom Agent
|
|
839
|
+
|
|
840
|
+
```markdown
|
|
841
|
+
<!-- .tachu/agents/code-reviewer.md -->
|
|
842
|
+
---
|
|
843
|
+
name: code-reviewer
|
|
844
|
+
description: Reviews pull request diffs and produces structured code review feedback
|
|
845
|
+
sideEffect: readonly
|
|
846
|
+
idempotent: true
|
|
847
|
+
requiresApproval: false
|
|
848
|
+
timeout: 180000
|
|
849
|
+
maxDepth: 1
|
|
850
|
+
availableTools: [read-file, search-code, run-command]
|
|
851
|
+
---
|
|
852
|
+
|
|
853
|
+
You are a careful code reviewer. When given a diff or a set of files:
|
|
854
|
+
1. Understand the intent of the change
|
|
855
|
+
2. Review for correctness, clarity, security, and performance
|
|
856
|
+
3. Produce a structured review with severity levels: critical / major / minor / nit
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
## Observability & Safety
|
|
862
|
+
|
|
863
|
+
### OpenTelemetry Integration
|
|
864
|
+
|
|
865
|
+
Every engine event maps to an OTel span, enabling full distributed tracing:
|
|
866
|
+
|
|
867
|
+
```typescript
|
|
868
|
+
import { OtelEmitter } from '@tachu/extensions/emitters';
|
|
869
|
+
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
870
|
+
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
871
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
872
|
+
|
|
873
|
+
const provider = new NodeTracerProvider();
|
|
874
|
+
provider.addSpanProcessor(
|
|
875
|
+
new SimpleSpanProcessor(new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }))
|
|
876
|
+
);
|
|
877
|
+
provider.register();
|
|
878
|
+
|
|
879
|
+
const engine = new Engine({
|
|
880
|
+
...config,
|
|
881
|
+
// The OtelEmitter consumes EngineEvents and creates OTel spans
|
|
882
|
+
});
|
|
883
|
+
engine.useEmitter(new OtelEmitter());
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**Events emitted for every request:**
|
|
887
|
+
|
|
888
|
+
| Event Type | When |
|
|
889
|
+
|-----------|------|
|
|
890
|
+
| `phase_enter` / `phase_exit` | Every pipeline phase |
|
|
891
|
+
| `llm_call_start` / `llm_call_end` | Every LLM invocation |
|
|
892
|
+
| `tool_call_start` / `tool_call_end` | Every Tool execution |
|
|
893
|
+
| `retry` | Task-level or system-level retry triggered |
|
|
894
|
+
| `provider_fallback` | Provider downgrade initiated |
|
|
895
|
+
| `budget_warning` | Budget at 80% of limit |
|
|
896
|
+
| `budget_exhausted` | Budget circuit-breaker activated |
|
|
897
|
+
| `error` | Any `EngineError` subclass |
|
|
898
|
+
|
|
899
|
+
### Safety Module
|
|
900
|
+
|
|
901
|
+
The Safety module operates in two independent layers:
|
|
902
|
+
|
|
903
|
+
**Engine baseline (non-disableable):**
|
|
904
|
+
- Input size enforcement (`maxInputSize` bytes)
|
|
905
|
+
- Recursion depth limit (`maxRecursionDepth`)
|
|
906
|
+
- Budget circuit-breaker (terminates immediately when token/time budget is exhausted)
|
|
907
|
+
|
|
908
|
+
**Business-injectable policies** (via hooks or configuration):
|
|
909
|
+
- Prompt injection detection (`enablePromptInjectionCheck: true`)
|
|
910
|
+
- Sensitive operation interception (register via `engine.registerSafetyPolicy()`)
|
|
911
|
+
- Output content compliance checks
|
|
912
|
+
|
|
913
|
+
```typescript
|
|
914
|
+
// Register a custom safety policy
|
|
915
|
+
engine.registerSafetyPolicy(async (input, ctx) => {
|
|
916
|
+
if (containsPersonalData(input.content)) {
|
|
917
|
+
return { passed: false, violations: [{ type: 'pii', message: 'PII detected in input' }] };
|
|
918
|
+
}
|
|
919
|
+
return { passed: true, violations: [] };
|
|
920
|
+
});
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### Graceful Degradation Policy
|
|
924
|
+
|
|
925
|
+
Tachu guarantees that **every response the user sees is a usable natural-language answer** — the engine never returns a bare "failed" status or leaks internal step IDs / phase numbers / sub-flow names. Three defensive layers enforce this:
|
|
926
|
+
|
|
927
|
+
1. **Origin** — every `EngineError` ships with a `userMessage` field resolved from a Chinese template table (46 codes covered); `toUserFacing()` projects only `{ code, userMessage, retryable }` to the UI layer, hiding `message` / `stack` / `cause` / `context`.
|
|
928
|
+
2. **Aggregation** — when `validation.passed === false` and the built-in `direct-answer` sub-flow also produced nothing, Phase 9's `ensureFallbackText()` first attempts a best-effort LLM summary (5 s timeout, no retry) and silently falls back to a deterministic local template. The returned text is always **≥ 30 characters**, contains a concrete next-step hint, and is sanitized of internal terminology.
|
|
929
|
+
3. **Final shield** — the CLI `StreamRenderer` runs a last-pass regex filter (`sanitizeUserText`) over every user-visible string (`finalize(text|markdown)` + `error` chunks), catching any internal term that might have slipped through upstream.
|
|
930
|
+
|
|
931
|
+
The contract is enforced by `packages/core/src/engine/phases/fallback-contract.test.ts` (55 assertions). Any regression that leaks `task-tool-N` / `Phase N` / `direct-answer 子流程` / `capability 路由` / `Tool / Agent 描述符` to a user-facing path fails CI.
|
|
932
|
+
|
|
933
|
+
---
|
|
934
|
+
|
|
935
|
+
## Roadmap
|
|
936
|
+
|
|
937
|
+
Tachu follows a `1.0.0-alpha.n` → `1.0.0-beta.n` → `1.0.0` release lane. Each
|
|
938
|
+
cut-line below is a real, shippable deliverable with tests, not a wish list.
|
|
939
|
+
|
|
940
|
+
### 1.0.0-alpha.1 — First public alpha (current)
|
|
941
|
+
|
|
942
|
+
- [x] 9-phase pipeline, descriptor registry, prompt assembler, scheduler and 8 core modules
|
|
943
|
+
- [x] OpenAI / Anthropic / Qwen / Mock provider adapters with
|
|
944
|
+
`apiKey` / `baseURL` / `organization` / `timeoutMs` configurable via env,
|
|
945
|
+
`tachu.config.ts` or CLI flags
|
|
946
|
+
- [x] CLI (`tachu init` / `run` / `chat`) with streaming renderer, session
|
|
947
|
+
persistence, double-Ctrl+C exit semantics and terminal Markdown rendering
|
|
948
|
+
- [x] MCP stdio + SSE adapters, auto-mounted from `tachu.config.ts`
|
|
949
|
+
- [x] Vector stores (`LocalFsVectorStore`, `QdrantVectorStore`) and
|
|
950
|
+
observability emitters (OTel / JSONL / Console)
|
|
951
|
+
- [x] `direct-answer` built-in Sub-flow — reserved in the registry, runs the
|
|
952
|
+
user-facing LLM reply inside Phase 7 with the same safety and
|
|
953
|
+
observability hooks as any other sub-flow
|
|
954
|
+
- [x] `tool-use` built-in Sub-flow — full agentic loop with tool selection,
|
|
955
|
+
approval, execution, feedback and termination
|
|
956
|
+
- [x] Phase 3 (Intent Analysis) as a real LLM call with structured JSON schema,
|
|
957
|
+
few-shot examples, bounded history window and composed timeouts
|
|
958
|
+
- [x] Phase 5 fallback contract — guarantees `plans[0].tasks.length >= 1` for
|
|
959
|
+
every request path; LLM-backed ranked planner slated for next alpha
|
|
960
|
+
- [x] Phase 9 Output Assembly — internal state JSON never leaks to end users
|
|
961
|
+
- [x] Optional `@tachu/web-fetch-server` sidecar powering the `web-fetch` and
|
|
962
|
+
`web-search` tools without pulling browser dependencies into the SDK
|
|
963
|
+
- [x] Structured text-to-image contract (`ChatResponse.images` /
|
|
964
|
+
`EngineOutput.metadata.generatedImages`) and `tachu run --save-image`
|
|
965
|
+
|
|
966
|
+
### Next alpha iterations
|
|
967
|
+
|
|
968
|
+
- [ ] LLM-backed Phase 5 planner producing ranked multi-step plans for
|
|
969
|
+
tool-chain complex requests
|
|
970
|
+
- [ ] Phase 8 Result Validation as a real LLM call with structured
|
|
971
|
+
`ValidationResult` driving the retry / re-plan loop
|
|
972
|
+
- [ ] `tachu run --plan-mode` real plan preview before execution
|
|
973
|
+
- [ ] Engine-level `delta` streaming so CLI can render token-by-token output
|
|
974
|
+
during Phase 3 / 7 / 8
|
|
975
|
+
- [ ] `tachu run --json` schema lock-down
|
|
976
|
+
- [ ] Failure-injection test harness
|
|
977
|
+
- [ ] Published end-to-end smoke test recordings under `docs/smoke/`
|
|
978
|
+
|
|
979
|
+
### 1.0.0-beta — Graduation criteria
|
|
980
|
+
|
|
981
|
+
- [ ] Two consecutive alpha releases without regressions
|
|
982
|
+
- [ ] ≥1 third-party user has run Tachu end-to-end against a real LLM and reported back
|
|
983
|
+
- [ ] Published coverage + benchmark baselines
|
|
984
|
+
- [ ] Public upgrade guide covering every breaking change since `1.0.0-alpha.1`
|
|
985
|
+
- [ ] Additional provider adapters (Gemini, Mistral) land behind the stable protocol
|
|
986
|
+
|
|
987
|
+
### 1.0.0 — Stable
|
|
988
|
+
|
|
989
|
+
- [ ] SLO / error-budget documentation
|
|
990
|
+
- [ ] Signed release provenance
|
|
991
|
+
- [ ] Backwards-compatibility policy
|
|
992
|
+
- [ ] Production deployments documented
|
|
993
|
+
|
|
994
|
+
### Beyond 1.0 — Vision
|
|
995
|
+
|
|
996
|
+
- Multi-agent collaboration (agent-to-agent communication protocol)
|
|
997
|
+
- Persistent long-term memory across deployment restarts
|
|
998
|
+
- Fine-grained budget allocation per sub-task
|
|
999
|
+
- Additional VectorStore adapters (Pinecone, pgvector)
|
|
1000
|
+
- Plan template library
|
|
1001
|
+
- Additional compression strategies
|
|
1002
|
+
|
|
1003
|
+
---
|
|
1004
|
+
|
|
1005
|
+
## Contributing
|
|
1006
|
+
|
|
1007
|
+
### Requirements
|
|
1008
|
+
|
|
1009
|
+
- [Bun](https://bun.sh) >= 1.1.0
|
|
1010
|
+
- TypeScript 5.x (provided via dev dependencies)
|
|
1011
|
+
|
|
1012
|
+
### Development Workflow
|
|
1013
|
+
|
|
1014
|
+
```bash
|
|
1015
|
+
# Clone and install
|
|
1016
|
+
git clone https://github.com/dangaogit/tachu.git
|
|
1017
|
+
cd tachu
|
|
1018
|
+
bun install
|
|
1019
|
+
|
|
1020
|
+
# Run all tests
|
|
1021
|
+
bun test
|
|
1022
|
+
|
|
1023
|
+
# Type check
|
|
1024
|
+
bun run typecheck
|
|
1025
|
+
|
|
1026
|
+
# Build all packages
|
|
1027
|
+
bun run build
|
|
1028
|
+
|
|
1029
|
+
# Run a specific package's tests
|
|
1030
|
+
bun test --filter packages/core
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### Project Conventions
|
|
1034
|
+
|
|
1035
|
+
- File names: `kebab-case`
|
|
1036
|
+
- Classes and types: `PascalCase`
|
|
1037
|
+
- Functions and variables: `camelCase`
|
|
1038
|
+
- Constants: `SCREAMING_SNAKE_CASE`
|
|
1039
|
+
- All public APIs must have TSDoc comments (`@param`, `@returns`, `@throws`, `@example`)
|
|
1040
|
+
- Test files co-located with source: `*.test.ts`
|
|
1041
|
+
- Integration tests under `__tests__/`
|
|
1042
|
+
|
|
1043
|
+
Pull requests require:
|
|
1044
|
+
- All tests passing (`bun test`)
|
|
1045
|
+
- Zero TypeScript errors (`bun run typecheck`)
|
|
1046
|
+
- Coverage thresholds met (≥80% line, ≥70% branch)
|
|
1047
|
+
- TSDoc on any new public API
|
|
1048
|
+
|
|
1049
|
+
See `CONTRIBUTING.md` for full guidelines.
|
|
1050
|
+
|
|
1051
|
+
---
|
|
1052
|
+
|
|
1053
|
+
## Benchmarks
|
|
1054
|
+
|
|
1055
|
+
Performance baselines are established in `packages/core/benchmarks/` and run with `bun test`:
|
|
1056
|
+
|
|
1057
|
+
| Benchmark | Metric | Baseline |
|
|
1058
|
+
|-----------|--------|----------|
|
|
1059
|
+
| `scheduler.bench.ts` — 100 parallel tasks | Scheduling throughput | *Populated by verifier phase* |
|
|
1060
|
+
| `vector-store.bench.ts` — 10,000 entries, topK=10 | Search QPS | *Populated by verifier phase* |
|
|
1061
|
+
| `prompt-assembler.bench.ts` — 4K token window assembly | Assembly latency (p99) | *Populated by verifier phase* |
|
|
1062
|
+
|
|
1063
|
+
Benchmarks serve as regression baselines; there are no minimum performance requirements for v1.
|
|
1064
|
+
|
|
1065
|
+
---
|
|
1066
|
+
|
|
1067
|
+
## Documentation
|
|
1068
|
+
|
|
1069
|
+
| Document | Description |
|
|
1070
|
+
|----------|-------------|
|
|
1071
|
+
| [Architecture Design](./docs/adr/architecture-design.md) | Vision, three-layer structure, four core abstractions, 9-phase pipeline design |
|
|
1072
|
+
| [Detailed Design](./docs/adr/detailed-design.md) | TypeScript interfaces, module specs, configuration schema |
|
|
1073
|
+
| [Technical Design](./docs/adr/technical-design.md) | Technology choices, engineering structure, implementation guide |
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1077
|
+
## Web Fetch Server (Optional)
|
|
1078
|
+
|
|
1079
|
+
The **Web Fetch Server** (`@tachu/web-fetch-server`) is an optional HTTP sidecar that performs remote browser rendering and structured extraction for the `web-fetch` and `web-search` tools in `@tachu/extensions`. It does **not** run automatically with the engine or CLI—start it only when you need those tools against live pages.
|
|
1080
|
+
|
|
1081
|
+
### Quick start (repo root)
|
|
1082
|
+
|
|
1083
|
+
```bash
|
|
1084
|
+
bun install
|
|
1085
|
+
bun run dev:server:install-browser # first-time: Chromium for Playwright
|
|
1086
|
+
bun run dev:server
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
### Tools
|
|
1090
|
+
|
|
1091
|
+
- **`web-fetch`** — Calls the server to retrieve a URL and return AI-friendly Markdown (Readability + Turndown).
|
|
1092
|
+
- **`web-search`** — In v0.1 this is a **stub**; real search providers are not wired yet.
|
|
1093
|
+
|
|
1094
|
+
For full configuration, env vars, and production/Docker notes, see [packages/web-fetch-server/README.md](./packages/web-fetch-server/README.md).
|
|
1095
|
+
|
|
1096
|
+
---
|
|
1097
|
+
|
|
1098
|
+
## License
|
|
1099
|
+
|
|
1100
|
+
[Apache License 2.0](./LICENSE) © 2026 Tachu Contributors
|
|
1101
|
+
|
|
1102
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. A copy of the License is included in the [LICENSE](./LICENSE) file or may be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
|
|
1103
|
+
|
|
1104
|
+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|