@kb-labs/adapters 0.5.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 (276) hide show
  1. package/.cursorrules +32 -0
  2. package/.github/workflows/ci.yml +13 -0
  3. package/.github/workflows/deploy.yml +28 -0
  4. package/.github/workflows/docker-build.yml +25 -0
  5. package/.github/workflows/drift-check.yml +10 -0
  6. package/.github/workflows/profiles-validate.yml +16 -0
  7. package/.github/workflows/release.yml +8 -0
  8. package/.kb/devkit/agents/devkit-maintainer/context.globs +15 -0
  9. package/.kb/devkit/agents/devkit-maintainer/permissions.yml +17 -0
  10. package/.kb/devkit/agents/devkit-maintainer/prompt.md +28 -0
  11. package/.kb/devkit/agents/devkit-maintainer/runbook.md +31 -0
  12. package/.kb/devkit/agents/docs-crafter/prompt.md +24 -0
  13. package/.kb/devkit/agents/docs-crafter/runbook.md +18 -0
  14. package/.kb/devkit/agents/release-manager/context.globs +7 -0
  15. package/.kb/devkit/agents/release-manager/prompt.md +27 -0
  16. package/.kb/devkit/agents/release-manager/runbook.md +17 -0
  17. package/.kb/devkit/agents/test-generator/context.globs +7 -0
  18. package/.kb/devkit/agents/test-generator/prompt.md +27 -0
  19. package/.kb/devkit/agents/test-generator/runbook.md +18 -0
  20. package/CONTRIBUTING.md +90 -0
  21. package/IMPLEMENTATION_COMPLETE.md +416 -0
  22. package/LICENSE +186 -0
  23. package/README-TEMPLATE.md +179 -0
  24. package/README.md +306 -0
  25. package/docs/DOCUMENTATION.md +74 -0
  26. package/docs/adr/0000-template.md +49 -0
  27. package/docs/adr/0001-architecture-and-repository-layout.md +33 -0
  28. package/docs/adr/0002-plugins-and-extensibility.md +46 -0
  29. package/docs/adr/0003-package-and-module-boundaries.md +37 -0
  30. package/docs/adr/0004-versioning-and-release-policy.md +38 -0
  31. package/docs/adr/0005-use-devkit-for-shared-tooling.md +48 -0
  32. package/docs/adr/0006-adopt-devkit-sync.md +47 -0
  33. package/docs/adr/0007-drift-kit-check.md +72 -0
  34. package/docs/adr/0008-devkit-sync-wrapper-strategy.md +67 -0
  35. package/docs/naming-convention.md +272 -0
  36. package/eslint.config.js +27 -0
  37. package/kb-labs.config.json +5 -0
  38. package/package.json +84 -0
  39. package/package.json.bin +25 -0
  40. package/package.json.lib +30 -0
  41. package/packages/adapters-analytics-duckdb/package.json +54 -0
  42. package/packages/adapters-analytics-duckdb/scripts/migrate-from-jsonl.mjs +253 -0
  43. package/packages/adapters-analytics-duckdb/src/index.ts +380 -0
  44. package/packages/adapters-analytics-duckdb/src/manifest.ts +36 -0
  45. package/packages/adapters-analytics-duckdb/src/schema.ts +161 -0
  46. package/packages/adapters-analytics-duckdb/tsconfig.build.json +15 -0
  47. package/packages/adapters-analytics-duckdb/tsconfig.json +9 -0
  48. package/packages/adapters-analytics-duckdb/tsup.config.ts +9 -0
  49. package/packages/adapters-analytics-file/README.md +32 -0
  50. package/packages/adapters-analytics-file/eslint.config.js +27 -0
  51. package/packages/adapters-analytics-file/package.json +50 -0
  52. package/packages/adapters-analytics-file/src/__tests__/daily-stats.spec.ts +287 -0
  53. package/packages/adapters-analytics-file/src/__tests__/scoped-analytics.test.ts +233 -0
  54. package/packages/adapters-analytics-file/src/index.test.ts +214 -0
  55. package/packages/adapters-analytics-file/src/index.ts +830 -0
  56. package/packages/adapters-analytics-file/src/manifest.ts +45 -0
  57. package/packages/adapters-analytics-file/tsconfig.build.json +15 -0
  58. package/packages/adapters-analytics-file/tsconfig.json +9 -0
  59. package/packages/adapters-analytics-file/tsup.config.ts +9 -0
  60. package/packages/adapters-analytics-sqlite/package.json +55 -0
  61. package/packages/adapters-analytics-sqlite/scripts/migrate-from-jsonl.mjs +194 -0
  62. package/packages/adapters-analytics-sqlite/src/index.ts +460 -0
  63. package/packages/adapters-analytics-sqlite/src/manifest.ts +41 -0
  64. package/packages/adapters-analytics-sqlite/tsconfig.build.json +15 -0
  65. package/packages/adapters-analytics-sqlite/tsconfig.json +9 -0
  66. package/packages/adapters-analytics-sqlite/tsup.config.ts +9 -0
  67. package/packages/adapters-environment-docker/README.md +28 -0
  68. package/packages/adapters-environment-docker/eslint.config.js +5 -0
  69. package/packages/adapters-environment-docker/package.json +49 -0
  70. package/packages/adapters-environment-docker/src/index.test.ts +138 -0
  71. package/packages/adapters-environment-docker/src/index.ts +439 -0
  72. package/packages/adapters-environment-docker/src/manifest.ts +65 -0
  73. package/packages/adapters-environment-docker/tsconfig.build.json +15 -0
  74. package/packages/adapters-environment-docker/tsconfig.json +16 -0
  75. package/packages/adapters-environment-docker/tsup.config.ts +9 -0
  76. package/packages/adapters-eventbus-cache/README.md +242 -0
  77. package/packages/adapters-eventbus-cache/eslint.config.js +27 -0
  78. package/packages/adapters-eventbus-cache/package.json +46 -0
  79. package/packages/adapters-eventbus-cache/src/index.test.ts +235 -0
  80. package/packages/adapters-eventbus-cache/src/index.ts +215 -0
  81. package/packages/adapters-eventbus-cache/src/manifest.ts +50 -0
  82. package/packages/adapters-eventbus-cache/src/types.ts +58 -0
  83. package/packages/adapters-eventbus-cache/tsconfig.build.json +15 -0
  84. package/packages/adapters-eventbus-cache/tsconfig.json +9 -0
  85. package/packages/adapters-eventbus-cache/tsup.config.ts +9 -0
  86. package/packages/adapters-fs/README.md +171 -0
  87. package/packages/adapters-fs/allowed.txt +1 -0
  88. package/packages/adapters-fs/conflict.txt +1 -0
  89. package/packages/adapters-fs/dest.txt +1 -0
  90. package/packages/adapters-fs/eslint.config.js +27 -0
  91. package/packages/adapters-fs/exists.txt +1 -0
  92. package/packages/adapters-fs/not-allowed.txt +1 -0
  93. package/packages/adapters-fs/other.txt +1 -0
  94. package/packages/adapters-fs/package.json +55 -0
  95. package/packages/adapters-fs/public/file1.txt +1 -0
  96. package/packages/adapters-fs/public/file2.txt +1 -0
  97. package/packages/adapters-fs/secret.txt +1 -0
  98. package/packages/adapters-fs/secrets/key.txt +1 -0
  99. package/packages/adapters-fs/src/index.test.ts +243 -0
  100. package/packages/adapters-fs/src/index.ts +258 -0
  101. package/packages/adapters-fs/src/manifest.ts +35 -0
  102. package/packages/adapters-fs/src/secure-storage.test.ts +380 -0
  103. package/packages/adapters-fs/src/secure-storage.ts +268 -0
  104. package/packages/adapters-fs/test.json +1 -0
  105. package/packages/adapters-fs/test.txt +1 -0
  106. package/packages/adapters-fs/test.xyz +1 -0
  107. package/packages/adapters-fs/test1.txt +1 -0
  108. package/packages/adapters-fs/test2.txt +1 -0
  109. package/packages/adapters-fs/tsconfig.build.json +15 -0
  110. package/packages/adapters-fs/tsconfig.json +9 -0
  111. package/packages/adapters-fs/tsup.config.ts +8 -0
  112. package/packages/adapters-fs/vitest.config.ts +19 -0
  113. package/packages/adapters-log-ringbuffer/README.md +228 -0
  114. package/packages/adapters-log-ringbuffer/eslint.config.js +27 -0
  115. package/packages/adapters-log-ringbuffer/package.json +47 -0
  116. package/packages/adapters-log-ringbuffer/src/__tests__/ring-buffer.test.ts +450 -0
  117. package/packages/adapters-log-ringbuffer/src/index.ts +212 -0
  118. package/packages/adapters-log-ringbuffer/src/manifest.ts +30 -0
  119. package/packages/adapters-log-ringbuffer/tsconfig.build.json +15 -0
  120. package/packages/adapters-log-ringbuffer/tsconfig.json +9 -0
  121. package/packages/adapters-log-ringbuffer/tsup.config.ts +9 -0
  122. package/packages/adapters-log-ringbuffer/vitest.config.ts +14 -0
  123. package/packages/adapters-log-sqlite/README.md +396 -0
  124. package/packages/adapters-log-sqlite/eslint.config.js +27 -0
  125. package/packages/adapters-log-sqlite/package.json +49 -0
  126. package/packages/adapters-log-sqlite/src/__tests__/log-persistence.test.ts +718 -0
  127. package/packages/adapters-log-sqlite/src/index.ts +1068 -0
  128. package/packages/adapters-log-sqlite/src/manifest.ts +36 -0
  129. package/packages/adapters-log-sqlite/src/schema.sql +46 -0
  130. package/packages/adapters-log-sqlite/tsconfig.build.json +15 -0
  131. package/packages/adapters-log-sqlite/tsconfig.json +9 -0
  132. package/packages/adapters-log-sqlite/tsup.config.ts +9 -0
  133. package/packages/adapters-log-sqlite/vitest.config.ts +15 -0
  134. package/packages/adapters-mongodb/README.md +147 -0
  135. package/packages/adapters-mongodb/eslint.config.js +27 -0
  136. package/packages/adapters-mongodb/package.json +53 -0
  137. package/packages/adapters-mongodb/src/index.ts +428 -0
  138. package/packages/adapters-mongodb/src/manifest.ts +45 -0
  139. package/packages/adapters-mongodb/src/secure-document.ts +231 -0
  140. package/packages/adapters-mongodb/tsconfig.build.json +15 -0
  141. package/packages/adapters-mongodb/tsconfig.json +9 -0
  142. package/packages/adapters-mongodb/tsup.config.ts +8 -0
  143. package/packages/adapters-openai/README.md +151 -0
  144. package/packages/adapters-openai/embeddings.ts +37 -0
  145. package/packages/adapters-openai/eslint.config.js +26 -0
  146. package/packages/adapters-openai/index.ts +22 -0
  147. package/packages/adapters-openai/package.json +57 -0
  148. package/packages/adapters-openai/src/embeddings-manifest.ts +45 -0
  149. package/packages/adapters-openai/src/embeddings.ts +104 -0
  150. package/packages/adapters-openai/src/index.ts +13 -0
  151. package/packages/adapters-openai/src/llm.ts +304 -0
  152. package/packages/adapters-openai/src/manifest.ts +47 -0
  153. package/packages/adapters-openai/tsconfig.build.json +15 -0
  154. package/packages/adapters-openai/tsconfig.json +9 -0
  155. package/packages/adapters-openai/tsup.config.ts +8 -0
  156. package/packages/adapters-pino/README.md +152 -0
  157. package/packages/adapters-pino/eslint.config.js +27 -0
  158. package/packages/adapters-pino/package.json +49 -0
  159. package/packages/adapters-pino/src/index.test.ts +44 -0
  160. package/packages/adapters-pino/src/index.ts +322 -0
  161. package/packages/adapters-pino/src/log-ring-buffer.ts +142 -0
  162. package/packages/adapters-pino/src/manifest.ts +49 -0
  163. package/packages/adapters-pino/tsconfig.build.json +15 -0
  164. package/packages/adapters-pino/tsconfig.json +9 -0
  165. package/packages/adapters-pino/tsup.config.ts +9 -0
  166. package/packages/adapters-pino-http/README.md +141 -0
  167. package/packages/adapters-pino-http/eslint.config.js +27 -0
  168. package/packages/adapters-pino-http/package.json +46 -0
  169. package/packages/adapters-pino-http/src/index.ts +229 -0
  170. package/packages/adapters-pino-http/tsconfig.build.json +15 -0
  171. package/packages/adapters-pino-http/tsconfig.json +9 -0
  172. package/packages/adapters-pino-http/tsup.config.ts +9 -0
  173. package/packages/adapters-qdrant/README.md +166 -0
  174. package/packages/adapters-qdrant/eslint.config.js +27 -0
  175. package/packages/adapters-qdrant/package.json +49 -0
  176. package/packages/adapters-qdrant/src/index.ts +490 -0
  177. package/packages/adapters-qdrant/src/manifest.ts +54 -0
  178. package/packages/adapters-qdrant/src/retry.ts +204 -0
  179. package/packages/adapters-qdrant/tsconfig.build.json +15 -0
  180. package/packages/adapters-qdrant/tsconfig.json +9 -0
  181. package/packages/adapters-qdrant/tsup.config.ts +9 -0
  182. package/packages/adapters-redis/README.md +159 -0
  183. package/packages/adapters-redis/eslint.config.js +27 -0
  184. package/packages/adapters-redis/package.json +49 -0
  185. package/packages/adapters-redis/src/index.ts +164 -0
  186. package/packages/adapters-redis/src/manifest.ts +49 -0
  187. package/packages/adapters-redis/tsconfig.build.json +15 -0
  188. package/packages/adapters-redis/tsconfig.json +9 -0
  189. package/packages/adapters-redis/tsup.config.ts +9 -0
  190. package/packages/adapters-snapshot-localfs/README.md +10 -0
  191. package/packages/adapters-snapshot-localfs/eslint.config.js +2 -0
  192. package/packages/adapters-snapshot-localfs/package.json +46 -0
  193. package/packages/adapters-snapshot-localfs/src/index.test.ts +40 -0
  194. package/packages/adapters-snapshot-localfs/src/index.ts +292 -0
  195. package/packages/adapters-snapshot-localfs/src/manifest.ts +32 -0
  196. package/packages/adapters-snapshot-localfs/tsconfig.build.json +15 -0
  197. package/packages/adapters-snapshot-localfs/tsconfig.json +16 -0
  198. package/packages/adapters-snapshot-localfs/tsup.config.ts +11 -0
  199. package/packages/adapters-sqlite/README.md +163 -0
  200. package/packages/adapters-sqlite/eslint.config.js +27 -0
  201. package/packages/adapters-sqlite/package.json +54 -0
  202. package/packages/adapters-sqlite/src/index.test.ts +245 -0
  203. package/packages/adapters-sqlite/src/index.ts +382 -0
  204. package/packages/adapters-sqlite/src/manifest.ts +47 -0
  205. package/packages/adapters-sqlite/src/secure-sql.test.ts +290 -0
  206. package/packages/adapters-sqlite/src/secure-sql.ts +281 -0
  207. package/packages/adapters-sqlite/tsconfig.build.json +15 -0
  208. package/packages/adapters-sqlite/tsconfig.json +9 -0
  209. package/packages/adapters-sqlite/tsup.config.ts +8 -0
  210. package/packages/adapters-sqlite/vitest.config.ts +19 -0
  211. package/packages/adapters-transport/README.md +170 -0
  212. package/packages/adapters-transport/eslint.config.js +27 -0
  213. package/packages/adapters-transport/package.json +49 -0
  214. package/packages/adapters-transport/src/__tests__/unix-socket-server.test.ts +550 -0
  215. package/packages/adapters-transport/src/index.ts +101 -0
  216. package/packages/adapters-transport/src/ipc-transport.ts +228 -0
  217. package/packages/adapters-transport/src/transport.ts +224 -0
  218. package/packages/adapters-transport/src/types.ts +92 -0
  219. package/packages/adapters-transport/src/unix-socket-server.ts +193 -0
  220. package/packages/adapters-transport/src/unix-socket-transport.ts +280 -0
  221. package/packages/adapters-transport/tsconfig.build.json +15 -0
  222. package/packages/adapters-transport/tsconfig.json +9 -0
  223. package/packages/adapters-transport/tsup.config.ts +9 -0
  224. package/packages/adapters-vibeproxy/README.md +159 -0
  225. package/packages/adapters-vibeproxy/eslint.config.js +27 -0
  226. package/packages/adapters-vibeproxy/package.json +51 -0
  227. package/packages/adapters-vibeproxy/src/index.ts +13 -0
  228. package/packages/adapters-vibeproxy/src/llm.ts +437 -0
  229. package/packages/adapters-vibeproxy/src/manifest.ts +51 -0
  230. package/packages/adapters-vibeproxy/tsconfig.build.json +15 -0
  231. package/packages/adapters-vibeproxy/tsconfig.json +9 -0
  232. package/packages/adapters-vibeproxy/tsup.config.ts +8 -0
  233. package/packages/adapters-workspace-agent/package.json +46 -0
  234. package/packages/adapters-workspace-agent/src/__tests__/adapter.test.ts +212 -0
  235. package/packages/adapters-workspace-agent/src/index.ts +220 -0
  236. package/packages/adapters-workspace-agent/src/manifest.ts +36 -0
  237. package/packages/adapters-workspace-agent/tsconfig.build.json +15 -0
  238. package/packages/adapters-workspace-agent/tsconfig.json +16 -0
  239. package/packages/adapters-workspace-agent/tsup.config.ts +11 -0
  240. package/packages/adapters-workspace-localfs/README.md +9 -0
  241. package/packages/adapters-workspace-localfs/eslint.config.js +2 -0
  242. package/packages/adapters-workspace-localfs/package.json +46 -0
  243. package/packages/adapters-workspace-localfs/src/index.test.ts +27 -0
  244. package/packages/adapters-workspace-localfs/src/index.ts +172 -0
  245. package/packages/adapters-workspace-localfs/src/manifest.ts +32 -0
  246. package/packages/adapters-workspace-localfs/tsconfig.build.json +15 -0
  247. package/packages/adapters-workspace-localfs/tsconfig.json +16 -0
  248. package/packages/adapters-workspace-localfs/tsup.config.ts +11 -0
  249. package/packages/adapters-workspace-worktree/README.md +9 -0
  250. package/packages/adapters-workspace-worktree/eslint.config.js +2 -0
  251. package/packages/adapters-workspace-worktree/package.json +46 -0
  252. package/packages/adapters-workspace-worktree/src/index.test.ts +38 -0
  253. package/packages/adapters-workspace-worktree/src/index.ts +245 -0
  254. package/packages/adapters-workspace-worktree/src/manifest.ts +38 -0
  255. package/packages/adapters-workspace-worktree/tsconfig.build.json +15 -0
  256. package/packages/adapters-workspace-worktree/tsconfig.json +16 -0
  257. package/packages/adapters-workspace-worktree/tsup.config.ts +11 -0
  258. package/pnpm-workspace.yaml +2800 -0
  259. package/prettierrc.json +1 -0
  260. package/scripts/devkit-sync.mjs +37 -0
  261. package/scripts/hooks/post-push +9 -0
  262. package/scripts/hooks/pre-commit +9 -0
  263. package/scripts/hooks/pre-push +9 -0
  264. package/test-integration.ts +242 -0
  265. package/test.txt +1 -0
  266. package/tsconfig.base.json +6 -0
  267. package/tsconfig.build.json +15 -0
  268. package/tsconfig.json +9 -0
  269. package/tsconfig.paths.json +26 -0
  270. package/tsconfig.tools.json +17 -0
  271. package/tsup.config.bin.ts +34 -0
  272. package/tsup.config.cli.ts +41 -0
  273. package/tsup.config.dual.ts +46 -0
  274. package/tsup.config.ts +36 -0
  275. package/tsup.external.json +103 -0
  276. package/vitest.config.ts +2 -0
@@ -0,0 +1,228 @@
1
+ /**
2
+ * @module @kb-labs/core-runtime/transport
3
+ * IPC transport implementation using process.send/process.on('message').
4
+ *
5
+ * This transport is used for communication between parent (CLI) and
6
+ * child (sandbox worker) processes created by child_process.fork().
7
+ *
8
+ * Features:
9
+ * - Thread-safe: Uses request IDs to match responses with requests
10
+ * - Timeout enforcement: Rejects calls that exceed timeout
11
+ * - Error propagation: Deserializes errors from parent
12
+ * - Cleanup: Properly removes listeners on close()
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { IPCTransport } from '@kb-labs/core-runtime/transport';
17
+ *
18
+ * // In child process
19
+ * const transport = new IPCTransport({ timeout: 10000 });
20
+ *
21
+ * const response = await transport.send({
22
+ * type: 'adapter:call',
23
+ * requestId: 'uuid-123',
24
+ * adapter: 'vectorStore',
25
+ * method: 'search',
26
+ * args: [[0.1, 0.2, 0.3], 10],
27
+ * });
28
+ *
29
+ * await transport.close();
30
+ * ```
31
+ */
32
+
33
+ import type { AdapterCall, AdapterResponse } from "./types.js";
34
+ import { isAdapterResponse } from "./types.js";
35
+ import {
36
+ type ITransport,
37
+ type TransportConfig,
38
+ type PendingRequest,
39
+ TransportError,
40
+ TimeoutError,
41
+ } from "./transport.js";
42
+
43
+ // Re-export TransportConfig for convenience
44
+ export type { TransportConfig };
45
+
46
+ /**
47
+ * IPC transport using process.send/process.on('message').
48
+ *
49
+ * Requires:
50
+ * - process.send() must be available (running as forked child)
51
+ * - Parent process must listen for messages and respond
52
+ *
53
+ * Thread-safety:
54
+ * - Uses Map for pending requests (safe for concurrent access in single thread)
55
+ * - Uses request IDs to match responses (no race conditions)
56
+ *
57
+ * Limitations:
58
+ * - Only works in forked child processes (not worker threads)
59
+ * - Parent must implement IPCServer to handle calls
60
+ * - Message size limited by Node.js IPC buffer (typically 1MB)
61
+ */
62
+ export class IPCTransport implements ITransport {
63
+ private pending = new Map<string, PendingRequest>();
64
+ private messageHandler: (msg: unknown) => void;
65
+ private closed = false;
66
+
67
+ constructor(private config: TransportConfig = {}) {
68
+ // Bind handler to preserve 'this' context
69
+ this.messageHandler = this.handleMessage.bind(this);
70
+
71
+ // Listen for responses from parent
72
+ process.on("message", this.messageHandler);
73
+
74
+ // Check if IPC channel is available
75
+ if (!process.send) {
76
+ throw new TransportError(
77
+ "No IPC channel available. IPCTransport can only be used in forked child processes.",
78
+ );
79
+ }
80
+ }
81
+
82
+ async send(call: AdapterCall): Promise<AdapterResponse> {
83
+ if (this.closed) {
84
+ throw new TransportError("Transport is closed");
85
+ }
86
+
87
+ // Determine timeout (call-specific > config default > 30s)
88
+ const timeout = call.timeout ?? this.config.timeout ?? 30000;
89
+
90
+ return new Promise((resolve, reject) => {
91
+ // Create timeout timer
92
+ const timer = setTimeout(() => {
93
+ this.pending.delete(call.requestId);
94
+ reject(
95
+ new TimeoutError(
96
+ `Adapter call timed out after ${timeout}ms`,
97
+ timeout,
98
+ ),
99
+ );
100
+ }, timeout);
101
+
102
+ // Store pending request
103
+ this.pending.set(call.requestId, { resolve, reject, timer });
104
+
105
+ // Try to send message, with backpressure handling
106
+ this.trySendWithBackpressure(call, 0).catch((error) => {
107
+ const pending = this.pending.get(call.requestId);
108
+ if (pending) {
109
+ clearTimeout(pending.timer);
110
+ this.pending.delete(call.requestId);
111
+ reject(error);
112
+ }
113
+ });
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Try to send IPC message with backpressure handling.
119
+ * If backpressure detected, waits and retries with exponential backoff.
120
+ *
121
+ * Note: Node.js IPC 'drain' event is unreliable, so we use sleep-based backoff instead.
122
+ *
123
+ * @param call - Adapter call to send
124
+ * @param retryCount - Current retry attempt (for exponential backoff)
125
+ */
126
+ private async trySendWithBackpressure(
127
+ call: AdapterCall,
128
+ retryCount: number,
129
+ ): Promise<void> {
130
+ const maxRetries = 20;
131
+ const baseDelayMs = 50;
132
+
133
+ try {
134
+ // process.send() is guaranteed to exist (checked in constructor)
135
+ // Returns false if backpressure detected (buffer full)
136
+ const sent = process.send!(call);
137
+
138
+ if (sent) {
139
+ // Message sent successfully
140
+ return;
141
+ }
142
+
143
+ // Backpressure detected
144
+ if (retryCount >= maxRetries) {
145
+ throw new TransportError(
146
+ `Failed to send IPC message after ${maxRetries} retries: persistent backpressure`,
147
+ );
148
+ }
149
+
150
+ // Wait with exponential backoff (sleep instead of drain event)
151
+ const delay = Math.min(baseDelayMs * Math.pow(1.5, retryCount), 5000); // Cap at 5s
152
+ await new Promise((resolve) => {
153
+ setTimeout(resolve, delay);
154
+ });
155
+
156
+ // Retry sending
157
+ return this.trySendWithBackpressure(call, retryCount + 1);
158
+ } catch (error) {
159
+ // Synchronous error (e.g., message too large, serialization error, channel closed)
160
+ if (error instanceof TransportError) {
161
+ throw error;
162
+ }
163
+ throw new TransportError(
164
+ `Failed to send IPC message: ${error}`,
165
+ error as Error,
166
+ );
167
+ }
168
+ }
169
+
170
+ private handleMessage(msg: unknown) {
171
+ // Ignore non-response messages
172
+ if (!isAdapterResponse(msg)) {
173
+ return;
174
+ }
175
+
176
+ // Find pending request
177
+ const pending = this.pending.get(msg.requestId);
178
+ if (!pending) {
179
+ // Response for unknown request (may have timed out)
180
+ return;
181
+ }
182
+
183
+ // Clear timeout and remove from pending
184
+ clearTimeout(pending.timer);
185
+ this.pending.delete(msg.requestId);
186
+
187
+ // Resolve with response (caller will check for error field)
188
+ pending.resolve(msg);
189
+ }
190
+
191
+ async close(): Promise<void> {
192
+ if (this.closed) {
193
+ return;
194
+ }
195
+
196
+ this.closed = true;
197
+
198
+ // Remove message listener
199
+ process.off("message", this.messageHandler);
200
+
201
+ // Reject all pending requests
202
+ for (const [_requestId, pending] of this.pending) {
203
+ clearTimeout(pending.timer);
204
+ pending.reject(new TransportError("Transport closed"));
205
+ }
206
+ this.pending.clear();
207
+ }
208
+
209
+ isClosed(): boolean {
210
+ return this.closed;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Create IPCTransport with default configuration.
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * import { createIPCTransport } from '@kb-labs/core-runtime/transport';
220
+ *
221
+ * const transport = createIPCTransport();
222
+ * // Use transport...
223
+ * await transport.close();
224
+ * ```
225
+ */
226
+ export function createIPCTransport(config?: TransportConfig): IPCTransport {
227
+ return new IPCTransport(config);
228
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * @module @kb-labs/core-runtime/transport
3
+ * Abstract transport layer for cross-process adapter communication.
4
+ *
5
+ * Provides a transport-agnostic interface for sending adapter calls
6
+ * between parent and child processes. Implementations can use:
7
+ * - IPC (process.send/process.on('message'))
8
+ * - HTTP REST API
9
+ * - Docker exec
10
+ * - gRPC
11
+ * - WebSockets
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { ITransport, IPCTransport } from '@kb-labs/core-runtime/transport';
16
+ *
17
+ * const transport: ITransport = new IPCTransport();
18
+ * const response = await transport.send({
19
+ * type: 'adapter:call',
20
+ * requestId: 'uuid-123',
21
+ * adapter: 'vectorStore',
22
+ * method: 'search',
23
+ * args: [[0.1, 0.2, 0.3], 10],
24
+ * });
25
+ * ```
26
+ */
27
+
28
+ import type { AdapterCall, AdapterResponse } from "./types.js";
29
+
30
+ /**
31
+ * Transport configuration options.
32
+ */
33
+ export interface TransportConfig {
34
+ /**
35
+ * Default timeout for adapter calls in milliseconds.
36
+ * @default 30000 (30 seconds)
37
+ */
38
+ timeout?: number;
39
+
40
+ /**
41
+ * Number of retry attempts for transient errors.
42
+ * @default 0 (no retries)
43
+ */
44
+ retries?: number;
45
+
46
+ /**
47
+ * Delay between retry attempts in milliseconds.
48
+ * @default 1000 (1 second)
49
+ */
50
+ retryDelay?: number;
51
+
52
+ /**
53
+ * Exponential backoff multiplier for retries.
54
+ * @default 2 (doubles delay each retry)
55
+ */
56
+ backoffMultiplier?: number;
57
+ }
58
+
59
+ /**
60
+ * Abstract transport for sending adapter calls.
61
+ *
62
+ * Implementations must handle:
63
+ * - Reliable message delivery
64
+ * - Timeout enforcement
65
+ * - Error propagation
66
+ * - Resource cleanup
67
+ *
68
+ * Thread-safety: Implementations must be safe to call concurrently
69
+ * from multiple async contexts.
70
+ */
71
+ export interface ITransport {
72
+ /**
73
+ * Send adapter call and await response.
74
+ *
75
+ * @param call - Adapter method call to send
76
+ * @returns Response with result or error
77
+ * @throws TransportError if communication fails
78
+ * @throws TimeoutError if timeout exceeded
79
+ * @throws SerializationError if message cannot be serialized
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const call: AdapterCall = {
84
+ * type: 'adapter:call',
85
+ * requestId: 'uuid-123',
86
+ * adapter: 'vectorStore',
87
+ * method: 'search',
88
+ * args: [[0.1, 0.2, 0.3], 10],
89
+ * timeout: 5000,
90
+ * };
91
+ *
92
+ * const response = await transport.send(call);
93
+ * if (response.error) {
94
+ * throw deserialize(response.error);
95
+ * }
96
+ * return deserialize(response.result);
97
+ * ```
98
+ */
99
+ send(call: AdapterCall): Promise<AdapterResponse>;
100
+
101
+ /**
102
+ * Close transport and cleanup resources.
103
+ *
104
+ * After close():
105
+ * - No new calls can be sent
106
+ * - Pending calls are rejected with TransportError
107
+ * - Listeners/connections are cleaned up
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * await transport.close();
112
+ * // All pending calls rejected
113
+ * // transport.send() will throw TransportError
114
+ * ```
115
+ */
116
+ close(): Promise<void>;
117
+
118
+ /**
119
+ * Check if transport is closed.
120
+ */
121
+ isClosed(): boolean;
122
+ }
123
+
124
+ /**
125
+ * Base error for transport failures.
126
+ */
127
+ export class TransportError extends Error {
128
+ public override readonly cause?: Error;
129
+
130
+ constructor(message: string, cause?: Error) {
131
+ super(message);
132
+ this.name = "TransportError";
133
+ this.cause = cause;
134
+ if (cause) {
135
+ this.stack = `${this.stack}\nCaused by: ${cause.stack}`;
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Error thrown when adapter call times out.
142
+ */
143
+ export class TimeoutError extends TransportError {
144
+ public readonly timeoutMs: number;
145
+
146
+ constructor(message: string, timeoutMs: number) {
147
+ super(message);
148
+ this.name = "TimeoutError";
149
+ this.timeoutMs = timeoutMs;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Error thrown when circuit breaker is open.
155
+ */
156
+ export class CircuitOpenError extends TransportError {
157
+ constructor(message: string) {
158
+ super(message);
159
+ this.name = "CircuitOpenError";
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Check if error is retryable (transient failure).
165
+ *
166
+ * Retryable errors:
167
+ * - Network timeouts (TimeoutError)
168
+ * - Connection errors (ECONNRESET, ECONNREFUSED, ETIMEDOUT)
169
+ * - Temporary server errors (503 Service Unavailable)
170
+ *
171
+ * Non-retryable errors:
172
+ * - Invalid requests (400 Bad Request)
173
+ * - Authentication failures (401, 403)
174
+ * - Not found (404)
175
+ * - Application errors from adapter (e.g., VectorStoreError)
176
+ *
177
+ * @param error - Error to check
178
+ * @returns true if error should be retried
179
+ */
180
+ export function isRetryableError(error: Error): boolean {
181
+ // Timeout errors are retryable
182
+ if (error instanceof TimeoutError) {
183
+ return true;
184
+ }
185
+
186
+ // Circuit breaker open is NOT retryable (wait for half-open state)
187
+ if (error instanceof CircuitOpenError) {
188
+ return false;
189
+ }
190
+
191
+ // Check error code for network errors
192
+ const code = (error as any).code;
193
+ if (code) {
194
+ const retryableCodes = [
195
+ "ECONNRESET", // Connection reset
196
+ "ECONNREFUSED", // Connection refused
197
+ "ETIMEDOUT", // Operation timed out
198
+ "ENOTFOUND", // DNS lookup failed
199
+ "EAI_AGAIN", // DNS temporary failure
200
+ ];
201
+ return retryableCodes.includes(code);
202
+ }
203
+
204
+ // Check HTTP status for server errors
205
+ const status = (error as any).status || (error as any).statusCode;
206
+ if (status) {
207
+ // 503 Service Unavailable is retryable
208
+ // 429 Too Many Requests is retryable
209
+ return status === 503 || status === 429;
210
+ }
211
+
212
+ // Default: not retryable
213
+ return false;
214
+ }
215
+
216
+ /**
217
+ * Internal pending request tracking.
218
+ * Used by transport implementations to match responses with requests.
219
+ */
220
+ export interface PendingRequest {
221
+ resolve: (response: AdapterResponse) => void;
222
+ reject: (error: Error) => void;
223
+ timer: NodeJS.Timeout;
224
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Core types for transport layer.
3
+ * Extracted from @kb-labs/core-platform to avoid circular dependencies.
4
+ */
5
+
6
+ export interface SerializableBuffer {
7
+ __type: "Buffer";
8
+ data: string;
9
+ }
10
+
11
+ export interface SerializableDate {
12
+ __type: "Date";
13
+ iso: string;
14
+ }
15
+
16
+ export interface SerializableError {
17
+ __type: "Error";
18
+ name: string;
19
+ message: string;
20
+ stack?: string;
21
+ code?: string;
22
+ }
23
+
24
+ export type SerializableValue =
25
+ | null
26
+ | boolean
27
+ | number
28
+ | string
29
+ | SerializableBuffer
30
+ | SerializableDate
31
+ | SerializableError
32
+ | SerializableArray
33
+ | SerializableObject;
34
+
35
+ export type SerializableArray = SerializableValue[];
36
+ export type SerializableObject = { [key: string]: SerializableValue };
37
+
38
+ export type AdapterType =
39
+ | "vectorStore"
40
+ | "cache"
41
+ | "llm"
42
+ | "embeddings"
43
+ | "storage"
44
+ | "logger"
45
+ | "analytics"
46
+ | "eventBus"
47
+ | "invoke"
48
+ | "artifacts";
49
+
50
+ export interface AdapterCallContext {
51
+ traceId?: string;
52
+ sessionId?: string;
53
+ pluginId?: string;
54
+ workspaceId?: string;
55
+ tenantId?: string;
56
+ permissions?: {
57
+ adapters?: string[];
58
+ storagePaths?: string[];
59
+ networkHosts?: string[];
60
+ };
61
+ }
62
+
63
+ export const IPC_PROTOCOL_VERSION = 2;
64
+
65
+ export interface AdapterCall {
66
+ version: number;
67
+ type: "adapter:call";
68
+ requestId: string;
69
+ adapter: AdapterType;
70
+ method: string;
71
+ args: SerializableValue[];
72
+ timeout?: number;
73
+ context?: AdapterCallContext;
74
+ }
75
+
76
+ export interface AdapterResponse {
77
+ type: "adapter:response";
78
+ requestId: string;
79
+ result?: SerializableValue;
80
+ error?: SerializableError;
81
+ }
82
+
83
+ export function isAdapterResponse(msg: unknown): msg is AdapterResponse {
84
+ return (
85
+ typeof msg === "object" &&
86
+ msg !== null &&
87
+ "type" in msg &&
88
+ (msg as any).type === "adapter:response" &&
89
+ "requestId" in msg &&
90
+ typeof (msg as any).requestId === "string"
91
+ );
92
+ }