npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@handsomecheese/opencode-hindsight

v0.2.17

Published

Hindsight memory plugin for OpenCode - Give your AI coding agent persistent long-term memory

Readme

@handsomecheese/opencode-hindsight

Hindsight memory plugin for OpenCode. It gives coding agents long-term memory across sessions while keeping the legacy single-bank path and the newer server-authorized policy path explicit.

Features

  • Explicit tools: hindsight_retain, hindsight_recall, hindsight_reflect
  • Legacy mode for the current single-bank or dynamic-bank workflow
  • Server mode for policy-aware OpenCode calls backed by Hindsight Server
  • Auto-recall, auto-retain, and compaction hooks, with fail closed behavior in server mode when no reliable snapshot exists

Quick Start

1. Enable the plugin

Add this to opencode.json in your project, or to ~/.config/opencode/opencode.json globally:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@handsomecheese/opencode-hindsight"]
}

OpenCode installs plugins listed here when it starts. No separate npm install step is needed for normal use.

2. Pick a setup path

Most users only need to list the plugin in opencode.json, then set a few values with environment variables, inline plugin options, or ~/.hindsight/opencode.json.

Minimal legacy setup

export HINDSIGHT_API_URL="http://localhost:8888"
export HINDSIGHT_POLICY_MODE="legacy"
export HINDSIGHT_BANK_ID="my-project"

This starts the current single-bank path. Every explicit tool call and automatic hook uses the same legacy bank.

Server policy mode setup

export HINDSIGHT_API_URL="http://localhost:8888"
export HINDSIGHT_POLICY_MODE="server"
export HINDSIGHT_POLICY_CANONICAL_AGENT_ID="opencode:workspace-1:builder"

In policyMode="server", Hindsight Server resolves OpenCode policy through /v1/opencode/* endpoints. The plugin does not authorize by picking a local fallback bank. Automatic hooks also fail closed when there is no reliable policy snapshot for the current session. The desired policy authored in Dashboard AgentAccess is the server-mode source of truth.

Using Hindsight Cloud

Get an API key at ui.hindsight.vectorize.io/connect, then either export environment variables:

export HINDSIGHT_API_URL="https://api.hindsight.vectorize.io"
export HINDSIGHT_API_TOKEN="your-api-key"
export HINDSIGHT_POLICY_MODE="server"
export HINDSIGHT_POLICY_CANONICAL_AGENT_ID="opencode:workspace-1:builder"

Or configure inline in opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    [
      "@handsomecheese/opencode-hindsight",
      {
        "hindsightApiUrl": "https://api.hindsight.vectorize.io",
        "hindsightApiToken": "your-api-key",
        "policyMode": "server"
      }
    ]
  ]
}

Configuration

OpenCode users normally configure this plugin in opencode.json. You can also keep shared defaults in ~/.hindsight/opencode.json, then override them per project or per shell.

Plugin Array Forms

Minimal plugin list:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@handsomecheese/opencode-hindsight"]
}

Inline tuple with options:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    [
      "@handsomecheese/opencode-hindsight",
      {
        "hindsightApiUrl": "http://localhost:8888",
        "policyMode": "legacy",
        "bankId": "my-project"
      }
    ]
  ]
}

OpenCode installs every plugin listed in the plugin array on startup.

Inline Plugin Options

Pass options directly in opencode.json:

{
  "plugin": [
    [
      "@handsomecheese/opencode-hindsight",
      {
        "hindsightApiUrl": "http://localhost:8888",
        "policyMode": "legacy",
        "bankId": "my-project",
        "dynamicBankId": false,
        "autoRecall": true,
        "autoRetain": true,
        "retainMode": "full-session",
        "recallBudget": "mid",
        "recallMaxTokens": 1024,
        "debug": false
      }
    ]
  ]
}

Common inline option names:

| Option | Meaning | Default | | --- | --- | --- | | hindsightApiUrl | Hindsight API base URL | null | | hindsightApiToken | API token for authenticated servers or cloud | null | | requestTimeoutSeconds | Bounds outbound Hindsight requests in seconds. 0 keeps the current unbounded request behavior. This is a plugin runtime guard only, not a persisted AgentAccess or server policy setting | 0 | | policyMode | legacy or server | legacy | | policyApiPrefix | Server policy endpoint prefix | /v1/opencode | | policyTenantId | Optional tenant override in server mode | null | | policyRuntimeId | Optional runtime override in server mode | null | | policyWorkspaceId | Optional workspace override in server mode | null | | policyCanonicalAgentId | Agent identity used for server policy registration | null | | bankId | Static bank ID in legacy mode | null, plugin falls back to opencode | | bankIdPrefix | Prefix added before static or dynamic bank IDs | "" | | directoryBankMap | Legacy directory prefix to bank ID map. Longest matching prefix wins before static or dynamic bank selection | {} | | dynamicBankId | Derive legacy bank IDs from context | false | | dynamicBankGranularity | Dynamic bank ID fields | ["agent", "project"] | | resolveWorktrees | Resolve linked git worktrees to the main worktree root before directory mapping or path-aware dynamic fields | false | | agentName | Agent naming hint used by legacy bank derivation | opencode | | autoRecall | Enable automatic recall hook | true | | recallAdditionalBanks | Extra bank IDs appended to recall reads. Empty keeps the current primary bank or server-selected read set | [] | | autoRetain | Enable automatic retain hook | true | | retainMode | full-session or last-turn | full-session | | retainEveryNTurns | Retain cadence for automatic retention | 3 | | retainOverlapTurns | Overlap when retainMode is last-turn | 2 | | retainContext | Retain context label | opencode | | retainTags | Tags attached to retained memories | [] | | retainRoles | Role allowlist for retained transcript candidate messages. Empty keeps all roles | [] | | sessionEndRetain | Opt in to a final retain pass when OpenCode reports idle status | false | | recallBudget | low, mid, or high | mid | | recallMaxTokens | Max tokens used for recall results | 1024 | | recallContextTurns | Recent turns used to shape recall query | 1 | | recallMaxQueryChars | Max recall query length | 800 | | recallTags | Optional recall tag filter | [] | | recallTagsMatch | any, all, any_strict, all_strict | any | | recallRoles | Role allowlist for prior-context lines used to compose recall queries. Empty keeps all roles | [] | | bankMission | Legacy bank mission text | "" | | retainMission | Optional retain mission override | null | | debug | Enable plugin debug logging | false |

All of the options above are opt in, except the existing defaults that already power the current plugin behavior. Leaving the new ported options unset preserves the current OpenCode behavior.

Compact example with all five ported capability groups enabled together:

{
  "plugin": [
    [
      "@handsomecheese/opencode-hindsight",
      {
        "hindsightApiUrl": "http://localhost:8888",
        "policyMode": "legacy",
        "dynamicBankId": true,
        "dynamicBankGranularity": ["agent", "gitProject"],
        "recallAdditionalBanks": ["team-memory", "release-memory"],
        "directoryBankMap": {
          "project/acme": "acme-shared",
          "shared/acme": "acme-shared"
        },
        "resolveWorktrees": true,
        "sessionEndRetain": true,
        "requestTimeoutSeconds": 10,
        "retainRoles": ["user", "assistant"],
        "recallRoles": ["user", "assistant"]
      }
    ]
  ]
}

Config File

Create ~/.hindsight/opencode.json for persistent settings:

{
  "hindsightApiUrl": "http://localhost:8888",
  "hindsightApiToken": "your-api-key",
  "policyMode": "server",
  "policyCanonicalAgentId": "opencode:workspace-1:builder",
  "recallBudget": "mid",
  "retainEveryNTurns": 3,
  "debug": false
}

Use this file for defaults you want across projects. Put project-specific overrides in opencode.json, then use environment variables when you want a quick local override.

Environment Variables

| Variable | Description | Default | | --- | --- | --- | | HINDSIGHT_API_URL | Hindsight API base URL | null | | HINDSIGHT_API_TOKEN | API key for authentication | (none) | | HINDSIGHT_REQUEST_TIMEOUT_SECONDS | Bounds outbound Hindsight requests in seconds. 0 keeps no configured request timeout | 0 | | HINDSIGHT_POLICY_MODE | legacy or server | legacy | | HINDSIGHT_POLICY_API_PREFIX | Policy endpoint prefix | /v1/opencode | | HINDSIGHT_POLICY_TENANT_ID | Optional tenant override for policy calls | (none) | | HINDSIGHT_POLICY_RUNTIME_ID | Optional runtime ID override | (none) | | HINDSIGHT_POLICY_WORKSPACE_ID | Optional workspace override | (none) | | HINDSIGHT_POLICY_CANONICAL_AGENT_ID | Optional canonical agent override | (none) | | HINDSIGHT_BANK_ID | Static memory bank ID in legacy mode | null, plugin falls back to opencode | | HINDSIGHT_AGENT_NAME | Agent name hint for legacy dynamic bank IDs and runtime naming | opencode | | HINDSIGHT_AUTO_RECALL | Auto-recall toggle | true | | HINDSIGHT_RECALL_ADDITIONAL_BANKS | Comma-separated extra bank IDs appended to recall reads | (none), parses to [] | | HINDSIGHT_AUTO_RETAIN | Auto-retain toggle | true | | HINDSIGHT_SESSION_END_RETAIN | Opt in to final retain on the idle/status completion mapping | false | | HINDSIGHT_RETAIN_MODE | full-session or last-turn | full-session | | HINDSIGHT_RETAIN_TAGS | Comma-separated tags attached to retained memories | (none), parses to [] | | HINDSIGHT_RETAIN_ROLES | Comma-separated roles allowed into retained transcript candidates | (none), parses to [] | | HINDSIGHT_RECALL_BUDGET | Recall budget, low, mid, high | mid | | HINDSIGHT_RECALL_MAX_TOKENS | Max tokens for recall results | 1024 | | HINDSIGHT_RECALL_TYPES | Comma-separated fact types included in recall/reflect operations | world,experience | | HINDSIGHT_RECALL_MAX_QUERY_CHARS | Max recall query length | 800 | | HINDSIGHT_RECALL_CONTEXT_TURNS | Recent turns used to shape recall query | 1 | | HINDSIGHT_RECALL_ROLES | Comma-separated roles allowed into recall prior-context lines | (none), parses to [] | | HINDSIGHT_RECALL_TAGS | Comma-separated recall tag filter | (none) | | HINDSIGHT_RECALL_TAGS_MATCH | any, all, any_strict, all_strict | any | | HINDSIGHT_DIRECTORY_BANK_MAP | JSON object string that maps directory prefixes to bank IDs | (none), parses to {} | | HINDSIGHT_DYNAMIC_BANK_ID | Enable dynamic bank ID derivation in legacy mode | false | | HINDSIGHT_RESOLVE_WORKTREES | Resolve linked git worktrees to the main worktree root before directory mapping or path-aware dynamic fields | false | | HINDSIGHT_CHANNEL_ID | Optional channel segment used by dynamic bank IDs | (none), falls back to default when channel granularity is used | | HINDSIGHT_USER_ID | Optional user segment used by dynamic bank IDs | (none), falls back to anonymous when user granularity is used | | HINDSIGHT_BANK_MISSION | Legacy bank mission and context | (none) | | HINDSIGHT_DEBUG | Enable debug logging | false |

Boolean environment variables treat true, 1, and yes as enabled. Other values are treated as false.

List environment variables use comma-separated values. HINDSIGHT_DIRECTORY_BANK_MAP must be a JSON object string such as {"project/acme":"acme-shared"}.

Configuration Priority

Settings are loaded in this order, later entries win:

  1. Built-in defaults
  2. ~/.hindsight/opencode.json
  3. Plugin options from opencode.json
  4. Environment variables

After that merge, policyMode decides the runtime path. This means a shell export can override both the user config file and the inline plugin tuple.

Porting notes from omo integration

These five capabilities were ported as opt-in OpenCode behavior. Leaving them unset keeps the current defaults.

  • recallAdditionalBanks extends recall reads only. In legacy mode, the plugin recalls the primary bank first, then queries extra banks in parallel, deduplicates results, and keeps additional results inside recallMaxTokens. In server mode, extra banks are appended only to an already scoped requested bank set or to snapshot-derived readable banks, so local config does not bypass server policy.
  • directoryBankMap and resolveWorktrees affect legacy bank selection before static bankId or dynamic derivation. Directory matching normalizes Windows drive letters to lowercase, converts backslashes to forward slashes, strips trailing slashes, and picks the longest matching prefix. When resolveWorktrees is true, the plugin uses git rev-parse --path-format=absolute --git-common-dir so linked worktrees can resolve to the main worktree root before directory matching or path-aware dynamic fields run.
  • sessionEndRetain adds a final retain pass for new user-turn tail messages only. OpenCode does not expose a dedicated session.end event, so this plugin maps the behavior to session.status with status.type === "idle", with session.idle kept as a compatibility fallback. The pass is backgrounded, opt in, and bounded.
  • requestTimeoutSeconds wraps outbound Hindsight requests with a positive-second bound. 0 means there is no configured request timeout. Positive values bound legacy and server calls alike. Session-end final retain still keeps a local safety bound even when this option stays at 0. requestTimeoutSeconds remains a plugin runtime guard only, not a persisted AgentAccess or server policy setting.
  • retainRoles and recallRoles are role filters, not role rewriters. Empty arrays keep the current output. Non-empty arrays filter retained transcript candidate messages or recall prior-context lines before formatting. These role allowlists shape retained or recalled content only, they are not a server security boundary.

Common Setup Scenarios

Project-local legacy bank

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    [
      "@handsomecheese/opencode-hindsight",
      {
        "hindsightApiUrl": "http://localhost:8888",
        "policyMode": "legacy",
        "bankId": "frontend-team"
      }
    ]
  ]
}

Use this when one OpenCode project should always talk to one bank.

Legacy dynamic bank per repo

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    [
      "@handsomecheese/opencode-hindsight",
      {
        "hindsightApiUrl": "http://localhost:8888",
        "policyMode": "legacy",
        "dynamicBankId": true,
        "dynamicBankGranularity": ["agent", "gitProject"]
      }
    ]
  ]
}

Use this when linked worktrees for the same repo should share one memory bank.

Server mode with local defaults file

{
  "hindsightApiUrl": "https://api.hindsight.vectorize.io",
  "hindsightApiToken": "your-api-key",
  "policyMode": "server",
  "policyCanonicalAgentId": "opencode:workspace-1:builder",
  "autoRecall": true,
  "autoRetain": true
}

Save that in ~/.hindsight/opencode.json when most projects should use the same Hindsight server and policy identity.

Policy Modes

policyMode="legacy"

  • Keeps the current single-bank and dynamic-bank behavior.
  • One bank is selected when the plugin starts.
  • Explicit tools and automatic hooks use that process-scoped bank.
  • HINDSIGHT_BANK_ID, HINDSIGHT_DYNAMIC_BANK_ID, and HINDSIGHT_AGENT_NAME keep their current meanings.

policyMode="server"

  • Hindsight Server becomes the policy authority.
  • Dashboard AgentAccess authors the desired server policy that OpenCode server mode consumes.
  • The plugin performs explicit policy-aware tool calls to /v1/opencode/* endpoints.
  • OpenCode tool context such as agent, sessionID, messageID, directory, and worktree is sent to the server.
  • Local legacy bank config is not an authorization source.

Normal pattern: keep hindsightApiUrl, hindsightApiToken, and policyCanonicalAgentId configured, then let the server decide readable and writable banks.

Startup Precedence

Mode selection is deterministic:

  1. Merge defaults, file config, plugin options, and environment variables.
  2. Read the resulting policyMode.
  3. If the result is legacy, use the legacy bank path.
  4. If the result is server, use policy-aware server endpoints.

If policyMode="server" is mixed with legacy bank config such as bankId, bankIdPrefix, or dynamicBankId, startup emits a deterministic warning and server policy wins.

Legacy vs Server Notes

  • legacy picks a bank locally when the plugin starts.
  • server asks Hindsight Server to authorize every policy-aware operation.
  • legacy honors bankId, bankIdPrefix, and dynamicBankId.
  • server may still accept those values in merged config, but they are not used as the authorization source.
  • In server mode, Dashboard AgentAccess and Hindsight Server policy are the source of truth. Local plugin config can identify the runtime, but it cannot grant access, widen read or write scope, or replace denied server policy.
  • In server mode, legacy local bank fields (bankId, bankIdPrefix, dynamicBankId, directoryBankMap, recallAdditionalBanks) and local naming/context fields (agentName, sessionID, messageID, policyCanonicalAgentId) are compatibility/configuration hints only. They cannot expand authorization, widen SubAgent filters, or stand in for missing context.agent; the server remains authoritative and filtered operations fail closed when the raw agent is absent or denied.
  • server is the right mode when you want centrally managed bank access, tenant policy, or dashboard bindings.

Troubleshooting

Missing HINDSIGHT_API_URL

If tools do not connect, make sure hindsightApiUrl or HINDSIGHT_API_URL is set. The plugin default is null, so there is no built-in server URL.

Missing token for cloud or protected servers

If your Hindsight deployment requires auth, set hindsightApiToken or HINDSIGHT_API_TOKEN. Do not put a real token in committed config files.

Mixed legacy settings in server mode

If policyMode is server and you also set bankId, bankIdPrefix, or dynamicBankId, the plugin warns and server policy still wins. Remove the legacy bank settings once your server bindings are ready.

Automatic hooks appear inactive in server mode

That can be expected. Auto-recall, auto-retain, and compaction fail closed when there is no reliable session policy snapshot. Check that the session registered successfully and that policyCanonicalAgentId maps to a valid server policy binding.

Server Policy Contract

No OpenCode Patch

No OpenCode Patch is part of v1. Hindsight does not patch OpenCode core.

Threat Model

V1 protects against model, tool, and integration misuse. It does not claim cryptographic isolation from a hostile local OS user who controls OpenCode and raw Hindsight credentials.

Plugin-side checks are convenience and early rejection only. Server-side policy enforcement is mandatory.

Authorization Boundary

experimental.chat.system.transform is not used as a per-agent authorization basis. That hook does not carry reliable agent identity. In server mode, explicit tools are the primary guaranteed path.

AgentAccess Desired Policy

In policyMode="server", the desired policy authored in Dashboard AgentAccess and stored on Hindsight Server is the source of truth for bank access, automatic retain behavior, role allowlists, deterministic document templates, SubAgent capture, and SubAgent bank filters. Local plugin config can help identify the runtime, but it cannot grant access or bypass a denied server policy.

Runtime Guard vs Persisted Policy

requestTimeoutSeconds remains a plugin runtime guard only. It bounds outbound Hindsight calls from the running OpenCode process, but it is not persisted in AgentAccess, not copied into SessionPolicySnapshot, and not treated as a server-side security control.

SessionPolicySnapshot

Every active OpenCode session is pinned to a SessionPolicySnapshot.

  • Desired policy edits affect new sessions by default.
  • Active sessions keep their immutable snapshot unless it is revoked, expires, or becomes broken because a required bank binding disappeared.
  • Live propagation of desired policy edits into active sessions is not part of v1.

Role Allowlists Shape Content

retainRoles and recallRoles shape which transcript lines become retain content or recall query context. In server mode the plugin reads the immutable snapshot allowlists instead of local expansion config, but those role allowlists still shape content only. They do not authorize banks or create a server security boundary.

Deterministic Document IDs

Server mode keeps session and SubAgent document identity backend-owned. Desired policy templates are validated against safe token sets, copied into immutable session snapshots, and expanded by the server after bank authorization succeeds.

  • Session retain templates may use {sessionID}, {start}, and {end}.
  • SubAgent capture templates may use {parentSessionID}, {subagentSessionID}, {start}, and {end}.
  • Unsupported tokens or unsafe static templates fail closed with policy validation errors.
  • Users do not need to invent timestamp-based document IDs to mimic snapshot behavior.

SubAgent Capture

SubAgent capture is off by default. When enabled, the plugin sends only sanitized public summary content, title, learnings, issues, problems, and decisions-style fields. It omits hidden reasoning, raw tool outputs, agent, agentName, canonicalAgentId, messageID, and documentId, and it fails closed when snapshot policy, target selection, or sanitization is not safe.

SubAgent Bank Filters

SubAgent filters are exact-match, case-sensitive narrowing rules on raw ToolContext.agent or context.agent values. They only narrow existing binding permissions, they do not expand them.

  • Missing context.agent fails closed for filtered banks.
  • Missing, denied, or non-matching SubAgent ids fail closed.
  • Filter rules never fall back to sessionID, messageID, display names, or policyCanonicalAgentId.

Emergency Revoke

Emergency revoke is the operator kill switch for an active snapshot. After revoke, later snapshot-based operations fail closed with SNAPSHOT_REVOKED.

Retain Targeting

Retain targeting is always resolved on the server in policyMode="server".

  • Zero writable banks, deny retain.
  • One writable bank, retain may use that bank implicitly.
  • Multiple writable banks, retain requires targetBankId unless there is one unique default write bank.
  • A readable-only targetBankId is denied for retain.
  • A writable but not readable target may still accept retain, but the response does not expose bank contents.

Ambiguous retain returns TARGET_BANK_REQUIRED.

Fail Closed Automatic Behavior

In server mode, auto-recall, auto-retain, and compaction fail closed when there is no reliable per-agent snapshot.

  • No reliable snapshot, no automatic recall.
  • No reliable snapshot, no automatic retain.
  • No reliable snapshot, no automatic compaction read or write.

This keeps plugin-side guesses out of the authorization path.

Redacted Local Identity

Server policy mode stores normalized hash and fingerprint keys, plus an optional basename, instead of full absolute local paths by default.

Migration From Legacy Config

| Legacy setting | policyMode="legacy" | policyMode="server" | Migration guidance | | --- | --- | --- | --- | | HINDSIGHT_BANK_ID | Used as the static bank ID | Ignored for authorization. Server policy wins if mixed | Remove after server bindings are authored | | HINDSIGHT_DYNAMIC_BANK_ID | Enables dynamic bank derivation | Ignored for authorization. Server policy wins if mixed | Replace with server-side bindings | | HINDSIGHT_AGENT_NAME | Used for legacy naming and defaults | Still useful as a naming hint, not as access control | Keep only if you want stable naming | | autoRecall | Legacy automatic recall path stays active | Automatic recall is policy-gated and fail closed without a reliable snapshot | Keep the toggle, but expect the snapshot to decide whether recall runs | | autoRetain | Legacy automatic retain path stays active | Automatic retain is policy-gated and fail closed without a reliable snapshot | Keep the toggle, but expect the snapshot to decide whether retain runs |

Examples

One Writable Bank

The agent has one writable bank, team-memory, and read access to that same bank.

{
  "tool": "hindsight_retain",
  "arguments": {
    "content": "The rollout starts Tuesday at 09:00 UTC."
  }
}

The server accepts the retain and uses team-memory as the implicit target.

Multiple Writable Banks With Explicit targetBankId

The agent can write to team-memory and incident-memory.

{
  "tool": "hindsight_retain",
  "arguments": {
    "content": "Incident 482 needs a customer update.",
    "targetBankId": "incident-memory"
  }
}

If targetBankId is omitted here and there is no unique default write bank, the server denies the call with TARGET_BANK_REQUIRED.

Unknown Agent Denial

Unknown or unbound agents do not fall back to a legacy bank in server mode.

HTTP 403
error.code = AGENT_UNBOUND

Tools

hindsight_retain

Stores information in long-term memory.

hindsight_recall

Searches long-term memory for relevant facts.

hindsight_reflect

Generates a synthesized answer from long-term memory.

Dynamic Bank IDs

Dynamic bank IDs are part of policyMode="legacy".

export HINDSIGHT_DYNAMIC_BANK_ID=true

The bank ID is composed from granularity fields, default agent::project. Supported fields are agent, project, gitProject, channel, and user.

directoryBankMap runs before static or dynamic legacy bank selection. A mapped directory wins immediately, and the longest matching normalized prefix is used when multiple entries overlap.

  • project uses the working directory basename. Separate git worktrees of the same repository can end up with different bank IDs because their paths differ.
  • gitProject resolves to the main worktree basename through git rev-parse --git-common-dir, so linked worktrees of the same repository can share one bank. It falls back to the working directory basename when git metadata is unavailable.

If resolveWorktrees is true, the plugin resolves the main worktree root before directory map matching and before any path-aware dynamic fields are evaluated.

{
  "dynamicBankId": true,
  "dynamicBankGranularity": ["agent", "gitProject"]
}

The bank ID is derived once when the plugin starts. These dimensions are process-scoped, they do not change per session inside one running OpenCode process.

For per-user isolation in legacy mode:

export HINDSIGHT_CHANNEL_ID="slack-general"
export HINDSIGHT_USER_ID="user123"

Development

npm install
npm test
npm run build

License

MIT