@migor/agentia 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +151 -0
  3. package/README.md +206 -76
  4. package/dist/AGENTS.md +854 -0
  5. package/dist/container/container.d.ts +10 -2
  6. package/dist/container/container.js +27 -3
  7. package/dist/container/container.js.map +1 -1
  8. package/dist/core/abort.d.ts +10 -0
  9. package/dist/core/abort.js +28 -0
  10. package/dist/core/abort.js.map +1 -0
  11. package/dist/core/blackboard.d.ts +37 -0
  12. package/dist/core/blackboard.js +2 -0
  13. package/dist/core/blackboard.js.map +1 -0
  14. package/dist/core/json.d.ts +2 -0
  15. package/dist/core/json.js +4 -0
  16. package/dist/core/json.js.map +1 -1
  17. package/dist/core/run.d.ts +18 -0
  18. package/dist/core/run.js +2 -0
  19. package/dist/core/run.js.map +1 -0
  20. package/dist/core/schema.js +2 -2
  21. package/dist/core/schema.js.map +1 -1
  22. package/dist/core/tool.d.ts +62 -7
  23. package/dist/core/trace.d.ts +20 -4
  24. package/dist/engine/budget.d.ts +49 -0
  25. package/dist/engine/budget.js +30 -0
  26. package/dist/engine/budget.js.map +1 -0
  27. package/dist/engine/concurrency.d.ts +43 -0
  28. package/dist/engine/concurrency.js +95 -0
  29. package/dist/engine/concurrency.js.map +1 -0
  30. package/dist/engine/errors.d.ts +2 -0
  31. package/dist/engine/errors.js +7 -0
  32. package/dist/engine/errors.js.map +1 -1
  33. package/dist/engine/loop.d.ts +35 -10
  34. package/dist/engine/loop.js +336 -66
  35. package/dist/engine/loop.js.map +1 -1
  36. package/dist/engine/policy.d.ts +9 -1
  37. package/dist/engine/policy.js +8 -5
  38. package/dist/engine/policy.js.map +1 -1
  39. package/dist/engine/replay.d.ts +8 -5
  40. package/dist/engine/replay.js +30 -29
  41. package/dist/engine/replay.js.map +1 -1
  42. package/dist/engine/retry.d.ts +47 -0
  43. package/dist/engine/retry.js +51 -0
  44. package/dist/engine/retry.js.map +1 -0
  45. package/dist/{run → engine}/spec.d.ts +24 -4
  46. package/dist/{run → engine}/spec.js +4 -1
  47. package/dist/engine/spec.js.map +1 -0
  48. package/dist/engine/tracer.d.ts +9 -0
  49. package/dist/engine/tracer.js +57 -1
  50. package/dist/engine/tracer.js.map +1 -1
  51. package/dist/engine/{context.d.ts → trimming.d.ts} +19 -20
  52. package/dist/engine/trimming.js +238 -0
  53. package/dist/engine/trimming.js.map +1 -0
  54. package/dist/engine/types.d.ts +109 -13
  55. package/dist/engine/types.js +9 -1
  56. package/dist/engine/types.js.map +1 -1
  57. package/dist/engine/usage.d.ts +28 -2
  58. package/dist/engine/usage.js +39 -6
  59. package/dist/engine/usage.js.map +1 -1
  60. package/dist/eval/defineEval.d.ts +73 -0
  61. package/dist/eval/defineEval.js +44 -0
  62. package/dist/eval/defineEval.js.map +1 -0
  63. package/dist/eval/scripted.d.ts +26 -0
  64. package/dist/eval/scripted.js +43 -0
  65. package/dist/eval/scripted.js.map +1 -0
  66. package/dist/index.d.ts +62 -35
  67. package/dist/index.js +39 -20
  68. package/dist/index.js.map +1 -1
  69. package/dist/integrations/anthropic.d.ts +53 -0
  70. package/dist/integrations/anthropic.js +24 -0
  71. package/dist/integrations/anthropic.js.map +1 -0
  72. package/dist/integrations/mcp.d.ts +79 -0
  73. package/dist/integrations/mcp.js +108 -0
  74. package/dist/integrations/mcp.js.map +1 -0
  75. package/dist/integrations/metrics.d.ts +131 -0
  76. package/dist/integrations/metrics.js +488 -0
  77. package/dist/integrations/metrics.js.map +1 -0
  78. package/dist/{run → integrations}/openai.d.ts +12 -5
  79. package/dist/integrations/openai.js +386 -0
  80. package/dist/integrations/openai.js.map +1 -0
  81. package/dist/integrations/otlp.js.map +1 -0
  82. package/dist/integrations/report.d.ts +78 -0
  83. package/dist/integrations/report.js +257 -0
  84. package/dist/integrations/report.js.map +1 -0
  85. package/dist/runtime/context.d.ts +25 -0
  86. package/dist/{run → runtime}/context.js +8 -1
  87. package/dist/runtime/context.js.map +1 -0
  88. package/dist/{run → runtime}/memory.d.ts +1 -1
  89. package/dist/{run → runtime}/memory.js +3 -1
  90. package/dist/runtime/memory.js.map +1 -0
  91. package/dist/{run → runtime}/run.d.ts +22 -5
  92. package/dist/runtime/run.js +218 -0
  93. package/dist/runtime/run.js.map +1 -0
  94. package/dist/runtime/session.d.ts +27 -0
  95. package/dist/runtime/session.js +16 -0
  96. package/dist/runtime/session.js.map +1 -0
  97. package/dist/{run → runtime}/systemPrompt.d.ts +12 -0
  98. package/dist/{run → runtime}/systemPrompt.js +16 -3
  99. package/dist/runtime/systemPrompt.js.map +1 -0
  100. package/dist/{run → store}/fsStore.d.ts +15 -0
  101. package/dist/{run → store}/fsStore.js +43 -1
  102. package/dist/store/fsStore.js.map +1 -0
  103. package/dist/{run → store}/redisStore.d.ts +19 -1
  104. package/dist/{run → store}/redisStore.js +33 -6
  105. package/dist/store/redisStore.js.map +1 -0
  106. package/dist/{run → store}/sqliteStore.js +22 -1
  107. package/dist/store/sqliteStore.js.map +1 -0
  108. package/dist/{run → store}/store.d.ts +25 -3
  109. package/dist/store/store.js +63 -0
  110. package/dist/store/store.js.map +1 -0
  111. package/dist/toolkit/asset.d.ts +7 -6
  112. package/dist/toolkit/asset.js +7 -6
  113. package/dist/toolkit/asset.js.map +1 -1
  114. package/dist/toolkit/collect.d.ts +20 -7
  115. package/dist/toolkit/collect.js +21 -7
  116. package/dist/toolkit/collect.js.map +1 -1
  117. package/dist/toolkit/discover.d.ts +21 -1
  118. package/dist/toolkit/discover.js +59 -12
  119. package/dist/toolkit/discover.js.map +1 -1
  120. package/dist/toolkit/env.d.ts +47 -0
  121. package/dist/toolkit/env.js +62 -0
  122. package/dist/toolkit/env.js.map +1 -0
  123. package/dist/toolkit/middleware.d.ts +10 -10
  124. package/dist/toolkit/middleware.js +17 -4
  125. package/dist/toolkit/middleware.js.map +1 -1
  126. package/dist/toolkit/module.d.ts +100 -34
  127. package/dist/toolkit/module.js +112 -36
  128. package/dist/toolkit/module.js.map +1 -1
  129. package/dist/toolkit/prompt.d.ts +4 -6
  130. package/dist/toolkit/prompt.js +11 -10
  131. package/dist/toolkit/prompt.js.map +1 -1
  132. package/dist/toolkit/skill.d.ts +14 -16
  133. package/dist/toolkit/skill.js +32 -25
  134. package/dist/toolkit/skill.js.map +1 -1
  135. package/dist/toolkit/subagent.d.ts +10 -12
  136. package/dist/toolkit/subagent.js +23 -21
  137. package/dist/toolkit/subagent.js.map +1 -1
  138. package/dist/toolkit/tool.d.ts +19 -11
  139. package/dist/toolkit/tool.js +11 -8
  140. package/dist/toolkit/tool.js.map +1 -1
  141. package/dist/toolkit/zod.d.ts +4 -3
  142. package/dist/toolkit/zod.js +2 -1
  143. package/dist/toolkit/zod.js.map +1 -1
  144. package/dist/transport/async.d.ts +133 -0
  145. package/dist/transport/async.js +415 -0
  146. package/dist/transport/async.js.map +1 -0
  147. package/dist/transport/http.d.ts +146 -0
  148. package/dist/transport/http.js +387 -0
  149. package/dist/transport/http.js.map +1 -0
  150. package/dist/{run → transport}/scheduler.d.ts +9 -1
  151. package/dist/transport/scheduler.js +133 -0
  152. package/dist/transport/scheduler.js.map +1 -0
  153. package/dist/transport/sse.d.ts +43 -0
  154. package/dist/transport/sse.js +51 -0
  155. package/dist/transport/sse.js.map +1 -0
  156. package/dist/{run → transport}/transport.d.ts +1 -1
  157. package/dist/{run → transport}/transport.js +1 -1
  158. package/dist/transport/transport.js.map +1 -0
  159. package/package.json +24 -9
  160. package/dist/engine/context.js +0 -146
  161. package/dist/engine/context.js.map +0 -1
  162. package/dist/run/async.d.ts +0 -59
  163. package/dist/run/async.js +0 -201
  164. package/dist/run/async.js.map +0 -1
  165. package/dist/run/context.d.ts +0 -16
  166. package/dist/run/context.js.map +0 -1
  167. package/dist/run/fsStore.js.map +0 -1
  168. package/dist/run/http.d.ts +0 -47
  169. package/dist/run/http.js +0 -119
  170. package/dist/run/http.js.map +0 -1
  171. package/dist/run/memory.js.map +0 -1
  172. package/dist/run/openai.js +0 -181
  173. package/dist/run/openai.js.map +0 -1
  174. package/dist/run/otlp.js.map +0 -1
  175. package/dist/run/redisStore.js.map +0 -1
  176. package/dist/run/run.js +0 -129
  177. package/dist/run/run.js.map +0 -1
  178. package/dist/run/scheduler.js +0 -85
  179. package/dist/run/scheduler.js.map +0 -1
  180. package/dist/run/spec.js.map +0 -1
  181. package/dist/run/sqliteStore.js.map +0 -1
  182. package/dist/run/store.js +0 -28
  183. package/dist/run/store.js.map +0 -1
  184. package/dist/run/systemPrompt.js.map +0 -1
  185. package/dist/run/transport.js.map +0 -1
  186. package/dist/run/types.d.ts +0 -12
  187. package/dist/run/types.js +0 -2
  188. package/dist/run/types.js.map +0 -1
  189. /package/dist/{run → integrations}/otlp.d.ts +0 -0
  190. /package/dist/{run → integrations}/otlp.js +0 -0
  191. /package/dist/{run → store}/sqliteStore.d.ts +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 retrychx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,151 @@
1
+ # Agentia
2
+
3
+ [![CI](https://github.com/retrychx/agentia/actions/workflows/ci.yml/badge.svg)](https://github.com/retrychx/agentia/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/@migor/agentia)](https://www.npmjs.com/package/@migor/agentia) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
+
5
+ [中文 README](./README.md) · **English**
6
+
7
+ **A declarative framework for building production agent services.** You declare four kinds of capability
8
+ with decorators + DI and a main agent orchestrates them; every run yields structured output and an
9
+ observable call tree (trace, cost, metrics).
10
+
11
+ > This is a **summary**. The authoritative and always-current documentation is Chinese:
12
+ > [`docs/usage-guide.md`](./docs/usage-guide.md) (API reference, type wiring, known limits) and the
13
+ > [website](https://agentia-web.pages.dev). We keep this file short on purpose — a hand-maintained
14
+ > full translation drifts.
15
+
16
+ ## Requirements
17
+
18
+ Node.js **≥ 18** (the only `engines` requirement; CI runs 18 / 20 / 22). Deno and edge runtimes are
19
+ **not** validated. The only store touching a Node builtin is `SqliteTaskStore` (needs Node ≥ 22.5 for
20
+ `node:sqlite`) — it fails with a readable error at construction time, and never breaks importing the package.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ npm i @migor/agentia # the framework
26
+ npm i -g @migor/cli # CLI: scaffold / generate / dev inspector
27
+
28
+ agentia create my-app # scaffold ships .env / .env.example
29
+ $EDITOR my-app/.env # put ANTHROPIC_API_KEY here (export works too — real env wins)
30
+ # optional: ANTHROPIC_BASE_URL (compatible gateways), AGENTIA_MODEL (default claude-opus-5)
31
+ ```
32
+
33
+ > The framework does **not** read `.env` on its own: the scaffolded `src/main.ts` calls
34
+ > `loadEnvFile()` on its first line. Real environment variables always win over the file
35
+ > (CI / docker / command line), unless you pass `loadEnvFile({ override: true })`.
36
+
37
+ ## Quick start
38
+
39
+ ### A. CLI
40
+
41
+ ```bash
42
+ agentia create my-app
43
+ cd my-app && npm install
44
+ agentia g tool weather # create src/tools/weather/index.ts and register it
45
+ agentia dev # tsx watch + local inspector panel
46
+ ```
47
+
48
+ Four self-describing directories hold capabilities — the directory name **is** the kind:
49
+ `src/tools/` · `src/skills/` · `src/prompts/` · `src/subagents/`; the registry is `src/registry.ts`.
50
+
51
+ ### B. By hand
52
+
53
+ ```ts
54
+ import { Tool, createApp, SystemPrompt } from '@migor/agentia';
55
+
56
+ class WeatherTools {
57
+ @Tool({
58
+ description: 'Look up the weather for a city',
59
+ schema: {
60
+ type: 'object',
61
+ properties: { city: { type: 'string' } },
62
+ required: ['city'],
63
+ additionalProperties: false,
64
+ },
65
+ })
66
+ get_weather(input: { city: string }): string {
67
+ return `city=${input.city}`;
68
+ }
69
+ }
70
+
71
+ const app = createApp({
72
+ name: 'weather-app',
73
+ providers: [{ provide: 'weather', useClass: WeatherTools }],
74
+ system: new SystemPrompt().add('role', 'You are a helpful weather assistant.', true),
75
+ });
76
+
77
+ const { result } = await app.run([{ role: 'user', content: 'Weather in Shanghai?' }]);
78
+ console.log(result.finalText); // final text
79
+ console.log(result.trace); // call tree + per-step tokens/cost (traceId === runId)
80
+ ```
81
+
82
+ Or point `createApp` at the directories instead of passing providers:
83
+ `createApp({ discover: ['src/tools', 'src/skills', 'src/prompts', 'src/subagents'], system })`.
84
+
85
+ ## The four capability kinds
86
+
87
+ The model picks from one shared "menu" by `description`; the decorator decides **who controls the flow**:
88
+
89
+ | Kind | Decorator | Who controls the flow | Typical use |
90
+ |---|---|---|---|
91
+ | Tool | `@Tool` | your code (one call = one function) | deterministic work: DB, math, HTTP |
92
+ | Skill | `@Skill` | your code (scripted; model calls only at explicit `ctx.llm()`) | fixed "fetch → let the model write → post-process" pipelines |
93
+ | Sub-agent | `@SubAgent` | **the model** (own loop, trimmed context) | multi-step autonomy without polluting the main context |
94
+ | Prompt asset | `@Prompt` | pulled by the model on demand | long specs/templates that shouldn't sit in context by default |
95
+
96
+ ## Runtime features
97
+
98
+ - **Triggering** — `app.run()` (sync RPC) · `AsyncRunner` (idempotency-keyed, retryable, resumable via a
99
+ store) · `Scheduler` (cron-like) · `createHttpHandler` (`POST /run`, `POST /tasks`, `GET /tasks/:id`,
100
+ `GET /healthz`, optional `GET /metrics`, graceful `drain()`)
101
+ - **Context budget** — `createBudgetPolicy({ budgetTokens, summarize })`
102
+ - **Hard cost caps** — `maxTotalTokens` / `maxCostUsd` (+ `priceOverrides` to price non-Anthropic models)
103
+ - **Structured output** — `resultSchema`; `result.typed` is validated, or use `fromZod<T>()` for full type inference
104
+ - **Middleware** — an onion chain around every capability call (auth, rate limiting, caching, audit)
105
+ - **Cancellation / retries / streaming / tool concurrency** — `signal`, retry policy, `onText`, `maxToolConcurrency`
106
+
107
+ ## Observability & cost
108
+
109
+ One run == one trace (`traceId === runId`), **built in from turn 0** — not a bolt-on third-party tracing SDK:
110
+
111
+ - **Per-step accounting** — every span carries model, input/output/cache tokens, cost estimate, status and error type
112
+ - **One seam out** — `TraceSink { export(trace) }`, delivered on **both** the success and failure paths; a throwing
113
+ sink never breaks the run. Persistence / sampling / redaction are composed outside the seam
114
+ (`examples/observability/`, [`docs/observability.md`](./docs/observability.md))
115
+ - **Cost you can cap** — `priceOverrides` patches the price table; unpriced models emit an explicit
116
+ `usage.unpriced` event (never a silent zero); `maxCostUsd` / `maxTotalTokens` stop the run
117
+ - **Replay** — `traceToMessages(trace)` linearises a finished trace back into model messages
118
+ - **Export** — `createOtlpExporter({ endpoint })`; `metricsSink()` (Prometheus text or OTLP metrics), zero dependency
119
+ - **Locally visible** — `agentia dev` opens an inspector panel sharing one renderer with the website Playground;
120
+ `agentia report <trace.jsonl>` ranks which capability is slow / expensive / error-prone
121
+
122
+ ## Integrations
123
+
124
+ - **Models** — `createAnthropicClient()` (default) and `createOpenAIClient()` for any OpenAI-compatible
125
+ endpoint (DeepSeek, **Ollama**, gateways). You never need to touch a vendor SDK: pass
126
+ `createAnthropicClient({ baseURL })` / `createOpenAIClient({ baseURL })`.
127
+ - **MCP** — `mcpTools()` bridges an MCP server's tools into the menu (duck-typed, zero dependency)
128
+ - **Memory** — `memory: { store, keys }` for cross-run state; task stores: memory / JSONL / SQLite / Redis
129
+ - **Evals** — `scriptedClient` + `defineEval` turn mocked runs into a first-class diagnostic loop
130
+ - **Platforms** — send traces to Langfuse / Phoenix / etc. via OTLP or a tiny custom `TraceSink`
131
+ (see [`docs/observability.md`](./docs/observability.md) §2.6)
132
+
133
+ ## Documentation
134
+
135
+ | | |
136
+ |---|---|
137
+ | Usage guide (authoritative, zh) | [`docs/usage-guide.md`](./docs/usage-guide.md) |
138
+ | Production observability recipes | [`docs/observability.md`](./docs/observability.md) |
139
+ | Examples (complete app / minimal deploy / observability) | [`examples/`](./examples/) |
140
+ | Design spec & roadmap | [`docs/spec.md`](./docs/spec.md) · [`docs/roadmap.md`](./docs/roadmap.md) |
141
+ | Website · Playground | [agentia-web.pages.dev](https://agentia-web.pages.dev) · [playground](https://agentia-web.pages.dev/playground.html) |
142
+
143
+ ## Contributing
144
+
145
+ See [`CONTRIBUTING.md`](./CONTRIBUTING.md) — and note that [`AGENTS.md`](./AGENTS.md) is the single
146
+ source for this repo's engineering conventions. Security issues: [`SECURITY.md`](./SECURITY.md)
147
+ (please don't open a public issue).
148
+
149
+ ## License
150
+
151
+ [MIT](./LICENSE)
package/README.md CHANGED
@@ -1,44 +1,70 @@
1
1
  # Agentia
2
2
 
3
- 声明式 agent 服务开发框架:TS 装饰器 + DI,主 agent 调度 `@Tool` / `@Skill` / `@SubAgent` / `@Prompt` 单元执行任务,产出结构化结果与调用树(trace)。
3
+ [中文](./README.md) · [English](./README.en.md)
4
4
 
5
- ## 环境准备
5
+ [![CI](https://github.com/retrychx/agentia/actions/workflows/ci.yml/badge.svg)](https://github.com/retrychx/agentia/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/@migor/agentia)](https://www.npmjs.com/package/@migor/agentia) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ 面向应用开发的声明式 agent 服务开发框架:装饰器 + DI 声明四类能力,主 agent 编排执行;每次 run 产出结构化结果与可观测调用树(trace、成本、指标),交付可直接上线的服务。
8
+
9
+ - [安装与配置](#安装与配置)
10
+ - [快速开始](#快速开始)
11
+ - [四类能力](#四类能力)
12
+ - [运行时特性](#运行时特性)
13
+ - [可观测性与成本](#可观测性与成本)
14
+ - [集成](#集成)
15
+ - [API 速查](#api-速查)
16
+ - [开发本仓库](#开发本仓库)
17
+ - [延伸阅读](#延伸阅读)
18
+
19
+ ## 安装与配置
6
20
 
7
21
  ```bash
8
- export ANTHROPIC_API_KEY=sk-... # 或 ANTHROPIC_AUTH_TOKEN
22
+ npm i @migor/agentia # 框架(项目依赖)
23
+ npm i -g @migor/cli # 命令行工具(脚手架 / 生成 / 调试)
24
+
25
+ agentia create my-app # 脚手架(含 .env / .env.example)
26
+ $EDITOR my-app/.env # 填 ANTHROPIC_API_KEY(也可直接 export,真实环境变量优先)
9
27
  # 可选:ANTHROPIC_BASE_URL(兼容端点)、AGENTIA_MODEL(缺省 claude-opus-5)
10
28
  ```
11
29
 
12
- ## 快速开始(CLI)
30
+ > 框架**不自动**读 `.env`:脚手架 `src/main.ts` 首行的 `loadEnvFile()` 负责把它读进 `process.env`。
31
+ > 真实环境变量优先(CI / docker / 命令行永远赢过文件),`loadEnvFile({ override: true })` 才反过来。
32
+
33
+ > **版本**:`0.3.0`(`@migor/agentia` 与 `@migor/cli` 均已发布到 npm)。`examples/` **刻意**用
34
+ > `file:../..` 指向本仓库而不是 npm 版本 —— 跑的是**工作区代码**,理由见
35
+ > [`examples/README.md`](examples/README.md) 的「依赖说明」。
36
+
37
+ > **运行时**:Node ≥ 18(`engines` 唯一要求,CI 在 18/20/22 上守)。按 Node 设计并测试,
38
+ > **未对 Deno / edge 做验证**。唯一碰内置模块的 store 是 `SqliteTaskStore`(需 Node ≥ 22.5 的
39
+ > `node:sqlite`)—— 未提供时**构造期给可读报错**,不影响包本身被导入。
40
+
41
+
42
+ ## 快速开始
43
+
44
+ 两条路线,按需选一即可(也可混用)。
45
+
46
+ ### 路线 A:CLI 脚手架
13
47
 
14
48
  ```bash
15
- npm i -g @migor/cli # 安装 CLI(提供 agentia 命令)
16
49
  agentia create my-app # 脚手架新项目(依赖 @migor/agentia)
17
50
  cd my-app && npm install
18
- agentia g tool weather # 生成 units/weather/index.ts 并登记 units.ts
19
- agentia g subagent doc-reviewer # 生成 units/doc-reviewer/{index.ts,system.md}
20
- agentia g skill note-writer # 生成 units/note-writer/index.ts
21
- agentia g prompt style-guide # 生成 units/style-guide/{index.ts,asset.md}
22
- npm run dev # 运行 src/main.ts
51
+ agentia g tool weather # 生成 src/tools/weather/index.ts 并登记 src/registry.ts
52
+ agentia g subagent doc-reviewer # 生成 src/subagents/doc-reviewer/{index.ts,system.md}
53
+ agentia g skill note-writer # 生成 src/skills/note-writer/index.ts
54
+ agentia g prompt style-guide # 生成 src/prompts/style-guide/{index.ts,asset.md}
55
+ agentia dev # tsx watch + 本地 inspector 面板
23
56
  ```
24
57
 
25
- **目录约定**:`units/<name>/` 一单元一文件夹,`index.ts` default export 一个 provider 类,DI token 缺省 = 文件夹名;长文本放文件夹内 `.md`,代码里用 `asset(import.meta.url, './system.md')` 读取。
58
+ **目录约定**:四分类目录,一能力一文件夹 —— `src/tools/` · `src/skills/` · `src/prompts/` · `src/subagents/`。
59
+ 目录名就是类型,不用记别名。每个文件夹的 `index.ts` default export 一个 provider 类,DI token 缺省 = 文件夹名;
60
+ 长文本放文件夹内 `.md`,代码里用 `asset(import.meta.url, './system.md')` 读取。
26
61
 
27
- **装配两条路(可混用)**:
62
+ ### 路线 B:手写装配
28
63
 
29
- ```ts
30
- // 1) 目录扫描:启动期扫描 units/*/ 自动装配(返回 Promise)
31
- const app = await createApp({ name: 'my-app', discover: 'units', system });
32
-
33
- // 2) 显式注册表:units.ts 由 CLI 自动维护
34
- import { providers } from './units.js';
35
- const app = createApp({ name: 'my-app', providers, system });
36
- ```
37
-
38
- ## 手写单元
64
+ 不依赖 CLI,直接声明能力并装配成应用:
39
65
 
40
66
  ```ts
41
- import { Tool, SubAgent, createApp, SystemPrompt, RunContext } from '@migor/agentia';
67
+ import { Tool, createApp, SystemPrompt, RunContext } from '@migor/agentia';
42
68
 
43
69
  class WeatherTools {
44
70
  // 工具名缺省取方法名(建议 snake_case);入参 = 模型按 schema 解析的结构化 input
@@ -53,30 +79,17 @@ class WeatherTools {
53
79
  strict: true,
54
80
  })
55
81
  get_weather(input: { city: string }): string {
56
- // 方法体内随时可读本次 run 的上下文(blackboard)
57
- const token = RunContext.current()?.get<string>('authToken');
82
+ // 方法体内随时可读本次 run 的上下文(blackboard)。
83
+ // 想让键有补全/校验:在一次 declare module 里合并 Blackboard(见 docs/usage-guide.md §5.1);
84
+ // 未声明时键为 string、值为 unknown,这里按需断言。
85
+ const token = RunContext.current()?.get('authToken') as string | undefined;
58
86
  return `city=${input.city};token=${token ?? 'none'}`;
59
87
  }
60
88
  }
61
89
 
62
- class Pipeline {
63
- // 子 agent:独立循环 + 裁剪上下文,只有最终报告回流主上下文(方法体不执行)
64
- @SubAgent({
65
- name: 'reviewer',
66
- description: '审查给定文档并输出书面评审意见',
67
- schema: { type: 'object', properties: { doc: { type: 'string' } }, required: ['doc'], additionalProperties: false },
68
- system: '你是评审 agent。结论必须以“审查通过/不通过”开头。',
69
- tools: ['weather'], // 子 agent 自己的工具菜单(provider token)
70
- })
71
- reviewer(_input: { doc: string }): void {}
72
- }
73
-
74
90
  const app = createApp({
75
91
  name: 'weather-app',
76
- providers: [
77
- { provide: 'weather', useClass: WeatherTools },
78
- { provide: 'pipeline', useClass: Pipeline },
79
- ],
92
+ providers: [{ provide: 'weather', useClass: WeatherTools }],
80
93
  system: new SystemPrompt().add('role', '你是天气助手。', true),
81
94
  });
82
95
 
@@ -88,9 +101,33 @@ console.log(result.finalText); // 最终文本
88
101
  console.log(result.trace); // 调用树 + 每步 token/成本(traceId == runId)
89
102
  ```
90
103
 
91
- 另外两类单元:
104
+ 装配还有**目录扫描**这条路(与显式注册表可混用):
92
105
 
93
106
  ```ts
107
+ // 按给定顺序扫各目录(返回 Promise);也可用 CLI 维护的 src/registry.ts 显式注册
108
+ const app = await createApp({
109
+ name: 'my-app',
110
+ discover: ['src/tools', 'src/skills', 'src/prompts', 'src/subagents'],
111
+ system,
112
+ });
113
+ ```
114
+
115
+ ## 四类能力
116
+
117
+ 主 agent 按 `description` 从同一张「能力菜单」自选;装饰器只决定**谁控制流程**:
118
+
119
+ | 能力 | 装饰器 | 谁决定流程 | 典型用途 |
120
+ |---|---|---|---|
121
+ | 工具 | `@Tool` | 你的代码(一次调用 = 一个函数) | 确定性操作:查库、算数、调 API |
122
+ | 技能 | `@Skill` | 你的代码(脚本式,显式 `ctx.llm()`) | 「先取数 → 再让模型写 → 再加工」的固定流程 |
123
+ | 子 agent | `@SubAgent` | **模型自己**(独立循环 + 裁剪上下文) | 自主多步、且中间过程不该污染主上下文 |
124
+ | 提示资产 | `@Prompt` | 模型拉取(按需注入的文本) | 长文规范/模板,平时不进上下文 |
125
+
126
+ `@Tool` 的写法见上方路线 B;其余三类:
127
+
128
+ ```ts
129
+ import { Skill, SubAgent, Prompt, asset, type SkillContext } from '@migor/agentia';
130
+
94
131
  // @Skill:代码控制的流程,模型调用只发生在显式 ctx.llm()
95
132
  class Notes {
96
133
  @Skill({ description: '按主题整理要点' })
@@ -100,20 +137,53 @@ class Notes {
100
137
  }
101
138
  }
102
139
 
140
+ // @SubAgent:独立循环 + 裁剪上下文,只有最终报告回流主上下文(方法体不执行)
141
+ class Pipeline {
142
+ @SubAgent({
143
+ name: 'reviewer',
144
+ description: '审查给定文档并输出书面评审意见',
145
+ schema: { type: 'object', properties: { doc: { type: 'string' } }, required: ['doc'], additionalProperties: false },
146
+ system: '你是评审 agent。结论必须以“审查通过/不通过”开头。',
147
+ tools: ['weather'], // 子 agent 自己的工具菜单(provider token)
148
+ })
149
+ reviewer(_input: { doc: string }): void {}
150
+ }
151
+
103
152
  // @Prompt:纯文本资产,模型判定需要时拉取进上下文
104
153
  class Assets {
105
154
  @Prompt({ description: '品牌基调文案规范' })
106
155
  style_guide(): string {
107
- return asset(import.meta.url, './asset.md'); // 从单元文件夹读 .md
156
+ return asset(import.meta.url, './asset.md'); // 从能力文件夹读 .md
108
157
  }
109
158
  }
110
159
  ```
111
160
 
112
- ## 触发方式
161
+ 四类一起装配(`providers` 里逐个登记;token 即子 agent / 技能引用工具的菜单名):
162
+
163
+ ```ts
164
+ import { createApp, SystemPrompt } from '@migor/agentia';
165
+
166
+ const app = createApp({
167
+ name: 'weather-app',
168
+ providers: [
169
+ { provide: 'weather', useClass: WeatherTools },
170
+ { provide: 'notes', useClass: Notes },
171
+ { provide: 'pipeline', useClass: Pipeline },
172
+ { provide: 'assets', useClass: Assets },
173
+ ],
174
+ system: new SystemPrompt().add('role', '你是天气助手。', true),
175
+ });
176
+ ```
177
+
178
+ ## 运行时特性
179
+
180
+ ### 触发方式
113
181
 
114
182
  同一份应用,三种触发任选:
115
183
 
116
184
  ```ts
185
+ import { AsyncRunner, FileTaskStore, Scheduler } from '@migor/agentia';
186
+
117
187
  // 同步 RPC
118
188
  const { run, result } = await app.run(messages);
119
189
 
@@ -127,7 +197,7 @@ const scheduler = new Scheduler(runner);
127
197
  scheduler.every(60_000, '巡检一次', { idempotencyKey: 'patrol' });
128
198
  ```
129
199
 
130
- ## 长上下文预算
200
+ ### 长上下文预算
131
201
 
132
202
  ```ts
133
203
  import { createBudgetPolicy } from '@migor/agentia';
@@ -135,22 +205,37 @@ import { createBudgetPolicy } from '@migor/agentia';
135
205
  const app = createApp({
136
206
  // ...
137
207
  contextPolicy: createBudgetPolicy({
138
- budgetTokens: 60_000, // 超预算先丢旧工具对,仍超且有 summarize 才压缩
208
+ budgetTokens: 60_000, // 超预算先丢旧工具对,仍超且有 summarize 才压缩
139
209
  summarize: (text) => mySummarizer(text), // 可选,框架不替你调模型
140
210
  }),
141
211
  });
142
212
  ```
143
213
 
144
- ## 中间件
214
+ ### 结构化结果
145
215
 
146
- 挂在每一次单元调用前后的洋葱链——鉴权、限流、缓存、审计都走这里:
216
+ ```ts
217
+ const { result } = await app.run(messages, {
218
+ resultSchema: {
219
+ type: 'object',
220
+ properties: { pass: { type: 'boolean' }, reason: { type: 'string' } },
221
+ required: ['pass', 'reason'],
222
+ additionalProperties: false,
223
+ },
224
+ });
225
+ console.log(result.typed); // 校验过的结构化结果,不再从文本里猜 JSON
226
+ // schema 也可来自 zod(peer 可选):fromZod(z.toJSONSchema(S), S)
227
+ ```
228
+
229
+ ### 中间件
230
+
231
+ 挂在每一次能力调用前后的洋葱链——鉴权、限流、缓存、审计都走这里:
147
232
 
148
233
  ```ts
149
234
  const app = createApp({
150
235
  // ...
151
236
  middleware: [
152
237
  async (call, next) => {
153
- if (call.unit.name === 'danger_op' && !RunContext.current()?.get('isAdmin')) {
238
+ if (call.capability.name === 'danger_op' && !RunContext.current()?.get('isAdmin')) {
154
239
  return 'forbidden'; // 短路,不调 next 即拦截
155
240
  }
156
241
  return next(); // next(newInput) 还可改写入参
@@ -159,52 +244,86 @@ const app = createApp({
159
244
  });
160
245
  ```
161
246
 
162
- ## 结构化结果
247
+ ## 可观测性与成本
248
+
249
+ 一次 run == 一条 trace(`traceId === runId`),**Turn 0 起内建** —— 不是外挂的第三方追踪 SDK 集成:
163
250
 
164
251
  ```ts
165
- const { result } = await app.run(messages, {
166
- resultSchema: {
167
- type: 'object',
168
- properties: { pass: { type: 'boolean' }, reason: { type: 'string' } },
169
- required: ['pass', 'reason'],
170
- additionalProperties: false,
171
- },
172
- });
173
- console.log(result.typed); // 校验过的结构化结果,不再从文本里猜 JSON
174
- // schema 也可来自 zod(peer 可选):fromZod(z.toJSONSchema(S), S)
252
+ const { run, result } = await app.run(messages);
253
+ result.trace.spans; // 调用树:llm.turn / 能力 span / 事件
254
+ result.trace.totalUsage; // token 汇总(只累加 llm.turn;能力 span 是子孙聚合,不参与求和)
175
255
  ```
176
256
 
177
- ## 宿主、多模型与记忆
257
+ - **每步记账**:span 属性带 model、input/output/cache tokens、成本估计、状态、错误类型
258
+ - **出口是一条缝**:`TraceSink { export(trace) }` —— run 收尾(成功/失败两条路径)都投递,sink 抛错不影响 run。落库 / 采样 / 脱敏都在缝外用 sink 组合(实码见 `examples/observability/`,说明见 `docs/observability.md`)
259
+ - **指标**:`metricsSink` 满足 `TraceSink` 即可接入(Prometheus 文本 / OTLP metrics),零依赖
260
+ - **成本**:`priceOverrides` 注入价目表;未定价模型显式发 `usage.unpriced` 事件;`createBudgetGuard` 做**硬管控**(超限 run 以 `budget_exceeded` 收尾、算失败)
261
+ - **调优闭环**:`buildRunReport` 出「能力 / 模型的耗时·token·成本·错误率排行」,CLI `agentia report <trace.jsonl>` 直接渲染
262
+ - **回放**:`traceToMessages` 把已完成的 trace 线性化喂回模型(调试基底)
263
+ - **本地开发**:`agentia dev` 的 inspector 面板与 `@migor/trace-view` 共用同一份渲染器(避免两处漂移)
264
+ - **导出 OTLP**:
178
265
 
179
266
  ```ts
180
- // HTTP 宿主:POST /run(同步) POST /tasks(异步) GET /tasks/:id(轮询)
267
+ import { createOtlpExporter } from '@migor/agentia';
268
+
269
+ await createOtlpExporter({ endpoint: 'http://localhost:4318' }).export(result.trace);
270
+ ```
271
+
272
+ > 权威口径见 `docs/usage-guide.md` 的「观测」与「成本硬管控」两节。
273
+
274
+ ## 集成
275
+
276
+ ### HTTP 宿主
277
+
278
+ ```ts
279
+ import { createServer } from 'node:http';
280
+ import { createHttpHandler } from '@migor/agentia';
281
+
282
+ // POST /run(同步) POST /tasks(异步) GET /tasks/:id(轮询)
181
283
  createServer(createHttpHandler(app, { runner })).listen(8080);
284
+ ```
285
+
286
+ ### 多模型(OpenAI 兼容端点)
287
+
288
+ ```ts
289
+ import { createOpenAIClient } from '@migor/agentia';
182
290
 
183
- // OpenAI 兼容端点(DeepSeek 等)+ 跨 run 记忆
184
291
  const { result } = await app.run(messages, {
185
292
  client: createOpenAIClient({ baseURL: 'https://api.deepseek.com' }),
186
293
  model: 'deepseek-chat',
187
- memory: { store: new InMemoryMemoryStore(), keys: ['profile'] },
188
294
  });
295
+ ```
189
296
 
190
- // trace 导出到 OTLP 收集器
191
- await createOtlpExporter({ endpoint: 'http://localhost:4318' }).export(result.trace);
297
+ > **本地模型(Ollama 等)**:Ollama 暴露 OpenAI 兼容端点,用同一个适配器即可 ——
298
+ > `createOpenAIClient({ baseURL: 'http://localhost:11434/v1' })` + `model: 'qwen3'`,
299
+ > **不需要任何新代码或新依赖**。
300
+
301
+
302
+ ### 跨 run 记忆
303
+
304
+ ```ts
305
+ import { InMemoryMemoryStore } from '@migor/agentia';
306
+
307
+ const { result } = await app.run(messages, {
308
+ memory: { store: new InMemoryMemoryStore(), keys: ['profile'] },
309
+ });
192
310
  ```
193
311
 
194
- ## 常用 API
312
+ ## API 速查
195
313
 
196
314
  | 导出 | 用途 |
197
315
  |---|---|
198
- | `createApp` / `discoverProviders` | 装配应用 / 扫描单元目录 |
316
+ | `createApp` / `discoverProviders` | 装配应用 / 扫描能力目录 |
199
317
  | `defineModule` | 能力包(providers + middleware 打包分发) |
200
- | `Tool` / `Skill` / `SubAgent` / `Prompt` | 四类单元装饰器 |
201
- | `UnitMiddleware`(`middleware` 选项) | 单元调用拦截器链 |
202
- | `asset` | 读单元文件夹内的文本资产 |
318
+ | `Tool` / `Skill` / `SubAgent` / `Prompt` | 四类能力装饰器 |
319
+ | `CapabilityMiddleware`(`middleware` 选项) | 能力调用拦截器链 |
320
+ | `asset` | 读能力文件夹内的文本资产 |
203
321
  | `SystemPrompt` | 拼装系统提示(自动打缓存 breakpoint) |
204
322
  | `RunContext.current()` | 取本次 run 的 blackboard / runId |
205
323
  | `AsyncRunner` / `Scheduler` / `runSync` / `createHttpHandler` | 异步 / 定时 / 同步 / HTTP 触发 |
206
324
  | `FileTaskStore` / `SqliteTaskStore` / `RedisTaskStore` / `InMemoryTaskStore` | 任务记录存储 |
207
325
  | `createBudgetPolicy` | 长上下文预算护栏 |
326
+ | `createAnthropicClient` | 默认 ModelClient(Anthropic):自定义只传 `apiKey` / `baseURL`,不必直接依赖厂商 SDK |
208
327
  | `createOpenAIClient` | OpenAI 兼容端点适配(多模型) |
209
328
  | `InMemoryMemoryStore`(`memory` 选项) | 跨 run 记忆水合/回写 |
210
329
  | `createOtlpExporter` | trace 导出 OTLP |
@@ -212,16 +331,27 @@ await createOtlpExporter({ endpoint: 'http://localhost:4318' }).export(result.tr
212
331
  | `fromZod` | zod schema 接入(peer 可选) |
213
332
  | `runAgent` / `executeRun` | 裸引擎入口(不走装配) |
214
333
 
215
- 完整导出见 [`src/index.ts`](src/index.ts),设计规格见 [`docs/spec.md`](docs/spec.md),roadmap 见 [`docs/roadmap.md`](docs/roadmap.md),官网见 [agentia-web.pages.dev](https://agentia-web.pages.dev)(含[在线 Playground](https://agentia-web.pages.dev/playground.html) 与[文档](https://agentia-web.pages.dev/docs.html))。
216
-
217
- ## 本仓库脚本
334
+ ## 开发本仓库
218
335
 
219
336
  ```bash
220
337
  npm install
221
338
  npm run build # 编译框架(dist/)
222
339
  npm run typecheck # 类型检查
340
+ npm run lint # lint + 格式检查(Biome)
223
341
  npm test # 单元测试(node:test)
224
- npm run e2e # 端到端:CLI 脚手架 → 目录发现/注册表装配 → mock run
342
+ npm run e2e # 端到端:CLI 脚手架 → 装配 mock run;再真跑 examples/complete 的完整服务
225
343
  ```
226
344
 
227
- > 注:`npm run dev`(tsx)前需先 `npm approve-scripts` 批准 esbuild/tsx 的 postinstall。
345
+ > 注:本仓库的 `npm run dev`(tsx)前需先 `npm approve-scripts` 批准 esbuild/tsx 的 postinstall;
346
+ > 脚手架项目里的 `agentia dev` 不受此限。
347
+
348
+ ## 延伸阅读
349
+
350
+ - **使用者向完整说明**(API 速查 / 类型链路 / 已知边界 / 反例):[`docs/usage-guide.md`](docs/usage-guide.md)
351
+ - 完整导出清单:[`src/index.ts`](src/index.ts)
352
+ - 设计规格:[`docs/spec.md`](docs/spec.md)
353
+ - 生产可观测配方(落库检索 / 日志关联 / 采样 / 脱敏):[`docs/observability.md`](docs/observability.md)
354
+ - 完整示例(四类能力 + 三种触发 + 全观测栈):[`examples/complete/`](examples/complete/)
355
+ - 最小部署示例:[`examples/deploy/`](examples/deploy/)
356
+ - 方向与状态:[`docs/roadmap.md`](docs/roadmap.md)
357
+ - 官网:[agentia-web.pages.dev](https://agentia-web.pages.dev)([在线 Playground](https://agentia-web.pages.dev/playground.html) · [文档](https://agentia-web.pages.dev/docs.html))