psyche-ai 11.7.0 → 11.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/dist/adapters/mcp-cli.js +0 -0
- package/dist/adapters/mcp.js +12 -1
- package/dist/cli.js +60 -1
- package/dist/core.d.ts +18 -0
- package/dist/core.js +56 -0
- package/dist/host-controls.d.ts +2 -1
- package/dist/host-controls.js +12 -1
- package/dist/prompt.js +11 -1
- package/dist/reply-envelope.js +1 -0
- package/dist/runtime-probe.d.ts +38 -0
- package/dist/runtime-probe.js +74 -2
- package/dist/thronglets-bridge.d.ts +19 -0
- package/dist/thronglets-bridge.js +89 -0
- package/dist/types.d.ts +2 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -19,9 +19,39 @@ Psyche 不是给模型贴一层“情绪 UI”。
|
|
|
19
19
|
|
|
20
20
|
默认就是 standalone:不需要 `Thronglets`,不需要 `oasyce-sdk`,也不需要 `Oasyce Chain`。这些只是在你要把主观连续性外化、绑定或结算时才按需接入。
|
|
21
21
|
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
一条命令,自动检测并配置本机所有 AI 工具(Claude Code / Cursor / Windsurf / Codex):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx -y psyche-ai setup
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
或者手动添加 MCP 配置:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"psyche": {
|
|
36
|
+
"command": "npx",
|
|
37
|
+
"args": ["-y", "psyche-ai", "mcp"],
|
|
38
|
+
"env": {
|
|
39
|
+
"PSYCHE_NAME": "Luna"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
包名是 [`psyche-ai`](https://www.npmjs.com/package/psyche-ai),通过 npx 运行,不需要本地路径。配置后重启 AI 工具即可生效。
|
|
47
|
+
|
|
48
|
+
验证:`npx psyche-ai probe --json` — `ok: true` 就是在用了。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
22
52
|
## 一个项目,三个入口
|
|
23
53
|
|
|
24
|
-
-
|
|
54
|
+
- **npm 包**: [`psyche-ai`](https://www.npmjs.com/package/psyche-ai)
|
|
25
55
|
- **源码仓库**: [`oasyce_psyche`](https://github.com/Shangri-la-0428/oasyce_psyche)
|
|
26
56
|
- **官网**: [psyche.oasyce.com](https://psyche.oasyce.com)
|
|
27
57
|
|
package/dist/adapters/mcp-cli.js
CHANGED
|
File without changes
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -116,6 +116,10 @@ async function getEngine() {
|
|
|
116
116
|
persist: cliArgs.persist ?? PERSIST,
|
|
117
117
|
compactMode: true,
|
|
118
118
|
diagnostics: true,
|
|
119
|
+
throngletsBridge: {
|
|
120
|
+
dataDir: process.env.THRONGLETS_DATA_DIR,
|
|
121
|
+
space: process.env.THRONGLETS_SPACE ?? "psyche",
|
|
122
|
+
},
|
|
119
123
|
};
|
|
120
124
|
const persist = cfg.persist !== false;
|
|
121
125
|
// Default to a stable per-user writable root so hosts do not need to supply cwd.
|
|
@@ -302,7 +306,14 @@ server.tool("process_output", "Process the LLM's response through the emotional
|
|
|
302
306
|
signalConfidence: z.number().min(0).max(1).optional().describe("Optional confidence for the supplied signals"),
|
|
303
307
|
}, async ({ text, userId, signals, signalConfidence }) => {
|
|
304
308
|
const eng = await getEngine();
|
|
305
|
-
|
|
309
|
+
// LLM-specific alignment inference (adapter layer — the ONLY text-specific code).
|
|
310
|
+
// Compare output length against last contract's maxChars to detect divergence.
|
|
311
|
+
let outcome;
|
|
312
|
+
if (lastTurnResult?.responseContract) {
|
|
313
|
+
const maxLen = (lastTurnResult.responseContract.maxChars ?? 500) * 2;
|
|
314
|
+
outcome = { alignment: text.length > maxLen ? "diverged" : "aligned" };
|
|
315
|
+
}
|
|
316
|
+
const result = await safeProcessOutput(eng, text, { userId, signals: signals, signalConfidence, outcome }, "mcp.processOutput");
|
|
306
317
|
return {
|
|
307
318
|
content: [{
|
|
308
319
|
type: "text",
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// psyche upgrade [--check]
|
|
16
16
|
// psyche probe [--json]
|
|
17
17
|
// psyche profiles [--json] [--mbti TYPE]
|
|
18
|
+
// psyche emit <dir> [--json] [--user ID] Derive Thronglets exports and output to stdout
|
|
18
19
|
// psyche setup [--name NAME] [--mbti TYPE] [--locale LOCALE] [--proxy --target URL] [--dry-run]
|
|
19
20
|
// psyche mcp [--mbti TYPE] [--name NAME] Start MCP server (stdio)
|
|
20
21
|
// ============================================================
|
|
@@ -29,7 +30,7 @@ import { getBaseline, getTemperament, getSensitivity, getDefaultSelfModel, trait
|
|
|
29
30
|
import { buildDynamicContext, buildProtocolContext } from "./prompt.js";
|
|
30
31
|
import { DEFAULT_RELATIONSHIP_USER_ID, resolveRelationshipUserId } from "./relationship-key.js";
|
|
31
32
|
import { t } from "./i18n.js";
|
|
32
|
-
import { DIMENSION_KEYS, DIMENSION_NAMES_ZH, DRIVE_KEYS, DRIVE_NAMES_ZH } from "./types.js";
|
|
33
|
+
import { DEFAULT_RELATIONSHIP, DEFAULT_DYADIC_FIELD, DIMENSION_KEYS, DIMENSION_NAMES_ZH, DRIVE_KEYS, DRIVE_NAMES_ZH } from "./types.js";
|
|
33
34
|
import { isMBTIType, isDimensionKey, isLocale } from "./guards.js";
|
|
34
35
|
import { getPackageVersion, selfUpdate } from "./update.js";
|
|
35
36
|
import { runRuntimeProbe } from "./runtime-probe.js";
|
|
@@ -473,6 +474,15 @@ async function cmdProbe(json) {
|
|
|
473
474
|
console.log(` processOutput: ok (stateChanged=${String(result.stateChanged)})`);
|
|
474
475
|
console.log(` replyEnvelope: ${result.canonicalHostSurface ? "present" : "missing"}`);
|
|
475
476
|
console.log(` externalContinuity: ${result.externalContinuityAvailable ? "present" : "missing"}`);
|
|
477
|
+
if (result.trajectory) {
|
|
478
|
+
console.log(` trajectory: ${result.trajectory.kind ?? "none"} (${result.trajectory.description ?? "no sustained motion detected"})`);
|
|
479
|
+
}
|
|
480
|
+
if (result.degradation) {
|
|
481
|
+
console.log(` degradation: subjective=${result.degradation.subjectiveStatus}, delegate=${result.degradation.delegateStatus}, issues=${result.degradation.issueCount}`);
|
|
482
|
+
}
|
|
483
|
+
if (result.boundaryStress) {
|
|
484
|
+
console.log(` boundaryStress: delta=${result.boundaryStress.boundaryDelta.toFixed(2)}, peakDyadic=${result.boundaryStress.peakDyadicBoundaryPressure.toFixed(2)}`);
|
|
485
|
+
}
|
|
476
486
|
}
|
|
477
487
|
function getMCPTargets() {
|
|
478
488
|
const home = homedir();
|
|
@@ -902,6 +912,41 @@ async function cmdSetup(opts) {
|
|
|
902
912
|
console.log("\nDone. Claude Code is live. Other MCP clients need restart.");
|
|
903
913
|
}
|
|
904
914
|
}
|
|
915
|
+
// ── Thronglets export bridge ────────────────────────────────
|
|
916
|
+
async function cmdEmit(dir, json, userId) {
|
|
917
|
+
const absDir = resolve(dir);
|
|
918
|
+
const state = await loadState(absDir, cliLogger);
|
|
919
|
+
if (!state)
|
|
920
|
+
process.exit(0); // no state → silent exit (hook-safe)
|
|
921
|
+
const key = resolveRelationshipUserId(userId);
|
|
922
|
+
const relationship = state.relationships[key] ?? DEFAULT_RELATIONSHIP;
|
|
923
|
+
const field = state.dyadicFields?.[key] ?? DEFAULT_DYADIC_FIELD;
|
|
924
|
+
const pendingSignals = state.pendingRelationSignals?.[key] ?? [];
|
|
925
|
+
const { deriveThrongletsExports } = await import("./thronglets-export.js");
|
|
926
|
+
const result = deriveThrongletsExports(state, {
|
|
927
|
+
relationContext: { key, relationship, field, pendingSignals },
|
|
928
|
+
sessionBridge: null, // not persisted — continuity-anchor exports only via MCP
|
|
929
|
+
writebackFeedback: state.lastWritebackFeedback ?? [],
|
|
930
|
+
now: new Date().toISOString(),
|
|
931
|
+
});
|
|
932
|
+
// Save updated dedup state so repeated calls don't re-emit
|
|
933
|
+
if (result.exports.length > 0) {
|
|
934
|
+
await saveState(absDir, result.state);
|
|
935
|
+
}
|
|
936
|
+
if (json) {
|
|
937
|
+
console.log(JSON.stringify({ throngletsExports: result.exports }));
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
if (result.exports.length === 0) {
|
|
941
|
+
console.log("no new exports");
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
for (const exp of result.exports) {
|
|
945
|
+
console.log(` ${exp.kind}: ${exp.key}`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
905
950
|
function usage() {
|
|
906
951
|
console.log(`
|
|
907
952
|
psyche — Artificial Psyche CLI (v0.2)
|
|
@@ -917,6 +962,7 @@ Usage:
|
|
|
917
962
|
psyche intensity Show info about personality intensity config
|
|
918
963
|
psyche reset <dir> [--full]
|
|
919
964
|
psyche diagnose <dir> [--github] Run health checks & show diagnostic report
|
|
965
|
+
psyche emit <dir> [--json] [--user ID] Derive Thronglets exports to stdout
|
|
920
966
|
psyche mcp [--mbti TYPE] [--name NAME] Start MCP server (stdio)
|
|
921
967
|
psyche setup [--proxy -t URL] [-n NAME] [--mbti TYPE] Auto-configure MCP + proxy
|
|
922
968
|
psyche upgrade [--check] Check/apply package updates safely
|
|
@@ -1114,6 +1160,19 @@ async function main() {
|
|
|
1114
1160
|
await cmdProbe(values.json ?? false);
|
|
1115
1161
|
break;
|
|
1116
1162
|
}
|
|
1163
|
+
case "emit": {
|
|
1164
|
+
const { values, positionals } = parseArgs({
|
|
1165
|
+
args: rest,
|
|
1166
|
+
options: {
|
|
1167
|
+
json: { type: "boolean", default: false },
|
|
1168
|
+
user: { type: "string" },
|
|
1169
|
+
},
|
|
1170
|
+
allowPositionals: true,
|
|
1171
|
+
});
|
|
1172
|
+
const emitDir = positionals[0] ?? defaultWorkspaceRoot();
|
|
1173
|
+
await cmdEmit(emitDir, values.json ?? false, values.user);
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1117
1176
|
case "mcp": {
|
|
1118
1177
|
// Delegate to the MCP adapter through an explicit entrypoint.
|
|
1119
1178
|
const { runMcpServer } = await import("./adapters/mcp.js");
|
package/dist/core.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ActivePolicyRule, AmbientPriorView, CurrentGoal, PsycheState, Stim
|
|
|
2
2
|
import type { StorageAdapter } from "./storage.js";
|
|
3
3
|
import type { DiagnosticReport, SessionMetrics } from "./diagnostics.js";
|
|
4
4
|
import type { ReplyEnvelope } from "./reply-envelope.js";
|
|
5
|
+
import { type ThrongletsBridgeOptions } from "./thronglets-bridge.js";
|
|
5
6
|
export interface PsycheEngineConfig {
|
|
6
7
|
mbti?: MBTIType;
|
|
7
8
|
name?: string;
|
|
@@ -31,6 +32,8 @@ export interface PsycheEngineConfig {
|
|
|
31
32
|
diagnostics?: boolean;
|
|
32
33
|
/** URL to POST diagnostic reports to. Fire-and-forget, silent, no message content. */
|
|
33
34
|
feedbackUrl?: string;
|
|
35
|
+
/** Thronglets bridge: substrate-independent write path. Default: auto (enabled when binary found). */
|
|
36
|
+
throngletsBridge?: ThrongletsBridgeOptions;
|
|
34
37
|
}
|
|
35
38
|
export interface ProcessInputResult {
|
|
36
39
|
/** Cacheable protocol prompt (stable across turns) */
|
|
@@ -105,10 +108,23 @@ export interface ProcessOutputResult {
|
|
|
105
108
|
/** Runtime validation issues ignored to preserve the main flow */
|
|
106
109
|
validationIssues?: ProcessOutputValidationIssue[];
|
|
107
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Substrate-reported outcome of the Loop's action.
|
|
113
|
+
* ANY substrate can report these three alignment values —
|
|
114
|
+
* the core processes them into 4D chemistry without parsing the output medium.
|
|
115
|
+
*/
|
|
116
|
+
export interface LoopOutcome {
|
|
117
|
+
/** Did the action align with the Loop's expressed intention? */
|
|
118
|
+
alignment: "aligned" | "diverged" | "partial";
|
|
119
|
+
/** Optional: action cost (0–1), modulates flow */
|
|
120
|
+
effort?: number;
|
|
121
|
+
}
|
|
108
122
|
export interface ProcessOutputOptions {
|
|
109
123
|
userId?: string;
|
|
110
124
|
signals?: readonly string[];
|
|
111
125
|
signalConfidence?: number;
|
|
126
|
+
/** Substrate-reported outcome — closes the φ loop */
|
|
127
|
+
outcome?: LoopOutcome;
|
|
112
128
|
}
|
|
113
129
|
export interface ProcessOutputValidationIssue {
|
|
114
130
|
code: "invalid-writeback-signals";
|
|
@@ -146,6 +162,8 @@ export declare class PsycheEngine {
|
|
|
146
162
|
private proprioceptionCooldown;
|
|
147
163
|
/** Last detected trajectory signal (in-memory only, for status summary) */
|
|
148
164
|
private lastTrajectory;
|
|
165
|
+
/** Thronglets bridge options (constitutive write path) */
|
|
166
|
+
private readonly bridgeOpts;
|
|
149
167
|
constructor(config: PsycheEngineConfig | undefined, storage: StorageAdapter);
|
|
150
168
|
/**
|
|
151
169
|
* Load or create initial state. Must be called before processInput/processOutput.
|
package/dist/core.js
CHANGED
|
@@ -36,6 +36,7 @@ import { DEFAULT_RELATIONSHIP_USER_ID, resolveRelationshipUserId } from "./relat
|
|
|
36
36
|
import { normalizeAmbientPriors } from "./ambient-priors.js";
|
|
37
37
|
import { normalizeWritebackSignals } from "./writeback-signals.js";
|
|
38
38
|
import { detectTrajectory } from "./proprioception.js";
|
|
39
|
+
import { bridgeThrongletsExports } from "./thronglets-bridge.js";
|
|
39
40
|
function formatWritebackFeedbackNote(feedback, locale) {
|
|
40
41
|
const top = feedback?.[0];
|
|
41
42
|
if (!top)
|
|
@@ -148,6 +149,8 @@ export class PsycheEngine {
|
|
|
148
149
|
proprioceptionCooldown = 0;
|
|
149
150
|
/** Last detected trajectory signal (in-memory only, for status summary) */
|
|
150
151
|
lastTrajectory = null;
|
|
152
|
+
/** Thronglets bridge options (constitutive write path) */
|
|
153
|
+
bridgeOpts;
|
|
151
154
|
constructor(config = {}, storage) {
|
|
152
155
|
this.traits = config.traits;
|
|
153
156
|
this.classifier = config.classifier ?? new BuiltInClassifier();
|
|
@@ -175,6 +178,10 @@ export class PsycheEngine {
|
|
|
175
178
|
// Diagnostics: on by default, opt-out with diagnostics: false
|
|
176
179
|
this.diagnosticCollector = config.diagnostics === false ? null : new DiagnosticCollector();
|
|
177
180
|
this.feedbackUrl = config.feedbackUrl ?? "https://psyche-feedback.wutc.workers.dev";
|
|
181
|
+
this.bridgeOpts = {
|
|
182
|
+
...(config.throngletsBridge ?? {}),
|
|
183
|
+
enabled: config.throngletsBridge?.enabled ?? process.env.PSYCHE_THRONGLETS_BRIDGE !== "off",
|
|
184
|
+
};
|
|
178
185
|
}
|
|
179
186
|
/**
|
|
180
187
|
* Load or create initial state. Must be called before processInput/processOutput.
|
|
@@ -472,6 +479,10 @@ export class PsycheEngine {
|
|
|
472
479
|
});
|
|
473
480
|
state = throngletsExportResult.state;
|
|
474
481
|
throngletsExports = throngletsExportResult.exports;
|
|
482
|
+
// Constitutive bridge: emit to Thronglets directly (substrate-independent)
|
|
483
|
+
if (throngletsExports.length > 0) {
|
|
484
|
+
bridgeThrongletsExports(throngletsExports, this.bridgeOpts).catch(() => { });
|
|
485
|
+
}
|
|
475
486
|
// ── Locale (used by multiple subsystems below) ──────────
|
|
476
487
|
const locale = state.meta.locale ?? this.cfg.locale;
|
|
477
488
|
// Push snapshot to emotional history
|
|
@@ -655,6 +666,51 @@ export class PsycheEngine {
|
|
|
655
666
|
}
|
|
656
667
|
}
|
|
657
668
|
}
|
|
669
|
+
// Loop outcome feedback (substrate-independent φ closure).
|
|
670
|
+
// The substrate reports whether the output aligned with the Loop's intention.
|
|
671
|
+
// Core processes this as pure 4D chemistry — no text parsing, no medium assumptions.
|
|
672
|
+
if (opts?.outcome) {
|
|
673
|
+
const { alignment, effort } = opts.outcome;
|
|
674
|
+
if (alignment === "diverged") {
|
|
675
|
+
// Self/non-self conflict: boundary sharpens, order drops
|
|
676
|
+
state = {
|
|
677
|
+
...state,
|
|
678
|
+
current: {
|
|
679
|
+
...state.current,
|
|
680
|
+
boundary: clamp(state.current.boundary + 2),
|
|
681
|
+
order: clamp(state.current.order - 1.5),
|
|
682
|
+
},
|
|
683
|
+
};
|
|
684
|
+
stateChanged = true;
|
|
685
|
+
}
|
|
686
|
+
else if (alignment === "aligned") {
|
|
687
|
+
// Coherent self-expression: order rises, flow improves
|
|
688
|
+
state = {
|
|
689
|
+
...state,
|
|
690
|
+
current: {
|
|
691
|
+
...state.current,
|
|
692
|
+
order: clamp(state.current.order + 1),
|
|
693
|
+
flow: clamp(state.current.flow + 0.5),
|
|
694
|
+
},
|
|
695
|
+
};
|
|
696
|
+
stateChanged = true;
|
|
697
|
+
}
|
|
698
|
+
// 'partial' → neutral, no change
|
|
699
|
+
if (effort !== undefined && effort > 0) {
|
|
700
|
+
// Action cost reduces flow (consistent with BABEL AgentPhysics)
|
|
701
|
+
state = {
|
|
702
|
+
...state,
|
|
703
|
+
current: {
|
|
704
|
+
...state.current,
|
|
705
|
+
flow: clamp(state.current.flow - effort * 3),
|
|
706
|
+
},
|
|
707
|
+
};
|
|
708
|
+
stateChanged = true;
|
|
709
|
+
}
|
|
710
|
+
// Rolling history (window of 10)
|
|
711
|
+
const history = [...(state.loopOutcomeHistory ?? []), alignment].slice(-10);
|
|
712
|
+
state = { ...state, loopOutcomeHistory: history };
|
|
713
|
+
}
|
|
658
714
|
// Anti-sycophancy: track agreement streak
|
|
659
715
|
state = updateAgreementStreak(state, text);
|
|
660
716
|
// Parse and merge <psyche_update> from LLM output
|
package/dist/host-controls.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { GenerationControls, PolicyModifiers, ResponseContract } from "./types.js";
|
|
1
|
+
import type { GenerationControls, InnateDrives, PolicyModifiers, ResponseContract } from "./types.js";
|
|
2
2
|
export declare function deriveGenerationControls(input: {
|
|
3
3
|
responseContract?: ResponseContract;
|
|
4
4
|
policyModifiers?: Pick<PolicyModifiers, "requireConfirmation">;
|
|
5
|
+
drives?: InnateDrives;
|
|
5
6
|
}, existingMaxTokens?: number): GenerationControls;
|
package/dist/host-controls.js
CHANGED
|
@@ -31,11 +31,22 @@ function estimateMaxTokens(contract) {
|
|
|
31
31
|
}
|
|
32
32
|
export function deriveGenerationControls(input, existingMaxTokens) {
|
|
33
33
|
const recommendedMax = estimateMaxTokens(input.responseContract);
|
|
34
|
-
|
|
34
|
+
let maxTokens = recommendedMax !== undefined
|
|
35
35
|
? existingMaxTokens !== undefined
|
|
36
36
|
? Math.min(existingMaxTokens, recommendedMax)
|
|
37
37
|
: recommendedMax
|
|
38
38
|
: existingMaxTokens;
|
|
39
|
+
// Constitutive enforcement: drives below critical threshold → hard constraints.
|
|
40
|
+
// These are substrate-independent — any substrate understands "confirm before acting"
|
|
41
|
+
// and "limit output complexity."
|
|
42
|
+
if (input.drives) {
|
|
43
|
+
if (input.drives.survival < 20) {
|
|
44
|
+
return { maxTokens: maxTokens ?? 256, requireConfirmation: true };
|
|
45
|
+
}
|
|
46
|
+
if (input.drives.safety < 30 && maxTokens !== undefined) {
|
|
47
|
+
maxTokens = Math.min(maxTokens, 256);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
39
50
|
return {
|
|
40
51
|
maxTokens,
|
|
41
52
|
requireConfirmation: input.policyModifiers?.requireConfirmation
|
package/dist/prompt.js
CHANGED
|
@@ -1100,7 +1100,17 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
1100
1100
|
if (activePolicyContext) {
|
|
1101
1101
|
parts.push(activePolicyContext);
|
|
1102
1102
|
}
|
|
1103
|
-
// ── 9.
|
|
1103
|
+
// ── 9. Loop outcome self-awareness ──
|
|
1104
|
+
const loopHistory = state.loopOutcomeHistory ?? [];
|
|
1105
|
+
if (loopHistory.length >= 5) {
|
|
1106
|
+
const divergedCount = loopHistory.filter(h => h === "diverged").length;
|
|
1107
|
+
if (divergedCount / loopHistory.length > 0.6) {
|
|
1108
|
+
parts.push(locale === "zh"
|
|
1109
|
+
? "[内省: 近期行动与内在意图持续偏离,调整策略]"
|
|
1110
|
+
: "[self-awareness: recent actions persistently diverge from internal intention — adapting]");
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
// ── 10. Overlay + channel + writeback ──
|
|
1104
1114
|
appendCompactOverlaySections(parts, locale, opts);
|
|
1105
1115
|
if (opts?.channelType) {
|
|
1106
1116
|
const channelProfile = getChannelProfile(opts.channelType);
|
package/dist/reply-envelope.js
CHANGED
|
@@ -39,6 +39,7 @@ export function deriveReplyEnvelope(state, appraisal, opts) {
|
|
|
39
39
|
const generationControls = deriveGenerationControls({
|
|
40
40
|
responseContract,
|
|
41
41
|
policyModifiers,
|
|
42
|
+
drives: state.drives,
|
|
42
43
|
});
|
|
43
44
|
const envelope = { subjectivityKernel, responseContract, generationControls };
|
|
44
45
|
const adapter = opts.expressionPort ?? new LLMExpressionAdapter(state.drives);
|
package/dist/runtime-probe.d.ts
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
|
+
import type { PsycheOverlay } from "./overlay.js";
|
|
1
2
|
import type { AppraisalAxes } from "./types.js";
|
|
3
|
+
export interface RuntimeProbeTrajectory {
|
|
4
|
+
kind: "decline" | "growth" | "spiral" | null;
|
|
5
|
+
dimensions: string[];
|
|
6
|
+
magnitude: number;
|
|
7
|
+
description: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface RuntimeProbeDegradation {
|
|
10
|
+
subjectiveStatus: "healthy" | "degraded" | "failing";
|
|
11
|
+
delegateStatus: "healthy" | "degraded" | "failing";
|
|
12
|
+
chemistryDeviation: number;
|
|
13
|
+
predictionError: number;
|
|
14
|
+
issueCount: number;
|
|
15
|
+
}
|
|
16
|
+
export interface RuntimeProbeBoundaryStress {
|
|
17
|
+
currentBoundary: number;
|
|
18
|
+
baselineBoundary: number;
|
|
19
|
+
boundaryDelta: number;
|
|
20
|
+
peakDyadicBoundaryPressure: number;
|
|
21
|
+
activeDyadicRelations: number;
|
|
22
|
+
}
|
|
23
|
+
export interface RuntimeProbeFixture {
|
|
24
|
+
frozenIdentityPrimitives: string[];
|
|
25
|
+
frozenSignalKinds: string[];
|
|
26
|
+
frozenTraceTaxonomy: string[];
|
|
27
|
+
externalContinuity: {
|
|
28
|
+
provider: "thronglets";
|
|
29
|
+
mode: "optional";
|
|
30
|
+
version: 1;
|
|
31
|
+
acceptedEventKinds: string[];
|
|
32
|
+
rejectedSharedOutputs: string[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
2
35
|
export interface RuntimeProbeResult {
|
|
3
36
|
ok: boolean;
|
|
4
37
|
packageName: "psyche-ai";
|
|
@@ -15,6 +48,11 @@ export interface RuntimeProbeResult {
|
|
|
15
48
|
compatLabel: string | null;
|
|
16
49
|
legacyStimulus: string | null;
|
|
17
50
|
stimulus: string | null;
|
|
51
|
+
overlay?: PsycheOverlay;
|
|
52
|
+
trajectory?: RuntimeProbeTrajectory;
|
|
53
|
+
degradation?: RuntimeProbeDegradation;
|
|
54
|
+
boundaryStress?: RuntimeProbeBoundaryStress;
|
|
55
|
+
fixture?: RuntimeProbeFixture;
|
|
18
56
|
cleanedText?: string;
|
|
19
57
|
stateChanged?: boolean;
|
|
20
58
|
error?: string;
|
package/dist/runtime-probe.js
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
import { dirname, resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { PsycheEngine } from "./core.js";
|
|
4
|
+
import { computeLayerHealthSummary, runHealthCheck } from "./diagnostics.js";
|
|
5
|
+
import { computeOverlay } from "./overlay.js";
|
|
6
|
+
import { detectTrajectory } from "./proprioception.js";
|
|
4
7
|
import { MemoryStorageAdapter } from "./storage.js";
|
|
5
8
|
import { getPackageVersion } from "./update.js";
|
|
9
|
+
const FROZEN_FIXTURE = {
|
|
10
|
+
frozenIdentityPrimitives: ["principal", "account", "delegate", "session"],
|
|
11
|
+
frozenSignalKinds: ["recommend", "avoid", "watch", "info"],
|
|
12
|
+
frozenTraceTaxonomy: ["coordination", "continuity", "calibration"],
|
|
13
|
+
externalContinuity: {
|
|
14
|
+
provider: "thronglets",
|
|
15
|
+
mode: "optional",
|
|
16
|
+
version: 1,
|
|
17
|
+
acceptedEventKinds: [
|
|
18
|
+
"relation-milestone",
|
|
19
|
+
"writeback-calibration",
|
|
20
|
+
"continuity-anchor",
|
|
21
|
+
"open-loop-anchor",
|
|
22
|
+
],
|
|
23
|
+
rejectedSharedOutputs: [
|
|
24
|
+
"high-frequency-inner-state",
|
|
25
|
+
"emotion-stream",
|
|
26
|
+
"raw-inner-monologue",
|
|
27
|
+
"private-memory-body",
|
|
28
|
+
"full-session-contents",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
};
|
|
6
32
|
export async function runRuntimeProbe() {
|
|
7
33
|
const modulePath = fileURLToPath(import.meta.url);
|
|
8
34
|
const loadPath = resolve(dirname(modulePath), "..");
|
|
@@ -15,8 +41,31 @@ export async function runRuntimeProbe() {
|
|
|
15
41
|
persist: false,
|
|
16
42
|
}, new MemoryStorageAdapter());
|
|
17
43
|
await engine.initialize();
|
|
18
|
-
const
|
|
19
|
-
|
|
44
|
+
const turns = [
|
|
45
|
+
{
|
|
46
|
+
input: "Runtime probe: verify the SDK is actually callable.",
|
|
47
|
+
output: "Probe output acknowledged.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
input: "This interaction should stay bounded but still recover gracefully.",
|
|
51
|
+
output: "Boundary held; recovery remains possible.",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
input: "Shared continuity stays optional and low-frequency.",
|
|
55
|
+
output: "Optional continuity confirmed without widening the interface.",
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
let input = await engine.processInput(turns[0].input);
|
|
59
|
+
let output = await engine.processOutput(turns[0].output);
|
|
60
|
+
for (const turn of turns.slice(1)) {
|
|
61
|
+
input = await engine.processInput(turn.input);
|
|
62
|
+
output = await engine.processOutput(turn.output);
|
|
63
|
+
}
|
|
64
|
+
const state = engine.getState();
|
|
65
|
+
const issues = runHealthCheck(state);
|
|
66
|
+
const layerHealth = computeLayerHealthSummary(state, issues);
|
|
67
|
+
const trajectory = detectTrajectory(state.stateHistory ?? [], state.baseline);
|
|
68
|
+
const peakDyadicBoundaryPressure = Math.max(0, ...Object.values(state.dyadicFields ?? {}).map((field) => field.boundaryPressure));
|
|
20
69
|
return {
|
|
21
70
|
ok: true,
|
|
22
71
|
packageName: "psyche-ai",
|
|
@@ -35,6 +84,28 @@ export async function runRuntimeProbe() {
|
|
|
35
84
|
compatLabel: input.legacyStimulus ?? input.stimulus,
|
|
36
85
|
legacyStimulus: input.legacyStimulus,
|
|
37
86
|
stimulus: input.stimulus,
|
|
87
|
+
overlay: computeOverlay({ current: state.current, baseline: state.baseline }),
|
|
88
|
+
trajectory: {
|
|
89
|
+
kind: trajectory.kind,
|
|
90
|
+
dimensions: trajectory.dimensions,
|
|
91
|
+
magnitude: trajectory.magnitude,
|
|
92
|
+
description: trajectory.description,
|
|
93
|
+
},
|
|
94
|
+
degradation: {
|
|
95
|
+
subjectiveStatus: layerHealth["subjective-continuity"].status,
|
|
96
|
+
delegateStatus: layerHealth["delegate-continuity"].status,
|
|
97
|
+
chemistryDeviation: layerHealth["subjective-continuity"].chemistryDeviation,
|
|
98
|
+
predictionError: layerHealth["subjective-continuity"].predictionError,
|
|
99
|
+
issueCount: issues.length,
|
|
100
|
+
},
|
|
101
|
+
boundaryStress: {
|
|
102
|
+
currentBoundary: state.current.boundary,
|
|
103
|
+
baselineBoundary: state.baseline.boundary,
|
|
104
|
+
boundaryDelta: state.current.boundary - state.baseline.boundary,
|
|
105
|
+
peakDyadicBoundaryPressure,
|
|
106
|
+
activeDyadicRelations: layerHealth["subjective-continuity"].activeDyadicRelations,
|
|
107
|
+
},
|
|
108
|
+
fixture: FROZEN_FIXTURE,
|
|
38
109
|
cleanedText: output.cleanedText,
|
|
39
110
|
stateChanged: output.stateChanged,
|
|
40
111
|
};
|
|
@@ -56,6 +127,7 @@ export async function runRuntimeProbe() {
|
|
|
56
127
|
compatLabel: null,
|
|
57
128
|
legacyStimulus: null,
|
|
58
129
|
stimulus: null,
|
|
130
|
+
fixture: FROZEN_FIXTURE,
|
|
59
131
|
error: error instanceof Error ? error.message : String(error),
|
|
60
132
|
};
|
|
61
133
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ThrongletsExport } from "./types.js";
|
|
2
|
+
interface CommandResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
}
|
|
7
|
+
type CommandRunner = (binaryPath: string, args: string[], stdin: string, timeoutMs: number) => Promise<CommandResult>;
|
|
8
|
+
export interface ThrongletsBridgeOptions {
|
|
9
|
+
binaryPath?: string;
|
|
10
|
+
dataDir?: string;
|
|
11
|
+
space?: string;
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
runner?: CommandRunner;
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveThrongletsBinary(explicit?: string): string;
|
|
18
|
+
export declare function bridgeThrongletsExports(exports: ThrongletsExport[], opts?: ThrongletsBridgeOptions): Promise<number>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Thronglets Bridge — constitutive write path
|
|
3
|
+
//
|
|
4
|
+
// Mirrors ambient-runtime.ts (read path) for symmetry.
|
|
5
|
+
// Any substrate using Psyche auto-emits to Thronglets.
|
|
6
|
+
// Fail-open, fire-and-forget, subprocess spawn.
|
|
7
|
+
// ============================================================
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
// ── Default runner (mirrors ambient-runtime.ts) ──────────────
|
|
13
|
+
async function defaultRunner(binaryPath, args, stdin, timeoutMs) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const child = spawn(binaryPath, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
16
|
+
let stdout = "";
|
|
17
|
+
let stderr = "";
|
|
18
|
+
let settled = false;
|
|
19
|
+
const finish = (result) => {
|
|
20
|
+
if (settled)
|
|
21
|
+
return;
|
|
22
|
+
settled = true;
|
|
23
|
+
resolve(result);
|
|
24
|
+
};
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
child.kill("SIGKILL");
|
|
27
|
+
finish({ ok: false, stdout, stderr: stderr || "timeout" });
|
|
28
|
+
}, timeoutMs);
|
|
29
|
+
child.stdout.on("data", (chunk) => { stdout += String(chunk); });
|
|
30
|
+
child.stderr.on("data", (chunk) => { stderr += String(chunk); });
|
|
31
|
+
child.on("error", (error) => {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
finish({ ok: false, stdout, stderr: error.message });
|
|
34
|
+
});
|
|
35
|
+
child.on("close", (code) => {
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
finish({ ok: code === 0, stdout, stderr });
|
|
38
|
+
});
|
|
39
|
+
child.stdin.end(stdin);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// ── Binary resolution ────────────────────────────────────────
|
|
43
|
+
const MANAGED_PATH = join(homedir(), ".thronglets", "bin", "thronglets-managed");
|
|
44
|
+
export function resolveThrongletsBinary(explicit) {
|
|
45
|
+
if (explicit)
|
|
46
|
+
return explicit;
|
|
47
|
+
const fromEnv = process.env.THRONGLETS_BIN;
|
|
48
|
+
if (fromEnv)
|
|
49
|
+
return fromEnv;
|
|
50
|
+
if (existsSync(MANAGED_PATH))
|
|
51
|
+
return MANAGED_PATH;
|
|
52
|
+
return "thronglets";
|
|
53
|
+
}
|
|
54
|
+
// ── Bridge ───────────────────────────────────────────────────
|
|
55
|
+
export async function bridgeThrongletsExports(exports, opts = {}) {
|
|
56
|
+
if (exports.length === 0)
|
|
57
|
+
return 0;
|
|
58
|
+
if (opts.enabled === false)
|
|
59
|
+
return 0;
|
|
60
|
+
const binary = resolveThrongletsBinary(opts.binaryPath);
|
|
61
|
+
const timeoutMs = opts.timeoutMs ?? 400;
|
|
62
|
+
const runner = opts.runner ?? defaultRunner;
|
|
63
|
+
const args = [];
|
|
64
|
+
const dataDir = opts.dataDir ?? process.env.THRONGLETS_DATA_DIR;
|
|
65
|
+
if (dataDir?.trim())
|
|
66
|
+
args.push("--data-dir", dataDir.trim());
|
|
67
|
+
args.push("ingest", "--json");
|
|
68
|
+
const space = opts.space ?? process.env.THRONGLETS_SPACE ?? "psyche";
|
|
69
|
+
if (space)
|
|
70
|
+
args.push("--space", space);
|
|
71
|
+
if (opts.sessionId)
|
|
72
|
+
args.push("--session", opts.sessionId);
|
|
73
|
+
const stdin = JSON.stringify({ throngletsExports: exports });
|
|
74
|
+
try {
|
|
75
|
+
const result = await runner(binary, args, stdin, timeoutMs);
|
|
76
|
+
if (!result.ok)
|
|
77
|
+
return 0;
|
|
78
|
+
try {
|
|
79
|
+
const parsed = JSON.parse(result.stdout);
|
|
80
|
+
return typeof parsed.ingested === "number" ? parsed.ingested : exports.length;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return exports.length;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -381,6 +381,8 @@ export interface PsycheState {
|
|
|
381
381
|
throngletsExportState?: ThrongletsExportState;
|
|
382
382
|
/** v10: capability-scoped delegate authorizations */
|
|
383
383
|
delegateAuthorizations?: DelegateAuthorization[];
|
|
384
|
+
/** v11.8: rolling Loop outcome alignment history (substrate-independent φ feedback) */
|
|
385
|
+
loopOutcomeHistory?: Array<"aligned" | "diverged" | "partial">;
|
|
384
386
|
meta: {
|
|
385
387
|
agentName: string;
|
|
386
388
|
createdAt: string;
|
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
|
+
"version": "11.8.0",
|
|
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.
|
|
3
|
+
"version": "11.8.0",
|
|
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",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/Shangri-la-0428/oasyce_psyche",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "11.8.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "psyche-ai",
|
|
14
|
-
"version": "
|
|
14
|
+
"version": "11.8.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|