Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
721e282
feat(quick-260707-7id): record MCP agent sessions into logs/history/r…
Jul 7, 2026
c54596c
docs(quick-260707-7id): Record MCP agent sessions into logs, history,…
Jul 7, 2026
054a04f
fix(mcp-session-recorder): record all action tools at the bridge with…
Jul 7, 2026
f4992f8
chore(mcp-session-recorder): wire session-recorder test into npm test…
Jul 8, 2026
7f59a3c
feat(control-panel): show MCP/Autopilot source badge in session list
Jul 8, 2026
2ec4207
fix(control-panel): keep open select menus from clipping inside setti…
Jul 8, 2026
7427825
fix(control-panel): contain dashboard scroll to the content pane
Jul 8, 2026
f6f4d90
feat(knowledge-graph): rework 3D graph into category overview with ex…
Jul 8, 2026
fed0acc
chore(showcase): sync knowledge graph categories with full site-guide…
Jul 8, 2026
42f6ea0
docs: add community guidelines
Jul 9, 2026
8e46173
feat(sheets): add bounded OAuth API client
Jul 15, 2026
23badd3
feat(sheets): add typed Sheets capabilities
Jul 15, 2026
a83d21b
fix(privacy): redact spreadsheet session data
Jul 15, 2026
6e8d40d
docs(quick-260715-8wh): implement production-safe Google Sheets API MVP
Jul 15, 2026
3d096b9
feat(sheets): replace OAuth with signed-in session
Jul 15, 2026
7895fce
feat(sheets): add bounded signed-in UI fallback
Jul 15, 2026
c1d9ed6
fix(sheets): enforce fail-closed UI recovery
Jul 15, 2026
4c1d388
fix(sheets): close UI session edge cases
Jul 15, 2026
d56e494
docs(sheets): document signed-in session UAT
Jul 15, 2026
9ab3d40
fix(sheets): block dynamic UI network loaders
Jul 15, 2026
55f1477
docs(quick-260715-hs1): zero-extra-auth Google Sheets
Jul 15, 2026
e9d0081
feat: finalize MCP session replay and v0.9.91 release
Jul 20, 2026
7a8cf83
Merge origin/main into refinements
Jul 20, 2026
15ddf9f
fix(sheets): redact nested tab titles from sessions
Jul 20, 2026
2f8359b
fix(mcp): resolve token-only lifecycle ownership
Jul 20, 2026
b827ca8
docs(quick-260720-jb5): Patch Sheets session redaction and multi-tab …
Jul 20, 2026
597c543
fix: patch reviewed P1 and P2 defects
Jul 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 136 additions & 6 deletions .planning/STATE.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-07-03T17:58:36.680Z",
"generatedAt": "2026-07-15T11:05:02.940Z",
"descriptorCount": 2314,
"rowCount": 2314,
"rows": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Phase 44 T1 Readiness Matrix

**Generated:** 2026-07-03T17:58:36.680Z
**Generated:** 2026-07-15T11:05:02.940Z

This report is generated from `extension/catalog/recipe-index.generated.js` plus the live `capability-catalog.js` resolver. It is the v1.1.0 truth surface: catalog/search support means a capability is searchable and routable, not that every app has direct API execution today.

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
phase: quick-260707-7id
plan: 01
subsystem: mcp-bridge / session-history
tags: [mcp, session-recording, replay, memory, history-badges, mv3-eviction]
requires:
- extension/ws/mcp-tool-dispatcher.js dispatch choke points (Phase 271 metrics-recorder pattern)
- extension/utils/automation-logger.js saveSession + fsbSessionLogs/fsbSessionIndex
- background.js extractAndStoreMemories + session-schema createSession
provides:
- globalThis.fsbMcpSessionRecorder (extension/utils/mcp-session-recorder.js)
- fsbSessionIndex entries with mode + mcpClient fields
- MCP/Autopilot source badges in sidepanel + options history lists
affects:
- extension/ws/mcp-tool-dispatcher.js (both finally blocks)
- extension/background.js (SW load order)
- extension/utils/automation-logger.js (NEW/APPEND/index paths)
tech-stack:
added: []
patterns:
- versioned chrome.storage.session envelope (mcp-task-store.js pattern, key fsbMcpSessionBuffer v1)
- promise-chain write lock (_withRecordLock pattern)
- lazy-global direct calls in SW (never chrome.runtime.sendMessage in-SW)
key-files:
created:
- extension/utils/mcp-session-recorder.js
- tests/mcp-session-recorder.test.js
modified:
- extension/ws/mcp-tool-dispatcher.js
- extension/background.js
- extension/utils/automation-logger.js
- extension/ui/sidepanel.js
- tests/lattice-provider-bridge-smoke.test.js
- extension/ui/options.js (LEFT UNCOMMITTED -- mixed with pre-existing local edits)
- package.json (LEFT UNCOMMITTED -- mixed with pre-existing local edits)
decisions:
- "Optional AI title/summary synthesis SKIPPED (locked default: task seeded from first visualReason; no single-call global provider helper exists)"
- "Single commit at Task 3 with exactly the seven clean files, per the plan's explicit commit protocol (options.js + package.json left as flagged local edits)"
- "closeSession re-seeds one session-bound log entry when getSessionLogs(sessionId) is empty so the saveSession empty-logs gate passes on post-eviction restore closes"
metrics:
duration: "20m 19s"
completed: "2026-07-07T10:59:31Z"
tests: "npm test exit 0; new suite 94 passed / 0 failed"
commit: 721e2826
---

# Quick Task 260707-7id: Record MCP Agent Sessions into Logs/History/Replay/Memory Summary

**One-liner:** MCP-agent browsing sessions (sidecar action calls through is_final or 60s idle) now land in fsbSessionLogs/fsbSessionIndex with mode 'mcp-agent', replay-shape actionHistory, and memory extraction via a dispatcher-choke-point sibling recorder that survives MV3 SW eviction through a versioned fsbMcpSessionBuffer envelope.

## What Was Built

1. **extension/utils/mcp-session-recorder.js** (new, 697 lines) -- IIFE classic script, lazy `globalThis.chrome`, registers `globalThis.fsbMcpSessionRecorder` plus a CommonJS mirror. Sessions keyed `agentId::tabId`; birth on first `visualSession` sidecar action (autopilot-format `session_<ms>` id with same-ms monotonic guard, task from first `visualReason`, `logSessionStart` seeds the saveSession log gate); sidecar-less calls JOIN the agent's most recently active session (unknown agentId ignored -- structurally enforces the >=1-action gate); `run_task` skipped entirely; close on `isFinal`/`is_final` or the 60s sliding idle window (own injectable timer + lazy sweep, mirroring mcp-visual-session-lifecycle semantics). Close builds the session via lazy `createSession` (manual same-keys object under Node) with `mode: 'mcp-agent'` + `mcpClient`, then calls `automationLogger.saveSession` and `extractAndStoreMemories` through DIRECT globals (never in-SW sendMessage), both fire-and-forget. Key-targeted redaction (`pass(word)?|secret|token|credential|api[-_]?key|authorization`) via lazy `redactForLog` (literal `[REDACTED]` fallback); url/selector/text persist raw for replay. Open sessions persist to `chrome.storage.session.fsbMcpSessionBuffer` `{v:1, records}` (canonical-empty on mismatch, key removed when empty); module-load restore closes expired sessions and re-arms live ones.

2. **Dispatcher hooks** -- separate sibling try/catch blocks AFTER the metrics recorder blocks in BOTH finally blocks of `dispatchMcpToolRoute` (`dispatcher_route: 'tool'`) and `dispatchMcpMessageRoute` (`dispatcher_route: 'message'`, INSIDE the `!_mcpMetricsSuppressInner` gate so alias-routed tools record once). Both use `client: resolveMcpClientLabel(payload)`, not awaited, no return-in-finally. Test 9's fsbMcpMetricsRecorder regex spans verified undisturbed (still exactly 2).

3. **SW load** -- one new line in background.js directly after the mcp-metrics-recorder load (comment lines deliberately token-free for the tally pin).

4. **automation-logger.js** -- `mode` (default 'autopilot') + `mcpClient` (default null) carried through the NEW session literal, the APPEND field carries, and the indexEntry; pre-existing index entries default to Autopilot in the UI.

5. **Badges** -- sidepanel `loadHistoryList` and options `loadSessionList` meta rows render `MCP · <client>` (`history-source-badge mcp` / `session-source-badge mcp`) for mcp-agent sessions, `Autopilot` otherwise; escapeHtml'd, no new CSS files, no filter control (locked scope: badge only -- optional history filter skipped).

6. **tests/mcp-session-recorder.test.js** (new, 623 lines, 94 assertions) -- the 10 locked cases plus eviction restore (expired closes + live rehydrates + buffer key removal) and malformed/wrong-version envelope collapse; registered in the package.json chain right after mcp-dispatcher-client-label.

## Verification Results

- `node tests/mcp-session-recorder.test.js`: **94 passed, 0 failed** (all 10 locked cases + eviction restore + source-pin guards).
- `node tests/mcp-dispatcher-client-label.test.js`: 51 passed, 0 failed (Test 9 metrics pins undisturbed).
- `node tests/lattice-provider-bridge-smoke.test.js`: 110 passed, 0 failed (tally bumped 309 -> 310 mentions, 305 -> 306 call sites, provenance comments added per convention).
- Full `npm test`: **exit 0**. Aggregate across the two dominant reporter formats: 2,817 passed / 0 failed ("Results:" format, 49 suites) + 1,081 passed / 0 failed ("PASS/FAIL" format, 28 suites) = 3,898 assertions, 0 failures; remaining suites use bespoke reporters and all passed (chain is `&&`-fatal).
- `git status mcp/`: clean -- zero changes under mcp/, no tool-schema changes.
- `git log -1 --stat`: exactly the seven staged files (1,387 insertions, 2 deletions -- the two replaced lattice pin lines); no file deletions.
- `git diff extension/ui/options.js`: 5 hunks -- 4 pre-existing (fsbSelect work, lines ~1067-1167) + exactly 1 new (the badge at ~2792).

## Commit

- **721e2826** `feat(quick-260707-7id): record MCP agent sessions into logs/history/replay/memory` -- extension/utils/mcp-session-recorder.js, extension/ws/mcp-tool-dispatcher.js, extension/background.js, extension/utils/automation-logger.js, extension/ui/sidepanel.js, tests/lattice-provider-bridge-smoke.test.js, tests/mcp-session-recorder.test.js.

## FLAGGED: Files Left Uncommitted (user action needed)

Per the plan's commit protocol, these two files carry BOTH pre-existing local edits AND this task's surgical additions -- commit them together with your pending work:

1. **extension/ui/options.js** -- pre-existing fsbSelect edits (~lines 1067-1167) + this task's badge insertion in `loadSessionList` (~line 2792, the `session-source-badge` span).
2. **package.json** -- pre-existing test-chain insertion (`settings-card-select-clipping` + `control-panel-scroll-containment`, which reference your untracked local test files) + this task's insertion of `node tests/mcp-session-recorder.test.js` after `node tests/mcp-dispatcher-client-label.test.js`.

Also untouched and still local: extension/ui/options.css (pre-existing), tests/control-panel-scroll-containment.test.js + tests/settings-card-select-clipping.test.js (pre-existing untracked).

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking environment] Fresh worktree had no installed dependencies**
- **Found during:** Task 1 verification (lattice test: `Cannot find package 'lattice'`), then Task 3 full-suite run (`better-sqlite3`, `ng` missing).
- **Fix:** `npm ci` (lockfile-exact restore, no new packages, no lockfile changes) in four locations: repo root, mcp/, showcase/server/, showcase/angular/.
- **Files modified:** none tracked (node_modules only; package-lock.json byte-identical).

**2. [Rule 2 - Missing critical functionality] Companion call-site pin also updated**
- **Found during:** Task 1 D. The plan cited only the `grep -c "importScripts"` mention tally, but lattice-provider-bridge-smoke.test.js ALSO pins the `importScripts(` call-site count (305), which my one new line increments.
- **Fix:** updated both assertions (310 mentions / 306 call sites) with provenance comments per the test's documented convention.
- **Commit:** 721e2826.

**3. [Rule 2 - Missing critical functionality] Post-eviction saveSession gate re-seed**
- **Found during:** Task 1 design of the restore path. After SW eviction the automationLogger in-memory log buffer is empty, so a restored-then-expired session would hit the saveSession empty-logs gate (automation-logger.js:709) and be silently dropped.
- **Fix:** closeSession re-seeds one session-bound log entry via `logSessionStart` when `getSessionLogs(sessionId)` is empty, before calling saveSession. Proven by Test 11 ("empty post-eviction log buffer re-seeded").
- **Commit:** 721e2826.

### Notes (not deviations)

- Plan's line citations for the lattice tally (568-584, older baselines) had drifted in this workspace (assertions now at ~618/652 with baselines 309/305 after parallel head work); handled per the test's own convention.
- Test 10's substring-order assertion initially matched the `typeof` guard instead of the call site during authoring; pinned to the call form (`recordDispatch({`) before the suite was first registered -- never committed broken.
- Orchestrator's generic per-task-commit rule was superseded by the plan's explicit Task 3 commit protocol (single commit, exactly seven files) which plan verification item 5 depends on.

## Optional AI Title Synthesis: SKIPPED

Per the plan's locked default: no single-call global provider helper exists in the SW, so `task` is seeded from the first `visualReason` (first-visualReason seeding). No provider plumbing was built.

## Pre-existing Test Failures

None. After dependency restore, the full suite is green (exit 0). No unrelated failures were encountered or left behind.

## Known Stubs

None -- all recorded fields are wired to real data sources; badges render live index fields.

## Self-Check: PASSED

- extension/utils/mcp-session-recorder.js: FOUND (committed)
- tests/mcp-session-recorder.test.js: FOUND (committed)
- Commit 721e2826: FOUND on refinements
- npm test exit 0: CONFIRMED
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
quick: 260715-8wh
plan: 01
type: execute
wave: 1
depends_on: []
autonomous: true
files_modified:
- extension/manifest.json
- extension/background.js
- extension/ui/control_panel.html
- extension/ui/options.js
- extension/utils/google-sheets-api.js
- extension/utils/capability-router.js
- extension/utils/capability-catalog.js
- extension/config/service-denylist.json
- catalog/handlers/gsheets.js
- catalog/descriptors/gsheets__get_spreadsheet.json
- catalog/descriptors/gsheets__get_values.json
- catalog/descriptors/gsheets__update_values.json
- catalog/descriptors/gsheets__append_values.json
- catalog/descriptors/gsheets__clear_values.json
- extension/catalog/handlers/gsheets.js
- extension/catalog/recipe-index.generated.js
- scripts/verify-recipe-path-guard.mjs
- extension/utils/spreadsheet-record-redaction.js
- extension/ws/mcp-tool-dispatcher.js
- extension/ws/mcp-bridge-client.js
- docs/google-sheets-api.md
- tests/google-sheets-api.test.js
- tests/gsheets-handler.test.js
- tests/google-sheets-wiring.test.js
- tests/spreadsheet-record-redaction.test.js
requirements:
- QT-SHEETS-01
- QT-SHEETS-02
- QT-SHEETS-03
user_setup:
- Enable the Google Sheets API in a Google Cloud project.
- Create a Chrome Extension OAuth client tied to the stable packaged extension ID and replace the fail-closed manifest placeholder before live use.
---

# Quick 260715-8wh Plan

## Objective

Add a production-safe Google Sheets API MVP to the extension: explicit user-initiated OAuth, a bounded REST facade, five discoverable typed capabilities, and mandatory shape-only session-recording redaction. Keep the existing `fill_sheet` and `read_sheet` UI automation tools unchanged as a fallback.

## Constraints

- The worktree contains substantial unrelated user changes, including changes in several files this task must touch. Preserve all of them and stage only task-owned hunks; never stage an entire already-dirty file without verifying its diff against `HEAD`.
- Do not ship or depend on Google's Developer Preview remote Sheets MCP server.
- Never expose, log, return, or persist an OAuth access token.
- Do not add an arbitrary HTTP proxy. The Sheets API facade must expose only the five named operations, validate inputs, cap request size, apply timeouts, and retry only once after an authentication rejection.
- Live OAuth cannot be provisioned from this repository. Use an unmistakable placeholder client ID and fail closed with an actionable typed error until the user supplies a real Chrome Extension OAuth client.

## Tasks

### 1. Add fail-closed Chrome OAuth, bounded Sheets REST access, and a click-initiated connection surface

**Requirements:** QT-SHEETS-01

**Files:** `extension/manifest.json`, `extension/background.js`, `extension/ui/control_panel.html`, `extension/ui/options.js`, `extension/utils/google-sheets-api.js`, `extension/utils/capability-router.js`, `docs/google-sheets-api.md`, `tests/google-sheets-api.test.js`

**Action:**

- Add Chrome Identity permission and manifest OAuth configuration for the single `https://www.googleapis.com/auth/spreadsheets` scope. The checked-in client ID must be a clearly invalid placeholder that the runtime detects and rejects before calling Google.
- Implement a classic-script/background-compatible `FsbGoogleSheetsApi` module. Provide connect/status/disconnect runtime actions plus a narrow handler context with only `getSpreadsheet`, `getValues`, `updateValues`, `appendValues`, and `clearValues`.
- Require `interactive: true` only from the control-panel Connect button; capability execution must request a token non-interactively. Disconnect must remove cached auth state. Do not store tokens or include them in errors/results.
- Restrict requests to the Sheets v4 base URL, validate spreadsheet IDs and A1 ranges, enforce a conservative JSON body-size cap and timeout, encode URL components, normalize Google/network/timeout failures into typed safe errors, and on HTTP 401 remove the cached token and retry once.
- Add a small control-panel status/connect/disconnect surface. Report missing OAuth configuration clearly and do not claim connection merely because configuration exists.
- Document the Google Cloud setup needed for live use, the exact capabilities and scope, the fallback tools, and the privacy behavior.
- Add focused unit tests for placeholder fail-closed behavior, noninteractive versus interactive auth, bounded request construction, safe typed errors, body limits, and token non-disclosure.

**Verify:** `node --test tests/google-sheets-api.test.js`

**Commit:** `feat(sheets): add bounded OAuth API client`

### 2. Register five typed, discoverable `gsheets.*` capabilities

**Requirements:** QT-SHEETS-02

**Files:** `catalog/handlers/gsheets.js`, `catalog/descriptors/gsheets__get_spreadsheet.json`, `catalog/descriptors/gsheets__get_values.json`, `catalog/descriptors/gsheets__update_values.json`, `catalog/descriptors/gsheets__append_values.json`, `catalog/descriptors/gsheets__clear_values.json`, `extension/catalog/handlers/gsheets.js`, `extension/catalog/recipe-index.generated.js`, `extension/background.js`, `extension/utils/capability-catalog.js`, `extension/config/service-denylist.json`, `scripts/verify-recipe-path-guard.mjs`, `tests/gsheets-handler.test.js`, `tests/google-sheets-wiring.test.js`

**Action:**

- Create handler-backed capabilities `gsheets.get_spreadsheet`, `gsheets.get_values`, `gsheets.update_values`, `gsheets.append_values`, and `gsheets.clear_values` for `https://docs.google.com`.
- Give every capability a strict JSON schema and explicit side-effect metadata. Make `spreadsheetId` optional only when it can be safely derived from the active `/spreadsheets/d/<id>` URL. Require an explicit range and structured two-dimensional values where applicable.
- Route handlers only through the narrow Sheets API context from Task 1. Never accept a URL, method, headers, token, or generic request body from a capability invocation.
- Register/import the handler through all explicit catalog/background/package verification paths, regenerate packaged catalog artifacts with the repository packaging script, and classify Google Sheets as a sensitive origin so mutating capabilities retain consent protections.
- Preserve `fill_sheet` and `read_sheet` exactly as existing fallback tools.
- Test parameter validation, ID derivation, operation-to-client routing, write/destructive classifications, catalog discoverability, packaged handler presence, and legacy fallback availability.

**Verify:** `node --test tests/gsheets-handler.test.js tests/google-sheets-wiring.test.js && node scripts/package-extension.mjs`

**Commit:** `feat(sheets): add typed Sheets capabilities`

### 3. Redact spreadsheet content before MCP session recording

**Requirements:** QT-SHEETS-03

**Files:** `extension/utils/spreadsheet-record-redaction.js`, `extension/background.js`, `extension/ws/mcp-tool-dispatcher.js`, `extension/ws/mcp-bridge-client.js`, `tests/spreadsheet-record-redaction.test.js`

**Action:**

- Add a pure, fail-safe sanitizer that recognizes direct legacy `fill_sheet`/`read_sheet` calls and `mcp:capabilities-invoke` requests whose slug starts with `gsheets.`.
- Apply the sanitizer at both recording ingress points before the session recorder sees the entry. Retain only non-content shape/operation facts needed for diagnostics, such as operation name, row/column/value counts, success/status, and a safe error code.
- Strip spreadsheet IDs, sheet names, ranges, cell values, formulas, rendered results, raw errors, and any nested request/response content. If sanitization cannot confidently classify the data, drop the spreadsheet payload rather than recording it.
- Prove with sentinel tests that sensitive strings never appear in serialized recorded entries for reads, writes, appends, clears, failures, or legacy fallbacks, while unrelated tool records remain unchanged.

**Verify:** `node --test tests/spreadsheet-record-redaction.test.js && npm run validate:extension`

**Commit:** `fix(privacy): redact spreadsheet session data`

## Final Verification

- Run all four focused test files together.
- Run `node scripts/package-extension.mjs` and `npm run validate:extension`.
- Inspect `git diff` and each task commit to confirm only task-owned hunks were committed and all unrelated pre-existing changes remain in the worktree.
- Write `260715-8wh-SUMMARY.md` with implemented behavior, verification evidence, commit hashes, and the remaining external OAuth setup requirement.
Loading
Loading