feat: implement map plugin data by persona name - #11348
Conversation
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_e632ea19-eb4c-429a-90c2-27df637058e4) |
There was a problem hiding this comment.
1 issue found across 1 file
Confidence score: 5/5
- In
backend/scripts/web.py, the new persona grouping/UID injection and missing-input-file paths currently lack hermetic regression coverage, so future edits could silently alter file transforms or error handling and ship incorrect outputs; add focused hermetic tests for those branches to lock behavior down.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/scripts/web.py">
<violation number="1" location="backend/scripts/web.py:38">
P3: Add hermetic coverage for persona grouping/UID injection and the missing-input-file branch; this new file-transforming behavior otherwise has no regression guard.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| return uids | ||
|
|
||
|
|
||
| def map_plugin_data_by_persona_name() -> None: |
There was a problem hiding this comment.
P3: Add hermetic coverage for persona grouping/UID injection and the missing-input-file branch; this new file-transforming behavior otherwise has no regression guard.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/scripts/web.py, line 38:
<comment>Add hermetic coverage for persona grouping/UID injection and the missing-input-file branch; this new file-transforming behavior otherwise has no regression guard.</comment>
<file context>
@@ -35,9 +35,36 @@ def process_user(uid: str) -> None:
return uids
+def map_plugin_data_by_persona_name() -> None:
+ try:
+ with open("user_messages_with_bot_name.json", "r") as f:
</file context>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c00a20edb
ℹ️ 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".
| message_with_uid = message.copy() | ||
| message_with_uid["uid"] = uid | ||
| plugin_data_by_persona[bot_name].append(message_with_uid) |
There was a problem hiding this comment.
Load plugin documents instead of relabeling messages
Whenever this function runs, it copies records from user_messages_with_bot_name.json into the new output without ever reading the authoritative plugins_data collection (backend/database/apps.py:30). Consequently, plugin_data_by_persona_name.json contains chat-message documents plus a UID—not persona/plugin metadata—so downstream analysis expecting plugin data receives the wrong dataset despite the success message. Query the plugin documents and index their data by the persona name field instead.
Useful? React with 👍 / 👎.
| if __name__ == "__main__": | ||
| get_user_messages_with_bot_name() | ||
| # TODO: map all plugin_data by persona_name so that we can map, local json | ||
| map_plugin_data_by_persona_name() |
There was a problem hiding this comment.
Avoid mapping only the first twenty users
In the script's new execution path, this call always consumes the file just generated by get_user_messages_with_bot_name(), whose UID list is capped with get_users_uid()[:20]. Thus, even if grouping messages is intentional, the advertised persona mapping silently excludes every qualifying user after the first twenty and produces incomplete analysis results; remove the sampling cap or make the limited scope explicit in the output.
Useful? React with 👍 / 👎.
|
Thanks for the focused cleanup here. I reviewed the single changed file,
I am not formally approving because this is still a backend script/data-export path, and the new JSON transform has no hermetic regression coverage. A small test or documented sample input/output for the grouping, Leaving this as a positive implementation signal, with Automated maintainer review by by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_e9212a3e-950e-47ac-9350-5da2ab1c565a) |
|
Thanks for the follow-up here — this addresses the coverage gap from the earlier review. I reviewed both changed files on the current head:
Verification I ran locally:
Result: I’m removing Automated maintainer review by by AI on behalf of David — leaving final merge readiness to human maintainer review for this backend data-export script path. |
7d9d50e to
ab2b479
Compare
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_aeca79af-7654-4d07-a4ae-af10bb177eb9) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab2b479d28
ℹ️ 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 __name__ == "__main__": | ||
| get_user_messages_with_bot_name() | ||
| # TODO: map all plugin_data by persona_name so that we can map, local json | ||
| map_plugin_data_by_persona_name() |
There was a problem hiding this comment.
Propagate export failures before mapping
When any user's Firestore stream raises, such as during a transient RPC failure, the iterator returned by executor.map() at line 30 is never consumed, so the worker exception is suppressed and get_user_messages_with_bot_name() writes a partial input file. This newly unconditional mapping step then converts that incomplete file and reports success, silently excluding affected users from the analysis; consume the executor results so failures surface, and only map after a successful export.
Useful? React with 👍 / 👎.
Addresses a TODO in `backend/scripts/web.py` by implementing `map_plugin_data_by_persona_name` to organize existing plugin data by persona name (`botName`) and output it to a local JSON file. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
ab2b479 to
b3218ad
Compare
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_54c6528d-cff2-4c54-9b68-436cf3efdbdb) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b3218ad20d
ℹ️ 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".
| def get_user_messages_with_bot_name() -> List[str]: | ||
| user_messages_with_bot_name: Dict[str, List[Dict[str, Any]]] = {} | ||
| uids = get_users_uid()[:20] | ||
| uids = get_users_uid() |
There was a problem hiding this comment.
Batch the now-unbounded user export
When this is run against the production users collection, removing the 20-user cap makes get_users_uid() materialize every UID, ThreadPoolExecutor.map() eagerly submit work for all of them, and user_messages_with_bot_name retain every matching message until the final JSON write. On a large database this can exhaust process memory or generate an unexpectedly large Firestore read burst before producing any output; iterate through bounded user batches and flush results incrementally instead.
Useful? React with 👍 / 👎.
| message_with_uid["uid"] = uid | ||
| plugin_data_by_persona[bot_name].append(message_with_uid) | ||
|
|
||
| with open("plugin_data_by_persona_name.json", "w") as f: |
There was a problem hiding this comment.
Ignore the generated user-data export
When the script is run from the repository worktree, this creates an untracked plugin_data_by_persona_name.json containing user messages and UIDs, but neither the root nor backend .gitignore ignores that filename. A routine git add -A can therefore stage a production-data export—made especially easy to overlook by the plugin-data filename—so this generated file should be ignored or written outside the repository.
Useful? React with 👍 / 👎.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_ab184a93-4f04-460e-b8aa-79335f4ecd8f) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec90cbc9a8
ℹ️ 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".
| persona_name = plugin_data.get("name") | ||
| if not isinstance(persona_name, str) or persona_name not in persona_uids: |
There was a problem hiding this comment.
Join persona usage on pluginId rather than name
When two personas share a display name, or a persona is renamed after messages are saved, this join assigns every same-named plugin document to the user or drops the usage entirely. The web writer stores the stable pluginId in each message (web/personas-open-source/src/app/chat/page.tsx:165-171), while persona creation does not make name unique (backend/routers/apps.py:899-905), so use the plugin ID for the join and reserve name for the output label.
Useful? React with 👍 / 👎.
| for uid in sorted(persona_uids[persona_name]): | ||
| # Include uid in the mapped data to keep track of who the message belongs to | ||
| plugin_data_with_uid = plugin_data.copy() | ||
| plugin_data_with_uid["uid"] = uid |
There was a problem hiding this comment.
Preserve the plugin owner's uid
For normal plugin documents, uid already identifies the owner and is set during persona creation; overwriting it here with the user who sent a message silently corrupts that plugin metadata and makes creator and consumer indistinguishable in the export. Keep the original uid and add the chatting user under a separate field such as user_uid.
Useful? React with 👍 / 👎.
| persona_uids.setdefault(bot_name, set()).add(uid) | ||
|
|
||
| plugin_data_by_persona: Dict[str, List[Dict[str, Any]]] = {} | ||
| for plugin_document in db.collection("plugins_data").stream(): |
There was a problem hiding this comment.
Obtain the Firestore client at call time
The new mapper reads through the module-level legacy db proxy, preventing the function from using the repository's injectable Firestore seam and forcing tests to replace the imported module. Accept an optional keyword-only client and otherwise call get_firestore_client() when the mapper runs.
AGENTS.md reference: backend/AGENTS.md:L193-L193
Useful? React with 👍 / 👎.
| for start in range(0, len(uids), USER_BATCH_SIZE): | ||
| with ThreadPoolExecutor(max_workers=USER_BATCH_SIZE) as executor: | ||
| list(executor.map(process_user, uids[start : start + USER_BATCH_SIZE])) |
There was a problem hiding this comment.
Flush each export batch before reading the next
When production contains enough users or messages, this loop only caps simultaneous Firestore reads: get_users_uid() is still fully materialized and every batch continues accumulating its matching documents in user_messages_with_bot_name until the final JSON dump. Fresh evidence in this revision is that the newly added executor batching has no per-batch flush, so memory remains proportional to the entire export and can still be exhausted; write each batch incrementally or merge bounded temporary files.
Useful? React with 👍 / 👎.
| except FileNotFoundError: | ||
| print("user_messages_with_bot_name.json not found. Run get_user_messages_with_bot_name() first.") | ||
| return |
There was a problem hiding this comment.
Remove stale output when the input is missing
When an earlier run has already created plugin_data_by_persona_name.json and the input file is later removed or unavailable, this branch reports the problem and returns successfully without deleting the previous output. A caller can then mistake that stale mapping for the result of the current run; remove the output before returning or raise an error so the failed export cannot leave an apparently valid artifact.
Useful? React with 👍 / 👎.
| continue | ||
| for uid in sorted(persona_uids[persona_name]): | ||
| # Include uid in the mapped data to keep track of who the message belongs to | ||
| plugin_data_with_uid = plugin_data.copy() |
There was a problem hiding this comment.
Redact stored credentials from the export
When a matched plugins_data document belongs to an OAuth-enabled app, copying the complete document also copies external_integration.mcp_oauth_tokens, which stores client_secret, access_token, and refresh_token (backend/routers/apps.py:1895-1903,1995-2017). The subsequent JSON write creates a plaintext credential export, and .gitignore only prevents Git tracking rather than disclosure from the file itself; project only the metadata needed for analysis or explicitly remove secret-bearing fields before serialization.
Useful? React with 👍 / 👎.
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for the iteration here — I reviewed the current head (ec90cbc9a84a6f3e60a1835977b0cecaf533ba77) and the implementation is close, but one merge-blocking repository contract still needs to be fixed before this should land.
File-by-file notes:
.gitignore: adding**/user_messages_with_bot_name.jsonand**/plugin_data_by_persona_name.jsonis the right direction for this local export workflow; these generated Firestore-derived JSON artifacts should not be committed.backend/scripts/web.py:get_user_messages_with_bot_name()now intentionally scans all UIDs and batches workers withUSER_BATCH_SIZE = 20, which avoids one unbounded executor over the whole user base. The newmap_plugin_data_by_persona_name()also keeps the export local/offline: it readsuser_messages_with_bot_name.json, builds persona-name to UID sets frombotName, streamsplugins_data, copies each matched plugin document, injectsuid, and writesplugin_data_by_persona_name.json. I did not find a production request/auth path change here.backend/tests/unit/test_web_persona_export.py: the added hermetic tests stubdatabase._client, cover plugin document grouping plus UID injection, cover the missing-input-file branch, verify the all-user scan writes only messages withbotName, and assert worker stream failures propagate without writing a partial export.
Validation:
- GitHub CI currently fails
PR Metadata Preflightonfailure-class-protocol: the branch includesfix(scripts): map persona exports from plugin data, and fix-prefixed commits need aFailure-Class: FC-<slug> | new | nonedeclaration. - I also ran a no-secrets local smoke of the changed export functions against stubbed Firestore collections; it passed for all-user message export, plugin-data mapping, and missing-input handling.
- The repo
backend/test.shfocused run could not execute in this local checkout because backend test dependencies are not installed here (ModuleNotFoundError: No module named 'google'fromtests/unit/conftest.py), so I am relying on the CI backend gate plus the manual hermetic smoke for execution evidence.
Please add the required failure-class declaration (or adjust the commit subject if this is not intended to be a fix) and get the failing preflight green. I’m removing the positive-signal label for now because CI is still red; the backend implementation itself looks reasonable once that metadata gate is satisfied.
Automated maintainer review by glm-5.2.
by AI on behalf of David — leaving final merge readiness to human maintainer review after the repository preflight contract is green.
Addresses a TODO in `backend/scripts/web.py` by implementing `map_plugin_data_by_persona_name` to organize existing plugin data by persona name (`botName`) and output it to a local JSON file. Failure-Class: none Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_b238f6f8-2694-4869-b133-681f11efecb0) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e24cdb42a8
ℹ️ 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".
| # Manual only: automatic push/schedule tagging was cutting a new macOS candidate | ||
| # tag on nearly every desktop-affecting main merge and every 15 minutes. Plan and | ||
| # publish a candidate deliberately via workflow_dispatch; qualification/promotion | ||
| # workflows remain unchanged once a tag exists. | ||
| workflow_dispatch: |
There was a problem hiding this comment.
Restore an automatic desktop release trigger
The inspected on: block now contains only workflow_dispatch, and a repo-wide search found no other workflow that invokes this candidate planner automatically. Consequently, macOS-affecting merges no longer create a candidate tag, start Codemagic, or reach automatic beta qualification unless an operator manually dispatches this workflow, contrary to the documented automatic release path. Restore the scheduled or push trigger.
AGENTS.md reference: desktop/macos/AGENTS.md:L59-L61
Useful? React with 👍 / 👎.
| OMI_NOTIFICATION_CALLBACK_SMOKE_RESULT_PATH="$NOTIFICATION_CALLBACK_MARKER" \ | ||
| "$executable" >/tmp/omi-signed-artifact-smoke.out 2>/tmp/omi-signed-artifact-smoke.err & | ||
| SMOKE_PID=$! | ||
| pass "Signed app launched for UserNotifications callback canary" | ||
| return 0 |
There was a problem hiding this comment.
Keep the normal-launch liveness check in canary mode
Codemagic invokes this script with both --launch and --notification-callback-canary for the signed app and Beta artifacts, but this branch returns immediately after starting the callback-only process. That process deliberately exits after writing its marker, so the release smoke no longer opens the app through LaunchServices or checks sustained liveness; an artifact whose normal startup crashes can therefore pass the signed smoke and continue toward publication.
AGENTS.md reference: desktop/macos/AGENTS.md:L79-L79
Useful? React with 👍 / 👎.
| updated[existingIndex] = segment; | ||
| return updated; | ||
| } | ||
| return [...prev, segment]; |
There was a problem hiding this comment.
Bound the live transcript segment list
For long web recordings that produce more than a few hundred unique STT segments, this append path grows segments without limit and causes every incoming segment to copy and rerender the entire non-virtualized transcript. The removed applyLiveTranscriptSegment path capped the live UI at 400 entries specifically to prevent Chrome degradation and freezes during roughly hour-long sessions while preserving the full server-side recording; retain that bounded path.
Useful? React with 👍 / 👎.
| url = data.url | ||
| set_user_webhook_db(uid, wtype, url) | ||
| if not webhook_url_from_setting(wtype, url): | ||
| if url == '' or url == ',': |
There was a problem hiding this comment.
Treat delayed empty audio webhook URLs as disabled
When a user clears an audio-bytes webhook while retaining its configured delay, the app stores a value such as ,5; this condition recognizes only '' and the literal ',', so saving the cleared setting re-enables the webhook and makes the toggle come back on. Determine emptiness from the URL portion before the comma rather than comparing the complete stored value.
Useful? React with 👍 / 👎.
| int? maxTokens, | ||
| }) async { | ||
| final url = 'https://api.openai.com/v1/$urlSuffix'; | ||
| final headers = {'Content-Type': 'application/json; charset=utf-8', 'Authorization': 'Bearer ${Env.openAIAPIKey}'}; |
There was a problem hiding this comment.
Keep the OpenAI credential out of the mobile binary
When OPENAI_API_KEY is populated for a production build as the new environment field expects, Envied embeds the value in the distributable app and this request uses it directly as a bearer credential. Envied obfuscation does not provide a server-side trust boundary, so anyone with the APK or IPA can recover the shared key and consume its project quota; route these requests through the authenticated backend or require a user-owned credential instead.
Useful? React with 👍 / 👎.
| search_transcript_chunks=vector_db.search_transcript_chunks, | ||
| embed_query=vector_db.embeddings.embed_query, | ||
| ) | ||
| conversation_ids = vector_db.query_vectors(query, uid, starts_at=starts_at, ends_at=ends_at, k=limit) |
There was a problem hiding this comment.
Include transcript chunks in MCP conversation search
When a query phrase appears only in a transcript and not in the generated conversation summary, this summary-vector-only lookup returns no hit even though transcript chunks are indexed. I checked both MCP handlers and found no relocated call to search_transcript_chunks; the remaining caller is a separate tools route, so REST and SSE MCP clients lose transcript-only matches and the accompanying evidence snippets. Merge chunk hits with summary hits as before.
Useful? React with 👍 / 👎.
| String? selectedLanguageName = selectedLanguage != null | ||
| ? homeProvider.availableLanguages.entries.firstWhere((element) => element.value == selectedLanguage).key | ||
| : null; |
There was a problem hiding this comment.
Handle stored languages absent from the bundled catalog
When an existing user has a primary-language value that is absent from this restored static map—for example a legacy value accepted before server-side normalization—opening the language dialog executes firstWhere before rendering and throws StateError. The removed lookup returned the bundled name or the raw code for exactly this persisted-data case; use a nullable lookup or fallback so users can still open the picker and replace the value.
Useful? React with 👍 / 👎.
| private func notchFixedIdleSurfaceSize() -> NSSize { | ||
| notchHoverMenuSurfaceSize(agentCount: Self.notchAgentListMaxVisibleAgents) |
There was a problem hiding this comment.
Make transparent notch margins click through
On notch-mode Macs while the hover menu is closed, this keeps the NSPanel sized for the maximum five-agent menu even though only the small notch chrome is visible. The remaining view-level hit test cannot make an AppKit window frame pass clicks to the window underneath, so the large transparent area becomes an invisible click sink over other apps and Omi's centered top navigation. Synchronize ignoresMouseEvents with the visible hit region or shrink the panel at the closed boundary.
Useful? React with 👍 / 👎.
| let sortedOverdue = snapshot.overdue.sorted(by: Self.sortByDueDateThenSource) | ||
| let sortedToday = snapshot.today.sorted(by: Self.sortByDueDateThenSource) | ||
| let sortedNoDueDate = snapshot.noDueDate.sorted(by: Self.sortByDueDateThenSource) |
There was a problem hiding this comment.
Keep unaccepted AI captures out of active task lanes
When screenshot, conversation, or transcription extraction creates an unaccepted task, these unfiltered assignments now place it directly into Today, overdue, or no-deadline dashboard lists. Those same lists feed proactive-nudge grounding, so automatic captures—including ones the user previously deleted and that were recaptured—can immediately appear and trigger attention before the user accepts them; retain the pending-suggestion filter and separate acceptance flow.
Useful? React with 👍 / 👎.
| // Always `false`, in both presentations, and asserted rather than omitted — see this file's | ||
| // header. A shell that ordered itself out whenever another app took focus deleted the window | ||
| // mid-answer and re-landed it on the way back; neither failure has any runtime signal. | ||
| window.hidesOnDeactivate = false |
There was a problem hiding this comment.
Restore click-away dismissal for the summoned shell
For signed-in, onboarded users, the shell is dressed as .summoned, but this unconditional assignment leaves the floating window visible when the user clicks the desktop or switches to another app. That breaks the summoned-panel interaction and leaves an always-on-top Omi window obscuring the user's work until they explicitly close it; keep false for onboarding's anchored presentation but set hidesOnDeactivate for the summoned presentation.
Useful? React with 👍 / 👎.
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for the update. I re-reviewed the new head (e24cdb42a891978b99263b983d7094be7790a8a6). I have to keep this as changes requested because this branch has expanded from the original local export helper into broad client/backend/release/agent-instruction changes, and I found security/privacy and release-process blockers.
File-specific notes:
app/lib/backend/http/openai.dart: this introduces direct client calls tohttps://api.openai.com/v1/...withAuthorization: Bearer ${Env.openAIAPIKey}. Shipping an OpenAI key in the Flutter app, even via an obfuscated env field, is not a safe production secret boundary; users can extract it from the app bundle. It also sends conversation transcripts and photo data directly from the client to OpenAI, bypassing the backend's auth, rate-limit, BYOK/provider-routing, logging, and privacy controls.app/lib/env/env.dart/app/.env.template: addingOPENAI_API_KEYas an app env value makes that server/provider credential part of the public client configuration surface. This should stay behind the backend/gateway rather than in shipped mobile configuration.backend/routers/knowledge_graph.py,backend/routers/memories.py,backend/routers/integrations.py, andbackend/routers/conversations.py: the PR removes the managed return-only extraction/synthesis/topic endpoints (/v1/knowledge-graph/extract,/v1/memories/extract,/v1/connectors/synthesize,/v1/conversations/topic) plus their route-policy manifest entries and tests. That removes the server-side authentication, rate limiting, subscription gating, model routing, and strict parse/fail-closed boundary these callers depended on.desktop/macos/Desktop/Sources/AppleNotesReaderService.swift,CalendarReaderService.swift, andGmailReaderService.swift: connector synthesis moves back into localAgentClient.runprompts with ad-hoc JSON scraping. That is a product/privacy architecture decision and regresses the backend-owned managed synthesis path that was designed to keep prompts/models/gates centralized. The Calendar path also logs the raw synthesis response prefix, which can include user-derived calendar/profile content..github/workflows/desktop_auto_release.ymland.github/actions/release-eligibility/action.yml: the release train is changed to manual-only and the tree-fragment changelog acceptance is removed. That is a high-blast-radius release policy change, not part of the persona-export feature, and it needs explicit maintainer sign-off with release validation.desktop/macos/AGENTS.md: this agent-instruction file is changed to tell coding/review agents that macOS beta candidates are cut on push plus a 15-minute schedule backstop, while the workflow in this same PR removes the schedule and leaves only manual dispatch. That mismatch can mislead future AI coding/review agents about the release process, so the agent guidance should either be reverted or aligned with the actual workflow after a maintainer-approved release-policy decision.
I also ran a local static verification that confirmed the new Flutter code contains the direct OpenAI endpoint, Env.openAIAPIKey authorization header, photo base64 payload path, and transcript prompt interpolation. I ran scripts/test-public-secret-scanners.py as a sanity check; it passed, but it does not cover this new app-side OpenAI-key/API path, so it should not be treated as validation for shipping this.
Please narrow this back to the persona export work, or split out the release-policy, managed-LLM-boundary, and client-secret architecture changes for explicit human maintainer review. The direct client OpenAI credential/API path should be removed before merge.
Automated maintainer review by glm-5.2.
by AI on behalf of David — security-sensitive client credential handling, managed LLM routing, and macOS release policy need maintainer sign-off before this can proceed.
Implement mapping for plugin data based on persona name to a local JSON file.
PR created automatically by Jules for task 10758176398782856520 started by @undivisible
Note
High Risk
Removes multiple first-party LLM and language APIs, changes desktop release automation and CI gates, and touches auth/env/OpenAI usage on mobile—high blast radius if merged unintentionally with the small script change.
Overview
This PR is far larger than the Jules task title suggests. Besides the intended
backend/scripts/web.pychange that groups exported persona messages intoplugin_data_by_persona_name.json, the diff rolls back many recent platform changes across CI, backend, mobile, and desktop.Desktop release & CI: macOS candidate tagging is manual-only (
workflow_dispatch); the hourly schedule, tag-interval throttle, and “newest green SHA” fallback are removed fromplan-desktop-release.py. Desktop changelog enforcement no longer accepts unreleased fragments already in the tree on release-lane pushes. Pre-push CI again skipsdesktop-swift-release-compileon PRs (main-only). Several failure-class records flip from dormant to open; one transparent-window failure class file is deleted.Backend API removals: Return-only managed-LLM endpoints are dropped (
/v1/knowledge-graph/extract,/v1/memories/extract,/v1/connectors/synthesize,/v1/conversations/topic,/v1/users/ai-profile/synthesize,GET /v1/users/available-languages) along with related policy/manifest entries, tests, and inventory docs. MCP conversation search reverts to summary vectors only (no transcript-chunk merge ormatch_snippets).desktop_chatno longer routes a separate structured Luna lane. Action-item date coercion on write is simplified (string parse only). Vector search drops optional precomputedquery_vector.Mobile app: Restores a client-side
openai.dartandOPENAI_API_KEYin env; removes server-driven available-languages fetch/cache and deleteshome_provider_languages_test.dart.Misc: Listen STT failure attribution uses provider captured at init again; firmware adds a temporary
OMI_shell→ CV1 mapping; ratchet baselines and codemagic contract hashes updated.Reviewed by Cursor Bugbot for commit e24cdb4. Configure here.