hebbrix 2.0.2 → 2.2.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.
package/dist/index.mjs CHANGED
@@ -1,3 +1,55 @@
1
+ // src/safety.ts
2
+ var REQUIRED_FIELDS = [
3
+ "no_match",
4
+ "abstain_recommended",
5
+ "query_confidence",
6
+ "grounding",
7
+ "evidence_ids",
8
+ "safety_contract_version"
9
+ ];
10
+ function enforceSearchSafety(response, rowsKey = "results") {
11
+ const data = { ...response };
12
+ const rawRows = data[rowsKey];
13
+ const rows = Array.isArray(rawRows) ? rawRows.filter((row) => typeof row === "object" && row !== null) : [];
14
+ const missing = REQUIRED_FIELDS.filter(
15
+ (field) => !Object.prototype.hasOwnProperty.call(data, field)
16
+ );
17
+ let reason;
18
+ if (missing.length > 0) {
19
+ reason = `missing_safety_fields:${missing.join(",")}`;
20
+ } else if (typeof data.no_match !== "boolean" || typeof data.abstain_recommended !== "boolean") {
21
+ reason = "invalid_abstention_fields";
22
+ } else if (typeof data.query_confidence !== "number" || !Number.isFinite(data.query_confidence) || data.query_confidence < 0 || data.query_confidence > 1) {
23
+ reason = "invalid_query_confidence";
24
+ } else if (typeof data.grounding !== "object" || data.grounding === null || Array.isArray(data.grounding)) {
25
+ reason = "invalid_grounding_receipt";
26
+ } else if (!Array.isArray(data.evidence_ids)) {
27
+ reason = "invalid_evidence_ids";
28
+ } else {
29
+ const evidenceIds = new Set(data.evidence_ids.map(String));
30
+ const rowIds = rows.map((row) => row.memory_id ?? row.id).filter((value) => typeof value === "string" && value.length > 0);
31
+ if (rowIds.some((value) => !evidenceIds.has(value))) {
32
+ reason = "rows_not_bound_to_evidence_ids";
33
+ } else if (data.no_match && (rowIds.length > 0 || evidenceIds.size > 0)) {
34
+ reason = "no_match_contains_evidence";
35
+ }
36
+ }
37
+ if (reason || data.degraded === true || data.no_match === true || data.abstain_recommended === true) {
38
+ data[rowsKey] = [];
39
+ if (rowsKey === "results") data.total = 0;
40
+ data.no_match = true;
41
+ data.abstain_recommended = true;
42
+ data.query_confidence = 0;
43
+ data.evidence_ids = [];
44
+ data.evidence_claims = [];
45
+ if (reason) {
46
+ data.sdk_safety_reason = reason;
47
+ data.grounding = { status: "no_grounded_match", reason };
48
+ }
49
+ }
50
+ return data;
51
+ }
52
+
1
53
  // src/resources.ts
2
54
  var BaseResource = class {
3
55
  constructor(client) {
@@ -77,14 +129,32 @@ var MemoriesResource = class extends BaseResource {
77
129
  * Create a new memory
78
130
  */
79
131
  async create(params) {
80
- return this.client.post("/v1/memories", params);
132
+ if (!params.content?.trim() && !params.messages?.length) {
133
+ throw new TypeError("content or messages must be provided");
134
+ }
135
+ const { idempotency_key, ...input } = params;
136
+ return this.client.request("POST", "/v1/memories", {
137
+ headers: idempotency_key ? { "Idempotency-Key": idempotency_key } : void 0,
138
+ body: JSON.stringify({
139
+ source_type: "text",
140
+ metadata: {},
141
+ infer: false,
142
+ wait_for_index: false,
143
+ ...input
144
+ })
145
+ });
81
146
  }
82
147
  /**
83
148
  * List memories
84
149
  */
85
- async list(params = {}) {
150
+ async listPage(params = {}) {
86
151
  return this.client.get("/v1/memories", params);
87
152
  }
153
+ /** Back-compatible one-page convenience; use listPage for cursor metadata. */
154
+ async list(params = {}) {
155
+ const page = await this.listPage(params);
156
+ return page.items;
157
+ }
88
158
  /**
89
159
  * Get a specific memory
90
160
  */
@@ -104,19 +174,79 @@ var MemoriesResource = class extends BaseResource {
104
174
  await this.client.delete(`/v1/memories/${memoryId}`);
105
175
  }
106
176
  };
177
+ var MemoryJobsResource = class extends BaseResource {
178
+ async get(jobId) {
179
+ return this.client.get(`/v1/memory-jobs/${jobId}`);
180
+ }
181
+ async wait(jobId, options = {}) {
182
+ const timeoutMs = Math.max(0, options.timeoutMs ?? 6e4);
183
+ const pollIntervalMs = Math.max(50, options.pollIntervalMs ?? 500);
184
+ const deadline = Date.now() + timeoutMs;
185
+ while (true) {
186
+ const receipt = await this.get(jobId);
187
+ const status = String(receipt.status || "").toLowerCase();
188
+ if (["completed", "failed", "cancelled", "canceled"].includes(status)) {
189
+ return receipt;
190
+ }
191
+ if (Date.now() >= deadline) {
192
+ throw new Error(`memory job ${jobId} did not finish in ${timeoutMs}ms`);
193
+ }
194
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
195
+ }
196
+ }
197
+ };
198
+ var CorrectionsResource = class extends BaseResource {
199
+ async create(params) {
200
+ const { idempotency_key, ...body } = params;
201
+ return this.client.request("POST", "/v1/corrections", {
202
+ headers: idempotency_key ? { "Idempotency-Key": idempotency_key } : void 0,
203
+ body: JSON.stringify({
204
+ correction_type: "preference",
205
+ confidence: 1,
206
+ ...body
207
+ })
208
+ });
209
+ }
210
+ async relevant(params) {
211
+ return this.client.get("/v1/corrections/relevant", {
212
+ include_global: false,
213
+ limit: 10,
214
+ ...params
215
+ });
216
+ }
217
+ async get(correctionId) {
218
+ return this.client.get(`/v1/corrections/${correctionId}`);
219
+ }
220
+ async delete(correctionId) {
221
+ return this.client.delete(`/v1/corrections/${correctionId}`);
222
+ }
223
+ };
107
224
  var SearchResource = class extends BaseResource {
108
225
  /**
109
226
  * Search memories
110
227
  */
111
228
  async search(params) {
229
+ const response = await this.searchWithProof(params);
230
+ return response.results;
231
+ }
232
+ /** Search while preserving the automatic ProofLoop evidence context. */
233
+ async searchWithProof(params) {
112
234
  const response = await this.client.post("/v1/search", {
113
235
  query: params.query,
114
236
  collection_id: params.collection_id,
237
+ user_id: params.user_id,
238
+ agent_id: params.agent_id,
239
+ run_id: params.run_id,
115
240
  limit: params.limit || 10,
116
241
  search_type: params.search_type || "hybrid",
117
- filters: params.filters || {}
242
+ filters: params.filters || {},
243
+ fast: params.fast,
244
+ threshold: params.threshold,
245
+ include_low_confidence: params.include_low_confidence ?? false,
246
+ group_by_source: params.group_by_source ?? true,
247
+ debug: params.debug ?? false
118
248
  });
119
- return response.results;
249
+ return enforceSearchSafety(response);
120
250
  }
121
251
  /**
122
252
  * Find similar memories
@@ -132,11 +262,69 @@ var SearchResource = class extends BaseResource {
132
262
  * Perform reasoning over memories
133
263
  */
134
264
  async reason(params) {
135
- return this.client.post("/v1/search/reason", {
136
- query: params.query,
265
+ const response = await this.client.post(
266
+ "/v1/search/reason",
267
+ {
268
+ query: params.query,
269
+ collection_id: params.collection_id,
270
+ provider: params.provider,
271
+ include_steps: params.include_steps ?? false,
272
+ user_id: params.user_id,
273
+ agent_id: params.agent_id,
274
+ run_id: params.run_id,
275
+ facets: params.facets ?? []
276
+ }
277
+ );
278
+ return enforceSearchSafety(response, "sources");
279
+ }
280
+ };
281
+ var ProofLoopResource = class extends BaseResource {
282
+ /** Create a causal decision bound to search/chat evidence automatically. */
283
+ async decide(params) {
284
+ const context = params.proof_context;
285
+ const token = typeof context === "string" ? context : context?.token;
286
+ const { proof_context: _context, ...body } = params;
287
+ return this.client.post("/v1/learning/decisions", {
288
+ ...body,
289
+ ...token ? { proof_context_token: token } : {}
290
+ });
291
+ }
292
+ async recordOutcome(decisionId, body) {
293
+ return this.client.post(`/v1/learning/decisions/${decisionId}/outcomes`, {
294
+ observations: [],
295
+ ...body
296
+ });
297
+ }
298
+ async getDecision(decisionId) {
299
+ return this.client.get(`/v1/learning/decisions/${decisionId}`);
300
+ }
301
+ async defineMetric(params) {
302
+ return this.client.post("/v1/learning/metrics", params);
303
+ }
304
+ async listMetrics(params = {}) {
305
+ return this.client.get("/v1/learning/metrics", params);
306
+ }
307
+ async policyInsights(policyKey, params = {}) {
308
+ return this.client.get(`/v1/learning/policies/${policyKey}/insights`, {
309
+ collection_id: params.collection_id,
310
+ user_id: params.user_id,
311
+ action_key: params.action_keys,
312
+ context: params.context ? JSON.stringify(params.context) : void 0
313
+ });
314
+ }
315
+ async evaluatePolicy(policyKey, params = {}) {
316
+ return this.client.post(`/v1/learning/policies/${policyKey}/evaluate`, {
137
317
  collection_id: params.collection_id,
138
- provider: params.provider,
139
- include_steps: params.include_steps || false
318
+ user_id: params.user_id,
319
+ limit: params.limit ?? 500
320
+ });
321
+ }
322
+ async proof(decisionId) {
323
+ return this.client.get(`/v1/learning/decisions/${decisionId}/proof`);
324
+ }
325
+ async publicKey(keyId) {
326
+ return this.client.get("/v1/learning/proof-key", {
327
+ key_id: keyId
140
328
  });
141
329
  }
142
330
  };
@@ -434,6 +622,8 @@ var MemoryClient = class {
434
622
  this.auth = new AuthResource(this);
435
623
  this.collections = new CollectionsResource(this);
436
624
  this.memories = new MemoriesResource(this);
625
+ this.memoryJobs = new MemoryJobsResource(this);
626
+ this.corrections = new CorrectionsResource(this);
437
627
  this.searchResource = new SearchResource(this);
438
628
  this.rl = new RLResource(this);
439
629
  this.procedural = new ProceduralResource(this);
@@ -442,11 +632,12 @@ var MemoryClient = class {
442
632
  this.consolidation = new ConsolidationResource(this);
443
633
  this.memoryTools = new MemoryToolsResource(this);
444
634
  this.worldModel = new WorldModelResource(this);
635
+ this.proofloop = new ProofLoopResource(this);
445
636
  }
446
637
  getHeaders() {
447
638
  const headers = {
448
639
  "Content-Type": "application/json",
449
- "User-Agent": "hebbrix-typescript/2.0.0"
640
+ "User-Agent": "hebbrix-typescript/2.2.0"
450
641
  };
451
642
  if (this.apiKey) {
452
643
  headers["Authorization"] = `Bearer ${this.apiKey}`;
@@ -500,7 +691,11 @@ var MemoryClient = class {
500
691
  const searchParams = new URLSearchParams();
501
692
  Object.entries(params).forEach(([key, value]) => {
502
693
  if (value !== void 0 && value !== null) {
503
- searchParams.append(key, String(value));
694
+ if (Array.isArray(value)) {
695
+ value.forEach((item) => searchParams.append(key, String(item)));
696
+ } else {
697
+ searchParams.append(key, String(value));
698
+ }
504
699
  }
505
700
  });
506
701
  url += `?${searchParams.toString()}`;
@@ -524,6 +719,9 @@ var MemoryClient = class {
524
719
  async search(params) {
525
720
  return this.searchResource.search(params);
526
721
  }
722
+ async searchWithProof(params) {
723
+ return this.searchResource.searchWithProof(params);
724
+ }
527
725
  async reason(params) {
528
726
  return this.searchResource.reason(params);
529
727
  }
@@ -533,12 +731,15 @@ export {
533
731
  AuthenticationError,
534
732
  CollectionsResource,
535
733
  ConsolidationResource,
734
+ CorrectionsResource,
536
735
  HebbrixError,
537
736
  MemoriesResource,
538
737
  MemoryClient,
738
+ MemoryJobsResource,
539
739
  MemoryToolsResource,
540
740
  NotFoundError,
541
741
  ProceduralResource,
742
+ ProofLoopResource,
542
743
  RLResource,
543
744
  RateLimitError,
544
745
  SearchResource,
@@ -546,5 +747,6 @@ export {
546
747
  TemporalResource,
547
748
  ValidationError,
548
749
  WorkingMemoryResource,
549
- WorldModelResource
750
+ WorldModelResource,
751
+ enforceSearchSafety
550
752
  };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "hebbrix",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "description": "Advanced Memory API for AI Agents with Reinforcement Learning - TypeScript/JavaScript SDK",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
+ "types": "./dist/index.d.ts",
10
11
  "import": "./dist/index.mjs",
11
- "require": "./dist/index.js",
12
- "types": "./dist/index.d.ts"
12
+ "require": "./dist/index.js"
13
13
  }
14
14
  },
15
15
  "files": [
@@ -19,7 +19,7 @@
19
19
  "scripts": {
20
20
  "build": "tsup src/index.ts --format cjs,esm --dts",
21
21
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
22
- "test": "jest",
22
+ "test": "npm run build && node --test tests/*.test.mjs",
23
23
  "lint": "eslint src --ext .ts",
24
24
  "format": "prettier --write \"src/**/*.ts\"",
25
25
  "prepublishOnly": "npm run build"
@@ -47,12 +47,15 @@
47
47
  "license": "MIT",
48
48
  "repository": {
49
49
  "type": "git",
50
- "url": "https://github.com/hebbrix/hebbrix-typescript.git"
50
+ "url": "git+https://github.com/Hebbrix/hebbrix-typescript.git"
51
51
  },
52
52
  "bugs": {
53
53
  "url": "https://github.com/hebbrix/hebbrix-typescript/issues"
54
54
  },
55
55
  "homepage": "https://hebbrix.com",
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
56
59
  "devDependencies": {
57
60
  "@types/node": "^20.10.0",
58
61
  "@typescript-eslint/eslint-plugin": "^6.15.0",