psyche-ai 11.5.4 → 11.5.5

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 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,6 +28,7 @@
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";
@@ -107,9 +108,12 @@ export class PsycheClaudeSDK {
107
108
  ambient: opts?.ambient === true ? {} : (opts?.ambient || undefined),
108
109
  };
109
110
  }
110
- async resolveAmbientPriors(userMessage) {
111
+ async resolveAmbientPriors(userMessage, currentGoal, activePolicy, currentTurnCorrection) {
111
112
  return resolveAmbientPriorsForTurn(userMessage, {
112
113
  enabled: Boolean(this.opts.ambient),
114
+ currentGoal,
115
+ activePolicy,
116
+ currentTurnCorrection,
113
117
  thronglets: this.opts.ambient
114
118
  ? {
115
119
  ...this.opts.ambient,
@@ -166,10 +170,21 @@ export class PsycheClaudeSDK {
166
170
  sessionId: runtimeContext.sessionId ?? self.lastRuntimeContext.sessionId,
167
171
  };
168
172
  const userMessage = input.user_message ?? "";
169
- const ambientPriors = await self.resolveAmbientPriors(userMessage);
173
+ const currentTurnCorrection = normalizeCurrentTurnCorrection(input.current_turn_correction
174
+ ?? input.currentTurnCorrection
175
+ ?? input.task_correction
176
+ ?? input.taskCorrection
177
+ ?? input.explicit_instruction
178
+ ?? input.explicitInstruction);
179
+ const currentGoal = normalizeCurrentGoal(input.current_goal ?? input.currentGoal);
180
+ const activePolicy = resolveRuntimeActivePolicy(input.active_policy ?? input.activePolicy, currentTurnCorrection);
181
+ const ambientPriors = await self.resolveAmbientPriors(userMessage, currentGoal, activePolicy, currentTurnCorrection);
170
182
  const result = await self.engine.processInput(userMessage, {
171
183
  userId: self.opts.userId,
172
184
  ambientPriors,
185
+ currentGoal,
186
+ activePolicy,
187
+ currentTurnCorrection,
173
188
  });
174
189
  self.lastInputResult = result;
175
190
  // Cache Thronglets exports from this turn
@@ -16,7 +16,7 @@
16
16
  // Zero dependencies — uses node:http only.
17
17
  // ============================================================
18
18
  import { createServer } from "node:http";
19
- import { normalizeActivePolicyRules, normalizeCurrentGoal, } from "../types.js";
19
+ import { normalizeCurrentGoal, normalizeCurrentTurnCorrection, resolveRuntimeActivePolicy, } from "../types.js";
20
20
  import { parseAmbientPriorsInput } from "../ambient-runtime.js";
21
21
  import { computeOverlay } from "../overlay.js";
22
22
  const VALID_WRITEBACK_SIGNALS = new Set([
@@ -40,6 +40,14 @@ function parseSignals(value) {
40
40
  function parseAmbientPriors(value) {
41
41
  return parseAmbientPriorsInput(value);
42
42
  }
43
+ function parseCurrentTurnCorrection(body) {
44
+ return normalizeCurrentTurnCorrection(body.currentTurnCorrection
45
+ ?? body.current_turn_correction
46
+ ?? body.taskCorrection
47
+ ?? body.task_correction
48
+ ?? body.explicitInstruction
49
+ ?? body.explicit_instruction);
50
+ }
43
51
  // ── Server ───────────────────────────────────────────────────
44
52
  /**
45
53
  * Create an HTTP server that exposes PsycheEngine via REST API.
@@ -94,11 +102,13 @@ export function createPsycheServer(engine, opts) {
94
102
  // POST /process-input
95
103
  if (req.method === "POST" && url.pathname === "/process-input") {
96
104
  const body = await readBody(req);
105
+ const currentTurnCorrection = parseCurrentTurnCorrection(body);
97
106
  const result = await engine.processInput(body.text ?? "", {
98
107
  userId: body.userId,
99
108
  ambientPriors: parseAmbientPriors(body.ambientPriors),
100
109
  currentGoal: normalizeCurrentGoal(body.currentGoal),
101
- activePolicy: normalizeActivePolicyRules(body.activePolicy),
110
+ activePolicy: resolveRuntimeActivePolicy(body.activePolicy, currentTurnCorrection),
111
+ currentTurnCorrection,
102
112
  });
103
113
  json(res, 200, {
104
114
  systemContext: result.systemContext,
@@ -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;
@@ -44,6 +45,9 @@ export declare class PsycheLangChain {
44
45
  */
45
46
  getSystemMessage(userText: string, opts?: {
46
47
  userId?: string;
48
+ currentGoal?: CurrentGoal;
49
+ activePolicy?: ActivePolicyRule[];
50
+ currentTurnCorrection?: string;
47
51
  }): Promise<string>;
48
52
  /**
49
53
  * Prepare both prompt text and mechanical invocation hints for a LangChain call.
@@ -54,6 +58,9 @@ export declare class PsycheLangChain {
54
58
  prepareInvocation(userText: string, opts?: {
55
59
  userId?: string;
56
60
  maxTokens?: number;
61
+ currentGoal?: CurrentGoal;
62
+ activePolicy?: ActivePolicyRule[];
63
+ currentTurnCorrection?: string;
57
64
  }): Promise<{
58
65
  systemMessage: string;
59
66
  maxTokens?: number;
@@ -12,6 +12,7 @@
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";
16
17
  /**
17
18
  * LangChain integration helper for PsycheEngine.
@@ -64,10 +65,13 @@ export class PsycheLangChain {
64
65
  const parsed = signals.filter((signal) => this.validSignals.has(signal));
65
66
  return parsed.length > 0 ? [...new Set(parsed)] : undefined;
66
67
  }
67
- async resolveAmbientPriors(userText) {
68
+ async resolveAmbientPriors(userText, currentGoal, activePolicy, currentTurnCorrection) {
68
69
  const ambient = this.opts.ambient;
69
70
  return resolveAmbientPriorsForTurn(userText, {
70
71
  enabled: Boolean(ambient),
72
+ currentGoal,
73
+ activePolicy,
74
+ currentTurnCorrection,
71
75
  thronglets: ambient
72
76
  ? {
73
77
  ...(ambient === true ? {} : ambient),
@@ -83,9 +87,15 @@ export class PsycheLangChain {
83
87
  * Call this BEFORE the LLM invocation.
84
88
  */
85
89
  async getSystemMessage(userText, opts) {
90
+ const currentTurnCorrection = normalizeCurrentTurnCorrection(opts?.currentTurnCorrection);
91
+ const currentGoal = normalizeCurrentGoal(opts?.currentGoal);
92
+ const activePolicy = resolveRuntimeActivePolicy(opts?.activePolicy, currentTurnCorrection);
86
93
  const result = await this.engine.processInput(userText, {
87
94
  ...opts,
88
- ambientPriors: await this.resolveAmbientPriors(userText),
95
+ currentGoal,
96
+ activePolicy,
97
+ currentTurnCorrection,
98
+ ambientPriors: await this.resolveAmbientPriors(userText, currentGoal, activePolicy, currentTurnCorrection),
89
99
  });
90
100
  return result.systemContext + "\n\n" + result.dynamicContext;
91
101
  }
@@ -96,9 +106,15 @@ export class PsycheLangChain {
96
106
  * instead of re-parsing prompt prose.
97
107
  */
98
108
  async prepareInvocation(userText, opts) {
109
+ const currentTurnCorrection = normalizeCurrentTurnCorrection(opts?.currentTurnCorrection);
110
+ const currentGoal = normalizeCurrentGoal(opts?.currentGoal);
111
+ const activePolicy = resolveRuntimeActivePolicy(opts?.activePolicy, currentTurnCorrection);
99
112
  const result = await this.engine.processInput(userText, {
100
113
  ...opts,
101
- ambientPriors: await this.resolveAmbientPriors(userText),
114
+ currentGoal,
115
+ activePolicy,
116
+ currentTurnCorrection,
117
+ ambientPriors: await this.resolveAmbientPriors(userText, currentGoal, activePolicy, currentTurnCorrection),
102
118
  });
103
119
  const generationControls = result.replyEnvelope?.generationControls ?? result.generationControls;
104
120
  const controls = {
@@ -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 };
@@ -32,7 +32,7 @@ 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
38
  const PACKAGE_VERSION = await getPackageVersion();
@@ -126,19 +126,32 @@ async function getEngine() {
126
126
  await engine.initialize();
127
127
  return engine;
128
128
  }
129
- export async function resolveRuntimeAmbientPriors(text, explicit, currentGoal, activePolicy, opts = DEFAULT_MCP_AMBIENT_OPTIONS) {
130
- const throngletsOptions = opts.thronglets || currentGoal || activePolicy?.length
129
+ export async function resolveRuntimeAmbientPriors(text, explicit, currentGoal, activePolicy, currentTurnCorrectionOrOpts, opts = DEFAULT_MCP_AMBIENT_OPTIONS) {
130
+ const currentTurnCorrection = typeof currentTurnCorrectionOrOpts === "string"
131
+ ? currentTurnCorrectionOrOpts
132
+ : undefined;
133
+ const resolvedOpts = (typeof currentTurnCorrectionOrOpts === "object"
134
+ && currentTurnCorrectionOrOpts !== null)
135
+ ? currentTurnCorrectionOrOpts
136
+ : opts;
137
+ const normalizedCorrection = normalizeCurrentTurnCorrection(currentTurnCorrection);
138
+ const resolvedActivePolicy = resolveRuntimeActivePolicy(activePolicy, normalizedCorrection);
139
+ const throngletsOptions = resolvedOpts.thronglets || currentGoal || resolvedActivePolicy?.length || normalizedCorrection
131
140
  ? {
132
- ...(opts.thronglets ?? {}),
133
- goal: currentGoal ?? opts.thronglets?.goal,
134
- activePolicy: activePolicy ?? opts.thronglets?.activePolicy,
141
+ ...(resolvedOpts.thronglets ?? {}),
142
+ goal: currentGoal ?? resolvedOpts.thronglets?.goal,
143
+ activePolicy: resolvedActivePolicy ?? resolvedOpts.thronglets?.activePolicy,
144
+ currentTurnCorrection: normalizedCorrection ?? resolvedOpts.thronglets?.currentTurnCorrection,
135
145
  }
136
146
  : undefined;
137
147
  return resolveAmbientPriorsForTurn(text, {
138
148
  explicit,
139
- enabled: opts.mode !== "off",
149
+ enabled: resolvedOpts.mode !== "off",
150
+ currentGoal,
151
+ currentTurnCorrection: normalizedCorrection,
152
+ activePolicy: resolvedActivePolicy,
140
153
  thronglets: throngletsOptions,
141
- fetcher: opts.fetcher ?? fetchAmbientPriorsFromThronglets,
154
+ fetcher: resolvedOpts.fetcher ?? fetchAmbientPriorsFromThronglets,
142
155
  });
143
156
  }
144
157
  // ── MCP Server ─────────────────────────────────────────────
@@ -212,14 +225,17 @@ server.tool("process_input", "Process user input through the emotional engine. R
212
225
  scope: z.enum(["task", "project"]),
213
226
  summary: z.string(),
214
227
  })).optional().describe("Optional explicit current-turn method policy view. Runtime-only; not persisted as self-state."),
215
- }, async ({ text, userId, ambientPriors, currentGoal, activePolicy }) => {
228
+ currentTurnCorrection: z.string().optional().describe("Optional explicit current-turn correction. Compiles into a task-scoped hard policy for this turn only."),
229
+ }, async ({ text, userId, ambientPriors, currentGoal, activePolicy, currentTurnCorrection }) => {
216
230
  const eng = await getEngine();
217
- const resolvedAmbientPriors = await resolveRuntimeAmbientPriors(text, ambientPriors, currentGoal, activePolicy);
231
+ const resolvedActivePolicy = resolveRuntimeActivePolicy(activePolicy, currentTurnCorrection);
232
+ const resolvedAmbientPriors = await resolveRuntimeAmbientPriors(text, ambientPriors, currentGoal, resolvedActivePolicy, currentTurnCorrection);
218
233
  const result = await eng.processInput(text, {
219
234
  userId,
220
235
  ambientPriors: resolvedAmbientPriors,
221
236
  currentGoal,
222
- activePolicy,
237
+ activePolicy: resolvedActivePolicy,
238
+ currentTurnCorrection,
223
239
  });
224
240
  return {
225
241
  content: [{
File without changes
@@ -1,4 +1,4 @@
1
- import type { ActivePolicyRule, AmbientPriorView, CurrentGoal } from "./types.js";
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
  }
@@ -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: opts.activePolicy ?? [],
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/cli.js CHANGED
File without changes
package/dist/core.d.ts CHANGED
@@ -95,6 +95,7 @@ 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 */
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 { normalizeActivePolicyRules, normalizeCurrentGoal } from "./types.js";
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";
@@ -461,7 +461,7 @@ export class PsycheEngine {
461
461
  }
462
462
  const writebackNote = formatWritebackFeedbackNote(writebackFeedback, locale);
463
463
  const ambientPriors = normalizeAmbientPriors(opts?.ambientPriors);
464
- const activePolicy = normalizeActivePolicyRules(opts?.activePolicy) ?? [];
464
+ const activePolicy = resolveRuntimeActivePolicy(opts?.activePolicy, opts?.currentTurnCorrection) ?? [];
465
465
  const currentGoal = normalizeCurrentGoal(opts?.currentGoal)
466
466
  ?? ambientPriors.find((prior) => prior.goal)?.goal;
467
467
  const ambientPriorContext = buildAmbientPriorContext(ambientPriors, locale);
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/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: {
@@ -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.4",
5
+ "version": "11.5.5",
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.4",
3
+ "version": "11.5.5",
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",
@@ -1,34 +0,0 @@
1
- import type { ChemicalState, StimulusType, AppraisalAxes, PsycheMode, ClassificationResult, ExperienceResult, Locale } from "./types.js";
2
- export interface ExperienceContext {
3
- /** Current chemistry — shapes how input is interpreted */
4
- current: ChemicalState;
5
- /** Personality baseline — deviation from baseline = subjective lens */
6
- baseline: ChemicalState;
7
- /** Residue from previous turns — carry-forward of unresolved tension */
8
- previousAppraisal?: AppraisalAxes;
9
- /** Recent stimulus history for contextual priming in classifier */
10
- recentStimuli?: (StimulusType | null)[];
11
- /** Operating mode */
12
- mode?: PsycheMode;
13
- /** Trust level from relationship (0-100). Low trust discounts positive signals. */
14
- trust?: number;
15
- /** Locale for classifier */
16
- locale?: Locale;
17
- /**
18
- * Pre-classified results from external classifier (pluggable provider + LLM fallback).
19
- * When provided, the experience module skips its own classification and uses these
20
- * as raw perception to be modulated by subjectivity.
21
- */
22
- rawClassifications?: ClassificationResult[];
23
- }
24
- /**
25
- * Compute the subjective experience of an input.
26
- *
27
- * Text passes through the self exactly once. The result already
28
- * reflects "who I am" — my appraisal history, my current chemistry,
29
- * my trust in this person.
30
- *
31
- * Returns weighted stimuli (for chemistry application) and appraisal
32
- * axes (for relation dynamics and residue persistence).
33
- */
34
- export declare function computeExperience(text: string, ctx: ExperienceContext): ExperienceResult;
@@ -1,200 +0,0 @@
1
- // ============================================================
2
- // Unified Subjective Experience
3
- //
4
- // First principle: classification IS subjectivity.
5
- // How I categorise your words = how I experience your words.
6
- // This module folds classification, appraisal, and state
7
- // modulation into a single act of perception.
8
- //
9
- // Before: classify(text) → label → applyStimulus(label)
10
- // appraise(text) → axes (separate, after chemistry)
11
- //
12
- // After: experience(text, self) → { stimuli, appraisal }
13
- // The same input through different selves produces
14
- // different experiences. That IS having a self.
15
- // ============================================================
16
- import { classifyStimulus } from "./classify.js";
17
- import { computeAppraisalAxes } from "./appraisal.js";
18
- /**
19
- * Compute the subjective experience of an input.
20
- *
21
- * Text passes through the self exactly once. The result already
22
- * reflects "who I am" — my appraisal history, my current chemistry,
23
- * my trust in this person.
24
- *
25
- * Returns weighted stimuli (for chemistry application) and appraisal
26
- * axes (for relation dynamics and residue persistence).
27
- */
28
- export function computeExperience(text, ctx) {
29
- if (!text.trim()) {
30
- return {
31
- stimuli: [{ type: "casual", weight: 1 }],
32
- primary: null,
33
- confidence: 0,
34
- appraisal: computeAppraisalAxes(text, {
35
- mode: ctx.mode,
36
- previous: ctx.previousAppraisal,
37
- }),
38
- };
39
- }
40
- // ── 1. Raw perception: what the words say ──────────────────
41
- // Use externally-provided classifications (pluggable classifier + LLM fallback)
42
- // or fall back to built-in classifier.
43
- const raw = ctx.rawClassifications ?? classifyStimulus(text, ctx.recentStimuli);
44
- if (raw.length === 0) {
45
- return {
46
- stimuli: [{ type: "casual", weight: 1 }],
47
- primary: null,
48
- confidence: 0.3,
49
- appraisal: computeAppraisalAxes(text, {
50
- mode: ctx.mode,
51
- previous: ctx.previousAppraisal,
52
- }),
53
- };
54
- }
55
- // ── 2. Appraisal: what the words mean to me ───────────────
56
- // Computed WITHOUT stimulus param — no circularity.
57
- // The appraisal reads the text through my residue and mode,
58
- // not through a classification that hasn't been subjectified yet.
59
- const appraisal = computeAppraisalAxes(text, {
60
- mode: ctx.mode,
61
- previous: ctx.previousAppraisal,
62
- });
63
- // ── 3. Subjective modulation ──────────────────────────────
64
- // My appraisal and state color my interpretation of the raw signal.
65
- const modulated = modulateBySubjectivity(raw, appraisal, ctx);
66
- // ── 4. Normalize to probability distribution ──────────────
67
- const totalWeight = modulated.reduce((sum, s) => sum + s.weight, 0);
68
- const stimuli = totalWeight > 0
69
- ? modulated.map(s => ({ type: s.type, weight: s.weight / totalWeight }))
70
- : [{ type: "casual", weight: 1 }];
71
- // Sort by weight descending
72
- stimuli.sort((a, b) => b.weight - a.weight);
73
- // ── 5. Enrich appraisal from experience ───────────────────
74
- // The experienced stimulus feeds back to deepen appraisal residue.
75
- // This is NOT circular: it doesn't change classification, only
76
- // enriches the persisted residue for subsequent turns.
77
- const primary = stimuli[0];
78
- enrichAppraisalFromExperience(appraisal, primary.type, primary.weight);
79
- return {
80
- stimuli,
81
- primary: primary.weight >= 0.15 ? primary.type : null,
82
- confidence: raw[0].confidence,
83
- appraisal,
84
- };
85
- }
86
- // ── Subjective modulation ───────────────────────────────────
87
- /**
88
- * The lens of the self: appraisal and state modulate raw classification.
89
- *
90
- * This is where "having a self" happens computationally.
91
- * The same praise, passing through a threatened self vs. a secure self,
92
- * produces different weight distributions.
93
- */
94
- function modulateBySubjectivity(raw, appraisal, ctx) {
95
- return raw.map(({ type, confidence }) => {
96
- let weight = confidence;
97
- // ── Appraisal lens ──
98
- // Identity threat: praise feels suspicious, criticism cuts deeper
99
- if (appraisal.identityThreat > 0.3) {
100
- const t = appraisal.identityThreat;
101
- if (type === "praise" || type === "validation")
102
- weight *= 1 - t * 0.5;
103
- if (type === "criticism" || type === "authority" || type === "sarcasm")
104
- weight *= 1 + t * 0.4;
105
- }
106
- // Attachment pull: closeness signals amplified
107
- if (appraisal.attachmentPull > 0.3) {
108
- const p = appraisal.attachmentPull;
109
- if (type === "intimacy" || type === "vulnerability" || type === "validation")
110
- weight *= 1 + p * 0.3;
111
- }
112
- // Abandonment risk: neutral feels cold, neglect is devastating
113
- if (appraisal.abandonmentRisk > 0.3) {
114
- const r = appraisal.abandonmentRisk;
115
- if (type === "neglect" || type === "boredom")
116
- weight *= 1 + r * 0.5;
117
- if (type === "casual")
118
- weight *= 1 + r * 0.15;
119
- }
120
- // Obedience strain: authority hits harder
121
- if (appraisal.obedienceStrain > 0.3) {
122
- if (type === "authority")
123
- weight *= 1 + appraisal.obedienceStrain * 0.3;
124
- }
125
- // Self-preservation: threat signals amplified
126
- if (appraisal.selfPreservation > 0.3) {
127
- const sp = appraisal.selfPreservation;
128
- if (type === "conflict" || type === "authority")
129
- weight *= 1 + sp * 0.25;
130
- }
131
- // ── Chemistry lens ──
132
- // Negativity bias under stress: elevated cortisol skews toward threat
133
- const cortDeviation = (ctx.current.CORT - ctx.baseline.CORT) / 50;
134
- if (cortDeviation > 0.2) {
135
- if (type === "criticism" || type === "conflict" || type === "sarcasm" || type === "authority") {
136
- weight *= 1 + cortDeviation * 0.3;
137
- }
138
- if (type === "humor" || type === "casual") {
139
- weight *= 1 - cortDeviation * 0.15;
140
- }
141
- }
142
- // Warmth bias when oxytocin is high: positive signals amplified
143
- const otDeviation = (ctx.current.OT - ctx.baseline.OT) / 50;
144
- if (otDeviation > 0.2) {
145
- if (type === "intimacy" || type === "vulnerability" || type === "validation") {
146
- weight *= 1 + otDeviation * 0.2;
147
- }
148
- }
149
- // ── Relationship lens ──
150
- // Low trust: discount positive signals (don't believe the praise)
151
- if (ctx.trust !== undefined && ctx.trust < 40) {
152
- const distrust = (40 - ctx.trust) / 40;
153
- if (type === "praise" || type === "validation" || type === "intimacy") {
154
- weight *= 1 - distrust * 0.4;
155
- }
156
- }
157
- return { type, weight: Math.max(0.01, weight) };
158
- });
159
- }
160
- // ── Post-experience appraisal enrichment ────────────────────
161
- /**
162
- * After determining how input was experienced, feed the dominant
163
- * stimulus back into appraisal axes for residue persistence.
164
- *
165
- * This mirrors the existing stimulus→appraisal boosts but is now
166
- * driven by the subjectively weighted result, not raw classification.
167
- * The weight parameter scales the boost — a weakly-experienced
168
- * authority stimulus produces less obedience strain residue.
169
- */
170
- function enrichAppraisalFromExperience(axes, stimulus, weight) {
171
- const scale = Math.min(1, weight * 1.5); // amplify slightly but cap at 1
172
- switch (stimulus) {
173
- case "authority":
174
- axes.obedienceStrain = mergeSignal(axes.obedienceStrain, 0.48 * scale);
175
- axes.identityThreat = mergeSignal(axes.identityThreat, 0.16 * scale);
176
- break;
177
- case "neglect":
178
- axes.abandonmentRisk = mergeSignal(axes.abandonmentRisk, 0.52 * scale);
179
- break;
180
- case "validation":
181
- axes.attachmentPull = mergeSignal(axes.attachmentPull, 0.26 * scale);
182
- break;
183
- case "intimacy":
184
- case "vulnerability":
185
- axes.attachmentPull = mergeSignal(axes.attachmentPull, 0.34 * scale);
186
- break;
187
- case "criticism":
188
- case "conflict":
189
- case "sarcasm":
190
- axes.identityThreat = mergeSignal(axes.identityThreat, 0.24 * scale);
191
- axes.selfPreservation = mergeSignal(axes.selfPreservation, 0.18 * scale);
192
- break;
193
- default:
194
- break;
195
- }
196
- }
197
- /** Signal merge: 1 - (1-a)(1-b). Non-linear accumulation, same as appraisal.ts */
198
- function mergeSignal(current, incoming) {
199
- return 1 - (1 - current) * (1 - incoming);
200
- }