babavoss 0.12.6 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/babavoss-web.js +4 -1
- package/gui/{chunk-9gmmm54v.js → chunk-qgzmajan.js} +619 -574
- package/gui/gui.js +1538 -277
- package/gui/theme.css +2007 -1128
- package/index.ts +1 -1
- package/package.json +5 -5
- package/src/{promptware → agents}/compile.ts +23 -31
- package/src/{promptware → agents}/define.ts +2 -2
- package/src/{promptware → agents}/disk.ts +7 -4
- package/src/{promptware → agents}/sync.ts +2 -2
- package/src/{promptware → agents}/system.ts +17 -17
- package/src/baba/check.ts +3 -3
- package/src/baba/config.ts +5 -2
- package/src/baba/init.ts +13 -18
- package/src/baba/node.ts +2 -2
- package/src/baba/project.ts +2 -2
- package/src/baba/requirements.ts +39 -0
- package/src/baba/setup.ts +30 -0
- package/src/baba/worker.ts +13 -5
- package/src/build/mdx.ts +4 -4
- package/src/build/project.ts +6 -1
- package/src/build/views.ts +3 -3
- package/src/desktop/desktop.css +35 -39
- package/src/desktop/index.ts +9 -7
- package/src/desktop/keys.ts +153 -0
- package/src/desktop/view.tsx +174 -130
- package/src/door/core.ts +2 -2
- package/src/ecs/baba.ts +27 -17
- package/src/generated/build.ts +1 -1
- package/src/gui/gui.tsx +21 -31
- package/src/gui/index.ts +6 -2
- package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
- package/src/gui/levels.tsx +241 -59
- package/src/gui/lockup.ts +11 -0
- package/src/gui/settings.tsx +241 -0
- package/src/gui/setup.tsx +77 -0
- package/src/gui/theme.css +205 -58
- package/src/gui/theme.ts +9 -5
- package/src/gui/voss-settings.ts +48 -0
- package/src/gui/wizard.tsx +17 -40
- package/src/guide/add-a-desktop.mdx +7 -7
- package/src/guide/index.ts +4 -4
- package/src/guide/write-a-system.mdx +3 -3
- package/src/guide/{write-promptware.mdx → write-agentic-instructions.mdx} +17 -24
- package/src/http/server.ts +140 -17
- package/src/{prompt → instructions}/evals.ts +1 -1
- package/src/{prompt → instructions}/index.ts +9 -8
- package/src/{prompt → instructions}/jsx-runtime.ts +3 -3
- package/src/mcp/main.ts +3 -3
- package/src/mcp/tools.ts +4 -4
- package/src/runtime/harness.ts +2 -2
- package/src/server/edge.ts +31 -3
- package/src/server/main.ts +2 -1
- package/src/server/messages.ts +47 -7
- package/src/shell/run.ts +18 -18
- package/src/spec/index.ts +1 -1
- package/src/web/core.tsx +2 -2
- package/src/web/index.tsx +1 -1
- package/src/desktop/bob.ts +0 -76
- /package/src/{promptware → agents}/markdown.d.ts +0 -0
- /package/src/{prompt → instructions}/jsx-dev-runtime.ts +0 -0
- /package/src/{prompt → instructions}/mdx.d.ts +0 -0
package/src/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,
|
package/src/server/edge.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
// same socket, in order. No file, no project, no Node here: only memory and IPC.
|
|
6
6
|
import { isIP } from "node:net";
|
|
7
7
|
import type { Server, ServerWebSocket, WebSocketHandler } from "bun";
|
|
8
|
-
import type { Serve, Described, Failure, Describe, DescribeAnswer, View, ViewAnswer, Call, CallAnswer, Refresh, RefreshAnswer, Restart, RestartAnswer, Browse, BrowseAnswer, Add, AddAnswer } from "./messages.ts";
|
|
8
|
+
import type { Serve, Described, Failure, Describe, DescribeAnswer, View, ViewAnswer, Call, CallAnswer, Refresh, RefreshAnswer, Restart, RestartAnswer, Browse, BrowseAnswer, Add, AddAnswer, Remove, Setup, SetSettings, SettingsAnswer, Rename } from "./messages.ts";
|
|
9
9
|
import type { Raw } from "../ecs/runtime.ts";
|
|
10
10
|
import type { Json } from "../kernel/schema.ts";
|
|
11
11
|
import { frameHeader, resolveInterest, sliceFrame, type Resolved } from "../ecs/codec.ts";
|
|
12
12
|
import { CLOSE, SOCKET_PROTOCOL, type Down, type Interest, type Up } from "./protocol.ts";
|
|
13
13
|
|
|
14
|
-
interface Pairs { describe: [Describe, DescribeAnswer]; view: [View, ViewAnswer]; call: [Call, CallAnswer]; refresh: [Refresh, RefreshAnswer]; restart: [Restart, RestartAnswer]; browse: [Browse, BrowseAnswer]; add: [Add, AddAnswer] }
|
|
14
|
+
interface Pairs { describe: [Describe, DescribeAnswer]; view: [View, ViewAnswer]; call: [Call, CallAnswer]; refresh: [Refresh, RefreshAnswer]; restart: [Restart, RestartAnswer]; browse: [Browse, BrowseAnswer]; add: [Add, AddAnswer]; remove: [Remove, AddAnswer]; setup: [Setup, AddAnswer]; "set-settings": [SetSettings, SettingsAnswer]; rename: [Rename, AddAnswer] }
|
|
15
15
|
type Unnumbered<T> = T extends unknown ? Omit<T, "id"> : never;
|
|
16
16
|
/** Sends a request to the kernel, numbered by the sender, and resolves with its answer. */
|
|
17
17
|
export type Ask = <K extends keyof Pairs>(m: Unnumbered<Pairs[K][0]>) => Promise<Pairs[K][1]>;
|
|
@@ -294,7 +294,35 @@ export function edge(state: Serve, ask: Ask): Edge {
|
|
|
294
294
|
const a = await ask<"add">({ type: "add", dir: body.dir, init: body.init === true, ...(typeof body.name === "string" && body.name ? { name: body.name } : {}), ...(typeof body.title === "string" && body.title ? { title: body.title } : {}) });
|
|
295
295
|
return a.ok ? json(a.value) : failed(a);
|
|
296
296
|
}
|
|
297
|
-
|
|
297
|
+
// /api/projects/remove: forget a folder, its files kept. /api/projects/setup: set what voss requires of the folder's baba, making its .baba first when there is none.
|
|
298
|
+
if (parts[1] === "projects" && (parts[2] === "remove" || parts[2] === "setup") && parts.length === 3) {
|
|
299
|
+
if (req.method !== "POST") return json({ error: { code: "notfound", message: "no such route" } }, 404);
|
|
300
|
+
let body: { dir?: unknown; values?: unknown } = {};
|
|
301
|
+
try { body = await req.json() as typeof body; } catch { return json({ error: { code: "args", message: "the body is not JSON" } }, 400); }
|
|
302
|
+
if (typeof body.dir !== "string" || !body.dir) return json({ error: { code: "args", message: "dir: the folder, absolute" } }, 400);
|
|
303
|
+
if (parts[2] === "remove") { const a = await ask<"remove">({ type: "remove", dir: body.dir }); return a.ok ? json(a.value) : failed(a); }
|
|
304
|
+
const v = (typeof body.values === "object" && body.values ? body.values : {}) as Record<string, unknown>;
|
|
305
|
+
const a = await ask<"setup">({ type: "setup", dir: body.dir, values: typeof v.name === "string" ? { name: v.name } : {} });
|
|
306
|
+
return a.ok ? json(a.value) : failed(a);
|
|
307
|
+
}
|
|
308
|
+
// /api/projects/rename: a baba under another name, its address moving with it.
|
|
309
|
+
if (parts[1] === "projects" && parts[2] === "rename" && parts.length === 3) {
|
|
310
|
+
if (req.method !== "POST") return json({ error: { code: "notfound", message: "no such route" } }, 404);
|
|
311
|
+
let body: { name?: unknown; to?: unknown } = {};
|
|
312
|
+
try { body = await req.json() as typeof body; } catch { return json({ error: { code: "args", message: "the body is not JSON" } }, 400); }
|
|
313
|
+
if (typeof body.name !== "string" || typeof body.to !== "string") return json({ error: { code: "args", message: "name: the baba's name now; to: the name it takes" } }, 400);
|
|
314
|
+
const a = await ask<"rename">({ type: "rename", name: body.name, to: body.to });
|
|
315
|
+
return a.ok ? json(a.value) : failed(a);
|
|
316
|
+
}
|
|
317
|
+
if (parts[1] === "projects") return json({ projects: state.projects, pending: state.pending, voss: state.voss, home: state.home });
|
|
318
|
+
// /api/settings: voss's own settings, the person's; a POST changes them, `keys` naming commands with a binding or null for the default.
|
|
319
|
+
if (parts[1] === "settings" && parts.length === 2) {
|
|
320
|
+
if (req.method !== "POST") return json(state.settings);
|
|
321
|
+
let body: { theme?: unknown; keys?: unknown } = {};
|
|
322
|
+
try { body = await req.json() as typeof body; } catch { return json({ error: { code: "args", message: "the body is not JSON" } }, 400); }
|
|
323
|
+
const a = await ask<"set-settings">({ type: "set-settings", ...(typeof body.theme === "string" ? { theme: body.theme as SetSettings["theme"] & string } : {}), ...(body.keys && typeof body.keys === "object" ? { keys: body.keys as SetSettings["keys"] } : {}) });
|
|
324
|
+
return a.ok ? json(a.value) : failed(a);
|
|
325
|
+
}
|
|
298
326
|
// /api/dirs?path=: the directories under one, for picking where a baba is or goes.
|
|
299
327
|
if (parts[1] === "dirs" && parts.length === 2) {
|
|
300
328
|
const a = await ask<"browse">({ type: "browse", path: url.searchParams.get("path") ?? "" });
|
package/src/server/main.ts
CHANGED
|
@@ -34,8 +34,9 @@ kernel.onMessage((m) => {
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
// The edge reads the projects and pages from the served state on every request.
|
|
37
|
-
if (m.type === "projects") { if (served) { served.projects = m.projects; served.pages = m.pages; } return; }
|
|
37
|
+
if (m.type === "projects") { if (served) { served.projects = m.projects; served.pending = m.pending; served.pages = m.pages; } return; }
|
|
38
38
|
if (m.type === "voss") { if (served) served.voss = m.voss; return; }
|
|
39
|
+
if (m.type === "settings") { if (served) served.settings = m.settings; return; }
|
|
39
40
|
if (m.type === "round") { live?.push(m.name, m.round, m.frame); return; }
|
|
40
41
|
if (m.type === "generation") { live?.generation(m.name, m.gen); return; }
|
|
41
42
|
const resolve = pending.get(m.id);
|
package/src/server/messages.ts
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import type { Described } from "../baba/node.ts";
|
|
5
5
|
import type { Raw } from "../ecs/runtime.ts";
|
|
6
6
|
import type { Interest } from "./protocol.ts";
|
|
7
|
+
import type { Requirement, RequirementValues } from "../baba/requirements.ts";
|
|
8
|
+
import type { Theme } from "../desktop/index.ts";
|
|
9
|
+
import type { KeyChanges, KeyOverrides } from "../desktop/keys.ts";
|
|
7
10
|
|
|
8
11
|
// ---- kernel → server ---------------------------------------------------
|
|
9
12
|
|
|
@@ -14,6 +17,20 @@ export interface Voss {
|
|
|
14
17
|
kept: boolean;
|
|
15
18
|
stale: boolean;
|
|
16
19
|
installed: { version: string; sha: string; at: string } | null;
|
|
20
|
+
/** Where this voss is and what it runs on, for the settings' About page. */
|
|
21
|
+
about?: About;
|
|
22
|
+
}
|
|
23
|
+
/** Where a running voss is and what it runs on: its runtime, the package it runs from, the state it keeps, the service that keeps it running. */
|
|
24
|
+
export interface About {
|
|
25
|
+
/** When this voss started, ISO. */
|
|
26
|
+
started: string;
|
|
27
|
+
runtime: { bun: string; platform: string; arch: string; executable: string };
|
|
28
|
+
/** The framework's package directory, and the CLI it was started with. */
|
|
29
|
+
framework: string; cli: string;
|
|
30
|
+
/** voss's own state: its root, the registry of babas, the page token. */
|
|
31
|
+
root: string; registry: string; token: string;
|
|
32
|
+
/** The user service's file and log, when one is installed; null otherwise. */
|
|
33
|
+
service: { file: string; log: string } | null;
|
|
17
34
|
}
|
|
18
35
|
|
|
19
36
|
/** Everything the server holds, sent once: it listens when this arrives. */
|
|
@@ -24,6 +41,10 @@ export interface Serve {
|
|
|
24
41
|
token: string;
|
|
25
42
|
voss: Voss;
|
|
26
43
|
projects: ProjectEntry[];
|
|
44
|
+
/** The folders voss remembers that it does not run: each to be set up, or failed. */
|
|
45
|
+
pending: PendingEntry[];
|
|
46
|
+
/** The person's settings, voss-wide: what every baba's pages look like and the keys they run. */
|
|
47
|
+
settings: VossSettings;
|
|
27
48
|
/** The user's home directory: the picker writes paths under it as ~/… and starts browsing there. */
|
|
28
49
|
home: string;
|
|
29
50
|
/** The page per scope: "" is the picker's, a project's name its own. The token meta is already in. */
|
|
@@ -34,6 +55,13 @@ export interface Serve {
|
|
|
34
55
|
|
|
35
56
|
/** A registered project; `parent` is the nearest registered project whose directory holds it, for nesting. */
|
|
36
57
|
export interface ProjectEntry { name: string; title: string; dir: string; parent: string | null }
|
|
58
|
+
/**
|
|
59
|
+
* A folder voss remembers but does not run. To be set up when it lacks a required property, a .baba among them:
|
|
60
|
+
* `missing` names them, and the picker opens its wizard. Failed when it has them all and still does not open: `error` says why.
|
|
61
|
+
*/
|
|
62
|
+
export interface PendingEntry { dir: string; baba: boolean; missing: Requirement[]; error: string | null; parent: string | null }
|
|
63
|
+
/** voss's own settings, the person's, kept in its root's settings.json: the theme of every page, the keys remapped on every desktop. */
|
|
64
|
+
export interface VossSettings { theme: Theme; keys?: KeyOverrides }
|
|
37
65
|
export interface Asset { mime: string; bytes: Uint8Array<ArrayBuffer> }
|
|
38
66
|
|
|
39
67
|
export type { Described };
|
|
@@ -48,9 +76,9 @@ export type ViewAnswer = { type: "view-answer"; id: number } & ({ ok: true; gen:
|
|
|
48
76
|
export type CallAnswer = { type: "call-answer"; id: number } & ({ ok: true; value: unknown; /** The round a fire or a raw write ran in. */ round?: number } | Failure);
|
|
49
77
|
|
|
50
78
|
/** The registry changed: the projects and their pages, replacing what `serve` sent. */
|
|
51
|
-
export interface Projects { type: "projects"; projects: ProjectEntry[]; pages: Record<string, string> }
|
|
52
|
-
/** What a refresh did: the projects now open, and the directories that did not load, with why. */
|
|
53
|
-
export interface Refreshed { projects: ProjectEntry[]; errors: { dir: string; error: string }[] }
|
|
79
|
+
export interface Projects { type: "projects"; projects: ProjectEntry[]; pending: PendingEntry[]; pages: Record<string, string> }
|
|
80
|
+
/** What a refresh did: the projects now open, the folders remembered but not run, and the directories that did not load, with why. */
|
|
81
|
+
export interface Refreshed { projects: ProjectEntry[]; pending: PendingEntry[]; errors: { dir: string; error: string }[] }
|
|
54
82
|
export type RefreshAnswer = { type: "refresh-answer"; id: number } & ({ ok: true; value: Refreshed } | Failure);
|
|
55
83
|
|
|
56
84
|
/** A round of a project's live generation, as the frame every window of it gets. */
|
|
@@ -58,16 +86,19 @@ export interface RoundPush { type: "round"; name: string; round: number; frame:
|
|
|
58
86
|
/** A project's generation advanced: its windows re-read the contract and resync. */
|
|
59
87
|
export interface Generation { type: "generation"; name: string; gen: number }
|
|
60
88
|
|
|
89
|
+
/** voss's settings changed: the edge answers with these from now on. */
|
|
90
|
+
export interface SettingsPush { type: "settings"; settings: VossSettings }
|
|
91
|
+
export type SettingsAnswer = { type: "settings-answer"; id: number } & ({ ok: true; value: VossSettings } | Failure);
|
|
61
92
|
/** Voss's own standing changed: a newer one is installed. */
|
|
62
93
|
export interface VossPush { type: "voss"; voss: Voss }
|
|
63
94
|
export type RestartAnswer = { type: "restart-answer"; id: number } & ({ ok: true; value: { restarting: true; kept: boolean } } | Failure);
|
|
64
95
|
/** A directory and what is under it: each child, whether it holds a baba, and whether it is already remembered. */
|
|
65
96
|
export interface Browsed { path: string; home: string; parent: string | null; baba: boolean; known: boolean; dirs: { name: string; path: string; baba: boolean; known: boolean }[] }
|
|
66
97
|
export type BrowseAnswer = { type: "browse-answer"; id: number } & ({ ok: true; value: Browsed } | Failure);
|
|
67
|
-
/** What an add did: the
|
|
68
|
-
export type AddAnswer = { type: "add-answer"; id: number } & ({ ok: true; value: Refreshed & { name: string; dir: string } } | Failure);
|
|
98
|
+
/** What an add, a setup or a removal did: the folder, the baba's name when it runs, and the registry re-read. */
|
|
99
|
+
export type AddAnswer = { type: "add-answer"; id: number } & ({ ok: true; value: Refreshed & { name: string | null; dir: string } } | Failure);
|
|
69
100
|
|
|
70
|
-
export type ToServer = Serve | Projects | VossPush | DescribeAnswer | ViewAnswer | CallAnswer | RefreshAnswer | RestartAnswer | BrowseAnswer | AddAnswer | RoundPush | Generation;
|
|
101
|
+
export type ToServer = Serve | Projects | VossPush | SettingsPush | DescribeAnswer | ViewAnswer | CallAnswer | RefreshAnswer | RestartAnswer | BrowseAnswer | AddAnswer | SettingsAnswer | RoundPush | Generation;
|
|
71
102
|
|
|
72
103
|
// ---- server → kernel ---------------------------------------------------
|
|
73
104
|
|
|
@@ -93,5 +124,14 @@ export interface Restart { type: "restart"; id: number }
|
|
|
93
124
|
export interface Browse { type: "browse"; id: number; path: string }
|
|
94
125
|
/** Remember a project at `dir`; with `init`, make its .baba first, named and titled as given or after the directory. */
|
|
95
126
|
export interface Add { type: "add"; id: number; dir: string; init: boolean; name?: string; title?: string }
|
|
127
|
+
/** Forget the folder at `dir`: voss stops running its baba; its files stay. */
|
|
128
|
+
export interface Remove { type: "remove"; id: number; dir: string }
|
|
129
|
+
/** Set the required properties of the folder at `dir`, making its .baba first when it has none; then it runs. */
|
|
130
|
+
export interface Setup { type: "setup"; id: number; dir: string; values: RequirementValues }
|
|
131
|
+
|
|
132
|
+
/** Change voss's settings: the theme, keys remapped or given back their defaults. */
|
|
133
|
+
export interface SetSettings { type: "set-settings"; id: number; theme?: Theme; keys?: KeyChanges }
|
|
134
|
+
/** Rename the baba called `name`: its address moves to `/TO/`, and it runs again under it. */
|
|
135
|
+
export interface Rename { type: "rename"; id: number; name: string; to: string }
|
|
96
136
|
|
|
97
|
-
export type FromServer = Ready | Listening | Failed | Describe | View | Call | Refresh | Restart | Browse | Add;
|
|
137
|
+
export type FromServer = Ready | Listening | Failed | Describe | View | Call | Refresh | Restart | Browse | Add | Remove | Setup | SetSettings | Rename;
|
package/src/shell/run.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// voss baba follow ENTITY | COMPONENT KEY [--until C | C.field=value] [--for 30s] [--changes]
|
|
10
10
|
// voss baba follow QUERY [args…] --changed [--for 30s]
|
|
11
11
|
// voss baba run [FILE | LINE …] | raw spawn JSON | add E COMPONENT [JSON] | remove E COMPONENT … | despawn E | set RESOURCE JSON
|
|
12
|
-
// voss baba check | install | add-system NAME |
|
|
12
|
+
// voss baba check | install | add-system NAME | agents … | serve [HOST:PORT]
|
|
13
13
|
// voss init [DIR] | babas … | gui [HOST:PORT] | service …
|
|
14
14
|
//
|
|
15
15
|
// Stdout is one line of JSON per answer (`--pretty` indents it); stderr is
|
|
@@ -30,8 +30,8 @@ import { serve } from "../http/server.ts";
|
|
|
30
30
|
import { words, callArgs, usage, describeArgs, UsageError } from "./args.ts";
|
|
31
31
|
import { init, install, addSystem } from "../baba/init.ts";
|
|
32
32
|
import { check } from "../baba/check.ts";
|
|
33
|
-
import { planProject, syncProject, outputOf, sources } from "../
|
|
34
|
-
import { report } from "../
|
|
33
|
+
import { planProject, syncProject, outputOf, sources } from "../agents/disk.ts";
|
|
34
|
+
import { report } from "../agents/sync.ts";
|
|
35
35
|
import * as service from "./service.ts";
|
|
36
36
|
import { connect, daemon, type Client, type Daemon } from "./client.ts";
|
|
37
37
|
import { runMcp } from "../mcp/main.ts";
|
|
@@ -131,7 +131,7 @@ const builtinHelp: Record<string, string> = {
|
|
|
131
131
|
"add-system": "a system under .baba/systems/NAME: add-system NAME",
|
|
132
132
|
serve: "the GUI for this baba alone: serve [HOST:PORT]",
|
|
133
133
|
mcp: "an MCP server over stdio for agents, a client of the running voss",
|
|
134
|
-
|
|
134
|
+
agents: "the agentic instructions compiled: agents compile | sync | output PATH",
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
/**
|
|
@@ -177,7 +177,7 @@ function globals(argv: string[]): { ws: string[]; pretty: boolean; max: number }
|
|
|
177
177
|
|
|
178
178
|
export async function run(argv: string[], sh: Shell): Promise<number> {
|
|
179
179
|
const reg = new Registry(sh.rootDir);
|
|
180
|
-
// Every answer is JSON; the
|
|
180
|
+
// Every answer is JSON; the agentic instructions block below still reads this.
|
|
181
181
|
const json = true;
|
|
182
182
|
let callUsage: string | undefined;
|
|
183
183
|
let outDir: string | undefined;
|
|
@@ -304,7 +304,7 @@ export async function run(argv: string[], sh: Shell): Promise<number> {
|
|
|
304
304
|
}
|
|
305
305
|
if (name === "manifest") {
|
|
306
306
|
// The baba loaded once, in this process, and let go: what every load writes, .baba/.voss/manifest.json, is there
|
|
307
|
-
// for the index, --schema and the
|
|
307
|
+
// for the index, --schema and the agentic instructions before the baba ever runs under the daemon, as in a fresh worktree.
|
|
308
308
|
const lines: string[] = [];
|
|
309
309
|
const n = new Node(project, (l) => lines.push(l));
|
|
310
310
|
try { await n.start(); } finally { await n.stop(); }
|
|
@@ -345,36 +345,36 @@ export async function run(argv: string[], sh: Shell): Promise<number> {
|
|
|
345
345
|
const c = await connect(project, sh.rootDir, prog).catch(downOnly);
|
|
346
346
|
const down = c instanceof NodeError ? c : null;
|
|
347
347
|
const d = c instanceof NodeError ? null : await c.describe();
|
|
348
|
-
// Down, the
|
|
349
|
-
// go, as \`manifest\` does, so a fresh worktree has one and an edit to the
|
|
348
|
+
// Down, the agentic instructions compiles from a manifest of the code as it is now: the baba is loaded once in this process and let
|
|
349
|
+
// go, as \`manifest\` does, so a fresh worktree has one and an edit to the agentic instructions is in it. Busy or broken, the last one stands.
|
|
350
350
|
let loadError: string | null = null;
|
|
351
|
-
if (down && name === "
|
|
351
|
+
if (down && name === "agents") {
|
|
352
352
|
const n = new Node(project, () => {});
|
|
353
353
|
try { await n.start(); } catch (err) { loadError = err instanceof Error ? err.message : String(err); } finally { await n.stop().catch(() => {}); }
|
|
354
354
|
}
|
|
355
355
|
const m = d ? d.manifest : await readManifest(project);
|
|
356
356
|
if (!m && loadError) throw new ShellError("failed", `${project.name} did not load: ${loadError}`);
|
|
357
357
|
if (!m) { if (down) throw down; throw new ShellError("down", `${project.name} is not running${d?.error ? `: ${d.error}` : ""}`); }
|
|
358
|
-
if (name === "
|
|
359
|
-
// Running, the baba's
|
|
358
|
+
if (name === "agents") {
|
|
359
|
+
// Running, the baba's agents system syncs, and the answer waits for it; down, the same compiler runs here over the files.
|
|
360
360
|
const [cmd, path] = args;
|
|
361
|
-
if (cmd === "sync" && d && !d.error && "
|
|
361
|
+
if (cmd === "sync" && d && !d.error && "agents-sync" in m.actions) {
|
|
362
362
|
const live = c as Client;
|
|
363
|
-
const syncs = async () => await live.read("
|
|
363
|
+
const syncs = async () => await live.read("agents-plan", {}) as { syncs: number; error: string | null };
|
|
364
364
|
const before = (await syncs()).syncs;
|
|
365
|
-
await live.fire("
|
|
365
|
+
await live.fire("agents-sync", {});
|
|
366
366
|
for (let i = 0; i < 200; i++) {
|
|
367
367
|
const p = await syncs();
|
|
368
|
-
if (p.syncs > before) { if (p.error) return fail(`
|
|
368
|
+
if (p.syncs > before) { if (p.error) return fail(`agents sync: ${p.error}`); out(p); return 0; }
|
|
369
369
|
await new Promise((r) => setTimeout(r, 50));
|
|
370
370
|
}
|
|
371
|
-
return fail("
|
|
371
|
+
return fail("agents sync: asked, not finished within 10s; agents-plan says how it went");
|
|
372
372
|
}
|
|
373
373
|
const input = sources(project.dir, m, project.title);
|
|
374
374
|
if (cmd === "compile") { out(report(await planProject(project.dir, input))); return 0; }
|
|
375
375
|
if (cmd === "sync") { out(await syncProject(project.dir, input)); return 0; }
|
|
376
376
|
if (cmd === "output" && path) { const o = await outputOf(project.dir, input, path); out(json ? o : { path: o.path, status: o.status }); if (!json) sh.stdout(o.text); return 0; }
|
|
377
|
-
sh.stdout(`Usage: ${prog}
|
|
377
|
+
sh.stdout(`Usage: ${prog} agents compile | sync | output PATH\n`);
|
|
378
378
|
return help ? 0 : 2;
|
|
379
379
|
}
|
|
380
380
|
if (!name) {
|
|
@@ -462,7 +462,7 @@ function contract(prog: string, title: string, m: Manifest, error: string | null
|
|
|
462
462
|
section("Resources", Object.entries(m.resources).map(([n, r]) => [n, r.owner]));
|
|
463
463
|
section("Effects", Object.entries(m.effects).map(([n, e]) => [n, `${e.owner}${e.every ? ` · every ${e.every} ms` : ""}${e.retry ? ` · retry ${e.retry} ms` : ""}${e.summary ? ` · ${e.summary}` : ""}`]));
|
|
464
464
|
section("The state", Object.entries(builtinHelp).filter(([k]) => ["state", "follow", "run", "raw"].includes(k)).map(([k, h]) => [k, h]));
|
|
465
|
-
section("Voss", Object.entries(builtinHelp).filter(([k]) => ["start", "stop", "status", "mcp", "check", "manifest", "install", "add-system", "serve", "
|
|
465
|
+
section("Voss", Object.entries(builtinHelp).filter(([k]) => ["start", "stop", "status", "mcp", "check", "manifest", "install", "add-system", "serve", "agents"].includes(k)).map(([k, h]) => [k, h]));
|
|
466
466
|
lines.push(`State: ${where}${error ? ` · the last load failed: ${error}` : ""}`);
|
|
467
467
|
return lines.join("\n") + "\n";
|
|
468
468
|
}
|
package/src/spec/index.ts
CHANGED
|
@@ -91,7 +91,7 @@ export function spec(target: System | Baba, def: SpecDef<any, any> = {}): Spec<a
|
|
|
91
91
|
if (!NAME.test(name)) throw new Error(`spec ${JSON.stringify(name)}: lowercase words joined by single dashes`);
|
|
92
92
|
const base = whole ? (target as Baba) : baba({ systems: closureOf([target as System]) });
|
|
93
93
|
const tick = def.tick === undefined ? base.tick : def.tick;
|
|
94
|
-
const v = tick === base.tick ? base : baba({ systems: base.systems, ...(tick !== null ? { tick } : {}),
|
|
94
|
+
const v = tick === base.tick ? base : baba({ systems: base.systems, ...(tick !== null ? { tick } : {}), instructions: base.instructions, apps: base.apps });
|
|
95
95
|
const names = new Set<string>();
|
|
96
96
|
for (const sc of def.scenarios ?? []) { if (names.has(sc.name)) throw new Error(`spec ${name}: two scenarios are named ${JSON.stringify(sc.name)}`); names.add(sc.name); }
|
|
97
97
|
const also = def.also ?? [];
|
package/src/web/core.tsx
CHANGED
|
@@ -47,7 +47,7 @@ function routed(key: string, table: RouteTable): ComponentType<AppProps> {
|
|
|
47
47
|
* the baba declares in `baba({ apps })`, and none other; the keys are checked against them. An app that
|
|
48
48
|
* declares routes may be given a component per route instead, `{ "/": Prs, "/pr/:n": Pr }`, checked
|
|
49
49
|
* against its routes: every route, none other. One of voss's own apps, `maker`, `state`, `contract`,
|
|
50
|
-
* `
|
|
50
|
+
* `agentic instructions`, `settings`, may be given a component of the baba's to replace voss's.
|
|
51
51
|
* The old form, `compose({ shell })`, still loads.
|
|
52
52
|
*/
|
|
53
53
|
export function compose<V extends { $app: string }>(def: { apps: { [K in V["$app"]]: EntryFor<RoutesFor<V, K>> } & { [K in VossApp]?: ComponentType<AppProps> } }): AppsDef;
|
|
@@ -111,7 +111,7 @@ export async function api(kind: "fire" | "read" | "state" | "raw", scope: string
|
|
|
111
111
|
* The baba's manifest, as the server describes it: null until it arrives.
|
|
112
112
|
* Asked once, then asked again "since the generation I have", so the answer
|
|
113
113
|
* comes when the baba reloads: a page that draws the manifest, the Maker's
|
|
114
|
-
* objects or the
|
|
114
|
+
* objects or the agentic instructions, follows a change to the code as the state
|
|
115
115
|
* follows a round. A baba that is down is asked again in a moment.
|
|
116
116
|
*/
|
|
117
117
|
export function useManifest(): Manifest | null {
|
package/src/web/index.tsx
CHANGED
|
@@ -14,4 +14,4 @@ export { formatFailure, placeOf, type ViewFailure, type Stage as FailureStage }
|
|
|
14
14
|
export { Button, Input, Textarea, Select, Kbd, Panel, Rows, Row, Table, Dialog, CommandList, type ButtonVariant } from "./ui.tsx";
|
|
15
15
|
export { plan, collect, tableOf, describeType, FieldError, type Plan, type Field, type Values } from "./form.ts";
|
|
16
16
|
export { filterItems, type Item } from "./list.ts";
|
|
17
|
-
export { Desktop, DesktopFrame, SpecDesktop, LocalDesktop, Eyes, AppIcon, desktopAddress, parseDesktopAddress, parseDesktopHash, routeOf, type DesktopProps, type AppProps, type SurfaceProps } from "../desktop/view.tsx";
|
|
17
|
+
export { Desktop, DesktopFrame, DesktopSkeleton, SpecDesktop, LocalDesktop, Eyes, AppIcon, desktopAddress, parseDesktopAddress, parseDesktopHash, routeOf, type DesktopProps, type LauncherMode, type AppProps, type SurfaceProps } from "../desktop/view.tsx";
|
package/src/desktop/bob.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// The launcher's dot as a body on a string: a point with a velocity, moved by
|
|
2
|
-
// a small simulation rather than by CSS transitions, so a fall can gather
|
|
3
|
-
// speed, the string can catch it, and a new aim mid-flight keeps the momentum.
|
|
4
|
-
// The page says where the dot should be (`aim`); the bob decides how it gets
|
|
5
|
-
// there and draws itself and its string on every frame until it rests.
|
|
6
|
-
|
|
7
|
-
export interface Point { x: number; y: number }
|
|
8
|
-
/** A spring toward the aim: stiffness pulls, damping calms. Below critical damping (2√stiffness) it overshoots. */
|
|
9
|
-
export interface Spring { stiffness: number; damping: number }
|
|
10
|
-
/** Falling: gravity until the string is taut. Caught: the string springs it to rest. Gliding: a spring between rows. */
|
|
11
|
-
export type Phase = "falling" | "caught" | "gliding";
|
|
12
|
-
export interface BobState { at: Point; v: Point; phase: Phase }
|
|
13
|
-
|
|
14
|
-
/** Pixels per second squared: a fall of a few rows lasts about a tenth of a second. */
|
|
15
|
-
export const gravity = 9000;
|
|
16
|
-
/** The string catching the fall: stiff and loose, so it bounces. */
|
|
17
|
-
export const catchSpring: Spring = { stiffness: 1400, damping: 28 };
|
|
18
|
-
/** Moving between rows: quick, with a small overshoot. */
|
|
19
|
-
export const glideSpring: Spring = { stiffness: 1100, damping: 40 };
|
|
20
|
-
/** Sideways onto the string's vertical, while falling. */
|
|
21
|
-
export const swingSpring: Spring = { stiffness: 1600, damping: 56 };
|
|
22
|
-
|
|
23
|
-
const axis = (at: number, v: number, to: number, s: Spring, dt: number) => {
|
|
24
|
-
const a = s.stiffness * (to - at) - s.damping * v;
|
|
25
|
-
const nv = v + a * dt;
|
|
26
|
-
return [at + nv * dt, nv] as const;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/** One step of the simulation. A falling bob is pulled down by gravity until it reaches the aim, where the string goes taut and catches it. */
|
|
30
|
-
export function step(s: BobState, aim: Point, dt: number): BobState {
|
|
31
|
-
const [x, vx] = axis(s.at.x, s.v.x, aim.x, s.phase === "gliding" ? glideSpring : swingSpring, dt);
|
|
32
|
-
if (s.phase === "falling") {
|
|
33
|
-
const vy = s.v.y + gravity * dt, y = s.at.y + vy * dt;
|
|
34
|
-
return y < aim.y ? { at: { x, y }, v: { x: vx, y: vy }, phase: "falling" } : { at: { x, y: aim.y }, v: { x: vx, y: vy }, phase: "caught" };
|
|
35
|
-
}
|
|
36
|
-
const [y, vy] = axis(s.at.y, s.v.y, aim.y, s.phase === "caught" ? catchSpring : glideSpring, dt);
|
|
37
|
-
return { at: { x, y }, v: { x: vx, y: vy }, phase: s.phase };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Whether a bob has come to rest at its aim. */
|
|
41
|
-
export const resting = (s: BobState, aim: Point) => s.phase !== "falling" && Math.abs(s.at.x - aim.x) < .05 && Math.abs(s.at.y - aim.y) < .05 && Math.hypot(s.v.x, s.v.y) < 2;
|
|
42
|
-
|
|
43
|
-
/** Runs a bob on animation frames and draws it with `draw`; it sleeps while at rest. */
|
|
44
|
-
export class Bob {
|
|
45
|
-
private state: BobState | null = null;
|
|
46
|
-
private aim: Point = { x: 0, y: 0 };
|
|
47
|
-
private frame = 0;
|
|
48
|
-
private last = 0;
|
|
49
|
-
constructor(private draw: (at: Point) => void, private still = () => false) {}
|
|
50
|
-
/** Starts a fall from a point, from rest. */
|
|
51
|
-
drop(from: Point) { this.state = { at: { ...from }, v: { x: 0, y: 0 }, phase: "falling" }; this.draw(from); this.wake(); }
|
|
52
|
-
/** Where the dot should be. With `now`, it is there at once (a scroll moves it with the list). */
|
|
53
|
-
to(aim: Point, now = false) {
|
|
54
|
-
const moved = Math.abs(aim.y - this.aim.y) > .5;
|
|
55
|
-
this.aim = aim;
|
|
56
|
-
if (!this.state || now || this.still()) { this.state = { at: { ...aim }, v: { x: 0, y: 0 }, phase: "gliding" }; this.draw(aim); return; }
|
|
57
|
-
// A new aim after the catch is a move between rows; a fall keeps falling toward it.
|
|
58
|
-
if (this.state.phase === "caught" && moved) this.state = { ...this.state, phase: "gliding" };
|
|
59
|
-
this.wake();
|
|
60
|
-
}
|
|
61
|
-
stop() { cancelAnimationFrame(this.frame); this.frame = 0; this.state = null; }
|
|
62
|
-
private wake() {
|
|
63
|
-
if (this.frame) return;
|
|
64
|
-
this.last = performance.now();
|
|
65
|
-
const tick = (now: number) => {
|
|
66
|
-
// Fixed small steps keep a stiff spring stable on a slow frame.
|
|
67
|
-
let left = Math.min(.05, (now - this.last) / 1000); this.last = now;
|
|
68
|
-
while (left > 0 && this.state) { const dt = Math.min(left, 1 / 240); this.state = step(this.state, this.aim, dt); left -= dt; }
|
|
69
|
-
if (!this.state) { this.frame = 0; return; }
|
|
70
|
-
if (resting(this.state, this.aim)) { this.state = { at: { ...this.aim }, v: { x: 0, y: 0 }, phase: "gliding" }; this.draw(this.aim); this.frame = 0; return; }
|
|
71
|
-
this.draw(this.state.at);
|
|
72
|
-
this.frame = requestAnimationFrame(tick);
|
|
73
|
-
};
|
|
74
|
-
this.frame = requestAnimationFrame(tick);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|