@vecteur/cli 0.5.6 → 0.5.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vecteur/cli",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vecteur": "bin/vecteur.js"
package/src/contract.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // GENERATED by scripts/gen-contract.mjs from owner TypeScript projections.
2
- export const OWNER_CONTRACT_IDENTITY = "api-sha256:8f81ea544eecd9e81da3fd56ad816ac24991cb0e68f3b1cb45180711eaf16056;run-wire-sha256:823acb39f2ff5bca7ea54db4beb4589d681bedc5673867a6e6dcdf5c212cf7db";
2
+ export const OWNER_CONTRACT_IDENTITY = "api-sha256:8f81ea544eecd9e81da3fd56ad816ac24991cb0e68f3b1cb45180711eaf16056;run-wire-sha256:e474079a0453c07e8249eddfc6781bb0c053ee81d08b0c7988e0670ad14d302b";
3
3
  export const RECOVERY_ACTIONS = Object.freeze({"delete_project":{"label":"Delete a project","href":"/projects"},"upgrade_plan":{"label":"Upgrade your plan","href":"/dashboard/billing"},"wait_for_reset":{"label":"Wait until the UTC reset","href":null}});
4
4
  export const EVENT_KINDS = Object.freeze(["source","step_started","step_finished","tool_call","tool_result","synthesis","artifact","terminal","heartbeat","assistant_delta","knowledge_binding","engineering_projection","provider_attempt","clarification","clarification_evidence","input_rows"]);
5
5
  export const RETIRED_EVENT_KINDS = Object.freeze(["context_frame","cta","step_label"]);
package/src/format.js CHANGED
@@ -90,17 +90,133 @@ const CAUSE_WORD = Object.freeze({
90
90
  answer_now: "stopped to answer now; what it printed stands",
91
91
  });
92
92
 
93
+ /**
94
+ * THE ENGINEERING DOOR, SPOKEN BY THIS DOOR TOO (v2.4 S3; the web reads the same bytes in
95
+ * `frontend/src/run/door.ts`, and both readers are held to `tests/fixtures/door-observations.json`).
96
+ *
97
+ * A `python` call whose summary is `run <verb>` is a call of one published engineering method;
98
+ * its result's stdout is the bounded observation `laplace_ope/door_tail.py` prints. The reader is
99
+ * tolerant: the excerpt keeps a long stream's first and last lines around `[…]`, so every section
100
+ * is optional and the last `RESULT:` line is the headline of last resort.
101
+ */
102
+ const DOOR_SUMMARY = /^run ([a-z][a-z0-9]*(?:_[a-z0-9]+)+)$/;
103
+ const DOOR_HEAD = /^([a-z][a-z0-9_]*): ok\b/;
104
+ const QUANTITY_LINE = /^ {2}(\S+) = (.*?)(?: {2}-- (.*))?$/;
105
+ const SECTION_OPEN = /^(inputs \(as given\):|inputs the owner defaulted|not set \(|quantities:|validity:|warnings:|files:|RESULT:)/;
106
+ const NUMBER = /^(-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?|true|false|nan|-?inf)(?: (.+))?$/;
107
+
108
+ function quantityParts(text) {
109
+ const trimmed = text.trim();
110
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) return { value: trimmed, unit: "" };
111
+ const number = NUMBER.exec(trimmed);
112
+ return number ? { value: number[1], unit: (number[2] ?? "").trim() } : { value: trimmed, unit: "" };
113
+ }
114
+
115
+ /** `{verb, headline: [{id, label, value, unit}], warnings}` of an observation, or null. */
116
+ export function doorObservation(text) {
117
+ const lines = String(text ?? "").split("\n").map(line => line.replace(/\s+$/, ""));
118
+ const head = DOOR_HEAD.exec(lines.find(line => line.trim()) ?? "");
119
+ if (!head) return null;
120
+ const cut = new Set();
121
+ lines.forEach((line, index) => {
122
+ if (line.trim() === "[…]") [index - 1, index, index + 1].forEach(n => cut.add(n));
123
+ });
124
+ const quantities = [];
125
+ const warnings = [];
126
+ let resultLine = "";
127
+ let section = "";
128
+ for (const [index, line] of lines.entries()) {
129
+ if (index === 0 || cut.has(index)) continue;
130
+ const open = SECTION_OPEN.exec(line);
131
+ if (open) {
132
+ section = open[1];
133
+ if (section === "warnings:" && line.includes("none")) section = "";
134
+ if (section === "RESULT:") resultLine = line.slice("RESULT:".length).trim();
135
+ continue;
136
+ }
137
+ if (section === "quantities:") {
138
+ const row = QUANTITY_LINE.exec(line);
139
+ if (row) quantities.push({ id: row[1], label: row[3]?.trim() || row[1].replaceAll("_", " "), ...quantityParts(row[2]) });
140
+ } else if (section === "warnings:" && /^ {2}- /.test(line)) {
141
+ warnings.push(line.replace(/^ {2}- /, "").trim());
142
+ }
143
+ }
144
+ const byId = new Map(quantities.map(quantity => [quantity.id, quantity]));
145
+ const headline = [];
146
+ for (const part of resultLine.split(/; (?=\S+ = )/)) {
147
+ const row = /^(\S+) = (.*)$/.exec(part.trim());
148
+ if (!row) continue;
149
+ headline.push({ id: row[1], label: byId.get(row[1])?.label ?? row[1].replaceAll("_", " "), ...quantityParts(row[2]) });
150
+ }
151
+ // No parsable RESULT line: the last three physical quantities stand in (else the first three).
152
+ if (!headline.length) headline.push(...quantities.filter(quantity => quantity.unit).slice(-3));
153
+ if (!headline.length) headline.push(...quantities.slice(0, 3));
154
+ return { verb: head[1], headline, warnings };
155
+ }
156
+
157
+ /** The door calls of each row, by `step_id`: `{purpose, verb, observation, refusal}` in call order. */
158
+ function doorCallsByStep(events) {
159
+ const results = new Map();
160
+ for (const event of events) {
161
+ if (event?.kind === "tool_result" && typeof event.payload?.call_id === "string") {
162
+ results.set(event.payload.call_id, event.payload);
163
+ }
164
+ }
165
+ const rows = new Map();
166
+ for (const event of events) {
167
+ const call = event?.kind === "tool_call" ? event.payload : null;
168
+ const verb = call?.tool === "python" ? DOOR_SUMMARY.exec(String(call.summary ?? "").trim())?.[1] : null;
169
+ if (!verb) continue;
170
+ const result = results.get(call.call_id);
171
+ const observation = result?.status === "ok" ? doorObservation(result.stdout_excerpt) : null;
172
+ if (result?.status === "ok" && observation?.verb !== verb) continue; // free code that opened with `# run x_y`
173
+ const refusal = result && result.status !== "ok"
174
+ ? String(result.stderr_excerpt || result.stdout_excerpt || "").trim().split("\n").filter(Boolean).slice(-3).join(" · ") : "";
175
+ const list = rows.get(call.step_id) ?? [];
176
+ list.push({ purpose: String(call.title ?? "").trim(), verb, observation, refusal, running: !result });
177
+ rows.set(call.step_id, list);
178
+ }
179
+ return rows;
180
+ }
181
+
182
+ function doorLine({ purpose, verb, observation, refusal, running }) {
183
+ const said = observation
184
+ ? observation.headline.map(quantity => `${quantity.label} ${quantity.value}${quantity.unit ? ` ${quantity.unit}` : ""}`).join(", ")
185
+ : (refusal ? `refused: ${refusal}` : running ? "running" : "computed");
186
+ const lines = [` ▸ ${plainLine(purpose || verb)} — ${verb}: ${plainLine(said)}`];
187
+ for (const warning of (observation?.warnings ?? []).slice(0, 2)) lines.push(` ! ${plainLine(warning)}`);
188
+ return lines.join("\n");
189
+ }
190
+
93
191
  export function taskLines(events) {
192
+ const doors = doorCallsByStep(events);
193
+ // The conclusion is engineering work (it prints the answer from evidence); its text is the
194
+ // synthesis, so the row stays one line.
195
+ const concluded = new Set(events.filter((event) => event?.kind === "tool_call" && event.payload?.tool === "conclude")
196
+ .map((event) => event.payload.step_id));
197
+ // Only a run that called the door tells engineering rows from execution rows; any other run
198
+ // prints exactly as before.
199
+ const distinguishes = doors.size > 0 || concluded.size > 0;
94
200
  return events.flatMap((event) => {
95
201
  if (event?.kind !== "step_finished") return [];
96
202
  const row = event.payload;
97
203
  if (!row || typeof row !== "object" || typeof row.step_id !== "string") return [];
204
+ const outcome = CAUSE_WORD[row.cause] ?? row.status;
205
+ const result = typeof row.result_line === "string" && row.result_line.trim() ? row.result_line : "";
206
+ if (distinguishes) {
207
+ const calls = doors.get(row.step_id);
208
+ if (calls?.length) {
209
+ const rest = row.status === "computed" ? [] : [` (${plainLine(row.title)} — ${outcome})`];
210
+ return [[...calls.map(doorLine), ...rest].join("\n")];
211
+ }
212
+ if (row.status === "asked" || concluded.has(row.step_id)) return [` ▸ ${plainLine(`${row.title} — ${outcome}`)}`];
213
+ // Execution, compressed to one line: what ran, how it ended, what it printed.
214
+ return [` · ${plainLine(`${row.title} — ${outcome}${result ? `: ${result}` : ""}`)}`];
215
+ }
98
216
  const attempts = Number.isInteger(row.attempts) && row.attempts > 0
99
217
  ? ` · ${row.attempts} ${row.attempts === 1 ? "attempt" : "attempts"}` : "";
100
- const result = typeof row.result_line === "string" && row.result_line.trim()
101
- ? `\n ${plainLine(row.result_line)}` : "";
102
- const outcome = CAUSE_WORD[row.cause] ?? row.status;
103
- return [` ${plainLine(`${row.title} — ${outcome}${attempts}`)}${result}`];
218
+ const printed = result ? `\n ${plainLine(result)}` : "";
219
+ return [` ${plainLine(`${row.title} — ${outcome}${attempts}`)}${printed}`];
104
220
  });
105
221
  }
106
222