@hexis-ai/engram-sdk 0.11.0 → 0.12.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.
@@ -70,6 +70,47 @@ export type TelemetryEvent = {
70
70
  type: "end";
71
71
  at?: string;
72
72
  };
73
+ /**
74
+ * Person observation. Same Langfuse-style semantics as session():
75
+ * host-supplied id, idempotent on the engram side (PUT /v1/persons/:id).
76
+ * Subsequent calls with the same id COALESCE — partial-info updates
77
+ * never clobber richer fields set earlier.
78
+ */
79
+ export interface TelemetryPerson {
80
+ id: string;
81
+ displayName?: string;
82
+ role?: string | null;
83
+ team?: string | null;
84
+ source?: string;
85
+ }
86
+ /**
87
+ * Identity observation — links a global ref like `slack:U12345` or
88
+ * `email:foo@bar.com` to a person id. Idempotent (PUT
89
+ * /v1/identities/:ref); writing a new person_id reroutes the ref.
90
+ */
91
+ export interface TelemetryIdentity {
92
+ ref: string;
93
+ personId: string;
94
+ service: string;
95
+ externalId: string;
96
+ displayName?: string | null;
97
+ source?: string | null;
98
+ picture?: string | null;
99
+ isPrimary?: boolean | null;
100
+ linkedAt: string;
101
+ }
102
+ /**
103
+ * Alias observation — a name the person goes by. The engram side
104
+ * key-collapses on (person_id, lower(name)); same name twice with
105
+ * `increment: true` bumps usage_count (default).
106
+ */
107
+ export interface TelemetryAlias {
108
+ personId: string;
109
+ name: string;
110
+ caller: string;
111
+ lastUsed: string;
112
+ increment?: boolean;
113
+ }
73
114
  /**
74
115
  * Internal coordinator that owns per-session buffers and gates flushes
75
116
  * on session-creation acks. Exposed on the parent Engram via
@@ -94,6 +135,17 @@ export declare class BufferedTelemetry {
94
135
  message(input: TelemetryMessage): void;
95
136
  /** Observe an arbitrary session event (participant / title / step / end). */
96
137
  event(input: TelemetryEvent): void;
138
+ /**
139
+ * Observe a person. Unlike session events these don't fan out to a
140
+ * per-session buffer — each call is an immediate fire-and-forget
141
+ * PUT via the SDK's low-level `persons.upsert`. Transport failures
142
+ * surface via onError and are not retried.
143
+ */
144
+ person(input: TelemetryPerson): void;
145
+ /** Observe an identity (ref → person_id link). */
146
+ identity(input: TelemetryIdentity): void;
147
+ /** Observe an alias (person nickname). */
148
+ alias(input: TelemetryAlias): void;
97
149
  /**
98
150
  * Drain every per-session buffer in parallel. Safe to call at any
99
151
  * point; failures route to onError, never thrown.
package/dist/buffered.js CHANGED
@@ -76,6 +76,47 @@ export class BufferedTelemetry {
76
76
  }
77
77
  handle.event(input);
78
78
  }
79
+ /**
80
+ * Observe a person. Unlike session events these don't fan out to a
81
+ * per-session buffer — each call is an immediate fire-and-forget
82
+ * PUT via the SDK's low-level `persons.upsert`. Transport failures
83
+ * surface via onError and are not retried.
84
+ */
85
+ person(input) {
86
+ void this.engram.persons
87
+ .upsert(input.id, {
88
+ ...(input.displayName !== undefined ? { display_name: input.displayName } : {}),
89
+ ...(input.role !== undefined ? { role: input.role } : {}),
90
+ ...(input.team !== undefined ? { team: input.team } : {}),
91
+ ...(input.source !== undefined ? { source: input.source } : {}),
92
+ })
93
+ .catch((e) => this.engram.config.onError(e));
94
+ }
95
+ /** Observe an identity (ref → person_id link). */
96
+ identity(input) {
97
+ void this.engram.identities
98
+ .upsert(input.ref, {
99
+ person_id: input.personId,
100
+ service: input.service,
101
+ external_id: input.externalId,
102
+ ...(input.displayName !== undefined ? { display_name: input.displayName } : {}),
103
+ ...(input.source !== undefined ? { source: input.source } : {}),
104
+ ...(input.picture !== undefined ? { picture: input.picture } : {}),
105
+ ...(input.isPrimary !== undefined ? { is_primary: input.isPrimary } : {}),
106
+ linked_at: input.linkedAt,
107
+ })
108
+ .catch((e) => this.engram.config.onError(e));
109
+ }
110
+ /** Observe an alias (person nickname). */
111
+ alias(input) {
112
+ void this.engram.persons
113
+ .upsertAlias(input.personId, input.name, {
114
+ caller: input.caller,
115
+ last_used: input.lastUsed,
116
+ ...(input.increment !== undefined ? { increment: input.increment } : {}),
117
+ })
118
+ .catch((e) => this.engram.config.onError(e));
119
+ }
79
120
  /**
80
121
  * Drain every per-session buffer in parallel. Safe to call at any
81
122
  * point; failures route to onError, never thrown.
@@ -106,6 +147,12 @@ export class BufferedTelemetry {
106
147
  class BufferedSession {
107
148
  engram;
108
149
  id;
150
+ /**
151
+ * Resolves to `true` when POST /v1/sessions acked, `false` on
152
+ * transport failure. Never rejects — preserves Bun's strict
153
+ * unhandled-rejection contract even when callers don't await
154
+ * the buffer (Langfuse-style fire-and-forget).
155
+ */
109
156
  ready;
110
157
  inner;
111
158
  ended = false;
@@ -113,14 +160,12 @@ class BufferedSession {
113
160
  this.engram = engram;
114
161
  this.id = init.id;
115
162
  this.inner = new EngramSession(engram, init.id);
116
- // Fire-and-forget POST /v1/sessions. The ready promise is awaited
117
- // before the first flush so messages enqueued before the create
118
- // ack don't race.
119
163
  this.ready = engram
120
164
  .startSessionWithoutHandle(init)
165
+ .then(() => true)
121
166
  .catch((e) => {
122
167
  engram.config.onError(e);
123
- throw e;
168
+ return false;
124
169
  });
125
170
  }
126
171
  message(input) {
@@ -170,9 +215,6 @@ class BufferedSession {
170
215
  * info and the host now has more (e.g. resolved channel later).
171
216
  */
172
217
  update(input) {
173
- // Only patch the fields callers might actually mutate post-creation;
174
- // skip `participants` / `viewable_by` which are append-only via
175
- // participant events.
176
218
  const patch = {};
177
219
  if (input.title !== undefined)
178
220
  patch.title = input.title;
@@ -190,33 +232,28 @@ class BufferedSession {
190
232
  patch.trigger_event_id = input.trigger_event_id;
191
233
  if (Object.keys(patch).length === 0)
192
234
  return;
193
- void this.ready
194
- .then(() => this.engram.updateSession(this.id, patch))
195
- .catch((e) => this.engram.config.onError(e));
235
+ void this.ready.then((ok) => {
236
+ if (!ok)
237
+ return;
238
+ return this.engram
239
+ .updateSession(this.id, patch)
240
+ .catch((e) => this.engram.config.onError(e));
241
+ });
196
242
  }
197
243
  async flush() {
198
- // Don't fail downstream flushes if the create eventually succeeds
199
- // after a retry — but skip flushing if the create has permanently
200
- // failed (ready rejected).
201
- try {
202
- await this.ready;
203
- }
204
- catch {
244
+ const ok = await this.ready;
245
+ if (!ok)
205
246
  return;
206
- }
207
- await this.inner.flush();
247
+ await this.inner.flush().catch((e) => this.engram.config.onError(e));
208
248
  }
209
249
  async shutdown() {
210
250
  if (this.ended)
211
251
  return;
212
252
  this.ended = true;
213
- try {
214
- await this.ready;
215
- }
216
- catch {
253
+ const ok = await this.ready;
254
+ if (!ok)
217
255
  return;
218
- }
219
- await this.inner.end();
256
+ await this.inner.end().catch((e) => this.engram.config.onError(e));
220
257
  }
221
258
  }
222
259
  // --- Helpers --------------------------------------------------------------
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ScoredSession, SearchOptions, Session, SessionStep } from "@hexis-ai/engram-core";
2
2
  import { type RefCandidate } from "./extract";
3
3
  import type { AliasInfo, AliasUpsert, EventBatch, IdentityInfo, IdentityUpsert, MessageContentBlock, PersonCreate, PersonInfo, PersonMap, PersonUpdate, SessionEvent, SessionInit, SessionUpdate } from "./types";
4
- import { type TelemetryEvent, type TelemetryMessage, type TelemetrySession } from "./buffered";
4
+ import { type TelemetryAlias, type TelemetryEvent, type TelemetryIdentity, type TelemetryMessage, type TelemetryPerson, type TelemetrySession } from "./buffered";
5
5
  /**
6
6
  * Envelope returned by session endpoints. The persons map is deduped
7
7
  * across whatever sessions the response carries so display info isn't
@@ -117,6 +117,24 @@ export declare class Engram {
117
117
  * tool step, end). Same fire-and-forget contract.
118
118
  */
119
119
  event(input: TelemetryEvent): void;
120
+ /**
121
+ * Observe a person record (display_name / role / team / source).
122
+ * Idempotent on the engram side — partial-info updates COALESCE.
123
+ * Fire-and-forget; transport failures route to `onError`.
124
+ */
125
+ person(input: TelemetryPerson): void;
126
+ /**
127
+ * Observe an identity (`slack:U1`, `email:foo@bar.com`, …) and link
128
+ * it to a person id. Writing a new person_id to an existing ref
129
+ * reroutes the ref. Fire-and-forget.
130
+ */
131
+ identity(input: TelemetryIdentity): void;
132
+ /**
133
+ * Observe an alias (a name the person goes by). Engram side
134
+ * collapses on (person_id, lower(name)); same name twice with
135
+ * `increment: true` bumps usage_count. Fire-and-forget.
136
+ */
137
+ alias(input: TelemetryAlias): void;
120
138
  /**
121
139
  * Drain every per-session buffer. Call before short-lived processes
122
140
  * exit; long-lived servers can rely on auto-flush (flushIntervalMs).
package/dist/client.js CHANGED
@@ -60,6 +60,30 @@ export class Engram {
60
60
  event(input) {
61
61
  this.bufferedTelemetry.event(input);
62
62
  }
63
+ /**
64
+ * Observe a person record (display_name / role / team / source).
65
+ * Idempotent on the engram side — partial-info updates COALESCE.
66
+ * Fire-and-forget; transport failures route to `onError`.
67
+ */
68
+ person(input) {
69
+ this.bufferedTelemetry.person(input);
70
+ }
71
+ /**
72
+ * Observe an identity (`slack:U1`, `email:foo@bar.com`, …) and link
73
+ * it to a person id. Writing a new person_id to an existing ref
74
+ * reroutes the ref. Fire-and-forget.
75
+ */
76
+ identity(input) {
77
+ this.bufferedTelemetry.identity(input);
78
+ }
79
+ /**
80
+ * Observe an alias (a name the person goes by). Engram side
81
+ * collapses on (person_id, lower(name)); same name twice with
82
+ * `increment: true` bumps usage_count. Fire-and-forget.
83
+ */
84
+ alias(input) {
85
+ this.bufferedTelemetry.alias(input);
86
+ }
63
87
  /**
64
88
  * Drain every per-session buffer. Call before short-lived processes
65
89
  * exit; long-lived servers can rely on auto-flush (flushIntervalMs).
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { Engram, EngramSession, type EngramOptions, type RecordStepInput, type SearchRequest, type SearchResponse, type SearchEnvelope, type SessionEnvelope, type SessionListEnvelope, } from "./client";
2
2
  export { extractReferences, encodeResourceId, type RefCandidate, type ReferenceService, type ReferenceAction, } from "./extract";
3
3
  export { parseToolName, type ParsedToolName } from "./tool-name";
4
- export { BufferedTelemetry, type TelemetrySession, type TelemetryMessage, type TelemetryEvent, } from "./buffered";
4
+ export { BufferedTelemetry, type TelemetrySession, type TelemetryMessage, type TelemetryEvent, type TelemetryPerson, type TelemetryIdentity, type TelemetryAlias, } from "./buffered";
5
5
  export { fetchIdToken, cloudRunIdTokenAuth } from "./id-token";
6
6
  export { EngramAdmin, createAdminClient, type AdminClientOptions, type CreateWorkspaceInput, type CreateWorkspaceResult, type Workspace as AdminWorkspace, type ApiKey as AdminApiKey, type IssuedKey as AdminIssuedKey, } from "./admin";
7
7
  export type { SessionInit, SessionUpdate, SessionAck, SessionEvent, StepEvent, ParticipantEvent, TitleEvent, EndEvent, MessageContentBlock, MessageEvent, EventBatch, PersonInfo, PersonCreate, PersonUpdate, PersonMap, AliasInfo, AliasUpsert, IdentityInfo, IdentityUpsert, } from "./types";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexis-ai/engram-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "author": "hexis ltd.",
5
5
  "repository": {
6
6
  "type": "git",