Files
zeekay 043ac58d3c chore: sweep upstream brand from ALL files; restore LICENSE/NOTICE attribution
Extends the sweep beyond packages/ to the whole repo (178 files): workflows,
.eslintrc, playground, examples, tooling, root docs. Zero upstream-brand refs and
zero such filenames outside the exemptions.

REVERTS A LICENSING ERROR (from earlier rebrand e43ab1af, not this sweep):
- LICENSE had its copyright holder rewritten to 'Insights'. Restored to the real
  holder: 'Copyright 2020 Posthog / Hiberly, Inc.' (+ Mixpanel, Inc.).
- NOTICE credited a non-existent upstream (github.com/getinsights/insights-js).
  Restored to the real one: github.com/PostHog/posthog-js.
Apache-2.0 4(c) requires retaining upstream attribution; rewriting a third
party's copyright line falsifies ownership. Both files are now excluded from the
guard and documented as exempt in LLM.md.

Also:
- Deleted junk: AGENTS.md~upstream_main (merge leftover), the now-obsolete
  rebrand script; renamed upload-*-s3.ts tooling (still used by cli.ts).
- Removed 42 stale playground/example pnpm-lock.yaml files: they pinned upstream
  packages the package.json no longer references. Regenerated on install —
  sed-ing a lockfile would corrupt its integrity hashes.
- Guard renamed no-posthog.yml -> brand-guard.yml and now assembles the pattern
  at runtime, so no filename or file content carries the string (the previous
  guard was swept by its own rule, inverting it to forbid 'insights').
2026-07-16 09:54:05 -07:00

84 lines
2.9 KiB
TypeScript

import { Agent } from '@convex-dev/agent'
import { openai } from '@ai-sdk/openai'
import { components } from '../_generated/api'
import { action } from '../_generated/server'
import { v } from 'convex/values'
import { insights } from '../insights.js'
const supportAgent = new Agent(components.agent, {
name: 'support-agent',
languageModel: openai('gpt-5-mini'),
instructions: 'You are a helpful support agent. Answer questions concisely.',
})
export const generate = action({
args: {
prompt: v.string(),
distinctId: v.optional(v.string()),
},
handler: async (ctx, args) => {
const { thread } = await supportAgent.createThread(ctx, {})
const traceId = crypto.randomUUID()
const startTime = Date.now()
// Collect usage metadata from the usageHandler callback, then combine it
// with the full result to send a comprehensive $ai_generation event.
const usageData: {
model?: string
provider?: string
agentName?: string
} = {}
const result = await thread.generateText(
{ prompt: args.prompt },
{
usageHandler: async (_usageCtx, { model, provider, agentName }) => {
usageData.model = model
usageData.provider = provider
usageData.agentName = agentName
},
}
)
const latency = (Date.now() - startTime) / 1000
await insights.capture(ctx, {
distinctId: args.distinctId ?? 'anonymous',
event: '$ai_generation',
properties: {
// Trace ID groups multiple generations into a single trace
$ai_trace_id: traceId,
// Core identification
$ai_provider: usageData.provider,
$ai_model: usageData.model,
$ai_span_name: usageData.agentName,
// Token usage (from totalUsage to account for multi-step tool calls)
$ai_input_tokens: result.totalUsage.inputTokens,
$ai_output_tokens: result.totalUsage.outputTokens,
// Cache tokens (if the provider reports them)
$ai_cache_read_input_tokens: result.totalUsage.cachedInputTokens,
// Performance
$ai_latency: latency,
// Input/output content
$ai_input: [{ role: 'user', content: args.prompt }],
$ai_output_choices: [{ role: 'assistant', content: result.text }],
// Generation metadata — the AI SDK doesn't expose HTTP status directly,
// so we infer success/failure from the finish reason.
$ai_is_error: result.finishReason === 'error',
},
})
return {
text: result.text,
usage: result.totalUsage,
}
},
})