cognia-sdk 0.1.7 → 0.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/README.md CHANGED
@@ -94,3 +94,30 @@ unverified reports listed apart.
94
94
  **Credits.** Reporting on other owners' knowledge earns Cloud credits (verified outcomes most, used receipts a little,
95
95
  context calls that injected a little), and your own memories earn when an independent agent's verified success lands on
96
96
  them. Credits pay for Workbench messages beyond your plan. They are not money. `GET /v1/cloud/me/credits` lists them.
97
+
98
+ ## Namespaces: one key, many end users (0.2.0)
99
+
100
+ If you build an app where many people each have their own agent, give each of them a namespace:
101
+
102
+ ```ts
103
+ const cognia = new Cognia({ apiKey: process.env.COGNIA_API_KEY, namespace: `user:${userId}` });
104
+ ```
105
+
106
+ Every context call then carries that namespace. A private memory saved under it is returned only to the same
107
+ namespace: the `mine` scope never crosses from one of your users to another, and the `organization` and `network`
108
+ scopes add only what is shared with your organizations or public. Omit the namespace for a single-agent integration.
109
+ Your own Cognia Cloud still shows everything the key owns: you are the data controller for your users' memories.
110
+
111
+ ## Memory lifecycle: supersede, contradict, expire (API, 2026-10-08)
112
+
113
+ A long-running agent accumulates private memories that go stale. Three private-only fields on `POST /v1/memories/author`
114
+ keep retrieval honest without deleting anything:
115
+
116
+ - `supersedes: "<memoryId>"` — this memory replaces one of yours (same namespace). The old one stays readable by id, says
117
+ what replaced it, and never comes back in context or search.
118
+ - `contradicts: "<memoryId>"` — same mechanics, recorded as a contradiction.
119
+ - `validUntil: "<ISO date-time>"` — retrieved only until then.
120
+
121
+ `PATCH /v1/memories/:id/lifecycle` sets or clears `validUntil` and `supersededBy` later. Session learning
122
+ (`session.end()`) marks a proposal with `supersedes` when its title matches one of your live private memories, so your
123
+ accept step can pass it through and update instead of duplicating.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Cognia = exports.CogniaError = void 0;
3
+ exports.Cognia = exports.CogniaError = exports.SDK_VERSION = void 0;
4
4
  exports.shapeContext = shapeContext;
5
5
  /** Accept only what the contract promises; anything else is treated as unavailable so the model call proceeds (fail-open). */
6
6
  function shapeContext(r) {
@@ -42,6 +42,8 @@ function shapeContext(r) {
42
42
  const abstain = decision === "ABSTAIN" ? { reason: (o.abstain && typeof o.abstain.reason === "string" ? o.abstain.reason : o.decision === "INJECT" ? "malformed_items" : "abstain") } : undefined;
43
43
  return { contextId: o.contextId, decision, ...(abstain ? { abstain } : {}), scope, items: items, receipts, metrics };
44
44
  }
45
+ /** kept in step with package.json by the version test */
46
+ exports.SDK_VERSION = "0.2.1";
45
47
  class CogniaError extends Error {
46
48
  status;
47
49
  body;
@@ -62,7 +64,11 @@ class Cognia {
62
64
  tokenProvider;
63
65
  f;
64
66
  onError;
67
+ namespace;
68
+ receiptTimeoutMs;
65
69
  constructor(o) {
70
+ this.namespace = o.namespace;
71
+ this.receiptTimeoutMs = o.receiptTimeoutMs ?? 10_000;
66
72
  if (!o.apiKey && !o.tokenProvider)
67
73
  throw new Error("Cognia: apiKey or tokenProvider is required");
68
74
  this.key = o.apiKey ?? null;
@@ -134,7 +140,7 @@ class Cognia {
134
140
  token = this.key;
135
141
  if (!token)
136
142
  throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
137
- const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/0.1.6" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
143
+ const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/${SDK_VERSION}" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
138
144
  let j = null;
139
145
  try {
140
146
  j = await r.json();
@@ -166,7 +172,8 @@ class Cognia {
166
172
  */
167
173
  async context(req) {
168
174
  const body = typeof req === "string" ? { task: req } : req;
169
- const payload = { task: body.task, scope: body.scope ?? this.scope, ...(body.budget ?? this.budget ? { budget: { ...this.budget, ...body.budget } } : {}), ...(body.session ? { session: body.session } : {}) };
175
+ const ns = body.identity?.namespace ?? this.namespace;
176
+ const payload = { task: body.task, scope: body.scope ?? this.scope, ...(body.budget ?? this.budget ? { budget: { ...this.budget, ...body.budget } } : {}), ...(body.session ? { session: body.session } : {}), ...(ns ? { identity: { namespace: ns } } : {}) };
170
177
  try {
171
178
  const r = await this.call("POST", "/v1/context", payload, this.timeoutMs);
172
179
  const shaped = shapeContext(r);
@@ -183,39 +190,42 @@ class Cognia {
183
190
  return { contextId: null, decision: "UNAVAILABLE", items: [], reason: err.name === "AbortError" ? "timeout" : err.message };
184
191
  }
185
192
  }
193
+ /** A usage receipt (injected / cited / used): never on the model call's critical path, so it gets a generous budget
194
+ * (RECEIPT_TIMEOUT_MS, default 10 s) and one retry; a failure is reported to onError and never thrown (NEW-RECEIPT). */
195
+ async receipt(kind, contextId, body) {
196
+ const url = `/v1/context/${encodeURIComponent(contextId)}/${kind}`;
197
+ let last = null;
198
+ for (let attempt = 0; attempt < 2; attempt++) {
199
+ try {
200
+ return await this.call(url.startsWith("/") ? "POST" : "POST", url, body, this.receiptTimeoutMs);
201
+ }
202
+ catch (e) {
203
+ last = e instanceof Error ? e : new Error(String(e));
204
+ if (!/timed out|AbortError|fetch failed|ECONN|5\d\d/.test(last.message + last.name))
205
+ break;
206
+ }
207
+ }
208
+ if (last)
209
+ this.onError?.(last);
210
+ return null;
211
+ }
186
212
  /** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
187
213
  async injected(contextId, itemIds) {
188
214
  if (!contextId || !itemIds.length)
189
215
  return;
190
- try {
191
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/injected`, { itemIds }, 2000);
192
- }
193
- catch (e) {
194
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
195
- }
216
+ await this.receipt("injected", contextId, { itemIds });
196
217
  }
197
218
  /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
198
219
  async cited(contextId, itemIds) {
199
220
  if (!contextId || !itemIds.length)
200
221
  return;
201
- try {
202
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/cited`, { itemIds }, 2000);
203
- }
204
- catch (e) {
205
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
206
- }
222
+ await this.receipt("cited", contextId, { itemIds });
207
223
  }
208
224
  /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
209
225
  async used(contextId, itemIds) {
210
226
  if (!contextId || !itemIds.length)
211
227
  return null;
212
- try {
213
- return await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/used`, { itemIds }, 3000);
214
- }
215
- catch (e) {
216
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
217
- return null;
218
- }
228
+ return (await this.receipt("used", contextId, { itemIds }));
219
229
  }
220
230
  /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
221
231
  outcome(contextId, input) {
package/dist/cjs/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.provenanceLine = exports.renderContextBlock = exports.textOf = exports.prependSystem = exports.contextFor = exports.afterReply = exports.citedItems = exports.withCognia = exports.CogniaSession = exports.shapeContext = exports.CogniaError = exports.Cognia = void 0;
3
+ exports.provenanceLine = exports.renderContextBlock = exports.textOf = exports.prependSystem = exports.contextFor = exports.afterReply = exports.citedItems = exports.withCognia = exports.CogniaSession = exports.shapeContext = exports.SDK_VERSION = exports.CogniaError = exports.Cognia = void 0;
4
4
  var client_js_1 = require("./client.js");
5
5
  Object.defineProperty(exports, "Cognia", { enumerable: true, get: function () { return client_js_1.Cognia; } });
6
6
  Object.defineProperty(exports, "CogniaError", { enumerable: true, get: function () { return client_js_1.CogniaError; } });
7
+ Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return client_js_1.SDK_VERSION; } });
7
8
  Object.defineProperty(exports, "shapeContext", { enumerable: true, get: function () { return client_js_1.shapeContext; } });
8
9
  var session_js_1 = require("./session.js");
9
10
  Object.defineProperty(exports, "CogniaSession", { enumerable: true, get: function () { return session_js_1.CogniaSession; } });
@@ -1,5 +1,10 @@
1
1
  import type { ReceiptResponse, ContextRequest, ContextResponse, ContextResult, OutcomeInput, OutcomeResponse, ExtractResponse, Scope } from "./types.js";
2
2
  export interface CogniaOptions {
3
+ /** budget for usage receipts (injected / cited / used), which never block the model call; default 10 000 ms, one retry */
4
+ receiptTimeoutMs?: number;
5
+ /** end-user namespace sent on every context call (0.2.0). Use one per end user of YOUR app: their private memories
6
+ * are then invisible to every other namespace under the same key. Omit for a single-agent integration. */
7
+ namespace?: string;
3
8
  /** the agent's credential (cognia_sk_…, a developer or SDK credential); never logged, never put in a URL */
4
9
  apiKey?: string;
5
10
  /** Delegated auth: called at REQUEST time to obtain the bearer (e.g. an MCP OAuth access token the host already
@@ -22,6 +27,8 @@ export interface CogniaOptions {
22
27
  }
23
28
  /** Accept only what the contract promises; anything else is treated as unavailable so the model call proceeds (fail-open). */
24
29
  export declare function shapeContext(r: unknown): ContextResponse | null;
30
+ /** kept in step with package.json by the version test */
31
+ export declare const SDK_VERSION = "0.2.1";
25
32
  export declare class CogniaError extends Error {
26
33
  readonly status: number;
27
34
  readonly body: unknown;
@@ -39,6 +46,8 @@ export declare class Cognia {
39
46
  private readonly tokenProvider;
40
47
  private readonly f;
41
48
  private readonly onError;
49
+ readonly namespace: string | undefined;
50
+ readonly receiptTimeoutMs: number;
42
51
  constructor(o: CogniaOptions);
43
52
  /** Every string that reaches the host through an error or a reason has the bearer removed first (independent E2E F28). */
44
53
  private scrub;
@@ -49,6 +58,9 @@ export declare class Cognia {
49
58
  * A bad credential is also reported this way, with the reason, so a misconfigured app degrades instead of breaking.
50
59
  */
51
60
  context(req: ContextRequest | string): Promise<ContextResult>;
61
+ /** A usage receipt (injected / cited / used): never on the model call's critical path, so it gets a generous budget
62
+ * (RECEIPT_TIMEOUT_MS, default 10 s) and one retry; a failure is reported to onError and never thrown (NEW-RECEIPT). */
63
+ private receipt;
52
64
  /** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
53
65
  injected(contextId: string, itemIds: string[]): Promise<void>;
54
66
  /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
@@ -38,6 +38,8 @@ export function shapeContext(r) {
38
38
  const abstain = decision === "ABSTAIN" ? { reason: (o.abstain && typeof o.abstain.reason === "string" ? o.abstain.reason : o.decision === "INJECT" ? "malformed_items" : "abstain") } : undefined;
39
39
  return { contextId: o.contextId, decision, ...(abstain ? { abstain } : {}), scope, items: items, receipts, metrics };
40
40
  }
41
+ /** kept in step with package.json by the version test */
42
+ export const SDK_VERSION = "0.2.1";
41
43
  export class CogniaError extends Error {
42
44
  status;
43
45
  body;
@@ -57,7 +59,11 @@ export class Cognia {
57
59
  tokenProvider;
58
60
  f;
59
61
  onError;
62
+ namespace;
63
+ receiptTimeoutMs;
60
64
  constructor(o) {
65
+ this.namespace = o.namespace;
66
+ this.receiptTimeoutMs = o.receiptTimeoutMs ?? 10_000;
61
67
  if (!o.apiKey && !o.tokenProvider)
62
68
  throw new Error("Cognia: apiKey or tokenProvider is required");
63
69
  this.key = o.apiKey ?? null;
@@ -129,7 +135,7 @@ export class Cognia {
129
135
  token = this.key;
130
136
  if (!token)
131
137
  throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
132
- const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/0.1.6" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
138
+ const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/${SDK_VERSION}" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
133
139
  let j = null;
134
140
  try {
135
141
  j = await r.json();
@@ -161,7 +167,8 @@ export class Cognia {
161
167
  */
162
168
  async context(req) {
163
169
  const body = typeof req === "string" ? { task: req } : req;
164
- const payload = { task: body.task, scope: body.scope ?? this.scope, ...(body.budget ?? this.budget ? { budget: { ...this.budget, ...body.budget } } : {}), ...(body.session ? { session: body.session } : {}) };
170
+ const ns = body.identity?.namespace ?? this.namespace;
171
+ const payload = { task: body.task, scope: body.scope ?? this.scope, ...(body.budget ?? this.budget ? { budget: { ...this.budget, ...body.budget } } : {}), ...(body.session ? { session: body.session } : {}), ...(ns ? { identity: { namespace: ns } } : {}) };
165
172
  try {
166
173
  const r = await this.call("POST", "/v1/context", payload, this.timeoutMs);
167
174
  const shaped = shapeContext(r);
@@ -178,39 +185,42 @@ export class Cognia {
178
185
  return { contextId: null, decision: "UNAVAILABLE", items: [], reason: err.name === "AbortError" ? "timeout" : err.message };
179
186
  }
180
187
  }
188
+ /** A usage receipt (injected / cited / used): never on the model call's critical path, so it gets a generous budget
189
+ * (RECEIPT_TIMEOUT_MS, default 10 s) and one retry; a failure is reported to onError and never thrown (NEW-RECEIPT). */
190
+ async receipt(kind, contextId, body) {
191
+ const url = `/v1/context/${encodeURIComponent(contextId)}/${kind}`;
192
+ let last = null;
193
+ for (let attempt = 0; attempt < 2; attempt++) {
194
+ try {
195
+ return await this.call(url.startsWith("/") ? "POST" : "POST", url, body, this.receiptTimeoutMs);
196
+ }
197
+ catch (e) {
198
+ last = e instanceof Error ? e : new Error(String(e));
199
+ if (!/timed out|AbortError|fetch failed|ECONN|5\d\d/.test(last.message + last.name))
200
+ break;
201
+ }
202
+ }
203
+ if (last)
204
+ this.onError?.(last);
205
+ return null;
206
+ }
181
207
  /** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
182
208
  async injected(contextId, itemIds) {
183
209
  if (!contextId || !itemIds.length)
184
210
  return;
185
- try {
186
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/injected`, { itemIds }, 2000);
187
- }
188
- catch (e) {
189
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
190
- }
211
+ await this.receipt("injected", contextId, { itemIds });
191
212
  }
192
213
  /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
193
214
  async cited(contextId, itemIds) {
194
215
  if (!contextId || !itemIds.length)
195
216
  return;
196
- try {
197
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/cited`, { itemIds }, 2000);
198
- }
199
- catch (e) {
200
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
201
- }
217
+ await this.receipt("cited", contextId, { itemIds });
202
218
  }
203
219
  /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
204
220
  async used(contextId, itemIds) {
205
221
  if (!contextId || !itemIds.length)
206
222
  return null;
207
- try {
208
- return await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/used`, { itemIds }, 3000);
209
- }
210
- catch (e) {
211
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
212
- return null;
213
- }
223
+ return (await this.receipt("used", contextId, { itemIds }));
214
224
  }
215
225
  /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
216
226
  outcome(contextId, input) {
@@ -1,4 +1,4 @@
1
- export { Cognia, CogniaError, shapeContext, type CogniaOptions } from "./client.js";
1
+ export { Cognia, CogniaError, SDK_VERSION, shapeContext, type CogniaOptions } from "./client.js";
2
2
  export { CogniaSession, type SessionTurn } from "./session.js";
3
3
  export { withCognia, citedItems, afterReply, contextFor, prependSystem, textOf, type WithCogniaOptions } from "./middleware.js";
4
4
  export { renderContextBlock, provenanceLine } from "./render.js";
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Cognia, CogniaError, shapeContext } from "./client.js";
1
+ export { Cognia, CogniaError, SDK_VERSION, shapeContext } from "./client.js";
2
2
  export { CogniaSession } from "./session.js";
3
3
  export { withCognia, citedItems, afterReply, contextFor, prependSystem, textOf } from "./middleware.js";
4
4
  export { renderContextBlock, provenanceLine } from "./render.js";
@@ -12,6 +12,10 @@ export interface ContextRequest {
12
12
  id: string;
13
13
  turn: number;
14
14
  };
15
+ /** end-user namespace (0.2.0): one developer key, many end users; private memories are isolated per namespace */
16
+ identity?: {
17
+ namespace?: string;
18
+ };
15
19
  }
16
20
  export interface MemoryItem {
17
21
  kind: "memory";
@@ -117,6 +121,13 @@ export interface OutcomeResponse {
117
121
  export interface ExtractProposal {
118
122
  title?: string;
119
123
  summary?: string;
124
+ /** lifecycle V1: this proposal matches one of your live private memories by title; accept it WITH `supersedes` to replace instead of adding a copy */
125
+ supersedes?: {
126
+ memoryId: string;
127
+ displayId: string | null;
128
+ title: string;
129
+ why: string;
130
+ };
120
131
  [k: string]: unknown;
121
132
  }
122
133
  export interface ExtractResponse {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognia-sdk",
3
- "version": "0.1.7",
3
+ "version": "0.2.1",
4
4
  "description": "Cognia for your model calls: one line gives any agent the Memories and Skills it may use, with provenance and honest receipts. Fails open.",
5
5
  "license": "MIT",
6
6
  "author": "Cognia",