babavoss 0.12.6 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/babavoss-web.js +4 -1
- package/gui/{chunk-9gmmm54v.js → chunk-qgzmajan.js} +619 -574
- package/gui/gui.js +1538 -277
- package/gui/theme.css +2007 -1128
- package/index.ts +1 -1
- package/package.json +5 -5
- package/src/{promptware → agents}/compile.ts +23 -31
- package/src/{promptware → agents}/define.ts +2 -2
- package/src/{promptware → agents}/disk.ts +7 -4
- package/src/{promptware → agents}/sync.ts +2 -2
- package/src/{promptware → agents}/system.ts +17 -17
- package/src/baba/check.ts +3 -3
- package/src/baba/config.ts +5 -2
- package/src/baba/init.ts +13 -18
- package/src/baba/node.ts +2 -2
- package/src/baba/project.ts +2 -2
- package/src/baba/requirements.ts +39 -0
- package/src/baba/setup.ts +30 -0
- package/src/baba/worker.ts +13 -5
- package/src/build/mdx.ts +4 -4
- package/src/build/project.ts +6 -1
- package/src/build/views.ts +3 -3
- package/src/desktop/desktop.css +35 -39
- package/src/desktop/index.ts +9 -7
- package/src/desktop/keys.ts +153 -0
- package/src/desktop/view.tsx +174 -130
- package/src/door/core.ts +2 -2
- package/src/ecs/baba.ts +27 -17
- package/src/generated/build.ts +1 -1
- package/src/gui/gui.tsx +21 -31
- package/src/gui/index.ts +6 -2
- package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
- package/src/gui/levels.tsx +241 -59
- package/src/gui/lockup.ts +11 -0
- package/src/gui/settings.tsx +241 -0
- package/src/gui/setup.tsx +77 -0
- package/src/gui/theme.css +205 -58
- package/src/gui/theme.ts +9 -5
- package/src/gui/voss-settings.ts +48 -0
- package/src/gui/wizard.tsx +17 -40
- package/src/guide/add-a-desktop.mdx +7 -7
- package/src/guide/index.ts +4 -4
- package/src/guide/write-a-system.mdx +3 -3
- package/src/guide/{write-promptware.mdx → write-agentic-instructions.mdx} +17 -24
- package/src/http/server.ts +140 -17
- package/src/{prompt → instructions}/evals.ts +1 -1
- package/src/{prompt → instructions}/index.ts +9 -8
- package/src/{prompt → instructions}/jsx-runtime.ts +3 -3
- package/src/mcp/main.ts +3 -3
- package/src/mcp/tools.ts +4 -4
- package/src/runtime/harness.ts +2 -2
- package/src/server/edge.ts +31 -3
- package/src/server/main.ts +2 -1
- package/src/server/messages.ts +47 -7
- package/src/shell/run.ts +18 -18
- package/src/spec/index.ts +1 -1
- package/src/web/core.tsx +2 -2
- package/src/web/index.tsx +1 -1
- package/src/desktop/bob.ts +0 -76
- /package/src/{promptware → agents}/markdown.d.ts +0 -0
- /package/src/{prompt → instructions}/jsx-dev-runtime.ts +0 -0
- /package/src/{prompt → instructions}/mdx.d.ts +0 -0
package/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.1",
|
|
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, []);
|
|
@@ -131,32 +131,25 @@ const quoted = (v: unknown) => `'${JSON.stringify(v).replaceAll("'", `'\\''`)}'`
|
|
|
131
131
|
/** The CLI line of a call with a literal example of its args. */
|
|
132
132
|
const cliLine = (prog: string, name: string, args: Json) => `${prog} ${name} ${quoted(exampleOf(args))}`;
|
|
133
133
|
|
|
134
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* The instruction file: the title, then voss's own section, short, what a baba is, that the agent is its author and
|
|
136
|
+
* which skills say how, and how the baba is reached by MCP and at the shell, with one real example; then the baba's
|
|
137
|
+
* own context, as it wrote it.
|
|
138
|
+
*/
|
|
135
139
|
function instructionFile(input: CompileInput): string {
|
|
136
140
|
const prog = input.program;
|
|
137
141
|
const m = input.manifest;
|
|
142
|
+
const skills = skillsOf(m).map((sk) => sk.name);
|
|
138
143
|
const lines = [
|
|
139
|
-
`<!-- Generated by ${prog} from the baba's
|
|
144
|
+
`<!-- 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
145
|
`# ${input.title ?? prog}`,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (context) lines.push("", context);
|
|
144
|
-
lines.push(
|
|
145
|
-
"", "## This baba", "",
|
|
146
|
-
"A baba is this project's own program: systems and apps over one state of entities and resources, moved by the actions its systems declare and read by their queries; the apps show it to people. Its code is under `.baba/`, and voss runs it.",
|
|
146
|
+
"", "## Baba Voss", "",
|
|
147
|
+
"This project has a baba: its own program, under `.baba/`, run by voss. A baba is systems over one state of entities and resources, and apps that show it to people. Each system declares the components it owns, the actions that move it and the queries that read it, and reaches outside only through effects; a spec beside it proves it. Voss serves the baba from a service on loopback, `http://localhost:47802/` by default; `voss service status` gives the configured URL.",
|
|
147
148
|
"",
|
|
148
|
-
`
|
|
149
|
+
`You are this baba's author. Use it through its contract, and change it when it falls short: a capability it lacks is a system to write, a mistake an agent made is a sentence for its instructions. The skills say how, read each when its description fits: ${skills.join(", ")}.`,
|
|
149
150
|
"",
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`- \`${prog} run\` sends several calls in one turn: lines of \`{"NAME": args}\`, \`"$0.entity"\` to reference an earlier answer.`,
|
|
153
|
-
`- \`${prog} state\` reads the state; \`${prog} raw …\` writes it, only when no action does it. \`--help\` after any word explains it.`,
|
|
154
|
-
`- \`--key K\` on an action makes a retry safe: the baba remembers the answer for a day and answers it again without running. A secret is never on the command line: \`--token @-\` reads it from stdin. An answer over 20 000 characters goes to \`.baba/.voss/out/\` and the line says where.`,
|
|
155
|
-
"",
|
|
156
|
-
"If your harness has this baba's MCP server (`voss baba mcp`), its `manifest`, `call`, `read`, `follow`, `watch` and `state` tools are the same six operations: prefer them.",
|
|
157
|
-
"",
|
|
158
|
-
"Voss serves this baba from a service on loopback, `http://localhost:47802/` by default. `voss baba start` opens it there, installing the service if need be; `voss service status` gives the configured URL and whether it runs. Never guess another port: read the status.",
|
|
159
|
-
);
|
|
151
|
+
`The contract is live; never rely on a copied list. By MCP, when your harness has this baba's server (\`voss baba mcp\`): \`manifest\`, \`call\`, \`read\`, \`follow\`, \`watch\` and \`state\`; prefer them. At the shell: \`${prog}\` lists every action and query with an example, \`${prog} NAME '{"arg": value}'\` calls one, \`${prog} NAME --schema\` gives its schemas, \`${prog} state N\` reads an entity an answer named and \`${prog} follow N [--until COMPONENT]\` waits on it, \`${prog} run\` sends several calls, \`${prog} state\` reads the state and \`${prog} raw …\` writes it, only when no action does. Every answer is one line of JSON; an error is one line on stderr, \`{"error":{"code","message","retry"}}\`, and the exit code says whose: 2 your arguments, 1 it failed, 3 time ran out, 4 gone. \`--key K\` on an action makes a retry safe; a secret is never on the command line, \`--token @-\` reads it from stdin; an answer over 20 000 characters goes to \`.baba/.voss/out/\` and the line says where.`,
|
|
152
|
+
];
|
|
160
153
|
const first = Object.entries(m.actions).find(([, d]) => !VOSS.has(d.owner));
|
|
161
154
|
if (first) {
|
|
162
155
|
const [name, d] = first;
|
|
@@ -172,9 +165,8 @@ function instructionFile(input: CompileInput): string {
|
|
|
172
165
|
...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
166
|
);
|
|
174
167
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (skills.length) lines.push("", `Skills: ${skills.join(", ")}.`);
|
|
168
|
+
const context = m.instructions.context?.trim();
|
|
169
|
+
if (context) lines.push("", context);
|
|
178
170
|
return lines.join("\n") + "\n";
|
|
179
171
|
}
|
|
180
172
|
|
|
@@ -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/project.ts
CHANGED
|
@@ -21,8 +21,8 @@ export interface Project {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const NAME = /^[a-z][a-z0-9-]*$/;
|
|
24
|
-
|
|
25
|
-
export
|
|
24
|
+
import { PAGE_WORDS } from "./requirements.ts";
|
|
25
|
+
export { PAGE_WORDS };
|
|
26
26
|
|
|
27
27
|
interface Pkg { name?: unknown; baba?: unknown; dependencies?: unknown; devDependencies?: unknown }
|
|
28
28
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// What voss needs of a baba before it runs it: a set of properties, each read
|
|
2
|
+
// from the baba's .baba/package.json and written back there by the setup
|
|
3
|
+
// wizard. A folder voss remembers is ready when every one is set and valid;
|
|
4
|
+
// until then it is set up, not run, and the picker opens its wizard in place
|
|
5
|
+
// of its page. A folder with no .baba lacks them all, and setting them up
|
|
6
|
+
// makes its .baba first. For now there is one: the name.
|
|
7
|
+
// Pure, so the page checks a name as the kernel will; reading and writing a
|
|
8
|
+
// folder is setup.ts's.
|
|
9
|
+
|
|
10
|
+
/** The page's own first segments: a baba's address is `/NAME/…`, so no baba is named one of them. */
|
|
11
|
+
export const PAGE_WORDS = new Set(["api", "assets", "view", "ws"]);
|
|
12
|
+
|
|
13
|
+
/** The properties voss needs of every baba, in the order the wizard asks for them. */
|
|
14
|
+
export const requirements = [
|
|
15
|
+
{
|
|
16
|
+
key: "name", title: "Name",
|
|
17
|
+
summary: "The baba's address and its name at the shell: voss serves it at /NAME/, and the CLI reaches it as voss NAME from anywhere.",
|
|
18
|
+
},
|
|
19
|
+
] as const;
|
|
20
|
+
export type Requirement = typeof requirements[number]["key"];
|
|
21
|
+
/** What the wizard has to set: each requirement's value. */
|
|
22
|
+
export type RequirementValues = Partial<Record<Requirement, string>>;
|
|
23
|
+
|
|
24
|
+
const NAME = /^[a-z][a-z0-9-]*$/;
|
|
25
|
+
|
|
26
|
+
/** Why a value will not do for a requirement, or null when it does; `taken` are the names other babas already have. */
|
|
27
|
+
export function requirementError(key: Requirement, value: string, taken: readonly string[] = []): string | null {
|
|
28
|
+
if (key === "name") {
|
|
29
|
+
if (!value) return "A baba needs a name.";
|
|
30
|
+
if (!NAME.test(value)) return "Lowercase letters, digits and dashes, starting with a letter.";
|
|
31
|
+
if (PAGE_WORDS.has(value)) return `/${value}/ is the page's own address; choose another.`;
|
|
32
|
+
if (taken.includes(value)) return `Another baba is already called ${value}.`;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A folder's name as a baba's name: what the wizard offers first. */
|
|
38
|
+
export const nameFrom = (dir: string) => (dir.split("/").filter(Boolean).pop() ?? "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/^[^a-z]+/, "");
|
|
39
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// A folder's standing against what voss requires of a baba, read from its
|
|
2
|
+
// .baba/package.json, and the requirements written back there.
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { exists } from "./project.ts";
|
|
5
|
+
import { nameFrom, requirementError, requirements, type Requirement, type RequirementValues } from "./requirements.ts";
|
|
6
|
+
|
|
7
|
+
/** Where a folder stands: whether it has a .baba, the requirements it lacks, and the values it already has. */
|
|
8
|
+
export interface Setup { dir: string; baba: boolean; missing: Requirement[]; values: RequirementValues; suggested: RequirementValues }
|
|
9
|
+
|
|
10
|
+
/** Reads a folder's .baba/package.json for the requirements; a package.json that does not parse is an error, not a missing value. */
|
|
11
|
+
export async function setupOf(dir: string): Promise<Setup> {
|
|
12
|
+
dir = resolve(dir);
|
|
13
|
+
const file = join(dir, ".baba", "package.json");
|
|
14
|
+
const suggested = { name: nameFrom(dir) };
|
|
15
|
+
if (!(await exists(file))) return { dir, baba: false, missing: requirements.map((r) => r.key), values: {}, suggested };
|
|
16
|
+
const pkg = await Bun.file(file).json() as { name?: unknown; baba?: { name?: unknown } };
|
|
17
|
+
const name = typeof pkg.baba?.name === "string" ? pkg.baba.name : typeof pkg.name === "string" ? pkg.name : undefined;
|
|
18
|
+
const values: RequirementValues = name === undefined ? {} : { name };
|
|
19
|
+
const missing = requirements.map((r) => r.key).filter((k) => values[k] === undefined || requirementError(k, values[k]!) !== null);
|
|
20
|
+
return { dir, baba: true, missing, values, suggested };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Writes the requirements into a .baba that exists, leaving everything else in its package.json as it was. */
|
|
24
|
+
export async function writeRequirements(dir: string, values: RequirementValues): Promise<void> {
|
|
25
|
+
const file = join(resolve(dir), ".baba", "package.json");
|
|
26
|
+
const pkg = await Bun.file(file).json() as Record<string, unknown> & { baba?: Record<string, unknown> };
|
|
27
|
+
const baba = { ...(typeof pkg.baba === "object" && pkg.baba ? pkg.baba : {}) };
|
|
28
|
+
if (values.name !== undefined) baba.name = values.name;
|
|
29
|
+
await Bun.write(file, JSON.stringify({ ...pkg, baba }, null, 2) + "\n");
|
|
30
|
+
}
|
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
|
|