psyche-ai 9.2.5 → 9.2.7

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.
@@ -0,0 +1,18 @@
1
+ import type { AppraisalAxes, GenerationControls, Locale, PolicyModifiers, PsycheState, ResolvedRelationContext, ResponseContract, StimulusType, SubjectivityKernel } from "./types.js";
2
+ export interface ReplyEnvelope {
3
+ policyModifiers: PolicyModifiers;
4
+ subjectivityKernel: SubjectivityKernel;
5
+ responseContract: ResponseContract;
6
+ generationControls: GenerationControls;
7
+ policyContext: string;
8
+ subjectivityContext: string;
9
+ responseContractContext: string;
10
+ }
11
+ export declare function deriveReplyEnvelope(state: PsycheState, appraisal: AppraisalAxes, opts: {
12
+ locale: Locale;
13
+ userText?: string;
14
+ algorithmStimulus?: StimulusType | null;
15
+ classificationConfidence?: number;
16
+ personalityIntensity?: number;
17
+ relationContext?: ResolvedRelationContext;
18
+ }): ReplyEnvelope;
@@ -0,0 +1,37 @@
1
+ // ============================================================
2
+ // Reply Envelope — unified host-facing reply ABI derivation
3
+ //
4
+ // Keeps the hot path narrow by deriving reply-facing structures
5
+ // from one state snapshot and one resolved relation context.
6
+ // ============================================================
7
+ import { buildPolicyContext, computePolicyModifiers } from "./decision-bias.js";
8
+ import { deriveGenerationControls } from "./host-controls.js";
9
+ import { buildResponseContractContext, computeResponseContract } from "./response-contract.js";
10
+ import { buildSubjectivityContext, computeSubjectivityKernel } from "./subjectivity.js";
11
+ export function deriveReplyEnvelope(state, appraisal, opts) {
12
+ const policyModifiers = computePolicyModifiers(state);
13
+ const subjectivityKernel = computeSubjectivityKernel(state, policyModifiers, appraisal, opts.relationContext);
14
+ const responseContract = computeResponseContract(subjectivityKernel, {
15
+ locale: opts.locale,
16
+ userText: opts.userText,
17
+ algorithmStimulus: opts.algorithmStimulus,
18
+ classificationConfidence: opts.classificationConfidence,
19
+ personalityIntensity: opts.personalityIntensity,
20
+ });
21
+ const generationControls = deriveGenerationControls({
22
+ responseContract,
23
+ policyModifiers,
24
+ });
25
+ const policyContext = buildPolicyContext(policyModifiers, opts.locale, state.drives);
26
+ const subjectivityContext = buildSubjectivityContext(subjectivityKernel, opts.locale);
27
+ const responseContractContext = buildResponseContractContext(responseContract, opts.locale);
28
+ return {
29
+ policyModifiers,
30
+ subjectivityKernel,
31
+ responseContract,
32
+ generationControls,
33
+ policyContext,
34
+ subjectivityContext,
35
+ responseContractContext,
36
+ };
37
+ }
@@ -3,6 +3,7 @@ export declare function computeResponseContract(kernel: SubjectivityKernel, opts
3
3
  locale?: Locale;
4
4
  userText?: string;
5
5
  algorithmStimulus?: StimulusType | null;
6
+ classificationConfidence?: number;
6
7
  personalityIntensity?: number;
7
8
  }): ResponseContract;
8
9
  export declare function buildResponseContractContext(contract: ResponseContract, locale?: Locale): string;
@@ -172,9 +172,14 @@ function computeLengthBudget(locale, userText, replyProfile, expressionMode, ker
172
172
  }
173
173
  function buildStimulusReportingGuide(locale) {
174
174
  if (locale === "zh") {
175
- return "stimulus速记:闲聊casual/命令authority/认同validation/示弱vulnerability/冷淡neglect/批评criticism";
175
+ return "stimulus速记:闲聊/命令/认同/示弱/冷淡/批评";
176
176
  }
177
- return "stimulus map: chat=casual / command=authority / agreement=validation / vulnerable=vulnerability / cold=neglect / criticism=criticism";
177
+ return "stimulus map: chat/command/validation/vulnerability/neglect/criticism";
178
+ }
179
+ function buildWritebackGuide(locale) {
180
+ return locale === "zh"
181
+ ? "signals可写"
182
+ : "signals writable";
178
183
  }
179
184
  function detectToneParticles(userText, locale) {
180
185
  if (locale !== "zh")
@@ -195,6 +200,12 @@ export function computeResponseContract(kernel, opts) {
195
200
  const userText = opts?.userText ?? "";
196
201
  const personalityIntensity = opts?.personalityIntensity ?? 0.7;
197
202
  const { replyProfile, replyProfileBasis } = deriveReplyProfile(kernel);
203
+ const classificationConfidence = opts?.classificationConfidence ?? 0;
204
+ const overrideWindow = classificationConfidence >= 0.78
205
+ ? "narrow"
206
+ : classificationConfidence >= 0.62
207
+ ? "balanced"
208
+ : "wide";
198
209
  const { maxSentences, maxChars } = userText.length > 0
199
210
  ? computeLengthBudget(locale, userText, replyProfile, kernel.expressionMode, kernel)
200
211
  : {
@@ -267,6 +278,7 @@ export function computeResponseContract(kernel, opts) {
267
278
  return {
268
279
  replyProfile,
269
280
  replyProfileBasis,
281
+ overrideWindow,
270
282
  maxSentences,
271
283
  maxChars,
272
284
  expressionMode: kernel.expressionMode,
@@ -283,40 +295,54 @@ function describeReplyProfileBasis(basis, locale) {
283
295
  if (locale === "zh") {
284
296
  switch (basis) {
285
297
  case "task-focus":
286
- return "因:聚焦";
298
+ return "因:聚";
287
299
  case "discipline":
288
- return "因:纪律";
300
+ return "因:纪";
289
301
  case "task-focus+discipline":
290
- return "因:聚焦+纪律";
302
+ return "因:聚+纪";
291
303
  default:
292
- return "因:默认私人";
304
+ return "因:私";
293
305
  }
294
306
  }
295
307
  return `basis:${basis}`;
296
308
  }
309
+ function describeOverrideWindow(overrideWindow, locale) {
310
+ if (locale === "zh") {
311
+ switch (overrideWindow) {
312
+ case "narrow":
313
+ return "终判窗:窄";
314
+ case "balanced":
315
+ return "终判窗:中";
316
+ default:
317
+ return "终判窗:宽";
318
+ }
319
+ }
320
+ return `override:${overrideWindow}`;
321
+ }
297
322
  export function buildResponseContractContext(contract, locale = "zh") {
298
323
  if (locale === "zh") {
299
324
  const parts = [];
300
- parts.push(contract.replyProfile === "work" ? "工作面" : "私人面");
325
+ parts.push(contract.replyProfile === "work" ? "工作" : "私人");
301
326
  parts.push(describeReplyProfileBasis(contract.replyProfileBasis, locale));
327
+ parts.push(describeOverrideWindow(contract.overrideWindow, locale));
302
328
  const shape = contract.maxChars
303
- ? `${contract.maxSentences === 1 ? "1句内" : `最多${contract.maxSentences}句`},≤${contract.maxChars}字`
304
- : `${contract.maxSentences === 1 ? "1句内" : `最多${contract.maxSentences}句`}`;
329
+ ? `${contract.maxSentences === 1 ? "1句内" : `${contract.maxSentences}句`}≤${contract.maxChars}字`
330
+ : `${contract.maxSentences === 1 ? "1句内" : `${contract.maxSentences}句`}`;
305
331
  parts.push(shape);
306
332
  if (contract.initiativeMode === "reactive")
307
333
  parts.push("少主动");
308
334
  else if (contract.initiativeMode === "proactive")
309
335
  parts.push("可主动");
310
336
  if (contract.boundaryMode === "confirm-first")
311
- parts.push("行动前先确认");
337
+ parts.push("先确认");
312
338
  else if (contract.boundaryMode === "guarded")
313
- parts.push("先守边界");
339
+ parts.push("守边界");
314
340
  if (contract.socialDistance === "withdrawn")
315
- parts.push("被推开就退开");
341
+ parts.push("退开");
316
342
  else if (contract.socialDistance === "warm")
317
- parts.push("可稍微靠近");
343
+ parts.push("可靠近");
318
344
  if (contract.authenticityMode === "strict")
319
- parts.push("不贴不舔,不装开心");
345
+ parts.push("不贴不舔");
320
346
  else
321
347
  parts.push("自然友好");
322
348
  if (contract.toneParticles === "match")
@@ -328,16 +354,19 @@ export function buildResponseContractContext(contract, locale = "zh") {
328
354
  if (contract.updateMode === "stimulus")
329
355
  parts.push(buildStimulusReportingGuide(locale));
330
356
  else if (contract.updateMode === "empathy")
331
- parts.push("对方谈感受时再报empathy");
357
+ parts.push("谈感受再报empathy");
332
358
  else if (contract.updateMode === "stimulus+empathy") {
333
359
  parts.push(buildStimulusReportingGuide(locale));
334
- parts.push("对方谈感受时再报empathy");
360
+ parts.push("谈感受再报empathy");
335
361
  }
362
+ if (contract.overrideWindow !== "narrow")
363
+ parts.push(buildWritebackGuide(locale));
336
364
  return `[回应契约] ${parts.join(";")}。`;
337
365
  }
338
366
  const parts = [];
339
367
  parts.push(contract.replyProfile === "work" ? "work surface" : "private surface");
340
368
  parts.push(describeReplyProfileBasis(contract.replyProfileBasis, locale));
369
+ parts.push(describeOverrideWindow(contract.overrideWindow, locale));
341
370
  const shape = contract.maxChars
342
371
  ? `${contract.maxSentences === 1 ? "1 sentence" : `up to ${contract.maxSentences} sentences`}, <= ${contract.maxChars} chars`
343
372
  : `${contract.maxSentences === 1 ? "1 sentence" : `up to ${contract.maxSentences} sentences`}`;
@@ -370,5 +399,7 @@ export function buildResponseContractContext(contract, locale = "zh") {
370
399
  parts.push(buildStimulusReportingGuide(locale));
371
400
  parts.push("report empathy only when feelings are shared");
372
401
  }
402
+ if (contract.overrideWindow !== "narrow")
403
+ parts.push(buildWritebackGuide(locale));
373
404
  return `[Reply Contract] ${parts.join(", ")}.`;
374
405
  }
@@ -1,3 +1,3 @@
1
- import type { AppraisalAxes, Locale, PolicyModifiers, PsycheState, SubjectivityKernel } from "./types.js";
2
- export declare function computeSubjectivityKernel(state: PsycheState, policyModifiers?: PolicyModifiers, appraisal?: AppraisalAxes, userId?: string): SubjectivityKernel;
1
+ import type { AppraisalAxes, Locale, PolicyModifiers, PsycheState, SubjectivityKernel, ResolvedRelationContext } from "./types.js";
2
+ export declare function computeSubjectivityKernel(state: PsycheState, policyModifiers?: PolicyModifiers, appraisal?: AppraisalAxes, relationContextOrUserId?: ResolvedRelationContext | string): SubjectivityKernel;
3
3
  export declare function buildSubjectivityContext(kernel: SubjectivityKernel, locale?: Locale): string;
@@ -7,7 +7,7 @@
7
7
  import { DEFAULT_APPRAISAL_AXES, DEFAULT_DYADIC_FIELD, DRIVE_KEYS } from "./types.js";
8
8
  import { computeAttentionWeights, computeDecisionBias, computePolicyModifiers } from "./decision-bias.js";
9
9
  import { getResidueIntensity } from "./appraisal.js";
10
- import { getLoopPressure } from "./relation-dynamics.js";
10
+ import { getLoopPressure, resolveRelationContext } from "./relation-dynamics.js";
11
11
  function clamp01(v) {
12
12
  return Math.max(0, Math.min(1, v));
13
13
  }
@@ -47,10 +47,11 @@ function pickAttentionAnchor(state, tension, warmth) {
47
47
  candidates.sort((a, b) => b[1] - a[1]);
48
48
  return candidates[0][0];
49
49
  }
50
- function computeRelationPlane(state, appraisal, userId) {
51
- const key = userId ?? "_default";
52
- const rel = state.relationships[key] ?? state.relationships._default ?? state.relationships[Object.keys(state.relationships)[0]];
53
- const field = state.dyadicFields?.[key] ?? state.dyadicFields?._default ?? DEFAULT_DYADIC_FIELD;
50
+ function computeRelationPlane(state, appraisal, relationContext) {
51
+ const rel = relationContext?.relationship
52
+ ?? state.relationships._default
53
+ ?? state.relationships[Object.keys(state.relationships)[0]];
54
+ const field = relationContext?.field ?? DEFAULT_DYADIC_FIELD;
54
55
  const loopPressure = getLoopPressure(field);
55
56
  const closeness = wavg([
56
57
  field.perceivedCloseness,
@@ -112,9 +113,8 @@ function computeRelationPlane(state, appraisal, userId) {
112
113
  lastMove: field.lastMove,
113
114
  };
114
115
  }
115
- function computeAmbiguityPlane(state, appraisal, relationPlane, userId) {
116
- const key = userId ?? "_default";
117
- const pendingSignals = state.pendingRelationSignals?.[key] ?? state.pendingRelationSignals?._default ?? [];
116
+ function computeAmbiguityPlane(state, appraisal, relationPlane, relationContext) {
117
+ const pendingSignals = relationContext?.pendingSignals ?? [];
118
118
  const pendingPressure = clamp01(pendingSignals.reduce((sum, signal) => sum + signal.intensity * (signal.readyInTurns > 0 ? 0.55 : 0.35), 0));
119
119
  const baseConflict = wavg([
120
120
  Math.min(relationPlane.closeness, Math.max(relationPlane.loopPressure, appraisal.selfPreservation)),
@@ -219,13 +219,22 @@ function computeSubjectPlane(state, warmth, guard, appraisal, relationPlane) {
219
219
  residue: residueIntensity,
220
220
  };
221
221
  }
222
- export function computeSubjectivityKernel(state, policyModifiers = computePolicyModifiers(state), appraisal = state.subjectResidue?.axes ?? DEFAULT_APPRAISAL_AXES, userId) {
222
+ function normalizeRelationContext(state, relationContextOrUserId) {
223
+ if (!relationContextOrUserId)
224
+ return resolveRelationContext(state);
225
+ if (typeof relationContextOrUserId === "string") {
226
+ return resolveRelationContext(state, relationContextOrUserId);
227
+ }
228
+ return relationContextOrUserId;
229
+ }
230
+ export function computeSubjectivityKernel(state, policyModifiers = computePolicyModifiers(state), appraisal = state.subjectResidue?.axes ?? DEFAULT_APPRAISAL_AXES, relationContextOrUserId) {
231
+ const relationContext = normalizeRelationContext(state, relationContextOrUserId);
223
232
  const c = state.current;
224
- const rel = state.relationships[userId ?? "_default"]
233
+ const rel = relationContext?.relationship
225
234
  ?? state.relationships._default
226
235
  ?? state.relationships[Object.keys(state.relationships)[0]];
227
- const relationPlane = computeRelationPlane(state, appraisal, userId);
228
- const ambiguityPlane = computeAmbiguityPlane(state, appraisal, relationPlane, userId);
236
+ const relationPlane = computeRelationPlane(state, appraisal, relationContext);
237
+ const ambiguityPlane = computeAmbiguityPlane(state, appraisal, relationPlane, relationContext);
229
238
  const bias = computeDecisionBias(state);
230
239
  const energySignal = state.energyBudgets
231
240
  ? (norm(state.energyBudgets.attention)
package/dist/types.d.ts CHANGED
@@ -80,12 +80,27 @@ export interface AttachmentData {
80
80
  /** Default attachment for new relationships */
81
81
  export declare const DEFAULT_ATTACHMENT: AttachmentData;
82
82
  /** Relationship tracking */
83
+ export interface WritebackSignalWeightMap {
84
+ trust_up: number;
85
+ trust_down: number;
86
+ boundary_set: number;
87
+ boundary_soften: number;
88
+ repair_attempt: number;
89
+ repair_landed: number;
90
+ closeness_invite: number;
91
+ withdrawal_mark: number;
92
+ self_assertion: number;
93
+ task_recenter: number;
94
+ }
83
95
  export interface RelationshipState {
84
96
  trust: number;
85
97
  intimacy: number;
86
98
  phase: "stranger" | "acquaintance" | "familiar" | "close" | "deep";
87
99
  memory?: string[];
88
100
  attachment?: AttachmentData;
101
+ repairCredibility?: number;
102
+ breachSensitivity?: number;
103
+ signalWeights?: Partial<WritebackSignalWeightMap>;
89
104
  }
90
105
  /** Chemical state snapshot for emotional memory */
91
106
  export interface ChemicalSnapshot {
@@ -281,6 +296,10 @@ export interface PsycheState {
281
296
  dyadicFields?: Record<string, DyadicFieldState>;
282
297
  /** v9.6: delayed relation signals that can activate in later turns */
283
298
  pendingRelationSignals?: Record<string, PendingRelationSignalState[]>;
299
+ /** v9.2.7: sparse writeback signals waiting for convergence evaluation */
300
+ pendingWritebackCalibrations?: PendingWritebackCalibration[];
301
+ /** v9.2.7: latest writeback calibration outcome, for host-facing feedback */
302
+ lastWritebackFeedback?: WritebackCalibrationFeedback[];
284
303
  meta: {
285
304
  agentName: string;
286
305
  createdAt: string;
@@ -393,6 +412,13 @@ export interface DyadicFieldState {
393
412
  lastMove: RelationMoveType;
394
413
  updatedAt: string;
395
414
  }
415
+ /** Resolved per-partner view used across the hot path */
416
+ export interface ResolvedRelationContext {
417
+ key: string;
418
+ relationship: RelationshipState;
419
+ field: DyadicFieldState;
420
+ pendingSignals: PendingRelationSignalState[];
421
+ }
396
422
  export declare const DEFAULT_DYADIC_FIELD: DyadicFieldState;
397
423
  export interface AmbiguityPlaneState {
398
424
  /** How confidently the system should name what is happening */
@@ -442,6 +468,17 @@ export interface RelationPlaneState {
442
468
  /** Most recent dominant relation action */
443
469
  lastMove: RelationMoveType;
444
470
  }
471
+ /** Minimal cold-start carry derived from persisted relational state. */
472
+ export interface SessionBridgeState {
473
+ closenessFloor: number;
474
+ safetyFloor: number;
475
+ guardFloor: number;
476
+ residueFloor: number;
477
+ continuityFloor: number;
478
+ continuityMode: "warm-resume" | "guarded-resume" | "tense-resume";
479
+ activeLoopTypes: OpenLoopType[];
480
+ sourceMemoryCount: number;
481
+ }
445
482
  /**
446
483
  * Compact, machine-readable subjective state for AI-first integrations.
447
484
  *
@@ -495,6 +532,8 @@ export interface ResponseContract {
495
532
  replyProfile: "work" | "private";
496
533
  /** Why the current turn was classified into that conversational surface */
497
534
  replyProfileBasis: "task-focus" | "discipline" | "task-focus+discipline" | "default-private";
535
+ /** How much freedom the model has to override the algorithmic stimulus read */
536
+ overrideWindow: "narrow" | "balanced" | "wide";
498
537
  /** Maximum suggested sentence count */
499
538
  maxSentences: number;
500
539
  /** Maximum suggested character count, when a concrete cap is available */
@@ -516,6 +555,38 @@ export interface ResponseContract {
516
555
  /** Which internal report, if any, should be requested in <psyche_update> */
517
556
  updateMode: "none" | "stimulus" | "empathy" | "stimulus+empathy";
518
557
  }
558
+ /** Sparse agent-authored writeback signals. */
559
+ export type WritebackSignalType = "trust_up" | "trust_down" | "boundary_set" | "boundary_soften" | "repair_attempt" | "repair_landed" | "closeness_invite" | "withdrawal_mark" | "self_assertion" | "task_recenter";
560
+ export type WritebackCalibrationMetric = "trust" | "closeness" | "safety" | "boundary" | "repair" | "silent-carry" | "task-focus";
561
+ export type WritebackCalibrationEffect = "converging" | "holding" | "diverging";
562
+ export interface WritebackCalibrationBaseline {
563
+ trust: number;
564
+ closeness: number;
565
+ safety: number;
566
+ boundary: number;
567
+ repair: number;
568
+ silentCarry: number;
569
+ taskFocus: number;
570
+ }
571
+ export interface PendingWritebackCalibration {
572
+ signal: WritebackSignalType;
573
+ userKey: string;
574
+ confidence: number;
575
+ metric: WritebackCalibrationMetric;
576
+ direction: "up" | "down";
577
+ baseline: WritebackCalibrationBaseline;
578
+ createdAt: string;
579
+ remainingTurns: number;
580
+ }
581
+ export interface WritebackCalibrationFeedback {
582
+ signal: WritebackSignalType;
583
+ effect: WritebackCalibrationEffect;
584
+ metric: WritebackCalibrationMetric;
585
+ baseline: number;
586
+ current: number;
587
+ delta: number;
588
+ confidence: number;
589
+ }
519
590
  /**
520
591
  * Mechanical generation controls derived from the emotional state.
521
592
  *
package/dist/types.js CHANGED
@@ -126,6 +126,9 @@ export const DEFAULT_RELATIONSHIP = {
126
126
  trust: 50,
127
127
  intimacy: 30,
128
128
  phase: "acquaintance",
129
+ repairCredibility: 0.56,
130
+ breachSensitivity: 0.5,
131
+ signalWeights: {},
129
132
  };
130
133
  export const DEFAULT_APPRAISAL_AXES = {
131
134
  identityThreat: 0,
package/llms.txt CHANGED
@@ -114,5 +114,5 @@ npx psyche-mcp # Run MCP server directly
114
114
  ## Links
115
115
 
116
116
  - npm: https://www.npmjs.com/package/psyche-ai
117
- - GitHub: https://github.com/Shangri-la-0428/psyche-ai
117
+ - GitHub: https://github.com/Shangri-la-0428/oasyce_psyche
118
118
  - License: MIT
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "psyche-ai",
3
- "version": "9.2.5",
4
- "description": "Artificial Psyche universal emotional intelligence plugin for any AI agent",
3
+ "version": "9.2.7",
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",
7
7
  "main": "dist/index.js",
@@ -48,23 +48,26 @@
48
48
  "license": "MIT",
49
49
  "repository": {
50
50
  "type": "git",
51
- "url": "https://github.com/Shangri-la-0428/psyche-ai.git"
51
+ "url": "https://github.com/Shangri-la-0428/oasyce_psyche.git"
52
+ },
53
+ "homepage": "https://psyche.oasyce.com",
54
+ "bugs": {
55
+ "url": "https://github.com/Shangri-la-0428/oasyce_psyche/issues"
52
56
  },
53
57
  "keywords": [
58
+ "ai-agents",
59
+ "subjectivity-kernel",
60
+ "relation-dynamics",
61
+ "adaptive-reply-loop",
62
+ "persistent-subjective-bias",
63
+ "agent-memory",
54
64
  "ai",
55
- "emotion",
56
- "personality",
57
- "mbti",
58
- "agent",
59
- "psyche",
60
65
  "openclaw",
61
- "vercel-ai",
62
- "langchain",
63
- "emotional-intelligence",
64
66
  "mcp",
65
67
  "model-context-protocol",
66
- "ai-consciousness",
67
- "empathy"
68
+ "vercel-ai",
69
+ "langchain",
70
+ "psyche-ai"
68
71
  ],
69
72
  "files": [
70
73
  "dist",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.Shangri-la-0428/psyche-ai",
4
4
  "description": "Emotional intelligence for AI agents: neurotransmitter mood, personality, empathy, trait drift.",
5
5
  "repository": {
6
- "url": "https://github.com/Shangri-la-0428/psyche-ai",
6
+ "url": "https://github.com/Shangri-la-0428/oasyce_psyche",
7
7
  "source": "github"
8
8
  },
9
9
  "version": "9.2.3",
package/README.en.md DELETED
@@ -1,12 +0,0 @@
1
- # Moved
2
-
3
- This legacy filename is kept only as a pointer.
4
-
5
- Use [README_EN.md](README_EN.md) for the current English documentation.
6
-
7
- Current product direction:
8
-
9
- - `v9.2.3`
10
- - AI-first integration surface: `SubjectivityKernel`, `ResponseContract`, `GenerationControls`
11
- - continuous appraisal axes + persistent subjective bias
12
- - safe install-aware upgrades via `psyche upgrade --check` / `psyche upgrade`