babavoss 0.12.6 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/babavoss-web.js +4 -1
- package/gui/{chunk-9gmmm54v.js → chunk-qgzmajan.js} +619 -574
- package/gui/gui.js +1538 -277
- package/gui/theme.css +2007 -1128
- package/index.ts +1 -1
- package/package.json +5 -5
- package/src/{promptware → agents}/compile.ts +23 -31
- package/src/{promptware → agents}/define.ts +2 -2
- package/src/{promptware → agents}/disk.ts +7 -4
- package/src/{promptware → agents}/sync.ts +2 -2
- package/src/{promptware → agents}/system.ts +17 -17
- package/src/baba/check.ts +3 -3
- package/src/baba/config.ts +5 -2
- package/src/baba/init.ts +13 -18
- package/src/baba/node.ts +2 -2
- package/src/baba/project.ts +2 -2
- package/src/baba/requirements.ts +39 -0
- package/src/baba/setup.ts +30 -0
- package/src/baba/worker.ts +13 -5
- package/src/build/mdx.ts +4 -4
- package/src/build/project.ts +6 -1
- package/src/build/views.ts +3 -3
- package/src/desktop/desktop.css +35 -39
- package/src/desktop/index.ts +9 -7
- package/src/desktop/keys.ts +153 -0
- package/src/desktop/view.tsx +174 -130
- package/src/door/core.ts +2 -2
- package/src/ecs/baba.ts +27 -17
- package/src/generated/build.ts +1 -1
- package/src/gui/gui.tsx +21 -31
- package/src/gui/index.ts +6 -2
- package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
- package/src/gui/levels.tsx +241 -59
- package/src/gui/lockup.ts +11 -0
- package/src/gui/settings.tsx +241 -0
- package/src/gui/setup.tsx +77 -0
- package/src/gui/theme.css +205 -58
- package/src/gui/theme.ts +9 -5
- package/src/gui/voss-settings.ts +48 -0
- package/src/gui/wizard.tsx +17 -40
- package/src/guide/add-a-desktop.mdx +7 -7
- package/src/guide/index.ts +4 -4
- package/src/guide/write-a-system.mdx +3 -3
- package/src/guide/{write-promptware.mdx → write-agentic-instructions.mdx} +17 -24
- package/src/http/server.ts +140 -17
- package/src/{prompt → instructions}/evals.ts +1 -1
- package/src/{prompt → instructions}/index.ts +9 -8
- package/src/{prompt → instructions}/jsx-runtime.ts +3 -3
- package/src/mcp/main.ts +3 -3
- package/src/mcp/tools.ts +4 -4
- package/src/runtime/harness.ts +2 -2
- package/src/server/edge.ts +31 -3
- package/src/server/main.ts +2 -1
- package/src/server/messages.ts +47 -7
- package/src/shell/run.ts +18 -18
- package/src/spec/index.ts +1 -1
- package/src/web/core.tsx +2 -2
- package/src/web/index.tsx +1 -1
- package/src/desktop/bob.ts +0 -76
- /package/src/{promptware → agents}/markdown.d.ts +0 -0
- /package/src/{prompt → instructions}/jsx-dev-runtime.ts +0 -0
- /package/src/{prompt → instructions}/mdx.d.ts +0 -0
package/src/ecs/baba.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { Context } from "../kernel/context.ts";
|
|
|
9
9
|
import { keyFields, keyField } from "./handles.ts";
|
|
10
10
|
import { State, type Component, type Ask, type Resource, type Effect, type Model, type Entity, type Args, type Result, type MirrorEvent } from "./state.ts";
|
|
11
11
|
import { checkPositional } from "../shell/positional.ts";
|
|
12
|
-
import { render, type InterpretationSuite, type Prompt } from "../
|
|
12
|
+
import { render, type InterpretationSuite, type Prompt } from "../instructions/index.ts";
|
|
13
13
|
import { routesError } from "../desktop/routes.ts";
|
|
14
14
|
|
|
15
15
|
// ---- shapes --------------------------------------------------------------
|
|
@@ -105,7 +105,7 @@ export function query<const S extends Shape = {}, R = unknown, X = any>(def: { s
|
|
|
105
105
|
/** A skill as the manifest carries it, rendered: what an agent reads when it opens it, and the calls it uses, by name. */
|
|
106
106
|
export interface SkillDef { description: string; body: string; uses?: string[]; eval?: InterpretationSuite }
|
|
107
107
|
/** What the baba tells agents, as the manifest carries it: its section of the context file, its skills, its hooks; the Markdown rendered. */
|
|
108
|
-
export interface
|
|
108
|
+
export interface Instructions { context: string | null; contextEval?: InterpretationSuite; skills: Record<string, SkillDef>; hooks: HookDef[] }
|
|
109
109
|
|
|
110
110
|
/** Which harnesses get the baba's files, and Claude Code's permissions; `table` renders the contract as a table of calls; `auto` writes the files on every change. */
|
|
111
111
|
export interface Harness { claude: { enabled: boolean; permissions: string[] }; codex: { enabled: boolean }; table: boolean; auto: boolean }
|
|
@@ -197,7 +197,7 @@ export const nameOf = (v: unknown): string => dataOf(v).name;
|
|
|
197
197
|
const NAME = /^[a-z][a-zA-Z0-9-]*$/;
|
|
198
198
|
const SYSTEM_NAME = /^[a-z][a-z0-9-]*$/;
|
|
199
199
|
/** Words the shell keeps for itself: an action or a query may not take them. */
|
|
200
|
-
export const RESERVED = new Set(["start", "stop", "status", "state", "follow", "run", "raw", "secret", "help", "mcp", "check", "install", "add-system", "serve", "
|
|
200
|
+
export const RESERVED = new Set(["start", "stop", "status", "state", "follow", "run", "raw", "secret", "help", "mcp", "check", "install", "add-system", "serve", "agents", "init", "babas", "service", "gui"]);
|
|
201
201
|
|
|
202
202
|
/** What a declaration makes: its handles by name, on an object with no prototype, and the model they form. */
|
|
203
203
|
export interface Declared { readonly name: string; readonly refs: Readonly<Record<string, Component | Resource | Effect>>; readonly model: Model }
|
|
@@ -313,8 +313,8 @@ export interface Baba<Ss extends readonly System[] = readonly System[], K extend
|
|
|
313
313
|
readonly $app: K;
|
|
314
314
|
/** The routes of each app, as a type: what `compose<typeof baba>` asks a table of components for. */
|
|
315
315
|
readonly $routes: R;
|
|
316
|
-
/** What the baba tells agents: `context(...)`, `skill(...)` and `hook(...)` from babavoss/
|
|
317
|
-
readonly
|
|
316
|
+
/** What the baba tells agents: `context(...)`, `skill(...)` and `hook(...)` from babavoss/instructions, in order. */
|
|
317
|
+
readonly instructions: readonly Prompt[];
|
|
318
318
|
/** Which harnesses get the files, and how. */
|
|
319
319
|
readonly harness: Harness;
|
|
320
320
|
/** Milliseconds between rounds the runtime causes on its own, for steps that need time to pass; null for a baba moved by actions and jobs. */
|
|
@@ -324,7 +324,8 @@ export interface Baba<Ss extends readonly System[] = readonly System[], K extend
|
|
|
324
324
|
readonly queries: Record<string, QueryDef & { owner: string }>;
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
export function baba<const Ss extends readonly System[], const Sc extends readonly AppSpec[] = readonly []>(def: { systems: Ss; apps?: Sc; tick?: number;
|
|
327
|
+
export function baba<const Ss extends readonly System[], const Sc extends readonly AppSpec[] = readonly []>(def: { systems: Ss; apps?: Sc; tick?: number; instructions?: readonly Prompt[]; harness?: HarnessInput }): Baba<Ss, Sc[number]["key"], RoutesOf<Sc>> {
|
|
328
|
+
if ("promptware" in def) throw new Error("baba({ promptware }) is gone: the agentic instructions are the .mdx files under .baba/agents/, which voss reads on its own; see the write-agentic-instructions skill");
|
|
328
329
|
const harness = harnessOf(def.harness);
|
|
329
330
|
if (def.tick !== undefined && !(Number.isInteger(def.tick) && def.tick > 0)) throw new Error("baba: tick is whole milliseconds, above zero");
|
|
330
331
|
const model: Model = { components: [], resources: [], effects: [] };
|
|
@@ -367,7 +368,7 @@ export function baba<const Ss extends readonly System[], const Sc extends readon
|
|
|
367
368
|
if (new Set(apps.map((v) => v.key)).size !== apps.length || apps.some((v) => !/^[a-z][a-z0-9-]*$/.test(v.key))) throw new Error("baba: app keys must be unique lowercase names");
|
|
368
369
|
for (const v of apps) for (const n of v.systems ?? []) if (!systemNames.has(n)) throw new Error(`app ${v.key} shows ${n}, which is not one of the baba's systems`);
|
|
369
370
|
for (const v of apps) { const e = v.routes && routesError(v.routes); if (e) throw new Error(`app ${v.key}: ${e}`); }
|
|
370
|
-
return Object.freeze({ kind: "baba", systems: def.systems, apps: apps.map((v) => ({ ...v, ...(v.systems ? { systems: [...v.systems] } : {}), ...(v.routes ? { routes: [...v.routes] } : {}) })), $app: undefined as never, $routes: undefined as never, tick: def.tick ?? null,
|
|
371
|
+
return Object.freeze({ kind: "baba", systems: def.systems, apps: apps.map((v) => ({ ...v, ...(v.systems ? { systems: [...v.systems] } : {}), ...(v.routes ? { routes: [...v.routes] } : {}) })), $app: undefined as never, $routes: undefined as never, tick: def.tick ?? null, instructions: [...(def.instructions ?? [])], harness, model, actions, queries });
|
|
371
372
|
}
|
|
372
373
|
|
|
373
374
|
export function isBaba(v: unknown): v is Baba {
|
|
@@ -387,7 +388,7 @@ export function ofSystem(c: System): Baba { return baba({ systems: closureOf([c]
|
|
|
387
388
|
export interface Manifest {
|
|
388
389
|
systems: { name: string }[];
|
|
389
390
|
/** What the baba tells agents, rendered: its context, its skills, its hooks; voss's own skills after the baba's. */
|
|
390
|
-
|
|
391
|
+
instructions: Instructions;
|
|
391
392
|
harness: Harness;
|
|
392
393
|
components: Record<string, { owner: string; schema: Json; accept?: true }>;
|
|
393
394
|
resources: Record<string, { owner: string; schema: Json }>;
|
|
@@ -403,29 +404,38 @@ export interface Manifest {
|
|
|
403
404
|
export interface Call { owner: string; summary: string; args: Json; positional?: string[]; result: Json }
|
|
404
405
|
|
|
405
406
|
export function manifestOf(v: Baba, program = "voss baba"): Manifest {
|
|
406
|
-
const m: Manifest = { systems: v.systems.map((c) => ({ name: nameOf(c) })),
|
|
407
|
+
const m: Manifest = { systems: v.systems.map((c) => ({ name: nameOf(c) })), instructions: { context: null, skills: {}, hooks: [] }, harness: v.harness, components: {}, resources: {}, effects: {}, actions: {}, queries: {}, tick: v.tick, apps: v.apps.map((x) => ({ ...x })) };
|
|
407
408
|
for (const c of v.model.components) m.components[c.name] = { owner: c.owner, schema: c.schema.json, ...(c.accept ? { accept: true as const } : {}) };
|
|
408
409
|
for (const r of v.model.resources) m.resources[r.name] = { owner: r.owner, schema: r.schema.json };
|
|
409
410
|
for (const e of v.model.effects) m.effects[e.name] = { owner: e.owner, summary: e.summary, args: e.args.json, result: e.result.json, retry: e.retry, every: e.every, repeat: e.repeat, shape: e.shape, ...(e.mirror ? { mirror: e.mirror } : {}), ...(e.event ? { event: e.event.json } : {}) };
|
|
410
411
|
const call = (d: (ActionDef | QueryDef) & { owner: string }): Call => ({ owner: d.owner, summary: d.summary, args: d.args.json, ...(d.positional ? { positional: d.positional } : {}), result: d.result.json });
|
|
411
412
|
for (const [n, a] of Object.entries(v.actions)) m.actions[n] = call(a);
|
|
412
413
|
for (const [n, q] of Object.entries(v.queries)) m.queries[n] = call(q);
|
|
413
|
-
// The
|
|
414
|
+
// The agentic instructions renders once the contract is in the manifest, since it may refer to it.
|
|
414
415
|
const refs: string[] = [];
|
|
415
|
-
m.
|
|
416
|
-
for (const r of refs) if (!(r in m.
|
|
417
|
-
for (const [n, sk] of Object.entries(m.
|
|
418
|
-
for (const h of m.
|
|
416
|
+
m.instructions = renderInstructions(v.instructions, { manifest: m, program, refs });
|
|
417
|
+
for (const r of refs) if (!(r in m.instructions.skills)) throw new Error(`a skill says "see ${r}", which is not a skill of the baba`);
|
|
418
|
+
for (const [n, sk] of Object.entries(m.instructions.skills)) for (const u of sk.uses ?? []) if (!(u in m.actions) && !(u in m.queries)) throw new Error(`skill ${n} uses ${u}, which is not an action or query of the baba`);
|
|
419
|
+
for (const h of m.instructions.hooks) if (!(h.action in m.actions)) throw new Error(`hook on ${h.on} calls ${h.action}, which is not an action of the baba`);
|
|
419
420
|
return m;
|
|
420
421
|
}
|
|
421
422
|
|
|
422
|
-
/** The baba
|
|
423
|
-
export function
|
|
423
|
+
/** The baba with the documents voss found under .baba/agents/, after any it declares itself: what the builder hands the runtime. */
|
|
424
|
+
export function instructed(v: Baba, docs: readonly Prompt[]): Baba {
|
|
425
|
+
if (!docs.length) return v;
|
|
426
|
+
return Object.freeze({ ...v, instructions: [...v.instructions, ...docs] }) as Baba;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** The baba's agentic instructions as Markdown: the one context, each skill by name, the hooks. */
|
|
430
|
+
export function renderInstructions(ps: readonly Prompt[], context: { manifest: Manifest; program: string; refs?: string[] }): Instructions {
|
|
424
431
|
const contexts = ps.filter((p) => p.kind === "context");
|
|
425
432
|
if (contexts.length > 1) throw new Error("a baba has one context(...); put every paragraph in it");
|
|
426
433
|
const text = contexts[0] ? render(contexts[0].body, context) : "";
|
|
427
434
|
const skills: Record<string, SkillDef> = {};
|
|
428
435
|
for (const p of ps) if (p.kind === "skill") { if (p.name in skills) throw new Error(`two skills are named ${p.name}`); skills[p.name] = { description: p.description, body: render(p.body, context), uses: p.uses, ...(p.eval ? { eval: p.eval } : {}) }; }
|
|
429
|
-
const hooks =
|
|
436
|
+
const hooks = [
|
|
437
|
+
...(contexts[0]?.hooks ?? []).map((h) => ({ on: h.on, match: h.match ?? null, action: h.action, strict: h.strict === true })),
|
|
438
|
+
...ps.filter((p) => p.kind === "hook").map((p) => ({ on: p.on, match: p.match, action: p.action, strict: p.strict })),
|
|
439
|
+
];
|
|
430
440
|
return { context: text || null, skills, hooks, ...(contexts[0]?.eval ? { contextEval: contexts[0].eval } : {}) };
|
|
431
441
|
}
|
package/src/generated/build.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// The commit and the moment a package was built from, stamped by the pack
|
|
2
2
|
// of a release (tools/release/pack.ts) and restored after it. From a
|
|
3
3
|
// checkout both are empty: the tree is the build.
|
|
4
|
-
export const build = {"sha":"
|
|
4
|
+
export const build = {"sha":"c288c8f","at":"2026-10-08T22:12:53.708Z"};
|
package/src/gui/gui.tsx
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
// voss's GUI
|
|
2
|
-
//
|
|
1
|
+
// voss's GUI. At the root, the picker: the babas voss knows, a folder added
|
|
2
|
+
// by its path, and the setup wizard of one that lacks what voss requires. At
|
|
3
|
+
// /NAME/ voss's shell over the baba,
|
|
3
4
|
// its apps and voss's own, the Maker, the state, the contract, the
|
|
4
|
-
//
|
|
5
|
+
// agentic instructions and the settings, as windows. One bundle, one React, served by the
|
|
5
6
|
// kernel's server child.
|
|
6
7
|
import "./theme.css";
|
|
7
8
|
import { Component, StrictMode, useEffect, useMemo, useState, type ComponentType, type ReactNode } from "react";
|
|
8
9
|
import { createRoot } from "react-dom/client";
|
|
9
|
-
import { ScopeProvider, apiHeaders, formatFailure, Maker, Desktop,
|
|
10
|
+
import { ScopeProvider, apiHeaders, formatFailure, Maker, Desktop, DesktopSkeleton, type AppProps, type ViewFailure } from "babavoss/web";
|
|
10
11
|
import { isApps, type Interface } from "../web/core.tsx";
|
|
11
|
-
import { desktop
|
|
12
|
+
import { desktop } from "../desktop/index.ts";
|
|
12
13
|
import type { AppSpec } from "../ecs/baba.ts";
|
|
13
14
|
import type { Described } from "../baba/node.ts";
|
|
14
15
|
import { Inspector } from "./inspector.tsx";
|
|
15
16
|
import { Runner } from "./runner.tsx";
|
|
16
|
-
import {
|
|
17
|
+
import { Instructions } from "./instructions.tsx";
|
|
17
18
|
import { Button } from "../web/ui.tsx";
|
|
18
19
|
import { Root } from "./levels.tsx";
|
|
20
|
+
import { Settings, SettingsBaba } from "./settings.tsx";
|
|
21
|
+
import { useVossSettings } from "./voss-settings.ts";
|
|
19
22
|
import type { Voss } from "../server/messages.ts";
|
|
20
23
|
import { tellApart } from "../kernel/names.ts";
|
|
21
24
|
|
|
@@ -117,7 +120,8 @@ function Host() {
|
|
|
117
120
|
const [, moveOn] = useState(0);
|
|
118
121
|
useEffect(() => { if (to) { history.replaceState(null, "", `/${encodeURIComponent(scope)}/${to}${location.search}${location.hash}`); moveOn((n) => n + 1); } }, [to]);
|
|
119
122
|
if (to) return null;
|
|
120
|
-
|
|
123
|
+
// While the baba is described the desktop's own skeleton shows, the look it will have, never a line of text.
|
|
124
|
+
if (!d) return failed ? <div className="gui-page"><p className="bv-muted">{scope} did not answer: {failed}</p></div> : <div className="gui-view"><DesktopSkeleton /></div>;
|
|
121
125
|
if (!d.manifest) return <div className="gui-page"><p className="bv-error">{d.title} is not running.{d.error ? ` ${d.error}` : ""}</p></div>;
|
|
122
126
|
return <ShellPage d={d} />;
|
|
123
127
|
}
|
|
@@ -180,15 +184,17 @@ const useShellSystem = (apps: readonly AppSpec[] | undefined) => {
|
|
|
180
184
|
function ShellPage({ d }: { d: Described }) {
|
|
181
185
|
const { view, failure } = useInterface(d);
|
|
182
186
|
const system = useShellSystem(d.manifest?.apps);
|
|
187
|
+
const person = useVossSettings();
|
|
183
188
|
const manifest = d.manifest!;
|
|
184
189
|
const components = useMemo(() => {
|
|
185
|
-
|
|
190
|
+
// Until the interface has loaded, a moment, an app's window is its empty sheet: no line of text that flashes and goes.
|
|
191
|
+
const pending = () => failure ? <Failure f={failure} /> : null;
|
|
186
192
|
const out: Record<string, ComponentType<AppProps>> = {
|
|
187
193
|
// The Maker with no specs: the interface gives voss's maker app its own component, with the baba's specs and its apps, to replace this.
|
|
188
194
|
maker: ({ path, navigate, setTitle }) => <Maker specs={[]} title={d.title} path={path} navigate={navigate} setTitle={setTitle} />,
|
|
189
195
|
state: () => <section className="gui-app">{d.error && <p className="bv-error">The code did not load; the last generation serves: {d.error}</p>}<Inspector manifest={manifest} /></section>,
|
|
190
196
|
contract: () => <section className="gui-app"><Runner manifest={manifest} /></section>,
|
|
191
|
-
|
|
197
|
+
instructions: () => <section className="gui-app"><Instructions manifest={manifest} /></section>,
|
|
192
198
|
settings: Settings,
|
|
193
199
|
};
|
|
194
200
|
for (const sc of manifest.apps ?? []) {
|
|
@@ -201,34 +207,18 @@ function ShellPage({ d }: { d: Described }) {
|
|
|
201
207
|
return out;
|
|
202
208
|
}, [view, failure, system, manifest, d.error, d.view, d.title]);
|
|
203
209
|
return (
|
|
204
|
-
<div className="gui-view"
|
|
205
|
-
|
|
206
|
-
|
|
210
|
+
<div className="gui-view">
|
|
211
|
+
{/* The interface arriving swaps the apps' components in place: the desktop stays mounted, so its windows, launcher and focus stay as they are. */}
|
|
212
|
+
<ViewBoundary reset={view} fallback={(f) => <div className="gui-page"><Failure f={f} /></div>}>
|
|
213
|
+
<SettingsBaba.Provider value={d}><Desktop system={system} apps={components} title={d.title} root={{ label: "voss", href: "/" }} settings={person} /></SettingsBaba.Provider>
|
|
207
214
|
</ViewBoundary>
|
|
208
215
|
</div>
|
|
209
216
|
);
|
|
210
217
|
}
|
|
211
218
|
|
|
212
|
-
/**
|
|
213
|
-
function Settings() {
|
|
214
|
-
const system = useMemo(() => desktop({}), []);
|
|
215
|
-
const current = useResource(system.desktopSettings)?.theme ?? "system";
|
|
216
|
-
const { fire } = useBaba<DesktopSystem>();
|
|
217
|
-
return (
|
|
218
|
-
<section className="gui-app">
|
|
219
|
-
<h2>Theme</h2>
|
|
220
|
-
<p className="bv-muted">The theme every page of this baba shows, kept in its state.</p>
|
|
221
|
-
<div className="gui-choices" role="radiogroup" aria-label="Theme">
|
|
222
|
-
{themes.map((t) => <Button key={t} variant={t === current ? "default" : "quiet"} role="radio" aria-checked={t === current} onClick={() => void fire("desktop-settings", { theme: t })}>{t}</Button>)}
|
|
223
|
-
</div>
|
|
224
|
-
<h2>voss</h2>
|
|
225
|
-
<p><a href="/">All babas</a></p>
|
|
226
|
-
</section>
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
class ViewBoundary extends Component<{ children: ReactNode; fallback: (f: ViewFailure) => ReactNode }, { failure: ViewFailure | null }> {
|
|
219
|
+
class ViewBoundary extends Component<{ children: ReactNode; fallback: (f: ViewFailure) => ReactNode; /** A failure is forgotten when this changes: a new interface gets its chance. */ reset?: unknown }, { failure: ViewFailure | null }> {
|
|
231
220
|
override state = { failure: null as ViewFailure | null };
|
|
221
|
+
override componentDidUpdate(before: { reset?: unknown }) { if (before.reset !== this.props.reset && this.state.failure) this.setState({ failure: null }); }
|
|
232
222
|
static getDerivedStateFromError(err: unknown) {
|
|
233
223
|
return { failure: { stage: "render" as const, message: err instanceof Error ? err.message : String(err), detail: err instanceof Error ? err.stack : undefined } };
|
|
234
224
|
}
|
package/src/gui/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// runner over any baba.
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { rm, rename } from "node:fs/promises";
|
|
6
|
-
import { early } from "./theme.ts";
|
|
6
|
+
import { early, firstPaint } from "./theme.ts";
|
|
7
7
|
import { shared, names, resolver } from "../build/views.ts";
|
|
8
8
|
|
|
9
9
|
const here = dirname(Bun.fileURLToPath(import.meta.url));
|
|
@@ -37,11 +37,15 @@ export async function buildGui(outdir: string): Promise<void> {
|
|
|
37
37
|
/** The import map, as the page carries it: the shared names to the modules built with the GUI. */
|
|
38
38
|
export const importMap = () => JSON.stringify({ imports: Object.fromEntries(Object.entries(shared).map(([n, f]) => [n, `/assets/${f}`])) });
|
|
39
39
|
|
|
40
|
+
/** The tab's icon: the Baba Voss favicon as the brand workshop makes it (marc/logo/favicon.ts, site/public/favicon.svg), the small symbol knocked out of a tan disc; inlined, since the package carries no brand files. */
|
|
41
|
+
const favicon = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="50" fill="#AE8D74"/><g fill="#151312"><circle cx="33.772" cy="50" r="18"/><circle cx="71.5" cy="50" r="12.728"/></g></svg>')}`;
|
|
42
|
+
|
|
40
43
|
/** The page of a scope: the theme before the first paint, the import map, the GUI. */
|
|
41
44
|
export function page(scope: string | null): string {
|
|
42
45
|
const meta = scope ? `<meta name="voss-scope" content="${escape(scope)}">` : "";
|
|
43
46
|
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
44
|
-
<
|
|
47
|
+
<meta name="color-scheme" content="light dark"><style>${firstPaint}</style>
|
|
48
|
+
<title>${scope ? escape(scope) : "voss"}</title><link rel="icon" type="image/svg+xml" href="${favicon}">${meta}<script>${early}</script><link rel="stylesheet" href="/assets/theme.css">
|
|
45
49
|
<script type="importmap">${importMap()}</script></head>
|
|
46
50
|
<body><div id="root"></div><script type="module" src="/assets/gui.js"></script></body></html>`;
|
|
47
51
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// The
|
|
1
|
+
// The agentic instructions drawer: the baba's know-how for agents as it declares it,
|
|
2
2
|
// the harness as the baba declares it, the plan of each output, and a sync.
|
|
3
|
-
// Read from the state by the
|
|
3
|
+
// Read from the state by the agents system's names.
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
5
|
import type { Manifest } from "../ecs/baba.ts";
|
|
6
6
|
import { useBabaState } from "../web/state.tsx";
|
|
@@ -9,12 +9,12 @@ import { ErrorText } from "./runner.tsx";
|
|
|
9
9
|
|
|
10
10
|
interface Plan { auto: boolean; lastSync: number | null; pending: number; error: string | null; outputs: { path: string; harness: string; status: string; action: string | null; reason: string }[] }
|
|
11
11
|
|
|
12
|
-
export function
|
|
12
|
+
export function Instructions({ manifest }: { manifest: Manifest }) {
|
|
13
13
|
const { fire, read, ready, round } = useBabaState();
|
|
14
14
|
const [plan, setPlan] = useState<Plan | null>(null);
|
|
15
15
|
const [error, setError] = useState<unknown>(null);
|
|
16
|
-
useEffect(() => { if (ready) read("
|
|
17
|
-
if (!("pwStatus" in manifest.resources)) return <p className="bv-muted">This baba has no
|
|
16
|
+
useEffect(() => { if (ready) read("agents-plan").then((p) => setPlan(p as Plan), setError); }, [ready, round]);
|
|
17
|
+
if (!("pwStatus" in manifest.resources)) return <p className="bv-muted">This baba has no agents system.</p>;
|
|
18
18
|
if (!ready) return <p className="bv-muted">Reading the state…</p>;
|
|
19
19
|
const act = (name: string, args: unknown) => { setError(null); return fire(name, args as never).catch((e: unknown) => { setError(e); throw e; }); };
|
|
20
20
|
return (
|
|
@@ -24,7 +24,7 @@ export function Promptware({ manifest }: { manifest: Manifest }) {
|
|
|
24
24
|
<section className="gui-section">
|
|
25
25
|
<h2>Outputs</h2>
|
|
26
26
|
<div className="bv-form-actions">
|
|
27
|
-
<Button variant="primary" onClick={() => void act("
|
|
27
|
+
<Button variant="primary" onClick={() => void act("agents-sync", {}).catch(() => {})}>Sync now</Button>
|
|
28
28
|
{plan && <span className="bv-muted">{plan.pending ? `${plan.pending} to write` : "up to date"} · {plan.auto ? "writes on every change" : "writes on sync"}{plan.lastSync ? ` · synced ${new Date(plan.lastSync).toLocaleTimeString()}` : ""}</span>}
|
|
29
29
|
</div>
|
|
30
30
|
{plan?.error && <p className="bv-error">{plan.error}</p>}
|
|
@@ -35,18 +35,18 @@ export function Promptware({ manifest }: { manifest: Manifest }) {
|
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/** What the baba tells agents, read-only here: it is code under .baba/
|
|
38
|
+
/** What the baba tells agents, read-only here: it is code under .baba/agents/. */
|
|
39
39
|
function Declared({ manifest }: { manifest: Manifest }) {
|
|
40
|
-
const pw = manifest.
|
|
40
|
+
const pw = manifest.instructions;
|
|
41
41
|
const skills = Object.entries(pw.skills);
|
|
42
42
|
return (
|
|
43
43
|
<section className="gui-section">
|
|
44
|
-
<h2>
|
|
45
|
-
{!pw.context && skills.length === 0 && pw.hooks.length === 0 && <p className="bv-muted">Nothing declared yet: give the baba a `context(...)`, a `skill(...)` or a `hook(...)` under
|
|
44
|
+
<h2>Instructions</h2>
|
|
45
|
+
{!pw.context && skills.length === 0 && pw.hooks.length === 0 && <p className="bv-muted">Nothing declared yet: give the baba a `context(...)`, a `skill(...)` or a `hook(...)` under `.baba/agents/`.</p>}
|
|
46
46
|
{pw.context && <p className="bv-muted">{pw.context.length} characters of context, {pw.hooks.length} hook{pw.hooks.length === 1 ? "" : "s"}.</p>}
|
|
47
47
|
{pw.hooks.length > 0 && <Rows>{pw.hooks.map((h, i) => <Row key={i} hint={`${h.on}${h.match ? ` on ${h.match}` : ""}${h.strict ? " · strict" : ""}`}><code>{h.action}</code></Row>)}</Rows>}
|
|
48
48
|
{skills.length > 0 && <Rows>{skills.map(([k, sk]) => <Row key={k} hint={sk.description}><code>{k}</code></Row>)}</Rows>}
|
|
49
|
-
<p className="bv-muted">The
|
|
49
|
+
<p className="bv-muted">The agentic instructions is the baba's code: edit it under .baba/agents/; the files below follow.</p>
|
|
50
50
|
</section>
|
|
51
51
|
);
|
|
52
52
|
}
|
|
@@ -62,7 +62,7 @@ function Harness({ manifest }: { manifest: Manifest }) {
|
|
|
62
62
|
<Row hint={h.codex.enabled ? "on" : "off"}><code>codex</code></Row>
|
|
63
63
|
<Row hint={`${h.auto ? "writes on every change" : "writes on sync"}${h.table ? " · the contract as a table" : ""}`}><code>files</code></Row>
|
|
64
64
|
</Rows>
|
|
65
|
-
<p className="bv-muted">
|
|
65
|
+
<p className="bv-muted">The .mdx files under .baba/agents/, each <code>{'export const prompt = { kind: "context" | "skill", … }'}</code>; voss reads them on its own.</p>
|
|
66
66
|
</section>
|
|
67
67
|
);
|
|
68
68
|
}
|