Files
hanzo.ai/components/home/AgentRuntime.tsx

133 lines
6.4 KiB
TypeScript

'use client'
import { YStack } from '@hanzo/ui'
import { Action, Actions, Band, Head } from './band'
import { Caption, Dim, Mono, Plate, Row } from './plate'
/**
* What an agent DOES — the layer between Enso and the company. The page could
* describe a model and a cloud but not the thing in between them that does the
* work, and this is that thing.
*
* THE LEDE IS AN ORDER, and that is still its job. Enso decides, agents
* coordinate, Tabs is where they act — the three ranked in one sentence instead
* of competing as equals. Anything added here has to fit somewhere in that order
* or it does not belong on this page.
*
* THE PICTURE IS ONE TOOL DISPATCH, in the shape the run's own span records it.
* "Agents with hands" was five words in five pills — use tools, operate
* software, write code, run workflows, hand off to each other — which is a comma
* list wearing borders, and the least legible way to render the most concrete
* thing on the page. A reader skips a list of five verbs. One hand actually
* moving is the claim.
*
* WHERE IT WAS READ: hanzoai/cloud `apps/agents/tools.go`. `dispatchOne` opens a
* span named `agent.tool <name>` and sets `gen_ai.tool.name`,
* `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result`,
* `hanzo.agent.tool_subsystem`, `hanzo.agent.tool_round` and
* `hanzo.agent.tool_outcome` — whose vocabulary is ok | rejected | error, set on
* every exit including the happy one, because a status written only on failure
* cannot tell "succeeded" from "never finished". The rows here are those fields.
* `post_v1_exec_run` is the tool name that file quotes; `exec` is not typed
* beside it but FOLLOWS from it, since `toolSubsystem` reads the owner as the
* second word of `<method>_<subsystem>_<rest>`. The argument keys are `CodeRun`'s
* own (`apps/exec/exec.go`): `lang` selects the toolchain, `code` is the whole
* program. The VALUES are a hand-authored sample and the caption says so.
*
* NO NUMBER HERE WAS CHOSEN. 8 rounds, 30s a call, 90s a run and 3 agents deep
* are `maxToolRounds`, `toolCallTimeout`, `toolRunBudget` and `maxAgentDepth` —
* the loop's own constants, in the same file. They are the whole answer to the
* question a reader actually has about handing software a credential, so they
* are drawn rather than reassured about. "The eighth gets no tools" is that
* file's design too: the last round is offered none, so the loop cannot end in
* anything but words.
*
* "HAND OFF TO EACH OTHER" SURVIVES AS A BOUND, which is the honest form of it.
* An agent is itself a tool (`agentToolProvider`), so a handoff IS one of these
* dispatches one level down, and `maxAgentDepth` is what stops A→B→A from
* starting a fresh round cap and a fresh fee at every level. A separate arrow
* would have drawn the same fact twice, once without its limit.
*
* THE ISOLATION SENTENCE IS CUT, and it was an overclaim rather than merely
* prose. "Every one of them in an isolated environment" said every hand runs
* isolated; the sandbox belongs to `exec`, in the family's own words ("run a
* snippet in a sandbox"), and it is per SESSION — `CodeRun.SessionID` continues
* an EXISTING sandbox so one run's output file is the next run's input — not per
* call. `Security` already records the same correction from the other side, plus
* the harder one: there is no egress policy at all, so network isolation is
* never ours to claim. The plate says `runs in a sandbox` on the one row that
* earns it and nowhere else.
*
* NOTHING DRAWS AN ARROW. The loop is a real sequence — complete, dispatch,
* append, complete again — and it is still not drawn as one: what a reader needs
* is that a hand is attributed, bounded and recorded, and five arrows would sell
* the mechanism to someone who came to find out whether this is safe. One
* dispatch, opened.
*
* DELIBERATELY ABSENT: latency, a success rate, and how many tools exist. The
* first two have no committed measurement behind them, and a tool COUNT argues
* against our own product — /mcp's thesis is that a catalog of hundreds
* collapses into one tool per subsystem, because a tool list is a prompt.
*
* This file used to render a SECOND section after this one, the Hanzo Team
* block. That is now `./Team`: two claims in one file meant the company layer
* could only be restyled by editing the agent layer.
*/
/** Attribute names print padded to the widest, as a span viewer would — in mono,
* NBSP is a full column, so the values start at one x. */
const lead = (s: string) => s.padEnd(9, '\u00A0')
export default function AgentRuntime() {
return (
<Band>
<Head
eyebrow="Agent runtime"
title="Agents with hands."
lede="Enso decides. Agents coordinate. Tabs gives them somewhere secure to act."
/>
{/* The plate is LEFT-set inside a centred measure. Head centres its own
grid; a log read down a column of aligned field names cannot be
centred, so it sits outside that grid rather than fighting it. */}
<YStack width="max-content" maxW="min(520px, 100%)" mx="auto" mt={28}>
<Plate>
<Row lead={lead('tool')}>post_v1_exec_run</Row>
<Row lead={lead('subsystem')}>
exec<Dim> · runs in a sandbox</Dim>
</Row>
<Row lead={lead('round')}>
2 of 8<Dim> · the eighth gets no tools</Dim>
</Row>
<Row lead={lead('actor')}>
acme/z<Dim> · billed here</Dim>
</Row>
<Row lead={lead('arguments')}>
{'{"lang":"py","code":"…"}'}
<Dim> · redacted</Dim>
</Row>
<Row lead={lead('result')} verdict="ok">
returned to the model
</Row>
{/* The bounds, under a hairline: they belong to the loop rather than
to this dispatch, and they are the line a reader weighing an
autonomous run is looking for. Allowed to wrap — it is a limit
table, not a log line, so nothing about it is read left to right. */}
<Mono mt={4} pt={10} borderTopWidth={1} borderColor="$edge" color="var(--text-tertiary)">
bounds · 30s a call · 90s a run · 3 agents deep
</Mono>
</Plate>
<Caption>one tool dispatch, as the run&rsquo;s span records it · sample values</Caption>
</YStack>
<Actions>
<Action href="/tabs" filled>
Explore Tabs
</Action>
<Action href="/agents">Explore agents</Action>
</Actions>
</Band>
)
}