From 837c67a16f861205cae2a2e2254c2381898c8e8b Mon Sep 17 00:00:00 2001 From: Hermes Agent <314875934+omgbabyweb@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:41:53 -0400 Subject: [PATCH 1/2] feat(obsidian): support observation scopes --- hindsight-integrations/obsidian/README.md | 7 ++- hindsight-integrations/obsidian/src/client.ts | 1 + hindsight-integrations/obsidian/src/main.ts | 1 + .../obsidian/src/node/cli.ts | 19 ++++++ .../obsidian/src/settings.ts | 27 +++++++- hindsight-integrations/obsidian/src/sync.ts | 4 ++ hindsight-integrations/obsidian/src/types.ts | 3 + .../obsidian/tests/client.spec.ts | 16 ++++- .../obsidian/tests/node/cli.spec.ts | 61 +++++++++++++++++++ .../obsidian/tests/sync.spec.ts | 47 ++++++++++++++ 10 files changed, 182 insertions(+), 4 deletions(-) diff --git a/hindsight-integrations/obsidian/README.md b/hindsight-integrations/obsidian/README.md index e5a83f7233..97734b30cb 100644 --- a/hindsight-integrations/obsidian/README.md +++ b/hindsight-integrations/obsidian/README.md @@ -57,6 +57,7 @@ Open **Settings → Hindsight**: | Bank name | `obsidian` | Shared bank for all your vaults (separated by `vault:` tags) | | Include / exclude folders | — | Limit which notes sync | | Sync on edit | on | Re-ingest notes automatically as you edit | +| Observation consolidation | server default | Optional named observation scope for newly ingested content | | Default chat depth | low | Reflect budget for chat answers | | Remember conversations | **off** | When on, chat turns are stored in Hindsight (creates memory outside your vault) | | Prefix document IDs | on | Vault-prefixes ids so shared-bank vaults don't collide; turn off only for a single-vault setup | @@ -83,11 +84,13 @@ hindsight-obsidian-sync reconcile \ hindsight-obsidian-sync reconcile --vault ~/Vaults/Brain --bank my-vault --watch ``` -`--api-url` / `--api-token` fall back to `HINDSIGHT_API_URL` / `HINDSIGHT_API_TOKEN`. Other flags: `--include ` / `--exclude ` (repeatable), `--vault-name ` (defaults to the vault dir name), `--prefix-doc-id` (prefix document ids with the vault name for multi-vault banks), `--index `, and `--help`. +`--api-url` / `--api-token` fall back to `HINDSIGHT_API_URL` / `HINDSIGHT_API_TOKEN`. Other flags: `--include ` / `--exclude ` (repeatable), `--vault-name ` (defaults to the vault dir name), `--prefix-doc-id` (prefix document ids with the vault name for multi-vault banks), `--observation-scopes `, `--index `, and `--help`. + +Observation consolidation is optional. When unset, the integration omits `observation_scopes` and preserves the Hindsight server's default. Source tags are still retained for filtering and provenance in every mode. Changing this setting affects new ingestion work; it does not automatically move existing memories or observations into the new scope, so historical scope migration requires deliberate document reingestion. The sync index (the CLI's equivalent of the plugin's `data.json`) defaults to a **per-target** file under `~/.hindsight/obsidian/` — `--.json`, deliberately **outside** the vault so Obsidian Sync never propagates it. The index is bound to the destination it was built against (API origin, bank, vault path, and the `--vault-name`/`--prefix-doc-id` document-id namespace); pointing a saved index at a different bank or API — whether via the default path or an explicit `--index` — **fails closed** with an actionable message rather than silently skipping files or mis-attributing deletes to a bank it never wrote to. Changing `--include`/`--exclude` on the _same_ destination is fine (it reuses the index and prunes newly-excluded notes it owns there). -> **Running the CLI and the plugin against the same bank + vault?** Keep their scope config identical (`--include`/`--exclude`, `--vault-name`, `--prefix-doc-id` matching the plugin's settings). They each keep their own index, and a reconcile prunes only what its own index tracks — so mismatched scope on the two frontends could let one prune documents the other owns. +> **Running the CLI and the plugin against the same bank + vault?** Keep their config identical (`--include`/`--exclude`, `--vault-name`, `--prefix-doc-id`, and `--observation-scopes` matching the plugin's settings). They each keep their own index, and a reconcile prunes only what its own index tracks — so mismatched folder/id scope on the two frontends could let one prune documents the other owns, while mismatched observation scope would produce inconsistent consolidation. ## How it works diff --git a/hindsight-integrations/obsidian/src/client.ts b/hindsight-integrations/obsidian/src/client.ts index 20e99ad190..f4fc3589bb 100644 --- a/hindsight-integrations/obsidian/src/client.ts +++ b/hindsight-integrations/obsidian/src/client.ts @@ -70,6 +70,7 @@ export class HindsightClient { update_mode: options.updateMode ?? "replace", }; if (options.tags?.length) item.tags = options.tags; + if (options.observationScopes) item.observation_scopes = options.observationScopes; if (options.metadata && Object.keys(options.metadata).length) item.metadata = options.metadata; if (options.timestamp) item.timestamp = options.timestamp; diff --git a/hindsight-integrations/obsidian/src/main.ts b/hindsight-integrations/obsidian/src/main.ts index ef61a11ec0..f89851fc68 100644 --- a/hindsight-integrations/obsidian/src/main.ts +++ b/hindsight-integrations/obsidian/src/main.ts @@ -291,6 +291,7 @@ export default class HindsightPlugin extends Plugin { excludeFolders: this.settings.excludeFolders, vaultName: this.app.vault.getName(), prefixDocId: this.settings.prefixDocId, + observationScopes: this.settings.observationScopes, }; } diff --git a/hindsight-integrations/obsidian/src/node/cli.ts b/hindsight-integrations/obsidian/src/node/cli.ts index 9953d8c14c..e7a9d3ff2c 100644 --- a/hindsight-integrations/obsidian/src/node/cli.ts +++ b/hindsight-integrations/obsidian/src/node/cli.ts @@ -18,6 +18,7 @@ import { parseArgs } from "node:util"; import { HindsightClient } from "../client"; import { SyncEngine, type ReconcileSummary, type SyncConfig } from "../sync"; import type { Transport } from "../transport"; +import type { ObservationScopes } from "../types"; import { fetchTransport } from "./fetch-transport"; import { FsVault } from "./fs-vault"; import { @@ -38,6 +39,7 @@ export interface CliOptions { exclude: string[]; vaultName: string; prefixDocId: boolean; + observationScopes?: ObservationScopes; indexPath: string; watch: boolean; /** Destination the sync index is bound to (issue #3257). */ @@ -58,6 +60,9 @@ Options: --exclude Skip this folder (repeatable) --vault-name Vault name for tags/ids (default: the vault dir name) --prefix-doc-id Prefix document ids with the vault name (multi-vault banks) + --observation-scopes + Observation consolidation: combined, shared, per_tag, + or all_combinations (default: server behavior) --index Sync-index JSON path (default: a per-target file under ~/.hindsight/obsidian/, scoped to bank + API + vault) --watch Keep running and sync changes as they happen @@ -70,6 +75,16 @@ export class UsageError extends Error {} /** Thrown for `--help`; its message is printed to stdout (exit 0). */ export class HelpRequested extends Error {} +const OBSERVATION_SCOPES = ["combined", "shared", "per_tag", "all_combinations"] as const; + +function parseObservationScopes(value: string | undefined): ObservationScopes | undefined { + if (value === undefined) return undefined; + if (OBSERVATION_SCOPES.includes(value as ObservationScopes)) return value as ObservationScopes; + throw new UsageError( + `--observation-scopes must be one of: ${OBSERVATION_SCOPES.join(", ")}` + ); +} + /** Parse argv into fully-resolved options (applying env fallbacks + defaults). */ export function parseCliArgs(argv: string[]): CliOptions { const { values, positionals } = parseArgs({ @@ -84,6 +99,7 @@ export function parseCliArgs(argv: string[]): CliOptions { exclude: { type: "string", multiple: true }, "vault-name": { type: "string" }, "prefix-doc-id": { type: "boolean", default: false }, + "observation-scopes": { type: "string" }, index: { type: "string" }, watch: { type: "boolean", default: false }, help: { type: "boolean", default: false }, @@ -104,6 +120,7 @@ export function parseCliArgs(argv: string[]): CliOptions { const vaultName = values["vault-name"] || basename(vault); const apiUrl = values["api-url"] || process.env.HINDSIGHT_API_URL || ""; if (!apiUrl) throw new UsageError("--api-url is required (or set HINDSIGHT_API_URL)"); + const observationScopes = parseObservationScopes(values["observation-scopes"]); const identity: IndexIdentity = { apiOrigin: canonicalApiOrigin(apiUrl), @@ -122,6 +139,7 @@ export function parseCliArgs(argv: string[]): CliOptions { exclude: values.exclude ?? [], vaultName, prefixDocId: values["prefix-doc-id"] ?? false, + ...(observationScopes ? { observationScopes } : {}), indexPath: values.index || defaultIndexPath(identity), watch: values.watch ?? false, identity, @@ -135,6 +153,7 @@ export function buildConfig(opts: CliOptions): SyncConfig { excludeFolders: opts.exclude, vaultName: opts.vaultName, prefixDocId: opts.prefixDocId, + ...(opts.observationScopes ? { observationScopes: opts.observationScopes } : {}), }; } diff --git a/hindsight-integrations/obsidian/src/settings.ts b/hindsight-integrations/obsidian/src/settings.ts index ce945291f2..5707fa3d3a 100644 --- a/hindsight-integrations/obsidian/src/settings.ts +++ b/hindsight-integrations/obsidian/src/settings.ts @@ -1,6 +1,6 @@ import { type App, Notice, PluginSettingTab, Setting } from "obsidian"; import type HindsightPlugin from "./main"; -import type { Budget } from "./types"; +import type { Budget, ObservationScopes } from "./types"; export interface HindsightSettings { apiUrl: string; @@ -14,6 +14,8 @@ export interface HindsightSettings { /** DESIGN.md §0.5: OFF by default — keeps Hindsight from becoming a 2nd source of truth. */ rememberConversations: boolean; prefixDocId: boolean; + /** Omitted to preserve the Hindsight server's default observation scope. */ + observationScopes?: ObservationScopes; /** Log reflect requests/responses to the console (open devtools to view). */ debugLogging: boolean; /** Chat disclosure open/closed state — remembered across sessions (last value wins). */ @@ -128,6 +130,29 @@ export class HindsightSettingTab extends PluginSettingTab { }) ); + new Setting(containerEl) + .setName("Observation consolidation") + .setDesc( + "How observations are grouped for newly ingested content. Source tags remain available for filtering and provenance. Changing this does not rescope existing memories." + ) + .addDropdown((d) => + d + .addOptions({ + "": "Server default", + combined: "Combined tags", + shared: "Shared", + per_tag: "Per tag", + all_combinations: "All tag combinations", + }) + .setValue(this.plugin.settings.observationScopes ?? "") + .onChange(async (v) => { + this.plugin.settings.observationScopes = v + ? (v as ObservationScopes) + : undefined; + await this.plugin.saveSettings(); + }) + ); + new Setting(containerEl) .setName("Default chat depth") .setDesc("Reflect budget for chat answers.") diff --git a/hindsight-integrations/obsidian/src/sync.ts b/hindsight-integrations/obsidian/src/sync.ts index 591b151177..f9fb99b4e4 100644 --- a/hindsight-integrations/obsidian/src/sync.ts +++ b/hindsight-integrations/obsidian/src/sync.ts @@ -11,6 +11,7 @@ import { createHash } from "node:crypto"; import type { HindsightClient } from "./client"; import { normalizeNote } from "./frontmatter"; +import type { ObservationScopes } from "./types"; export interface SyncFile { path: string; @@ -38,6 +39,8 @@ export interface SyncConfig { vaultName: string; /** Prefix document ids with the vault name (for multi-vault shared banks). */ prefixDocId: boolean; + /** Omit to preserve the Hindsight server's default consolidation scope. */ + observationScopes?: ObservationScopes; } export interface ReconcileSummary { @@ -167,6 +170,7 @@ export class SyncEngine { await this.client.retain(this.config.bankId, this.docId(file.path), note.body, { tags, + observationScopes: this.config.observationScopes, metadata, timestamp: note.timestamp ?? isoFromMillis(file.stat.ctime), updateMode: "replace", diff --git a/hindsight-integrations/obsidian/src/types.ts b/hindsight-integrations/obsidian/src/types.ts index 69a805d44f..ffea1c8fd1 100644 --- a/hindsight-integrations/obsidian/src/types.ts +++ b/hindsight-integrations/obsidian/src/types.ts @@ -6,6 +6,8 @@ export type Budget = "low" | "mid" | "high"; +export type ObservationScopes = "combined" | "shared" | "per_tag" | "all_combinations"; + export type TagMatch = "any" | "all" | "any_strict" | "all_strict"; /** A leaf tag filter. `all_strict` = AND match that also excludes untagged memories. */ @@ -24,6 +26,7 @@ export type TagGroup = TagLeaf | TagAnd; export interface RetainOptions { tags?: string[]; + observationScopes?: ObservationScopes; metadata?: Record; /** ISO 8601 timestamp, or "unset" for timeless content. */ timestamp?: string; diff --git a/hindsight-integrations/obsidian/tests/client.spec.ts b/hindsight-integrations/obsidian/tests/client.spec.ts index ddff6c3423..1357d40a99 100644 --- a/hindsight-integrations/obsidian/tests/client.spec.ts +++ b/hindsight-integrations/obsidian/tests/client.spec.ts @@ -105,10 +105,24 @@ describe("HindsightClient", () => { expect(body.tag_groups).toBeUndefined(); }); - it("retain omits the tags field when no tags are given", async () => { + it("retain sends a named observation scope beside existing tags", async () => { + const client = new HindsightClient("https://api.example.com", undefined, mock); + await client.retain("b", "Note.md", "body", { + tags: ["vault:Notes", "topic:planning"], + observationScopes: "shared", + }); + const body = JSON.parse(lastCall().body ?? "{}"); + expect(body.items[0]).toMatchObject({ + tags: ["vault:Notes", "topic:planning"], + observation_scopes: "shared", + }); + }); + + it("retain omits optional tags and observation scopes when unset", async () => { const client = new HindsightClient("https://api.example.com", undefined, mock); await client.retain("b", "Note.md", "body"); const body = JSON.parse(lastCall().body ?? "{}"); expect(body.items[0].tags).toBeUndefined(); + expect(body.items[0].observation_scopes).toBeUndefined(); }); }); diff --git a/hindsight-integrations/obsidian/tests/node/cli.spec.ts b/hindsight-integrations/obsidian/tests/node/cli.spec.ts index e7a941a8e5..cfb1b91c8b 100644 --- a/hindsight-integrations/obsidian/tests/node/cli.spec.ts +++ b/hindsight-integrations/obsidian/tests/node/cli.spec.ts @@ -83,6 +83,18 @@ describe("parseCliArgs", () => { expect(o.apiToken).toBe("envtok"); }); + it("accepts named observation scopes and rejects unsupported values", () => { + expect(parseCliArgs([...base, "--observation-scopes", "shared"]).observationScopes).toBe( + "shared" + ); + expect(parseCliArgs([...base, "--observation-scopes", "all_combinations"]).observationScopes).toBe( + "all_combinations" + ); + expect(() => parseCliArgs([...base, "--observation-scopes", "custom"])).toThrow( + /observation-scopes/ + ); + }); + it("requires --vault, --bank and an api url", () => { expect(() => parseCliArgs(["--bank", "b", "--api-url", "h"])).toThrow(UsageError); expect(() => parseCliArgs(["--vault", "/v", "--api-url", "h"])).toThrow(UsageError); @@ -108,6 +120,22 @@ describe("buildConfig", () => { prefixDocId: true, }); }); + + it("maps an explicitly configured observation scope", () => { + const cfg = buildConfig( + parseCliArgs([ + "--vault", + "/v", + "--bank", + "b", + "--api-url", + "h", + "--observation-scopes", + "shared", + ]) + ); + expect(cfg.observationScopes).toBe("shared"); + }); }); describe("createWatchHandlers", () => { @@ -163,6 +191,39 @@ describe("runCli", () => { expect(init?.method).toBe("POST"); }); + it("sends observation_scopes through the full CLI retain path", async () => { + await writeFile(join(root, "note.md"), "# Note\nremember this"); + let retained: Record | undefined; + vi.stubGlobal( + "fetch", + vi.fn(async (_url: string, init?: RequestInit) => { + const body = JSON.parse(String(init?.body ?? "{}")); + retained = body.items?.[0]; + return { status: 200, text: async () => "{}" } as unknown as Response; + }) + ); + + expect( + await runCli( + [ + "--vault", + root, + "--bank", + "b", + "--api-url", + "https://h", + "--observation-scopes", + "shared", + "--index", + join(root, "scope.json"), + ], + () => {} + ) + ).toBe(0); + expect(retained).toMatchObject({ observation_scopes: "shared", update_mode: "replace" }); + expect(retained?.tags).toEqual(expect.arrayContaining([expect.stringMatching(/^vault:/)])); + }); + it("prints help and exits 0", async () => { const out: string[] = []; expect(await runCli(["--help"], (m) => out.push(m))).toBe(0); diff --git a/hindsight-integrations/obsidian/tests/sync.spec.ts b/hindsight-integrations/obsidian/tests/sync.spec.ts index 3ffde7dd89..f3d24c843c 100644 --- a/hindsight-integrations/obsidian/tests/sync.spec.ts +++ b/hindsight-integrations/obsidian/tests/sync.spec.ts @@ -106,6 +106,53 @@ describe("SyncEngine", () => { expect(opts.metadata.vault).toBe("Personal"); }); + it("passes observation scope through without changing note provenance", async () => { + const created = Date.UTC(2026, 2, 15); + const updated = Date.UTC(2026, 5, 20); + const files = { + "Work/note.md": { + content: "---\ntags: [planning]\nsource: meeting\n---\n# Decision\nkeep it simple", + mtime: updated, + ctime: created, + }, + }; + const { engine, client } = makeEngine(files, {}, { observationScopes: "shared" }); + + await engine.reconcile(); + + const [, docId, content, opts] = client.retain.mock.calls[0] as [ + string, + string, + string, + { + tags: string[]; + metadata: Record; + timestamp: string; + updateMode: string; + observationScopes?: string; + }, + ]; + expect(docId).toBe("Work/note.md"); + expect(content).toBe("# Decision\nkeep it simple"); + expect(opts).toMatchObject({ + observationScopes: "shared", + updateMode: "replace", + timestamp: new Date(created).toISOString(), + metadata: { source: "meeting", vault: "Vault", path: "Work/note.md" }, + }); + expect(opts.tags).toEqual( + expect.arrayContaining(["planning", "vault:Vault", "folder:Work", "created:2026-03"]) + ); + }); + + it("omits observation scope from retain options when unset", async () => { + const files = { "a.md": { content: "body", mtime: 1, ctime: 0 } }; + const { engine, client } = makeEngine(files); + await engine.reconcile(); + const opts = client.retain.mock.calls[0][3] as Record; + expect(opts.observationScopes).toBeUndefined(); + }); + it("re-ingests (updated) when content changes", async () => { const index: SyncIndex = {}; const filesV1 = { "a.md": { content: "v1", mtime: 1, ctime: 0 } }; From 788bdbf1b1708099a3e40148a86b63af4635e779 Mon Sep 17 00:00:00 2001 From: Hermes Agent <314875934+omgbabyweb@users.noreply.github.com> Date: Fri, 28 Aug 2026 23:31:51 -0400 Subject: [PATCH 2/2] [verified] fix(obsidian): apply scopes to remembered chat --- .../obsidian/src/chat-view.ts | 1 + hindsight-integrations/obsidian/src/chat.ts | 6 +++++- .../obsidian/src/node/cli.ts | 4 +--- .../obsidian/src/settings.ts | 4 +--- .../obsidian/tests/chat.spec.ts | 21 +++++++++++-------- .../obsidian/tests/node/cli.spec.ts | 6 +++--- 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/hindsight-integrations/obsidian/src/chat-view.ts b/hindsight-integrations/obsidian/src/chat-view.ts index d633c595bc..e28b280129 100644 --- a/hindsight-integrations/obsidian/src/chat-view.ts +++ b/hindsight-integrations/obsidian/src/chat-view.ts @@ -360,6 +360,7 @@ export class ChatView extends ItemView { bankId: this.plugin.getBankId(), budget: this.plugin.settings.defaultBudget, rememberConversations: this.plugin.settings.rememberConversations, + observationScopes: this.plugin.settings.observationScopes, tagGroups: this.scopeTagGroups(), debug: this.plugin.settings.debugLogging, }, diff --git a/hindsight-integrations/obsidian/src/chat.ts b/hindsight-integrations/obsidian/src/chat.ts index 5f26a7d00a..81a0d3ca05 100644 --- a/hindsight-integrations/obsidian/src/chat.ts +++ b/hindsight-integrations/obsidian/src/chat.ts @@ -9,13 +9,15 @@ import type { HindsightClient } from "./client"; import { retrievedNotes } from "./reflect-util"; -import type { Budget, ReflectResponse, TagGroup } from "./types"; +import type { Budget, ObservationScopes, ReflectResponse, TagGroup } from "./types"; export interface ChatTurnDeps { client: HindsightClient; bankId: string; budget: Budget; rememberConversations: boolean; + /** Observation grouping applied to retained chat turns; undefined preserves the server default. */ + observationScopes?: ObservationScopes; /** Scope filter (vault/folder) applied to reflect; undefined = whole bank. */ tagGroups?: TagGroup[]; /** When true, log the reflect request/response to the console for debugging. */ @@ -40,6 +42,7 @@ export async function runChatTurn(deps: ChatTurnDeps, message: string): Promise< await deps.client.retain(deps.bankId, genId("user"), message, { tags: ["conversation", "user"], context: "obsidian-chat", + observationScopes: deps.observationScopes, }); } @@ -71,6 +74,7 @@ export async function runChatTurn(deps: ChatTurnDeps, message: string): Promise< await deps.client.retain(deps.bankId, genId("assistant"), response.text, { tags: ["conversation", "assistant"], context: "obsidian-chat", + observationScopes: deps.observationScopes, }); } diff --git a/hindsight-integrations/obsidian/src/node/cli.ts b/hindsight-integrations/obsidian/src/node/cli.ts index e7a9d3ff2c..a1c4c17eaa 100644 --- a/hindsight-integrations/obsidian/src/node/cli.ts +++ b/hindsight-integrations/obsidian/src/node/cli.ts @@ -80,9 +80,7 @@ const OBSERVATION_SCOPES = ["combined", "shared", "per_tag", "all_combinations"] function parseObservationScopes(value: string | undefined): ObservationScopes | undefined { if (value === undefined) return undefined; if (OBSERVATION_SCOPES.includes(value as ObservationScopes)) return value as ObservationScopes; - throw new UsageError( - `--observation-scopes must be one of: ${OBSERVATION_SCOPES.join(", ")}` - ); + throw new UsageError(`--observation-scopes must be one of: ${OBSERVATION_SCOPES.join(", ")}`); } /** Parse argv into fully-resolved options (applying env fallbacks + defaults). */ diff --git a/hindsight-integrations/obsidian/src/settings.ts b/hindsight-integrations/obsidian/src/settings.ts index 5707fa3d3a..d1617cd69e 100644 --- a/hindsight-integrations/obsidian/src/settings.ts +++ b/hindsight-integrations/obsidian/src/settings.ts @@ -146,9 +146,7 @@ export class HindsightSettingTab extends PluginSettingTab { }) .setValue(this.plugin.settings.observationScopes ?? "") .onChange(async (v) => { - this.plugin.settings.observationScopes = v - ? (v as ObservationScopes) - : undefined; + this.plugin.settings.observationScopes = v ? (v as ObservationScopes) : undefined; await this.plugin.saveSettings(); }) ); diff --git a/hindsight-integrations/obsidian/tests/chat.spec.ts b/hindsight-integrations/obsidian/tests/chat.spec.ts index 235fb7eaa8..25b0fe0407 100644 --- a/hindsight-integrations/obsidian/tests/chat.spec.ts +++ b/hindsight-integrations/obsidian/tests/chat.spec.ts @@ -59,7 +59,7 @@ describe("runChatTurn", () => { ); }); - it("retains user + assistant turns only when explicitly enabled", async () => { + it("applies observation scopes to retained user and assistant turns", async () => { const client = fakeClient(ANSWER); let n = 0; await runChatTurn( @@ -68,25 +68,28 @@ describe("runChatTurn", () => { bankId: "bank", budget: "low", rememberConversations: true, + observationScopes: "shared", newConversationDocId: (role) => `conversation/${(n += 1)}-${role}`, }, "hello" ); expect(client.retain).toHaveBeenCalledTimes(2); - expect(client.retain).toHaveBeenNthCalledWith( - 1, - "bank", - "conversation/1-user", - "hello", - expect.objectContaining({ tags: ["conversation", "user"] }) - ); + expect(client.retain).toHaveBeenNthCalledWith(1, "bank", "conversation/1-user", "hello", { + tags: ["conversation", "user"], + context: "obsidian-chat", + observationScopes: "shared", + }); expect(client.retain).toHaveBeenNthCalledWith( 2, "bank", "conversation/2-assistant", "grounded answer", - expect.objectContaining({ tags: ["conversation", "assistant"] }) + { + tags: ["conversation", "assistant"], + context: "obsidian-chat", + observationScopes: "shared", + } ); }); }); diff --git a/hindsight-integrations/obsidian/tests/node/cli.spec.ts b/hindsight-integrations/obsidian/tests/node/cli.spec.ts index cfb1b91c8b..9e82e55443 100644 --- a/hindsight-integrations/obsidian/tests/node/cli.spec.ts +++ b/hindsight-integrations/obsidian/tests/node/cli.spec.ts @@ -87,9 +87,9 @@ describe("parseCliArgs", () => { expect(parseCliArgs([...base, "--observation-scopes", "shared"]).observationScopes).toBe( "shared" ); - expect(parseCliArgs([...base, "--observation-scopes", "all_combinations"]).observationScopes).toBe( - "all_combinations" - ); + expect( + parseCliArgs([...base, "--observation-scopes", "all_combinations"]).observationScopes + ).toBe("all_combinations"); expect(() => parseCliArgs([...base, "--observation-scopes", "custom"])).toThrow( /observation-scopes/ );