@namzu/sdk 0.1.5-rc.2 → 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 (122) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/bridge/tools/connector/adapter.d.ts +2 -2
  3. package/dist/bridge/tools/connector/adapter.d.ts.map +1 -1
  4. package/dist/bridge/tools/connector/adapter.js +3 -1
  5. package/dist/bridge/tools/connector/adapter.js.map +1 -1
  6. package/dist/connector/BaseConnector.d.ts +2 -1
  7. package/dist/connector/BaseConnector.d.ts.map +1 -1
  8. package/dist/connector/BaseConnector.js.map +1 -1
  9. package/dist/connector/builtins/http.d.ts +1 -1
  10. package/dist/connector/builtins/http.d.ts.map +1 -1
  11. package/dist/connector/builtins/http.js +1 -1
  12. package/dist/connector/builtins/http.js.map +1 -1
  13. package/dist/connector/builtins/webhook.d.ts +1 -1
  14. package/dist/connector/builtins/webhook.d.ts.map +1 -1
  15. package/dist/connector/builtins/webhook.js +1 -1
  16. package/dist/connector/builtins/webhook.js.map +1 -1
  17. package/dist/index.d.ts +5 -32
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -22
  20. package/dist/index.js.map +1 -1
  21. package/dist/manager/connector/environment.d.ts +4 -4
  22. package/dist/manager/connector/environment.d.ts.map +1 -1
  23. package/dist/manager/connector/environment.js.map +1 -1
  24. package/dist/manager/connector/lifecycle.d.ts +2 -2
  25. package/dist/manager/connector/lifecycle.d.ts.map +1 -1
  26. package/dist/manager/connector/lifecycle.js.map +1 -1
  27. package/dist/manager/connector/tenant.d.ts +3 -3
  28. package/dist/manager/connector/tenant.d.ts.map +1 -1
  29. package/dist/manager/connector/tenant.js.map +1 -1
  30. package/dist/manager/index.d.ts +1 -0
  31. package/dist/manager/index.d.ts.map +1 -1
  32. package/dist/manager/index.js +1 -0
  33. package/dist/manager/index.js.map +1 -1
  34. package/dist/manager/run/emergency.d.ts.map +1 -1
  35. package/dist/manager/run/emergency.js +44 -12
  36. package/dist/manager/run/emergency.js.map +1 -1
  37. package/dist/rag/vector-store.d.ts +2 -2
  38. package/dist/rag/vector-store.d.ts.map +1 -1
  39. package/dist/rag/vector-store.js.map +1 -1
  40. package/dist/registry/connector/scoped.d.ts +5 -4
  41. package/dist/registry/connector/scoped.d.ts.map +1 -1
  42. package/dist/registry/connector/scoped.js.map +1 -1
  43. package/dist/registry/index.d.ts +1 -0
  44. package/dist/registry/index.d.ts.map +1 -1
  45. package/dist/registry/index.js +1 -0
  46. package/dist/registry/index.js.map +1 -1
  47. package/dist/store/index.d.ts +4 -0
  48. package/dist/store/index.d.ts.map +1 -1
  49. package/dist/store/index.js +3 -0
  50. package/dist/store/index.js.map +1 -1
  51. package/dist/store/task/__tests__/disk-concurrency.test.d.ts +2 -0
  52. package/dist/store/task/__tests__/disk-concurrency.test.d.ts.map +1 -0
  53. package/dist/store/task/__tests__/disk-concurrency.test.js +91 -0
  54. package/dist/store/task/__tests__/disk-concurrency.test.js.map +1 -0
  55. package/dist/store/task/disk.d.ts +6 -0
  56. package/dist/store/task/disk.d.ts.map +1 -1
  57. package/dist/store/task/disk.js +150 -36
  58. package/dist/store/task/disk.js.map +1 -1
  59. package/dist/types/connector/core.d.ts +2 -2
  60. package/dist/types/connector/core.d.ts.map +1 -1
  61. package/dist/types/connector/definition.d.ts +7 -7
  62. package/dist/types/connector/definition.d.ts.map +1 -1
  63. package/dist/types/connector/mcp.d.ts +4 -4
  64. package/dist/types/connector/mcp.d.ts.map +1 -1
  65. package/dist/types/connector/scope.d.ts +3 -2
  66. package/dist/types/connector/scope.d.ts.map +1 -1
  67. package/dist/types/connector/scope.js.map +1 -1
  68. package/dist/types/connector/tenant.d.ts +4 -4
  69. package/dist/types/connector/tenant.d.ts.map +1 -1
  70. package/dist/types/rag/knowledge-base.d.ts +2 -2
  71. package/dist/types/rag/knowledge-base.d.ts.map +1 -1
  72. package/dist/types/rag/scope.d.ts +2 -1
  73. package/dist/types/rag/scope.d.ts.map +1 -1
  74. package/dist/types/rag/storage.d.ts +3 -3
  75. package/dist/types/rag/storage.d.ts.map +1 -1
  76. package/dist/types/rag/vector.d.ts +3 -3
  77. package/dist/types/rag/vector.d.ts.map +1 -1
  78. package/dist/vault/InMemoryCredentialVault.d.ts +3 -3
  79. package/dist/vault/InMemoryCredentialVault.d.ts.map +1 -1
  80. package/dist/vault/InMemoryCredentialVault.js.map +1 -1
  81. package/package.json +1 -1
  82. package/src/bridge/tools/connector/adapter.ts +5 -3
  83. package/src/connector/BaseConnector.ts +2 -1
  84. package/src/connector/builtins/http.ts +1 -1
  85. package/src/connector/builtins/webhook.ts +1 -1
  86. package/src/index.ts +46 -40
  87. package/src/manager/connector/environment.ts +5 -5
  88. package/src/manager/connector/lifecycle.ts +2 -2
  89. package/src/manager/connector/tenant.ts +8 -3
  90. package/src/manager/index.ts +1 -0
  91. package/src/manager/run/emergency.ts +45 -16
  92. package/src/rag/vector-store.ts +2 -2
  93. package/src/registry/connector/scoped.ts +7 -6
  94. package/src/registry/index.ts +1 -0
  95. package/src/store/index.ts +5 -0
  96. package/src/store/task/__tests__/disk-concurrency.test.ts +118 -0
  97. package/src/store/task/disk.ts +150 -37
  98. package/src/types/connector/core.ts +2 -2
  99. package/src/types/connector/definition.ts +7 -7
  100. package/src/types/connector/mcp.ts +4 -4
  101. package/src/types/connector/scope.ts +3 -2
  102. package/src/types/connector/tenant.ts +4 -4
  103. package/src/types/rag/knowledge-base.ts +2 -2
  104. package/src/types/rag/scope.ts +3 -1
  105. package/src/types/rag/storage.ts +3 -3
  106. package/src/types/rag/vector.ts +3 -3
  107. package/src/vault/InMemoryCredentialVault.ts +3 -3
  108. package/dist/manager/agent/index.d.ts +0 -2
  109. package/dist/manager/agent/index.d.ts.map +0 -1
  110. package/dist/manager/agent/index.js +0 -2
  111. package/dist/manager/agent/index.js.map +0 -1
  112. package/dist/registry/agent/index.d.ts +0 -2
  113. package/dist/registry/agent/index.d.ts.map +0 -1
  114. package/dist/registry/agent/index.js +0 -2
  115. package/dist/registry/agent/index.js.map +0 -1
  116. package/dist/router/index.d.ts +0 -2
  117. package/dist/router/index.d.ts.map +0 -1
  118. package/dist/router/index.js +0 -2
  119. package/dist/router/index.js.map +0 -1
  120. package/src/manager/agent/index.ts +0 -1
  121. package/src/registry/agent/index.ts +0 -1
  122. package/src/router/index.ts +0 -1
@@ -0,0 +1,118 @@
1
+ import { mkdtempSync, rmSync } from 'node:fs'
2
+ import { tmpdir } from 'node:os'
3
+ import { join } from 'node:path'
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
5
+ import type { RunId } from '../../../types/ids/index.js'
6
+ import { generateRunId } from '../../../utils/id.js'
7
+ import { DiskTaskStore } from '../disk.js'
8
+
9
+ describe('DiskTaskStore — concurrency regressions', () => {
10
+ let baseDir: string
11
+ let runId: RunId
12
+ let store: DiskTaskStore
13
+
14
+ beforeEach(() => {
15
+ baseDir = mkdtempSync(join(tmpdir(), 'namzu-task-concurrency-'))
16
+ runId = generateRunId()
17
+ store = new DiskTaskStore({ baseDir, defaultRunId: runId })
18
+ })
19
+
20
+ afterEach(() => {
21
+ rmSync(baseDir, { recursive: true, force: true })
22
+ })
23
+
24
+ it('does not deadlock when two deletes race on mutually-referencing tasks', async () => {
25
+ const a = await store.create({ runId, subject: 'A' })
26
+ const b = await store.create({ runId, subject: 'B' })
27
+
28
+ // Establish bidirectional edge: A blocks B AND B blocks A.
29
+ // (Nonsensical semantically, but the store allows it and the lock logic
30
+ // must not deadlock.)
31
+ await store.block(a.id, b.id)
32
+ await store.block(b.id, a.id)
33
+
34
+ // Race the two deletes. The pre-fix implementation (lock this → iterate
35
+ // related → lock each) could acquire A→B from one call and B→A from the
36
+ // other, deadlocking. withLocks() sorts IDs canonically so both calls
37
+ // acquire [A, B] in the same order.
38
+ const withTimeout = <T>(p: Promise<T>, ms: number): Promise<T> =>
39
+ Promise.race([
40
+ p,
41
+ new Promise<never>((_resolve, reject) =>
42
+ setTimeout(() => reject(new Error('timeout — likely deadlocked')), ms),
43
+ ),
44
+ ])
45
+
46
+ const [resA, resB] = await withTimeout(
47
+ Promise.all([store.delete(a.id), store.delete(b.id)]),
48
+ 2000,
49
+ )
50
+ expect(resA).toBe(true)
51
+ expect(resB).toBe(true)
52
+ expect(await store.get(a.id)).toBeUndefined()
53
+ expect(await store.get(b.id)).toBeUndefined()
54
+ })
55
+
56
+ it('serializes concurrent same-ID updates (withLock race regression)', async () => {
57
+ const task = await store.create({ runId, subject: 'shared' })
58
+
59
+ // update()'s metadata merge does `{ ...task.metadata, ...updates.metadata }`
60
+ // inside withLock. If withLock serializes correctly, every update's key
61
+ // survives into the final metadata (each sees the latest merged state).
62
+ // If withLock had the race bug, two concurrent updates would both read
63
+ // the SAME snapshot, each add their one key, and one update's key would
64
+ // be overwritten when the second committed — yielding < N keys in the
65
+ // final metadata.
66
+ const n = 20
67
+ await Promise.all(
68
+ Array.from({ length: n }, (_v, i) =>
69
+ store.update(task.id, { metadata: { [`k${i}`]: true } }),
70
+ ),
71
+ )
72
+
73
+ const final = await store.get(task.id)
74
+ const keys = Object.keys(final?.metadata ?? {})
75
+ expect(keys).toHaveLength(n)
76
+ for (let i = 0; i < n; i++) {
77
+ expect(final?.metadata?.[`k${i}`]).toBe(true)
78
+ }
79
+ })
80
+
81
+ it('establishes bidirectional edge atomically under create()', async () => {
82
+ const blocker = await store.create({ runId, subject: 'blocker' })
83
+
84
+ // Race: create a child with blockedBy=[blocker] while concurrently
85
+ // deleting the blocker. Either outcome is acceptable (child created
86
+ // and blocker still present in its blocks list, OR blocker gone and
87
+ // child created with dangling reference), but we must NEVER see
88
+ // blocker still present WITHOUT having child in its blocks list.
89
+ const tasks = await Promise.all([
90
+ store.create({ runId, subject: 'child', blockedBy: [blocker.id] }),
91
+ // No delete here — keep the create edge test focused. The point is
92
+ // that after create() resolves, the blocker's blocks list contains
93
+ // the new task ID atomically.
94
+ ])
95
+ const child = tasks[0]
96
+
97
+ const blockerAfter = await store.get(blocker.id)
98
+ expect(blockerAfter).toBeDefined()
99
+ expect(blockerAfter?.blocks).toContain(child.id)
100
+ expect(child.blockedBy).toContain(blocker.id)
101
+ })
102
+
103
+ it('block() skips gracefully when one task disappeared before lock acquired', async () => {
104
+ const a = await store.create({ runId, subject: 'A' })
105
+ const b = await store.create({ runId, subject: 'B' })
106
+
107
+ // Delete B, then try to block a → b. The findTask pre-check passes for a
108
+ // but fails for b, so block() returns early (silent no-op per existing
109
+ // contract).
110
+ await store.delete(b.id)
111
+
112
+ // block() should not throw and A's blocks list should remain empty.
113
+ await store.block(a.id, b.id)
114
+
115
+ const aAfter = await store.get(a.id)
116
+ expect(aAfter?.blocks).not.toContain(b.id)
117
+ })
118
+ })
@@ -61,8 +61,13 @@ export class DiskTaskStore implements TaskStore {
61
61
  }
62
62
 
63
63
  private async withLock<T>(taskId: TaskId, fn: () => Promise<T>): Promise<T> {
64
- const existing = this.locks.get(taskId)
65
- if (existing) {
64
+ // Loop instead of single await: after awaiting a lock, the map may
65
+ // already hold a NEW lock acquired by another coroutine that woke
66
+ // up before us. Re-check on each iteration until we observe an empty
67
+ // slot, at which point the synchronous set() below claims it.
68
+ while (true) {
69
+ const existing = this.locks.get(taskId)
70
+ if (!existing) break
66
71
  await existing.catch(() => undefined)
67
72
  }
68
73
 
@@ -82,6 +87,22 @@ export class DiskTaskStore implements TaskStore {
82
87
  }
83
88
  }
84
89
 
90
+ /**
91
+ * Acquires locks on multiple task IDs in a canonical (lexicographic) order
92
+ * to prevent deadlocks when operations touch several related tasks.
93
+ * Duplicates are removed; each ID is locked exactly once.
94
+ */
95
+ private async withLocks<T>(taskIds: readonly TaskId[], fn: () => Promise<T>): Promise<T> {
96
+ const unique = [...new Set(taskIds)].sort() as TaskId[]
97
+ const acquire = async (i: number): Promise<T> => {
98
+ if (i >= unique.length) return fn()
99
+ const nextId = unique[i]
100
+ if (nextId === undefined) return fn()
101
+ return this.withLock(nextId, () => acquire(i + 1))
102
+ }
103
+ return acquire(0)
104
+ }
105
+
85
106
  on(listener: TaskEventListener): () => void {
86
107
  this.listeners.push(listener)
87
108
  return () => {
@@ -93,7 +114,12 @@ export class DiskTaskStore implements TaskStore {
93
114
  for (const listener of this.listeners) {
94
115
  try {
95
116
  listener(event)
96
- } catch {}
117
+ } catch (err) {
118
+ this.log.warn('Task event listener threw', {
119
+ error: err instanceof Error ? err.message : String(err),
120
+ eventType: event.type,
121
+ })
122
+ }
97
123
  }
98
124
  }
99
125
 
@@ -118,18 +144,27 @@ export class DiskTaskStore implements TaskStore {
118
144
 
119
145
  const dir = this.taskDir(runId)
120
146
  await mkdir(dir, { recursive: true })
121
- await atomicWriteJson(this.taskPath(runId, taskId), task)
122
147
 
123
- if (params.blockedBy) {
124
- for (const blockerId of params.blockedBy) {
125
- await this.withLock(blockerId, async () => {
148
+ const blockers = params.blockedBy ?? []
149
+ if (blockers.length === 0) {
150
+ await atomicWriteJson(this.taskPath(runId, taskId), task)
151
+ } else {
152
+ // Hold locks on all blockers while establishing the bidirectional edge:
153
+ // update each blocker's `blocks` list AND write the new task together,
154
+ // so concurrent delete(blockerId) sees a consistent pair.
155
+ await this.withLocks(blockers, async () => {
156
+ for (const blockerId of blockers) {
126
157
  const blocker = await this.readTask(runId, blockerId)
127
158
  if (blocker && !blocker.blocks.includes(taskId)) {
128
159
  blocker.blocks.push(taskId)
129
160
  await atomicWriteJson(this.taskPath(runId, blockerId), blocker)
130
161
  }
131
- })
132
- }
162
+ // If blocker is missing, we still write the new task with its
163
+ // blockedBy reference; the dangling reference is visible to
164
+ // subsequent readers rather than silently pruned.
165
+ }
166
+ await atomicWriteJson(this.taskPath(runId, taskId), task)
167
+ })
133
168
  }
134
169
 
135
170
  this.log.info('Task created', { taskId, subject: params.subject, runId })
@@ -185,30 +220,59 @@ export class DiskTaskStore implements TaskStore {
185
220
  const found = await this.findTask(id)
186
221
  if (!found) return false
187
222
 
188
- return this.withLock(id, async () => {
223
+ // Read the task once (unlocked) to discover its related IDs, then acquire
224
+ // locks on the entire set (self + blockers + blocked) in canonical order.
225
+ // Locking the full set up-front in sorted order prevents deadlock when two
226
+ // deletes race on tasks that mutually reference each other.
227
+ //
228
+ // Known trade-off: the lock set is computed from the unlocked preview. If
229
+ // create()/block() adds a NEW relation between preview and lock acquisition,
230
+ // we will mutate that neighbor without holding its lock. The alternative
231
+ // (retry loop with expanding lock set) adds substantial complexity for a
232
+ // rare interleaving in a single-tenant single-writer store; revisit if the
233
+ // store grows concurrent writers.
234
+ const preview = await this.readTask(found.runId, id)
235
+ if (!preview) return false
236
+
237
+ const relatedIds: TaskId[] = [id, ...preview.blockedBy, ...preview.blocks]
238
+
239
+ return this.withLocks(relatedIds, async () => {
240
+ // Re-read under lock: the task's block graph may have changed between
241
+ // the unlocked preview and lock acquisition.
189
242
  const task = await this.readTask(found.runId, id)
190
243
  if (!task) return false
191
244
 
192
245
  for (const blockerId of task.blockedBy) {
193
- await this.withLock(blockerId, async () => {
194
- const blocker = await this.readTask(task.runId, blockerId)
195
- if (blocker) {
196
- blocker.blocks = blocker.blocks.filter((bid) => bid !== id)
197
- await atomicWriteJson(this.taskPath(task.runId, blockerId), blocker)
198
- }
199
- })
246
+ const blocker = await this.readTask(task.runId, blockerId)
247
+ if (blocker) {
248
+ blocker.blocks = blocker.blocks.filter((bid) => bid !== id)
249
+ await atomicWriteJson(this.taskPath(task.runId, blockerId), blocker)
250
+ }
200
251
  }
201
252
  for (const blockedId of task.blocks) {
202
- await this.withLock(blockedId, async () => {
203
- const blocked = await this.readTask(task.runId, blockedId)
204
- if (blocked) {
205
- blocked.blockedBy = blocked.blockedBy.filter((bid) => bid !== id)
206
- await atomicWriteJson(this.taskPath(task.runId, blockedId), blocked)
207
- }
208
- })
253
+ const blocked = await this.readTask(task.runId, blockedId)
254
+ if (blocked) {
255
+ blocked.blockedBy = blocked.blockedBy.filter((bid) => bid !== id)
256
+ await atomicWriteJson(this.taskPath(task.runId, blockedId), blocked)
257
+ }
209
258
  }
210
259
 
211
- await unlink(this.taskPath(task.runId, id)).catch(() => undefined)
260
+ try {
261
+ await unlink(this.taskPath(task.runId, id))
262
+ } catch (err) {
263
+ const code = (err as NodeJS.ErrnoException).code
264
+ if (code !== 'ENOENT') {
265
+ this.log.error(
266
+ 'Failed to delete task file; relations may be in a partially-updated state',
267
+ {
268
+ taskId: id,
269
+ error: err instanceof Error ? err.message : String(err),
270
+ },
271
+ )
272
+ throw err
273
+ }
274
+ // ENOENT: already gone, treat as success.
275
+ }
212
276
  this.log.info('Task deleted', { taskId: id })
213
277
  this.emit({ type: 'task.deleted', taskId: id, task, timestamp: Date.now() })
214
278
  return true
@@ -222,7 +286,13 @@ export class DiskTaskStore implements TaskStore {
222
286
  let files: string[]
223
287
  try {
224
288
  files = await readdir(dir)
225
- } catch {
289
+ } catch (err) {
290
+ const code = (err as NodeJS.ErrnoException).code
291
+ if (code === 'ENOENT') return []
292
+ this.log.warn('Failed to list task directory', {
293
+ dir,
294
+ error: err instanceof Error ? err.message : String(err),
295
+ })
226
296
  return []
227
297
  }
228
298
 
@@ -233,8 +303,11 @@ export class DiskTaskStore implements TaskStore {
233
303
  const raw = await readFile(join(dir, file), 'utf-8')
234
304
  const task = JSON.parse(raw) as Task
235
305
  tasks.push(task)
236
- } catch {
237
- this.log.warn('Failed to read task file', { file })
306
+ } catch (err) {
307
+ this.log.warn('Failed to read task file', {
308
+ file,
309
+ error: err instanceof Error ? err.message : String(err),
310
+ })
238
311
  }
239
312
  }
240
313
 
@@ -274,19 +347,40 @@ export class DiskTaskStore implements TaskStore {
274
347
  const blockedFound = await this.findTask(blockedId)
275
348
  if (!blockerFound || !blockedFound) return
276
349
 
277
- await this.withLock(blockerId, async () => {
350
+ // Acquire BOTH locks before mutating either side of the edge. Sequential
351
+ // single-locks allow a concurrent operation to interleave and observe
352
+ // a half-established relationship.
353
+ await this.withLocks([blockerId, blockedId], async () => {
278
354
  const blocker = await this.readTask(blockerFound.runId, blockerId)
279
- if (blocker && !blocker.blocks.includes(blockedId)) {
355
+ const blocked = await this.readTask(blockedFound.runId, blockedId)
356
+
357
+ // Re-validate under lock: either side may have been deleted between
358
+ // the pre-check (findTask) and lock acquisition. Establishing only
359
+ // one side of the edge would leave a dangling reference; skip the
360
+ // whole operation instead.
361
+ if (!blocker || !blocked) {
362
+ this.log.warn('block(): task disappeared before lock acquired; skipping', {
363
+ blockerId,
364
+ blockedId,
365
+ blockerExists: !!blocker,
366
+ blockedExists: !!blocked,
367
+ })
368
+ return
369
+ }
370
+
371
+ let mutated = false
372
+ if (!blocker.blocks.includes(blockedId)) {
280
373
  blocker.blocks.push(blockedId)
281
374
  await atomicWriteJson(this.taskPath(blocker.runId, blockerId), blocker)
375
+ mutated = true
282
376
  }
283
- })
284
-
285
- await this.withLock(blockedId, async () => {
286
- const blocked = await this.readTask(blockedFound.runId, blockedId)
287
- if (blocked && !blocked.blockedBy.includes(blockerId)) {
377
+ if (!blocked.blockedBy.includes(blockerId)) {
288
378
  blocked.blockedBy.push(blockerId)
289
379
  await atomicWriteJson(this.taskPath(blocked.runId, blockedId), blocked)
380
+ mutated = true
381
+ }
382
+ if (!mutated) {
383
+ this.log.debug('block(): edge already exists', { blockerId, blockedId })
290
384
  }
291
385
  })
292
386
  }
@@ -307,10 +401,29 @@ export class DiskTaskStore implements TaskStore {
307
401
  }
308
402
 
309
403
  private async readTask(runId: RunId, taskId: TaskId): Promise<Task | null> {
404
+ const path = this.taskPath(runId, taskId)
405
+ let raw: string
406
+ try {
407
+ raw = await readFile(path, 'utf-8')
408
+ } catch (err) {
409
+ const code = (err as NodeJS.ErrnoException).code
410
+ if (code === 'ENOENT') return null
411
+ this.log.warn('Failed to read task', {
412
+ taskId,
413
+ path,
414
+ error: err instanceof Error ? err.message : String(err),
415
+ })
416
+ return null
417
+ }
418
+
310
419
  try {
311
- const raw = await readFile(this.taskPath(runId, taskId), 'utf-8')
312
420
  return JSON.parse(raw) as Task
313
- } catch {
421
+ } catch (err) {
422
+ this.log.error('Corrupt task JSON on disk', {
423
+ taskId,
424
+ path,
425
+ error: err instanceof Error ? err.message : String(err),
426
+ })
314
427
  return null
315
428
  }
316
429
  }
@@ -1,5 +1,5 @@
1
1
  import type { z } from 'zod'
2
- import type { ConnectorInstanceId } from '../ids/index.js'
2
+ import type { ConnectorId, ConnectorInstanceId } from '../ids/index.js'
3
3
 
4
4
  export type ConnectionType = 'http' | 'webhook' | 'custom'
5
5
 
@@ -39,7 +39,7 @@ export interface ConnectorTrigger {
39
39
  }
40
40
 
41
41
  export interface ConnectorEvent {
42
- connectorId: string
42
+ connectorId: ConnectorId
43
43
  instanceId: ConnectorInstanceId
44
44
  trigger: string
45
45
  payload: unknown
@@ -1,5 +1,5 @@
1
1
  import type { z } from 'zod'
2
- import type { ConnectorInstanceId } from '../ids/index.js'
2
+ import type { ConnectorId, ConnectorInstanceId } from '../ids/index.js'
3
3
  import type {
4
4
  AuthConfig,
5
5
  AuthType,
@@ -11,7 +11,7 @@ import type {
11
11
  } from './core.js'
12
12
 
13
13
  export interface ConnectorDefinition<TConfig = unknown> {
14
- id: string
14
+ id: ConnectorId
15
15
  name: string
16
16
  description: string
17
17
  version?: string
@@ -24,7 +24,7 @@ export interface ConnectorDefinition<TConfig = unknown> {
24
24
  }
25
25
 
26
26
  export interface ConnectorConfig {
27
- connectorId: string
27
+ connectorId: ConnectorId
28
28
  name: string
29
29
  auth?: AuthConfig
30
30
  options?: Record<string, unknown>
@@ -32,7 +32,7 @@ export interface ConnectorConfig {
32
32
 
33
33
  export interface ConnectorInstance {
34
34
  id: ConnectorInstanceId
35
- connectorId: string
35
+ connectorId: ConnectorId
36
36
  config: ConnectorConfig
37
37
  status: ConnectorStatus
38
38
  createdAt: number
@@ -63,9 +63,9 @@ export interface ConnectorLifecycle<TConfig = unknown> {
63
63
  }
64
64
 
65
65
  export type ConnectorLifecycleEvent =
66
- | { type: 'connector_registered'; connectorId: string }
67
- | { type: 'connector_unregistered'; connectorId: string }
68
- | { type: 'instance_created'; instanceId: ConnectorInstanceId; connectorId: string }
66
+ | { type: 'connector_registered'; connectorId: ConnectorId }
67
+ | { type: 'connector_unregistered'; connectorId: ConnectorId }
68
+ | { type: 'instance_created'; instanceId: ConnectorInstanceId; connectorId: ConnectorId }
69
69
  | { type: 'instance_connecting'; instanceId: ConnectorInstanceId }
70
70
  | { type: 'instance_connected'; instanceId: ConnectorInstanceId }
71
71
  | { type: 'instance_disconnected'; instanceId: ConnectorInstanceId }
@@ -1,4 +1,4 @@
1
- import type { ConnectorInstanceId, MCPClientId, MCPServerId } from '../ids/index.js'
1
+ import type { ConnectorId, ConnectorInstanceId, MCPClientId, MCPServerId } from '../ids/index.js'
2
2
  import type {
3
3
  ConnectorDefinition,
4
4
  ConnectorExecuteParams,
@@ -180,7 +180,7 @@ export interface MCPConnectorBridgeConfig {
180
180
 
181
181
  export interface MCPConnectorBridgeToolMapping {
182
182
  mcpToolName: string
183
- connectorId: string
183
+ connectorId: ConnectorId
184
184
  instanceId: ConnectorInstanceId
185
185
  methodName: string
186
186
  }
@@ -205,8 +205,8 @@ export type MCPEventListener = (event: MCPLifecycleEvent) => void
205
205
  type ConnectorManager = {
206
206
  getInstance(instanceId: ConnectorInstanceId): ConnectorInstance | undefined
207
207
  getRegistry(): {
208
- get(connectorId: string): ConnectorDefinition | undefined
209
- getOrThrow(connectorId: string): ConnectorDefinition
208
+ get(connectorId: ConnectorId): ConnectorDefinition | undefined
209
+ getOrThrow(connectorId: ConnectorId): ConnectorDefinition
210
210
  }
211
211
  listConnectedInstances(): ConnectorInstance[]
212
212
  execute(params: ConnectorExecuteParams): Promise<ConnectorExecuteResult>
@@ -1,3 +1,4 @@
1
+ import type { ConnectorId } from '../ids/index.js'
1
2
  import type { AuthConfig } from './core.js'
2
3
  import type { ConnectorConfig } from './definition.js'
3
4
 
@@ -18,7 +19,7 @@ export interface ScopeRef {
18
19
 
19
20
  export interface ScopedConnectorConfig {
20
21
  scope: ScopeRef
21
- connectorId: string
22
+ connectorId: ConnectorId
22
23
 
23
24
  config?: Partial<ConnectorConfig>
24
25
 
@@ -30,7 +31,7 @@ export interface ScopedConnectorConfig {
30
31
  }
31
32
 
32
33
  export interface ResolvedConnectorConfig {
33
- connectorId: string
34
+ connectorId: ConnectorId
34
35
  config: ConnectorConfig
35
36
  auth?: AuthConfig
36
37
  enabled: boolean
@@ -1,4 +1,4 @@
1
- import type { CredentialId, EnvironmentId, TenantId } from '../ids/index.js'
1
+ import type { ConnectorId, CredentialId, EnvironmentId, TenantId } from '../ids/index.js'
2
2
  import type { AuthConfig, AuthType } from './core.js'
3
3
 
4
4
  export type EnvironmentTier = 'production' | 'staging' | 'development' | 'testing'
@@ -25,7 +25,7 @@ export interface TenantRateLimitConfig {
25
25
 
26
26
  export interface CredentialRef {
27
27
  id: CredentialId
28
- connectorId: string
28
+ connectorId: ConnectorId
29
29
  tenantId: TenantId
30
30
  label: string
31
31
  authType: AuthType
@@ -36,11 +36,11 @@ export interface CredentialRef {
36
36
  export interface CredentialVault {
37
37
  store(
38
38
  tenantId: TenantId,
39
- connectorId: string,
39
+ connectorId: ConnectorId,
40
40
  label: string,
41
41
  auth: AuthConfig,
42
42
  ): Promise<CredentialRef>
43
43
  retrieve(credentialId: CredentialId): Promise<AuthConfig | undefined>
44
44
  revoke(credentialId: CredentialId): Promise<boolean>
45
- list(tenantId: TenantId, connectorId?: string): Promise<CredentialRef[]>
45
+ list(tenantId: TenantId, connectorId?: ConnectorId): Promise<CredentialRef[]>
46
46
  }
@@ -1,4 +1,4 @@
1
- import type { DocumentId, KnowledgeBaseId } from '../ids/index.js'
1
+ import type { DocumentId, KnowledgeBaseId, TenantId } from '../ids/index.js'
2
2
  import type { ChunkingConfig } from './chunking.js'
3
3
  import type { EmbeddingConfig } from './embedding.js'
4
4
  import type { IngestionResult } from './ingestion.js'
@@ -9,7 +9,7 @@ export interface KnowledgeBaseConfig {
9
9
  id?: KnowledgeBaseId
10
10
  name: string
11
11
  description?: string
12
- tenantId: string
12
+ tenantId: TenantId
13
13
  namespace?: string
14
14
  chunking?: Partial<ChunkingConfig>
15
15
  retrieval?: Partial<RetrievalConfig>
@@ -1,5 +1,7 @@
1
+ import type { TenantId } from '../ids/index.js'
2
+
1
3
  export interface TenantScope {
2
- tenantId: string
4
+ tenantId: TenantId
3
5
  namespace?: string
4
6
  }
5
7
 
@@ -1,10 +1,10 @@
1
- import type { ChunkId, DocumentId, KnowledgeBaseId } from '../ids/index.js'
1
+ import type { ChunkId, DocumentId, KnowledgeBaseId, TenantId } from '../ids/index.js'
2
2
  import type { DocumentMetadata } from './scope.js'
3
3
 
4
4
  export interface Document {
5
5
  id: DocumentId
6
6
  knowledgeBaseId: KnowledgeBaseId
7
- tenantId: string
7
+ tenantId: TenantId
8
8
  content: string
9
9
  metadata: DocumentMetadata
10
10
  createdAt: number
@@ -15,7 +15,7 @@ export interface Chunk {
15
15
  id: ChunkId
16
16
  documentId: DocumentId
17
17
  knowledgeBaseId: KnowledgeBaseId
18
- tenantId: string
18
+ tenantId: TenantId
19
19
  content: string
20
20
  index: number
21
21
  tokenCount: number
@@ -1,4 +1,4 @@
1
- import type { ChunkId, DocumentId, KnowledgeBaseId } from '../ids/index.js'
1
+ import type { ChunkId, DocumentId, KnowledgeBaseId, TenantId } from '../ids/index.js'
2
2
  import type { Chunk } from './storage.js'
3
3
 
4
4
  export interface VectorSearchResult {
@@ -9,7 +9,7 @@ export interface VectorSearchResult {
9
9
  export interface VectorStoreQuery {
10
10
  embedding: number[]
11
11
  topK: number
12
- tenantId: string
12
+ tenantId: TenantId
13
13
  knowledgeBaseId?: KnowledgeBaseId
14
14
  filter?: Record<string, unknown>
15
15
  minScore?: number
@@ -20,5 +20,5 @@ export interface VectorStore {
20
20
  search(query: VectorStoreQuery): Promise<VectorSearchResult[]>
21
21
  delete(chunkIds: ChunkId[]): Promise<void>
22
22
  deleteByDocument(documentId: DocumentId): Promise<void>
23
- deleteByKnowledgeBase(knowledgeBaseId: KnowledgeBaseId, tenantId: string): Promise<void>
23
+ deleteByKnowledgeBase(knowledgeBaseId: KnowledgeBaseId, tenantId: TenantId): Promise<void>
24
24
  }
@@ -1,5 +1,5 @@
1
1
  import type { AuthConfig, CredentialRef, CredentialVault } from '../types/connector/index.js'
2
- import type { CredentialId, TenantId } from '../types/ids/index.js'
2
+ import type { ConnectorId, CredentialId, TenantId } from '../types/ids/index.js'
3
3
  import { generateCredentialId } from '../utils/id.js'
4
4
  import { type Logger, getRootLogger } from '../utils/logger.js'
5
5
 
@@ -14,7 +14,7 @@ export class InMemoryCredentialVault implements CredentialVault {
14
14
 
15
15
  async store(
16
16
  tenantId: TenantId,
17
- connectorId: string,
17
+ connectorId: ConnectorId,
18
18
  label: string,
19
19
  auth: AuthConfig,
20
20
  ): Promise<CredentialRef> {
@@ -48,7 +48,7 @@ export class InMemoryCredentialVault implements CredentialVault {
48
48
  return existed
49
49
  }
50
50
 
51
- async list(tenantId: TenantId, connectorId?: string): Promise<CredentialRef[]> {
51
+ async list(tenantId: TenantId, connectorId?: ConnectorId): Promise<CredentialRef[]> {
52
52
  const results: CredentialRef[] = []
53
53
  for (const ref of this.refs.values()) {
54
54
  if (ref.tenantId !== tenantId) continue
@@ -1,2 +0,0 @@
1
- export { AgentManager } from './lifecycle.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/manager/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { AgentManager } from './lifecycle.js';
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/manager/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { AgentRegistry } from './definitions.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/registry/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { AgentRegistry } from './definitions.js';
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/registry/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { resolveTaskModel } from './task-router.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { resolveTaskModel } from './task-router.js';
2
- //# sourceMappingURL=index.js.map