@hexis-ai/engram-server 0.5.0 → 0.6.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.
@@ -1,5 +1,5 @@
1
1
  import type { Session } from "@hexis-ai/engram-core";
2
- import type { AliasInfo, AliasUpsert, PersonCreate, PersonInfo, PersonUpdate, SessionEvent, SessionInit } from "@hexis-ai/engram-sdk";
2
+ import type { AliasInfo, AliasUpsert, IdentityInfo, IdentityUpsert, PersonCreate, PersonInfo, PersonUpdate, SessionEvent, SessionInit } from "@hexis-ai/engram-sdk";
3
3
  import { type StorageAdapter } from "../storage";
4
4
  export interface InMemoryAdapterOptions {
5
5
  /** Override for tests. Default: `p_${random}` with 8 chars. */
@@ -14,6 +14,8 @@ export declare class InMemoryAdapter implements StorageAdapter {
14
14
  private readonly persons;
15
15
  /** Keyed by `${personId} ${name.toLowerCase()}` — see `aliasKey` below. */
16
16
  private readonly aliases;
17
+ /** Keyed by ref (e.g. `slack:U12345`). */
18
+ private readonly identities;
17
19
  private readonly newPersonId;
18
20
  constructor(opts?: InMemoryAdapterOptions);
19
21
  createSession(init: SessionInit & {
@@ -45,4 +47,7 @@ export declare class InMemoryAdapter implements StorageAdapter {
45
47
  name: string;
46
48
  } & AliasUpsert): Promise<AliasInfo | null>;
47
49
  listAliases(personId: string): Promise<AliasInfo[]>;
50
+ upsertIdentity(ref: string, input: IdentityUpsert): Promise<IdentityInfo | null>;
51
+ getIdentityByRef(ref: string): Promise<IdentityInfo | null>;
52
+ listIdentitiesByPerson(personId: string): Promise<IdentityInfo[]>;
48
53
  }
Binary file
@@ -1,5 +1,5 @@
1
1
  import type { Session } from "@hexis-ai/engram-core";
2
- import type { AliasInfo, AliasUpsert, PersonCreate, PersonInfo, PersonUpdate, SessionEvent, SessionInit } from "@hexis-ai/engram-sdk";
2
+ import type { AliasInfo, AliasUpsert, IdentityInfo, IdentityUpsert, PersonCreate, PersonInfo, PersonUpdate, SessionEvent, SessionInit } from "@hexis-ai/engram-sdk";
3
3
  import { type StorageAdapter } from "../storage";
4
4
  /**
5
5
  * Minimal subset of `postgres` driver's tagged-template surface that this
@@ -60,4 +60,7 @@ export declare class PostgresAdapter implements StorageAdapter {
60
60
  name: string;
61
61
  } & AliasUpsert): Promise<AliasInfo | null>;
62
62
  listAliases(personId: string): Promise<AliasInfo[]>;
63
+ upsertIdentity(ref: string, input: IdentityUpsert): Promise<IdentityInfo | null>;
64
+ getIdentityByRef(ref: string): Promise<IdentityInfo | null>;
65
+ listIdentitiesByPerson(personId: string): Promise<IdentityInfo[]>;
63
66
  }
@@ -288,6 +288,66 @@ export class PostgresAdapter {
288
288
  `;
289
289
  return rows.map(toAliasInfo);
290
290
  }
291
+ // --- Identities ---------------------------------------------------
292
+ async upsertIdentity(ref, input) {
293
+ // Pre-check rather than rely on the FK so unknown persons return
294
+ // `null` instead of throwing a constraint violation.
295
+ const personExists = await this.sql `
296
+ SELECT id FROM engram_persons
297
+ WHERE workspace_id = ${this.workspaceId} AND id = ${input.person_id}
298
+ LIMIT 1
299
+ `;
300
+ if (personExists.length === 0)
301
+ return null;
302
+ const rows = await this.sql `
303
+ INSERT INTO engram_identities (
304
+ workspace_id, ref, person_id, service, external_id,
305
+ display_name, source, is_primary, picture, linked_at
306
+ )
307
+ VALUES (
308
+ ${this.workspaceId}, ${ref}, ${input.person_id},
309
+ ${input.service}, ${input.external_id},
310
+ ${input.display_name ?? null}, ${input.source ?? null},
311
+ ${input.is_primary ?? null}, ${input.picture ?? null},
312
+ ${input.linked_at}
313
+ )
314
+ ON CONFLICT (workspace_id, ref) DO UPDATE SET
315
+ person_id = EXCLUDED.person_id,
316
+ service = EXCLUDED.service,
317
+ external_id = EXCLUDED.external_id,
318
+ display_name = COALESCE(EXCLUDED.display_name, engram_identities.display_name),
319
+ source = COALESCE(EXCLUDED.source, engram_identities.source),
320
+ is_primary = COALESCE(EXCLUDED.is_primary, engram_identities.is_primary),
321
+ picture = COALESCE(EXCLUDED.picture, engram_identities.picture),
322
+ linked_at = EXCLUDED.linked_at,
323
+ updated_at = now()
324
+ RETURNING ref, person_id, service, external_id, display_name, source,
325
+ is_primary, picture, linked_at, created_at, updated_at
326
+ `;
327
+ return toIdentityInfo(rows[0]);
328
+ }
329
+ async getIdentityByRef(ref) {
330
+ const rows = await this.sql `
331
+ SELECT ref, person_id, service, external_id, display_name, source,
332
+ is_primary, picture, linked_at, created_at, updated_at
333
+ FROM engram_identities
334
+ WHERE workspace_id = ${this.workspaceId} AND ref = ${ref}
335
+ LIMIT 1
336
+ `;
337
+ if (rows.length === 0)
338
+ return null;
339
+ return toIdentityInfo(rows[0]);
340
+ }
341
+ async listIdentitiesByPerson(personId) {
342
+ const rows = await this.sql `
343
+ SELECT ref, person_id, service, external_id, display_name, source,
344
+ is_primary, picture, linked_at, created_at, updated_at
345
+ FROM engram_identities
346
+ WHERE workspace_id = ${this.workspaceId} AND person_id = ${personId}
347
+ ORDER BY linked_at DESC
348
+ `;
349
+ return rows.map(toIdentityInfo);
350
+ }
291
351
  }
292
352
  function toPersonInfo(r) {
293
353
  const toIso = (v) => (typeof v === "string" ? v : v.toISOString());
@@ -315,3 +375,22 @@ function toAliasInfo(r) {
315
375
  updated_at: toIso(r.updated_at),
316
376
  };
317
377
  }
378
+ function toIdentityInfo(r) {
379
+ const toIso = (v) => (typeof v === "string" ? v : v.toISOString());
380
+ const linkedAt = typeof r.linked_at === "string"
381
+ ? r.linked_at.slice(0, 10)
382
+ : r.linked_at.toISOString().slice(0, 10);
383
+ return {
384
+ ref: r.ref,
385
+ person_id: r.person_id,
386
+ service: r.service,
387
+ external_id: r.external_id,
388
+ display_name: r.display_name,
389
+ source: r.source,
390
+ is_primary: r.is_primary,
391
+ picture: r.picture,
392
+ linked_at: linkedAt,
393
+ created_at: toIso(r.created_at),
394
+ updated_at: toIso(r.updated_at),
395
+ };
396
+ }
@@ -0,0 +1,2 @@
1
+ export declare const name = "0003-identities";
2
+ export declare const sql = "\n-- External identity \u2192 person mapping. Mirrors monet's `identities`\n-- table at the canonical-engram level so identity resolution\n-- (slack:U12345 \u2192 person id, email:foo@bar \u2192 person id, \u2026) can move\n-- out of monet once consumers catch up.\n--\n-- Ref is workspace-scoped here even though monet's PK is just `ref`;\n-- engram is multi-tenant from the floor up.\nCREATE TABLE IF NOT EXISTS engram_identities (\n workspace_id TEXT NOT NULL,\n ref TEXT NOT NULL,\n person_id TEXT NOT NULL,\n service TEXT NOT NULL,\n external_id TEXT NOT NULL,\n display_name TEXT,\n source TEXT,\n is_primary BOOLEAN,\n picture TEXT,\n linked_at DATE NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n PRIMARY KEY (workspace_id, ref),\n FOREIGN KEY (workspace_id, person_id)\n REFERENCES engram_persons(workspace_id, id) ON DELETE CASCADE\n);\n\n-- The two query shapes the resolver needs.\nCREATE INDEX IF NOT EXISTS idx_engram_identities_person\n ON engram_identities (workspace_id, person_id);\nCREATE INDEX IF NOT EXISTS idx_engram_identities_service_external\n ON engram_identities (workspace_id, service, external_id);\n";
@@ -0,0 +1,33 @@
1
+ export const name = "0003-identities";
2
+ export const sql = `
3
+ -- External identity → person mapping. Mirrors monet's \`identities\`
4
+ -- table at the canonical-engram level so identity resolution
5
+ -- (slack:U12345 → person id, email:foo@bar → person id, …) can move
6
+ -- out of monet once consumers catch up.
7
+ --
8
+ -- Ref is workspace-scoped here even though monet's PK is just \`ref\`;
9
+ -- engram is multi-tenant from the floor up.
10
+ CREATE TABLE IF NOT EXISTS engram_identities (
11
+ workspace_id TEXT NOT NULL,
12
+ ref TEXT NOT NULL,
13
+ person_id TEXT NOT NULL,
14
+ service TEXT NOT NULL,
15
+ external_id TEXT NOT NULL,
16
+ display_name TEXT,
17
+ source TEXT,
18
+ is_primary BOOLEAN,
19
+ picture TEXT,
20
+ linked_at DATE NOT NULL,
21
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
22
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
23
+ PRIMARY KEY (workspace_id, ref),
24
+ FOREIGN KEY (workspace_id, person_id)
25
+ REFERENCES engram_persons(workspace_id, id) ON DELETE CASCADE
26
+ );
27
+
28
+ -- The two query shapes the resolver needs.
29
+ CREATE INDEX IF NOT EXISTS idx_engram_identities_person
30
+ ON engram_identities (workspace_id, person_id);
31
+ CREATE INDEX IF NOT EXISTS idx_engram_identities_service_external
32
+ ON engram_identities (workspace_id, service, external_id);
33
+ `;
@@ -1,5 +1,6 @@
1
1
  import * as m0001 from "./0001-baseline";
2
2
  import * as m0002 from "./0002-aliases";
3
+ import * as m0003 from "./0003-identities";
3
4
  /**
4
5
  * Schema migrations, applied in array order. Add a new file under
5
6
  * `migrations/NNNN-<slug>.ts` exporting `name` and `sql`, then append it
@@ -10,4 +11,5 @@ import * as m0002 from "./0002-aliases";
10
11
  export const MIGRATIONS = [
11
12
  { name: m0001.name, sql: m0001.sql },
12
13
  { name: m0002.name, sql: m0002.sql },
14
+ { name: m0003.name, sql: m0003.sql },
13
15
  ];
package/dist/openapi.js CHANGED
@@ -58,6 +58,10 @@ const TAG_DEFS = [
58
58
  { name: "Sessions", description: "セッションの作成・取得・イベント追加" },
59
59
  { name: "Search", description: "ワークスペースコーパスへのスコアリング検索" },
60
60
  { name: "Persons", description: "person の作成・更新・検索" },
61
+ {
62
+ name: "Identities",
63
+ description: "外部 ID(slack: / email: など)の resolve と upsert",
64
+ },
61
65
  {
62
66
  name: "Workspaces (admin)",
63
67
  description: "ワークスペースの管理(管理者トークン必須)",
@@ -257,6 +261,38 @@ function buildPaths() {
257
261
  },
258
262
  },
259
263
  }),
264
+ "/v1/persons/{id}/identities": tagged("Persons", {
265
+ get: {
266
+ summary: "この person のすべての identity を取得する(直近 link が先頭)。",
267
+ parameters: [pathParam("id", "person id。")],
268
+ responses: {
269
+ "200": res("identity 一覧"),
270
+ "401": res("認証エラー"),
271
+ },
272
+ },
273
+ }),
274
+ "/v1/identities/{ref}": tagged("Identities", {
275
+ put: {
276
+ summary: "ref(例: `slack:U12345`、`email:foo@bar.com`)で identity を upsert する。",
277
+ parameters: [pathParam("ref", "global identity ref(URL-encoded)。")],
278
+ requestBody: jsonBody("IdentityUpsert"),
279
+ responses: {
280
+ "200": res("upsert された identity"),
281
+ "400": res("リクエストボディが不正"),
282
+ "404": res("person_id が見つからない"),
283
+ "401": res("認証エラー"),
284
+ },
285
+ },
286
+ get: {
287
+ summary: "ref から identity を resolve する。",
288
+ parameters: [pathParam("ref", "global identity ref(URL-encoded)。")],
289
+ responses: {
290
+ "200": res("identity 情報"),
291
+ "404": res("ref が見つからない"),
292
+ "401": res("認証エラー"),
293
+ },
294
+ },
295
+ }),
260
296
  "/admin/v1/workspaces": tagged("Workspaces (admin)", {
261
297
  post: {
262
298
  summary: "ワークスペースを作成する(デフォルトで初期キーも発行する)。",
@@ -0,0 +1,14 @@
1
+ import { Hono } from "hono";
2
+ import type { Env } from "../context";
3
+ import type { RouteConfig } from "./helpers";
4
+ /**
5
+ * Identity routes. Mount under `/v1`:
6
+ * PUT /v1/identities/:ref upsert by global ref (e.g. `slack:U12345`)
7
+ * GET /v1/identities/:ref resolve a ref to its identity record
8
+ *
9
+ * The "list this person's identities" endpoint is exposed by the
10
+ * persons routes at `/v1/persons/:id/identities` because that's the
11
+ * natural client mental model — start from a person, fan out — not
12
+ * start from a ref.
13
+ */
14
+ export declare function identitiesRoutes(_cfg: RouteConfig): Hono<Env>;
@@ -0,0 +1,33 @@
1
+ import { Hono } from "hono";
2
+ import { identityUpsertSchema, parseJsonBody } from "../schemas";
3
+ /**
4
+ * Identity routes. Mount under `/v1`:
5
+ * PUT /v1/identities/:ref upsert by global ref (e.g. `slack:U12345`)
6
+ * GET /v1/identities/:ref resolve a ref to its identity record
7
+ *
8
+ * The "list this person's identities" endpoint is exposed by the
9
+ * persons routes at `/v1/persons/:id/identities` because that's the
10
+ * natural client mental model — start from a person, fan out — not
11
+ * start from a ref.
12
+ */
13
+ export function identitiesRoutes(_cfg) {
14
+ const app = new Hono();
15
+ app.put("/identities/:ref", async (c) => {
16
+ const ref = c.req.param("ref");
17
+ const body = await parseJsonBody(c, identityUpsertSchema);
18
+ if (body instanceof Response)
19
+ return body;
20
+ const identity = await c.var.ctx.storage.upsertIdentity(ref, body);
21
+ if (!identity)
22
+ return c.json({ error: "person_not_found" }, 404);
23
+ return c.json(identity);
24
+ });
25
+ app.get("/identities/:ref", async (c) => {
26
+ const ref = c.req.param("ref");
27
+ const identity = await c.var.ctx.storage.getIdentityByRef(ref);
28
+ if (!identity)
29
+ return c.json({ error: "identity_not_found" }, 404);
30
+ return c.json(identity);
31
+ });
32
+ return app;
33
+ }
@@ -68,6 +68,11 @@ export function personsRoutes(cfg) {
68
68
  const aliases = await c.var.ctx.storage.listAliases(id);
69
69
  return c.json({ aliases });
70
70
  });
71
+ app.get("/persons/:id/identities", async (c) => {
72
+ const id = c.req.param("id");
73
+ const identities = await c.var.ctx.storage.listIdentitiesByPerson(id);
74
+ return c.json({ identities });
75
+ });
71
76
  app.get("/persons/:id/sessions", async (c) => {
72
77
  const id = c.req.param("id");
73
78
  const limit = clampLimit(c, cfg.defaultListLimit, cfg.maxListLimit);
package/dist/schemas.d.ts CHANGED
@@ -71,6 +71,16 @@ export declare const aliasUpsertSchema: z.ZodObject<{
71
71
  last_used: z.ZodString;
72
72
  increment: z.ZodOptional<z.ZodBoolean>;
73
73
  }, z.core.$strip>;
74
+ export declare const identityUpsertSchema: z.ZodObject<{
75
+ person_id: z.ZodString;
76
+ service: z.ZodString;
77
+ external_id: z.ZodString;
78
+ display_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
79
+ source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
80
+ is_primary: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
81
+ picture: z.ZodOptional<z.ZodNullable<z.ZodString>>;
82
+ linked_at: z.ZodString;
83
+ }, z.core.$strip>;
74
84
  export declare const searchRequestSchema: z.ZodObject<{
75
85
  query: z.ZodUnion<readonly [z.ZodObject<{
76
86
  sessionId: z.ZodString;
package/dist/schemas.js CHANGED
@@ -66,6 +66,19 @@ export const aliasUpsertSchema = z.object({
66
66
  .regex(/^\d{4}-\d{2}-\d{2}/, "expected YYYY-MM-DD"),
67
67
  increment: z.boolean().optional(),
68
68
  });
69
+ // --- Identities -------------------------------------------------------
70
+ export const identityUpsertSchema = z.object({
71
+ person_id: z.string().min(1),
72
+ service: z.string().min(1),
73
+ external_id: z.string().min(1),
74
+ display_name: z.string().nullable().optional(),
75
+ source: z.string().nullable().optional(),
76
+ is_primary: z.boolean().nullable().optional(),
77
+ picture: z.string().nullable().optional(),
78
+ linked_at: z
79
+ .string()
80
+ .regex(/^\d{4}-\d{2}-\d{2}/, "expected YYYY-MM-DD"),
81
+ });
69
82
  // --- Search ----------------------------------------------------------
70
83
  const searchQuerySchema = z.union([
71
84
  z.object({ sessionId: z.string().min(1) }),
package/dist/server.js CHANGED
@@ -3,6 +3,7 @@ import { log, newRequestId } from "./logger";
3
3
  import { createAdminRouter } from "./admin";
4
4
  import { sessionsRoutes } from "./routes/sessions";
5
5
  import { personsRoutes } from "./routes/persons";
6
+ import { identitiesRoutes } from "./routes/identities";
6
7
  import { searchRoutes } from "./routes/search";
7
8
  /**
8
9
  * Build the engram HTTP app. Wiring only: this sets up cross-cutting
@@ -60,6 +61,8 @@ export function createServer(opts) {
60
61
  personSessions: "GET /v1/persons/:id/sessions",
61
62
  personAliases: "GET /v1/persons/:id/aliases",
62
63
  upsertAlias: "PUT /v1/persons/:id/aliases/:name",
64
+ identityByRef: "GET/PUT /v1/identities/:ref",
65
+ personIdentities: "GET /v1/persons/:id/identities",
63
66
  },
64
67
  }));
65
68
  app.get("/healthz", (c) => c.json({ ok: true }));
@@ -84,6 +87,7 @@ export function createServer(opts) {
84
87
  app.get("/v1/me", (c) => c.json({ workspaceId: c.var.ctx.workspaceId }));
85
88
  app.route("/v1", sessionsRoutes(cfg));
86
89
  app.route("/v1", personsRoutes(cfg));
90
+ app.route("/v1", identitiesRoutes(cfg));
87
91
  app.route("/v1", searchRoutes(cfg));
88
92
  return app;
89
93
  }
package/dist/storage.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Session } from "@hexis-ai/engram-core";
2
- import type { AliasInfo, AliasUpsert, PersonCreate, PersonInfo, PersonUpdate, SessionEvent, SessionInit } from "@hexis-ai/engram-sdk";
2
+ import type { AliasInfo, AliasUpsert, IdentityInfo, IdentityUpsert, PersonCreate, PersonInfo, PersonUpdate, SessionEvent, SessionInit } from "@hexis-ai/engram-sdk";
3
3
  /**
4
4
  * Storage adapter interface. Each implementation owns persistence for
5
5
  * a single workspace's sessions and persons. Multi-tenancy is the host's
@@ -77,6 +77,19 @@ export interface StorageAdapter {
77
77
  } & AliasUpsert): Promise<AliasInfo | null>;
78
78
  /** A person's aliases, ordered newest-used-first. */
79
79
  listAliases(personId: string): Promise<AliasInfo[]>;
80
+ /**
81
+ * Upsert by `ref` (e.g. `slack:U12345`). Idempotent: writing the same
82
+ * ref multiple times converges. If the ref points to a different
83
+ * person than before, the row's `person_id` is updated — refs are
84
+ * the global handle, persons are reassignable underneath.
85
+ *
86
+ * Returns `null` if `input.person_id` does not exist.
87
+ */
88
+ upsertIdentity(ref: string, input: IdentityUpsert): Promise<IdentityInfo | null>;
89
+ /** Lookup by ref. Returns null if unknown. */
90
+ getIdentityByRef(ref: string): Promise<IdentityInfo | null>;
91
+ /** All identities for a person, ordered newest-linked-first. */
92
+ listIdentitiesByPerson(personId: string): Promise<IdentityInfo[]>;
80
93
  }
81
94
  /**
82
95
  * Pure fold of an event log into the parts a Session needs. Used by adapters
package/openapi.json CHANGED
@@ -22,6 +22,10 @@
22
22
  "name": "Persons",
23
23
  "description": "person の作成・更新・検索"
24
24
  },
25
+ {
26
+ "name": "Identities",
27
+ "description": "外部 ID(slack: / email: など)の resolve と upsert"
28
+ },
25
29
  {
26
30
  "name": "Workspaces (admin)",
27
31
  "description": "ワークスペースの管理(管理者トークン必須)"
@@ -877,6 +881,104 @@
877
881
  ]
878
882
  }
879
883
  },
884
+ "/v1/persons/{id}/identities": {
885
+ "get": {
886
+ "summary": "この person のすべての identity を取得する(直近 link が先頭)。",
887
+ "parameters": [
888
+ {
889
+ "name": "id",
890
+ "in": "path",
891
+ "required": true,
892
+ "schema": {
893
+ "type": "string"
894
+ },
895
+ "description": "person id。"
896
+ }
897
+ ],
898
+ "responses": {
899
+ "200": {
900
+ "description": "identity 一覧"
901
+ },
902
+ "401": {
903
+ "description": "認証エラー"
904
+ }
905
+ },
906
+ "tags": [
907
+ "Persons"
908
+ ]
909
+ }
910
+ },
911
+ "/v1/identities/{ref}": {
912
+ "put": {
913
+ "summary": "ref(例: `slack:U12345`、`email:foo@bar.com`)で identity を upsert する。",
914
+ "parameters": [
915
+ {
916
+ "name": "ref",
917
+ "in": "path",
918
+ "required": true,
919
+ "schema": {
920
+ "type": "string"
921
+ },
922
+ "description": "global identity ref(URL-encoded)。"
923
+ }
924
+ ],
925
+ "requestBody": {
926
+ "required": true,
927
+ "content": {
928
+ "application/json": {
929
+ "schema": {
930
+ "$ref": "#/components/schemas/IdentityUpsert"
931
+ }
932
+ }
933
+ }
934
+ },
935
+ "responses": {
936
+ "200": {
937
+ "description": "upsert された identity"
938
+ },
939
+ "400": {
940
+ "description": "リクエストボディが不正"
941
+ },
942
+ "401": {
943
+ "description": "認証エラー"
944
+ },
945
+ "404": {
946
+ "description": "person_id が見つからない"
947
+ }
948
+ },
949
+ "tags": [
950
+ "Identities"
951
+ ]
952
+ },
953
+ "get": {
954
+ "summary": "ref から identity を resolve する。",
955
+ "parameters": [
956
+ {
957
+ "name": "ref",
958
+ "in": "path",
959
+ "required": true,
960
+ "schema": {
961
+ "type": "string"
962
+ },
963
+ "description": "global identity ref(URL-encoded)。"
964
+ }
965
+ ],
966
+ "responses": {
967
+ "200": {
968
+ "description": "identity 情報"
969
+ },
970
+ "401": {
971
+ "description": "認証エラー"
972
+ },
973
+ "404": {
974
+ "description": "ref が見つからない"
975
+ }
976
+ },
977
+ "tags": [
978
+ "Identities"
979
+ ]
980
+ }
981
+ },
880
982
  "/admin/v1/workspaces": {
881
983
  "post": {
882
984
  "summary": "ワークスペースを作成する(デフォルトで初期キーも発行する)。",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexis-ai/engram-server",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
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.1.5",
53
- "@hexis-ai/engram-sdk": "^0.4.0",
53
+ "@hexis-ai/engram-sdk": "^0.5.0",
54
54
  "hono": "^4.6.0",
55
55
  "zod": "^4.0.0"
56
56
  },