@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
@@ -1,14 +1,13 @@
1
1
  /**
2
- * Agentia —— 单元收集器共用内核。
2
+ * Agentia —— 能力收集器共用内核。
3
3
  *
4
- * 四类单元(tool/skill/subagent/prompt)的装饰器都只把「方法函数 → spec」登记进
4
+ * 四类能力(tool/skill/subagent/prompt)的装饰器都只把「方法函数 → spec」登记进
5
5
  * 各自的 WeakMap;收集器在容器实例解析后沿原型链扫描出被装饰的方法。
6
- * 本模块是这一扫描的唯一实现,四个 collect* 只做各自单元形态的组装。
6
+ * 本模块是这一扫描的唯一实现,四个 collect* 只做各自能力形态的组装。
7
7
  */
8
- /** 一次扫描命中:方法 key、原型上的方法函数、登记的 spec。 */
8
+ /** 一次扫描命中:方法 key、登记的 spec。 */
9
9
  export interface DecoratedMethod<S> {
10
10
  key: string | symbol;
11
- fn: Function;
12
11
  spec: S;
13
12
  }
14
13
  /**
@@ -17,7 +16,21 @@ export interface DecoratedMethod<S> {
17
16
  * 父类的 spec(spec 沿原型链继承,调用仍走实例上的子类实现)。
18
17
  */
19
18
  export declare function scanDecoratedMethods<S>(instance: object, registry: WeakMap<Function, S>): DecoratedMethod<S>[];
20
- /** 单元名解析:spec.name 缺省取方法名;私有符号方法名必须显式给 name。 */
21
- export declare function unitName(spec: {
19
+ /** 装饰器收到的标准 context 子集(本框架只用到这三个字段) */
20
+ export interface CapabilityDecoratorContext {
21
+ kind: string;
22
+ name: string | symbol;
23
+ /** 标准装饰器 context 的私有方法标记(`#method` 的 kind 同样是 'method') */
24
+ private?: boolean;
25
+ }
26
+ /**
27
+ * 四类能力装饰器共用的目标守卫:只接类方法,且**拒绝私有方法**。
28
+ *
29
+ * 收集走 `Reflect.ownKeys`,私有名(#method)在其中不可见 —— 不拦的话能力会
30
+ * 静默从菜单里消失,作者只能靠「模型说没有这个工具」反推。
31
+ */
32
+ export declare function assertMethodTarget(context: CapabilityDecoratorContext, kind: string): void;
33
+ /** 能力名解析:spec.name 缺省取方法名;私有符号方法名必须显式给 name。 */
34
+ export declare function capabilityName(spec: {
22
35
  name?: string;
23
36
  }, key: string | symbol, kind: string): string;
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Agentia —— 单元收集器共用内核。
2
+ * Agentia —— 能力收集器共用内核。
3
3
  *
4
- * 四类单元(tool/skill/subagent/prompt)的装饰器都只把「方法函数 → spec」登记进
4
+ * 四类能力(tool/skill/subagent/prompt)的装饰器都只把「方法函数 → spec」登记进
5
5
  * 各自的 WeakMap;收集器在容器实例解析后沿原型链扫描出被装饰的方法。
6
- * 本模块是这一扫描的唯一实现,四个 collect* 只做各自单元形态的组装。
6
+ * 本模块是这一扫描的唯一实现,四个 collect* 只做各自能力形态的组装。
7
7
  */
8
8
  /**
9
9
  * 沿实例原型链扫描被装饰的方法(子类 → 父类)。
@@ -16,7 +16,7 @@ export function scanDecoratedMethods(instance, registry) {
16
16
  let proto = Object.getPrototypeOf(instance);
17
17
  while (proto && proto !== Object.prototype) {
18
18
  // Reflect.ownKeys 含 symbol key:symbol 命名的装饰方法也会被找到,
19
- // 无显式 name 时由 unitName 抛出提示(而非静默忽略)
19
+ // 无显式 name 时由 capabilityName 抛出提示(而非静默忽略)
20
20
  for (const key of Reflect.ownKeys(proto)) {
21
21
  if (seen.has(key))
22
22
  continue;
@@ -27,14 +27,28 @@ export function scanDecoratedMethods(instance, registry) {
27
27
  if (!spec)
28
28
  continue; // 未装饰的 override 不标 seen,父类 spec 继续生效
29
29
  seen.add(key);
30
- found.push({ key, fn: desc.value, spec });
30
+ found.push({ key, spec });
31
31
  }
32
32
  proto = Object.getPrototypeOf(proto);
33
33
  }
34
34
  return found;
35
35
  }
36
- /** 单元名解析:spec.name 缺省取方法名;私有符号方法名必须显式给 name。 */
37
- export function unitName(spec, key, kind) {
36
+ /**
37
+ * 四类能力装饰器共用的目标守卫:只接类方法,且**拒绝私有方法**。
38
+ *
39
+ * 收集走 `Reflect.ownKeys`,私有名(#method)在其中不可见 —— 不拦的话能力会
40
+ * 静默从菜单里消失,作者只能靠「模型说没有这个工具」反推。
41
+ */
42
+ export function assertMethodTarget(context, kind) {
43
+ if (context.kind !== 'method') {
44
+ throw new Error(`${kind} 只能修饰类方法,收到 kind=${String(context.kind)}`);
45
+ }
46
+ if (context.private) {
47
+ throw new Error(`${kind} 不支持私有方法(#method):收集用 Reflect.ownKeys,私有名不可见`);
48
+ }
49
+ }
50
+ /** 能力名解析:spec.name 缺省取方法名;私有符号方法名必须显式给 name。 */
51
+ export function capabilityName(spec, key, kind) {
38
52
  if (typeof spec.name === 'string')
39
53
  return spec.name;
40
54
  if (typeof key !== 'string') {
@@ -1 +1 @@
1
- {"version":3,"file":"collect.js","sourceRoot":"","sources":["../../src/toolkit/collect.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB,EAChB,QAA8B;IAE9B,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAmB,CAAC;IAExC,IAAI,KAAK,GAAkB,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC3D,OAAO,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC3C,oDAAoD;QACpD,oCAAoC;QACpC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACzD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU;gBAAE,SAAS;YACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAiB,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI;gBAAE,SAAS,CAAC,qCAAqC;YAC1D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,KAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,QAAQ,CAAC,IAAuB,EAAE,GAAoB,EAAE,IAAY;IAClF,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACpD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,uBAAuB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"collect.js","sourceRoot":"","sources":["../../src/toolkit/collect.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB,EAChB,QAA8B;IAE9B,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAmB,CAAC;IAExC,IAAI,KAAK,GAAkB,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC3D,OAAO,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC3C,oDAAoD;QACpD,0CAA0C;QAC1C,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACzD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU;gBAAE,SAAS;YACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAiB,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI;gBAAE,SAAS,CAAC,qCAAqC;YAC1D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5B,CAAC;QACD,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAUD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAmC,EAAE,IAAY;IAClF,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oBAAoB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8CAA8C,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,cAAc,CAC5B,IAAuB,EACvB,GAAoB,EACpB,IAAY;IAEZ,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACpD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,uBAAuB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -1,2 +1,22 @@
1
1
  import type { Provider } from '../container/container.js';
2
- export declare function discoverProviders(dir: string): Promise<Provider[]>;
2
+ /**
3
+ * Agentia —— 能力目录发现(`<目录>/<name>/index.ts` 约定,spec §7 静态校验仍由装配层统一做)。
4
+ *
5
+ * 入参给**一个目录**或**一组目录**:数组拼成一条搜索路径,**顺序即装配顺序**
6
+ * (典型布局是四分类目录 `src/tools` / `src/skills` / `src/prompts` / `src/subagents`)。
7
+ *
8
+ * 约定(对每个目录各自成立):
9
+ * - 每个子目录 = 一个能力文件夹,入口为 index.ts / index.mts / index.js / index.mjs(按序取先存在者);
10
+ * - 入口 default export 支持三种形态:
11
+ * 1. 类 → { provide: <文件夹名>, useClass: 该类 }
12
+ * 2. Provider 对象 → 原样使用(可用 useFactory/useValue 自定义 token)
13
+ * 3. Provider 数组 → 展开(一个文件夹暴露多个 provider)
14
+ * - DI token 缺省 = 文件夹名(kebab-case 文件夹即 kebab-case token,能力引用照写)。
15
+ *
16
+ * 与手动 providers 可混用:createApp({ providers, discover }) 合并注册,
17
+ * 重名/引用校验在装配期统一进行(AgentApp 构造函数)。
18
+ *
19
+ * 动态 import 决定本函数是异步的 —— createApp 带 discover 时同样返回 Promise。
20
+ */
21
+ export declare const ENTRY_CANDIDATES: string[];
22
+ export declare function discoverProviders(dir: string | string[]): Promise<Provider[]>;
@@ -1,50 +1,97 @@
1
- import { existsSync, readdirSync } from 'node:fs';
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
2
  import { isAbsolute, join, resolve } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { isProvider } from '../container/container.js';
5
5
  /**
6
- * Agentia —— 单元目录发现(units/<name>/ 目录约定,spec §7 静态校验仍由装配层统一做)。
6
+ * Agentia —— 能力目录发现(`<目录>/<name>/index.ts` 约定,spec §7 静态校验仍由装配层统一做)。
7
7
  *
8
- * 约定:
9
- * - 每个子目录 = 一个单元文件夹,入口为 index.ts / index.mts / index.js / index.mjs(按序取先存在者);
8
+ * 入参给**一个目录**或**一组目录**:数组拼成一条搜索路径,**顺序即装配顺序**
9
+ * (典型布局是四分类目录 `src/tools` / `src/skills` / `src/prompts` / `src/subagents`)。
10
+ *
11
+ * 约定(对每个目录各自成立):
12
+ * - 每个子目录 = 一个能力文件夹,入口为 index.ts / index.mts / index.js / index.mjs(按序取先存在者);
10
13
  * - 入口 default export 支持三种形态:
11
14
  * 1. 类 → { provide: <文件夹名>, useClass: 该类 }
12
15
  * 2. Provider 对象 → 原样使用(可用 useFactory/useValue 自定义 token)
13
16
  * 3. Provider 数组 → 展开(一个文件夹暴露多个 provider)
14
- * - DI token 缺省 = 文件夹名(kebab-case 文件夹即 kebab-case token,单元引用照写)。
17
+ * - DI token 缺省 = 文件夹名(kebab-case 文件夹即 kebab-case token,能力引用照写)。
15
18
  *
16
19
  * 与手动 providers 可混用:createApp({ providers, discover }) 合并注册,
17
20
  * 重名/引用校验在装配期统一进行(AgentApp 构造函数)。
18
21
  *
19
22
  * 动态 import 决定本函数是异步的 —— createApp 带 discover 时同样返回 Promise。
20
23
  */
21
- const ENTRY_CANDIDATES = ['index.ts', 'index.mts', 'index.js', 'index.mjs'];
24
+ export const ENTRY_CANDIDATES = ['index.ts', 'index.mts', 'index.js', 'index.mjs'];
22
25
  export async function discoverProviders(dir) {
26
+ const dirs = Array.isArray(dir) ? dir : [dir];
27
+ const providers = [];
28
+ /** 跨目录的重名 token 记账:四个分类目录下同名文件夹会撞 token,装配期后者静默覆盖 —— 留痕 */
29
+ const seen = new Map();
30
+ for (const d of dirs) {
31
+ for (const p of await discoverOne(d)) {
32
+ const token = String(p.provide);
33
+ const prev = seen.get(token);
34
+ if (prev !== undefined && prev !== d) {
35
+ console.warn(`[agentia:discover] 重名能力 ${token}:先在 ${prev},后在 ${d} —— 装配期后者覆盖前者,菜单里只剩一个`);
36
+ }
37
+ seen.set(token, d);
38
+ providers.push(p);
39
+ }
40
+ }
41
+ return providers;
42
+ }
43
+ /** 扫描单个目录(缺目录即报错:显式给出的搜索路径不该静默落空) */
44
+ async function discoverOne(dir) {
23
45
  const root = isAbsolute(dir) ? dir : resolve(process.cwd(), dir);
24
46
  if (!existsSync(root)) {
25
- throw new Error(`单元目录不存在: ${root}(先创建或用 CLI: agentia g <type> <name>)`);
47
+ throw new Error(`能力目录不存在: ${root}(先创建或用 CLI: agentia g <type> <name>)`);
48
+ }
49
+ if (!statSync(root).isDirectory()) {
50
+ // 存在但是普通文件:readdirSync 会抛原始 ENOTDIR,信息量低且不像配置错误
51
+ throw new Error(`能力目录不是文件夹: ${root}`);
26
52
  }
27
53
  const providers = [];
28
54
  const entries = readdirSync(root, { withFileTypes: true })
29
- .filter((e) => e.isDirectory())
55
+ .filter((e) => isDirLike(join(root, e.name), e))
30
56
  .map((e) => e.name)
31
57
  .sort(); // 排序保证装配顺序稳定(菜单顺序 = 目录名序,可复现)
32
58
  for (const name of entries) {
33
59
  const entry = ENTRY_CANDIDATES.map((f) => join(root, name, f)).find(existsSync);
34
- if (!entry)
35
- continue; // 无入口的目录视为非单元目录(如 assets/),跳过
60
+ if (!entry) {
61
+ // 静默跳过过一次(无入口的目录视为非能力目录,如 assets/):
62
+ // 但「菜单莫名少一个能力」需要留痕,否则只能靠猜
63
+ console.warn(`[agentia:discover] 跳过 ${name}/:无 ${ENTRY_CANDIDATES.join(' / ')} 入口`);
64
+ continue;
65
+ }
36
66
  let mod;
37
67
  try {
38
68
  mod = await import(pathToFileURL(entry).href);
39
69
  }
40
70
  catch (e) {
41
- throw new Error(`单元 ${name} 入口加载失败(${entry}): ${e instanceof Error ? e.message : String(e)}`, { cause: e });
71
+ throw new Error(`能力 ${name} 入口加载失败(${entry}): ${e instanceof Error ? e.message : String(e)}`, { cause: e });
42
72
  }
43
73
  const exported = mod.default;
44
74
  providers.push(...normalizeExport(name, exported, entry));
45
75
  }
46
76
  return providers;
47
77
  }
78
+ /**
79
+ * 目录判定:**软链目录**(pnpm store / monorepo 里 `src/tools/foo` 常是软链)的
80
+ * `isDirectory()` 为 false,直接用会把真能力目录静默漏掉、菜单空着却不报错 ——
81
+ * 软链要 stat 解引用后再判。悬空软链按非目录处理。
82
+ */
83
+ function isDirLike(full, e) {
84
+ if (e.isDirectory())
85
+ return true;
86
+ if (!e.isSymbolicLink())
87
+ return false;
88
+ try {
89
+ return statSync(full).isDirectory();
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
48
95
  function normalizeExport(name, exported, entry) {
49
96
  if (typeof exported === 'function') {
50
97
  return [{ provide: name, useClass: exported }];
@@ -55,7 +102,7 @@ function normalizeExport(name, exported, entry) {
55
102
  if (Array.isArray(exported) && exported.every(isProvider)) {
56
103
  return exported;
57
104
  }
58
- throw new Error(`单元入口 ${entry} 的 default export 形态非法:应为「类 / Provider / Provider[]」,实际 ${describe(exported)}`);
105
+ throw new Error(`能力入口 ${entry} 的 default export 形态非法:应为「类 / Provider / Provider[]」,实际 ${describe(exported)}`);
59
106
  }
60
107
  function describe(x) {
61
108
  if (x === undefined)
@@ -1 +1 @@
1
- {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/toolkit/discover.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/toolkit/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Agentia —— 可选的 `.env` 引导工具(零依赖手写解析,Node ≥18 即可用)。
3
+ *
4
+ * 定位:**框架自身不会自动读 `.env`** —— 读哪个文件、什么时候读,是宿主的启动决策。
5
+ * 本函数把它收成一行显式调用(`agentia create` 生成的 `src/main.ts` 首行就是它):
6
+ *
7
+ * ```ts
8
+ * import { createApp, loadEnvFile } from '@migor/agentia';
9
+ *
10
+ * loadEnvFile(); // 缺省读 cwd/.env;已存在的真实环境变量优先,不会被文件覆盖
11
+ * const app = await createApp({ ... });
12
+ * ```
13
+ *
14
+ * **为什么不塞进 `createApp` 自动做**:读 `.env` 会改 `process.env`,而 `.env` 是按 cwd 找的。
15
+ * 隐式生效后,「同一份代码换个目录跑结果不同」和「本地多放了个 `.env`,测试行为悄悄变了」
16
+ * 都会变成要花时间排查的悬案。显式一行,时机与路径都摆在文件里(`loadEnvFile({ path })`)。
17
+ *
18
+ * **为什么不放 CLI**:`agentia dev` 只是宿主之一。放 CLI 会让 `node dist/main.js`、docker、
19
+ * 别的宿主都读不到 —— 那才是真的「只有 dev 生效」(与 spec §10 的决策一致)。
20
+ *
21
+ * 解析规则(刻意窄,够用就好):
22
+ * - 逐行 `KEY=VALUE`:允许 `export ` 前缀、`=` 两侧空白;空行与 `#` 开头整行忽略;
23
+ * - 单引号内**原样**;双引号内认 `\n` `\r` `\t` `\"` `\\`;未加引号的值里 ` #` 起为行内注释;
24
+ * - **不做变量展开**(`${VAR}` 原样保留)、不合并多行续行(行尾 `\`)—— 需要这些请上专门的库;
25
+ * - 键名只认 `[A-Za-z_][A-Za-z0-9_]*`;既不像 `KEY=VALUE` 又不是注释的行**直接抛错**(附行号)——
26
+ * 静默跳过等于让你以为「配上了其实没配上」。
27
+ */
28
+ /** `loadEnvFile` 的选项 */
29
+ export interface LoadEnvOptions {
30
+ /** 文件路径,缺省 `.env`(相对 `process.cwd()` 解析) */
31
+ path?: string;
32
+ /** `true` = 文件里的值**覆盖**已存在的进程环境变量(缺省 `false`:真实环境变量优先) */
33
+ override?: boolean;
34
+ }
35
+ /** 解析 `.env` 文本 → 键值表(不碰 `process.env`,纯函数;抛错信息带行号) */
36
+ export declare function parseEnvText(text: string): Record<string, string>;
37
+ /**
38
+ * 读 `.env` 并写进 `process.env`,返回**本次真正生效**的键值。
39
+ *
40
+ * 三条语义(都刻意,别当成实现细节):
41
+ * - **文件不存在 = 正常情况**(首次 clone、CI、生产靠真实环境变量)→ 静默返回 `{}`,
42
+ * 不制造「没建 .env 就跑不起来」的假依赖;
43
+ * - **已有环境变量优先**:`process.env` 里已定义(哪怕空串)的键不覆盖 —— 这样 CI / docker /
44
+ * 命令行的显式变量永远赢过文件,`override: true` 才反过来;
45
+ * - 想知道「到底生效没」看返回值,别去看文件。
46
+ */
47
+ export declare function loadEnvFile(options?: LoadEnvOptions): Record<string, string>;
@@ -0,0 +1,62 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ const KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
4
+ /** 解析 `.env` 文本 → 键值表(不碰 `process.env`,纯函数;抛错信息带行号) */
5
+ export function parseEnvText(text) {
6
+ const out = {};
7
+ // 先剥 BOM:Windows 记事本存过的 .env 会带上 \uFEFF,不剥会让第一个键名匹配失败
8
+ const lines = text.replace(/^\uFEFF/, '').split(/\r?\n/);
9
+ for (let i = 0; i < lines.length; i++) {
10
+ const raw = lines[i] ?? '';
11
+ const line = raw.trim();
12
+ if (line === '' || line.startsWith('#'))
13
+ continue;
14
+ const body = line.startsWith('export ') ? line.slice('export '.length).trim() : line;
15
+ const eq = body.indexOf('=');
16
+ if (eq === -1)
17
+ throw new Error(`.env 第 ${i + 1} 行不是 KEY=VALUE:${raw}`);
18
+ const key = body.slice(0, eq).trim();
19
+ if (!KEY_RE.test(key))
20
+ throw new Error(`.env 第 ${i + 1} 行键名非法:${key || '(空)'}`);
21
+ out[key] = parseValue(body.slice(eq + 1));
22
+ }
23
+ return out;
24
+ }
25
+ function parseValue(raw) {
26
+ const v = raw.trim();
27
+ if (v.length >= 2 && v.startsWith('"') && v.endsWith('"')) {
28
+ return v
29
+ .slice(1, -1)
30
+ .replace(/\\([nrt"\\])/g, (_m, c) => c === 'n' ? '\n' : c === 'r' ? '\r' : c === 't' ? '\t' : c);
31
+ }
32
+ if (v.length >= 2 && v.startsWith("'") && v.endsWith("'"))
33
+ return v.slice(1, -1);
34
+ // 未加引号:` #` 起为行内注释(`#` 紧贴值不当作注释,避免吃掉含 # 的 token)
35
+ const hash = v.search(/\s#/);
36
+ return (hash === -1 ? v : v.slice(0, hash)).trim();
37
+ }
38
+ /**
39
+ * 读 `.env` 并写进 `process.env`,返回**本次真正生效**的键值。
40
+ *
41
+ * 三条语义(都刻意,别当成实现细节):
42
+ * - **文件不存在 = 正常情况**(首次 clone、CI、生产靠真实环境变量)→ 静默返回 `{}`,
43
+ * 不制造「没建 .env 就跑不起来」的假依赖;
44
+ * - **已有环境变量优先**:`process.env` 里已定义(哪怕空串)的键不覆盖 —— 这样 CI / docker /
45
+ * 命令行的显式变量永远赢过文件,`override: true` 才反过来;
46
+ * - 想知道「到底生效没」看返回值,别去看文件。
47
+ */
48
+ export function loadEnvFile(options = {}) {
49
+ const path = resolve(options.path ?? '.env');
50
+ if (!existsSync(path))
51
+ return {};
52
+ const parsed = parseEnvText(readFileSync(path, 'utf8'));
53
+ const applied = {};
54
+ for (const [key, value] of Object.entries(parsed)) {
55
+ if (!options.override && process.env[key] !== undefined)
56
+ continue;
57
+ process.env[key] = value;
58
+ applied[key] = value;
59
+ }
60
+ return applied;
61
+ }
62
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/toolkit/env.ts"],"names":[],"mappings":"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"}
@@ -1,27 +1,27 @@
1
1
  import type { AgentTool, ToolRunContext } from '../core/tool.js';
2
2
  /**
3
- * Agentia —— 单元调用中间件(拦截器链,roadmap R1)。
3
+ * Agentia —— 能力调用中间件(拦截器链,roadmap R1)。
4
4
  *
5
- * 挂在每一次单元(tool/skill/subagent/prompt)调用的前后:鉴权、限流、结果缓存、
6
- * 审计日志、超时包装等横切关注点都走这里,不进业务单元。
5
+ * 挂在每一次能力(tool/skill/subagent/prompt)调用的前后:鉴权、限流、结果缓存、
6
+ * 审计日志、超时包装等横切关注点都走这里,不进业务能力。
7
7
  *
8
8
  * 语义:
9
9
  * - 链序 = 注册顺序(洋葱模型:先注册的最外层);
10
10
  * - `next()` 放行(可用 `next(newInput)` 改写入参);不调 next 即短路(结果缓存等);
11
- * - 抛错按单元失败处理(engine 包成 is_error 回给模型,不中断 run)。
11
+ * - 抛错按能力失败处理(engine 包成 is_error 回给模型,不中断 run)。
12
12
  *
13
13
  * 装配期包裹(AgentApp 构造函数),对 engine 零侵入。
14
14
  */
15
- export interface UnitCall {
16
- /** 被调用的单元(name/description/inputSchema 可读) */
17
- readonly unit: AgentTool;
15
+ export interface CapabilityCall {
16
+ /** 被调用的能力(name/description/inputSchema 可读) */
17
+ readonly capability: AgentTool;
18
18
  /** 模型给出的结构化入参(schema 校验已过) */
19
19
  readonly input: unknown;
20
20
  /** engine 注入的执行上下文(recorder/parentSpanId/client),同步宿主下可用 */
21
21
  readonly ctx?: ToolRunContext;
22
22
  }
23
23
  /** 放行到下一层;`next(newInput)` 可改写入参,缺省沿用当前 input */
24
- export type UnitNext = (input?: unknown) => unknown;
25
- export type UnitMiddleware = (call: UnitCall, next: UnitNext) => unknown;
24
+ export type CapabilityNext = (input?: unknown) => unknown;
25
+ export type CapabilityMiddleware = (call: CapabilityCall, next: CapabilityNext) => unknown;
26
26
  /** 用中间件链包裹工具菜单(链为空时原样返回,零开销)。 */
27
- export declare function applyMiddleware(tools: AgentTool[], middleware: UnitMiddleware[]): AgentTool[];
27
+ export declare function applyMiddleware(tools: AgentTool[], middleware: CapabilityMiddleware[]): AgentTool[];
@@ -5,10 +5,23 @@ export function applyMiddleware(tools, middleware) {
5
5
  return tools.map((tool) => ({
6
6
  ...tool,
7
7
  run: (input, ctx) => {
8
- const invoke = (i, inp) => i >= middleware.length
9
- ? tool.run(inp, ctx)
10
- : middleware[i]({ unit: tool, input: inp, ctx }, (nextInput = inp) => invoke(i + 1, nextInput));
11
- return invoke(0, input);
8
+ const step = (i, inp) => {
9
+ if (i >= middleware.length)
10
+ return tool.run(inp, ctx);
11
+ let passed = false;
12
+ return middleware[i]({ capability: tool, input: inp, ctx },
13
+ // rest 形参而非默认值:`next(undefined)` 是「把入参改写成 undefined」,
14
+ // 与 `next()`(沿用当前入参)语义不同,默认值写法分不开这两者。
15
+ // 连调两次会让能力体跑两遍(有副作用的能力尤其危险)——直接报错。
16
+ (...a) => {
17
+ if (passed) {
18
+ throw new Error(`中间件链上 next() 被重复调用(能力 ${tool.name}):一次调用只能放行一次`);
19
+ }
20
+ passed = true;
21
+ return step(i + 1, a.length > 0 ? a[0] : inp);
22
+ });
23
+ };
24
+ return step(0, input);
12
25
  },
13
26
  }));
14
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/toolkit/middleware.ts"],"names":[],"mappings":"AA6BA,iCAAiC;AACjC,MAAM,UAAU,eAAe,CAAC,KAAkB,EAAE,UAA4B;IAC9E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1B,GAAG,IAAI;QACP,GAAG,EAAE,CAAC,KAAc,EAAE,GAAoB,EAAE,EAAE;YAC5C,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,GAAY,EAAW,EAAE,CAClD,CAAC,IAAI,UAAU,CAAC,MAAM;gBACpB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC;gBACpB,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CACX,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAC/B,CAAC,SAAS,GAAY,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CACvD,CAAC;YACR,OAAO,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;KACF,CAAC,CAAC,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/toolkit/middleware.ts"],"names":[],"mappings":"AA6BA,iCAAiC;AACjC,MAAM,UAAU,eAAe,CAC7B,KAAkB,EAClB,UAAkC;IAElC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1B,GAAG,IAAI;QACP,GAAG,EAAE,CAAC,KAAc,EAAE,GAAoB,EAAE,EAAE;YAC5C,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,GAAY,EAAW,EAAE;gBAChD,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM;oBAAE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBACtD,IAAI,MAAM,GAAG,KAAK,CAAC;gBACnB,OAAO,UAAU,CAAC,CAAC,CAAC,CAClB,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE;gBACrC,sDAAsD;gBACtD,sCAAsC;gBACtC,mCAAmC;gBACnC,CAAC,GAAG,CAAY,EAAE,EAAE;oBAClB,IAAI,MAAM,EAAE,CAAC;wBACX,MAAM,IAAI,KAAK,CACb,yBAAyB,IAAI,CAAC,IAAI,cAAc,CACjD,CAAC;oBACJ,CAAC;oBACD,MAAM,GAAG,IAAI,CAAC;oBACd,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAChD,CAAC,CACF,CAAC;YACJ,CAAC,CAAC;YACF,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;KACF,CAAC,CAAC,CAAC;AACN,CAAC"}