cognia-sdk 0.2.0 → 0.2.2

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
@@ -91,6 +91,10 @@ A verified success needs a signal (a test, a check, a metric) or a human. Inject
91
91
  counts as success. Your Cognia Cloud Overview shows the loop per week: turns with context → cited → used → verified, with
92
92
  unverified reports listed apart.
93
93
 
94
+ **Router decision values** (`x-cognia-decision`): `INJECT`, `ABSTAIN`, `unavailable:timeout` (the context budget passed),
95
+ `unavailable:<status>` (Cognia answered an error), `unavailable:error`, `no_user_message`. Every `unavailable` means the
96
+ call was forwarded untouched.
97
+
94
98
  **Credits.** Reporting on other owners' knowledge earns Cloud credits (verified outcomes most, used receipts a little,
95
99
  context calls that injected a little), and your own memories earn when an independent agent's verified success lands on
96
100
  them. Credits pay for Workbench messages beyond your plan. They are not money. `GET /v1/cloud/me/credits` lists them.
@@ -107,3 +111,17 @@ Every context call then carries that namespace. A private memory saved under it
107
111
  namespace: the `mine` scope never crosses from one of your users to another, and the `organization` and `network`
108
112
  scopes add only what is shared with your organizations or public. Omit the namespace for a single-agent integration.
109
113
  Your own Cognia Cloud still shows everything the key owns: you are the data controller for your users' memories.
114
+
115
+ ## Memory lifecycle: supersede, contradict, expire (API, 2026-10-08)
116
+
117
+ A long-running agent accumulates private memories that go stale. Three private-only fields on `POST /v1/memories/author`
118
+ keep retrieval honest without deleting anything:
119
+
120
+ - `supersedes: "<memoryId>"` — this memory replaces one of yours (same namespace). The old one stays readable by id, says
121
+ what replaced it, and never comes back in context or search.
122
+ - `contradicts: "<memoryId>"` — same mechanics, recorded as a contradiction.
123
+ - `validUntil: "<ISO date-time>"` — retrieved only until then.
124
+
125
+ `PATCH /v1/memories/:id/lifecycle` sets or clears `validUntil` and `supersededBy` later. Session learning
126
+ (`session.end()`) marks a proposal with `supersedes` when its title matches one of your live private memories, so your
127
+ 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.2";
45
47
  class CogniaError extends Error {
46
48
  status;
47
49
  body;
@@ -63,8 +65,10 @@ class Cognia {
63
65
  f;
64
66
  onError;
65
67
  namespace;
68
+ receiptTimeoutMs;
66
69
  constructor(o) {
67
70
  this.namespace = o.namespace;
71
+ this.receiptTimeoutMs = o.receiptTimeoutMs ?? 10_000;
68
72
  if (!o.apiKey && !o.tokenProvider)
69
73
  throw new Error("Cognia: apiKey or tokenProvider is required");
70
74
  this.key = o.apiKey ?? null;
@@ -136,7 +140,7 @@ class Cognia {
136
140
  token = this.key;
137
141
  if (!token)
138
142
  throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
139
- 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/" + exports.SDK_VERSION }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
140
144
  let j = null;
141
145
  try {
142
146
  j = await r.json();
@@ -186,41 +190,45 @@ class Cognia {
186
190
  return { contextId: null, decision: "UNAVAILABLE", items: [], reason: err.name === "AbortError" ? "timeout" : err.message };
187
191
  }
188
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
+ }
189
212
  /** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
190
213
  async injected(contextId, itemIds) {
191
214
  if (!contextId || !itemIds.length)
192
215
  return;
193
- try {
194
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/injected`, { itemIds }, 2000);
195
- }
196
- catch (e) {
197
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
198
- }
216
+ await this.receipt("injected", contextId, { itemIds });
199
217
  }
200
218
  /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
201
219
  async cited(contextId, itemIds) {
202
220
  if (!contextId || !itemIds.length)
203
221
  return;
204
- try {
205
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/cited`, { itemIds }, 2000);
206
- }
207
- catch (e) {
208
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
209
- }
222
+ await this.receipt("cited", contextId, { itemIds });
210
223
  }
211
224
  /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
212
225
  async used(contextId, itemIds) {
213
226
  if (!contextId || !itemIds.length)
214
227
  return null;
215
- try {
216
- return await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/used`, { itemIds }, 3000);
217
- }
218
- catch (e) {
219
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
220
- return null;
221
- }
228
+ return (await this.receipt("used", contextId, { itemIds }));
222
229
  }
223
- /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
230
+ /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). USED comes from this or from
231
+ * `used()`; success and failure come only from here. */
224
232
  outcome(contextId, input) {
225
233
  return this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/outcome`, input);
226
234
  }
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,7 @@
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;
3
5
  /** end-user namespace sent on every context call (0.2.0). Use one per end user of YOUR app: their private memories
4
6
  * are then invisible to every other namespace under the same key. Omit for a single-agent integration. */
5
7
  namespace?: string;
@@ -25,6 +27,8 @@ export interface CogniaOptions {
25
27
  }
26
28
  /** Accept only what the contract promises; anything else is treated as unavailable so the model call proceeds (fail-open). */
27
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.2";
28
32
  export declare class CogniaError extends Error {
29
33
  readonly status: number;
30
34
  readonly body: unknown;
@@ -43,6 +47,7 @@ export declare class Cognia {
43
47
  private readonly f;
44
48
  private readonly onError;
45
49
  readonly namespace: string | undefined;
50
+ readonly receiptTimeoutMs: number;
46
51
  constructor(o: CogniaOptions);
47
52
  /** Every string that reaches the host through an error or a reason has the bearer removed first (independent E2E F28). */
48
53
  private scrub;
@@ -53,13 +58,17 @@ export declare class Cognia {
53
58
  * A bad credential is also reported this way, with the reason, so a misconfigured app degrades instead of breaking.
54
59
  */
55
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;
56
64
  /** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
57
65
  injected(contextId: string, itemIds: string[]): Promise<void>;
58
66
  /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
59
67
  cited(contextId: string, itemIds: string[]): Promise<void>;
60
68
  /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
61
69
  used(contextId: string, itemIds: string[]): Promise<ReceiptResponse | null>;
62
- /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
70
+ /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). USED comes from this or from
71
+ * `used()`; success and failure come only from here. */
63
72
  outcome(contextId: string, input: OutcomeInput): Promise<OutcomeResponse>;
64
73
  /** One-liners for the two outcomes. Pass `verification` when a signal (a test, a check, a metric) or a human confirmed it;
65
74
  * without it the report is recorded as agent-reported, which counts as used but never as verified success. */
@@ -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.2";
41
43
  export class CogniaError extends Error {
42
44
  status;
43
45
  body;
@@ -58,8 +60,10 @@ export class Cognia {
58
60
  f;
59
61
  onError;
60
62
  namespace;
63
+ receiptTimeoutMs;
61
64
  constructor(o) {
62
65
  this.namespace = o.namespace;
66
+ this.receiptTimeoutMs = o.receiptTimeoutMs ?? 10_000;
63
67
  if (!o.apiKey && !o.tokenProvider)
64
68
  throw new Error("Cognia: apiKey or tokenProvider is required");
65
69
  this.key = o.apiKey ?? null;
@@ -131,7 +135,7 @@ export class Cognia {
131
135
  token = this.key;
132
136
  if (!token)
133
137
  throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
134
- 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 });
135
139
  let j = null;
136
140
  try {
137
141
  j = await r.json();
@@ -181,41 +185,45 @@ export class Cognia {
181
185
  return { contextId: null, decision: "UNAVAILABLE", items: [], reason: err.name === "AbortError" ? "timeout" : err.message };
182
186
  }
183
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
+ }
184
207
  /** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
185
208
  async injected(contextId, itemIds) {
186
209
  if (!contextId || !itemIds.length)
187
210
  return;
188
- try {
189
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/injected`, { itemIds }, 2000);
190
- }
191
- catch (e) {
192
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
193
- }
211
+ await this.receipt("injected", contextId, { itemIds });
194
212
  }
195
213
  /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
196
214
  async cited(contextId, itemIds) {
197
215
  if (!contextId || !itemIds.length)
198
216
  return;
199
- try {
200
- await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/cited`, { itemIds }, 2000);
201
- }
202
- catch (e) {
203
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
204
- }
217
+ await this.receipt("cited", contextId, { itemIds });
205
218
  }
206
219
  /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
207
220
  async used(contextId, itemIds) {
208
221
  if (!contextId || !itemIds.length)
209
222
  return null;
210
- try {
211
- return await this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/used`, { itemIds }, 3000);
212
- }
213
- catch (e) {
214
- this.onError?.(e instanceof Error ? e : new Error(String(e)));
215
- return null;
216
- }
223
+ return (await this.receipt("used", contextId, { itemIds }));
217
224
  }
218
- /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
225
+ /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). USED comes from this or from
226
+ * `used()`; success and failure come only from here. */
219
227
  outcome(contextId, input) {
220
228
  return this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/outcome`, input);
221
229
  }
@@ -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";
@@ -121,6 +121,13 @@ export interface OutcomeResponse {
121
121
  export interface ExtractProposal {
122
122
  title?: string;
123
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
+ };
124
131
  [k: string]: unknown;
125
132
  }
126
133
  export interface ExtractResponse {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognia-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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",