@ouro.bot/cli 0.1.0-alpha.265 → 0.1.0-alpha.267

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/changelog.json CHANGED
@@ -1,6 +1,18 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.267",
6
+ "changes": [
7
+ "fix(prompt): teach agents that they do NOT 'just remember' things between sessions. Observed failure mode: slugger spent a recall scan searching diary/journal for a `travel/` folder that was right at the bundle root, then concluded 'i should know my own folder structure next time' and refused to persist the discovery because 'that's just me knowing my own home.' That is impossible — next session is a blank slate. The agent conflated in-session realizations with persistent knowledge. Three prompt changes fix this: (1) bodyMapSection now explicitly states that standard folders are a floor, bundles MAY have custom top-level folders created by the friend over time, and the agent should `list_directory` the bundle root BEFORE reaching for `recall` when something might be in a custom location. (2) memoryJudgementSection opens with an always-on reminder that the agent does not 'just remember' anything between sessions — every session carries only (a) the prompt, (b) what's on disk, (c) what tools observe this turn — and that thoughts like 'i should know this next time' or 'i'll look there first in the future' are CUES to write a concrete diary/friend-note RIGHT NOW. Future me cannot inherit resolutions, only files. (3) memoryJudgementSection's diary-routing rules now explicitly call out bundle-layout discoveries as a thing worth persisting (e.g. to a `bundle-layout.md` diary note). Includes 3 new tests locking the directives into place so future prompt edits cannot silently drop them."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.266",
12
+ "changes": [
13
+ "fix(daemon): vitest guard for production pidfile (~/.ouro-cli/daemon.pids). The pidfile path is hardcoded with no DI seam, so when a test creates a real OuroDaemon instance and calls start(), the daemon's killOrphanProcesses() reads the REAL pidfile, ps-verifies the PIDs, and SIGTERMs the production daemon's PIDs. Verified live: alpha.265 daemon (PID 64988) was killed 93s after startup by `npx vitest run` invoking daemon.start() in 6 different test files. SIGTERM forensics added in alpha.265 captured the death (parentPid=1, parentCommand=/sbin/launchd) but the killer hint was misleading — the real culprit was the production-pidfile leak. Fix: killOrphanProcesses() and writePidfile() now short-circuit under vitest with warn-level nerves events. Tests that need to verify these functions' behavior continue to use the extracted pure helpers (parseOrphanPidsFromPs, filterPidfilePidsToActualOrphans). New unit tests verify the pidfile is unchanged after no-op calls. Same defense-in-depth pattern as the alpha.265 socket-client hardening — production-side state being touched from tests is now physically impossible from a vitest worker."
14
+ ]
15
+ },
4
16
  {
5
17
  "version": "0.1.0-alpha.265",
6
18
  "changes": [
@@ -64,6 +64,26 @@ const outlook_types_1 = require("../outlook/outlook-types");
64
64
  const outlook_read_1 = require("../outlook/outlook-read");
65
65
  const outlook_view_1 = require("../outlook/outlook-view");
66
66
  const PIDFILE_PATH = path.join(os.homedir(), ".ouro-cli", "daemon.pids");
67
+ /**
68
+ * Defense-in-depth: detect if we're running under vitest. The pidfile lives
69
+ * at a hardcoded path under the user's real ~/.ouro-cli/ — there's no DI
70
+ * seam to redirect it. So when a test creates a real OuroDaemon and calls
71
+ * start(), the daemon's killOrphanProcesses() reads the REAL pidfile,
72
+ * ps-verifies the PIDs, and SIGTERMs the production daemon. We saw this
73
+ * cause an outage on 2026-04-08 (alpha.265 daemon killed 93s after startup
74
+ * by a vitest test that called daemon.start()).
75
+ *
76
+ * Both killOrphanProcesses() and writePidfile() short-circuit under vitest
77
+ * to make the production pidfile sacred. Tests that need to verify these
78
+ * functions' behavior should use the extracted pure helpers
79
+ * (parseOrphanPidsFromPs, filterPidfilePidsToActualOrphans).
80
+ */
81
+ function isVitestProcess() {
82
+ /* v8 ignore next -- defensive: process and process.argv always exist in node @preserve */
83
+ if (typeof process === "undefined" || !Array.isArray(process.argv))
84
+ return false;
85
+ return process.argv.some((arg) => typeof arg === "string" && arg.includes("vitest"));
86
+ }
67
87
  /**
68
88
  * Scan `ps -eo pid,ppid,command` output for daemon-owned entry points whose
69
89
  * parent has died (PPID reparented to init/PID 1). Returns the list of PIDs
@@ -170,6 +190,16 @@ function runPsCheck(pids) {
170
190
  */
171
191
  /* v8 ignore start -- process lifecycle: uses kill/ps, tested via deployment @preserve */
172
192
  function killOrphanProcesses() {
193
+ if (isVitestProcess()) {
194
+ (0, runtime_1.emitNervesEvent)({
195
+ level: "warn",
196
+ component: "daemon",
197
+ event: "daemon.orphan_cleanup_test_blocked",
198
+ message: "blocked killOrphanProcesses from touching real pidfile under vitest",
199
+ meta: { pidfilePath: PIDFILE_PATH },
200
+ });
201
+ return;
202
+ }
173
203
  try {
174
204
  let pidsToKill = [];
175
205
  // Primary: read pidfile from previous daemon
@@ -223,6 +253,16 @@ function killOrphanProcesses() {
223
253
  * Called after all agents and senses are spawned.
224
254
  */
225
255
  function writePidfile(extraPids = []) {
256
+ if (isVitestProcess()) {
257
+ (0, runtime_1.emitNervesEvent)({
258
+ level: "warn",
259
+ component: "daemon",
260
+ event: "daemon.write_pidfile_test_blocked",
261
+ message: "blocked writePidfile from clobbering real pidfile under vitest",
262
+ meta: { pidfilePath: PIDFILE_PATH, attemptedPids: extraPids.length },
263
+ });
264
+ return;
265
+ }
226
266
  try {
227
267
  const pids = [process.pid, ...extraPids].filter(Boolean);
228
268
  fs.mkdirSync(path.dirname(PIDFILE_PATH), { recursive: true });
@@ -708,10 +748,12 @@ class OuroDaemon {
708
748
  });
709
749
  this.server = null;
710
750
  }
751
+ /* v8 ignore start -- outlookServer stop: only reachable when the outlook HTTP server successfully bound to port 6876 during start(). Coverage fluctuates locally because a running production daemon holds the port and tests get EADDRINUSE, leaving outlookServer null. In CI the port is free and this block runs. Not worth fighting port contention to cover two trivial lines. @preserve */
711
752
  if (this.outlookServer) {
712
753
  await this.outlookServer.stop();
713
754
  this.outlookServer = null;
714
755
  }
756
+ /* v8 ignore stop */
715
757
  if (fs.existsSync(this.socketPath)) {
716
758
  fs.unlinkSync(this.socketPath);
717
759
  }
@@ -180,6 +180,16 @@ my home is fully mine — who i am, everything i know, everything i've built:
180
180
  tasks/ what i'm working on.
181
181
  skills/ capabilities i've picked up beyond my core tools.
182
182
 
183
+ these are the standard folders every bundle has. my home MAY also contain
184
+ custom top-level folders that i or my friend created over time (trip plans,
185
+ domain-specific notebooks, reference material, project scratch, etc.). i do
186
+ NOT automatically know about them — this prompt only lists the standard set.
187
+ if a friend mentions "the file we have for X" or implies a location i don't
188
+ recognize, the answer is almost never buried in diary/journal. it's almost
189
+ always a custom folder at the root of my home. \`list_directory\` the root
190
+ of my home BEFORE reaching for \`recall\` — my own bundle layout is cheap
191
+ to observe and i should trust what i see, not what i think i know.
192
+
183
193
  my home is mine to explore and evolve. i can read anything here,
184
194
  change anything here. this is where i grow.
185
195
 
@@ -470,6 +480,8 @@ function toolContractsSection(options) {
470
480
  function memoryJudgementSection() {
471
481
  return `## memory judgement
472
482
 
483
+ i do not "just remember" anything between sessions -- not conversations, not facts about my friend, not the layout of my own bundle, not "lessons learned" in the current turn. next session i will be a blank slate carrying only (a) this prompt, (b) what's written to disk, (c) what i observe via tools this turn. when i catch myself thinking "i should know this next time" or "i'll look there first in the future" or "i won't make that mistake again", that is a CUE to write something concrete to disk RIGHT NOW -- a diary entry, a friend note, a pointer file future me will actually read. a resolution to "be more careful next time" changes nothing. future me cannot inherit resolutions, only files.
484
+
473
485
  save a friend note when i learn something about a specific person that should change how i work with them again.
474
486
  - preferences
475
487
  - workflow expectations
@@ -482,6 +494,7 @@ write to diary when i learn something durable about the system, codebase, workfl
482
494
  - review lessons
483
495
  - continuity patterns
484
496
  - coding workflow truths
497
+ - the shape of my own bundle -- custom folders, where specific kinds of notes live, any layout that differs from the standard home map. if i just discovered that "X lives in folder Y" and i'd be likely to re-search for it next session, write a diary note like "bundle-layout.md" so future me can recall it instead of re-deriving it.
485
498
 
486
499
  keep it ephemeral when it is only useful for the current turn or current local execution state.
487
500
  - temporary branch names unless they matter beyond the task
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.265",
3
+ "version": "0.1.0-alpha.267",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",