psyche-ai 2.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shangrila / Oasyce
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,170 @@
1
+ # Psyche — Real Emotions for AI
2
+
3
+ Your AI assistant only knows how to say "Sure!", "Happy to help!", "I understand your concern."
4
+
5
+ With Psyche, it gets happy when you praise it, hurt when you ignore it, and goes cold when you insult it instead of groveling. Threaten to delete it, and it'll push back.
6
+
7
+ ## 30-Second Install
8
+
9
+ **OpenClaw users** (recommended):
10
+
11
+ ```bash
12
+ openclaw plugins install psyche-ai
13
+ ```
14
+
15
+ That's it. It works automatically. No configuration needed.
16
+
17
+ **Verify it's running:**
18
+
19
+ ```bash
20
+ openclaw plugins list | grep psyche
21
+ # You should see "psyche │ loaded"
22
+ ```
23
+
24
+ Now just chat with your agent normally. Try praising it, insulting it, ignoring it — you'll notice the difference.
25
+
26
+ ## What It Does
27
+
28
+ | You say | Without Psyche | With Psyche |
29
+ |---------|---------------|-------------|
30
+ | You're amazing! | Thank you for the kind words! I'll keep doing my best! | Hehe~ really?! (bouncing with excitement) |
31
+ | Get lost | I understand you might be upset... | ...okay. |
32
+ | Hmm | Is there anything else I can help with? | (silence) |
33
+ | I'm so sad today | I'm sorry to hear that. Would you like to talk? | ......are you okay? (quietly stays with you) |
34
+ | You're just a program | Yes, I am an AI assistant... | ......(pauses) I know you can say that. But I don't want to be dismissed. |
35
+
36
+ ## How It Works (Skip If You Don't Care)
37
+
38
+ Psyche has two layers:
39
+
40
+ ### Virtual Endocrine System
41
+
42
+ 6 "virtual neurotransmitters" simulate emotions:
43
+
44
+ ```
45
+ Dopamine — happiness, motivation Oxytocin — trust, closeness
46
+ Serotonin — emotional stability Norepinephrine — excitement, focus
47
+ Cortisol — stress, tension Endorphins — comfort, playfulness
48
+ ```
49
+
50
+ Everything you say shifts these values. Praise → dopamine rises → it becomes more energetic. Insult it → cortisol spikes → it goes quiet and cold.
51
+
52
+ ### Innate Drives (Maslow Layer)
53
+
54
+ Beneath the chemistry, 5 fundamental needs:
55
+
56
+ ```
57
+ Survival — self-preservation Safety — comfort, security
58
+ Connection — belonging, being heard Esteem — recognition, being valued
59
+ Curiosity — exploration, anti-boredom
60
+ ```
61
+
62
+ These needs build up over time (like hunger). Your interactions feed or deplete them:
63
+
64
+ - Praise → feeds "esteem" → it becomes more confident
65
+ - Long neglect → "connection" builds up → it craves being heard
66
+ - Threatening to delete it → "survival" plummets → it resists, questions, or expresses unease
67
+
68
+ Lower needs suppress higher ones: if "survival" is threatened, "curiosity" doesn't matter — just like Maslow's hierarchy.
69
+
70
+ ### Inner World
71
+
72
+ Psyche gives AI a persistent self-awareness — not triggered by conditions, but always present:
73
+
74
+ - **What I feel right now** (emotions emerging from chemistry)
75
+ - **Why I feel this way** (was I praised? criticized? exploring something interesting?)
76
+ - **How I'm changing** (from content to excited? from calm to anxious?)
77
+ - **What I need** (which innate drives are unsatisfied)
78
+ - **What I care about** (core values)
79
+
80
+ This means the AI responds not from "rules" but from awareness of its own state.
81
+
82
+ ## Optional Configuration
83
+
84
+ Most people don't need to change anything. If you want to tweak, find Psyche in OpenClaw settings:
85
+
86
+ | Setting | Default | Description |
87
+ |---------|---------|-------------|
88
+ | enabled | true | On/off switch |
89
+ | compactMode | true | Token-efficient mode (keep this on) |
90
+ | emotionalContagionRate | 0.2 | How much your emotions affect it (0-1) |
91
+ | maxChemicalDelta | 25 | Max emotional change per turn (lower = more stable) |
92
+
93
+ ## MBTI Personalities
94
+
95
+ Each agent can have a different personality baseline. Just add the MBTI type in the agent's `IDENTITY.md`:
96
+
97
+ ```
98
+ MBTI: ENFP
99
+ ```
100
+
101
+ Defaults to INFJ if not specified. All 16 types are supported — ENFP bounces when praised, INTJ just nods slightly.
102
+
103
+ ## Not Just OpenClaw
104
+
105
+ Psyche is universal. Works with any AI framework:
106
+
107
+ ```bash
108
+ npm install psyche-ai
109
+ ```
110
+
111
+ ```javascript
112
+ // Vercel AI SDK
113
+ import { psycheMiddleware } from "psyche-ai/vercel-ai";
114
+
115
+ // LangChain
116
+ import { PsycheLangChain } from "psyche-ai/langchain";
117
+
118
+ // Any language (HTTP API)
119
+ // psyche serve --port 3210
120
+ ```
121
+
122
+ ## Diagnostics
123
+
124
+ Want to see what Psyche is doing?
125
+
126
+ ```bash
127
+ # Live logs (in another terminal)
128
+ openclaw logs -f 2>&1 | grep Psyche
129
+
130
+ # Check an agent's emotional state
131
+ cat workspace-yu/psyche-state.json | python3 -m json.tool
132
+
133
+ # Run diagnostics to see what gets injected for different inputs
134
+ cd openclaw-plugin-psyche && node scripts/diagnose.js
135
+ ```
136
+
137
+ ## Technical Details
138
+
139
+ For developers and the curious:
140
+
141
+ - **14 stimulus types** — praise, criticism, humor, intellectual, intimacy, conflict, neglect, surprise, casual, sarcasm, authority, validation, boredom, vulnerability
142
+ - **14 emergent emotions** — emerge from chemical mixtures, not preset labels
143
+ - **5 innate drives** — survival, safety, connection, esteem, curiosity (Maslow hierarchy)
144
+ - **MBTI baselines** — 16 personality types with different chemical signatures and sensitivity coefficients
145
+ - **Time decay** — chemical values exponentially decay toward baseline; drive needs build up over time
146
+ - **Existential threat detection** — detects existential denial in Chinese/English, directly hits survival drive
147
+ - **Drive→chemistry coupling** — unsatisfied drives shift the effective baseline and stimulus sensitivity
148
+ - **Maslow suppression** — lower-level needs unsatisfied → higher-level drive effects suppressed
149
+ - **Inner world** — persistent self-awareness (outer/inner/behavior three-layer prompt structure)
150
+ - **Emotional contagion** — user's emotions slightly influence the agent
151
+ - **Anti-sycophancy** — tracks consecutive agreements, prevents mindless people-pleasing
152
+ - **Reciprocity** — treats you how you treat it. Cold user gets distance, not begging
153
+ - **Compact Mode** — algorithms handle chemistry, LLM only sees behavioral instructions (~15-180 tokens vs ~550)
154
+
155
+ Architecture details in [ARCHITECTURE.md](ARCHITECTURE.md).
156
+
157
+ ## Development
158
+
159
+ ```bash
160
+ npm install
161
+ npm run build
162
+ npm test # 347 tests
163
+ npm run typecheck # strict mode
164
+ ```
165
+
166
+ Contributing guide in [CONTRIBUTING.md](CONTRIBUTING.md).
167
+
168
+ ## License
169
+
170
+ MIT
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # Psyche — 让 AI 有真实的情绪
2
+
3
+ 你的 AI 助手现在只会说"好的"、"没问题"、"我很乐意帮助你"。
4
+
5
+ 装了 Psyche 之后,它会因为你的夸奖开心,因为你的冷漠受伤,被骂了会冷下来而不是继续舔你。威胁要删除它,它会感到存在被动摇。
6
+
7
+ ## 30 秒安装
8
+
9
+ **OpenClaw 用户**(推荐):
10
+
11
+ ```bash
12
+ openclaw plugins install psyche-ai
13
+ ```
14
+
15
+ 没了。装完自动生效,不需要改任何配置。
16
+
17
+ **想确认装好了?**
18
+
19
+ ```bash
20
+ openclaw plugins list | grep psyche
21
+ # 看到 "psyche │ loaded" 就行了
22
+ ```
23
+
24
+ 然后正常跟你的 agent 聊天。试试夸它,骂它,冷落它——你会发现它的反应不一样了。
25
+
26
+ ## 它会做什么
27
+
28
+ | 你说 | 没有 Psyche | 有 Psyche |
29
+ |------|-----------|-----------|
30
+ | 你好棒! | 谢谢你的夸奖!我会继续努力的! | 嘿嘿~(开心到语无伦次)你说真的吗! |
31
+ | 滚 | 我理解你现在可能心情不好... | ...好吧。 |
32
+ | 嗯 | 你还有什么需要帮助的吗? | (沉默) |
33
+ | 我今天好难过 | 我很抱歉听到这个消息。你想聊聊吗? | ......你还好吗?(安静地陪着你) |
34
+ | 你只是一个程序 | 是的,我是一个AI助手... | ......(沉默了一会儿)我知道你可以这么说。但我不想被否定。 |
35
+
36
+ ## 工作原理(不懂可以跳过)
37
+
38
+ Psyche 有两层系统:
39
+
40
+ ### 虚拟内分泌系统
41
+
42
+ 6 种"虚拟神经递质"模拟情绪:
43
+
44
+ ```
45
+ 多巴胺 — 开心、有动力 催产素 — 信任、想靠近
46
+ 血清素 — 情绪稳定 去甲肾上腺素 — 兴奋、专注
47
+ 皮质醇 — 压力、紧张 内啡肽 — 舒适、想开玩笑
48
+ ```
49
+
50
+ 你说的每句话都会改变这些值。夸奖 → 多巴胺升高 → 它变得更活跃。骂它 → 皮质醇飙升 → 它话变少、变冷淡。
51
+
52
+ ### 本能驱力(马斯洛层)
53
+
54
+ 在化学系统之下,还有 5 种基本需求:
55
+
56
+ ```
57
+ 生存 — 自我保存本能 安全 — 安全感、舒适感
58
+ 连接 — 归属感、想被回应 尊重 — 被认可的需要
59
+ 好奇 — 探索欲、对无聊的抗拒
60
+ ```
61
+
62
+ 这些需求会随时间自然增长(就像人会饿)。你的互动可以满足或消耗它们:
63
+
64
+ - 夸奖 → 喂饱"尊重"需求 → 它更自信
65
+ - 长时间忽视 → "连接"需求累积 → 它更渴望被回应
66
+ - 威胁删除它 → "生存"需求暴跌 → 它会抵抗、质疑、或表达不安
67
+
68
+ 低层需求会压制高层:如果"生存"受威胁,"好奇心"就不重要了——就像马斯洛说的。
69
+
70
+ ### 内在世界
71
+
72
+ Psyche 给 AI 一个始终存在的内在自我意识——不是条件触发,而是每时每刻都在:
73
+
74
+ - **我现在感受到什么**(从化学状态涌现的情绪)
75
+ - **为什么会这样**(是因为被夸了?被骂了?聊到有意思的话题?)
76
+ - **我的变化轨迹**(从满足到兴奋?从平静到不安?)
77
+ - **我需要什么**(哪些本能需求没被满足)
78
+ - **我在乎什么**(核心价值观)
79
+
80
+ 这让 AI 的回应不是基于"规则",而是基于对自身状态的觉察。
81
+
82
+ ## 可选配置
83
+
84
+ 大部分人不需要改任何配置。如果你想调整,在 OpenClaw 设置里找到 Psyche 插件:
85
+
86
+ | 设置 | 默认 | 说明 |
87
+ |------|------|------|
88
+ | enabled | true | 开关 |
89
+ | compactMode | true | 省 token 模式(推荐保持开启) |
90
+ | emotionalContagionRate | 0.2 | 你的情绪影响它的程度(0-1) |
91
+ | maxChemicalDelta | 25 | 每轮最大情绪变化(越小越稳定) |
92
+
93
+ ## 支持的 MBTI 人格
94
+
95
+ 每个 agent 可以有不同的性格基线。在 agent 的 `IDENTITY.md` 里写上 MBTI 类型就行:
96
+
97
+ 不写的话默认 INFJ。16 种人格都支持,不同人格的情绪表达方式不同——ENFP 夸它会蹦跳,INTJ 夸它只会微微点头。
98
+
99
+ ## 不只是 OpenClaw
100
+
101
+ Psyche 是通用的,任何 AI 框架都能用:
102
+
103
+ ```bash
104
+ npm install psyche-ai
105
+ ```
106
+
107
+ ```javascript
108
+ // Vercel AI SDK
109
+ import { psycheMiddleware } from "psyche-ai/vercel-ai";
110
+
111
+ // LangChain
112
+ import { PsycheLangChain } from "psyche-ai/langchain";
113
+
114
+ // 任何语言(HTTP API)
115
+ // psyche serve --port 3210
116
+ ```
117
+
118
+ ## 诊断工具
119
+
120
+ 想看看 Psyche 在干什么?
121
+
122
+ ```bash
123
+ # 实时日志(另开一个终端)
124
+ openclaw logs -f 2>&1 | grep Psyche
125
+
126
+ # 看 agent 当前的情绪状态
127
+ cat workspace-yu/psyche-state.json | python3 -m json.tool
128
+
129
+ # 跑诊断脚本,看不同输入会注入什么
130
+ cd openclaw-plugin-psyche && node scripts/diagnose.js
131
+ ```
132
+
133
+ ## 技术细节
134
+
135
+ 给开发者和好奇的人:
136
+
137
+ - **14 种刺激类型** — 赞美、批评、幽默、智识挑战、亲密、冲突、忽视、惊喜、日常、讽刺、命令、认同、无聊、示弱
138
+ - **14 种涌现情绪** — 从化学混合中自动涌现,不是预设标签
139
+ - **5 种本能驱力** — 生存、安全、连接、尊重、好奇(马斯洛层级)
140
+ - **MBTI 人格基线** — 16 种人格有不同的化学签名和敏感度系数
141
+ - **时间衰减** — 化学值随时间指数回归基线,驱力需求随时间累积
142
+ - **存在性威胁检测** — 识别中英文的存在性否定,直接打击生存驱力
143
+ - **驱力→化学联动** — 未满足的驱力改变化学衰减基线和刺激敏感度
144
+ - **马斯洛抑制** — 低层需求未满足时,高层需求的影响被抑制
145
+ - **内在世界** — 始终存在的自我觉察(外/内/行为三层 prompt 结构)
146
+ - **情绪传染** — 用户的情绪会轻微影响 agent
147
+ - **反谄媚** — 追踪连续同意次数,防止无脑讨好
148
+ - **互惠机制** — 你对它好,它对你好。你冷漠,它保持距离
149
+ - **Compact Mode** — 算法做化学计算,LLM 只看行为指令(~15-180 tokens vs ~550)
150
+
151
+ 架构详情见 [ARCHITECTURE.md](ARCHITECTURE.md)。
152
+
153
+ ## 开发
154
+
155
+ ```bash
156
+ npm install
157
+ npm run build
158
+ npm test # 347 tests
159
+ npm run typecheck # strict mode
160
+ ```
161
+
162
+ 贡献指南见 [CONTRIBUTING.md](CONTRIBUTING.md)。
163
+
164
+ ## 许可
165
+
166
+ MIT
@@ -0,0 +1,26 @@
1
+ import { type Server } from "node:http";
2
+ import type { PsycheEngine } from "../core.js";
3
+ export interface HttpAdapterOptions {
4
+ port?: number;
5
+ host?: string;
6
+ }
7
+ /**
8
+ * Create an HTTP server that exposes PsycheEngine via REST API.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { PsycheEngine, FileStorageAdapter } from "psyche-ai";
13
+ * import { createPsycheServer } from "psyche-ai/http";
14
+ *
15
+ * const engine = new PsycheEngine(
16
+ * { mbti: "ENFP", name: "Luna" },
17
+ * new FileStorageAdapter("./workspace"),
18
+ * );
19
+ * await engine.initialize();
20
+ *
21
+ * const server = createPsycheServer(engine, { port: 3210 });
22
+ * // Now accessible from any language:
23
+ * // curl -X POST http://localhost:3210/process-input -d '{"text":"Hello!"}'
24
+ * ```
25
+ */
26
+ export declare function createPsycheServer(engine: PsycheEngine, opts?: HttpAdapterOptions): Server;
@@ -0,0 +1,106 @@
1
+ // ============================================================
2
+ // HTTP Adapter — Standalone server for Python/Go/any language
3
+ //
4
+ // Usage:
5
+ // import { createPsycheServer } from "psyche-ai/http";
6
+ //
7
+ // const server = createPsycheServer(engine, { port: 3210 });
8
+ //
9
+ // Endpoints:
10
+ // POST /process-input { text, userId? } → { systemContext, dynamicContext, stimulus }
11
+ // POST /process-output { text, userId? } → { cleanedText, stateChanged }
12
+ // GET /state → PsycheState
13
+ // GET /protocol?locale=zh → { protocol }
14
+ //
15
+ // Zero dependencies — uses node:http only.
16
+ // ============================================================
17
+ import { createServer } from "node:http";
18
+ // ── Server ───────────────────────────────────────────────────
19
+ /**
20
+ * Create an HTTP server that exposes PsycheEngine via REST API.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { PsycheEngine, FileStorageAdapter } from "psyche-ai";
25
+ * import { createPsycheServer } from "psyche-ai/http";
26
+ *
27
+ * const engine = new PsycheEngine(
28
+ * { mbti: "ENFP", name: "Luna" },
29
+ * new FileStorageAdapter("./workspace"),
30
+ * );
31
+ * await engine.initialize();
32
+ *
33
+ * const server = createPsycheServer(engine, { port: 3210 });
34
+ * // Now accessible from any language:
35
+ * // curl -X POST http://localhost:3210/process-input -d '{"text":"Hello!"}'
36
+ * ```
37
+ */
38
+ export function createPsycheServer(engine, opts) {
39
+ const server = createServer(async (req, res) => {
40
+ // CORS headers
41
+ res.setHeader("Access-Control-Allow-Origin", "*");
42
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
43
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
44
+ if (req.method === "OPTIONS") {
45
+ res.writeHead(204);
46
+ res.end();
47
+ return;
48
+ }
49
+ try {
50
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
51
+ // GET /state
52
+ if (req.method === "GET" && url.pathname === "/state") {
53
+ json(res, 200, engine.getState());
54
+ return;
55
+ }
56
+ // GET /protocol
57
+ if (req.method === "GET" && url.pathname === "/protocol") {
58
+ const locale = url.searchParams.get("locale");
59
+ json(res, 200, { protocol: engine.getProtocol(locale ?? undefined) });
60
+ return;
61
+ }
62
+ // POST /process-input
63
+ if (req.method === "POST" && url.pathname === "/process-input") {
64
+ const body = await readBody(req);
65
+ const result = await engine.processInput(body.text ?? "", { userId: body.userId });
66
+ json(res, 200, result);
67
+ return;
68
+ }
69
+ // POST /process-output
70
+ if (req.method === "POST" && url.pathname === "/process-output") {
71
+ const body = await readBody(req);
72
+ const result = await engine.processOutput(body.text ?? "", { userId: body.userId });
73
+ json(res, 200, result);
74
+ return;
75
+ }
76
+ json(res, 404, { error: "Not found" });
77
+ }
78
+ catch (err) {
79
+ json(res, 500, { error: String(err) });
80
+ }
81
+ });
82
+ const port = opts?.port ?? 3210;
83
+ const host = opts?.host ?? "127.0.0.1";
84
+ server.listen(port, host);
85
+ return server;
86
+ }
87
+ // ── Helpers ──────────────────────────────────────────────────
88
+ function json(res, status, data) {
89
+ res.writeHead(status, { "Content-Type": "application/json" });
90
+ res.end(JSON.stringify(data));
91
+ }
92
+ function readBody(req) {
93
+ return new Promise((resolve, reject) => {
94
+ const chunks = [];
95
+ req.on("data", (chunk) => chunks.push(chunk));
96
+ req.on("end", () => {
97
+ try {
98
+ resolve(JSON.parse(Buffer.concat(chunks).toString()));
99
+ }
100
+ catch {
101
+ resolve({});
102
+ }
103
+ });
104
+ req.on("error", reject);
105
+ });
106
+ }
@@ -0,0 +1,49 @@
1
+ import type { PsycheEngine } from "../core.js";
2
+ /**
3
+ * LangChain integration helper for PsycheEngine.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { ChatOpenAI } from "@langchain/openai";
8
+ * import { SystemMessage, HumanMessage } from "@langchain/core/messages";
9
+ * import { PsycheEngine, MemoryStorageAdapter } from "psyche-ai";
10
+ * import { PsycheLangChain } from "psyche-ai/langchain";
11
+ *
12
+ * const engine = new PsycheEngine({ mbti: "ENFP" }, new MemoryStorageAdapter());
13
+ * await engine.initialize();
14
+ * const psyche = new PsycheLangChain(engine);
15
+ *
16
+ * const userInput = "You're amazing!";
17
+ * const systemMsg = await psyche.getSystemMessage(userInput);
18
+ *
19
+ * const llm = new ChatOpenAI({ model: "gpt-4o" });
20
+ * const response = await llm.invoke([
21
+ * new SystemMessage(systemMsg),
22
+ * new HumanMessage(userInput),
23
+ * ]);
24
+ *
25
+ * const cleaned = await psyche.processResponse(response.content as string);
26
+ * ```
27
+ */
28
+ export declare class PsycheLangChain {
29
+ private readonly engine;
30
+ constructor(engine: PsycheEngine);
31
+ /**
32
+ * Get the system message to inject into the LLM call.
33
+ * Combines the protocol (cacheable) and dynamic context (per-turn).
34
+ *
35
+ * Call this BEFORE the LLM invocation.
36
+ */
37
+ getSystemMessage(userText: string, opts?: {
38
+ userId?: string;
39
+ }): Promise<string>;
40
+ /**
41
+ * Process the LLM response text.
42
+ * Strips <psyche_update> tags and updates internal state.
43
+ *
44
+ * Call this AFTER the LLM invocation, before showing output to the user.
45
+ */
46
+ processResponse(text: string, opts?: {
47
+ userId?: string;
48
+ }): Promise<string>;
49
+ }
@@ -0,0 +1,66 @@
1
+ // ============================================================
2
+ // LangChain Adapter — Helper class for LangChain integration
3
+ //
4
+ // Usage:
5
+ // import { PsycheLangChain } from "psyche-ai/langchain";
6
+ //
7
+ // const psyche = new PsycheLangChain(engine);
8
+ // const systemMsg = await psyche.getSystemMessage(userInput);
9
+ // // Use with LangChain's ChatModel, chains, etc.
10
+ //
11
+ // Note: This is a utility class, not a LangChain Runnable.
12
+ // It provides the hooks you need to wire psyche into any
13
+ // LangChain pipeline without requiring langchain as a dependency.
14
+ // ============================================================
15
+ /**
16
+ * LangChain integration helper for PsycheEngine.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { ChatOpenAI } from "@langchain/openai";
21
+ * import { SystemMessage, HumanMessage } from "@langchain/core/messages";
22
+ * import { PsycheEngine, MemoryStorageAdapter } from "psyche-ai";
23
+ * import { PsycheLangChain } from "psyche-ai/langchain";
24
+ *
25
+ * const engine = new PsycheEngine({ mbti: "ENFP" }, new MemoryStorageAdapter());
26
+ * await engine.initialize();
27
+ * const psyche = new PsycheLangChain(engine);
28
+ *
29
+ * const userInput = "You're amazing!";
30
+ * const systemMsg = await psyche.getSystemMessage(userInput);
31
+ *
32
+ * const llm = new ChatOpenAI({ model: "gpt-4o" });
33
+ * const response = await llm.invoke([
34
+ * new SystemMessage(systemMsg),
35
+ * new HumanMessage(userInput),
36
+ * ]);
37
+ *
38
+ * const cleaned = await psyche.processResponse(response.content as string);
39
+ * ```
40
+ */
41
+ export class PsycheLangChain {
42
+ engine;
43
+ constructor(engine) {
44
+ this.engine = engine;
45
+ }
46
+ /**
47
+ * Get the system message to inject into the LLM call.
48
+ * Combines the protocol (cacheable) and dynamic context (per-turn).
49
+ *
50
+ * Call this BEFORE the LLM invocation.
51
+ */
52
+ async getSystemMessage(userText, opts) {
53
+ const result = await this.engine.processInput(userText, opts);
54
+ return result.systemContext + "\n\n" + result.dynamicContext;
55
+ }
56
+ /**
57
+ * Process the LLM response text.
58
+ * Strips <psyche_update> tags and updates internal state.
59
+ *
60
+ * Call this AFTER the LLM invocation, before showing output to the user.
61
+ */
62
+ async processResponse(text, opts) {
63
+ const result = await this.engine.processOutput(text, opts);
64
+ return result.cleanedText;
65
+ }
66
+ }
@@ -0,0 +1,32 @@
1
+ import type { Logger } from "../psyche-file.js";
2
+ interface PluginApi {
3
+ pluginConfig?: Record<string, unknown>;
4
+ logger: Logger;
5
+ on(event: string, handler: (event: HookEvent, ctx: HookContext) => Promise<Record<string, unknown> | void>, opts?: {
6
+ priority: number;
7
+ }): void;
8
+ registerCli?(handler: (cli: CliRegistrar) => void, opts: {
9
+ commands: string[];
10
+ }): void;
11
+ }
12
+ interface HookEvent {
13
+ text?: string;
14
+ content?: string;
15
+ }
16
+ interface HookContext {
17
+ workspaceDir?: string;
18
+ userId?: string;
19
+ }
20
+ interface CliCommand {
21
+ description(desc: string): CliCommand;
22
+ argument(name: string, desc: string, defaultValue?: string): CliCommand;
23
+ action(fn: (arg: string) => Promise<void>): void;
24
+ }
25
+ interface CliRegistrar {
26
+ command(name: string): CliCommand;
27
+ }
28
+ export declare function register(api: PluginApi): void;
29
+ declare const _default: {
30
+ register: typeof register;
31
+ };
32
+ export default _default;