Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -61,40 +61,52 @@ export default defineConfig({
// a title nobody would write on the page itself, and the page is what it goes to. So the
// section is the first line, and the groups under it are what they were.
sidebar: {
// One page per feature, each built around a diagram you can push. The first group is
// the one to send somebody who wants to know what is unusual about this.
// The capability map groups the whole system; the pages beneath it take one mechanism
// far enough to explain its trade-offs, each around a diagram the reader can push.
'/features/': [
{ text: 'Features', link: '/features/' },
{ text: 'Capability map', link: '/features/capabilities' },
{
text: 'The deep end',
text: 'Flow system',
collapsed: false,
items: [
{ text: 'The anchor', link: '/features/anchor' },
{ text: 'Two accounts of one CLI', link: '/features/accounts' },
{ text: 'One timeline', link: '/features/tracing' },
{ text: 'A line typed mid-turn', link: '/features/steering' },
{ text: 'Answers in a shape', link: '/features/shapes' },
],
},
{
text: 'The shape of a run',
collapsed: false,
items: [
{ text: 'Twelve CLIs, one agent', link: '/features/backends' },
{ text: 'Python becomes a prophecy', link: '/features/prophecy' },
{ text: 'A flow is Python', link: '/features/flows' },
{ text: 'Many turns at once', link: '/features/concurrency' },
{ text: 'Picked up where it stopped', link: '/features/resuming' },
],
},
{
text: 'Who is at the other end',
text: 'Agent control plane',
collapsed: false,
items: [
{ text: 'Many backends, one agent', link: '/features/backends' },
{ text: 'Two accounts of one CLI', link: '/features/accounts' },
{ text: 'A line typed mid-turn', link: '/features/steering' },
{ text: 'Answers in a shape', link: '/features/shapes' },
{ text: 'It decides when it is done', link: '/features/goals' },
{ text: 'The moments of a turn', link: '/features/hooks' },
{ text: 'You, as one of the agents', link: '/features/human' },
],
},
{
text: 'Execution fabric',
collapsed: false,
items: [{ text: 'The anchor', link: '/features/anchor' }],
},
{
text: 'Run continuity and observability',
collapsed: false,
items: [
{ text: 'The terminal can leave', link: '/features/daemon' },
{ text: 'One timeline', link: '/features/tracing' },
],
},
{
text: 'Product surfaces',
collapsed: false,
items: [{ text: 'One system, four ways in', link: '/features/surfaces' }],
},
],

// One page per flow, named the way `-f` takes it. The order is the order they are worth
Expand Down
72 changes: 50 additions & 22 deletions docs/.vitepress/theme/components/HmzBackends.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// The rows are `hmz/backends.py` and the classes in `hmz/agents/`: which session base a
// backend derives from is what decides whether it can be talked to mid-turn, `shapes` is
// whether it can be held to a schema, and a `_pursue` of its own is whether it has a goal.
// `trace` means `hmz/tracing/collector.py` has a reader for the backend's logs; it does not
// mean only that the backend writes logs or that humanize can tally them while it runs.
import { computed, ref } from 'vue'

type Driven = 'held' | 'server' | 'command' | 'sdk' | 'protocol'
Expand All @@ -16,7 +18,7 @@ interface Backend {
steer: string
shape: string
goal: boolean
logs: string
trace: boolean
skills: string
note: string
}
Expand All @@ -38,7 +40,7 @@ const BACKENDS: Backend[] = [
steer: 'answered inside the same turn',
shape: 'held to it',
goal: true,
logs: 'read back',
trace: true,
skills: 'its own, and the project’s',
note: '“ultracode” is “xhigh” with the turn opted into orchestrating a fleet of its own. It is real, undocumented, and no listing the CLI answers with will ever name it — so humanize writes it down.',
},
Expand All @@ -50,7 +52,7 @@ const BACKENDS: Backend[] = [
steer: 'a steer on the running turn',
shape: 'held to it',
goal: true,
logs: 'read back',
trace: true,
skills: 'four places, the shared one included',
note: 'Its models differ from each other: one takes “ultra” and the one beside it does not, so the ladder is narrowed per model by what the backend itself says when it is asked what it runs.',
},
Expand All @@ -62,7 +64,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'asked in the prompt',
goal: false,
logs: 'none to read',
trace: false,
skills: 'its own, and the project’s',
note: 'It has no flag for a rung: its models are parameterized, so how hard it thinks and how quickly it is served are written into the model itself — “composer-2.5[effort=high,fast=false]”. A model already spelled with a bracket is passed exactly as it was written.',
},
Expand All @@ -74,7 +76,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'asked in the prompt',
goal: true,
logs: 'read back',
trace: true,
skills: 'none',
note: 'The one backend that arrives with humanize rather than being found on your PATH — its SDK and the runtime its turns are taken on are ordinary dependencies.',
},
Expand All @@ -87,7 +89,7 @@ const BACKENDS: Backend[] = [
steer: 'queued, then steered in',
shape: 'asked in the prompt',
goal: true,
logs: 'read back',
trace: true,
skills: 'its own, the shared one, the project’s',
note: 'Its effort says how wide as well as how hard: “max” is one agent and “swarmmax” is the same thinking at the width of a fleet, so width is chosen beside the effort rather than among the rungs.',
},
Expand All @@ -99,7 +101,7 @@ const BACKENDS: Backend[] = [
steer: 'a steer on the run it is making',
shape: 'asked in the prompt',
goal: false,
logs: 'read back',
trace: false,
skills: 'its own, and the shared one',
note: '“off” is the model asked not to think at all. That is an effort like any other here: the least of them, not the absence of a setting.',
},
Expand All @@ -111,7 +113,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'held to it',
goal: false,
logs: 'read back',
trace: false,
skills: 'eight places, two of them other harnesses’',
note: 'The ladder is written as it enumerates them when it refuses one, because a rung it refuses is a turn that never starts.',
},
Expand All @@ -123,7 +125,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'held to it',
goal: false,
logs: 'read back',
trace: false,
skills: 'four places',
note: 'It has no flag for an effort — they are a setting of its own settings file, so a turn is pointed at one of humanize’s instead of anybody’s being rewritten.',
},
Expand All @@ -135,7 +137,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'held to it',
goal: false,
logs: 'none to read',
trace: false,
skills: 'its own',
note: 'A conversation here is rows of a database whose payloads are protobuf, so there is no log to read a run’s cost out of as it is spent, and none to gather afterwards.',
},
Expand All @@ -147,7 +149,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'asked in the prompt',
goal: false,
logs: 'none to read',
trace: false,
skills: 'three places',
note: 'Its effort is the model variant rather than a thinking level of its own, and a provider with no variants takes the flag and ignores it.',
},
Expand All @@ -159,7 +161,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'asked in the prompt',
goal: false,
logs: 'none to read',
trace: false,
skills: 'five places',
note: 'A fork of opencode, and one directory more: it reads Codex’s skills as well as Claude Code’s.',
},
Expand All @@ -171,7 +173,7 @@ const BACKENDS: Backend[] = [
steer: 'no',
shape: 'asked in the prompt',
goal: true,
logs: 'read back',
trace: false,
skills: 'four places, the shared one included',
note: 'Its ladder is two vocabularies at once, because its models have two: the ones that take a thinking budget answer “max”, “high”, “low” and “nothink”, and the ones that only take thinking or not answer “enabled” or “disabled”. A model narrows it to its own half.',
},
Expand All @@ -180,10 +182,10 @@ const BACKENDS: Backend[] = [
called: 'anything speaking ACP',
driven: 'protocol',
efforts: ['as configured'],
steer: 'a word put in',
steer: 'no',
shape: 'asked in the prompt',
goal: false,
logs: 'none to read',
trace: false,
skills: 'none',
note: 'The protocol says nothing about which models an agent runs or how hard it may be asked to think — both are the agent’s own — so one rung is offered and none is sent.',
},
Expand All @@ -199,7 +201,7 @@ const WANTS: Want[] = [
{ key: 'steer', said: 'takes a word mid-turn', holds: (one) => one.steer !== 'no' },
{ key: 'shape', said: 'held to a shape', holds: (one) => one.shape === 'held to it' },
{ key: 'goal', said: 'has a goal of its own', holds: (one) => one.goal },
{ key: 'logs', said: 'leaves a log to trace', holds: (one) => one.logs === 'read back' },
{ key: 'trace', said: 'can be read back into a trace', holds: (one) => one.trace },
{ key: 'swarm', said: 'runs a turn as a fleet', holds: (one) => Boolean(one.swarms) },
]

Expand All @@ -216,24 +218,39 @@ const asked = computed(() => WANTS.filter((one) => wanted.value.includes(one.key
const fits = (one: Backend) => asked.value.every((each) => each.holds(one))
const open = computed(() => BACKENDS.find((one) => one.name === opened.value) ?? BACKENDS[0])
const counted = computed(() => BACKENDS.filter(fits).length)

function backendLabel(one: Backend) {
return [
one.name,
`driven through ${DRIVEN[one.driven]}`,
`hardest effort ${one.efforts[0]}`,
`mid-turn ${one.steer}`,
`shape ${one.shape}`,
`goal ${one.goal ? 'yes' : 'no'}`,
`trace ${one.trace ? 'read back' : 'no reader'}`,
].join(', ')
}
</script>

<template>
<div class="backends hmz-panel">
<div class="bar">
<span class="what">what a flow may ask an agent for</span>
<div class="wants">
<div class="wants" role="group" aria-label="filter backends by capability">
<button
v-for="one in WANTS"
:key="one.key"
type="button"
:aria-pressed="wanted.includes(one.key)"
:class="{ on: wanted.includes(one.key) }"
@click="want(one.key)"
>
{{ one.said }}
</button>
</div>
<span class="count">{{ counted }} of {{ BACKENDS.length }}</span>
<span class="count" aria-live="polite">
{{ counted }} of {{ BACKENDS.length }}
</span>
</div>

<div class="table">
Expand All @@ -244,14 +261,17 @@ const counted = computed(() => BACKENDS.filter(fits).length)
<span>mid-turn</span>
<span>a shape</span>
<span>a goal</span>
<span>a log</span>
<span>trace</span>
</div>
<button
v-for="one in BACKENDS"
:key="one.name"
type="button"
class="row"
:class="{ dim: !fits(one), on: opened === one.name }"
:aria-label="backendLabel(one)"
:aria-pressed="opened === one.name"
aria-controls="backend-detail"
@click="opened = one.name"
>
<span class="name">
Expand All @@ -273,11 +293,11 @@ const counted = computed(() => BACKENDS.filter(fits).length)
<span :class="{ yes: one.steer !== 'no', no: one.steer === 'no' }">{{ one.steer }}</span>
<span :class="{ yes: one.shape === 'held to it' }">{{ one.shape }}</span>
<span :class="one.goal ? 'yes' : 'no'">{{ one.goal ? 'yes' : 'no' }}</span>
<span :class="one.logs === 'read back' ? 'yes' : 'no'">{{ one.logs }}</span>
<span :class="one.trace ? 'yes' : 'no'">{{ one.trace ? 'read back' : 'no reader' }}</span>
</button>
</div>

<div class="open">
<div id="backend-detail" class="open" role="status" aria-live="polite">
<div class="ladder">
<span class="lab">{{ open.name }} · its own ladder, hardest first</span>
<div class="rungs">
Expand All @@ -292,6 +312,14 @@ const counted = computed(() => BACKENDS.filter(fits).length)
</div>
<div class="said">
<p class="driven"><strong>driven through</strong> {{ DRIVEN[open.driven] }}</p>
<p class="trace">
<strong>trace read-back</strong>
{{
open.trace
? 'humanize can collect this backend’s session log into a Chrome trace'
: 'no trace reader yet — it may still write logs or report usage while it runs'
}}
</p>
<p class="skills"><strong>skills it would load</strong> {{ open.skills }}</p>
<p class="note">{{ open.note }}</p>
</div>
Expand Down Expand Up @@ -387,7 +415,7 @@ const counted = computed(() => BACKENDS.filter(fits).length)
}

.row.dim {
opacity: 0.28;
opacity: 0.72;
}

.name {
Expand Down
Loading