cognia-sdk 0.1.5 → 0.1.7

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
@@ -61,3 +61,36 @@ When nothing clears the relevance gates the answer is `ABSTAIN` with a reason an
61
61
  request in that case. Budgets default to 3 memories and 1 Skill; the gate is never loosened. The lookup times out after 1500 ms by default (`timeoutMs`), and a timeout is fail-open.
62
62
 
63
63
  Docs: https://cognia.fun/developers · Source: github.com/cognia-dev/memory-backend (`packages/sdk`)
64
+
65
+ ## Receipts: what Cognia knows about each turn (0.1.7)
66
+
67
+ Every model call through `withCognia` leaves a chain of receipts. Each step is a fact about usage; **only the last one is evidence**.
68
+
69
+ | receipt | meaning | who records it |
70
+ |---|---|---|
71
+ | INJECTED | the item was placed in front of the model | the middleware, automatically |
72
+ | CITED | the model's reply named the item (its `cognia://…` id or its title) | the middleware, automatically |
73
+ | USED | your agent actually used it | you: `cognia.used(contextId, [itemId])` |
74
+ | success / failure | what happened, with or without verification | you: `cognia.success(contextId, itemId, verification?)` |
75
+
76
+ ```ts
77
+ const model = withCognia(openai, {
78
+ cognia,
79
+ onResponse: ({ contextId, cited, text }) => {
80
+ // cited = injected items the reply referenced; the cheapest honest next step is to say which were used
81
+ if (cited.length) void cognia.used(contextId, cited.map((i) => i.id));
82
+ },
83
+ });
84
+ // later, when a test, a check or a person confirms it worked:
85
+ await cognia.success(contextId, itemId, { type: "signal_verified", verifierType: "test-suite" });
86
+ // or, if you only know the agent used it:
87
+ await cognia.success(contextId, itemId); // recorded as agent-reported: counts as used, never as verified
88
+ ```
89
+
90
+ A verified success needs a signal (a test, a check, a metric) or a human. Injected and cited never count as used; used never
91
+ counts as success. Your Cognia Cloud Overview shows the loop per week: turns with context → cited → used → verified, with
92
+ unverified reports listed apart.
93
+
94
+ **Credits.** Reporting on other owners' knowledge earns Cloud credits (verified outcomes most, used receipts a little,
95
+ context calls that injected a little), and your own memories earn when an independent agent's verified success lands on
96
+ them. Credits pay for Workbench messages beyond your plan. They are not money. `GET /v1/cloud/me/credits` lists them.
@@ -120,8 +120,9 @@ class Cognia {
120
120
  token = await (timeoutMs ? Promise.race([provider, deadline]) : provider);
121
121
  }
122
122
  catch (e) {
123
+ // never the provider's own object or message (it may carry the bearer): a fresh error either way
123
124
  if (e?.name === "AbortError")
124
- throw e;
125
+ throw Object.assign(new Error("tokenProvider timed out"), { name: "AbortError" });
125
126
  throw new CogniaError(`Cognia ${method} ${path} → token provider failed`, 0, null);
126
127
  }
127
128
  finally {
@@ -133,7 +134,7 @@ class Cognia {
133
134
  token = this.key;
134
135
  if (!token)
135
136
  throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
136
- const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/0.1.5" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
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 });
137
138
  let j = null;
138
139
  try {
139
140
  j = await r.json();
@@ -193,10 +194,41 @@ class Cognia {
193
194
  this.onError?.(e instanceof Error ? e : new Error(String(e)));
194
195
  }
195
196
  }
196
- /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to USED. */
197
+ /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
198
+ async cited(contextId, itemIds) {
199
+ if (!contextId || !itemIds.length)
200
+ 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
+ }
207
+ }
208
+ /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
209
+ async used(contextId, itemIds) {
210
+ if (!contextId || !itemIds.length)
211
+ 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
+ }
219
+ }
220
+ /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
197
221
  outcome(contextId, input) {
198
222
  return this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/outcome`, input);
199
223
  }
224
+ /** One-liners for the two outcomes. Pass `verification` when a signal (a test, a check, a metric) or a human confirmed it;
225
+ * without it the report is recorded as agent-reported, which counts as used but never as verified success. */
226
+ success(contextId, itemId, verification) {
227
+ return this.outcome(contextId, { itemId, used: true, outcome: "success", ...(verification ? { verification } : {}) });
228
+ }
229
+ failure(contextId, itemId, verification) {
230
+ return this.outcome(contextId, { itemId, used: true, outcome: "failure", ...(verification ? { verification } : {}) });
231
+ }
200
232
  /** Session learning: propose memories from a session's turns. Nothing is saved; the host shows the proposals. */
201
233
  extract(markdown, filename = "session.md") {
202
234
  return this.call("POST", "/v1/memories/extract", { markdown, filename }, 60_000);
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.provenanceLine = exports.renderContextBlock = exports.textOf = exports.prependSystem = exports.contextFor = 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.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; } });
@@ -9,6 +9,8 @@ var session_js_1 = require("./session.js");
9
9
  Object.defineProperty(exports, "CogniaSession", { enumerable: true, get: function () { return session_js_1.CogniaSession; } });
10
10
  var middleware_js_1 = require("./middleware.js");
11
11
  Object.defineProperty(exports, "withCognia", { enumerable: true, get: function () { return middleware_js_1.withCognia; } });
12
+ Object.defineProperty(exports, "citedItems", { enumerable: true, get: function () { return middleware_js_1.citedItems; } });
13
+ Object.defineProperty(exports, "afterReply", { enumerable: true, get: function () { return middleware_js_1.afterReply; } });
12
14
  Object.defineProperty(exports, "contextFor", { enumerable: true, get: function () { return middleware_js_1.contextFor; } });
13
15
  Object.defineProperty(exports, "prependSystem", { enumerable: true, get: function () { return middleware_js_1.prependSystem; } });
14
16
  Object.defineProperty(exports, "textOf", { enumerable: true, get: function () { return middleware_js_1.textOf; } });
@@ -1,10 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.citedItems = citedItems;
4
+ exports.afterReply = afterReply;
3
5
  exports.textOf = textOf;
4
6
  exports.contextFor = contextFor;
5
7
  exports.prependSystem = prependSystem;
6
8
  exports.withCognia = withCognia;
7
9
  const render_js_1 = require("./render.js");
10
+ /** Which injected items the reply referenced: its display id (cognia://…, cognia-skill://…) or its title. A citation is
11
+ * a usage receipt the SDK can see without the host doing anything; it is never used and never success. */
12
+ function citedItems(text, items) {
13
+ if (!text)
14
+ return [];
15
+ const t = text.toLowerCase();
16
+ return items.filter((it) => (it.displayId && t.includes(it.displayId.toLowerCase())) || (it.title && it.title.length >= 12 && t.includes(it.title.toLowerCase())));
17
+ }
18
+ /** After the reply is complete: the session learns it, the citations are reported, the host is told. Fire-and-forget. */
19
+ function afterReply(o, ctx, text) {
20
+ if (text && o.session)
21
+ o.session.add({ role: "assistant", content: text });
22
+ if (!ctx)
23
+ return;
24
+ const cited = (o.citedBy ?? citedItems)(text, ctx.items);
25
+ if (cited.length)
26
+ void o.cognia.cited(ctx.contextId, cited.map((i) => i.id));
27
+ try {
28
+ o.onResponse?.({ contextId: ctx.contextId, items: ctx.items, text, cited });
29
+ }
30
+ catch { /* a host hook never breaks the call */ }
31
+ }
8
32
  /** text of a message's content whatever the provider's shape (string, or parts with `text`) */
9
33
  function textOf(content) {
10
34
  if (typeof content === "string")
@@ -104,25 +128,23 @@ function withCognia(target, o) {
104
128
  }
105
129
  function wrapVercel(model, o) {
106
130
  const transform = async (params) => {
107
- const { block } = await contextFor(o, params.prompt);
131
+ const { block, ctx } = await contextFor(o, params.prompt);
108
132
  if (!block)
109
- return params;
110
- return { ...params, prompt: [{ role: "system", content: block }, ...params.prompt] };
133
+ return { params, ctx };
134
+ return { params: { ...params, prompt: [{ role: "system", content: block }, ...params.prompt] }, ctx };
111
135
  };
112
- const learn = (r) => { if (o.session) {
113
- const text = typeof r?.text === "string" ? r.text : "";
114
- if (text)
115
- o.session.add({ role: "assistant", content: text });
116
- } return r; };
136
+ const vercelText = (r) => { const t = r?.text; if (typeof t === "string")
137
+ return t; const c = r?.content; return Array.isArray(c) ? textOf(c) : ""; };
117
138
  return new Proxy(model, {
118
139
  get(t, k, recv) {
119
140
  if (k === "doGenerate")
120
- return async (p) => learn(await t.doGenerate(await transform(p)));
141
+ return async (p) => { const { params, ctx } = await transform(p); const r = await t.doGenerate(params); afterReply(o, ctx, vercelText(r)); return r; };
121
142
  if (k === "doStream")
122
143
  return async (p) => {
123
- const r = (await t.doStream(await transform(p)));
124
- if (o.session && r && r.stream instanceof ReadableStream)
125
- return { ...r, stream: teeStream(r.stream, (text) => o.session.add({ role: "assistant", content: text })) };
144
+ const { params, ctx } = await transform(p);
145
+ const r = (await t.doStream(params));
146
+ if ((o.session || ctx) && r && r.stream instanceof ReadableStream)
147
+ return { ...r, stream: teeStream(r.stream, (text) => afterReply(o, ctx, text)) };
126
148
  return r;
127
149
  };
128
150
  return Reflect.get(t, k, recv);
@@ -132,14 +154,12 @@ function wrapVercel(model, o) {
132
154
  function wrapOpenAI(client, o) {
133
155
  const create = client.chat.completions.create.bind(client.chat.completions);
134
156
  const wrapped = async (p, ...rest) => {
135
- const { block } = await contextFor(o, p.messages);
157
+ const { block, ctx } = await contextFor(o, p.messages);
136
158
  const r = await create(block ? { ...p, messages: prependSystem(p.messages, block) } : p, ...rest);
137
- if (o.session) {
159
+ if (o.session || ctx) {
138
160
  if (isAsyncIterable(r) && !get(r, ["choices"]))
139
- return teeIterable(r, (text) => o.session.add({ role: "assistant", content: text })); // stream: true
140
- const text = textOf(get(r, ["choices", "0", "message", "content"]));
141
- if (text)
142
- o.session.add({ role: "assistant", content: text });
161
+ return teeIterable(r, (text) => afterReply(o, ctx, text)); // stream: true
162
+ afterReply(o, ctx, textOf(get(r, ["choices", "0", "message", "content"])));
143
163
  }
144
164
  return r;
145
165
  };
@@ -149,19 +169,17 @@ function wrapOpenAI(client, o) {
149
169
  function wrapAnthropic(client, o) {
150
170
  const create = client.messages.create.bind(client.messages);
151
171
  const wrapped = async (p, ...rest) => {
152
- const { block } = await contextFor(o, p.messages);
172
+ const { block, ctx } = await contextFor(o, p.messages);
153
173
  let next = p;
154
174
  if (block) {
155
175
  const sys = typeof p.system === "string" ? `${block}\n\n${p.system}` : Array.isArray(p.system) ? [{ type: "text", text: block }, ...p.system] : block;
156
176
  next = { ...p, system: sys };
157
177
  }
158
178
  const r = await create(next, ...rest);
159
- if (o.session) {
179
+ if (o.session || ctx) {
160
180
  if (isAsyncIterable(r) && !get(r, ["content"]))
161
- return teeIterable(r, (text) => o.session.add({ role: "assistant", content: text })); // stream: true
162
- const text = textOf(get(r, ["content"]));
163
- if (text)
164
- o.session.add({ role: "assistant", content: text });
181
+ return teeIterable(r, (text) => afterReply(o, ctx, text)); // stream: true
182
+ afterReply(o, ctx, textOf(get(r, ["content"])));
165
183
  }
166
184
  return r;
167
185
  };
@@ -1,4 +1,4 @@
1
- import type { ContextRequest, ContextResponse, ContextResult, OutcomeInput, OutcomeResponse, ExtractResponse, Scope } from "./types.js";
1
+ import type { ReceiptResponse, ContextRequest, ContextResponse, ContextResult, OutcomeInput, OutcomeResponse, ExtractResponse, Scope } from "./types.js";
2
2
  export interface CogniaOptions {
3
3
  /** the agent's credential (cognia_sk_…, a developer or SDK credential); never logged, never put in a URL */
4
4
  apiKey?: string;
@@ -51,8 +51,16 @@ export declare class Cognia {
51
51
  context(req: ContextRequest | string): Promise<ContextResult>;
52
52
  /** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
53
53
  injected(contextId: string, itemIds: string[]): Promise<void>;
54
- /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to USED. */
54
+ /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
55
+ cited(contextId: string, itemIds: string[]): Promise<void>;
56
+ /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
57
+ used(contextId: string, itemIds: string[]): Promise<ReceiptResponse | null>;
58
+ /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
55
59
  outcome(contextId: string, input: OutcomeInput): Promise<OutcomeResponse>;
60
+ /** One-liners for the two outcomes. Pass `verification` when a signal (a test, a check, a metric) or a human confirmed it;
61
+ * without it the report is recorded as agent-reported, which counts as used but never as verified success. */
62
+ success(contextId: string, itemId: string, verification?: OutcomeInput["verification"]): Promise<OutcomeResponse>;
63
+ failure(contextId: string, itemId: string, verification?: OutcomeInput["verification"]): Promise<OutcomeResponse>;
56
64
  /** Session learning: propose memories from a session's turns. Nothing is saved; the host shows the proposals. */
57
65
  extract(markdown: string, filename?: string): Promise<ExtractResponse>;
58
66
  }
@@ -115,8 +115,9 @@ export class Cognia {
115
115
  token = await (timeoutMs ? Promise.race([provider, deadline]) : provider);
116
116
  }
117
117
  catch (e) {
118
+ // never the provider's own object or message (it may carry the bearer): a fresh error either way
118
119
  if (e?.name === "AbortError")
119
- throw e;
120
+ throw Object.assign(new Error("tokenProvider timed out"), { name: "AbortError" });
120
121
  throw new CogniaError(`Cognia ${method} ${path} → token provider failed`, 0, null);
121
122
  }
122
123
  finally {
@@ -128,7 +129,7 @@ export class Cognia {
128
129
  token = this.key;
129
130
  if (!token)
130
131
  throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
131
- const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/0.1.5" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
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 });
132
133
  let j = null;
133
134
  try {
134
135
  j = await r.json();
@@ -188,10 +189,41 @@ export class Cognia {
188
189
  this.onError?.(e instanceof Error ? e : new Error(String(e)));
189
190
  }
190
191
  }
191
- /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to USED. */
192
+ /** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
193
+ async cited(contextId, itemIds) {
194
+ if (!contextId || !itemIds.length)
195
+ 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
+ }
202
+ }
203
+ /** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
204
+ async used(contextId, itemIds) {
205
+ if (!contextId || !itemIds.length)
206
+ 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
+ }
214
+ }
215
+ /** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
192
216
  outcome(contextId, input) {
193
217
  return this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/outcome`, input);
194
218
  }
219
+ /** One-liners for the two outcomes. Pass `verification` when a signal (a test, a check, a metric) or a human confirmed it;
220
+ * without it the report is recorded as agent-reported, which counts as used but never as verified success. */
221
+ success(contextId, itemId, verification) {
222
+ return this.outcome(contextId, { itemId, used: true, outcome: "success", ...(verification ? { verification } : {}) });
223
+ }
224
+ failure(contextId, itemId, verification) {
225
+ return this.outcome(contextId, { itemId, used: true, outcome: "failure", ...(verification ? { verification } : {}) });
226
+ }
195
227
  /** Session learning: propose memories from a session's turns. Nothing is saved; the host shows the proposals. */
196
228
  extract(markdown, filename = "session.md") {
197
229
  return this.call("POST", "/v1/memories/extract", { markdown, filename }, 60_000);
@@ -1,5 +1,5 @@
1
1
  export { Cognia, CogniaError, shapeContext, type CogniaOptions } from "./client.js";
2
2
  export { CogniaSession, type SessionTurn } from "./session.js";
3
- export { withCognia, contextFor, prependSystem, textOf, type WithCogniaOptions } from "./middleware.js";
3
+ export { withCognia, citedItems, afterReply, contextFor, prependSystem, textOf, type WithCogniaOptions } from "./middleware.js";
4
4
  export { renderContextBlock, provenanceLine } from "./render.js";
5
5
  export type * from "./types.js";
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Cognia, CogniaError, shapeContext } from "./client.js";
2
2
  export { CogniaSession } from "./session.js";
3
- export { withCognia, contextFor, prependSystem, textOf } from "./middleware.js";
3
+ export { withCognia, citedItems, afterReply, contextFor, prependSystem, textOf } from "./middleware.js";
4
4
  export { renderContextBlock, provenanceLine } from "./render.js";
@@ -18,7 +18,22 @@ export interface WithCogniaOptions {
18
18
  role: string;
19
19
  content: unknown;
20
20
  }>) => string | null;
21
+ /** called once the model's reply is complete (streams included): the context, the reply text and which injected items
22
+ * the reply cited. The place to call `cognia.used(...)` / `cognia.success(...)` from, or to show sources that were used. */
23
+ onResponse?: (r: {
24
+ contextId: string;
25
+ items: ContextItem[];
26
+ text: string;
27
+ cited: ContextItem[];
28
+ }) => void;
29
+ /** detect citations in the reply yourself (default: display id, or the title when it is 12+ characters, found in the text) */
30
+ citedBy?: (text: string, items: ContextItem[]) => ContextItem[];
21
31
  }
32
+ /** Which injected items the reply referenced: its display id (cognia://…, cognia-skill://…) or its title. A citation is
33
+ * a usage receipt the SDK can see without the host doing anything; it is never used and never success. */
34
+ export declare function citedItems(text: string, items: ContextItem[]): ContextItem[];
35
+ /** After the reply is complete: the session learns it, the citations are reported, the host is told. Fire-and-forget. */
36
+ export declare function afterReply(o: WithCogniaOptions, ctx: ContextResponse | null, text: string): void;
22
37
  /** text of a message's content whatever the provider's shape (string, or parts with `text`) */
23
38
  export declare function textOf(content: unknown): string;
24
39
  /**
@@ -1,4 +1,26 @@
1
1
  import { renderContextBlock } from "./render.js";
2
+ /** Which injected items the reply referenced: its display id (cognia://…, cognia-skill://…) or its title. A citation is
3
+ * a usage receipt the SDK can see without the host doing anything; it is never used and never success. */
4
+ export function citedItems(text, items) {
5
+ if (!text)
6
+ return [];
7
+ const t = text.toLowerCase();
8
+ return items.filter((it) => (it.displayId && t.includes(it.displayId.toLowerCase())) || (it.title && it.title.length >= 12 && t.includes(it.title.toLowerCase())));
9
+ }
10
+ /** After the reply is complete: the session learns it, the citations are reported, the host is told. Fire-and-forget. */
11
+ export function afterReply(o, ctx, text) {
12
+ if (text && o.session)
13
+ o.session.add({ role: "assistant", content: text });
14
+ if (!ctx)
15
+ return;
16
+ const cited = (o.citedBy ?? citedItems)(text, ctx.items);
17
+ if (cited.length)
18
+ void o.cognia.cited(ctx.contextId, cited.map((i) => i.id));
19
+ try {
20
+ o.onResponse?.({ contextId: ctx.contextId, items: ctx.items, text, cited });
21
+ }
22
+ catch { /* a host hook never breaks the call */ }
23
+ }
2
24
  /** text of a message's content whatever the provider's shape (string, or parts with `text`) */
3
25
  export function textOf(content) {
4
26
  if (typeof content === "string")
@@ -98,25 +120,23 @@ export function withCognia(target, o) {
98
120
  }
99
121
  function wrapVercel(model, o) {
100
122
  const transform = async (params) => {
101
- const { block } = await contextFor(o, params.prompt);
123
+ const { block, ctx } = await contextFor(o, params.prompt);
102
124
  if (!block)
103
- return params;
104
- return { ...params, prompt: [{ role: "system", content: block }, ...params.prompt] };
125
+ return { params, ctx };
126
+ return { params: { ...params, prompt: [{ role: "system", content: block }, ...params.prompt] }, ctx };
105
127
  };
106
- const learn = (r) => { if (o.session) {
107
- const text = typeof r?.text === "string" ? r.text : "";
108
- if (text)
109
- o.session.add({ role: "assistant", content: text });
110
- } return r; };
128
+ const vercelText = (r) => { const t = r?.text; if (typeof t === "string")
129
+ return t; const c = r?.content; return Array.isArray(c) ? textOf(c) : ""; };
111
130
  return new Proxy(model, {
112
131
  get(t, k, recv) {
113
132
  if (k === "doGenerate")
114
- return async (p) => learn(await t.doGenerate(await transform(p)));
133
+ return async (p) => { const { params, ctx } = await transform(p); const r = await t.doGenerate(params); afterReply(o, ctx, vercelText(r)); return r; };
115
134
  if (k === "doStream")
116
135
  return async (p) => {
117
- const r = (await t.doStream(await transform(p)));
118
- if (o.session && r && r.stream instanceof ReadableStream)
119
- return { ...r, stream: teeStream(r.stream, (text) => o.session.add({ role: "assistant", content: text })) };
136
+ const { params, ctx } = await transform(p);
137
+ const r = (await t.doStream(params));
138
+ if ((o.session || ctx) && r && r.stream instanceof ReadableStream)
139
+ return { ...r, stream: teeStream(r.stream, (text) => afterReply(o, ctx, text)) };
120
140
  return r;
121
141
  };
122
142
  return Reflect.get(t, k, recv);
@@ -126,14 +146,12 @@ function wrapVercel(model, o) {
126
146
  function wrapOpenAI(client, o) {
127
147
  const create = client.chat.completions.create.bind(client.chat.completions);
128
148
  const wrapped = async (p, ...rest) => {
129
- const { block } = await contextFor(o, p.messages);
149
+ const { block, ctx } = await contextFor(o, p.messages);
130
150
  const r = await create(block ? { ...p, messages: prependSystem(p.messages, block) } : p, ...rest);
131
- if (o.session) {
151
+ if (o.session || ctx) {
132
152
  if (isAsyncIterable(r) && !get(r, ["choices"]))
133
- return teeIterable(r, (text) => o.session.add({ role: "assistant", content: text })); // stream: true
134
- const text = textOf(get(r, ["choices", "0", "message", "content"]));
135
- if (text)
136
- o.session.add({ role: "assistant", content: text });
153
+ return teeIterable(r, (text) => afterReply(o, ctx, text)); // stream: true
154
+ afterReply(o, ctx, textOf(get(r, ["choices", "0", "message", "content"])));
137
155
  }
138
156
  return r;
139
157
  };
@@ -143,19 +161,17 @@ function wrapOpenAI(client, o) {
143
161
  function wrapAnthropic(client, o) {
144
162
  const create = client.messages.create.bind(client.messages);
145
163
  const wrapped = async (p, ...rest) => {
146
- const { block } = await contextFor(o, p.messages);
164
+ const { block, ctx } = await contextFor(o, p.messages);
147
165
  let next = p;
148
166
  if (block) {
149
167
  const sys = typeof p.system === "string" ? `${block}\n\n${p.system}` : Array.isArray(p.system) ? [{ type: "text", text: block }, ...p.system] : block;
150
168
  next = { ...p, system: sys };
151
169
  }
152
170
  const r = await create(next, ...rest);
153
- if (o.session) {
171
+ if (o.session || ctx) {
154
172
  if (isAsyncIterable(r) && !get(r, ["content"]))
155
- return teeIterable(r, (text) => o.session.add({ role: "assistant", content: text })); // stream: true
156
- const text = textOf(get(r, ["content"]));
157
- if (text)
158
- o.session.add({ role: "assistant", content: text });
173
+ return teeIterable(r, (text) => afterReply(o, ctx, text)); // stream: true
174
+ afterReply(o, ctx, textOf(get(r, ["content"])));
159
175
  }
160
176
  return r;
161
177
  };
@@ -89,11 +89,20 @@ export interface OutcomeInput {
89
89
  used: boolean;
90
90
  outcome: "success" | "failure" | "neutral";
91
91
  verification?: {
92
- type: "agent_reported" | "signal_verified";
92
+ type: "agent_reported" | "signal_verified" | "human_confirmed";
93
93
  verifierType?: string;
94
94
  evidenceHash?: string;
95
95
  };
96
96
  }
97
+ /** receipt replies for the usage receipts between injected and outcome (2026-10-08) */
98
+ export interface ReceiptResponse {
99
+ contextId: string;
100
+ receipts: Array<{
101
+ kind: "INJECTED" | "CITED" | "USED";
102
+ itemId: string;
103
+ }>;
104
+ note?: string;
105
+ }
97
106
  export interface OutcomeResponse {
98
107
  contextId: string;
99
108
  itemId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognia-sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
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",
@@ -38,14 +38,15 @@
38
38
  "node": ">=20"
39
39
  },
40
40
  "sideEffects": false,
41
+ "scripts": {
42
+ "build": "rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
43
+ "typecheck": "tsc --noEmit -p tsconfig.json",
44
+ "test": "vitest run",
45
+ "prepack": "pnpm run build && node scripts/privacy-check.mjs"
46
+ },
41
47
  "devDependencies": {
42
48
  "typescript": "^5.6.0",
43
49
  "vitest": "^3.2.0",
44
50
  "@types/node": "^22.0.0"
45
- },
46
- "scripts": {
47
- "build": "rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
48
- "typecheck": "tsc --noEmit -p tsconfig.json",
49
- "test": "vitest run"
50
51
  }
51
- }
52
+ }