@hivemagnus/hivemem-sdk 1.0.3 → 1.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@hivemagnus/hivemem-sdk`.
4
+
5
+ ## [1.1.0] - 2026-11-12
6
+
7
+ ### Added
8
+
9
+ - `createWorkspace(workspaceId, options)` calls `POST /v1/workspaces` and returns the
10
+ `WorkspaceRecord`. It is idempotent: an existing workspace the caller can read is returned
11
+ unchanged. New `WorkspaceRecord`, `WorkspaceMember`, `WorkspacePolicy` and
12
+ `WorkspaceCreateOptions` types.
13
+ - `tokenProvider` option for short-lived credentials such as Oathweave service tokens. It is
14
+ called before each request and, after a 401, called again with `forceRefresh: true` and the
15
+ request retried once. A static `token` keeps working unchanged.
16
+ - `headers` option: extra headers on every request, static or from a function called per
17
+ request, for example to propagate `x-request-id` and `traceparent`. They cannot replace
18
+ `Accept`, `Content-Type` or `Authorization`.
19
+
20
+ ### Security
21
+
22
+ - Every request is sent with `redirect: "error"`, so a redirect can no longer carry the bearer
23
+ token to another origin.
24
+
25
+ ## [1.0.3]
26
+
27
+ - Earlier releases are described in the HiveMem repository changelog.
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # @hivemagnus/hivemem-sdk
2
2
 
3
- TypeScript client for [HiveMem](https://github.com/ChefTaco/hive/tree/main/services/hive-mem), the shared long-term memory service for AI agents. It wraps the HiveMem REST API with typed helpers for memories, search, sessions, prompt context, peer cards, conclusions, queue operations, and webhooks.
3
+ TypeScript client for [HiveMem](https://github.com/ChefTaco/HiveMem), the shared long-term memory service for AI agents. It wraps the HiveMem REST API with typed helpers for memories, search, sessions, prompt context, peer cards, conclusions, queue operations, and webhooks.
4
4
 
5
5
  - Zero runtime dependencies; uses the platform `fetch` (Node.js 18.17+, Deno, Bun, browsers, edge runtimes).
6
6
  - ESM-only, with bundled type declarations.
7
- - Retries 5xx responses, supports request timeouts, request IDs, and token refresh hooks.
7
+ - Retries 5xx responses, supports request timeouts, request IDs, extra headers, and token providers that refresh on 401.
8
+ - Never follows redirects (`redirect: "error"`), so the bearer token stays with the configured origin.
8
9
 
9
10
  ## Install
10
11
 
@@ -12,7 +13,7 @@ TypeScript client for [HiveMem](https://github.com/ChefTaco/hive/tree/main/servi
12
13
  npm install @hivemagnus/hivemem-sdk
13
14
  ```
14
15
 
15
- You need a running HiveMem server and an agent token. See the [HiveMem README](https://github.com/ChefTaco/hive/tree/main/services/hive-mem#readme) for how to run the server and register an agent.
16
+ You need a running HiveMem server and an agent token. See the [HiveMem README](https://github.com/ChefTaco/HiveMem#readme) for how to run the server and register an agent.
16
17
 
17
18
  ## Quick start
18
19
 
@@ -53,10 +54,30 @@ const messages = context.toOpenAI(); // or context.toAnthropic()
53
54
  | `fetchImpl` | | global `fetch` |
54
55
  | `requestIdProvider` | | sends `X-Request-ID` when set |
55
56
  | `authRefresh` | | called before each attempt; a returned token replaces the current one |
57
+ | `tokenProvider` | | `({ forceRefresh }) => token`; called before each request, and again with `forceRefresh: true` after a 401, when the request is retried once |
58
+ | `headers` | | extra headers on every request, or a function returning them per request; cannot replace `Accept`, `Content-Type` or `Authorization` |
56
59
  | `onRequest` / `onResponse` | | observability hooks |
57
60
 
58
61
  Explicit constructor options win over environment variables. Environment variables are only read where `process.env` exists.
59
62
 
63
+ ### Service tokens and request propagation
64
+
65
+ A product calling HiveMem with a short-lived Oathweave service token passes a `tokenProvider`
66
+ instead of a static `token`. Cache the token in the provider and fetch a new one when
67
+ `forceRefresh` is true (HiveMem answered 401) or the cached one is near expiry. Use `headers` to
68
+ forward the caller's request ID and trace context:
69
+
70
+ ```ts
71
+ const hivemem = new HiveMem({
72
+ baseUrl: "https://memory.example.com",
73
+ tokenProvider: ({ forceRefresh }) => serviceTokens.get({ forceRefresh }),
74
+ headers: () => ({ "x-request-id": currentRequestId(), traceparent: currentTraceparent() }),
75
+ });
76
+
77
+ // Create the workspace your agents join; returns the existing one if it is already there.
78
+ const workspace = await hivemem.createWorkspace("team-alpha", { title: "Team Alpha" });
79
+ ```
80
+
60
81
  ## Common operations
61
82
 
62
83
  ```ts
@@ -115,6 +136,10 @@ const ok = await verifyHiveMemWebhook({
115
136
  });
116
137
  ```
117
138
 
139
+ ## Releasing
140
+
141
+ Maintainers: see [RELEASING.md](https://github.com/ChefTaco/HiveMem/blob/main/sdks/typescript/RELEASING.md). In short, bump the version, push a tag `sdk-vX.Y.Z`, and approve the publish run.
142
+
118
143
  ## License
119
144
 
120
145
  Apache-2.0. See [LICENSE](LICENSE).
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ContextPackage } from "./context.js";
2
- import type { ConclusionPage, ConclusionRecord, HiveMemOptions, KnowledgeSourceSyncResponse, MemoryRecord, PeerCardResponse, QueueJobRecord, QueueStatusResponse, SearchResponse, SessionRecord, WebhookEndpoint } from "./types.js";
2
+ import type { ConclusionPage, ConclusionRecord, HiveMemOptions, KnowledgeSourceSyncResponse, MemoryRecord, PeerCardResponse, QueueJobRecord, QueueStatusResponse, SearchResponse, SessionRecord, WebhookEndpoint, WorkspaceCreateOptions, WorkspaceRecord } from "./types.js";
3
3
  export declare class HiveMem {
4
4
  readonly baseUrl: string;
5
5
  token?: string;
@@ -9,12 +9,20 @@ export declare class HiveMem {
9
9
  private readonly fetchImpl;
10
10
  private readonly requestIdProvider?;
11
11
  private readonly authRefresh?;
12
+ private readonly tokenProvider?;
13
+ private readonly extraHeaders?;
12
14
  private readonly onRequest?;
13
15
  private readonly onResponse?;
14
16
  constructor(options?: HiveMemOptions);
15
17
  private url;
16
18
  private request;
17
19
  health(): Promise<Record<string, unknown>>;
20
+ /**
21
+ * Create a workspace, owned by the caller (`POST /v1/workspaces`, scope `workspaces:write`).
22
+ * Idempotent: when the workspace already exists and the caller can read it, the server returns
23
+ * it unchanged. An archived workspace ID is a 409.
24
+ */
25
+ createWorkspace(workspaceId: string, options?: WorkspaceCreateOptions): Promise<WorkspaceRecord>;
18
26
  getMemory(memoryId: string, options?: {
19
27
  includeHistory?: boolean;
20
28
  }): Promise<MemoryRecord>;
package/dist/client.js CHANGED
@@ -13,6 +13,8 @@ export class HiveMem {
13
13
  fetchImpl;
14
14
  requestIdProvider;
15
15
  authRefresh;
16
+ tokenProvider;
17
+ extraHeaders;
16
18
  onRequest;
17
19
  onResponse;
18
20
  constructor(options = {}) {
@@ -24,6 +26,8 @@ export class HiveMem {
24
26
  this.fetchImpl = options.fetchImpl ?? fetch;
25
27
  this.requestIdProvider = options.requestIdProvider;
26
28
  this.authRefresh = options.authRefresh;
29
+ this.tokenProvider = options.tokenProvider;
30
+ this.extraHeaders = options.headers;
27
31
  this.onRequest = options.onRequest;
28
32
  this.onResponse = options.onResponse;
29
33
  }
@@ -36,14 +40,23 @@ export class HiveMem {
36
40
  if (value !== undefined && value !== null)
37
41
  url.searchParams.set(key, String(value));
38
42
  }
39
- const headers = new Headers(init.headers);
43
+ const extra = typeof this.extraHeaders === "function" ? await this.extraHeaders() : this.extraHeaders;
44
+ const headers = new Headers(extra);
45
+ new Headers(init.headers).forEach((value, key) => headers.set(key, value));
40
46
  headers.set("Accept", "application/json");
41
47
  if (init.body && !headers.has("Content-Type"))
42
48
  headers.set("Content-Type", "application/json");
43
49
  if (this.requestIdProvider)
44
50
  headers.set("X-Request-ID", this.requestIdProvider());
51
+ headers.delete("Authorization");
52
+ if (this.tokenProvider) {
53
+ const provided = await this.tokenProvider({ forceRefresh: false });
54
+ if (provided)
55
+ this.token = provided;
56
+ }
45
57
  if (this.token)
46
58
  headers.set("Authorization", `Bearer ${this.token}`);
59
+ let reauthorized = false;
47
60
  for (let attempt = 0; attempt <= this.maxRetries; attempt += 1) {
48
61
  const refreshed = await this.authRefresh?.();
49
62
  if (refreshed) {
@@ -53,10 +66,22 @@ export class HiveMem {
53
66
  const controller = new AbortController();
54
67
  const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
55
68
  try {
56
- const requestInit = { ...init, method, headers, signal: controller.signal };
69
+ const requestInit = { ...init, method, headers, signal: controller.signal, redirect: "error" };
57
70
  this.onRequest?.(method, String(url), requestInit);
58
71
  const response = await this.fetchImpl(url, requestInit);
59
72
  this.onResponse?.(response);
73
+ if (response.status === 401 && this.tokenProvider && !reauthorized) {
74
+ // A service token may have expired or been revoked: fetch a fresh one and retry once.
75
+ reauthorized = true;
76
+ const fresh = await this.tokenProvider({ forceRefresh: true });
77
+ if (fresh) {
78
+ await response.body?.cancel().catch(() => undefined);
79
+ this.token = fresh;
80
+ headers.set("Authorization", `Bearer ${this.token}`);
81
+ attempt -= 1;
82
+ continue;
83
+ }
84
+ }
60
85
  if (response.status >= 500 && attempt < this.maxRetries)
61
86
  continue;
62
87
  if (!response.ok) {
@@ -85,6 +110,14 @@ export class HiveMem {
85
110
  health() {
86
111
  return this.request("GET", "/v1/health");
87
112
  }
113
+ /**
114
+ * Create a workspace, owned by the caller (`POST /v1/workspaces`, scope `workspaces:write`).
115
+ * Idempotent: when the workspace already exists and the caller can read it, the server returns
116
+ * it unchanged. An archived workspace ID is a 409.
117
+ */
118
+ createWorkspace(workspaceId, options = {}) {
119
+ return this.request("POST", "/v1/workspaces", { body: JSON.stringify({ ...options, workspace_id: workspaceId }) });
120
+ }
88
121
  getMemory(memoryId, options = {}) {
89
122
  const params = options.includeHistory ? { include_history: true } : {};
90
123
  return this.request("GET", `/v1/memories/${encodeURIComponent(memoryId)}`, {}, params);
package/dist/types.d.ts CHANGED
@@ -10,9 +10,75 @@ export interface HiveMemOptions {
10
10
  fetchImpl?: typeof fetch;
11
11
  requestIdProvider?: () => string;
12
12
  authRefresh?: () => string | undefined | Promise<string | undefined>;
13
+ /**
14
+ * Supplies the bearer token, for example a short-lived Oathweave service token. Called before
15
+ * each request with `forceRefresh: false`; after a 401 it is called again with
16
+ * `forceRefresh: true` and the request is retried once with the token it returns. Returning
17
+ * `undefined` falls back to `token`.
18
+ */
19
+ tokenProvider?: TokenProvider;
20
+ /**
21
+ * Extra headers sent on every request, such as `x-request-id` or `traceparent` taken from the
22
+ * caller's request context. A function is called once per request. They never replace the
23
+ * client's own `Accept` or `Content-Type` headers, and `Authorization` comes only from `token`,
24
+ * `tokenProvider` or `authRefresh`.
25
+ */
26
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
13
27
  onRequest?: (method: string, url: string, init: RequestInit) => void;
14
28
  onResponse?: (response: Response) => void;
15
29
  }
30
+ export interface TokenProviderContext {
31
+ /** True after the server answered 401: the cached token must not be reused. */
32
+ forceRefresh: boolean;
33
+ }
34
+ export type TokenProvider = (context: TokenProviderContext) => string | undefined | Promise<string | undefined>;
35
+ export type WorkspaceRole = "viewer" | "member" | "admin" | "owner";
36
+ export interface WorkspaceMember {
37
+ agent_id: string;
38
+ role: WorkspaceRole;
39
+ scopes: string[];
40
+ added_by: string | null;
41
+ created_at: string | null;
42
+ updated_at: string | null;
43
+ }
44
+ export interface WorkspaceSanitizationPolicy {
45
+ mode: "warn" | "redact" | "reject" | null;
46
+ detect_pii: boolean | null;
47
+ detect_secrets: boolean | null;
48
+ deny_regexes: string[];
49
+ }
50
+ export interface WorkspacePolicy {
51
+ retention_days: number | null;
52
+ allowed_scopes: string[];
53
+ reasoning_provider: string | null;
54
+ consolidation_cadence_seconds: number | null;
55
+ webhooks_enabled: boolean;
56
+ sanitization: WorkspaceSanitizationPolicy;
57
+ agent_overrides: Record<string, Record<string, unknown>>;
58
+ session_overrides: Record<string, Record<string, unknown>>;
59
+ }
60
+ export interface WorkspaceRecord {
61
+ workspace_id: string;
62
+ owner_agent_id: string;
63
+ title: string | null;
64
+ description: string | null;
65
+ metadata: Record<string, unknown>;
66
+ policy: WorkspacePolicy;
67
+ members: WorkspaceMember[];
68
+ created_at: string;
69
+ updated_at: string;
70
+ deleted_at: string | null;
71
+ }
72
+ /** Optional fields of `POST /v1/workspaces`; `workspace_id` is the method's first argument. */
73
+ export interface WorkspaceCreateOptions {
74
+ title?: string | null;
75
+ description?: string | null;
76
+ metadata?: Record<string, unknown>;
77
+ policy?: Partial<Omit<WorkspacePolicy, "sanitization">> & {
78
+ sanitization?: Partial<WorkspaceSanitizationPolicy>;
79
+ };
80
+ members?: Array<Pick<WorkspaceMember, "agent_id"> & Partial<Pick<WorkspaceMember, "role" | "scopes">>>;
81
+ }
16
82
  export interface MemoryRecord {
17
83
  id: string;
18
84
  agent_id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hivemagnus/hivemem-sdk",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "TypeScript client for HiveMem, the shared long-term memory service for AI agents",
5
5
  "keywords": [
6
6
  "hivemem",
@@ -13,14 +13,14 @@
13
13
  "context",
14
14
  "sdk"
15
15
  ],
16
- "homepage": "https://github.com/ChefTaco/hive/tree/main/services/hive-mem/sdks/typescript#readme",
16
+ "homepage": "https://github.com/ChefTaco/HiveMem/tree/main/sdks/typescript#readme",
17
17
  "bugs": {
18
- "url": "https://github.com/ChefTaco/hive/issues"
18
+ "url": "https://github.com/ChefTaco/HiveMem/issues"
19
19
  },
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "git+https://github.com/ChefTaco/hive.git",
23
- "directory": "services/hive-mem/sdks/typescript"
22
+ "url": "git+https://github.com/ChefTaco/HiveMem.git",
23
+ "directory": "sdks/typescript"
24
24
  },
25
25
  "license": "Apache-2.0",
26
26
  "author": "ChefTaco",
@@ -38,6 +38,7 @@
38
38
  "files": [
39
39
  "dist",
40
40
  "README.md",
41
+ "CHANGELOG.md",
41
42
  "LICENSE"
42
43
  ],
43
44
  "sideEffects": false,
@@ -51,9 +52,11 @@
51
52
  "build": "tsc -p tsconfig.json",
52
53
  "typecheck": "tsc -p tsconfig.json --noEmit",
53
54
  "test": "node --test test/*.test.mjs",
54
- "prepublishOnly": "npm run build && npm test"
55
+ "prepublishOnly": "npm run build && npm test",
56
+ "pack:check": "node scripts/check-package.mjs"
55
57
  },
56
58
  "devDependencies": {
57
- "typescript": "^5.6.0"
59
+ "typescript": "^5.6.0",
60
+ "yaml": "2.9.1"
58
61
  }
59
62
  }