@pentatonic-ai/ai-agent-sdk 0.5.11 → 0.7.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 (119) hide show
  1. package/README.md +345 -174
  2. package/bin/__tests__/callback-server.test.js +70 -0
  3. package/bin/__tests__/credentials.test.js +58 -0
  4. package/bin/__tests__/login.test.js +210 -0
  5. package/bin/__tests__/pkce.test.js +39 -0
  6. package/bin/__tests__/whoami.test.js +77 -0
  7. package/bin/cli.js +109 -440
  8. package/bin/commands/config.js +251 -0
  9. package/bin/commands/login.js +219 -0
  10. package/bin/commands/whoami.js +41 -0
  11. package/bin/lib/callback-server.js +137 -0
  12. package/bin/lib/credentials.js +100 -0
  13. package/bin/lib/pkce.js +26 -0
  14. package/package.json +4 -2
  15. package/packages/doctor/__tests__/detect.test.js +2 -6
  16. package/packages/doctor/src/checks/local-memory.js +164 -196
  17. package/packages/doctor/src/detect.js +11 -3
  18. package/packages/memory/src/__tests__/corpus-chunkers.test.js +143 -0
  19. package/packages/memory/src/__tests__/corpus-discover.test.js +175 -0
  20. package/packages/memory/src/__tests__/corpus-ingest.test.js +236 -0
  21. package/packages/memory/src/__tests__/corpus-signatures.test.js +175 -0
  22. package/packages/memory/src/__tests__/corpus-state.test.js +161 -0
  23. package/packages/memory/src/__tests__/ingest-corpus-opts.test.js +129 -0
  24. package/packages/memory/src/__tests__/search-kind.test.js +108 -0
  25. package/packages/memory/src/corpus/adapters.js +398 -0
  26. package/packages/memory/src/corpus/chunkers.js +328 -0
  27. package/packages/memory/src/corpus/cli.js +613 -0
  28. package/packages/memory/src/corpus/discover.js +379 -0
  29. package/packages/memory/src/corpus/index.js +68 -0
  30. package/packages/memory/src/corpus/ingest.js +356 -0
  31. package/packages/memory/src/corpus/signatures.js +280 -0
  32. package/packages/memory/src/corpus/state.js +134 -0
  33. package/packages/memory/src/index.js +18 -0
  34. package/packages/memory/src/ingest.js +20 -11
  35. package/packages/memory/src/openclaw/index.js +39 -1
  36. package/packages/memory/src/search.js +30 -7
  37. package/packages/memory-engine/.env.example +13 -0
  38. package/packages/memory-engine/README.md +131 -0
  39. package/packages/memory-engine/bench/README.md +99 -0
  40. package/packages/memory-engine/bench/scorecards-engine/agent-coding__pentatonic-baseline__20260427-142523.json +1115 -0
  41. package/packages/memory-engine/bench/scorecards-engine/chat-recall__pentatonic-baseline__20260427-142648.json +819 -0
  42. package/packages/memory-engine/bench/scorecards-engine/circular-economy__pentatonic-baseline__20260427-142757.json +1278 -0
  43. package/packages/memory-engine/bench/scorecards-engine/customer-support__pentatonic-baseline__20260427-142900.json +1018 -0
  44. package/packages/memory-engine/bench/scorecards-engine/marketplace-ops__pentatonic-baseline__20260427-142957.json +1038 -0
  45. package/packages/memory-engine/bench/scorecards-engine/product-catalogue__pentatonic-baseline__20260427-143122.json +961 -0
  46. package/packages/memory-engine/bench/scorecards-engine-via-docker/agent-coding__pentatonic-memory__20260427-161812.json +1115 -0
  47. package/packages/memory-engine/bench/scorecards-engine-via-docker/chat-recall__pentatonic-memory__20260427-161701.json +819 -0
  48. package/packages/memory-engine/bench/scorecards-engine-via-docker/circular-economy__pentatonic-memory__20260427-161713.json +1278 -0
  49. package/packages/memory-engine/bench/scorecards-engine-via-docker/customer-support__pentatonic-memory__20260427-161723.json +1018 -0
  50. package/packages/memory-engine/bench/scorecards-engine-via-docker/marketplace-ops__pentatonic-memory__20260427-161732.json +1038 -0
  51. package/packages/memory-engine/bench/scorecards-engine-via-docker/product-catalogue__pentatonic-memory__20260427-161741.json +937 -0
  52. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/agent-coding__pentatonic-memory__20260427-184718.json +1115 -0
  53. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/chat-recall__pentatonic-memory__20260427-184614.json +819 -0
  54. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/circular-economy__pentatonic-memory__20260427-184809.json +1278 -0
  55. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/customer-support__pentatonic-memory__20260427-184854.json +1018 -0
  56. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/marketplace-ops__pentatonic-memory__20260427-184929.json +1038 -0
  57. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/product-catalogue__pentatonic-memory__20260427-185015.json +961 -0
  58. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/agent-coding__pentatonic-memory__20260427-175252.json +1115 -0
  59. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/chat-recall__pentatonic-memory__20260427-175312.json +819 -0
  60. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/circular-economy__pentatonic-memory__20260427-175335.json +1278 -0
  61. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/customer-support__pentatonic-memory__20260427-175355.json +1018 -0
  62. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/marketplace-ops__pentatonic-memory__20260427-175413.json +1038 -0
  63. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/product-catalogue__pentatonic-memory__20260427-175430.json +883 -0
  64. package/packages/memory-engine/bench/scorecards-engine-via-shim/agent-coding__pentatonic-memory__20260427-155409.json +1115 -0
  65. package/packages/memory-engine/bench/scorecards-engine-via-shim/chat-recall__pentatonic-memory__20260427-155421.json +819 -0
  66. package/packages/memory-engine/bench/scorecards-engine-via-shim/circular-economy__pentatonic-memory__20260427-155433.json +1278 -0
  67. package/packages/memory-engine/bench/scorecards-engine-via-shim/customer-support__pentatonic-memory__20260427-155443.json +1018 -0
  68. package/packages/memory-engine/bench/scorecards-engine-via-shim/marketplace-ops__pentatonic-memory__20260427-155453.json +1038 -0
  69. package/packages/memory-engine/bench/scorecards-engine-via-shim/product-catalogue__pentatonic-memory__20260427-155503.json +937 -0
  70. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory-latest__20260427-145103.json +1115 -0
  71. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory__20260427-144909.json +1115 -0
  72. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory-latest__20260427-145153.json +819 -0
  73. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory__20260427-145120.json +542 -0
  74. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory-latest__20260427-145313.json +1278 -0
  75. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory__20260427-145207.json +894 -0
  76. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory-latest__20260427-145412.json +1018 -0
  77. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory__20260427-145327.json +680 -0
  78. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory-latest__20260427-145517.json +1038 -0
  79. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory__20260427-145422.json +693 -0
  80. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory-latest__20260427-145616.json +961 -0
  81. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory__20260427-145528.json +727 -0
  82. package/packages/memory-engine/compat/Dockerfile +11 -0
  83. package/packages/memory-engine/compat/server.py +680 -0
  84. package/packages/memory-engine/docker-compose.yml +243 -0
  85. package/packages/memory-engine/docs/MIGRATION.md +178 -0
  86. package/packages/memory-engine/docs/RUNBOOK-AWS.md +375 -0
  87. package/packages/memory-engine/docs/why-v05-underperforms.md +138 -0
  88. package/packages/memory-engine/engine/README.md +52 -0
  89. package/packages/memory-engine/engine/l2-hybridrag-proxy.py +1543 -0
  90. package/packages/memory-engine/engine/l5-comms-layer.py +663 -0
  91. package/packages/memory-engine/engine/l6-document-store.py +1018 -0
  92. package/packages/memory-engine/engine/services/l2/Dockerfile +41 -0
  93. package/packages/memory-engine/engine/services/l2/init_databases.py +81 -0
  94. package/packages/memory-engine/engine/services/l2/l2-hybridrag-proxy.py +1543 -0
  95. package/packages/memory-engine/engine/services/l4/Dockerfile +15 -0
  96. package/packages/memory-engine/engine/services/l4/server.py +235 -0
  97. package/packages/memory-engine/engine/services/l5/Dockerfile +9 -0
  98. package/packages/memory-engine/engine/services/l5/l5-comms-layer.py +678 -0
  99. package/packages/memory-engine/engine/services/l6/Dockerfile +11 -0
  100. package/packages/memory-engine/engine/services/l6/l6-document-store.py +1016 -0
  101. package/packages/memory-engine/engine/services/nv-embed/Dockerfile +28 -0
  102. package/packages/memory-engine/engine/services/nv-embed/server.py +152 -0
  103. package/packages/memory-engine/pme_memory/__init__.py +0 -0
  104. package/packages/memory-engine/pme_memory/__main__.py +129 -0
  105. package/packages/memory-engine/pme_memory/artifacts.py +95 -0
  106. package/packages/memory-engine/pme_memory/embed.py +74 -0
  107. package/packages/memory-engine/pme_memory/health.py +36 -0
  108. package/packages/memory-engine/pme_memory/hygiene.py +159 -0
  109. package/packages/memory-engine/pme_memory/indexer.py +200 -0
  110. package/packages/memory-engine/pme_memory/needs.py +55 -0
  111. package/packages/memory-engine/pme_memory/provenance.py +80 -0
  112. package/packages/memory-engine/pme_memory/scoring.py +168 -0
  113. package/packages/memory-engine/pme_memory/search.py +52 -0
  114. package/packages/memory-engine/pme_memory/store.py +86 -0
  115. package/packages/memory-engine/pme_memory/synthesis.py +114 -0
  116. package/packages/memory-engine/pyproject.toml +65 -0
  117. package/packages/memory-engine/scripts/kg-extractor.py +557 -0
  118. package/packages/memory-engine/scripts/kg-preflexor-v2.py +738 -0
  119. package/packages/memory-engine/tests/test_api_contract.sh +57 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * File chunkers for corpus ingest.
3
+ *
4
+ * Each chunker takes a file (path + content + metadata) and returns
5
+ * an array of Chunk objects ready for embedding + memory ingest.
6
+ *
7
+ * { content: string, metadata: { kind, name?, lineRange?, ... } }
8
+ *
9
+ * Strategy by file type:
10
+ * - .md/.mdx/.rst/.txt → heading-aware split with overlap
11
+ * - code (.ts/.js/.py/.go/.rs/.java/.rb/...) → sliding window with
12
+ * line-stable boundaries (split on blank lines, not mid-statement)
13
+ * - .json/.yaml/.yml/.toml → whole file as one chunk if small,
14
+ * otherwise top-level key split
15
+ * - everything else → sliding window
16
+ *
17
+ * Tree-sitter integration for proper AST-aware code chunking is a
18
+ * follow-up (see specs/01 §10). Sliding window with blank-line snapping
19
+ * gives 80% of the value at 5% of the dependency cost.
20
+ */
21
+
22
+ const DEFAULT_CHUNK_TOKEN_TARGET = 800;
23
+ const DEFAULT_CHUNK_OVERLAP_TOKENS = 150;
24
+ const TOKEN_PER_CHAR_HEURISTIC = 0.25; // 4 chars ≈ 1 token
25
+
26
+ const CODE_EXTENSIONS = new Set([
27
+ ".js", ".jsx", ".mjs", ".cjs",
28
+ ".ts", ".tsx",
29
+ ".py", ".pyi",
30
+ ".go",
31
+ ".rs",
32
+ ".java", ".kt", ".kts", ".scala",
33
+ ".rb",
34
+ ".php",
35
+ ".c", ".h", ".cpp", ".hpp", ".cc",
36
+ ".cs",
37
+ ".swift",
38
+ ".sh", ".bash", ".zsh",
39
+ ".sql",
40
+ ".lua",
41
+ ".pl", ".pm",
42
+ ".r",
43
+ ".erl", ".ex", ".exs",
44
+ ".clj", ".cljs",
45
+ ".dart",
46
+ ".groovy",
47
+ ]);
48
+
49
+ const PROSE_EXTENSIONS = new Set([
50
+ ".md", ".mdx", ".markdown",
51
+ ".rst",
52
+ ".txt",
53
+ ".adoc",
54
+ ".org",
55
+ ]);
56
+
57
+ const STRUCTURED_EXTENSIONS = new Set([
58
+ ".json",
59
+ ".yaml", ".yml",
60
+ ".toml",
61
+ ".ini",
62
+ ".env.example", // not real .env (which is hard-excluded)
63
+ ]);
64
+
65
+ function approxTokens(text) {
66
+ return Math.ceil(text.length * TOKEN_PER_CHAR_HEURISTIC);
67
+ }
68
+
69
+ function tokensToChars(tokens) {
70
+ return Math.ceil(tokens / TOKEN_PER_CHAR_HEURISTIC);
71
+ }
72
+
73
+ /**
74
+ * Chunk a file into ingest-ready pieces.
75
+ *
76
+ * @param {object} file - { relPath, content, ext, basename }
77
+ * @param {object} [opts]
78
+ * @param {number} [opts.chunkTokens=800] - Target chunk size in tokens
79
+ * @param {number} [opts.overlapTokens=150] - Overlap between chunks
80
+ * @returns {Array<{content: string, metadata: object}>}
81
+ */
82
+ export function chunkFile(file, opts = {}) {
83
+ const { ext = "", content = "", relPath = "" } = file;
84
+ if (!content.trim()) return [];
85
+
86
+ const chunkTokens = opts.chunkTokens || DEFAULT_CHUNK_TOKEN_TARGET;
87
+ const overlap = opts.overlapTokens ?? DEFAULT_CHUNK_OVERLAP_TOKENS;
88
+
89
+ // Tiny files: one chunk, no splitting
90
+ if (approxTokens(content) <= chunkTokens) {
91
+ return [{
92
+ content,
93
+ metadata: {
94
+ kind: classifyKind(ext),
95
+ chunk_index: 0,
96
+ total_chunks: 1,
97
+ },
98
+ }];
99
+ }
100
+
101
+ if (PROSE_EXTENSIONS.has(ext)) {
102
+ return chunkMarkdown(content, chunkTokens, overlap);
103
+ }
104
+ if (STRUCTURED_EXTENSIONS.has(ext)) {
105
+ return chunkStructured(content, ext, chunkTokens);
106
+ }
107
+ if (CODE_EXTENSIONS.has(ext)) {
108
+ return chunkCode(content, chunkTokens, overlap, ext);
109
+ }
110
+ return chunkSlidingWindow(content, chunkTokens, overlap, "text");
111
+ }
112
+
113
+ function classifyKind(ext) {
114
+ if (PROSE_EXTENSIONS.has(ext)) return "prose";
115
+ if (CODE_EXTENSIONS.has(ext)) return "code";
116
+ if (STRUCTURED_EXTENSIONS.has(ext)) return "config";
117
+ return "text";
118
+ }
119
+
120
+ /**
121
+ * Markdown chunker — splits on h1/h2/h3 headings, keeping each section
122
+ * intact when it fits, otherwise sliding-window inside the section.
123
+ * Each chunk carries its heading path in metadata so retrieval can
124
+ * surface "from README.md > Installation > Local setup".
125
+ */
126
+ function chunkMarkdown(content, chunkTokens, overlap) {
127
+ const lines = content.split("\n");
128
+ const chunks = [];
129
+ const headingStack = []; // [{level, text}]
130
+ let buffer = [];
131
+ let bufferStartLine = 0;
132
+
133
+ function flush() {
134
+ if (!buffer.length) return;
135
+ const text = buffer.join("\n").trim();
136
+ if (!text) {
137
+ buffer = [];
138
+ return;
139
+ }
140
+ if (approxTokens(text) > chunkTokens) {
141
+ // Section too big — fall back to sliding window inside it
142
+ const sub = chunkSlidingWindow(text, chunkTokens, overlap, "prose");
143
+ for (const c of sub) {
144
+ chunks.push({
145
+ content: c.content,
146
+ metadata: {
147
+ kind: "prose",
148
+ heading_path: headingPath(),
149
+ line_start: bufferStartLine + 1,
150
+ chunk_index: chunks.length,
151
+ },
152
+ });
153
+ }
154
+ } else {
155
+ chunks.push({
156
+ content: text,
157
+ metadata: {
158
+ kind: "prose",
159
+ heading_path: headingPath(),
160
+ line_start: bufferStartLine + 1,
161
+ line_end: bufferStartLine + buffer.length,
162
+ chunk_index: chunks.length,
163
+ },
164
+ });
165
+ }
166
+ buffer = [];
167
+ }
168
+
169
+ function headingPath() {
170
+ return headingStack.map((h) => h.text).join(" > ");
171
+ }
172
+
173
+ for (let i = 0; i < lines.length; i++) {
174
+ const line = lines[i];
175
+ const m = line.match(/^(#{1,6})\s+(.+?)\s*$/);
176
+ if (m) {
177
+ flush();
178
+ const level = m[1].length;
179
+ while (
180
+ headingStack.length &&
181
+ headingStack[headingStack.length - 1].level >= level
182
+ ) {
183
+ headingStack.pop();
184
+ }
185
+ headingStack.push({ level, text: m[2] });
186
+ bufferStartLine = i;
187
+ buffer.push(line);
188
+ } else {
189
+ if (!buffer.length) bufferStartLine = i;
190
+ buffer.push(line);
191
+ }
192
+ }
193
+ flush();
194
+
195
+ // Stamp total_chunks
196
+ for (const c of chunks) c.metadata.total_chunks = chunks.length;
197
+ return chunks;
198
+ }
199
+
200
+ /**
201
+ * Code chunker — sliding window that snaps to blank-line boundaries so
202
+ * we don't split mid-function. Tracks line ranges in metadata.
203
+ */
204
+ function chunkCode(content, chunkTokens, overlap, ext) {
205
+ const lines = content.split("\n");
206
+ const targetChars = tokensToChars(chunkTokens);
207
+ const overlapChars = tokensToChars(overlap);
208
+ const chunks = [];
209
+
210
+ let cursor = 0;
211
+ let charCount = 0;
212
+ let chunkStart = 0;
213
+ let lineCharOffsets = [0];
214
+ for (let i = 0; i < lines.length; i++) {
215
+ lineCharOffsets.push(lineCharOffsets[i] + lines[i].length + 1);
216
+ }
217
+
218
+ function emit(startLine, endLine) {
219
+ const text = lines.slice(startLine, endLine + 1).join("\n").trim();
220
+ if (!text) return;
221
+ chunks.push({
222
+ content: text,
223
+ metadata: {
224
+ kind: "code",
225
+ ext,
226
+ line_start: startLine + 1,
227
+ line_end: endLine + 1,
228
+ chunk_index: chunks.length,
229
+ },
230
+ });
231
+ }
232
+
233
+ while (cursor < lines.length) {
234
+ let endLine = cursor;
235
+ let chunkChars = 0;
236
+ while (endLine < lines.length && chunkChars < targetChars) {
237
+ chunkChars += lines[endLine].length + 1;
238
+ endLine++;
239
+ }
240
+ // Snap end to a blank line if one is within +/- 5 lines
241
+ let snapTarget = endLine;
242
+ for (let k = 0; k < 5 && endLine - 1 - k > cursor; k++) {
243
+ if (!lines[endLine - 1 - k].trim()) {
244
+ snapTarget = endLine - k;
245
+ break;
246
+ }
247
+ }
248
+ snapTarget = Math.min(snapTarget, lines.length);
249
+
250
+ emit(cursor, snapTarget - 1);
251
+
252
+ if (snapTarget >= lines.length) break;
253
+
254
+ // Compute overlap in lines: walk back until overlapChars consumed
255
+ let overlapStart = snapTarget;
256
+ let oc = 0;
257
+ while (overlapStart > cursor + 1 && oc < overlapChars) {
258
+ overlapStart--;
259
+ oc += lines[overlapStart].length + 1;
260
+ }
261
+ cursor = overlapStart;
262
+ }
263
+
264
+ for (const c of chunks) c.metadata.total_chunks = chunks.length;
265
+ return chunks;
266
+ }
267
+
268
+ /**
269
+ * Structured-data chunker — small files become one chunk; bigger ones
270
+ * split at top-level keys (JSON/YAML) or at section boundaries (TOML/INI).
271
+ * Doesn't try to parse; uses heuristics so we don't crash on malformed
272
+ * configs.
273
+ */
274
+ function chunkStructured(content, ext, chunkTokens) {
275
+ if (approxTokens(content) <= chunkTokens) {
276
+ return [{
277
+ content,
278
+ metadata: { kind: "config", ext, chunk_index: 0, total_chunks: 1 },
279
+ }];
280
+ }
281
+
282
+ // For larger configs, just sliding-window — config files at this size
283
+ // are usually generated and unlikely to be hand-edited reference material.
284
+ return chunkSlidingWindow(content, chunkTokens, 0, "config", { ext });
285
+ }
286
+
287
+ /**
288
+ * Generic sliding-window chunker. Used as fallback and inside other
289
+ * chunkers when a section is too large.
290
+ */
291
+ function chunkSlidingWindow(content, chunkTokens, overlap, kind, extraMeta = {}) {
292
+ const targetChars = tokensToChars(chunkTokens);
293
+ const overlapChars = tokensToChars(overlap);
294
+ const chunks = [];
295
+
296
+ let cursor = 0;
297
+ while (cursor < content.length) {
298
+ let end = Math.min(cursor + targetChars, content.length);
299
+
300
+ // Snap to nearest newline if within 200 chars
301
+ if (end < content.length) {
302
+ const nl = content.lastIndexOf("\n", end);
303
+ if (nl > cursor && end - nl < 200) end = nl;
304
+ }
305
+
306
+ const text = content.slice(cursor, end).trim();
307
+ if (text) {
308
+ chunks.push({
309
+ content: text,
310
+ metadata: {
311
+ kind,
312
+ chunk_index: chunks.length,
313
+ char_start: cursor,
314
+ char_end: end,
315
+ ...extraMeta,
316
+ },
317
+ });
318
+ }
319
+
320
+ if (end >= content.length) break;
321
+ cursor = Math.max(end - overlapChars, cursor + 1);
322
+ }
323
+
324
+ for (const c of chunks) c.metadata.total_chunks = chunks.length;
325
+ return chunks;
326
+ }
327
+
328
+ export { CODE_EXTENSIONS, PROSE_EXTENSIONS, STRUCTURED_EXTENSIONS, approxTokens };