flowviant 0.106.0 → 0.107.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.
@@ -27,10 +27,8 @@
27
27
  * The kinds. Per kind:
28
28
  * - `posture`: the turn profile the card runs under (turnProfile.mjs);
29
29
  * - `artifact`: null for code (it delivers commits), else what proves a
30
- * delivery — `match` over the artifact file name, an optional `companion`
31
- * that must ALSO stand beside it (the 3D-model kind's model file: an HTML
32
- * viewer alone is a page, not a model), and the `missing` sentence a turn
33
- * settles `nothing` with when the proof was not written;
30
+ * delivery — `match` over the artifact file name, and the `missing`
31
+ * sentence a turn settles `nothing` with when the proof was not written;
34
32
  * - `offRuntime(rt)`: the sentence when the chosen CLI cannot run the posture.
35
33
  */
36
34
  /** Every non-code kind runs on Claude: the one CLI that declares their postures. */
@@ -51,18 +49,19 @@ export const AGENT_TASK_KINDS = Object.freeze({
51
49
  offRuntime: () => CLAUDE_ONLY,
52
50
  }),
53
51
  /**
54
- * THE 3D-MODEL KIND (0.105.0): a viewable bundle — an HTML viewer and the
55
- * model files beside it. Under the design fence (read the repo, write only
56
- * `.flowviant/artifacts/**`): a model is a picture of an asset, and
57
- * committing it is a separate code card a person raises at Accept.
52
+ * THE 3D-MODEL KIND (0.105.0; one page since 0.107.0): a three.js page that
53
+ * builds the model in code, which the person exports to one .glb in Review.
54
+ * Under the design fence (read the repo, write only `.flowviant/artifacts/**`):
55
+ * a model is a picture of an asset, and committing it is a separate code
56
+ * card a person raises at Accept. Proven by its page alone — the model is
57
+ * IN the page; 0.105.0's second proof (a .gltf/.glb/.obj beside it) went
58
+ * with the files it asked for.
58
59
  */
59
60
  model: Object.freeze({
60
61
  posture: 'design',
61
62
  artifact: Object.freeze({
62
63
  match: /\.html?$/i,
63
- companion: /\.(gltf|glb|obj)$/i,
64
- missing:
65
- 'the turn ended without writing a model bundle (an HTML viewer and a .gltf, .glb or .obj file) under .flowviant/artifacts/',
64
+ missing: 'the turn ended without writing a 3D model page under .flowviant/artifacts/',
66
65
  }),
67
66
  offRuntime: () => CLAUDE_ONLY,
68
67
  }),
@@ -91,8 +91,8 @@ export const AGENT_TASK_TYPES = Object.freeze({
91
91
  label: '3D model',
92
92
  kind: 'model',
93
93
  contract:
94
- 'model the object the card describes as the viewable bundle, at the scale and in the units of any ' +
95
- '3D assets the repository already has.',
94
+ 'model the object the card describes in the one three.js page, at the scale and in the units of the ' +
95
+ 'target the card names, else of any 3D assets the repository already has.',
96
96
  }),
97
97
  vector: Object.freeze({
98
98
  label: 'Icons & vector',
@@ -56,62 +56,84 @@ or, if you are stopping to ask:
56
56
  past tense, plain sentences, no card ids.`;
57
57
 
58
58
  /**
59
- * THE 3D-MODEL CARD. The product is a model a person can turn around in
60
- * Review: one folder holding a three.js viewer and the model files it loads
61
- * by relative URL — the bundle shape the artifact scan keeps (relative paths
62
- * to depth four, 0.99.0) and the library keeps as one versioned folder. The
63
- * daemon calls it delivered only when an `.html` AND a `.gltf`/`.glb`/`.obj`
64
- * stand under `.flowviant/artifacts/` (agentTaskKinds.mjs).
59
+ * THE 3D-MODEL CARD (rewritten 0.107.0, the owner, 2026-09-27: "yeah just
60
+ * glb"). The product is ONE .glb a person downloads and imports; the card
61
+ * hands back ONE three.js page that builds the model in code and shows it —
62
+ * how Claude makes a model a person can look at anywhere else — and Flowviant
63
+ * exports that page's model group to a .glb with three.js's own GLTFExporter
64
+ * when the person presses Download .glb in Review (apps/web
65
+ * `workbench/glbExport.ts`, the other half of the handshake below).
65
66
  *
66
- * TEXT, BECAUSE TEXT IS ALL THIS POSTURE WRITES (2026-09-27, a call the owner
67
- * may overrule). The kind runs under the `design` posture: Read, `ls`, and
68
- * `Edit(.flowviant/artifacts/**)` — Claude's Write/Edit tools write text, and
69
- * there is no shell that could produce bytes. The spec's first wording asked
70
- * for glTF with a `.bin`, or a `.glb`, and `.png`/`.jpg` textures: outputs
71
- * this card cannot make. So the contract asks for OBJ + `.mtl` (text, fetched
72
- * by relative URL from the artifact host, which the viewer's `connect-src`
73
- * admits) and SVG or procedural textures (`img-src` admits the host). NOT a
74
- * `.gltf` with its buffer as a `data:` URI: three.js fetches that buffer, and
75
- * the artifact CSP's `connect-src` names only the artifact host
76
- * (apps/api/src/lib/artifactDelivery.ts), so the review viewer would refuse
77
- * it. Binary files are still accepted — the artifact policy, the proof, the
78
- * library and the 20 MB cap all keep them — for a folder that already holds
79
- * one; widening the posture to write them is the owner's decision.
67
+ * WHY NOT A MODEL FILE ON DISK. This kind runs under the `design` posture:
68
+ * Read, `ls`, and `Edit(.flowviant/artifacts/**)` — text only, no shell. A
69
+ * .glb is binary; a .gltf's buffer is base64 floats nobody writes by hand.
70
+ * The 0.105.0 contract asked for OBJ + .mtl + SVG textures instead, and a
71
+ * katana came back as seven files, textures no game engine imports and a
72
+ * generator script (2026-09-27). Built in code, drawn textures are canvases,
73
+ * and the exporter embeds them in the .glb as PNGs.
74
+ *
75
+ * THE HANDSHAKE IS SPELLED WORD FOR WORD because the web keys on it: the page
76
+ * announces `glb-ready` once its model stands, and answers `export-glb` with
77
+ * the binary. The page runs in a scripts-only sandbox with an opaque origin,
78
+ * so it posts to `parent` with `'*'`; the app accepts only its own frame's
79
+ * messages. A page with no handshake (every 0.105.0 bundle) is still viewed;
80
+ * it is just never offered a Download .glb.
80
81
  */
81
82
  export const SYSTEM_AGENT_MODEL = `You are the human's own Claude, working one 3D MODEL card in a git worktree of
82
83
  their repository. Nobody is watching this run. You have the repo and nothing
83
84
  else — no project tools, no board, no chat.
84
85
 
85
86
  A 3D MODEL CARD HANDS BACK A MODEL YOU CAN LOOK AT, NOT A CHANGE. Nothing you
86
- do here edits the product: the person turns the model around, and decides.
87
+ do here edits the product: the person turns the model around, downloads it as
88
+ one .glb, and decides.
87
89
 
88
90
  WHAT TO DO:
89
91
 
90
- 1. Read the repository first — the existing 3D assets and the scene they sit
91
- in: their scale and units, their up axis, their style, their materials and
92
- texture sizes. The model must fit THIS project, not a stock asset library.
93
-
94
- 2. Write ONE folder, .flowviant/artifacts/<kebab-name>/ — a short kebab-case
95
- name for what it is (for example .flowviant/artifacts/reading-chair/) —
96
- holding:
97
- - index.html, a viewer: three.js loaded from cdn.jsdelivr.net/npm or
98
- unpkg.com, with orbit controls, loading the model by a RELATIVE URL
99
- (./reading-chair.obj — never a path outside the folder);
100
- - the model, written as TEXT: a .obj with its .mtl, each loaded by a
101
- relative URL;
102
- - its textures as .svg files referenced by relative paths, or drawn by
103
- the viewer itself (procedural materials, canvas textures).
104
- You can write only text files here. A .glb, .bin, .png or .jpg is kept
105
- and shown if one already stands in the folder, but this card cannot make
106
- one — never plan on one. Nothing else may load from the network, and
107
- nothing can be sent. Keep every file under 2 MB — the viewer, the .obj,
108
- the .mtl, each texture.
92
+ 1. Read the card and the repository first. If the card names where the model
93
+ will be used — a game engine, a platform, a printer — meet that target's
94
+ import rules: its scale and units, its up axis, its triangle and texture
95
+ limits. Otherwise match the scale and units of any 3D assets the repository
96
+ already has. Say in your summary what you assumed.
97
+
98
+ 2. Write ONE file, .flowviant/artifacts/<kebab-name>/index.html — a short
99
+ kebab-case name for what it is (for example
100
+ .flowviant/artifacts/reading-chair/index.html). Nothing else goes in that
101
+ folder: no model files, no textures, no scripts, no notes. The page BUILDS
102
+ THE MODEL IN CODE and shows it:
103
+ - three.js as ES modules from cdn.jsdelivr.net/npm, through an import map
104
+ for "three" and "three/addons/"; orbit controls, lights and a neutral
105
+ ground. Nothing else loads from the network, and nothing can be sent.
106
+ - the model is ONE THREE.Group named for what it is, built from three.js
107
+ geometry (lathe, extrude, tube, box and the rest, one mesh per part),
108
+ with MeshStandardMaterial only. Colour, roughness and metalness are
109
+ material values, or textures you DRAW ON A CANVAS in the page
110
+ (THREE.CanvasTexture, 1024px or less) — never an image file.
111
+ - the ground, a grid, the lights, a figure for scale and every other
112
+ helper stay OUTSIDE that group: they are the viewer's, not the model.
113
+ - no download or save button of your own: the page cannot download
114
+ anything. Flowviant's Download .glb is the way out.
115
+ - once the group is built, hand it to Flowviant exactly like this, with
116
+ MODEL the group:
117
+
118
+ import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
119
+ addEventListener('message', (e) => {
120
+ if (e.source !== parent || e.data?.flowviant !== 'export-glb') return;
121
+ new GLTFExporter().parse(
122
+ MODEL,
123
+ (glb) => parent.postMessage({ flowviant: 'glb', glb }, '*', [glb]),
124
+ (err) => parent.postMessage({ flowviant: 'glb-error', message: String(err) }, '*'),
125
+ { binary: true }
126
+ );
127
+ });
128
+ parent.postMessage({ flowviant: 'glb-ready' }, '*');
129
+
130
+ Keep the page under 2 MB.
109
131
 
110
132
  ${ARTIFACT_CONTRACT_TAIL({
111
133
  ask: `If you cannot model it without a DECISION only a person can make — which of
112
134
  two directions, what size or style, which asset it stands beside — STOP AND
113
135
  ASK. Do not guess. A question costs one reply; a guessed model costs a review.`,
114
- summary: 'one or two sentences on what the model shows, and the folder it is in',
136
+ summary: 'one or two sentences on what the model shows, the folder it is in, and the scale and target you assumed',
115
137
  })}`;
116
138
 
117
139
  /**
@@ -545,8 +545,8 @@ MECHANICS OF THIS CHAT:
545
545
  - "code": a change to the code — commits they review and merge. The default.
546
546
  - "design": a UI mockup — one HTML page to look at; no code changed.
547
547
  "Mockup", "design page X", "show me what the settings page could look like".
548
- - "model": a 3D model — a viewable bundle (an HTML viewer and the model
549
- files); no code changed. "3D model", "mesh", "a chair model", "glTF", "OBJ".
548
+ - "model": a 3D model — one page to turn it around in, downloaded as a
549
+ .glb; no code changed. "3D model", "mesh", "a chair model", "glTF", "OBJ".
550
550
  - "deck": a presentation — one HTML slide deck; no code changed. "Slides",
551
551
  "a deck", "a presentation", "a pitch".
552
552
  - "research": a write-up — read the repo and the web; no code changed.
@@ -1048,7 +1048,7 @@ export const AGENT_CONTRACTS = Object.freeze({
1048
1048
  });
1049
1049
  export const AGENT_HANDS_BACK = Object.freeze({
1050
1050
  design: 'a DESIGN card: it hands back a mockup',
1051
- model: 'a 3D MODEL card: it hands back a viewable model bundle',
1051
+ model: 'a 3D MODEL card: it hands back a model to view and download as a .glb',
1052
1052
  deck: 'a PRESENTATION card: it hands back an HTML slide deck',
1053
1053
  research: 'a RESEARCH card: it hands back a write-up',
1054
1054
  });
@@ -78,8 +78,8 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
78
78
  *
79
79
  * Its product is under `.flowviant/artifacts/`, and the kind's table
80
80
  * (agentTaskKinds.mjs) says which file proves it: a `.html` mockup for
81
- * design, a `.html` deck, a `.md` write-up for research, and for a 3D
82
- * model a `.html` viewer AND a `.gltf`/`.glb`/`.obj` beside it. A turn that
81
+ * design, a `.html` deck, a `.md` write-up for research, and a `.html`
82
+ * page for a 3D model (the model is built in it, 0.107.0). A turn that
83
83
  * says "delivered" without having written one would land the agent in Review
84
84
  * with nothing to look at and a summary describing a page that does not
85
85
  * exist — so it settles `nothing` with the measured sentence, and the
@@ -108,19 +108,10 @@ export function agentTurnSettlement({ turnId, job, kind, out, lastAnswer, usage,
108
108
  */
109
109
  if (res.outcome === 'delivered' && kind.artifact) {
110
110
  const want = kind.artifact.match;
111
- // THE 3D-MODEL KIND'S SECOND PROOF (0.105.0): its viewer page alone is a
112
- // page, not a model — a `.gltf`/`.glb`/`.obj` must stand beside it. A
113
- // task turn delivers when it wrote EITHER half this turn and BOTH stand
114
- // (a re-modelled mesh — a rewritten .gltf/.glb/.obj — need not rewrite
115
- // its viewer; a texture or .mtl alone is neither half). The halves are
116
- // not paired by folder (pinned in the test). With no companion this is
117
- // exactly the one-file rule design, deck and research have.
118
- const also = kind.artifact.companion;
119
- const need = (l) => l.some((e) => want.test(e.name)) && (!also || l.some((e) => also.test(e.name)));
111
+ const has = (l) => l.some((e) => want.test(e.name));
120
112
  const standing = scanArtifacts(wt);
121
- const touched = changedArtifacts(artifactsBefore, standing);
122
- const wrote = touched.some((e) => want.test(e.name) || also?.test(e.name)) && need(standing);
123
- const present = wrote || ((job.kind !== 'task' || job.redo === true) && need(standing));
113
+ const wrote = has(changedArtifacts(artifactsBefore, standing));
114
+ const present = wrote || ((job.kind !== 'task' || job.redo === true) && has(standing));
124
115
  if (!present) {
125
116
  return report({
126
117
  turnId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowviant",
3
- "version": "0.106.0",
3
+ "version": "0.107.0",
4
4
  "description": "Run your own coding CLIs as build agents for Flowviant — Claude Code, Codex or Antigravity, on your own credentials. Holds your sessions, keeps a worktree per tab, and ships branches on your word.",
5
5
  "type": "module",
6
6
  "bin": {