@namzu/sdk 0.1.5-rc.1-fix → 0.1.5

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 (224) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +279 -129
  3. package/dist/bridge/tools/connector/adapter.d.ts +2 -2
  4. package/dist/bridge/tools/connector/adapter.d.ts.map +1 -1
  5. package/dist/bridge/tools/connector/adapter.js +3 -1
  6. package/dist/bridge/tools/connector/adapter.js.map +1 -1
  7. package/dist/connector/BaseConnector.d.ts +2 -1
  8. package/dist/connector/BaseConnector.d.ts.map +1 -1
  9. package/dist/connector/BaseConnector.js.map +1 -1
  10. package/dist/connector/builtins/http.d.ts +1 -1
  11. package/dist/connector/builtins/http.d.ts.map +1 -1
  12. package/dist/connector/builtins/http.js +1 -1
  13. package/dist/connector/builtins/http.js.map +1 -1
  14. package/dist/connector/builtins/webhook.d.ts +1 -1
  15. package/dist/connector/builtins/webhook.d.ts.map +1 -1
  16. package/dist/connector/builtins/webhook.js +1 -1
  17. package/dist/connector/builtins/webhook.js.map +1 -1
  18. package/dist/contracts/api.d.ts.map +1 -1
  19. package/dist/contracts/index.d.ts +1 -0
  20. package/dist/contracts/index.d.ts.map +1 -1
  21. package/dist/contracts/index.js +7 -0
  22. package/dist/contracts/index.js.map +1 -1
  23. package/dist/index.d.ts +5 -33
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +5 -23
  26. package/dist/index.js.map +1 -1
  27. package/dist/manager/connector/environment.d.ts +4 -4
  28. package/dist/manager/connector/environment.d.ts.map +1 -1
  29. package/dist/manager/connector/environment.js.map +1 -1
  30. package/dist/manager/connector/lifecycle.d.ts +2 -2
  31. package/dist/manager/connector/lifecycle.d.ts.map +1 -1
  32. package/dist/manager/connector/lifecycle.js.map +1 -1
  33. package/dist/manager/connector/tenant.d.ts +3 -3
  34. package/dist/manager/connector/tenant.d.ts.map +1 -1
  35. package/dist/manager/connector/tenant.js.map +1 -1
  36. package/dist/manager/index.d.ts +2 -1
  37. package/dist/manager/index.d.ts.map +1 -1
  38. package/dist/manager/index.js +2 -1
  39. package/dist/manager/index.js.map +1 -1
  40. package/dist/manager/run/emergency.d.ts.map +1 -1
  41. package/dist/manager/run/emergency.js +44 -12
  42. package/dist/manager/run/emergency.js.map +1 -1
  43. package/dist/manager/run/persistence.d.ts +0 -2
  44. package/dist/manager/run/persistence.d.ts.map +1 -1
  45. package/dist/manager/run/persistence.js +0 -1
  46. package/dist/manager/run/persistence.js.map +1 -1
  47. package/dist/plugin/__tests__/enable-contributions.test.d.ts +2 -0
  48. package/dist/plugin/__tests__/enable-contributions.test.d.ts.map +1 -0
  49. package/dist/plugin/__tests__/enable-contributions.test.js +265 -0
  50. package/dist/plugin/__tests__/enable-contributions.test.js.map +1 -0
  51. package/dist/plugin/__tests__/lifecycle.test.js +30 -0
  52. package/dist/plugin/__tests__/lifecycle.test.js.map +1 -1
  53. package/dist/plugin/lifecycle.d.ts +4 -1
  54. package/dist/plugin/lifecycle.d.ts.map +1 -1
  55. package/dist/plugin/lifecycle.js +157 -49
  56. package/dist/plugin/lifecycle.js.map +1 -1
  57. package/dist/rag/vector-store.d.ts +2 -2
  58. package/dist/rag/vector-store.d.ts.map +1 -1
  59. package/dist/rag/vector-store.js.map +1 -1
  60. package/dist/registry/ManagedRegistry.d.ts +6 -0
  61. package/dist/registry/ManagedRegistry.d.ts.map +1 -1
  62. package/dist/registry/ManagedRegistry.js +9 -3
  63. package/dist/registry/ManagedRegistry.js.map +1 -1
  64. package/dist/registry/agent/definitions.d.ts +2 -7
  65. package/dist/registry/agent/definitions.d.ts.map +1 -1
  66. package/dist/registry/agent/definitions.js +3 -41
  67. package/dist/registry/agent/definitions.js.map +1 -1
  68. package/dist/registry/connector/definitions.d.ts +2 -7
  69. package/dist/registry/connector/definitions.d.ts.map +1 -1
  70. package/dist/registry/connector/definitions.js +3 -41
  71. package/dist/registry/connector/definitions.js.map +1 -1
  72. package/dist/registry/connector/scoped.d.ts +5 -4
  73. package/dist/registry/connector/scoped.d.ts.map +1 -1
  74. package/dist/registry/connector/scoped.js.map +1 -1
  75. package/dist/registry/index.d.ts +1 -0
  76. package/dist/registry/index.d.ts.map +1 -1
  77. package/dist/registry/index.js +1 -0
  78. package/dist/registry/index.js.map +1 -1
  79. package/dist/registry/tool/execute.d.ts +2 -4
  80. package/dist/registry/tool/execute.d.ts.map +1 -1
  81. package/dist/registry/tool/execute.js +4 -21
  82. package/dist/registry/tool/execute.js.map +1 -1
  83. package/dist/run/index.d.ts +4 -4
  84. package/dist/run/index.d.ts.map +1 -1
  85. package/dist/run/index.js +3 -3
  86. package/dist/run/index.js.map +1 -1
  87. package/dist/run/reporter.d.ts +0 -2
  88. package/dist/run/reporter.d.ts.map +1 -1
  89. package/dist/run/reporter.js +0 -1
  90. package/dist/run/reporter.js.map +1 -1
  91. package/dist/runtime/query/__tests__/executor-plugin-hooks.test.d.ts +2 -0
  92. package/dist/runtime/query/__tests__/executor-plugin-hooks.test.d.ts.map +1 -0
  93. package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js +236 -0
  94. package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js.map +1 -0
  95. package/dist/runtime/query/__tests__/plugin-hooks.test.d.ts +2 -0
  96. package/dist/runtime/query/__tests__/plugin-hooks.test.d.ts.map +1 -0
  97. package/dist/runtime/query/__tests__/plugin-hooks.test.js +21 -0
  98. package/dist/runtime/query/__tests__/plugin-hooks.test.js.map +1 -0
  99. package/dist/runtime/query/executor.d.ts +6 -0
  100. package/dist/runtime/query/executor.d.ts.map +1 -1
  101. package/dist/runtime/query/executor.js +110 -6
  102. package/dist/runtime/query/executor.js.map +1 -1
  103. package/dist/runtime/query/index.d.ts.map +1 -1
  104. package/dist/runtime/query/index.js +8 -6
  105. package/dist/runtime/query/index.js.map +1 -1
  106. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  107. package/dist/runtime/query/iteration/index.js +13 -16
  108. package/dist/runtime/query/iteration/index.js.map +1 -1
  109. package/dist/runtime/query/plugin-hooks.d.ts +11 -0
  110. package/dist/runtime/query/plugin-hooks.d.ts.map +1 -0
  111. package/dist/runtime/query/plugin-hooks.js +28 -0
  112. package/dist/runtime/query/plugin-hooks.js.map +1 -0
  113. package/dist/runtime/query/tooling.d.ts +2 -0
  114. package/dist/runtime/query/tooling.d.ts.map +1 -1
  115. package/dist/runtime/query/tooling.js +1 -0
  116. package/dist/runtime/query/tooling.js.map +1 -1
  117. package/dist/store/index.d.ts +5 -1
  118. package/dist/store/index.d.ts.map +1 -1
  119. package/dist/store/index.js +4 -1
  120. package/dist/store/index.js.map +1 -1
  121. package/dist/store/run/disk.d.ts +0 -2
  122. package/dist/store/run/disk.d.ts.map +1 -1
  123. package/dist/store/run/disk.js +0 -1
  124. package/dist/store/run/disk.js.map +1 -1
  125. package/dist/store/task/__tests__/disk-concurrency.test.d.ts +2 -0
  126. package/dist/store/task/__tests__/disk-concurrency.test.d.ts.map +1 -0
  127. package/dist/store/task/__tests__/disk-concurrency.test.js +91 -0
  128. package/dist/store/task/__tests__/disk-concurrency.test.js.map +1 -0
  129. package/dist/store/task/disk.d.ts +6 -0
  130. package/dist/store/task/disk.d.ts.map +1 -1
  131. package/dist/store/task/disk.js +150 -36
  132. package/dist/store/task/disk.js.map +1 -1
  133. package/dist/types/connector/core.d.ts +2 -2
  134. package/dist/types/connector/core.d.ts.map +1 -1
  135. package/dist/types/connector/definition.d.ts +7 -7
  136. package/dist/types/connector/definition.d.ts.map +1 -1
  137. package/dist/types/connector/mcp.d.ts +4 -4
  138. package/dist/types/connector/mcp.d.ts.map +1 -1
  139. package/dist/types/connector/scope.d.ts +3 -2
  140. package/dist/types/connector/scope.d.ts.map +1 -1
  141. package/dist/types/connector/scope.js.map +1 -1
  142. package/dist/types/connector/tenant.d.ts +4 -4
  143. package/dist/types/connector/tenant.d.ts.map +1 -1
  144. package/dist/types/plugin/index.d.ts +4 -21
  145. package/dist/types/plugin/index.d.ts.map +1 -1
  146. package/dist/types/plugin/index.js +1 -10
  147. package/dist/types/plugin/index.js.map +1 -1
  148. package/dist/types/rag/knowledge-base.d.ts +2 -2
  149. package/dist/types/rag/knowledge-base.d.ts.map +1 -1
  150. package/dist/types/rag/scope.d.ts +2 -1
  151. package/dist/types/rag/scope.d.ts.map +1 -1
  152. package/dist/types/rag/storage.d.ts +3 -3
  153. package/dist/types/rag/storage.d.ts.map +1 -1
  154. package/dist/types/rag/vector.d.ts +3 -3
  155. package/dist/types/rag/vector.d.ts.map +1 -1
  156. package/dist/types/run/config.d.ts +0 -6
  157. package/dist/types/run/config.d.ts.map +1 -1
  158. package/dist/vault/InMemoryCredentialVault.d.ts +3 -3
  159. package/dist/vault/InMemoryCredentialVault.d.ts.map +1 -1
  160. package/dist/vault/InMemoryCredentialVault.js.map +1 -1
  161. package/package.json +1 -1
  162. package/src/bridge/tools/connector/adapter.ts +5 -3
  163. package/src/connector/BaseConnector.ts +2 -1
  164. package/src/connector/builtins/http.ts +1 -1
  165. package/src/connector/builtins/webhook.ts +1 -1
  166. package/src/contracts/api.ts +3 -0
  167. package/src/contracts/index.ts +10 -0
  168. package/src/index.ts +46 -41
  169. package/src/manager/connector/environment.ts +5 -5
  170. package/src/manager/connector/lifecycle.ts +2 -2
  171. package/src/manager/connector/tenant.ts +8 -3
  172. package/src/manager/index.ts +2 -1
  173. package/src/manager/run/emergency.ts +45 -16
  174. package/src/manager/run/persistence.ts +0 -4
  175. package/src/plugin/__tests__/enable-contributions.test.ts +295 -0
  176. package/src/plugin/__tests__/lifecycle.test.ts +44 -0
  177. package/src/plugin/lifecycle.ts +186 -57
  178. package/src/rag/vector-store.ts +2 -2
  179. package/src/registry/ManagedRegistry.ts +14 -3
  180. package/src/registry/agent/definitions.ts +3 -53
  181. package/src/registry/connector/definitions.ts +3 -53
  182. package/src/registry/connector/scoped.ts +7 -6
  183. package/src/registry/index.ts +1 -0
  184. package/src/registry/tool/execute.ts +4 -22
  185. package/src/run/index.ts +4 -4
  186. package/src/run/reporter.ts +0 -4
  187. package/src/runtime/query/__tests__/executor-plugin-hooks.test.ts +305 -0
  188. package/src/runtime/query/__tests__/plugin-hooks.test.ts +28 -0
  189. package/src/runtime/query/executor.ts +148 -7
  190. package/src/runtime/query/index.ts +16 -6
  191. package/src/runtime/query/iteration/index.ts +29 -16
  192. package/src/runtime/query/plugin-hooks.ts +34 -0
  193. package/src/runtime/query/tooling.ts +3 -0
  194. package/src/store/index.ts +6 -1
  195. package/src/store/run/disk.ts +0 -4
  196. package/src/store/task/__tests__/disk-concurrency.test.ts +118 -0
  197. package/src/store/task/disk.ts +150 -37
  198. package/src/types/connector/core.ts +2 -2
  199. package/src/types/connector/definition.ts +7 -7
  200. package/src/types/connector/mcp.ts +4 -4
  201. package/src/types/connector/scope.ts +3 -2
  202. package/src/types/connector/tenant.ts +4 -4
  203. package/src/types/plugin/index.ts +0 -14
  204. package/src/types/rag/knowledge-base.ts +2 -2
  205. package/src/types/rag/scope.ts +3 -1
  206. package/src/types/rag/storage.ts +3 -3
  207. package/src/types/rag/vector.ts +3 -3
  208. package/src/types/run/config.ts +0 -9
  209. package/src/vault/InMemoryCredentialVault.ts +3 -3
  210. package/dist/manager/agent/index.d.ts +0 -2
  211. package/dist/manager/agent/index.d.ts.map +0 -1
  212. package/dist/manager/agent/index.js +0 -2
  213. package/dist/manager/agent/index.js.map +0 -1
  214. package/dist/registry/agent/index.d.ts +0 -2
  215. package/dist/registry/agent/index.d.ts.map +0 -1
  216. package/dist/registry/agent/index.js +0 -2
  217. package/dist/registry/agent/index.js.map +0 -1
  218. package/dist/router/index.d.ts +0 -2
  219. package/dist/router/index.d.ts.map +0 -1
  220. package/dist/router/index.js +0 -2
  221. package/dist/router/index.js.map +0 -1
  222. package/src/manager/agent/index.ts +0 -1
  223. package/src/registry/agent/index.ts +0 -1
  224. package/src/router/index.ts +0 -1
@@ -1,4 +1,4 @@
1
- import type { ChunkId, DocumentId, KnowledgeBaseId } from '../types/ids/index.js'
1
+ import type { ChunkId, DocumentId, KnowledgeBaseId, TenantId } from '../types/ids/index.js'
2
2
  import type {
3
3
  Chunk,
4
4
  VectorSearchResult,
@@ -57,7 +57,7 @@ export class InMemoryVectorStore implements VectorStore {
57
57
  }
58
58
  }
59
59
 
60
- async deleteByKnowledgeBase(knowledgeBaseId: KnowledgeBaseId, tenantId: string): Promise<void> {
60
+ async deleteByKnowledgeBase(knowledgeBaseId: KnowledgeBaseId, tenantId: TenantId): Promise<void> {
61
61
  for (const [id, chunk] of this.chunks) {
62
62
  if (chunk.knowledgeBaseId === knowledgeBaseId && chunk.tenantId === tenantId) {
63
63
  this.chunks.delete(id)
@@ -4,16 +4,23 @@ import { Registry } from './Registry.js'
4
4
  export interface ManagedRegistryConfig<TDefinition> {
5
5
  componentName: string
6
6
  idField?: keyof TDefinition & string
7
+ /**
8
+ * Extract id from a full item. Takes precedence over `idField` when provided.
9
+ * Required when the id field is nested (e.g. `def.info.id`).
10
+ */
11
+ computeId?: (item: TDefinition) => string
7
12
  logger?: Logger
8
13
  }
9
14
 
10
15
  export class ManagedRegistry<TDefinition> extends Registry<TDefinition> {
11
16
  protected log: Logger
12
17
  private idField?: keyof TDefinition & string
18
+ private computeId?: (item: TDefinition) => string
13
19
 
14
20
  constructor(config: ManagedRegistryConfig<TDefinition>) {
15
21
  super()
16
22
  this.idField = config.idField
23
+ this.computeId = config.computeId
17
24
  this.log = (config.logger ?? getRootLogger()).child({
18
25
  component: config.componentName,
19
26
  })
@@ -45,10 +52,14 @@ export class ManagedRegistry<TDefinition> extends Registry<TDefinition> {
45
52
  }
46
53
 
47
54
  const item = idOrItem
48
- if (!this.idField) {
49
- throw new Error('register(item) requires idField to be configured')
55
+ const id = this.computeId
56
+ ? this.computeId(item)
57
+ : this.idField
58
+ ? String(item[this.idField])
59
+ : undefined
60
+ if (id === undefined) {
61
+ throw new Error('register(item) requires idField or computeId to be configured')
50
62
  }
51
- const id = String(item[this.idField])
52
63
 
53
64
  if (this.has(id)) {
54
65
  this.log.warn(`"${id}" already registered, overwriting.`)
@@ -1,61 +1,11 @@
1
1
  import type { AgentType, BaseAgentConfig, BaseAgentResult } from '../../types/agent/base.js'
2
2
  import type { Agent } from '../../types/agent/core.js'
3
3
  import type { AgentDefinition } from '../../types/agent/factory.js'
4
- import { type Logger, getRootLogger } from '../../utils/logger.js'
5
- import { Registry } from '../Registry.js'
6
-
7
- export class AgentRegistry extends Registry<AgentDefinition> {
8
- private log: Logger
4
+ import { ManagedRegistry } from '../ManagedRegistry.js'
9
5
 
6
+ export class AgentRegistry extends ManagedRegistry<AgentDefinition> {
10
7
  constructor() {
11
- super()
12
- this.log = getRootLogger().child({ component: 'AgentRegistry' })
13
- }
14
-
15
- override register(id: string, definition: AgentDefinition): void
16
- override register(definition: AgentDefinition): void
17
- override register(definitions: AgentDefinition[]): void
18
- override register(
19
- idOrDef: string | AgentDefinition | AgentDefinition[],
20
- maybeDef?: AgentDefinition,
21
- ): void {
22
- if (Array.isArray(idOrDef)) {
23
- for (const def of idOrDef) {
24
- this.register(def)
25
- }
26
- return
27
- }
28
-
29
- if (typeof idOrDef === 'string') {
30
- if (!maybeDef) {
31
- throw new Error('register(id, definition) requires a definition argument')
32
- }
33
- const id = idOrDef
34
- const def = maybeDef
35
- if (this.has(id)) {
36
- this.log.warn(`Agent "${id}" already registered, overwriting.`)
37
- }
38
- super.register(id, def)
39
- this.log.info(`Agent registered: ${id}`)
40
- return
41
- }
42
-
43
- const def = idOrDef
44
- const id = def.info.id
45
-
46
- if (this.has(id)) {
47
- this.log.warn(`Agent "${id}" already registered, overwriting.`)
48
- }
49
- super.register(id, def)
50
- this.log.info(`Agent registered: ${id}`)
51
- }
52
-
53
- getOrThrow(id: string): AgentDefinition {
54
- const def = this.get(id)
55
- if (!def) {
56
- throw new Error(`Agent not found: "${id}". Available: ${this.listIds().join(', ')}`)
57
- }
58
- return def
8
+ super({ componentName: 'AgentRegistry', computeId: (def) => def.info.id })
59
9
  }
60
10
 
61
11
  resolve(agentId: string): Agent<BaseAgentConfig, BaseAgentResult> {
@@ -1,59 +1,9 @@
1
1
  import type { ConnectorDefinition } from '../../types/connector/index.js'
2
- import { type Logger, getRootLogger } from '../../utils/logger.js'
3
- import { Registry } from '../Registry.js'
4
-
5
- export class ConnectorRegistry extends Registry<ConnectorDefinition> {
6
- private log: Logger
2
+ import { ManagedRegistry } from '../ManagedRegistry.js'
7
3
 
4
+ export class ConnectorRegistry extends ManagedRegistry<ConnectorDefinition> {
8
5
  constructor() {
9
- super()
10
- this.log = getRootLogger().child({ component: 'ConnectorRegistry' })
11
- }
12
-
13
- override register(id: string, definition: ConnectorDefinition): void
14
- override register(definition: ConnectorDefinition): void
15
- override register(definitions: ConnectorDefinition[]): void
16
- override register(
17
- idOrDef: string | ConnectorDefinition | ConnectorDefinition[],
18
- maybeDef?: ConnectorDefinition,
19
- ): void {
20
- if (Array.isArray(idOrDef)) {
21
- for (const def of idOrDef) {
22
- this.register(def)
23
- }
24
- return
25
- }
26
-
27
- if (typeof idOrDef === 'string') {
28
- if (!maybeDef) {
29
- throw new Error('register(id, definition) requires a definition argument')
30
- }
31
- const id = idOrDef
32
- const def = maybeDef
33
- if (this.has(id)) {
34
- this.log.warn(`Connector "${id}" is already registered, overwriting.`)
35
- }
36
- super.register(id, def)
37
- this.log.info(`Connector registered: ${id}`)
38
- return
39
- }
40
-
41
- const def = idOrDef
42
- const id = def.id
43
-
44
- if (this.has(id)) {
45
- this.log.warn(`Connector "${id}" is already registered, overwriting.`)
46
- }
47
- super.register(id, def)
48
- this.log.info(`Connector registered: ${id}`)
49
- }
50
-
51
- getOrThrow(id: string): ConnectorDefinition {
52
- const def = this.get(id)
53
- if (!def) {
54
- throw new Error(`Connector not found: "${id}". Available: ${this.listIds().join(', ')}`)
55
- }
56
- return def
6
+ super({ componentName: 'ConnectorRegistry', idField: 'id' })
57
7
  }
58
8
 
59
9
  listByType(connectionType: ConnectorDefinition['connectionType']): ConnectorDefinition[] {
@@ -8,9 +8,10 @@ import type {
8
8
  ScopedConnectorConfig,
9
9
  } from '../../types/connector/index.js'
10
10
  import { CONNECTOR_SCOPE_ORDER } from '../../types/connector/index.js'
11
+ import type { ConnectorId } from '../../types/ids/index.js'
11
12
  import { type Logger, getRootLogger } from '../../utils/logger.js'
12
13
 
13
- function scopeKey(scope: ConnectorScope, scopeId: string, connectorId: string): string {
14
+ function scopeKey(scope: ConnectorScope, scopeId: string, connectorId: ConnectorId): string {
14
15
  return `${scope}:${scopeId}:${connectorId}`
15
16
  }
16
17
 
@@ -28,7 +29,7 @@ export class ScopedConnectorRegistry {
28
29
  this.log.info(`Scoped config set: ${key}`)
29
30
  }
30
31
 
31
- remove(scope: ScopeRef, connectorId: string): boolean {
32
+ remove(scope: ScopeRef, connectorId: ConnectorId): boolean {
32
33
  const key = scopeKey(scope.scope, scope.scopeId, connectorId)
33
34
  const removed = this.configs.delete(key)
34
35
  if (removed) {
@@ -37,11 +38,11 @@ export class ScopedConnectorRegistry {
37
38
  return removed
38
39
  }
39
40
 
40
- getAt(scope: ScopeRef, connectorId: string): ScopedConnectorConfig | undefined {
41
+ getAt(scope: ScopeRef, connectorId: ConnectorId): ScopedConnectorConfig | undefined {
41
42
  return this.configs.get(scopeKey(scope.scope, scope.scopeId, connectorId))
42
43
  }
43
44
 
44
- resolve(connectorId: string, chain: ScopeChain): ResolvedConnectorConfig | undefined {
45
+ resolve(connectorId: ConnectorId, chain: ScopeChain): ResolvedConnectorConfig | undefined {
45
46
  const layers: ScopedConnectorConfig[] = []
46
47
 
47
48
  for (const scope of CONNECTOR_SCOPE_ORDER) {
@@ -59,7 +60,7 @@ export class ScopedConnectorRegistry {
59
60
  return this.mergeLayers(connectorId, layers)
60
61
  }
61
62
 
62
- listForConnector(connectorId: string): ScopedConnectorConfig[] {
63
+ listForConnector(connectorId: ConnectorId): ScopedConnectorConfig[] {
63
64
  const results: ScopedConnectorConfig[] = []
64
65
  for (const config of this.configs.values()) {
65
66
  if (config.connectorId === connectorId) {
@@ -81,7 +82,7 @@ export class ScopedConnectorRegistry {
81
82
  }
82
83
 
83
84
  private mergeLayers(
84
- connectorId: string,
85
+ connectorId: ConnectorId,
85
86
  layers: ScopedConnectorConfig[],
86
87
  ): ResolvedConnectorConfig {
87
88
  let mergedOptions: Record<string, unknown> = {}
@@ -9,3 +9,4 @@ export { ConnectorRegistry } from './connector/definitions.js'
9
9
  export { ScopedConnectorRegistry } from './connector/scoped.js'
10
10
 
11
11
  export { AgentRegistry } from './agent/definitions.js'
12
+ export { PluginRegistry } from './plugin/index.js'
@@ -12,21 +12,16 @@ import type {
12
12
  ToolTierConfig,
13
13
  } from '../../types/tool/index.js'
14
14
  import { toErrorMessage } from '../../utils/error.js'
15
- import { type Logger, getRootLogger } from '../../utils/logger.js'
16
- import { Registry } from '../Registry.js'
15
+ import { ManagedRegistry } from '../ManagedRegistry.js'
17
16
 
18
17
  export type { ToolExecutionResult }
19
18
 
20
- export class ToolRegistry extends Registry<ToolDefinition> {
21
- private log: Logger
19
+ export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
22
20
  private availability: Map<string, ToolAvailability> = new Map()
23
21
  private tierConfig?: ToolTierConfig
24
22
 
25
23
  constructor(config?: ToolRegistryConfig) {
26
- super()
27
- this.log = (config?.logger ?? getRootLogger()).child({
28
- component: 'ToolRegistry',
29
- })
24
+ super({ componentName: 'ToolRegistry', idField: 'name', logger: config?.logger })
30
25
  this.tierConfig = config?.tierConfig
31
26
  }
32
27
 
@@ -54,10 +49,9 @@ export class ToolRegistry extends Registry<ToolDefinition> {
54
49
  }
55
50
 
56
51
  const tool = idOrToolOrTools
57
- const id = tool.name
58
52
  const state: ToolAvailability = typeof maybeTool === 'string' ? maybeTool : 'active'
59
53
 
60
- this.registerOne(id, tool, state)
54
+ this.registerOne(tool.name, tool, state)
61
55
  }
62
56
 
63
57
  private registerOne(id: string, tool: ToolDefinition, state: ToolAvailability): void {
@@ -69,12 +63,8 @@ export class ToolRegistry extends Registry<ToolDefinition> {
69
63
  )
70
64
  }
71
65
  }
72
- if (this.has(id)) {
73
- this.log.warn(`Tool "${id}" is already registered, overwriting.`)
74
- }
75
66
  super.register(id, tool)
76
67
  this.availability.set(id, state)
77
- this.log.info(`Tool registered: ${id} (${state})`)
78
68
  }
79
69
 
80
70
  override unregister(id: string): boolean {
@@ -150,14 +140,6 @@ export class ToolRegistry extends Registry<ToolDefinition> {
150
140
  return this.tierConfig.guidanceTemplate(this.tierConfig.tiers)
151
141
  }
152
142
 
153
- getOrThrow(name: string): ToolDefinition {
154
- const tool = this.get(name)
155
- if (!tool) {
156
- throw new Error(`Tool not found: "${name}". Available tools: ${this.listIds().join(', ')}`)
157
- }
158
- return tool
159
- }
160
-
161
143
  listNames(): string[] {
162
144
  return this.listIds()
163
145
  }
package/src/run/index.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { RunPersistence, SessionManager } from '../manager/run/persistence.js'
1
+ export { RunPersistence } from '../manager/run/persistence.js'
2
2
 
3
- export { RunDiskStore, SessionStore } from '../store/run/disk.js'
3
+ export { RunDiskStore } from '../store/run/disk.js'
4
4
 
5
- export { createRunReporter, createSessionReporter } from './reporter.js'
6
- export type { RunReporter, SessionReporter } from './reporter.js'
5
+ export { createRunReporter } from './reporter.js'
6
+ export type { RunReporter } from './reporter.js'
7
7
 
8
8
  export { checkLimitsDetailed, buildLimitConfig } from './LimitChecker.js'
9
9
  export type { LimitCheckerState, LimitCheckResult } from './LimitChecker.js'
@@ -16,8 +16,6 @@ export interface RunReporter {
16
16
  summary(run: AgentRun): void
17
17
  }
18
18
 
19
- export type SessionReporter = RunReporter
20
-
21
19
  export function createRunReporter(parentLogger?: Logger): RunReporter {
22
20
  const log = (parentLogger ?? getRootLogger()).child({
23
21
  component: 'RunReporter',
@@ -223,5 +221,3 @@ export function createRunReporter(parentLogger?: Logger): RunReporter {
223
221
 
224
222
  return { listener, summary }
225
223
  }
226
-
227
- export const createSessionReporter = createRunReporter
@@ -0,0 +1,305 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import type { PluginLifecycleManager } from '../../../plugin/lifecycle.js'
3
+ import { ActivityStore } from '../../../store/activity/memory.js'
4
+ import type { RunId } from '../../../types/ids/index.js'
5
+ import type { PluginHookResult } from '../../../types/plugin/index.js'
6
+ import type { ChatCompletionResponse } from '../../../types/provider/index.js'
7
+ import type { RunEvent } from '../../../types/run/index.js'
8
+ import type { ToolRegistryContract } from '../../../types/tool/index.js'
9
+ import type { Logger } from '../../../utils/logger.js'
10
+ import { ToolExecutor } from '../executor.js'
11
+
12
+ const mockRunId = 'run_test' as RunId
13
+
14
+ function makeLogger(): Logger {
15
+ const stub = {
16
+ info: vi.fn(),
17
+ warn: vi.fn(),
18
+ error: vi.fn(),
19
+ debug: vi.fn(),
20
+ }
21
+ return { ...stub, child: vi.fn(() => ({ ...stub, child: vi.fn() })) } as unknown as Logger
22
+ }
23
+
24
+ function makeToolRegistry(execute: ToolRegistryContract['execute']): ToolRegistryContract {
25
+ return {
26
+ register: vi.fn(),
27
+ unregister: vi.fn(),
28
+ execute,
29
+ get: vi.fn(() => undefined),
30
+ has: vi.fn(() => true),
31
+ listNames: vi.fn(() => []),
32
+ getAvailability: vi.fn(),
33
+ } as unknown as ToolRegistryContract
34
+ }
35
+
36
+ function makePluginManager(
37
+ executeHooks: PluginLifecycleManager['executeHooks'],
38
+ ): PluginLifecycleManager {
39
+ return { executeHooks } as unknown as PluginLifecycleManager
40
+ }
41
+
42
+ function buildResponse(toolName: string, args: object): ChatCompletionResponse {
43
+ return {
44
+ message: {
45
+ role: 'assistant',
46
+ content: null,
47
+ toolCalls: [
48
+ {
49
+ id: 'call_1',
50
+ type: 'function',
51
+ function: { name: toolName, arguments: JSON.stringify(args) },
52
+ },
53
+ ],
54
+ },
55
+ finishReason: 'tool_calls',
56
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
57
+ } as ChatCompletionResponse
58
+ }
59
+
60
+ describe('ToolExecutor plugin hooks', () => {
61
+ let activityStore: ActivityStore
62
+ let emitted: RunEvent[]
63
+ let emitEvent: (e: RunEvent) => Promise<void>
64
+
65
+ beforeEach(() => {
66
+ activityStore = new ActivityStore(mockRunId, {
67
+ enabled: true,
68
+ trackToolCalls: true,
69
+ trackLlmTurns: true,
70
+ })
71
+ emitted = []
72
+ emitEvent = async (e) => {
73
+ emitted.push(e)
74
+ }
75
+ })
76
+
77
+ it('continues normally when no pluginManager is configured', async () => {
78
+ const tools = makeToolRegistry(vi.fn(async () => ({ success: true, output: 'ok' })))
79
+ const exec = new ToolExecutor(
80
+ {
81
+ tools,
82
+ runId: mockRunId,
83
+ workingDirectory: '/tmp',
84
+ permissionMode: 'auto',
85
+ env: {},
86
+ abortSignal: new AbortController().signal,
87
+ },
88
+ activityStore,
89
+ emitEvent,
90
+ makeLogger(),
91
+ )
92
+ const batch = await exec.executeBatch(buildResponse('echo', { msg: 'hi' }))
93
+ expect(batch.results[0]?.output).toBe('ok')
94
+ })
95
+
96
+ it('replaces input on pre_tool_use modify', async () => {
97
+ const executeMock = vi.fn(async () => ({ success: true, output: 'ok' }))
98
+ const tools = makeToolRegistry(executeMock)
99
+ const pluginManager = makePluginManager(async (event) =>
100
+ event === 'pre_tool_use'
101
+ ? ([{ action: 'modify', input: { replaced: true } }] as PluginHookResult[])
102
+ : [],
103
+ )
104
+ const exec = new ToolExecutor(
105
+ {
106
+ tools,
107
+ runId: mockRunId,
108
+ workingDirectory: '/tmp',
109
+ permissionMode: 'auto',
110
+ env: {},
111
+ abortSignal: new AbortController().signal,
112
+ pluginManager,
113
+ },
114
+ activityStore,
115
+ emitEvent,
116
+ makeLogger(),
117
+ )
118
+ await exec.executeBatch(buildResponse('echo', { original: true }))
119
+ expect(executeMock).toHaveBeenCalledWith('echo', { replaced: true }, expect.any(Object))
120
+ })
121
+
122
+ it('skips registry execution and synthesizes output on pre_tool_use skip', async () => {
123
+ const executeMock = vi.fn(async () => ({ success: true, output: 'should-not-run' }))
124
+ const tools = makeToolRegistry(executeMock)
125
+ const pluginManager = makePluginManager(async (event) =>
126
+ event === 'pre_tool_use'
127
+ ? ([{ action: 'skip', reason: 'policy' }] as PluginHookResult[])
128
+ : [],
129
+ )
130
+ const exec = new ToolExecutor(
131
+ {
132
+ tools,
133
+ runId: mockRunId,
134
+ workingDirectory: '/tmp',
135
+ permissionMode: 'auto',
136
+ env: {},
137
+ abortSignal: new AbortController().signal,
138
+ pluginManager,
139
+ },
140
+ activityStore,
141
+ emitEvent,
142
+ makeLogger(),
143
+ )
144
+ const batch = await exec.executeBatch(buildResponse('echo', {}))
145
+ expect(executeMock).not.toHaveBeenCalled()
146
+ expect(batch.results[0]?.output).toBe('Tool echo skipped by plugin: policy')
147
+ })
148
+
149
+ it('overrides post_tool_use output on error action', async () => {
150
+ const tools = makeToolRegistry(vi.fn(async () => ({ success: true, output: 'raw' })))
151
+ const pluginManager = makePluginManager(async (event) =>
152
+ event === 'post_tool_use'
153
+ ? ([{ action: 'error', message: 'rejected' }] as PluginHookResult[])
154
+ : [],
155
+ )
156
+ const exec = new ToolExecutor(
157
+ {
158
+ tools,
159
+ runId: mockRunId,
160
+ workingDirectory: '/tmp',
161
+ permissionMode: 'auto',
162
+ env: {},
163
+ abortSignal: new AbortController().signal,
164
+ pluginManager,
165
+ },
166
+ activityStore,
167
+ emitEvent,
168
+ makeLogger(),
169
+ )
170
+ const batch = await exec.executeBatch(buildResponse('echo', {}))
171
+ expect(batch.results[0]?.output).toBe('Error: rejected')
172
+ })
173
+
174
+ it('threads modified input through chained pre_tool_use modify hooks', async () => {
175
+ const seenInputs: unknown[] = []
176
+ const executeMock = vi.fn(async (_name, input) => {
177
+ seenInputs.push(input)
178
+ return { success: true, output: 'ok' }
179
+ })
180
+ const tools = makeToolRegistry(executeMock)
181
+ const handlers = [
182
+ async (ctx: any) => {
183
+ seenInputs.push(['hook1-saw', ctx.toolInput])
184
+ return { action: 'modify', input: { step: 1 } } as PluginHookResult
185
+ },
186
+ async (ctx: any) => {
187
+ seenInputs.push(['hook2-saw', ctx.toolInput])
188
+ return { action: 'modify', input: { step: 2 } } as PluginHookResult
189
+ },
190
+ ]
191
+ // Real PluginLifecycleManager so the modify-threading path is exercised end-to-end.
192
+ const { PluginLifecycleManager } = await import('../../../plugin/lifecycle.js')
193
+ const realManager = new PluginLifecycleManager({
194
+ pluginRegistry: {} as any,
195
+ toolRegistry: {} as any,
196
+ log: makeLogger(),
197
+ })
198
+ realManager['hookHandlers'].set('pre_tool_use', [
199
+ { pluginId: 'p1' as any, handler: handlers[0] as any },
200
+ { pluginId: 'p2' as any, handler: handlers[1] as any },
201
+ ])
202
+
203
+ const exec = new ToolExecutor(
204
+ {
205
+ tools,
206
+ runId: mockRunId,
207
+ workingDirectory: '/tmp',
208
+ permissionMode: 'auto',
209
+ env: {},
210
+ abortSignal: new AbortController().signal,
211
+ pluginManager: realManager,
212
+ },
213
+ activityStore,
214
+ emitEvent,
215
+ makeLogger(),
216
+ )
217
+ await exec.executeBatch(buildResponse('echo', { original: true }))
218
+ expect(seenInputs).toEqual([
219
+ ['hook1-saw', { original: true }],
220
+ ['hook2-saw', { step: 1 }],
221
+ { step: 2 },
222
+ ])
223
+ })
224
+
225
+ it('carries modified input into synthetic skip outcome (modify -> skip chain)', async () => {
226
+ const executeMock = vi.fn(async () => ({ success: true, output: 'should-not-run' }))
227
+ const tools = makeToolRegistry(executeMock)
228
+ const { PluginLifecycleManager } = await import('../../../plugin/lifecycle.js')
229
+ const realManager = new PluginLifecycleManager({
230
+ pluginRegistry: {} as any,
231
+ toolRegistry: {} as any,
232
+ log: makeLogger(),
233
+ })
234
+ const seenInputs: unknown[] = []
235
+ realManager['hookHandlers'].set('pre_tool_use', [
236
+ {
237
+ pluginId: 'p1' as any,
238
+ handler: (async (ctx: any) => {
239
+ seenInputs.push(['hook1', ctx.toolInput])
240
+ return { action: 'modify', input: { patched: true } }
241
+ }) as any,
242
+ },
243
+ {
244
+ pluginId: 'p2' as any,
245
+ handler: (async (ctx: any) => {
246
+ seenInputs.push(['hook2', ctx.toolInput])
247
+ return { action: 'skip', reason: 'after patch' }
248
+ }) as any,
249
+ },
250
+ ])
251
+
252
+ const exec = new ToolExecutor(
253
+ {
254
+ tools,
255
+ runId: mockRunId,
256
+ workingDirectory: '/tmp',
257
+ permissionMode: 'auto',
258
+ env: {},
259
+ abortSignal: new AbortController().signal,
260
+ pluginManager: realManager,
261
+ },
262
+ activityStore,
263
+ emitEvent,
264
+ makeLogger(),
265
+ )
266
+ const batch = await exec.executeBatch(buildResponse('echo', { original: true }))
267
+ expect(executeMock).not.toHaveBeenCalled()
268
+ expect(batch.results[0]?.output).toBe('Tool echo skipped by plugin: after patch')
269
+ expect(seenInputs).toEqual([
270
+ ['hook1', { original: true }],
271
+ ['hook2', { patched: true }],
272
+ ])
273
+ // Synthetic tool_executing event should carry the modified input, not the original.
274
+ const executingEvent = emitted.find((e) => e.type === 'tool_executing')
275
+ expect(executingEvent).toMatchObject({
276
+ type: 'tool_executing',
277
+ toolName: 'echo',
278
+ input: { patched: true },
279
+ })
280
+ })
281
+
282
+ it('throws on pre_tool_use retry misuse', async () => {
283
+ const tools = makeToolRegistry(vi.fn(async () => ({ success: true, output: 'ok' })))
284
+ const pluginManager = makePluginManager(async (event) =>
285
+ event === 'pre_tool_use' ? ([{ action: 'retry' }] as PluginHookResult[]) : [],
286
+ )
287
+ const exec = new ToolExecutor(
288
+ {
289
+ tools,
290
+ runId: mockRunId,
291
+ workingDirectory: '/tmp',
292
+ permissionMode: 'auto',
293
+ env: {},
294
+ abortSignal: new AbortController().signal,
295
+ pluginManager,
296
+ },
297
+ activityStore,
298
+ emitEvent,
299
+ makeLogger(),
300
+ )
301
+ await expect(exec.executeBatch(buildResponse('echo', {}))).rejects.toThrow(
302
+ /unsupported action 'retry'/,
303
+ )
304
+ })
305
+ })
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { PluginHookResult } from '../../../types/plugin/index.js'
3
+ import { applyLifecycleHookResults } from '../plugin-hooks.js'
4
+
5
+ describe('applyLifecycleHookResults', () => {
6
+ it('returns silently when all results are continue', () => {
7
+ const results: PluginHookResult[] = [{ action: 'continue' }, { action: 'continue' }]
8
+ expect(() => applyLifecycleHookResults('run_start', results)).not.toThrow()
9
+ })
10
+
11
+ it('throws when a result is error', () => {
12
+ const results: PluginHookResult[] = [{ action: 'error', message: 'kaboom' }]
13
+ expect(() => applyLifecycleHookResults('pre_llm_call', results)).toThrow(
14
+ /pre_llm_call reported error: kaboom/,
15
+ )
16
+ })
17
+
18
+ it.each([
19
+ ['skip', { action: 'skip', reason: 'no' } as PluginHookResult],
20
+ ['modify', { action: 'modify', input: {} } as PluginHookResult],
21
+ ['retry', { action: 'retry' } as PluginHookResult],
22
+ ['resume', { action: 'resume', input: 'x' } as PluginHookResult],
23
+ ])('throws on unsupported %s action for lifecycle event', (_, result) => {
24
+ expect(() => applyLifecycleHookResults('iteration_start', [result])).toThrow(
25
+ /unsupported action/,
26
+ )
27
+ })
28
+ })