flowviant 0.92.1 → 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.
@@ -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())
@@ -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.1",
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": {