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.
Files changed (62) hide show
  1. package/gui/babavoss-web.js +4 -1
  2. package/gui/{chunk-9gmmm54v.js → chunk-qgzmajan.js} +619 -574
  3. package/gui/gui.js +1538 -277
  4. package/gui/theme.css +2007 -1128
  5. package/index.ts +1 -1
  6. package/package.json +5 -5
  7. package/src/{promptware → agents}/compile.ts +23 -31
  8. package/src/{promptware → agents}/define.ts +2 -2
  9. package/src/{promptware → agents}/disk.ts +7 -4
  10. package/src/{promptware → agents}/sync.ts +2 -2
  11. package/src/{promptware → agents}/system.ts +17 -17
  12. package/src/baba/check.ts +3 -3
  13. package/src/baba/config.ts +5 -2
  14. package/src/baba/init.ts +13 -18
  15. package/src/baba/node.ts +2 -2
  16. package/src/baba/project.ts +2 -2
  17. package/src/baba/requirements.ts +39 -0
  18. package/src/baba/setup.ts +30 -0
  19. package/src/baba/worker.ts +13 -5
  20. package/src/build/mdx.ts +4 -4
  21. package/src/build/project.ts +6 -1
  22. package/src/build/views.ts +3 -3
  23. package/src/desktop/desktop.css +35 -39
  24. package/src/desktop/index.ts +9 -7
  25. package/src/desktop/keys.ts +153 -0
  26. package/src/desktop/view.tsx +174 -130
  27. package/src/door/core.ts +2 -2
  28. package/src/ecs/baba.ts +27 -17
  29. package/src/generated/build.ts +1 -1
  30. package/src/gui/gui.tsx +21 -31
  31. package/src/gui/index.ts +6 -2
  32. package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
  33. package/src/gui/levels.tsx +241 -59
  34. package/src/gui/lockup.ts +11 -0
  35. package/src/gui/settings.tsx +241 -0
  36. package/src/gui/setup.tsx +77 -0
  37. package/src/gui/theme.css +205 -58
  38. package/src/gui/theme.ts +9 -5
  39. package/src/gui/voss-settings.ts +48 -0
  40. package/src/gui/wizard.tsx +17 -40
  41. package/src/guide/add-a-desktop.mdx +7 -7
  42. package/src/guide/index.ts +4 -4
  43. package/src/guide/write-a-system.mdx +3 -3
  44. package/src/guide/{write-promptware.mdx → write-agentic-instructions.mdx} +17 -24
  45. package/src/http/server.ts +140 -17
  46. package/src/{prompt → instructions}/evals.ts +1 -1
  47. package/src/{prompt → instructions}/index.ts +9 -8
  48. package/src/{prompt → instructions}/jsx-runtime.ts +3 -3
  49. package/src/mcp/main.ts +3 -3
  50. package/src/mcp/tools.ts +4 -4
  51. package/src/runtime/harness.ts +2 -2
  52. package/src/server/edge.ts +31 -3
  53. package/src/server/main.ts +2 -1
  54. package/src/server/messages.ts +47 -7
  55. package/src/shell/run.ts +18 -18
  56. package/src/spec/index.ts +1 -1
  57. package/src/web/core.tsx +2 -2
  58. package/src/web/index.tsx +1 -1
  59. package/src/desktop/bob.ts +0 -76
  60. /package/src/{promptware → agents}/markdown.d.ts +0 -0
  61. /package/src/{prompt → instructions}/jsx-dev-runtime.ts +0 -0
  62. /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 "../prompt/index.ts";
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 Promptware { context: string | null; contextEval?: InterpretationSuite; skills: Record<string, SkillDef>; hooks: HookDef[] }
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", "promptware", "init", "babas", "service", "gui"]);
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/prompt, in order. */
317
- readonly promptware: readonly Prompt[];
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; promptware?: readonly Prompt[]; harness?: HarnessInput }): Baba<Ss, Sc[number]["key"], RoutesOf<Sc>> {
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, promptware: [...(def.promptware ?? [])], harness, model, actions, queries });
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
- promptware: Promptware;
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) })), promptware: { context: null, skills: {}, hooks: [] }, harness: v.harness, components: {}, resources: {}, effects: {}, actions: {}, queries: {}, tick: v.tick, apps: v.apps.map((x) => ({ ...x })) };
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 promptware renders once the contract is in the manifest, since it may refer to it.
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.promptware = renderPromptware(v.promptware, { manifest: m, program, refs });
416
- for (const r of refs) if (!(r in m.promptware.skills)) throw new Error(`a skill says "see ${r}", which is not a skill of the baba`);
417
- for (const [n, sk] of Object.entries(m.promptware.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`);
418
- for (const h of m.promptware.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`);
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's promptware as Markdown: the one context, each skill by name, the hooks. */
423
- export function renderPromptware(ps: readonly Prompt[], context: { manifest: Manifest; program: string; refs?: string[] }): Promptware {
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 = ps.filter((p) => p.kind === "hook").map((p) => ({ on: p.on, match: p.match, action: p.action, strict: p.strict }));
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
  }
@@ -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":"fe82400","at":"2026-10-08T21:33:13.250Z"};
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: voss's shell at every level. At the root, over page state: the
2
- // babas and the New baba wizard. At /NAME/ over the baba,
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
- // promptware and the settings, as windows. One bundle, one React, served by the
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, useResource, useBaba, type AppProps, type ViewFailure } from "babavoss/web";
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, themes, type DesktopSystem } from "../desktop/index.ts";
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 { Promptware } from "./promptware.tsx";
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
- if (!d) return <div className="gui-page"><p className="bv-muted">{failed ? `${scope} did not answer: ${failed}` : "…"}</p></div>;
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
- const pending = () => failure ? <Failure f={failure} /> : <p className="desktop-empty bv-muted">…</p>;
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
- promptware: () => <section className="gui-app"><Promptware manifest={manifest} /></section>,
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" key={view ? "loaded" : "loading"}>
205
- <ViewBoundary fallback={(f) => <div className="gui-page"><Failure f={f} /></div>}>
206
- <Desktop system={system} apps={components} title={d.title} root={{ label: "voss", href: "/" }} />
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
- /** voss's settings for this baba: the theme its pages share, in its state. */
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
- <title>${scope ? escape(scope) : "voss"}</title>${meta}<script>${early}</script><link rel="stylesheet" href="/assets/theme.css">
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 promptware drawer: the baba's know-how for agents as it declares it,
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 promptware system's names.
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 Promptware({ manifest }: { manifest: Manifest }) {
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("promptware-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 promptware system.</p>;
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("promptware-sync", {}).catch(() => {})}>Sync now</Button>
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/promptware/. */
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.promptware;
40
+ const pw = manifest.instructions;
41
41
  const skills = Object.entries(pw.skills);
42
42
  return (
43
43
  <section className="gui-section">
44
- <h2>Promptware</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 `promptware`.</p>}
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 promptware is the baba's code: edit it under .baba/promptware/; the files below follow.</p>
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">Declared in .baba/index.ts: <code>{"baba({ systems, promptware, harness })"}</code>.</p>
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
  }