psyche-ai 10.1.1 → 10.2.1
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 +9 -1
- package/dist/adapters/proxy.d.ts +10 -0
- package/dist/adapters/proxy.js +301 -0
- package/dist/cli.js +130 -3
- package/llms.txt +9 -0
- package/package.json +7 -2
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Psyche — 面向智能体的 AI-first 主观性内核
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/psyche-ai)
|
|
4
|
-
[]()
|
|
5
5
|
[]()
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
@@ -230,6 +230,14 @@ npm view psyche-ai version
|
|
|
230
230
|
- `git` 工作树和本地路径安装不会偷偷改你的代码;脏工作树只会提示手动命令。
|
|
231
231
|
- OpenClaw 如果是从当前仓库本地加载,升级后记得重启 gateway 以载入新的 `dist/`。
|
|
232
232
|
|
|
233
|
+
## 透明代理(推荐:任意 LLM 一行接入)
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
psyche-proxy --target https://api.openai.com/v1 --name Luna --mbti ENFP
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
然后把客户端的 API 地址改成 `http://localhost:3340/v1`。Agent 获得持续主观性,但完全不知道 Psyche 存在。Psyche 像内分泌系统一样在后台观测行为、注入偏置——镜子,不是麦克风。
|
|
240
|
+
|
|
233
241
|
## 给本机其他 Agent 的真实验收
|
|
234
242
|
|
|
235
243
|
如果另一个 agent 声称“已经在用 Psyche”,不要接受口头解释,直接让它跑:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type Server } from "node:http";
|
|
3
|
+
import { PsycheEngine } from "../core.js";
|
|
4
|
+
export interface ProxyOptions {
|
|
5
|
+
/** Target LLM API base URL, e.g. "https://api.openai.com/v1" */
|
|
6
|
+
target: string;
|
|
7
|
+
port?: number;
|
|
8
|
+
host?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function createPsycheProxy(engine: PsycheEngine, opts: ProxyOptions): Server;
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ============================================================
|
|
3
|
+
// psyche-proxy — Transparent reverse proxy
|
|
4
|
+
//
|
|
5
|
+
// Adds persistent subjectivity to any OpenAI-compatible LLM API.
|
|
6
|
+
// The agent never knows Psyche exists. Psyche observes behavior
|
|
7
|
+
// bidirectionally and injects behavioral context only when the
|
|
8
|
+
// internal state deviates from baseline.
|
|
9
|
+
//
|
|
10
|
+
// Architecture:
|
|
11
|
+
// Client → psyche-proxy → Target LLM
|
|
12
|
+
// Client ← psyche-proxy ← Target LLM
|
|
13
|
+
//
|
|
14
|
+
// Usage:
|
|
15
|
+
// psyche-proxy --target https://api.openai.com/v1
|
|
16
|
+
// psyche-proxy -t https://api.x.ai/v1 -n Luna --mbti ENFP
|
|
17
|
+
// psyche-proxy -t http://localhost:11434/v1 -d ./psyche-data
|
|
18
|
+
//
|
|
19
|
+
// Then point any client to http://localhost:3340/v1/chat/completions
|
|
20
|
+
// ============================================================
|
|
21
|
+
import { createServer } from "node:http";
|
|
22
|
+
import { PsycheEngine } from "../core.js";
|
|
23
|
+
import { MemoryStorageAdapter, FileStorageAdapter } from "../storage.js";
|
|
24
|
+
import { isNearBaseline, deriveBehavioralBias } from "../prompt.js";
|
|
25
|
+
// ── Helpers ─────────────────────────────────────────────────
|
|
26
|
+
function readBody(req) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const chunks = [];
|
|
29
|
+
req.on("data", (c) => chunks.push(c));
|
|
30
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
31
|
+
req.on("error", reject);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function lastUserMessage(messages) {
|
|
35
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
36
|
+
if (messages[i].role === "user" && typeof messages[i].content === "string") {
|
|
37
|
+
return messages[i].content;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function injectBias(messages, context) {
|
|
43
|
+
if (!context)
|
|
44
|
+
return messages;
|
|
45
|
+
const out = messages.map((m) => ({ ...m }));
|
|
46
|
+
const idx = out.findIndex((m) => m.role === "system");
|
|
47
|
+
if (idx >= 0) {
|
|
48
|
+
out[idx].content = (out[idx].content ?? "") + "\n\n" + context;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
out.unshift({ role: "system", content: context });
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
/** Extract assistant text from a non-streaming OpenAI response. */
|
|
56
|
+
function extractAssistantText(body) {
|
|
57
|
+
const obj = body;
|
|
58
|
+
return obj?.choices?.[0]?.message?.content ?? "";
|
|
59
|
+
}
|
|
60
|
+
/** Extract assistant text from buffered SSE chunks. */
|
|
61
|
+
function extractStreamText(chunks) {
|
|
62
|
+
const parts = [];
|
|
63
|
+
for (const chunk of chunks) {
|
|
64
|
+
for (const line of chunk.split("\n")) {
|
|
65
|
+
if (!line.startsWith("data: ") || line === "data: [DONE]")
|
|
66
|
+
continue;
|
|
67
|
+
try {
|
|
68
|
+
const d = JSON.parse(line.slice(6));
|
|
69
|
+
const c = d?.choices?.[0]?.delta?.content;
|
|
70
|
+
if (typeof c === "string")
|
|
71
|
+
parts.push(c);
|
|
72
|
+
}
|
|
73
|
+
catch { /* skip malformed chunks */ }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return parts.join("");
|
|
77
|
+
}
|
|
78
|
+
/** Build safe headers for forwarding, stripping hop-by-hop headers. */
|
|
79
|
+
function forwardHeaders(req) {
|
|
80
|
+
const h = {};
|
|
81
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
82
|
+
if (k === "host" || k === "content-length" || k === "transfer-encoding")
|
|
83
|
+
continue;
|
|
84
|
+
if (typeof v === "string")
|
|
85
|
+
h[k] = v;
|
|
86
|
+
}
|
|
87
|
+
return h;
|
|
88
|
+
}
|
|
89
|
+
/** Strip content-encoding/content-length from upstream (fetch auto-decompresses). */
|
|
90
|
+
function safeResponseHeaders(headers) {
|
|
91
|
+
const h = {};
|
|
92
|
+
for (const [k, v] of headers.entries()) {
|
|
93
|
+
if (["content-encoding", "content-length", "transfer-encoding"].includes(k))
|
|
94
|
+
continue;
|
|
95
|
+
h[k] = v;
|
|
96
|
+
}
|
|
97
|
+
return h;
|
|
98
|
+
}
|
|
99
|
+
// ── Proxy Server ────────────────────────────────────────────
|
|
100
|
+
export function createPsycheProxy(engine, opts) {
|
|
101
|
+
const targetBase = opts.target.replace(/\/+$/, "");
|
|
102
|
+
const port = opts.port ?? 3340;
|
|
103
|
+
const host = opts.host ?? "127.0.0.1";
|
|
104
|
+
const locale = (engine.getState().meta.locale ?? "zh");
|
|
105
|
+
const server = createServer(async (req, res) => {
|
|
106
|
+
// Only intercept chat completions; pass everything else through.
|
|
107
|
+
if (req.method !== "POST" || !req.url?.includes("/chat/completions")) {
|
|
108
|
+
return passThrough(req, res, targetBase);
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const rawBody = await readBody(req);
|
|
112
|
+
const parsed = JSON.parse(rawBody.toString("utf-8"));
|
|
113
|
+
const userMsg = lastUserMessage(parsed.messages);
|
|
114
|
+
const userId = parsed.user ?? undefined;
|
|
115
|
+
// ── 1. Observe input ────────────────────────────
|
|
116
|
+
if (userMsg) {
|
|
117
|
+
await engine.processInput(userMsg, { userId });
|
|
118
|
+
}
|
|
119
|
+
// ── 2. Inject behavioral bias (silent when near baseline) ──
|
|
120
|
+
const state = engine.getState();
|
|
121
|
+
let messages = parsed.messages;
|
|
122
|
+
if (!isNearBaseline(state)) {
|
|
123
|
+
const bias = deriveBehavioralBias(state, locale);
|
|
124
|
+
if (bias) {
|
|
125
|
+
messages = injectBias(parsed.messages, bias);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const modifiedBody = JSON.stringify({ ...parsed, messages });
|
|
129
|
+
const headers = forwardHeaders(req);
|
|
130
|
+
headers["content-length"] = Buffer.byteLength(modifiedBody).toString();
|
|
131
|
+
// ── 3. Forward to target LLM ────────────────────
|
|
132
|
+
const upstream = await fetch(`${targetBase}${req.url}`, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers,
|
|
135
|
+
body: modifiedBody,
|
|
136
|
+
});
|
|
137
|
+
// ── 4. Return response + observe output ─────────
|
|
138
|
+
res.writeHead(upstream.status, safeResponseHeaders(upstream.headers));
|
|
139
|
+
if (parsed.stream && upstream.body) {
|
|
140
|
+
// Stream: forward chunks in real-time, buffer for observation
|
|
141
|
+
const reader = upstream.body.getReader();
|
|
142
|
+
const decoder = new TextDecoder();
|
|
143
|
+
const sseChunks = [];
|
|
144
|
+
let done = false;
|
|
145
|
+
while (!done) {
|
|
146
|
+
const result = await reader.read();
|
|
147
|
+
done = result.done;
|
|
148
|
+
if (result.value) {
|
|
149
|
+
res.write(result.value);
|
|
150
|
+
sseChunks.push(decoder.decode(result.value, { stream: true }));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
res.end();
|
|
154
|
+
// Observe output (background — response already sent)
|
|
155
|
+
const text = extractStreamText(sseChunks);
|
|
156
|
+
if (text)
|
|
157
|
+
engine.processOutput(text, { userId }).catch(() => { });
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Non-stream: buffer, send, observe
|
|
161
|
+
const buf = Buffer.from(await upstream.arrayBuffer());
|
|
162
|
+
res.end(buf);
|
|
163
|
+
try {
|
|
164
|
+
const obj = JSON.parse(buf.toString("utf-8"));
|
|
165
|
+
const text = extractAssistantText(obj);
|
|
166
|
+
if (text)
|
|
167
|
+
engine.processOutput(text, { userId }).catch(() => { });
|
|
168
|
+
}
|
|
169
|
+
catch { /* response not JSON, skip observation */ }
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
if (!res.headersSent) {
|
|
174
|
+
res.writeHead(502, { "content-type": "application/json" });
|
|
175
|
+
}
|
|
176
|
+
res.end(JSON.stringify({
|
|
177
|
+
error: { message: `psyche-proxy: ${err.message}`, type: "proxy_error" },
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
server.listen(port, host, () => {
|
|
182
|
+
const name = engine.getState().meta.agentName ?? "agent";
|
|
183
|
+
const mbti = engine.getState().mbti ?? "";
|
|
184
|
+
console.error(`[psyche-proxy] ${name}${mbti ? ` (${mbti})` : ""} → ${targetBase}`);
|
|
185
|
+
console.error(`[psyche-proxy] http://${host}:${port}`);
|
|
186
|
+
console.error(`[psyche-proxy] mode: mirror (observe behavior, inject bias, agent never knows)`);
|
|
187
|
+
});
|
|
188
|
+
return server;
|
|
189
|
+
}
|
|
190
|
+
// ── Pass-through for non-chat endpoints ─────────────────────
|
|
191
|
+
async function passThrough(req, res, targetBase) {
|
|
192
|
+
try {
|
|
193
|
+
const rawBody = (req.method !== "GET" && req.method !== "HEAD")
|
|
194
|
+
? (await readBody(req)).toString("utf-8")
|
|
195
|
+
: undefined;
|
|
196
|
+
const headers = forwardHeaders(req);
|
|
197
|
+
const upstream = await fetch(`${targetBase}${req.url}`, {
|
|
198
|
+
method: req.method,
|
|
199
|
+
headers,
|
|
200
|
+
body: rawBody,
|
|
201
|
+
});
|
|
202
|
+
res.writeHead(upstream.status, safeResponseHeaders(upstream.headers));
|
|
203
|
+
const buf = Buffer.from(await upstream.arrayBuffer());
|
|
204
|
+
res.end(buf);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
res.writeHead(502);
|
|
208
|
+
res.end(`psyche-proxy: ${err.message}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// ── CLI ─────────────────────────────────────────────────────
|
|
212
|
+
async function main() {
|
|
213
|
+
const args = process.argv.slice(2);
|
|
214
|
+
// Quick help
|
|
215
|
+
if (args.includes("--help") || args.includes("-h") || args.length === 0) {
|
|
216
|
+
console.error(`psyche-proxy — transparent subjectivity proxy for any OpenAI-compatible API
|
|
217
|
+
|
|
218
|
+
Usage:
|
|
219
|
+
psyche-proxy --target <URL> [options]
|
|
220
|
+
|
|
221
|
+
Options:
|
|
222
|
+
-t, --target <url> Target LLM API base URL (required)
|
|
223
|
+
-p, --port <n> Local port (default: 3340)
|
|
224
|
+
-n, --name <name> Agent name (default: agent)
|
|
225
|
+
--mbti <type> MBTI personality preset (e.g., ENFP, INTJ)
|
|
226
|
+
--mode <mode> Operating mode: natural | work | companion
|
|
227
|
+
-l, --locale <loc> Locale: zh | en (default: zh)
|
|
228
|
+
-d, --dir <path> Persist state to directory (default: in-memory)
|
|
229
|
+
--host <addr> Bind address (default: 127.0.0.1)
|
|
230
|
+
|
|
231
|
+
Examples:
|
|
232
|
+
psyche-proxy -t https://api.openai.com/v1
|
|
233
|
+
psyche-proxy -t https://api.x.ai/v1 -n Luna --mbti ENFP
|
|
234
|
+
psyche-proxy -t http://localhost:11434/v1 -d ./psyche-data
|
|
235
|
+
|
|
236
|
+
Then point your client to http://localhost:3340/v1 instead of the real API.
|
|
237
|
+
The agent gains persistent subjectivity without knowing Psyche exists.`);
|
|
238
|
+
process.exit(args.includes("--help") || args.includes("-h") ? 0 : 1);
|
|
239
|
+
}
|
|
240
|
+
// Parse args
|
|
241
|
+
let target = "", port = 3340, hostAddr = "127.0.0.1", dir = "";
|
|
242
|
+
const engineOpts = {};
|
|
243
|
+
for (let i = 0; i < args.length; i++) {
|
|
244
|
+
const a = args[i], next = args[i + 1];
|
|
245
|
+
switch (a) {
|
|
246
|
+
case "-t":
|
|
247
|
+
case "--target":
|
|
248
|
+
target = next ?? "";
|
|
249
|
+
i++;
|
|
250
|
+
break;
|
|
251
|
+
case "-p":
|
|
252
|
+
case "--port":
|
|
253
|
+
port = parseInt(next ?? "3340", 10);
|
|
254
|
+
i++;
|
|
255
|
+
break;
|
|
256
|
+
case "-n":
|
|
257
|
+
case "--name":
|
|
258
|
+
engineOpts.name = next;
|
|
259
|
+
i++;
|
|
260
|
+
break;
|
|
261
|
+
case "--mbti":
|
|
262
|
+
engineOpts.mbti = next?.toUpperCase() ?? undefined;
|
|
263
|
+
i++;
|
|
264
|
+
break;
|
|
265
|
+
case "--mode":
|
|
266
|
+
engineOpts.mode = next ?? "natural";
|
|
267
|
+
i++;
|
|
268
|
+
break;
|
|
269
|
+
case "-l":
|
|
270
|
+
case "--locale":
|
|
271
|
+
engineOpts.locale = next ?? "zh";
|
|
272
|
+
i++;
|
|
273
|
+
break;
|
|
274
|
+
case "-d":
|
|
275
|
+
case "--dir":
|
|
276
|
+
dir = next ?? "";
|
|
277
|
+
i++;
|
|
278
|
+
break;
|
|
279
|
+
case "--host":
|
|
280
|
+
hostAddr = next ?? "127.0.0.1";
|
|
281
|
+
i++;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (!target) {
|
|
286
|
+
console.error("error: --target is required");
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
const storage = dir ? new FileStorageAdapter(dir) : new MemoryStorageAdapter();
|
|
290
|
+
const engine = new PsycheEngine(engineOpts, storage);
|
|
291
|
+
await engine.initialize();
|
|
292
|
+
createPsycheProxy(engine, { target, port, host: hostAddr });
|
|
293
|
+
}
|
|
294
|
+
// Only run CLI when executed directly (not when imported as a module)
|
|
295
|
+
const isCLI = process.argv[1]?.replace(/\.ts$/, ".js").endsWith("/adapters/proxy.js");
|
|
296
|
+
if (isCLI) {
|
|
297
|
+
main().catch((err) => {
|
|
298
|
+
console.error(err);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
});
|
|
301
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
// psyche upgrade [--check]
|
|
16
16
|
// psyche probe [--json]
|
|
17
17
|
// psyche profiles [--json] [--mbti TYPE]
|
|
18
|
+
// psyche setup [--name NAME] [--mbti TYPE] [--locale LOCALE] [--dry-run]
|
|
18
19
|
// ============================================================
|
|
19
|
-
import { resolve } from "node:path";
|
|
20
|
+
import { resolve, join } from "node:path";
|
|
21
|
+
import { homedir } from "node:os";
|
|
20
22
|
import { parseArgs } from "node:util";
|
|
21
|
-
import { readFile } from "node:fs/promises";
|
|
23
|
+
import { readFile, writeFile, mkdir, access } from "node:fs/promises";
|
|
22
24
|
import { loadState, saveState, decayAndSave, initializeState, mergeUpdates, generatePsycheMd, getRelationship, } from "./psyche-file.js";
|
|
23
25
|
import { describeEmotionalState, getExpressionHint, detectEmotions } from "./chemistry.js";
|
|
24
26
|
import { generateReport, formatReport, toGitHubIssueBody } from "./diagnostics.js";
|
|
@@ -459,7 +461,117 @@ async function cmdProbe(json) {
|
|
|
459
461
|
console.log(` replyEnvelope: ${result.canonicalHostSurface ? "present" : "missing"}`);
|
|
460
462
|
console.log(` externalContinuity: ${result.externalContinuityAvailable ? "present" : "missing"}`);
|
|
461
463
|
}
|
|
462
|
-
|
|
464
|
+
function getMCPTargets() {
|
|
465
|
+
const home = homedir();
|
|
466
|
+
const isMac = process.platform === "darwin";
|
|
467
|
+
const isWin = process.platform === "win32";
|
|
468
|
+
const targets = [];
|
|
469
|
+
// Claude Desktop
|
|
470
|
+
if (isMac) {
|
|
471
|
+
targets.push({
|
|
472
|
+
name: "Claude Desktop",
|
|
473
|
+
configPath: join(home, "Library/Application Support/Claude/claude_desktop_config.json"),
|
|
474
|
+
mcpKey: "mcpServers",
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
else if (isWin) {
|
|
478
|
+
targets.push({
|
|
479
|
+
name: "Claude Desktop",
|
|
480
|
+
configPath: join(process.env.APPDATA ?? join(home, "AppData/Roaming"), "Claude/claude_desktop_config.json"),
|
|
481
|
+
mcpKey: "mcpServers",
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
// Cursor
|
|
485
|
+
targets.push({
|
|
486
|
+
name: "Cursor",
|
|
487
|
+
configPath: join(home, ".cursor/mcp.json"),
|
|
488
|
+
mcpKey: "mcpServers",
|
|
489
|
+
});
|
|
490
|
+
// Claude Code
|
|
491
|
+
targets.push({
|
|
492
|
+
name: "Claude Code",
|
|
493
|
+
configPath: join(home, ".claude/settings.json"),
|
|
494
|
+
mcpKey: "mcpServers",
|
|
495
|
+
});
|
|
496
|
+
// Windsurf
|
|
497
|
+
targets.push({
|
|
498
|
+
name: "Windsurf",
|
|
499
|
+
configPath: join(home, ".windsurf/mcp.json"),
|
|
500
|
+
mcpKey: "mcpServers",
|
|
501
|
+
});
|
|
502
|
+
return targets;
|
|
503
|
+
}
|
|
504
|
+
async function fileExists(path) {
|
|
505
|
+
try {
|
|
506
|
+
await access(path);
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function cmdSetup(name, mbti, locale, dryRun) {
|
|
514
|
+
const targets = getMCPTargets();
|
|
515
|
+
const psycheEntry = {
|
|
516
|
+
command: "npx",
|
|
517
|
+
args: ["-y", "psyche-mcp"],
|
|
518
|
+
env: {
|
|
519
|
+
...(name ? { PSYCHE_NAME: name } : {}),
|
|
520
|
+
...(mbti ? { PSYCHE_MBTI: mbti.toUpperCase() } : {}),
|
|
521
|
+
...(locale ? { PSYCHE_LOCALE: locale } : {}),
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
let configured = 0;
|
|
525
|
+
let skipped = 0;
|
|
526
|
+
for (const target of targets) {
|
|
527
|
+
// Check if the config file's parent directory exists (= client is installed)
|
|
528
|
+
const configDir = resolve(target.configPath, "..");
|
|
529
|
+
if (!(await fileExists(configDir)))
|
|
530
|
+
continue;
|
|
531
|
+
// Read existing config or start fresh
|
|
532
|
+
let config = {};
|
|
533
|
+
if (await fileExists(target.configPath)) {
|
|
534
|
+
try {
|
|
535
|
+
config = JSON.parse(await readFile(target.configPath, "utf-8"));
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
config = {};
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
// Check if already configured
|
|
542
|
+
const servers = config[target.mcpKey] ?? {};
|
|
543
|
+
if (servers["psyche"]) {
|
|
544
|
+
console.log(` ✓ ${target.name} — already configured`);
|
|
545
|
+
skipped++;
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
// Add psyche entry
|
|
549
|
+
servers["psyche"] = psycheEntry;
|
|
550
|
+
config[target.mcpKey] = servers;
|
|
551
|
+
if (dryRun) {
|
|
552
|
+
console.log(` → ${target.name} — would write to ${target.configPath}`);
|
|
553
|
+
configured++;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
// Write config
|
|
557
|
+
await mkdir(configDir, { recursive: true });
|
|
558
|
+
await writeFile(target.configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
559
|
+
console.log(` ✓ ${target.name} — configured (${target.configPath})`);
|
|
560
|
+
configured++;
|
|
561
|
+
}
|
|
562
|
+
if (configured === 0 && skipped === 0) {
|
|
563
|
+
console.log(" No MCP clients detected. Install Claude Desktop, Cursor, or Claude Code first.");
|
|
564
|
+
console.log(" Or run psyche-proxy for universal LLM proxy integration.");
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
console.log("");
|
|
568
|
+
if (configured > 0 && !dryRun) {
|
|
569
|
+
console.log(`Done. Restart your MCP client${configured > 1 ? "s" : ""} to activate Psyche.`);
|
|
570
|
+
}
|
|
571
|
+
if (configured > 0 && dryRun) {
|
|
572
|
+
console.log(`Dry run complete. Run without --dry-run to apply.`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
463
575
|
function usage() {
|
|
464
576
|
console.log(`
|
|
465
577
|
psyche — Artificial Psyche CLI (v0.2)
|
|
@@ -663,6 +775,21 @@ async function main() {
|
|
|
663
775
|
await cmdProbe(values.json ?? false);
|
|
664
776
|
break;
|
|
665
777
|
}
|
|
778
|
+
case "setup": {
|
|
779
|
+
const { values } = parseArgs({
|
|
780
|
+
args: rest,
|
|
781
|
+
options: {
|
|
782
|
+
name: { type: "string", short: "n" },
|
|
783
|
+
mbti: { type: "string" },
|
|
784
|
+
locale: { type: "string", short: "l" },
|
|
785
|
+
"dry-run": { type: "boolean", default: false },
|
|
786
|
+
},
|
|
787
|
+
allowPositionals: true,
|
|
788
|
+
});
|
|
789
|
+
console.log("\npsyche setup — auto-configuring MCP clients\n");
|
|
790
|
+
await cmdSetup(values.name ?? "", values.mbti ?? "", values.locale ?? "", values["dry-run"] ?? false);
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
666
793
|
default:
|
|
667
794
|
die(`unknown command: ${command}. Run 'psyche help' for usage.`);
|
|
668
795
|
}
|
package/llms.txt
CHANGED
|
@@ -162,9 +162,18 @@ npm install psyche-ai
|
|
|
162
162
|
npx psyche-mcp
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
## Transparent Proxy (recommended for universal integration)
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
psyche-proxy --target https://api.openai.com/v1 --name Luna --mbti ENFP
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Then point any client to `http://localhost:3340/v1`. The agent gains persistent subjectivity without knowing Psyche exists. Psyche observes behavior bidirectionally and injects behavioral bias only when the internal state deviates from baseline. Zero new interfaces for the agent.
|
|
172
|
+
|
|
165
173
|
## Adapter Sub-paths
|
|
166
174
|
|
|
167
175
|
- `psyche-ai` — core engine (PsycheEngine, MemoryStorageAdapter)
|
|
176
|
+
- `psyche-ai/proxy` — transparent reverse proxy (agent never knows)
|
|
168
177
|
- `psyche-ai/claude-sdk` — Claude Agent SDK hooks (PsycheClaudeSDK)
|
|
169
178
|
- `psyche-ai/mcp` — MCP stdio server
|
|
170
179
|
- `psyche-ai/vercel-ai` — Vercel AI SDK middleware
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "psyche-ai",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.2.1",
|
|
4
4
|
"description": "AI-first subjectivity kernel for agents with continuous appraisal, relation dynamics, and adaptive reply loops",
|
|
5
5
|
"mcpName": "io.github.Shangri-la-0428/psyche-ai",
|
|
6
6
|
"type": "module",
|
|
@@ -34,12 +34,17 @@
|
|
|
34
34
|
"./claude-sdk": {
|
|
35
35
|
"types": "./dist/adapters/claude-sdk.d.ts",
|
|
36
36
|
"default": "./dist/adapters/claude-sdk.js"
|
|
37
|
+
},
|
|
38
|
+
"./proxy": {
|
|
39
|
+
"types": "./dist/adapters/proxy.d.ts",
|
|
40
|
+
"default": "./dist/adapters/proxy.js"
|
|
37
41
|
}
|
|
38
42
|
},
|
|
39
43
|
"bin": {
|
|
40
44
|
"psyche": "dist/cli.js",
|
|
41
45
|
"psyche-ai": "dist/cli.js",
|
|
42
|
-
"psyche-mcp": "dist/adapters/mcp.js"
|
|
46
|
+
"psyche-mcp": "dist/adapters/mcp.js",
|
|
47
|
+
"psyche-proxy": "dist/adapters/proxy.js"
|
|
43
48
|
},
|
|
44
49
|
"scripts": {
|
|
45
50
|
"build": "tsc",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/Shangri-la-0428/oasyce_psyche",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "10.1.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "psyche-ai",
|
|
14
|
-
"version": "
|
|
14
|
+
"version": "10.1.1",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|