hebbrix 2.2.0 → 2.2.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## 2.2.1 — 2026-08-26
4
+
5
+ - Preserve bearer authentication, content type, user agent, idempotency, and
6
+ caller-supplied headers on every request.
7
+ - Handle `204`, empty, and non-JSON successful responses without attempting an
8
+ unconditional JSON parse.
9
+ - Preserve valid evidence-bound search rows during a degraded or abstaining
10
+ server response while retaining the safety metadata; malformed and explicit
11
+ no-match envelopes still fail closed.
12
+ - Preserve caller-provided abort signals while retaining the default timeout.
13
+ - Add `temporal.deleteFact(factId)` for tenant-scoped, idempotent cleanup of
14
+ facts created through the temporal API.
15
+ - Align procedure create/list/get/update/execute/delete with the canonical
16
+ `/v1/procedures` REST and OpenAPI contract, including empty `204` deletion.
17
+ - Add explicit synchronous/asynchronous batch readiness receipts and
18
+ `waitForBatchSearchable`, which polls every accepted memory, propagates
19
+ terminal failures, respects deadlines, and supports cancellation.
20
+
21
+ The supported server/SDK release pair is published by the server OpenAPI
22
+ document in `info.x-hebbrix-sdk-compatibility`. Patch releases preserve the
23
+ public API within the same major version.
24
+
25
+ ## 2.2.0
26
+
27
+ - Added the GA scoped memory, corrections, search proof, and ProofLoop surface.
package/README.md CHANGED
@@ -50,8 +50,22 @@ const main = async () => {
50
50
  collection_id: collection.id,
51
51
  content: 'User prefers dark mode and loves TypeScript',
52
52
  importance: 0.9,
53
+ wait_for_index: true,
53
54
  });
54
55
 
56
+ // Batch writes have an explicit two-mode contract. With wait_for_index=true,
57
+ // success means every item is searchable; a server deadline rejects with a
58
+ // retryable error instead of returning a misleading successful 202.
59
+ const batch = await client.memories.createBatch({
60
+ memories: [{ content: 'First fact' }, { content: 'Second fact' }],
61
+ collection_id: collection.id,
62
+ wait_for_index: true,
63
+ idempotency_key: 'import-42',
64
+ });
65
+
66
+ // For fire-and-forget batches, poll every item (with timeout/cancellation):
67
+ // await client.memories.waitForBatchSearchable(batch, { signal });
68
+
55
69
  // Search memories
56
70
  const results = await client.search({
57
71
  query: 'What programming language does user like?',
@@ -322,6 +336,8 @@ const updated = await client.procedural.update(procedure.id, {
322
336
 
323
337
  // Delete procedure
324
338
  await client.procedural.delete(procedure.id);
339
+ // DELETE is tenant-scoped and idempotent: deleted, absent, and foreign IDs all
340
+ // return 204 without revealing whether another tenant owns the identifier.
325
341
  ```
326
342
 
327
343
  ### 8. Temporal Knowledge Graphs
package/dist/index.d.mts CHANGED
@@ -175,6 +175,45 @@ interface MemoryAddResponse {
175
175
  updated_count: number;
176
176
  skipped_count: number;
177
177
  }
178
+ interface BatchMemoryItemParams {
179
+ content: string;
180
+ collection_id?: string;
181
+ importance?: number;
182
+ user_id?: string;
183
+ agent_id?: string;
184
+ run_id?: string;
185
+ tags?: string[];
186
+ metadata?: Record<string, any>;
187
+ }
188
+ interface BatchMemoryCreateParams {
189
+ memories: BatchMemoryItemParams[];
190
+ collection_id?: string;
191
+ user_id?: string;
192
+ agent_id?: string;
193
+ run_id?: string;
194
+ app_id?: string;
195
+ namespace?: string;
196
+ wait_for_index?: boolean;
197
+ /** Stable retry key sent as the Idempotency-Key transport header. */
198
+ idempotency_key?: string;
199
+ /** Optional caller cancellation signal; it is never serialized. */
200
+ signal?: AbortSignal;
201
+ }
202
+ interface BatchMemoryResponse {
203
+ created: number;
204
+ failed: number;
205
+ memory_ids: string[];
206
+ errors: string[];
207
+ results: Array<{
208
+ id: string;
209
+ memory_id?: string;
210
+ processing_status: string;
211
+ }>;
212
+ processing_status: string;
213
+ searchable: boolean;
214
+ outbox_event_id?: string;
215
+ status_url?: string;
216
+ }
178
217
  interface UpdateMemoryParams {
179
218
  content?: string;
180
219
  importance?: number;
@@ -324,6 +363,19 @@ declare class MemoriesResource extends BaseResource {
324
363
  * Create a new memory
325
364
  */
326
365
  create(params: CreateMemoryParams): Promise<MemoryAddResponse>;
366
+ /**
367
+ * Create up to 100 memories with one unambiguous readiness contract.
368
+ * `wait_for_index=true` resolves only for a fully searchable batch; a server
369
+ * deadline or indexing failure rejects the request instead of returning a
370
+ * successful processing receipt.
371
+ */
372
+ createBatch(params: BatchMemoryCreateParams): Promise<BatchMemoryResponse>;
373
+ /** Poll every item in an asynchronous batch receipt until it is searchable. */
374
+ waitForBatchSearchable(receipt: BatchMemoryResponse, options?: {
375
+ timeoutMs?: number;
376
+ pollIntervalMs?: number;
377
+ signal?: AbortSignal;
378
+ }): Promise<BatchMemoryResponse>;
327
379
  /**
328
380
  * List memories
329
381
  */
@@ -429,6 +481,7 @@ declare class RLResource extends BaseResource {
429
481
  evaluate(agentType: string, collectionId?: string): Promise<any>;
430
482
  }
431
483
  declare class ProceduralResource extends BaseResource {
484
+ private unwrapProcedure;
432
485
  /**
433
486
  * Create a new procedure
434
487
  */
@@ -500,6 +553,8 @@ declare class TemporalResource extends BaseResource {
500
553
  * Query knowledge state at a specific point in time
501
554
  */
502
555
  pointInTime(timestamp: string, entity?: string): Promise<any>;
556
+ /** Permanently delete a tenant-scoped temporal fact by stable ID. */
557
+ deleteFact(factId: string): Promise<any>;
503
558
  }
504
559
  declare class WorkingMemoryResource extends BaseResource {
505
560
  /**
@@ -640,10 +695,10 @@ interface SafetyEnvelope {
640
695
  }
641
696
  /**
642
697
  * Validate the API-owned grounding receipt before exposing evidence to an agent.
643
- * A malformed, degraded, abstaining, or ungrounded response is converted into
644
- * one deterministic no-match envelope. This prevents SDK/API contract drift
645
- * from turning a nearest neighbour into verified evidence.
698
+ * A malformed or explicit no-match response is converted into one deterministic
699
+ * no-match envelope. Degraded, evidence-bound rows remain visible together with
700
+ * the API's abstention signal so the SDK cannot introduce a false negative.
646
701
  */
647
702
  declare function enforceSearchSafety<T extends object>(response: T, rowsKey?: "results" | "sources"): T & SafetyEnvelope;
648
703
 
649
- export { type APIKeyResponse, AuthResource, type AuthResponse, AuthenticationError, type ClientConfig, type Collection, CollectionsResource, ConsolidationResource, type CorrectionCreateParams, type CorrectionSearchParams, CorrectionsResource, type CreateCollectionParams, type CreateMemoryParams, type CursorPage, type EvidenceClaim, type GroundingReceipt, HebbrixError, type ListParams, MemoriesResource, type Memory, type MemoryAddResponse, type MemoryAddResult, MemoryClient, type MemoryJobReceipt, MemoryJobsResource, type MemoryListParams, MemoryToolsResource, type MemoryWithMetadata, NotFoundError, ProceduralResource, type ProofContext, type ProofLoopCandidate, type ProofLoopDecisionParams, type ProofLoopMetricParams, ProofLoopResource, RLResource, RateLimitError, type ReasonParams, type ReasoningResponse, type ReasoningSource, type SafetyEnvelope, type SearchParams, SearchResource, type SearchResponse, type SearchResult, ServerError, TemporalResource, type UpdateCollectionParams, type UpdateMemoryParams, type User, ValidationError, WorkingMemoryResource, WorldModelResource, enforceSearchSafety };
704
+ export { type APIKeyResponse, AuthResource, type AuthResponse, AuthenticationError, type BatchMemoryCreateParams, type BatchMemoryItemParams, type BatchMemoryResponse, type ClientConfig, type Collection, CollectionsResource, ConsolidationResource, type CorrectionCreateParams, type CorrectionSearchParams, CorrectionsResource, type CreateCollectionParams, type CreateMemoryParams, type CursorPage, type EvidenceClaim, type GroundingReceipt, HebbrixError, type ListParams, MemoriesResource, type Memory, type MemoryAddResponse, type MemoryAddResult, MemoryClient, type MemoryJobReceipt, MemoryJobsResource, type MemoryListParams, MemoryToolsResource, type MemoryWithMetadata, NotFoundError, ProceduralResource, type ProofContext, type ProofLoopCandidate, type ProofLoopDecisionParams, type ProofLoopMetricParams, ProofLoopResource, RLResource, RateLimitError, type ReasonParams, type ReasoningResponse, type ReasoningSource, type SafetyEnvelope, type SearchParams, SearchResource, type SearchResponse, type SearchResult, ServerError, TemporalResource, type UpdateCollectionParams, type UpdateMemoryParams, type User, ValidationError, WorkingMemoryResource, WorldModelResource, enforceSearchSafety };
package/dist/index.d.ts CHANGED
@@ -175,6 +175,45 @@ interface MemoryAddResponse {
175
175
  updated_count: number;
176
176
  skipped_count: number;
177
177
  }
178
+ interface BatchMemoryItemParams {
179
+ content: string;
180
+ collection_id?: string;
181
+ importance?: number;
182
+ user_id?: string;
183
+ agent_id?: string;
184
+ run_id?: string;
185
+ tags?: string[];
186
+ metadata?: Record<string, any>;
187
+ }
188
+ interface BatchMemoryCreateParams {
189
+ memories: BatchMemoryItemParams[];
190
+ collection_id?: string;
191
+ user_id?: string;
192
+ agent_id?: string;
193
+ run_id?: string;
194
+ app_id?: string;
195
+ namespace?: string;
196
+ wait_for_index?: boolean;
197
+ /** Stable retry key sent as the Idempotency-Key transport header. */
198
+ idempotency_key?: string;
199
+ /** Optional caller cancellation signal; it is never serialized. */
200
+ signal?: AbortSignal;
201
+ }
202
+ interface BatchMemoryResponse {
203
+ created: number;
204
+ failed: number;
205
+ memory_ids: string[];
206
+ errors: string[];
207
+ results: Array<{
208
+ id: string;
209
+ memory_id?: string;
210
+ processing_status: string;
211
+ }>;
212
+ processing_status: string;
213
+ searchable: boolean;
214
+ outbox_event_id?: string;
215
+ status_url?: string;
216
+ }
178
217
  interface UpdateMemoryParams {
179
218
  content?: string;
180
219
  importance?: number;
@@ -324,6 +363,19 @@ declare class MemoriesResource extends BaseResource {
324
363
  * Create a new memory
325
364
  */
326
365
  create(params: CreateMemoryParams): Promise<MemoryAddResponse>;
366
+ /**
367
+ * Create up to 100 memories with one unambiguous readiness contract.
368
+ * `wait_for_index=true` resolves only for a fully searchable batch; a server
369
+ * deadline or indexing failure rejects the request instead of returning a
370
+ * successful processing receipt.
371
+ */
372
+ createBatch(params: BatchMemoryCreateParams): Promise<BatchMemoryResponse>;
373
+ /** Poll every item in an asynchronous batch receipt until it is searchable. */
374
+ waitForBatchSearchable(receipt: BatchMemoryResponse, options?: {
375
+ timeoutMs?: number;
376
+ pollIntervalMs?: number;
377
+ signal?: AbortSignal;
378
+ }): Promise<BatchMemoryResponse>;
327
379
  /**
328
380
  * List memories
329
381
  */
@@ -429,6 +481,7 @@ declare class RLResource extends BaseResource {
429
481
  evaluate(agentType: string, collectionId?: string): Promise<any>;
430
482
  }
431
483
  declare class ProceduralResource extends BaseResource {
484
+ private unwrapProcedure;
432
485
  /**
433
486
  * Create a new procedure
434
487
  */
@@ -500,6 +553,8 @@ declare class TemporalResource extends BaseResource {
500
553
  * Query knowledge state at a specific point in time
501
554
  */
502
555
  pointInTime(timestamp: string, entity?: string): Promise<any>;
556
+ /** Permanently delete a tenant-scoped temporal fact by stable ID. */
557
+ deleteFact(factId: string): Promise<any>;
503
558
  }
504
559
  declare class WorkingMemoryResource extends BaseResource {
505
560
  /**
@@ -640,10 +695,10 @@ interface SafetyEnvelope {
640
695
  }
641
696
  /**
642
697
  * Validate the API-owned grounding receipt before exposing evidence to an agent.
643
- * A malformed, degraded, abstaining, or ungrounded response is converted into
644
- * one deterministic no-match envelope. This prevents SDK/API contract drift
645
- * from turning a nearest neighbour into verified evidence.
698
+ * A malformed or explicit no-match response is converted into one deterministic
699
+ * no-match envelope. Degraded, evidence-bound rows remain visible together with
700
+ * the API's abstention signal so the SDK cannot introduce a false negative.
646
701
  */
647
702
  declare function enforceSearchSafety<T extends object>(response: T, rowsKey?: "results" | "sources"): T & SafetyEnvelope;
648
703
 
649
- export { type APIKeyResponse, AuthResource, type AuthResponse, AuthenticationError, type ClientConfig, type Collection, CollectionsResource, ConsolidationResource, type CorrectionCreateParams, type CorrectionSearchParams, CorrectionsResource, type CreateCollectionParams, type CreateMemoryParams, type CursorPage, type EvidenceClaim, type GroundingReceipt, HebbrixError, type ListParams, MemoriesResource, type Memory, type MemoryAddResponse, type MemoryAddResult, MemoryClient, type MemoryJobReceipt, MemoryJobsResource, type MemoryListParams, MemoryToolsResource, type MemoryWithMetadata, NotFoundError, ProceduralResource, type ProofContext, type ProofLoopCandidate, type ProofLoopDecisionParams, type ProofLoopMetricParams, ProofLoopResource, RLResource, RateLimitError, type ReasonParams, type ReasoningResponse, type ReasoningSource, type SafetyEnvelope, type SearchParams, SearchResource, type SearchResponse, type SearchResult, ServerError, TemporalResource, type UpdateCollectionParams, type UpdateMemoryParams, type User, ValidationError, WorkingMemoryResource, WorldModelResource, enforceSearchSafety };
704
+ export { type APIKeyResponse, AuthResource, type AuthResponse, AuthenticationError, type BatchMemoryCreateParams, type BatchMemoryItemParams, type BatchMemoryResponse, type ClientConfig, type Collection, CollectionsResource, ConsolidationResource, type CorrectionCreateParams, type CorrectionSearchParams, CorrectionsResource, type CreateCollectionParams, type CreateMemoryParams, type CursorPage, type EvidenceClaim, type GroundingReceipt, HebbrixError, type ListParams, MemoriesResource, type Memory, type MemoryAddResponse, type MemoryAddResult, MemoryClient, type MemoryJobReceipt, MemoryJobsResource, type MemoryListParams, MemoryToolsResource, type MemoryWithMetadata, NotFoundError, ProceduralResource, type ProofContext, type ProofLoopCandidate, type ProofLoopDecisionParams, type ProofLoopMetricParams, ProofLoopResource, RLResource, RateLimitError, type ReasonParams, type ReasoningResponse, type ReasoningSource, type SafetyEnvelope, type SearchParams, SearchResource, type SearchResponse, type SearchResult, ServerError, TemporalResource, type UpdateCollectionParams, type UpdateMemoryParams, type User, ValidationError, WorkingMemoryResource, WorldModelResource, enforceSearchSafety };
package/dist/index.js CHANGED
@@ -81,7 +81,7 @@ function enforceSearchSafety(response, rowsKey = "results") {
81
81
  reason = "no_match_contains_evidence";
82
82
  }
83
83
  }
84
- if (reason || data.degraded === true || data.no_match === true || data.abstain_recommended === true) {
84
+ if (reason || data.no_match === true) {
85
85
  data[rowsKey] = [];
86
86
  if (rowsKey === "results") data.total = 0;
87
87
  data.no_match = true;
@@ -93,6 +93,8 @@ function enforceSearchSafety(response, rowsKey = "results") {
93
93
  data.sdk_safety_reason = reason;
94
94
  data.grounding = { status: "no_grounded_match", reason };
95
95
  }
96
+ } else if (data.degraded === true || data.abstain_recommended === true) {
97
+ data.sdk_safety_reason = "degraded_evidence_preserved";
96
98
  }
97
99
  return data;
98
100
  }
@@ -191,6 +193,78 @@ var MemoriesResource = class extends BaseResource {
191
193
  })
192
194
  });
193
195
  }
196
+ /**
197
+ * Create up to 100 memories with one unambiguous readiness contract.
198
+ * `wait_for_index=true` resolves only for a fully searchable batch; a server
199
+ * deadline or indexing failure rejects the request instead of returning a
200
+ * successful processing receipt.
201
+ */
202
+ async createBatch(params) {
203
+ if (!params.memories?.length || params.memories.length > 100) {
204
+ throw new TypeError("memories must contain between 1 and 100 items");
205
+ }
206
+ if (params.memories.some((item) => !item.content?.trim())) {
207
+ throw new TypeError("every batch memory must contain non-empty content");
208
+ }
209
+ const { idempotency_key, signal, ...body } = params;
210
+ const receipt = await this.client.request(
211
+ "POST",
212
+ "/v1/memories/batch",
213
+ {
214
+ headers: idempotency_key ? { "Idempotency-Key": idempotency_key } : void 0,
215
+ body: JSON.stringify({ wait_for_index: false, ...body }),
216
+ signal
217
+ }
218
+ );
219
+ if (params.wait_for_index && !(receipt.searchable === true && receipt.processing_status === "completed")) {
220
+ throw new Error(
221
+ "wait_for_index batch response was not fully searchable; retry with the same Idempotency-Key"
222
+ );
223
+ }
224
+ return receipt;
225
+ }
226
+ /** Poll every item in an asynchronous batch receipt until it is searchable. */
227
+ async waitForBatchSearchable(receipt, options = {}) {
228
+ const ids = [...new Set(receipt.memory_ids || [])];
229
+ if (!ids.length) {
230
+ throw new Error("batch receipt does not contain memory_ids");
231
+ }
232
+ const timeoutMs = Math.max(0, options.timeoutMs ?? 6e4);
233
+ const pollIntervalMs = Math.max(50, options.pollIntervalMs ?? 500);
234
+ const deadline = Date.now() + timeoutMs;
235
+ while (true) {
236
+ if (options.signal?.aborted) {
237
+ throw options.signal.reason || new Error("batch readiness polling was aborted");
238
+ }
239
+ const rows = await Promise.all(ids.map((id) => this.get(id)));
240
+ const terminal = rows.find(
241
+ (row) => ["failed", "cancelled", "canceled"].includes(
242
+ String(row.processing_status || "").toLowerCase()
243
+ )
244
+ );
245
+ if (terminal) {
246
+ throw new Error(
247
+ `memory ${terminal.id} indexing reached terminal state ${terminal.processing_status}`
248
+ );
249
+ }
250
+ if (rows.every((row) => row.searchable === true)) {
251
+ return {
252
+ ...receipt,
253
+ processing_status: "completed",
254
+ searchable: true,
255
+ results: ids.map((id) => ({
256
+ id,
257
+ memory_id: id,
258
+ processing_status: "completed"
259
+ }))
260
+ };
261
+ }
262
+ if (Date.now() >= deadline) {
263
+ throw new Error(`batch was not searchable within ${timeoutMs}ms`);
264
+ }
265
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
266
+ }
267
+ }
194
268
  /**
195
269
  * List memories
196
270
  */
@@ -413,56 +487,78 @@ var RLResource = class extends BaseResource {
413
487
  }
414
488
  };
415
489
  var ProceduralResource = class extends BaseResource {
490
+ unwrapProcedure(response) {
491
+ if (response?.procedure) return response.procedure;
492
+ if (response?.procedure_id && !response?.id) {
493
+ return { ...response, id: response.procedure_id };
494
+ }
495
+ return response;
496
+ }
416
497
  /**
417
498
  * Create a new procedure
418
499
  */
419
500
  async create(params) {
420
- return this.client.post("/procedural", {
501
+ const response = await this.client.post("/v1/procedures", {
421
502
  name: params.name,
422
503
  description: params.description,
423
- trigger_condition: params.trigger_condition,
424
- action_sequence: params.action_sequence,
504
+ condition: { expression: params.trigger_condition },
505
+ action: { steps: params.action_sequence },
425
506
  collection_id: params.collection_id,
426
507
  category: params.category,
427
- metadata: params.metadata || {}
508
+ parameters: params.metadata || {}
428
509
  });
510
+ return this.unwrapProcedure(response);
429
511
  }
430
512
  /**
431
513
  * List procedures
432
514
  */
433
515
  async list(params) {
434
- return this.client.get("/procedural", {
516
+ const response = await this.client.get("/v1/procedures", {
435
517
  collection_id: params?.collection_id,
436
518
  category: params?.category,
437
519
  skip: params?.skip || 0,
438
520
  limit: params?.limit || 100
439
521
  });
522
+ return Array.isArray(response) ? response : response?.procedures || [];
440
523
  }
441
524
  /**
442
525
  * Get a specific procedure
443
526
  */
444
527
  async get(procedureId) {
445
- return this.client.get(`/procedural/${procedureId}`);
528
+ const response = await this.client.get(`/v1/procedures/${procedureId}`);
529
+ return this.unwrapProcedure(response);
446
530
  }
447
531
  /**
448
532
  * Execute a procedure
449
533
  */
450
534
  async execute(procedureId, context) {
451
- return this.client.post(`/procedural/${procedureId}/execute`, {
452
- context: context || {}
535
+ const response = await this.client.post(`/v1/procedures/${procedureId}/execute`, {
536
+ input_state: context || {}
453
537
  });
538
+ return response?.execution_result || response;
454
539
  }
455
540
  /**
456
541
  * Update a procedure
457
542
  */
458
543
  async update(procedureId, params) {
459
- return this.client.patch(`/procedural/${procedureId}`, params);
544
+ const body = {};
545
+ if (params.name !== void 0) body.name = params.name;
546
+ if (params.description !== void 0) body.description = params.description;
547
+ if (params.trigger_condition !== void 0) {
548
+ body.condition = { expression: params.trigger_condition };
549
+ }
550
+ if (params.action_sequence !== void 0) {
551
+ body.action = { steps: params.action_sequence };
552
+ }
553
+ if (params.metadata !== void 0) body.parameters = params.metadata;
554
+ const response = await this.client.patch(`/v1/procedures/${procedureId}`, body);
555
+ return this.unwrapProcedure(response);
460
556
  }
461
557
  /**
462
558
  * Delete a procedure
463
559
  */
464
560
  async delete(procedureId) {
465
- await this.client.delete(`/procedural/${procedureId}`);
561
+ await this.client.delete(`/v1/procedures/${procedureId}`);
466
562
  }
467
563
  };
468
564
  var TemporalResource = class extends BaseResource {
@@ -501,6 +597,10 @@ var TemporalResource = class extends BaseResource {
501
597
  entity
502
598
  });
503
599
  }
600
+ /** Permanently delete a tenant-scoped temporal fact by stable ID. */
601
+ async deleteFact(factId) {
602
+ return this.client.delete(`/temporal/facts/${factId}`);
603
+ }
504
604
  };
505
605
  var WorkingMemoryResource = class extends BaseResource {
506
606
  /**
@@ -684,7 +784,7 @@ var MemoryClient = class {
684
784
  getHeaders() {
685
785
  const headers = {
686
786
  "Content-Type": "application/json",
687
- "User-Agent": "hebbrix-typescript/2.2.0"
787
+ "User-Agent": "hebbrix-typescript/2.2.1"
688
788
  };
689
789
  if (this.apiKey) {
690
790
  headers["Authorization"] = `Bearer ${this.apiKey}`;
@@ -693,7 +793,8 @@ var MemoryClient = class {
693
793
  }
694
794
  handleError(response, data) {
695
795
  const statusCode = response.status;
696
- const message = data?.error?.message || data?.detail || response.statusText;
796
+ const detail = data?.detail;
797
+ const message = data?.error?.message || (typeof detail === "string" ? detail : detail?.message) || response.statusText;
697
798
  if (statusCode === 401) {
698
799
  throw new AuthenticationError(message);
699
800
  } else if (statusCode === 404) {
@@ -711,21 +812,29 @@ var MemoryClient = class {
711
812
  }
712
813
  async request(method, path, options = {}) {
713
814
  const url = `${this.baseUrl}${path}`;
815
+ const { headers: requestHeaders, signal: requestSignal, ...requestOptions } = options;
714
816
  const response = await fetch(url, {
817
+ ...requestOptions,
715
818
  method,
716
819
  headers: {
717
820
  ...this.getHeaders(),
718
- ...options.headers || {}
821
+ ...requestHeaders || {}
719
822
  },
720
- ...options,
721
- signal: AbortSignal.timeout(this.timeout)
823
+ signal: requestSignal || AbortSignal.timeout(this.timeout)
722
824
  });
723
- let data;
825
+ let data = void 0;
724
826
  const contentType = response.headers.get("content-type");
725
- if (contentType?.includes("application/json")) {
726
- data = await response.json();
727
- } else {
728
- data = await response.text();
827
+ const responseText = await response.text();
828
+ if (responseText) {
829
+ if (contentType?.includes("application/json")) {
830
+ try {
831
+ data = JSON.parse(responseText);
832
+ } catch {
833
+ data = { detail: responseText };
834
+ }
835
+ } else {
836
+ data = responseText;
837
+ }
729
838
  }
730
839
  if (!response.ok) {
731
840
  this.handleError(response, data);
package/dist/index.mjs CHANGED
@@ -34,7 +34,7 @@ function enforceSearchSafety(response, rowsKey = "results") {
34
34
  reason = "no_match_contains_evidence";
35
35
  }
36
36
  }
37
- if (reason || data.degraded === true || data.no_match === true || data.abstain_recommended === true) {
37
+ if (reason || data.no_match === true) {
38
38
  data[rowsKey] = [];
39
39
  if (rowsKey === "results") data.total = 0;
40
40
  data.no_match = true;
@@ -46,6 +46,8 @@ function enforceSearchSafety(response, rowsKey = "results") {
46
46
  data.sdk_safety_reason = reason;
47
47
  data.grounding = { status: "no_grounded_match", reason };
48
48
  }
49
+ } else if (data.degraded === true || data.abstain_recommended === true) {
50
+ data.sdk_safety_reason = "degraded_evidence_preserved";
49
51
  }
50
52
  return data;
51
53
  }
@@ -144,6 +146,78 @@ var MemoriesResource = class extends BaseResource {
144
146
  })
145
147
  });
146
148
  }
149
+ /**
150
+ * Create up to 100 memories with one unambiguous readiness contract.
151
+ * `wait_for_index=true` resolves only for a fully searchable batch; a server
152
+ * deadline or indexing failure rejects the request instead of returning a
153
+ * successful processing receipt.
154
+ */
155
+ async createBatch(params) {
156
+ if (!params.memories?.length || params.memories.length > 100) {
157
+ throw new TypeError("memories must contain between 1 and 100 items");
158
+ }
159
+ if (params.memories.some((item) => !item.content?.trim())) {
160
+ throw new TypeError("every batch memory must contain non-empty content");
161
+ }
162
+ const { idempotency_key, signal, ...body } = params;
163
+ const receipt = await this.client.request(
164
+ "POST",
165
+ "/v1/memories/batch",
166
+ {
167
+ headers: idempotency_key ? { "Idempotency-Key": idempotency_key } : void 0,
168
+ body: JSON.stringify({ wait_for_index: false, ...body }),
169
+ signal
170
+ }
171
+ );
172
+ if (params.wait_for_index && !(receipt.searchable === true && receipt.processing_status === "completed")) {
173
+ throw new Error(
174
+ "wait_for_index batch response was not fully searchable; retry with the same Idempotency-Key"
175
+ );
176
+ }
177
+ return receipt;
178
+ }
179
+ /** Poll every item in an asynchronous batch receipt until it is searchable. */
180
+ async waitForBatchSearchable(receipt, options = {}) {
181
+ const ids = [...new Set(receipt.memory_ids || [])];
182
+ if (!ids.length) {
183
+ throw new Error("batch receipt does not contain memory_ids");
184
+ }
185
+ const timeoutMs = Math.max(0, options.timeoutMs ?? 6e4);
186
+ const pollIntervalMs = Math.max(50, options.pollIntervalMs ?? 500);
187
+ const deadline = Date.now() + timeoutMs;
188
+ while (true) {
189
+ if (options.signal?.aborted) {
190
+ throw options.signal.reason || new Error("batch readiness polling was aborted");
191
+ }
192
+ const rows = await Promise.all(ids.map((id) => this.get(id)));
193
+ const terminal = rows.find(
194
+ (row) => ["failed", "cancelled", "canceled"].includes(
195
+ String(row.processing_status || "").toLowerCase()
196
+ )
197
+ );
198
+ if (terminal) {
199
+ throw new Error(
200
+ `memory ${terminal.id} indexing reached terminal state ${terminal.processing_status}`
201
+ );
202
+ }
203
+ if (rows.every((row) => row.searchable === true)) {
204
+ return {
205
+ ...receipt,
206
+ processing_status: "completed",
207
+ searchable: true,
208
+ results: ids.map((id) => ({
209
+ id,
210
+ memory_id: id,
211
+ processing_status: "completed"
212
+ }))
213
+ };
214
+ }
215
+ if (Date.now() >= deadline) {
216
+ throw new Error(`batch was not searchable within ${timeoutMs}ms`);
217
+ }
218
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
219
+ }
220
+ }
147
221
  /**
148
222
  * List memories
149
223
  */
@@ -366,56 +440,78 @@ var RLResource = class extends BaseResource {
366
440
  }
367
441
  };
368
442
  var ProceduralResource = class extends BaseResource {
443
+ unwrapProcedure(response) {
444
+ if (response?.procedure) return response.procedure;
445
+ if (response?.procedure_id && !response?.id) {
446
+ return { ...response, id: response.procedure_id };
447
+ }
448
+ return response;
449
+ }
369
450
  /**
370
451
  * Create a new procedure
371
452
  */
372
453
  async create(params) {
373
- return this.client.post("/procedural", {
454
+ const response = await this.client.post("/v1/procedures", {
374
455
  name: params.name,
375
456
  description: params.description,
376
- trigger_condition: params.trigger_condition,
377
- action_sequence: params.action_sequence,
457
+ condition: { expression: params.trigger_condition },
458
+ action: { steps: params.action_sequence },
378
459
  collection_id: params.collection_id,
379
460
  category: params.category,
380
- metadata: params.metadata || {}
461
+ parameters: params.metadata || {}
381
462
  });
463
+ return this.unwrapProcedure(response);
382
464
  }
383
465
  /**
384
466
  * List procedures
385
467
  */
386
468
  async list(params) {
387
- return this.client.get("/procedural", {
469
+ const response = await this.client.get("/v1/procedures", {
388
470
  collection_id: params?.collection_id,
389
471
  category: params?.category,
390
472
  skip: params?.skip || 0,
391
473
  limit: params?.limit || 100
392
474
  });
475
+ return Array.isArray(response) ? response : response?.procedures || [];
393
476
  }
394
477
  /**
395
478
  * Get a specific procedure
396
479
  */
397
480
  async get(procedureId) {
398
- return this.client.get(`/procedural/${procedureId}`);
481
+ const response = await this.client.get(`/v1/procedures/${procedureId}`);
482
+ return this.unwrapProcedure(response);
399
483
  }
400
484
  /**
401
485
  * Execute a procedure
402
486
  */
403
487
  async execute(procedureId, context) {
404
- return this.client.post(`/procedural/${procedureId}/execute`, {
405
- context: context || {}
488
+ const response = await this.client.post(`/v1/procedures/${procedureId}/execute`, {
489
+ input_state: context || {}
406
490
  });
491
+ return response?.execution_result || response;
407
492
  }
408
493
  /**
409
494
  * Update a procedure
410
495
  */
411
496
  async update(procedureId, params) {
412
- return this.client.patch(`/procedural/${procedureId}`, params);
497
+ const body = {};
498
+ if (params.name !== void 0) body.name = params.name;
499
+ if (params.description !== void 0) body.description = params.description;
500
+ if (params.trigger_condition !== void 0) {
501
+ body.condition = { expression: params.trigger_condition };
502
+ }
503
+ if (params.action_sequence !== void 0) {
504
+ body.action = { steps: params.action_sequence };
505
+ }
506
+ if (params.metadata !== void 0) body.parameters = params.metadata;
507
+ const response = await this.client.patch(`/v1/procedures/${procedureId}`, body);
508
+ return this.unwrapProcedure(response);
413
509
  }
414
510
  /**
415
511
  * Delete a procedure
416
512
  */
417
513
  async delete(procedureId) {
418
- await this.client.delete(`/procedural/${procedureId}`);
514
+ await this.client.delete(`/v1/procedures/${procedureId}`);
419
515
  }
420
516
  };
421
517
  var TemporalResource = class extends BaseResource {
@@ -454,6 +550,10 @@ var TemporalResource = class extends BaseResource {
454
550
  entity
455
551
  });
456
552
  }
553
+ /** Permanently delete a tenant-scoped temporal fact by stable ID. */
554
+ async deleteFact(factId) {
555
+ return this.client.delete(`/temporal/facts/${factId}`);
556
+ }
457
557
  };
458
558
  var WorkingMemoryResource = class extends BaseResource {
459
559
  /**
@@ -637,7 +737,7 @@ var MemoryClient = class {
637
737
  getHeaders() {
638
738
  const headers = {
639
739
  "Content-Type": "application/json",
640
- "User-Agent": "hebbrix-typescript/2.2.0"
740
+ "User-Agent": "hebbrix-typescript/2.2.1"
641
741
  };
642
742
  if (this.apiKey) {
643
743
  headers["Authorization"] = `Bearer ${this.apiKey}`;
@@ -646,7 +746,8 @@ var MemoryClient = class {
646
746
  }
647
747
  handleError(response, data) {
648
748
  const statusCode = response.status;
649
- const message = data?.error?.message || data?.detail || response.statusText;
749
+ const detail = data?.detail;
750
+ const message = data?.error?.message || (typeof detail === "string" ? detail : detail?.message) || response.statusText;
650
751
  if (statusCode === 401) {
651
752
  throw new AuthenticationError(message);
652
753
  } else if (statusCode === 404) {
@@ -664,21 +765,29 @@ var MemoryClient = class {
664
765
  }
665
766
  async request(method, path, options = {}) {
666
767
  const url = `${this.baseUrl}${path}`;
768
+ const { headers: requestHeaders, signal: requestSignal, ...requestOptions } = options;
667
769
  const response = await fetch(url, {
770
+ ...requestOptions,
668
771
  method,
669
772
  headers: {
670
773
  ...this.getHeaders(),
671
- ...options.headers || {}
774
+ ...requestHeaders || {}
672
775
  },
673
- ...options,
674
- signal: AbortSignal.timeout(this.timeout)
776
+ signal: requestSignal || AbortSignal.timeout(this.timeout)
675
777
  });
676
- let data;
778
+ let data = void 0;
677
779
  const contentType = response.headers.get("content-type");
678
- if (contentType?.includes("application/json")) {
679
- data = await response.json();
680
- } else {
681
- data = await response.text();
780
+ const responseText = await response.text();
781
+ if (responseText) {
782
+ if (contentType?.includes("application/json")) {
783
+ try {
784
+ data = JSON.parse(responseText);
785
+ } catch {
786
+ data = { detail: responseText };
787
+ }
788
+ } else {
789
+ data = responseText;
790
+ }
682
791
  }
683
792
  if (!response.ok) {
684
793
  this.handleError(response, data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hebbrix",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
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",
@@ -14,7 +14,8 @@
14
14
  },
15
15
  "files": [
16
16
  "dist",
17
- "README.md"
17
+ "README.md",
18
+ "CHANGELOG.md"
18
19
  ],
19
20
  "scripts": {
20
21
  "build": "tsup src/index.ts --format cjs,esm --dts",