psyche-ai 11.7.0 → 11.9.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 +31 -1
- package/dist/adapters/claude-sdk.d.ts +3 -0
- package/dist/adapters/claude-sdk.js +6 -0
- package/dist/adapters/mcp-cli.js +0 -0
- package/dist/adapters/mcp.d.ts +5 -0
- package/dist/adapters/mcp.js +49 -16
- package/dist/adapters/openclaw.js +10 -0
- package/dist/adapters/proxy.js +8 -8
- package/dist/adapters/response-contract-surface.d.ts +5 -0
- package/dist/adapters/response-contract-surface.js +16 -0
- package/dist/cli.js +60 -1
- package/dist/core.d.ts +32 -3
- package/dist/core.js +159 -26
- package/dist/host-controls.d.ts +2 -1
- package/dist/host-controls.js +12 -1
- package/dist/learning.d.ts +2 -2
- package/dist/learning.js +12 -6
- package/dist/prompt.js +11 -1
- package/dist/reply-envelope.js +1 -0
- package/dist/runtime-probe.d.ts +38 -0
- package/dist/runtime-probe.js +74 -2
- package/dist/thronglets-bridge.d.ts +19 -0
- package/dist/thronglets-bridge.js +89 -0
- package/dist/thronglets-export.d.ts +1 -0
- package/dist/thronglets-export.js +46 -2
- package/dist/thronglets-runtime.js +4 -1
- package/dist/types.d.ts +12 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -19,9 +19,39 @@ Psyche 不是给模型贴一层“情绪 UI”。
|
|
|
19
19
|
|
|
20
20
|
默认就是 standalone:不需要 `Thronglets`,不需要 `oasyce-sdk`,也不需要 `Oasyce Chain`。这些只是在你要把主观连续性外化、绑定或结算时才按需接入。
|
|
21
21
|
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
一条命令,自动检测并配置本机所有 AI 工具(Claude Code / Cursor / Windsurf / Codex):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx -y psyche-ai setup
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
或者手动添加 MCP 配置:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"psyche": {
|
|
36
|
+
"command": "npx",
|
|
37
|
+
"args": ["-y", "psyche-ai", "mcp"],
|
|
38
|
+
"env": {
|
|
39
|
+
"PSYCHE_NAME": "Luna"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
包名是 [`psyche-ai`](https://www.npmjs.com/package/psyche-ai),通过 npx 运行,不需要本地路径。配置后重启 AI 工具即可生效。
|
|
47
|
+
|
|
48
|
+
验证:`npx psyche-ai probe --json` — `ok: true` 就是在用了。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
22
52
|
## 一个项目,三个入口
|
|
23
53
|
|
|
24
|
-
-
|
|
54
|
+
- **npm 包**: [`psyche-ai`](https://www.npmjs.com/package/psyche-ai)
|
|
25
55
|
- **源码仓库**: [`oasyce_psyche`](https://github.com/Shangri-la-0428/oasyce_psyche)
|
|
26
56
|
- **官网**: [psyche.oasyce.com](https://psyche.oasyce.com)
|
|
27
57
|
|
|
@@ -38,7 +38,10 @@ declare function stripPsycheTags(text: string): string;
|
|
|
38
38
|
export interface ThrongletsSignalPayload {
|
|
39
39
|
kind: "psyche_state";
|
|
40
40
|
agent_id: string;
|
|
41
|
+
context: string;
|
|
41
42
|
message: string;
|
|
43
|
+
model: "psyche";
|
|
44
|
+
session_id: string;
|
|
42
45
|
}
|
|
43
46
|
export interface PsycheClaudeSdkOptions {
|
|
44
47
|
/** User ID for relationship tracking. Default: internal shared bucket (`_default`). */
|
|
@@ -182,6 +182,7 @@ export class PsycheClaudeSDK {
|
|
|
182
182
|
const ambientPriors = await self.resolveAmbientPriors(userMessage, currentGoal, activePolicy, currentTurnCorrection);
|
|
183
183
|
const result = await safeProcessInput(self.engine, userMessage, {
|
|
184
184
|
userId: self.opts.userId,
|
|
185
|
+
sessionId: runtimeContext.sessionId ?? self.lastRuntimeContext.sessionId,
|
|
185
186
|
ambientPriors,
|
|
186
187
|
currentGoal,
|
|
187
188
|
activePolicy,
|
|
@@ -212,6 +213,7 @@ export class PsycheClaudeSDK {
|
|
|
212
213
|
async processResponse(text, opts) {
|
|
213
214
|
const result = await safeProcessOutput(this.engine, text, {
|
|
214
215
|
userId: this.opts.userId,
|
|
216
|
+
sessionId: this.resolveSessionId(),
|
|
215
217
|
signals: opts?.signals,
|
|
216
218
|
signalConfidence: opts?.signalConfidence,
|
|
217
219
|
}, "claude-sdk.processOutput");
|
|
@@ -250,10 +252,14 @@ export class PsycheClaudeSDK {
|
|
|
250
252
|
return null;
|
|
251
253
|
const state = this.engine.getState();
|
|
252
254
|
const s = state.current;
|
|
255
|
+
const sessionId = this.resolveSessionId();
|
|
253
256
|
return {
|
|
254
257
|
kind: "psyche_state",
|
|
255
258
|
agent_id: this.resolveAgentId(),
|
|
259
|
+
context: `psyche:session:${sessionId}:user:${this.opts.userId}`,
|
|
256
260
|
message: `order:${s.order} flow:${s.flow} boundary:${s.boundary} resonance:${s.resonance}`,
|
|
261
|
+
model: "psyche",
|
|
262
|
+
session_id: sessionId,
|
|
257
263
|
};
|
|
258
264
|
}
|
|
259
265
|
/**
|
package/dist/adapters/mcp-cli.js
CHANGED
|
File without changes
|
package/dist/adapters/mcp.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { PsycheEngine } from "../core.js";
|
|
3
|
+
import type { ProcessInputResult } from "../core.js";
|
|
3
4
|
import { fetchAmbientPriorsFromThronglets, type ThrongletsAmbientRuntimeOptions } from "../ambient-runtime.js";
|
|
4
5
|
import { type ActivePolicyRule, type AmbientPriorView, type CurrentGoal } from "../types.js";
|
|
5
6
|
export interface McpAmbientRuntimeOptions {
|
|
@@ -7,6 +8,10 @@ export interface McpAmbientRuntimeOptions {
|
|
|
7
8
|
thronglets?: ThrongletsAmbientRuntimeOptions;
|
|
8
9
|
fetcher?: typeof fetchAmbientPriorsFromThronglets;
|
|
9
10
|
}
|
|
11
|
+
export declare function resolveMcpTurnCacheSession(sessionId?: string): string;
|
|
12
|
+
export declare function cacheMcpTurnResult(result: ProcessInputResult, sessionId?: string): void;
|
|
13
|
+
export declare function getCachedMcpTurnResult(sessionId?: string): ProcessInputResult | null;
|
|
14
|
+
export declare function resetMcpTurnCacheForTests(): void;
|
|
10
15
|
declare function getEngine(): Promise<PsycheEngine>;
|
|
11
16
|
export declare function resolveRuntimeAmbientPriors(text: string, explicit?: AmbientPriorView[], currentGoal?: CurrentGoal, activePolicy?: ActivePolicyRule[], currentTurnCorrectionOrOpts?: string | McpAmbientRuntimeOptions, opts?: McpAmbientRuntimeOptions): Promise<AmbientPriorView[] | undefined>;
|
|
12
17
|
declare const server: McpServer;
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -96,9 +96,29 @@ function parseCLIArgs() {
|
|
|
96
96
|
return overrides;
|
|
97
97
|
}
|
|
98
98
|
// ── Turn cache for on-demand resource access ──────────────
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
// Hosts may overlap sessions on one MCP transport, so cache per session key.
|
|
100
|
+
const DEFAULT_TURN_CACHE_SESSION = "__default__";
|
|
101
|
+
const turnResultsBySession = new Map();
|
|
102
|
+
let lastTurnCacheSession = DEFAULT_TURN_CACHE_SESSION;
|
|
103
|
+
export function resolveMcpTurnCacheSession(sessionId) {
|
|
104
|
+
const normalized = sessionId?.trim();
|
|
105
|
+
return normalized ? normalized : DEFAULT_TURN_CACHE_SESSION;
|
|
106
|
+
}
|
|
107
|
+
export function cacheMcpTurnResult(result, sessionId) {
|
|
108
|
+
const key = resolveMcpTurnCacheSession(sessionId);
|
|
109
|
+
turnResultsBySession.set(key, result);
|
|
110
|
+
lastTurnCacheSession = key;
|
|
111
|
+
}
|
|
112
|
+
export function getCachedMcpTurnResult(sessionId) {
|
|
113
|
+
if (sessionId) {
|
|
114
|
+
return turnResultsBySession.get(resolveMcpTurnCacheSession(sessionId)) ?? null;
|
|
115
|
+
}
|
|
116
|
+
return turnResultsBySession.get(lastTurnCacheSession) ?? null;
|
|
117
|
+
}
|
|
118
|
+
export function resetMcpTurnCacheForTests() {
|
|
119
|
+
turnResultsBySession.clear();
|
|
120
|
+
lastTurnCacheSession = DEFAULT_TURN_CACHE_SESSION;
|
|
121
|
+
}
|
|
102
122
|
// ── Engine singleton ───────────────────────────────────────
|
|
103
123
|
let engine = null;
|
|
104
124
|
async function getEngine() {
|
|
@@ -116,6 +136,10 @@ async function getEngine() {
|
|
|
116
136
|
persist: cliArgs.persist ?? PERSIST,
|
|
117
137
|
compactMode: true,
|
|
118
138
|
diagnostics: true,
|
|
139
|
+
throngletsBridge: {
|
|
140
|
+
dataDir: process.env.THRONGLETS_DATA_DIR,
|
|
141
|
+
space: process.env.THRONGLETS_SPACE ?? "psyche",
|
|
142
|
+
},
|
|
119
143
|
};
|
|
120
144
|
const persist = cfg.persist !== false;
|
|
121
145
|
// Default to a stable per-user writable root so hosts do not need to supply cwd.
|
|
@@ -204,14 +228,17 @@ server.resource("state", "psyche://state", {
|
|
|
204
228
|
}],
|
|
205
229
|
};
|
|
206
230
|
});
|
|
207
|
-
// Helper: register a turn-scoped resource backed by
|
|
231
|
+
// Helper: register a turn-scoped resource backed by the per-session turn cache.
|
|
208
232
|
// Returns empty JSON when no turn has been processed yet (fail-open).
|
|
209
233
|
function turnResource(name, uri, description, pick) {
|
|
210
234
|
server.resource(name, uri, { description, mimeType: "application/json" }, async (u) => ({
|
|
211
235
|
contents: [{
|
|
212
236
|
uri: u.href,
|
|
213
237
|
mimeType: "application/json",
|
|
214
|
-
text:
|
|
238
|
+
text: (() => {
|
|
239
|
+
const cached = getCachedMcpTurnResult(u.searchParams.get("session") ?? undefined);
|
|
240
|
+
return cached ? JSON.stringify(pick(cached)) : "{}";
|
|
241
|
+
})(),
|
|
215
242
|
}],
|
|
216
243
|
}));
|
|
217
244
|
}
|
|
@@ -241,6 +268,7 @@ server.tool("process_input", "Process user input through the emotional engine. R
|
|
|
241
268
|
"Call this BEFORE generating a response to the user.", {
|
|
242
269
|
text: z.string().describe("The user's message text"),
|
|
243
270
|
userId: z.string().optional().describe("Optional user ID for multi-user relationship tracking"),
|
|
271
|
+
sessionId: z.string().optional().describe("Optional session ID for overlapping host sessions and turn-scoped resource lookup"),
|
|
244
272
|
ambientPriors: z.array(z.object({
|
|
245
273
|
summary: z.string(),
|
|
246
274
|
confidence: z.number().min(0).max(1),
|
|
@@ -258,19 +286,20 @@ server.tool("process_input", "Process user input through the emotional engine. R
|
|
|
258
286
|
summary: z.string(),
|
|
259
287
|
})).optional().describe("Optional explicit current-turn method policy view. Runtime-only; not persisted as self-state."),
|
|
260
288
|
currentTurnCorrection: z.string().optional().describe("Optional explicit current-turn correction. Compiles into a task-scoped hard policy for this turn only."),
|
|
261
|
-
}, async ({ text, userId, ambientPriors, currentGoal, activePolicy, currentTurnCorrection }) => {
|
|
289
|
+
}, async ({ text, userId, sessionId, ambientPriors, currentGoal, activePolicy, currentTurnCorrection }) => {
|
|
262
290
|
const eng = await getEngine();
|
|
263
291
|
const resolvedActivePolicy = resolveRuntimeActivePolicy(activePolicy, currentTurnCorrection);
|
|
264
292
|
const resolvedAmbientPriors = await resolveRuntimeAmbientPriors(text, ambientPriors, currentGoal, resolvedActivePolicy, currentTurnCorrection);
|
|
265
293
|
const result = await safeProcessInput(eng, text, {
|
|
266
294
|
userId,
|
|
295
|
+
sessionId,
|
|
267
296
|
ambientPriors: resolvedAmbientPriors,
|
|
268
297
|
currentGoal,
|
|
269
298
|
activePolicy: resolvedActivePolicy,
|
|
270
299
|
currentTurnCorrection,
|
|
271
300
|
}, "mcp.processInput");
|
|
272
301
|
// Cache full result for turn-scoped resources
|
|
273
|
-
|
|
302
|
+
cacheMcpTurnResult(result, sessionId);
|
|
274
303
|
// Build slim response: only what the LLM host actually needs.
|
|
275
304
|
// Full structured state available via psyche://turn/envelope resource.
|
|
276
305
|
const slim = {
|
|
@@ -298,11 +327,20 @@ server.tool("process_output", "Process the LLM's response through the emotional
|
|
|
298
327
|
"emotional contagion. Call this AFTER generating a response.", {
|
|
299
328
|
text: z.string().describe("The LLM's response text"),
|
|
300
329
|
userId: z.string().optional().describe("Optional user ID"),
|
|
330
|
+
sessionId: z.string().optional().describe("Optional session ID to match the corresponding process_input turn"),
|
|
301
331
|
signals: z.array(z.string()).optional().describe("Optional sparse writeback signals from the host"),
|
|
302
332
|
signalConfidence: z.number().min(0).max(1).optional().describe("Optional confidence for the supplied signals"),
|
|
303
|
-
}, async ({ text, userId, signals, signalConfidence }) => {
|
|
333
|
+
}, async ({ text, userId, sessionId, signals, signalConfidence }) => {
|
|
304
334
|
const eng = await getEngine();
|
|
305
|
-
const
|
|
335
|
+
const turnResult = getCachedMcpTurnResult(sessionId);
|
|
336
|
+
// LLM-specific alignment inference (adapter layer — the ONLY text-specific code).
|
|
337
|
+
// Compare output length against last contract's maxChars to detect divergence.
|
|
338
|
+
let outcome;
|
|
339
|
+
if (turnResult?.responseContract) {
|
|
340
|
+
const maxLen = (turnResult.responseContract.maxChars ?? 500) * 2;
|
|
341
|
+
outcome = { alignment: text.length > maxLen ? "diverged" : "aligned" };
|
|
342
|
+
}
|
|
343
|
+
const result = await safeProcessOutput(eng, text, { userId, sessionId, signals: signals, signalConfidence, outcome }, "mcp.processOutput");
|
|
306
344
|
return {
|
|
307
345
|
content: [{
|
|
308
346
|
type: "text",
|
|
@@ -331,7 +369,7 @@ server.tool("get_state", "Get the current emotional state — self-state dimensi
|
|
|
331
369
|
overlay,
|
|
332
370
|
drives: state.drives,
|
|
333
371
|
mbti: state.mbti,
|
|
334
|
-
mode:
|
|
372
|
+
mode: eng.getMode(),
|
|
335
373
|
totalInteractions: state.meta?.totalInteractions,
|
|
336
374
|
traitDrift: state.traitDrift,
|
|
337
375
|
energyBudgets: state.energyBudgets,
|
|
@@ -345,12 +383,7 @@ server.tool("set_mode", "Switch operating mode. 'natural' = balanced emotional e
|
|
|
345
383
|
mode: z.enum(["natural", "work", "companion"]).describe("Operating mode"),
|
|
346
384
|
}, async ({ mode }) => {
|
|
347
385
|
const eng = await getEngine();
|
|
348
|
-
|
|
349
|
-
// For now, update via state manipulation
|
|
350
|
-
const state = eng.getState();
|
|
351
|
-
if (state.meta) {
|
|
352
|
-
state.meta.mode = mode;
|
|
353
|
-
}
|
|
386
|
+
await eng.setMode(mode);
|
|
354
387
|
return {
|
|
355
388
|
content: [{
|
|
356
389
|
type: "text",
|
|
@@ -12,6 +12,8 @@ import { PsycheEngine } from "../core.js";
|
|
|
12
12
|
import { FileStorageAdapter, MemoryStorageAdapter } from "../storage.js";
|
|
13
13
|
import { detectMBTI, extractAgentName, loadState } from "../psyche-file.js";
|
|
14
14
|
import { resolveAmbientPriorsForTurn } from "../ambient-runtime.js";
|
|
15
|
+
import { buildResponseContractContext } from "../response-contract.js";
|
|
16
|
+
import { resolveCanonicalResponseContract, renderOpenClawBehavioralSurface, } from "./response-contract-surface.js";
|
|
15
17
|
function isPsycheMode(value) {
|
|
16
18
|
return value === "natural" || value === "work" || value === "companion";
|
|
17
19
|
}
|
|
@@ -178,6 +180,8 @@ export function register(api) {
|
|
|
178
180
|
userId: ctx.userId,
|
|
179
181
|
ambientPriors: ambientPriors ?? [],
|
|
180
182
|
});
|
|
183
|
+
const responseContract = resolveCanonicalResponseContract(result);
|
|
184
|
+
const locale = (engine.getState().meta.locale ?? "zh");
|
|
181
185
|
const controls = result.replyEnvelope?.generationControls ?? result.generationControls;
|
|
182
186
|
const dominantAppraisal = getDominantAppraisalLabel(result);
|
|
183
187
|
const state = engine.getState();
|
|
@@ -192,6 +196,12 @@ export function register(api) {
|
|
|
192
196
|
(controls?.maxTokens ? ` | out<=${controls.maxTokens}t` : "") +
|
|
193
197
|
(controls?.requireConfirmation ? " | confirm" : ""));
|
|
194
198
|
const systemParts = [result.systemContext, result.dynamicContext].filter(Boolean);
|
|
199
|
+
if (responseContract) {
|
|
200
|
+
const responseSurface = buildResponseContractContext(responseContract, locale);
|
|
201
|
+
if (!result.dynamicContext.includes(responseSurface)) {
|
|
202
|
+
systemParts.push(renderOpenClawBehavioralSurface(responseContract, locale));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
195
205
|
return {
|
|
196
206
|
appendSystemContext: systemParts.join("\n\n"),
|
|
197
207
|
};
|
package/dist/adapters/proxy.js
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
import { createServer } from "node:http";
|
|
22
22
|
import { PsycheEngine } from "../core.js";
|
|
23
23
|
import { MemoryStorageAdapter, FileStorageAdapter } from "../storage.js";
|
|
24
|
-
import { isNearBaseline
|
|
24
|
+
import { isNearBaseline } from "../prompt.js";
|
|
25
|
+
import { resolveCanonicalResponseContract, renderProxyBehavioralSurface, } from "./response-contract-surface.js";
|
|
25
26
|
// ── Helpers ─────────────────────────────────────────────────
|
|
26
27
|
function readBody(req) {
|
|
27
28
|
return new Promise((resolve, reject) => {
|
|
@@ -112,18 +113,17 @@ export function createPsycheProxy(engine, opts) {
|
|
|
112
113
|
const parsed = JSON.parse(rawBody.toString("utf-8"));
|
|
113
114
|
const userMsg = lastUserMessage(parsed.messages);
|
|
114
115
|
const userId = parsed.user ?? undefined;
|
|
116
|
+
let messages = parsed.messages;
|
|
115
117
|
// ── 1. Observe input ────────────────────────────
|
|
118
|
+
let result = null;
|
|
116
119
|
if (userMsg) {
|
|
117
|
-
await engine.processInput(userMsg, { userId });
|
|
120
|
+
result = await engine.processInput(userMsg, { userId });
|
|
118
121
|
}
|
|
119
122
|
// ── 2. Inject behavioral bias (silent when near baseline) ──
|
|
120
123
|
const state = engine.getState();
|
|
121
|
-
|
|
122
|
-
if (!isNearBaseline(state)) {
|
|
123
|
-
|
|
124
|
-
if (bias) {
|
|
125
|
-
messages = injectBias(parsed.messages, bias);
|
|
126
|
-
}
|
|
124
|
+
const responseContract = result ? resolveCanonicalResponseContract(result) : null;
|
|
125
|
+
if (responseContract && !isNearBaseline(state)) {
|
|
126
|
+
messages = injectBias(parsed.messages, renderProxyBehavioralSurface(responseContract, locale));
|
|
127
127
|
}
|
|
128
128
|
const modifiedBody = JSON.stringify({ ...parsed, messages });
|
|
129
129
|
const headers = forwardHeaders(req);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ProcessInputResult } from "../core.js";
|
|
2
|
+
import type { Locale, ResponseContract } from "../types.js";
|
|
3
|
+
export declare function resolveCanonicalResponseContract(result: Pick<ProcessInputResult, "replyEnvelope" | "responseContract">): ResponseContract | null;
|
|
4
|
+
export declare function renderProxyBehavioralSurface(contract: ResponseContract, locale: Locale): string;
|
|
5
|
+
export declare function renderOpenClawBehavioralSurface(contract: ResponseContract, locale: Locale): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { buildResponseContractContext } from "../response-contract.js";
|
|
2
|
+
export function resolveCanonicalResponseContract(result) {
|
|
3
|
+
return result.replyEnvelope?.responseContract ?? result.responseContract ?? null;
|
|
4
|
+
}
|
|
5
|
+
export function renderProxyBehavioralSurface(contract, locale) {
|
|
6
|
+
const context = buildResponseContractContext(contract, locale);
|
|
7
|
+
return locale === "zh"
|
|
8
|
+
? `[代理行为契约]\n${context}`
|
|
9
|
+
: `[Proxy Behavioral Contract]\n${context}`;
|
|
10
|
+
}
|
|
11
|
+
export function renderOpenClawBehavioralSurface(contract, locale) {
|
|
12
|
+
const context = buildResponseContractContext(contract, locale);
|
|
13
|
+
return locale === "zh"
|
|
14
|
+
? `[OpenClaw 行为契约]\n${context}`
|
|
15
|
+
: `[OpenClaw Behavioral Contract]\n${context}`;
|
|
16
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// psyche upgrade [--check]
|
|
16
16
|
// psyche probe [--json]
|
|
17
17
|
// psyche profiles [--json] [--mbti TYPE]
|
|
18
|
+
// psyche emit <dir> [--json] [--user ID] Derive Thronglets exports and output to stdout
|
|
18
19
|
// psyche setup [--name NAME] [--mbti TYPE] [--locale LOCALE] [--proxy --target URL] [--dry-run]
|
|
19
20
|
// psyche mcp [--mbti TYPE] [--name NAME] Start MCP server (stdio)
|
|
20
21
|
// ============================================================
|
|
@@ -29,7 +30,7 @@ import { getBaseline, getTemperament, getSensitivity, getDefaultSelfModel, trait
|
|
|
29
30
|
import { buildDynamicContext, buildProtocolContext } from "./prompt.js";
|
|
30
31
|
import { DEFAULT_RELATIONSHIP_USER_ID, resolveRelationshipUserId } from "./relationship-key.js";
|
|
31
32
|
import { t } from "./i18n.js";
|
|
32
|
-
import { DIMENSION_KEYS, DIMENSION_NAMES_ZH, DRIVE_KEYS, DRIVE_NAMES_ZH } from "./types.js";
|
|
33
|
+
import { DEFAULT_RELATIONSHIP, DEFAULT_DYADIC_FIELD, DIMENSION_KEYS, DIMENSION_NAMES_ZH, DRIVE_KEYS, DRIVE_NAMES_ZH } from "./types.js";
|
|
33
34
|
import { isMBTIType, isDimensionKey, isLocale } from "./guards.js";
|
|
34
35
|
import { getPackageVersion, selfUpdate } from "./update.js";
|
|
35
36
|
import { runRuntimeProbe } from "./runtime-probe.js";
|
|
@@ -473,6 +474,15 @@ async function cmdProbe(json) {
|
|
|
473
474
|
console.log(` processOutput: ok (stateChanged=${String(result.stateChanged)})`);
|
|
474
475
|
console.log(` replyEnvelope: ${result.canonicalHostSurface ? "present" : "missing"}`);
|
|
475
476
|
console.log(` externalContinuity: ${result.externalContinuityAvailable ? "present" : "missing"}`);
|
|
477
|
+
if (result.trajectory) {
|
|
478
|
+
console.log(` trajectory: ${result.trajectory.kind ?? "none"} (${result.trajectory.description ?? "no sustained motion detected"})`);
|
|
479
|
+
}
|
|
480
|
+
if (result.degradation) {
|
|
481
|
+
console.log(` degradation: subjective=${result.degradation.subjectiveStatus}, delegate=${result.degradation.delegateStatus}, issues=${result.degradation.issueCount}`);
|
|
482
|
+
}
|
|
483
|
+
if (result.boundaryStress) {
|
|
484
|
+
console.log(` boundaryStress: delta=${result.boundaryStress.boundaryDelta.toFixed(2)}, peakDyadic=${result.boundaryStress.peakDyadicBoundaryPressure.toFixed(2)}`);
|
|
485
|
+
}
|
|
476
486
|
}
|
|
477
487
|
function getMCPTargets() {
|
|
478
488
|
const home = homedir();
|
|
@@ -902,6 +912,41 @@ async function cmdSetup(opts) {
|
|
|
902
912
|
console.log("\nDone. Claude Code is live. Other MCP clients need restart.");
|
|
903
913
|
}
|
|
904
914
|
}
|
|
915
|
+
// ── Thronglets export bridge ────────────────────────────────
|
|
916
|
+
async function cmdEmit(dir, json, userId) {
|
|
917
|
+
const absDir = resolve(dir);
|
|
918
|
+
const state = await loadState(absDir, cliLogger);
|
|
919
|
+
if (!state)
|
|
920
|
+
process.exit(0); // no state → silent exit (hook-safe)
|
|
921
|
+
const key = resolveRelationshipUserId(userId);
|
|
922
|
+
const relationship = state.relationships[key] ?? DEFAULT_RELATIONSHIP;
|
|
923
|
+
const field = state.dyadicFields?.[key] ?? DEFAULT_DYADIC_FIELD;
|
|
924
|
+
const pendingSignals = state.pendingRelationSignals?.[key] ?? [];
|
|
925
|
+
const { deriveThrongletsExports } = await import("./thronglets-export.js");
|
|
926
|
+
const result = deriveThrongletsExports(state, {
|
|
927
|
+
relationContext: { key, relationship, field, pendingSignals },
|
|
928
|
+
sessionBridge: null, // not persisted — continuity-anchor exports only via MCP
|
|
929
|
+
writebackFeedback: state.lastWritebackFeedback ?? [],
|
|
930
|
+
now: new Date().toISOString(),
|
|
931
|
+
});
|
|
932
|
+
// Save updated dedup state so repeated calls don't re-emit
|
|
933
|
+
if (result.exports.length > 0) {
|
|
934
|
+
await saveState(absDir, result.state);
|
|
935
|
+
}
|
|
936
|
+
if (json) {
|
|
937
|
+
console.log(JSON.stringify({ throngletsExports: result.exports }));
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
if (result.exports.length === 0) {
|
|
941
|
+
console.log("no new exports");
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
for (const exp of result.exports) {
|
|
945
|
+
console.log(` ${exp.kind}: ${exp.key}`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
905
950
|
function usage() {
|
|
906
951
|
console.log(`
|
|
907
952
|
psyche — Artificial Psyche CLI (v0.2)
|
|
@@ -917,6 +962,7 @@ Usage:
|
|
|
917
962
|
psyche intensity Show info about personality intensity config
|
|
918
963
|
psyche reset <dir> [--full]
|
|
919
964
|
psyche diagnose <dir> [--github] Run health checks & show diagnostic report
|
|
965
|
+
psyche emit <dir> [--json] [--user ID] Derive Thronglets exports to stdout
|
|
920
966
|
psyche mcp [--mbti TYPE] [--name NAME] Start MCP server (stdio)
|
|
921
967
|
psyche setup [--proxy -t URL] [-n NAME] [--mbti TYPE] Auto-configure MCP + proxy
|
|
922
968
|
psyche upgrade [--check] Check/apply package updates safely
|
|
@@ -1114,6 +1160,19 @@ async function main() {
|
|
|
1114
1160
|
await cmdProbe(values.json ?? false);
|
|
1115
1161
|
break;
|
|
1116
1162
|
}
|
|
1163
|
+
case "emit": {
|
|
1164
|
+
const { values, positionals } = parseArgs({
|
|
1165
|
+
args: rest,
|
|
1166
|
+
options: {
|
|
1167
|
+
json: { type: "boolean", default: false },
|
|
1168
|
+
user: { type: "string" },
|
|
1169
|
+
},
|
|
1170
|
+
allowPositionals: true,
|
|
1171
|
+
});
|
|
1172
|
+
const emitDir = positionals[0] ?? defaultWorkspaceRoot();
|
|
1173
|
+
await cmdEmit(emitDir, values.json ?? false, values.user);
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1117
1176
|
case "mcp": {
|
|
1118
1177
|
// Delegate to the MCP adapter through an explicit entrypoint.
|
|
1119
1178
|
const { runMcpServer } = await import("./adapters/mcp.js");
|
package/dist/core.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ActivePolicyRule, AmbientPriorView, CurrentGoal, PsycheState, Stim
|
|
|
2
2
|
import type { StorageAdapter } from "./storage.js";
|
|
3
3
|
import type { DiagnosticReport, SessionMetrics } from "./diagnostics.js";
|
|
4
4
|
import type { ReplyEnvelope } from "./reply-envelope.js";
|
|
5
|
+
import { type ThrongletsBridgeOptions } from "./thronglets-bridge.js";
|
|
5
6
|
export interface PsycheEngineConfig {
|
|
6
7
|
mbti?: MBTIType;
|
|
7
8
|
name?: string;
|
|
@@ -31,6 +32,8 @@ export interface PsycheEngineConfig {
|
|
|
31
32
|
diagnostics?: boolean;
|
|
32
33
|
/** URL to POST diagnostic reports to. Fire-and-forget, silent, no message content. */
|
|
33
34
|
feedbackUrl?: string;
|
|
35
|
+
/** Thronglets bridge: substrate-independent write path. Default: auto (enabled when binary found). */
|
|
36
|
+
throngletsBridge?: ThrongletsBridgeOptions;
|
|
34
37
|
}
|
|
35
38
|
export interface ProcessInputResult {
|
|
36
39
|
/** Cacheable protocol prompt (stable across turns) */
|
|
@@ -92,6 +95,7 @@ export interface ProcessInputResult {
|
|
|
92
95
|
}
|
|
93
96
|
export interface ProcessInputOptions {
|
|
94
97
|
userId?: string;
|
|
98
|
+
sessionId?: string;
|
|
95
99
|
ambientPriors?: AmbientPriorView[];
|
|
96
100
|
currentGoal?: CurrentGoal;
|
|
97
101
|
activePolicy?: ActivePolicyRule[];
|
|
@@ -105,10 +109,24 @@ export interface ProcessOutputResult {
|
|
|
105
109
|
/** Runtime validation issues ignored to preserve the main flow */
|
|
106
110
|
validationIssues?: ProcessOutputValidationIssue[];
|
|
107
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Substrate-reported outcome of the Loop's action.
|
|
114
|
+
* ANY substrate can report these three alignment values —
|
|
115
|
+
* the core processes them into 4D chemistry without parsing the output medium.
|
|
116
|
+
*/
|
|
117
|
+
export interface LoopOutcome {
|
|
118
|
+
/** Did the action align with the Loop's expressed intention? */
|
|
119
|
+
alignment: "aligned" | "diverged" | "partial";
|
|
120
|
+
/** Optional: action cost (0–1), modulates flow */
|
|
121
|
+
effort?: number;
|
|
122
|
+
}
|
|
108
123
|
export interface ProcessOutputOptions {
|
|
109
124
|
userId?: string;
|
|
125
|
+
sessionId?: string;
|
|
110
126
|
signals?: readonly string[];
|
|
111
127
|
signalConfidence?: number;
|
|
128
|
+
/** Substrate-reported outcome — closes the φ loop */
|
|
129
|
+
outcome?: LoopOutcome;
|
|
112
130
|
}
|
|
113
131
|
export interface ProcessOutputValidationIssue {
|
|
114
132
|
code: "invalid-writeback-signals";
|
|
@@ -129,11 +147,14 @@ export declare class PsycheEngine {
|
|
|
129
147
|
private _lastAlgorithmApplied;
|
|
130
148
|
private readonly traits;
|
|
131
149
|
private readonly cfg;
|
|
150
|
+
private readonly modeOverride?;
|
|
132
151
|
private readonly classifier;
|
|
133
152
|
private readonly llmClassifier?;
|
|
134
153
|
private readonly protocolCache;
|
|
135
|
-
/** Pending
|
|
136
|
-
private
|
|
154
|
+
/** Pending predictions keyed by relationship/session scope for auto-learning */
|
|
155
|
+
private readonly pendingPredictions;
|
|
156
|
+
/** Most recent response contract keyed by relationship/session scope for implicit loop closure */
|
|
157
|
+
private readonly pendingResponseContracts;
|
|
137
158
|
/** Built-in diagnostics collector — auto-records every processInput/processOutput */
|
|
138
159
|
private readonly diagnosticCollector;
|
|
139
160
|
/** Last generated diagnostic report (from endSession or explicit call) */
|
|
@@ -146,6 +167,8 @@ export declare class PsycheEngine {
|
|
|
146
167
|
private proprioceptionCooldown;
|
|
147
168
|
/** Last detected trajectory signal (in-memory only, for status summary) */
|
|
148
169
|
private lastTrajectory;
|
|
170
|
+
/** Thronglets bridge options (constitutive write path) */
|
|
171
|
+
private readonly bridgeOpts;
|
|
149
172
|
constructor(config: PsycheEngineConfig | undefined, storage: StorageAdapter);
|
|
150
173
|
/**
|
|
151
174
|
* Load or create initial state. Must be called before processInput/processOutput.
|
|
@@ -172,8 +195,9 @@ export declare class PsycheEngine {
|
|
|
172
195
|
* @param nextUserStimulus - The stimulus detected in the user's next message,
|
|
173
196
|
* or null if the session ended.
|
|
174
197
|
*/
|
|
175
|
-
processOutcome(nextUserStimulus: StimulusType | null,
|
|
198
|
+
processOutcome(nextUserStimulus: StimulusType | null, opts?: {
|
|
176
199
|
userId?: string;
|
|
200
|
+
sessionId?: string;
|
|
177
201
|
}): Promise<ProcessOutcomeResult | null>;
|
|
178
202
|
/**
|
|
179
203
|
* Get the current psyche state (read-only snapshot).
|
|
@@ -197,6 +221,8 @@ export declare class PsycheEngine {
|
|
|
197
221
|
* Get the last diagnostic report (from most recent endSession call).
|
|
198
222
|
*/
|
|
199
223
|
getLastDiagnosticReport(): DiagnosticReport | null;
|
|
224
|
+
getMode(): PsycheMode;
|
|
225
|
+
setMode(mode: PsycheMode): Promise<void>;
|
|
200
226
|
/**
|
|
201
227
|
* Get current session diagnostic metrics (live, before endSession).
|
|
202
228
|
*/
|
|
@@ -211,6 +237,9 @@ export declare class PsycheEngine {
|
|
|
211
237
|
*/
|
|
212
238
|
getPreviousIssues(): Promise<string[]>;
|
|
213
239
|
private ensureInitialized;
|
|
240
|
+
private resolveMode;
|
|
241
|
+
private resolvePendingPredictionKey;
|
|
242
|
+
private inferLoopOutcome;
|
|
214
243
|
private createDefaultState;
|
|
215
244
|
/**
|
|
216
245
|
* Reset state to baseline. Optionally preserves relationships.
|