psyche-ai 9.2.7 → 9.2.9

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
@@ -1,7 +1,7 @@
1
1
  # Psyche — 面向智能体的 AI-first 主观性内核
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/psyche-ai)](https://www.npmjs.com/package/psyche-ai)
4
- [![tests](https://img.shields.io/badge/tests-1291%20passing-brightgreen)]()
4
+ [![tests](https://img.shields.io/badge/tests-1308%20passing-brightgreen)]()
5
5
  [![deps](https://img.shields.io/badge/dependencies-0-blue)]()
6
6
  [![license](https://img.shields.io/badge/license-MIT-yellow)](LICENSE)
7
7
 
@@ -23,6 +23,91 @@ Psyche 不是给模型贴一层“情绪 UI”。
23
23
  - **源码仓库**: [`oasyce_psyche`](https://github.com/Shangri-la-0428/oasyce_psyche)
24
24
  - **官网**: [psyche.oasyce.com](https://psyche.oasyce.com)
25
25
 
26
+ ## 第一性原理
27
+
28
+ Psyche 要解决的不可压缩问题只有一个:
29
+
30
+ **互动历史必须持续改变 agent 后续的行为分布,而且这件事要足够便宜、足够稳定、足够可移植。**
31
+
32
+ 因此它的本体不是:
33
+
34
+ - 情绪标签系统
35
+ - 陪伴产品
36
+ - prompt 皮肤
37
+ - 记忆仓库
38
+
39
+ 它真正是:
40
+
41
+ - 本地主观性内核
42
+ - 关系动力学运行时
43
+ - 宿主可消费的行为控制面
44
+
45
+ 更完整的战略定义见:
46
+
47
+ - [docs/PROJECT_DIRECTION.md](docs/PROJECT_DIRECTION.md)
48
+ - [docs/STACK_ARCHITECTURE.md](docs/STACK_ARCHITECTURE.md)
49
+
50
+ ## 冻结身份蓝图
51
+
52
+ 跨 Psyche / Thronglets / Oasyce Net / Oasyce Chain 的身份抽象现在冻结成 4 个对象:
53
+
54
+ 1. `principal`:持续主体
55
+ 2. `account`:资产与结算容器
56
+ 3. `delegate`:被授权执行者
57
+ 4. `session`:一次具体运行,永不作为经济主体
58
+
59
+ 对应的四层分工是:
60
+
61
+ - `Psyche = subjective continuity substrate`
62
+ - `Thronglets = delegate continuity + session traces/coordination + emergent collective intelligence`
63
+ - `Oasyce Net = policy, operations, and resource orchestration`
64
+ - `Oasyce Chain = account truth, authorization truth, commitments, settlement, and public finality`
65
+
66
+ 正式版本见:
67
+
68
+ - [docs/IDENTITY_MODEL.md](docs/IDENTITY_MODEL.md)
69
+ - [docs/STACK_ARCHITECTURE.md](docs/STACK_ARCHITECTURE.md)
70
+
71
+ ## Psyche 和 Thronglets 的关系
72
+
73
+ 两者不是竞争关系,也不该揉成一个系统。
74
+
75
+ - **Psyche** 负责私有主观性:关系残留、未完成张力、行为偏置、局部学习
76
+ - **Thronglets** 负责外部连续性:owner / device identity、签名、跨设备延续、低频可验证轨迹
77
+
78
+ 一句话:
79
+
80
+ - `Psyche` 回答“我因此变成了什么”
81
+ - `Thronglets` 回答“这个变化属于谁、谁能验证、谁能继续承认它”
82
+
83
+ ## 可分离安装
84
+
85
+ 这两层默认就是可分离的,不应互相成为硬依赖。
86
+
87
+ - **只装 Psyche**:正常可用,拥有主观连续性和关系动力学;只是没有外部连续性层
88
+ - **只装 Thronglets**:正常可用,拥有 delegate / session 连续性与环境协作;只是没有主观连续性内核
89
+ - **两者都装**:Psyche 通过可选 `externalContinuity` envelope 输出稀疏、低频、可归属的 `signals / traces` 给 Thronglets
90
+
91
+ 原则上:
92
+
93
+ - `Psyche` 必须 standalone 可用
94
+ - `Thronglets` 必须 additive,而不是 Psyche 的运行前提
95
+ - 两者之间只通过稀疏 `signal / trace` 接口连接
96
+
97
+ 更细的分层、边界和运行流见:[docs/STACK_ARCHITECTURE.md](docs/STACK_ARCHITECTURE.md)
98
+
99
+ ## 新概念准入规则
100
+
101
+ 以后引入任何新概念,先尝试压进这 5 个原始容器:
102
+
103
+ 1. `Relation Move`
104
+ 2. `Dyadic Field`
105
+ 3. `Open Loop / Residue`
106
+ 4. `Reply Bias / Control ABI`
107
+ 5. `Writeback / Learning`
108
+
109
+ 如果一个新概念放不进这 5 个盒子,先怀疑概念本身,而不是继续加对象类型。
110
+
26
111
  ## 为什么它值得被看见
27
112
 
28
113
  - **不是 persona prompt**:输入会留下持续主体偏置,不是一轮一清。
@@ -124,6 +209,30 @@ npm view psyche-ai version
124
209
  - `git` 工作树和本地路径安装不会偷偷改你的代码;脏工作树只会提示手动命令。
125
210
  - OpenClaw 如果是从当前仓库本地加载,升级后记得重启 gateway 以载入新的 `dist/`。
126
211
 
212
+ ## 给本机其他 Agent 的真实验收
213
+
214
+ 如果另一个 agent 声称“已经在用 Psyche”,不要接受口头解释,直接让它跑:
215
+
216
+ ```bash
217
+ psyche probe --json
218
+ ```
219
+
220
+ 这条命令会实际调用一次 `processInput()` 和 `processOutput()`,并返回:
221
+
222
+ - 实际版本
223
+ - 实际加载路径
224
+ - 是否真的调用成功
225
+ - `replyEnvelope` 这个规范主接口是否存在
226
+
227
+ 通过标准不是“它理解了 Psyche”,而是:
228
+
229
+ - `ok = true`
230
+ - `processInputCalled = true`
231
+ - `processOutputCalled = true`
232
+ - `canonicalHostSurface = true`
233
+
234
+ 正式说明见:[docs/AGENT_RUNTIME_PROBE.md](docs/AGENT_RUNTIME_PROBE.md)
235
+
127
236
  ---
128
237
 
129
238
  ## 它和你认知中的 AI 完全不同
@@ -215,6 +324,8 @@ Psyche 现在更准确的目标,不是“模仿某种像人的风格”,而
215
324
 
216
325
  这样做不是为了加抽象层,而是为了减少散装编排,让宿主稳定消费同一套行为 ABI。
217
326
 
327
+ 当前主线里,`processInput()` 会直接返回 `replyEnvelope` 作为规范主接口;原有平级字段继续保留为兼容别名,避免打断现有宿主。`policyModifiers` 不再属于规范主接口,只保留为 legacy raw vector。
328
+
218
329
  ### 内在世界
219
330
 
220
331
  Psyche 给 AI 一个始终运转的内在自我——不是条件触发,而是每时每刻都在:
@@ -436,7 +547,7 @@ Psyche 核心引擎永久开源(MIT)。
436
547
  ```bash
437
548
  npm install
438
549
  npm run build
439
- npm test # 1291 tests
550
+ npm test # 1308 tests
440
551
  npm run typecheck # strict mode
441
552
  ```
442
553
 
@@ -7,7 +7,7 @@
7
7
  // const server = createPsycheServer(engine, { port: 3210 });
8
8
  //
9
9
  // Endpoints:
10
- // POST /process-input { text, userId? } → { systemContext, dynamicContext, stimulus, policyModifiers?, subjectivityKernel?, responseContract?, generationControls?, policyContext }
10
+ // POST /process-input { text, userId? } → { systemContext, dynamicContext, stimulus, replyEnvelope?, ...compat aliases, sessionBridge?, writebackFeedback?, externalContinuity?, throngletsExports?, policyContext }
11
11
  // POST /process-output { text, userId?, signals?, signalConfidence? } → { cleanedText, stateChanged }
12
12
  // GET /state → PsycheState
13
13
  // GET /protocol?locale=zh → { protocol }
@@ -79,11 +79,12 @@ export class PsycheLangChain {
79
79
  */
80
80
  async prepareInvocation(userText, opts) {
81
81
  const result = await this.engine.processInput(userText, opts);
82
+ const generationControls = result.replyEnvelope?.generationControls ?? result.generationControls;
82
83
  const controls = {
83
- ...(result.generationControls ?? {}),
84
- maxTokens: result.generationControls?.maxTokens !== undefined && opts?.maxTokens !== undefined
85
- ? Math.min(opts.maxTokens, result.generationControls.maxTokens)
86
- : result.generationControls?.maxTokens ?? opts?.maxTokens,
84
+ ...(generationControls ?? {}),
85
+ maxTokens: generationControls?.maxTokens !== undefined && opts?.maxTokens !== undefined
86
+ ? Math.min(opts.maxTokens, generationControls.maxTokens)
87
+ : generationControls?.maxTokens ?? opts?.maxTokens,
87
88
  };
88
89
  return {
89
90
  systemMessage: result.systemContext + "\n\n" + result.dynamicContext,
@@ -145,8 +145,10 @@ server.resource("state", "psyche://state", {
145
145
  // ── Tools ──────────────────────────────────────────────────
146
146
  server.tool("process_input", "Process user input through the emotional engine. Returns emotional " +
147
147
  "context to inject into the LLM system prompt (systemContext + dynamicContext), " +
148
- "detected stimulus type, behavioral policy modifiers, the narrow AI-first ABI " +
149
- "(subjectivityKernel + responseContract), and generationControls. " +
148
+ "detected stimulus type, a canonical replyEnvelope, compatibility aliases " +
149
+ "(policyModifiers + subjectivityKernel + responseContract + generationControls), an optional " +
150
+ "externalContinuity envelope, and sparse low-frequency throngletsExports " +
151
+ "suitable for additive external continuity layers. " +
150
152
  "Call this BEFORE generating a response to the user.", {
151
153
  text: z.string().describe("The user's message text"),
152
154
  userId: z.string().optional().describe("Optional user ID for multi-user relationship tracking"),
@@ -160,10 +162,15 @@ server.tool("process_input", "Process user input through the emotional engine. R
160
162
  systemContext: result.systemContext,
161
163
  dynamicContext: result.dynamicContext,
162
164
  stimulus: result.stimulus,
165
+ replyEnvelope: result.replyEnvelope ?? null,
163
166
  policyModifiers: result.policyModifiers ?? null,
164
167
  subjectivityKernel: result.subjectivityKernel ?? null,
165
168
  responseContract: result.responseContract ?? null,
166
169
  generationControls: result.generationControls ?? null,
170
+ sessionBridge: result.sessionBridge ?? null,
171
+ writebackFeedback: result.writebackFeedback ?? null,
172
+ externalContinuity: result.externalContinuity ?? null,
173
+ throngletsExports: result.throngletsExports ?? null,
167
174
  policyContext: result.policyContext,
168
175
  }, null, 2),
169
176
  }],
@@ -44,7 +44,7 @@ export function sanitizeOpenClawInputText(text) {
44
44
  .trim();
45
45
  }
46
46
  function getDominantAppraisalLabel(result) {
47
- const appraisal = result.subjectivityKernel?.appraisal;
47
+ const appraisal = result.replyEnvelope?.subjectivityKernel?.appraisal ?? result.subjectivityKernel?.appraisal;
48
48
  if (!appraisal)
49
49
  return null;
50
50
  const entries = [
@@ -118,7 +118,7 @@ export function register(api) {
118
118
  }
119
119
  const engine = await getEngine(workspaceDir);
120
120
  const result = await engine.processInput(inputText, { userId: ctx.userId });
121
- const controls = result.generationControls;
121
+ const controls = result.replyEnvelope?.generationControls ?? result.generationControls;
122
122
  const dominantAppraisal = getDominantAppraisalLabel(result);
123
123
  const state = engine.getState();
124
124
  logger.info(`Psyche [input] stimulus=${result.stimulus ?? "none"} | ` +
@@ -49,11 +49,13 @@ export function psycheMiddleware(engine, _opts) {
49
49
  transformParams: async ({ params }) => {
50
50
  const userText = extractLastUserText(params.prompt ?? []);
51
51
  const result = await engine.processInput(userText);
52
+ const envelope = result.replyEnvelope;
53
+ const generationControls = envelope?.generationControls ?? result.generationControls;
52
54
  const controls = {
53
- ...(result.generationControls ?? {}),
54
- maxTokens: result.generationControls?.maxTokens !== undefined && typeof params.maxTokens === "number"
55
- ? Math.min(params.maxTokens, result.generationControls.maxTokens)
56
- : result.generationControls?.maxTokens ?? (typeof params.maxTokens === "number" ? params.maxTokens : undefined),
55
+ ...(generationControls ?? {}),
56
+ maxTokens: generationControls?.maxTokens !== undefined && typeof params.maxTokens === "number"
57
+ ? Math.min(params.maxTokens, generationControls.maxTokens)
58
+ : generationControls?.maxTokens ?? (typeof params.maxTokens === "number" ? params.maxTokens : undefined),
57
59
  };
58
60
  const psycheContext = result.systemContext + "\n\n" + result.dynamicContext;
59
61
  return {
package/dist/cli.js CHANGED
@@ -13,6 +13,7 @@
13
13
  // psyche reset <dir> [--full]
14
14
  // psyche diagnose <dir> [--github]
15
15
  // psyche upgrade [--check]
16
+ // psyche probe [--json]
16
17
  // psyche profiles [--json] [--mbti TYPE]
17
18
  // ============================================================
18
19
  import { resolve } from "node:path";
@@ -27,6 +28,7 @@ import { t } from "./i18n.js";
27
28
  import { CHEMICAL_KEYS, CHEMICAL_NAMES_ZH, DRIVE_KEYS, DRIVE_NAMES_ZH } from "./types.js";
28
29
  import { isMBTIType, isChemicalKey, isLocale } from "./guards.js";
29
30
  import { getPackageVersion, selfUpdate } from "./update.js";
31
+ import { runRuntimeProbe } from "./runtime-probe.js";
30
32
  // ── Logger ───────────────────────────────────────────────────
31
33
  const cliLogger = {
32
34
  info: (msg) => console.error(`[info] ${msg}`),
@@ -425,6 +427,33 @@ async function cmdUpgrade(checkOnly) {
425
427
  const result = await selfUpdate({ checkOnly });
426
428
  console.log(result.message);
427
429
  }
430
+ async function cmdProbe(json) {
431
+ const result = await runRuntimeProbe();
432
+ if (json) {
433
+ console.log(JSON.stringify(result, null, 2));
434
+ return;
435
+ }
436
+ if (!result.ok) {
437
+ console.log("\nPsyche runtime probe: FAILED\n");
438
+ console.log(` version: ${result.version}`);
439
+ console.log(` entry: ${result.entry}`);
440
+ console.log(` load path: ${result.loadPath}`);
441
+ console.log(` module path: ${result.modulePath}`);
442
+ console.log(` cli path: ${result.cliPath}`);
443
+ console.log(` error: ${result.error ?? "unknown error"}`);
444
+ return;
445
+ }
446
+ console.log("\nPsyche runtime probe: OK\n");
447
+ console.log(` version: ${result.version}`);
448
+ console.log(` entry: ${result.entry}`);
449
+ console.log(` load path: ${result.loadPath}`);
450
+ console.log(` module path: ${result.modulePath}`);
451
+ console.log(` cli path: ${result.cliPath}`);
452
+ console.log(` processInput: ok (stimulus=${result.stimulus ?? "null"})`);
453
+ console.log(` processOutput: ok (stateChanged=${String(result.stateChanged)})`);
454
+ console.log(` replyEnvelope: ${result.canonicalHostSurface ? "present" : "missing"}`);
455
+ console.log(` externalContinuity: ${result.externalContinuityAvailable ? "present" : "missing"}`);
456
+ }
428
457
  // ── Usage ────────────────────────────────────────────────────
429
458
  function usage() {
430
459
  console.log(`
@@ -442,6 +471,7 @@ Usage:
442
471
  psyche reset <dir> [--full]
443
472
  psyche diagnose <dir> [--github] Run health checks & show diagnostic report
444
473
  psyche upgrade [--check] Check/apply package updates safely
474
+ psyche probe [--json] Verify the runtime is truly callable
445
475
  psyche profiles [--mbti TYPE] [--json]
446
476
 
447
477
  Options:
@@ -474,6 +504,9 @@ Examples:
474
504
 
475
505
  # Check for new package versions without applying them
476
506
  psyche upgrade --check
507
+
508
+ # Prove this environment can really call Psyche
509
+ psyche probe --json
477
510
  `);
478
511
  }
479
512
  // ── Main ─────────────────────────────────────────────────────
@@ -614,6 +647,17 @@ async function main() {
614
647
  await cmdUpgrade(values.check ?? false);
615
648
  break;
616
649
  }
650
+ case "probe": {
651
+ const { values } = parseArgs({
652
+ args: rest,
653
+ options: {
654
+ json: { type: "boolean", default: false },
655
+ },
656
+ allowPositionals: true,
657
+ });
658
+ await cmdProbe(values.json ?? false);
659
+ break;
660
+ }
617
661
  default:
618
662
  die(`unknown command: ${command}. Run 'psyche help' for usage.`);
619
663
  }
package/dist/core.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, WritebackCalibrationFeedback, WritebackSignalType } from "./types.js";
1
+ import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, WritebackCalibrationFeedback, WritebackSignalType, ExternalContinuityEnvelope } from "./types.js";
2
2
  import type { StorageAdapter } from "./storage.js";
3
3
  import type { DiagnosticReport, SessionMetrics } from "./diagnostics.js";
4
+ import type { ReplyEnvelope } from "./reply-envelope.js";
4
5
  export interface PsycheEngineConfig {
5
6
  mbti?: MBTIType;
6
7
  name?: string;
@@ -38,20 +39,26 @@ export interface ProcessInputResult {
38
39
  stimulus: StimulusType | null;
39
40
  /** Confidence of the primary algorithmic stimulus guess, if any */
40
41
  stimulusConfidence?: number;
41
- /** v9: Structured behavioral policy modifiers machine-readable "off baseline" signals */
42
+ /** Legacy compatibility alias: raw policy vector behind the canonical replyEnvelope. */
42
43
  policyModifiers?: PolicyModifiers;
43
- /** v9.3: Compact machine-readable subjective state for AI-first hosts */
44
+ /** v9.3+: canonical host-facing reply surface */
45
+ replyEnvelope?: ReplyEnvelope;
46
+ /** v9.3 compatibility alias: use replyEnvelope.subjectivityKernel when possible */
44
47
  subjectivityKernel?: SubjectivityKernel;
45
- /** v9.3: Compact next-reply behavioral envelope */
48
+ /** v9.3 compatibility alias: use replyEnvelope.responseContract when possible */
46
49
  responseContract?: ResponseContract;
47
- /** v9.3: Mechanical host controls derived from the reply envelope */
50
+ /** v9.3 compatibility alias: use replyEnvelope.generationControls when possible */
48
51
  generationControls?: GenerationControls;
49
52
  /** v9.2.7: cold-start carry derived from persisted relation state */
50
53
  sessionBridge?: SessionBridgeState | null;
51
54
  /** v9.2.8: sparse writeback signals evaluated on the latest turn */
52
55
  writebackFeedback?: WritebackCalibrationFeedback[];
56
+ /** v9.2.8: optional additive external continuity contract */
57
+ externalContinuity?: ExternalContinuityEnvelope<ThrongletsExport>;
58
+ /** v9.2.8: sparse low-frequency export surface suitable for Thronglets */
59
+ throngletsExports?: ThrongletsExport[];
53
60
  /**
54
- * v9: Ready-to-use LLM prompt fragment summarizing current behavioral policy.
61
+ * Legacy compatibility alias: ready-to-use prompt fragment summarizing raw policy modifiers.
55
62
  *
56
63
  * This is the output of `buildPolicyContext(policyModifiers, locale)` —
57
64
  * a human-readable string like "[行为策略] 简短回复、被动应答为主".