cognia-sdk 0.1.6 → 0.2.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/README.md +46 -0
- package/dist/cjs/client.js +36 -2
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/middleware.js +42 -24
- package/dist/esm/client.d.ts +14 -2
- package/dist/esm/client.js +36 -2
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/middleware.d.ts +15 -0
- package/dist/esm/middleware.js +40 -24
- package/dist/esm/types.d.ts +14 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -61,3 +61,49 @@ 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.
|
|
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.
|
package/dist/cjs/client.js
CHANGED
|
@@ -62,7 +62,9 @@ class Cognia {
|
|
|
62
62
|
tokenProvider;
|
|
63
63
|
f;
|
|
64
64
|
onError;
|
|
65
|
+
namespace;
|
|
65
66
|
constructor(o) {
|
|
67
|
+
this.namespace = o.namespace;
|
|
66
68
|
if (!o.apiKey && !o.tokenProvider)
|
|
67
69
|
throw new Error("Cognia: apiKey or tokenProvider is required");
|
|
68
70
|
this.key = o.apiKey ?? null;
|
|
@@ -166,7 +168,8 @@ class Cognia {
|
|
|
166
168
|
*/
|
|
167
169
|
async context(req) {
|
|
168
170
|
const body = typeof req === "string" ? { task: req } : req;
|
|
169
|
-
const
|
|
171
|
+
const ns = body.identity?.namespace ?? this.namespace;
|
|
172
|
+
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
173
|
try {
|
|
171
174
|
const r = await this.call("POST", "/v1/context", payload, this.timeoutMs);
|
|
172
175
|
const shaped = shapeContext(r);
|
|
@@ -194,10 +197,41 @@ class Cognia {
|
|
|
194
197
|
this.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
195
198
|
}
|
|
196
199
|
}
|
|
197
|
-
/**
|
|
200
|
+
/** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
|
|
201
|
+
async cited(contextId, itemIds) {
|
|
202
|
+
if (!contextId || !itemIds.length)
|
|
203
|
+
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
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
|
|
212
|
+
async used(contextId, itemIds) {
|
|
213
|
+
if (!contextId || !itemIds.length)
|
|
214
|
+
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
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
|
|
198
224
|
outcome(contextId, input) {
|
|
199
225
|
return this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/outcome`, input);
|
|
200
226
|
}
|
|
227
|
+
/** One-liners for the two outcomes. Pass `verification` when a signal (a test, a check, a metric) or a human confirmed it;
|
|
228
|
+
* without it the report is recorded as agent-reported, which counts as used but never as verified success. */
|
|
229
|
+
success(contextId, itemId, verification) {
|
|
230
|
+
return this.outcome(contextId, { itemId, used: true, outcome: "success", ...(verification ? { verification } : {}) });
|
|
231
|
+
}
|
|
232
|
+
failure(contextId, itemId, verification) {
|
|
233
|
+
return this.outcome(contextId, { itemId, used: true, outcome: "failure", ...(verification ? { verification } : {}) });
|
|
234
|
+
}
|
|
201
235
|
/** Session learning: propose memories from a session's turns. Nothing is saved; the host shows the proposals. */
|
|
202
236
|
extract(markdown, filename = "session.md") {
|
|
203
237
|
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; } });
|
package/dist/cjs/middleware.js
CHANGED
|
@@ -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
|
|
113
|
-
const
|
|
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) =>
|
|
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
|
|
124
|
-
|
|
125
|
-
|
|
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
|
|
140
|
-
|
|
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
|
|
162
|
-
|
|
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
|
};
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
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
|
+
/** end-user namespace sent on every context call (0.2.0). Use one per end user of YOUR app: their private memories
|
|
4
|
+
* are then invisible to every other namespace under the same key. Omit for a single-agent integration. */
|
|
5
|
+
namespace?: string;
|
|
3
6
|
/** the agent's credential (cognia_sk_…, a developer or SDK credential); never logged, never put in a URL */
|
|
4
7
|
apiKey?: string;
|
|
5
8
|
/** Delegated auth: called at REQUEST time to obtain the bearer (e.g. an MCP OAuth access token the host already
|
|
@@ -39,6 +42,7 @@ export declare class Cognia {
|
|
|
39
42
|
private readonly tokenProvider;
|
|
40
43
|
private readonly f;
|
|
41
44
|
private readonly onError;
|
|
45
|
+
readonly namespace: string | undefined;
|
|
42
46
|
constructor(o: CogniaOptions);
|
|
43
47
|
/** Every string that reaches the host through an error or a reason has the bearer removed first (independent E2E F28). */
|
|
44
48
|
private scrub;
|
|
@@ -51,8 +55,16 @@ export declare class Cognia {
|
|
|
51
55
|
context(req: ContextRequest | string): Promise<ContextResult>;
|
|
52
56
|
/** Tell Cognia which returned items were actually placed in front of the model. A usage receipt, never evidence. */
|
|
53
57
|
injected(contextId: string, itemIds: string[]): Promise<void>;
|
|
54
|
-
/**
|
|
58
|
+
/** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
|
|
59
|
+
cited(contextId: string, itemIds: string[]): Promise<void>;
|
|
60
|
+
/** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
|
|
61
|
+
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. */
|
|
55
63
|
outcome(contextId: string, input: OutcomeInput): Promise<OutcomeResponse>;
|
|
64
|
+
/** One-liners for the two outcomes. Pass `verification` when a signal (a test, a check, a metric) or a human confirmed it;
|
|
65
|
+
* without it the report is recorded as agent-reported, which counts as used but never as verified success. */
|
|
66
|
+
success(contextId: string, itemId: string, verification?: OutcomeInput["verification"]): Promise<OutcomeResponse>;
|
|
67
|
+
failure(contextId: string, itemId: string, verification?: OutcomeInput["verification"]): Promise<OutcomeResponse>;
|
|
56
68
|
/** Session learning: propose memories from a session's turns. Nothing is saved; the host shows the proposals. */
|
|
57
69
|
extract(markdown: string, filename?: string): Promise<ExtractResponse>;
|
|
58
70
|
}
|
package/dist/esm/client.js
CHANGED
|
@@ -57,7 +57,9 @@ export class Cognia {
|
|
|
57
57
|
tokenProvider;
|
|
58
58
|
f;
|
|
59
59
|
onError;
|
|
60
|
+
namespace;
|
|
60
61
|
constructor(o) {
|
|
62
|
+
this.namespace = o.namespace;
|
|
61
63
|
if (!o.apiKey && !o.tokenProvider)
|
|
62
64
|
throw new Error("Cognia: apiKey or tokenProvider is required");
|
|
63
65
|
this.key = o.apiKey ?? null;
|
|
@@ -161,7 +163,8 @@ export class Cognia {
|
|
|
161
163
|
*/
|
|
162
164
|
async context(req) {
|
|
163
165
|
const body = typeof req === "string" ? { task: req } : req;
|
|
164
|
-
const
|
|
166
|
+
const ns = body.identity?.namespace ?? this.namespace;
|
|
167
|
+
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
168
|
try {
|
|
166
169
|
const r = await this.call("POST", "/v1/context", payload, this.timeoutMs);
|
|
167
170
|
const shaped = shapeContext(r);
|
|
@@ -189,10 +192,41 @@ export class Cognia {
|
|
|
189
192
|
this.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
|
-
/**
|
|
195
|
+
/** CITED: the model's reply referenced these items (the middleware detects this by itself). A usage receipt, never evidence. */
|
|
196
|
+
async cited(contextId, itemIds) {
|
|
197
|
+
if (!contextId || !itemIds.length)
|
|
198
|
+
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
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** USED: your agent actually used these items, whether or not you know yet how it turned out. The cheapest honest signal. */
|
|
207
|
+
async used(contextId, itemIds) {
|
|
208
|
+
if (!contextId || !itemIds.length)
|
|
209
|
+
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
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** Report what happened with one item: used or not, success / failure (/ neutral for a Skill). This is the only path to success. */
|
|
193
219
|
outcome(contextId, input) {
|
|
194
220
|
return this.call("POST", `/v1/context/${encodeURIComponent(contextId)}/outcome`, input);
|
|
195
221
|
}
|
|
222
|
+
/** One-liners for the two outcomes. Pass `verification` when a signal (a test, a check, a metric) or a human confirmed it;
|
|
223
|
+
* without it the report is recorded as agent-reported, which counts as used but never as verified success. */
|
|
224
|
+
success(contextId, itemId, verification) {
|
|
225
|
+
return this.outcome(contextId, { itemId, used: true, outcome: "success", ...(verification ? { verification } : {}) });
|
|
226
|
+
}
|
|
227
|
+
failure(contextId, itemId, verification) {
|
|
228
|
+
return this.outcome(contextId, { itemId, used: true, outcome: "failure", ...(verification ? { verification } : {}) });
|
|
229
|
+
}
|
|
196
230
|
/** Session learning: propose memories from a session's turns. Nothing is saved; the host shows the proposals. */
|
|
197
231
|
extract(markdown, filename = "session.md") {
|
|
198
232
|
return this.call("POST", "/v1/memories/extract", { markdown, filename }, 60_000);
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/esm/middleware.d.ts
CHANGED
|
@@ -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
|
/**
|
package/dist/esm/middleware.js
CHANGED
|
@@ -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
|
|
107
|
-
const
|
|
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) =>
|
|
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
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
134
|
-
|
|
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
|
|
156
|
-
|
|
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
|
};
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -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";
|
|
@@ -89,11 +93,20 @@ export interface OutcomeInput {
|
|
|
89
93
|
used: boolean;
|
|
90
94
|
outcome: "success" | "failure" | "neutral";
|
|
91
95
|
verification?: {
|
|
92
|
-
type: "agent_reported" | "signal_verified";
|
|
96
|
+
type: "agent_reported" | "signal_verified" | "human_confirmed";
|
|
93
97
|
verifierType?: string;
|
|
94
98
|
evidenceHash?: string;
|
|
95
99
|
};
|
|
96
100
|
}
|
|
101
|
+
/** receipt replies for the usage receipts between injected and outcome (2026-10-08) */
|
|
102
|
+
export interface ReceiptResponse {
|
|
103
|
+
contextId: string;
|
|
104
|
+
receipts: Array<{
|
|
105
|
+
kind: "INJECTED" | "CITED" | "USED";
|
|
106
|
+
itemId: string;
|
|
107
|
+
}>;
|
|
108
|
+
note?: string;
|
|
109
|
+
}
|
|
97
110
|
export interface OutcomeResponse {
|
|
98
111
|
contextId: string;
|
|
99
112
|
itemId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognia-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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
|
+
}
|