@alexein/sdk 0.0.0-stage → 0.3.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.
@@ -0,0 +1,234 @@
1
+ import {
2
+ LazyClient,
3
+ NOTHING_FOUND,
4
+ NOTHING_YET,
5
+ SAVED,
6
+ UNAVAILABLE,
7
+ isClient,
8
+ notFoundAnswer,
9
+ openAnswer,
10
+ promptBlock,
11
+ searchAnswer,
12
+ searchFor,
13
+ stableId
14
+ } from "./chunk-OVDZ3D4M.js";
15
+ import {
16
+ AlexeinError,
17
+ AuthenticationError,
18
+ NotFoundError,
19
+ PermissionDeniedError
20
+ } from "./chunk-UNVZ22EN.js";
21
+
22
+ // src/openai-agents/run.ts
23
+ function userIdOf(context) {
24
+ const found = context?.userId;
25
+ if (!found) {
26
+ throw new Error(
27
+ 'Alexein needs to know whose memory this is: run the agent with { context: { userId: "..." } }'
28
+ );
29
+ }
30
+ return String(found);
31
+ }
32
+ function sessionIdOf(context) {
33
+ const found = context?.sessionId;
34
+ return found ? String(found) : void 0;
35
+ }
36
+ function textOf(item2) {
37
+ const content = "content" in item2 ? item2.content : void 0;
38
+ if (typeof content === "string") return content;
39
+ if (!Array.isArray(content)) return "";
40
+ return content.flatMap((part) => "text" in part && typeof part.text === "string" ? [part.text] : []).join("\n");
41
+ }
42
+ var saidByUser = (item2) => "role" in item2 && item2.role === "user";
43
+ var turns = /* @__PURE__ */ new WeakMap();
44
+ function turnOf(result) {
45
+ if (result.lastResponseId) return result.lastResponseId;
46
+ const made = turns.get(result) ?? crypto.randomUUID();
47
+ turns.set(result, made);
48
+ return made;
49
+ }
50
+ function answer(output) {
51
+ if (output === void 0 || output === null) return "";
52
+ return typeof output === "string" ? output : JSON.stringify(output);
53
+ }
54
+ async function item(said, role, turn, author) {
55
+ if (!said.trim()) return void 0;
56
+ const id = await stableId(turn, role, said);
57
+ return { id, role, text: said, ...author ? { author } : {} };
58
+ }
59
+ async function turnItems(result, session) {
60
+ const input = result.input ?? [];
61
+ const said = typeof input === "string" ? [{ role: "user", content: input }] : input;
62
+ const asked2 = said.findLast(saidByUser);
63
+ const turn = `${session ?? ""}|${turnOf(result)}`;
64
+ const items = [
65
+ await item(asked2 ? textOf(asked2) : "", "user", turn),
66
+ await item(answer(result.finalOutput), "assistant", turn, result.lastAgent?.name)
67
+ ];
68
+ return items.filter((i) => i !== void 0);
69
+ }
70
+
71
+ // src/openai-agents/tools.ts
72
+ import { tool } from "@openai/agents";
73
+ import { z } from "zod";
74
+ function failed(strict) {
75
+ return (run, error) => {
76
+ const yours = error instanceof AuthenticationError || error instanceof PermissionDeniedError;
77
+ const outage = error instanceof AlexeinError && error.outage;
78
+ const noUser = !run.context?.userId;
79
+ if (yours || noUser || strict && outage) throw error;
80
+ if (outage) {
81
+ console.warn(`Alexein is unavailable: ${error.message}`);
82
+ return UNAVAILABLE;
83
+ }
84
+ return `That did not work: ${error instanceof Error ? error.message : String(error)}`;
85
+ };
86
+ }
87
+ function buildTools(memory, write) {
88
+ const errorFunction = failed(memory.strict);
89
+ const search = tool({
90
+ name: "memory_search",
91
+ description: "Search what you remember about this user: what they said, did and decided in earlier conversations. Ask in plain language. Ends with the refs of what it found, to read whole with memory_open.",
92
+ parameters: z.object({ query: z.string().describe("What to look for") }),
93
+ errorFunction,
94
+ async execute({ query }, run) {
95
+ const found = await searchFor(memory.userOf(run?.context), query, memory.fast);
96
+ return searchAnswer(found, memory.maxChars);
97
+ }
98
+ });
99
+ const open = tool({
100
+ name: "memory_open",
101
+ description: "Read one remembered memory whole, by a ref memory_search gave.",
102
+ parameters: z.object({ ref: z.string().describe("A ref memory_search gave") }),
103
+ errorFunction,
104
+ async execute({ ref }, run) {
105
+ try {
106
+ return openAnswer(await memory.userOf(run?.context).open(ref));
107
+ } catch (error) {
108
+ if (!(error instanceof NotFoundError)) throw error;
109
+ return notFoundAnswer(error);
110
+ }
111
+ }
112
+ });
113
+ const add = tool({
114
+ name: "memory_add",
115
+ description: "Remember something about this user for later conversations: a preference, a fact about them, a decision. Write it as one plain sentence. Never save passwords, keys or secrets.",
116
+ parameters: z.object({ text: z.string().describe("One plain sentence") }),
117
+ errorFunction,
118
+ async execute({ text }, run) {
119
+ const saved = await memory.userOf(run?.context).add(text, {
120
+ kind: "note",
121
+ session: sessionIdOf(run?.context)
122
+ });
123
+ return saved.failed ? UNAVAILABLE : SAVED;
124
+ }
125
+ });
126
+ return write ? [search, open, add] : [search, open];
127
+ }
128
+
129
+ // src/openai-agents/index.ts
130
+ function asked(items) {
131
+ for (let i = items.length - 1; i >= 0; i--) {
132
+ const item2 = items[i];
133
+ const role = "role" in item2 ? item2.role : "";
134
+ if (role === "developer" || role === "system") continue;
135
+ return role === "user" ? textOf(item2).trim() : void 0;
136
+ }
137
+ return void 0;
138
+ }
139
+ var AlexeinMemory = class {
140
+ fast;
141
+ maxChars;
142
+ strict;
143
+ #client;
144
+ /**
145
+ * What `recall` found for each run - its context object - for the model calls after the first
146
+ * in a turn (a tool loop, a handoff), whose input no longer ends with the user's message. Per
147
+ * run, so two turns in flight for one user keep their own.
148
+ */
149
+ #recalled = /* @__PURE__ */ new WeakMap();
150
+ /** `client` defaults to one made on first use: fail-open unless strict, a 10 s timeout. */
151
+ constructor(client, options = {}) {
152
+ const given = isClient(client) ? client : void 0;
153
+ const settings = isClient(client) ? options : { ...client, ...options };
154
+ this.fast = settings.fast ?? false;
155
+ this.maxChars = settings.maxChars ?? 4e3;
156
+ this.strict = settings.strict ?? false;
157
+ this.#client = new LazyClient(given, "openai-agents", this.strict, settings.timeout);
158
+ }
159
+ get client() {
160
+ return this.#client.client;
161
+ }
162
+ /** The memory of the app user a run's context names. */
163
+ userOf(context) {
164
+ return this.client.user(userIdOf(context));
165
+ }
166
+ /**
167
+ * `callModelInputFilter: memory.recall`: before the model, what memory holds for the user's
168
+ * latest message, added to the instructions. Already using a filter? Run both:
169
+ * `async (args) => yours({ ...args, modelData: await memory.recall(args) })`.
170
+ */
171
+ recall = async ({
172
+ modelData,
173
+ context
174
+ }) => {
175
+ const user = this.userOf(context);
176
+ const run = context;
177
+ let question = asked(modelData.input);
178
+ if (question === void 0 && !this.#recalled.has(run)) {
179
+ const said = modelData.input.findLast(saidByUser);
180
+ question = said && textOf(said).trim();
181
+ }
182
+ if (question !== void 0) {
183
+ const found = question ? await searchFor(user, question, this.fast) : void 0;
184
+ this.#recalled.set(run, promptBlock(found?.context(this.maxChars) ?? ""));
185
+ }
186
+ const block = this.#recalled.get(run);
187
+ if (!block) return modelData;
188
+ const given = modelData.instructions;
189
+ return { ...modelData, instructions: given ? `${given}
190
+
191
+ ${block}` : block };
192
+ };
193
+ /**
194
+ * Save the turn a run answered - the user's latest message and the final output, under the
195
+ * agent's name - in the context's `sessionId`. Call it once the run is done (for a stream, once
196
+ * `completed` resolves). Saving the same result again edits rather than copies. Tool calls and
197
+ * their results are not saved, and a run paused for an approval saves nothing. Needs a key that
198
+ * writes.
199
+ */
200
+ remember = async (result) => {
201
+ if (result.interruptions?.length) return;
202
+ const context = result.runContext?.context;
203
+ const session = sessionIdOf(context);
204
+ const items = await turnItems(result, session);
205
+ if (items.length) await this.userOf(context).add(items, { session });
206
+ };
207
+ /**
208
+ * Tools the model calls when it decides to: `memory_search` and `memory_open`, and with
209
+ * `write`, `memory_add`. The user comes from the run's context, which a model never sees.
210
+ */
211
+ tools({ write = false } = {}) {
212
+ return buildTools(this, write);
213
+ }
214
+ };
215
+ var fallback;
216
+ function defaultMemory() {
217
+ fallback ??= new AlexeinMemory();
218
+ return fallback;
219
+ }
220
+ var recall = (args) => defaultMemory().recall(args);
221
+ var remember = (result) => defaultMemory().remember(result);
222
+ var memoryTools = (options = {}) => defaultMemory().tools(options);
223
+ export {
224
+ AlexeinMemory,
225
+ NOTHING_FOUND,
226
+ NOTHING_YET,
227
+ UNAVAILABLE,
228
+ defaultMemory,
229
+ memoryTools,
230
+ recall,
231
+ remember,
232
+ sessionIdOf,
233
+ userIdOf
234
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * What the framework integrations share: a client made on first use, the tools' answers in words
3
+ * a model reads, and the ids that make saving a turn twice an edit rather than a copy. Built on
4
+ * the client alone; never HTTP.
5
+ */
6
+
7
+ /** What the model is told when memory is down, unless the integration is strict. */
8
+ declare const UNAVAILABLE = "Memory is unavailable right now; carry on without it.";
9
+ declare const NOTHING_FOUND = "Nothing remembered about that.";
10
+ declare const NOTHING_YET = "Nothing has been remembered for this user yet.";
11
+ /** What an integration's entry point takes besides its client. */
12
+ interface IntegrationOptions {
13
+ /** Skip search's relevance check: free and quicker, a little less precise. */
14
+ fast?: boolean;
15
+ /** How much of what memory holds goes into a prompt or a tool's answer, in characters. */
16
+ maxChars?: number;
17
+ /** Throw on an outage instead of carrying on without memory. */
18
+ strict?: boolean;
19
+ /** Milliseconds each attempt may take, for the client made on first use. Default 10 s. */
20
+ timeout?: number;
21
+ }
22
+
23
+ export { type IntegrationOptions as I, NOTHING_FOUND as N, UNAVAILABLE as U, NOTHING_YET as a };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * What the framework integrations share: a client made on first use, the tools' answers in words
3
+ * a model reads, and the ids that make saving a turn twice an edit rather than a copy. Built on
4
+ * the client alone; never HTTP.
5
+ */
6
+
7
+ /** What the model is told when memory is down, unless the integration is strict. */
8
+ declare const UNAVAILABLE = "Memory is unavailable right now; carry on without it.";
9
+ declare const NOTHING_FOUND = "Nothing remembered about that.";
10
+ declare const NOTHING_YET = "Nothing has been remembered for this user yet.";
11
+ /** What an integration's entry point takes besides its client. */
12
+ interface IntegrationOptions {
13
+ /** Skip search's relevance check: free and quicker, a little less precise. */
14
+ fast?: boolean;
15
+ /** How much of what memory holds goes into a prompt or a tool's answer, in characters. */
16
+ maxChars?: number;
17
+ /** Throw on an outage instead of carrying on without memory. */
18
+ strict?: boolean;
19
+ /** Milliseconds each attempt may take, for the client made on first use. Default 10 s. */
20
+ timeout?: number;
21
+ }
22
+
23
+ export { type IntegrationOptions as I, NOTHING_FOUND as N, UNAVAILABLE as U, NOTHING_YET as a };
package/package.json CHANGED
@@ -1,6 +1,129 @@
1
1
  {
2
2
  "name": "@alexein/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.0",
4
+ "description": "JavaScript and TypeScript client for Alexein: long-term memory for your users and agents, with LangGraph.js and OpenAI Agents SDK integrations.",
5
+ "license": "MIT",
6
+ "author": "Alexein AI",
7
+ "homepage": "https://docs.alexein.ai/sdks/typescript",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Alexein-AI/sdk.git",
11
+ "directory": "typescript"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Alexein-AI/sdk/issues"
15
+ },
16
+ "keywords": [
17
+ "alexein",
18
+ "memory",
19
+ "long-term-memory",
20
+ "agents",
21
+ "ai-agents",
22
+ "langgraph",
23
+ "openai-agents"
24
+ ],
25
+ "type": "module",
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
29
+ "sideEffects": false,
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "import": {
36
+ "types": "./dist/index.d.ts",
37
+ "default": "./dist/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/index.d.cts",
41
+ "default": "./dist/index.cjs"
42
+ }
43
+ },
44
+ "./langgraph": {
45
+ "import": {
46
+ "types": "./dist/langgraph.d.ts",
47
+ "default": "./dist/langgraph.js"
48
+ },
49
+ "require": {
50
+ "types": "./dist/langgraph.d.cts",
51
+ "default": "./dist/langgraph.cjs"
52
+ }
53
+ },
54
+ "./openai-agents": {
55
+ "import": {
56
+ "types": "./dist/openai-agents.d.ts",
57
+ "default": "./dist/openai-agents.js"
58
+ },
59
+ "require": {
60
+ "types": "./dist/openai-agents.d.cts",
61
+ "default": "./dist/openai-agents.cjs"
62
+ }
63
+ },
64
+ "./package.json": "./package.json"
65
+ },
66
+ "typesVersions": {
67
+ "*": {
68
+ "langgraph": [
69
+ "./dist/langgraph.d.ts"
70
+ ],
71
+ "openai-agents": [
72
+ "./dist/openai-agents.d.ts"
73
+ ]
74
+ }
75
+ },
76
+ "files": [
77
+ "dist/*.js",
78
+ "dist/*.cjs",
79
+ "dist/*.d.ts",
80
+ "dist/*.d.cts",
81
+ "README.md",
82
+ "CHANGELOG.md",
83
+ "LICENSE"
84
+ ],
85
+ "publishConfig": {
86
+ "access": "public"
87
+ },
88
+ "scripts": {
89
+ "build": "tsup",
90
+ "typecheck": "tsc --noEmit",
91
+ "lint": "prettier --check .",
92
+ "check": "tsc --noEmit && prettier --check . && vitest run",
93
+ "test": "vitest run",
94
+ "format": "prettier --write .",
95
+ "prepublishOnly": "npm run check && npm run build"
96
+ },
97
+ "peerDependencies": {
98
+ "@langchain/core": ">=1.0.0 <2.0.0",
99
+ "@langchain/langgraph": ">=1.0.0 <2.0.0",
100
+ "@openai/agents": ">=0.21.0 <1.0.0",
101
+ "zod": "^3.25.0 || ^4.0.0"
102
+ },
103
+ "peerDependenciesMeta": {
104
+ "@langchain/core": {
105
+ "optional": true
106
+ },
107
+ "@langchain/langgraph": {
108
+ "optional": true
109
+ },
110
+ "@openai/agents": {
111
+ "optional": true
112
+ },
113
+ "zod": {
114
+ "optional": true
115
+ }
116
+ },
117
+ "devDependencies": {
118
+ "@langchain/core": "^1.3.0",
119
+ "@langchain/langgraph": "^1.6.0",
120
+ "@openai/agents": "^0.21.0",
121
+ "@types/node": "^22.10.0",
122
+ "langchain": "^1.6.0",
123
+ "prettier": "^3.4.0",
124
+ "tsup": "^8.3.0",
125
+ "typescript": "^5.7.0",
126
+ "vitest": "^3.0.0",
127
+ "zod": "^4.0.0"
128
+ }
129
+ }