Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
541 changes: 541 additions & 0 deletions AGENTS.md

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Goal: ship a professional open-source customer messaging platform with strong de
- [ ] allow customising the agent's system prompt?
- [p] a CI AI agent to check for any doc drift and update docs based on the latest code
- [ ] convert supportAttachments.finalizeUpload into an action + internal mutation pipeline so we can add real signature checks too. The current finalizeUpload boundary is a Convex mutation and ctx.storage.get() is only available in actions. Doing true magic-byte validation would need a larger refactor of that finalize flow.
- [ ] add URL param deep links for the widget - Go to a url like ?open-widget-tab=home to open the widget to that tab, etc.


apps/web/src/app/outbound/[id]/OutboundTriggerPanel.tsx
Expand Down Expand Up @@ -210,3 +211,80 @@ Legend: `[x]` complete, `[~]` present but still maturing, `[ ]` not yet implemen
- [ ] Should OTP be the only default path for new users, with password optional?
- [ ] How strict should hosted-tier limits be for email campaigns and push usage?
- [ ] What is required for Production Readiness, vs nice-to-haves?




I followed the AGENTS.md memory/instructions (focused, repo-scoped audit with PNPM-era conventions) and used jcodemunch + repo grep to map this.

1) Manual makeFunctionReference hotspots

RAG-follow-up critical

packages/convex/convex/aiAgentActionsKnowledge.ts:45 (GET_CONTENT_BY_ID_REF)
packages/convex/convex/aiAgentActionsKnowledge.ts:54 (GET_EMBEDDING_BY_ID_REF)
packages/convex/convex/aiAgentActions.ts:162 (GET_RELEVANT_KNOWLEDGE_FOR_RUNTIME_ACTION_REF)
packages/convex/convex/embeddings.ts:172 (GENERATE_BATCH_INTERNAL_REF)
packages/convex/convex/embeddings.ts:137 (LIST_BY_CONTENT_REF) and adjacent refs used in embedding pipeline
Broader repo hotspots (same pattern)

packages/convex/convex/notifications/functionRefs.ts
packages/convex/convex/push/functionRefs.ts
packages/convex/convex/series/scheduler.ts
packages/convex/convex/pushCampaigns.ts
packages/convex/convex/testing/helpers/notifications.ts
packages/convex/convex/emailChannel.ts
packages/convex/convex/embeddings/functionRefs.ts
packages/convex/convex/carousels/triggering.ts
packages/convex/convex/events.ts
packages/convex/convex/http.ts
packages/convex/convex/outboundMessages.ts
packages/convex/convex/snippets.ts
packages/convex/convex/testAdmin.ts
packages/convex/convex/visitors/mutations.ts
packages/convex/convex/widgetSessions.ts
packages/convex/convex/workspaceMembers.ts
packages/convex/convex/tickets.ts
2) as unknown as reduction targets

Immediate (RAG path)

packages/convex/convex/aiAgentActionsKnowledge.ts:39
packages/convex/convex/aiAgentActionsKnowledge.ts:49
packages/convex/convex/aiAgentActionsKnowledge.ts:58
packages/convex/convex/aiAgentActions.ts:123
packages/convex/convex/aiAgentActions.ts:155
packages/convex/convex/aiAgentActions.ts:172
packages/convex/convex/aiAgentActions.ts:189
packages/convex/convex/embeddings.ts:30
packages/convex/convex/embeddings.ts:37
packages/convex/convex/embeddings.ts:44
packages/convex/convex/embeddings.ts:123
packages/convex/convex/embeddings.ts:176
Full broader set

Same file list as section 1 (19 Convex files matched for makeFunctionReference ... as unknown as).
3) Batching/perf refactor sites

packages/convex/convex/embeddings.ts:371 (generateBatch loops serially over args.items)
packages/convex/convex/embeddings.ts:465 (backfillExisting runs batch chunks sequentially)
packages/convex/convex/embeddings.ts:509 (generateBatchInternal loops serially over args.items)
These are the concrete places to introduce concurrency-limited parallelism / true batched embedding work.

4) SDK route migration sites (getRelevantKnowledge old query path)

Source

packages/sdk-core/src/api/aiAgent.ts:12 (aiAgent:getRelevantKnowledge ref)
packages/sdk-core/src/api/aiAgent.ts:76 (getRelevantKnowledge exported function)
packages/sdk-core/src/api/aiAgent.ts:83 (client.query(GET_RELEVANT_KNOWLEDGE_REF, ...))
Tests/contracts expecting old route

packages/sdk-core/tests/contracts.test.ts:478
packages/sdk-core/tests/api.test.ts:56
packages/sdk-core/tests/api.test.ts:150
Backend fallback currently kept for compatibility

packages/convex/convex/aiAgent.ts:318 (getRelevantKnowledge public query)
If you want, I can turn this into a prioritized migration checklist (P0/P1/P2) with exact replacement strategy per file.

2 changes: 1 addition & 1 deletion apps/widget/src/components/conversationView/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export function ConversationFooter({
type="button"
>
<span className="opencom-suggestion-title">{suggestion.title}</span>
<span className="opencom-suggestion-snippet">{suggestion.snippet}</span>
{/* <span className="opencom-suggestion-snippet">{suggestion.snippet}</span> */}
</button>
))}
</div>
Expand Down
15 changes: 15 additions & 0 deletions opencode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"jcodemunch-mcp": {
"type":"local",
"command": ["uvx", "jcodemunch-mcp"],
"enabled": true
},
"jdocmunch-mcp": {
"type":"local",
"command": ["uvx", "jdocmunch-mcp"],
"enabled": true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-12
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Context

Currently, the `aiAgent.ts` inside the `packages/convex` workspace relies on an in-memory string-matching approach (`calculateRelevanceScore`) to find relevant articles and snippets for a given query. It loads all articles and snippets in a workspace and loops through them, which is a bottleneck for larger workspaces.

However, we already have a `contentEmbeddings` table and `embeddings:generateBatch` in the backend which creates embeddings for articles and snippets using `text-embedding-3-small` and stores them. The widget and suggestions are already using `ctx.vectorSearch` against `contentEmbeddings`. The AI agent should use this same infrastructure.

## Goals / Non-Goals

**Goals:**
- Replace the inefficient in-memory string-matching search with Convex Vector similarity search (`ctx.vectorSearch`).
- Refactor `getRelevantKnowledgeForRuntime` to use the new vector search method.
- Keep the system prompt and the number of returned results (limit: 5) the same to preserve the current AI behavior.
- Reduce latency for AI responses.

**Non-Goals:**
- We are not changing the AI models used (e.g., OpenAI/Anthropic).
- We are not changing how the UI renders responses.
- We are not modifying the fallback human handoff logic.

## Decisions

1. **Use Convex Vector Search**: We will update the AI agent to query the vector search index `by_embedding` on the `contentEmbeddings` table, similar to how `searchForWidget` in `suggestions.ts` works.
2. **Move embedding generation logic to AI Action**: Since vector search requires embedding the query first, we will change `getRelevantKnowledgeForRuntime` from an `internalQuery` to an `internalAction` so it can call the `embed` AI action before running the vector search query.

## Risks / Trade-offs

- **Risk: Embedding Latency.** Generating an embedding for the query adds a small latency (via OpenAI API call), but it will be much faster than loading all articles and string matching in memory.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Why

Currently, the AI agent retrieves knowledge by loading all articles in a workspace into memory and performing a basic string-matching relevance score (`calculateRelevanceScore`) on every query. While it doesn't pass the entire knowledge base into the prompt (it limits to the top 5 results), the process of loading and scoring every article in memory on every request is highly inefficient and causes significant latency, especially as the knowledge base grows. We need a proper Vector RAG solution to improve response times and relevance.

## What Changes

- Refactor `aiAgentActions.ts` and `aiAgent.ts` to utilize the existing `contentEmbeddings` table and Convex Vector Search instead of the legacy in-memory matching.
- Remove the legacy string-matching algorithms.

## Capabilities

### Modified Capabilities
- `ai-agent-knowledge-retrieval`: Changing the underlying retrieval mechanism from in-memory string matching to vector search.

## Impact

- **Convex Backend**: `packages/convex/convex/aiAgent.ts` and `packages/convex/convex/aiAgentActions.ts` will be updated to use Convex Vector Search.
- **Performance**: Significant reduction in AI agent response latency.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## MODIFIED Requirements

### Requirement: AI Agent retrieves knowledge context
The system SHALL retrieve relevant knowledge context to answer customer questions using vector similarity search.

#### Scenario: AI Agent answers a question
- **WHEN** a customer asks a question
- **THEN** the AI Agent retrieves the top 5 most semantically similar knowledge items (articles/snippets) using vector search and uses them as context to generate an answer

## REMOVED Requirements

### Requirement: In-memory string matching for relevance score
**Reason**: Replaced by vector similarity search for improved performance and scalability.
**Migration**: Use the new vector search capability provided natively by Convex (`ctx.vectorSearch`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## 1. Setup Vector Search for AI Agent

- [x] 1.1 Update `getRelevantKnowledgeForRuntime` to be an internal action (or handle embedding in `aiAgentActions.ts`) since it needs to call OpenAI `embed`.
- [x] 1.2 Implement Convex vector search logic in `aiAgentActions.ts` or `aiAgent.ts` querying the `contentEmbeddings` table `by_embedding` index.
- [x] 1.3 Ensure the results are returned in the exact format expected by the `buildSystemPrompt` function.

## 2. Cleanup Legacy Matching

- [x] 2.1 Remove the `calculateRelevanceScore` function and associated `escapeRegex` from `packages/convex/convex/aiAgent.ts`.
- [x] 2.2 Remove the legacy `collectRelevantKnowledge` string matching loop logic.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-13
69 changes: 69 additions & 0 deletions openspec/changes/harden-convex-function-references/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Context

The repo has already completed several earlier Convex ref-hardening slices, but the current backend inventory still contains residual `makeFunctionReference(...)` helpers that accept `name: string` and a small set of repeated `as unknown as` runner/ref casts. The remaining work is concentrated in RAG-critical files (`aiAgent.ts`, `aiAgentActions.ts`, `aiAgentActionsKnowledge.ts`, `embeddings.ts`), shared helper modules (`notifications/functionRefs.ts`, `push/functionRefs.ts`, `embeddings/functionRefs.ts`), and a set of domain-specific runtime files such as `http.ts`, `tickets.ts`, `emailChannel.ts`, and `series/scheduler.ts`.

The active constraints are already documented in `docs/convex-type-safety-playbook.md`: generated refs remain the default, shallow runner helpers are the first `TS2589` escape hatch, fixed `makeFunctionReference("module:function")` refs are allowed only for confirmed hotspots, and new generic string-ref factories are not allowed. The repo also already finished two adjacent slices that this design must not reopen by default: sdk-core `getRelevantKnowledge` now routes through an action, and embedding batch/backfill flows already use concurrency helpers.

## Goals / Non-Goals

**Goals:**

- Remove remaining generic `name: string` Convex ref helper patterns from the covered backend inventory.
- Keep any unavoidable `TS2589` workaround localized to fixed refs or named shallow runner helpers.
- Preserve existing runtime behavior while tightening the boundary shapes in `packages/convex`.
- Update guardrails and ownership records so the remaining accepted exceptions are explicit and minimal.

**Non-Goals:**

- Changing AI retrieval semantics, ranking, or public API behavior.
- Reopening sdk-core route migration work that is already verified.
- Reworking embedding throughput or batching behavior beyond boundary cleanup.
- Removing intentionally dynamic admin/test dispatch unless requirements explicitly change.

## Decisions

### Use explicit module-scope refs instead of reusable `name: string` factories

Covered backend files will replace generic helpers such as `makeInternalQueryRef(name)` with explicit named refs per target. For shared clusters that already centralize refs, the module stays shared, but it exports only fixed refs and any required named runner helpers.

- **Why:** this matches the playbook, makes each remaining `TS2589` workaround auditable, and prevents the helper surface from silently expanding.
- **Alternative considered:** keep the current helper factories and document them as accepted legacy. Rejected because it preserves the broad pattern the repo is trying to eliminate.

### Keep shallow runner casts only where generated refs still force them

The implementation will continue to use named `getShallowRunQuery`, `getShallowRunMutation`, `getShallowRunAction`, or `getShallowRunAfter` helpers only at confirmed hotspot boundaries. Inline runner casts or repeated double-cast patterns inside feature logic will not be expanded.

- **Why:** shallow helpers are the approved first-line `TS2589` workaround and are smaller than broad object casts.
- **Alternative considered:** switch every covered call site back to generated refs and raw `ctx.run*` invocations immediately. Rejected because some existing hotspots were introduced specifically to avoid deep-instantiation regressions.

### Treat RAG-critical files as the first implementation batch

`aiAgent.ts`, `aiAgentActions.ts`, `aiAgentActionsKnowledge.ts`, and `embeddings.ts` form the highest-value cleanup cluster because they still contain the most visible residual helpers and are adjacent to already-finished AI route and embedding performance work. Shared AI/embedding ref modules may be introduced or expanded if that reduces duplication without reintroducing a generic selector helper.

- **Why:** this cluster closes the remaining audit items around the active AI runtime path first and reduces duplicate helper patterns before wider backend cleanup.
- **Alternative considered:** start with lower-risk leaf files such as `widgetSessions.ts` and `workspaceMembers.ts`. Rejected because it leaves the largest remaining hotspot cluster unresolved.

### Update guardrails with each file-cluster migration

Guard tests will move in lockstep with each cleanup batch so the approved inventory reflects the new steady state. Positive assertions that currently depend on helper names or legacy hotspot lists will be rewritten to validate the new fixed-ref or exception shape.

- **Why:** the repo already uses guard tests as the enforcement mechanism for these boundaries, so the inventory cannot lag behind implementation.
- **Alternative considered:** defer all guard updates to the end. Rejected because intermediate batches would either fail verification or leave the wrong exceptions approved.

## Risks / Trade-offs

- **[TS2589 resurfaces in previously stable files]** -> Migrate in small clusters and rerun `pnpm --filter @opencom/convex typecheck` after each cluster before broadening the slice.
- **[Boundary cleanup accidentally changes runtime targets or argument shapes]** -> Preserve existing ref strings and call signatures exactly, then cover the touched cluster with focused tests.
- **[Guard tests become too coupled to temporary implementation details]** -> Prefer assertions about approved patterns and explicit exception inventory rather than helper naming alone.
- **[Scope expands back into already-finished sdk-core or performance work]** -> Keep proposal/tasks explicit that route migration and embedding concurrency items are already satisfied unless new evidence appears.

## Migration Plan

1. Migrate the RAG-critical cluster and update `packages/convex/tests/runtimeTypeHardeningGuard.test.ts` to match the new boundary shape.
2. Migrate shared helper modules and adjacent domain-specific runtime files in verification-gated micro-batches.
3. Preserve explicitly approved dynamic exceptions, validate the change with focused Convex checks, and then continue into implementation from the generated task list.

## Open Questions

- Should `supportAttachmentFunctionRefs.ts` remain a tiny fixed-ref module or be folded into the caller once the residual cast is removed?
- Which shared helper clusters still truly require shallow runner helpers after fixed refs replace the remaining generic factories?
30 changes: 30 additions & 0 deletions openspec/changes/harden-convex-function-references/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## Why

Residual Convex type-hardening work still leaves manual string-based function-ref helpers and repeated `as unknown as` escape hatches in backend hotspots, especially around AI knowledge retrieval and shared helper modules. A focused follow-up is needed now so the remaining exceptions match the repo playbook, stay explicitly guard-railed, and do not expand again.

## What Changes

- Replace remaining generic `name: string` `makeFunctionReference(...)` helpers in covered `packages/convex/convex/**` hotspots with fixed module-scope refs or narrowly scoped shared ref modules.
- Reduce remaining repeated `as unknown as` ref and runner casts in runtime-critical Convex files by keeping unavoidable `TS2589` workarounds behind named local helpers only.
- Close the residual backend inventory across RAG-critical files, shared helper modules, and domain-specific runtime boundaries, while preserving accepted dynamic exceptions such as `testAdmin.ts` unless requirements change.
- Update guardrails and change ownership records so the repo-wide hardening inventory reflects the remaining covered files and the new steady-state exception list.
- Keep already completed work, including sdk-core `getRelevantKnowledge` action routing and embedding batching concurrency, out of scope except where guard or proposal text must acknowledge it as already satisfied.

## Capabilities

### New Capabilities

- None.

### Modified Capabilities

- `convex-function-ref-boundaries`: extend the covered backend inventory to the remaining shared helper modules and runtime hotspots that still rely on generic string-based ref helpers.
- `runtime-type-safety-hardening`: tighten the remaining runtime-critical Convex boundaries so ref and runner casts stay localized, named, and minimal.
- `cross-surface-convex-ref-boundary-hardening`: record this change as the owner for the remaining backend inventory and anti-regression guard updates, while keeping approved exceptions explicit.

## Impact

- Affected code: `packages/convex/convex/aiAgent.ts`, `packages/convex/convex/aiAgentActions.ts`, `packages/convex/convex/aiAgentActionsKnowledge.ts`, `packages/convex/convex/embeddings.ts`, `packages/convex/convex/notifications/functionRefs.ts`, `packages/convex/convex/push/functionRefs.ts`, `packages/convex/convex/embeddings/functionRefs.ts`, `packages/convex/convex/http.ts`, `packages/convex/convex/tickets.ts`, and the remaining runtime helper files identified by the repo audit.
- Guardrails: `packages/convex/tests/runtimeTypeHardeningGuard.test.ts`, plus any supporting spec deltas and inventory notes that define approved exceptions.
- Docs/process: `docs/convex-type-safety-playbook.md` may need minor follow-up if the accepted hotspot inventory changes.
- Runtime/API impact: no intended product behavior change; this is a hardening and maintainability cleanup of Convex call boundaries.
Loading
Loading