@ouro.bot/cli 0.1.0-alpha.32 → 0.1.0-alpha.33

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
@@ -164,13 +164,10 @@ Plus 2 optional:
164
164
 
165
165
  - **FriendRecord** (`friends/types.ts`): the single merged type for a person the agent knows. Contains `displayName`, `externalIds[]` (cross-provider identity links), `toolPreferences` (keyed by integration name), `notes` (general friend knowledge), `tenantMemberships`, timestamps, and schema version.
166
166
  - **FriendStore** (`friends/store.ts`): domain-specific persistence interface (`get`, `put`, `delete`, `findByExternalId`).
167
- - **FileFriendStore** (`friends/store-file.ts`): two-backend storage split by PII boundary:
168
- - **Agent knowledge** (`{agentRoot}/friends/{uuid}.json`): id, displayName, toolPreferences, notes, timestamps, schemaVersion. Committed to the repo -- no PII.
169
- - **PII bridge** (`~/.agentstate/{agentName}/friends/{uuid}.json`): id, externalIds, tenantMemberships, schemaVersion. Local-only -- contains PII.
170
- - `get()` merges both backends. `put()` splits and writes both. `findByExternalId()` scans PII bridge, then merges with agent knowledge.
167
+ - **FileFriendStore** (`friends/store-file.ts`): bundle-local JSON storage at `{agentRoot}/friends/{uuid}.json`. Friend identity, notes, externalIds, tenantMemberships, timestamps, and schemaVersion are kept together in the bundle.
171
168
  - **Channel** (`friends/channel.ts`): `ChannelCapabilities` -- what the current channel supports (markdown, streaming, rich cards, max message length, available integrations).
172
169
  - **FriendResolver** (`friends/resolver.ts`): find-or-create by external ID. First encounter creates a new FriendRecord with system-provided name and empty notes/preferences. Returning friends are found via `findByExternalId()`. DisplayName is never overwritten on existing records.
173
- - **Session paths**: `~/.agentstate/{agentName}/sessions/{friendUuid}/{channel}/{sessionId}.json`. Each friend gets their own session directory.
170
+ - **Session paths**: `{agentRoot}/state/sessions/{friendUuid}/{channel}/{sessionId}.json`. Each friend gets their own session directory inside the bundle-local runtime state area.
174
171
 
175
172
  Design principles: don't persist what you can re-derive; conversation IS the cache; the model manages memory freeform via `save_friend_note`; toolPreferences go to tool descriptions (not system prompt); notes go to system prompt (not tool descriptions).
176
173
 
package/changelog.json CHANGED
@@ -1,6 +1,13 @@
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.33",
6
+ "changes": [
7
+ "Agent-owned runtime state now lives inside each bundle's `state/` directory instead of under `~/.agentstate/<agent>/...`, so sessions, logs, pending messages, coding session persistence, and BlueBubbles mutation logs stay co-located with the rest of the bundle.",
8
+ "Bundle-local state paths now stay durable on Azure too, and `state/` is treated as canonical bundle content while secrets continue to live in `~/.agentsecrets/<agent>/secrets.json`."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.32",
6
13
  "changes": [
@@ -51,11 +51,11 @@ exports.getBlueBubblesChannelConfig = getBlueBubblesChannelConfig;
51
51
  exports.getIntegrationsConfig = getIntegrationsConfig;
52
52
  exports.getOpenAIEmbeddingsApiKey = getOpenAIEmbeddingsApiKey;
53
53
  exports.getLogsDir = getLogsDir;
54
+ exports.resolveSessionPath = resolveSessionPath;
54
55
  exports.sessionPath = sessionPath;
55
56
  exports.logPath = logPath;
56
57
  const fs = __importStar(require("fs"));
57
58
  const path = __importStar(require("path"));
58
- const os = __importStar(require("os"));
59
59
  const identity_1 = require("./identity");
60
60
  const runtime_1 = require("../nerves/runtime");
61
61
  const DEFAULT_SECRETS_TEMPLATE = {
@@ -339,20 +339,21 @@ function getOpenAIEmbeddingsApiKey() {
339
339
  return getIntegrationsConfig().openaiEmbeddingsApiKey;
340
340
  }
341
341
  function getLogsDir() {
342
- return path.join(os.homedir(), ".agentstate", (0, identity_1.getAgentName)(), "logs");
342
+ return path.join((0, identity_1.getAgentRoot)(), "state", "logs");
343
343
  }
344
344
  function sanitizeKey(key) {
345
345
  return key.replace(/[/:]/g, "_");
346
346
  }
347
- function sessionPath(friendId, channel, key) {
348
- // On Azure App Service, os.homedir() returns /root which is ephemeral.
349
- // Use /home (persistent storage) when WEBSITE_SITE_NAME is set.
350
- /* v8 ignore next -- Azure vs local path branch; environment-specific @preserve */
351
- const homeBase = process.env.WEBSITE_SITE_NAME ? "/home" : os.homedir();
352
- const dir = path.join(homeBase, ".agentstate", (0, identity_1.getAgentName)(), "sessions", friendId, channel);
353
- fs.mkdirSync(dir, { recursive: true });
347
+ function resolveSessionPath(friendId, channel, key, options) {
348
+ const dir = path.join((0, identity_1.getAgentRoot)(), "state", "sessions", friendId, channel);
349
+ if (options?.ensureDir) {
350
+ fs.mkdirSync(dir, { recursive: true });
351
+ }
354
352
  return path.join(dir, sanitizeKey(key) + ".json");
355
353
  }
354
+ function sessionPath(friendId, channel, key) {
355
+ return resolveSessionPath(friendId, channel, key, { ensureDir: true });
356
+ }
356
357
  function logPath(channel, key) {
357
358
  return path.join(getLogsDir(), channel, sanitizeKey(key) + ".ndjson");
358
359
  }
@@ -39,6 +39,7 @@ exports.getAgentName = getAgentName;
39
39
  exports.getRepoRoot = getRepoRoot;
40
40
  exports.getAgentBundlesRoot = getAgentBundlesRoot;
41
41
  exports.getAgentRoot = getAgentRoot;
42
+ exports.getAgentStateRoot = getAgentStateRoot;
42
43
  exports.getAgentSecretsPath = getAgentSecretsPath;
43
44
  exports.loadAgentConfig = loadAgentConfig;
44
45
  exports.setAgentName = setAgentName;
@@ -175,13 +176,20 @@ function getRepoRoot() {
175
176
  * Returns the shared bundle root directory: `~/AgentBundles/`
176
177
  */
177
178
  function getAgentBundlesRoot() {
178
- return path.join(os.homedir(), "AgentBundles");
179
+ const homeBase = process.env.WEBSITE_SITE_NAME ? "/home" : os.homedir();
180
+ return path.join(homeBase, "AgentBundles");
179
181
  }
180
182
  /**
181
183
  * Returns the agent-specific bundle directory: `~/AgentBundles/<agentName>.ouro/`
182
184
  */
183
- function getAgentRoot() {
184
- return path.join(getAgentBundlesRoot(), `${getAgentName()}.ouro`);
185
+ function getAgentRoot(agentName = getAgentName()) {
186
+ return path.join(getAgentBundlesRoot(), `${agentName}.ouro`);
187
+ }
188
+ /**
189
+ * Returns the bundle-local runtime state directory: `~/AgentBundles/<agentName>.ouro/state/`
190
+ */
191
+ function getAgentStateRoot(agentName = getAgentName()) {
192
+ return path.join(getAgentRoot(agentName), "state");
185
193
  }
186
194
  /**
187
195
  * Returns the conventional secrets path: `~/.agentsecrets/<agentName>/secrets.json`
@@ -54,6 +54,7 @@ exports.CANONICAL_BUNDLE_MANIFEST = [
54
54
  { path: "psyche/ASPIRATIONS.md", kind: "file" },
55
55
  { path: "psyche/memory", kind: "dir" },
56
56
  { path: "friends", kind: "dir" },
57
+ { path: "state", kind: "dir" },
57
58
  { path: "tasks", kind: "dir" },
58
59
  { path: "skills", kind: "dir" },
59
60
  { path: "senses", kind: "dir" },
@@ -37,10 +37,10 @@ exports.getPendingDir = getPendingDir;
37
37
  exports.drainPending = drainPending;
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
- const os = __importStar(require("os"));
40
+ const identity_1 = require("../heart/identity");
41
41
  const runtime_1 = require("../nerves/runtime");
42
42
  function getPendingDir(agentName, friendId, channel, key) {
43
- return path.join(os.homedir(), ".agentstate", agentName, "pending", friendId, channel, key);
43
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "pending", friendId, channel, key);
44
44
  }
45
45
  function drainPending(pendingDir) {
46
46
  if (!fs.existsSync(pendingDir))
@@ -44,7 +44,6 @@ const core_1 = require("../heart/core");
44
44
  const tools_1 = require("../repertoire/tools");
45
45
  const skills_1 = require("../repertoire/skills");
46
46
  const identity_1 = require("../heart/identity");
47
- const os = __importStar(require("os"));
48
47
  const channel_1 = require("./friends/channel");
49
48
  const runtime_1 = require("../nerves/runtime");
50
49
  const bundle_manifest_1 = require("./bundle-manifest");
@@ -421,7 +420,7 @@ async function buildSystem(channel = "cli", options, context) {
421
420
  skillsSection(),
422
421
  taskBoardSection(),
423
422
  buildSessionSummary({
424
- sessionsDir: path.join(os.homedir(), ".agentstate", (0, identity_1.getAgentName)(), "sessions"),
423
+ sessionsDir: path.join((0, identity_1.getAgentRoot)(), "state", "sessions"),
425
424
  friendsDir: path.join((0, identity_1.getAgentRoot)(), "friends"),
426
425
  agentName: (0, identity_1.getAgentName)(),
427
426
  currentFriendId: context?.friend?.id,
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CodingSessionManager = void 0;
37
37
  const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
38
  const path = __importStar(require("path"));
40
39
  const identity_1 = require("../../heart/identity");
41
40
  const runtime_1 = require("../../nerves/runtime");
@@ -49,7 +48,7 @@ function safeAgentName() {
49
48
  }
50
49
  }
51
50
  function defaultStateFilePath(agentName) {
52
- return path.join(os.homedir(), ".agentstate", agentName, "coding", "sessions.json");
51
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "coding", "sessions.json");
53
52
  }
54
53
  function isPidAlive(pid) {
55
54
  try {
@@ -41,10 +41,10 @@ const skills_1 = require("./skills");
41
41
  const config_1 = require("../heart/config");
42
42
  const runtime_1 = require("../nerves/runtime");
43
43
  const identity_1 = require("../heart/identity");
44
- const os = __importStar(require("os"));
45
44
  const tasks_1 = require("./tasks");
46
45
  const tools_1 = require("./coding/tools");
47
46
  const memory_1 = require("../mind/memory");
47
+ const pending_1 = require("../mind/pending");
48
48
  const postIt = (msg) => `post-it from past you:\n${msg}`;
49
49
  function normalizeOptionalText(value) {
50
50
  if (typeof value !== "string")
@@ -692,7 +692,7 @@ exports.baseToolDefinitions = [
692
692
  const channel = args.channel;
693
693
  const key = args.key || "session";
694
694
  const count = parseInt(args.messageCount || "20", 10);
695
- const sessFile = path.join(os.homedir(), ".agentstate", (0, identity_1.getAgentName)(), "sessions", friendId, channel, `${key}.json`);
695
+ const sessFile = (0, config_1.resolveSessionPath)(friendId, channel, key);
696
696
  const raw = fs.readFileSync(sessFile, "utf-8");
697
697
  const data = JSON.parse(raw);
698
698
  const messages = (data.messages || [])
@@ -741,7 +741,7 @@ exports.baseToolDefinitions = [
741
741
  const key = args.key || "session";
742
742
  const content = args.content;
743
743
  const now = Date.now();
744
- const pendingDir = path.join(os.homedir(), ".agentstate", (0, identity_1.getAgentName)(), "pending", friendId, channel, key);
744
+ const pendingDir = (0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), friendId, channel, key);
745
745
  fs.mkdirSync(pendingDir, { recursive: true });
746
746
  const fileName = `${now}-${Math.random().toString(36).slice(2, 10)}.json`;
747
747
  const filePath = path.join(pendingDir, fileName);
@@ -36,14 +36,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getBlueBubblesMutationLogPath = getBlueBubblesMutationLogPath;
37
37
  exports.recordBlueBubblesMutation = recordBlueBubblesMutation;
38
38
  const fs = __importStar(require("node:fs"));
39
- const os = __importStar(require("node:os"));
40
39
  const path = __importStar(require("node:path"));
41
40
  const runtime_1 = require("../nerves/runtime");
41
+ const identity_1 = require("../heart/identity");
42
42
  function sanitizeKey(key) {
43
43
  return key.replace(/[/:]/g, "_");
44
44
  }
45
45
  function getBlueBubblesMutationLogPath(agentName, sessionKey) {
46
- return path.join(os.homedir(), ".agentstate", agentName, "senses", "bluebubbles", "mutations", `${sanitizeKey(sessionKey)}.ndjson`);
46
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "senses", "bluebubbles", "mutations", `${sanitizeKey(sessionKey)}.ndjson`);
47
47
  }
48
48
  function recordBlueBubblesMutation(agentName, event) {
49
49
  const filePath = getBlueBubblesMutationLogPath(agentName, event.chat.sessionKey);
@@ -431,12 +431,7 @@ async function withConversationLock(convId, fn) {
431
431
  // Create a fresh friend store per request so mkdirSync re-runs if directories
432
432
  // are deleted while the process is alive.
433
433
  function getFriendStore() {
434
- // On Azure App Service, os.homedir() returns /root which is ephemeral.
435
- // Use /home/.agentstate/ (persistent) when WEBSITE_SITE_NAME is set.
436
- /* v8 ignore next 3 -- Azure vs local path branch; environment-specific @preserve */
437
- const friendsPath = process.env.WEBSITE_SITE_NAME
438
- ? path.join("/home", ".agentstate", (0, identity_1.getAgentName)(), "friends")
439
- : path.join((0, identity_1.getAgentRoot)(), "friends");
434
+ const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
440
435
  return new store_file_1.FileFriendStore(friendsPath);
441
436
  }
442
437
  // Handle an incoming Teams message
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.32",
3
+ "version": "0.1.0-alpha.33",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",