psyche-ai 9.2.8 → 9.2.10

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-1307%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
 
@@ -209,6 +209,30 @@ npm view psyche-ai version
209
209
  - `git` 工作树和本地路径安装不会偷偷改你的代码;脏工作树只会提示手动命令。
210
210
  - OpenClaw 如果是从当前仓库本地加载,升级后记得重启 gateway 以载入新的 `dist/`。
211
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
+
212
236
  ---
213
237
 
214
238
  ## 它和你认知中的 AI 完全不同
@@ -302,6 +326,14 @@ Psyche 现在更准确的目标,不是“模仿某种像人的风格”,而
302
326
 
303
327
  当前主线里,`processInput()` 会直接返回 `replyEnvelope` 作为规范主接口;原有平级字段继续保留为兼容别名,避免打断现有宿主。`policyModifiers` 不再属于规范主接口,只保留为 legacy raw vector。
304
328
 
329
+ 在这个规范主接口之外,`processInput()` 现在还会可选返回一个很薄的 `observability` side-channel。它不是新的控制面,也不会反向改写 `replyEnvelope`;它只回答三件事:
330
+
331
+ - 这轮当前由哪一个控制平面主导
332
+ - 当前 turn / writeback / session bridge / persisted relationship 四层是怎么对账的
333
+ - 这次 `work/private` 选择为什么会落在这个 profile 上
334
+
335
+ 这样别的 agent 或宿主不用再自己猜“现在到底是谁在主导”和“为什么这次选了这个策略”,但主链路仍然只有 `replyEnvelope` 一个真相源。
336
+
305
337
  ### 内在世界
306
338
 
307
339
  Psyche 给 AI 一个始终运转的内在自我——不是条件触发,而是每时每刻都在:
@@ -523,7 +555,7 @@ Psyche 核心引擎永久开源(MIT)。
523
555
  ```bash
524
556
  npm install
525
557
  npm run build
526
- npm test # 1307 tests
558
+ npm test # 1308 tests
527
559
  npm run typecheck # strict mode
528
560
  ```
529
561
 
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,4 +1,4 @@
1
- import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, WritebackCalibrationFeedback, WritebackSignalType, ExternalContinuityEnvelope } from "./types.js";
1
+ import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, TurnObservability, WritebackCalibrationFeedback, WritebackSignalType, ExternalContinuityEnvelope } 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";
@@ -57,6 +57,8 @@ export interface ProcessInputResult {
57
57
  externalContinuity?: ExternalContinuityEnvelope<ThrongletsExport>;
58
58
  /** v9.2.8: sparse low-frequency export surface suitable for Thronglets */
59
59
  throngletsExports?: ThrongletsExport[];
60
+ /** v9.2.10: low-cost side channel for control boundary, state layers, and output attribution */
61
+ observability?: TurnObservability;
60
62
  /**
61
63
  * Legacy compatibility alias: ready-to-use prompt fragment summarizing raw policy modifiers.
62
64
  *
package/dist/core.js CHANGED
@@ -28,6 +28,7 @@ import { runReflectiveTurnPhases } from "./input-turn.js";
28
28
  import { applyRelationalTurn, applySessionBridge, applyWritebackSignals, createWritebackCalibrations, evaluateWritebackCalibrations } from "./relation-dynamics.js";
29
29
  import { buildExternalContinuityEnvelope } from "./external-continuity.js";
30
30
  import { deriveThrongletsExports } from "./thronglets-export.js";
31
+ import { buildTurnObservability } from "./observability.js";
31
32
  function formatWritebackFeedbackNote(feedback, locale) {
32
33
  const top = feedback?.[0];
33
34
  if (!top)
@@ -480,6 +481,17 @@ export class PsycheEngine {
480
481
  responseContractContext: derivedReplyEnvelope.responseContractContext,
481
482
  policyContext: derivedReplyEnvelope.policyContext || undefined,
482
483
  };
484
+ const observability = buildTurnObservability(state, {
485
+ replyEnvelope,
486
+ promptRenderInputs,
487
+ compactMode: this.cfg.compactMode,
488
+ stimulus: appliedStimulus,
489
+ userText: text || undefined,
490
+ sessionBridge,
491
+ writebackFeedback,
492
+ relationContext: relationalTurn.relationContext,
493
+ externalContinuityExports: throngletsExports.length,
494
+ });
483
495
  if (this.cfg.compactMode) {
484
496
  const externalContinuity = buildExternalContinuityEnvelope(throngletsExports);
485
497
  return {
@@ -496,6 +508,7 @@ export class PsycheEngine {
496
508
  writebackFeedback,
497
509
  externalContinuity,
498
510
  throngletsExports,
511
+ observability,
499
512
  policyContext: derivedReplyEnvelope.policyContext,
500
513
  };
501
514
  }
@@ -514,6 +527,7 @@ export class PsycheEngine {
514
527
  writebackFeedback,
515
528
  externalContinuity,
516
529
  throngletsExports,
530
+ observability,
517
531
  policyContext: derivedReplyEnvelope.policyContext,
518
532
  };
519
533
  }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { PsycheEngine } from "./core.js";
2
2
  export type { PsycheEngineConfig, ProcessInputResult, ProcessOutputResult, ProcessOutcomeResult } from "./core.js";
3
3
  export { FileStorageAdapter, MemoryStorageAdapter } from "./storage.js";
4
4
  export type { StorageAdapter } from "./storage.js";
5
- export type { PsycheState, MBTIType, Locale, StimulusType, ChemicalState, ChemicalSnapshot, SelfModel, RelationshipState, EmpathyEntry, EmotionPattern, DriveType, InnateDrives, LearningState, LearnedVectorAdjustment, PredictionRecord, OutcomeScore, OutcomeSignals, AttachmentStyle, AttachmentData, MetacognitiveState, RegulationRecord, DefensePatternRecord, RegulationStrategyType, DefenseMechanismType, PersonhoodState, PersistedCausalInsight, GrowthDirection, PersonalityTraits, PsycheMode, PolicyModifiers, SubjectivityKernel, ResponseContract, GenerationControls, AppraisalAxes, SubjectResidue, TaskPlaneState, SubjectPlaneState, RelationPlaneState, AmbiguityPlaneState, RelationMoveType, RelationMove, OpenLoopType, OpenLoopState, PendingRelationSignalState, DyadicFieldState, SessionBridgeState, ThrongletsExportSubject, ThrongletsExportPrimitive, ThrongletsExportBase, RelationMilestoneExport, OpenLoopAnchorExport, WritebackCalibrationExport, ContinuityAnchorExport, ThrongletsExport, ThrongletsExportState, ExternalContinuityEvent, ExternalContinuityEnvelope, ThrongletsTraceTaxonomy, ThrongletsExternalContinuityRecord, ThrongletsTracePayload, ThrongletsTraceSerializationOptions, WritebackSignalType, WritebackSignalWeightMap, PendingWritebackCalibration, WritebackCalibrationFeedback, WritebackCalibrationMetric, TraitDriftState, EnergyBudgets, ClassifierProvider, ClassifierContext, ClassificationResult, } from "./types.js";
5
+ export type { PsycheState, MBTIType, Locale, StimulusType, ChemicalState, ChemicalSnapshot, SelfModel, RelationshipState, EmpathyEntry, EmotionPattern, DriveType, InnateDrives, LearningState, LearnedVectorAdjustment, PredictionRecord, OutcomeScore, OutcomeSignals, AttachmentStyle, AttachmentData, MetacognitiveState, RegulationRecord, DefensePatternRecord, RegulationStrategyType, DefenseMechanismType, PersonhoodState, PersistedCausalInsight, GrowthDirection, PersonalityTraits, PsycheMode, PolicyModifiers, SubjectivityKernel, ResponseContract, GenerationControls, TurnControlPlane, TurnControlDriver, ControlBoundaryObservation, StateLayerKind, StateLayerObservation, PromptRenderInputName, RuntimeHookName, OutputAttributionObservation, StateReconciliationObservation, DecisionCandidateName, DecisionCandidateObservation, DecisionRationaleObservation, TurnObservability, AppraisalAxes, SubjectResidue, TaskPlaneState, SubjectPlaneState, RelationPlaneState, AmbiguityPlaneState, RelationMoveType, RelationMove, OpenLoopType, OpenLoopState, PendingRelationSignalState, DyadicFieldState, SessionBridgeState, ThrongletsExportSubject, ThrongletsExportPrimitive, ThrongletsExportBase, RelationMilestoneExport, OpenLoopAnchorExport, WritebackCalibrationExport, ContinuityAnchorExport, ThrongletsExport, ThrongletsExportState, ExternalContinuityEvent, ExternalContinuityEnvelope, ThrongletsTraceTaxonomy, ThrongletsExternalContinuityRecord, ThrongletsTracePayload, ThrongletsTraceSerializationOptions, WritebackSignalType, WritebackSignalWeightMap, PendingWritebackCalibration, WritebackCalibrationFeedback, WritebackCalibrationMetric, TraitDriftState, EnergyBudgets, ClassifierProvider, ClassifierContext, ClassificationResult, } from "./types.js";
6
6
  export { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, DEFAULT_ATTACHMENT, DRIVE_KEYS, DRIVE_NAMES_ZH, DEFAULT_TRAIT_DRIFT, DEFAULT_ENERGY_BUDGETS, DEFAULT_APPRAISAL_AXES, DEFAULT_SUBJECT_RESIDUE, DEFAULT_DYADIC_FIELD, } from "./types.js";
7
7
  export { computeSelfReflection, computeEmotionalTendency, buildSelfReflectionContext } from "./self-recognition.js";
8
8
  export type { SelfReflection } from "./self-recognition.js";
@@ -28,11 +28,14 @@ export { computeResponseContract, buildResponseContractContext } from "./respons
28
28
  export { deriveGenerationControls } from "./host-controls.js";
29
29
  export { deriveReplyEnvelope } from "./reply-envelope.js";
30
30
  export type { ReplyEnvelope } from "./reply-envelope.js";
31
+ export { buildTurnObservability } from "./observability.js";
31
32
  export { computeAppraisalAxes, mergeAppraisalResidue, getResidueIntensity } from "./appraisal.js";
32
33
  export { computeRelationMove, evolveDyadicField, evolvePendingRelationSignals, getLoopPressure, applySessionBridge, applyWritebackSignals, createWritebackCalibrations, evaluateWritebackCalibrations, } from "./relation-dynamics.js";
33
34
  export { EXTERNAL_CONTINUITY_SIGNAL_KINDS, EXTERNAL_CONTINUITY_TRACE_KINDS, buildExternalContinuityEnvelope, } from "./external-continuity.js";
34
35
  export { deriveThrongletsExports } from "./thronglets-export.js";
35
36
  export { taxonomyForThrongletsExport, serializeThrongletsExportAsTrace, serializeExternalContinuityForThronglets, } from "./thronglets-runtime.js";
37
+ export { runRuntimeProbe } from "./runtime-probe.js";
38
+ export type { RuntimeProbeResult } from "./runtime-probe.js";
36
39
  export { computeExperientialField, computeCoherence, detectUnnamedEmotion, computeAffectCore } from "./experiential-field.js";
37
40
  export type { ExperientialField, ExperientialQuality, ConstructionContext } from "./experiential-field.js";
38
41
  export { computeGenerativeSelf, predictSelfReaction, detectInternalConflicts, buildIdentityNarrative } from "./generative-self.js";
package/dist/index.js CHANGED
@@ -38,11 +38,13 @@ export { computeSubjectivityKernel, buildSubjectivityContext } from "./subjectiv
38
38
  export { computeResponseContract, buildResponseContractContext } from "./response-contract.js";
39
39
  export { deriveGenerationControls } from "./host-controls.js";
40
40
  export { deriveReplyEnvelope } from "./reply-envelope.js";
41
+ export { buildTurnObservability } from "./observability.js";
41
42
  export { computeAppraisalAxes, mergeAppraisalResidue, getResidueIntensity } from "./appraisal.js";
42
43
  export { computeRelationMove, evolveDyadicField, evolvePendingRelationSignals, getLoopPressure, applySessionBridge, applyWritebackSignals, createWritebackCalibrations, evaluateWritebackCalibrations, } from "./relation-dynamics.js";
43
44
  export { EXTERNAL_CONTINUITY_SIGNAL_KINDS, EXTERNAL_CONTINUITY_TRACE_KINDS, buildExternalContinuityEnvelope, } from "./external-continuity.js";
44
45
  export { deriveThrongletsExports } from "./thronglets-export.js";
45
46
  export { taxonomyForThrongletsExport, serializeThrongletsExportAsTrace, serializeExternalContinuityForThronglets, } from "./thronglets-runtime.js";
47
+ export { runRuntimeProbe } from "./runtime-probe.js";
46
48
  // Experiential field (P6 + P8 Barrett construction)
47
49
  export { computeExperientialField, computeCoherence, detectUnnamedEmotion, computeAffectCore } from "./experiential-field.js";
48
50
  // Generative self (P6)
@@ -0,0 +1,14 @@
1
+ import type { PsycheState, ResolvedRelationContext, SessionBridgeState, StimulusType, TurnObservability, WritebackCalibrationFeedback } from "./types.js";
2
+ import type { PromptRenderInputs } from "./prompt.js";
3
+ import type { ReplyEnvelope } from "./reply-envelope.js";
4
+ export declare function buildTurnObservability(state: PsycheState, opts: {
5
+ replyEnvelope: ReplyEnvelope;
6
+ promptRenderInputs: PromptRenderInputs;
7
+ compactMode: boolean;
8
+ stimulus: StimulusType | null;
9
+ userText?: string;
10
+ sessionBridge: SessionBridgeState | null;
11
+ writebackFeedback: WritebackCalibrationFeedback[];
12
+ relationContext?: ResolvedRelationContext;
13
+ externalContinuityExports: number;
14
+ }): TurnObservability;
@@ -0,0 +1,274 @@
1
+ function clamp01(v) {
2
+ return Math.max(0, Math.min(1, v));
3
+ }
4
+ function pickDominantPlane(replyEnvelope) {
5
+ const { subjectivityKernel, responseContract } = replyEnvelope;
6
+ const taskCandidates = [
7
+ ["task-focus", subjectivityKernel.taskPlane.focus],
8
+ ["discipline", subjectivityKernel.taskPlane.discipline],
9
+ ];
10
+ const subjectCandidates = [
11
+ ["attachment", subjectivityKernel.subjectPlane.attachment],
12
+ ["guardedness", subjectivityKernel.subjectPlane.guardedness],
13
+ ["identity-strain", subjectivityKernel.subjectPlane.identityStrain],
14
+ ["residue", subjectivityKernel.subjectPlane.residue],
15
+ ];
16
+ const relationCandidates = [
17
+ ["closeness", subjectivityKernel.relationPlane.closeness],
18
+ ["loop-pressure", subjectivityKernel.relationPlane.loopPressure],
19
+ ["repair-readiness", subjectivityKernel.relationPlane.repairReadiness],
20
+ ["repair-friction", subjectivityKernel.relationPlane.repairFriction],
21
+ ["hysteresis", subjectivityKernel.relationPlane.hysteresis],
22
+ ["silent-carry", subjectivityKernel.relationPlane.silentCarry],
23
+ ];
24
+ const ambiguityCandidates = [
25
+ ["conflict-load", subjectivityKernel.ambiguityPlane.conflictLoad],
26
+ ["expression-inhibition", subjectivityKernel.ambiguityPlane.expressionInhibition],
27
+ ["naming-uncertainty", 1 - subjectivityKernel.ambiguityPlane.namingConfidence],
28
+ ];
29
+ const planeSummaries = [
30
+ ["task", ...taskCandidates.sort((a, b) => b[1] - a[1])[0]],
31
+ ["subject", ...subjectCandidates.sort((a, b) => b[1] - a[1])[0]],
32
+ ["relation", ...relationCandidates.sort((a, b) => b[1] - a[1])[0]],
33
+ ["ambiguity", ...ambiguityCandidates.sort((a, b) => b[1] - a[1])[0]],
34
+ ];
35
+ planeSummaries.sort((a, b) => {
36
+ if (b[2] !== a[2])
37
+ return b[2] - a[2];
38
+ if (responseContract.replyProfile === "work") {
39
+ if (a[0] === "task")
40
+ return -1;
41
+ if (b[0] === "task")
42
+ return 1;
43
+ }
44
+ return 0;
45
+ });
46
+ const [dominantPlane, dominantDriver, strength] = planeSummaries[0];
47
+ return {
48
+ dominantPlane,
49
+ dominantDriver,
50
+ strength: clamp01(strength),
51
+ replyProfile: responseContract.replyProfile,
52
+ replyProfileBasis: responseContract.replyProfileBasis,
53
+ overrideWindow: responseContract.overrideWindow,
54
+ };
55
+ }
56
+ function summarizeCurrentTurn(stimulus, userText) {
57
+ if (stimulus)
58
+ return `stimulus:${stimulus}`;
59
+ if (userText && userText.trim().length > 0)
60
+ return "stimulus:none";
61
+ return "no-user-input";
62
+ }
63
+ function summarizeWriteback(feedback) {
64
+ if (feedback.length === 0)
65
+ return "none";
66
+ const top = feedback[0];
67
+ return `${top.signal}:${top.effect}`;
68
+ }
69
+ function summarizeSessionBridge(bridge) {
70
+ if (!bridge)
71
+ return "none";
72
+ const loops = bridge.activeLoopTypes.length > 0 ? bridge.activeLoopTypes.join("+") : "no-open-loops";
73
+ return `${bridge.continuityMode}/${loops}`;
74
+ }
75
+ function summarizeRelationship(state, relationContext) {
76
+ const relationship = relationContext?.relationship
77
+ ?? state.relationships._default
78
+ ?? state.relationships[Object.keys(state.relationships)[0]];
79
+ if (!relationship)
80
+ return "none";
81
+ return `${relationship.phase}/trust:${Math.round(relationship.trust)}/intimacy:${Math.round(relationship.intimacy)}`;
82
+ }
83
+ function buildStateLayers(state, opts) {
84
+ return [
85
+ {
86
+ layer: "current-turn",
87
+ precedence: 1,
88
+ scope: "turn",
89
+ active: Boolean(opts.stimulus) || Boolean(opts.userText?.trim()),
90
+ summary: summarizeCurrentTurn(opts.stimulus, opts.userText),
91
+ },
92
+ {
93
+ layer: "writeback-feedback",
94
+ precedence: 2,
95
+ scope: "session",
96
+ active: opts.writebackFeedback.length > 0,
97
+ summary: summarizeWriteback(opts.writebackFeedback),
98
+ },
99
+ {
100
+ layer: "session-bridge",
101
+ precedence: 3,
102
+ scope: "session",
103
+ active: Boolean(opts.sessionBridge),
104
+ summary: summarizeSessionBridge(opts.sessionBridge),
105
+ },
106
+ {
107
+ layer: "persisted-relationship",
108
+ precedence: 4,
109
+ scope: "persistent",
110
+ active: true,
111
+ summary: summarizeRelationship(state, opts.relationContext),
112
+ },
113
+ ];
114
+ }
115
+ function buildStateReconciliation(stateLayers) {
116
+ const activeObservations = stateLayers
117
+ .filter((layer) => layer.active)
118
+ .sort((a, b) => a.precedence - b.precedence);
119
+ const activeLayers = activeObservations.map((layer) => layer.layer);
120
+ const carryLayers = activeLayers.filter((layer) => layer !== "current-turn");
121
+ const governingLayer = activeObservations[0]?.layer ?? "persisted-relationship";
122
+ let resolution = "persistent-baseline";
123
+ if (activeLayers.includes("writeback-feedback")) {
124
+ resolution = "writeback-adjusted";
125
+ }
126
+ else if (activeLayers.includes("session-bridge") && activeLayers.includes("current-turn")) {
127
+ resolution = "session-bridge-biased";
128
+ }
129
+ else if (activeLayers.includes("current-turn")) {
130
+ resolution = "current-turn-dominant";
131
+ }
132
+ const notes = stateLayers
133
+ .filter((layer) => layer.active && layer.layer !== "persisted-relationship")
134
+ .map((layer) => `${layer.layer}:${layer.summary}`);
135
+ return {
136
+ governingLayer,
137
+ activeLayers,
138
+ carryLayers,
139
+ resolution,
140
+ notes,
141
+ };
142
+ }
143
+ function pushReason(reasons, condition, label) {
144
+ if (condition)
145
+ reasons.push(label);
146
+ }
147
+ function buildDecisionRationale(replyEnvelope) {
148
+ const { subjectivityKernel, responseContract } = replyEnvelope;
149
+ const taskFocus = subjectivityKernel.taskPlane.focus;
150
+ const discipline = subjectivityKernel.taskPlane.discipline;
151
+ const attachment = subjectivityKernel.subjectPlane.attachment;
152
+ const residue = subjectivityKernel.subjectPlane.residue;
153
+ const guardedness = subjectivityKernel.subjectPlane.guardedness;
154
+ const closeness = subjectivityKernel.relationPlane.closeness;
155
+ const loopPressure = subjectivityKernel.relationPlane.loopPressure;
156
+ const repairFriction = subjectivityKernel.relationPlane.repairFriction;
157
+ const expressionInhibition = subjectivityKernel.ambiguityPlane.expressionInhibition;
158
+ const namingConfidence = subjectivityKernel.ambiguityPlane.namingConfidence;
159
+ const taskFocused = taskFocus >= 0.62;
160
+ const disciplined = discipline >= 0.72;
161
+ const taskFocusRatio = clamp01(taskFocus / 0.62);
162
+ const disciplineRatio = clamp01(discipline / 0.72);
163
+ const triggerConditions = [];
164
+ pushReason(triggerConditions, taskFocused, "task-focus>=0.62");
165
+ pushReason(triggerConditions, disciplined, "discipline>=0.72");
166
+ pushReason(triggerConditions, expressionInhibition > 0.64, "expression-inhibition>0.64");
167
+ pushReason(triggerConditions, loopPressure > 0.68, "loop-pressure>0.68");
168
+ pushReason(triggerConditions, repairFriction > 0.6, "repair-friction>0.60");
169
+ pushReason(triggerConditions, namingConfidence < 0.36, "naming-confidence<0.36");
170
+ if (triggerConditions.length === 0) {
171
+ triggerConditions.push("default-private-fallback");
172
+ }
173
+ const workReasons = [];
174
+ pushReason(workReasons, taskFocused, "task focus crossed work threshold");
175
+ pushReason(workReasons, disciplined, "discipline crossed work threshold");
176
+ pushReason(workReasons, taskFocus > 0.78 && discipline > 0.68, "high task-focus and discipline reinforce work mode");
177
+ const workScore = taskFocused && disciplined
178
+ ? 1
179
+ : taskFocused || disciplined
180
+ ? 0.82
181
+ : clamp01(Math.max(taskFocusRatio, disciplineRatio) * 0.35);
182
+ const privateReasons = [];
183
+ pushReason(privateReasons, !taskFocused && !disciplined, "no work threshold active");
184
+ pushReason(privateReasons, attachment > 0.58, "attachment keeps private surface viable");
185
+ pushReason(privateReasons, closeness > 0.58, "relational closeness favors private surface");
186
+ pushReason(privateReasons, guardedness > 0.62 || loopPressure > 0.58, "guarded relation state prefers private handling");
187
+ pushReason(privateReasons, repairFriction > 0.48 || residue > 0.45, "carry or friction remains active");
188
+ const privateScore = !taskFocused && !disciplined
189
+ ? clamp01(0.78 + (((1 - taskFocusRatio) + (1 - disciplineRatio)) / 2) * 0.22)
190
+ : clamp01((1 - Math.max(taskFocusRatio, disciplineRatio)) * 0.3);
191
+ const selected = responseContract.replyProfile === "work"
192
+ ? "work-profile"
193
+ : "private-profile";
194
+ return {
195
+ selected,
196
+ triggerConditions,
197
+ candidates: [
198
+ {
199
+ candidate: "work-profile",
200
+ score: workScore,
201
+ accepted: selected === "work-profile",
202
+ reasons: workReasons,
203
+ },
204
+ {
205
+ candidate: "private-profile",
206
+ score: privateScore,
207
+ accepted: selected === "private-profile",
208
+ reasons: privateReasons,
209
+ },
210
+ ],
211
+ };
212
+ }
213
+ function listRenderInputs(inputs) {
214
+ const names = [];
215
+ if (inputs.userText)
216
+ names.push("sensing");
217
+ if (inputs.subjectivityContext)
218
+ names.push("subjectivity");
219
+ if (inputs.responseContractContext)
220
+ names.push("response-contract");
221
+ if (inputs.metacognitiveNote)
222
+ names.push("metacognition");
223
+ if (inputs.decisionContext)
224
+ names.push("decision");
225
+ if (inputs.ethicsContext)
226
+ names.push("ethics");
227
+ if (inputs.sharedIntentionalityContext)
228
+ names.push("shared-intentionality");
229
+ if (inputs.experientialNarrative)
230
+ names.push("experiential");
231
+ if (inputs.autonomicDescription)
232
+ names.push("autonomic");
233
+ if (inputs.primarySystemsDescription)
234
+ names.push("primary-systems");
235
+ if (inputs.policyContext)
236
+ names.push("policy");
237
+ return names;
238
+ }
239
+ function listRuntimeHooks(externalContinuityExports, writebackFeedbackCount) {
240
+ const hooks = [
241
+ "appraisal",
242
+ "relation-dynamics",
243
+ "reply-envelope",
244
+ "prompt-renderer",
245
+ ];
246
+ if (writebackFeedbackCount > 0)
247
+ hooks.push("writeback-evaluation");
248
+ if (externalContinuityExports > 0)
249
+ hooks.push("external-continuity");
250
+ return hooks;
251
+ }
252
+ export function buildTurnObservability(state, opts) {
253
+ const stateLayers = buildStateLayers(state, {
254
+ stimulus: opts.stimulus,
255
+ userText: opts.userText,
256
+ sessionBridge: opts.sessionBridge,
257
+ writebackFeedback: opts.writebackFeedback,
258
+ relationContext: opts.relationContext,
259
+ });
260
+ return {
261
+ controlBoundary: pickDominantPlane(opts.replyEnvelope),
262
+ stateLayers,
263
+ stateReconciliation: buildStateReconciliation(stateLayers),
264
+ decisionRationale: buildDecisionRationale(opts.replyEnvelope),
265
+ outputAttribution: {
266
+ canonicalSurface: "reply-envelope",
267
+ promptRenderer: opts.compactMode ? "compact" : "dynamic",
268
+ renderInputs: listRenderInputs(opts.promptRenderInputs),
269
+ runtimeHooks: listRuntimeHooks(opts.externalContinuityExports, opts.writebackFeedback.length),
270
+ externalContinuityExports: opts.externalContinuityExports,
271
+ writebackFeedbackCount: opts.writebackFeedback.length,
272
+ },
273
+ };
274
+ }
@@ -0,0 +1,18 @@
1
+ export interface RuntimeProbeResult {
2
+ ok: boolean;
3
+ packageName: "psyche-ai";
4
+ version: string;
5
+ entry: "sdk";
6
+ loadPath: string;
7
+ modulePath: string;
8
+ cliPath: string;
9
+ processInputCalled: boolean;
10
+ processOutputCalled: boolean;
11
+ canonicalHostSurface: boolean;
12
+ externalContinuityAvailable: boolean;
13
+ stimulus: string | null;
14
+ cleanedText?: string;
15
+ stateChanged?: boolean;
16
+ error?: string;
17
+ }
18
+ export declare function runRuntimeProbe(): Promise<RuntimeProbeResult>;
@@ -0,0 +1,56 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { PsycheEngine } from "./core.js";
4
+ import { MemoryStorageAdapter } from "./storage.js";
5
+ import { getPackageVersion } from "./update.js";
6
+ export async function runRuntimeProbe() {
7
+ const modulePath = fileURLToPath(import.meta.url);
8
+ const loadPath = resolve(dirname(modulePath), "..");
9
+ const cliPath = resolve(loadPath, "dist", "cli.js");
10
+ const version = await getPackageVersion();
11
+ try {
12
+ const engine = new PsycheEngine({
13
+ name: "Probe",
14
+ locale: "en",
15
+ persist: false,
16
+ }, new MemoryStorageAdapter());
17
+ await engine.initialize();
18
+ const input = await engine.processInput("Runtime probe: verify the SDK is actually callable.");
19
+ const output = await engine.processOutput("Probe output acknowledged.");
20
+ return {
21
+ ok: true,
22
+ packageName: "psyche-ai",
23
+ version,
24
+ entry: "sdk",
25
+ loadPath,
26
+ modulePath,
27
+ cliPath,
28
+ processInputCalled: true,
29
+ processOutputCalled: true,
30
+ canonicalHostSurface: Boolean(input.replyEnvelope?.subjectivityKernel
31
+ && input.replyEnvelope?.responseContract
32
+ && input.replyEnvelope?.generationControls),
33
+ externalContinuityAvailable: Boolean(input.externalContinuity?.provider === "thronglets"),
34
+ stimulus: input.stimulus,
35
+ cleanedText: output.cleanedText,
36
+ stateChanged: output.stateChanged,
37
+ };
38
+ }
39
+ catch (error) {
40
+ return {
41
+ ok: false,
42
+ packageName: "psyche-ai",
43
+ version,
44
+ entry: "sdk",
45
+ loadPath,
46
+ modulePath,
47
+ cliPath,
48
+ processInputCalled: false,
49
+ processOutputCalled: false,
50
+ canonicalHostSurface: false,
51
+ externalContinuityAvailable: false,
52
+ stimulus: null,
53
+ error: error instanceof Error ? error.message : String(error),
54
+ };
55
+ }
56
+ }
package/dist/types.d.ts CHANGED
@@ -638,6 +638,60 @@ export interface ResponseContract {
638
638
  /** Which internal report, if any, should be requested in <psyche_update> */
639
639
  updateMode: "none" | "stimulus" | "empathy" | "stimulus+empathy";
640
640
  }
641
+ export type TurnControlPlane = "task" | "subject" | "relation" | "ambiguity";
642
+ export type TurnControlDriver = "task-focus" | "discipline" | "attachment" | "guardedness" | "identity-strain" | "residue" | "closeness" | "loop-pressure" | "repair-readiness" | "repair-friction" | "hysteresis" | "silent-carry" | "conflict-load" | "expression-inhibition" | "naming-uncertainty";
643
+ export interface ControlBoundaryObservation {
644
+ dominantPlane: TurnControlPlane;
645
+ dominantDriver: TurnControlDriver;
646
+ strength: number;
647
+ replyProfile: ResponseContract["replyProfile"];
648
+ replyProfileBasis: ResponseContract["replyProfileBasis"];
649
+ overrideWindow: ResponseContract["overrideWindow"];
650
+ }
651
+ export type StateLayerKind = "current-turn" | "writeback-feedback" | "session-bridge" | "persisted-relationship";
652
+ export interface StateLayerObservation {
653
+ layer: StateLayerKind;
654
+ precedence: number;
655
+ scope: "turn" | "session" | "persistent";
656
+ active: boolean;
657
+ summary: string;
658
+ }
659
+ export type PromptRenderInputName = "sensing" | "subjectivity" | "response-contract" | "metacognition" | "decision" | "ethics" | "shared-intentionality" | "experiential" | "autonomic" | "primary-systems" | "policy";
660
+ export type RuntimeHookName = "appraisal" | "relation-dynamics" | "writeback-evaluation" | "reply-envelope" | "prompt-renderer" | "external-continuity";
661
+ export interface OutputAttributionObservation {
662
+ canonicalSurface: "reply-envelope";
663
+ promptRenderer: "compact" | "dynamic";
664
+ renderInputs: PromptRenderInputName[];
665
+ runtimeHooks: RuntimeHookName[];
666
+ externalContinuityExports: number;
667
+ writebackFeedbackCount: number;
668
+ }
669
+ export interface StateReconciliationObservation {
670
+ governingLayer: StateLayerKind;
671
+ activeLayers: StateLayerKind[];
672
+ carryLayers: StateLayerKind[];
673
+ resolution: "current-turn-dominant" | "writeback-adjusted" | "session-bridge-biased" | "persistent-baseline";
674
+ notes: string[];
675
+ }
676
+ export type DecisionCandidateName = "work-profile" | "private-profile";
677
+ export interface DecisionCandidateObservation {
678
+ candidate: DecisionCandidateName;
679
+ score: number;
680
+ accepted: boolean;
681
+ reasons: string[];
682
+ }
683
+ export interface DecisionRationaleObservation {
684
+ selected: DecisionCandidateName;
685
+ triggerConditions: string[];
686
+ candidates: DecisionCandidateObservation[];
687
+ }
688
+ export interface TurnObservability {
689
+ controlBoundary: ControlBoundaryObservation;
690
+ stateLayers: StateLayerObservation[];
691
+ stateReconciliation: StateReconciliationObservation;
692
+ decisionRationale: DecisionRationaleObservation;
693
+ outputAttribution: OutputAttributionObservation;
694
+ }
641
695
  /** Sparse agent-authored writeback signals. */
642
696
  export type WritebackSignalType = "trust_up" | "trust_down" | "boundary_set" | "boundary_soften" | "repair_attempt" | "repair_landed" | "closeness_invite" | "withdrawal_mark" | "self_assertion" | "task_recenter";
643
697
  export type WritebackCalibrationMetric = "trust" | "closeness" | "safety" | "boundary" | "repair" | "silent-carry" | "task-focus";
package/llms.txt CHANGED
@@ -142,6 +142,23 @@ npm install psyche-ai
142
142
  npx psyche-mcp
143
143
  ```
144
144
 
145
+ ## Runtime Proof
146
+
147
+ If an agent claims it is already using Psyche in the current environment, require a real probe:
148
+
149
+ ```bash
150
+ psyche probe --json
151
+ ```
152
+
153
+ Passing means:
154
+
155
+ - `ok = true`
156
+ - `processInputCalled = true`
157
+ - `processOutputCalled = true`
158
+ - `canonicalHostSurface = true`
159
+
160
+ This is runtime proof. Conceptual understanding is not enough.
161
+
145
162
  ## Links
146
163
 
147
164
  - npm: https://www.npmjs.com/package/psyche-ai
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "psyche-ai",
3
- "version": "9.2.8",
3
+ "version": "9.2.10",
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",
@@ -43,7 +43,10 @@
43
43
  "test": "npm run build && npm run build:test && node --test dist-test/tests/*.test.js",
44
44
  "typecheck": "tsc --noEmit --strict",
45
45
  "dev": "tsc --watch",
46
- "demo": "node scripts/demo-ab.js"
46
+ "demo": "node scripts/demo-ab.js",
47
+ "probe": "node dist/cli.js probe --json",
48
+ "release:guard": "node scripts/release-guard.mjs",
49
+ "prepublishOnly": "npm run release:guard && npm test"
47
50
  },
48
51
  "license": "MIT",
49
52
  "repository": {