Motty
터미널·SFTP·포트 포워딩·VNC·RDP·AI 도우미를 하나의 창에 통합, Go 기반 무료 오픈소스
WitUS Inbox 제출을 분류하고 조치를 제안하며 인간 승인 단계로 라우팅하는 LangGraph 에이전트
A LangGraph agent that classifies WitUS Inbox submissions, proposes an action, and routes through a human-in-the-loop approval gate.
개발 도구TypeScript · JavaScriptGitHub @dapperAuteur온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
A LangGraph agent that reads incoming WitUS Inbox submissions, classifies them, enriches them with context, proposes an action, and waits for a human to approve before doing anything irreversible.
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A LangGraph agent that reads incoming WitUS Inbox submissions, classifies them, enriches them with context, proposes an action, and waits for a human to approve before doing anything irreversible.
classify → enrich → propose → human approval → execute | log rejection
WitUS Inbox collects form submissions from across a small ecosystem of products into one table. Reading every webhook by hand does not scale. This repo is the LangGraph agent that triages that traffic: typed graph state, schema-validated tools, and a human-in-the-loop interrupt that pauses the graph until an operator approves. It is production code with a real database, real auth, and real failure handling, not a demo.
It also runs on two LLM providers from one codebase: Gemini 2.5 Flash for
development and CI, Claude Sonnet 4.6 for production. The provider is selected by
environment, and an admin dashboard at /admin picks the model per graph node, so
the same agent can be tested cheaply and tuned without a redeploy.
A 4-lesson curriculum under docs/lessons/ teaches the
patterns this repo demonstrates.
WitUS Inbox is a small internal tool that ingests signed webhooks from every product in
the WitUS ecosystem (CentenarianOS, FlashLearnAI, Wanderlearn, Fly.WitUS, Work.WitUS,
Tour Manager OS, witus.online) and lands them in one canonical submission table.
Without an agent, every submission, the obvious ones included, costs human attention. Most are resolvable in two clicks; a few are urgent; the rest are pattern-matchable noise. This agent sits between the webhook and the human. It does the read-and-propose. The human keeps the decision.
graph TD
START([Webhook received]) --> CLASSIFY[Classify submission]
CLASSIFY --> ENRICH[Enrich with past context]
ENRICH --> PROPOSE[Propose action]
PROPOSE --> APPROVAL{Human approval}
APPROVAL -->|approved / edited| EXECUTE[Execute action]
APPROVAL -->|rejected| LOG[Log + close]
EXECUTE --> END([Done])
LOG --> END
Six nodes. One durable approval interrupt. Five tools. One typed TriageState object
that carries everything between steps. The graph is one small, readable file:
agent/graph.ts.
This repo demonstrates three LangGraph patterns; each has a companion lesson.
Every node reads and writes one TriageState object, the contract between nodes. Each
field is owned by exactly one producing node and is optional until that node runs.
export const TriageStateAnnotation = Annotation.Root({
rawSubmission: Annotation<RawSubmission>,
classification: Annotation<Classification | undefined>,
enrichment: Annotation<Enrichment | undefined>,
proposedAction: Annotation<ProposedAction | undefined>,
approval: Annotation<Approval | undefined>,
execution: Annotation<Execution | undefined>,
});
Full state: agent/state.ts · Schemas: agent/schemas.ts
Each of the five tools is a tool() wrapper with a Zod input schema: the same schema
documents the inputs, validates them, and (when bound to a model) becomes the JSON schema
the model sees.
export const searchPastSubmissions = tool(runSearchPastSubmissions, {
name: "search_past_submissions",
description: "Find prior WitUS Inbox submissions from the same contact email.",
schema: SearchPastSubmissionsInputSchema,
});
All five tools: agent/tools/
The graph pauses at human_approval via a real LangGraph interrupt(). A Postgres
checkpointer persists the paused run, so the pause survives across HTTP requests and
process restarts. The approval arrives on a separate request and resumes the exact
same graph thread with new Command({ resume }). Execution is unreachable except through
an approval. This is the pattern most agent demos skip and production systems need.
Nodes: agent/nodes/ · Checkpointer: agent/checkpointer.ts
You need Node 20+ and a Postgres 16 database (local, or Neon's free tier). This repo owns its own database. It does not connect to the live WitUS Inbox DB.
# 1. Install
npm install
# 2. Create a database
createdb witus_triage_agent
# 3. Configure
cp .env.example .env.local
# Minimum to run the graph:
# STORAGE_DATABASE_URL=postgresql://localhost:5432/witus_triage_agent
# STORAGE_DATABASE_URL_UNPOOLED=postgresql://localhost:5432/witus_triage_agent
# GEMINI_API_KEY=... (testing — or ANTHROPIC_API_KEY for production)
# 4. Create the tables
npm run db:migrate
# 5. Seed the 25 hand-labeled fixture submissions
npm run db:seed
# 6. Run
npm run dev # http://localhost:3000
npm test exercises the graph end to end (skipping live-LLM tests when no key is set).
The PRD's target is clone-to-running in under 15 minutes. If it is slower for you, that
is a real bug worth an issue.
If you just need to use the dashboard (review the queue, approve or reject), start with the operator guide, written in plain language with no setup steps:
/help (linked from the menu and
footer; reachable without signing in).docs/operator-guide/: getting
started, the queue, reading a run, approving/rejecting, what the categories mean, and an
FAQ.여기까지가 README 앞부분입니다. 나머지는 원문에서 읽을 수 있습니다. README 전문 보기 ↗