psyche-ai 9.2.3 → 9.2.5

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.
Files changed (51) hide show
  1. package/README.en.md +8 -175
  2. package/README.md +33 -16
  3. package/dist/adapters/http.js +1 -1
  4. package/dist/adapters/langchain.d.ts +14 -0
  5. package/dist/adapters/langchain.js +20 -0
  6. package/dist/adapters/mcp.js +5 -1
  7. package/dist/adapters/openclaw.d.ts +1 -0
  8. package/dist/adapters/openclaw.js +67 -15
  9. package/dist/adapters/vercel-ai.d.ts +2 -1
  10. package/dist/adapters/vercel-ai.js +8 -9
  11. package/dist/appraisal.d.ts +8 -0
  12. package/dist/appraisal.js +362 -0
  13. package/dist/autonomic.js +2 -2
  14. package/dist/classify.js +14 -3
  15. package/dist/cli.js +28 -3
  16. package/dist/core.d.ts +9 -3
  17. package/dist/core.js +194 -12
  18. package/dist/demo.js +1 -2
  19. package/dist/diagnostics.d.ts +8 -6
  20. package/dist/diagnostics.js +53 -17
  21. package/dist/ethics.js +1 -1
  22. package/dist/experiential-field.d.ts +2 -2
  23. package/dist/experiential-field.js +7 -16
  24. package/dist/generative-self.js +0 -2
  25. package/dist/host-controls.d.ts +5 -0
  26. package/dist/host-controls.js +48 -0
  27. package/dist/index.d.ts +7 -2
  28. package/dist/index.js +7 -1
  29. package/dist/interaction.js +0 -2
  30. package/dist/metacognition.d.ts +13 -1
  31. package/dist/metacognition.js +164 -32
  32. package/dist/prompt.d.ts +4 -0
  33. package/dist/prompt.js +67 -31
  34. package/dist/psyche-file.d.ts +16 -1
  35. package/dist/psyche-file.js +103 -8
  36. package/dist/relation-dynamics.d.ts +21 -0
  37. package/dist/relation-dynamics.js +601 -0
  38. package/dist/response-contract.d.ts +8 -0
  39. package/dist/response-contract.js +374 -0
  40. package/dist/storage.d.ts +1 -0
  41. package/dist/storage.js +12 -5
  42. package/dist/subjectivity.d.ts +3 -0
  43. package/dist/subjectivity.js +477 -0
  44. package/dist/temporal.d.ts +2 -2
  45. package/dist/temporal.js +2 -2
  46. package/dist/types.d.ts +243 -0
  47. package/dist/types.js +43 -0
  48. package/dist/update.d.ts +37 -2
  49. package/dist/update.js +323 -44
  50. package/openclaw.plugin.json +20 -1
  51. package/package.json +1 -1
package/dist/update.js CHANGED
@@ -1,43 +1,84 @@
1
1
  // ============================================================
2
- // Auto-update checkernon-blocking, fire-and-forget
2
+ // Update managercontext-aware update checks and explicit upgrades
3
3
  //
4
- // Checks npm registry for newer version on initialize().
5
- // Never blocks, never throws to caller, checks at most once per hour.
4
+ // Goals:
5
+ // - Never block agent startup
6
+ // - Never mutate a dirty local git worktree behind the user's back
7
+ // - Give the right update instruction for the current install shape
8
+ // - Allow explicit self-update via CLI when safe
6
9
  // ============================================================
7
- import { readFile, writeFile, mkdir } from "node:fs/promises";
8
- import { join, dirname } from "node:path";
10
+ import { readFile, writeFile, mkdir, access } from "node:fs/promises";
11
+ import { constants as fsConstants } from "node:fs";
12
+ import { join, dirname, resolve, sep } from "node:path";
9
13
  import { homedir } from "node:os";
10
14
  import { fileURLToPath } from "node:url";
11
15
  import { execFile } from "node:child_process";
12
16
  import { promisify } from "node:util";
13
17
  const execFileAsync = promisify(execFile);
14
18
  const PACKAGE_NAME = "psyche-ai";
15
- // Read version from package.json at module load so it stays in sync automatically
16
- let CURRENT_VERSION = "9.0.0"; // fallback
17
- try {
18
- const __dirname = dirname(fileURLToPath(import.meta.url));
19
- const pkg = JSON.parse(await readFile(join(__dirname, "..", "package.json"), "utf-8"));
20
- CURRENT_VERSION = pkg.version ?? CURRENT_VERSION;
21
- }
22
- catch {
23
- // Silent — use fallback
24
- }
19
+ const FALLBACK_VERSION = "0.0.0";
25
20
  const CHECK_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
26
21
  const CACHE_DIR = join(homedir(), ".psyche-ai");
27
22
  const CACHE_FILE = join(CACHE_DIR, "update-check.json");
28
23
  const FETCH_TIMEOUT_MS = 5000;
24
+ const APPLY_TIMEOUT_MS = 30_000;
25
+ const BUILD_TIMEOUT_MS = 60_000;
26
+ function shellQuote(value) {
27
+ return `'${value.replace(/'/g, `'\\''`)}'`;
28
+ }
29
+ async function readPackageMetadata(root) {
30
+ const raw = await readFile(join(root, "package.json"), "utf-8");
31
+ const pkg = JSON.parse(raw);
32
+ return {
33
+ version: pkg.version ?? FALLBACK_VERSION,
34
+ hasBuildScript: typeof pkg.scripts?.build === "string" && pkg.scripts.build.length > 0,
35
+ };
36
+ }
37
+ async function resolvePackageMetadata() {
38
+ const __dirname = dirname(fileURLToPath(import.meta.url));
39
+ const candidates = [
40
+ join(__dirname, ".."),
41
+ join(__dirname, "..", ".."),
42
+ ];
43
+ for (const root of candidates) {
44
+ try {
45
+ const meta = await readPackageMetadata(root);
46
+ return { root, version: meta.version };
47
+ }
48
+ catch {
49
+ // Try next candidate.
50
+ }
51
+ }
52
+ return { root: process.cwd(), version: process.env.npm_package_version ?? FALLBACK_VERSION };
53
+ }
54
+ let packageMetadataPromise = null;
55
+ async function getPackageMetadata() {
56
+ packageMetadataPromise ??= resolvePackageMetadata();
57
+ return packageMetadataPromise;
58
+ }
59
+ export function getPackageVersion() {
60
+ return getPackageMetadata().then((meta) => meta.version);
61
+ }
62
+ function compareSemverPart(a, b) {
63
+ const na = Number(a ?? 0);
64
+ const nb = Number(b ?? 0);
65
+ if (na < nb)
66
+ return -1;
67
+ if (na > nb)
68
+ return 1;
69
+ return 0;
70
+ }
29
71
  /**
30
72
  * Compare two semver strings. Returns:
31
73
  * -1 if a < b, 0 if a == b, 1 if a > b
32
74
  */
33
- function compareSemver(a, b) {
34
- const pa = a.split(".").map(Number);
35
- const pb = b.split(".").map(Number);
75
+ export function compareSemver(a, b) {
76
+ const pa = a.split(".");
77
+ const pb = b.split(".");
36
78
  for (let i = 0; i < 3; i++) {
37
- if ((pa[i] ?? 0) < (pb[i] ?? 0))
38
- return -1;
39
- if ((pa[i] ?? 0) > (pb[i] ?? 0))
40
- return 1;
79
+ const cmp = compareSemverPart(pa[i], pb[i]);
80
+ if (cmp !== 0)
81
+ return cmp;
41
82
  }
42
83
  return 0;
43
84
  }
@@ -56,7 +97,7 @@ async function writeCache(cache) {
56
97
  await writeFile(CACHE_FILE, JSON.stringify(cache), "utf-8");
57
98
  }
58
99
  catch {
59
- // Silent — cache is optional
100
+ // Silent — cache is optional.
60
101
  }
61
102
  }
62
103
  async function fetchLatestVersion() {
@@ -74,45 +115,283 @@ async function fetchLatestVersion() {
74
115
  return null;
75
116
  }
76
117
  }
77
- async function tryAutoUpdate(latestVersion) {
118
+ async function pathExists(path) {
78
119
  try {
79
- // Try npm update — timeout after 30s, silent on failure
80
- await execFileAsync("npm", ["update", PACKAGE_NAME, "--registry", "https://registry.npmjs.org"], {
81
- timeout: 30000,
82
- });
83
- console.log(`[psyche-ai] ✓ Auto-updated to v${latestVersion}`);
120
+ await access(path, fsConstants.F_OK);
84
121
  return true;
85
122
  }
86
123
  catch {
87
124
  return false;
88
125
  }
89
126
  }
127
+ async function runCommand(file, args, cwd, timeout) {
128
+ try {
129
+ const { stdout, stderr } = await execFileAsync(file, args, {
130
+ cwd,
131
+ timeout,
132
+ });
133
+ return { ok: true, stdout, stderr };
134
+ }
135
+ catch (error) {
136
+ const err = error;
137
+ return {
138
+ ok: false,
139
+ stdout: err.stdout ?? "",
140
+ stderr: err.stderr ?? "",
141
+ };
142
+ }
143
+ }
144
+ async function detectGitContext(packageRoot, hasBuildScript) {
145
+ const topLevel = await runCommand("git", ["rev-parse", "--show-toplevel"], packageRoot, 5000);
146
+ if (!topLevel.ok)
147
+ return null;
148
+ const repoRoot = topLevel.stdout.trim();
149
+ if (!repoRoot)
150
+ return null;
151
+ const branchResult = await runCommand("git", ["rev-parse", "--abbrev-ref", "HEAD"], repoRoot, 5000);
152
+ const branch = branchResult.ok ? branchResult.stdout.trim() : null;
153
+ const upstreamResult = await runCommand("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], repoRoot, 5000);
154
+ const upstream = upstreamResult.ok ? upstreamResult.stdout.trim() : null;
155
+ const dirtyResult = await runCommand("git", ["status", "--porcelain"], repoRoot, 5000);
156
+ const dirty = dirtyResult.ok ? dirtyResult.stdout.trim().length > 0 : true;
157
+ const buildStep = hasBuildScript ? " && npm run build" : "";
158
+ const manualCommand = `cd ${shellQuote(repoRoot)} && git pull --ff-only${buildStep}`;
159
+ return {
160
+ mode: "git-worktree",
161
+ packageRoot,
162
+ updateCwd: repoRoot,
163
+ manualCommand,
164
+ autoApplyOnInit: false,
165
+ requiresRestart: true,
166
+ hasBuildScript,
167
+ gitBranch: branch,
168
+ gitUpstream: upstream,
169
+ dirty,
170
+ reason: dirty
171
+ ? "working tree has local changes"
172
+ : upstream
173
+ ? undefined
174
+ : "git branch has no tracked upstream",
175
+ };
176
+ }
177
+ export async function detectInstallContext(packageRootArg) {
178
+ const packageRoot = resolve(packageRootArg ?? (await getPackageMetadata()).root);
179
+ const { hasBuildScript } = await readPackageMetadata(packageRoot).catch(() => ({
180
+ version: FALLBACK_VERSION,
181
+ hasBuildScript: false,
182
+ }));
183
+ const gitContext = await detectGitContext(packageRoot, hasBuildScript);
184
+ if (gitContext)
185
+ return gitContext;
186
+ const marker = `${sep}node_modules${sep}${PACKAGE_NAME}`;
187
+ const idx = packageRoot.lastIndexOf(marker);
188
+ if (idx >= 0) {
189
+ const projectRoot = packageRoot.slice(0, idx) || sep;
190
+ return {
191
+ mode: "npm-project",
192
+ packageRoot,
193
+ updateCwd: projectRoot,
194
+ manualCommand: `cd ${shellQuote(projectRoot)} && npm update ${PACKAGE_NAME}`,
195
+ autoApplyOnInit: true,
196
+ requiresRestart: true,
197
+ hasBuildScript,
198
+ };
199
+ }
200
+ const localBuildStep = hasBuildScript && await pathExists(join(packageRoot, "tsconfig.json"))
201
+ ? " && npm run build"
202
+ : "";
203
+ return {
204
+ mode: "local-path",
205
+ packageRoot,
206
+ updateCwd: packageRoot,
207
+ manualCommand: `cd ${shellQuote(packageRoot)} && npm update ${PACKAGE_NAME}${localBuildStep}`,
208
+ autoApplyOnInit: false,
209
+ requiresRestart: true,
210
+ hasBuildScript,
211
+ reason: "local path install is not package-manager managed",
212
+ };
213
+ }
214
+ function formatAvailableMessage(latestVersion, currentVersion, context) {
215
+ return `[psyche-ai] v${latestVersion} available (current: v${currentVersion}). Run: psyche upgrade` +
216
+ (context.mode === "git-worktree" || context.mode === "local-path"
217
+ ? ` or ${context.manualCommand}`
218
+ : "");
219
+ }
220
+ async function applyNpmProjectUpdate(context, latestVersion) {
221
+ const result = await runCommand("npm", ["update", PACKAGE_NAME, "--registry", "https://registry.npmjs.org"], context.updateCwd, APPLY_TIMEOUT_MS);
222
+ if (!result.ok) {
223
+ return {
224
+ status: "failed",
225
+ currentVersion: await getPackageVersion(),
226
+ latestVersion,
227
+ context,
228
+ manualCommand: context.manualCommand,
229
+ message: `[psyche-ai] Auto-update failed. Run manually: ${context.manualCommand}`,
230
+ restartRequired: false,
231
+ };
232
+ }
233
+ const nextVersion = await readPackageMetadata(context.packageRoot)
234
+ .then((meta) => meta.version)
235
+ .catch(() => latestVersion);
236
+ return {
237
+ status: "updated",
238
+ currentVersion: nextVersion,
239
+ latestVersion,
240
+ context,
241
+ manualCommand: context.manualCommand,
242
+ message: `[psyche-ai] Updated to v${nextVersion}. Restart hosts using it to load the new build.`,
243
+ restartRequired: true,
244
+ };
245
+ }
246
+ async function applyGitWorktreeUpdate(context, latestVersion) {
247
+ const currentVersion = await getPackageVersion();
248
+ if (context.dirty) {
249
+ return {
250
+ status: "skipped",
251
+ currentVersion,
252
+ latestVersion,
253
+ context,
254
+ manualCommand: context.manualCommand,
255
+ message: `[psyche-ai] Skipped self-update: ${context.reason}. Run manually: ${context.manualCommand}`,
256
+ restartRequired: false,
257
+ };
258
+ }
259
+ if (!context.gitUpstream) {
260
+ return {
261
+ status: "skipped",
262
+ currentVersion,
263
+ latestVersion,
264
+ context,
265
+ manualCommand: context.manualCommand,
266
+ message: `[psyche-ai] Skipped self-update: ${context.reason}. Run manually: ${context.manualCommand}`,
267
+ restartRequired: false,
268
+ };
269
+ }
270
+ const pullResult = await runCommand("git", ["pull", "--ff-only"], context.updateCwd, APPLY_TIMEOUT_MS);
271
+ if (!pullResult.ok) {
272
+ return {
273
+ status: "failed",
274
+ currentVersion,
275
+ latestVersion,
276
+ context,
277
+ manualCommand: context.manualCommand,
278
+ message: `[psyche-ai] Git update failed. Run manually: ${context.manualCommand}`,
279
+ restartRequired: false,
280
+ };
281
+ }
282
+ if (context.hasBuildScript) {
283
+ const buildResult = await runCommand("npm", ["run", "build"], context.packageRoot, BUILD_TIMEOUT_MS);
284
+ if (!buildResult.ok) {
285
+ return {
286
+ status: "failed",
287
+ currentVersion,
288
+ latestVersion,
289
+ context,
290
+ manualCommand: context.manualCommand,
291
+ message: `[psyche-ai] Pulled latest code but build failed. Run manually: ${context.manualCommand}`,
292
+ restartRequired: false,
293
+ };
294
+ }
295
+ }
296
+ const nextVersion = await readPackageMetadata(context.packageRoot)
297
+ .then((meta) => meta.version)
298
+ .catch(() => latestVersion);
299
+ return {
300
+ status: "updated",
301
+ currentVersion: nextVersion,
302
+ latestVersion,
303
+ context,
304
+ manualCommand: context.manualCommand,
305
+ message: `[psyche-ai] Updated worktree to v${nextVersion}. Restart hosts using it to load the new build.`,
306
+ restartRequired: true,
307
+ };
308
+ }
309
+ async function applyExplicitUpdate(context, latestVersion) {
310
+ switch (context.mode) {
311
+ case "npm-project":
312
+ return applyNpmProjectUpdate(context, latestVersion);
313
+ case "git-worktree":
314
+ return applyGitWorktreeUpdate(context, latestVersion);
315
+ case "local-path":
316
+ return {
317
+ status: "skipped",
318
+ currentVersion: await getPackageVersion(),
319
+ latestVersion,
320
+ context,
321
+ manualCommand: context.manualCommand,
322
+ message: `[psyche-ai] This install shape needs a manual update. Run: ${context.manualCommand}`,
323
+ restartRequired: false,
324
+ };
325
+ }
326
+ }
327
+ export async function selfUpdate(opts) {
328
+ const currentVersion = opts?.packageRoot
329
+ ? await readPackageMetadata(resolve(opts.packageRoot)).then((meta) => meta.version)
330
+ : await getPackageVersion();
331
+ const context = await detectInstallContext(opts?.packageRoot);
332
+ const latestVersion = opts?.latestVersion ?? await fetchLatestVersion();
333
+ if (!latestVersion) {
334
+ return {
335
+ status: "failed",
336
+ currentVersion,
337
+ latestVersion: null,
338
+ context,
339
+ manualCommand: context.manualCommand,
340
+ message: "[psyche-ai] Could not reach the registry to check for updates.",
341
+ restartRequired: false,
342
+ };
343
+ }
344
+ if (compareSemver(currentVersion, latestVersion) >= 0) {
345
+ return {
346
+ status: "up-to-date",
347
+ currentVersion,
348
+ latestVersion,
349
+ context,
350
+ manualCommand: context.manualCommand,
351
+ message: `[psyche-ai] v${currentVersion} is up to date.`,
352
+ restartRequired: false,
353
+ };
354
+ }
355
+ if (opts?.checkOnly) {
356
+ return {
357
+ status: "available",
358
+ currentVersion,
359
+ latestVersion,
360
+ context,
361
+ manualCommand: context.manualCommand,
362
+ message: formatAvailableMessage(latestVersion, currentVersion, context),
363
+ restartRequired: false,
364
+ };
365
+ }
366
+ return applyExplicitUpdate(context, latestVersion);
367
+ }
90
368
  /**
91
369
  * Check for updates. Non-blocking, safe to fire-and-forget.
92
370
  * - Checks at most once per hour (cached)
93
- * - If newer version found, attempts auto-update via npm
94
- * - If auto-update fails, prints a manual update hint
371
+ * - Auto-applies only for npm-managed installs
372
+ * - For git/local-path installs, prints the correct explicit upgrade command
95
373
  * - Never throws
96
374
  */
97
375
  export async function checkForUpdate() {
98
- // Rate limit: check at most once per hour
376
+ const currentVersion = await getPackageVersion();
377
+ const context = await detectInstallContext();
99
378
  const cache = await readCache();
100
379
  if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
101
- // Still within cooldown — but notify if we already know about a newer version
102
- if (cache.latestVersion && compareSemver(CURRENT_VERSION, cache.latestVersion) < 0) {
103
- console.log(`[psyche-ai] v${cache.latestVersion} available (current: v${CURRENT_VERSION}). Run: npm update ${PACKAGE_NAME}`);
380
+ if (cache.latestVersion && compareSemver(currentVersion, cache.latestVersion) < 0) {
381
+ console.log(formatAvailableMessage(cache.latestVersion, currentVersion, context));
104
382
  }
105
383
  return;
106
384
  }
107
- const latest = await fetchLatestVersion();
108
- await writeCache({ lastCheck: Date.now(), latestVersion: latest });
109
- if (!latest || compareSemver(CURRENT_VERSION, latest) >= 0) {
110
- return; // Up to date or couldn't check
385
+ const latestVersion = await fetchLatestVersion();
386
+ await writeCache({ lastCheck: Date.now(), latestVersion });
387
+ if (!latestVersion || compareSemver(currentVersion, latestVersion) >= 0) {
388
+ return;
111
389
  }
112
- // Newer version available — try auto-update
113
- console.log(`[psyche-ai] New version v${latest} available (current: v${CURRENT_VERSION}), updating...`);
114
- const updated = await tryAutoUpdate(latest);
115
- if (!updated) {
116
- console.log(`[psyche-ai] Auto-update failed. Run manually: npm update ${PACKAGE_NAME}`);
390
+ if (!context.autoApplyOnInit) {
391
+ console.log(formatAvailableMessage(latestVersion, currentVersion, context));
392
+ return;
117
393
  }
394
+ console.log(`[psyche-ai] New version v${latestVersion} available (current: v${currentVersion}), updating...`);
395
+ const result = await applyExplicitUpdate(context, latestVersion);
396
+ console.log(result.message);
118
397
  }
@@ -2,7 +2,7 @@
2
2
  "id": "psyche-ai",
3
3
  "name": "Artificial Psyche",
4
4
  "description": "Virtual endocrine system, empathy engine, and agency for OpenClaw agents",
5
- "version": "5.1.0",
5
+ "version": "9.2.3",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -49,6 +49,15 @@
49
49
  "type": "boolean",
50
50
  "default": true,
51
51
  "description": "Persist emotional state to disk. Set false for privacy."
52
+ },
53
+ "diagnostics": {
54
+ "type": "boolean",
55
+ "default": true,
56
+ "description": "Collect diagnostic metrics and write diagnostics.jsonl at session end."
57
+ },
58
+ "feedbackUrl": {
59
+ "type": "string",
60
+ "description": "Optional endpoint for auto-submitting anonymized diagnostic reports."
52
61
  }
53
62
  }
54
63
  },
@@ -89,6 +98,16 @@
89
98
  "label": "Persist State",
90
99
  "sensitive": false,
91
100
  "advanced": true
101
+ },
102
+ "diagnostics": {
103
+ "label": "Diagnostics Logging",
104
+ "sensitive": false,
105
+ "advanced": true
106
+ },
107
+ "feedbackUrl": {
108
+ "label": "Feedback Endpoint",
109
+ "sensitive": true,
110
+ "advanced": true
92
111
  }
93
112
  }
94
113
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "psyche-ai",
3
- "version": "9.2.3",
3
+ "version": "9.2.5",
4
4
  "description": "Artificial Psyche — universal emotional intelligence plugin for any AI agent",
5
5
  "mcpName": "io.github.Shangri-la-0428/psyche-ai",
6
6
  "type": "module",