babavoss 0.12.5 → 0.13.0
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 +234 -0
- package/gui/chunk-5hpp1ypv.js +11710 -0
- package/gui/chunk-a20zfekq.js +8639 -0
- package/gui/chunk-k3cm8j6r.js +433 -0
- package/gui/chunk-pk09y98y.js +50 -0
- package/gui/chunk-smz02qa6.js +186 -0
- package/gui/chunk-wwqypxre.js +49 -0
- package/gui/gui.js +2015 -0
- package/gui/react-compiler-runtime.js +39 -0
- package/gui/react-dom-client.js +21 -0
- package/gui/react-dom.js +44 -0
- package/gui/react-jsx-runtime.js +19 -0
- package/gui/react.js +105 -0
- package/gui/theme.css +3053 -0
- package/index.ts +1 -1
- package/package.json +8 -6
- package/src/{promptware → agents}/compile.ts +12 -12
- 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/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/index.ts +1 -1
- 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 +3 -3
- package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
- package/src/gui/theme.css +1 -1
- package/src/guide/add-a-desktop.mdx +2 -2
- 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/{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/shell/run.ts +18 -18
- package/src/spec/index.ts +1 -1
- package/src/web/core.tsx +2 -2
- /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 = {
|
|
4
|
+
export const build = {"sha":"ff74ab0","at":"2026-10-08T22:02:34.371Z"};
|
package/src/gui/gui.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// voss's GUI: voss's shell at every level. At the root, over page state: the
|
|
2
2
|
// babas and the New baba wizard. At /NAME/ over the baba,
|
|
3
3
|
// its apps and voss's own, the Maker, the state, the contract, the
|
|
4
|
-
//
|
|
4
|
+
// agentic instructions and the settings, as windows. One bundle, one React, served by the
|
|
5
5
|
// kernel's server child.
|
|
6
6
|
import "./theme.css";
|
|
7
7
|
import { Component, StrictMode, useEffect, useMemo, useState, type ComponentType, type ReactNode } from "react";
|
|
@@ -13,7 +13,7 @@ import type { AppSpec } from "../ecs/baba.ts";
|
|
|
13
13
|
import type { Described } from "../baba/node.ts";
|
|
14
14
|
import { Inspector } from "./inspector.tsx";
|
|
15
15
|
import { Runner } from "./runner.tsx";
|
|
16
|
-
import {
|
|
16
|
+
import { Instructions } from "./instructions.tsx";
|
|
17
17
|
import { Button } from "../web/ui.tsx";
|
|
18
18
|
import { Root } from "./levels.tsx";
|
|
19
19
|
import type { Voss } from "../server/messages.ts";
|
|
@@ -188,7 +188,7 @@ function ShellPage({ d }: { d: Described }) {
|
|
|
188
188
|
maker: ({ path, navigate, setTitle }) => <Maker specs={[]} title={d.title} path={path} navigate={navigate} setTitle={setTitle} />,
|
|
189
189
|
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
190
|
contract: () => <section className="gui-app"><Runner manifest={manifest} /></section>,
|
|
191
|
-
|
|
191
|
+
instructions: () => <section className="gui-app"><Instructions manifest={manifest} /></section>,
|
|
192
192
|
settings: Settings,
|
|
193
193
|
};
|
|
194
194
|
for (const sc of manifest.apps ?? []) {
|
|
@@ -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
|
}
|
package/src/gui/theme.css
CHANGED
|
@@ -295,7 +295,7 @@ button.pk-crumb:hover { background: var(--accent); color: var(--foreground); }
|
|
|
295
295
|
@keyframes gui-appear { from { opacity: 0 } to { opacity: 1 } }
|
|
296
296
|
@media (prefers-reduced-motion: reduce) { .gui-fade { animation: none; } }
|
|
297
297
|
|
|
298
|
-
/* ---- voss's apps, in the shell's windows: the state, the contract, the
|
|
298
|
+
/* ---- voss's apps, in the shell's windows: the state, the contract, the agentic instructions, the settings, with a page's gutters; the Maker edge to edge ---- */
|
|
299
299
|
.gui-app { max-width: 1100px; padding: 16px 20px 32px; }
|
|
300
300
|
.gui-app > h2 { margin: 16px 0 4px; font-size: 13px; }
|
|
301
301
|
.gui-choices { display: flex; gap: 6px; margin-top: 8px; }
|
|
@@ -2,7 +2,7 @@ export const prompt = { kind: "skill", name: "add-a-desktop", description: "Give
|
|
|
2
2
|
|
|
3
3
|
# Add a desktop
|
|
4
4
|
|
|
5
|
-
Every baba runs voss's shell: the desktop system, which voss adds to every baba as it adds `maker` and `
|
|
5
|
+
Every baba runs voss's shell: the desktop system, which voss adds to every baba as it adds `maker` and `agents`, over the apps the baba declares in `baba({ apps })`. The interface gives each app its component; the page is voss's. Three nouns and one verb: a **app** is a kind the baba registers; a **window** is one instance of it in a session, at a path; the **session** shows one window, or nothing. The verb is `desktop-open`: it brings the window at that path to the front, else the app's current window, else makes one; asked for a `fresh` window, an app declared `many` gets another, any other app gives the one it has. The launcher, a link in an app, the page's address, the CLI and another system's ask all go through it.
|
|
6
6
|
|
|
7
7
|
The page is a bar over one app. At the bar's far left the mark, the two eyes, opens the launcher; then a pill per open window, its icon at the left and the one in front filled; pointing at a pill turns its icon into the × that closes it, and a middle click closes it too, as it closes a browser tab; closing the one in front brings the pill to its left forward, else the one to its right. The launcher is the page's, not the state's: it shows while its search has focus, and while nothing is in front, since there is nothing else to show. The mark or Cmd/Ctrl K gives the search focus; a click on the launcher's empty space keeps it; choosing a row, Escape or the mark again takes it, and the launcher goes. It covers the page and changes nothing beneath: the bar's row becomes the search, and below it one column of rows, a palette: the icon, beginning where the search begins, then the label; what is open first, then every app under its group. One dot under the mark says which row is chosen, by the keys or the pointer, and glides between them. Typing ranks both by the words, a word at the start of a name first, and drops the groups. An app's row opens a fresh window of it, or brings the one it has to the front; a window's row brings it to the front, and pointing at it turns its icon into its ×. A browser tab shows one window at a time: a pill on the bar brings an open one to the front, the mark then a row opens another. Below the apps, under Sessions, the tab's other sessions and New session. A page inside a baba is that baba only; the last row, Leave, and the `voss` link before the title on the bar go back to the picker, voss's root page, which has no state: it lists the babas voss knows, one inside another under its parent, and New baba, the wizard that makes a baba in a folder or remembers one already there.
|
|
8
8
|
|
|
@@ -30,7 +30,7 @@ import { Tasks } from "./apps/tasks.tsx";
|
|
|
30
30
|
export default compose<typeof baba>({ apps: { tasks: Tasks } });
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
An app missing from `compose`, or a component for an app the baba does not declare, is a type error. After the baba's apps come voss's own, under Voss: `maker` the specs run live, `state` the entities and resources, `contract` the actions and queries as forms, `
|
|
33
|
+
An app missing from `compose`, or a component for an app the baba does not declare, is a type error. After the baba's apps come voss's own, under Voss: `maker` the specs run live, `state` the entities and resources, `contract` the actions and queries as forms, `agentic instructions` the know-how, `settings` the theme. A baba never declares them; its interface may give one a component of its own to draw it otherwise, `compose<typeof baba>({ apps: { …, agentic instructions: Mine } })`. A baba that declares no apps has one, `view`, which draws its interface whole. A baba whose interface fails to build or load still has its shell: its apps say why, voss's work. The old addresses, `/NAME/run`, `/inspect`, `/proofs`, open the app they became, unless the baba has an app by that name. The old form, `compose({ shell })`, still loads for one release, in the `view` app; a baba cannot declare apps and keep a desktop system of its own.
|
|
34
34
|
|
|
35
35
|
An app's component receives `AppProps`: `{ session, window, path, route, params, active, memory, navigate, open, setTitle, remember }`. It never names an action: `navigate(path)` moves its own window, `open(app, path)` opens any app by the one rule, `setTitle(title)` names its window after what it shows, the pill's tooltip, `remember(key, value)` keeps a filter, a selection or a fold on its window, in the state, and `memory` reads it back. Windows stay mounted when hidden. A path begins with one `/`.
|
|
36
36
|
|
package/src/guide/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// voss's own know-how for agents, given to every baba after its own: how to
|
|
2
2
|
// write, prove and compose systems, how to draw the page as apps and give it
|
|
3
|
-
// a desktop, and how to write
|
|
4
|
-
import type { Prompt } from "../
|
|
3
|
+
// a desktop, and how to write agentic instructions.
|
|
4
|
+
import type { Prompt } from "../instructions/index.ts";
|
|
5
5
|
import writeASystem from "./write-a-system.mdx";
|
|
6
6
|
import reachOutside from "./reach-outside.mdx";
|
|
7
7
|
import systemsTogether from "./systems-together.mdx";
|
|
8
8
|
import composeAnInterface from "./compose-an-interface.mdx";
|
|
9
9
|
import addADesktop from "./add-a-desktop.mdx";
|
|
10
|
-
import
|
|
10
|
+
import writeInstructions from "./write-agentic-instructions.mdx";
|
|
11
11
|
import specASystem from "./spec-a-system.mdx";
|
|
12
12
|
|
|
13
|
-
export const guide: readonly Prompt[] = [writeASystem, reachOutside, systemsTogether, specASystem, composeAnInterface, addADesktop,
|
|
13
|
+
export const guide: readonly Prompt[] = [writeASystem, reachOutside, systemsTogether, specASystem, composeAnInterface, addADesktop, writeInstructions];
|
|
@@ -164,11 +164,11 @@ p.query("pond", {
|
|
|
164
164
|
- A fire may carry an idempotency key (`--key K` at the shell, `key` by MCP): the baba remembers the answer for a day, so a retry answers the same without running again. Make actions safe to repeat anyway.
|
|
165
165
|
- `w.transaction(fn)` gives any code what an action has: a throw undoes every write inside it.
|
|
166
166
|
- Validation is at the boundary: args and results are checked; your writes inside are trusted, so write what the schema says.
|
|
167
|
-
- Reserved names, not for actions or queries: start, stop, status, state, follow, run, raw, secret, help, mcp, check, install, add-system, serve,
|
|
167
|
+
- Reserved names, not for actions or queries: start, stop, status, state, follow, run, raw, secret, help, mcp, check, install, add-system, serve, agents, init, babas, service, gui.
|
|
168
168
|
|
|
169
169
|
## The baba
|
|
170
170
|
|
|
171
|
-
`.baba/index.ts` lists every system, in the order their steps run: `baba({ systems: [tasks, pond], apps,
|
|
171
|
+
`.baba/index.ts` lists every system, in the order their steps run: `baba({ systems: [tasks, pond], apps, harness, tick })`. A system that reads another goes after it, so it sees this round's writes.
|
|
172
172
|
|
|
173
173
|
## Schemas
|
|
174
174
|
|
|
@@ -179,5 +179,5 @@ p.query("pond", {
|
|
|
179
179
|
1. Every component and resource the system writes is in its `model`; every other system it reads or asks of is in its `reads`, and none of those reads it back.
|
|
180
180
|
2. Steps are ordered on purpose; the hot ones use `w.each`; the reactive ones use change terms.
|
|
181
181
|
3. Every piece is made with this system's `p` and listed once; actions answer data; queries change nothing; names are flat and not reserved.
|
|
182
|
-
4. The baba's
|
|
182
|
+
4. The baba's context, an `.mdx` under `.baba/agents/`, says in a line what the system is for (see `write-agentic-instructions`); its `spec.ts` beside it proves it, a scenario for each thing it must do (see `spec-a-system`); an app in `.baba/apps/` that names it in `systems` shows it, live and in the Maker (see `compose-an-interface`).
|
|
183
183
|
5. `bun test` (the runner `.baba/spec.test.ts` proves every spec) and the type-check are green; the running baba reloads on save and `voss baba` shows the new contract.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export const prompt = { kind: "skill", name: "write-
|
|
2
|
-
description: "Write or change what a baba tells agents: its context, skills and hooks,
|
|
1
|
+
export const prompt = { kind: "skill", name: "write-agentic-instructions",
|
|
2
|
+
description: "Write or change what a baba tells agents: its context, skills and hooks, each an .mdx file under .baba/agents/. Use before writing any agentic instructions, in any baba, including skills like this one.",
|
|
3
3
|
};
|
|
4
4
|
|
|
5
|
-
# Write
|
|
5
|
+
# Write agentic instructions
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
An instruction file an agent can act on at first read: one trigger, steps in order, one example, claims it can check, rules it cannot break. The agentic instructions of a baba are the `.mdx` files under `.baba/agents/`; voss reads every one of them, in name order, and there is no list to keep.
|
|
8
8
|
|
|
9
9
|
## When
|
|
10
10
|
|
|
@@ -14,17 +14,17 @@ A promptware file an agent can act on at first read: one trigger, steps in order
|
|
|
14
14
|
|
|
15
15
|
## Do
|
|
16
16
|
|
|
17
|
-
1. Pick the kind. `context`: the baba's section of the context file, read by every session; what the project is, how it is worked on, its rules; one per baba,
|
|
17
|
+
1. Pick the kind. `context`: the baba's section of the context file, read by every session; what the project is, how it is worked on, its rules; one per baba, `export const prompt = { kind: "context" }`. `skill`: a file read when its description fits; how to do one thing; `export const prompt = { kind: "skill", name, description, uses }`, one `.mdx` per skill. A hook, a harness moment handed to an action, is `hooks: [{ on, match, action }]` on the context's `prompt`.
|
|
18
18
|
2. Write the description as the trigger: `Use when …`, the situations, the files, the words an agent would be thinking. The description decides whether the skill is read; the body decides whether it works.
|
|
19
|
-
3. Write Markdown
|
|
19
|
+
3. Write Markdown in the `.mdx` file; `import { Call } from "babavoss/instructions"` for the tags below. Export the `prompt` metadata object with `kind: "skill"`, name, description, and optional uses/eval; for the context, `kind: "context"` with optional hooks/eval. An `eval` is the owner's golden interpretation, run on demand from the Trials app to score readers of the text; it gates nothing. Changing a skill, leave its eval alone unless the owner asks. Import contract components and embed them as JSX. MDX expressions are JavaScript; keep typed helpers in imported .ts files. Existing TSX and `md` templates remain supported.
|
|
20
20
|
4. Reference the contract, never restate it: `<Call of="feed" />` for a call with its example and summary, `<Calls of="pond" />` for a system's calls, `<Schema of>` for args, `<Usage of>` for the shell's spelling, `<See skills={[…]} />` for the skills to read next. A name that does not exist refuses to load.
|
|
21
|
-
5.
|
|
21
|
+
5. Save it under `.baba/agents/`: voss finds it there. `baba({ harness })` says which harnesses get the files and Claude Code's permissions.
|
|
22
22
|
6. Read the output: `CLAUDE.md` and `.claude/skills/NAME/SKILL.md` after the sync. Cut every sentence that does not change what an agent does.
|
|
23
23
|
|
|
24
24
|
## Example
|
|
25
25
|
|
|
26
26
|
```mdx
|
|
27
|
-
import { Call, See } from "babavoss/
|
|
27
|
+
import { Call, See } from "babavoss/instructions";
|
|
28
28
|
|
|
29
29
|
export const prompt = {
|
|
30
30
|
kind: "skill",
|
|
@@ -48,22 +48,15 @@ Do not add or remove fish with raw writes.
|
|
|
48
48
|
<See skills={["write-a-system"]} />
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import feedThePond from "./feed-the-pond.mdx";
|
|
51
|
+
```mdx
|
|
52
|
+
{/* .baba/agents/context.mdx */}
|
|
53
|
+
export const prompt = { kind: "context", hooks: [{ on: "PreToolUse", match: "Bash", action: "guard-raw" }] };
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
context(md\`
|
|
58
|
-
The demo: an example baba. It shows what a baba is; it is not a product.
|
|
55
|
+
The demo: an example baba. It shows what a baba is; it is not a product.
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
## Rules
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
\`),
|
|
64
|
-
feedThePond,
|
|
65
|
-
hook({ on: "PreToolUse", match: "Bash", action: "guard-raw" }),
|
|
66
|
-
];
|
|
59
|
+
- Keep each system small and proved by its scenarios.
|
|
67
60
|
```
|
|
68
61
|
|
|
69
62
|
## Check
|
|
@@ -76,15 +69,15 @@ export default [
|
|
|
76
69
|
|
|
77
70
|
## Never
|
|
78
71
|
|
|
79
|
-
- Read the state in
|
|
72
|
+
- Read the state in agentic instructions: it is static, rendered from the manifest once per generation.
|
|
80
73
|
- Explain why in a step; put reasons in the context if they are needed at all.
|
|
81
74
|
- Write `simply`, `just`, `easily`, `should`, or a synonym for a term that has one name: system, app, spec, step, state, round, action, query, effect, source, mirror, context, skill, hook, baba, voss.
|
|
82
|
-
- Edit `CLAUDE.md`, `AGENTS.md`, a `SKILL.md` or `.claude/settings.local.json`: they are outputs. Git keeps none of them: voss writes its lines of the project's `.gitignore`, and a checkout makes its own with `voss baba
|
|
75
|
+
- Edit `CLAUDE.md`, `AGENTS.md`, a `SKILL.md` or `.claude/settings.local.json`: they are outputs. Git keeps none of them: voss writes its lines of the project's `.gitignore`, and a checkout makes its own with `voss baba agents sync`.
|
|
83
76
|
- Commit an output. A hand-written `.claude/settings.json` beside the made `settings.local.json` is the place for what the repository itself tells Claude Code, a `SessionStart` hook that runs the sync, say.
|
|
84
77
|
|
|
85
78
|
## Hooks
|
|
86
79
|
|
|
87
|
-
A hook hands a harness moment to an action: `
|
|
80
|
+
A hook hands a harness moment to an action: `{ on, match?, action, strict? }` in the context's `hooks`, with `on` one of PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, SessionStart, PreCompact, Notification, and `match` a tool-name regex for the two tool events. The action takes `hookArgs(EVENT)` and answers `hookResult()`: `{ decision: "block", reason }` stops the tool call and the agent reads the reason; `{ context }` is told to the agent; `{}` lets it through. Voss compiles it into `.claude/settings.local.json` as `voss baba ACTION @- --hook EVENT`; a baba that is down lets through with a note, `strict: true` blocks. Prove a hook with a scenario: `fire` the action with an event, `check` the decision.
|
|
88
81
|
|
|
89
82
|
## Voice
|
|
90
83
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The golden interpretation of a piece of
|
|
1
|
+
// The golden interpretation of a piece of agentic instructions: situations a fresh
|
|
2
2
|
// reader must decide from the text alone. Authored as givens, each with the
|
|
3
3
|
// cases under it: when (the moment of decision) and then (the right move, a
|
|
4
4
|
// few words). A situation the text never mentions, decided by what the text
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// babavoss/
|
|
1
|
+
// babavoss/instructions: agentic instructions as code. What a baba tells agents, written in
|
|
2
2
|
// MDX (or TSX) that renders to Markdown: `context(...)` for the baba's section of the
|
|
3
3
|
// context file, `skill({ name, description }, ...)` for a skill file,
|
|
4
4
|
// `hook(...)` for a moment of the harness; `md` for raw Markdown; `<Call
|
|
@@ -15,9 +15,9 @@ import { isElement, jsx, Fragment, type PromptNode, type PromptElement, type Pro
|
|
|
15
15
|
|
|
16
16
|
export { Fragment, type PromptNode, type PromptElement, type PromptComponent, type JSX } from "./jsx-runtime.ts";
|
|
17
17
|
|
|
18
|
-
/** What a baba declares under
|
|
18
|
+
/** What a baba declares under `.baba/agents/`. */
|
|
19
19
|
export type Prompt =
|
|
20
|
-
| { kind: "context"; body: PromptNode; eval?: InterpretationSuite }
|
|
20
|
+
| { kind: "context"; body: PromptNode; eval?: InterpretationSuite; hooks?: HookDef[] }
|
|
21
21
|
| { kind: "skill"; name: string; description: string; uses: string[]; body: PromptNode; eval?: InterpretationSuite }
|
|
22
22
|
| { kind: "hook"; on: HookEvent; match: string | null; action: string; strict: boolean };
|
|
23
23
|
|
|
@@ -33,7 +33,8 @@ export type HookEvent = (typeof HOOK_EVENTS)[number];
|
|
|
33
33
|
* PostToolUse to tool names, a regex. With `strict`, a baba that does not
|
|
34
34
|
* answer blocks; otherwise it lets through.
|
|
35
35
|
*/
|
|
36
|
-
export
|
|
36
|
+
export interface HookDef { on: HookEvent; match?: string; action: string; strict?: boolean }
|
|
37
|
+
export function hook(def: HookDef): Prompt {
|
|
37
38
|
if (!HOOK_EVENTS.includes(def.on)) throw new Error(`hook: on is one of ${HOOK_EVENTS.join(", ")}, not ${JSON.stringify(def.on)}`);
|
|
38
39
|
if (def.match !== undefined && def.on !== "PreToolUse" && def.on !== "PostToolUse") throw new Error(`hook on ${def.on}: match is for PreToolUse and PostToolUse`);
|
|
39
40
|
return { kind: "hook", on: def.on, match: def.match ?? null, action: def.action, strict: def.strict === true };
|
|
@@ -72,7 +73,7 @@ export const hookResult = (): Schema<HookAnswer> => s.object({ decision: s.optio
|
|
|
72
73
|
const WORD = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
73
74
|
|
|
74
75
|
/** The baba's section of the context file, CLAUDE.md and AGENTS.md: what the project is and how it is worked on, in any Markdown; one per baba. Voss wraps it with the doors and the skills. */
|
|
75
|
-
export const context = (body: PromptNode, options?: { eval?: InterpretationSuite }): Prompt => ({ kind: "context", body, ...(options?.eval ? { eval: options.eval } : {}) });
|
|
76
|
+
export const context = (body: PromptNode, options?: { eval?: InterpretationSuite; hooks?: HookDef[] }): Prompt => ({ kind: "context", body, ...(options?.eval ? { eval: options.eval } : {}), ...(options?.hooks?.length ? { hooks: options.hooks.map((h) => { hook(h); return h; }) } : {}) });
|
|
76
77
|
|
|
77
78
|
/** A skill: a file an agent opens when the description fits what it is doing. `uses` names the calls it is about, checked against the contract. The body is any Markdown node. */
|
|
78
79
|
export function skill(head: { name: string; description: string; uses?: string[]; eval?: InterpretationSuite }, body: PromptNode): Prompt {
|
|
@@ -82,11 +83,11 @@ export function skill(head: { name: string; description: string; uses?: string[]
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
/** MDX's metadata and lazy document component become the same prompt as TSX. */
|
|
85
|
-
export function mdxDocument(meta: { kind: "context"; eval?: InterpretationSuite } | { kind: "skill"; name: string; description: string; uses?: string[]; eval?: InterpretationSuite }, Content: PromptComponent): Prompt {
|
|
86
|
+
export function mdxDocument(meta: { kind: "context"; eval?: InterpretationSuite; hooks?: HookDef[] } | { kind: "skill"; name: string; description: string; uses?: string[]; eval?: InterpretationSuite }, Content: PromptComponent): Prompt {
|
|
86
87
|
const body = jsx(Content, {});
|
|
87
88
|
if (meta?.kind === "context") return context(body, meta);
|
|
88
89
|
if (meta?.kind === "skill") return skill(meta, body);
|
|
89
|
-
throw new Error('MDX
|
|
90
|
+
throw new Error('an MDX instruction file needs export const prompt = { kind: "context" | "skill", ... }');
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
/** Raw Markdown, verbatim, with the common indentation removed: for prose longer than a line. */
|
|
@@ -172,7 +173,7 @@ function block(node: PromptNode): string {
|
|
|
172
173
|
case "table": return table(kids) + "\n";
|
|
173
174
|
case "strong": case "em": case "del": case "img": case "input": case "code": case "b": case "i": case "a": case "br": return inline(node) + "\n\n";
|
|
174
175
|
case "tr": case "th": case "td": return inline(kids);
|
|
175
|
-
default: throw new Error(`
|
|
176
|
+
default: throw new Error(`instructions: no such tag <${String(type)}>`);
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// The JSX runtime for
|
|
2
|
-
// the DOM. A file says `/** @jsxImportSource babavoss/
|
|
1
|
+
// The JSX runtime for agentic instructions: elements that render to Markdown, not to
|
|
2
|
+
// the DOM. A file says `/** @jsxImportSource babavoss/instructions */` and writes
|
|
3
3
|
// `<p>`, `<ul>`, `<pre lang="ts">`, `<Call of="feed" />`; `render` turns the
|
|
4
4
|
// tree into text. Pure, no React.
|
|
5
5
|
export interface PromptElement { readonly $prompt: true; type: string | PromptComponent; props: Record<string, unknown> }
|
|
@@ -18,7 +18,7 @@ export const isElement = (v: unknown): v is PromptElement => typeof v === "objec
|
|
|
18
18
|
|
|
19
19
|
type Children = { children?: PromptNode };
|
|
20
20
|
|
|
21
|
-
// What TypeScript admits in
|
|
21
|
+
// What TypeScript admits in an instruction file: the Markdown tags, each with the props it renders.
|
|
22
22
|
export namespace JSX {
|
|
23
23
|
export type Element = PromptElement;
|
|
24
24
|
/** What a tag may be: a Markdown tag, or a component answering any node, text and lists included, not only an element. */
|
package/src/mcp/main.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { toolsOf, callName, promptsOf, promptText, type Tools } from "./tools.ts
|
|
|
17
17
|
import { BUDGET, DoorError, budget, errorOf, index, schemaOf, followEntity, watchQuery, stateOf, untilOf, byKey, isTruncated, type Outcome } from "../door/core.ts";
|
|
18
18
|
|
|
19
19
|
const VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
20
|
-
const empty: Manifest = { systems: [],
|
|
20
|
+
const empty: Manifest = { systems: [], instructions: { context: null, skills: {}, hooks: [] }, harness: defaultHarness, components: {}, resources: {}, effects: {}, actions: {}, queries: {}, tick: null };
|
|
21
21
|
|
|
22
22
|
const log = (l: string) => process.stderr.write(`voss mcp: ${l}\n`);
|
|
23
23
|
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -159,13 +159,13 @@ export async function runMcp(project: Project, rootDir: string): Promise<number>
|
|
|
159
159
|
}
|
|
160
160
|
case "resources/list": return {
|
|
161
161
|
resources: [
|
|
162
|
-
...(m.
|
|
162
|
+
...(m.instructions.context ? [{ uri: "baba://context", name: "context", description: "what the baba tells agents", mimeType: "text/markdown" }] : []),
|
|
163
163
|
...[...outs.keys()].map((f) => ({ uri: `baba://out/${f}`, name: f, description: "an answer over the output budget", mimeType: "application/json" })),
|
|
164
164
|
],
|
|
165
165
|
};
|
|
166
166
|
case "resources/read": {
|
|
167
167
|
const uri = String(p.uri ?? "");
|
|
168
|
-
const context = uri === "baba://context" ? m.
|
|
168
|
+
const context = uri === "baba://context" ? m.instructions.context : null;
|
|
169
169
|
if (context) return { contents: [{ uri, mimeType: "text/markdown", text: context }] };
|
|
170
170
|
const out = uri.startsWith("baba://out/") ? outs.get(basename(uri.slice(11))) : undefined;
|
|
171
171
|
if (out) return { contents: [{ uri, mimeType: "application/json", text: await readFile(out, "utf8") }] };
|
package/src/mcp/tools.ts
CHANGED
|
@@ -16,12 +16,12 @@ export interface Target { kind: "action" | "query"; name: string; system: string
|
|
|
16
16
|
|
|
17
17
|
export interface Tools { tools: Tool[]; targets: Map<string, Target>; generic: boolean }
|
|
18
18
|
|
|
19
|
-
/** The
|
|
19
|
+
/** The agentic instructions's threshold for describing the contract as `call`/`read`; the server itself is generic unless VOSS_MCP_TOOLS=1. */
|
|
20
20
|
|
|
21
21
|
/** How an agent learns the contract: said in every tool that takes a call's name. */
|
|
22
22
|
const LEARN = "`manifest` lists every call with an example; `manifest` with a name gives its schemas.";
|
|
23
23
|
|
|
24
|
-
/** `<system>_<name>`; a name that already starts with its system's, as voss's own do (`
|
|
24
|
+
/** `<system>_<name>`; a name that already starts with its system's, as voss's own do (`agents-sync`), is not prefixed twice: `agents_sync`. */
|
|
25
25
|
export const toolName = (system: string, name: string) => name.startsWith(`${system}-`) ? `${system}_${name.slice(system.length + 1)}` : `${system}_${name}`;
|
|
26
26
|
|
|
27
27
|
const isObj = (v: unknown): v is Obj => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -107,12 +107,12 @@ export function callName(m: Manifest, n: string): { kind: "action" | "query"; na
|
|
|
107
107
|
|
|
108
108
|
/** One prompt per skill, by its name. */
|
|
109
109
|
export function promptsOf(m: Manifest): { name: string; description: string; skill: string }[] {
|
|
110
|
-
return Object.entries(m.
|
|
110
|
+
return Object.entries(m.instructions.skills).map(([k, sk]) => ({ name: k, description: sk.description, skill: k }));
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** A skill's prompt text: its body, then the tools it uses. */
|
|
114
114
|
export function promptText(m: Manifest, skill: string, generic: boolean): string | null {
|
|
115
|
-
const sk = m.
|
|
115
|
+
const sk = m.instructions.skills[skill];
|
|
116
116
|
if (!sk) return null;
|
|
117
117
|
const uses = (sk.uses ?? []).map((n) => {
|
|
118
118
|
const c = m.actions[n] ?? m.queries[n];
|
package/src/runtime/harness.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { manifestOf, type Baba } from "../ecs/baba.ts";
|
|
|
6
6
|
import { Runtime, ContractError } from "../ecs/runtime.ts";
|
|
7
7
|
import { StateError, type Snapshot } from "../ecs/state.ts";
|
|
8
8
|
import { makeBridge, portOver } from "./bridge.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { withInstructions } from "../agents/system.ts";
|
|
10
10
|
|
|
11
11
|
/** How long a `state` read waits for the round past `since` before answering the state as it is. */
|
|
12
12
|
export const STATE_WAIT_MS = 25_000;
|
|
@@ -23,7 +23,7 @@ export class Harness {
|
|
|
23
23
|
/** Loads the baba over the snapshot given; answers `loaded`, or throws for `failed`. */
|
|
24
24
|
load(own: Baba, project: { name: string; dir: string; baba: string }, env: Record<string, string>, snapshot: Snapshot | null, prepare = false): void {
|
|
25
25
|
// Voss's own systems join every baba after its own.
|
|
26
|
-
const v =
|
|
26
|
+
const v = withInstructions(own);
|
|
27
27
|
const manifest = manifestOf(v);
|
|
28
28
|
const make = (snapshot: Snapshot | null, auto: boolean) => new Runtime(v, {
|
|
29
29
|
auto,
|