psyche-ai 11.5.4 → 11.5.6
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 +23 -0
- package/dist/adapters/claude-sdk.js +24 -7
- package/dist/adapters/fail-open.d.ts +9 -0
- package/dist/adapters/fail-open.js +58 -0
- package/dist/adapters/http.js +19 -25
- package/dist/adapters/langchain.d.ts +7 -2
- package/dist/adapters/langchain.js +30 -30
- package/dist/adapters/mcp.d.ts +1 -1
- package/dist/adapters/mcp.js +32 -14
- package/dist/adapters/vercel-ai.d.ts +1 -1
- package/dist/adapters/vercel-ai.js +8 -7
- package/dist/ambient-runtime.d.ts +4 -1
- package/dist/ambient-runtime.js +7 -2
- package/dist/core.d.ts +11 -2
- package/dist/core.js +25 -4
- package/dist/prompt.js +4 -0
- package/dist/psyche-file.d.ts +2 -0
- package/dist/psyche-file.js +12 -16
- package/dist/types.d.ts +2 -0
- package/dist/types.js +24 -0
- package/dist/writeback-signals.d.ts +8 -0
- package/dist/writeback-signals.js +39 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,6 +103,29 @@ Psyche 要解决的不可压缩问题只有一个:
|
|
|
103
103
|
|
|
104
104
|
一句话:`Psyche` 读取当前执行边界,但不把执行边界误当成“我是谁”。
|
|
105
105
|
|
|
106
|
+
## 中期方向:更像人,但不是靠 prompt 工程
|
|
107
|
+
|
|
108
|
+
如果 `Psyche` 未来要变得更有感情、更会和人相处,这种能力也必须从连续性里长出来,而不是从 prompt 皮肤里长出来。
|
|
109
|
+
|
|
110
|
+
允许增长的来源只有:
|
|
111
|
+
|
|
112
|
+
- 持续本地状态
|
|
113
|
+
- appraisal 残留
|
|
114
|
+
- 关系动力学
|
|
115
|
+
- 调节与修复历史
|
|
116
|
+
- 低频写回与学习
|
|
117
|
+
|
|
118
|
+
不允许把下面这些误当成“主体性升级”:
|
|
119
|
+
|
|
120
|
+
- 静态 persona prompt
|
|
121
|
+
- 手写情绪脚本
|
|
122
|
+
- 高频情绪仪表盘
|
|
123
|
+
- 只改措辞、不改后续行为分布的情感 UI
|
|
124
|
+
|
|
125
|
+
冻结约束见:
|
|
126
|
+
|
|
127
|
+
- [../Oasyce-Sigil/EMERGENT_SUBJECTIVITY_CONTRACT_V1.md](../Oasyce-Sigil/EMERGENT_SUBJECTIVITY_CONTRACT_V1.md)
|
|
128
|
+
|
|
106
129
|
## 可分离安装
|
|
107
130
|
|
|
108
131
|
这两层默认就是可分离的,不应互相成为硬依赖。
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
// The SDK has no middleware interface and hooks cannot modify assistant
|
|
29
29
|
// output, so processResponse must be called explicitly by the host.
|
|
30
30
|
// ============================================================
|
|
31
|
+
import { normalizeCurrentGoal, normalizeCurrentTurnCorrection, resolveRuntimeActivePolicy, } from "../types.js";
|
|
31
32
|
import { describeEmotionalState } from "../chemistry.js";
|
|
32
33
|
import { serializeThrongletsExportAsTrace } from "../thronglets-runtime.js";
|
|
33
34
|
import { resolveRelationshipUserId } from "../relationship-key.js";
|
|
34
35
|
import { resolveAmbientPriorsForTurn, } from "../ambient-runtime.js";
|
|
36
|
+
import { safeProcessInput, safeProcessOutput } from "./fail-open.js";
|
|
35
37
|
// ── Dimension description ────────────────────────────────────
|
|
36
38
|
const DIM_THRESHOLDS = {
|
|
37
39
|
high: 70,
|
|
@@ -107,9 +109,12 @@ export class PsycheClaudeSDK {
|
|
|
107
109
|
ambient: opts?.ambient === true ? {} : (opts?.ambient || undefined),
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
|
-
async resolveAmbientPriors(userMessage) {
|
|
112
|
+
async resolveAmbientPriors(userMessage, currentGoal, activePolicy, currentTurnCorrection) {
|
|
111
113
|
return resolveAmbientPriorsForTurn(userMessage, {
|
|
112
114
|
enabled: Boolean(this.opts.ambient),
|
|
115
|
+
currentGoal,
|
|
116
|
+
activePolicy,
|
|
117
|
+
currentTurnCorrection,
|
|
113
118
|
thronglets: this.opts.ambient
|
|
114
119
|
? {
|
|
115
120
|
...this.opts.ambient,
|
|
@@ -166,17 +171,29 @@ export class PsycheClaudeSDK {
|
|
|
166
171
|
sessionId: runtimeContext.sessionId ?? self.lastRuntimeContext.sessionId,
|
|
167
172
|
};
|
|
168
173
|
const userMessage = input.user_message ?? "";
|
|
169
|
-
const
|
|
170
|
-
|
|
174
|
+
const currentTurnCorrection = normalizeCurrentTurnCorrection(input.current_turn_correction
|
|
175
|
+
?? input.currentTurnCorrection
|
|
176
|
+
?? input.task_correction
|
|
177
|
+
?? input.taskCorrection
|
|
178
|
+
?? input.explicit_instruction
|
|
179
|
+
?? input.explicitInstruction);
|
|
180
|
+
const currentGoal = normalizeCurrentGoal(input.current_goal ?? input.currentGoal);
|
|
181
|
+
const activePolicy = resolveRuntimeActivePolicy(input.active_policy ?? input.activePolicy, currentTurnCorrection);
|
|
182
|
+
const ambientPriors = await self.resolveAmbientPriors(userMessage, currentGoal, activePolicy, currentTurnCorrection);
|
|
183
|
+
const result = await safeProcessInput(self.engine, userMessage, {
|
|
171
184
|
userId: self.opts.userId,
|
|
172
185
|
ambientPriors,
|
|
173
|
-
|
|
186
|
+
currentGoal,
|
|
187
|
+
activePolicy,
|
|
188
|
+
currentTurnCorrection,
|
|
189
|
+
}, "claude-sdk.processInput");
|
|
174
190
|
self.lastInputResult = result;
|
|
175
191
|
// Cache Thronglets exports from this turn
|
|
176
192
|
if (self.opts.thronglets && result.throngletsExports) {
|
|
177
193
|
self.lastThrongletsExports = result.throngletsExports;
|
|
178
194
|
}
|
|
179
|
-
|
|
195
|
+
const systemMessage = result.dynamicContext;
|
|
196
|
+
return systemMessage ? { systemMessage } : {};
|
|
180
197
|
},
|
|
181
198
|
],
|
|
182
199
|
},
|
|
@@ -193,11 +210,11 @@ export class PsycheClaudeSDK {
|
|
|
193
210
|
* @returns Cleaned text with tags removed
|
|
194
211
|
*/
|
|
195
212
|
async processResponse(text, opts) {
|
|
196
|
-
const result = await this.engine
|
|
213
|
+
const result = await safeProcessOutput(this.engine, text, {
|
|
197
214
|
userId: this.opts.userId,
|
|
198
215
|
signals: opts?.signals,
|
|
199
216
|
signalConfidence: opts?.signalConfidence,
|
|
200
|
-
});
|
|
217
|
+
}, "claude-sdk.processOutput");
|
|
201
218
|
return result.cleanedText;
|
|
202
219
|
}
|
|
203
220
|
// ── Thronglets integration ────────────────────────────────
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProcessInputOptions, ProcessInputResult, ProcessOutputOptions, ProcessOutputResult, PsycheEngine } from "../core.js";
|
|
2
|
+
export declare function stripPsycheUpdateTags(text: string): string;
|
|
3
|
+
export interface FailOpenOutputFallbackOptions {
|
|
4
|
+
stripUpdateTags?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function composePsycheContext(result: Pick<ProcessInputResult, "systemContext" | "dynamicContext">): string;
|
|
7
|
+
export declare function buildFailOpenProcessInputResult(opts: ProcessInputOptions | undefined): ProcessInputResult;
|
|
8
|
+
export declare function safeProcessInput(engine: PsycheEngine, text: string, opts?: ProcessInputOptions, phase?: string): Promise<ProcessInputResult>;
|
|
9
|
+
export declare function safeProcessOutput(engine: PsycheEngine, text: string, opts?: ProcessOutputOptions, phase?: string, fallback?: FailOpenOutputFallbackOptions): Promise<ProcessOutputResult>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const PSYCHE_TAG_RE = /<psyche_update>[\s\S]*?<\/psyche_update>/g;
|
|
2
|
+
export function stripPsycheUpdateTags(text) {
|
|
3
|
+
return text
|
|
4
|
+
.replace(PSYCHE_TAG_RE, "")
|
|
5
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
6
|
+
.trim();
|
|
7
|
+
}
|
|
8
|
+
export function composePsycheContext(result) {
|
|
9
|
+
return [result.systemContext, result.dynamicContext].filter(Boolean).join("\n\n");
|
|
10
|
+
}
|
|
11
|
+
export function buildFailOpenProcessInputResult(opts) {
|
|
12
|
+
return {
|
|
13
|
+
systemContext: "",
|
|
14
|
+
dynamicContext: "",
|
|
15
|
+
ambientPriors: opts?.ambientPriors ?? [],
|
|
16
|
+
activePolicy: opts?.activePolicy ?? [],
|
|
17
|
+
currentGoal: opts?.currentGoal,
|
|
18
|
+
ambientPriorContext: undefined,
|
|
19
|
+
appraisal: null,
|
|
20
|
+
legacyStimulus: null,
|
|
21
|
+
stimulus: null,
|
|
22
|
+
legacyStimulusConfidence: undefined,
|
|
23
|
+
stimulusConfidence: undefined,
|
|
24
|
+
policyModifiers: undefined,
|
|
25
|
+
replyEnvelope: undefined,
|
|
26
|
+
subjectivityKernel: undefined,
|
|
27
|
+
responseContract: undefined,
|
|
28
|
+
generationControls: undefined,
|
|
29
|
+
sessionBridge: null,
|
|
30
|
+
writebackFeedback: [],
|
|
31
|
+
externalContinuity: undefined,
|
|
32
|
+
throngletsExports: [],
|
|
33
|
+
observability: undefined,
|
|
34
|
+
policyContext: "",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function safeProcessInput(engine, text, opts, phase = "processInput") {
|
|
38
|
+
try {
|
|
39
|
+
return await engine.processInput(text, opts);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
engine.recordDiagnosticError(phase, error);
|
|
43
|
+
return buildFailOpenProcessInputResult(opts);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function safeProcessOutput(engine, text, opts, phase = "processOutput", fallback) {
|
|
47
|
+
try {
|
|
48
|
+
return await engine.processOutput(text, opts);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
engine.recordDiagnosticError(phase, error);
|
|
52
|
+
const shouldStripTags = fallback?.stripUpdateTags !== false && text.includes("<psyche_update>");
|
|
53
|
+
return {
|
|
54
|
+
cleanedText: shouldStripTags ? stripPsycheUpdateTags(text) : text,
|
|
55
|
+
stateChanged: false,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/adapters/http.js
CHANGED
|
@@ -16,30 +16,22 @@
|
|
|
16
16
|
// Zero dependencies — uses node:http only.
|
|
17
17
|
// ============================================================
|
|
18
18
|
import { createServer } from "node:http";
|
|
19
|
-
import {
|
|
19
|
+
import { safeProcessInput, safeProcessOutput } from "./fail-open.js";
|
|
20
|
+
import { normalizeCurrentGoal, normalizeCurrentTurnCorrection, resolveRuntimeActivePolicy, } from "../types.js";
|
|
20
21
|
import { parseAmbientPriorsInput } from "../ambient-runtime.js";
|
|
21
22
|
import { computeOverlay } from "../overlay.js";
|
|
22
|
-
|
|
23
|
-
"trust_up",
|
|
24
|
-
"trust_down",
|
|
25
|
-
"boundary_set",
|
|
26
|
-
"boundary_soften",
|
|
27
|
-
"repair_attempt",
|
|
28
|
-
"repair_landed",
|
|
29
|
-
"closeness_invite",
|
|
30
|
-
"withdrawal_mark",
|
|
31
|
-
"self_assertion",
|
|
32
|
-
"task_recenter",
|
|
33
|
-
]);
|
|
34
|
-
function parseSignals(value) {
|
|
35
|
-
if (!Array.isArray(value))
|
|
36
|
-
return undefined;
|
|
37
|
-
const parsed = value.filter((item) => (typeof item === "string" && VALID_WRITEBACK_SIGNALS.has(item)));
|
|
38
|
-
return parsed.length > 0 ? [...new Set(parsed)] : undefined;
|
|
39
|
-
}
|
|
23
|
+
import { coerceWritebackSignalInput } from "../writeback-signals.js";
|
|
40
24
|
function parseAmbientPriors(value) {
|
|
41
25
|
return parseAmbientPriorsInput(value);
|
|
42
26
|
}
|
|
27
|
+
function parseCurrentTurnCorrection(body) {
|
|
28
|
+
return normalizeCurrentTurnCorrection(body.currentTurnCorrection
|
|
29
|
+
?? body.current_turn_correction
|
|
30
|
+
?? body.taskCorrection
|
|
31
|
+
?? body.task_correction
|
|
32
|
+
?? body.explicitInstruction
|
|
33
|
+
?? body.explicit_instruction);
|
|
34
|
+
}
|
|
43
35
|
// ── Server ───────────────────────────────────────────────────
|
|
44
36
|
/**
|
|
45
37
|
* Create an HTTP server that exposes PsycheEngine via REST API.
|
|
@@ -94,12 +86,14 @@ export function createPsycheServer(engine, opts) {
|
|
|
94
86
|
// POST /process-input
|
|
95
87
|
if (req.method === "POST" && url.pathname === "/process-input") {
|
|
96
88
|
const body = await readBody(req);
|
|
97
|
-
const
|
|
89
|
+
const currentTurnCorrection = parseCurrentTurnCorrection(body);
|
|
90
|
+
const result = await safeProcessInput(engine, body.text ?? "", {
|
|
98
91
|
userId: body.userId,
|
|
99
92
|
ambientPriors: parseAmbientPriors(body.ambientPriors),
|
|
100
93
|
currentGoal: normalizeCurrentGoal(body.currentGoal),
|
|
101
|
-
activePolicy:
|
|
102
|
-
|
|
94
|
+
activePolicy: resolveRuntimeActivePolicy(body.activePolicy, currentTurnCorrection),
|
|
95
|
+
currentTurnCorrection,
|
|
96
|
+
}, "http.processInput");
|
|
103
97
|
json(res, 200, {
|
|
104
98
|
systemContext: result.systemContext,
|
|
105
99
|
dynamicContext: result.dynamicContext,
|
|
@@ -127,11 +121,11 @@ export function createPsycheServer(engine, opts) {
|
|
|
127
121
|
// POST /process-output
|
|
128
122
|
if (req.method === "POST" && url.pathname === "/process-output") {
|
|
129
123
|
const body = await readBody(req);
|
|
130
|
-
const result = await engine
|
|
124
|
+
const result = await safeProcessOutput(engine, body.text ?? "", {
|
|
131
125
|
userId: body.userId,
|
|
132
|
-
signals:
|
|
126
|
+
signals: coerceWritebackSignalInput(body.signals),
|
|
133
127
|
signalConfidence: typeof body.signalConfidence === "number" ? body.signalConfidence : undefined,
|
|
134
|
-
});
|
|
128
|
+
}, "http.processOutput");
|
|
135
129
|
json(res, 200, result);
|
|
136
130
|
return;
|
|
137
131
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PsycheEngine } from "../core.js";
|
|
2
|
+
import type { ActivePolicyRule, CurrentGoal } from "../types.js";
|
|
2
3
|
import { type ThrongletsAmbientRuntimeOptions } from "../ambient-runtime.js";
|
|
3
4
|
export interface PsycheLangChainOptions {
|
|
4
5
|
ambient?: boolean | ThrongletsAmbientRuntimeOptions;
|
|
@@ -33,8 +34,6 @@ export declare class PsycheLangChain {
|
|
|
33
34
|
private readonly engine;
|
|
34
35
|
private readonly opts;
|
|
35
36
|
constructor(engine: PsycheEngine, opts?: PsycheLangChainOptions);
|
|
36
|
-
private readonly validSignals;
|
|
37
|
-
private parseSignals;
|
|
38
37
|
private resolveAmbientPriors;
|
|
39
38
|
/**
|
|
40
39
|
* Get the system message to inject into the LLM call.
|
|
@@ -44,6 +43,9 @@ export declare class PsycheLangChain {
|
|
|
44
43
|
*/
|
|
45
44
|
getSystemMessage(userText: string, opts?: {
|
|
46
45
|
userId?: string;
|
|
46
|
+
currentGoal?: CurrentGoal;
|
|
47
|
+
activePolicy?: ActivePolicyRule[];
|
|
48
|
+
currentTurnCorrection?: string;
|
|
47
49
|
}): Promise<string>;
|
|
48
50
|
/**
|
|
49
51
|
* Prepare both prompt text and mechanical invocation hints for a LangChain call.
|
|
@@ -54,6 +56,9 @@ export declare class PsycheLangChain {
|
|
|
54
56
|
prepareInvocation(userText: string, opts?: {
|
|
55
57
|
userId?: string;
|
|
56
58
|
maxTokens?: number;
|
|
59
|
+
currentGoal?: CurrentGoal;
|
|
60
|
+
activePolicy?: ActivePolicyRule[];
|
|
61
|
+
currentTurnCorrection?: string;
|
|
57
62
|
}): Promise<{
|
|
58
63
|
systemMessage: string;
|
|
59
64
|
maxTokens?: number;
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
// It provides the hooks you need to wire psyche into any
|
|
13
13
|
// LangChain pipeline without requiring langchain as a dependency.
|
|
14
14
|
// ============================================================
|
|
15
|
+
import { normalizeCurrentGoal, normalizeCurrentTurnCorrection, resolveRuntimeActivePolicy, } from "../types.js";
|
|
15
16
|
import { resolveAmbientPriorsForTurn, } from "../ambient-runtime.js";
|
|
17
|
+
import { composePsycheContext, safeProcessInput, safeProcessOutput } from "./fail-open.js";
|
|
18
|
+
import { coerceWritebackSignalInput } from "../writeback-signals.js";
|
|
16
19
|
/**
|
|
17
20
|
* LangChain integration helper for PsycheEngine.
|
|
18
21
|
*
|
|
@@ -46,28 +49,13 @@ export class PsycheLangChain {
|
|
|
46
49
|
this.engine = engine;
|
|
47
50
|
this.opts = opts;
|
|
48
51
|
}
|
|
49
|
-
|
|
50
|
-
"trust_up",
|
|
51
|
-
"trust_down",
|
|
52
|
-
"boundary_set",
|
|
53
|
-
"boundary_soften",
|
|
54
|
-
"repair_attempt",
|
|
55
|
-
"repair_landed",
|
|
56
|
-
"closeness_invite",
|
|
57
|
-
"withdrawal_mark",
|
|
58
|
-
"self_assertion",
|
|
59
|
-
"task_recenter",
|
|
60
|
-
]);
|
|
61
|
-
parseSignals(signals) {
|
|
62
|
-
if (!signals)
|
|
63
|
-
return undefined;
|
|
64
|
-
const parsed = signals.filter((signal) => this.validSignals.has(signal));
|
|
65
|
-
return parsed.length > 0 ? [...new Set(parsed)] : undefined;
|
|
66
|
-
}
|
|
67
|
-
async resolveAmbientPriors(userText) {
|
|
52
|
+
async resolveAmbientPriors(userText, currentGoal, activePolicy, currentTurnCorrection) {
|
|
68
53
|
const ambient = this.opts.ambient;
|
|
69
54
|
return resolveAmbientPriorsForTurn(userText, {
|
|
70
55
|
enabled: Boolean(ambient),
|
|
56
|
+
currentGoal,
|
|
57
|
+
activePolicy,
|
|
58
|
+
currentTurnCorrection,
|
|
71
59
|
thronglets: ambient
|
|
72
60
|
? {
|
|
73
61
|
...(ambient === true ? {} : ambient),
|
|
@@ -83,11 +71,17 @@ export class PsycheLangChain {
|
|
|
83
71
|
* Call this BEFORE the LLM invocation.
|
|
84
72
|
*/
|
|
85
73
|
async getSystemMessage(userText, opts) {
|
|
86
|
-
const
|
|
74
|
+
const currentTurnCorrection = normalizeCurrentTurnCorrection(opts?.currentTurnCorrection);
|
|
75
|
+
const currentGoal = normalizeCurrentGoal(opts?.currentGoal);
|
|
76
|
+
const activePolicy = resolveRuntimeActivePolicy(opts?.activePolicy, currentTurnCorrection);
|
|
77
|
+
const result = await safeProcessInput(this.engine, userText, {
|
|
87
78
|
...opts,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
79
|
+
currentGoal,
|
|
80
|
+
activePolicy,
|
|
81
|
+
currentTurnCorrection,
|
|
82
|
+
ambientPriors: await this.resolveAmbientPriors(userText, currentGoal, activePolicy, currentTurnCorrection),
|
|
83
|
+
}, "langchain.processInput");
|
|
84
|
+
return composePsycheContext(result);
|
|
91
85
|
}
|
|
92
86
|
/**
|
|
93
87
|
* Prepare both prompt text and mechanical invocation hints for a LangChain call.
|
|
@@ -96,10 +90,16 @@ export class PsycheLangChain {
|
|
|
96
90
|
* instead of re-parsing prompt prose.
|
|
97
91
|
*/
|
|
98
92
|
async prepareInvocation(userText, opts) {
|
|
99
|
-
const
|
|
93
|
+
const currentTurnCorrection = normalizeCurrentTurnCorrection(opts?.currentTurnCorrection);
|
|
94
|
+
const currentGoal = normalizeCurrentGoal(opts?.currentGoal);
|
|
95
|
+
const activePolicy = resolveRuntimeActivePolicy(opts?.activePolicy, currentTurnCorrection);
|
|
96
|
+
const result = await safeProcessInput(this.engine, userText, {
|
|
100
97
|
...opts,
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
currentGoal,
|
|
99
|
+
activePolicy,
|
|
100
|
+
currentTurnCorrection,
|
|
101
|
+
ambientPriors: await this.resolveAmbientPriors(userText, currentGoal, activePolicy, currentTurnCorrection),
|
|
102
|
+
}, "langchain.processInput");
|
|
103
103
|
const generationControls = result.replyEnvelope?.generationControls ?? result.generationControls;
|
|
104
104
|
const controls = {
|
|
105
105
|
...(generationControls ?? {}),
|
|
@@ -108,7 +108,7 @@ export class PsycheLangChain {
|
|
|
108
108
|
: generationControls?.maxTokens ?? opts?.maxTokens,
|
|
109
109
|
};
|
|
110
110
|
return {
|
|
111
|
-
systemMessage: result
|
|
111
|
+
systemMessage: composePsycheContext(result),
|
|
112
112
|
maxTokens: controls.maxTokens,
|
|
113
113
|
requireConfirmation: controls.requireConfirmation ?? false,
|
|
114
114
|
};
|
|
@@ -120,11 +120,11 @@ export class PsycheLangChain {
|
|
|
120
120
|
* Call this AFTER the LLM invocation, before showing output to the user.
|
|
121
121
|
*/
|
|
122
122
|
async processResponse(text, opts) {
|
|
123
|
-
const result = await this.engine
|
|
123
|
+
const result = await safeProcessOutput(this.engine, text, {
|
|
124
124
|
userId: opts?.userId,
|
|
125
|
-
signals:
|
|
125
|
+
signals: coerceWritebackSignalInput(opts?.signals),
|
|
126
126
|
signalConfidence: opts?.signalConfidence,
|
|
127
|
-
});
|
|
127
|
+
}, "langchain.processOutput");
|
|
128
128
|
return result.cleanedText;
|
|
129
129
|
}
|
|
130
130
|
}
|
package/dist/adapters/mcp.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface McpAmbientRuntimeOptions {
|
|
|
8
8
|
fetcher?: typeof fetchAmbientPriorsFromThronglets;
|
|
9
9
|
}
|
|
10
10
|
declare function getEngine(): Promise<PsycheEngine>;
|
|
11
|
-
export declare function resolveRuntimeAmbientPriors(text: string, explicit?: AmbientPriorView[], currentGoal?: CurrentGoal, activePolicy?: ActivePolicyRule[], opts?: McpAmbientRuntimeOptions): Promise<AmbientPriorView[] | undefined>;
|
|
11
|
+
export declare function resolveRuntimeAmbientPriors(text: string, explicit?: AmbientPriorView[], currentGoal?: CurrentGoal, activePolicy?: ActivePolicyRule[], currentTurnCorrectionOrOpts?: string | McpAmbientRuntimeOptions, opts?: McpAmbientRuntimeOptions): Promise<AmbientPriorView[] | undefined>;
|
|
12
12
|
declare const server: McpServer;
|
|
13
13
|
export declare function runMcpServer(): Promise<void>;
|
|
14
14
|
export { server, getEngine };
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -32,9 +32,10 @@ import { z } from "zod";
|
|
|
32
32
|
import { PsycheEngine } from "../core.js";
|
|
33
33
|
import { fetchAmbientPriorsFromThronglets, resolveAmbientPriorsForTurn, } from "../ambient-runtime.js";
|
|
34
34
|
import { MemoryStorageAdapter, FileStorageAdapter, resolveWorkspaceDir } from "../storage.js";
|
|
35
|
-
import { CURRENT_GOALS } from "../types.js";
|
|
35
|
+
import { CURRENT_GOALS, normalizeCurrentTurnCorrection, resolveRuntimeActivePolicy, } from "../types.js";
|
|
36
36
|
import { getPackageVersion } from "../update.js";
|
|
37
37
|
import { runDemo } from "../demo.js";
|
|
38
|
+
import { safeProcessInput, safeProcessOutput } from "./fail-open.js";
|
|
38
39
|
const PACKAGE_VERSION = await getPackageVersion();
|
|
39
40
|
// ── Config from env ────────────────────────────────────────
|
|
40
41
|
const MBTI = (process.env.PSYCHE_MBTI ?? "ENFP");
|
|
@@ -126,19 +127,32 @@ async function getEngine() {
|
|
|
126
127
|
await engine.initialize();
|
|
127
128
|
return engine;
|
|
128
129
|
}
|
|
129
|
-
export async function resolveRuntimeAmbientPriors(text, explicit, currentGoal, activePolicy, opts = DEFAULT_MCP_AMBIENT_OPTIONS) {
|
|
130
|
-
const
|
|
130
|
+
export async function resolveRuntimeAmbientPriors(text, explicit, currentGoal, activePolicy, currentTurnCorrectionOrOpts, opts = DEFAULT_MCP_AMBIENT_OPTIONS) {
|
|
131
|
+
const currentTurnCorrection = typeof currentTurnCorrectionOrOpts === "string"
|
|
132
|
+
? currentTurnCorrectionOrOpts
|
|
133
|
+
: undefined;
|
|
134
|
+
const resolvedOpts = (typeof currentTurnCorrectionOrOpts === "object"
|
|
135
|
+
&& currentTurnCorrectionOrOpts !== null)
|
|
136
|
+
? currentTurnCorrectionOrOpts
|
|
137
|
+
: opts;
|
|
138
|
+
const normalizedCorrection = normalizeCurrentTurnCorrection(currentTurnCorrection);
|
|
139
|
+
const resolvedActivePolicy = resolveRuntimeActivePolicy(activePolicy, normalizedCorrection);
|
|
140
|
+
const throngletsOptions = resolvedOpts.thronglets || currentGoal || resolvedActivePolicy?.length || normalizedCorrection
|
|
131
141
|
? {
|
|
132
|
-
...(
|
|
133
|
-
goal: currentGoal ??
|
|
134
|
-
activePolicy:
|
|
142
|
+
...(resolvedOpts.thronglets ?? {}),
|
|
143
|
+
goal: currentGoal ?? resolvedOpts.thronglets?.goal,
|
|
144
|
+
activePolicy: resolvedActivePolicy ?? resolvedOpts.thronglets?.activePolicy,
|
|
145
|
+
currentTurnCorrection: normalizedCorrection ?? resolvedOpts.thronglets?.currentTurnCorrection,
|
|
135
146
|
}
|
|
136
147
|
: undefined;
|
|
137
148
|
return resolveAmbientPriorsForTurn(text, {
|
|
138
149
|
explicit,
|
|
139
|
-
enabled:
|
|
150
|
+
enabled: resolvedOpts.mode !== "off",
|
|
151
|
+
currentGoal,
|
|
152
|
+
currentTurnCorrection: normalizedCorrection,
|
|
153
|
+
activePolicy: resolvedActivePolicy,
|
|
140
154
|
thronglets: throngletsOptions,
|
|
141
|
-
fetcher:
|
|
155
|
+
fetcher: resolvedOpts.fetcher ?? fetchAmbientPriorsFromThronglets,
|
|
142
156
|
});
|
|
143
157
|
}
|
|
144
158
|
// ── MCP Server ─────────────────────────────────────────────
|
|
@@ -212,15 +226,18 @@ server.tool("process_input", "Process user input through the emotional engine. R
|
|
|
212
226
|
scope: z.enum(["task", "project"]),
|
|
213
227
|
summary: z.string(),
|
|
214
228
|
})).optional().describe("Optional explicit current-turn method policy view. Runtime-only; not persisted as self-state."),
|
|
215
|
-
|
|
229
|
+
currentTurnCorrection: z.string().optional().describe("Optional explicit current-turn correction. Compiles into a task-scoped hard policy for this turn only."),
|
|
230
|
+
}, async ({ text, userId, ambientPriors, currentGoal, activePolicy, currentTurnCorrection }) => {
|
|
216
231
|
const eng = await getEngine();
|
|
217
|
-
const
|
|
218
|
-
const
|
|
232
|
+
const resolvedActivePolicy = resolveRuntimeActivePolicy(activePolicy, currentTurnCorrection);
|
|
233
|
+
const resolvedAmbientPriors = await resolveRuntimeAmbientPriors(text, ambientPriors, currentGoal, resolvedActivePolicy, currentTurnCorrection);
|
|
234
|
+
const result = await safeProcessInput(eng, text, {
|
|
219
235
|
userId,
|
|
220
236
|
ambientPriors: resolvedAmbientPriors,
|
|
221
237
|
currentGoal,
|
|
222
|
-
activePolicy,
|
|
223
|
-
|
|
238
|
+
activePolicy: resolvedActivePolicy,
|
|
239
|
+
currentTurnCorrection,
|
|
240
|
+
}, "mcp.processInput");
|
|
224
241
|
return {
|
|
225
242
|
content: [{
|
|
226
243
|
type: "text",
|
|
@@ -257,13 +274,14 @@ server.tool("process_output", "Process the LLM's response through the emotional
|
|
|
257
274
|
signalConfidence: z.number().min(0).max(1).optional().describe("Optional confidence for the supplied signals"),
|
|
258
275
|
}, async ({ text, userId, signals, signalConfidence }) => {
|
|
259
276
|
const eng = await getEngine();
|
|
260
|
-
const result = await eng
|
|
277
|
+
const result = await safeProcessOutput(eng, text, { userId, signals: signals, signalConfidence }, "mcp.processOutput");
|
|
261
278
|
return {
|
|
262
279
|
content: [{
|
|
263
280
|
type: "text",
|
|
264
281
|
text: JSON.stringify({
|
|
265
282
|
cleanedText: result.cleanedText,
|
|
266
283
|
stateChanged: result.stateChanged,
|
|
284
|
+
validationIssues: result.validationIssues ?? [],
|
|
267
285
|
}, null, 2),
|
|
268
286
|
}],
|
|
269
287
|
};
|
|
@@ -54,7 +54,7 @@ export declare function psycheMiddleware(engine: PsycheEngine, _opts?: PsycheMid
|
|
|
54
54
|
type: string;
|
|
55
55
|
params: CallParams;
|
|
56
56
|
}) => Promise<{
|
|
57
|
-
system: string;
|
|
57
|
+
system: string | undefined;
|
|
58
58
|
maxTokens?: number | undefined;
|
|
59
59
|
prompt?: PromptMessage[];
|
|
60
60
|
}>;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// - wrapGenerate: process output, strip <psyche_update> tags
|
|
16
16
|
// - wrapStream: buffer stream, detect & strip tags at end
|
|
17
17
|
// ============================================================
|
|
18
|
+
import { composePsycheContext, safeProcessInput, safeProcessOutput } from "./fail-open.js";
|
|
18
19
|
/**
|
|
19
20
|
* Create Vercel AI SDK middleware that injects psyche emotional context
|
|
20
21
|
* and processes LLM output for state updates.
|
|
@@ -48,7 +49,7 @@ export function psycheMiddleware(engine, _opts) {
|
|
|
48
49
|
return {
|
|
49
50
|
transformParams: async ({ params }) => {
|
|
50
51
|
const userText = extractLastUserText(params.prompt ?? []);
|
|
51
|
-
const result = await engine.processInput
|
|
52
|
+
const result = await safeProcessInput(engine, userText, undefined, "vercel-ai.processInput");
|
|
52
53
|
const envelope = result.replyEnvelope;
|
|
53
54
|
const generationControls = envelope?.generationControls ?? result.generationControls;
|
|
54
55
|
const controls = {
|
|
@@ -57,19 +58,19 @@ export function psycheMiddleware(engine, _opts) {
|
|
|
57
58
|
? Math.min(params.maxTokens, generationControls.maxTokens)
|
|
58
59
|
: generationControls?.maxTokens ?? (typeof params.maxTokens === "number" ? params.maxTokens : undefined),
|
|
59
60
|
};
|
|
60
|
-
const psycheContext = result
|
|
61
|
+
const psycheContext = composePsycheContext(result);
|
|
61
62
|
return {
|
|
62
63
|
...params,
|
|
63
64
|
...(controls.maxTokens !== undefined ? { maxTokens: controls.maxTokens } : {}),
|
|
64
|
-
system:
|
|
65
|
-
? psycheContext + "\n\n" + params.system
|
|
66
|
-
:
|
|
65
|
+
system: psycheContext
|
|
66
|
+
? (params.system ? psycheContext + "\n\n" + params.system : psycheContext)
|
|
67
|
+
: params.system,
|
|
67
68
|
};
|
|
68
69
|
},
|
|
69
70
|
wrapGenerate: async ({ doGenerate }) => {
|
|
70
71
|
const result = await doGenerate();
|
|
71
72
|
if (typeof result.text === "string") {
|
|
72
|
-
const processed = await engine
|
|
73
|
+
const processed = await safeProcessOutput(engine, result.text, undefined, "vercel-ai.processOutput");
|
|
73
74
|
return { ...result, text: processed.cleanedText };
|
|
74
75
|
}
|
|
75
76
|
return result;
|
|
@@ -134,7 +135,7 @@ export function psycheMiddleware(engine, _opts) {
|
|
|
134
135
|
else if (chunk.type === "finish") {
|
|
135
136
|
// Process full text through engine before finishing
|
|
136
137
|
if (fullText) {
|
|
137
|
-
await engine.processOutput
|
|
138
|
+
await safeProcessOutput(engine, fullText, undefined, "vercel-ai.processOutput");
|
|
138
139
|
}
|
|
139
140
|
yield chunk;
|
|
140
141
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ActivePolicyRule, type AmbientPriorView, type CurrentGoal } from "./types.js";
|
|
2
2
|
interface CommandResult {
|
|
3
3
|
ok: boolean;
|
|
4
4
|
stdout: string;
|
|
@@ -11,6 +11,7 @@ export interface ThrongletsAmbientRuntimeOptions {
|
|
|
11
11
|
space?: string;
|
|
12
12
|
goal?: CurrentGoal;
|
|
13
13
|
activePolicy?: ActivePolicyRule[];
|
|
14
|
+
currentTurnCorrection?: string;
|
|
14
15
|
limit?: number;
|
|
15
16
|
timeoutMs?: number;
|
|
16
17
|
runner?: CommandRunner;
|
|
@@ -18,7 +19,9 @@ export interface ThrongletsAmbientRuntimeOptions {
|
|
|
18
19
|
export interface AmbientPriorResolutionOptions {
|
|
19
20
|
explicit?: readonly AmbientPriorView[] | unknown;
|
|
20
21
|
enabled?: boolean;
|
|
22
|
+
currentGoal?: CurrentGoal;
|
|
21
23
|
activePolicy?: ActivePolicyRule[];
|
|
24
|
+
currentTurnCorrection?: string;
|
|
22
25
|
thronglets?: ThrongletsAmbientRuntimeOptions;
|
|
23
26
|
fetcher?: typeof fetchAmbientPriorsFromThronglets;
|
|
24
27
|
}
|
package/dist/ambient-runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { normalizeCurrentTurnCorrection, resolveRuntimeActivePolicy, } from "./types.js";
|
|
2
3
|
import { normalizeAmbientPriors } from "./ambient-priors.js";
|
|
3
4
|
const DEFAULT_AMBIENT_LIMIT = 3;
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 800;
|
|
@@ -64,6 +65,8 @@ export async function fetchAmbientPriorsFromThronglets(text, opts = {}) {
|
|
|
64
65
|
const limit = Math.max(1, Math.min(5, opts.limit ?? DEFAULT_AMBIENT_LIMIT));
|
|
65
66
|
const timeoutMs = Math.max(100, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
66
67
|
const runner = opts.runner ?? defaultRunner;
|
|
68
|
+
const currentTurnCorrection = normalizeCurrentTurnCorrection(opts.currentTurnCorrection);
|
|
69
|
+
const activePolicy = resolveRuntimeActivePolicy(opts.activePolicy, currentTurnCorrection) ?? [];
|
|
67
70
|
const args = [];
|
|
68
71
|
if (dataDir && dataDir.trim()) {
|
|
69
72
|
args.push("--data-dir", dataDir.trim());
|
|
@@ -74,7 +77,8 @@ export async function fetchAmbientPriorsFromThronglets(text, opts = {}) {
|
|
|
74
77
|
space,
|
|
75
78
|
goal,
|
|
76
79
|
limit,
|
|
77
|
-
active_policy:
|
|
80
|
+
active_policy: activePolicy,
|
|
81
|
+
current_turn_correction: currentTurnCorrection,
|
|
78
82
|
});
|
|
79
83
|
try {
|
|
80
84
|
const result = await runner(binaryPath, args, payload, timeoutMs);
|
|
@@ -99,7 +103,8 @@ export async function resolveAmbientPriorsForTurn(text, opts = {}) {
|
|
|
99
103
|
const fetcher = opts.fetcher ?? fetchAmbientPriorsFromThronglets;
|
|
100
104
|
const priors = await fetcher(text, {
|
|
101
105
|
...(opts.thronglets ?? {}),
|
|
102
|
-
activePolicy: opts.activePolicy ?? opts.thronglets?.activePolicy,
|
|
106
|
+
activePolicy: resolveRuntimeActivePolicy(opts.activePolicy ?? opts.thronglets?.activePolicy, opts.currentTurnCorrection ?? opts.thronglets?.currentTurnCorrection),
|
|
107
|
+
currentTurnCorrection: normalizeCurrentTurnCorrection(opts.currentTurnCorrection ?? opts.thronglets?.currentTurnCorrection),
|
|
103
108
|
});
|
|
104
109
|
return priors.length > 0 ? priors : undefined;
|
|
105
110
|
}
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActivePolicyRule, AmbientPriorView, CurrentGoal, PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, TurnObservability, WritebackCalibrationFeedback,
|
|
1
|
+
import type { ActivePolicyRule, AmbientPriorView, CurrentGoal, PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, TurnObservability, WritebackCalibrationFeedback, ExternalContinuityEnvelope, AppraisalAxes } from "./types.js";
|
|
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";
|
|
@@ -95,18 +95,27 @@ export interface ProcessInputOptions {
|
|
|
95
95
|
ambientPriors?: AmbientPriorView[];
|
|
96
96
|
currentGoal?: CurrentGoal;
|
|
97
97
|
activePolicy?: ActivePolicyRule[];
|
|
98
|
+
currentTurnCorrection?: string;
|
|
98
99
|
}
|
|
99
100
|
export interface ProcessOutputResult {
|
|
100
101
|
/** LLM output with <psyche_update> tags stripped */
|
|
101
102
|
cleanedText: string;
|
|
102
103
|
/** Whether self-state was meaningfully updated (contagion or psyche_update) */
|
|
103
104
|
stateChanged: boolean;
|
|
105
|
+
/** Runtime validation issues ignored to preserve the main flow */
|
|
106
|
+
validationIssues?: ProcessOutputValidationIssue[];
|
|
104
107
|
}
|
|
105
108
|
export interface ProcessOutputOptions {
|
|
106
109
|
userId?: string;
|
|
107
|
-
signals?:
|
|
110
|
+
signals?: readonly string[];
|
|
108
111
|
signalConfidence?: number;
|
|
109
112
|
}
|
|
113
|
+
export interface ProcessOutputValidationIssue {
|
|
114
|
+
code: "invalid-writeback-signals";
|
|
115
|
+
level: "warning";
|
|
116
|
+
message: string;
|
|
117
|
+
ignoredSignals: string[];
|
|
118
|
+
}
|
|
110
119
|
export interface ProcessOutcomeResult {
|
|
111
120
|
/** Outcome evaluation score (-1 to 1) */
|
|
112
121
|
outcomeScore: OutcomeScore;
|
package/dist/core.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
//
|
|
12
12
|
// Orchestrates: self-state, appraisal, prompt, profiles, guards, learning
|
|
13
13
|
// ============================================================
|
|
14
|
-
import {
|
|
14
|
+
import { normalizeCurrentGoal, resolveRuntimeActivePolicy } from "./types.js";
|
|
15
15
|
import { DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, DEFAULT_ENERGY_BUDGETS, DEFAULT_TRAIT_DRIFT, DEFAULT_SUBJECT_RESIDUE, DEFAULT_DYADIC_FIELD } from "./types.js";
|
|
16
16
|
import { MemoryStorageAdapter } from "./storage.js";
|
|
17
17
|
import { applyDecay, applyStimulus, applyContagion, clamp, describeEmotionalState } from "./chemistry.js";
|
|
@@ -34,6 +34,7 @@ import { deriveThrongletsExports } from "./thronglets-export.js";
|
|
|
34
34
|
import { buildTurnObservability } from "./observability.js";
|
|
35
35
|
import { DEFAULT_RELATIONSHIP_USER_ID, resolveRelationshipUserId } from "./relationship-key.js";
|
|
36
36
|
import { normalizeAmbientPriors } from "./ambient-priors.js";
|
|
37
|
+
import { normalizeWritebackSignals } from "./writeback-signals.js";
|
|
37
38
|
function formatWritebackFeedbackNote(feedback, locale) {
|
|
38
39
|
const top = feedback?.[0];
|
|
39
40
|
if (!top)
|
|
@@ -461,7 +462,7 @@ export class PsycheEngine {
|
|
|
461
462
|
}
|
|
462
463
|
const writebackNote = formatWritebackFeedbackNote(writebackFeedback, locale);
|
|
463
464
|
const ambientPriors = normalizeAmbientPriors(opts?.ambientPriors);
|
|
464
|
-
const activePolicy =
|
|
465
|
+
const activePolicy = resolveRuntimeActivePolicy(opts?.activePolicy, opts?.currentTurnCorrection) ?? [];
|
|
465
466
|
const currentGoal = normalizeCurrentGoal(opts?.currentGoal)
|
|
466
467
|
?? ambientPriors.find((prior) => prior.goal)?.goal;
|
|
467
468
|
const ambientPriorContext = buildAmbientPriorContext(ambientPriors, locale);
|
|
@@ -564,6 +565,7 @@ export class PsycheEngine {
|
|
|
564
565
|
async processOutput(text, opts) {
|
|
565
566
|
let state = this.ensureInitialized();
|
|
566
567
|
let stateChanged = false;
|
|
568
|
+
const validationIssues = [];
|
|
567
569
|
// Emotional contagion from empathy log
|
|
568
570
|
if (state.empathyLog?.userState && this.cfg.emotionalContagionRate > 0) {
|
|
569
571
|
const userEmotion = state.empathyLog.userState.toLowerCase();
|
|
@@ -615,6 +617,18 @@ export class PsycheEngine {
|
|
|
615
617
|
// Parse and merge <psyche_update> from LLM output
|
|
616
618
|
let combinedSignals = [];
|
|
617
619
|
let combinedSignalConfidence = opts?.signalConfidence;
|
|
620
|
+
const recordInvalidSignals = (source, invalidSignals) => {
|
|
621
|
+
if (invalidSignals.length === 0)
|
|
622
|
+
return;
|
|
623
|
+
const issue = {
|
|
624
|
+
code: "invalid-writeback-signals",
|
|
625
|
+
level: "warning",
|
|
626
|
+
message: `Ignored unsupported writeback signals from ${source}`,
|
|
627
|
+
ignoredSignals: invalidSignals,
|
|
628
|
+
};
|
|
629
|
+
validationIssues.push(issue);
|
|
630
|
+
this.recordDiagnosticError("processOutput", new Error(`${issue.message}: ${invalidSignals.join(", ")}`));
|
|
631
|
+
};
|
|
618
632
|
if (text.includes("<psyche_update>")) {
|
|
619
633
|
const parseResult = parsePsycheUpdate(text, NOOP_LOGGER);
|
|
620
634
|
if (parseResult) {
|
|
@@ -645,10 +659,13 @@ export class PsycheEngine {
|
|
|
645
659
|
combinedSignals.push(...parseResult.signals);
|
|
646
660
|
combinedSignalConfidence = Math.max(combinedSignalConfidence ?? 0, parseResult.signalConfidence ?? 0);
|
|
647
661
|
}
|
|
662
|
+
recordInvalidSignals("llm", parseResult.invalidSignals ?? []);
|
|
648
663
|
}
|
|
649
664
|
}
|
|
650
665
|
if (opts?.signals && opts.signals.length > 0) {
|
|
651
|
-
|
|
666
|
+
const normalizedSignals = normalizeWritebackSignals(opts.signals);
|
|
667
|
+
combinedSignals.push(...normalizedSignals.validSignals);
|
|
668
|
+
recordInvalidSignals("host", normalizedSignals.invalidSignals);
|
|
652
669
|
combinedSignalConfidence = Math.max(combinedSignalConfidence ?? 0, opts.signalConfidence ?? 0);
|
|
653
670
|
}
|
|
654
671
|
if (combinedSignals.length > 0) {
|
|
@@ -681,7 +698,11 @@ export class PsycheEngine {
|
|
|
681
698
|
.replace(/\n{3,}/g, "\n\n")
|
|
682
699
|
.trim();
|
|
683
700
|
}
|
|
684
|
-
return {
|
|
701
|
+
return {
|
|
702
|
+
cleanedText,
|
|
703
|
+
stateChanged,
|
|
704
|
+
...(validationIssues.length > 0 ? { validationIssues } : {}),
|
|
705
|
+
};
|
|
685
706
|
}
|
|
686
707
|
/**
|
|
687
708
|
* Phase 3 (optional): Explicitly evaluate the outcome of the last interaction.
|
package/dist/prompt.js
CHANGED
|
@@ -1094,6 +1094,10 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
1094
1094
|
if (unified)
|
|
1095
1095
|
parts.push(unified);
|
|
1096
1096
|
}
|
|
1097
|
+
const activePolicyContext = buildActivePolicyContext(opts?.activePolicy, locale);
|
|
1098
|
+
if (activePolicyContext) {
|
|
1099
|
+
parts.push(activePolicyContext);
|
|
1100
|
+
}
|
|
1097
1101
|
// ── 9. Overlay + channel + writeback ──
|
|
1098
1102
|
appendCompactOverlaySections(parts, locale, opts);
|
|
1099
1103
|
if (opts?.channelType) {
|
package/dist/psyche-file.d.ts
CHANGED
|
@@ -97,6 +97,8 @@ export interface PsycheUpdateResult {
|
|
|
97
97
|
llmStimulus?: StimulusType;
|
|
98
98
|
/** Sparse agent-authored writeback signals */
|
|
99
99
|
signals?: WritebackSignalType[];
|
|
100
|
+
/** Unsupported writeback signals that were ignored */
|
|
101
|
+
invalidSignals?: string[];
|
|
100
102
|
/** Optional writeback confidence */
|
|
101
103
|
signalConfidence?: number;
|
|
102
104
|
}
|
package/dist/psyche-file.js
CHANGED
|
@@ -12,6 +12,7 @@ import { t } from "./i18n.js";
|
|
|
12
12
|
import { computeSelfReflection } from "./self-recognition.js";
|
|
13
13
|
import { DEFAULT_RELATIONSHIP_USER_ID, resolveRelationshipUserId } from "./relationship-key.js";
|
|
14
14
|
import { describeSnapshotResidue, summarizeSnapshotMarkers } from "./appraisal-markers.js";
|
|
15
|
+
import { normalizeWritebackSignals } from "./writeback-signals.js";
|
|
15
16
|
const STATE_FILE = "psyche-state.json";
|
|
16
17
|
const PSYCHE_MD = "PSYCHE.md";
|
|
17
18
|
const IDENTITY_MD = "IDENTITY.md";
|
|
@@ -792,28 +793,20 @@ export function parsePsycheUpdate(text, logger = NOOP_LOGGER) {
|
|
|
792
793
|
llmStimulus = candidate;
|
|
793
794
|
}
|
|
794
795
|
}
|
|
795
|
-
const VALID_WRITEBACK_SIGNALS = new Set([
|
|
796
|
-
"trust_up",
|
|
797
|
-
"trust_down",
|
|
798
|
-
"boundary_set",
|
|
799
|
-
"boundary_soften",
|
|
800
|
-
"repair_attempt",
|
|
801
|
-
"repair_landed",
|
|
802
|
-
"closeness_invite",
|
|
803
|
-
"withdrawal_mark",
|
|
804
|
-
"self_assertion",
|
|
805
|
-
"task_recenter",
|
|
806
|
-
]);
|
|
807
796
|
let signals;
|
|
797
|
+
let invalidSignals;
|
|
808
798
|
const signalsMatch = block.match(/signals\s*[::]\s*([^\n]+)/i);
|
|
809
799
|
if (signalsMatch) {
|
|
810
800
|
const parsed = signalsMatch[1]
|
|
811
801
|
.split(/[,\s|]+/)
|
|
812
802
|
.map((item) => item.trim())
|
|
813
|
-
.filter(Boolean)
|
|
814
|
-
|
|
815
|
-
if (
|
|
816
|
-
signals =
|
|
803
|
+
.filter(Boolean);
|
|
804
|
+
const normalized = normalizeWritebackSignals(parsed);
|
|
805
|
+
if (normalized.validSignals.length > 0) {
|
|
806
|
+
signals = normalized.validSignals;
|
|
807
|
+
}
|
|
808
|
+
if (normalized.invalidSignals.length > 0) {
|
|
809
|
+
invalidSignals = normalized.invalidSignals;
|
|
817
810
|
}
|
|
818
811
|
}
|
|
819
812
|
let signalConfidence;
|
|
@@ -858,6 +851,9 @@ export function parsePsycheUpdate(text, logger = NOOP_LOGGER) {
|
|
|
858
851
|
if (signals) {
|
|
859
852
|
result.signals = signals;
|
|
860
853
|
}
|
|
854
|
+
if (invalidSignals) {
|
|
855
|
+
result.invalidSignals = invalidSignals;
|
|
856
|
+
}
|
|
861
857
|
if (signalConfidence !== undefined) {
|
|
862
858
|
result.signalConfidence = signalConfidence;
|
|
863
859
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -593,8 +593,10 @@ export interface ActivePolicyRule {
|
|
|
593
593
|
scope: ActivePolicyScope;
|
|
594
594
|
summary: string;
|
|
595
595
|
}
|
|
596
|
+
export declare function normalizeCurrentTurnCorrection(value: unknown): string | undefined;
|
|
596
597
|
export declare function normalizeCurrentGoal(value: unknown): CurrentGoal | undefined;
|
|
597
598
|
export declare function normalizeActivePolicyRules(value: unknown, limit?: number): ActivePolicyRule[] | undefined;
|
|
599
|
+
export declare function resolveRuntimeActivePolicy(activePolicy: unknown, currentTurnCorrection?: unknown, limit?: number): ActivePolicyRule[] | undefined;
|
|
598
600
|
export interface AmbientPriorView {
|
|
599
601
|
summary: string;
|
|
600
602
|
confidence: number;
|
package/dist/types.js
CHANGED
|
@@ -224,6 +224,12 @@ export const AMBIENT_POLICY_STATES = [
|
|
|
224
224
|
"method-conflict",
|
|
225
225
|
"stable-path",
|
|
226
226
|
];
|
|
227
|
+
export function normalizeCurrentTurnCorrection(value) {
|
|
228
|
+
if (typeof value !== "string")
|
|
229
|
+
return undefined;
|
|
230
|
+
const summary = value.trim().replace(/\s+/g, " ");
|
|
231
|
+
return summary.length > 0 ? summary : undefined;
|
|
232
|
+
}
|
|
227
233
|
export function normalizeCurrentGoal(value) {
|
|
228
234
|
return typeof value === "string" && CURRENT_GOALS.includes(value)
|
|
229
235
|
? value
|
|
@@ -251,6 +257,24 @@ export function normalizeActivePolicyRules(value, limit = 3) {
|
|
|
251
257
|
.slice(0, Math.max(1, limit));
|
|
252
258
|
return normalized.length > 0 ? normalized : undefined;
|
|
253
259
|
}
|
|
260
|
+
export function resolveRuntimeActivePolicy(activePolicy, currentTurnCorrection, limit = 3) {
|
|
261
|
+
const explicit = normalizeActivePolicyRules(activePolicy, limit) ?? [];
|
|
262
|
+
const correction = normalizeCurrentTurnCorrection(currentTurnCorrection);
|
|
263
|
+
if (!correction)
|
|
264
|
+
return explicit.length > 0 ? explicit : undefined;
|
|
265
|
+
const merged = [{
|
|
266
|
+
id: "task:current-turn-correction",
|
|
267
|
+
strength: "hard",
|
|
268
|
+
scope: "task",
|
|
269
|
+
summary: correction,
|
|
270
|
+
}];
|
|
271
|
+
for (const rule of explicit) {
|
|
272
|
+
if (rule.summary === correction)
|
|
273
|
+
continue;
|
|
274
|
+
merged.push(rule);
|
|
275
|
+
}
|
|
276
|
+
return merged.slice(0, Math.max(1, limit));
|
|
277
|
+
}
|
|
254
278
|
/** Default empty trait drift state */
|
|
255
279
|
export const DEFAULT_TRAIT_DRIFT = {
|
|
256
280
|
accumulators: {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WritebackSignalType } from "./types.js";
|
|
2
|
+
export declare const WRITEBACK_SIGNAL_VALUES: readonly ["trust_up", "trust_down", "boundary_set", "boundary_soften", "repair_attempt", "repair_landed", "closeness_invite", "withdrawal_mark", "self_assertion", "task_recenter"];
|
|
3
|
+
export interface NormalizedWritebackSignals {
|
|
4
|
+
validSignals: WritebackSignalType[];
|
|
5
|
+
invalidSignals: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function coerceWritebackSignalInput(value: unknown): string[] | undefined;
|
|
8
|
+
export declare function normalizeWritebackSignals(value: unknown): NormalizedWritebackSignals;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const WRITEBACK_SIGNAL_VALUES = [
|
|
2
|
+
"trust_up",
|
|
3
|
+
"trust_down",
|
|
4
|
+
"boundary_set",
|
|
5
|
+
"boundary_soften",
|
|
6
|
+
"repair_attempt",
|
|
7
|
+
"repair_landed",
|
|
8
|
+
"closeness_invite",
|
|
9
|
+
"withdrawal_mark",
|
|
10
|
+
"self_assertion",
|
|
11
|
+
"task_recenter",
|
|
12
|
+
];
|
|
13
|
+
const WRITEBACK_SIGNAL_SET = new Set(WRITEBACK_SIGNAL_VALUES);
|
|
14
|
+
export function coerceWritebackSignalInput(value) {
|
|
15
|
+
if (!Array.isArray(value))
|
|
16
|
+
return undefined;
|
|
17
|
+
const signals = value
|
|
18
|
+
.filter((item) => typeof item === "string")
|
|
19
|
+
.map((item) => item.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
return signals.length > 0 ? [...new Set(signals)] : undefined;
|
|
22
|
+
}
|
|
23
|
+
export function normalizeWritebackSignals(value) {
|
|
24
|
+
const rawSignals = coerceWritebackSignalInput(value) ?? [];
|
|
25
|
+
const validSignals = [];
|
|
26
|
+
const invalidSignals = [];
|
|
27
|
+
for (const signal of rawSignals) {
|
|
28
|
+
if (WRITEBACK_SIGNAL_SET.has(signal)) {
|
|
29
|
+
validSignals.push(signal);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
invalidSignals.push(signal);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
validSignals,
|
|
37
|
+
invalidSignals,
|
|
38
|
+
};
|
|
39
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "psyche-ai",
|
|
3
3
|
"name": "Artificial Psyche",
|
|
4
4
|
"description": "AI-first subjectivity kernel for agents with continuous appraisal, relation dynamics, and adaptive reply loops",
|
|
5
|
-
"version": "11.5.
|
|
5
|
+
"version": "11.5.6",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "psyche-ai",
|
|
3
|
-
"version": "11.5.
|
|
3
|
+
"version": "11.5.6",
|
|
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",
|