NewDayKnowledge

How this AI news platform was built, and how it works

Data Flow

What happens during one daily agent run

  1. launchd, or you manually, runs run-agent.sh, which sets PATH, loads .env, and calls npx tsx agents/news/run.ts.
  2. run.ts creates a NewsRunLog row in Postgres with status RUNNING, so the dashboard has something to show even if the run later fails partway.
  3. run.ts loads the AgentConfig row from Postgres: topics, minimum score, and max articles. If this is the very first run ever, it creates that row using the defaults from agents/news/config.ts.
  4. fetchFeeds.ts downloads each RSS feed URL and parses out title, link, publish date, and a short snippet for every article.
  5. dedupe.ts compares article titles to each other and drops near-duplicates.
  6. run.ts checks which of the remaining articles already exist in the Article table, matched by their source URL, and skips those so reruns do not waste AI time reprocessing old stories.
  7. classify.ts sends each new article to the AI model, asking for category, companies, keywords, an importance score, and sentiment, as JSON. If the AI's answer is not valid JSON, it is asked once more, more strictly.
  8. rank.ts filters out anything matching an excluded topic, filters out anything not matching an included topic when any are set, filters out anything below the minimum score, sorts by score, and keeps only the configured maximum number.
  9. generateContent.ts asks the AI model to write one Twitter/X post and one LinkedIn post for each surviving article, using the prompt templates currently stored in the PromptTemplate table.
  10. run.ts saves each article and its generated posts to Postgres, with GeneratedContent rows starting in DRAFT status.
  11. run.ts updates the NewsRunLog row with final counts and SUCCESS, or FAILED with the error message if something went wrong.

What happens when you use the dashboard

  1. Logging in: you submit your email and password on /login. NextAuth checks the email against ADMIN_EMAIL and the password against ADMIN_PASSWORD_HASH using bcrypt, and if they match, sets a signed session cookie in your browser. Every dashboard page and API route checks for a valid session before doing anything.
  2. Viewing Articles: the page calls GET /api/articles, which reads Article rows and their related GeneratedContent rows straight from Postgres and returns them as JSON, which the page then renders into a table.
  3. Reviewing drafts: the Review Queue page calls GET /api/generated-content, defaulting to status DRAFT, to show what needs a decision. Clicking Approve or Reject calls PATCH /api/generated-content/{id} with the new status, which updates that one row.
  4. Publishing: only shown for APPROVED posts. Clicking Publish calls POST /api/generated-content/{id}/publish, which looks up the real platform, Twitter or LinkedIn, and calls the matching function in lib/social/, using whatever credentials are currently in that environment's variables.
  5. If publishing succeeds, the row is updated to PUBLISHED with the real post's link. If it fails, including simply not being configured yet, the exact error is saved to that row and shown directly in the dashboard rather than a generic failure message.
  6. Editing Agent Config: the Config page loads the current AgentConfig row via GET /api/agent-config and saves changes via PUT to the same route. This does not affect anything until the next time the local agent runs and reads that row fresh.
  7. Editing Prompts: works the same way but never overwrites. Each save creates a new PromptTemplate row with an incremented version number, and the agent always reads whichever version is highest.