babavoss 0.12.6 → 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 +1 -1
- package/gui/{chunk-9gmmm54v.js → chunk-a20zfekq.js} +4 -2
- package/gui/gui.js +15 -15
- package/index.ts +1 -1
- package/package.json +5 -5
- 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/gui/babavoss-web.js
CHANGED
|
@@ -2783,7 +2783,7 @@ function dataOf(v) {
|
|
|
2783
2783
|
var nameOf = (v) => dataOf(v).name;
|
|
2784
2784
|
var NAME2 = /^[a-z][a-zA-Z0-9-]*$/;
|
|
2785
2785
|
var SYSTEM_NAME = /^[a-z][a-z0-9-]*$/;
|
|
2786
|
-
var RESERVED = new Set(["start", "stop", "status", "state", "follow", "run", "raw", "secret", "help", "mcp", "check", "install", "add-system", "serve", "
|
|
2786
|
+
var RESERVED = new Set(["start", "stop", "status", "state", "follow", "run", "raw", "secret", "help", "mcp", "check", "install", "add-system", "serve", "agents", "init", "babas", "service", "gui"]);
|
|
2787
2787
|
function declareModel(name, model) {
|
|
2788
2788
|
if (typeof name !== "string" || !SYSTEM_NAME.test(name))
|
|
2789
2789
|
throw new Error(`system ${JSON.stringify(name)}: lowercase letters, digits and dashes`);
|
|
@@ -2883,6 +2883,8 @@ function closureOf(systems) {
|
|
|
2883
2883
|
return out;
|
|
2884
2884
|
}
|
|
2885
2885
|
function baba(def) {
|
|
2886
|
+
if ("promptware" in def)
|
|
2887
|
+
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");
|
|
2886
2888
|
const harness = harnessOf(def.harness);
|
|
2887
2889
|
if (def.tick !== undefined && !(Number.isInteger(def.tick) && def.tick > 0))
|
|
2888
2890
|
throw new Error("baba: tick is whole milliseconds, above zero");
|
|
@@ -2942,7 +2944,7 @@ function baba(def) {
|
|
|
2942
2944
|
if (e)
|
|
2943
2945
|
throw new Error(`app ${v.key}: ${e}`);
|
|
2944
2946
|
}
|
|
2945
|
-
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, $routes: undefined, tick: def.tick ?? null,
|
|
2947
|
+
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, $routes: undefined, tick: def.tick ?? null, instructions: [...def.instructions ?? []], harness, model, actions, queries });
|
|
2946
2948
|
}
|
|
2947
2949
|
function isSystem(v) {
|
|
2948
2950
|
return typeof v === "object" && v !== null && SYSTEM in v;
|
package/gui/gui.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
useBabaState,
|
|
36
36
|
useResource,
|
|
37
37
|
useScope
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-a20zfekq.js";
|
|
39
39
|
import {
|
|
40
40
|
require_jsx_runtime
|
|
41
41
|
} from "./chunk-pk09y98y.js";
|
|
@@ -825,21 +825,21 @@ function Output({ value }) {
|
|
|
825
825
|
});
|
|
826
826
|
}
|
|
827
827
|
|
|
828
|
-
// src/gui/
|
|
828
|
+
// src/gui/instructions.tsx
|
|
829
829
|
var import_react3 = __toESM(require_react(), 1);
|
|
830
830
|
var jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
831
|
-
function
|
|
831
|
+
function Instructions({ manifest }) {
|
|
832
832
|
const { fire, read, ready, round } = useBabaState();
|
|
833
833
|
const [plan2, setPlan] = import_react3.useState(null);
|
|
834
834
|
const [error, setError] = import_react3.useState(null);
|
|
835
835
|
import_react3.useEffect(() => {
|
|
836
836
|
if (ready)
|
|
837
|
-
read("
|
|
837
|
+
read("agents-plan").then((p) => setPlan(p), setError);
|
|
838
838
|
}, [ready, round]);
|
|
839
839
|
if (!("pwStatus" in manifest.resources))
|
|
840
840
|
return /* @__PURE__ */ jsx_runtime3.jsx("p", {
|
|
841
841
|
className: "bv-muted",
|
|
842
|
-
children: "This baba has no
|
|
842
|
+
children: "This baba has no agents system."
|
|
843
843
|
});
|
|
844
844
|
if (!ready)
|
|
845
845
|
return /* @__PURE__ */ jsx_runtime3.jsx("p", {
|
|
@@ -872,7 +872,7 @@ function Promptware({ manifest }) {
|
|
|
872
872
|
children: [
|
|
873
873
|
/* @__PURE__ */ jsx_runtime3.jsx(Button, {
|
|
874
874
|
variant: "primary",
|
|
875
|
-
onClick: () => void act("
|
|
875
|
+
onClick: () => void act("agents-sync", {}).catch(() => {}),
|
|
876
876
|
children: "Sync now"
|
|
877
877
|
}),
|
|
878
878
|
plan2 && /* @__PURE__ */ jsx_runtime3.jsxs("span", {
|
|
@@ -903,17 +903,17 @@ function Promptware({ manifest }) {
|
|
|
903
903
|
});
|
|
904
904
|
}
|
|
905
905
|
function Declared({ manifest }) {
|
|
906
|
-
const pw = manifest.
|
|
906
|
+
const pw = manifest.instructions;
|
|
907
907
|
const skills = Object.entries(pw.skills);
|
|
908
908
|
return /* @__PURE__ */ jsx_runtime3.jsxs("section", {
|
|
909
909
|
className: "gui-section",
|
|
910
910
|
children: [
|
|
911
911
|
/* @__PURE__ */ jsx_runtime3.jsx("h2", {
|
|
912
|
-
children: "
|
|
912
|
+
children: "Instructions"
|
|
913
913
|
}),
|
|
914
914
|
!pw.context && skills.length === 0 && pw.hooks.length === 0 && /* @__PURE__ */ jsx_runtime3.jsx("p", {
|
|
915
915
|
className: "bv-muted",
|
|
916
|
-
children: "Nothing declared yet: give the baba a `context(...)`, a `skill(...)` or a `hook(...)` under
|
|
916
|
+
children: "Nothing declared yet: give the baba a `context(...)`, a `skill(...)` or a `hook(...)` under `.baba/agents/`."
|
|
917
917
|
}),
|
|
918
918
|
pw.context && /* @__PURE__ */ jsx_runtime3.jsxs("p", {
|
|
919
919
|
className: "bv-muted",
|
|
@@ -944,7 +944,7 @@ function Declared({ manifest }) {
|
|
|
944
944
|
}),
|
|
945
945
|
/* @__PURE__ */ jsx_runtime3.jsx("p", {
|
|
946
946
|
className: "bv-muted",
|
|
947
|
-
children: "The
|
|
947
|
+
children: "The agentic instructions is the baba's code: edit it under .baba/agents/; the files below follow."
|
|
948
948
|
})
|
|
949
949
|
]
|
|
950
950
|
});
|
|
@@ -982,11 +982,11 @@ function Harness({ manifest }) {
|
|
|
982
982
|
/* @__PURE__ */ jsx_runtime3.jsxs("p", {
|
|
983
983
|
className: "bv-muted",
|
|
984
984
|
children: [
|
|
985
|
-
"
|
|
985
|
+
"The .mdx files under .baba/agents/, each ",
|
|
986
986
|
/* @__PURE__ */ jsx_runtime3.jsx("code", {
|
|
987
|
-
children:
|
|
987
|
+
children: 'export const prompt = { kind: "context" | "skill", … }'
|
|
988
988
|
}),
|
|
989
|
-
"."
|
|
989
|
+
"; voss reads them on its own."
|
|
990
990
|
]
|
|
991
991
|
})
|
|
992
992
|
]
|
|
@@ -1918,9 +1918,9 @@ function ShellPage({ d }) {
|
|
|
1918
1918
|
manifest
|
|
1919
1919
|
})
|
|
1920
1920
|
}),
|
|
1921
|
-
|
|
1921
|
+
instructions: () => /* @__PURE__ */ jsx_runtime6.jsx("section", {
|
|
1922
1922
|
className: "gui-app",
|
|
1923
|
-
children: /* @__PURE__ */ jsx_runtime6.jsx(
|
|
1923
|
+
children: /* @__PURE__ */ jsx_runtime6.jsx(Instructions, {
|
|
1924
1924
|
manifest
|
|
1925
1925
|
})
|
|
1926
1926
|
}),
|
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// babavoss: the framework. What a baba imports.
|
|
2
|
-
export { baba, effect, source, accept, resource, shape, manifestOf, isBaba, isSystem, nameOf, SYSTEM } from "./src/ecs/baba.ts";
|
|
2
|
+
export { baba, instructed, effect, source, accept, resource, shape, manifestOf, isBaba, isSystem, nameOf, SYSTEM } from "./src/ecs/baba.ts";
|
|
3
3
|
export { system, declare, part } from "./src/ecs/system.ts";
|
|
4
4
|
export type { Decl, Entry, Handles, HandlesOf, Building, Maker, Part, Piece, Maybe, StepPiece, OnPiece, ActionPiece, QueryPiece, BindingOf, Scope, Reader, StateOf, ReaderOf, Act, Read } from "./src/ecs/system.ts";
|
|
5
5
|
export { not, added, changed, removed, select } from "./src/ecs/state.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babavoss",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": "github:alanremarc/babavoss",
|
|
6
6
|
"homepage": "https://babavoss.org",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"./web": "./src/web/index.tsx",
|
|
15
15
|
"./test": "./src/test/index.ts",
|
|
16
16
|
"./bench": "./src/bench/index.ts",
|
|
17
|
-
"./
|
|
18
|
-
"./
|
|
19
|
-
"./
|
|
17
|
+
"./instructions": "./src/instructions/index.ts",
|
|
18
|
+
"./instructions/jsx-runtime": "./src/instructions/jsx-runtime.ts",
|
|
19
|
+
"./instructions/jsx-dev-runtime": "./src/instructions/jsx-dev-runtime.ts",
|
|
20
20
|
"./desktop": "./src/desktop/index.ts",
|
|
21
21
|
"./kit": "./src/kit/index.ts",
|
|
22
22
|
"./spec": "./src/spec/index.ts",
|
|
23
|
-
"./
|
|
23
|
+
"./instructions/register": "./src/build/mdx-register.ts"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"bin",
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Instructions to harness files, as text. Pure: the manifest, with the baba's
|
|
2
2
|
// context and skills rendered and its harness declaration, becomes what
|
|
3
3
|
// each output path, relative to the project, should hold.
|
|
4
4
|
//
|
|
5
5
|
// Claude Code CLAUDE.md .claude/skills/NAME/SKILL.md .claude/settings.local.json
|
|
6
6
|
// Codex AGENTS.md .agents/skills/NAME/SKILL.md
|
|
7
7
|
//
|
|
8
|
-
//
|
|
8
|
+
// Instructions is know-how; the contract teaches itself at the doors, the
|
|
9
9
|
// CLI's index and schemas and the MCP server's tools. The instruction file
|
|
10
10
|
// teaches the doors, true whatever the contract becomes; the table of calls
|
|
11
11
|
// is rendered only when the baba asks for it. The MCP registration is
|
|
12
|
-
// not
|
|
12
|
+
// not agentic instructions's: it is the machine's. The permissions, in a file the
|
|
13
13
|
// project shares with others, are a merge: voss owns only its entries.
|
|
14
14
|
import type { Json } from "../kernel/schema.ts";
|
|
15
15
|
import type { Manifest } from "../ecs/baba.ts";
|
|
@@ -18,7 +18,7 @@ import { exampleOf } from "../ecs/handles.ts";
|
|
|
18
18
|
import { skillsOf, type SkillManifest } from "./define.ts";
|
|
19
19
|
|
|
20
20
|
/** The systems voss gives every baba: their calls are not the project's contract. */
|
|
21
|
-
export const VOSS = new Set(["
|
|
21
|
+
export const VOSS = new Set(["agents", "maker"]);
|
|
22
22
|
|
|
23
23
|
export type Harness = "claude" | "codex";
|
|
24
24
|
export const harnesses: Harness[] = ["claude", "codex"];
|
|
@@ -31,8 +31,8 @@ const files: Record<Harness, { instructions: string; skills: string }> = {
|
|
|
31
31
|
/** Which harness an output path belongs to. */
|
|
32
32
|
export const harnessOf = (path: string): Harness => (path === "AGENTS.md" || path.startsWith(".agents/") || path.startsWith(".codex/") ? "codex" : "claude");
|
|
33
33
|
|
|
34
|
-
/** Where the
|
|
35
|
-
export const SOURCE = ".baba/
|
|
34
|
+
/** Where the agentic instructions lives, relative to the project. */
|
|
35
|
+
export const SOURCE = ".baba/agents";
|
|
36
36
|
|
|
37
37
|
// ---- merges ------------------------------------------------------------
|
|
38
38
|
|
|
@@ -55,11 +55,11 @@ export type Merge =
|
|
|
55
55
|
export const CLAUDE_SETTINGS = ".claude/settings.local.json";
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* The outputs are made from the
|
|
58
|
+
* The outputs are made from the agentic instructions: a checkout makes its own with `voss baba agents sync` (a landing does),
|
|
59
59
|
* and git keeps none of them. Voss's lines of the project's .gitignore say so.
|
|
60
60
|
*/
|
|
61
61
|
export const ignoreLines = (): string[] => [
|
|
62
|
-
"# voss makes these from .baba/
|
|
62
|
+
"# voss makes these from .baba/agents: `voss baba agents sync` writes them, git keeps none",
|
|
63
63
|
...harnesses.flatMap((h) => [`/${files[h].instructions}`, `/${files[h].skills}/`]),
|
|
64
64
|
`/${CLAUDE_SETTINGS}`,
|
|
65
65
|
];
|
|
@@ -108,7 +108,7 @@ export function compile(input: CompileInput): Compiled {
|
|
|
108
108
|
const perms = st.claude.permissions;
|
|
109
109
|
if (perms.length) lists.push({ at: ["permissions", "allow"], values: [...new Set(perms)] });
|
|
110
110
|
const byEvent = new Map<string, unknown[]>();
|
|
111
|
-
for (const hk of input.manifest.
|
|
111
|
+
for (const hk of input.manifest.instructions.hooks) {
|
|
112
112
|
const command = `${input.program} ${hk.action} @- --hook ${hk.on}${hk.strict ? " --strict=true" : ""}`;
|
|
113
113
|
const entry = { ...(hk.match ? { matcher: hk.match } : {}), hooks: [{ type: "command", command }] };
|
|
114
114
|
if (!byEvent.has(hk.on)) byEvent.set(hk.on, []);
|
|
@@ -136,10 +136,10 @@ function instructionFile(input: CompileInput): string {
|
|
|
136
136
|
const prog = input.program;
|
|
137
137
|
const m = input.manifest;
|
|
138
138
|
const lines = [
|
|
139
|
-
`<!-- Generated by ${prog} from the baba's
|
|
139
|
+
`<!-- Generated by ${prog} from the baba's agentic instructions. Do not edit: a hand edit is held and never overwritten. Edit the agentic instructions under .baba/agents/; they sync. -->`,
|
|
140
140
|
`# ${input.title ?? prog}`,
|
|
141
141
|
];
|
|
142
|
-
const context = m.
|
|
142
|
+
const context = m.instructions.context?.trim();
|
|
143
143
|
if (context) lines.push("", context);
|
|
144
144
|
lines.push(
|
|
145
145
|
"", "## This baba", "",
|
|
@@ -172,7 +172,7 @@ function instructionFile(input: CompileInput): string {
|
|
|
172
172
|
...Object.entries(m.queries).filter(([, q]) => !VOSS.has(q.owner)).map(([n, q]) => `| \`${prog} ${[n, ...usage(q.args, q.positional)].join(" ")}\` | query | ${q.owner} | ${system(q.summary)} |`),
|
|
173
173
|
);
|
|
174
174
|
}
|
|
175
|
-
lines.push("", "Voss adds two systems to this baba. `
|
|
175
|
+
lines.push("", "Voss adds two systems to this baba. `agents` compiles what the baba tells agents, its context, skills and hooks, each an .mdx file under `.baba/agents/`, into each harness's files: `CLAUDE.md`, `AGENTS.md`, the skills, Claude Code's permissions and hooks. Those are generated: edit the agentic instructions, never the outputs. `maker` keeps the specs, each system's `systems/NAME/spec.ts` beside it and the whole baba's `.baba/spec.ts`, each a seeded state with its scenarios and the adapters that play its outside, proved under `bun test` by `.baba/spec.test.ts`; `spec` lists them with their verdicts, `spec-run` runs a state's scenarios headless so the verdicts are remade, and the Maker app runs them live, where `spec-step` and `spec-read` reach the state a person has open.");
|
|
176
176
|
const skills = skillsOf(m).map((sk) => sk.name);
|
|
177
177
|
if (skills.length) lines.push("", `Skills: ${skills.join(", ")}.`);
|
|
178
178
|
return lines.join("\n") + "\n";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// What a baba tells agent harnesses, as data: its
|
|
1
|
+
// What a baba tells agent harnesses, as data: its agentic instructions rendered into
|
|
2
2
|
// the manifest; the kernel renders the files from it.
|
|
3
3
|
import type { Manifest } from "../ecs/baba.ts";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ export interface SkillManifest { name: string; description: string; body: string
|
|
|
8
8
|
/** The skills of a manifest, by name, each checked to name actions and queries that exist. */
|
|
9
9
|
export function skillsOf(m: Manifest): SkillManifest[] {
|
|
10
10
|
const out: SkillManifest[] = [];
|
|
11
|
-
for (const [name, s] of Object.entries(m.
|
|
11
|
+
for (const [name, s] of Object.entries(m.instructions.skills).sort(([a], [b]) => a.localeCompare(b))) {
|
|
12
12
|
for (const u of s.uses ?? []) if (!(u in m.actions) && !(u in m.queries)) throw new Error(`skill ${name} uses ${u}, which is not an action or query of the baba`);
|
|
13
13
|
out.push({ name, description: s.description, body: s.body, uses: [...(s.uses ?? [])] });
|
|
14
14
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
// wrote, which carries the baba's
|
|
1
|
+
// Instructions with the baba down: the shell reads the manifest the last load
|
|
2
|
+
// wrote, which carries the baba's agentic instructions rendered and its harness, and
|
|
3
3
|
// runs the same compiler and the same plan the system runs.
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { lstat, mkdir, rename, rm, rmdir } from "node:fs/promises";
|
|
@@ -32,7 +32,7 @@ async function look(root: string, path: string): Promise<Seen> {
|
|
|
32
32
|
|
|
33
33
|
async function writeAtomic(path: string, text: string): Promise<void> {
|
|
34
34
|
await mkdir(dirname(path), { recursive: true });
|
|
35
|
-
const tmp = join(dirname(path), `.
|
|
35
|
+
const tmp = join(dirname(path), `.agents-${crypto.randomUUID()}`);
|
|
36
36
|
try {
|
|
37
37
|
await Bun.write(tmp, text);
|
|
38
38
|
await rename(tmp, path);
|
|
@@ -44,7 +44,10 @@ async function writeAtomic(path: string, text: string): Promise<void> {
|
|
|
44
44
|
|
|
45
45
|
/** What the compiler takes for the project at root. */
|
|
46
46
|
export function sources(_root: string, manifest: Manifest, title?: string): CompileInput {
|
|
47
|
-
|
|
47
|
+
// A manifest an older voss wrote names the instructions `promptware`: read as they are.
|
|
48
|
+
const older = (manifest as unknown as { promptware?: Manifest["instructions"] }).promptware;
|
|
49
|
+
const m = manifest.instructions || !older ? manifest : { ...manifest, instructions: older };
|
|
50
|
+
return { program: "voss baba", ...(title !== undefined ? { title } : {}), manifest: m };
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
/** Compiles and plans against the project at root. Writes nothing. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Compiled
|
|
1
|
+
// Compiled agentic instructions onto disk, owning only what it wrote. The state file
|
|
2
2
|
// records the sha256 of every output the compiler last wrote, and of its
|
|
3
3
|
// part of a shared file; comparing it with the files gives each output a
|
|
4
4
|
// status:
|
|
@@ -25,7 +25,7 @@ export type Status = "current" | "stale" | "missing" | "held" | "foreign";
|
|
|
25
25
|
export type Action = "write" | "remove";
|
|
26
26
|
|
|
27
27
|
/** Relative to the project root. */
|
|
28
|
-
export const statePath = ".baba/.voss/
|
|
28
|
+
export const statePath = ".baba/.voss/agents.json";
|
|
29
29
|
|
|
30
30
|
/** What the compiler last wrote: output path to the hash of its text, or of its part of a shared file, and those parts. */
|
|
31
31
|
export interface Written {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// The
|
|
2
|
-
// baba's
|
|
1
|
+
// The agents system: voss gives it to every baba. The know-how is the
|
|
2
|
+
// baba's agentic instructions, rendered into the manifest with its harness
|
|
3
3
|
// declaration; this system's state is the outputs' hashes; its effects
|
|
4
4
|
// compile the know-how for each harness and write the files through the
|
|
5
5
|
// kernel. The system is made per baba, given the manifest of the
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { effect, manifestOf, resource, baba, nameOf, type Manifest, type Baba } from "../ecs/baba.ts";
|
|
8
8
|
import { system, type StateOf } from "../ecs/system.ts";
|
|
9
9
|
import { s } from "../kernel/schema.ts";
|
|
10
|
-
import type { Prompt } from "../
|
|
10
|
+
import type { Prompt } from "../instructions/index.ts";
|
|
11
11
|
import type { Fs } from "../kernel/context.ts";
|
|
12
12
|
import type { Value } from "../ecs/state.ts";
|
|
13
13
|
import maker from "../maker/system.ts";
|
|
@@ -65,9 +65,9 @@ const writtenRow = s.object({ path: s.string(), hash: s.string(), harness: s.str
|
|
|
65
65
|
* nothing. Its rows are resources, not entities: voss's own system takes no
|
|
66
66
|
* ids from the baba's sequence and adds nothing to its entities.
|
|
67
67
|
*/
|
|
68
|
-
export const
|
|
69
|
-
//
|
|
70
|
-
name: "
|
|
68
|
+
export const agentsSystem = (manifest: () => Manifest | null) => system({
|
|
69
|
+
// agentic instructions, declared: the same for every baba; what it compiles is the manifest each implementation is given.
|
|
70
|
+
name: "agents",
|
|
71
71
|
model: {
|
|
72
72
|
/** One row per output of the last plan: its hash on disk then, its harness, its status and what a sync does there. */
|
|
73
73
|
pwWritten: resource(s.array(writtenRow), []),
|
|
@@ -105,9 +105,9 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
105
105
|
},
|
|
106
106
|
}),
|
|
107
107
|
},
|
|
108
|
-
}, (
|
|
109
|
-
type W = StateOf<typeof
|
|
110
|
-
const { pwWritten, pwStatus, pwScan, pwWrite } =
|
|
108
|
+
}, (agents, p) => {
|
|
109
|
+
type W = StateOf<typeof agents>;
|
|
110
|
+
const { pwWritten, pwStatus, pwScan, pwWrite } = agents;
|
|
111
111
|
const setIf = <K extends typeof pwWritten | typeof pwStatus>(w: W, r: K, v: Value<K>) => { if (!same(w.get(r), v)) w.set(r, v); };
|
|
112
112
|
|
|
113
113
|
/** The outputs the state compiles to and their key; null before the baba has a manifest. */
|
|
@@ -148,7 +148,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
148
148
|
},
|
|
149
149
|
}),
|
|
150
150
|
|
|
151
|
-
// What the baba's
|
|
151
|
+
// What the baba's agentic instructions compiles to, planned when it changed, written when the harness says auto or a sync asked.
|
|
152
152
|
p.step("plan", (w) => {
|
|
153
153
|
const st = w.get(pwStatus);
|
|
154
154
|
if (w.running(pwWrite) || !st.scanned) return;
|
|
@@ -167,7 +167,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
167
167
|
});
|
|
168
168
|
}),
|
|
169
169
|
|
|
170
|
-
p.action("
|
|
170
|
+
p.action("agents-sync", {
|
|
171
171
|
summary: "compile the know-how for each harness and write the files now",
|
|
172
172
|
result: s.object({ asked: s.boolean(), outputs: s.array(writtenRow) }),
|
|
173
173
|
run: (w) => {
|
|
@@ -176,7 +176,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
176
176
|
},
|
|
177
177
|
}),
|
|
178
178
|
|
|
179
|
-
p.query("
|
|
179
|
+
p.query("agents-plan", {
|
|
180
180
|
summary: "each output of each harness: its status and what a sync does there",
|
|
181
181
|
result: s.object({ auto: s.boolean(), lastSync: s.nullable(s.integer()), syncs: s.integer(), pending: s.integer(), error: s.nullable(s.string()), outputs: s.array(writtenRow) }),
|
|
182
182
|
read: (w) => {
|
|
@@ -187,7 +187,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
187
187
|
];
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
-
/** What voss tells agents in every baba, after the baba's own
|
|
190
|
+
/** What voss tells agents in every baba, after the baba's own agentic instructions: its skills. The word on the two systems it adds is the compiler's, in the shell of the context file. */
|
|
191
191
|
const voss: readonly Prompt[] = guide;
|
|
192
192
|
|
|
193
193
|
/** Voss's own systems, by identity: filtered from what a project's docs and tables show. */
|
|
@@ -195,20 +195,20 @@ export const ours = new WeakSet<object>();
|
|
|
195
195
|
export const isOurs = (c: object) => ours.has(c);
|
|
196
196
|
|
|
197
197
|
/** The baba with voss's own systems after its own: what the runtime runs. Every baba gets the shell, the desktop system, over the apps it declares. */
|
|
198
|
-
export function
|
|
198
|
+
export function withInstructions(v: Baba): Baba {
|
|
199
199
|
if (v.systems.some((c) => ours.has(c))) return v;
|
|
200
|
-
for (const name of ["
|
|
200
|
+
for (const name of ["agents", "maker"]) if (v.systems.some((c) => nameOf(c) === name)) throw new Error(`a system of this baba is named ${name}, which voss gives every baba; name it otherwise`);
|
|
201
201
|
// A baba from before the shell was voss's brings its own desktop system, and keeps it; one system or the other, never both.
|
|
202
202
|
const own = v.systems.find((c) => nameOf(c) === "desktop");
|
|
203
203
|
if (own && v.apps.length) throw new Error("this baba declares apps and also has a system named desktop; the shell is voss's now: drop the system and keep baba({ apps })");
|
|
204
204
|
let m: Manifest | null = null;
|
|
205
|
-
const pw =
|
|
205
|
+
const pw = agentsSystem(() => m);
|
|
206
206
|
const catalog = own ? v.apps : catalogOf(v.apps);
|
|
207
207
|
const shell = own ? null : desktop({ apps: catalog });
|
|
208
208
|
ours.add(pw);
|
|
209
209
|
ours.add(maker);
|
|
210
210
|
if (shell) ours.add(shell);
|
|
211
|
-
const composed = baba({ systems: [...v.systems, ...(shell ? [shell] : []), pw, maker], apps: catalog,
|
|
211
|
+
const composed = baba({ systems: [...v.systems, ...(shell ? [shell] : []), pw, maker], apps: catalog, instructions: [...v.instructions, ...voss], harness: v.harness, ...(v.tick !== null ? { tick: v.tick } : {}) });
|
|
212
212
|
m = manifestOf(composed);
|
|
213
213
|
return composed;
|
|
214
214
|
}
|
package/src/baba/check.ts
CHANGED
|
@@ -81,10 +81,10 @@ export async function check(p: Project, stdout: (s: string) => void, stderr: (s:
|
|
|
81
81
|
}
|
|
82
82
|
if (err) stderr(err);
|
|
83
83
|
}
|
|
84
|
-
const mdx = [...new Bun.Glob("
|
|
84
|
+
const mdx = [...new Bun.Glob("agents/**/*.mdx").scanSync({ cwd: p.baba })];
|
|
85
85
|
if (mdx.length) {
|
|
86
|
-
try { await bundleProject(p); stdout("
|
|
87
|
-
catch (error) { failed = true; stderr(`
|
|
86
|
+
try { await bundleProject(p); stdout("agentic instructions MDX: clean\n"); }
|
|
87
|
+
catch (error) { failed = true; stderr(`agentic instructions MDX: ${String(error)}\n`); }
|
|
88
88
|
}
|
|
89
89
|
return failed ? 1 : 0;
|
|
90
90
|
}
|
package/src/baba/config.ts
CHANGED
|
@@ -38,7 +38,7 @@ const note = "Written by voss on every load. Edit .baba/tsconfig.json to extend
|
|
|
38
38
|
export function editorConfig(): string {
|
|
39
39
|
return JSON.stringify({
|
|
40
40
|
"//": note,
|
|
41
|
-
// JSX is React's unless a file says otherwise:
|
|
41
|
+
// JSX is React's unless a file says otherwise: agentic instructions, TSX that renders to Markdown, names its runtime with a pragma.
|
|
42
42
|
// No alias here: the project's tsconfig.json, which extends this, states `@/` relative to itself, and tools that read it resolve it there.
|
|
43
43
|
compilerOptions: { lib: ["ESNext"], ...options, jsx: "react-jsx", jsxImportSource: "react" },
|
|
44
44
|
include: ["guard.d.ts", "assets.d.ts", "../**/*.ts"],
|
|
@@ -205,7 +205,7 @@ export function assetDeclarations(): string {
|
|
|
205
205
|
"// Written by voss on every load: what a baba imports besides code.",
|
|
206
206
|
"// Text: `import guide from \"./guide.md\" with { type: \"text\" }`.",
|
|
207
207
|
...["md", "txt"].map((ext) => `declare module "*.${ext}" {\n const text: string;\n export default text;\n}`),
|
|
208
|
-
`declare module "*.mdx" { const prompt: import("babavoss/
|
|
208
|
+
`declare module "*.mdx" { const prompt: import("babavoss/instructions").Prompt; export default prompt; }`,
|
|
209
209
|
"// Every spec of the baba, found where it lives (systems/NAME/spec.ts, spec.ts): what the Maker's page runs.",
|
|
210
210
|
`declare module "babavoss:specs" { const specs: import("babavoss/spec").Spec<any, any>[]; export default specs; }`,
|
|
211
211
|
"// A stylesheet, bundled with the view and attached to its windows: `import \"./view.css\"`.",
|
|
@@ -242,6 +242,9 @@ export async function writeEditorConfig(p: Project): Promise<string> {
|
|
|
242
242
|
* tsconfig.json and .gitignore that pointed there follow.
|
|
243
243
|
*/
|
|
244
244
|
export async function moveOldDir(p: Project): Promise<void> {
|
|
245
|
+
// The agentic instructions lived in .baba/promptware, their record in .voss/promptware.json: moved once, by name.
|
|
246
|
+
if (existsSync(join(p.baba, "promptware")) && !existsSync(join(p.baba, "agents"))) await rename(join(p.baba, "promptware"), join(p.baba, "agents"));
|
|
247
|
+
if (existsSync(join(p.baba, VOSS, "promptware.json")) && !existsSync(join(p.baba, VOSS, "agents.json"))) await rename(join(p.baba, VOSS, "promptware.json"), join(p.baba, VOSS, "agents.json"));
|
|
245
248
|
const old = join(p.baba, "state");
|
|
246
249
|
if (!existsSync(old) || existsSync(join(p.baba, VOSS))) return;
|
|
247
250
|
await rename(old, join(p.baba, VOSS));
|
package/src/baba/init.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// voss init: a .baba in a directory, its dependencies installed. One entry,
|
|
2
|
-
// one interface, one system to start from, and the
|
|
2
|
+
// one interface, one system to start from, and the agentic instructions: what the baba tells
|
|
3
3
|
// agents, as code, versioned with the project.
|
|
4
4
|
import { basename, join, resolve } from "node:path";
|
|
5
5
|
import { mkdir } from "node:fs/promises";
|
|
@@ -42,7 +42,7 @@ export async function init(dir: string, o: InitOptions): Promise<{ name: string;
|
|
|
42
42
|
await Bun.write(join(baba, "systems", "hello", "index.ts"), systemFile("hello"));
|
|
43
43
|
await Bun.write(join(baba, "systems", "hello", "spec.ts"), systemSpecFile("hello"));
|
|
44
44
|
await Bun.write(join(baba, "spec.test.ts"), specRunnerFile);
|
|
45
|
-
await Bun.write(join(baba, "
|
|
45
|
+
await Bun.write(join(baba, "agents", "context.mdx"), contextFile(o.title ?? name));
|
|
46
46
|
if (o.install !== false) await install(baba);
|
|
47
47
|
return { name, dir };
|
|
48
48
|
}
|
|
@@ -55,34 +55,29 @@ export async function install(baba: string): Promise<void> {
|
|
|
55
55
|
if (code !== 0) throw new Error(`bun install failed in ${baba}: ${(err || out).trim().split("\n").slice(-5).join("; ")}`);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/** The
|
|
59
|
-
export const
|
|
58
|
+
/** The agentic instructions: what the baba tells agents, one .mdx per document under .baba/agents/. The contract is not here: agents get it at `voss baba`. */
|
|
59
|
+
export const contextFile = (title: string) => `export const prompt = { kind: "context" };
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// Markdown and compiled by voss into CLAUDE.md and the skills.
|
|
64
|
-
export default [
|
|
65
|
-
context(md\`
|
|
66
|
-
${title.replaceAll("\\", "\\\\").replaceAll("`", "\\`").replaceAll("$", "\\$")}: say here what this project is, in a sentence or two.
|
|
61
|
+
{/* What this baba tells agents: its section of the context file. A skill is another .mdx beside this one,
|
|
62
|
+
with export const prompt = { kind: "skill", name, description, uses }. A hook is hooks: [{ on, match, action }] here. */}
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
${title.replaceAll("{", "\\{").replaceAll("}", "\\}").replaceAll("<", "\\<")}: say here what this project is, in a sentence or two.
|
|
69
65
|
|
|
70
|
-
|
|
66
|
+
How to work on it: the conventions, the checks to run before a commit, what not to touch.
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
## Rules
|
|
69
|
+
|
|
70
|
+
- Keep the tests green on every commit.
|
|
75
71
|
`;
|
|
76
72
|
|
|
77
|
-
/** The entry: the baba, its systems listed
|
|
73
|
+
/** The entry: the baba, its systems listed; `harness` for which harnesses get the files. The agentic instructions are the .mdx files under agents/, read by voss. */
|
|
78
74
|
export const entry = `import { baba } from "babavoss";
|
|
79
75
|
import { kit } from "babavoss/kit";
|
|
80
76
|
import hello from "./systems/hello/index.ts";
|
|
81
|
-
import promptware from "./promptware/index.ts";
|
|
82
77
|
|
|
83
78
|
// kit keeps the baba's components, under ui/, as objects: \`voss baba ui\` lists them, \`ui-add\` brings more from shadcn.
|
|
84
79
|
// The apps voss's shell offers; interface.tsx gives each its component.
|
|
85
|
-
export default baba({ systems: [hello, kit], apps: [{ key: "hello", title: "Hello", icon: "hand" }]
|
|
80
|
+
export default baba({ systems: [hello, kit], apps: [{ key: "hello", title: "Hello", icon: "hand" }] });
|
|
86
81
|
`;
|
|
87
82
|
|
|
88
83
|
/** A system: a data model, one action, one query, written as one expression: the declaration, then its pieces made from it. */
|
package/src/baba/node.ts
CHANGED
|
@@ -60,7 +60,7 @@ const ACTIVATE_MS = 10_000;
|
|
|
60
60
|
const QUIET_MS = 300;
|
|
61
61
|
/** How often a reload that waits for a job asks again. */
|
|
62
62
|
const WAIT_MS = 500;
|
|
63
|
-
// What changes under .baba without changing the code: voss's own files and the dependencies.
|
|
63
|
+
// What changes under .baba without changing the code: voss's own files and the dependencies. Instructions is code: a change there reloads.
|
|
64
64
|
const IGNORED = [".voss", "node_modules"];
|
|
65
65
|
|
|
66
66
|
/** Makes the transport a Node loads a project over. The worker is the first; a runtime process is next. */
|
|
@@ -188,7 +188,7 @@ export class Node {
|
|
|
188
188
|
throw err;
|
|
189
189
|
});
|
|
190
190
|
fresh.manifest = loaded.manifest;
|
|
191
|
-
// The contract outlives the run: discovery and
|
|
191
|
+
// The contract outlives the run: discovery and agentic instructions read it with the baba down.
|
|
192
192
|
await mkdir(join(this.project.baba, ".voss"), { recursive: true })
|
|
193
193
|
.then(() => Bun.write(manifestPath(this.project), JSON.stringify(loaded.manifest, null, 2) + "\n"))
|
|
194
194
|
.catch((err) => this.log(`${this.name}: the manifest was not written: ${err instanceof Error ? err.message : String(err)}`));
|
package/src/baba/worker.ts
CHANGED
|
@@ -5,7 +5,8 @@ import { plugin as bunPlugin } from "bun";
|
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import type { ToRuntime, FromRuntime } from "../transport/messages.ts";
|
|
7
7
|
import type { Snapshot } from "../ecs/state.ts";
|
|
8
|
-
import { isBaba } from "../ecs/baba.ts";
|
|
8
|
+
import { isBaba, instructed } from "../ecs/baba.ts";
|
|
9
|
+
import type { Prompt } from "../instructions/index.ts";
|
|
9
10
|
import { openProject } from "./project.ts";
|
|
10
11
|
import { readEnv } from "../runtime/env.ts";
|
|
11
12
|
import { Harness } from "../runtime/harness.ts";
|
|
@@ -20,9 +21,9 @@ const names: Record<string, string> = {
|
|
|
20
21
|
"babavoss": join(root, "index.ts"),
|
|
21
22
|
"babavoss/desktop": join(root, "src/desktop/index.ts"),
|
|
22
23
|
"babavoss/kit": join(root, "src/kit/index.ts"),
|
|
23
|
-
"babavoss/
|
|
24
|
-
"babavoss/
|
|
25
|
-
"babavoss/
|
|
24
|
+
"babavoss/instructions": join(root, "src/instructions/index.ts"),
|
|
25
|
+
"babavoss/instructions/jsx-runtime": join(root, "src/instructions/jsx-runtime.ts"),
|
|
26
|
+
"babavoss/instructions/jsx-dev-runtime": join(root, "src/instructions/jsx-dev-runtime.ts"),
|
|
26
27
|
"babavoss/test": join(root, "src/test/index.ts"),
|
|
27
28
|
"babavoss/bench": join(root, "src/bench/index.ts"),
|
|
28
29
|
};
|
|
@@ -45,7 +46,14 @@ self.onmessage = async (e: MessageEvent<ToWorker>) => {
|
|
|
45
46
|
const project = await openProject(m.dir);
|
|
46
47
|
const mod = (await import(`${project.entry}?t=${Date.now()}`)) as { default?: unknown };
|
|
47
48
|
if (!isBaba(mod.default)) throw new Error(`${project.entry}: the default export is not a baba(); export default baba({ systems: [...] })`);
|
|
48
|
-
|
|
49
|
+
// The agentic instructions: every .mdx under .baba/agents/, in name order, as the builder's bundle has them.
|
|
50
|
+
const docs: Prompt[] = [];
|
|
51
|
+
for (const f of [...new Bun.Glob("agents/*.mdx").scanSync({ cwd: project.baba })].sort()) {
|
|
52
|
+
const d = (await import(join(project.baba, f))) as { default?: Prompt }; // the plugin reads .mdx by its suffix: no query here
|
|
53
|
+
if (!d.default || typeof d.default !== "object" || !("kind" in d.default)) throw new Error(`${f}: not an instruction document; export const prompt = { kind: "context" | "skill", … }`);
|
|
54
|
+
docs.push(d.default);
|
|
55
|
+
}
|
|
56
|
+
h.load(instructed(mod.default, docs), { name: project.name, dir: project.dir, baba: project.baba }, await readEnv(project.baba), m.snapshot, m.prepare);
|
|
49
57
|
} catch (err) {
|
|
50
58
|
post({ type: "failed", error: err instanceof Error ? err.message : String(err) });
|
|
51
59
|
}
|
package/src/build/mdx.ts
CHANGED
|
@@ -6,28 +6,28 @@ import { forbiddenModules, advice } from "./guard.ts";
|
|
|
6
6
|
function promptExport() {
|
|
7
7
|
return (tree: any) => {
|
|
8
8
|
const metadata = tree.body.some((n: any) => n.type === "ExportNamedDeclaration" && n.declaration?.type === "VariableDeclaration" && n.declaration.declarations.some((d: any) => d.id.name === "prompt"));
|
|
9
|
-
if (!metadata) throw new Error('MDX
|
|
9
|
+
if (!metadata) throw new Error('an MDX instruction file needs an exported prompt metadata object');
|
|
10
10
|
const exported = tree.body.find((n: any) => n.type === "ExportDefaultDeclaration");
|
|
11
11
|
if (!exported || exported.declaration.type !== "FunctionDeclaration") throw new Error("MDX must export a document component");
|
|
12
12
|
const component = exported.declaration;
|
|
13
13
|
const id = (name: string) => ({ type: "Identifier", name });
|
|
14
14
|
const literal = (value: string) => ({ type: "Literal", value });
|
|
15
15
|
tree.body[tree.body.indexOf(exported)] = component;
|
|
16
|
-
tree.body.push({ type: "ImportDeclaration", source: literal("babavoss/
|
|
16
|
+
tree.body.push({ type: "ImportDeclaration", source: literal("babavoss/instructions"), specifiers: [{ type: "ImportSpecifier", imported: id("mdxDocument"), local: id("$promptDocument") }] });
|
|
17
17
|
tree.body.push({ type: "ExportDefaultDeclaration", declaration: { type: "CallExpression", callee: id("$promptDocument"), arguments: [id("prompt"), id(component.id.name)], optional: false } });
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export async function compilePromptMdx(value: string, path: string): Promise<string> {
|
|
22
22
|
const result = String(await compile({ value, path }, {
|
|
23
|
-
jsxImportSource: "babavoss/
|
|
23
|
+
jsxImportSource: "babavoss/instructions", format: "mdx", development: false,
|
|
24
24
|
remarkPlugins: [gfm], recmaPlugins: [promptExport],
|
|
25
25
|
}));
|
|
26
26
|
// Bun's bun-target builtin imports can bypass onResolve: apply the same guard after MDX compilation.
|
|
27
27
|
for (const item of new Bun.Transpiler({ loader: "js" }).scanImports(result)) {
|
|
28
28
|
if (forbiddenModules[item.path]) throw new Error(`${path} imports ${item.path}; ${advice(forbiddenModules[item.path]!)}`);
|
|
29
29
|
}
|
|
30
|
-
if (/\b(?:with|assert)\s*\{\s*type\s*:\s*["']macro["']\s*\}/.test(result)) throw new Error(`${path}: macros are not allowed in
|
|
30
|
+
if (/\b(?:with|assert)\s*\{\s*type\s*:\s*["']macro["']\s*\}/.test(result)) throw new Error(`${path}: macros are not allowed in agentic instructions`);
|
|
31
31
|
return result;
|
|
32
32
|
}
|
|
33
33
|
|