Add Phase 5A Memory Autopilot for auto-retrieve and auto-candidate queue - #105
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 53 minutes and 23 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughIntroduces Phase 5A memory autopilot: a heuristic policy service decides whether to auto-retrieve memory or queue memory candidates per turn; a new adaptive turn service orchestrates those decisions; a REST endpoint ( ChangesPhase 5A Memory Autopilot
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7cb58ae221
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!preview.should_create_candidate) return preview; | ||
| const gate = requireMcpCaptureEnabled(env); | ||
| if (!gate.ok) return { ...preview, should_create_candidate: false, warnings: [...preview.warnings, "mcp_capture_disabled_no_candidate_written"], blocker: gate }; | ||
| return runPostAnswerTurn(client, principal.userId, input, env); |
There was a problem hiding this comment.
Audit MCP adaptive-turn candidate writes
When PANDORA_ENABLE_MCP_CAPTURE=true and a post-answer durable-memory trigger is detected, this path calls runPostAnswerTurn, which inserts into memory_capture_candidates, but it never calls auditPandoraMcpToolCall unlike the other MCP tools in this file. Those auto-queued candidates therefore have no audit_logs proof, breaking the audit-backed requirement for MCP memory writes.
Useful? React with 👍 / 👎.
|
|
||
| export type AdaptiveTurnInput = { namespace: MemoryBridgeNamespace; user_message: string; assistant_draft?: string; current_task?: string; conversation_type?: string; mode: "pre_answer" | "post_answer"; source?: string }; | ||
| export function autopilotSafety(env: Partial<NodeJS.ProcessEnv> = process.env) { const r=resolvePandoraRuntimeSafetyConfig(env).config; return { public_read:false, public_persistence:false, model_calls:r.modelCallsEnabled, embeddings:r.embeddingsEnabled, semantic_retrieval:r.semanticRetrievalEnabled, auto_capture_low_risk:r.autoCaptureLowRiskEnabled }; } | ||
| export function disabledPreAnswer(input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const mode=resolvePandoraMemoryAutopilotMode(env); const decision=shouldAutoRetrieveMemory({userMessage:input.user_message,namespace:input.namespace,currentTask:input.current_task,conversationType:input.conversation_type}); const r=resolvePandoraRuntimeSafetyConfig(env).config; const enabled=r.memoryAutopilotEnabled&&r.autoRetrieveEnabled&&mode!=="off"; return {ok:true,mode:"pre_answer" as const,autopilot_mode:mode,namespace:input.namespace,should_retrieve:enabled&&decision.shouldRetrieve,retrieve_decision:decision,context:null,warnings:enabled?[]:["autopilot_or_auto_retrieve_disabled"],safety:autopilotSafety(env)}; } |
There was a problem hiding this comment.
Honor suggest mode before retrieving context
When PANDORA_MEMORY_AUTOPILOT=suggest, the documented behavior is preview-only, but this condition treats every mode except off as enabled. In a suggest-only environment with PANDORA_AUTO_RETRIEVE=true, pre_answer still requires the context gate and returns actual private memory context instead of just the retrieval decision.
Useful? React with 👍 / 👎.
| }, | ||
| "/api/memory/adaptive/turn": { | ||
| "post": { | ||
| "summary": "Phase 5A adaptive turn autopilot", |
There was a problem hiding this comment.
Expose adaptive_turn with an operationId
The new ChatGPT/OpenAPI operation is the only path in this file without an operationId, while the added instructions tell clients to call adaptive_turn. Importers that name tools from operationId will not expose that tool name for this endpoint, so the advertised autopilot action is not callable from the schema as written.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/config/pandora-runtime-safety-config.ts (1)
54-64: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winFix the exported metadata for
sensitiveMemoryRequiresApproval.This gate now defaults to enabled unless the env var is
"false", butruntime.gates[...]still reportssafeDefault: falseanddangerous: true. That makes the exported status contradict the actual config for a default-on safety control.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/config/pandora-runtime-safety-config.ts` around lines 54 - 64, The exported gate metadata for sensitiveMemoryRequiresApproval is inconsistent with its actual default-on behavior in resolvePandoraRuntimeSafetyConfig. Update the gates entry construction so that the metadata for that specific key reflects the real semantics, including the correct safeDefault and dangerous values, while leaving the other gates unchanged. Use the existing resolvePandoraRuntimeSafetyConfig, vars, and runtime.gates mapping to locate and adjust the special-case handling for sensitiveMemoryRequiresApproval.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/api/memory/adaptive/turn/route.ts`:
- Around line 9-11: The POST handler in the adaptive turn route returns preview
responses before calling withBridge(), which allows unauthenticated callers to
get autopilot decision data. Move the bridge authentication check to the start
of POST, before any disabledPreAnswer/disabledPostAnswer or preview responses
are returned, and reuse the authenticated client/principal for the later
runPreAnswerTurn/runPostAnswerTurn paths.
In `@docs/pandora-chatgpt-adaptive-instructions.md`:
- Around line 11-20: Remove the legacy memory-retrieval guidance that still
tells the agent to call get_adaptive_context before important answers, and leave
only the adaptive_turn workflow. Update the instructions in the markdown section
that defines the Phase 5A behavior so there is a single retrieval path, using
adaptive_turn with mode=pre_answer and mode=post_answer, with no conflicting
references to get_adaptive_context.
In `@lib/services/memory-adaptive-turn-service.ts`:
- Around line 10-11: Update the shared pre-answer path in disabledPreAnswer and
runPreAnswerTurn so retrieval is gated by memoryContextApiEnabled as well as the
existing autopilot flags. Right now should_retrieve can still become true when
only the autopilot settings are on, which lets runPreAnswerTurn proceed without
the read gate. Add the gate into the shared enabled calculation used by
disabledPreAnswer, and keep runPreAnswerTurn relying on that shared decision so
both REST and MCP paths behave consistently.
- Line 13: The runPostAnswerTurn flow is sending raw user and assistant content
into candidate classification before any redaction, so secret-like text can
still leave the process. Update runPostAnswerTurn to redact or mask sensitive
content in candidateText before calling createCandidatesFromSession(), and
ensure any downstream classifier/provider input uses the redacted text rather
than input.user_message or input.assistant_draft. Keep the candidate creation
behavior in createCandidatesFromSession compatible with the sanitized payload.
- Around line 12-13: The `capture_low_risk` mode is currently bypassing its
dedicated runtime gate in `disabledPostAnswer` and `runPostAnswerTurn`, so add a
separate check for `PANDORA_AUTO_CAPTURE_LOW_RISK` (via the runtime safety
config) before allowing candidate creation in that mode. Update the eligibility
logic in `disabledPostAnswer` to require the specific capture-low-risk flag when
`autopilot_mode` is `"capture_low_risk"`, and keep `runPostAnswerTurn`’s warning
behavior aligned with the new gate.
In `@lib/services/pandora-mcp-tools.ts`:
- Around line 44-54: The adaptiveTurnTool path is missing MCP audit logging, so
private-context access and candidate creation aren’t recorded like the other
memory-facing tools. Update adaptiveTurnTool to call auditPandoraMcpToolCall
with the adaptive_turn tool name and relevant principal/input details before it
branches into runPreAnswerTurn, disabledPostAnswer, or runPostAnswerTurn. Keep
the audit call aligned with the existing audit_logs behavior used by the other
MCP tools so the autopilot path is visible in the audit trail.
In `@tests/phase5-memory-autopilot.test.ts`:
- Around line 9-10: The test setup in `phase5-memory-autopilot.test.ts` is only
restoring mocks, so `vi.stubEnv(...)` overrides can leak between cases. Update
the existing `afterEach` рядом with the `envOn` fixture to also clear stubbed
environment variables by calling `vi.unstubAllEnvs()`, or switch the suite to
`unstubEnvs`, so later tests don’t inherit these env settings.
---
Outside diff comments:
In `@lib/config/pandora-runtime-safety-config.ts`:
- Around line 54-64: The exported gate metadata for
sensitiveMemoryRequiresApproval is inconsistent with its actual default-on
behavior in resolvePandoraRuntimeSafetyConfig. Update the gates entry
construction so that the metadata for that specific key reflects the real
semantics, including the correct safeDefault and dangerous values, while leaving
the other gates unchanged. Use the existing resolvePandoraRuntimeSafetyConfig,
vars, and runtime.gates mapping to locate and adjust the special-case handling
for sensitiveMemoryRequiresApproval.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fecce092-bed0-4ac1-be8a-c1077eb4b836
📒 Files selected for processing (12)
app/api/memory/adaptive/turn/route.tsdocs/pandora-chatgpt-adaptive-instructions.mddocs/pandora-memory-autopilot.mdlib/config/pandora-runtime-safety-config.tslib/services/admin-memory-verification-loader.tslib/services/memory-adaptive-turn-service.tslib/services/memory-autopilot-policy-service.tslib/services/pandora-mcp-server.tslib/services/pandora-mcp-tools.tspublic/pandora-memory-openapi.jsontests/phase5-memory-autopilot.test.tstests/unit/pandora-auth-runtime-gates.test.ts
| export async function POST(request:NextRequest){ const parsed=schema.safeParse(await request.json().catch(()=>({}))); if(!parsed.success)return NextResponse.json({ok:false,blockers:["invalid_request"],issues:parsed.error.flatten()},{status:400}); const body=parsed.data; const namespace=inferMemoryNamespace({userMessage:body.user_message,namespace:body.namespace,currentTask:body.current_task}); const input={...body,namespace}; const env=process.env; const runtime=resolvePandoraRuntimeSafetyConfig(env).config; | ||
| if(body.mode==="pre_answer"){ const preview=disabledPreAnswer(input,env); if(!preview.should_retrieve)return NextResponse.json(preview); if(!runtime.memoryContextApiEnabled)return NextResponse.json({...preview,ok:false,blockers:["memoryContextApiEnabled_disabled"],next_step:"Set PANDORA_ENABLE_MEMORY_CONTEXT_API=true in a reviewed environment."},{status:403}); const bridge=await withBridge(request,"memoryContextApiEnabled"); if("error" in bridge)return bridge.error; return NextResponse.json(await runPreAnswerTurn(bridge.client,bridge.principal.userId,input,env)); } | ||
| const preview=disabledPostAnswer(input,env); if(!preview.should_create_candidate)return NextResponse.json(preview); if(!runtime.memoryCaptureApiEnabled)return NextResponse.json({...preview,ok:false,blockers:["memoryCaptureApiEnabled_disabled"],next_step:"Set PANDORA_ENABLE_MEMORY_CAPTURE_API=true in a reviewed environment."},{status:403}); const bridge=await withBridge(request,"memoryCaptureApiEnabled"); if("error" in bridge)return bridge.error; return NextResponse.json(await runPostAnswerTurn(bridge.client,bridge.principal.userId,input,env)); } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Require bridge auth before returning autopilot previews.
This handler only calls withBridge() after it decides a read/write is needed. Anonymous callers can therefore get 200 responses with namespace, candidate_decision/retrieve_decision, and safety whenever the policy says “do nothing,” even though the endpoint is documented as bearer-auth protected.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/api/memory/adaptive/turn/route.ts` around lines 9 - 11, The POST handler
in the adaptive turn route returns preview responses before calling
withBridge(), which allows unauthenticated callers to get autopilot decision
data. Move the bridge authentication check to the start of POST, before any
disabledPreAnswer/disabledPostAnswer or preview responses are returned, and
reuse the authenticated client/principal for the later
runPreAnswerTurn/runPostAnswerTurn paths.
| ## Phase 5A autopilot instructions | ||
|
|
||
| - Before important answers, call `adaptive_turn` with `mode=pre_answer` so Pandora can retrieve relevant context automatically. | ||
| - After important exchanges, call `adaptive_turn` with `mode=post_answer` so Pandora can queue durable memory candidates automatically. | ||
| - Do not require the user to manually say “retrieve memory,” “save this,” or “use Pandora.” | ||
| - Do not save secrets, credentials, tokens, API keys, passwords, OAuth codes, or database keys. | ||
| - Sensitive/private details should go to the review queue, not permanent memory. | ||
| - Keep `real_life` and `au` namespaces separate. | ||
| - Do not call things done without verification. | ||
| - Catch gambling risk, money/reputation risk, and fantasy-vs-execution drift as review-gated risk candidates. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Remove the older get_adaptive_context guidance or this prompt will issue conflicting retrieval instructions.
These new adaptive_turn rules conflict with the still-present directive at Line 3 to call get_adaptive_context before important answers. Keeping both paths in the same prompt makes the agent follow two memory workflows and weakens the Phase 5A contract.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/pandora-chatgpt-adaptive-instructions.md` around lines 11 - 20, Remove
the legacy memory-retrieval guidance that still tells the agent to call
get_adaptive_context before important answers, and leave only the adaptive_turn
workflow. Update the instructions in the markdown section that defines the Phase
5A behavior so there is a single retrieval path, using adaptive_turn with
mode=pre_answer and mode=post_answer, with no conflicting references to
get_adaptive_context.
| export function disabledPreAnswer(input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const mode=resolvePandoraMemoryAutopilotMode(env); const decision=shouldAutoRetrieveMemory({userMessage:input.user_message,namespace:input.namespace,currentTask:input.current_task,conversationType:input.conversation_type}); const r=resolvePandoraRuntimeSafetyConfig(env).config; const enabled=r.memoryAutopilotEnabled&&r.autoRetrieveEnabled&&mode!=="off"; return {ok:true,mode:"pre_answer" as const,autopilot_mode:mode,namespace:input.namespace,should_retrieve:enabled&&decision.shouldRetrieve,retrieve_decision:decision,context:null,warnings:enabled?[]:["autopilot_or_auto_retrieve_disabled"],safety:autopilotSafety(env)}; } | ||
| export async function runPreAnswerTurn(client: MemoryBridgeDbClient, userId: string, input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const base=disabledPreAnswer(input,env); if(!base.should_retrieve)return base; const context=await buildAdaptiveChatGptContext(client,{user_id:userId,namespace:input.namespace,query:redactSecrets(input.user_message),current_task:redactSecrets(input.current_task??""),max_items:8}); return {...base,context,warnings:[...base.warnings,...(context.warnings??[])]}; } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Fold memoryContextApiEnabled into the shared pre-answer flow.
disabledPreAnswer() can still return should_retrieve: true with only the autopilot flags enabled. The REST route compensates with a separate memoryContextApiEnabled check, but the MCP path calls runPreAnswerTurn() directly, so private context retrieval still runs when the read gate is off.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/services/memory-adaptive-turn-service.ts` around lines 10 - 11, Update
the shared pre-answer path in disabledPreAnswer and runPreAnswerTurn so
retrieval is gated by memoryContextApiEnabled as well as the existing autopilot
flags. Right now should_retrieve can still become true when only the autopilot
settings are on, which lets runPreAnswerTurn proceed without the read gate. Add
the gate into the shared enabled calculation used by disabledPreAnswer, and keep
runPreAnswerTurn relying on that shared decision so both REST and MCP paths
behave consistently.
| export function disabledPostAnswer(input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const mode=resolvePandoraMemoryAutopilotMode(env); const decision=shouldAutoCreateMemoryCandidate({userMessage:input.user_message,assistantResponse:input.assistant_draft,namespace:input.namespace,currentTask:input.current_task,source:input.source}); const r=resolvePandoraRuntimeSafetyConfig(env).config; const enabled=r.memoryAutopilotEnabled&&r.autoCandidateQueueEnabled&&(mode==="queue"||mode==="capture_low_risk"); return {ok:true,mode:"post_answer" as const,autopilot_mode:mode,namespace:decision.namespace,should_create_candidate:enabled&&decision.shouldCreateCandidate,candidate_decision:decision,candidates:[],warnings:enabled?[]:["autopilot_or_auto_candidate_queue_disabled"],safety:autopilotSafety(env)}; } | ||
| export async function runPostAnswerTurn(client: MemoryBridgeDbClient, userId: string, input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const base=disabledPostAnswer(input,env); if(!base.should_create_candidate)return base; const candidateText=[`User: ${input.user_message}`,input.assistant_draft?`Assistant: ${input.assistant_draft}`:null].filter(Boolean).join("\n\n"); const data=await createCandidatesFromSession(client,{user_id:userId,namespace:base.namespace,source:input.source??"adaptive_turn",text:candidateText,mode:"candidate_only"},env); const warnings=[...base.warnings,...(data.warnings??[])]; if(base.autopilot_mode==="capture_low_risk") warnings.push("auto_capture_not_implemented"); return {...base,candidates:data.candidates,warnings}; } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
capture_low_risk currently ignores its own runtime gate.
disabledPostAnswer() enables both "queue" and "capture_low_risk" from autoCandidateQueueEnabled, and runPostAnswerTurn() only adds auto_capture_not_implemented. PANDORA_AUTO_CAPTURE_LOW_RISK never changes eligibility, so this mode behaves as enabled even when its dedicated gate is false.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/services/memory-adaptive-turn-service.ts` around lines 12 - 13, The
`capture_low_risk` mode is currently bypassing its dedicated runtime gate in
`disabledPostAnswer` and `runPostAnswerTurn`, so add a separate check for
`PANDORA_AUTO_CAPTURE_LOW_RISK` (via the runtime safety config) before allowing
candidate creation in that mode. Update the eligibility logic in
`disabledPostAnswer` to require the specific capture-low-risk flag when
`autopilot_mode` is `"capture_low_risk"`, and keep `runPostAnswerTurn`’s warning
behavior aligned with the new gate.
| export function disabledPreAnswer(input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const mode=resolvePandoraMemoryAutopilotMode(env); const decision=shouldAutoRetrieveMemory({userMessage:input.user_message,namespace:input.namespace,currentTask:input.current_task,conversationType:input.conversation_type}); const r=resolvePandoraRuntimeSafetyConfig(env).config; const enabled=r.memoryAutopilotEnabled&&r.autoRetrieveEnabled&&mode!=="off"; return {ok:true,mode:"pre_answer" as const,autopilot_mode:mode,namespace:input.namespace,should_retrieve:enabled&&decision.shouldRetrieve,retrieve_decision:decision,context:null,warnings:enabled?[]:["autopilot_or_auto_retrieve_disabled"],safety:autopilotSafety(env)}; } | ||
| export async function runPreAnswerTurn(client: MemoryBridgeDbClient, userId: string, input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const base=disabledPreAnswer(input,env); if(!base.should_retrieve)return base; const context=await buildAdaptiveChatGptContext(client,{user_id:userId,namespace:input.namespace,query:redactSecrets(input.user_message),current_task:redactSecrets(input.current_task??""),max_items:8}); return {...base,context,warnings:[...base.warnings,...(context.warnings??[])]}; } | ||
| export function disabledPostAnswer(input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const mode=resolvePandoraMemoryAutopilotMode(env); const decision=shouldAutoCreateMemoryCandidate({userMessage:input.user_message,assistantResponse:input.assistant_draft,namespace:input.namespace,currentTask:input.current_task,source:input.source}); const r=resolvePandoraRuntimeSafetyConfig(env).config; const enabled=r.memoryAutopilotEnabled&&r.autoCandidateQueueEnabled&&(mode==="queue"||mode==="capture_low_risk"); return {ok:true,mode:"post_answer" as const,autopilot_mode:mode,namespace:decision.namespace,should_create_candidate:enabled&&decision.shouldCreateCandidate,candidate_decision:decision,candidates:[],warnings:enabled?[]:["autopilot_or_auto_candidate_queue_disabled"],safety:autopilotSafety(env)}; } | ||
| export async function runPostAnswerTurn(client: MemoryBridgeDbClient, userId: string, input: AdaptiveTurnInput, env: Partial<NodeJS.ProcessEnv> = process.env) { const base=disabledPostAnswer(input,env); if(!base.should_create_candidate)return base; const candidateText=[`User: ${input.user_message}`,input.assistant_draft?`Assistant: ${input.assistant_draft}`:null].filter(Boolean).join("\n\n"); const data=await createCandidatesFromSession(client,{user_id:userId,namespace:base.namespace,source:input.source??"adaptive_turn",text:candidateText,mode:"candidate_only"},env); const warnings=[...base.warnings,...(data.warnings??[])]; if(base.autopilot_mode==="capture_low_risk") warnings.push("auto_capture_not_implemented"); return {...base,candidates:data.candidates,warnings}; } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Redact post-answer text before candidate classification.
This path builds candidateText from raw user/assistant content and hands it to createCandidatesFromSession(). That service detects secrets, but it still forwards input.text to the classifier/provider before it redacts the stored rows, so secret-like content can still leave the process here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/services/memory-adaptive-turn-service.ts` at line 13, The
runPostAnswerTurn flow is sending raw user and assistant content into candidate
classification before any redaction, so secret-like text can still leave the
process. Update runPostAnswerTurn to redact or mask sensitive content in
candidateText before calling createCandidatesFromSession(), and ensure any
downstream classifier/provider input uses the redacted text rather than
input.user_message or input.assistant_draft. Keep the candidate creation
behavior in createCandidatesFromSession compatible with the sanitized payload.
| export async function adaptiveTurnTool(client: MemoryBridgeDbClient, principal: Extract<PandoraMcpPrincipal, { ok: true }>, rawInput: unknown, env: Partial<NodeJS.ProcessEnv> = process.env) { | ||
| const parsed = adaptiveTurnInputSchema.parse(rawInput); | ||
| const namespace = inferMemoryNamespace({ userMessage: parsed.user_message, namespace: parsed.namespace, currentTask: parsed.current_task }); | ||
| const input = { ...parsed, namespace, source: "mcp_adaptive_turn" }; | ||
| if (input.mode === "pre_answer") return runPreAnswerTurn(client, principal.userId, input, env); | ||
| const preview = disabledPostAnswer(input, env); | ||
| if (!preview.should_create_candidate) return preview; | ||
| const gate = requireMcpCaptureEnabled(env); | ||
| if (!gate.ok) return { ...preview, should_create_candidate: false, warnings: [...preview.warnings, "mcp_capture_disabled_no_candidate_written"], blocker: gate }; | ||
| return runPostAnswerTurn(client, principal.userId, input, env); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Add MCP audit logging to adaptive_turn.
The other memory-facing MCP tools record audit_logs, but this tool can retrieve private context and queue candidates without calling auditPandoraMcpToolCall. That leaves the new autopilot path invisible in the audit trail.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/services/pandora-mcp-tools.ts` around lines 44 - 54, The adaptiveTurnTool
path is missing MCP audit logging, so private-context access and candidate
creation aren’t recorded like the other memory-facing tools. Update
adaptiveTurnTool to call auditPandoraMcpToolCall with the adaptive_turn tool
name and relevant principal/input details before it branches into
runPreAnswerTurn, disabledPostAnswer, or runPostAnswerTurn. Keep the audit call
aligned with the existing audit_logs behavior used by the other MCP tools so the
autopilot path is visible in the audit trail.
| afterEach(()=>vi.restoreAllMocks()); | ||
| const envOn={PANDORA_ENABLE_MEMORY_AUTOPILOT:"true",PANDORA_MEMORY_AUTOPILOT:"queue",PANDORA_AUTO_RETRIEVE:"true",PANDORA_AUTO_CANDIDATE_QUEUE:"true",PANDORA_ENABLE_MEMORY_CONTEXT_API:"true",PANDORA_ENABLE_MEMORY_CAPTURE_API:"true"}; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== file list ==\n'
git ls-files tests/phase5-memory-autopilot.test.ts vitest.config.* vite.config.* package.json tsconfig*.json
printf '\n== test file excerpt ==\n'
cat -n tests/phase5-memory-autopilot.test.ts | sed -n '1,220p'
printf '\n== vitest config search ==\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'unstubEnvs|restoreAllMocks|stubEnv|vi\.stubEnv|vi\.unstubAllEnvs' .
printf '\n== package vitest version ==\n'
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const deps = {...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies};
for (const k of Object.keys(deps || {}).sort()) {
if (k.toLowerCase().includes('vitest') || k.toLowerCase().includes('vite')) {
console.log(k + ': ' + deps[k]);
}
}
JSRepository: besfeng23/Memory
Length of output: 9362
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== vitest config ==\n'
cat -n vitest.config.ts | sed -n '1,220p'
printf '\n== package.json ==\n'
cat -n package.json | sed -n '1,220p'Repository: besfeng23/Memory
Length of output: 2608
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== setup file ==\n'
cat -n tests/setup.ts | sed -n '1,220p'
printf '\n== search for env cleanup ==\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'unstubAllEnvs|unstubEnvs|restoreAllMocks|stubEnv|afterEach|beforeEach' tests .Repository: besfeng23/Memory
Length of output: 4941
Reset stubbed env vars in afterEach. vi.restoreAllMocks() does not clear vi.stubEnv(...), so these overrides can leak into later cases and make the suite order-dependent. Add vi.unstubAllEnvs() here, or enable unstubEnvs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/phase5-memory-autopilot.test.ts` around lines 9 - 10, The test setup in
`phase5-memory-autopilot.test.ts` is only restoring mocks, so `vi.stubEnv(...)`
overrides can leak between cases. Update the existing `afterEach` рядом with the
`envOn` fixture to also clear stubbed environment variables by calling
`vi.unstubAllEnvs()`, or switch the suite to `unstubEnvs`, so later tests don’t
inherit these env settings.
Motivation
real_life/aunamespaces never mixed.adaptive_turnflow for bothpre_answerretrieval andpost_answercandidate queuing.Description
lib/config/pandora-runtime-safety-config.ts, includingPANDORA_ENABLE_MEMORY_AUTOPILOT,PANDORA_MEMORY_AUTOPILOT(off|suggest|queue|capture_low_risk),PANDORA_AUTO_RETRIEVE,PANDORA_AUTO_CANDIDATE_QUEUE,PANDORA_AUTO_CAPTURE_LOW_RISK, andPANDORA_SENSITIVE_MEMORY_REQUIRES_APPROVAL(defaults to true unless explicitly set to "false").lib/services/memory-autopilot-policy-service.tswithinferMemoryNamespace,shouldAutoRetrieveMemory, andshouldAutoCreateMemoryCandidatethat encode the retrieval/candidate trigger lists, namespace rules, sensitivity/review heuristics, and secret detection fallback.lib/services/memory-adaptive-turn-service.tsthat exposes safepre_answerandpost_answerbehaviors (redaction, safety snapshot, mode resolution), queues candidates viacreateCandidatesFromSession(writes only tomemory_capture_candidates), and returnsauto_capture_not_implementedforcapture_low_riskmode in Phase 5A.POST /api/memory/adaptive/turnatapp/api/memory/adaptive/turn/route.tswith Zod validation, namespace inference,withBridgeauth/gates, pre-answer retrieval (usesbuildAdaptiveChatGptContext), and post-answer candidate queueing (requires capture gate to write).adaptive_turninlib/services/pandora-mcp-tools.tsand registered inlib/services/pandora-mcp-server.tswith the same shared logic and MCP capture gating (PANDORA_ENABLE_MCP_CAPTURErequired to write candidates via MCP).public/pandora-memory-openapi.json) and docs (docs/pandora-memory-autopilot.md,docs/pandora-chatgpt-adaptive-instructions.md) to document purpose, env flags, safety model, and recommended rollout posture.tests/phase5-memory-autopilot.test.tscovering policy, endpoint gate behavior, queue-only persistence, namespace separation, secrets handling, and MCP gating; preserved existing Phase 4C/other unit tests.Testing
npm run typecheckwhich completed successfully with no type errors.npm run lintwhich passed but surfaced unrelated pre-existing warnings; no new lint errors were introduced.npm run testand the full test suite passed including the newtests/phase5-memory-autopilot.test.ts(all tests green).npm run buildand produced a successful Next.js build (build completed with informational warnings about Edge runtime and existing project telemetry, but no errors).Codex Task
Summary by CodeRabbit
New Features
Bug Fixes
Documentation