Motty
原生桌面 SSH 工作台:终端、SFTP/SCP、端口转发、VNC 与 RDP 远程桌面、AI 助手都在一个窗口里。Go 编写,没有 Electron,空闲时保持安静。免费开源,支持 macOS、Windows 和 Linux。
翻译准备中
尚未检测到更新。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 ↗