flowviant 0.92.0 → 0.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,7 +38,7 @@ It shows a short code. Open your project's **Workbench** in Flowviant and enter
38
38
  Prefer an explicit token? Create a machine credential in the app and pass it directly:
39
39
 
40
40
  ```bash
41
- FLOWVIANT_FLEET=fva_… npx flowviant@latest
41
+ FLOWVIANT_MACHINE_TOKEN=fva_… npx flowviant@latest # FLOWVIANT_FLEET still works
42
42
  ```
43
43
 
44
44
  Launch with `@latest` so each start pulls the newest published version — a bare `npx flowviant` can reuse a stale cache. A running daemon also keeps itself current: from **0.58.0** it restarts itself through `npx flowviant@latest` when a new version ships, so npx launches stay up to date the same way a global install does. (Before 0.58.0 that was only true of a global install — under npx the daemon printed a notice and stayed put, which is how machines ended up sitting several releases back.) It only ever restarts when no turn is running. `FLOWVIANT_NO_UPDATE=1` makes it nag-only; `flowviant update` updates now.
@@ -65,7 +65,7 @@ The daemon never executes anything the repository declares. An earlier version r
65
65
 
66
66
  | Env | What runs |
67
67
  | --- | --- |
68
- | _(stored login)_ or `FLOWVIANT_FLEET` | **the daemon** — the project's machine, serving its sessions |
68
+ | _(stored login)_ or `FLOWVIANT_MACHINE_TOKEN` (`FLOWVIANT_FLEET` still read) | **the daemon** — the project's machine, serving its sessions |
69
69
  | `FLOWVIANT_SAFE=1` | restrict the toolset instead of running unattended |
70
70
 
71
71
  ## Not freezing the box
package/bin/cli.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * ONE mode, one credential:
7
7
  *
8
- * FLOWVIANT_FLEET=fva_… npx flowviant@latest # the machine daemon
8
+ * FLOWVIANT_MACHINE_TOKEN=fva_… npx flowviant@latest # the machine daemon, headless
9
9
  *
10
10
  * `FLOWVIANT_TOKEN` (one worker, current checkout) and `FLOWVIANT_TOKENS` (a
11
11
  * comma list, one worktree each) stood beside it until 2026-08-19. Both ran the
@@ -46,7 +46,8 @@
46
46
  * because a daemon on another computer is invisible from here.
47
47
  *
48
48
  * Env:
49
- * FLOWVIANT_FLEET the machine credential (or use `flowviant login`).
49
+ * FLOWVIANT_MACHINE_TOKEN the machine credential (or use `flowviant login`);
50
+ * `FLOWVIANT_FLEET` is the old name and still works.
50
51
  * FLOWVIANT_API_URL default https://api.flowviant.com/api
51
52
  * FLOWVIANT_MCP_URL default <API_URL>/mcp
52
53
  * FLOWVIANT_FLEET_URL default <API_URL>/fleet/agents
@@ -358,7 +359,10 @@ const interactive = canPrompt() && process.env.FLOWVIANT_REEXEC !== '1';
358
359
  * asked). What was left was a person reading a list being told "no answer
359
360
  * in 60s — nothing started". A prompt a person can see waits for the
360
361
  * person; tty.mjs's header carries the argument. */
361
- const externalToken = process.argv.includes('--fleet') || Boolean(process.env.FLOWVIANT_FLEET);
362
+ const externalToken =
363
+ process.argv.includes('--fleet') ||
364
+ Boolean(process.env.FLOWVIANT_MACHINE_TOKEN) ||
365
+ Boolean(process.env.FLOWVIANT_FLEET);
362
366
 
363
367
  /** Re-exec a plain `flowviant` after an inline login — the login command's own
364
368
  * pattern: config.mjs read the store at IMPORT time, before the credential
@@ -157,6 +157,35 @@ export function parseProposal(text) {
157
157
  };
158
158
  }
159
159
 
160
+ /**
161
+ * The running account, off whichever shape the turn ended with — `{ progress }`
162
+ * or nothing at all.
163
+ *
164
+ * A SPREAD RATHER THAN A FIELD, so an absent account produces no key on the
165
+ * result and therefore no key on the settle body. That is the whole contract
166
+ * with the server: absence means KEEP the last account, and an empty string
167
+ * sent instead would be indistinguishable from an agent saying the branch now
168
+ * amounts to nothing.
169
+ *
170
+ * AN ABSURDITY BOUND HERE, NOT THE DISPLAY CAP (corrected 2026-09-22, in
171
+ * review). This sliced to the wire's own 1000 first, which quietly INVERTED the
172
+ * order the caller's own comment claims to keep: `envScrub` replaces EXACT
173
+ * values, so handing it a paragraph already cut at 1000 hands it a credential
174
+ * cut in half — it matches nothing and the surviving prefix ships. The scrub is
175
+ * the only thing standing between a model that pasted a secret into its own
176
+ * summary and the wire, and a cap must never run ahead of it. So this is the
177
+ * same absurdity bound `summary` takes one line up (8000): enough that nothing
178
+ * a model writes reaches the caller unbounded, far enough above the 1000 the
179
+ * wire cuts to that the scrub always sees whole values. `work.mjs` scrubs and
180
+ * THEN cuts to 1000, and the server clamps again because the wire never trusts
181
+ * a machine to have clamped.
182
+ */
183
+ const MAX_PROGRESS = 8000;
184
+ const progressOf = (v) => {
185
+ const t = typeof v.progress === 'string' ? v.progress.trim() : '';
186
+ return t ? { progress: t.slice(0, MAX_PROGRESS) } : {};
187
+ };
188
+
160
189
  /**
161
190
  * READING AN AGENT'S ANSWER at the end of a turn.
162
191
  *
@@ -170,6 +199,28 @@ export function parseProposal(text) {
170
199
  * `nothing`, which sends the agent to Stuck. Optimistic status from a machine
171
200
  * that quit is the one lie this board cannot afford, so anything ambiguous ends
172
201
  * up here rather than being read as success.
202
+ *
203
+ * ── AND IT CARRIES THE AGENT'S RUNNING ACCOUNT OF ITSELF (2026-09-22) ─────
204
+ *
205
+ * The owner: "we should add a brief summary of what the agent has done overall
206
+ * at the top that updates." `progress` is one more key on the SAME object —
207
+ * two or three cumulative sentences about the whole branch, rewritten fresh
208
+ * every turn — and it is read here because this is where everything the agent
209
+ * SAYS is already read. Nothing derives it: this file parses, it does not
210
+ * summarise, and a daemon that read the turn log and wrote its own paragraph
211
+ * would be the second brain the product forbids.
212
+ *
213
+ * ON BOTH SHAPES, because a turn that stopped to ask has still done work — an
214
+ * agent that built three quarters of a feature and then needed a decision is
215
+ * exactly the run whose account a person wants. It is NOT read off an
216
+ * unparseable turn, and that is not an omission: there is no object to read it
217
+ * from, and a `nothing` outcome means the CLI never got as far as saying
218
+ * anything about itself.
219
+ *
220
+ * MISSING IS MISSING. An older prompt, a resumed conversation that never saw
221
+ * the key, a model that dropped it — all of them leave it absent, and the
222
+ * caller omits the field rather than sending an empty string, so the server
223
+ * keeps the last account that was true instead of blanking the head.
173
224
  */
174
225
  export function parseTurnResult(text) {
175
226
  for (const v of candidateObjects(String(text ?? ''))) {
@@ -179,12 +230,13 @@ export function parseTurnResult(text) {
179
230
  // parks an agent with nothing to reply to. Treated as `nothing`, which
180
231
  // at least says truthfully that the machine went quiet.
181
232
  if (!question) continue;
182
- return { outcome: 'question', answer: question.slice(0, 8000) };
233
+ return { outcome: 'question', answer: question.slice(0, 8000), ...progressOf(v) };
183
234
  }
184
235
  if (v.status === 'delivered') {
185
236
  return {
186
237
  outcome: 'delivered',
187
238
  answer: (typeof v.summary === 'string' ? v.summary : '').slice(0, 8000),
239
+ ...progressOf(v),
188
240
  raised: Array.isArray(v.raised)
189
241
  ? v.raised
190
242
  .filter((r) => r && typeof r.title === 'string' && r.title.trim())
@@ -160,6 +160,7 @@ export function childEnv({ cwd, extra, deploy = false } = {}) {
160
160
  * purpose: the default is what almost every caller gets, and the sample is
161
161
  * about the default. */
162
162
  export const DROPPED_SAMPLE = [
163
+ 'FLOWVIANT_MACHINE_TOKEN',
163
164
  'FLOWVIANT_FLEET',
164
165
  'FLOWVIANT_FLEET_URL',
165
166
  'FLOWVIANT_API_URL',
@@ -290,8 +290,20 @@ export const PROCESS_STARTED_AT = new Date(
290
290
  // ambiguous store with a picker, adoptStoredCredential updates every importer
291
291
  // before the daemon touches the network. Everything before that point (the
292
292
  // resolution, the flags) is settled synchronously at import, as it always was.
293
+ // THE VARIABLE IS NAMED FOR WHAT IT IS (2026-09-21): `FLOWVIANT_MACHINE_TOKEN`.
294
+ // The owner met `FLOWVIANT_FLEET=… npx flowviant` in the app's headless
295
+ // credential panel and asked "what is flowviant fleet?" — a fair question,
296
+ // because a FLEET was a pool of worker lanes deleted 2026-08-19 and the word
297
+ // now names nothing a person can see. `FLOWVIANT_FLEET` stays as the fallback
298
+ // forever: a CI runner somewhere may have it in its secrets, and a rename that
299
+ // silently stops a headless box from authenticating is the substitution class
300
+ // this codebase floors everything against.
293
301
  export let FLEET_TOKEN =
294
- argFlag('--fleet') || process.env.FLOWVIANT_FLEET || CREDENTIAL.entry?.fleetToken || '';
302
+ argFlag('--fleet') ||
303
+ process.env.FLOWVIANT_MACHINE_TOKEN ||
304
+ process.env.FLOWVIANT_FLEET ||
305
+ CREDENTIAL.entry?.fleetToken ||
306
+ '';
295
307
 
296
308
  /**
297
309
  * WHICH PROJECT THIS DAEMON SERVES, or null when nothing on this box names one.
@@ -777,6 +777,7 @@ END YOUR TURN WITH ONE JSON OBJECT AND NOTHING AFTER IT, in a \`\`\`json fence:
777
777
  {
778
778
  "status": "delivered",
779
779
  "summary": "one or two sentences on what you actually changed",
780
+ "progress": "two or three sentences on what you have done on this branch SO FAR, across every card",
780
781
  "raised": [{ "title": "short title", "brief": "what is wrong and what you did" }]
781
782
  }
782
783
  \`\`\`
@@ -786,13 +787,20 @@ or, if you are stopping to ask:
786
787
  \`\`\`json
787
788
  {
788
789
  "status": "blocked",
790
+ "progress": "two or three sentences on what you have done on this branch SO FAR, across every card",
789
791
  "question": "the specific thing you need decided, in one or two sentences"
790
792
  }
791
793
  \`\`\`
792
794
 
793
795
  "raised" is optional and only for work you did that was NOT this card. Do not
794
796
  list the card itself there. Do not put commit shas in the summary — they are
795
- read from git.`;
797
+ read from git.
798
+
799
+ "progress" is REQUIRED on both shapes. It is the running account a person reads
800
+ at the top of this run, and it REPLACES the previous one whole — so write it
801
+ fresh every turn, covering everything you have done on this branch so far and
802
+ not only this card. Past tense, plain sentences, no commit shas and no card ids.
803
+ "summary" is about this one card; "progress" is about the branch.`;
796
804
 
797
805
  /**
798
806
  * The turn itself.
package/bin/lib/work.mjs CHANGED
@@ -5062,6 +5062,32 @@ export function createWorkManager({
5062
5062
  ...(commits.length ? { commits } : {}),
5063
5063
  ...(res.raised?.length ? { raised: res.raised } : {}),
5064
5064
  ...(usage ? { usage } : {}),
5065
+ /**
5066
+ * THE AGENT'S RUNNING ACCOUNT OF THIS BRANCH (2026-09-22).
5067
+ *
5068
+ * The owner: "we should add a brief summary of what the agent has done
5069
+ * overall at the top that updates." A RELAY — the agent wrote it in its
5070
+ * own final JSON object and nothing here composes, narrows or infers
5071
+ * one. It rides only this settle, which is the one that follows a
5072
+ * PARSED result: the two `nothing` settles above it come from a turn
5073
+ * that declared no outcome at all, so there is no account to carry and
5074
+ * inventing one would be the machine speaking for the agent.
5075
+ *
5076
+ * SCRUBBED BEFORE IT IS CUT, the order that matters and the one the
5077
+ * check's output lane learned the expensive way: `envScrub` replaces
5078
+ * EXACT values, so a paragraph capped first hands the scrub a
5079
+ * credential already cut in half — it matches nothing and the surviving
5080
+ * prefix ships. `parseTurnResult` trims it and bounds it for absurdity
5081
+ * at 8000, DELIBERATELY above the 1000 below, so this slice is the
5082
+ * first one a value of any interest meets and the scrub has already
5083
+ * run when it does. The first cut of this feature bounded the parser at
5084
+ * 1000 as well, which read identically and quietly put the cap first.
5085
+ *
5086
+ * OMITTED WHEN THE TURN DID NOT WRITE ONE, never sent empty. Absence is
5087
+ * the server's signal to KEEP the last account that was true; an empty
5088
+ * string would blank the head because a model dropped a key.
5089
+ */
5090
+ ...(res.progress ? { progress: envScrub(res.progress).slice(0, 1000) } : {}),
5065
5091
  branch,
5066
5092
  worktree: wt,
5067
5093
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowviant",
3
- "version": "0.92.0",
3
+ "version": "0.93.0",
4
4
  "description": "Run your own coding CLIs as build agents for Flowviant \u2014 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": {