@hexis-ai/engram-server 0.10.1 → 0.11.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.
@@ -29,7 +29,11 @@ export declare class InMemoryAdapter implements StorageAdapter {
29
29
  listSessions(opts: {
30
30
  limit: number;
31
31
  channel?: string;
32
+ channel_prefix?: string;
32
33
  status?: "active" | "idle" | "completed";
34
+ has_trigger?: boolean;
35
+ no_summary?: boolean;
36
+ updated_before?: string;
33
37
  }): Promise<Session[]>;
34
38
  sessionsForPerson(personId: string, opts: {
35
39
  limit: number;
@@ -147,8 +147,17 @@ export class InMemoryAdapter {
147
147
  for (const stored of this.sessions.values()) {
148
148
  if (opts.channel && stored.row.channel !== opts.channel)
149
149
  continue;
150
+ if (opts.channel_prefix &&
151
+ !(stored.row.channel ?? "").startsWith(opts.channel_prefix))
152
+ continue;
150
153
  if (opts.status && stored.row.status !== opts.status)
151
154
  continue;
155
+ if (opts.has_trigger && stored.row.trigger_conversation_id == null)
156
+ continue;
157
+ if (opts.no_summary && stored.row.summary != null)
158
+ continue;
159
+ if (opts.updated_before && stored.row.updatedAt >= opts.updated_before)
160
+ continue;
152
161
  all.push({
153
162
  s: foldEvents(stored.row, [...stored.events.values()], now),
154
163
  updatedAt: stored.row.updatedAt,
@@ -42,7 +42,11 @@ export declare class PostgresAdapter implements StorageAdapter {
42
42
  listSessions(opts: {
43
43
  limit: number;
44
44
  channel?: string;
45
+ channel_prefix?: string;
45
46
  status?: "active" | "idle" | "completed";
47
+ has_trigger?: boolean;
48
+ no_summary?: boolean;
49
+ updated_before?: string;
46
50
  }): Promise<Session[]>;
47
51
  sessionsForPerson(personId: string, opts: {
48
52
  limit: number;
@@ -173,12 +173,20 @@ export class PostgresAdapter {
173
173
  }
174
174
  async listSessions(opts) {
175
175
  const channelFilter = opts.channel ?? null;
176
+ const channelPrefix = opts.channel_prefix ?? null;
176
177
  const statusFilter = opts.status ?? null;
178
+ const hasTrigger = opts.has_trigger === true;
179
+ const noSummary = opts.no_summary === true;
180
+ const updatedBefore = opts.updated_before ?? null;
177
181
  const rows = await this.sql `
178
182
  SELECT id FROM engram_sessions
179
183
  WHERE workspace_id = ${this.workspaceId}
180
184
  AND (${channelFilter}::text IS NULL OR channel = ${channelFilter}::text)
185
+ AND (${channelPrefix}::text IS NULL OR channel LIKE ${channelPrefix ? channelPrefix + "%" : null}::text)
181
186
  AND (${statusFilter}::text IS NULL OR status = ${statusFilter}::text)
187
+ AND (${hasTrigger}::boolean = FALSE OR trigger_conversation_id IS NOT NULL)
188
+ AND (${noSummary}::boolean = FALSE OR summary IS NULL)
189
+ AND (${updatedBefore}::timestamptz IS NULL OR updated_at < ${updatedBefore}::timestamptz)
182
190
  ORDER BY updated_at DESC
183
191
  LIMIT ${opts.limit}
184
192
  `;
@@ -63,14 +63,22 @@ export function sessionsRoutes(cfg) {
63
63
  app.get("/sessions", async (c) => {
64
64
  const limit = clampLimit(c, cfg.defaultListLimit, cfg.maxListLimit);
65
65
  const channel = c.req.query("channel") || undefined;
66
+ const channel_prefix = c.req.query("channel_prefix") || undefined;
66
67
  const rawStatus = c.req.query("status");
67
68
  const status = rawStatus === "active" || rawStatus === "idle" || rawStatus === "completed"
68
69
  ? rawStatus
69
70
  : undefined;
71
+ const has_trigger = c.req.query("has_trigger") === "true" || undefined;
72
+ const no_summary = c.req.query("no_summary") === "true" || undefined;
73
+ const updated_before = c.req.query("updated_before") || undefined;
70
74
  const sessions = await c.var.ctx.storage.listSessions({
71
75
  limit,
72
76
  channel,
77
+ channel_prefix,
73
78
  status,
79
+ has_trigger,
80
+ no_summary,
81
+ updated_before,
74
82
  });
75
83
  const persons = await resolvePersonMap(c.var.ctx.storage, sessions);
76
84
  return c.json({ sessions, persons });
package/dist/schemas.d.ts CHANGED
@@ -112,9 +112,15 @@ export declare const eventBatchSchema: z.ZodObject<{
112
112
  }, z.core.$strip>;
113
113
  export declare const personCreateSchema: z.ZodObject<{
114
114
  display_name: z.ZodOptional<z.ZodString>;
115
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
116
+ team: z.ZodOptional<z.ZodNullable<z.ZodString>>;
117
+ source: z.ZodOptional<z.ZodString>;
115
118
  }, z.core.$strip>;
116
119
  export declare const personUpdateSchema: z.ZodObject<{
117
120
  display_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
121
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
122
+ team: z.ZodOptional<z.ZodNullable<z.ZodString>>;
123
+ source: z.ZodOptional<z.ZodString>;
118
124
  }, z.core.$strip>;
119
125
  export declare const aliasUpsertSchema: z.ZodObject<{
120
126
  caller: z.ZodString;
package/dist/schemas.js CHANGED
@@ -90,9 +90,15 @@ export const eventBatchSchema = z.object({
90
90
  // --- Persons ---------------------------------------------------------
91
91
  export const personCreateSchema = z.object({
92
92
  display_name: z.string().optional(),
93
+ role: z.string().nullable().optional(),
94
+ team: z.string().nullable().optional(),
95
+ source: z.string().optional(),
93
96
  });
94
97
  export const personUpdateSchema = z.object({
95
98
  display_name: z.string().nullable().optional(),
99
+ role: z.string().nullable().optional(),
100
+ team: z.string().nullable().optional(),
101
+ source: z.string().optional(),
96
102
  });
97
103
  // --- Aliases ----------------------------------------------------------
98
104
  export const aliasUpsertSchema = z.object({
package/dist/storage.d.ts CHANGED
@@ -33,11 +33,27 @@ export interface StorageAdapter {
33
33
  * events, which returns `[]`).
34
34
  */
35
35
  getSessionEvents(sessionId: string): Promise<SessionEvent[] | null>;
36
- /** List recent sessions, ordered by `updated_at` desc. */
36
+ /**
37
+ * List recent sessions, ordered by `updated_at` desc.
38
+ *
39
+ * Filter combinations are AND'd. Pass `channel_prefix` instead of
40
+ * `channel` to match channels by prefix (e.g. `slack_dm:C123/`) —
41
+ * this is the only way to express "any thread under DM Cxxx".
42
+ * `has_trigger=true` restricts to sessions whose
43
+ * `trigger_conversation_id` is set; `no_summary=true` restricts to
44
+ * sessions whose `summary` is null. `updated_before` restricts to
45
+ * sessions whose `updated_at` is strictly less than the ISO timestamp
46
+ * — combine with `status='idle' + no_summary=true` to find idle,
47
+ * unsummarized conversations older than N minutes.
48
+ */
37
49
  listSessions(opts: {
38
50
  limit: number;
39
51
  channel?: string;
52
+ channel_prefix?: string;
40
53
  status?: "active" | "idle" | "completed";
54
+ has_trigger?: boolean;
55
+ no_summary?: boolean;
56
+ updated_before?: string;
41
57
  }): Promise<Session[]>;
42
58
  /**
43
59
  * Create a person with a freshly allocated id. The host (e.g. monet)
package/openapi.json CHANGED
@@ -340,6 +340,29 @@
340
340
  "properties": {
341
341
  "display_name": {
342
342
  "type": "string"
343
+ },
344
+ "role": {
345
+ "anyOf": [
346
+ {
347
+ "type": "string"
348
+ },
349
+ {
350
+ "type": "null"
351
+ }
352
+ ]
353
+ },
354
+ "team": {
355
+ "anyOf": [
356
+ {
357
+ "type": "string"
358
+ },
359
+ {
360
+ "type": "null"
361
+ }
362
+ ]
363
+ },
364
+ "source": {
365
+ "type": "string"
343
366
  }
344
367
  },
345
368
  "additionalProperties": false
@@ -356,6 +379,29 @@
356
379
  "type": "null"
357
380
  }
358
381
  ]
382
+ },
383
+ "role": {
384
+ "anyOf": [
385
+ {
386
+ "type": "string"
387
+ },
388
+ {
389
+ "type": "null"
390
+ }
391
+ ]
392
+ },
393
+ "team": {
394
+ "anyOf": [
395
+ {
396
+ "type": "string"
397
+ },
398
+ {
399
+ "type": "null"
400
+ }
401
+ ]
402
+ },
403
+ "source": {
404
+ "type": "string"
359
405
  }
360
406
  },
361
407
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexis-ai/engram-server",
3
- "version": "0.10.1",
3
+ "version": "0.11.1",
4
4
  "description": "Engram server: ingest agent session events, persist via a pluggable adapter, expose search.",
5
5
  "keywords": [
6
6
  "engram",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@hexis-ai/engram-core": "^0.2.0",
53
- "@hexis-ai/engram-sdk": "^0.9.1",
53
+ "@hexis-ai/engram-sdk": "^0.10.0",
54
54
  "hono": "^4.6.0",
55
55
  "zod": "^4.0.0"
56
56
  },