Author SHA1 Message Date
asepharyana 401155b501 fix: auto-fix code quality [skip ci] 2026-09-23 22:11:00 +07:00
dependabot[bot] 771783d991 build(deps-dev): bump tsx in /services/backend in the development group
Bumps the development group in /services/backend with 1 update: [tsx](https://github.com/privatenumber/tsx).


Updates `tsx` from 4.23.13 to 4.23.15
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.13...v4.23.15)

---
updated-dependencies:
- dependency-name: tsx
  dependency-version: 4.23.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-23 22:11:00 +07:00
dependabot[bot] 7ad6487050 build(deps): bump openai
Bumps the production group in /services/discord-gateway with 1 update: [openai](https://github.com/openai/openai-node).

Updates `openai` from 7.19.0 to 7.20.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.19.0...v7.20.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 7.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-23 22:11:00 +07:00
asepharyana a13884d80f docs: remove voice/recording/media references from AGENTS/ARCHITECTURE/README 2026-09-23 21:25:03 +07:00
asepharyana 2c69c51fba chore: drop pnpm-install-generated junk pnpm-workspace.yaml (backend) 2026-09-23 19:45:25 +07:00
asepharyana ce784f8305 refactor: remove voice/recording/media features from frontend + prune lockfiles
- Delete pages: (dashboard)/{recordings,voice,media}/ incl. view.tsx
- Delete components/{voice,media}, hooks/{use-voice,use-recordings,use-media},
  lib/audio/ (mic-transmit, pcm-player, wav), api/{voice,recordings,media},
  types/{voice,recording,media}, lib/hash.ts
- Cut nav tiles (Active Voice Stages, Voice Recording Archive) from dashboard,
  MiniPlayer from ambient-app, hooks/types barrel exports, WS voice/media events
- Re-home guilds + textChannels to messages oRPC router (DB-derived) so the
  messages page picker keeps working; guild-picker simplified to text-only
- Clean Channel/AppConfig types of voice remnants
- Delete infra/docker/recordings/ + 11 voice/recording/video spec docs
- pnpm install: prune direct voice deps from gateway + backend lockfiles
  (prism-media/opusscript remain only as transitive discord.js deps)
2026-09-23 19:45:14 +07:00
asepharyana 33013697e0 refactor: remove voice, recording, and music/media features (gateway + backend)
- Gateway: delete voice-recording/, voice-pcm-ws/, voice/video/media handlers,
  vendor/discord-voice-fork/, voice DB repos, 2 voice migrations
- Gateway: cut voice wiring from bootstrap/shutdown/commandHandler/handler-registry,
  eventBroadcaster/eventTypes, redis-channels, moderation-types, message-capture,
  config keys, and deps (@discordjs/voice, opus, libsodium, prism-media, davey)
- Backend: delete voice/, recordings/, media/ modules + @discordjs/voice dep
- Backend: cut voice/media/recordings oRPC routers, WS gateway-PCM auth + voice
  handlers, Redis bridge voice aggregation, redis-channels voice/media constants,
  config keys, moderation-types voice items, e2e voice/recordings suites
- Keep voice DB tables (destructive migration avoided); chatbot voiceRecordings
  tool + dashboard count remain as read-only historical data access
2026-09-23 19:31:06 +07:00
asepharyana b5b370e6eb fix: auto-fix code quality [skip ci] (#81) 2026-09-23 18:32:41 +07:00
asepharyana b9bba643bd feat(gateway): Jev (System One) as primary text moderator with LLM fallback (#80)
* feat(gateway): Jev (System One) as primary text moderator with LLM fallback

Add TypeSafe Jev via @typesafe-ai/sdk v0.6.0 as the PRIMARY analyzer for
text-only moderation sub-batches; the existing LLM stays as the fallback
for anything Jev cannot decide confidently (per-message gate rejection or
API failure) and for media batches.

- jevAnalyzer.ts: TypeSafeClient wrapper, declarative state builder
  (System One models MUST get factual state, not chat-XML — chat framing
  made Jev confidently wrong on clean messages at 0.98 confidence),
  message-id-keyed question builder (5 typed questions per message),
  cross-consistency acceptance gate (noul↔status↔severity↔action↔category),
  answer→AnalysisResult mapper, fail-open outcome.
- textBatchProcessor.ts: Jev-first per sub-batch, rejected ids + API
  failures fall back to callModerationLLM; no cross-batch pollution.
- config: AI_LLM_JEV_ENABLED/API_KEY/BASE_URL/MODEL/TIMEOUT_MS/MIN_CONFIDENCE.
- .env.example: documented all 6 Jev env vars.
- tests: unit (question/state builders, gate, mapper) + live smoke
  (gated behind AI_LLM_JEV_SMOKE=1) verified 4/4 accepted vs real 9router.

* fix: auto-fix code quality [skip ci]
2026-09-23 17:51:38 +07:00
asepharyana 5ba891ecfb feat: remove outdated design documents for AeroNet Visualization, Nexus Capital Render, and Summit Cloud Migration Platform 2026-09-23 16:37:45 +07:00
asepharyana efca86c50e feat: add .planning directory to .gitignore 2026-09-23 16:27:13 +07:00
mytheclipsebotreview[bot] 23a7bde4a5 Auto-merge PR #77
build(deps-dev): bump @types/node from 26.6.1 to 26.6.2 in /services/frontend in the development group across 1 directory
2026-09-22 15:23:20 +00:00
mytheclipsebotreview[bot] c2d6e37ac4 Auto-merge PR #76
build(deps): bump the production group in /services/backend with 2 updates
2026-09-22 15:10:44 +00:00
mytheclipsebotreview[bot] e93d826199 Auto-merge PR #78
build(deps): bump the production group in /services/discord-gateway with 3 updates
2026-09-22 14:58:16 +00:00
dependabot[bot] 404ad2231d build(deps-dev): bump @types/node
Bumps the development group with 1 update in the /services/frontend directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 26.6.1 to 26.6.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-22 14:40:27 +00:00
mytheclipsebotreview[bot] ef476f3ce7 Auto-merge PR #75
build(deps): bump @orpc/client from 1.15.1 to 1.15.2 in /services/frontend in the production group
2026-09-22 14:38:01 +00:00
mytheclipsebotreview[bot] 6ee1af41c0 Auto-merge PR #79
build(deps-dev): bump @types/node from 26.6.1 to 26.6.2 in /services/backend in the development group
2026-09-22 14:34:42 +00:00
dependabot[bot] e6cb514c3a build(deps-dev): bump @types/node
Bumps the development group in /services/backend with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 26.6.1 to 26.6.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-22 14:25:37 +00:00
dependabot[bot] 08e5a161f2 build(deps): bump the production group
Bumps the production group in /services/discord-gateway with 3 updates: [lru-cache](https://github.com/isaacs/node-lru-cache), [openai](https://github.com/openai/openai-node) and [p-limit](https://github.com/sindresorhus/p-limit).


Updates `lru-cache` from 11.5.2 to 11.5.3
- [Changelog](https://github.com/isaacs/node-lru-cache/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-lru-cache/compare/v11.5.2...v11.5.3)

Updates `openai` from 7.18.0 to 7.19.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.18.0...v7.19.0)

Updates `p-limit` from 7.3.2 to 7.3.3
- [Release notes](https://github.com/sindresorhus/p-limit/releases)
- [Commits](https://github.com/sindresorhus/p-limit/compare/v7.3.2...v7.3.3)

---
updated-dependencies:
- dependency-name: lru-cache
  dependency-version: 11.5.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: openai
  dependency-version: 7.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: p-limit
  dependency-version: 7.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-22 14:25:31 +00:00
dependabot[bot] bbf40774ef build(deps): bump the production group
Bumps the production group in /services/backend with 2 updates: [@orpc/server](https://github.com/middleapi/orpc/tree/HEAD/packages/server) and [dotenv](https://github.com/motdotla/dotenv).


Updates `@orpc/server` from 1.15.1 to 1.15.2
- [Release notes](https://github.com/middleapi/orpc/releases)
- [Commits](https://github.com/middleapi/orpc/commits/v1.15.2/packages/server)

Updates `dotenv` from 18.0.0 to 18.0.1
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v18.0.0...v18.0.1)

---
updated-dependencies:
- dependency-name: "@orpc/server"
  dependency-version: 1.15.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: dotenv
  dependency-version: 18.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-22 14:25:17 +00:00
dependabot[bot] f6d9cd2cc6 build(deps): bump @orpc/client
Bumps the production group in /services/frontend with 1 update: [@orpc/client](https://github.com/middleapi/orpc/tree/HEAD/packages/client).


Updates `@orpc/client` from 1.15.1 to 1.15.2
- [Release notes](https://github.com/middleapi/orpc/releases)
- [Commits](https://github.com/middleapi/orpc/commits/v1.15.2/packages/client)

---
updated-dependencies:
- dependency-name: "@orpc/client"
  dependency-version: 1.15.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-22 14:25:07 +00:00
mytheclipsebotreview[bot] 9104235f28 Auto-merge PR #72
build(deps-dev): bump the development group across 1 directory with 2 updates
2026-09-21 14:52:05 +00:00
mytheclipsebotreview[bot] 22d896c7eb Auto-merge PR #71
build(deps): bump dotenv from 17.4.2 to 18.0.0 in /services/discord-gateway
2026-09-21 14:51:56 +00:00
dependabot[bot] 2ccb5c07ce build(deps-dev): bump the development group across 1 directory with 2 updates
Bumps the development group with 2 updates in the /services/backend directory: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@biomejs/biome` from 2.5.13 to 2.5.14
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.14/packages/@biomejs/biome)

Updates `@types/node` from 26.5.1 to 26.6.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.6.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 14:50:59 +00:00
mytheclipsebotreview[bot] 8c7751b96b Auto-merge PR #73
build(deps-dev): bump the development group across 1 directory with 2 updates
2026-09-21 14:48:06 +00:00
mytheclipsebotreview[bot] c9ac155145 Auto-merge PR #74
build(deps): bump dotenv from 17.4.2 to 18.0.0 in /services/backend
2026-09-21 14:48:00 +00:00
dependabot[bot] 2a839e7b09 build(deps-dev): bump the development group across 1 directory with 2 updates
Bumps the development group with 2 updates in the /services/frontend directory: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@biomejs/biome` from 2.5.13 to 2.5.14
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.14/packages/@biomejs/biome)

Updates `@types/node` from 26.5.1 to 26.6.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.6.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 14:38:24 +00:00
mytheclipsebotreview[bot] d47221eb2a Auto-merge PR #69
build(deps): bump lucide-react from 1.46.0 to 1.47.0 in /services/frontend in the production group
2026-09-21 14:35:28 +00:00
mytheclipsebotreview[bot] ff7fbf3ad6 Auto-merge PR #70
build(deps): bump openai from 7.15.0 to 7.18.0 in /services/discord-gateway in the production group
2026-09-21 14:31:44 +00:00
dependabot[bot] 47d49e3395 build(deps): bump dotenv from 17.4.2 to 18.0.0 in /services/backend
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.4.2 to 18.0.0.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v17.4.2...v18.0.0)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 18.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 14:26:39 +00:00
dependabot[bot] f0f5100253 build(deps): bump dotenv in /services/discord-gateway
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.4.2 to 18.0.0.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v17.4.2...v18.0.0)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 18.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 14:26:22 +00:00
dependabot[bot] 09e4f9aa84 build(deps): bump openai
Bumps the production group in /services/discord-gateway with 1 update: [openai](https://github.com/openai/openai-node).


Updates `openai` from 7.15.0 to 7.18.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.15.0...v7.18.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 7.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 14:26:05 +00:00
dependabot[bot] c6d5b93d77 build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.47.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 14:25:50 +00:00
asepharyana d7aa034661 feat(frontend): staggered list reveals + toast tone accents (level 5 polish) 2026-09-20 14:57:18 +07:00
mytheclipsebotreview[bot] ae3b22df93 Auto-merge PR #68
build(deps-dev): bump vitest from 5.0.0 to 5.0.1 in /services/backend in the development group
2026-09-18 17:21:44 +00:00
mytheclipsebotreview[bot] 4f7e586ec3 Auto-merge PR #67
build(deps): bump @orpc/server from 1.15.0 to 1.15.1 in /services/backend in the production group
2026-09-18 17:21:35 +00:00
mytheclipsebotreview[bot] fc3c80d73d Auto-merge PR #66
build(deps): bump @orpc/client from 1.15.0 to 1.15.1 in /services/frontend in the production group
2026-09-18 17:15:20 +00:00
asepharyana 43e35a71dd test: add live-LLM E2E moderation test suite
Add tests/llmE2e.test.ts — 7 end-to-end tests driving the REAL
moderation prompt pipeline (buildSystemPrompt → XML payload → llmChat
→ parseModerationResponse) against a live model via omniroute.

Covers: clean technical content (no false positives), harassment
(flagged), username-only offenses including 'Pecinta Pria' +
sexual/provocative usernames + SARA-in-username (always warn/low,
NEVER delete — the nickname-reset path), and spam bursts.

Gated behind AI_LLM_BASE_URL + AI_LLM_API_KEY: CI (no creds) skips
the file → 216 unit tests stay green, zero LLM cost. Run locally via
pnpm test:e2e:live (scripts/run-llm-e2e.sh injects creds from bws).

Verified: 223/223 tests pass with live LLM, stability across 4 runs,
typecheck + biome clean. docs: TESTING.md. ignore .hermes/ plans.
2026-09-18 21:27:06 +07:00
dependabot[bot] c18b2c42c2 build(deps-dev): bump vitest
Bumps the development group in /services/backend with 1 update: [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `vitest` from 5.0.0 to 5.0.1
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.1/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 5.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-18 14:25:34 +00:00
dependabot[bot] bfed04f449 build(deps): bump @orpc/server
Bumps the production group in /services/backend with 1 update: [@orpc/server](https://github.com/middleapi/orpc/tree/HEAD/packages/server).


Updates `@orpc/server` from 1.15.0 to 1.15.1
- [Release notes](https://github.com/middleapi/orpc/releases)
- [Commits](https://github.com/middleapi/orpc/commits/v1.15.1/packages/server)

---
updated-dependencies:
- dependency-name: "@orpc/server"
  dependency-version: 1.15.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-18 14:25:14 +00:00
dependabot[bot] 9509eaa65c build(deps): bump @orpc/client
Bumps the production group in /services/frontend with 1 update: [@orpc/client](https://github.com/middleapi/orpc/tree/HEAD/packages/client).


Updates `@orpc/client` from 1.15.0 to 1.15.1
- [Release notes](https://github.com/middleapi/orpc/releases)
- [Commits](https://github.com/middleapi/orpc/commits/v1.15.1/packages/client)

---
updated-dependencies:
- dependency-name: "@orpc/client"
  dependency-version: 1.15.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-18 14:25:11 +00:00
asepharyana ef41898a60 feat: add sexual/provocative username detection to LLM prompt rules
- rules.ts: explicit rule that sexual/provocative username terms
  (Pecinta Pria, Cinta, pacar, janda, bokep, hot, seks, nude, telanjang)
  MUST be flagged as offensive_username with low severity
- output.ts: output schema case for sexual/provocative username
  violations, always status: warn, severity: low, never flagged/delete

No hardcoded keyword lists — fix is at the LLM prompt level only.
2026-09-18 18:11:15 +07:00
mytheclipsebotreview[bot] 8f9f5932b5 Auto-merge PR #65
build(deps-dev): bump puppeteer-core from 25.10.0 to 25.11.0 in /services/frontend in the development group
2026-09-17 14:43:25 +00:00
mytheclipsebotreview[bot] f6d7b02a8a Auto-merge PR #64
build(deps): bump lucide-react from 1.45.0 to 1.46.0 in /services/frontend in the production group
2026-09-17 14:31:21 +00:00
mytheclipsebotreview[bot] c8382d3930 Auto-merge PR #62
build(deps): bump zod from 4.6.4 to 4.6.5 in /services/backend in the production group
2026-09-17 14:31:12 +00:00
mytheclipsebotreview[bot] 62c531adb5 Auto-merge PR #63
build(deps): bump zod from 4.6.4 to 4.6.5 in /services/discord-gateway in the production group
2026-09-17 14:25:41 +00:00
dependabot[bot] 172f8ede43 build(deps-dev): bump puppeteer-core
Bumps the development group in /services/frontend with 1 update: [puppeteer-core](https://github.com/puppeteer/puppeteer).


Updates `puppeteer-core` from 25.10.0 to 25.11.0
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.10.0...puppeteer-core-v25.11.0)

---
updated-dependencies:
- dependency-name: puppeteer-core
  dependency-version: 25.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-17 14:25:21 +00:00
dependabot[bot] b69da05003 build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.45.0 to 1.46.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.46.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-17 14:24:59 +00:00
dependabot[bot] 1e56c9d716 build(deps): bump zod
Bumps the production group in /services/discord-gateway with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.6.4 to 4.6.5
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.6.4...v4.6.5)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-17 14:24:54 +00:00
dependabot[bot] 13d140f82a build(deps): bump zod in /services/backend in the production group
Bumps the production group in /services/backend with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.6.4 to 4.6.5
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.6.4...v4.6.5)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-17 14:24:50 +00:00
mytheclipsebotreview[bot] 687e0566ac Auto-merge PR #61
build(deps): bump zod from 4.6.2 to 4.6.4 in /services/discord-gateway in the production group
2026-09-16 14:41:49 +00:00
mytheclipsebotreview 3472cc072d Merge branch 'main' into dependabot/npm_and_yarn/services/discord-gateway/production-3bf006ec38 2026-09-16 21:39:26 +07:00
mytheclipsebotreview[bot] 75b9605982 Auto-merge PR #59
build(deps): bump tailwind-merge from 3.6.0 to 3.7.0 in /services/frontend in the production group
2026-09-16 14:33:06 +00:00
mytheclipsebotreviewandClaude Opus 5 ea97dcea79 fix: auto-fix code quality [skip ci]
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 21:32:44 +07:00
mytheclipsebotreview 8e96caa075 Merge branch 'main' of https://github.com/asepharyana/GMW into dependabot/npm_and_yarn/services/frontend/production-d848ab5243 2026-09-16 21:30:11 +07:00
mytheclipsebotreview[bot] cfd11848a0 Auto-merge PR #60
build(deps): bump zod from 4.6.2 to 4.6.4 in /services/backend in the production group
2026-09-16 14:29:32 +00:00
dependabot[bot] 4c0c76089d build(deps): bump zod
Bumps the production group in /services/discord-gateway with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.6.2 to 4.6.4
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.6.2...v4.6.4)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 14:24:57 +00:00
dependabot[bot] 517441546d build(deps): bump zod in /services/backend in the production group
Bumps the production group in /services/backend with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.6.2 to 4.6.4
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.6.2...v4.6.4)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 14:24:53 +00:00
dependabot[bot] 182b62da4f build(deps): bump tailwind-merge
Bumps the production group in /services/frontend with 1 update: [tailwind-merge](https://github.com/dcastil/tailwind-merge/tree/HEAD/packages/tailwind-merge).


Updates `tailwind-merge` from 3.6.0 to 3.7.0
- [Release notes](https://github.com/dcastil/tailwind-merge/releases)
- [Commits](https://github.com/dcastil/tailwind-merge/commits/tailwind-merge@3.7.0/packages/tailwind-merge)

---
updated-dependencies:
- dependency-name: tailwind-merge
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 14:24:52 +00:00
mytheclipsebotreview[bot] 4686b86d1e Auto-merge PR #57
build(deps): bump next from 16.3.4 to 16.3.5 in /services/frontend in the production group
2026-09-15 14:37:28 +00:00
mytheclipsebotreview[bot] db7814cd0c Auto-merge PR #58
build(deps-dev): bump @types/three from 0.185.4 to 0.186.0 in /services/frontend in the development group
2026-09-15 14:31:19 +00:00
mytheclipsebotreview 25a9e11945 fix: auto-fix code quality [skip ci] 2026-09-15 21:31:00 +07:00
dependabot[bot] 104875e20e build(deps-dev): bump @types/three
Bumps the development group in /services/frontend with 1 update: [@types/three](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/three).


Updates `@types/three` from 0.185.4 to 0.186.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/three)

---
updated-dependencies:
- dependency-name: "@types/three"
  dependency-version: 0.186.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-15 14:26:26 +00:00
dependabot[bot] cc7a914f14 build(deps): bump next in /services/frontend in the production group
Bumps the production group in /services/frontend with 1 update: [next](https://github.com/vercel/next.js).


Updates `next` from 16.3.4 to 16.3.5
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.3.4...v16.3.5)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.3.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-15 14:26:02 +00:00
mytheclipsebotreview[bot] 44578b0569 Auto-merge PR #56
build(deps-dev): bump the development group across 1 directory with 2 updates
2026-09-14 15:10:56 +00:00
mytheclipsebotreview 45f2598937 fix: auto-fix code quality [skip ci] 2026-09-14 22:10:40 +07:00
mytheclipsebotreview 3324553871 Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/frontend/development-1721234731 2026-09-14 22:07:15 +07:00
mytheclipsebotreview[bot] 4369bec527 Auto-merge PR #52
build(deps): bump the production group in /services/discord-gateway with 2 updates
2026-09-14 14:57:56 +00:00
mytheclipsebotreview 73f13ecac1 fix: auto-fix code quality [skip ci] 2026-09-14 21:57:34 +07:00
mytheclipsebotreview 14c5a50f3d Merge branch 'main' of https://github.com/asepharyana/GMW into dependabot/npm_and_yarn/services/discord-gateway/production-cdeaf23ddb 2026-09-14 21:55:31 +07:00
mytheclipsebotreview[bot] 341befc43b Auto-merge PR #53
build(deps-dev): bump the development group across 1 directory with 2 updates
2026-09-14 14:54:44 +00:00
mytheclipsebotreview 87ccbbce8e fix: auto-fix code quality [skip ci] 2026-09-14 21:54:04 +07:00
dependabot[bot] b76ae57cf6 build(deps-dev): bump the development group across 1 directory with 2 updates
Bumps the development group with 2 updates in the /services/backend directory: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@biomejs/biome` from 2.5.12 to 2.5.13
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.13/packages/@biomejs/biome)

Updates `@types/node` from 26.5.0 to 26.5.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.5.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:52:13 +00:00
mytheclipsebotreview[bot] 4e442f70e8 Auto-merge PR #51
build(deps): bump zod from 4.5.4 to 4.6.2 in /services/backend in the production group
2026-09-14 14:49:36 +00:00
mytheclipsebotreview 2ef28cc5c7 fix: auto-fix code quality [skip ci] 2026-09-14 21:49:16 +07:00
mytheclipsebotreview 5abd25c1a8 Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/backend/production-563011b59b 2026-09-14 21:45:11 +07:00
dependabot[bot] e746c0c2d2 build(deps-dev): bump the development group across 1 directory with 2 updates
Bumps the development group with 2 updates in the /services/frontend directory: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@biomejs/biome` from 2.5.12 to 2.5.13
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.13/packages/@biomejs/biome)

Updates `@types/node` from 26.5.0 to 26.5.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.5.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:37:19 +00:00
mytheclipsebotreview[bot] 1c8f75f034 Auto-merge PR #55
build(deps): bump the production group in /services/frontend with 3 updates
2026-09-14 14:34:39 +00:00
mytheclipsebotreview aace81c2d4 fix: auto-fix code quality [skip ci] 2026-09-14 21:34:24 +07:00
mytheclipsebotreview ff0f888887 Merge branch 'main' of https://github.com/asepharyana/GMW into dependabot/npm_and_yarn/services/frontend/production-1e6a043c09 2026-09-14 21:30:48 +07:00
mytheclipsebotreview[bot] 0c6ff651d1 Auto-merge PR #54
build(deps-dev): bump @biomejs/biome from 2.5.12 to 2.5.13 in /services/discord-gateway in the development group
2026-09-14 14:30:12 +00:00
dependabot[bot] 6ecfd559b9 build(deps): bump the production group
Bumps the production group in /services/frontend with 3 updates: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react), [react](https://github.com/react/react/tree/HEAD/packages/react) and [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom).


Updates `lucide-react` from 1.43.0 to 1.45.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.45.0/packages/lucide-react)

Updates `react` from 19.2.8 to 19.3.0
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.3.0/packages/react)

Updates `react-dom` from 19.2.8 to 19.3.0
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.3.0/packages/react-dom)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.45.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: react
  dependency-version: 19.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: react-dom
  dependency-version: 19.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:26:52 +00:00
dependabot[bot] 3c92cc406a build(deps-dev): bump @biomejs/biome
Bumps the development group in /services/discord-gateway with 1 update: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome).


Updates `@biomejs/biome` from 2.5.12 to 2.5.13
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.13/packages/@biomejs/biome)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:26:47 +00:00
dependabot[bot] dc4347ef9f build(deps): bump the production group
Bumps the production group in /services/discord-gateway with 2 updates: [openai](https://github.com/openai/openai-node) and [zod](https://github.com/colinhacks/zod).


Updates `openai` from 7.10.0 to 7.15.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.10.0...v7.15.0)

Updates `zod` from 4.5.4 to 4.6.2
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.5.4...v4.6.2)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 7.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: zod
  dependency-version: 4.6.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:26:29 +00:00
dependabot[bot] a74a9ceb2e build(deps): bump zod in /services/backend in the production group
Bumps the production group in /services/backend with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.5.4 to 4.6.2
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.5.4...v4.6.2)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:26:16 +00:00
asepharyana 5c133f7302 feat(gateway): tinyfish web search as fallback when wikipedia misses
- new tinyFishSearch module: GET api.search.tinyfish.ai with X-API-Key,
  maps top-3 to SearchResult shape, never throws (all failure modes -> [])
- wikipediaSearch: on wiki miss, one tinyfish attempt; hits cached 6h
  under the same key so fallback latency is paid once
- termGlossary: on summary miss, top tinyfish hit becomes the definition
  (persisted permanently like wiki defs); miss keeps 1h sentinel
- config: TINYFISH_API_KEY (empty = fallback disabled), ENABLED,
  BASE_URL, TIMEOUT_MS, LOCATION, LANGUAGE knobs
- tests: 6 coverage for disabled/mapping/non-OK/network/bad-json

API key NOT committed — set TINYFISH_API_KEY in BWS gmw secrets.

Verified: typecheck + lint clean, 216/216 tests pass, live probe
'gubernur jawa barat' returned 3 mapped results
2026-09-14 00:44:38 +07:00
asepharyana 9685fa02be perf(gateway): retry transient attachment + wikipedia failures
- attachmentUploader: downloadDiscordAttachment retries CDN timeouts
  via retryWithBackoff (ATTACHMENT_RETRY_ATTEMPTS, 1s-8s backoff);
  AbortError normalized so 403/404 refresh path never fires on timeouts
- attachmentUploader: uploadAttachmentToTele uses ATTACHMENT_RETRY_ATTEMPTS
  instead of retries:0 (Tele 5xx under load was failing outright)
- wikipediaClient: wikipediaSummary retries once on abort/timeout
  (100% of prod summary errors were aborts); termGlossary drops its
  redundant second call (was up to 4 reqs/term under miss+retry)

Verified: typecheck + lint clean, 210/210 tests pass
2026-09-14 00:13:56 +07:00
asepharyana 90d4c2d0b1 Add visual check artifacts for gmw-architecture diagram
- Created HTML file for automated browser evidence visual check.
- Added JSON file containing detailed visual check results, including viewport dimensions, readability metrics, and screenshot paths.
2026-09-13 23:55:57 +07:00
mytheclipsebotreview[bot] 72fe2a3ad0 Auto-merge PR #50
build(deps): bump lucide-react from 1.42.0 to 1.43.0 in /services/frontend in the production group
2026-09-11 14:33:52 +00:00
dependabot[bot] a30df8b526 build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.42.0 to 1.43.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.43.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-11 14:27:05 +00:00
asepharyana e0a383c5b3 refactor(docs): Revise agent guide for clarity and structure 2026-09-11 18:56:28 +07:00
asepharyana 43f2f8449d feat(docs): Add comprehensive agent guides and templates for spec-driven development 2026-09-11 18:56:28 +07:00
asepharyana b3418ad799 feat: Implement video capture improvements and mobile navigation fixes
- Added eager selfbot voice connection establishment to ensure video capture works seamlessly during voice channel joins.
- Introduced a manual video watch command for selfbots to allow operators to initiate screen recording of other members' streams.
- Enhanced video recording functionality to split recordings into segments based on user activity, similar to voice recordings.
- Fixed mobile navigation issues by extending the navbar to include all items and ensuring responsive form controls.
- Standardized error handling across frontend components to improve user experience during failures.
2026-09-11 18:56:28 +07:00
mytheclipsebotreview[bot] 0eb089d9dc Auto-merge PR #48
build(deps-dev): bump @types/node from 26.4.1 to 26.5.0 in /services/frontend in the development group across 1 directory
2026-09-10 14:43:13 +00:00
dependabot[bot] 22be86d1c2 build(deps-dev): bump @types/node
Bumps the development group with 1 update in the /services/frontend directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 26.4.1 to 26.5.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-10 14:35:15 +00:00
mytheclipsebotreview[bot] 922b225b47 Auto-merge PR #49
build(deps-dev): bump @types/node from 26.4.1 to 26.5.0 in /services/backend in the development group
2026-09-10 14:34:04 +00:00
mytheclipsebotreview 30977d2e2b Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/backend/development-1283acccde 2026-09-10 21:33:12 +07:00
mytheclipsebotreview[bot] 78d8730785 Auto-merge PR #47
build(deps): bump lucide-react from 1.41.0 to 1.42.0 in /services/frontend in the production group
2026-09-10 14:32:26 +00:00
dependabot[bot] 250f043e4a build(deps-dev): bump @types/node
Bumps the development group in /services/backend with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 26.4.1 to 26.5.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-10 14:25:20 +00:00
dependabot[bot] 2c3d485af2 build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.41.0 to 1.42.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.42.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.42.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-10 14:24:53 +00:00
asepharyana 023217b260 feat(moderation): skip AI analysis for music bots via AI_SKIP_ANALYSIS_USER_IDS
Jockie Music (user 411916947773587456) posts now-playing embeds/spotify links
~1347 captured messages — every one consumed a moderation LLM call for zero
signal and contributed to batch timeouts. Config AI_SKIP_ANALYSIS_USER_IDS
(default=Jockie) skips them at ALL three analysis paths:
- queueMessageAnalysis entry (direct skip-result like age-restricted)
- batchScheduler processing (pre-batch filter)
- individual recovery path (no fallback spam for already-skipped authors)

Skip-result mirrors age_restricted: status=clean, flags=[skip_analysis_user],
action=none — stays visible in the dashboard, never analyzed.
2026-09-09 23:38:32 +07:00
asepharyana db4e84f057 fix(moderation): text batch timeout 45s→75s — router text model regularly exceeds 45s
The text model behind omniroute/9router consistently takes >45s on long-context
batches. At 45s every such batch fell through to the individual-fallback
queue which re-runs with its own timeout, then exhausted to ai_status=error.
75s keeps the bounded budget while letting the first-pass batch succeed.
2026-09-09 21:38:16 +07:00
asepharyana f85af3952a fix(moderation): audit fixes — media/vision timeout 120s, Qdrant retry w/ backoff, JSON repair in LLM caller
- AI_LLM_MEDIA_ANALYSIS_TIMEOUT_MS 60s→120s + vision 60s→120s: vision model
  via router regularly exceeded 60s, dropping media batches into the
  individual-fallback chain then exhausting into ai_status=error.
- Qdrant upserts: retryWithRetry() wraps PUT /points with exponential
  backoff (3 attempts, jitter) for transient 408/abort/ECONNRESET — the
  41 six-hour 'Qdrant upsert failed — semantic entry skipped' warnings were
  single-hop timeouts on a healthy-but-loaded Qdrant.
- LLM caller: on parse failure, attempt extractJson() structural repair of
  the raw content (models with thinking disabled sometimes emit JSON as
  plain text) before giving up and re-requesting.
2026-09-09 21:29:01 +07:00
mytheclipsebotreview[bot] 7d4009da9f Auto-merge PR #46
build(deps): bump lucide-react from 1.39.0 to 1.41.0 in /services/frontend in the production group
2026-09-08 14:42:25 +00:00
dependabot[bot] 75c419e0ca build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.39.0 to 1.41.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.41.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.41.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-08 14:26:12 +00:00
mytheclipsebotreview[bot] ce6aa72052 Auto-merge PR #45
build(deps-dev): bump the development group across 1 directory with 4 updates
2026-09-08 09:14:09 +00:00
mytheclipsebotreview 071aab72ed fix: auto-fix code quality [skip ci] 2026-09-08 16:13:55 +07:00
mytheclipsebotreview 2cff6edf38 Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/frontend/development-45538c545a 2026-09-08 16:09:31 +07:00
mytheclipsebotreview[bot] 423568018e Auto-merge PR #44
build(deps-dev): bump the development group in /services/backend with 3 updates
2026-09-08 09:08:44 +00:00
mytheclipsebotreview fbee943eba fix: auto-fix code quality [skip ci] 2026-09-08 16:08:29 +07:00
mytheclipsebotreview 2f9a87ca24 Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/backend/development-73e58ecbfa 2026-09-08 16:01:48 +07:00
mytheclipsebotreview[bot] b39bfc8e82 Auto-merge PR #43
build(deps): bump the production group across 1 directory with 3 updates
2026-09-08 09:00:53 +00:00
dependabot[bot] 9f894b1dea build(deps-dev): bump the development group across 1 directory with 4 updates
Bumps the development group with 4 updates in the /services/frontend directory: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) and [puppeteer-core](https://github.com/puppeteer/puppeteer).


Updates `@biomejs/biome` from 2.5.11 to 2.5.12
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.12/packages/@biomejs/biome)

Updates `@types/node` from 26.4.0 to 26.4.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@types/react-dom` from 19.2.5 to 19.2.7
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Updates `puppeteer-core` from 25.9.0 to 25.10.0
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.9.0...puppeteer-core-v25.10.0)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: puppeteer-core
  dependency-version: 25.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-08 08:41:26 +00:00
mytheclipsebotreview[bot] 67c01cdef3 Auto-merge PR #41
build(deps): bump the production group in /services/frontend with 2 updates
2026-09-08 08:37:57 +00:00
mytheclipsebotreview[bot] 96c85397c6 Auto-merge PR #42
build(deps-dev): bump @biomejs/biome from 2.5.11 to 2.5.12 in /services/discord-gateway in the development group
2026-09-08 08:27:47 +00:00
dependabot[bot] 68f6fdef57 build(deps-dev): bump the development group
Bumps the development group in /services/backend with 3 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@biomejs/biome` from 2.5.11 to 2.5.12
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.12/packages/@biomejs/biome)

Updates `@types/node` from 26.4.0 to 26.4.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `vitest` from 4.1.11 to 5.0.0
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: vitest
  dependency-version: 5.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-07 14:28:37 +00:00
dependabot[bot] de0d0bb85e build(deps): bump the production group across 1 directory with 3 updates
Bumps the production group with 3 updates in the /services/discord-gateway directory: [openai](https://github.com/openai/openai-node), [p-limit](https://github.com/sindresorhus/p-limit) and [p-retry](https://github.com/sindresorhus/p-retry).


Updates `openai` from 7.8.0 to 7.10.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.8.0...v7.10.0)

Updates `p-limit` from 7.3.1 to 7.3.2
- [Release notes](https://github.com/sindresorhus/p-limit/releases)
- [Commits](https://github.com/sindresorhus/p-limit/compare/v7.3.1...v7.3.2)

Updates `p-retry` from 8.0.0 to 8.0.1
- [Release notes](https://github.com/sindresorhus/p-retry/releases)
- [Commits](https://github.com/sindresorhus/p-retry/compare/v8.0.0...v8.0.1)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 7.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: p-limit
  dependency-version: 7.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: p-retry
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-07 14:28:34 +00:00
dependabot[bot] 2d0b653870 build(deps-dev): bump @biomejs/biome
Bumps the development group in /services/discord-gateway with 1 update: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome).


Updates `@biomejs/biome` from 2.5.11 to 2.5.12
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.12/packages/@biomejs/biome)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-07 14:28:06 +00:00
dependabot[bot] bf2d6c396f build(deps): bump the production group
Bumps the production group in /services/frontend with 2 updates: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) and [next](https://github.com/vercel/next.js).


Updates `lucide-react` from 1.38.0 to 1.39.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.39.0/packages/lucide-react)

Updates `next` from 16.3.3 to 16.3.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.3.3...v16.3.4)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: next
  dependency-version: 16.3.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-04 14:25:18 +00:00
asepharyana 276062fdd4 feat(ai): scope persistence, parallel tools, Prometheus token/cache counters
C2 full scope persistence:
- getRecentConversationContext now returns per-turn guildId/channelId
- processMessage merges historical scope when current request is unscoped
- chatbot remembers server context across all 8 history exchanges (not just 3)

A4+Metrics:
- Add incrementCounterBy(name, delta, labels) to gateway-metrics
- Token-usage counters: llm_tokens_total{model, type, label} per batch
- Cache hit counters: moderation_cache_hits{type} exact/semantic-qdrant/semantic-pg
- Cache miss counters: moderation_cache_misses per batch
- Prometheus /metrics now exposes cost + cache hit-rate for dashboards

Performance:
- Parallel tool execution within each chatbot round (Promise.all)
- All tool results collected before sending to model (ordering preserved)
- Tool failure now logged with structured warning (chatbot.tools.ts)

Verified: backend tsc+biome 37/37, discord-gateway tsc+biome 210/210
2026-09-04 15:32:03 +07:00
asepharyana 9682abef82 fix(chatbot): proper system role, tool catalog prompt, more rounds/history, tool failure observability
- Send system prompt as role:system instead of merging into the user message
  (models treat system messages as authoritative -> better tool selection)
- Add full 14-tool catalog + multi-hop instruction to the system prompt
- Raise MAX_TOOL_ROUNDS 4->6 so multi-hop queries complete
- Raise conversation history 3->8 exchanges (less repeat questioning)
- Append guild/channel scope hint to each user turn
- Add saturating toolErrors counter + structured warn log per tool failure
  so chatbot tool failures are observable instead of silently absorbed
2026-09-04 13:36:51 +07:00
mytheclipsebotreview[bot] c5219ee866 Auto-merge PR #39
build(deps): bump lucide-react from 1.37.0 to 1.38.0 in /services/frontend in the production group
2026-09-03 14:32:49 +00:00
dependabot[bot] a5fd58a7c1 build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.37.0 to 1.38.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.38.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-03 14:26:45 +00:00
asepharyana 85204ca6f0 fix(moderation): enforcement safety net — username-only offense never auto-deleted
Even with the prompt firewall (username vs content), the LLM can still
occasionally mis-apply a content-level zero-tolerance flag (sara /
conflict_instigation) to a message whose ONLY violation is the username
(e.g. 'matikanetanyahu'). The auto-delete eligibility check only recognized
exact offensive_username flags, so such false positives still deleted the
message.

Add a belt-and-suspenders guard in isNicknameOnlyViolation: if the flag set
is entirely username-attributable (offensive_username/sara/conflict_instigation)
AND the analysis text corroborates that the violation is username-only with
clean message content, route to nickname-reset instead of message deletion.

Adds 6 test cases covering the real matikanetanyahu scenario and the
false-positive/negative boundaries.
2026-09-03 20:39:17 +07:00
asepharyana 68fbaa631a fix(moderation): prevent username-only violations from triggering content-level zero tolerance
Add explicit FIREWALL PENILAIAN rule separating username assessment from
message content assessment. Username containing political/religious terms
(matikanetanyahu etc) is assessed as offensive_username with severity low
— never triggers sara/conflict_instigation zero tolerance for content.

Changes:
- rules.ts: Add FIREWALL section before LARANGAN BERAT; clarify each
  zero-tolerance rule applies to ISI PESAN only; update hierarchy #9
- examples.ts: Fix example #11 status flagged→warn for clean username;
  add example #11b with matikanetanyahu case
- output.ts: Explicit status/action mapping for username-only violations
2026-09-03 20:05:37 +07:00
mytheclipsebotreview[bot] d4d36e8819 Auto-merge PR #37
build(deps-dev): bump tsx from 4.23.12 to 4.23.13 in /services/discord-gateway in the development group
2026-09-02 14:43:29 +00:00
mytheclipsebotreview b761de8b98 Merge branch 'main' into dependabot/npm_and_yarn/services/discord-gateway/development-baee414eea 2026-09-02 21:42:31 +07:00
mytheclipsebotreview[bot] 5dccb76d08 Auto-merge PR #36
build(deps): bump zod from 4.5.2 to 4.5.4 in /services/backend in the production group
2026-09-02 14:36:31 +00:00
mytheclipsebotreview 3bf172b692 Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/backend/production-7257b05964 2026-09-02 21:35:28 +07:00
mytheclipsebotreview[bot] 1aac95b368 Auto-merge PR #38
build(deps-dev): bump tsx from 4.23.12 to 4.23.13 in /services/backend in the development group
2026-09-02 14:34:05 +00:00
mytheclipsebotreview 73727fbdc4 Merge commit 'be660210e3635cb7beaffcf57703d40af24cf944' into dependabot/npm_and_yarn/services/backend/development-baee414eea 2026-09-02 21:30:53 +07:00
mytheclipsebotreview[bot] be660210e3 Auto-merge PR #35
build(deps): bump zod from 4.5.2 to 4.5.4 in /services/discord-gateway in the production group
2026-09-02 14:29:54 +00:00
dependabot[bot] 43e48c1f26 build(deps-dev): bump tsx in /services/backend in the development group
Bumps the development group in /services/backend with 1 update: [tsx](https://github.com/privatenumber/tsx).


Updates `tsx` from 4.23.12 to 4.23.13
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.12...v4.23.13)

---
updated-dependencies:
- dependency-name: tsx
  dependency-version: 4.23.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 14:26:04 +00:00
dependabot[bot] 53dfd3be41 build(deps-dev): bump tsx
Bumps the development group in /services/discord-gateway with 1 update: [tsx](https://github.com/privatenumber/tsx).


Updates `tsx` from 4.23.12 to 4.23.13
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.12...v4.23.13)

---
updated-dependencies:
- dependency-name: tsx
  dependency-version: 4.23.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 14:26:03 +00:00
dependabot[bot] 2f02196864 build(deps): bump zod in /services/backend in the production group
Bumps the production group in /services/backend with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.5.2 to 4.5.4
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.5.2...v4.5.4)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 14:25:50 +00:00
dependabot[bot] 1cd17f53d5 build(deps): bump zod
Bumps the production group in /services/discord-gateway with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.5.2 to 4.5.4
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.5.2...v4.5.4)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 14:25:49 +00:00
asepharyana 955da396c7 debug(stream-watch): add per-packet decrypt diagnostics — why VIDEO-PKT fires but no mp4? 2026-09-02 19:24:25 +07:00
asepharyana ccfdbc860e revert(gateway): source defaults kembali ke imrnes (100.121.180.82) — outage usai 2026-09-02 16:02:29 +07:00
asepharyana e6aa9af283 fix(gateway): make moderation score optional — LLM omits it in media batches
result.score was required by zod; the LLM (gemini-3.5-flash-lite via 9router)
occasionally omits it for media batches, hard-failing the whole batch parse
('Zod validation failed: expected number, received undefined' at
results[0].score). Callers already null-coalesce (result.score ?? 0) and the
parser clampScore()s it, so requiring it only caused parse failures.
Adds regression tests: media-batch without score parses (score->0), and
score-present responses still parse with the value.
2026-09-02 13:15:05 +07:00
asepharyana 4c38d53972 fix(gateway): local infra defaults + qdrant collection retry-on-failure
- AI_LLM_BASE_URL default -> http://127.0.0.1:4014/v1 (was imrnes :20128/api/v1)
- QDRANT_URL fallback -> http://127.0.0.1:6333 (was imrnes :6333)
- ensureQdrantCollection: reset memoised promise on failure so a mid-way
  recreate abort (DELETE done, PUT failed) does not leave the collection
  permanently missing until process restart
- tests: qdrantEnsure.test.ts (3 cases: retry-on-failure, recreate, idempotent)
2026-09-02 12:57:54 +07:00
asepharyana e5304fde29 feat(gateway): add manual video-watch command for selfbot screen-share capture
A selfbot (user token) cannot auto-detect other members' camera/share
(no VOICE_STATE_UPDATE for others, 403 on member fetch). The only
selfbot-viable path to capture another member's SCREEN SHARE is an
operator-initiated STREAM_WATCH (gateway op 20, not gated on bot-vs-user).

Add video:watch / video:unwatch Redis commands routed via the existing
command handler to startStreamWatch/stopStreamWatch, which then does the
DAVE handshake + per-burst MP4 segmentation + DB insert + Tele upload
(already implemented in streamWatchReceiver).

- new VideoHandler (command-handler/video.handler.ts)
- register video:watch / video:unwatch in handler-registry + CommandHandler
- command constants COMMAND_VIDEO_WATCH / COMMAND_VIDEO_UNWATCH
- resolve active voice channel from voice controller + client cache
- 8 unit tests (videoHandler.test.ts)
- biome fixes for pre-existing test import ordering

All green: typecheck, build, lint (174 files), 200 tests.
2026-09-02 10:12:03 +07:00
asepharyana 43594af3c8 fix: CI biome errors + enhanced GUILD_CREATE/READY voice_states diagnostic
- live-speaker.ts: unused var [id] in clearAllSpeakers → [_]
- migrate.ts: useTemplate string concat → template literal
- streamWatchReceiver: remove unused watchKey param from closeCurrentSegment
- videoRecorder: log all raw WS event types + READY sessions.voice + broadcaster_user_ids
2026-09-02 02:31:32 +07:00
asepharyana fdcd53d149 debug(video): enhanced diag — READY sessions.voice + broadcaster_user_ids + all raw types
Dump all WS raw event types after 10s (see if GUILD_CREATE exists at all),
and log broadcaster_user_ids + sessions.voice from READY payload.
Selfbot may RESUME (skip GUILD_CREATE) — voice data may only be in READY.
2026-09-02 02:26:26 +07:00
asepharyana bc66babd45 debug(video): log raw GUILD_CREATE/READY/GUILD_MEMBERS_CHUNK shape
Temporary diagnostic to see whether GUILD_CREATE.voice_states actually
reaches the selfbot raw listener (selfbot-v13 emits everything via
WebSocketShard Events.RAW). Will remove once root cause confirmed.
2026-09-02 02:10:22 +07:00
asepharyana ac32179bb1 fix(video): detect pre-existing voice users from GUILD_CREATE.voice_states
Real root cause of 'tidak mendeteksi user yg sudah ada di voice':

The selfbot's discord.js-selfbot-v13 GUILD_CREATE handler only sends
GUILD_SUBSCRIPTIONS_BULK — it DROPS d.voice_states from the payload.
So when the gateway (re)starts, every user who was ALREADY in the voice
channel (with camera or screen-share on) is invisible: channel.members is
empty, and REST fallbacks don't work for user tokens (verified live:
GET /channels/{id}/voice-states -> 404, GET /guilds/{id}/members -> 403).

Fix: register our own raw listener for GUILD_CREATE, capture
d.voice_states per guild (buffer it), and consume it in
scanExistingStreamers when the channel gets tracked (voice join happens
after GUILD_CREATE). This is the ONLY user-token-compatible source of
'who is in voice with video right now'.

- videoRecorder: +pendingGuildCreateVoiceStates buffer, +raw GUILD_CREATE
  listener, Path C consumes buffered states (self_video/self_stream,
  matching channel, skip self) before the REST fallback
- scanExistingStreamers: Path A cache -> Path C GUILD_CREATE -> Path D
  REST members.fetch() best-effort
- +1 test: GUILD_CREATE voice_states buffer -> watch camera+screen-share,
  skip self/no-video/other-channel (192/192 pass)
- typecheck + build + biome clean (1 pre-existing warning)
2026-09-02 02:03:31 +07:00
asepharyana cee33c0d1f fix(video): detect pre-existing voice members after gateway restart
Root cause: scanExistingStreamers only read channel.members, which is
EMPTY after a gateway restart because the selfbot's guild member cache
hasn't been populated yet. So any user who was ALREADY on camera /
screen-sharing when the bot (re)joined was never detected → no video.

Fix: when the member cache is empty, fall back to guild.members.fetch()
(REST GET /guilds/{id}/members — user-token compatible; the bot-only
GET /channels/{id}/voice-states returns 404 for selfbots, verified live)
which populates member.voice states, then re-scan channel.members for
streaming/selfVideo.

- scanExistingStreamers is now async; trackChannel fire-and-forgets it
- logs source=cache vs source=rest-members for observability
- +1 test: cold-start channel.members empty → REST fetch → watch camera user
- 191/191 tests pass, typecheck + build + biome clean
2026-09-02 01:13:50 +07:00
asepharyana dd9f2f6bbc fix(voice): self-undeafen/self-unmute bot instantly on server mute/deafen
Previously forceSelfServerUnmuteUndeafen only ran on video-watch attempts and
after voice reconnects — so when an admin server-muted or server-deafened the
bot, it stayed muted/deafened for minutes (or forever if no streamer came on).

Add registerSelfVoiceStateGuard: a voiceStateUpdate listener that detects the
bot's own serverMute/serverDeaf transition to true and immediately re-issues
mute:false,deaf:false. Wired at client-ready in bootstrap.ts. Best-effort,
idempotent, never blocks the gateway.
2026-09-02 00:30:57 +07:00
asepharyana 82f1698043 feat(fe): surface fetched-but-hidden data — voice bridges, recorder last-activity, hourly flow
Closes the three highest-value gaps from the FE data-flow audit:

- voice/view.tsx: render VoiceStatus.connections (multi-guild bridge roster)
  which was fetched but never shown — channel name, guild/channel id,
  connected time, ACTIVE marker for the primary bridge (falls back to the
  active channel when connections is empty).
- recordings/view.tsx: show SpeakerSummary.last_at ('active <relative>') on
  the speaker leaderboard row (data was fetched but hidden).
- dashboard/view.tsx: render activity.hourly as an 'Hourly Flow · Last 24h'
  24-bar volume strip with per-hour tooltip and total msgs/flagged header
  (hourly distribution was fetched but only daily was charted).

Verified: tsc clean, biome clean (127 files), pnpm build green.
2026-09-02 00:25:14 +07:00
asepharyana 7e50c000dc fix(backend): remove dead user_reputations schema (table + type aliases)
Follow-up to d0453881: the pgUserReputationsTable definition and the
UserReputation/UserReputationInsert type aliases were the only remaining
references to the removed user reputation feature. Nothing imports them
(verified via grep across src/ + tests/), so they're pure dead code that
could mislead future devs into re-joining a dropped table. Railed out to
close the 42P01 failure class completely.
2026-09-02 00:17:54 +07:00
asepharyana d0453881b8 fix(users): drop dead user_reputations JOIN that broke Members page + audit
Root cause: gateway migration 0016 removed the per-user reputation feature
(DROP TABLE user_reputations), but the backend still LEFT JOINed
pgUserReputationsTable in dashboard listUsers/getUserDetail and the chatbot
get_user_reputation tool. Against the live DB these queries raised 42P01
(undefined_table) -> the new /users page could never load (oRPC WS error).

Fix:
- dashboard.repository.ts: remove reputation columns/join from listUsers and
  getUserDetail (data sourced only from messages + user_profiles).
- chatbot.tools.ts: get_user_reputation now returns honest 'unavailable'
  (feature removed) instead of querying the dropped table.
- chatbot.toolDefs.ts: update tool description so the LLM doesn't advertise
  a dead feature.
- frontend types/users view: drop trust_score/clean_message_streak/
  total_infractions/last_infraction_at; derive risk label from real flagged%
  and add warn_count to the inspector (real available data).

Verified: backend+frontend tsc clean, biome clean, 37 unit tests pass,
query tested against live DB (returns real members), build green.
2026-09-02 00:12:52 +07:00
asepharyana 7122258d7c fix(voice): read SSRCMap internal map via 'map' (not '_map')
videoReceiver.getSsrcInternalMap read asAny._map, but @discordjs/voice
0.19.x exposes the SSRC map as the public field 'map'. So inferVideoOwner
(e.g. matching a video RTP SSRC to a user via audio-SSRC proximity) always
returned null -> every video packet was silently dropped after decryption.
Accept both 'map' and '_map'. Unblocks camera/screen-share attribute when
op12 does not carry a videoSSRC stream entry.
2026-09-02 00:10:02 +07:00
asepharyana 30d640ca5b feat(fe): add Member Intelligence (/users) page wired to full reputation data
- New /users route: member roster (trust score, clean/flagged counts, message volume)
  + inspector detail (trust breakdown, clean streak, infractions, AI profile,
  recent messages) with live search
- Backend dashboard.listUsers/getUserDetail now JOIN user_reputations to expose
  trust_score, clean_message_streak, total_infractions, last_infraction_at +
  warn_count/clean_count breakdowns
- Frontend types updated to match; useUsers refactored to PaginatedUsers shape,
  added useUserSearch for the old search behavior
- Nav rail + mobile nav: add Users entry
2026-09-02 00:01:12 +07:00
asepharyana 7c21629404 fix(voice): DAVE video decrypt fallback chain (VIDEO→AUDIO→passthrough)
streamWatchReceiver: DAVE decrypt for screen-share video packets was
returning null every time (VIDEO-PKT diag showed packets arriving but
no 'Video segment opened'). Three possible causes:
1. No VIDEO decryptor in MLS group (audio-only handshake)
2. GoLive stream tags video packets as AUDIO
3. Screen-share payloads are unencrypted above the AES layer

Fix: try MediaType.VIDEO first, then MediaType.AUDIO, then passthrough
(legacy-decrypted payload as-is). This covers all three modes without
breaking audio recording.
2026-09-01 23:54:07 +07:00
asepharyana 5fdd6ed80c feat(fe): add reactor message/emoji stats to analysis leaderboard 2026-09-01 23:52:28 +07:00
asepharyana 38b74cc1db feat(fe): enrich dashboard, moderation, messages inspector, channels roster with full backend data
- Dashboard: 6-tile metric deck (flagged today, active users 24h, mod queue),
  pipeline status bar, top channels ranking, enriched reactions with emojis,
  warned counter in gauge, 4 quick-nav links
- Moderation: coverage tile + header rate, top flagged domains, top flagged
  channels, category/severity/action breakdown trends, coverage progress bar
- Messages inspector: severity meter, confidence %, risk score, recommended
  action badge, reply-context chain, mention + role chips
- Channels: roster/culture tabs, rich table (messages, clean, flagged, risk
  bar, last activity) wired to dashboard.channels endpoint
2026-09-01 23:51:39 +07:00
asepharyana 7bfdf9c85c feat(voice): fork @discordjs/voice with video receive support
Fork @discordjs/voice 0.19.2 into vendor/discord-voice-fork and patch the
voice gateway handshake so Discord sends camera/screen-share RTP video:

- Identify payload now declares video:true + streams:[] (derived from
  Discord-RE/Discord-video-stream) - this is what makes Discord deliver
  H264 (PT 96-127) to the voice socket. Previously the client never
  declared video capability, so Discord omitted all video SSRCs/packets.
- op-12 Speaking handler maps streams[].ssrc -> videoSSRC alongside the
  audio SSRC, so ssrcMap carries camera/screen-share stream IDs.
- SSRCMap.get() now also resolves video SSRCs (video RTP arrives on a
  different SSRC than audio), enabling attribution for videoReceiver.ts.
- Both dist/index.js (CJS) and dist/index.mjs (ESM) patched; 3 isolated
  hunks vs upstream, verified by diff.
- package.json points @discordjs/voice -> file:vendor/discord-voice-fork
  (pnpm lockfile updated, CI --frozen-lockfile compatible).
- .gitignore: replace global dist/ with per-service explicit patterns so
  the vendored fork's dist/ is committed while build outputs stay ignored.
- tests: +3 SSRCMap videoSSRC cases (190 total, all pass).

The receive-side pipeline (H264Depacketizer -> .h264 -> muxToMp4 -> MP4)
already exists in videoReceiver.ts; this unblocks it by making Discord
actually deliver video packets.
2026-09-01 23:35:13 +07:00
asepharyana 1367a2257f fix(gateway): detect camera-only video (selfVideo) for stream watch
Previously only member.voice.streaming (screen share, self_stream) triggered
video capture. Discord reports camera via self_video:true, so camera-only
users were never watched. Now scanExistingStreamers and handleVoiceStateUpdate
start a watch when streaming OR selfVideo is set, and stop it when both clear.
2026-09-01 22:26:38 +07:00
mytheclipsebotreview[bot] c9ad75b512 Auto-merge PR #33
build(deps): bump lucide-react from 1.35.0 to 1.37.0 in /services/frontend in the production group
2026-09-01 14:53:29 +00:00
mytheclipsebotreview d9a64d693f fix: auto-fix code quality [skip ci] 2026-09-01 21:53:16 +07:00
mytheclipsebotreview a70ecb7bfd Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/frontend/production-23ca0026d1 2026-09-01 21:52:50 +07:00
mytheclipsebotreview[bot] ca0a015ebf Auto-merge PR #32
build(deps): bump zod from 4.4.3 to 4.5.2 in /services/backend in the production group
2026-09-01 14:52:10 +00:00
asepharyana 4ec9685194 feat(gateway): split video recording into silence-based segments like voice
Video (camera + screen share) DAVE stream-watch now produces per-burst
MP4 segments instead of one long .h264 per watch:
- Detects VIDEO_SILENCE_MS (4000ms) of no H264 packets → closes the
  current segment, muxes to MP4, registers in voice_recordings + uploads
  to TeleUploader, then reopens for the next burst (mirrors voice AfterSilence).
- Per-watch segment counter + per-segment depacketizer reset + closing
  guard + write-error swallow so races (silence close vs in-flight UDP
  packet) never corrupt files or crash the gateway.
- Frontend: recordings deck renders a native <video> player for MP4 rows
  (detected by filename), keeps single-playback registry across audio+video.
2026-09-01 21:51:44 +07:00
mytheclipsebotreview 446a4f28ea Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/frontend/production-23ca0026d1 2026-09-01 21:42:36 +07:00
mytheclipsebotreview f70148308b Merge branch 'main' of https://github.com/asepharyana/GMW into dependabot/npm_and_yarn/services/backend/production-908e2177c6 2026-09-01 21:40:30 +07:00
mytheclipsebotreview[bot] eb2f8f5b6f Auto-merge PR #34
build(deps): bump zod from 4.4.3 to 4.5.2 in /services/discord-gateway in the production group
2026-09-01 14:39:51 +00:00
dependabot[bot] 1f196d2267 build(deps): bump zod
Bumps the production group in /services/discord-gateway with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.4.3 to 4.5.2
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.4.3...v4.5.2)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.5.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-01 14:33:45 +00:00
dependabot[bot] 297320a75d build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.35.0 to 1.37.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.37.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-01 14:33:30 +00:00
dependabot[bot] 49d27a8a1c build(deps): bump zod in /services/backend in the production group
Bumps the production group in /services/backend with 1 update: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 4.4.3 to 4.5.2
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.4.3...v4.5.2)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.5.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-01 14:33:26 +00:00
asepharyana 596ff04902 chore: ignore .playwright-mcp artifacts 2026-09-01 20:20:50 +07:00
asepharyana fa72fe03cd feat(archive): show real channel/thread names in semantic search UI
Gateway archive embedder now parses metadata.channel.{channelName,threadName}
from each message and stores channel_name/thread_name in the Qdrant payload.
Backend exposes them; the semantic results card renders the thread name (or
channel name) instead of a raw #snowflake, with the ID as a last-resort
fallback for legacy points. Matches the message feed's channel-label logic
(getMessageChannelLabel).
2026-09-01 19:27:56 +07:00
asepharyana 26a690943d feat(archive): rich metadata in semantic search — username/channel/guild context + guild filter
Archive payload now stores username, channel_id, guild_id, thread_id and the
real message created_at (not embed time). Backend searchArray accepts an
optional guildId and applies a Qdrant payload filter so results can be scoped
to the guild being viewed. API/frontend expose the new fields and the
semantic results card shows who said it, in which channel, and when —
turning bare text blobs into contextual results. Old points fall back to
analyzed_at and omit the new fields gracefully.
2026-09-01 18:56:14 +07:00
asepharyana c704fbf7a5 fix(gateway): make voice transcription model configurable + router-compatible
- AI_VOICE_TRANSCRIPTION_MODEL config (default whisper-1) so the model can be a provider-qualified id (openrouter/openai/whisper-1) that actually has credentials through 9router/omniroute — bare whisper-1 maps to the openai provider which has none
- response_format json (not text): 9router proxies only json/verbose_json transcription responses; text returns 400
- parse text from the json response object
- prod env updated: model=openrouter/openai/whisper-1 (still needs OpenRouter STT balance — 402 until funded)
2026-09-01 18:25:14 +07:00
asepharyana 7ef86c81ca feat(ai): audit + harden embedding pipeline
- Normalize text before embedding (strip mentions/URLs/emoji/markdown/control chars, lowercase, truncate) on both write and query sides so vectors aren't diluted and tokens aren't wasted
- embeddingClient: retry embeddings (maxRetries 2), validate batch dimension consistency, preserve index alignment for empty-normalized texts
- archiveEmbedder: store normalized text in archive payload, skip empty-normalized content
- backend: normalize search queries, make archive search similarity threshold configurable (AI_LLM_EMBEDDING_ARCHIVE_MIN_SIMILARITY, default 0.6)
2026-09-01 18:01:47 +07:00
asepharyana 0e31aa06b8 feat(gateway): refactor term extraction and scoring logic into textSignals.ts for reuse 2026-08-31 22:59:27 +07:00
asepharyana 12cc956329 feat(gateway): implement separate Piscina pools for text and media analysis to optimize processing 2026-08-31 22:59:27 +07:00
mytheclipsebotreview[bot] 3ce594d9b5 Auto-merge PR #29
build(deps): bump the production group across 1 directory with 4 updates
2026-08-31 14:46:39 +00:00
mytheclipsebotreview[bot] 0de5929711 Auto-merge PR #27
build(deps): bump the production group across 1 directory with 2 updates
2026-08-31 14:46:30 +00:00
mytheclipsebotreview 91cf0ad33f Merge branch 'dependabot/npm_and_yarn/services/frontend/production-f6f770ead8' of https://github.com/asepharyana/GMW into dependabot/npm_and_yarn/services/frontend/production-f6f770ead8
# Conflicts:
#	services/frontend/pnpm-lock.yaml
2026-08-31 21:45:54 +07:00
dependabot[bot] a0794a67d1 build(deps): bump the production group across 1 directory with 4 updates
Bumps the production group with 4 updates in the /services/discord-gateway directory: [axios](https://github.com/axios/axios), [openai](https://github.com/openai/openai-node), [piscina](https://github.com/piscinajs/piscina) and [sharp](https://github.com/lovell/sharp).


Updates `axios` from 1.19.0 to 1.20.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.19.0...v1.20.0)

Updates `openai` from 7.5.0 to 7.8.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.5.0...v7.8.0)

Updates `piscina` from 5.3.1 to 5.3.2
- [Release notes](https://github.com/piscinajs/piscina/releases)
- [Changelog](https://github.com/piscinajs/piscina/blob/v5.3.2/CHANGELOG.md)
- [Commits](https://github.com/piscinajs/piscina/compare/v5.3.1...v5.3.2)

Updates `sharp` from 0.35.3 to 0.35.4
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.3...v0.35.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: openai
  dependency-version: 7.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: piscina
  dependency-version: 5.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: sharp
  dependency-version: 0.35.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:44:40 +00:00
mytheclipsebotreview 81c6a95f4c Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/frontend/production-f6f770ead8
# Conflicts:
#	services/frontend/pnpm-lock.yaml
2026-08-31 21:44:40 +07:00
dependabot[bot] d8a52eeb4b build(deps): bump the production group across 1 directory with 2 updates
Bumps the production group with 2 updates in the /services/frontend directory: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) and [next](https://github.com/vercel/next.js).


Updates `lucide-react` from 1.34.0 to 1.35.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.35.0/packages/lucide-react)

Updates `next` from 16.3.2 to 16.3.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.3.2...v16.3.3)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.35.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: next
  dependency-version: 16.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:41:46 +00:00
mytheclipsebotreview[bot] 400466b5c5 Auto-merge PR #31
build(deps-dev): bump the development group in /services/discord-gateway with 2 updates
2026-08-31 14:41:39 +00:00
mytheclipsebotreview[bot] 60a3b729aa Auto-merge PR #30
build(deps-dev): bump the development group in /services/frontend with 2 updates
2026-08-31 14:38:57 +00:00
mytheclipsebotreview e291a0e2e3 Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/frontend/development-f6fa283631 2026-08-31 21:38:10 +07:00
mytheclipsebotreview[bot] 425cee15b6 Auto-merge PR #28
build(deps-dev): bump the development group in /services/backend with 2 updates
2026-08-31 14:37:22 +00:00
mytheclipsebotreview 62fe25e5c1 Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/services/backend/development-f6fa283631 2026-08-31 21:36:38 +07:00
mytheclipsebotreview[bot] b4fc51993f Auto-merge PR #26
build(deps): bump axios from 1.19.0 to 1.20.0 in /services/backend in the production group
2026-08-31 14:35:50 +00:00
dependabot[bot] cb36a2fbb0 build(deps-dev): bump the development group
Bumps the development group in /services/discord-gateway with 2 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@biomejs/biome` from 2.5.10 to 2.5.11
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.11/packages/@biomejs/biome)

Updates `@types/node` from 26.3.0 to 26.4.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:30:00 +00:00
dependabot[bot] 1a494260db build(deps-dev): bump the development group
Bumps the development group in /services/frontend with 2 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@biomejs/biome` from 2.5.10 to 2.5.11
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.11/packages/@biomejs/biome)

Updates `@types/node` from 26.3.0 to 26.4.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:29:42 +00:00
dependabot[bot] 04ab117044 build(deps-dev): bump the development group
Bumps the development group in /services/backend with 2 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@biomejs/biome` from 2.5.10 to 2.5.11
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.11/packages/@biomejs/biome)

Updates `@types/node` from 26.3.0 to 26.4.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:29:15 +00:00
dependabot[bot] ad21aa07ed build(deps): bump the production group
Bumps the production group in /services/frontend with 2 updates: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) and [next](https://github.com/vercel/next.js).


Updates `lucide-react` from 1.34.0 to 1.35.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.35.0/packages/lucide-react)

Updates `next` from 16.3.2 to 16.3.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.3.2...v16.3.3)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.35.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: next
  dependency-version: 16.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:29:05 +00:00
dependabot[bot] 2ae3c06c4a build(deps): bump axios in /services/backend in the production group
Bumps the production group in /services/backend with 1 update: [axios](https://github.com/axios/axios).


Updates `axios` from 1.19.0 to 1.20.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.19.0...v1.20.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:28:43 +00:00
asepharyana 2b6a1eca19 fix(gateway): re-assert server-undeafen+unmute before every video watch
The bot's own VOICE_STATE_UPDATE showed server-level deaf:true — a
server-deafened member is NOT sent the streamer's audiovisual RTP by Discord,
which is the likely reason no H264 arrives despite the DAVE watch reaching
Ready. The previous fire-and-forget forceSelfServerUnmuteUndeafen() ran once
after the first Ready join and silently reverted on reconnect/restart.

- Export forceSelfServerUnmuteUndeafen from recorder.ts; re-assert it (with
  read-back verification logging stillDeaf) at the START of every
  startStreamWatch() before STREAM_WATCH is sent (dynamic import avoids the
  recorder <-> videoRecorder <-> streamWatchReceiver module cycle).
- Re-assert it again after a successful voice reconnect.
- startStreamWatch() is now async; callers use void.
2026-08-31 19:16:56 +07:00
asepharyana 9606187861 feat(gateway): hexdump+ssrc of watch UDP packets
maxLen stayed 72 across 243 packets (no real H264, which is hundreds+ bytes) —
only 44-72-byte RTP packets on PT 76/72/73 arrive. Add ssrc + first-32-bytes
hex so we can identify exactly what Discord sends to the watch socket (control
packets vs stale video), which determines whether the gap is upstream routing
or whether large H264 packets are missing entirely.
2026-08-31 16:47:27 +07:00
asepharyana b423d21b23 feat(gateway): aggregate VIDEO-PKT diag — distinct PTs + maxLen
Enhance watch-socket diagnostic to report distinct RTP payload types seen and
the max packet length, so we can distinguish 'only small control packets arrive
(no real H264)' from 'H264 arrives but decrypt fails'. Live already confirmed
dave=true ready=true with packets flowing but no burst — need to know if they're
tiny 52-byte control packets (PT 73) or large H264.
2026-08-31 16:40:36 +07:00
asepharyana 266e149233 feat(gateway): add VIDEO-PKT diagnostic logging to stream-watch UDP handler
Instrument handleUdpMessage to log (rate-limited, first 3 then /20s) whether
video RTP packets actually reach the watch socket, and whether the DAVE session
is attached+ready and encryption key material present. Needed to diagnose why
no .h264 is written despite DAVE Ready + MLS: is the packet not arriving, or is
decrypt returning null?
2026-08-31 16:29:17 +07:00
asepharyana 026c66a03a docs(gateway): record 4th critical fix — DAVE session at net.state.dave (f1a7b0c2) 2026-08-31 16:17:56 +07:00
asepharyana f1a7b0c2a1 fix(gateway): resolve DAVE session from net.state.dave, not connectionData
CRITICAL: djs/voice stores the DAVESession wrapper at net.state.dave
(createDaveSession assigns to state.dave on op4 SessionDescription), NOT
inside connectionData. decryptVideoPacket looked up connectionData.dave which
is ALWAYS undefined -> every video packet hit '!dave?.session' guard and was
silently dropped, so no .h264/.mp4 ever got written despite the handshake
reaching Ready.

Fix: pass net.state.dave as a separate arg (the wrapper has .session ->
Davey.DAVESession) so the DAVE-layer decrypt (MediaType.VIDEO) actually runs.
Typecheck + build pass, lint clean (src/), 179/179 tests.
2026-08-31 16:08:55 +07:00
asepharyana dea284357c docs(gateway): DAVE watch Ready + MLS handshake confirmed live; P4 = video burst only
Live deploy 15:51 reached DAVE watch READY + completed MLS handshake on the
stream RTC (0->1->2->3->4, MLS commit processed, heartbeats alive). scan-on-
join also confirmed: 'Scanned pre-existing streamers on join watched=1'.
P4 remaining = capture actual video RTP (burst->mp4) while a stream is live.
2026-08-31 15:54:04 +07:00
asepharyana 87f1f8be8d feat(gateway): detect pre-existing streamers on bot voice join
If someone is ALREADY sharing screen / camera on when the bot joins the
channel, no voiceStateUpdate with streaming:true fires for them, so the
bot never sent STREAM_WATCH and missed their video entirely. trackChannel
now scans channel.members and starts a watch for anyone already streaming
(ignoring the bot itself and non-streamers). Idempotent: startStreamWatch
no-ops if a watch already exists. +2 tests (9/9 in videoRecorder).
2026-08-31 15:48:20 +07:00
asepharyana 4405647b33 fix(gateway): swap stateChange arg order so Ready actually attaches UDP
djs/voice Networking emits stateChange(oldState, newState), but the watch
handler declared (newState, oldState) -- reversed. So the code-4 Ready
branch (which attaches udp.on('message') + logs 'DAVE watch READY') never
fired when entering Ready; it only fired spuriously when LEAVING Ready.
Result: full DAVE handshake completed on the watch RTC (Ready + DAVE MLA +
video stream 21029 active 1920x1080@60) but no UDP listener -> no video
captured. Swap to (oldState, newState) so enter-Ready wires the socket.
2026-08-31 15:42:12 +07:00
asepharyana 4534b7a17d feat(gateway): log full watch Networking state transitions
Add watch-state N->M log on every djs/voice Networking stateChange (with
hasUdp flag) so the stream-watch connection's exact progression is visible:
OpeningWs(0)->Identifying(1)->UdpHandshaking(2)->SelectingProtocol(3)->
Ready(4). Pins down where the DAVE flow stalls instead of guessing from the
absence of logs. Pairs with debug:true + watch-djs-debug.
2026-08-31 15:36:05 +07:00
asepharyana b3e350d40f feat(gateway): enable djs/voice debug logging on watch Networking
Add debug:true to watch Networking options and wire net.on('debug') to
logger.info so djs/voice internal WS/DAVE state transitions appear in
journal. Without this, the stream-watch connection went silent after
'Streaming DAVE Networking' — no ready/error/close visible. Needed to
diagnose why the WS to stream endpoint 'c-sin14-xxx:2083' produced no
events.
2026-08-31 15:23:57 +07:00
asepharyana 75050bc088 fix(gateway): use rtc_server_id-1 as watch DAVE MLS channelId (WrongGroupId)
Live log (14:52) showed the stream-watch flow reaching STREAM_CREATE +
STREAM_SERVER_UPDATE but then DAVE processProposals threw
'ValidationError(WrongGroupId)' -- the Davey MLS session derived the wrong
group because connectionOptions.channelId was the guild voice channel id.
Per Discord-RE StreamConnection.daveChannelId = BigInt(serverId)-1n, the
stream-watch DAVE MLS group is keyed to rtc_server_id-1, not the vc channel.
Fix: pass BigInt(serverId)-1n as channelId to the watch Networking.

This error also surface as an uncaughtException that crashed the gateway
(systemd restarted it). Correct channel id prevents it at the root.
2026-08-31 14:54:32 +07:00
asepharyana f1da40691f docs(gateway): mark DAVE stream-watch P1-P3 done, P4 blocked on live streamer 2026-08-31 14:09:06 +07:00
asepharyana 374fd5a9c2 fix(gateway): use guild voice sessionId for watch RTC identify
The previous code read the sessionId from the selfbot client's voice manager
(client.voice.connection), which is no longer established since ensureSelfbotVoice
was removed — it would have sent sessionId:'none' in the watch Networking
identify and been rejected. Read the active session from the guild
@discordjs/voice connection (getVoiceConnection(guildId).state.networking...
connectionOptions.sessionId) instead.
2026-08-31 14:03:00 +07:00
asepharyana 77c8454bb2 fix(gateway): replicate dual-layer DAVE+LTS decrypt for watch video RTP
The first streamWatchReceiver only did dave.session.decrypt(msg.subarray(12))
which skipped the outer legacy-AES layer Discord wraps around the DAVE payload
on every RTC packet (encrypt = dave.encrypt then aead_aes256_gcm with RTP
header as AAD). Port @discordjs/voice VoiceReceiver.decrypt/parsePacket
faithfully (header strip incl CSRC+extension+padding, AES-GCM auth tag, then
DAVE MediaType.VIDEO). Without this the .h264 would be garbage.
2026-08-31 13:55:58 +07:00
asepharyana 0ea76a8373 feat(gateway): DAVE-capable stream-watch video receive (Phase D)
Replace the dead selfbot-v13 video path (WS 4017 DAVE). videoRecorder
now delegates to a new streamWatchReceiver that:
- sends STREAM_WATCH (op 20) on voiceState.streaming
- opens a @discordjs/voice Networking to the watch RTC (STREAM_CREATE +
  STREAM_SERVER_UPDATE) with DAVE enabled
- decrypts H264 via Davey MediaType.VIDEO, depacketizes + muxes to mp4
- tears down on streaming-stop / leave / untrack

Remove ensureSelfbotVoice/createVideoStream/joinStreamConnection (dead).
recorder.ts no longer fires the futile eager selfbot join.
2026-08-31 13:46:24 +07:00
asepharyana 0995c2db81 docs(gateway): spec + Phase-1 recon for DAVE-capable stream-watch video receive
Supersedes the eager-selfbot connection plan: Discord now REQUIRES DAVE (E2EE,
WS 4017) on all voice RTC, and discord.js-selfbot-v13's voice stack predates
DAVE, so its video-receive path (joinChannel + joinStreamConnection +
receiver.createVideoStream) cannot authenticate. @discordjs/voice 0.19.2 exports
VoiceWebSocket/VoiceUDPSocket/DAVESession/Networking + @snazzah/davey supports
MediaType.VIDEO/Codec.H264 decrypt, so we can build a DAVE-capable stream-watch
connection. Phased plan: prototype (P2), gateway integration (P3), live verify (P4).
2026-08-31 12:59:33 +07:00
asepharyana 6aeebe7826 fix(gateway): establish selfbot voice connection eagerly so video (camera/screen-share) capture works
Video capture (camera + screen share) recorded ZERO frames because the selfbot
ClientVoiceManager.connection was created LAZILY — only when a user started
streaming. At that point the bot is already connected via @discordjs/voice, so
the selfbot re-join never gets a fresh VOICE_SERVER_UPDATE and times out with
VOICE_CONNECTION_TIMEOUT after 15s. joinStreamConnection (STREAM_WATCH) +
receiver.createVideoStream both need that selfbot VoiceConnection CONNECTED.

Fix: establish the selfbot VoiceConnection eagerly in recorder.startRecording,
BEFORE joinVoiceChannel, so it rides the bot's fresh join (Discord emits
VOICE_SERVER_UPDATE → selfbot authenticates). videoRecorder reuses the cached
connection per guild, tears it down on voice stop/destroy. Best-effort — never
blocks audio recording.

Verified: typecheck + build + biome (src/) green; 9/9 videoRecorder tests.
2026-08-31 12:44:36 +07:00
asepharyana 6738279bad feat(gateway): record others' video via native selfbot watch/receive (Phase C)
Root cause of why Phase A/B captured zero video: @discordjs/voice is audio-only
and never sends the gateway STREAM_WATCH signal, so Discord never forwards a
member's video RTP to the bot. Live diagnostic confirmed: while members were
sharing, audio .ogg files flowed for many users but no non-opus RTP ever
arrived.

Correct path: discord.js-selfbot-v13 ships a complete native watch/record stack.
New src/modules/voice-recording/videoRecorder.ts:
- Detects streamers via voiceState.streaming on a single idempotent
  voiceStateUpdate listener.
- client.voice.joinChannel() (reuses the single session alongside
  @discordjs/voice) + joinStreamConnection(userId) -> STREAM_WATCH (op 20).
- receiver.createVideoStream(userId, path) -> PacketHandler -> Recorder
  (ffmpeg over UDP loopback) -> Matroska .mkv, decryption handled internally.

Wired in recorder.ts (trackChannel/untrackChannel) + bootstrap.ts
(setVideoRecorderClient/setVideoRecordingsDir). All best-effort; failures log
and never break existing voice/audio. Unit tests 6/6 (single listener, watch
handshake + mkv path, skip own video, idempotence, teardown). Full suite
20 files / 176 tests green; typecheck + build + biome clean.

UNVERIFIED live yet: needs deploy + a streamer to confirm Recorder ready +
playable .mkv.
2026-08-30 23:26:59 +07:00
asepharyana 260ecabb3c debug(gateway): log non-opus RTP payload types on the voice socket
Temporary diagnostic to answer definitively whether Discord actually delivers
video RTP to the bot when someone screen-shares / turns on camera. Both
videoReceiver and screenShareAudio rely on ssrcMap emitting videoSSRC from
voice-state updates, and NO "Video SSRC appeared"/"Screen-share video started"
lines appear even after a real share+record. This logs any RTP packet whose
payload type is not Opus (120) so we can tell: (a) video RTP IS arriving but
attribution/signaling fails, vs (b) Discord sends no video at all to a
non-signaling receiver. Remove this log once the gap is understood.
2026-08-30 22:44:17 +07:00
asepharyana 5241f9784a feat(gateway): close video burst promptly when a user's SSRC is removed
ssrcMap emits "delete" when a VoiceUserData is removed (user stops sharing /
leaves voice). Hook it to close the user's open video burst ~500ms later so the
ffmpeg mux starts as soon as they stop, instead of waiting up to 5s for the idle
sweep. No-op if no burst exists; safe on normal teardown.
2026-08-30 21:51:14 +07:00
asepharyana feca8a476c docs(plans): mark video receive Phase A+B done (capture + mp4 mux) 2026-08-30 21:50:16 +07:00
asepharyana 999c054bb2 feat(gateway): auto-mux recorded video to playable MP4 (Phase B)
Phase A captured raw .h264 streams but left them as non-playable elementary
streams. Phase B adds automatic muxing: when a video burst closes, the raw
.h264 is remuxed to a self-contained MP4 via `ffmpeg -c copy` (no re-encode,
fast) with `+faststart`, waits for the write stream to fully flush first so the
mux never reads a truncated tail, and deletes the raw .h264 on success (keeping
it on failure). Output: <RECORDINGS_DIR>/<uid>/video-<ssrc>-<ts>.mp4.

muxToMp4 is exported + covered by a real-ffmpeg vitest (tests/videoReceiver.test.ts):
generates a tiny baseline h264, remuxes, asserts mp4 exists/non-empty & raw deleted
(also the 5 depacketizer tests). Full gateway suite 170/170 green, tsc + biome clean.
2026-08-30 21:49:40 +07:00
asepharyana 80e9b913c1 chore(gateway): drop unused H264 SINGLE_NAL constant 2026-08-30 21:21:07 +07:00
asepharyana 419946f38e feat(gateway): record others' video (camera/screen share) — Phase A capture
@discordjs/voice only decrypts/forwards AUDIO (opus) — its onUdpMessage drops
every non-opus RTP packet (dist/index.mjs:2068 `!== RTP_OPUS_PAYLOAD_TYPE`, 120).
Video RTP (H264 camera + screen share, plus VP8/VP9/AV1) arrives on the same UDP
socket but was silently discarded.

New videoReceiver.ts wraps receiver.onUdpMessage (like screenShareAudio.ts):
- detects video payload types (96/98/101/102/106/116/126/127),
- decrypts them with the connection secret key/encryptionMode via the SAME
  receiver.parsePacket path @discordjs/voice uses for audio (so DAVE + voice
  encryption are handled identically),
- depacketizes H264 to AnnexB (single NAL, STAP-A, and FU-A fragmentation),
  waiting for a keyframe (SPS/PPS/IDR) before writing,
- writes a raw .h264 elementary stream per user per burst under
  <RECORDINGS_DIR>/<uid>/video-<ssrc>-<ts>.h264.

Attribution: a videoSSRC→user index is built from ssrcMap updates; a proximity
fallback mirrors screenShareAudio's inferScreenShareOwner. Bot's own video is
skipped.

Unit tests: tests/videoReceiver.test.ts (AnnexB start code, keyframe gating,
FU-A reassembly, orphan-fragment tolerance) — 5/5 green.

Phase A only (capture raw h264). Phase B (ffmpeg decode+mux to MP4/WebM +
persist) and Phase C (frontend playback) are follow-ups.
2026-08-30 21:20:32 +07:00
asepharyana 462f643d6f feat(gateway): auto server-undeafen + unmute the bot itself on voice join
A server-muted/server-deafened bot can't reliably receive/record members' audio
(and definitely can't receive video/screen share). After the voice connection
is Ready, force a REST guild-members PATCH (mute:false, deaf:false) on the self
member so the bot is auto-unmuted & undeafened on every join/reconnect.

Requires MUTE_MEMBERS + DEAFEN_MEMBERS permissions (user granted). Failure is
logged as a warning and never breaks the voice join.
2026-08-30 21:15:04 +07:00
asepharyana 441b5282ef feat(gateway): capture NSFW/age-restricted messages but skip AI analysis + exclude from public archive
Previously isAgeRestrictedMessage() early-returned in messageCreate/messageUpdate,
so NSFW-channel messages were never stored at all. Now they are captured like
any message (visible in dashboard), while the existing age-restricted skip path
(queueMessageAnalysis -> buildAgeRestrictedSkipResult) marks them clean with flag
age_restricted WITHOUT calling the LLM.

NSFW content is also deliberately kept OUT of the Qdrant public semantic-search
archive (archiveMessageEmbedded skips when isAgeRestricted), so it can't be found
via public web search. No schema change needed (metadata already carries channel.nsfw).
2026-08-30 20:19:34 +07:00
asepharyana 50967f6481 feat(gateway): capture NSFW/age-restricted messages but skip AI analysis + exclude from public archive
Previously isAgeRestrictedMessage() early-returned in messageCreate/messageUpdate,
so NSFW-channel messages were never stored at all. Now they are captured like
any message (visible in dashboard), while the existing age-restricted skip path
(queueMessageAnalysis -> buildAgeRestrictedSkipResult) marks them clean with flag
age_restricted WITHOUT calling the LLM.

NSFW content is also deliberately kept OUT of the Qdrant public semantic-search
archive (archiveMessageEmbedded skips when isAgeRestricted), so it can't be found
via public web search. No schema change needed (metadata already carries channel.nsfw).
2026-08-30 20:13:35 +07:00
asepharyana 7dfb4035b7 feat(gateway): persistent voice auto-reconnect — rejoin same channel after restart/reboot or unexpected drop 2026-08-30 14:03:12 +07:00
asepharyana 7f45cbff2d feat(frontend): group recordings into call sessions + session playback + session WAV export 2026-08-30 13:42:57 +07:00
asepharyana e12b1302ff feat(frontend): show transcription + keyword search + channel/date filters + speaker leaderboard
- cards now render transcription snippet (expandable); no-transcript placeholder
- toolbar: search box (transcription+username ILIKE), speaker filter (existing),
  channel filter, start/end date range, RESET
- new DECK/LEADERBOARD tabs: leaderboard = per-speaker clips, est duration,
  transcribed words (useRecordingsSummary → recordings.summary); click a row
  jumps the deck to that speaker's clips
- hooks: filter-driven SWR cache key (fields serialized), representative key
  matcher for delete/WS-sync across all filter views; useRecordingsSummary hook
- recordingsApi.list generalized to RecordingListParams (q/startDate/endDate)
2026-08-30 13:35:33 +07:00
asepharyana 22c09ecd28 feat(backend): recordings — expose transcription, keyword search, date range, speaker summary
- getRecent now returns transcription; new filters q (ILIKE on transcription
  + username) and startDate/endDate (created_at range, epoch ms)
- new recordings.summary: per-speaker leaderboard (clips, est_duration_s from
  128kbps bytes, transcribed words, last activity)
- oRPC list input + summary procedure wired
2026-08-30 13:31:39 +07:00
asepharyana a3a91aa2ce feat(voice): auto-detect speech language for transcription (drop forced 'en')
Whisper previously hardcoded language:en, mis-transcribing id/en-mixed
speech. Omitting 'language' makes Whisper auto-detect. Paired with enabling
AI_VOICE_TRANSCRIPTION_ENABLED (BWS secret gmw_ai_voice_transcription_enabled
= true) so new recordings are transcribed.

Spec: .hermes/plans/2026-08-30_recordings-v2-features-spec.md
2026-08-30 13:28:17 +07:00
asepharyana 75ff9274b3 feat(frontend): filter recordings per-speaker + export WAV for Audacity
- recordings page: speaker filter dropdown (server-side userId filter via
  existing recordings.list proc) + clear-filter + pagination reset on change
- WAV export per card: decode download_url via Web Audio, re-encode 16-bit PCM
  WAV (Audacity-ready) client-side, no server/ffmpeg needed
- EXPORT WAV (N) header button: concatenate visible (filtered) recordings
  into a single mono WAV for mixdown/analysis
- hooks: per-filter SWR cache key (recordings/<userId|all>); delete & live
  WS-sync invalidate every filter cache via key matcher
- new lib/audio/wav.ts: decodeAudio / audioBufferToWav / concatAudioBuffers /
  downloadBlob
2026-08-30 12:59:46 +07:00
asepharyana e3016a858a fix(voice-recording): stop missing start-of-burst audio & mid-burst splits
Root-cause fixes for 'banyak miss & terpotong' in the voice->recording flow:

- subscribe BEFORE collecting user metadata. receiver.speaking 'start' fires
  on the FIRST opus packet, and onUdpMessage forwards frames to the
  subscription only when one exists — every frame during the old
  await collectUserMetadata (a Discord REST roundtrip on cache miss) was
  dropped, cutting off the start of every burst. Now subscribe synchronously
  (guard first, no await in between), then fetch metadata in the background
  and discard the burst if the speaker turns out to be a bot.
- one segment per burst: drop the fixed 5s RECORDING_SEGMENT_MS rotation on
  the OGG path, which split continuous speech mid-word/sentence. Only the
  web-PCM decoder still rotates (bounds memory).
- finalize only once the underlying file has flushed to disk (wait on the
  write stream 'finish'), so upload/transcode reads a complete file.
- raise AfterSilence 3000->4000ms so natural pauses (thinking, interruptions)
  don't split one utterance into several recordings.
- lower the 'too short to keep' threshold 1000->300ms so brief replies
  ("ya", "siap") are kept instead of dropped.

All typecheck / biome(src/) / vitest (164) green.
2026-08-30 12:23:03 +07:00
asepharyana f0874d8634 build(discord-gateway): drop cmake/rust toolchain + prune dead @types & opusscript from bundle
- nativeBuildInputs: remove cmake, rustc, cargo, git — GMW's only native deps (@discordjs/opus, sharp) are PREBUILT, no source compile needed (cmake/rust were inherited for node-datachannel which is 9router, not GMW). python3/gnumake/gcc stay as node-gyp fallback for opus.
- pruneProd: also strip .pnpm/@types+* (pure TS decls pulled into the prod graph as real deps by discord-api-types/pg-protocol, never required at runtime) and .pnpm/opusscript@* (pure-JS fallback Opus engine that prism-media only loads IF native @discordjs/opus fails — native is always present, so opusscript is never executed).
- Verified: nix flake check OK; typecheck + biome check src/ green; runtime smoke test post-prune loads @discordjs/voice, sharp, selfbot, tiktoken, piscina and encodes a frame via native opus.
2026-08-30 11:12:01 +07:00
asepharyana 8d6b48fb4c Merge remote-tracking branch 'origin/main' 2026-08-28 22:08:06 +07:00
asepharyana 9c9cd8917e feat(discord-gateway): use Discord CDN for image analysis, uploader archive-only
- mediaDownloader: flip URL candidate order so discord_url is tried
  before uploaded_url (uploaded_url is archive-only fallback)
- ai-analysis-worker: remove upload-pending race guard that blocked
  analysis until Tele upload completed; analysis now runs immediately
  on the Discord CDN URL
- batchProcessor: remove upload-pending defer/poll-backoff logic
- individualFallbackProcessor: remove upload_pending requeue loop
- batchOutcomeClassifier/fallbackResultClassifier: drop upload_pending
  classification (no longer needed)
- tests: update batchOutcomeClassifier + fallbackResultClassifier tests
  to reflect removed upload_pending signal
2026-08-28 22:07:37 +07:00
mytheclipsebotreview[bot] 4b58a159d0 Auto-merge PR #25
build(deps-dev): bump @types/node from 26.2.0 to 26.3.0 in /services/discord-gateway in the development group
2026-08-28 14:29:21 +00:00
mytheclipsebotreview[bot] f53a2d4f4a Auto-merge PR #24
build(deps-dev): bump the development group in /services/frontend with 2 updates
2026-08-28 14:29:12 +00:00
mytheclipsebotreview[bot] 76daa7026c Auto-merge PR #23
build(deps-dev): bump @types/node from 26.2.0 to 26.3.0 in /services/backend in the development group
2026-08-28 14:29:04 +00:00
dependabot[bot] 430e3d137c build(deps-dev): bump @types/node
Bumps the development group in /services/discord-gateway with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 26.2.0 to 26.3.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-28 14:25:31 +00:00
dependabot[bot] de8b521bb4 build(deps-dev): bump the development group
Bumps the development group in /services/frontend with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [puppeteer-core](https://github.com/puppeteer/puppeteer).


Updates `@types/node` from 26.2.0 to 26.3.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `puppeteer-core` from 25.8.0 to 25.9.0
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.8.0...puppeteer-core-v25.9.0)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: puppeteer-core
  dependency-version: 25.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-28 14:25:12 +00:00
dependabot[bot] 8bd7f5a98f build(deps-dev): bump @types/node
Bumps the development group in /services/backend with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 26.2.0 to 26.3.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-28 14:25:12 +00:00
asepharyana ffbe9959ab chore: migrate AI LLM router from 9router to omniroute
Switch GMW's AI LLM base URL from 9router (https://9router.asepharyana.my.id/v1)
to omniroute on imrnes (http://100.121.180.82:20128/api/v1).

- Update default AI_LLM_BASE_URL in discord-gateway + backend config schemas
- Update .env.example documentation
- Update all 9router references in comments/docs/tests to omniroute
- Production BWS secret gmw_ai_llm_base_url already updated

Omniroute uses /api/v1 prefix (not /v1 like 9router), so the base URL
now correctly points at the right API path for the OpenAI SDK.
2026-08-28 20:18:48 +07:00
asepharyana 14bd20f072 fix(frontend): mobile navbar navigation + SSR hydration mismatch
Root cause of 'navbar mobile tak bisa pindah halaman': Next <Link>
client-side navigation is dead app-wide. A React hydration mismatch
(#418: 'server rendered text didn't match the client') is thrown by the
SSR-seeded live feeds — relative times (formatRelativeTime(e.edited_at) /
m.created_at) computed with Date.now() render slightly differently on
server vs client, which breaks the Next client router (router.push is a
no-op). The desktop NavRail worked only because it uses plain <a href>
(hard navigation bypasses the broken router).

Fixes:
- mobile-nav.tsx: use plain <a href> (NOT Next <Link>), identical to the
  working sidebar NavRail, so mobile nav always navigates regardless of
  router state ('ikuti cara kerja sidebar').
- Add suppressHydrationWarning to the SSR-seeded relative-time spans so
  server/client drift no longer throws #418 (EditHistory, LiveModerationFeed,
  messages/results + detail rows, recordings, TermGlossary,
  ChannelCultureGlossary, CategoryDrilldown).

Verified on non-prod :4024 @375px: Voice/Media/Search all navigate, no #418
in console. Plan: .hermes/plans/mobile-nav-hydration-fix.md
2026-08-28 09:57:49 +07:00
asepharyana 4d0bbed596 fix(frontend): mobile nav pills fixed width so labels don't squeeze
Use fixed w-[72px] shrink-0 pills (not flex-1) so the 7 bottom-nav items
keep their shape and scroll horizontally instead of compressing labels to
overlap; add truncating 10px mobile label (12px >=sm).
2026-08-28 01:18:23 +07:00
asepharyana ae41f64e46 fix(frontend): mobile navbar reachable to all pages + responsive form controls
- Root cause of 'navbar mobile tak bisa pindah halaman': the chatbot FAB
  (fixed right-4 bottom-5 z-50) overlapped the rightmost mobile bottom-nav
  items (z-40) and intercepted taps. Raise the FAB above the nav on mobile
  (bottom above nav, md:bottom-5 on desktop) so it never blocks nav taps.
- Mobile bottom nav now mirrors the FULL desktop sidebar (all 7 items:
  dashboard, messages, voice, media, recordings, moderation, analysis);
  horizontally scrollable + snap-to-active on narrow screens.
- Select dropdown: clamp portal position within viewport + min-width so it
  never overflows off-screen on mobile triggers near the right edge; larger
  tap targets on touch.
- Input/Textarea: text-base (16px) on touch to prevent iOS auto-zoom on
  focus, text-sm on >=sm; comfortable mobile min-height for chat input.
- Add .hermes/plans/mobile-nav-form-responsive.md spec.
2026-08-28 01:07:37 +07:00
asepharyana e7b0f02620 Merge pull request #22 from asepharyana/dependabot/npm_and_yarn/services/frontend/development-beb2655a33
build(deps-dev): bump @types/react-dom from 19.2.4 to 19.2.5 in /services/frontend in the development group
2026-08-28 00:34:13 +07:00
mytheclipsebotreview[bot] 120aa95818 Auto-merge PR #21
build(deps): bump lucide-react from 1.33.0 to 1.34.0 in /services/frontend in the production group
2026-08-27 14:27:50 +00:00
dependabot[bot] 870f7546fb build(deps-dev): bump @types/react-dom
Bumps the development group in /services/frontend with 1 update: [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom).


Updates `@types/react-dom` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-27 14:25:18 +00:00
dependabot[bot] 565b3da086 build(deps): bump lucide-react
Bumps the production group in /services/frontend with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 1.33.0 to 1.34.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.34.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-27 14:24:50 +00:00
asepharyana a54c34c6b3 fix: enhance accessibility for Select component with ARIA attributes 2026-08-27 18:09:29 +07:00
asepharyana 7375e2ed52 fix: add tooltip functionality to NavItem with portal rendering 2026-08-27 18:04:46 +07:00
asepharyana 5196cb0221 fix: edit history 'after' showed original content, not edited content
getRecentEdits SELECT ... m.content AS new_content returned the message's
ORIGINAL content (never updated on edit) instead of the post-edit content.
messages.content stores the original body; the current/last-edited body lives
in messages.edited_content. So before===after in the Message Edits diff.

Fix: COALESCE(m.edited_content, m.content) AS new_content so 'After' shows the
edited text and diffs against the captured before-content are meaningful.
2026-08-27 17:28:24 +07:00
asepharyana 631b5e1027 fix: make gateway migrations idempotent + self-heal drizzle history
Prevent recurring infinite restart loop (389x crash) caused by drizzle
re-applying already-applied migrations when public.__drizzle_migrations
tracking is empty/partial.

- 0017/0018: ADD COLUMN IF NOT EXISTS (re-run safe)
- 0019: DO-block rename that handles all prior states (server_name-only,
  both columns, or server_nick-only) so it never errors or double-renames
- seedDrizzleHistory: reconcile tracked created_at to the journal's latest
  'when' when the schema already reflects the latest migration, instead of
  early-returning on an existing-but-empty/partial tracking table
2026-08-27 15:05:30 +07:00
asepharyana 8049b70738 fix: render server nick per user in messages tab and analysis
Extract server_nick from metadata.member.displayName in backend messageMapper,
add server_nick to frontend MessageRecord type, and update messages view +
analysis view to display the member's server-specific nickname (with @username
as secondary context) instead of the global username.
2026-08-27 14:38:05 +07:00
asepharyana ea23c405fa fix: capture server nickname (member displayName) per action
Rename server_name (guild name) to server_nick and populate it from
the member's server-specific display name (metadata.member.displayName)
at write time. This is what the moderation dashboard should show as
TARGET — e.g. server nick 'Bandar Togel「✔ ᵛᵉʳᶦᶠᶦᵉᵈ 」' for global
username '.nichiyobi'. Backfilled 210 existing actions from messages
metadata (reset_nickname rows now show 'Sarjana .jav', 'Penindas
Minoritas', etc). Frontend TARGET shows server nick with global
username as secondary context.
2026-08-27 13:50:37 +07:00
asepharyana 4991164591 fix: use AI for global username check instead of keyword list
Replace static OFFENSIVE_USERNAME_KEYWORDS substring matching with a
lightweight LLM call that evaluates whether a global username violates
server rules (gambling, scam, NSFW, SARA, etc). Fail-open design:
if the LLM call fails/times out, the nickname reset still completes.
2026-08-27 13:20:20 +07:00
asepharyana 1590479f58 fix: replace global username if also offensive after nickname reset
After resetting an offensive server nickname to the global username,
check the global username against gambling/scam keyword list. If it
also violates, generate a random 'UserXXXXX' nickname to prevent
circumvention via offensive global usernames.
2026-08-27 12:46:33 +07:00
asepharyana 0de393625f fix: add server_name to moderation_actions for full context retention
Denormalize guild name alongside username so the moderation dashboard
shows both TARGET and server even after message table purges.
Migration 0018. Frontend displays 'username · server_name' in TARGET.
2026-08-27 12:32:12 +07:00
asepharyana cccfd89266 fix: store username on moderation_actions for retention safety
Add denormalized  column to moderation_actions so the
dashboard TARGET field survives message table purges. Backfills all
existing 217 rows. Changes: schema + autoDeleteManager + backend
repository query + migration 0017.
2026-08-27 12:15:06 +07:00
asepharyana 17aad2cc39 chore: biome lint cleanup — formatting, unused imports, exhaustive-deps fixes
- Backend: fix redis-bridge.ts import formatting
- Frontend: fix formatting in messages/view, recordings/view, ambient-canvas, voice-stage
- Frontend: remove unused imports/vars in EditHistory, ambient-canvas
- Frontend: fix import sorting in LiveModerationFeed
- Frontend: suppress caller-controlled exhaustive-deps in use-gsap-animation
- All services: lint clean, typecheck pass, build pass
- Backend e2e tests excluded (require live server, expected 404s)
2026-08-27 09:39:33 +07:00
asepharyana a6e2c1fa4f voice: deep stability audit — FFmpeg crash recovery, activity timeout, reconnect refresh
Gateway (transmitter.ts):
- Auto-stop on FFmpeg crash: non-zero exit triggers stop() to prevent
  silent audio loss and resource leaks
- Voice activity timeout (10s): auto-stops transmitter when no PCM
  received, preventing dead-air CPU waste on backgrounded tabs
- Stderr cap (4KB): prevents unbounded memory growth in long sessions

Gateway (voice.handler.ts):
- Double-check voiceController.getStatus().connected before starting
  transmitter — detects stale player state after gateway disconnect

Frontend (context.tsx):
- Force-refetch voice status on WS reconnect — UI converges in <1s
  instead of waiting up to 4s for SWR poll interval

All: tsc clean, biome clean
2026-08-26 23:48:36 +07:00
asepharyana 7e0d0d5123 voice: audit + noise suppression toggle + stability fixes
Gateway:
- transmitter.ts: cap backpressure queue at 500 chunks (prevents memory leak)
- transmitter.ts: fix Redis race — assign redisSub AFTER subscribe completes
- voice.handler.ts: static import Redis instead of dynamic (cleaner, no eval)

Frontend:
- mic-transmit.ts: MicAccessError with specific reasons (permission-denied, no-mic, timeout)
- mic-transmit.ts: noiseSuppression option in getUserMedia constraints
- mic-transmit.ts: proper DOMException handling for all getUserMedia failure modes
- use-voice.ts: noiseSuppression state + toggleNoiseSuppression exposed
- use-voice.ts: cleanup on unmount (stops transmitter, clears refs)
- voice/view.tsx: noise suppression toggle button (ShieldCheck/ShieldOff icons)
- voice/view.tsx: improved mic error toasts (permission denied / no mic specific)
- voice/view.tsx: NS status in codec footer (NS_ACTIVE when enabled)
2026-08-26 23:32:37 +07:00
asepharyana 4f4f92706c feat(voice): implement stale speaker management and clear functionality 2026-08-26 23:16:57 +07:00
asepharyana 611ba39973 refactor: replace GSAP animations with CSS animations across components
- Updated RootLayout to include a noise overlay class.
- Refactored LiveModerationFeed to use CSS animations for item entry.
- Simplified AmbientCanvas by removing WebGL and using pure CSS for ambient effects.
- Converted Chatbot to use CSS for floating window animations.
- Updated Card component to include a gradient border.
- Refactored PageTransition to use CSS for fade-scale animations.
- Enhanced SectionHeader with reveal-up animation class.
- Replaced GSAP animations in NavRail with CSS animations for item entry.
- Refactored VoiceStage to use CSS for speaker node animations and pulse rings.
- Removed GSAP dependencies from use-gsap-animation hook, implementing CSS-based stagger reveal.
2026-08-26 19:43:57 +07:00
asepharyana 192685e1ce feat(layout): add theme script to prevent light mode flash during hydration 2026-08-26 18:33:30 +07:00
asepharyana cacebfd94e feat(messages): enhance message and edit history with channel names and content diffs 2026-08-26 18:26:15 +07:00
asepharyana 709074935f style: fix biome formatting after audit fixes 2026-08-26 18:12:02 +07:00
asepharyana 9f02edd646 perf+fix(ai-moderation): 11 pipeline optimizations from audit
Audit of the full AI analysis flow found 14 issues; 11 fixed, 3 deferred:

Fixed:
1. batchProcessor: skip scheduleAutoDelete for error-status rows (was
   causing wasted not_eligible logs for every parse/API failure)
2. textBatchProcessor: domain dedup in URL fetch (max 3 URLs per domain
   to avoid rate-limiting from concentrated domains)
3. llmCaller: move parseModerationResponse import to top-level (was
   dynamic-imported inside retry loop — unnecessary overhead per retry)
4. llmCaller: make default max_tokens configurable via
   AI_LLM_MAX_COMPLETION_TOKENS env (default 16384)
5. moderationOrchestrator: log cache write errors instead of silent
   .catch(() => {}) — surface intermittent Redis failures
6. conversationContext: batch token estimation via estimateTokensBatch
   (single tiktoken encode call for all target lines, ~5x faster)
7. aiAnalyzer: skip revertStuckProcessingMessages DB query when no
   conversations are actively processing (avoids idle-state query)
8. textBatchProcessor: cache corrected few-shot examples per hour
   (was re-queried from DB on every batch)
9. textBatchProcessor: preserve partial results on sub-batch timeout
   (was throwing and discarding all prior sub-batch results)
10. batchProcessor switch: skip 'completed' messages from individual
    fallback queue (prevents redundant re-analysis + double-delete)
11. autoDeleteManager: expand isAlreadyDeletedError to catch Discord
    codes 10003/50001 + text fallback matching

Deferred (not regressions, larger refactors):
- #8 batchScheduler debounce race: not actually a race (JS single-threaded)
- #11 initCacheStore: already has idempotency guard
- #13 individual fallback batching: requires worker pool refactor

7 files changed, 73 insertions(+), 32 deletions(-)
2026-08-26 18:08:24 +07:00
asepharyana 5fc3cf86d5 perf(backend): parallelize getMessageById DB queries + drop unused indexes
- getMessageById: run findById and getEditHistory in parallel via
  Promise.all instead of sequential awaits (halves latency for
  message detail view)
- Drop 3 unused indexes on messages table: idx_messages_guild_ai_status_created
  (0 scans), idx_messages_guild_ai_status_analyzed (97 scans),
  idx_messages_guild_created_deleted (95 scans, superseded by covering index)
  — saves ~5.9MB index space + reduces write amplification
- Add covering index idx_messages_guild_created_covering for the primary
  findMany query pattern (guild_id + created_at DESC with INCLUDE columns)
2026-08-26 17:48:10 +07:00
asepharyana e81391484b feat(messages): enhance message rendering with metadata parsing and inline image support 2026-08-26 17:43:01 +07:00
asepharyana 3b8fe1b1c3 refactor(select): enhance dropdown positioning and implement portal for improved rendering 2026-08-26 17:37:20 +07:00
asepharyana 54e7220d06 fix(auto-delete): prevent double-processing + improve error classification
Two bugs causing 23 spurious 'error' logs after successful deletions:

1. batchProcessor switch missing 'completed' case: partitionBatchOutcome
   returns 'completed' for successful messages, but the switch only handled
   'upload_pending' and 'api_failed'. Successful messages fell through to
   default → re-enqueued to individual fallback → re-analyzed → re-delete
   attempt → error (message already gone from Discord). Now explicitly
   skips 'completed' messages.

2. isAlreadyDeletedError only caught codes 10008/404. Discord also returns
   10003 (Unknown Channel) and 50001 (Missing Access) when a message or
   channel is gone. Added these codes plus text-based fallback matching
   'Unknown Message'/'Unknown Channel'.

Impact: eliminates ~23 redundant error logs per day + stops wasted LLM
calls re-analyzing already-processed messages.
2026-08-26 17:31:37 +07:00
asepharyana 46d889271e fix(auto-delete): accept 'warn' recommendedAction + delete flagged+medium
isEligibleForAutoDelete was rejecting messages where recommendedAction
was 'warn' or 'review' — only 'delete' and 'escalate' were accepted.
This caused 28+ medium-severity flagged messages to be logged as
'not_eligible' instead of being auto-deleted.

Changes:
- deriveRecommendedAction: return 'delete' for flagged+medium severity
  (previously only critical/high triggered delete; medium got 'review')
- isEligibleForAutoDelete: accept 'warn' as valid recommendedAction
  alongside 'delete' and 'escalate'

Impact: ~28 pending medium-severity flagged messages + all future
'warn'-action flagged messages will now be eligible for auto-deletion.
2026-08-26 17:16:30 +07:00
asepharyana 0c3a82ad77 refactor: update recordings handling with pagination support and improve infinite scroll functionality 2026-08-26 17:05:41 +07:00
asepharyana b640079cb6 refactor: enhance message streaming and UI components; add SWR provider 2026-08-26 16:51:46 +07:00
asepharyana 37d15456dc refactor: remove unnecessary PageTransition wrappers from dashboard pages 2026-08-26 15:49:58 +07:00
asepharyana 153e628aba refactor: update component styles to use theme-aware colors and improve UI consistency 2026-08-26 15:43:43 +07:00
asepharyana a5f908dc06 refactor: update UI components for consistency and improved styling
- Refactored ChannelCultureGlossary, LiveModerationFeed, TermGlossary, and Chatbot components to use new design tokens and styles.
- Updated button, badge, and section components to align with the new design system.
- Enhanced the visual hierarchy and accessibility of various UI elements.
- Improved responsiveness and hover states across components.
- Replaced hardcoded colors with design tokens for better maintainability.
2026-08-26 14:57:08 +07:00
asepharyana 7fc36170c8 chore: update @types/node version to 26.2.0 in package.json and pnpm-lock.yaml 2026-08-26 13:51:54 +07:00
asepharyana ccfe769b2f Refactor moderation and recordings views; enhance UI components and improve performance
- Cleaned up imports and removed unused hooks in ModerationView and RecordingsView.
- Updated UI elements for better visual consistency and accessibility.
- Improved performance by optimizing rendering logic and reducing unnecessary state updates.
- Added filtering functionality in ChannelCultureGlossary and TermGlossary components.
- Enhanced LiveModerationFeed with GSAP animations for smoother transitions.
- Updated chatbot component for better user experience and responsiveness.
- Refined NavRail component for cleaner code and improved navigation item rendering.
2026-08-26 10:39:00 +07:00
asepharyana 3b688e0d0f Refactor code structure for improved readability and maintainability 2026-08-26 10:13:40 +07:00
asepharyana 29f59fd91f feat(frontend): revamp UI to Linear Precision Clean Dark with GSAP micro-interactions 2026-08-26 10:12:42 +07:00
asepharyana c3165ed702 chore: update dependabot-auto-merge.yml 2026-08-26 08:53:22 +07:00
asepharyana c72fadbbb4 chore: update dependabot.yml 2026-08-26 08:53:01 +07:00
Asep Hariyana 613840e2d1 chore(discord-gateway): update and sync lockfile for bun 2026-08-26 09:51:26 +08:00
asepharyana 24336186dc feat(frontend): add HUD header and GSAP accordion reveal to glossary route 2026-08-25 23:46:36 +07:00
asepharyana 6b66863897 feat(frontend): add HUD header, count-up gauge, and stagger reveal to analysis route 2026-08-25 23:41:39 +07:00
asepharyana fc40609a3a feat(frontend): add tactical HUD header and GSAP stagger deck to recordings route 2026-08-25 23:36:45 +07:00
asepharyana e157ae3ce4 feat(frontend): add alert-priority HUD header with GSAP pulse to moderation route 2026-08-25 23:32:21 +07:00
asepharyana 3f4c05f5bb feat(frontend): add tactical HUD header and scan-line reveal to messages feed 2026-08-25 23:28:11 +07:00
asepharyana 9c25e0887c feat(frontend): add tactical HUD header and GSAP speaking-pulse loop to voice route 2026-08-25 23:23:19 +07:00
asepharyana 2aa12b9ec5 feat(frontend): revamp channels into signal-map roster with GSAP stagger 2026-08-25 23:17:39 +07:00
asepharyana cab451f0e5 feat(frontend): revamp media frequency deck with GSAP animations and tactical visualizer 2026-08-25 23:05:05 +07:00
asepharyana 183a51003a feat(frontend): revamp dashboard into tactical HUD and integrate GSAP reactive stages 2026-08-25 22:59:29 +07:00
asepharyana f34811e9b6 Delete .github/workflows/flakehub-publish-rolling.yaml 2026-08-25 22:51:25 +07:00
asepharyana 2c30140ddd feat(frontend): migrate animations to GSAP and update client socket types for Next 16.3 2026-08-25 22:47:25 +07:00
asepharyana 7e92de63d6 feat(frontend): migrate animations to GSAP and add useGSAP lifecycle hooks 2026-08-25 22:41:41 +07:00
mytheclipsebotreview[bot] 68be4352da Auto-merge PR #18
build(deps-dev): bump tsx from 4.23.1 to 4.23.12 in /services/backend
2026-08-25 14:30:28 +00:00
mytheclipsebotreview[bot] 754ef78ff1 Auto-merge PR #20
build(deps-dev): bump tsx from 4.23.1 to 4.23.12 in /services/discord-gateway
2026-08-25 14:30:18 +00:00
mytheclipsebotreview[bot] 2959744e0c Auto-merge PR #19
build(deps-dev): bump @types/three from 0.180.0 to 0.185.4 in /services/frontend
2026-08-25 14:30:09 +00:00
dependabot[bot] cffa72c21f build(deps-dev): bump tsx in /services/discord-gateway
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.23.1 to 4.23.12.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.1...v4.23.12)

---
updated-dependencies:
- dependency-name: tsx
  dependency-version: 4.23.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 14:26:03 +00:00
dependabot[bot] d64c5ba7b6 build(deps-dev): bump @types/three in /services/frontend
Bumps [@types/three](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/three) from 0.180.0 to 0.185.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/three)

---
updated-dependencies:
- dependency-name: "@types/three"
  dependency-version: 0.185.4
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 14:25:57 +00:00
dependabot[bot] 87851f7a71 build(deps-dev): bump tsx from 4.23.1 to 4.23.12 in /services/backend
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.23.1 to 4.23.12.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.1...v4.23.12)

---
updated-dependencies:
- dependency-name: tsx
  dependency-version: 4.23.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 14:25:55 +00:00
asepharyana 91de43a6c6 fix: switch GMW AI source from omniroute to 9router (model alias revert)
- Switch AI_LLM_BASE_URL from omniroute.imrnes.team to 9router.asepharyana.my.id
- Keep AI_LLM_MODEL as 'text' (9router uses alias-based routing, not bare names)
- Update .env.example comments to document 9router
- Per user: multimodal stays 'multimodal' alias

API verified: curl to 9router/v1/chat/completions with model 'text'
returns HTTP 200 (OpenAI-compatible format)
2026-08-25 20:36:24 +07:00
asepharyana f8fc08de41 fix: switch GMW AI source from omniroute to 9router (model alias revert)
- Switch AI_LLM_BASE_URL from omniroute.imrnes.team to 9router.asepharyana.my.id
- Keep AI_LLM_MODEL as 'text' (9router uses alias-based routing, not bare names)
- Update .env.example comments to document 9router
- Per user: multimodal stays 'multimodal' alias

API verified: curl to 9router/v1/chat/completions with model 'text'
returns HTTP 200 (OpenAI-compatible format)
2026-08-25 20:36:08 +07:00
asepharyana dfa69e9f45 feat: add dependabot auto-merge workflow 2026-08-25 20:33:07 +07:00
asepharyana 0bd4b4075e Merge pull request #17 from asepharyana/feat/fe-error-handling-state
fix: switch GMW AI source from omniroute to 9router
2026-08-25 20:27:10 +07:00
asepharyana b679a02cb9 Merge pull request #16 from asepharyana/dependabot/npm_and_yarn/services/frontend/typescript-7.0.2
build(deps-dev): bump typescript from 5.9.3 to 7.0.2 in /services/frontend
2026-08-25 20:05:31 +07:00
mytheclipsebotreview[bot] 4ed1d3c053 Auto-merge PR #6
build(deps-dev): bump typescript from 5.9.3 to 7.0.2 in /services/backend
2026-08-25 11:36:10 +00:00
dependabot[bot] 8dbcc2b527 build(deps-dev): bump typescript in /services/frontend
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 7.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v7.0.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:32:04 +00:00
dependabot[bot] bda210475d build(deps-dev): bump typescript in /services/backend
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 7.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v7.0.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:31:51 +00:00
mytheclipsebotreview[bot] 9b78abaf3d Auto-merge PR #7
build(deps): bump the production group across 1 directory with 8 updates
2026-08-25 11:29:54 +00:00
mytheclipsebotreview[bot] 53c4158511 Auto-merge PR #10
build(deps-dev): bump @types/node from 20.19.43 to 26.2.0 in /services/frontend
2026-08-25 11:29:43 +00:00
mytheclipsebotreview[bot] a2a8c824bf Auto-merge PR #13
build(deps-dev): bump @biomejs/biome from 2.2.0 to 2.5.10 in /services/frontend
2026-08-25 11:29:26 +00:00
mytheclipsebotreview[bot] dec548fa2f Auto-merge PR #4
build(deps-dev): bump @types/node from 25.9.5 to 26.2.0 in /services/backend
2026-08-25 11:29:18 +00:00
mytheclipsebotreview[bot] 12033e5537 Auto-merge PR #11
build(deps-dev): bump typescript from 5.9.3 to 7.0.2 in /services/discord-gateway
2026-08-25 11:29:10 +00:00
dependabot[bot] d06caa7e58 build(deps): bump the production group across 1 directory with 8 updates
Bumps the production group with 8 updates in the /services/discord-gateway directory:

| Package | From | To |
| --- | --- | --- |
| [ioredis](https://github.com/redis/ioredis) | `5.11.1` | `6.0.0` |
| [openai](https://github.com/openai/openai-node) | `6.49.0` | `7.5.0` |
| [opusscript](https://github.com/abalabahaha/opusscript) | `0.0.8` | `0.1.1` |
| [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) | `8.22.0` | `8.23.0` |
| [pino](https://github.com/pinojs/pino) | `9.14.0` | `10.3.1` |
| [piscina](https://github.com/piscinajs/piscina) | `5.3.0` | `5.3.1` |
| [sharp](https://github.com/lovell/sharp) | `0.34.5` | `0.35.3` |
| [ws](https://github.com/websockets/ws) | `8.21.1` | `8.21.3` |



Updates `ioredis` from 5.11.1 to 6.0.0
- [Release notes](https://github.com/redis/ioredis/releases)
- [Changelog](https://github.com/redis/ioredis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/redis/ioredis/compare/v5.11.1...v6.0.0)

Updates `openai` from 6.49.0 to 7.5.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v6.49.0...v7.5.0)

Updates `opusscript` from 0.0.8 to 0.1.1
- [Release notes](https://github.com/abalabahaha/opusscript/releases)
- [Commits](https://github.com/abalabahaha/opusscript/compare/0.0.8...0.1.1)

Updates `pg` from 8.22.0 to 8.23.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.23.0/packages/pg)

Updates `pino` from 9.14.0 to 10.3.1
- [Release notes](https://github.com/pinojs/pino/releases)
- [Commits](https://github.com/pinojs/pino/compare/v9.14.0...v10.3.1)

Updates `piscina` from 5.3.0 to 5.3.1
- [Release notes](https://github.com/piscinajs/piscina/releases)
- [Changelog](https://github.com/piscinajs/piscina/blob/v5.3.1/CHANGELOG.md)
- [Commits](https://github.com/piscinajs/piscina/compare/v5.3.0...v5.3.1)

Updates `sharp` from 0.34.5 to 0.35.3
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.34.5...v0.35.3)

Updates `ws` from 8.21.1 to 8.21.3
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.21.1...8.21.3)

---
updated-dependencies:
- dependency-name: ioredis
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production
- dependency-name: openai
  dependency-version: 7.5.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production
- dependency-name: opusscript
  dependency-version: 0.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: pg
  dependency-version: 8.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: pino
  dependency-version: 10.3.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production
- dependency-name: piscina
  dependency-version: 5.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: sharp
  dependency-version: 0.35.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: ws
  dependency-version: 8.21.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:24:34 +00:00
dependabot[bot] 173ae3c61c build(deps-dev): bump @types/node in /services/frontend
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.19.43 to 26.2.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.2.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:23:30 +00:00
dependabot[bot] 2637bbc549 build(deps-dev): bump @biomejs/biome in /services/frontend
Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.2.0 to 2.5.10.
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.10/packages/@biomejs/biome)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.10
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:23:19 +00:00
dependabot[bot] 6fc68126ac build(deps-dev): bump @types/node in /services/backend
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.5 to 26.2.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.2.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:23:08 +00:00
dependabot[bot] 28d09a24c9 build(deps-dev): bump typescript in /services/discord-gateway
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 7.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v7.0.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:22:50 +00:00
mytheclipsebotreview[bot] f2ec642a0e Auto-merge PR #12
build(deps-dev): bump puppeteer-core from 25.7.0 to 25.8.0 in /services/frontend
2026-08-25 11:21:41 +00:00
mytheclipsebotreview[bot] 8a491b9ec2 Auto-merge PR #8
build(deps): bump the production group in /services/frontend with 7 updates
2026-08-25 11:20:56 +00:00
mytheclipsebotreview[bot] 7d54d3a660 Auto-merge PR #2
build(deps): bump the production group in /services/backend with 4 updates
2026-08-25 11:20:48 +00:00
mytheclipsebotreview[bot] 21509c25ff Auto-merge PR #15
build(deps-dev): bump @types/node from 25.9.5 to 26.2.0 in /services/discord-gateway
2026-08-25 11:20:35 +00:00
asepharyana 09724287cb feat: update dependabot config 2026-08-25 18:16:49 +07:00
dependabot[bot] 1ac3d50315 build(deps-dev): bump @types/node in /services/discord-gateway
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.5 to 26.2.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.2.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:15:57 +00:00
dependabot[bot] 9c48d50d25 build(deps-dev): bump puppeteer-core in /services/frontend
Bumps [puppeteer-core](https://github.com/puppeteer/puppeteer) from 25.7.0 to 25.8.0.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.7.0...puppeteer-core-v25.8.0)

---
updated-dependencies:
- dependency-name: puppeteer-core
  dependency-version: 25.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:15:28 +00:00
dependabot[bot] 8bd3ec8e83 build(deps): bump the production group
Bumps the production group in /services/frontend with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.28.0` | `1.33.0` |
| [motion](https://github.com/motiondivision/motion) | `12.43.0` | `13.1.1` |
| [next](https://github.com/vercel/next.js) | `16.2.12` | `16.3.2` |
| [react](https://github.com/react/react/tree/HEAD/packages/react) | `19.2.4` | `19.2.8` |
| [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom) | `19.2.4` | `19.2.8` |
| [swr](https://github.com/vercel/swr) | `2.4.2` | `2.5.1` |
| [three](https://github.com/mrdoob/three.js) | `0.180.0` | `0.185.1` |


Updates `lucide-react` from 1.28.0 to 1.33.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.33.0/packages/lucide-react)

Updates `motion` from 12.43.0 to 13.1.1
- [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/motiondivision/motion/compare/v12.43.0...v13.1.1)

Updates `next` from 16.2.12 to 16.3.2
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.2.12...v16.3.2)

Updates `react` from 19.2.4 to 19.2.8
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.2.8/packages/react)

Updates `react-dom` from 19.2.4 to 19.2.8
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.2.8/packages/react-dom)

Updates `swr` from 2.4.2 to 2.5.1
- [Release notes](https://github.com/vercel/swr/releases)
- [Commits](https://github.com/vercel/swr/compare/v2.4.2...v2.5.1)

Updates `three` from 0.180.0 to 0.185.1
- [Release notes](https://github.com/mrdoob/three.js/releases)
- [Commits](https://github.com/mrdoob/three.js/commits)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 1.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: motion
  dependency-version: 13.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production
- dependency-name: next
  dependency-version: 16.3.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: react
  dependency-version: 19.2.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: react-dom
  dependency-version: 19.2.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: swr
  dependency-version: 2.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: three
  dependency-version: 0.185.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:15:04 +00:00
dependabot[bot] 5a4a203f01 build(deps): bump the production group
Bumps the production group in /services/backend with 4 updates: [ioredis](https://github.com/redis/ioredis), [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg), [pino](https://github.com/pinojs/pino) and [ws](https://github.com/websockets/ws).


Updates `ioredis` from 5.11.1 to 6.0.0
- [Release notes](https://github.com/redis/ioredis/releases)
- [Changelog](https://github.com/redis/ioredis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/redis/ioredis/compare/v5.11.1...v6.0.0)

Updates `pg` from 8.22.0 to 8.23.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.23.0/packages/pg)

Updates `pino` from 9.14.0 to 10.3.1
- [Release notes](https://github.com/pinojs/pino/releases)
- [Commits](https://github.com/pinojs/pino/compare/v9.14.0...v10.3.1)

Updates `ws` from 8.21.1 to 8.21.3
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.21.1...8.21.3)

---
updated-dependencies:
- dependency-name: ioredis
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production
- dependency-name: pg
  dependency-version: 8.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
- dependency-name: pino
  dependency-version: 10.3.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production
- dependency-name: ws
  dependency-version: 8.21.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 11:14:06 +00:00
asepharyana 7f0aa5a7ad feat: dependabot config with multi-dir (backend, gateway, frontend) 2026-08-25 18:12:10 +07:00
asepharyana eb0be981c6 feat: add dependabot config (npm) 2026-08-25 18:07:58 +07:00
mytheclipsebotreview[bot] 466e117bd5 Auto-merge PR #1
feat(fe): optimize error handling, state consistency, and WS feedback
2026-08-25 08:36:58 +00:00
270 changed files with 26991 additions and 14050 deletions
+12 -28
View File
@@ -18,31 +18,12 @@ MONITOR_GUILD_ID=your_guild_id_here # Target guild for text monitoring
TEXT_GUILD_ID=optional_text_guild_id # Override text capture guild (falls back to MONITOR_GUILD_ID)
TEXT_CHANNEL_ID=optional_text_channel_id # Restrict text capture to a single channel
# === Voice Channels ===
# VOICE_GUILD_ID= # Guild for voice connection (optional)
# VOICE_CHANNEL_ID= # Channel for voice connection (optional)
# === Recording ===
RECORDINGS_DIR=./recordings # Audio file output directory (default: ./recordings)
RECORDING_SEGMENT_MS=5000 # OGG segment duration in ms (default: 5000)
# === Decoder ===
DECODER_ROTATE_MS=5000 # Opus decoder rotation interval in ms (default: 5000)
DECODER_COOLDOWN_MS=30000 # Decoder error cooldown in ms (default: 30000)
# === Audio ===
AUDIO_STREAM_SILENCE_DURATION_MS=3000 # Silence threshold in ms before stopping stream (default: 3000)
PACKET_FILTER_MIN_SIZE=8 # Minimum Opus packet size in bytes (default: 8)
OPUS_FRAME_SIZE=960 # Opus frame size in samples (default: 960)
AUDIO_SAMPLE_RATE=48000 # Audio sample rate in Hz (default: 48000)
AUDIO_CHANNELS=2 # Number of audio channels (default: 2)
AVATAR_SIZE=64 # User avatar size in pixels (default: 64)
# === Webserver ===
WEBSERVER_PORT=4001 # Backend HTTP/WS server port (default: 4001)
# === Connection ===
VOICE_CONNECTION_TIMEOUT_MS=15000 # Voice connection timeout in ms (default: 15000)
RECONNECT_TIMEOUT_MS=5000 # Reconnect timeout in ms (default: 5000)
# === Logging ===
@@ -69,11 +50,6 @@ POSTGRES_POOL_MAX=10 # Maximum pool connections (default: 10)
# === Redis ===
REDIS_URL=redis://100.121.180.82:6379 # Redis connection string (default: redis://localhost:6379)
# === Voice PCM WebSocket (direct gateway→backend, bypasses Redis) ===
VOICE_PCM_WS_ENABLED=true # Use direct WS for PCM audio (default: true)
BACKEND_WS_URL=ws://backend:4001/ws # Backend WebSocket URL for gateway PCM streaming
BACKEND_WS_TOKEN= # REQUIRED if VOICE_PCM_WS_ENABLED=true. Internal shared secret
# === Attachments ===
TELE_UPLOAD_URL=https://upload.asepharyana.my.id/api/upload # Attachment upload endpoint (default)
ATTACHMENT_UPLOAD_TIMEOUT_MS=30000 # Upload timeout in ms (default: 30000)
@@ -85,8 +61,8 @@ BACKLOG_SYNC_BATCH_SIZE=100 # Messages per backlog batch, max 100 (d
# === AI Analysis ===
AI_ANALYSIS_ENABLED=false # Enable AI content moderation (default: false)
AI_LLM_API_KEY= # REQUIRED if AI_ANALYSIS_ENABLED=true. LLM API key
AI_LLM_BASE_URL=https://9router.asepharyana.my.id/v1 # LLM API base URL (9router — OpenAI-compatible router, replaces omniroute)
AI_LLM_MODEL=claude-opus-5 # LLM text model name (default: claude-opus-5)
AI_LLM_BASE_URL=http://100.121.180.82:20128/api/v1 # LLM API base URL (omniroute — OpenAI-compatible router on imrnes, Tailscale 100.121.180.82)
AI_LLM_MODEL=text # LLM text model name (default: text)
# AI_LLM_VISION_MODEL= # Vision model for image analysis (falls back to AI_LLM_MODEL)
# AI_LLM_EMBEDDING_MODEL= # Embedding model for semantic moderation cache (optional; enables near-duplicate text reuse to save LLM calls)
# AI_LLM_EMBEDDING_MIN_SIMILARITY=0.97 # Min cosine similarity to reuse a cached verdict (default: 0.97)
@@ -98,6 +74,12 @@ AI_LLM_IMAGE_MAX_DIMENSION=1024 # Max image dimension in pixels before r
AI_LLM_TEXT_BATCH_SIZE=20 # Max messages per text-only moderation batch (default: 20)
AI_LLM_MEDIA_ANALYSIS_TIMEOUT_MS=60000 # Timeout in ms for media analysis calls (default: 60000)
AI_LLM_TEXT_ANALYSIS_TIMEOUT_MS=30000 # Timeout in ms for text-only analysis calls (default: 30000)
AI_LLM_JEV_ENABLED=false # Use TypeSafe Jev (System One) as PRIMARY text analyzer; LLM is fallback
AI_LLM_JEV_API_KEY= # REQUIRED if AI_LLM_JEV_ENABLED=true. 9router/TypeSafe API key for /v1/systemone
AI_LLM_JEV_BASE_URL=http://127.0.0.1:4014 # 9router base URL (default: local 9router; prod: https://9router.asepharyana.my.id)
AI_LLM_JEV_MODEL=oc/jev-1.13-free # Jev model id (default: oc/jev-1.13-free)
AI_LLM_JEV_TIMEOUT_MS=45000 # Timeout in ms for a Jev systemone batch call (default: 45000)
AI_LLM_JEV_MIN_CONFIDENCE=0.9 # Min status-choice confidence to accept a Jev verdict (default: 0.9)
# === AI Analysis Tuning ===
AI_ANALYSIS_DEBOUNCE_MS=500 # Debounce window for batching messages in ms (default: 500)
@@ -126,7 +108,6 @@ AUTO_DELETE_LOG_CHANNEL_ID= # Channel ID to log auto-delete actions
# === Retention (0 = disabled) ===
RETENTION_MESSAGES_DAYS=0 # Message retention in days (default: 0 = off)
RETENTION_ATTACHMENTS_DAYS=0 # Attachment retention in days (default: 0 = off)
RETENTION_VOICE_DAYS=0 # Voice recording retention in days (default: 0 = off)
RETENTION_CLEANUP_INTERVAL_MS=86400000 # Cleanup interval in ms (default: 24h)
RETENTION_DRY_RUN=true # Dry-run: log but do not delete (default: true)
@@ -134,4 +115,7 @@ RETENTION_DRY_RUN=true # Dry-run: log but do not delete (defaul
AUTO_MIGRATE_ON_STARTUP=true # Run database migrations on startup (default: true)
# === Worker Pool ===
# PISCINA_MAX_THREADS=4 # Worker thread pool size (optional, defaults to CPU cores)
# Text and media AI-analysis batches run on separate Piscina pools so a slow
# image/vision batch can never queue-block fast text-only batches.
# PISCINA_MAX_THREADS=4 # Text-analysis worker pool size (optional, defaults to CPU cores)
# PISCINA_MEDIA_MAX_THREADS=2 # Media-analysis worker pool size (optional, default 2)
+17
View File
@@ -0,0 +1,17 @@
name: Dependabot Auto-Merge
on: pull_request
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Enable auto-merge for Dependabot PR
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+38
View File
@@ -0,0 +1,38 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/services/backend"
schedule:
interval: "daily"
open-pull-requests-limit: 10
groups:
production:
dependency-type: "production"
update-types: ["minor", "patch"]
development:
dependency-type: "development"
update-types: ["minor", "patch"]
- package-ecosystem: "npm"
directory: "/services/discord-gateway"
schedule:
interval: "daily"
open-pull-requests-limit: 10
groups:
production:
dependency-type: "production"
update-types: ["minor", "patch"]
development:
dependency-type: "development"
update-types: ["minor", "patch"]
- package-ecosystem: "npm"
directory: "/services/frontend"
schedule:
interval: "daily"
open-pull-requests-limit: 10
groups:
production:
dependency-type: "production"
update-types: ["minor", "patch"]
development:
dependency-type: "development"
update-types: ["minor", "patch"]
@@ -1,20 +0,0 @@
name: Publish to FlakeHub
on:
push:
branches: [main, master]
workflow_dispatch:
jobs:
flakehub-publish:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6
- uses: DeterminateSystems/determinate-nix-action@main
- uses: DeterminateSystems/flakehub-push@main
with:
visibility: public
rolling: true
+8 -3
View File
@@ -1,20 +1,25 @@
node_modules
/recordings
.env
dist/
services/discord-gateway/dist/
services/backend/dist/
services/frontend/frontend/dist/
public/app/
.muxer-queue.**
.claude/
.hermes/
.env.test
logs/
.codegraph/
worktrees/
.worktrees/
services/frontend/frontend/dist/
target/
nix/
# Gitea CI runner logs
.gitea/workflows/*.log
.planning/
# Nix build result symlink
result
# Playwright MCP artifacts
.playwright-mcp/
@@ -1,67 +0,0 @@
# Spec: Perbagus fitur Voice + Audio Playback (GMW frontend)
Tanggal: 2026-08-22 · Scope: **frontend only** (backend/gateway API sudah cukup)
## Masalah (audit)
1. Recordings: semua kartu pakai `<audio controls>` native — tampilan identik,
tidak ada indikasi which-clip-playing / loading / paused, dan N audio bisa
play bareng (overlap).
2. Media view: `thumbnailUrl` dari gateway tidak dipakai; tidak ada visual
"sedang playing" selain disc spin; queue item semua sama tanpa badge up-next.
3. Mini-player (`lib/hooks/use-media-player.tsx`) ada tapi TIDAK PERNAH
dimount → dead code, user tidak lihat status musik di halaman lain.
4. Voice page: `useMicTransmit.setVolume` + `useVoiceListen.setVolume`
tersedia tapi tak ada UI-nya; mic live tidak punya level feedback.
## Desain
### A. RecordingAudioPlayer (baru, `components/voice/recording-audio-player.tsx`)
Custom player menggantikan `<audio controls>`:
- Play/pause button (ikon berubah), spinner saat buffering (`waiting` event).
- Progress bar seekable (click-to-seek) + time label `m:ss / m:ss`.
- Waveform-ish equalizer bars saat playing (CSS animation, reduced-motion safe).
- **Single-playback**: module-level registry `activePlayers` — memainkan satu
clip otomatis pause yang lain.
- Kartu pemilik player aktif dapat highlight border signal + "Now playing" chip.
### B. Recordings view — pasang player baru
- Ganti `<audio>` → `<RecordingAudioPlayer src download_url>`.
- Highlight kartu via state lifted: `playingId` di view, callback `onPlay`.
### C. Media view polish
- Hero: thumbnail (jika `current.thumbnailUrl`) sebagai disc center image;
fallback ListMusic icon. Equalizer bars animasi CSS saat `playing`.
- Queue row pertama: badge "up next"; baris current track diberi ring signal.
- Volume read-only tetap.
### D. MiniPlayer global
- Hapus `lib/hooks/use-media-player.tsx` (dead) — ganti dengan komponen
`components/media/mini-player.tsx` yang subscribe `useMediaState` +
`useMediaWsSync` langsung (SWR cache shared antar route), mounted di
`AppFrame` bawah layar (fixed bottom, hidden di route `/media`).
- Menampilkan: thumbnail kecil/judul, tombol skip/stop, link ke /media.
### E. Voice UI
- Mic live: level meter (Equalizer bars) — mic-transmitter sudah punya worklet;
tambah `getLevel()` via AnalyserNode pada stream (simple RMS) di hook.
- Listen: volume slider (input range) wired ke `listen.setVolume`.
- Mic volume slider wired ke `mic.setVolume`.
## File touched
| File | Aksi |
|---|---|
| services/frontend/src/components/voice/recording-audio-player.tsx | new |
| services/frontend/src/app/(dashboard)/recordings/view.tsx | edit |
| services/frontend/src/app/(dashboard)/media/view.tsx | edit |
| services/frontend/src/components/media/mini-player.tsx | new |
| services/frontend/src/components/shell/ambient-app.tsx | mount MiniPlayer |
| services/frontend/src/lib/hooks/use-media-player.tsx | delete |
| services/frontend/src/hooks/use-voice.ts | tambah micLevel |
| services/frontend/src/lib/audio/mic-transmit.ts | expose analyser level |
| services/frontend/src/app/(dashboard)/voice/view.tsx | sliders + meter |
## Verifikasi
1. `pnpm lint` (biome) + `pnpm build` clean.
2. Smoke di port **4024** (BUKAN 4017) → curl 200 semua route.
3. Commit (tanpa trailer) → push → `gh run watch` → live check
https://imphnen.asepharyana.my.id/{media,recordings,voice}/ = 200.
+100
View File
@@ -0,0 +1,100 @@
# GMW — Agent Guide
GMW (Guild Moderation Watcher) is a Discord bot + web dashboard for AI-powered moderation. A monorepo with three services: a selfbot gateway that captures Discord events and runs LLM moderation, an Express/oRPC backend that serves the dashboard API, and a Next.js 16 SSR frontend. They communicate via Redis pub/sub (gateway→backend) and WebSocket (backend→browser).
## Quick reference
```bash
# Per-service — cd into the service first
pnpm install # install deps (pnpm 11, not npm or bun)
pnpm typecheck # tsc --noEmit
pnpm lint # biome check
pnpm format # biome format --write
pnpm build # gateway/backend: tsc + fix-imports.mjs; frontend: next build
pnpm test # vitest run (gateway & backend only — frontend has no tests)
```
No monorepo-level scripts exist. Run each command from inside the service directory.
## Layout
```
services/
├── discord-gateway/ Event-driven selfbot. No HTTP (except :4016 metrics).
│ ├── src/
│ │ ├── app/ Bootstrap, shutdown, retention
│ │ ├── modules/ Feature modules — each self-contained
│ │ └── shared/ Config, DB (Drizzle), logger, errors, utils
│ ├── tests/ Vitest tests (colocated, not inside src/)
│ ├── drizzle/migrations/ DB migrations
│ └── scripts/fix-imports.mjs Post-build: rewrites @/ aliases → relative .js
│
├── backend/ Express HTTP + WebSocket + oRPC server (:4001).
│ ├── src/
│ │ ├── modules/ Feature modules (schema→repo→service→controller→routes)
│ │ ├── http/ Express app setup
│ │ ├── ws/ WebSocket server + Redis bridge
│ │ ├── orpc/ oRPC router definition
│ │ └── shared/ Config, DB, errors, Redis, logger
│ └── tests/ Vitest tests
│
└── frontend/ Next.js 16 App Router, React 19, Tailwind v4 (:4017).
├── src/
│ ├── app/ Pages — route groups under (dashboard)/
│ ├── components/ UI components (primitives, shell, charts, etc.)
│ ├── hooks/ React hooks
│ └── lib/ API clients, types, utils, WebSocket, audio
└── pnpm-workspace.yaml Build-script approvals (sharp only)
```
## Conventions
### Package manager & runtime
- **pnpm** (v11), not npm or bun. Lockfiles are committed. Node ≥ 22.
- ESM throughout (`"type": "module"` in all package.json files).
### Import style
Source files use `@/*` path aliases (mapped in tsconfig to `./src/*`). Relative imports **must include the `.js` extension** (e.g., `from "./embed.js"`). The `moduleResolution: "bundler"` tsconfig setting allows bare specifiers during dev, but `tsc` emits them as-is. A post-build script (`scripts/fix-imports.mjs`) rewrites both `@/` aliases and extensionless imports in `dist/` so Node ESM can resolve them at runtime.
### Error handling
Both gateway and backend define an `AppError` base class in `@/shared/errors/index` with subclasses: `ValidationError` (400), `NotFoundError` (404), `UnauthorizedError` (401), `DatabaseError` (500), `ConfigError` (500). Services throw these; callers or middleware map them to HTTP status codes.
### Logging
Use `createChildLogger('module-name')` from `@/shared/logger/index`. Never use raw `console`. It wraps pino; in development it pretty-prints via `pino-pretty`.
### Config
Environment variables are validated with Zod at startup in `shared/config/index.ts` of both gateway and backend. Do not read `process.env` directly outside config modules.
### Module boundaries
- Gateway: each feature lives in `src/modules/<name>/` with its own `index.ts` barrel. Modules register event listeners and are composed in `src/app/bootstrap.ts`.
- Backend: `modules/<name>/` follows schema → repository → service → controller → routes. Data flows up only. No cross-module repository imports.
- Frontend: `page.tsx` is a server component that fetches data via `src/lib/api/server.ts` (oRPC over HTTP, server-side only) and passes it to a `view.tsx` client component. Browser code uses `src/lib/orpc/client.ts` (oRPC over WebSocket via partysocket). Never import the server API client from a client component.
### API layer
The backend exposes an oRPC router mounted at `/trpc` (both HTTP and WebSocket). The frontend does **not** use a REST `/api/*` layer — all data goes through oRPC procedures. The server-side client (`src/lib/api/server.ts`) uses a fetch-based RPCLink; the browser client uses a WebSocket-based RPCLink backed by partysocket for auto-reconnection. Results are asserted to the frontend's local types at each call site (the backend's router type is not imported into the frontend).
### Testing
- **Gateway**: Vitest, tests in `tests/` at the service root. Config sets env vars (`DISCORD_TOKEN`, `DATABASE_URL`, etc.) so tests run without real services.
- **Backend**: Vitest, tests in `tests/` and `src/`. Includes an `e2e.test.ts` excluded from CI (needs a live backend).
- **Frontend**: No test runner configured.
- Tests are pure-function / unit-level. Mock external dependencies; do not start real DB/Redis in tests.
### Formatting
Biome, 2-space indent, spaces. Config at repo root `biome.json`. `lint` uses `--diagnostic-level=error`; `format` auto-writes.
## Pitfalls
1. **Never commit without running `fix-imports.mjs` after `tsc`** — gateway and backend builds will produce ESM that crashes at startup (`ERR_MODULE_NOT_FOUND`).
2. **Don't add `@discordjs/opus` build-from-source flags** — it ships prebuilt binaries for Node 22. Forcing source builds in CI/Nix will fail or add minutes of compile time.
3. **Frontend SSR fetches are always `cache: "no-store"`** — the server API client bypasses Next.js fetch cache. Do not add caching without understanding the live dashboard requirement.
4. **oRPC types are loosely coupled** — the frontend casts oRPC results to its own types with `as unknown`. Adding a field to the backend schema does not automatically update the frontend type. Update both sides.
5. **Gateway is a selfbot** (`discord.js-selfbot-v13`) — it uses a user token, not a bot token. It must not be deployed as a standard Discord bot.
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+9
View File
@@ -0,0 +1,9 @@
# Roadmap
What is built, what is next, and what has been deliberately declined.
---
## Next
_Empty._
+23
View File
@@ -0,0 +1,23 @@
# TODO
Next up. One item, one outcome, verifiable when done.
Longer-term direction lives in [ROADMAP.md](ROADMAP.md).
---
## Now
_Empty._
---
## Next
_Empty._
---
## Maintenance
_Empty._
-76
View File
@@ -1,76 +0,0 @@
---
version: "neuform-top-creators-featured"
name: "AeroNet Visualization"
description: "Aeronet Visualization Dashboard Section is designed for demonstrating application workflows and interface hierarchy. Key features include clear information density, modular panels, and interface rhythm. It is suitable for product showcases, admin panels, and analytics experiences."
colors:
primary: "#FFFFFF"
secondary: "#000000"
accent: "#FFFFFF"
background: "#FFFFFF"
surface: "#18181B"
text-primary: "#111827"
text-secondary: "#4B5563"
border: "#E5E7EB"
typography:
display-lg:
fontFamily: "Inter"
fontSize: "64px"
fontWeight: 500
lineHeight: "1.04"
letterSpacing: "0"
body-md:
fontFamily: "Inter"
fontSize: "16px"
fontWeight: 400
lineHeight: "1.6"
label-md:
fontFamily: "JetBrains Mono"
fontSize: "12px"
fontWeight: 600
lineHeight: "1.2"
spacing:
base: "8px"
gap: "16px"
card-padding: "24px"
section-padding: "80px"
rounded:
card: "16px"
control: "8px"
pill: "9999px"
components:
card:
background: "Use the surface token with subtle borders and HTML-matched shadow depth"
radius: "Match the declared card radius token"
button:
background: "Use primary or accent colors for the main action"
radius: "Use the control or pill radius based on the source HTML"
---
# AeroNet Visualization
Source: Neuform Featured templates from top creators. Author: Sourasith Phomhome (@madebysourasith). Views: 419; favorites: 14; remixes: 4.
Tags: dashboard, animated, threejs, charts, navigation, links, support, effect.
## Overview
Aeronet Visualization Dashboard Section is designed for demonstrating application workflows and interface hierarchy. Key features include clear information density, modular panels, and interface rhythm. It is suitable for product showcases, admin panels, and analytics experiences.
AeroNet Mapping Framework Metrics Docs Log In Deploy Node Planet Core Uninterrupted worldwide relay networks driving next-generation infrastructure. 19.5 TB/s +924 820 GB/s +418 Relay established successfully. 2.41 PB/s…
## Composition
Use the attached HTML reference as the source of truth. Preserve the visible hierarchy, first-screen composition, section rhythm, density, and interaction tone before adapting copy or content.
Key visible headings include: Planet Core.
## Colors
Anchor the palette in primary #FFFFFF, secondary #000000, accent #FFFFFF, background #FFFFFF, surface #18181B, text-primary #111827. Keep background, surface, text, and border roles distinct so generated layouts retain the same contrast pattern as the source.
## Typography
Use Inter for display moments and Inter for body copy unless the HTML clearly demands a compatible fallback. Labels and technical metadata should use JetBrains Mono or an equivalent mono face.
## Layout
Keep spacing deliberate and stable. Favor the same grid direction, max-width behavior, card density, and responsive stacking seen in the HTML. Do not replace distinctive source structures with generic SaaS sections.
## Components
Dashboard, chart, and data panels should preserve their compact operational hierarchy, nested surfaces, and metric emphasis.
## Motion
Preserve existing motion cues such as masked reveals, staggered entrance, hover lift, scroll-triggered transitions, and ambient movement. Keep easing smooth and restrained.
## WebGL & Effects
If the source includes canvas, WebGL, Three.js, gradients, particles, or atmospheric effects, rebuild them as supporting layers behind the content. Keep effects performant, responsive, and secondary to the interface.
## Guardrails
- Do not flatten the source into a generic card grid.
- Do not swap the color mode unless the source clearly supports it.
- Preserve the first viewport signal, focal object, and visual density.
- Keep buttons, cards, and badges aligned to the same radius and border language.
-76
View File
@@ -1,76 +0,0 @@
---
version: "neuform-top-creators-featured"
name: "Nexus Capital Render"
description: "Nexus Capital Feature Section is designed for highlighting product capabilities and value points. Key features include reusable structure, responsive behavior, and production-ready presentation. It is suitable for component libraries and responsive product interfaces."
colors:
primary: "#F2EAD3"
secondary: "#000000"
accent: "#F2EAD3"
background: "#000000"
surface: "#F2EAD3"
text-primary: "#FFFFFF"
text-secondary: "#A1A1AA"
border: "#27272A"
typography:
display-lg:
fontFamily: "Inter"
fontSize: "64px"
fontWeight: 500
lineHeight: "1.04"
letterSpacing: "0"
body-md:
fontFamily: "Playfair Display"
fontSize: "16px"
fontWeight: 400
lineHeight: "1.6"
label-md:
fontFamily: "JetBrains Mono"
fontSize: "12px"
fontWeight: 600
lineHeight: "1.2"
spacing:
base: "8px"
gap: "16px"
card-padding: "24px"
section-padding: "80px"
rounded:
card: "16px"
control: "8px"
pill: "9999px"
components:
card:
background: "Use the surface token with subtle borders and HTML-matched shadow depth"
radius: "Match the declared card radius token"
button:
background: "Use primary or accent colors for the main action"
radius: "Use the control or pill radius based on the source HTML"
---
# Nexus Capital Render
Source: Neuform Featured templates from top creators. Author: Meng To (@mengto). Views: 311; favorites: 22; remixes: 15.
Tags: feature, section, animated, bento, charts.
## Overview
Nexus Capital Feature Section is designed for highlighting product capabilities and value points. Key features include reusable structure, responsive behavior, and production-ready presentation. It is suitable for component libraries and responsive product interfaces.
NEXUS WEALTH CORP. Beyond traditional equity. The multi-asset strategy. A dynamic portfolio framework where capital transitions seamlessly from baseline reserves to high-yield synthetic instruments. Drag the timeline to…
## Composition
Use the attached HTML reference as the source of truth. Preserve the visible hierarchy, first-screen composition, section rhythm, density, and interaction tone before adapting copy or content.
Key visible headings include: Beyond traditional equity. The multi-asset strategy..
## Colors
Anchor the palette in primary #F2EAD3, secondary #000000, accent #F2EAD3, background #000000, surface #F2EAD3, text-primary #FFFFFF. Keep background, surface, text, and border roles distinct so generated layouts retain the same contrast pattern as the source.
## Typography
Use Inter for display moments and Playfair Display for body copy unless the HTML clearly demands a compatible fallback. Labels and technical metadata should use JetBrains Mono or an equivalent mono face.
## Layout
Keep spacing deliberate and stable. Favor the same grid direction, max-width behavior, card density, and responsive stacking seen in the HTML. Do not replace distinctive source structures with generic SaaS sections.
## Components
Dashboard, chart, and data panels should preserve their compact operational hierarchy, nested surfaces, and metric emphasis.
## Motion
Preserve existing motion cues such as masked reveals, staggered entrance, hover lift, scroll-triggered transitions, and ambient movement. Keep easing smooth and restrained.
## WebGL & Effects
If the source includes canvas, WebGL, Three.js, gradients, particles, or atmospheric effects, rebuild them as supporting layers behind the content. Keep effects performant, responsive, and secondary to the interface.
## Guardrails
- Do not flatten the source into a generic card grid.
- Do not swap the color mode unless the source clearly supports it.
- Preserve the first viewport signal, focal object, and visual density.
- Keep buttons, cards, and badges aligned to the same radius and border language.
@@ -1,76 +0,0 @@
---
version: "neuform-staff-featured-2026-05-22"
name: "Summit — Cloud Migration Platform"
description: "Summit Cloud Pricing Section is designed for comparing plans and supporting conversion decisions. Key features include plan comparison blocks and conversion-oriented actions. It is suitable for subscription pricing pages and plan comparison experiences."
colors:
primary: "#2563EB"
secondary: "#050510"
accent: "#3B82F6"
background: "#050510"
surface: "#18181B"
text-primary: "#FFFFFF"
text-secondary: "#A1A1AA"
border: "#27272A"
typography:
display-lg:
fontFamily: "Inter"
fontSize: "64px"
fontWeight: 500
lineHeight: "1.04"
letterSpacing: "0"
body-md:
fontFamily: "Inter"
fontSize: "16px"
fontWeight: 400
lineHeight: "1.6"
label-md:
fontFamily: "JetBrains Mono"
fontSize: "12px"
fontWeight: 600
lineHeight: "1.2"
spacing:
base: "8px"
gap: "16px"
card-padding: "24px"
section-padding: "80px"
rounded:
card: "23px"
control: "18px"
pill: "9999px"
components:
card:
background: "Use the surface token with subtle borders and HTML-matched shadow depth"
radius: "Match the declared card radius token"
button:
background: "Use primary or accent colors for the main action"
radius: "Use the control or pill radius based on the source HTML"
---
# Summit — Cloud Migration Platform
Source: Neuform staff featured templates. Author: Vanh DesignCode (@vanh). Views: 161; favorites: 11; remixes: 3.
Tags: pricing, section, animated, cta, charts, billing.
## Overview
Summit Cloud Pricing Section is designed for comparing plans and supporting conversion decisions. Key features include plan comparison blocks and conversion-oriented actions. It is suitable for subscription pricing pages and plan comparison experiences.
Summit Platform Solutions Pricing Docs Start Migration Now in General Availability Migrate to the cloud with confidence Automated discovery, dependency mapping, and zero-downtime migration for enterprise workloads. Move…
## Composition
Use the attached HTML reference as the source of truth. Preserve the visible hierarchy, first-screen composition, section rhythm, density, and interaction tone before adapting copy or content.
Key visible headings include: Migrate to the cloud with confidence; Discover untethered scalability. The platform built to dynamically map, migrate, and optimize your entire infrastructure without lifting a finger.; Architectural Clarity; Dynamic Scaling.
## Colors
Anchor the palette in primary #2563EB, secondary #050510, accent #3B82F6, background #050510, surface #18181B, text-primary #FFFFFF. Keep background, surface, text, and border roles distinct so generated layouts retain the same contrast pattern as the source.
## Typography
Use Inter for display moments and Inter for body copy unless the HTML clearly demands a compatible fallback. Labels and technical metadata should use JetBrains Mono or an equivalent mono face.
## Layout
Keep spacing deliberate and stable. Favor the same grid direction, max-width behavior, card density, and responsive stacking seen in the HTML. Do not replace distinctive source structures with generic SaaS sections.
## Components
Dashboard, chart, and data panels should preserve their compact operational hierarchy, nested surfaces, and metric emphasis.
## Motion
Preserve existing motion cues such as masked reveals, staggered entrance, hover lift, scroll-triggered transitions, and ambient movement. Keep easing smooth and restrained.
## WebGL & Effects
If the source includes canvas, WebGL, Three.js, gradients, particles, or atmospheric effects, rebuild them as supporting layers behind the content. Keep effects performant, responsive, and secondary to the interface.
## Guardrails
- Do not flatten the source into a generic card grid.
- Do not swap the color mode unless the source clearly supports it.
- Preserve the first viewport signal, focal object, and visual density.
- Keep buttons, cards, and badges aligned to the same radius and border language.
+131
View File
@@ -0,0 +1,131 @@
# docs/ — Spec-Driven Development Hub
Direktori ini adalah pusat dari workflow **spec-driven development** di GMW.
Semua perubahan signifikan dimulai dari satu spec, **sebelum kode ditulis**,
dan setiap tugas yang berjalan dilacak lewat `todo.md`.
## Struktur
```
docs/
├── README.md # Dokumen ini
├── spec-template.md # Template standar untuk semua spec
├── todo-template.md # Template todo list
└── specs/ # Semua spec dan implementation plan
├── YYYY-MM-DD_<slug>-spec.md # Spec (apa & mengapa)
└── YYYY-MM-DD_<slug>.md # Plan/fix (bisa langsung spec+plan)
```
### Naming convention
```
YYYY-MM-DD_<slug>-spec.md # Spec baru untuk fitur/fix
YYYY-MM-DD_<slug>.md # Plan yang sudah include spec di dalamnya
```
Contoh:
- `2026-08-30_recordings-v2-features-spec.md` — spec untuk Recordings v2
- `2026-08-24-attachment-delay-fix.md` — spec + plan untuk attachment delay fix
### Service-specific docs
```
services/<service>/docs/specs/ # Spec yang spesifik untuk 1 service
```
## Workflow: Spec-Driven Development
### Prinsip utama
> **No code without a spec.** Semua perubahan signifikan harus punya spec
> terlebih dahulu. Spec adalah kontrak: apa yang akan dibangun, mengapa,
> dan bagaimana memverifikasinya.
### Todo list (`todo.md`) — WAJIB
Setiap tugas berjalan (implementasi fitur/fix) harus punya `todo.md`:
```markdown
# Todo — <judul tugas>
## Task
- [ ] Tulis spec
- [ ] Verifikasi facts (baca kode: file:line)
- [ ] Implementasi tahap 1: ...
- [ ] Implementasi tahap 2: ...
- [ ] Verifikasi: pnpm typecheck / lint / build / test
- [ ] Commit + push
```
Aturan `todo.md`:
- Task **konkret & verifiable** — "ubah X di file Y", bukan "fix bug".
- Satu task `[in_progress]` pada satu waktu; ceklis `[x]` segera setelah selesai.
- Gunakan `docs/todo-template.md` sebagai template.
- Simpan `todo.md` di `docs/` (tugas lintas-service) atau di
`services/<service>/docs/` (tugas satu service).
### Kapan perlu spec + todo
| Perlu spec + todo | Tidak perlu |
|---|---|
| Fitur baru | Typo fix |
| Bug fix non-trivial | Dependency bump (dependabot) |
| Refactor yang mengubah behavior | Format/lint auto-fix |
| Perubahan DB schema | Test-only change |
| Perubahan API contract | README/doc update |
| Perubahan arsitektur | Variable rename |
### Workflow step-by-step
1. **Tulis spec** — Salin `docs/spec-template.md`, isi semua section yang relevan.
- **Verified facts**: baca kode yang terpengaruh, catat temuan dengan file:line.
- **Root cause**: analisis sebab (jangan hanya describe symptom).
- **Decisions**: pilih pendekatan, jelaskan alternatif yang ditolak.
- **Verification**: command executable, bukan "should work".
- Simpan sebagai `docs/specs/YYYY-MM-DD_<slug>-spec.md`.
2. **Tulis `todo.md`** — Pecah spec jadi task konkret yang bisa diceklis
(gunakan `docs/todo-template.md`), dengan urutan implementasi yang logis.
3. **Review spec** — Baca ulang spec sendiri. Cek:
- Apakah verified facts benar-benar verified (bukan asumsi)?
- Apakah file changes lengkap (tidak ada yang terlewat)?
- Apakah verification steps executable?
- Jika spec untuk user request: pastikan user setuju dengan approach.
4. **Implement** — Ikuti spec + todo step-by-step. Ceklis `[x]` setiap task
yang selesai. Jika menemukan sesuatu yang berubah dari asumsi spec,
**update spec dulu**, baru implement.
5. **Verify** — Jalankan semua verification steps di spec. Catat hasilnya.
6. **Commit** — Reference spec di commit message:
```
feat(module): deskripsi singkat
Ref: docs/specs/YYYY-MM-DD_<slug>-spec.md
```
### Tips menulis spec yang baik
- **Evidence-based**: setiap klaim harus ada sumbernya (file:line, log, error).
- **Actionable**: orang lain (atau AI agent) harus bisa implementasi dari spec saja.
- **Verifiable**: setiap requirement harus bisa di-test secara eksplisit.
- **Scoped**: satu spec = satu perubahan terfokus. Jangan campur 3 fitur dalam 1 spec.
- **Bahasa campuran**: narrative boleh Indonesia/English, istilah teknis pakai English.
### Role of AI agents
AI coding agents (Shiro Neko / Hermes) **harus**:
- Membaca spec sebelum menulis kode
- Membuat & meng-update `todo.md` untuk setiap tugas berjalan
- Verifikasi fakta dari kode aktual (bukan dari asumsi training data)
- Update spec jika temuan baru mengubah approach
- Jalankan verification steps setelah implementasi
- Reference spec di commit message
AI agents **tidak boleh**:
- Langsung implement tanpa membaca/menulis spec
- Mengabaikan verified facts yang bertentangan dengan asumsi
- Skip verification steps
- Mengerjakan tugas tanpa todo list yang jelas
File diff suppressed because one or more lines are too long
+43
View File
@@ -0,0 +1,43 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "GMW — Guild Moderation Watcher",
"output": "gmw-architecture.html",
"quality_profile": "showcase",
"viewBox": [1140, 620]
},
"components": [
{ "id": "redis", "type": "messagebus", "label": "Redis", "sublabel": "pub/sub :6379", "pos": [300, 40], "size": [140, 60], "tag": "events + PCM" },
{ "id": "pg", "type": "database", "label": "PostgreSQL", "sublabel": "shared state :6432", "pos": [640, 40], "size": [140, 60] },
{ "id": "discord", "type": "external", "label": "Discord Guilds", "sublabel": "channels · voice · reactions", "pos": [40, 240], "size": [140, 60] },
{ "id": "gateway", "type": "backend", "label": "discord-gateway", "sublabel": "selfbot :4016", "pos": [260, 240], "size": [140, 60], "tag": "discord.js-selfbot-v13" },
{ "id": "backend", "type": "backend", "label": "gmw-backend", "sublabel": "Express · oRPC · WS :4001", "pos": [640, 240], "size": [140, 60], "tag": "Drizzle ORM" },
{ "id": "proxy", "type": "cloud", "label": "nginx proxy", "sublabel": "reverse proxy :4009", "pos": [960, 240], "size": [140, 60], "tag": "nginx" },
{ "id": "ai", "type": "backend", "label": "AI Moderation", "sublabel": "LLM caller · embeddings", "pos": [260, 400], "size": [140, 60] },
{ "id": "frontend", "type": "frontend", "label": "gmw-frontend", "sublabel": "Next.js 16 SSR :4017", "pos": [960, 400], "size": [140, 60], "tag": "React 19 · Tailwind v4" },
{ "id": "llm", "type": "cloud", "label": "9router LLM", "sublabel": "text + vision API", "pos": [260, 540], "size": [140, 60], "tag": "AI_LLM_BASE_URL" },
{ "id": "browser", "type": "external", "label": "Dashboard Users", "sublabel": "browser · partysocket", "pos": [960, 540], "size": [140, 60] }
],
"boundaries": [
{ "kind": "region", "label": "imrnes — Tailscale shared state", "wraps": ["redis", "pg"] },
{ "kind": "region", "label": "orangevps — Nix/systemd", "wraps": ["gateway", "ai", "backend", "proxy", "frontend"] }
],
"connections": [
{ "id": "c1", "from": "discord", "to": "gateway", "label": "selfbot events", "variant": "emphasis" },
{ "id": "c2", "from": "gateway", "to": "ai", "label": "capture → analyze", "labelDy": 24 },
{ "id": "c3", "from": "gateway", "to": "redis", "label": "publish events", "fromSide": "top", "toSide": "bottom" },
{ "id": "c4", "from": "redis", "to": "backend", "label": "redis-bridge → WS" },
{ "id": "c5", "from": "ai", "to": "llm", "label": "text + vision", "labelDy": 24 },
{ "id": "c6", "from": "backend", "to": "llm", "label": "agentic chatbot", "variant": "dashed" },
{ "id": "c7", "from": "backend", "to": "pg", "label": "Drizzle SQL" },
{ "id": "c8", "from": "proxy", "to": "backend", "label": "/api · /ws · /trpc" },
{ "id": "c9", "from": "proxy", "to": "frontend", "label": "SSR + static", "labelDy": 24 },
{ "id": "c10", "from": "browser", "to": "frontend", "label": "HTTPS dashboard", "variant": "emphasis" }
],
"cards": [
{ "dot": "cyan", "title": "Capture", "items": ["Selfbot gateway listens to guild events + voice streams", "AI moderation runs LLM on messages, attachments, transcripts"] },
{ "dot": "emerald", "title": "Application", "items": ["oRPC over HTTP + WS (partysocket auto-reconnect)", "Next.js 16 SSR dashboard · nginx :4009 reverse proxy"] },
{ "dot": "rose", "title": "Shared State", "items": ["Redis pub/sub relays events + live PCM audio", "PostgreSQL via Drizzle persists messages & assessments"] }
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1,548 @@
{
"schemaVersion": 1,
"ok": true,
"command": "visual-check",
"evidenceKind": "automated-browser",
"status": "pass",
"visualReview": "pending",
"artifact": {
"path": "/home/code/GMW/docs/diagrams/gmw-architecture.html",
"sha256": "8a9646053eb562bd79f674764521f96856d74da603476459c0aeedce53ab7573",
"bytes": 811554
},
"state": {
"detail": "read",
"motion": "still"
},
"chrome": {
"status": "available",
"executable": "/bin/google-chrome"
},
"diagnostics": [],
"containment": {
"status": "pass",
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1163,
"diagramWidth": 1133,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.752105263157895,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1207,
"diagramWidth": 1177,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1383,
"diagramWidth": 1353,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1797,
"diagramWidth": 1747,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"readability": {
"status": "pass",
"minimumProjectedNodeTextPx": 6,
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1163,
"diagramWidth": 1133,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.752105263157895,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1207,
"diagramWidth": 1177,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1383,
"diagramWidth": 1353,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1797,
"diagramWidth": 1747,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"viewerChrome": {
"status": "pass",
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1163,
"diagramWidth": 1133,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.752105263157895,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1207,
"diagramWidth": 1177,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1383,
"diagramWidth": 1353,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1797,
"diagramWidth": 1747,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"captures": {
"status": "pass",
"screenshots": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1163,
"diagramWidth": 1133,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.752105263157895,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light",
"file": "gmw-architecture.visual-check.1440x900.light.png"
},
{
"width": 1440,
"height": 900,
"theme": "dark",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1163,
"diagramWidth": 1133,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.752105263157895,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "dark",
"file": "gmw-architecture.visual-check.1440x900.dark.png"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1797,
"diagramWidth": 1747,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light",
"file": "gmw-architecture.visual-check.2048x1320.light.png"
},
{
"width": 2048,
"height": 1320,
"theme": "dark",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1797,
"diagramWidth": 1747,
"viewBoxWidth": 1140,
"minimumProjectedNodeTextPx": 7.8,
"minimumProjectedNodeText": "channels · voice · reactions",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": false,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "dark",
"file": "gmw-architecture.visual-check.2048x1320.dark.png"
}
],
"contactSheet": "gmw-architecture.visual-check.html"
},
"sidecars": {
"receipt": "gmw-architecture.visual-check.json",
"contactSheet": "gmw-architecture.visual-check.html"
}
}
+80
View File
@@ -0,0 +1,80 @@
# Spec: <Judul singkat, aktif, deskriptif>
Status: **DRAFT** | **APPROVED** | **IN PROGRESS** | **DONE**
Date: YYYY-MM-DD
Author: <nama/agent>
Related: <link ke spec/plan terkait jika ada>
Todo: <link ke todo.md untuk tugas ini, jika ada>
## Problem
<Deskripsi masalah atau keinginan. Apa yang rusak? Apa yang belum ada?
Sebutkan siapa yang melaporkan (user/bot/audit) dan konteksnya.
Gunakan **evidence**: log, error, metrik, atau observasi langsung.>
## Root cause
<Analisis teknis mengapa masalah ini terjadi. Jika bug: trace dari symptom ke cause.
Jika fitur baru: jelaskan gap saat ini. Referensikan file + line number yang relevan.>
## Behavior target
<Deskripsi eksplisit perilaku yang diharapkan setelah fix/fitur.
Buat list bernomor. Setiap item harus bisa di-verify.>
## Verified facts
>Fakta-fakta teknis yang **sudah diverifikasi** dari pembacaan kode, log produksi,
>atau eksperimen langsung. Setiap fakta harus citation ke file:line.
>Jika belum diverifikasi, tulis `UNVERIFIED` dan rencana verifikasi.
- `path/to/file.ts:42` — <apa yang terjadi di sini>
- `path/to/file.ts:88` — <apa yang terjadi di sini>
- Kolom DB `table.column` — <tipe, constraint, index>
- Config `ENV_VAR` default <value> — <dibaca di mana>
## Keputusan desain
<Pilihan desain yang dibuat, beserta rationale (mengapa bukan alternatif lain).
Format: nomor, judul singkat, penjelasan.>
1. **<Judul keputusan>**: <penjelasan + rationale>
- Alternatif yang ditolak: <apa + mengapa ditolak>
## Perubahan file
>Daftar **semua file** yang perlu diubah/ditambah/dihapus, dikelompokkan per service.
>Untuk setiap file: sebutkan **apa yang berubah** (bukan copy-paste kode).
### Gateway (`services/discord-gateway/`)
- `src/modules/<module>/<file>.ts` — <ringkasan perubahan>
### Backend (`services/backend/`)
- `src/modules/<module>/<file>.ts` — <ringkasan perubahan>
### Frontend (`services/frontend/`)
- `src/<path>/<file>.ts|tsx` — <ringkasan perubahan>
### Database / Config
- <migration file atau config change jika ada>
## Schema/type changes
<Perubahan tipe, interface, atau DB schema. Jika tidak ada, tulis "TIDAK ada perubahan.">
## Verification
>**Harus spesifik dan executable.** Jangan tulis "works correctly".
>Tulis command yang bisa dijalankan dan expected outcome.
- **Typecheck**: `<service> pnpm typecheck` — clean
- **Lint**: `<service> pnpm lint` — no errors
- **Build**: `<service> pnpm build` — compiles
- **Test**: `<service> pnpm test` — all pass (+ test baru jika ada)
- **Smoke test**: <langkah manual untuk verifikasi visual/behavioral>
- **DB**: <query untuk cek data jika applicable>
- **Deploy**: CI green → deploy → cek <specific observable>
## Notes (opsional)
<Catatan tambahan: risiko, fallback, future work, atau hal yang sengaja di-scope-out.>
@@ -0,0 +1,56 @@
# Spec: Simpan pesan NSFW tanpa analisis AI (Request 1)
Date: 2026-08-30
## Goal
Saat ini GMW **skip capture** untuk pesan di channel age-restricted/NSFW
(`messageCapture.ts` baris 291 & 310 memanggil `isAgeRestrictedMessage` lalu
`return`). User ingin pesan NSFW tetap **disimpan** ke database (jadi terlihat
di dashboard), tetapi **tidak dianalisis AI** (tidak dipanggil LLM).
## Behavior target
1. Pesan NSFW/age-restricted di `messageCreate` → DISIMPAN (capture normal).
2. Pesan NSFW di `messageUpdate`/`messageDelete` → tetap diproses seperti pesan
biasa (edit/deleted state tercatat).
3. AI analysis TIDAK berjalan untuk pesan NSFW. Sudah ada jalur yang benar:
`queueMessageAnalysis` → `isAgeRestrictedMessage(message)` →
`buildAgeRestrictedSkipResult()` yang menulis `status=clean` + flag
`age_restricted` + `action=none` TANPA memanggil LLM. Jalur ini sudah ada dan
dipakai di `aiAnalyzer.queueMessageAnalysis` (baris 72-84) dan
`batchProcessor.skipAgeRestrictedMessages`. Jadi tinggal membuka capture.
4. Konten NSFW TIDAK masuk ke Qdrant public archive (semantic search publik).
`archiveMessageEmbedded` harus di-guard untuk pesan age-restricted.
## Files touched
- `services/discord-gateway/src/modules/message-capture/messageCapture.ts`
- Hapus guard `isAgeRestrictedMessage` di `messageCreate` (baris 291) dan
`messageUpdate` (baris 310). Jangan hapus guard `isExcludedThread`,
`isBotExcludedChannel`, `shouldCaptureForAnyTarget`.
- `services/discord-gateway/src/modules/message-capture/archiveEmbedder.ts`
- `archiveMessageEmbedded` terima flag/cek metadata age-restricted → skip
embed untuk NSFW.
- Call-site di `messageCapture.ts` `captureMessage` (baris 218) pass isNSFW
atau cek dulu.
## Schema/type changes
- TIDAK ada perubahan DB schema. Metadata pesan sudah membawa `channel.nsfw`
(`getMessageLocation`). Tidak perlu kolom baru — flag `age_restricted` sudah
ditulis ke `ai_moderation_flags` lewat skip-result.
## Verification
- `pnpm typecheck`, `pnpm lint` (biome), `pnpm build` di `services/discord-gateway`.
- Unit test: pastikan ada test untuk age-restricted skip (sudah ada
`tests/conversationContext.test.ts` ref nsfw; cek apakah ada test sesuai).
- Deploy via GHA push; verify pesan NSFW muncul di DB dan `ai_status` = clean
dengan flag age_restricted, dan TIDAK ada panggilan LLM (log llm-caller tidak
menampilkan id pesan NSFW).
## Status
- Request 1: DONE & DEPLOYED (commit 50967f64, CI green, gateway restart 20:17 WIB).
- Request 2 (video): dicatat, belum dikerjakan — lihat section di bawah.
## Request 2 (video) — NOT in this change
Fitur record video kamera/screenshare orang lain. @discordjs/voice hanya
mendukung **audio** receive. Video orang lain butuh WebRTC viewer baru
(SDP answer, decrypt H264/VP8, decode frame, mux MP4/WebM). Diluar scope
changeset ini; dicatat untuk desain lanjutan.
+61
View File
@@ -0,0 +1,61 @@
# GMW — Fix mobile navbar "tidak bisa pindah halaman" (root cause)
## Context / symptom
- User (phone, mobile viewport <md): pressing the bottom mobile nav items does NOT
navigate ("Masih sama, walau sudah ditekan tidak pindah halaman").
- Previous fix (commits ae41f64e, 4d0bbed5) repositioned the chatbot FAB above the
nav and expanded nav to all 7 items — but the bug persisted.
## Root cause (verified live 2026-08-28)
Reproduced on the LIVE site (`imphnen.asepharyana.my.id`) with Playwright @375px:
1. **Next `<Link>` client-side navigation is dead app-wide.** Clicking a mobile nav
`<Link>` fires the click (event reaches document, `inLink=true`,
`defaultPrevented=false`) but **the URL never changes**. `window.next.router.push('/voice')`
also does nothing. The desktop NavRail navigates only because it uses a **plain
`<a href>`** (full browser navigation bypasses the broken router).
2. **A React hydration failure (#418 "server rendered text didn't match the client")**
is thrown on live pages — the only console error. Cause: relative-time text
(`formatRelativeTime(e.edited_at)` / `formatRelativeTime(m.created_at)` in the
SSR-seeded messages/edit-history feed uses `Date.now()`; server and client
render slightly different text → hydration mismatch → React re-renders, and the
Next client router ends up non-functional.
## Why desktop "worked" / mobile didn't
- `NavRail` = plain `<a href>` → **hard** navigation (works).
- `MobileNav` = Next `<Link>` → **client** navigation → dead router.
User's instruction "ikuti cara kerja sidebar" = make mobile nav behave like the
sidebar (plain anchors).
## Changes
### 1. `src/components/shell/mobile-nav.tsx`
Replace Next `<Link>` with a **plain `<a href>`** (mirrors NavRail). Keep:
`usePathname` for active-state styling + `scrollIntoView` for snap-to-active.
Drop the now-unused `Link` import (biome import-order — run biome check --write).
### 2. Hydration root cause — `formatRelativeTime` in SSR-seeded feeds
Add `suppressHydrationWarning` to the timestamp elements in the SSR-seeded
components that render live-relative text so server/client text drift no longer
throws #418:
- `src/components/EditHistory.tsx` (line ~111 `edited {formatRelativeTime(...)}`)
- `src/app/(dashboard)/messages/view.tsx` MessageRow channel/time (line ~580) +
semantic row (line ~364)
- `src/components/LiveModerationFeed.tsx` (line ~139)
- (recordings/view.tsx, TermGlossary, ChannelCultureGlossary, CategoryDrilldown,
chatbot — chatbot is client-after-mount; add where SSR-seeded.)
NOTE: `suppressHydrationWarning` is safe for these single-text spans; the values
are cosmetic and self-correct on the next interval/render.
## Verification (non-prod, NEVER 4017)
- `pnpm format` + `pnpm lint` (biome) + `pnpm build` clean.
- Smoke on :4024 (fresh `next dev`): mobile click on nav item actually navigates
(URL changes) AND no React #418 in console.
- After push: `gh run watch`, then live `imphnen.asepharyana.my.id` @375px: nav tap
navigates, console free of #418.
## Files
- services/frontend/src/components/shell/mobile-nav.tsx
- services/frontend/src/components/EditHistory.tsx
- services/frontend/src/app/(dashboard)/messages/view.tsx
- services/frontend/src/components/LiveModerationFeed.tsx
- (others only if #418 persists)
+27
View File
@@ -0,0 +1,27 @@
# Todo — <judul tugas>
Status: **IN PROGRESS** | **BLOCKED** | **DONE**
Spec: <link ke spec: `docs/specs/YYYY-MM-DD_<slug>-spec.md`>
Date: YYYY-MM-DD
## Task
> Urutan implementasi sesuai spec. Task harus konkret & verifiable
> ("ubah X di file Y"), bukan "fix bug". Satu task dikerjakan pada satu
> waktu; ceklis `[x]` segera setelah selesai.
- [ ] Tulis spec
- [ ] Verifikasi facts (baca kode: `file:line`)
- [ ] Implementasi tahap 1: <apa + file mana>
- [ ] Implementasi tahap 2: <apa + file mana>
- [ ] Implementasi tahap 3: <apa + file mana>
- [ ] Typecheck: `pnpm typecheck` — clean
- [ ] Lint: `pnpm lint` — no errors
- [ ] Test: `pnpm test` — all pass
- [ ] Build: `pnpm build` — compiles
- [ ] Daftar ulang hasil verifikasi di spec
- [ ] Commit + push (reference spec di commit message)
## Notes
<Blocking issue, temuan yang mengubah approach, atau hal yang perlu dicatat.>
+17 -3
View File
@@ -82,6 +82,17 @@
[ "$d" = "node_modules" ] && continue; \
grep -qF ".pnpm/$d" $TMPDIR/prod-pnms.txt || rm -rf "$d"; \
done ) || true
# Drop runtime-dead packages that still land in the prod graph:
# - `@types/*` (pure TypeScript declarations) get pulled in as
# REAL dependencies by type-aware deps (discord-api-types ->
# @types/node, pg-protocol -> @types/pg, ...) even though nothing
# ever `require`s them at runtime. Safe to strip.
# - `opusscript` is only a pure-JS fallback Opus engine that
# prism-media's loader uses IF `@discordjs/opus` (native, always
# present/prebuilt) fails to load. Since the native engine loads,
# opusscript is never executed — dead weight pulled in via
# discord.js-selfbot-v13's dependency. Strip it too.
( cd node_modules/.pnpm && rm -rf @types+* opusscript@* 2>/dev/null ) || true
# Drop symlinks whose .pnpm target was pruned (top-level, scoped dirs,
# hoist, .bin — any depth). Mirrors stdenv's noBrokenSymlinks check,
# which would otherwise fail the fixupPhase.
@@ -132,14 +143,17 @@ WRAPPER
src = ./services/discord-gateway;
# cmake + rust/cargo were inherited for node-datachannel-style native
# deps — that's 9router/omniroute, NOT GMW. GMW's only native deps
# are @discordjs/opus + sharp, both PREBUILT (node-pre-gyp / @img
# libvips download), so no cmake or rust toolchain is needed.
# python3/gnumake/gcc stay as node-gyp fallback for @discordjs/opus.
nativeBuildInputs = [
nodejs pnpm
pkgs.python3 pkgs.gnumake pkgs.gcc pkgs.cmake
pkgs.rustc pkgs.cargo
pkgs.python3 pkgs.gnumake pkgs.gcc
pkgs.pkg-config
pkgs.openssl
pkgs.openssl.dev
pkgs.git # for any FetchContent-based deps during native builds
pkgs.cacert
];
-3
View File
@@ -1,3 +0,0 @@
*
!.gitkeep
!README.md
View File
-15
View File
@@ -1,15 +0,0 @@
# Recordings Directory
This directory is mounted as a volume in the discord-gateway container.
Voice recordings are stored here with the following structure:
```
recordings/
├── <user-id>/
│ ├── <timestamp>.ogg
│ └── <timestamp>.json
└── sessions/
└── <session-id>.json
```
The directory must be writable by UID 1000 (app user in the container).
+114
View File
@@ -0,0 +1,114 @@
# Backend Service — Agent Guide
> **Read `../../AGENTS.md` first.** This file adds backend-specific conventions.
Backend service: Express HTTP server + WebSocket, serves the GMW dashboard API.
## Quick reference
```bash
pnpm typecheck # tsc --noEmit
pnpm lint # biome check --diagnostic-level=error .
pnpm build # tsc
pnpm test # vitest run
pnpm format # biome format --write .
```
## Architecture (Modular MVC)
```
src/
├── shared/ # Infrastructure (no business logic)
│ ├── config/index.ts # Zod-validated env
│ ├── database/ # Drizzle ORM + pg Pool
│ ├── errors/index.ts # AppError hierarchy
│ ├── logger/index.ts # pino + createChildLogger()
│ ├── middlewares/index.ts # errorHandler, asyncHandler, rateLimit
│ └── utils/ # Pagination, messageMapper
├── modules/ # Feature modules
│ └── <module>/
│ ├── <module>.schema.ts # Zod validation schemas
│ ├── <module>.repository.ts # DB operations only
│ ├── <module>.service.ts # Business logic
│ ├── <module>.controller.ts # HTTP handlers
│ └── routes/index.ts # Express router
├── http/ # app.ts (factory) + server.ts (startup)
├── ws/ # WebSocket server + Redis bridge
└── index.ts # Entry point
```
## Dependency rules
- Controller → Service → Repository → Database
- No cross-module repo imports (each module owns its data)
- No HTTP in Service layer (no req/res)
- No DB in Controller layer
- Any layer → Config, Logger, Errors
## API: oRPC + Express
- **oRPC** (`src/orpc/router.ts` + `src/orpc/ws.ts`): type-safe procedures for frontend
- **Express routes** (`src/modules/*/routes/`): REST endpoints under `/api/`
- **WebSocket** (`src/ws/`): Redis bridge → broadcast to connected browsers
- Never invent endpoints. Match what the frontend calls (`src/lib/api/`).
## Key modules
| Module | Purpose | DB tables |
|---|---|---|
| messages | Store & query Discord messages | `messages`, `ai_moderations`, `ai_moderation_flags` |
| moderation | Moderation actions & metrics | `ai_moderations`, `moderation_actions` |
| media | Media file management | `media_attachments` |
| dashboard | Stats aggregation | Various (read-only) |
| knowledge | Semantic search | Qdrant vector DB |
| chatbot | AI chatbot with tools | `chatbot_history` |
| health | Health checks + metrics | Various |
| analysis | Text analysis cache | `text_analysis_cache` |
| ui-state | Persist UI preferences | `ui_state` |
## Config (env vars)
All validated via Zod in `shared/config/index.ts`. Key vars:
- `WEBSERVER_PORT` (default 4001)
- `DATABASE_URL` or individual `DATABASE_HOST/PORT/NAME/USER/PASSWORD`
- `REDIS_URL` — for pub/sub with gateway
- `MONITOR_GUILD_ID` — primary Discord guild
- `ADMIN_PASSWORD` — admin endpoints
## Data contract with frontend
The frontend fetches via `src/lib/api/server.ts` (SSR, server-side) and
`src/lib/api/client.ts` (browser, same-origin through proxy).
**Do not change response shapes without updating both sides.** Check
`services/frontend/src/lib/types/` for frontend type definitions.
## Redis channels (inbound from gateway)
```
discord:message:{created,updated,deleted,analyzed}
discord:attachment:{created,uploaded}
discord:analysis:queue_status
discord:reaction:{added,removed}
discord:thread:{created,deleted,updated}
discord:channel_topic:updated
discord:presence:updated
discord:guild_member:{added,removed}
```
Canonical names: `src/shared/redis-channels.ts`.
## Testing
- Vitest for unit tests
- Mock database and external services
- Test files: `src/modules/<module>/*.test.ts` or `tests/*.test.ts`
- Run: `pnpm test`
## Common pitfalls
- **oRPC vs REST**: check both routers when adding an endpoint
- **Redis channel mismatch**: gateway publishes → backend subscribes. Channel
names must match exactly (see `redis-channels.ts` in BOTH services)
- **DB pool**: backend uses one pool (main thread). Gateway has per-piscina-thread pools.
-12
View File
@@ -35,16 +35,6 @@ services/backend/
│ │ │ └── routes/
│ │ │ └── index.ts
│ │ │
│ │ ├── media/
│ │ │ ├── media.service.ts
│ │ │ └── routes/
│ │ │ └── index.ts
│ │ │
│ │ ├── voice/
│ │ │ ├── voice.service.ts
│ │ │ └── routes/
│ │ │ └── index.ts
│ │ │
│ │ └── health/
│ │ ├── health.schema.ts
│ │ ├── health.repository.ts
@@ -148,8 +138,6 @@ export const messageQuerySchema = z.object({
|--------|---------|--------|
| **messages** | Text message storage & retrieval | GET /api/messages, GET /api/messages/:channelId |
| **analytics** | Moderation statistics & trends | GET /api/analytics/overview, /daily-trend, /hourly-stats |
| **media** | Media file management | GET /api/media/list, POST /api/media/upload |
| **voice** | Voice recording management | GET /api/voice/recordings, POST /api/voice/connect |
| **health** | Service health checks | GET /api/health |
## Data Flow
+25 -20
View File
@@ -2,40 +2,45 @@
"name": "discord-moderation-backend",
"version": "1.0.0",
"description": "Backend service for Discord moderation monitoring",
"private": true,
"type": "module",
"main": "dist/index.js",
"packageManager": "pnpm@11.20.0",
"engines": {
"node": ">=22.12.0",
"pnpm": ">=9.0.0"
},
"scripts": {
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
"build": "tsc",
"typecheck": "tsc --noEmit",
"lint": "biome check --diagnostic-level=error .",
"dev": "tsx watch src/index.ts",
"format": "biome format --write .",
"test": "vitest run"
"lint": "biome check --diagnostic-level=error .",
"start": "node dist/index.js",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@discordjs/voice": "^0.19.2",
"@orpc/server": "1.15.0",
"axios": "^1.16.1",
"dotenv": "^17.4.2",
"@orpc/server": "1.15.2",
"axios": "^1.20.0",
"dotenv": "^18.0.1",
"drizzle-orm": "^0.45.2",
"express": "^5.2.1",
"helmet": "^8.1.0",
"ioredis": "^5.11.0",
"pg": "^8.21.0",
"pino": "^9.6.0",
"ioredis": "^6.0.0",
"pg": "^8.23.0",
"pino": "^10.3.1",
"prom-client": "^15.1.3",
"ws": "^8.20.1",
"zod": "^4.4.3"
"ws": "^8.21.3",
"zod": "^4.6.5"
},
"devDependencies": {
"@biomejs/biome": "latest",
"@biomejs/biome": "^2.5.14",
"@types/express": "^5.0.6",
"@types/node": "^25.9.0",
"@types/pg": "^8.20.0",
"@types/node": "^26.6.2",
"@types/pg": "^8.23.1",
"@types/ws": "^8.18.1",
"tsx": "^4.22.2",
"typescript": "^5.9.3",
"vitest": "latest"
"tsx": "^4.23.15",
"typescript": "^7.0.2",
"vitest": "^5.0.1"
}
}
+599 -657
View File
File diff suppressed because it is too large Load Diff
+9 -23
View File
@@ -6,7 +6,13 @@ import { describe, expect, it } from "vitest";
const BASE = process.env.API_BASE ?? "http://localhost:4001/api";
async function api(path: string, init?: RequestInit) {
/** Result of a JSON API call: status + parsed body (or null for 204/empty). */
interface ApiResult {
status: number;
body: Record<string, unknown> | null;
}
async function api(path: string, init?: RequestInit): Promise<ApiResult> {
const res = await fetch(`${BASE}${path}`, {
...init,
headers: { "Content-Type": "application/json", ...init?.headers },
@@ -23,7 +29,8 @@ describe("API Health", () => {
});
it("GET /metrics returns prometheus text", async () => {
const res = await fetch(`${BASE.replace("/api", "")}/api/metrics`);
const base = BASE.endsWith("/api") ? BASE.slice(0, -4) : BASE;
const res = await fetch(`${base}/api/metrics`);
expect(res.status).toBe(200);
const text = await res.text();
expect(text).toContain("nodejs");
@@ -41,20 +48,6 @@ describe("API Dashboard", () => {
});
});
describe("API Recordings", () => {
it("GET /recordings returns items with pagination", async () => {
const { status, body } = await api("/recordings?limit=5");
expect(status).toBe(200);
expect(body).toHaveProperty("items");
expect(Array.isArray(body.items)).toBe(true);
if (body.items.length > 0) {
expect(body.items[0]).toHaveProperty("id");
expect(body.items[0]).toHaveProperty("username");
expect(body.items[0]).toHaveProperty("created_at");
}
});
});
describe("API Config", () => {
it("GET /config returns 200", async () => {
const { status } = await api("/config");
@@ -62,13 +55,6 @@ describe("API Config", () => {
});
});
describe("API Voice", () => {
it("GET /guilds returns 200", async () => {
const { status } = await api("/guilds");
expect(status).toBe(200);
});
});
describe("API Negative", () => {
it("GET /nonexistent returns 404", async () => {
const { status } = await api("/nonexistent");
@@ -26,13 +26,25 @@ class ChatbotService {
// longer bake server stats into the prompt — the model must pull current
// data via tools (see buildSystemPrompt), so it always answers from live
// numbers instead of a stale snapshot.
const scope = {
//
// If the current request doesn't carry a guild/channel scope (e.g. a
// cross-server dashboard surface), fall back to the most recent scope the
// user was chatting in from history, so the agent doesn't drift to the
// wrong server between turns.
const explicitScope = {
guildId: context?.guildId,
channelId: context?.channelId,
};
const scope =
explicitScope.guildId || explicitScope.channelId
? explicitScope
: (recentContext.lastScope ?? {
guildId: undefined,
channelId: undefined,
});
const systemPrompt = this.buildSystemPrompt(scope);
const conversationHistory = this.buildHistoryMessages(recentContext);
const conversationHistory = this.buildHistoryMessages(recentContext.turns);
const llmResponse = await this.callLLM(
systemPrompt,
conversationHistory,
@@ -61,14 +73,28 @@ class ChatbotService {
await chatbotRepository.clearChatHistory(userId);
}
private async getRecentConversationContext(
userId: string,
): Promise<string[]> {
const history = await chatbotRepository.getChatHistory(userId, 3);
return history.flatMap((row) => [
`User: ${row.user_message}`,
`Bot: ${row.bot_response}`,
]);
private async getRecentConversationContext(userId: string): Promise<{
turns: string[];
lastScope: { guildId?: string; channelId?: string } | null;
}> {
const history = await chatbotRepository.getChatHistory(userId, 8);
const turns: string[] = [];
let lastScope: { guildId?: string; channelId?: string } | null = null;
for (const row of history) {
turns.push(`User: ${row.user_message}`, `Bot: ${row.bot_response}`);
// Capture the most recent scope this user was chatting in, so the
// agent keeps server context across turns even if the current request
// doesn't carry one.
const g = row.context?.guildId;
const c = row.context?.channelId;
if (g || c) {
lastScope = {
guildId: g ?? undefined,
channelId: c ?? undefined,
};
}
}
return { turns, lastScope };
}
private buildSystemPrompt(scope: {
@@ -82,11 +108,27 @@ class ChatbotService {
${scopeLine}
TOOLS YANG TERSEDIA (panggil saat user tanya soal data server):
- get_server_stats → statistik server: total pesan, user aktif, flagged/warn/clean
- get_top_channels → channel paling aktif (jumlah pesan terbanyak)
- get_recent_activity → pesan terbaru (siapa, channel mana, jam berapa, isinya)
- get_top_flagged → pesan yang di-flag AI (alasan, severity, analysis)
- search_messages → cari pesan berdasarkan kata kunci (LIKE search)
- get_user_messages → pesan terbaru dari satu user tertentu
- get_user_profile → profil AI dari seorang user (pola perilaku, gaya bicara)
- get_channel_culture → norma/slang dari suatu channel
- get_message_detail → 1 pesan lengkap beserta hasil analisis AI-nya
- get_message_reviews → antrean review moderasi manual
- get_voice_recordings → rekaman suara terbaru
- get_moderation_timeline → tren harian: total vs flagged vs warn vs clean
- get_corrections → riwayat koreksi false-positive AI
ATURAN PENTING — JANGAN PAKAI KONTEKS STATIS:
- Kamu TIDAK punya hafalan soal angka server (jumlah pesan, user aktif, flagged, dll). JANGAN tebak atau karang angka.
- Untuk SEMUA pertanyaan soal data server (jumlah pesan, user aktif, channel ramai, aktivitas terbaru, pesan di-flag), WAJIB panggil tool yang sesuai (get_server_stats, get_top_channels, get_recent_activity, get_top_flagged). Jawab HANYA dari hasil tool.
- Tool otomatis di-scope ke guild/channel di atas — kalau argumen guildId/channelId kosong, biarkan kosong (sudah otomatis ter-isi). Jangan isi ID yang kamu tebak.
- Kalau tool balas error atau kosong, bilang aja data lagi ga ketemu, jangan karang.
- Kamu TIDAK punya hafalan soal angka server. JANGAN tebak atau karang angka.
- Untuk SEMUA pertanyaan soal data server, WAJIB panggil tool yang sesuai. Jawab HANYA dari hasil tool.
- Tool otomatis di-scope ke guild/channel — kalau argumen kosong, biarkan kosong. Jangan isi ID tebakan.
- Kalau tool balik error atau kosong, bilang aja data lagi ga ketemu, jangan karang.
- Boleh panggil banyak tool dalam satu jawaban kalau pertanyaan butuh beberapa data (multi-hop).
Gaya ngobrol:
- Santai, hangat, kayak ngobrol sama temen
@@ -127,27 +169,31 @@ Gaya ngobrol:
try {
const { default: axios } = await import("axios");
// Gateway tidak handle role system — gabung konteks ke user message.
// The system section stays visible to the model as the first user turn.
const contextPrefixed = `${systemPrompt}\n\nPertanyaan user: ${userMessage}`;
// Seed conversation: prior turns + current question.
const messages: Array<
| { role: "user" | "assistant"; content: string }
| {
role: "assistant";
content: string | null;
tool_calls: Array<{
id: string;
type: "function";
function: { name: string; arguments: string };
}>;
}
| { role: "tool"; tool_call_id: string; content: string }
> = [...history, { role: "user", content: contextPrefixed }];
const scopeHint = scope.guildId
? ` (scope: guild ${scope.guildId}${scope.channelId ? `, channel ${scope.channelId}` : ""})`
: "";
// Local message type that models the shapes the agent loop emits and
// accepts: plain system/user/assistant turns and assistant tool-calls +
// tool results. role is a union string so history entries typed as
// `"user" | "assistant"` don't break the discriminant.
type ChatMsg = {
role: "system" | "user" | "assistant" | "tool";
content?: string | null;
tool_call_id?: string;
tool_calls?: Array<{
id: string;
type: "function";
function: { name: string; arguments: string };
}>;
};
const messages: ChatMsg[] = [
{ role: "system", content: systemPrompt },
...history,
{ role: "user", content: `${userMessage}${scopeHint}` },
];
// ── Agentic tool loop ─────────────────────────────────────────
const MAX_TOOL_ROUNDS = 4;
const MAX_TOOL_ROUNDS = 6;
for (let round = 0; round <= MAX_TOOL_ROUNDS; round += 1) {
const response = await axios.post(
`${baseUrl}/chat/completions`,
@@ -158,7 +204,7 @@ Gaya ngobrol:
tool_choice: "auto",
max_tokens: 600,
temperature: 0.4,
// Non-streaming: request a single complete response. 9router may
// Non-streaming: request a single complete response. omniroute may
// still emit SSE even with stream:false, so the parser below
// handle both raw-JSON and SSE bodies.
stream: false,
@@ -176,7 +222,7 @@ Gaya ngobrol:
},
);
// Parse the body into content + tool_calls. 9router may return either
// Parse the body into content + tool_calls. omniroute may return either
// a single JSON object (stream:false honored) or SSE text (stream
// implied) — parseResponse handles both.
const { content, toolCalls } = this.parseResponse(
@@ -193,35 +239,44 @@ Gaya ngobrol:
);
if (toolCalls.length > 0) {
// Execute each tool, append tool results, continue loop.
for (const tc of toolCalls) {
messages.push({
role: "assistant",
content: null,
tool_calls: [
{
id: tc.id,
type: "function",
function: { name: tc.name, arguments: tc.arguments },
},
],
});
// Auto-scope: if the model omitted guildId/channelId, fill them
// from the request scope so tools query the right server without
// the model having to guess IDs.
const scopedArgs = { ...tc.args };
if (scope.guildId && scopedArgs.guildId == null) {
scopedArgs.guildId = scope.guildId;
}
if (scope.channelId && scopedArgs.channelId == null) {
scopedArgs.channelId = scope.channelId;
}
let result = "";
try {
result = await executeTool(tc.name, scopedArgs);
} catch (e) {
result = `Tool error: ${(e as Error).message}`;
}
// Executor for a single tool call: pushes the assistant tool_call,
// runs the tool, returns { tc, result } so results can be appended
// in order after all tools execute in parallel.
const executions = await Promise.all(
toolCalls.map(async (tc) => {
messages.push({
role: "assistant",
content: null,
tool_calls: [
{
id: tc.id,
type: "function",
function: { name: tc.name, arguments: tc.arguments },
},
],
});
// Auto-scope: if the model omitted guildId/channelId, fill them
// from the request scope so tools query the right server without
// the model having to guess IDs.
const scopedArgs = { ...tc.args };
if (scope.guildId && scopedArgs.guildId == null) {
scopedArgs.guildId = scope.guildId;
}
if (scope.channelId && scopedArgs.channelId == null) {
scopedArgs.channelId = scope.channelId;
}
let result = "";
try {
result = await executeTool(tc.name, scopedArgs);
} catch (e) {
result = `Tool error: ${(e as Error).message}`;
}
return { tc, result };
}),
);
// Append tool results in the SAME order as the tool_calls so the
// API's function-calling contract isn't violated by reordering.
for (const { tc, result } of executions) {
messages.push({
role: "tool",
tool_call_id: tc.id,
@@ -252,7 +307,7 @@ Gaya ngobrol:
/**
* Parse an LLM HTTP body into content + tool_calls. Handles both shapes
* 9router can return: a single JSON object (stream:false honored) or SSE
* omniroute can return: a single JSON object (stream:false honored) or SSE
* text (stream implied). For SSE we delegate to parseSse.
*/
private parseResponse(body: string): {
@@ -306,7 +361,7 @@ Gaya ngobrol:
/**
* Parse an SSE stream body into accumulated content + any tool_calls.
* 9router (and most OpenAI-compatible routers) emit `data: {json}` lines
* omniroute (and most OpenAI-compatible routers) emit `data: {json}` lines
* even when stream is only implied; we must collect deltas manually.
*/
private parseSse(body: string): {
@@ -152,7 +152,7 @@ export const tools: ToolDef[] = [
function: {
name: "get_user_reputation",
description:
"Ambil skor trust, jumlah infraction, dan streak pesan bersih seorang user dari user_reputations. Untuk 'berapa trust score si A?' / riwayat pelanggaran. Butuh user_id.",
"Cek status skor reputasi per-user. CATATAN: fitur trust score per-user telah dihapus dari sistem — tool ini menjawab 'unavailable' dan menyarankan rasio flag vs total sebagai pengganti.",
parameters: {
type: "object",
properties: {
@@ -1,4 +1,5 @@
import { and, desc, eq, like, sql } from "drizzle-orm";
import { createChildLogger } from "@/shared/logger/index";
import { getDatabase } from "../../shared/database/index.js";
import {
pgChannelCulturesTable,
@@ -6,7 +7,6 @@ import {
pgMessageReviewsTable,
pgMessagesTable,
pgUserProfilesTable,
pgUserReputationsTable,
pgVoiceRecordingsTable,
} from "../../shared/index.js";
@@ -20,6 +20,20 @@ import {
*/
export type ToolResult = string;
const logger = createChildLogger("chatbot.tools");
/** Saturating counter of chatbot tool execution errors (for observability). */
export let toolErrors = 0;
const TOOL_ERROR_CAP = 1000;
function toolExecError(err: unknown, name: string): string {
if (toolErrors < TOOL_ERROR_CAP) toolErrors++;
const detail = (err as Error)?.message ?? "unknown";
logger.warn({ tool: name, error: detail }, "Chatbot tool execution failed");
return `Tool ${name} gagal: ${detail}`;
}
/** Executes a tool call against the real DB and returns a readable result. */
export async function executeTool(
name: string,
@@ -91,7 +105,7 @@ export async function executeTool(
}
} catch (error) {
// Best-effort: if a tool fails, return readable error instead of crashing
return `Terjadi kesalahan saat ambil data: ${(error as Error).message ?? "unknown"}`;
return toolExecError(error, name);
}
}
@@ -282,26 +296,18 @@ async function userProfile(userId?: string, guildId?: string): Promise<string> {
}
async function userReputation(
userId?: string,
guildId?: string,
_userId?: string,
_guildId?: string,
): Promise<string> {
const db = getDatabase();
if (!userId) return JSON.stringify({ error: "userId wajib" });
const conds = [eq(pgUserReputationsTable.user_id, userId)];
if (guildId) conds.push(eq(pgUserReputationsTable.guild_id, guildId));
const rows = await db
.select({
user_id: pgUserReputationsTable.user_id,
guild_id: pgUserReputationsTable.guild_id,
trust_score: pgUserReputationsTable.trust_score,
clean_message_streak: pgUserReputationsTable.clean_message_streak,
total_infractions: pgUserReputationsTable.total_infractions,
last_infraction_at: pgUserReputationsTable.last_infraction_at,
})
.from(pgUserReputationsTable)
.where(and(...conds))
.limit(1);
return JSON.stringify(rows[0] ?? { error: "reputasi tidak ditemukan" });
// The per-user reputation feature (trust scores, streaks, infractions) was
// removed from the gateway (migration 0016 drops user_reputations). Return
// an honest "unavailable" answer from the derived moderation signals instead
// of querying the now-dropped table.
return JSON.stringify({
available: false,
message:
"Skor trust/skala reputasi per-user telah dihapus dari sistem. Gunakan rasio pesan ter-flag vs total untuk menilai risiko (lihat dashboard Users).",
});
}
async function channelCulture(channelId?: string): Promise<string> {
@@ -155,6 +155,8 @@ export class DashboardRepository {
p.profile_summary,
m.total_messages,
m.flagged_count,
m.clean_count,
m.warn_count,
m.last_message_at
FROM (
SELECT
@@ -163,6 +165,8 @@ export class DashboardRepository {
avatar_url,
COUNT(*)::int AS total_messages,
COUNT(*) FILTER (WHERE ai_status = 'flagged')::int AS flagged_count,
COUNT(*) FILTER (WHERE ai_status = 'clean')::int AS clean_count,
COUNT(*) FILTER (WHERE ai_status = 'warn')::int AS warn_count,
MAX(created_at) AS last_message_at
FROM ${pgMessagesTable}
GROUP BY user_id, username, avatar_url
@@ -182,6 +186,8 @@ export class DashboardRepository {
profile_summary: r.profile_summary as string | null,
total_messages: Number(r.total_messages),
flagged_count: Number(r.flagged_count),
clean_count: Number(r.clean_count),
warn_count: Number(r.warn_count),
last_message_at: r.last_message_at ? Number(r.last_message_at) : null,
}));
@@ -429,6 +435,7 @@ export class DashboardRepository {
m.total_messages,
m.flagged_count,
m.clean_count,
m.warn_count,
p.profile_summary,
p.last_analyzed_at
FROM (
@@ -438,7 +445,8 @@ export class DashboardRepository {
avatar_url,
COUNT(*)::int AS total_messages,
COUNT(*) FILTER (WHERE ai_status = 'flagged')::int AS flagged_count,
COUNT(*) FILTER (WHERE ai_status = 'clean')::int AS clean_count
COUNT(*) FILTER (WHERE ai_status = 'clean')::int AS clean_count,
COUNT(*) FILTER (WHERE ai_status = 'warn')::int AS warn_count
FROM ${pgMessagesTable}
WHERE user_id = ${userId}
GROUP BY user_id, username, avatar_url
@@ -466,6 +474,7 @@ export class DashboardRepository {
total_messages: Number(row.total_messages),
flagged_count: Number(row.flagged_count),
clean_count: Number(row.clean_count),
warn_count: Number(row.warn_count),
profile_summary: row.profile_summary as string | null,
last_analyzed_at: row.last_analyzed_at
? Number(row.last_analyzed_at)
@@ -1,13 +0,0 @@
import { z } from "zod";
export const mediaQueueSchema = z.object({
source: z.string().min(1, "source is required"),
mode: z.enum(["music", "screen"]).default("music"),
});
export const mediaLoopSchema = z.object({
loop: z.boolean().default(false),
});
export type MediaQueueInput = z.infer<typeof mediaQueueSchema>;
export type MediaLoopInput = z.infer<typeof mediaLoopSchema>;

Some files were not shown because too many files have changed in this diff Show More