Skip to content

fix(proxy): bridge session resolution + make Pi a first-class client - #1260

Draft
nicwn wants to merge 2 commits into
TencentCloud:feat/server_teamfrom
nicwn:fix/bridge-session-resolution
Draft

fix(proxy): bridge session resolution + make Pi a first-class client#1260
nicwn wants to merge 2 commits into
TencentCloud:feat/server_teamfrom
nicwn:fix/bridge-session-resolution

Conversation

@nicwn

@nicwn nicwn commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Problem

The memory-bridge and skill-bridge read path is broken for any agent source other than codebuddy and claude-code. When an agent (pi, dsh, codex, workbuddy, opencode) tries to search its L1/L0 memory or load skills via the bridge, it receives 40101 "session not initialized" — even though the session was successfully initialized and the agent has persona/skills injected.

This affects every default-install user (the 3-container start-all.sh deploy with no Redis and no storage: section), not just custom deployments.

Root Causes

Bug 1: BindingRepo never activated in default install

The default install generates redis: enabled: false and no storage: section. The eager activation block in server.ts only calls tryActivateStorage and tryActivateRedis — both silently no-op when their config flags are false.

ensureBindingRepoPersistent (the fs fallback) is only called inside buildPipelineBundle, which runs lazily on the first main request — after completeRegistrationstore.set() already skipped the L2b binding write because this.bindingRepo was still null.

Result: the L2b binding (designed as the bridge's L2 backstop, keyed by (spaceId, sessionId)) is never written. The bridge L2 lookup always misses.

Bug 2: Bridge L1 lookup uses a hardcoded agent-source prefix list

loadSessionIdsL1 in both memory-bridge.ts and skill-bridge.ts tries these candidate keys:

const candidates = sessionId.includes(":")
  ? [sessionId]
  : [sessionId, `codebuddy:${sessionId}`, `claude-code:${sessionId}`];

The pi: prefix (and dsh:, codex:, workbuddy:, opencode:) is missing. The L1 state IS in the states Map under pi:sessionId, but the bridge never looks there.

Fixes

Fix 1: Eagerly activate BindingRepo at startup

Add ensureBindingRepoPersistent(config) to the server.ts eager block, right after tryActivateStorage/tryActivateRedis. It is idempotent (no-ops if a BindingRepo was already set by storage/redis).

This ensures the BindingRepo is ready before any request can trigger completeRegistration, so the L2b binding write actually happens.

Fix 2: Enumerate L1 keys by suffix instead of hardcoded prefixes

Add SessionStore.keysWithSuffix(sessionId) — returns all L1 keys matching :${sessionId} (or the bare sessionId). Both bridges use it in loadSessionIdsL1:

function loadSessionIdsL1(sessionId: string): SessionIdFields | null {
  const store = getSessionStore();
  const bare = store.get(sessionId);
  if (bare) { /* ... */ }
  if (!sessionId.includes(":")) {
    for (const k of store.keysWithSuffix(sessionId)) {
      const state = store.get(k);
      if (state) { /* ... */ }
    }
  }
  return null;
}

This works for any current or future agentSource without maintaining a hardcoded list.

Impact

  • No breaking changes. The L2b binding write and L1 lookup are additive — existing codebuddy/claude-code sessions continue to work exactly as before.
  • No new dependencies. Uses the existing SessionStore.states Map.
  • Default install now functional. The 3-container deploy (no Redis, no storage) now has a working bridge read path.

Testing

6 new tests in src/__tests__/bridge-session-resolution.test.ts:

  • keysWithSuffix: bare match, multi-agent-source, no-match, substring-vs-suffix
  • Integration: pi-prefixed and dsh-prefixed sessions found with bare sessionId

All 14 tests pass (8 existing + 6 new).

Files Changed

File Change
src/server.ts Add ensureBindingRepoPersistent(config) to eager activation
src/session/store.ts Add keysWithSuffix(sessionId) method
src/memory/memory-bridge.ts Use keysWithSuffix in loadSessionIdsL1
src/skill/skill-bridge.ts Use keysWithSuffix in loadSessionIdsL1
src/__tests__/bridge-session-resolution.test.ts New test file

…idge session resolution

Two bugs broke the memory-bridge and skill-bridge read path for any
agent source other than codebuddy/claude-code (pi, dsh, codex, workbuddy,
opencode). The agent could not search its own L1/L0 memory or load skills
via the bridge — it silently received 40101 'session not initialized'.

Bug 1 — BindingRepo never activated in default 3-container install:
  The default install (start-all.sh) generates config with
  redis.enabled=false and no storage: section. The eager activation block
  in server.ts only called tryActivateStorage and tryActivateRedis, both
  of which silently no-op when their config flags are false.
  ensureBindingRepoPersistent (the fs fallback) was only called inside
  buildPipelineBundle, which runs lazily on the first main request —
  AFTER completeRegistration's store.set() already skipped the L2b
  binding write because bindingRepo was still null.

  Fix: call ensureBindingRepoPersistent(config) in the server.ts eager
  block, right after tryActivateStorage/tryActivateRedis. It is idempotent
  (no-ops if a BindingRepo was already set by storage/redis).

Bug 2 — bridge L1 lookup used a hardcoded agent-source prefix list:
  loadSessionIdsL1 tried [bare, 'codebuddy:', 'claude-code:'] — missing
  every other agentSource (pi, dsh, codex, workbuddy, opencode). The L1
  state was in the Map under 'pi:sessionId' but the bridge never looked
  there.

  Fix: add SessionStore.keysWithSuffix(sessionId) and use it in both
  bridges to enumerate all L1 keys ending with ':sessionId'. This works
  for any current or future agentSource without maintaining a list.

Tests: 6 new tests covering bare match, multi-agent-source, no-match,
substring-vs-suffix, and pi/dsh integration scenarios.
@nicwn
nicwn marked this pull request as draft September 4, 2026 10:06
…set-import adapter, and bundled pi-plugin source

Pi already had proxy-side support (agent adapter, injection profile,
session-init picker) but was missing the user-facing onboarding surface
that every other agent has. This commit makes Pi a first-class client:

agents/pi/README.md:
  Connection guide following the house style of agents/claude-code/README.md.
  Documents the Pi extension approach (env vars + extension install,
  not a config file), the interactive TUI picker, preset identity,
  dynamic model catalog, injection profile, and troubleshooting.

agents/pi/asset-import.md:
  How to import local Pi skill/session history into team memory.
  Pi sessions live at ~/.pi/agent/sessions/<workspace-slug>/*.jsonl.

agents/setup-proxy.sh:
  - Add 'pi' to the AGENTS array, menu, config paths, help text
  - Add write_pi() function: env-var-guided setup (no config file to
    write — Pi uses a Pi extension + env vars), with optional
    'pi install' and a model-switch reminder
  - Add Pi scan block: detect existing @tencentdb-agent-memory/pi-tdai-client
    package in ~/.pi/agent/settings.json

agents/asset-import.ts:
  - Add make_piAdapter() to the ADAPTERS registry: scans
    ~/.pi/agent/skills/ and <cwd>/.pi/skills/ for SKILL.md files,
    and ~/.pi/agent/sessions/<workspace>/*.jsonl for conversation history
  - Add '.pi' to CWD_MARKER_DIRS for auto-detection
  - Add 'session' to parseJsonlLines skip list (Pi's JSONL header type)

agents/README.md:
  - Add Pi to the agent comparison table and asset-import table
  - Update count from 7 to 8 agents

MemoryCore/pi-plugin/:
  Bundle the full Pi extension source (interactive picker, dynamic model
  discovery, form parser, TUI picker) with the upstream package name
  (@tencentdb-agent-memory/pi-tdai-client). The upstream repo had a
  minimal stub; this brings it to feature parity with the fork's version.
  30 tests across 4 test files (index, discovery, form, picker).
@nicwn nicwn changed the title fix(proxy): bridge session resolution broken for non-codebuddy agents (pi, dsh, codex, workbuddy) fix(proxy): bridge session resolution + make Pi a first-class client Sep 4, 2026
@Maxwell-Code07

Copy link
Copy Markdown
Collaborator

Thank you so much for your attention and contribution! We will arrange an internal review for this PR shortly, and all feedback will be shared right here in the discussion.

@Movtrength Movtrength left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review (local verification)

Verified on top of current feat/server_team (merge is clean; PR was 3 commits behind: #1195 / #1222 / #1052).

What looks good

  • Bug 1 fix — eager ensureBindingRepoPersistent(config) in server.ts is the right place; idempotent and matches the lazy path in buildPipelineBundle.
  • Bug 2 fixSessionStore.keysWithSuffix + bridge loadSessionIdsL1 correctly drops the hardcoded codebuddy/claude-code prefix list. This is the durable fix vs forever extending the candidate list.
  • Pi first-class surface — docs / setup-proxy.sh / asset-import / pi-plugin tests are coherent with the earlier Pi adapter work.
  • Tests — pi-plugin: 30/30 pass. Original bridge unit tests: 6/6 pass after install.

Nits / follow-ups before un-draft

  1. Please merge/rebase onto latest feat/server_team — no conflicts; needed for #1195 (pi in AGENT_PREFIX_RE).
  2. Bridge “integration” tests don’t call the bridge — they only exercise keysWithSuffix, and session_id: "" would make real toIdFields return null. I have a local patch that drives createMemoryBridgeHandler with a mocked fetcher (pi/dsh hit → 200 upstream, miss → 40101). Happy to push it if you add write access / cherry-pick, or I can paste a follow-up PR once there’s a fork to target.
  3. Ambiguous multi-prefix — if both pi:abc and dsh:abc exist, first Map hit wins. Probably fine for real traffic (one agent source per conversation), but worth a one-line comment.
  4. Scope — bridge fix + Pi onboarding are independently reviewable; splitting would speed merge of the bugfix if Pi docs need more bikeshedding.

Ready?

Approach LGTM for the bridge bugs. After syncing with feat/server_team (+ ideally the real handler-level tests), this can leave draft.

@Movtrength

Copy link
Copy Markdown

Pushed a companion with write access on my fork: #1265

Includes your two commits + merge of current feat/server_team + handler-level bridge tests (087d4de). Cherry-pick that commit here if you want this draft to stay the landing PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants