feat(agnt): Aura como superficie de presencia de la demo con modelo real

La respuesta a «¿está pensando?» no se improvisa — es Aura, el componente
del eje construido para ello. La sección real de la página agnt bootea un
uix standalone mínimo (createActiveUix + Soma + ActiveEidos + eidos css):
legítimo porque un app compone ambos tiers, y la demo ES el app.

- Motor llama → createActiveAgent (mismas opciones bus+journal); activeRun
  reactivo satisface AuraAgentSource; stream vía .engine.onEvent
- <Aura {agent}> con Indicator (orb $scene) + Status + Region + Cancel;
  authorize/escalation quedan como afordancias app-level (doctrina §7)
- Verificado sobre el run real de qwen3-coder: «Inactivo» → «Propone un
  plan» → announcement «Agente: terminó — control devuelto» en la Region,
  doc mutado, answer en es; Cancel = Button disabled-en-idle; consola 0

check 0 propios · agent 30/30 · smoke agnt PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alpha-0.1-sec-dom
dev 3 months ago
parent 447a87239c
commit 595040b73b

File diff suppressed because one or more lines are too long

@ -10,6 +10,7 @@
AGENT_EVENT_RUN_ESCALATED,
AGENT_EVENT_RUN_OPENED,
AGENT_EVENT_RUN_STATE,
createActiveAgent,
createEngineAgent,
createMemoryAgentJournal
} from '$agent';
@ -27,6 +28,13 @@
import { createOpenAiChatAgentTransport } from '$agent/adapters/openai-chat';
import { meta, object, pipe, string } from '$sium';
import { createEngineBus } from '$bus';
import { createActiveUix, setActiveUix } from '$active-uix';
import { Soma } from '$soma/core/soma.svelte';
import { siumLangs } from '$sium/langs/langs';
import { secsLangs } from '$libs/secs';
import { ActiveEidos } from '$uix/eidos';
import { Aura } from '$uix/eidos/components/aura';
import '@/uix/eidos/index.css';
import { createEngineTimers } from '$timer';
import { ORCA_STAGE_MAIN, createEngineOrca, orcaSuccess } from '$orca';
@ -143,8 +151,10 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
});
$effect(() => () => {
engine.dispose();
llamaEngine.dispose();
llamaAgent.dispose();
timers.dispose();
activeEidos.dispose();
uixRt.dispose();
});
// Deterministic gates (no timers, no fakes): the model turn and the act
@ -245,6 +255,23 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
);
// ── Real model — the same machine over a local llama (openai-chat) ─────
// Aura is the axis' presence surface — an APP composes BOTH tiers, so
// this section boots a minimal standalone uix (the demo IS the app).
const uixRt = createActiveUix({
langs: { schema: { sium: siumLangs, secs: secsLangs }, defaultLocale: 'es' as const }
});
setActiveUix(uixRt);
Soma.create();
let shellTheme = $state<'light' | 'dark'>('dark');
const activeEidos = ActiveEidos.create({
applyDom: true,
theme: 'base',
modeSource: { get: () => shellTheme, onChange: () => () => {} }
});
$effect(() => {
shellTheme = document.documentElement.dataset.theme === 'light' ? 'light' : 'dark';
});
const demoDoc = $state<{ title: string; lines: string[] }>({ title: 'Untitled', lines: [] });
const docCapabilities: AgentCapability[] = [
@ -280,7 +307,7 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
}
];
const llamaEngine = createEngineAgent({
const llamaAgent = createActiveAgent({
identity: {
name: 'Aria',
description: 'You act on a small demo document on behalf of the user.',
@ -291,38 +318,34 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
bus
});
const llamaLast = $derived(llamaAgent.runs.at(-1) ?? null);
let llamaBaseUrl = $state('http://localhost:11434/v1');
let llamaModel = $state('qwen3-coder:30b');
let llamaGoal = $state(
'Ponle al documento el título «Notas activeUIX» y añade dos líneas resumiendo qué es el eje agéntico.'
);
let llamaAutonomy = $state<AgentAutonomy>('auto');
let llamaRun = $state<AgentRunSnapshot | null>(null);
let llamaAnswer = $state('');
let llamaActivity = $state<string[]>([]);
let llamaError = $state('');
llamaEngine.onEvent((event) => {
if (event.type === 'run-changed') {
llamaRun = event.run;
return;
}
llamaAgent.engine.onEvent((event) => {
if (event.type === 'run-changed') return;
const stream = event.event;
if (stream.type === 'text-delta') llamaAnswer += stream.delta;
else if (stream.type === 'text-end') llamaAnswer += '\n\n';
else if (stream.type === 'call-start') llamaActivity = [...llamaActivity, stream.name];
});
const llamaActive = $derived(
llamaRun !== null && llamaRun.state !== 'returned' && llamaRun.state !== 'cancelled'
);
const llamaActive = $derived(llamaAgent.loading);
function startLlama() {
llamaAnswer = '';
llamaActivity = [];
llamaError = '';
try {
llamaEngine.start({
llamaAgent.start({
goal: llamaGoal,
autonomy: llamaAutonomy,
capabilities: docCapabilities,
@ -497,9 +520,11 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
<code>openai-chat</code> adapter (<code>$agent/adapters/openai-chat</code>: Ollama, llama.cpp
server, LM Studio or any OpenAI-compatible endpoint). The agent acts on a small demo document
through three typed capabilities; <code>doc.clear</code> is irreversible, so under
<code>auto</code> the machine escalates and waits for you. Direct browser→localhost is the
DEV story — in production the base URL points at your backend proxy (the credential never
ships to the browser).
<code>auto</code> the machine escalates and waits for you. The presence surface is
<strong>Aura</strong> — the axis' own component: the orb sustains while the model works,
tints on escalation, announces the return, and its Cancel IS the delegation exit. Direct
browser→localhost is the DEV story — in production the base URL points at your backend
proxy (the credential never ships to the browser).
</p>
<div class="playground">
@ -523,31 +548,41 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
{#if !llamaActive}
<button class="btn" onclick={startLlama}>Delegate to the model</button>
{/if}
{#if llamaRun && llamaRun.state === 'reviewing'}
<button class="btn" onclick={() => llamaEngine.authorize(llamaRun!.id)}>Authorize</button>
<button class="btn soft" onclick={() => llamaEngine.reject(llamaRun!.id)}>Reject</button>
{#if llamaLast && llamaLast.state === 'reviewing'}
<button class="btn" onclick={() => llamaAgent.authorize(llamaLast!.id)}>Authorize</button>
<button class="btn soft" onclick={() => llamaAgent.reject(llamaLast!.id)}>Reject</button>
{/if}
{#if llamaRun && llamaRun.state === 'escalated'}
<button class="btn" onclick={() => llamaEngine.resolveEscalation(llamaRun!.id, 'continue')}>Approve</button>
<button class="btn soft" onclick={() => llamaEngine.resolveEscalation(llamaRun!.id, 'cancel')}>Deny</button>
{/if}
{#if llamaActive}
<button class="btn soft" onclick={() => llamaEngine.stop(llamaRun!.id)}>Stop (return)</button>
{#if llamaLast && llamaLast.state === 'escalated'}
<button class="btn" onclick={() => llamaAgent.resolveEscalation(llamaLast!.id, 'continue')}>Approve</button>
<button class="btn soft" onclick={() => llamaAgent.resolveEscalation(llamaLast!.id, 'cancel')}>Deny</button>
{/if}
</div>
<!-- The presence surface: Aura observes the session (activeRun) and
answers «is it thinking?» — sustain-processing on the orb while
the model works, escalated/reviewing tints, announcements in the
live region, and Cancel as the delegation exit. -->
<div class="aura-slot" data-theme={shellTheme} data-mode={shellTheme}>
<Aura agent={llamaAgent} size="md">
<Aura.Indicator />
<Aura.Status />
<Aura.Region />
<Aura.Cancel>Stop</Aura.Cancel>
</Aura>
</div>
<div class="row status">
<span class="k">state</span><code>{llamaRun?.state ?? 'idle'}</code>
<span class="k">acts</span><code>{llamaRun?.acts ?? 0}</code>
<span class="k">turns</span><code>{llamaRun?.turns ?? 0}</code>
<span class="k">outcome</span><code>{llamaRun?.outcome ?? '—'}</code>
<span class="k">reason</span><code>{llamaRun?.reason ?? '—'}</code>
<span class="k">state</span><code>{llamaLast?.state ?? 'idle'}</code>
<span class="k">acts</span><code>{llamaLast?.acts ?? 0}</code>
<span class="k">turns</span><code>{llamaLast?.turns ?? 0}</code>
<span class="k">outcome</span><code>{llamaLast?.outcome ?? '—'}</code>
<span class="k">reason</span><code>{llamaLast?.reason ?? '—'}</code>
</div>
{#if llamaRun && llamaRun.state === 'planned' && llamaRun.plan}
{#if llamaLast && llamaLast.state === 'planned' && llamaLast.plan}
<div class="row">
<span class="k">proposed plan</span>
{#each llamaRun.plan as call}<span class="chip act">{call.name}</span>{/each}
{#each llamaLast.plan as call}<span class="chip act">{call.name}</span>{/each}
</div>
{/if}
@ -569,7 +604,7 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
{/if}
<pre class="answer">{llamaAnswer || '—'}</pre>
{#if llamaError}<p class="err">{llamaError}</p>{/if}
{#if llamaRun && llamaRun.reason === 'transport-error'}
{#if llamaLast && llamaLast.reason === 'transport-error'}
<p class="err">
Transport error — is the model server up at <code>{llamaBaseUrl}</code>? The bus log
above still shows the closed run: failures return control, they never vanish.
@ -770,4 +805,10 @@ App.orca.onEvent(SESSION_EVENT_EXPIRING, {
color: color-mix(in oklab, red 70%, currentColor);
font-size: 0.85em;
}
.aura-slot {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 0.25rem;
}
</style>

Loading…
Cancel
Save

Powered by TurnKey Linux.