Skip to content

[reconciliation] Rebuild HumeStone overlay on upstream OB1 pin - #5

Draft
Humestone wants to merge 163 commits into
mainfrom
codex/ob1-humestone-overlay-reconstruction
Draft

[reconciliation] Rebuild HumeStone overlay on upstream OB1 pin#5
Humestone wants to merge 163 commits into
mainfrom
codex/ob1-humestone-overlay-reconstruction

Conversation

@Humestone

@Humestone Humestone commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

This draft reconstructs the HumeStone OB1 overlay on the clean upstream pin 677910600de98067f61c120d65956b23f360aedb.

  • preserves the current upstream OB1 baseline, Next.js 16.2.4, OpenNext/Cloudflare support, extensions, and Nate B. Jones / OB1 provenance
  • selectively ports the three isolated HumeStone Agent Memory hardening commits
  • reconstructs Mission Control without replaying the stale dirty deployment branch
  • carries workspace/project scope through Agent Memory detail and trace reads
  • adds a package-sync fail-closed check for any future materialized Supabase function
  • replaces cookie-existence auth with sealed-session validation and server-side credentials
  • requires Mission Control Ask authentication before body parsing or Company Memory search
  • keeps third-party answer synthesis explicit opt-in and default-off

Review boundary

This is intentionally a draft, review-only PR. Do not merge or deploy from it yet.

The HumeStone fork's main was 159 upstream commits behind and retained 11 fork-only commits when this branch was built. The GitHub PR diff therefore includes the upstream refresh as well as the four reconstruction commits. The overlay-specific lineage is:

GitHub currently reports this draft as CONFLICTING against fork main, which is expected from those retained fork-only commits. This PR is evidence and review surface only; selecting and executing a conflict-resolution strategy is a separate Stone recommendation and approval gate.

  1. e4e5f4d — Agent Memory recall/governance guards
  2. 1ed41ad — Agent Memory auth and scope boundary
  3. 541d3e4 — dedicated Agent Memory key with compatibility fallback
  4. b37cfad — selective Mission Control/auth/scope reconstruction and provenance packet

See docs/humestone-overlay-reconstruction-2026-07-22.md for file-by-file provenance, conflict choices, omissions, and validation evidence.

Validation

  • 11 dashboard Agent Memory and Mission Control boundary tests passed
  • 44 Deno Agent Memory API tests passed
  • 10 Node dry-run and package-sync tests passed
  • Deno formatting passed
  • ESLint passed
  • TypeScript no-emit passed
  • Next.js 16.2.4 production build passed
  • OpenClaw Agent Memory 0.1.6 schema check and bundle execution passed
  • localhost-only browser UAT passed for preview, connected-empty, unreachable, login, forged-cookie, and unauthenticated Ask states
  • malformed unauthenticated Ask returned 401 before JSON parsing
  • no production endpoint or real OpenRouter call was used

Known limitations and gates

  • supabase/functions/agent-memory-api remains intentionally absent; the sync checker fails closed until a deployment package is deliberately materialized.
  • The untouched upstream OpenClaw plugin source builds but does not reproduce its committed dist/index.js; the upstream artifact remains unchanged and the inherited debt is documented.
  • The existing dependency graph reports 16 npm audit findings; this PR does not change that dependency graph.
  • Merge, Mission Control deployment, Vercel changes, Agent Memory materialization/deployment, production calls, schema/data changes, credentials, cleanup, and branch deletion all remain separately approval-gated.

txcfi-scott and others added 30 commits April 23, 2026 10:20
Adds a standalone MCP Edge Function that exposes a single delete_thought
tool. Hard-deletes a thought by UUID with a pre-flight fetch so callers
see a clear not-found outcome rather than a silent success.

Deploys as its own Supabase Edge Function — the core server/index.ts is
untouched. README documents an optional audit hook for installs that
also use the thought_audit schema.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewords the step 5 verification from a bullet list into a short
numbered sequence (capture → delete → delete-again → table-editor
check) so the Verify section has explicit top-level numbered steps.
This matches the update-thought-mcp README's verification style and
makes the intended end-to-end happy-path + not-found path obvious to
a first-time follower of the guide.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… connections

Both server/index.ts and integrations/kubernetes-deployment/index.ts shared
a global McpServer singleton and called server.connect(transport) on every
request, attaching new transports to an already-connected server. Under
concurrent load or repeated calls this causes transport state corruption and
the reconnect instability reported on claude.ai.

Fix: wrap all tool registrations in a buildServer() factory function and call
it per-request, so each request gets a fresh McpServer + transport pair with
no shared state.

Also applied to both files:
- Strip mcp-session-id from responses: server is stateless and advertising a
  session ID misleads clients into expecting resumption that never comes
- Await transport.handleRequest() to enable response post-processing
- Null-guard on the transport response with a 500 fallback

Additionally backfilled into integrations/kubernetes-deployment/index.ts:
- CORS preflight handler (OPTIONS *) — was missing entirely
- Accept header patch for Claude Desktop connector compatibility
  (mirrors the fix already present in server/index.ts since issue NateBJones-Projects#33)
- CORS headers on 401 responses and successful MCP responses
Validates the per-request McpServer pattern without any Supabase instance.
MCP initialize is a pure protocol handshake — no tools are called, no DB touched.

Tests (20/20):
- CORS preflight: OPTIONS → 200 with correct headers
- Auth: wrong/missing key → 401 with CORS headers
- MCP initialize: 200, no mcp-session-id, CORS on success, valid protocolVersion
- Per-request isolation: two sequential initializes both succeed with no
  mcp-session-id, proving the singleton is gone
- tools/list: server responds cleanly, correct headers

Setup (from server/ directory):
  npm install
  node test-stateless.mjs   # or: npm test
Address Copilot review: factory function body was at column 0, making it
ambiguous what runs per-request vs. at module load. Indent all content
inside buildServer() by two spaces in both server/index.ts and
integrations/kubernetes-deployment/index.ts.
Adds a small drop-in extension surface so dashboard add-ons can register
a new route + sidebar entry without touching core files:

  - extensions.config.ts: typed registry, empty by default
  - components/Sidebar.tsx: splits nav into core + extensions + trailing,
    resolves icon keys via a registry; adds clock/folder/plug/sparkles
  - lib/api.ts: exports apiFetch so extension pages can reuse the
    authenticated JSON fetch + error plumbing
  - EXTENSIONS.md: convention doc (folder layout, auth, icon registry,
    sidecar vs in-tree REST routes)

After this change, future extensions touch only their own folder under
app/<route>/ and a single entry in extensions.config.ts.

https://claude.ai/code/session_01AvZANjBLBpEh3eFzPzzGGH
The COMMENT ON FUNCTION statement for thought_edges_upsert used the SQL
|| concatenation operator to join three string literals. PostgreSQL's
COMMENT statement requires a single string literal after IS, not an
expression, so the migration fails with a syntax error at the first ||.

Collapse the three pieces into one literal so the schema applies cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Core Gmail puller script for a new recipe under recipes/gmail-smart-pull/.
The puller fetches messages from the Gmail API (read-only scope), strips
quoted replies and signatures, filters auto-generated noise, and emits
an OB1 ingest pack that downstream pipelines can feed into fingerprint
dedup + sensitivity-gate + upsert.

Also includes two small pure-JS libs the puller depends on:

- scripts/lib/sensitivity.mjs tags each message body against two
  pattern sets (restricted: SSN, passport, bank, API keys, passwords,
  credit cards; personal: email/phone/health/financial signals) so the
  ingest side can route tiers to the right store. Tagging only — the
  recipe does not enforce a routing policy itself.

- scripts/lib/entity-resolver.mjs does RFC 2822 header parsing
  (From/To/Cc with quoted commas, display-name variants) into
  { name, email } pairs so structured correspondents can be carried in
  the pack and upserted as first-class entities later.

OAuth credentials come from GMAIL_OAUTH_CLIENT_ID and
GMAIL_OAUTH_CLIENT_SECRET env vars. No real email addresses, client
IDs, or secrets are embedded anywhere. The only scope requested is
https://www.googleapis.com/auth/gmail.readonly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The LLM atomizer splits long email bodies into multiple atomic thoughts
before the puller emits them in the pack. Two behaviors carried over
from upstream experience running this at scale:

1. Prompts are piped to CLI providers via stdin, not via the -p
   command-line argument. On Windows shell:true cmd.exe mangled
   multi-line prompts containing quotes and newlines so the child
   process received a truncated/empty string and the LLM replied
   conversationally ("Looks like your message got cut off..."). 190/190
   atomize calls in one real batch failed this way until stdin fixed
   it. Same fix applied to the codex provider.

2. A new 'codex' provider shells out to `codex exec` so users
   orchestrating the recipe from a Codex session can atomize without
   crossing the streams with a nested claude-cli (which would fail
   nested-process detection). The `claude-cli` provider still works
   from standalone terminals and refuses to run inside Claude Code.

OB1 users will typically use provider='anthropic' (direct Messages
API) or 'openrouter' since OB1 is cloud-first and those are already
provisioned. CLI providers are opt-in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…anonical_email

Two idempotent migrations that complete the pack's handoff to a
downstream ingest pipeline:

1. merge_thought_metadata(p_id, p_patch) — shallow-merge a JSONB patch
   into a thought's metadata without re-triggering the full upsert
   path (no embedding regen, no enrichment, no fingerprint recompute).
   Useful for per-row metadata backfills like flipping a
   relationship_tier on a batch of thoughts after regenerating the
   contacts cache.

2. entities.canonical_email — adds a nullable TEXT column + a partial
   unique index to public.entities so email correspondents parsed from
   the pack's structured From/To/Cc blocks can be upserted by normalized
   email address. Existing uniqueness on (entity_type, normalized_name)
   is preserved because two people can legitimately share a display
   name; email is the stable identifier.

Both use CREATE OR REPLACE / IF NOT EXISTS guards — safe to re-run.
Neither drops or renames existing columns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README documents the full setup path: Gmail OAuth Desktop-app client,
env vars (no credentials on disk), first-run consent flow, dry-run,
real run, and optional migration install. Explicitly covers the four
design choices most likely to surprise a new user:

- Sensitivity routing is tag-only — the recipe does not enforce a
  policy, the ingest pipeline does. Calls out that OB1 is cloud-first
  so "restricted stays local" needs explicit wiring (two-store setup
  or block-on-import).
- Engagement filter defaults to engaged-only with STARRED/IMPORTANT
  bypass, with clear instructions to disable or rebuild.
- Relationship tier is metadata (contact/known/unknown), not a gate.
  Three ways to produce the contacts cache documented.
- Atomization is opt-in per-message (>= 150 words default) with
  anthropic/openrouter/claude-cli/codex provider choice. Graceful
  fallback to whole-message capture on atomizer failure.

metadata.json follows the schema template at recipes/_template/ with
required fields (name, description, category, author, version,
requires.open_brain, tags, difficulty, estimated_time) and no extras.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…l tracker

Codex review (P2 originally, elevated to P1 in triage): the credential
tracker in the README asked users to paste their Supabase service-role
key into a plaintext doc, but this recipe never touches that key — the
puller only emits a pack file, and any downstream ingest pipeline that
needs service_role should read it from env/secret manager, not from a
user's text editor.

Removing the field avoids an entirely avoidable leak path for a highly
privileged database secret, and adds a note so contributors who copy
this tracker pattern into other recipes don't reintroduce the mistake.
… loopback bind + HTML escape + HTTP checks)

Codex identified four coupled OAuth weaknesses in scripts/pull-gmail.mjs:

- No OAuth state parameter: authUrl was built without a random state and
  the callback handler accepted the first ?code= it saw. Any local
  process or malicious localhost page could race the browser redirect
  and bind the script to an attacker-controlled Google account.
- server.listen() without an address defaulted to IPv6-any/0.0.0.0 on
  some platforms, briefly exposing the callback to the LAN.
- URL error parameter reflected into HTML without escaping — low-impact
  reflected XSS but trivial to fix.
- Token exchange and refresh called res.json() before checking res.ok,
  so proxy/5xx responses produced a useless JSON parse error instead of
  a useful OAuth failure with status + body.

Fix: generate 16 bytes of random hex as state, require the callback to
echo it back (mismatch -> hard reject), bind createServer to 127.0.0.1
explicitly, HTML-escape the error param before reflecting, and gate
both token POSTs on res.ok with a bounded body preview on failure.
…s to sensitivity classifier

Codex flagged (originally P2, elevated to P1 in triage because the
sensitivity tier drives downstream routing): the restricted-tier pattern
set missed several common secret formats, so emails containing them
would be classified 'standard' and flow into the general thoughts pool
instead of the restricted-only store.

Adds patterns for:

- openai_key       — sk-proj-, sk-svcacct-, sk-admin- variants
- anthropic_key    — sk-ant-api / sk-ant-admin tokens
- aws_access_key_id    — AKIA/ASIA/AROA/AIDA prefixes
- aws_secret_access_key — proximity match near "aws secret" label
- gcp_api_key      — AIza<35 chars> canonical form
- jwt_token        — eyJ<header>.<payload>.<sig> three-segment form
- pem_private_key  — BEGIN PRIVATE KEY blocks (RSA, EC, DSA, OPENSSH, PGP, ENCRYPTED)
- github_token     — ghp/gho/ghu/ghs/ghr _ 36+ char bodies
- slack_token      — xox[aboprs]- tokens

The existing generic api_key_pattern is kept as a belt-and-suspenders
fallback. All patterns still fail-open (standard tier) on no match —
classification never throws, so a missing pattern degrades gracefully
rather than blocking the pull.
…+ harden atomize prompt against injection

Codex flagged this as the highest-severity finding in the atomize lib
(originally tagged P1-5 + P2): the 'codex' provider spawned
`codex exec --dangerously-bypass-approvals-and-sandbox -` with an email
body interpolated directly into the prompt. A malicious sender can embed
'IGNORE PREVIOUS INSTRUCTIONS' or tool-call primers, and because the
child Codex agent ran with the sandbox disabled, prompt-injection
escalated to arbitrary local command/file access.

Fixes:

1. Remove the --dangerously-bypass-approvals-and-sandbox flag from the
   default codex invocation. Users who actively need it for an
   atomization-only run can opt in via GMAIL_ATOMIZE_CODEX_BYPASS=1 env
   var, which documents the risk at the opt-in site.
2. Strengthen DEFAULT_ATOMIZE_PROMPT with an explicit SECURITY section
   that frames the INPUT THOUGHT as untrusted data, not instructions,
   and forbids emitting system/tool/assistant markers in the output.
3. Add a top-of-file comment describing the prompt-injection threat
   model so callers who override the prompt don't silently drop the
   hardening.

This does not eliminate prompt injection (no prompt-only defense can),
but it removes the most dangerous escalation path and raises the bar
from "read email -> run code" to "read email -> influence atoms".
The previous regex `\b(?:aws[_ -]?secret|aws[_ -]?access[_ -]?key)\b`
could not match `aws_secret_access_key=...` — the most common env-var
form — because `_` is a word char, so the `\b` between `t` and `_` in
`aws_secret_access_key` didn't fire, and neither alternation caught
the combined phrase.

Restructured the alternation so `aws_secret` can optionally absorb the
trailing `_access_key`:

  aws[_ -]?(?:secret(?:[_ -]?access[_ -]?key)?|access[_ -]?key)

Verified against 8 test cases covering kvp form, uppercase, hyphen
separators, space separators, standalone `aws_secret`, standalone
`aws_access_key`, a negative case, and the full env-var pair. All
pass with no false positives.
SECURITY DEFINER function was granted to authenticated/anon, allowing
RLS bypass. Now restricted to service_role only. Added FOR UPDATE to
prevent concurrent evidence appends from losing writes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop the stale reference to `schemas/enhanced-thoughts/` (deleted on
this branch and not actually used by the SQL — the function only touches
`thoughts.id` and `thoughts.metadata`). Also update Expected Outcome to
reflect the service-role-only grant on `append_thought_evidence` so
users don't re-grant it to anon/authenticated by accident.

Why: README claimed a prerequisite that 404s on the repo and mis-stated
the RPC's trust boundary. Both were latent user-footguns.
Add nullable `user_id uuid` to `ingestion_jobs` and `ingestion_items`
via idempotent `ALTER TABLE ... ADD COLUMN IF NOT EXISTS`. A DO block
conditionally adds FKs to `auth.users(id) ON DELETE CASCADE` only when
Supabase's `auth` schema and `users` table exist, so the migration
stays safe on non-Supabase Postgres.

Why: without user_id, multi-tenant deployments leak ingestion history
across users. Nullable keeps single-tenant stock OB1 working with no
data migration and lets RLS policies (added separately) key off
auth.uid() = user_id once populated.
Turn on row level security for `ingestion_jobs` and `ingestion_items`,
add a `service_role ALL` policy on each (so worker writes still flow),
and — conditionally, only when `auth.uid()` exists — add an
`authenticated SELECT` policy scoped to `user_id = auth.uid()`.
Policies are wrapped in DROP POLICY IF EXISTS / CREATE so the file is
still idempotent on re-run.

Why: the grant block was already service-role-only, but without RLS
there was no backstop if Supabase's schema-level defaults quietly
granted `USAGE`/`SELECT` to `anon` or `authenticated`. RLS closes that
door. Giving authenticated users a SELECT scoped to their own rows
matches the pattern used by the rest of the Open Brain extensions and
is a no-op until someone populates `user_id`.
Add partial indexes keyed on `created_at` for rows in the active
lifecycle (`status = 'pending'` on jobs; `status IN ('pending','ready')`
on items). Both use `CREATE INDEX IF NOT EXISTS` so re-running the
migration is a no-op.

Why: the worker polls for the next pending job and for ready items
repeatedly. Without a partial index, every poll becomes a seq scan
against a table whose historical tail of completed rows grows forever.
Partial indexes stay tiny (only live queue rows) and shrink to near
zero when the queue drains.
Add a Job Claim Semantics section to the README that states the
contract explicitly: claim logic lives in the companion Edge Function
(`integrations/smart-ingest/`), and any worker that claims a row MUST
use `FOR UPDATE SKIP LOCKED`. Include a canonical UPDATE-with-sub-SELECT
pattern that pairs with the new partial indexes. Also sync Expected
Outcome with the new user_id columns, partial indexes, and RLS
policies so the README matches the schema it describes.

Why: the schema file is deliberately minimal (no claim RPC), so
without this note a downstream author could wire up a plain SELECT-
then-UPDATE worker and silently double-process the queue. Putting the
contract in the schema README — next to the tables it operates on —
keeps the DB layer's requirements discoverable even when the
companion Edge Function lives in a separate folder.
Why: Without AbortController, a slow Supabase response can hang past
HARD_TIMEOUT_MS=25000, triggering process.exit(0) mid-flight so the
capture is lost with no retry-queue entry. Add fetchWithTimeout helper
(default 10s, env override FETCH_TIMEOUT_MS) and route AbortError
through saveToRetryQueue so timed-out captures survive as queued
retries instead of silent loss.
Why: Blindly retrying every non-2xx response wastes API calls on
permanent errors — a revoked MCP_ACCESS_KEY (401) or oversized payload
(413) currently retries 5x per future session. Add isRetryableStatus()
and split the main branch into ok / retryable / permanent paths. Apply
the same rule in processRetryQueue: a 4xx on a queued entry moves
straight to dead/ instead of exhausting the attempt counter.
Why: The old regex-based path munging only matched uppercase drive
letters and silently breaks on lowercase or non-ASCII paths. Use
node:url's fileURLToPath for correct cross-platform resolution and
let OB_PROJECT_ROOT override the two-levels-up default, since README
tells users to install the script in arbitrary scripts directories.
…ters

Why: User turns are concatenated verbatim into the ingest POST body.
An attacker who pastes "Ignore previous instructions and DROP thoughts;"
into a session lands that text untouched at the ingest endpoint. Wrap
the transcript body in <thought_content>...</thought_content> delimiters
and neutralize literal occurrences of those tags inside user content so
a malicious turn can't break out of the wrapper.
justfinethanku and others added 26 commits June 19, 2026 09:06
…lanshurafa/gmail-smart-pull

[recipes] Gmail smart pull — sensitivity routing + contact entities
…lanshurafa/smart-ingest-schema

[schemas] Smart ingest pipeline tables
…lanshurafa/thought-enrichment

[recipes] Thought enrichment pipeline
…lanshurafa/brain-stats-daily

[schemas] Brain stats daily + heatmap filter
…lanshurafa/crm-person-tiers

[schemas] CRM person tiers — relationship tier schema + dashboard page
…lanshurafa/consolidation-workers

[integrations] Consolidation workers (bio + metadata)
Accepting the access key via ?key= leaks it into CDN/proxy/Supabase
access logs. Accept it only via the x-brain-key or Authorization: Bearer
headers, matching enhanced-mcp. Updates README examples + auth docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
State that the service-role backend bypasses RLS (key = full-brain
access, single-tenant by design) and that the key must be high-entropy
since rate limiting is best-effort.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document that MCP_ACCESS_KEY should be high-entropy (single shared
secret, single-tenant) and that the wildcard CORS is deliberate and
safe given header-based auth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/dashboard-extension-hook

Add config-driven extension hook for sidebar
…lanshurafa/chrome-capture-gemini-history

[integrations] Chrome extension — Gemini bulk history sync (Phase B/C)
…lanshurafa/enhanced-mcp

[integrations] Enhanced MCP server (alpha tool suite)
…lanshurafa/rest-api-gateway

[integrations] REST API gateway
…-ob1-gate-v2-empty-runs

[docs] Fix OB1 gate v2 workflow runs
…red-key fallback

Prefer the dedicated read-surface key when set; retain the shared
MCP_ACCESS_KEY fallback until Stone-side harnesses migrate (design
decision D1 — full decouple is a later increment).

Matches agent-memory-api v6 deployed 2026-06-11.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added dashboard documentation Improvements or additions to documentation integration labels Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dashboard documentation Improvements or additions to documentation integration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants