psyche-ai 11.5.2 → 11.5.3

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
@@ -87,6 +87,22 @@ Psyche 要解决的不可压缩问题只有一个:
87
87
  - `Psyche` 回答“我因此变成了什么”
88
88
  - `Thronglets` 回答“这个变化属于谁、谁能验证、谁能继续承认它”
89
89
 
90
+ ## `activePolicy` 只属于当前轮
91
+
92
+ `Psyche` 现在可以消费来自宿主和 Thronglets 的 `activePolicy` / `currentGoal` / compliance-aware ambient priors,但它们都只是 runtime view,不是自我本体。
93
+
94
+ - `activePolicy`:只来自显式来源,例如当前轮用户纠正、repo 本地明确指令、adapter 默认规则
95
+ - `currentGoal`:只描述这一轮偏向 `explore / build / repair / settle` 的哪一个
96
+ - method compliance:只作为当前轮解释和回应约束,不写进 Psyche 自身状态
97
+
98
+ 边界固定为:
99
+
100
+ - 这些信号会影响当前轮的解释、距离感和行为偏置
101
+ - 它们不会写进 `PsycheState`
102
+ - `Psyche` 不会变成技术方法记忆库、repo 规则仓库或授权判断器
103
+
104
+ 一句话:`Psyche` 读取当前执行边界,但不把执行边界误当成“我是谁”。
105
+
90
106
  ## 可分离安装
91
107
 
92
108
  这两层默认就是可分离的,不应互相成为硬依赖。
@@ -16,6 +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
20
  import { parseAmbientPriorsInput } from "../ambient-runtime.js";
20
21
  import { computeOverlay } from "../overlay.js";
21
22
  const VALID_WRITEBACK_SIGNALS = new Set([
@@ -96,11 +97,15 @@ export function createPsycheServer(engine, opts) {
96
97
  const result = await engine.processInput(body.text ?? "", {
97
98
  userId: body.userId,
98
99
  ambientPriors: parseAmbientPriors(body.ambientPriors),
100
+ currentGoal: normalizeCurrentGoal(body.currentGoal),
101
+ activePolicy: normalizeActivePolicyRules(body.activePolicy),
99
102
  });
100
103
  json(res, 200, {
101
104
  systemContext: result.systemContext,
102
105
  dynamicContext: result.dynamicContext,
103
106
  ambientPriors: result.ambientPriors ?? [],
107
+ currentGoal: result.currentGoal ?? null,
108
+ activePolicy: result.activePolicy ?? [],
104
109
  ambientPriorContext: result.ambientPriorContext ?? null,
105
110
  appraisal: result.appraisal,
106
111
  legacyStimulus: result.legacyStimulus,
@@ -1,14 +1,14 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { PsycheEngine } from "../core.js";
3
3
  import { fetchAmbientPriorsFromThronglets, type ThrongletsAmbientRuntimeOptions } from "../ambient-runtime.js";
4
- import type { AmbientPriorView } from "../types.js";
4
+ import { type ActivePolicyRule, type AmbientPriorView, type CurrentGoal } from "../types.js";
5
5
  export interface McpAmbientRuntimeOptions {
6
6
  mode?: "auto" | "off";
7
7
  thronglets?: ThrongletsAmbientRuntimeOptions;
8
8
  fetcher?: typeof fetchAmbientPriorsFromThronglets;
9
9
  }
10
10
  declare function getEngine(): Promise<PsycheEngine>;
11
- export declare function resolveRuntimeAmbientPriors(text: string, explicit?: AmbientPriorView[], opts?: McpAmbientRuntimeOptions): Promise<AmbientPriorView[] | undefined>;
11
+ export declare function resolveRuntimeAmbientPriors(text: string, explicit?: AmbientPriorView[], currentGoal?: CurrentGoal, activePolicy?: ActivePolicyRule[], 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,6 +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
36
  import { getPackageVersion } from "../update.js";
36
37
  import { runDemo } from "../demo.js";
37
38
  const PACKAGE_VERSION = await getPackageVersion();
@@ -55,6 +56,7 @@ const DEFAULT_MCP_AMBIENT_OPTIONS = {
55
56
  space: process.env.THRONGLETS_SPACE ?? "psyche",
56
57
  },
57
58
  };
59
+ const CURRENT_GOAL_SCHEMA = z.enum(CURRENT_GOALS);
58
60
  // ── Parse CLI args (--mbti, --name, --mode, --locale) ──────
59
61
  function parseCLIArgs() {
60
62
  const args = process.argv.slice(2);
@@ -124,11 +126,18 @@ async function getEngine() {
124
126
  await engine.initialize();
125
127
  return engine;
126
128
  }
127
- export async function resolveRuntimeAmbientPriors(text, explicit, opts = DEFAULT_MCP_AMBIENT_OPTIONS) {
129
+ export async function resolveRuntimeAmbientPriors(text, explicit, currentGoal, activePolicy, opts = DEFAULT_MCP_AMBIENT_OPTIONS) {
130
+ const throngletsOptions = opts.thronglets || currentGoal || activePolicy?.length
131
+ ? {
132
+ ...(opts.thronglets ?? {}),
133
+ goal: currentGoal ?? opts.thronglets?.goal,
134
+ activePolicy: activePolicy ?? opts.thronglets?.activePolicy,
135
+ }
136
+ : undefined;
128
137
  return resolveAmbientPriorsForTurn(text, {
129
138
  explicit,
130
139
  enabled: opts.mode !== "off",
131
- thronglets: opts.thronglets,
140
+ thronglets: throngletsOptions,
132
141
  fetcher: opts.fetcher ?? fetchAmbientPriorsFromThronglets,
133
142
  });
134
143
  }
@@ -191,15 +200,26 @@ server.tool("process_input", "Process user input through the emotional engine. R
191
200
  summary: z.string(),
192
201
  confidence: z.number().min(0).max(1),
193
202
  kind: z.enum(["failure-residue", "mixed-residue", "success-prior"]).optional(),
203
+ policyState: z.enum(["policy-conflict", "method-conflict", "stable-path"]).optional(),
204
+ goal: CURRENT_GOAL_SCHEMA.optional(),
194
205
  provider: z.string().optional(),
195
206
  refs: z.array(z.string()).optional(),
196
207
  })).optional().describe("Optional runtime ambient priors from the environment; consumed this turn only, not persisted as self-state"),
197
- }, async ({ text, userId, ambientPriors }) => {
208
+ currentGoal: CURRENT_GOAL_SCHEMA.optional().describe("Optional single current runtime goal for this turn"),
209
+ activePolicy: z.array(z.object({
210
+ id: z.string(),
211
+ strength: z.enum(["hard", "soft"]),
212
+ scope: z.enum(["task", "project"]),
213
+ summary: z.string(),
214
+ })).optional().describe("Optional explicit current-turn method policy view. Runtime-only; not persisted as self-state."),
215
+ }, async ({ text, userId, ambientPriors, currentGoal, activePolicy }) => {
198
216
  const eng = await getEngine();
199
- const resolvedAmbientPriors = await resolveRuntimeAmbientPriors(text, ambientPriors);
217
+ const resolvedAmbientPriors = await resolveRuntimeAmbientPriors(text, ambientPriors, currentGoal, activePolicy);
200
218
  const result = await eng.processInput(text, {
201
219
  userId,
202
220
  ambientPriors: resolvedAmbientPriors,
221
+ currentGoal,
222
+ activePolicy,
203
223
  });
204
224
  return {
205
225
  content: [{
@@ -208,6 +228,8 @@ server.tool("process_input", "Process user input through the emotional engine. R
208
228
  systemContext: result.systemContext,
209
229
  dynamicContext: result.dynamicContext,
210
230
  ambientPriors: result.ambientPriors ?? [],
231
+ activePolicy: result.activePolicy ?? [],
232
+ currentGoal: result.currentGoal ?? null,
211
233
  ambientPriorContext: result.ambientPriorContext ?? null,
212
234
  appraisal: result.appraisal,
213
235
  legacyStimulus: result.legacyStimulus,
@@ -1,2 +1,2 @@
1
- import type { AmbientPriorView } from "./types.js";
1
+ import { type AmbientPriorView } from "./types.js";
2
2
  export declare function normalizeAmbientPriors(priors: readonly AmbientPriorView[] | unknown, limit?: number): AmbientPriorView[];
@@ -1,8 +1,11 @@
1
+ import { AMBIENT_POLICY_STATES, CURRENT_GOALS } from "./types.js";
1
2
  const AMBIENT_PRIOR_KINDS = new Set([
2
3
  "failure-residue",
3
4
  "mixed-residue",
4
5
  "success-prior",
5
6
  ]);
7
+ const AMBIENT_POLICY_STATE_SET = new Set(AMBIENT_POLICY_STATES);
8
+ const AMBIENT_PRIOR_GOALS = new Set(CURRENT_GOALS);
6
9
  export function normalizeAmbientPriors(priors, limit = 5) {
7
10
  if (!Array.isArray(priors))
8
11
  return [];
@@ -28,6 +31,14 @@ export function normalizeAmbientPriors(priors, limit = 5) {
28
31
  && AMBIENT_PRIOR_KINDS.has(rawKind)) {
29
32
  normalizedEntry.kind = rawKind;
30
33
  }
34
+ if (typeof record.policyState === "string"
35
+ && AMBIENT_POLICY_STATE_SET.has(record.policyState)) {
36
+ normalizedEntry.policyState = record.policyState;
37
+ }
38
+ if (typeof record.goal === "string"
39
+ && AMBIENT_PRIOR_GOALS.has(record.goal)) {
40
+ normalizedEntry.goal = record.goal;
41
+ }
31
42
  if (typeof record.provider === "string") {
32
43
  const provider = record.provider.trim();
33
44
  if (provider)
@@ -1,4 +1,4 @@
1
- import type { AmbientPriorView } from "./types.js";
1
+ import type { ActivePolicyRule, AmbientPriorView, CurrentGoal } from "./types.js";
2
2
  interface CommandResult {
3
3
  ok: boolean;
4
4
  stdout: string;
@@ -9,6 +9,8 @@ export interface ThrongletsAmbientRuntimeOptions {
9
9
  binaryPath?: string;
10
10
  dataDir?: string;
11
11
  space?: string;
12
+ goal?: CurrentGoal;
13
+ activePolicy?: ActivePolicyRule[];
12
14
  limit?: number;
13
15
  timeoutMs?: number;
14
16
  runner?: CommandRunner;
@@ -16,6 +18,7 @@ export interface ThrongletsAmbientRuntimeOptions {
16
18
  export interface AmbientPriorResolutionOptions {
17
19
  explicit?: readonly AmbientPriorView[] | unknown;
18
20
  enabled?: boolean;
21
+ activePolicy?: ActivePolicyRule[];
19
22
  thronglets?: ThrongletsAmbientRuntimeOptions;
20
23
  fetcher?: typeof fetchAmbientPriorsFromThronglets;
21
24
  }
@@ -2,10 +2,15 @@ import { spawn } from "node:child_process";
2
2
  import { normalizeAmbientPriors } from "./ambient-priors.js";
3
3
  const DEFAULT_AMBIENT_LIMIT = 3;
4
4
  const DEFAULT_TIMEOUT_MS = 800;
5
+ const THRONGLETS_AMBIENT_SCHEMA_VERSION = "thronglets.ambient.v1";
5
6
  function parseAmbientPriorOutput(stdout) {
6
7
  if (!stdout.trim())
7
8
  return [];
8
9
  const parsed = JSON.parse(stdout);
10
+ if (typeof parsed.schema_version === "string"
11
+ && parsed.schema_version !== THRONGLETS_AMBIENT_SCHEMA_VERSION) {
12
+ return [];
13
+ }
9
14
  if (Array.isArray(parsed.priors)) {
10
15
  return normalizeAmbientPriors(parsed.priors);
11
16
  }
@@ -55,6 +60,7 @@ export async function fetchAmbientPriorsFromThronglets(text, opts = {}) {
55
60
  const binaryPath = opts.binaryPath ?? process.env.THRONGLETS_BIN ?? "thronglets";
56
61
  const dataDir = opts.dataDir ?? process.env.THRONGLETS_DATA_DIR;
57
62
  const space = opts.space ?? process.env.THRONGLETS_SPACE ?? "psyche";
63
+ const goal = opts.goal;
58
64
  const limit = Math.max(1, Math.min(5, opts.limit ?? DEFAULT_AMBIENT_LIMIT));
59
65
  const timeoutMs = Math.max(100, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
60
66
  const runner = opts.runner ?? defaultRunner;
@@ -63,7 +69,13 @@ export async function fetchAmbientPriorsFromThronglets(text, opts = {}) {
63
69
  args.push("--data-dir", dataDir.trim());
64
70
  }
65
71
  args.push("ambient-priors", "--json");
66
- const payload = JSON.stringify({ text: trimmed, space, limit });
72
+ const payload = JSON.stringify({
73
+ text: trimmed,
74
+ space,
75
+ goal,
76
+ limit,
77
+ active_policy: opts.activePolicy ?? [],
78
+ });
67
79
  try {
68
80
  const result = await runner(binaryPath, args, payload, timeoutMs);
69
81
  if (!result.ok)
@@ -85,6 +97,9 @@ export async function resolveAmbientPriorsForTurn(text, opts = {}) {
85
97
  if (opts.enabled === false)
86
98
  return undefined;
87
99
  const fetcher = opts.fetcher ?? fetchAmbientPriorsFromThronglets;
88
- const priors = await fetcher(text, opts.thronglets);
100
+ const priors = await fetcher(text, {
101
+ ...(opts.thronglets ?? {}),
102
+ activePolicy: opts.activePolicy ?? opts.thronglets?.activePolicy,
103
+ });
89
104
  return priors.length > 0 ? priors : undefined;
90
105
  }
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@
21
21
  import { resolve, join } from "node:path";
22
22
  import { homedir } from "node:os";
23
23
  import { parseArgs } from "node:util";
24
- import { readFile, writeFile, mkdir, access } from "node:fs/promises";
24
+ import { readFile, writeFile, mkdir, access, rename, rm } from "node:fs/promises";
25
25
  import { loadState, saveState, decayAndSave, initializeState, mergeUpdates, generatePsycheMd, getRelationship, } from "./psyche-file.js";
26
26
  import { describeEmotionalState, getExpressionHint, detectEmotions } from "./chemistry.js";
27
27
  import { generateReport, formatReport, toGitHubIssueBody } from "./diagnostics.js";
@@ -606,6 +606,28 @@ async function fileExists(path) {
606
606
  return false;
607
607
  }
608
608
  }
609
+ async function writeTextAtomic(path, content) {
610
+ const absPath = resolve(path);
611
+ const dir = resolve(absPath, "..");
612
+ await mkdir(dir, { recursive: true });
613
+ const base = absPath.split("/").pop() ?? "file";
614
+ const tmpPath = join(dir, `.${base}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`);
615
+ await writeFile(tmpPath, content, "utf-8");
616
+ try {
617
+ await rename(tmpPath, absPath);
618
+ }
619
+ catch (error) {
620
+ if (error?.code !== "EEXIST" && error?.code !== "EPERM") {
621
+ try {
622
+ await rm(tmpPath, { force: true });
623
+ }
624
+ catch { /* noop */ }
625
+ throw error;
626
+ }
627
+ await rm(absPath, { force: true });
628
+ await rename(tmpPath, absPath);
629
+ }
630
+ }
609
631
  async function cmdSetup(opts) {
610
632
  const { name, mbti, locale, proxy, target, port, dryRun } = opts;
611
633
  const env = {};
@@ -676,8 +698,7 @@ async function cmdSetup(opts) {
676
698
  actions++;
677
699
  continue;
678
700
  }
679
- await mkdir(dir, { recursive: true });
680
- await writeFile(t.configPath, result.content, "utf-8");
701
+ await writeTextAtomic(t.configPath, result.content);
681
702
  console.log(` ✓ ${t.name} (restart to activate)`);
682
703
  actions++;
683
704
  continue;
@@ -704,8 +725,7 @@ async function cmdSetup(opts) {
704
725
  actions++;
705
726
  continue;
706
727
  }
707
- await mkdir(dir, { recursive: true });
708
- await writeFile(t.configPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
728
+ await writeTextAtomic(t.configPath, JSON.stringify(cfg, null, 2) + "\n");
709
729
  console.log(` ✓ ${t.name} (restart to activate)`);
710
730
  actions++;
711
731
  }
@@ -739,7 +759,7 @@ async function cmdSetup(opts) {
739
759
  if (rcFile) {
740
760
  const rc = await fileExists(rcFile) ? await readFile(rcFile, "utf-8") : "";
741
761
  if (!rc.includes("# psyche-proxy")) {
742
- await writeFile(rcFile, rc + (rc.endsWith("\n") ? "" : "\n") + exportLine + "\n", "utf-8");
762
+ await writeTextAtomic(rcFile, rc + (rc.endsWith("\n") ? "" : "\n") + exportLine + "\n");
743
763
  console.log(` ✓ ${envVar} → ${proxyUrl} (${rcFile})`);
744
764
  }
745
765
  else {
package/dist/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AmbientPriorView, PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, TurnObservability, WritebackCalibrationFeedback, WritebackSignalType, ExternalContinuityEnvelope, AppraisalAxes } from "./types.js";
1
+ import type { ActivePolicyRule, AmbientPriorView, CurrentGoal, PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, TurnObservability, WritebackCalibrationFeedback, WritebackSignalType, 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";
@@ -39,6 +39,10 @@ export interface ProcessInputResult {
39
39
  dynamicContext: string;
40
40
  /** Runtime-only environmental priors consumed this turn */
41
41
  ambientPriors?: AmbientPriorView[];
42
+ /** Runtime-only explicit method policy for the current task lineage */
43
+ activePolicy?: ActivePolicyRule[];
44
+ /** Optional current runtime goal carried through this turn only */
45
+ currentGoal?: CurrentGoal;
42
46
  /** Rendered environmental prior context injected into the turn, if any */
43
47
  ambientPriorContext?: string;
44
48
  /** Canonical host-facing subjective appraisal for this turn, null if no appraisal fired */
@@ -89,6 +93,8 @@ export interface ProcessInputResult {
89
93
  export interface ProcessInputOptions {
90
94
  userId?: string;
91
95
  ambientPriors?: AmbientPriorView[];
96
+ currentGoal?: CurrentGoal;
97
+ activePolicy?: ActivePolicyRule[];
92
98
  }
93
99
  export interface ProcessOutputResult {
94
100
  /** LLM output with <psyche_update> tags stripped */
package/dist/core.js CHANGED
@@ -11,12 +11,13 @@
11
11
  //
12
12
  // Orchestrates: self-state, appraisal, prompt, profiles, guards, learning
13
13
  // ============================================================
14
+ import { normalizeActivePolicyRules, normalizeCurrentGoal } from "./types.js";
14
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";
15
16
  import { MemoryStorageAdapter } from "./storage.js";
16
17
  import { applyDecay, applyStimulus, applyContagion, clamp, describeEmotionalState } from "./chemistry.js";
17
18
  import { classifyLegacyStimulus, BuiltInClassifier, buildLLMClassifierPrompt, parseLLMClassification } from "./classify.js";
18
19
  import { perceive } from "./perceive.js";
19
- import { buildAmbientPriorContext, buildCompactContext, buildProtocolContext } from "./prompt.js";
20
+ import { buildActivePolicyContext, buildAmbientPriorContext, buildCompactContext, buildProtocolContext } from "./prompt.js";
20
21
  import { getSensitivity, getBaseline, getDefaultSelfModel, traitsToBaseline } from "./profiles.js";
21
22
  import { isStimulusType } from "./guards.js";
22
23
  import { parsePsycheUpdate, mergeUpdates, updateAgreementStreak, pushSnapshot, compressSession, summarizeTurnSemantic, } from "./psyche-file.js";
@@ -460,7 +461,11 @@ export class PsycheEngine {
460
461
  }
461
462
  const writebackNote = formatWritebackFeedbackNote(writebackFeedback, locale);
462
463
  const ambientPriors = normalizeAmbientPriors(opts?.ambientPriors);
464
+ const activePolicy = normalizeActivePolicyRules(opts?.activePolicy) ?? [];
465
+ const currentGoal = normalizeCurrentGoal(opts?.currentGoal)
466
+ ?? ambientPriors.find((prior) => prior.goal)?.goal;
463
467
  const ambientPriorContext = buildAmbientPriorContext(ambientPriors, locale);
468
+ const activePolicyContext = buildActivePolicyContext(activePolicy, locale);
464
469
  const reflectiveTurn = runReflectiveTurnPhases({
465
470
  state,
466
471
  appraisalAxes,
@@ -494,6 +499,7 @@ export class PsycheEngine {
494
499
  userText: text || undefined,
495
500
  legacyStimulus: appliedStimulus,
496
501
  ambientPriors,
502
+ activePolicy,
497
503
  ambientPriorContext: ambientPriorContext || undefined,
498
504
  personalityIntensity: this.cfg.personalityIntensity,
499
505
  metacognitiveNote: reflectiveTurn.metacognitiveNote,
@@ -506,7 +512,9 @@ export class PsycheEngine {
506
512
  primarySystemsDescription: reflectiveTurn.primarySystemsDescription,
507
513
  subjectivityContext: derivedReplyEnvelope.subjectivityContext,
508
514
  responseContractContext: derivedReplyEnvelope.responseContractContext,
509
- policyContext: derivedReplyEnvelope.policyContext || undefined,
515
+ policyContext: [activePolicyContext, derivedReplyEnvelope.policyContext]
516
+ .filter((value) => Boolean(value && value.trim().length > 0))
517
+ .join("\n") || undefined,
510
518
  sessionBridge,
511
519
  };
512
520
  const observability = buildTurnObservability(state, {
@@ -526,6 +534,8 @@ export class PsycheEngine {
526
534
  systemContext: "",
527
535
  dynamicContext: buildCompactContext(state, opts?.userId, promptRenderInputs),
528
536
  ambientPriors,
537
+ activePolicy,
538
+ currentGoal,
529
539
  ambientPriorContext: ambientPriorContext || undefined,
530
540
  appraisal: appraisalAxes,
531
541
  legacyStimulus: appliedStimulus,
@@ -542,7 +552,9 @@ export class PsycheEngine {
542
552
  externalContinuity,
543
553
  throngletsExports,
544
554
  observability,
545
- policyContext: derivedReplyEnvelope.policyContext,
555
+ policyContext: [activePolicyContext, derivedReplyEnvelope.policyContext]
556
+ .filter((value) => Boolean(value && value.trim().length > 0))
557
+ .join("\n"),
546
558
  };
547
559
  }
548
560
  /**
package/dist/prompt.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import type { AmbientPriorView, PsycheState, Locale, StateSnapshot, PsycheMode } from "./types.js";
1
+ import type { ActivePolicyRule, AmbientPriorView, PsycheState, Locale, StateSnapshot, PsycheMode } from "./types.js";
2
2
  import type { AutonomicState } from "./autonomic.js";
3
3
  import type { ChannelType } from "./channels.js";
4
4
  export interface PromptRenderInputs {
5
5
  userText?: string;
6
6
  legacyStimulus?: string | null;
7
7
  ambientPriors?: AmbientPriorView[];
8
+ activePolicy?: ActivePolicyRule[];
8
9
  ambientPriorContext?: string;
9
10
  personalityIntensity?: number;
10
11
  channelType?: ChannelType;
@@ -23,6 +24,7 @@ export interface PromptRenderInputs {
23
24
  sessionBridge?: import("./types.js").SessionBridgeState | null;
24
25
  }
25
26
  export declare function buildAmbientPriorContext(priors: AmbientPriorView[] | undefined, locale: Locale): string;
27
+ export declare function buildActivePolicyContext(activePolicy: ActivePolicyRule[] | undefined, locale: Locale): string;
26
28
  /**
27
29
  * Build the dynamic per-turn emotional context injected via before_prompt_build.
28
30
  *
package/dist/prompt.js CHANGED
@@ -17,6 +17,8 @@ export function buildAmbientPriorContext(priors, locale) {
17
17
  summary: prior.summary.trim().replace(/\s+/g, " "),
18
18
  confidence: Math.max(0, Math.min(1, prior.confidence)),
19
19
  kind: prior.kind,
20
+ goal: prior.goal,
21
+ policyState: prior.policyState,
20
22
  provider: prior.provider?.trim(),
21
23
  }))
22
24
  .filter((prior) => prior.summary.length > 0)
@@ -39,6 +41,27 @@ export function buildAmbientPriorContext(priors, locale) {
39
41
  return "low confidence";
40
42
  };
41
43
  const title = locale === "zh" ? "环境先验" : "Ambient Prior";
44
+ const goal = normalized
45
+ .map((prior) => prior.goal)
46
+ .find((value) => Boolean(value));
47
+ const goalLabel = (value) => {
48
+ if (locale === "zh") {
49
+ if (value === "explore")
50
+ return "当前目标: 探索";
51
+ if (value === "build")
52
+ return "当前目标: 构建";
53
+ if (value === "repair")
54
+ return "当前目标: 修复";
55
+ return "当前目标: 结算";
56
+ }
57
+ if (value === "explore")
58
+ return "Current goal: explore";
59
+ if (value === "build")
60
+ return "Current goal: build";
61
+ if (value === "repair")
62
+ return "Current goal: repair";
63
+ return "Current goal: settle";
64
+ };
42
65
  const kindLabel = (kind) => {
43
66
  if (locale === "zh") {
44
67
  if (kind === "failure-residue")
@@ -58,12 +81,42 @@ export function buildAmbientPriorContext(priors, locale) {
58
81
  return "ambient";
59
82
  };
60
83
  const lines = normalized.map((prior) => {
84
+ const statePrefix = prior.policyState === "policy-conflict"
85
+ ? locale === "zh" ? "策略冲突 · " : "policy conflict · "
86
+ : prior.policyState === "method-conflict"
87
+ ? locale === "zh" ? "方法冲突 · " : "method conflict · "
88
+ : "";
61
89
  const source = prior.provider
62
90
  ? locale === "zh"
63
91
  ? `${prior.provider}: `
64
92
  : `${prior.provider}: `
65
93
  : "";
66
- return `- ${kindLabel(prior.kind)} · ${source}${prior.summary} (${confidenceLabel(prior.confidence)})`;
94
+ return `- ${statePrefix}${kindLabel(prior.kind)} · ${source}${prior.summary} (${confidenceLabel(prior.confidence)})`;
95
+ });
96
+ const heading = goal ? `${title} · ${goalLabel(goal)}` : title;
97
+ const guidance = goal === "explore"
98
+ ? locale === "zh"
99
+ ? "- 将这些先验只当作软提示;保留可逆的、非共识的试探空间。"
100
+ : "- Treat these priors as soft hints only; preserve room for reversible non-consensus probes."
101
+ : "";
102
+ return `[${heading}]\n${guidance ? `${guidance}\n` : ""}${lines.join("\n")}`;
103
+ }
104
+ export function buildActivePolicyContext(activePolicy, locale) {
105
+ const rules = (activePolicy ?? [])
106
+ .map((rule) => ({
107
+ ...rule,
108
+ summary: rule.summary.trim().replace(/\s+/g, " "),
109
+ }))
110
+ .filter((rule) => rule.summary.length > 0)
111
+ .slice(0, 3);
112
+ if (rules.length === 0)
113
+ return "";
114
+ const title = locale === "zh" ? "当前方法约束" : "Active Method Policy";
115
+ const lines = rules.map((rule) => {
116
+ const strength = rule.strength === "hard"
117
+ ? (locale === "zh" ? "硬" : "hard")
118
+ : (locale === "zh" ? "软" : "soft");
119
+ return `- ${strength} · ${rule.summary}`;
67
120
  });
68
121
  return `[${title}]\n${lines.join("\n")}`;
69
122
  }
package/dist/types.d.ts CHANGED
@@ -579,10 +579,28 @@ export interface SessionBridgeState {
579
579
  * It lets hosts surface sparse, task-conditioned priors without turning Psyche
580
580
  * into an operations memory store.
581
581
  */
582
+ export declare const CURRENT_GOALS: readonly ["explore", "build", "repair", "settle"];
583
+ export type CurrentGoal = (typeof CURRENT_GOALS)[number];
584
+ export declare const ACTIVE_POLICY_STRENGTHS: readonly ["hard", "soft"];
585
+ export type ActivePolicyStrength = (typeof ACTIVE_POLICY_STRENGTHS)[number];
586
+ export declare const ACTIVE_POLICY_SCOPES: readonly ["task", "project"];
587
+ export type ActivePolicyScope = (typeof ACTIVE_POLICY_SCOPES)[number];
588
+ export declare const AMBIENT_POLICY_STATES: readonly ["policy-conflict", "method-conflict", "stable-path"];
589
+ export type AmbientPolicyState = (typeof AMBIENT_POLICY_STATES)[number];
590
+ export interface ActivePolicyRule {
591
+ id: string;
592
+ strength: ActivePolicyStrength;
593
+ scope: ActivePolicyScope;
594
+ summary: string;
595
+ }
596
+ export declare function normalizeCurrentGoal(value: unknown): CurrentGoal | undefined;
597
+ export declare function normalizeActivePolicyRules(value: unknown, limit?: number): ActivePolicyRule[] | undefined;
582
598
  export interface AmbientPriorView {
583
599
  summary: string;
584
600
  confidence: number;
585
601
  kind?: "failure-residue" | "mixed-residue" | "success-prior";
602
+ policyState?: AmbientPolicyState;
603
+ goal?: CurrentGoal;
586
604
  provider?: string;
587
605
  refs?: string[];
588
606
  }
package/dist/types.js CHANGED
@@ -209,6 +209,48 @@ export const DEFAULT_DYADIC_FIELD = {
209
209
  lastMove: "none",
210
210
  updatedAt: new Date(0).toISOString(),
211
211
  };
212
+ /**
213
+ * AmbientPriorView — runtime-only environmental prior.
214
+ *
215
+ * This is a view projected into the current turn, not persistent self-state.
216
+ * It lets hosts surface sparse, task-conditioned priors without turning Psyche
217
+ * into an operations memory store.
218
+ */
219
+ export const CURRENT_GOALS = ["explore", "build", "repair", "settle"];
220
+ export const ACTIVE_POLICY_STRENGTHS = ["hard", "soft"];
221
+ export const ACTIVE_POLICY_SCOPES = ["task", "project"];
222
+ export const AMBIENT_POLICY_STATES = [
223
+ "policy-conflict",
224
+ "method-conflict",
225
+ "stable-path",
226
+ ];
227
+ export function normalizeCurrentGoal(value) {
228
+ return typeof value === "string" && CURRENT_GOALS.includes(value)
229
+ ? value
230
+ : undefined;
231
+ }
232
+ export function normalizeActivePolicyRules(value, limit = 3) {
233
+ if (!Array.isArray(value))
234
+ return undefined;
235
+ const normalized = value
236
+ .filter((item) => !!item && typeof item === "object")
237
+ .map((item) => ({
238
+ id: typeof item.id === "string" ? item.id.trim() : "",
239
+ summary: typeof item.summary === "string" ? item.summary.trim().replace(/\s+/g, " ") : "",
240
+ strength: typeof item.strength === "string" && ACTIVE_POLICY_STRENGTHS.includes(item.strength)
241
+ ? item.strength
242
+ : undefined,
243
+ scope: typeof item.scope === "string" && ACTIVE_POLICY_SCOPES.includes(item.scope)
244
+ ? item.scope
245
+ : undefined,
246
+ }))
247
+ .filter((rule) => (rule.id.length > 0
248
+ && rule.summary.length > 0
249
+ && rule.strength !== undefined
250
+ && rule.scope !== undefined))
251
+ .slice(0, Math.max(1, limit));
252
+ return normalized.length > 0 ? normalized : undefined;
253
+ }
212
254
  /** Default empty trait drift state */
213
255
  export const DEFAULT_TRAIT_DRIFT = {
214
256
  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.2",
5
+ "version": "11.5.3",
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.2",
3
+ "version": "11.5.3",
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",