@vecteur/cli 0.4.1 → 0.4.3
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 +1 -1
- package/src/contract.js +246 -1
- package/src/format.js +27 -2
package/package.json
CHANGED
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:5fe54838e61336acc12ae1ff2409970a21fac99c7457383ca9c7a6d8c6e882cc;run-wire-sha256:
|
|
2
|
+
export const OWNER_CONTRACT_IDENTITY = "api-sha256:5fe54838e61336acc12ae1ff2409970a21fac99c7457383ca9c7a6d8c6e882cc;run-wire-sha256:79ea7f059ca20d8f66533d76d90342915487199f7e59fd73c2058a0d55fb0b3a";
|
|
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","synthesis","artifact","cta","state_advanced","terminal","heartbeat","assistant_delta","step_label","context_frame","knowledge_binding","engineering_projection","provider_attempt","clarification"]);
|
|
5
5
|
export const TERMINAL_FIELDS = Object.freeze(["answer","defect","evidence_dag","instruction_source","intent","missing_input_need","missing_inputs","provider_retry_offer","recovery","state","steps"]);
|
|
@@ -45,3 +45,248 @@ const usage=(v)=>exact(v,["coverage","provider_id","model_id","observed_at","bil
|
|
|
45
45
|
export const validateAdmitted=(v)=>shape(v,["run_id","state_hash","result_hash","requested_route_id","profile","terminal","usage","events"],["duration_seconds"])&&[v.run_id,v.state_hash,v.result_hash,v.requested_route_id,v.profile].every(text)&&["ok","partial","blocked","refused","infeasible_physics","capability_gap"].includes(v.terminal)&&usage(v.usage)&&Array.isArray(v.events)&&(v.duration_seconds===undefined||v.duration_seconds===null||(integer(v.duration_seconds)&&v.duration_seconds>=0));
|
|
46
46
|
export const validatePatCreated=(v)=>exact(v,["id","name","token","prefix","project_id","scopes","created_at","expires_at"])&&[v.id,v.name,v.token,v.prefix].every(text)&&(v.project_id===null||text(v.project_id))&&Array.isArray(v.scopes)&&v.scopes.every(s=>["project:read","project:execute"].includes(s))&&integer(v.created_at)&&integer(v.expires_at);
|
|
47
47
|
export const validateDeviceAuthorization=(v)=>exact(v,["device_code","user_code","verification_uri","verification_uri_complete","expires_at","interval"])&&[v.device_code,v.user_code,v.verification_uri,v.verification_uri_complete].every(text)&&integer(v.expires_at)&&integer(v.interval)&&v.interval>=0;
|
|
48
|
+
// --- frontend/src/run/tasks.ts, transpiled: the task projection the canvas and this door share ---
|
|
49
|
+
export function stepVerb(step) {
|
|
50
|
+
return (step.method?.verb ?? '').replace(/^mcp__[a-z0-9]+__/i, '');
|
|
51
|
+
}
|
|
52
|
+
const PROGRAM_ROW = /^call_(\d+)_steps$/;
|
|
53
|
+
export function programNodes(step) {
|
|
54
|
+
if (stepVerb(step) !== 'run_program')
|
|
55
|
+
return null;
|
|
56
|
+
const nodes = [];
|
|
57
|
+
for (const row of step.inputs ?? []) {
|
|
58
|
+
if (!PROGRAM_ROW.test(row.name) || !Array.isArray(row.value))
|
|
59
|
+
continue;
|
|
60
|
+
for (const entry of row.value) {
|
|
61
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
62
|
+
return null;
|
|
63
|
+
const { bind, verb } = entry;
|
|
64
|
+
if (typeof bind !== 'string' || typeof verb !== 'string' || !verb)
|
|
65
|
+
return null;
|
|
66
|
+
nodes.push({ bind, verb });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return nodes.length ? nodes : null;
|
|
70
|
+
}
|
|
71
|
+
export function attemptState(step) {
|
|
72
|
+
const produced = (step.outputs ?? []).length > 0;
|
|
73
|
+
if (!produced && (step.refusals ?? []).length > 0)
|
|
74
|
+
return 'refused';
|
|
75
|
+
return produced ? 'computed' : 'open';
|
|
76
|
+
}
|
|
77
|
+
function attemptOf(step) {
|
|
78
|
+
const state = attemptState(step);
|
|
79
|
+
return { step, state, code: state === 'refused' ? (step.refusals?.[0]?.code ?? 'refused') : null };
|
|
80
|
+
}
|
|
81
|
+
export function taskGroups(steps) {
|
|
82
|
+
const tasks = [];
|
|
83
|
+
let open = null;
|
|
84
|
+
for (const step of steps) {
|
|
85
|
+
const verb = stepVerb(step);
|
|
86
|
+
if (verb === 'thinking')
|
|
87
|
+
continue;
|
|
88
|
+
const nodes = programNodes(step);
|
|
89
|
+
const verbs = nodes ? [...new Set(nodes.map((node) => node.verb))] : [];
|
|
90
|
+
if (nodes && open && verbs.some((verb) => open.verbs.includes(verb))) {
|
|
91
|
+
const attempt = attemptOf(step);
|
|
92
|
+
open.attempts.push(attempt);
|
|
93
|
+
if (attempt.state === 'refused')
|
|
94
|
+
open.refused.push(attempt);
|
|
95
|
+
for (const verb of verbs)
|
|
96
|
+
if (!open.verbs.includes(verb))
|
|
97
|
+
open.verbs.push(verb);
|
|
98
|
+
open.state = attempt.state;
|
|
99
|
+
if (attempt.state === 'computed')
|
|
100
|
+
open = null;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const attempt = attemptOf(step);
|
|
104
|
+
const task = {
|
|
105
|
+
id: step.id, program: nodes !== null, verbs,
|
|
106
|
+
attempts: [attempt], refused: attempt.state === 'refused' ? [attempt] : [],
|
|
107
|
+
state: attempt.state,
|
|
108
|
+
};
|
|
109
|
+
tasks.push(task);
|
|
110
|
+
if (nodes)
|
|
111
|
+
open = attempt.state === 'computed' ? null : task;
|
|
112
|
+
else if (verb !== 'phys_search')
|
|
113
|
+
open = null;
|
|
114
|
+
}
|
|
115
|
+
return tasks;
|
|
116
|
+
}
|
|
117
|
+
const INTENT_ROW = /^call_(\d+)_intent$/;
|
|
118
|
+
const MISSION_ROW = /^call_(\d+)_(intent|hoisted_requirements|unmet_wanted)$/;
|
|
119
|
+
const UNMET_ROW = /^call_(\d+)_unmet_wanted$/;
|
|
120
|
+
const HOISTED_ROW = /^call_(\d+)_hoisted_requirements$/;
|
|
121
|
+
export function isIntentRow(name) {
|
|
122
|
+
return MISSION_ROW.test(name);
|
|
123
|
+
}
|
|
124
|
+
const words = (value) => (Array.isArray(value) ? value.filter((item) => typeof item === 'string' && item.trim() !== '').map((item) => item.trim()) : []);
|
|
125
|
+
function asThresholds(value) {
|
|
126
|
+
if (!Array.isArray(value))
|
|
127
|
+
return [];
|
|
128
|
+
const out = [];
|
|
129
|
+
for (const row of value) {
|
|
130
|
+
if (!row || typeof row !== 'object' || Array.isArray(row))
|
|
131
|
+
continue;
|
|
132
|
+
const { quantity, comparator, limit, unit, quote } = row;
|
|
133
|
+
if (typeof quantity !== 'string' || typeof comparator !== 'string' || typeof limit !== 'number' || typeof quote !== 'string')
|
|
134
|
+
continue;
|
|
135
|
+
out.push({ quantity, comparator, limit, unit: typeof unit === 'string' ? unit : '', quote });
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
export function stepIntent(step) {
|
|
140
|
+
let found = null;
|
|
141
|
+
for (const row of step.inputs ?? []) {
|
|
142
|
+
if (!INTENT_ROW.test(row.name))
|
|
143
|
+
continue;
|
|
144
|
+
const value = row.value;
|
|
145
|
+
if (typeof value === 'string') {
|
|
146
|
+
found = { title: value.trim(), rationale: '', wanted: [], thresholds: [] };
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
150
|
+
continue;
|
|
151
|
+
const { title, rationale, wanted, thresholds } = value;
|
|
152
|
+
found = {
|
|
153
|
+
title: typeof title === 'string' ? title.trim() : '',
|
|
154
|
+
rationale: typeof rationale === 'string' ? rationale.trim() : '',
|
|
155
|
+
wanted: words(wanted),
|
|
156
|
+
thresholds: asThresholds(thresholds),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return found;
|
|
160
|
+
}
|
|
161
|
+
export function hoistedRequirements(step) {
|
|
162
|
+
const out = [];
|
|
163
|
+
for (const row of step.inputs ?? []) {
|
|
164
|
+
if (!HOISTED_ROW.test(row.name) || !Array.isArray(row.value))
|
|
165
|
+
continue;
|
|
166
|
+
for (const entry of row.value) {
|
|
167
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
168
|
+
continue;
|
|
169
|
+
const { quantity, comparator, limit, unit } = entry;
|
|
170
|
+
if (typeof quantity !== 'string' || typeof comparator !== 'string' || typeof limit !== 'number')
|
|
171
|
+
continue;
|
|
172
|
+
out.push({ quantity: quantity.replace(/^\$/, ''), comparator, limit, unit: typeof unit === 'string' ? unit : '' });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
export function hoistedQuotes(step) {
|
|
178
|
+
const quotes = new Map();
|
|
179
|
+
const thresholds = stepIntent(step)?.thresholds ?? [];
|
|
180
|
+
for (const row of hoistedRequirements(step)) {
|
|
181
|
+
const matches = thresholds.filter((threshold) => (threshold.comparator === row.comparator && threshold.limit === row.limit && threshold.unit === row.unit));
|
|
182
|
+
if (matches.length === 1)
|
|
183
|
+
quotes.set(row.quantity, matches[0].quote);
|
|
184
|
+
}
|
|
185
|
+
return quotes;
|
|
186
|
+
}
|
|
187
|
+
export function unmetWanted(step) {
|
|
188
|
+
let found = [];
|
|
189
|
+
for (const row of step.outputs ?? []) {
|
|
190
|
+
if (!row || typeof row !== 'object')
|
|
191
|
+
continue;
|
|
192
|
+
const { name, value } = row;
|
|
193
|
+
if (typeof name === 'string' && UNMET_ROW.test(name))
|
|
194
|
+
found = words(value);
|
|
195
|
+
}
|
|
196
|
+
return found;
|
|
197
|
+
}
|
|
198
|
+
const sameWords = (left, right) => {
|
|
199
|
+
const norm = (value) => value.replace(/[.!?]+$/u, '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
200
|
+
const a = norm(left);
|
|
201
|
+
return Boolean(a) && a === norm(right);
|
|
202
|
+
};
|
|
203
|
+
export function intentTitle(step) {
|
|
204
|
+
const recorded = (step.title ?? '').trim();
|
|
205
|
+
const intent = stepIntent(step);
|
|
206
|
+
return recorded && intent && sameWords(recorded, intent.title) ? recorded : '';
|
|
207
|
+
}
|
|
208
|
+
export const ENGINEERING_VERBS = {
|
|
209
|
+
analyze_acquisition_contact_latency: 'Acquisition-to-contact latency',
|
|
210
|
+
analyze_eclipse_profile: 'Eclipse profile',
|
|
211
|
+
close_link: 'Link closure',
|
|
212
|
+
resolve_payload: 'Image resolution',
|
|
213
|
+
resolve_utc_interval: 'Time window',
|
|
214
|
+
sample_projection: 'Sampled projection',
|
|
215
|
+
select_propulsion: 'Propulsion selection',
|
|
216
|
+
size_access: 'Coverage and revisit',
|
|
217
|
+
size_annual_power: 'Annual power',
|
|
218
|
+
size_burn_sequence: 'Burn sequence',
|
|
219
|
+
size_delta_v_budget: 'Delta-v budget',
|
|
220
|
+
size_drag_makeup: 'Drag make-up',
|
|
221
|
+
size_ground_contacts: 'Ground contacts',
|
|
222
|
+
size_hohmann_transfer: 'Hohmann transfer',
|
|
223
|
+
size_hybrid_transfer: 'Hybrid transfer',
|
|
224
|
+
size_in_view: 'Satellites in view',
|
|
225
|
+
size_orbit: 'Orbit geometry',
|
|
226
|
+
size_phase_duration: 'Phasing duration',
|
|
227
|
+
size_platform: 'Platform sizing',
|
|
228
|
+
size_pnt_geometry: 'Navigation geometry',
|
|
229
|
+
size_power: 'Power system',
|
|
230
|
+
size_propellant: 'Propellant',
|
|
231
|
+
size_raan_change: 'Plane change',
|
|
232
|
+
size_repeat_orbit: 'Repeat orbit',
|
|
233
|
+
size_station_keeping: 'Station keeping',
|
|
234
|
+
size_topology: 'Topology search',
|
|
235
|
+
size_transfer: 'Orbit transfer',
|
|
236
|
+
};
|
|
237
|
+
export function engineeringVerbLabel(verb) {
|
|
238
|
+
return ENGINEERING_VERBS[verb] ?? verb.replace(/^size_/, '').replaceAll('_', ' ')
|
|
239
|
+
.replace(/^./, (letter) => letter.toUpperCase());
|
|
240
|
+
}
|
|
241
|
+
export function taskTitle(task) {
|
|
242
|
+
for (let index = task.attempts.length - 1; index >= 0; index -= 1) {
|
|
243
|
+
const attempt = task.attempts[index];
|
|
244
|
+
if (attempt.state !== 'computed')
|
|
245
|
+
continue;
|
|
246
|
+
const title = intentTitle(attempt.step);
|
|
247
|
+
if (title)
|
|
248
|
+
return title;
|
|
249
|
+
}
|
|
250
|
+
return task.verbs.map(engineeringVerbLabel).join(' · ');
|
|
251
|
+
}
|
|
252
|
+
export function taskRationale(task) {
|
|
253
|
+
for (let index = task.attempts.length - 1; index >= 0; index -= 1) {
|
|
254
|
+
const rationale = stepIntent(task.attempts[index].step)?.rationale ?? '';
|
|
255
|
+
if (rationale)
|
|
256
|
+
return rationale;
|
|
257
|
+
}
|
|
258
|
+
return '';
|
|
259
|
+
}
|
|
260
|
+
export const REFUSAL_WORDS = {
|
|
261
|
+
support_mismatch: 'sources',
|
|
262
|
+
unit_error: 'units',
|
|
263
|
+
authoring_defect: 'program',
|
|
264
|
+
owner_refusal: 'bound',
|
|
265
|
+
INVALID_TOOL_OUTPUT: 'output',
|
|
266
|
+
missing_knowledge_dependency: 'knowledge',
|
|
267
|
+
capability_gap: 'capability',
|
|
268
|
+
budget: 'budget',
|
|
269
|
+
missing_input: 'input',
|
|
270
|
+
};
|
|
271
|
+
export function taskStateWord(task) {
|
|
272
|
+
if (task.state === 'open')
|
|
273
|
+
return 'no result';
|
|
274
|
+
if (task.state !== 'refused')
|
|
275
|
+
return task.state;
|
|
276
|
+
const word = REFUSAL_WORDS[task.attempts[task.attempts.length - 1]?.code ?? ''];
|
|
277
|
+
return word ? `refused — ${word}` : 'refused';
|
|
278
|
+
}
|
|
279
|
+
export function taskUnmet(task) {
|
|
280
|
+
if (task.state !== 'computed')
|
|
281
|
+
return [];
|
|
282
|
+
return unmetWanted(task.attempts[task.attempts.length - 1].step);
|
|
283
|
+
}
|
|
284
|
+
export function taskMeta(task) {
|
|
285
|
+
const count = task.attempts.length;
|
|
286
|
+
const unmet = taskUnmet(task).length;
|
|
287
|
+
return [
|
|
288
|
+
taskStateWord(task),
|
|
289
|
+
count > 1 ? `${count} attempts` : '',
|
|
290
|
+
unmet ? `${unmet} still to compute` : '',
|
|
291
|
+
].filter(Boolean).join(' · ');
|
|
292
|
+
}
|
package/src/format.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ORIGIN_KINDS, ORIGIN_WORD, TERMINAL_FIELDS, TERMINAL_RECOVERY_DECLARATIONS,
|
|
3
|
-
validateArtifact, validateSynthesis,
|
|
3
|
+
isIntentRow, taskGroups, taskMeta, taskTitle, validateArtifact, validateSynthesis,
|
|
4
4
|
} from "./contract.js";
|
|
5
5
|
import { CliError } from "./errors.js";
|
|
6
6
|
|
|
@@ -64,7 +64,8 @@ export function originWord(origin) {
|
|
|
64
64
|
/** `name = value unit (origin word, ref)` — one line per input, in wire order. */
|
|
65
65
|
export function inputLines(inputs) {
|
|
66
66
|
if (!Array.isArray(inputs)) return [];
|
|
67
|
-
|
|
67
|
+
// `call_N_intent` is the step's title and rationale (v1.9.36), not one of its inputs.
|
|
68
|
+
return inputs.filter((row) => !isIntentRow(String(row?.name ?? ""))).map((row) => {
|
|
68
69
|
const unit = typeof row?.unit === "string" && row.unit ? ` ${row.unit}` : "";
|
|
69
70
|
const word = originWord(row?.origin);
|
|
70
71
|
// The REF is what makes the word actionable — "your file" is a category, "your file
|
|
@@ -76,6 +77,28 @@ export function inputLines(inputs) {
|
|
|
76
77
|
});
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
/**
|
|
81
|
+
* TASK OVER STEPS, SPOKEN BY THIS DOOR TOO.
|
|
82
|
+
*
|
|
83
|
+
* The owner's hybrid run of 2026-09-11 was eleven model turns for one objective, nine of them
|
|
84
|
+
* refused `run_program` calls. `taskGroups` in `contract.js` — the same bytes the canvas
|
|
85
|
+
* renders, generated from `frontend/src/run/tasks.ts` — folds them into the tasks the reader
|
|
86
|
+
* asked for: one line per task, its verbs as the title, what it is and how many attempts it
|
|
87
|
+
* took. Binding receipts (`binding-*`) are the workspace register's rows, not work.
|
|
88
|
+
*/
|
|
89
|
+
export function taskLines(events) {
|
|
90
|
+
const steps = events.flatMap((event) => {
|
|
91
|
+
if (event?.kind !== "step_finished") return [];
|
|
92
|
+
const step = event.payload?.step ?? event.payload;
|
|
93
|
+
return step && typeof step === "object" && typeof step.id === "string"
|
|
94
|
+
&& !step.id.startsWith("binding-") ? [step] : [];
|
|
95
|
+
});
|
|
96
|
+
return taskGroups(steps).filter((task) => task.program).map((task) => {
|
|
97
|
+
const refused = task.refused.length ? ` (${task.refused.length} refused)` : "";
|
|
98
|
+
return ` ${plainLine(`${taskTitle(task)} — ${taskMeta(task)}${refused}`)}`;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
79
102
|
function shellQuote(value) {
|
|
80
103
|
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
81
104
|
}
|
|
@@ -166,8 +189,10 @@ export function humanRun(value, { projectId, cost } = {}) {
|
|
|
166
189
|
if (!artifacts.every(validateArtifact)) {
|
|
167
190
|
throw new CliError("artifact_invalid", "Run carries malformed artifact metadata");
|
|
168
191
|
}
|
|
192
|
+
const tasks = taskLines(value.events);
|
|
169
193
|
const lines = [];
|
|
170
194
|
if (terminal.answer !== undefined) lines.push(terminal.answer, "");
|
|
195
|
+
if (tasks.length) lines.push("Tasks:", ...tasks, "");
|
|
171
196
|
if (origins.length) lines.push("Inputs and origins:", ...origins, "");
|
|
172
197
|
if (artifacts.length) {
|
|
173
198
|
lines.push("Artifacts:");
|