@ouro.bot/cli 0.1.0-alpha.291 → 0.1.0-alpha.293
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,21 @@
|
|
|
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.293",
|
|
6
|
+
"changes": [
|
|
7
|
+
"chore(tests): three test-isolation fixes bundled as chain D1 from the follow-up investigation after PR #372 (default.ouro leak). (1) Remove the `agentName = \"default\"` catch fallback in `src/repertoire/credential-access.ts` getCredentialStore() — same silent-leak class as coding/manager.ts, would have routed BuiltInCredentialStore writes to `~/AgentBundles/default.ouro/vault/` and `~/.agentsecrets/default/` on any test hit that didn't mock identity. Hoisted `getAgentName()` out of the outer try/catch so it throws loudly; the remaining try/catch now only guards the bitwarden store wiring (the only code path that has a legitimate fall-through to built-in). Also switched from `require(\"../heart/identity\")` inside the function body to a static ESM import at the top of the file — require() bypasses vitest's module registry so `vi.mock(\"../heart/identity\", ...)` was silently not applying to the old dynamic require; the static import finally lets the existing test mocks intercept. Tests in credential-access.test.ts that had been accidentally leaning on the default fallback now hit the real mock as intended.",
|
|
8
|
+
"chore(tests): fix the tmpbundle leak guard false-positive on daemon-cli.test.ts. The guard was firing on \"ouro CLI parsing > parses primary daemon commands\" every run, but the real cause was `createTmpBundle` being called at describe-scope inside the \"ouro thoughts CLI execution\" suite (line 5662) — synchronous describe callbacks run during test collection, so the handle landed in _liveHandles before ANY test ran. The first afterEach hook (on the first test in the file) then noticed the dangling handle and blamed it. Fix: moved the createTmpBundle call into a beforeAll hook and tagged it `{ shared: true }` so the handle only exists while the thoughts suite is actually running, with afterAll cleanup aligned to it.",
|
|
9
|
+
"chore(tests): extend the tmpbundle leak guard with a `shared: true` opt-in. `TmpBundleHandle` gains a `shared` field, `CreateTmpBundleOptions.shared` defaults to false, and the per-test leak guard in `src/__tests__/nerves/global-capture.ts` now skips shared handles (they're cleaned in afterAll, not after every test). Prevents the class of false positive exposed by the daemon-cli.test.ts fix above.",
|
|
10
|
+
"chore(tests): new runtime prod-path leak guard in `src/__tests__/nerves/global-capture.ts`. Snapshots `~/AgentBundles` entries at worker boot, diffs at worker teardown (afterAll without describe context runs once per worker), force-removes any new entries, and emits a loud console.error naming them. Text-based contract tests can't catch runtime leaks where production code routes a write via a silent fallback (exactly the bug class PR #372 fixed in coding/manager.ts). This runtime guard is the belt to the contract test's suspenders — would have caught the default.ouro leak in the first run instead of requiring my investigation chain."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"version": "0.1.0-alpha.292",
|
|
15
|
+
"changes": [
|
|
16
|
+
"feat(mind): add structured provenance tracking to diary entries. New DiaryEntryProvenance interface records tool, channel (cli/teams/bluebubbles/inner/mcp), friend identity (id + name), and trust level (family/friend/acquaintance/stranger) at write time. diary_write handler automatically extracts provenance from ToolContext. Associative recall and recall tool render provenance fields when present. Fully backwards compatible — existing entries without provenance parse and display normally. 19 tests across 4 test files with 100% coverage on new code."
|
|
17
|
+
]
|
|
18
|
+
},
|
|
4
19
|
{
|
|
5
20
|
"version": "0.1.0-alpha.291",
|
|
6
21
|
"changes": [
|
|
@@ -197,8 +197,17 @@ async function injectAssociativeRecall(messages, options) {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
for (const fact of recalled) {
|
|
200
|
+
let meta = `score=${fact.score.toFixed(3)} source=${fact.source}`;
|
|
201
|
+
if (fact.provenance) {
|
|
202
|
+
if (fact.provenance.channel)
|
|
203
|
+
meta += ` channel=${fact.provenance.channel}`;
|
|
204
|
+
if (fact.provenance.friendName)
|
|
205
|
+
meta += ` friend=${fact.provenance.friendName}`;
|
|
206
|
+
if (fact.provenance.trust)
|
|
207
|
+
meta += ` trust=${fact.provenance.trust}`;
|
|
208
|
+
}
|
|
200
209
|
resultLines.push({
|
|
201
|
-
text: `[diary] ${fact.text} [
|
|
210
|
+
text: `[diary] ${fact.text} [${meta}]`,
|
|
202
211
|
score: fact.score,
|
|
203
212
|
});
|
|
204
213
|
}
|
package/dist/mind/diary.js
CHANGED
|
@@ -235,6 +235,7 @@ async function saveDiaryEntry(options) {
|
|
|
235
235
|
about: options.about?.trim() || undefined,
|
|
236
236
|
createdAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
237
237
|
embedding,
|
|
238
|
+
...(options.provenance ? { provenance: options.provenance } : {}),
|
|
238
239
|
};
|
|
239
240
|
return appendEntriesWithDedup(stores, [fact], { semanticThreshold: SEMANTIC_DEDUP_THRESHOLD });
|
|
240
241
|
}
|
|
@@ -54,6 +54,7 @@ const path = __importStar(require("node:path"));
|
|
|
54
54
|
const os = __importStar(require("node:os"));
|
|
55
55
|
const node_child_process_1 = require("node:child_process");
|
|
56
56
|
const runtime_1 = require("../nerves/runtime");
|
|
57
|
+
const identity = __importStar(require("../heart/identity"));
|
|
57
58
|
// ---------------------------------------------------------------------------
|
|
58
59
|
// Helpers
|
|
59
60
|
// ---------------------------------------------------------------------------
|
|
@@ -459,14 +460,25 @@ function ensureVaultDefaults(agentName, agentRoot, existingVault) {
|
|
|
459
460
|
function getCredentialStore() {
|
|
460
461
|
if (_store)
|
|
461
462
|
return _store;
|
|
462
|
-
|
|
463
|
+
// Resolve identity up front. The previous implementation wrapped this call
|
|
464
|
+
// in a try/catch that silently fell back to `agentName = "default"`, which
|
|
465
|
+
// meant any test that hit getCredentialStore() without mocking identity
|
|
466
|
+
// silently constructed a BuiltInCredentialStore("default") routing writes
|
|
467
|
+
// to `~/AgentBundles/default.ouro/vault/` and `~/.agentsecrets/default/`.
|
|
468
|
+
// Same silent-leak class we just fixed in coding/manager.ts. No fallback:
|
|
469
|
+
// getAgentName() throws loudly if argv lacks --agent, which is correct for
|
|
470
|
+
// the only caller (production agents with a real identity). Uses the
|
|
471
|
+
// static ESM import above instead of `require("../heart/identity")` so
|
|
472
|
+
// `vi.mock("../heart/identity", ...)` in tests actually intercepts the
|
|
473
|
+
// call — require() bypasses vitest's module registry.
|
|
474
|
+
const agentName = identity.getAgentName();
|
|
475
|
+
const agentRoot = identity.getAgentRoot(agentName);
|
|
476
|
+
// Try to load vault config from agent.json + secrets.json. Only this block
|
|
477
|
+
// is defended with try/catch — bitwarden wiring can legitimately fail
|
|
478
|
+
// (missing vault config, no bw CLI, unreachable server) and the fallback
|
|
479
|
+
// to BuiltInCredentialStore is the intended behavior.
|
|
463
480
|
let backend = "built-in";
|
|
464
481
|
try {
|
|
465
|
-
// Dynamic import to avoid circular dependency at module level
|
|
466
|
-
const identity = require("../heart/identity");
|
|
467
|
-
agentName = identity.getAgentName();
|
|
468
|
-
const agentRoot = identity.getAgentRoot(agentName);
|
|
469
|
-
// Try to load vault config from agent.json + secrets.json
|
|
470
482
|
const config = identity.loadAgentConfig?.();
|
|
471
483
|
// Auto-configure vault defaults if missing
|
|
472
484
|
const vaultSection = ensureVaultDefaults(agentName, agentRoot, config?.vault);
|
|
@@ -495,7 +507,7 @@ function getCredentialStore() {
|
|
|
495
507
|
/* v8 ignore stop */
|
|
496
508
|
}
|
|
497
509
|
catch {
|
|
498
|
-
|
|
510
|
+
// Bitwarden wiring failed — fall through to built-in store below.
|
|
499
511
|
}
|
|
500
512
|
/* v8 ignore next -- false branch only reachable when BitwardenCredentialStore was created above @preserve */
|
|
501
513
|
if (!_store) {
|
|
@@ -505,7 +517,7 @@ function getCredentialStore() {
|
|
|
505
517
|
event: "repertoire.credential_store_init",
|
|
506
518
|
component: "repertoire",
|
|
507
519
|
message: "credential store initialized",
|
|
508
|
-
meta: { backend, agentName
|
|
520
|
+
meta: { backend, agentName },
|
|
509
521
|
});
|
|
510
522
|
return _store;
|
|
511
523
|
}
|
|
@@ -172,7 +172,16 @@ exports.memoryToolDefinitions = [
|
|
|
172
172
|
// Search diary entries
|
|
173
173
|
const hits = await (0, diary_1.searchDiaryEntries)(query, (0, diary_1.readDiaryEntries)());
|
|
174
174
|
for (const fact of hits) {
|
|
175
|
-
|
|
175
|
+
let meta = `source=${fact.source}, createdAt=${fact.createdAt}`;
|
|
176
|
+
if (fact.provenance) {
|
|
177
|
+
if (fact.provenance.channel)
|
|
178
|
+
meta += `, channel=${fact.provenance.channel}`;
|
|
179
|
+
if (fact.provenance.friendName)
|
|
180
|
+
meta += `, friend=${fact.provenance.friendName}`;
|
|
181
|
+
if (fact.provenance.trust)
|
|
182
|
+
meta += `, trust=${fact.provenance.trust}`;
|
|
183
|
+
}
|
|
184
|
+
resultLines.push(`[diary] ${fact.text} (${meta})`);
|
|
176
185
|
}
|
|
177
186
|
// Search journal index
|
|
178
187
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
@@ -219,14 +228,32 @@ exports.memoryToolDefinitions = [
|
|
|
219
228
|
},
|
|
220
229
|
},
|
|
221
230
|
},
|
|
222
|
-
handler: async (a) => {
|
|
231
|
+
handler: async (a, ctx) => {
|
|
223
232
|
const entry = (a.entry || "").trim();
|
|
224
233
|
if (!entry)
|
|
225
234
|
return "entry is required";
|
|
235
|
+
let provenance;
|
|
236
|
+
if (ctx?.context) {
|
|
237
|
+
const p = { tool: "diary_write" };
|
|
238
|
+
const channel = ctx.context.channel?.channel;
|
|
239
|
+
if (channel)
|
|
240
|
+
p.channel = channel;
|
|
241
|
+
const friendId = ctx.context.friend?.id;
|
|
242
|
+
if (friendId)
|
|
243
|
+
p.friendId = friendId;
|
|
244
|
+
const friendName = ctx.context.friend?.name;
|
|
245
|
+
if (friendName)
|
|
246
|
+
p.friendName = friendName;
|
|
247
|
+
const trust = ctx.context.friend?.trustLevel;
|
|
248
|
+
if (trust)
|
|
249
|
+
p.trust = trust;
|
|
250
|
+
provenance = p;
|
|
251
|
+
}
|
|
226
252
|
const result = await (0, diary_1.saveDiaryEntry)({
|
|
227
253
|
text: entry,
|
|
228
254
|
source: "tool:diary_write",
|
|
229
255
|
about: typeof a.about === "string" ? a.about : undefined,
|
|
256
|
+
provenance,
|
|
230
257
|
});
|
|
231
258
|
return `saved diary entry (added=${result.added}, skipped=${result.skipped})`;
|
|
232
259
|
},
|