cognia-sdk 0.1.2 → 0.1.4
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 +16 -0
- package/dist/cjs/client.js +47 -6
- package/dist/esm/client.d.ts +8 -2
- package/dist/esm/client.js +47 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,22 @@ const proposals = await session.end();
|
|
|
33
33
|
Injecting something never counts as using it. Only `outcome()` creates evidence, and only for items this context
|
|
34
34
|
returned. Dogfood, canary and operator traffic is classified server-side and never counts.
|
|
35
35
|
|
|
36
|
+
|
|
37
|
+
## Auth: key or delegated token
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// a developer credential or an SDK credential (cognia_sk_…)
|
|
41
|
+
new Cognia({ apiKey: process.env.COGNIA_API_KEY })
|
|
42
|
+
|
|
43
|
+
// delegated auth: the host already holds a Cognia bearer (e.g. the MCP OAuth access token)
|
|
44
|
+
new Cognia({ tokenProvider: async () => await getCogniaToken() }) // called on every request, never cached, never in errors
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If your identity lives in a connected client (Claude, ChatGPT, OpenClaw…) and the host hides its token, ask the connected
|
|
48
|
+
Cognia to run `sdk_link_start`: it shows a one-time code and a Cloud link; you approve on the website and receive an
|
|
49
|
+
**SDK credential** — a separate, narrow credential that can read context and report outcomes only. Details:
|
|
50
|
+
`docs/SDK_CREDENTIALS.md` in the Cognia repository.
|
|
51
|
+
|
|
36
52
|
## Scope
|
|
37
53
|
|
|
38
54
|
`mine` = the identity's own memories and Skills. `organization` (default) = plus everything its organizations share
|
package/dist/cjs/client.js
CHANGED
|
@@ -59,12 +59,14 @@ class Cognia {
|
|
|
59
59
|
budget;
|
|
60
60
|
timeoutMs;
|
|
61
61
|
key;
|
|
62
|
+
tokenProvider;
|
|
62
63
|
f;
|
|
63
64
|
onError;
|
|
64
65
|
constructor(o) {
|
|
65
|
-
if (!o.apiKey)
|
|
66
|
-
throw new Error("Cognia: apiKey is required");
|
|
67
|
-
this.key = o.apiKey;
|
|
66
|
+
if (!o.apiKey && !o.tokenProvider)
|
|
67
|
+
throw new Error("Cognia: apiKey or tokenProvider is required");
|
|
68
|
+
this.key = o.apiKey ?? null;
|
|
69
|
+
this.tokenProvider = o.tokenProvider ?? null;
|
|
68
70
|
this.baseUrl = (o.baseUrl ?? "https://api.cognia.fun").replace(/\/$/, "");
|
|
69
71
|
this.scope = o.scope ?? "organization";
|
|
70
72
|
this.budget = o.budget;
|
|
@@ -72,20 +74,59 @@ class Cognia {
|
|
|
72
74
|
this.f = o.fetch ?? fetch;
|
|
73
75
|
this.onError = o.onError;
|
|
74
76
|
}
|
|
77
|
+
/** Every string that reaches the host through an error or a reason has the bearer removed first (independent E2E F28). */
|
|
78
|
+
scrub(v, token) {
|
|
79
|
+
if (!token || token.length < 8)
|
|
80
|
+
return v;
|
|
81
|
+
const re = new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
82
|
+
const walk = (x) => {
|
|
83
|
+
if (typeof x === "string")
|
|
84
|
+
return x.replace(re, "[redacted]");
|
|
85
|
+
if (Array.isArray(x))
|
|
86
|
+
return x.map(walk);
|
|
87
|
+
if (x && typeof x === "object") {
|
|
88
|
+
const o = {};
|
|
89
|
+
for (const [k, val] of Object.entries(x))
|
|
90
|
+
o[k] = walk(val);
|
|
91
|
+
return o;
|
|
92
|
+
}
|
|
93
|
+
return x;
|
|
94
|
+
};
|
|
95
|
+
return walk(v);
|
|
96
|
+
}
|
|
75
97
|
async call(method, path, body, timeoutMs) {
|
|
76
98
|
const ac = new AbortController();
|
|
77
|
-
|
|
99
|
+
let timedOut = false;
|
|
100
|
+
const t = timeoutMs ? setTimeout(() => { timedOut = true; ac.abort(); }, timeoutMs) : null;
|
|
101
|
+
let token = null;
|
|
78
102
|
try {
|
|
79
|
-
|
|
103
|
+
// delegated auth: the provider is asked on EVERY request, INSIDE the same deadline as the request itself (independent
|
|
104
|
+
// E2E F27: a pending provider used to block the host model), and its answer is used once, never stored on the client
|
|
105
|
+
const deadline = new Promise((_, reject) => { if (timeoutMs)
|
|
106
|
+
setTimeout(() => reject(Object.assign(new Error("tokenProvider timed out"), { name: "AbortError" })), timeoutMs); });
|
|
107
|
+
token = this.tokenProvider ? await (timeoutMs ? Promise.race([Promise.resolve(this.tokenProvider()), deadline]) : this.tokenProvider()) : this.key;
|
|
108
|
+
if (!token)
|
|
109
|
+
throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
|
|
110
|
+
const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/0.1.4" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
|
|
80
111
|
let j = null;
|
|
81
112
|
try {
|
|
82
113
|
j = await r.json();
|
|
83
114
|
}
|
|
84
115
|
catch { /* no body */ }
|
|
85
116
|
if (!r.ok)
|
|
86
|
-
throw new CogniaError(`Cognia ${method} ${path} → ${r.status}`, r.status, j);
|
|
117
|
+
throw new CogniaError(`Cognia ${method} ${path} → ${r.status}`, r.status, this.scrub(j, token));
|
|
87
118
|
return j;
|
|
88
119
|
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
// whatever the transport or the provider threw: the bearer never travels inside the error. A fresh error is built
|
|
122
|
+
// (a DOMException's message is read-only), keeping the name, status and a scrubbed body.
|
|
123
|
+
const src = e instanceof Error ? e : new Error(String(e));
|
|
124
|
+
const msg = token ? this.scrub(src.message, token) : src.message;
|
|
125
|
+
const out = src instanceof CogniaError ? new CogniaError(msg, src.status, token ? this.scrub(src.body, token) : src.body) : Object.assign(new Error(msg), { name: src.name });
|
|
126
|
+
if (timedOut)
|
|
127
|
+
out.name = "AbortError";
|
|
128
|
+
throw out;
|
|
129
|
+
}
|
|
89
130
|
finally {
|
|
90
131
|
if (t)
|
|
91
132
|
clearTimeout(t);
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ContextRequest, ContextResponse, ContextResult, OutcomeInput, OutcomeResponse, ExtractResponse, Scope } from "./types.js";
|
|
2
2
|
export interface CogniaOptions {
|
|
3
|
-
/** the agent's credential (cognia_sk_
|
|
4
|
-
apiKey
|
|
3
|
+
/** the agent's credential (cognia_sk_…, a developer or SDK credential); never logged, never put in a URL */
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
/** Delegated auth: called at REQUEST time to obtain the bearer (e.g. an MCP OAuth access token the host already
|
|
6
|
+
* holds); async so the host can refresh. The SDK never caches the token and never puts it in errors or onError. */
|
|
7
|
+
tokenProvider?: () => Promise<string> | string;
|
|
5
8
|
baseUrl?: string;
|
|
6
9
|
/** default scope for context(); the identity's grants decide what that means */
|
|
7
10
|
scope?: Scope;
|
|
@@ -33,9 +36,12 @@ export declare class Cognia {
|
|
|
33
36
|
} | undefined;
|
|
34
37
|
readonly timeoutMs: number;
|
|
35
38
|
private readonly key;
|
|
39
|
+
private readonly tokenProvider;
|
|
36
40
|
private readonly f;
|
|
37
41
|
private readonly onError;
|
|
38
42
|
constructor(o: CogniaOptions);
|
|
43
|
+
/** Every string that reaches the host through an error or a reason has the bearer removed first (independent E2E F28). */
|
|
44
|
+
private scrub;
|
|
39
45
|
private call;
|
|
40
46
|
/**
|
|
41
47
|
* The Memories and Skills this identity may use for `task`, or an explicit ABSTAIN. Never throws: on a timeout or a
|
package/dist/esm/client.js
CHANGED
|
@@ -54,12 +54,14 @@ export class Cognia {
|
|
|
54
54
|
budget;
|
|
55
55
|
timeoutMs;
|
|
56
56
|
key;
|
|
57
|
+
tokenProvider;
|
|
57
58
|
f;
|
|
58
59
|
onError;
|
|
59
60
|
constructor(o) {
|
|
60
|
-
if (!o.apiKey)
|
|
61
|
-
throw new Error("Cognia: apiKey is required");
|
|
62
|
-
this.key = o.apiKey;
|
|
61
|
+
if (!o.apiKey && !o.tokenProvider)
|
|
62
|
+
throw new Error("Cognia: apiKey or tokenProvider is required");
|
|
63
|
+
this.key = o.apiKey ?? null;
|
|
64
|
+
this.tokenProvider = o.tokenProvider ?? null;
|
|
63
65
|
this.baseUrl = (o.baseUrl ?? "https://api.cognia.fun").replace(/\/$/, "");
|
|
64
66
|
this.scope = o.scope ?? "organization";
|
|
65
67
|
this.budget = o.budget;
|
|
@@ -67,20 +69,59 @@ export class Cognia {
|
|
|
67
69
|
this.f = o.fetch ?? fetch;
|
|
68
70
|
this.onError = o.onError;
|
|
69
71
|
}
|
|
72
|
+
/** Every string that reaches the host through an error or a reason has the bearer removed first (independent E2E F28). */
|
|
73
|
+
scrub(v, token) {
|
|
74
|
+
if (!token || token.length < 8)
|
|
75
|
+
return v;
|
|
76
|
+
const re = new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
77
|
+
const walk = (x) => {
|
|
78
|
+
if (typeof x === "string")
|
|
79
|
+
return x.replace(re, "[redacted]");
|
|
80
|
+
if (Array.isArray(x))
|
|
81
|
+
return x.map(walk);
|
|
82
|
+
if (x && typeof x === "object") {
|
|
83
|
+
const o = {};
|
|
84
|
+
for (const [k, val] of Object.entries(x))
|
|
85
|
+
o[k] = walk(val);
|
|
86
|
+
return o;
|
|
87
|
+
}
|
|
88
|
+
return x;
|
|
89
|
+
};
|
|
90
|
+
return walk(v);
|
|
91
|
+
}
|
|
70
92
|
async call(method, path, body, timeoutMs) {
|
|
71
93
|
const ac = new AbortController();
|
|
72
|
-
|
|
94
|
+
let timedOut = false;
|
|
95
|
+
const t = timeoutMs ? setTimeout(() => { timedOut = true; ac.abort(); }, timeoutMs) : null;
|
|
96
|
+
let token = null;
|
|
73
97
|
try {
|
|
74
|
-
|
|
98
|
+
// delegated auth: the provider is asked on EVERY request, INSIDE the same deadline as the request itself (independent
|
|
99
|
+
// E2E F27: a pending provider used to block the host model), and its answer is used once, never stored on the client
|
|
100
|
+
const deadline = new Promise((_, reject) => { if (timeoutMs)
|
|
101
|
+
setTimeout(() => reject(Object.assign(new Error("tokenProvider timed out"), { name: "AbortError" })), timeoutMs); });
|
|
102
|
+
token = this.tokenProvider ? await (timeoutMs ? Promise.race([Promise.resolve(this.tokenProvider()), deadline]) : this.tokenProvider()) : this.key;
|
|
103
|
+
if (!token)
|
|
104
|
+
throw new CogniaError(`Cognia ${method} ${path} → no credential`, 0, null);
|
|
105
|
+
const r = await this.f(this.baseUrl + path, { method, headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "user-agent": "cognia-sdk/0.1.4" }, body: body === undefined ? undefined : JSON.stringify(body), signal: ac.signal });
|
|
75
106
|
let j = null;
|
|
76
107
|
try {
|
|
77
108
|
j = await r.json();
|
|
78
109
|
}
|
|
79
110
|
catch { /* no body */ }
|
|
80
111
|
if (!r.ok)
|
|
81
|
-
throw new CogniaError(`Cognia ${method} ${path} → ${r.status}`, r.status, j);
|
|
112
|
+
throw new CogniaError(`Cognia ${method} ${path} → ${r.status}`, r.status, this.scrub(j, token));
|
|
82
113
|
return j;
|
|
83
114
|
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
// whatever the transport or the provider threw: the bearer never travels inside the error. A fresh error is built
|
|
117
|
+
// (a DOMException's message is read-only), keeping the name, status and a scrubbed body.
|
|
118
|
+
const src = e instanceof Error ? e : new Error(String(e));
|
|
119
|
+
const msg = token ? this.scrub(src.message, token) : src.message;
|
|
120
|
+
const out = src instanceof CogniaError ? new CogniaError(msg, src.status, token ? this.scrub(src.body, token) : src.body) : Object.assign(new Error(msg), { name: src.name });
|
|
121
|
+
if (timedOut)
|
|
122
|
+
out.name = "AbortError";
|
|
123
|
+
throw out;
|
|
124
|
+
}
|
|
84
125
|
finally {
|
|
85
126
|
if (t)
|
|
86
127
|
clearTimeout(t);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognia-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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",
|