AI platform · vevano — component status
The platform's own components · ai.vevano.vevano.no. Agents are tenant artifacts and are not listed here; observability lives in the shared foundation.
Core
| Component | Status | Endpoint | Time |
|---|---|---|---|
| LLM observability (Langfuse) | up · 200 | http://langfuse-web.langfuse.svc.cluster.local:3000/api/public/health | 6 ms |
| Agent runtime (LangGraph) | up · 200 | http://langgraph.assistant.svc.cluster.local:8000/health | 5 ms |
| LiteLLM gateway | up · 200 | http://litellm.litellm.svc.cluster.local:4000/health/liveliness | 4 ms |
| MCP tool servers speaks MCP, not HTTP health — no endpoint to probe | not probed | — | — |
| Vector store (Qdrant) | up · 200 | http://vevano-qdrant.qdrant.svc.cluster.local:6333/healthz | 6 ms |
| Embeddings (TEI) | up · 200 | http://tei.tei.svc.cluster.local:8080/health | 6 ms |
Sandbox
| Component | Status | Endpoint | Time |
|---|---|---|---|
| Credential broker not deployed on this tenant yet | not probed | — | — |
| Browser (Playwright) no Service published by this release | not probed | — | — |
| Agent workspace verb/MCP surface only — no HTTP health route | not probed | — | — |
Board
| Component | Status | Endpoint | Time |
|---|---|---|---|
| HITL receiver no Service published by this release | not probed | — | — |
How it is wired
Down is where the agent acts:
MCP servers is the tool layer — the packs we ship — and the browser is one more server in that layer, drawn beside the systems a tool reaches, inside a pink sandbox frame. The nesting is the point: the browser is a tool by its interface (Chromium + @playwright/mcp: the agent seesbrowser_* in its tool list like any other tool) and a sandbox by its deployment — one disposable pod per session, its own namespace, an egress list that is empty by default and only grows when a human types a destination into the release, a TTL reaper. It is also the platform's largest untrusted-input surface, because it renders someone else's page, which is exactly why it is fenced rather than shared. Pink is the third colour: blue says whose thing it is, pink says what is quarantined.One thing the arrow cannot say on its own: the edge from the tool layer meansthe browser is one of these, not the browser is proxied through the packs. Every MCP server is its own endpoint and the agent calls each directly — the box stands for the layer, because a picture with one box per pack would be a list, not a wiring.
The two grey boxes under the tools are what a pack would reach once a tenant names one — an external SaaS with its own API and the caller's token, or the tenant's own services inside the fence. Both are dashed on this tenant: nothing is wired to them yet, and the diagram says so rather than implying a capability we have not proved. The sandbox and the tool edges are dashed for the same reason.
Right is who calls us. Three kinds of caller run into ONE
inbound gateway, because the platform must not learn which caller spoke — only that a verified one did: a human surface (a Teams message, a card action, a column move on the board), a partner agent over A2A, or a system event(a push, a signature, a settlement). A webhook and an A2A task are the same shape of thing and the same hazard: attacker-controllable text that will end up in a model's context, so the gateway verifies the sender, dedupes on their event id (they retry — twice is normal), maps it to an identity, a subject and an event, and enqueues. It ACKs in seconds and never runs the agent inline; the run happens off the inbox row. Every one of those four edges is dashed today: no inbound path is wired on this tenant yet.A2A is half-built, and the figure says which half. The runtime already serves an Agent Card at
/.well-known/agent-card.json, projected from the agent's own manifest, so a partner can discover what this agent does — that is the card the peer reads. What is missing is the inbound task endpoint and the per-caller policy, which is why the peer's edge is dashed. The task vocabulary is already in the runtime (TASK_STATE_INPUT_REQUIRED is how a parked human gate is projected onto the wire), so the same gateway that answers a person answers an agent.What is NOT drawn, and why. The Cline runner and the surface it talked to are tenant artifacts, not platform components — drawing them here made the page look like a two-agent product, so they are gone. The gateway's measured negative stays true and is stated in the band: it exports spans to the collector but ships no traces to Langfuse, which is fed by the agent SDK.
Custom Surfaces
A surface is a purpose-built interface on its own Postgres — the shape the work already has, made clickable. An agent is defined with one of them: the surface is what gives the work a structure the agent can act on, and the agent is what makes the surface able to act back.
- the surface owns the data — It has its own Postgres and its own read model. The agent does not get a database connection.
- the agent acts through verbs — A narrow, declared set —
task.move,task.schedule— checked per call against the verb table. Never SQL, never a generic (table, row). - nothing runs inline — An action in the UI emits a webhook into the inbound gateway (verify · dedupe · enqueue). The run answers with a verb and a comment. A UI that waits on a model is a frozen UI.
| Kanban board | Gantt chart | |
|---|---|---|
| the question it answers | What state is this work in, and who owns it? | When does this happen, and what blocks it? |
| what it projects | columns = state · cards = tasks · a comment thread per card | rows = tasks on a time axis · bars = start…end · arrows = dependencies |
| what it owns (its Postgres) | tasks(id, title, state, owner, updated_at) · task_comments | tasks(id, title, start, end) · task_dependencies(from, to) |
| what the agent may call | task.move · task.comment · task.assign | task.schedule · task.link |
| what arrives as a webhook | card.moved · comment.added | task.rescheduled · dependency.added |
| the failure it prevents | work that moves without a record of who moved it | dates that quietly violate a dependency |
The list will grow — a calendar, a checklist, a spreadsheet — and a new surface is defined by the same three answers: what question it answers, what it projects, and which verbs the agent may call on it. What does not change is the wiring: the surface writes its own rows, the agent writes through verbs, and the surface talks to the agent only by emitting an event on the inbound seam.
Ideas
Agent runtime — who decides what
"Fully autonomous" is not a property of an agent, and it is not one dial either. Every agent shape is a different answer to three questions:(1) who picks each transition — your code, the model, or a check;(2) who says what steps exist — you at design time, or the model at run time;(3) who says stop. The figure colours the arrows for the first question, because the boxes are the same kind of thing in every lane: what makes an agent an agent is that some of its edges are blue.
| pattern | who picks each transition | who says what steps exist | who says stop |
|---|---|---|---|
| prompt chain | code | code | code |
| routing | model | code | code |
| orchestrator–workers | code, in a worker | model | model |
| evaluator–optimizer | code | code | check |
| free agent loop | model | code (tools) | model |
| the model writes the plan | model | model | model |
The design conclusion is not "pick a level" — it is that these are four labelings of one engine. A node is a step, an edge is a transition, and every edge carries an author (code, model, check). Then a router, an orchestrator, a quality loop and a free agent are configurations, not architectures — same executor, same checkpoint, same accounting. Which is also what makes a framework question the wrong one: you are choosing a label per edge, not a platform for the whole agent.
MCP or a plain API — it is decided by clients, not by agents
This is the question that decides the shape of an integration, and it cannot be settled until three words stop being used as if they were one thing.
- spec
- The service's own description of itself —
/openapi.json: paths, parameters, schemas, error shapes. It is not a tool, and the model never sees it. It belongs to the provider, it changes when the provider changes, and it is far wider than anything we want to expose: Flowcase's spec has hundreds of operations and this agent needs two. - tool
- One thing the model may choose: a name, a sentence saying when to use it, a JSON schema for its arguments, and one verb. This is the model's whole vocabulary for acting, and it is re-sent every turn — so a large catalogue is a real per-turn cost. That is also why a tool is intent-shaped (
submit_application), never a generic door (delete(table, row)): the second one makes the wrong action representable. - adapter
- The code in between, and the only part that costs anything to own: it reads the spec, picks and narrows the two operations worth exposing, mints the tool schemas, maps auth and errors, and enforces the verb. It is a program in a process — therefore the entire questionMCP or OpenAPI is really whose process the adapter lives in: one copy per runtime, or one copy in a service they all call.
The instinct "one agent can call an OpenAPI service, but several agents need MCP" is wrong in its premise: several agents call the same OpenAPI service exactly as easily as one does. What scales with consumers is the adapter, so count runtimes — not agents. Cases A and B below are the same Flowcase integration with more agents and the same single adapter; the only thing that changes in case C is how many dashed boxes you keep alive.
Case C is where the arithmetic changes, because there are three client programs to teach. Over OpenAPI that is three adapters, each re-tested when the spec moves; over MCP it is zero, because a LangGraph client, a Cline runner and a partner's app already speak the protocol.Note what did not change: Flowcase is reached once either way. The protocol is a property of the seam between the platform and its clients — not of the SaaS on the end.
Case D is the correction case C invites. "Three adapters" is only the price if the adapter stays inside each runtime. Nothing forces that: the translator is one program, so it can be lifted into a service the three runtimes call — and then the copy count is one again, which is where we started in case A. What the three runtimes still need is a client, and a client for a gateway you specified is a list/call pair — about a hundred lines, written once per runtime, and then reused unchanged for every tool you ever add, because the gateway already knows how to read a spec. So cases C2 and D are the same box: a shared, fenced, schema-carrying service. The protocol is the wire to it, not the architecture of it — and the honest reason to prefer the MCP wire is that somebody else already wrote the hundred lines for every runtime you might ever have, including one you write yourself.
So the decision rule, in the order it should be applied. Write the translator once, and never once per runtime. With one runtime that means in-process, which is also the cheapest thing to debug and the easiest to curl. With two or more it means a service — a gateway holding the spec→tool conversion, the verbs, the error mapping and the rate limit, with each runtime keeping only a generic list/call client.Choose the wire separately from the shape: stay on plain HTTP while the callers are ours (one client, ~100 lines, no protocol to adopt); speak MCP when a caller appears that you do not control and do not want to write one for — a partner's app, a no-code tool, a runtime someone else maintains. Choose a per-integration MCP pack (case C2) instead of a gateway when the integration ships on its own version, with its own credential and its own team — a release boundary, not a client count. For this scenario none of those triggers holds today, and one thing argues against a shared service holding the token specifically: the Flowcase credential is hers, so a server storing N users' tokens becomes a token-swapping proxy whose whole job is to never leak one session into another. A gateway that forwards her bearer — or a client that calls Flowcase directly — leaves that authorization where it already works: upstream.
What is free, and in which stack
The only practical question left after the atlas: for each thing a runtime needs, is itprovided, a library you wire (and own the policy for), orcode you write? Two shapes and three stacks, because the shape changes fewer rows than the stack does. ⬤ provided ·◐ library, you wire it · ✎ you write it ·▤ a manifest, not code · — not applicable at that shape. A ✓ in front of a name means the shipped LG-ag runtime implements it — LG-ag is in yellow because that is the column this product now occupies.
| capability | LG-wf | LG-ag | Go-wf | Go-ag | Rs-wf | Rs-ag |
|---|---|---|---|---|---|---|
| execution skeleton | ||||||
| implemented in the shipped runtime. step scheduler incl. cycles | provided | provided | you write it | you write it | you write it | you write it |
| implemented in the shipped runtime. conditional routing | provided | provided | you write it | you write it | you write it | you write it |
| parallel fan-out + join | provided | library, you wire it | you write it | you write it | you write it | you write it |
| subgraphs / subagent delegation | library, you wire it | provided | you write it | you write it | you write it | you write it |
| implemented in the shipped runtime. the model ↔ tool loop itself | not applicable | provided | not applicable | you write it | not applicable | you write it |
| implemented in the shipped runtime. provider tool-call parsing + schema binding | library, you wire it | provided | you write it | you write it | you write it | you write it |
| implemented in the shipped runtime. streaming (text and tool deltas) | library, you wire it | library, you wire it | you write it | you write it | you write it | you write it |
| implemented in the shipped runtime. per-run cancellation | library, you wire it | library, you wire it | provided | provided | library, you wire it | library, you wire it |
| implemented in the shipped runtime. step cap | provided | provided | you write it | you write it | you write it | you write it |
| durability | ||||||
| implemented in the shipped runtime. checkpoint store + resume | provided | provided | you write it | you write it | you write it | you write it |
| HITL park / resume | provided | provided | you write it | you write it | you write it | you write it |
| replay / eval fixtures from a trace | library, you wire it | library, you wire it | provided | provided | provided | provided |
| idempotency on resume | you write it | you write it | you write it | you write it | you write it | you write it |
| context — where the quality comes from | ||||||
| implemented in the shipped runtime. context budgeter (tokens per section) | not applicable | you write it | not applicable | you write it | not applicable | you write it |
| implemented in the shipped runtime. compaction + working-set pinning | not applicable | library, you wire it | not applicable | you write it | not applicable | you write it |
| implemented in the shipped runtime. tool results → artifact refs | library, you wire it | library, you wire it | you write it | you write it | you write it | you write it |
| skill catalogue / progressive disclosure | not applicable | you write it | not applicable | you write it | not applicable | you write it |
| memory retrieval with provenance | not applicable | library, you wire it | not applicable | you write it | not applicable | you write it |
| implemented in the shipped runtime. dedup + cache-friendly ordering | not applicable | you write it | not applicable | you write it | not applicable | you write it |
| authority | ||||||
| implemented in the shipped runtime. per-call policy / verb table | you write it | you write it | you write it | you write it | you write it | you write it |
| implemented in the shipped runtime. budget ledger — steps · tokens · cost · cycles | you write it | you write it | you write it | you write it | you write it | you write it |
| depth + concurrency caps | you write it | you write it | you write it | you write it | you write it | you write it |
| implemented in the shipped runtime. kill switch | you write it | you write it | you write it | you write it | you write it | you write it |
| credential brokering (short-lived) | you write it | you write it | you write it | you write it | you write it | you write it |
| platform | ||||||
| sandbox pod supervision + k8s client | library, you wire it | library, you wire it | library, you wire it | library, you wire it | library, you wire it | library, you wire it |
| implemented in the shipped runtime. egress / NetworkPolicy | a manifest | a manifest | a manifest | a manifest | a manifest | a manifest |
| trace emitter → Langfuse | provided | provided | you write it | you write it | you write it | you write it |
| a new edge kind is compiler-checked | you write it | you write it | you write it | you write it | provided | provided |
| build / iterate cycle | not applicable | not applicable | provided | provided | library, you wire it | library, you wire it |
| implemented in the shipped runtime. no dependency resolution in the shipped image | you write it | you write it | provided | provided | provided | provided |
| rows you write, of 30 | 8 | 11 | 18 | 24 | 17 | 23 |
| ✓ implemented in the shipped LG-ag runtime | 17 of 30 rows | |||||
| rows the stack provides | 7 | 9 | 4 | 4 | 3 | 3 |
The numbers in the footer are computed from the table, not typed in — and they make the point sharper than an argument would: 6 of 30 rows are ✎ in all six columns, and those are the ones that decide whether an agent is good (context discipline) or safe (budget ledger, verb table, caps, kill switch, brokering, idempotency). Choosing the LangGraph agent over the Go agent moves18 of 30 rows — but every one of them is tedious-but-known work, and not one is in those 6. That is why the stack question is real but not urgent: the 6 are the design, the rest is typing.