@vzn/vx 0.0.652 → 0.0.653
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 +2 -2
- package/launcher.sh +2 -1
- package/package.json +5 -5
- package/schemas/error.json +1 -1
- package/schemas/show.json +1 -0
- package/skills/vx/SKILL.md +3 -3
- package/src/bin.ts +25 -3
- package/src/cache/archive.ts +196 -31
- package/src/cache/cache.ts +863 -225
- package/src/cache/checkpointer.ts +102 -0
- package/src/cache/file-hashes.ts +2 -1
- package/src/cache/fold-memo.ts +24 -4
- package/src/cache/fs-clock.ts +26 -0
- package/src/cache/git-inputs.ts +188 -22
- package/src/cache/history-stream.ts +22 -5
- package/src/cache/index.ts +10 -1
- package/src/cache/inputs.ts +573 -152
- package/src/cache/key-fold.ts +1 -1
- package/src/cache/layer.ts +23 -10
- package/src/cache/layered-cache.ts +28 -7
- package/src/cache/output-index.ts +106 -44
- package/src/cache/plugin-cache.ts +2 -2
- package/src/cache/policy.ts +6 -2
- package/src/cache/remote-lane.ts +3 -1
- package/src/cache/run-history.ts +50 -10
- package/src/cache/schema.ts +42 -9
- package/src/cli/cache.ts +147 -34
- package/src/cli/completions.ts +2 -2
- package/src/cli/core-alias.ts +9 -2
- package/src/cli/docs.ts +4 -2
- package/src/cli/help.ts +25 -2
- package/src/cli/index.ts +38 -7
- package/src/cli/info.ts +4 -3
- package/src/cli/init.ts +55 -20
- package/src/cli/last.ts +184 -41
- package/src/cli/lock.ts +187 -24
- package/src/cli/plan-format.ts +24 -21
- package/src/cli/plugin-commands.ts +6 -1
- package/src/cli/plugin-templates.ts +1 -1
- package/src/cli/run.ts +68 -34
- package/src/cli/select.ts +160 -64
- package/src/cli/show.ts +63 -18
- package/src/cli/task-verb.ts +12 -7
- package/src/cli/upgrade.ts +210 -76
- package/src/cli/watch-filter.ts +88 -15
- package/src/cli/watch-fs.ts +34 -47
- package/src/cli/watch-judge.ts +72 -8
- package/src/cli/watch-set.ts +6 -4
- package/src/cli/watch.ts +179 -25
- package/src/cli/why.ts +107 -33
- package/src/cli/workspace-config.ts +11 -1
- package/src/config.ts +9 -0
- package/src/exec/env.ts +3 -1
- package/src/exec/executor.ts +76 -15
- package/src/exec/kill-tree.ts +7 -7
- package/src/exec/runner.ts +270 -108
- package/src/exec/sandbox-binds.ts +109 -18
- package/src/exec/sandbox-runtime.ts +134 -37
- package/src/exec/sandbox-violations.ts +250 -46
- package/src/exec/spawn-off.ts +10 -5
- package/src/exec/warm-shell.ts +29 -7
- package/src/graph/folds.ts +89 -16
- package/src/graph/index.ts +8 -0
- package/src/graph/priorities.ts +8 -5
- package/src/graph/scheduler.ts +173 -38
- package/src/graph/stand-ins.ts +36 -0
- package/src/graph/task-graph.ts +327 -141
- package/src/graph/task-status.ts +5 -1
- package/src/handoff.ts +45 -10
- package/src/history/failure-mode.ts +15 -5
- package/src/history/history.ts +103 -30
- package/src/history/index.ts +1 -0
- package/src/history/metrics.ts +189 -69
- package/src/index.ts +2 -1
- package/src/orchestrator/admission.ts +13 -9
- package/src/orchestrator/affected-tasks.ts +35 -13
- package/src/orchestrator/command-windows.ts +8 -5
- package/src/orchestrator/deferred-outputs.ts +41 -12
- package/src/orchestrator/doctor.ts +21 -9
- package/src/orchestrator/download-policy.ts +3 -3
- package/src/orchestrator/excluded-keys.ts +14 -2
- package/src/orchestrator/execute-args.ts +9 -2
- package/src/orchestrator/execute-task.ts +564 -123
- package/src/orchestrator/graph-memo.ts +26 -4
- package/src/orchestrator/graph-stage.ts +27 -4
- package/src/orchestrator/held-servers.ts +36 -14
- package/src/orchestrator/hit-restore.ts +136 -87
- package/src/orchestrator/index.ts +4 -1
- package/src/orchestrator/keyed-projects.ts +25 -0
- package/src/orchestrator/local-floor.ts +21 -3
- package/src/orchestrator/lockfile-claim.ts +30 -4
- package/src/orchestrator/miss-save.ts +33 -53
- package/src/orchestrator/options.ts +13 -0
- package/src/orchestrator/output-host.ts +16 -4
- package/src/orchestrator/output-log.ts +1 -54
- package/src/orchestrator/persistent-task.ts +31 -17
- package/src/orchestrator/persistent.ts +152 -15
- package/src/orchestrator/placement.ts +81 -15
- package/src/orchestrator/plan.ts +37 -15
- package/src/orchestrator/plugin-host.ts +232 -108
- package/src/orchestrator/plugin.ts +53 -27
- package/src/orchestrator/prepare.ts +194 -38
- package/src/orchestrator/projects.ts +89 -10
- package/src/orchestrator/read-only.ts +115 -0
- package/src/orchestrator/run-context.ts +6 -2
- package/src/orchestrator/run-failures.ts +43 -14
- package/src/orchestrator/run-id.ts +13 -2
- package/src/orchestrator/run-keys.ts +87 -28
- package/src/orchestrator/run-live.ts +16 -13
- package/src/orchestrator/run-lock.ts +27 -4
- package/src/orchestrator/run-records.ts +12 -10
- package/src/orchestrator/run-report.ts +2 -1
- package/src/orchestrator/run-stop.ts +17 -9
- package/src/orchestrator/run.ts +443 -107
- package/src/orchestrator/sandbox-fix.ts +40 -12
- package/src/orchestrator/sandbox-request.ts +286 -59
- package/src/orchestrator/save-lane.ts +24 -13
- package/src/orchestrator/shell-verdict.ts +20 -1
- package/src/orchestrator/signals.ts +103 -8
- package/src/orchestrator/stable-keys.ts +17 -9
- package/src/orchestrator/task-artifacts.ts +15 -2
- package/src/orchestrator/task-hash.ts +187 -30
- package/src/orchestrator/telemetry-host.ts +25 -6
- package/src/orchestrator/telemetry.ts +80 -12
- package/src/orchestrator/upstream.ts +3 -1
- package/src/report/events.ts +20 -3
- package/src/report/forecast.ts +103 -37
- package/src/report/framed-output.ts +165 -157
- package/src/report/index.ts +2 -1
- package/src/report/logger.ts +107 -30
- package/src/report/plain-output.ts +13 -9
- package/src/report/status-line.ts +28 -55
- package/src/report/summary.ts +57 -14
- package/src/settings.ts +42 -6
- package/src/ui/components.ts +36 -42
- package/src/ui/index.ts +3 -1
- package/src/ui/live-region.ts +67 -8
- package/src/ui/terminal.ts +12 -0
- package/src/ui/theme.ts +16 -1
- package/src/util/config-frame.ts +17 -3
- package/src/util/edit-distance.ts +17 -1
- package/src/util/errors.ts +23 -7
- package/src/util/foreign-cache-dir.ts +60 -0
- package/src/util/git-ignored.ts +59 -5
- package/src/util/index.ts +15 -1
- package/src/util/paths.ts +50 -6
- package/src/util/plain-text.ts +19 -0
- package/src/util/secret-mask.ts +57 -1
- package/src/util/settle.ts +16 -0
- package/src/util/size.ts +12 -2
- package/src/util/write-file.ts +86 -9
- package/src/workspace/affected.ts +225 -27
- package/src/workspace/config-bytecode.ts +23 -4
- package/src/workspace/config-eval.ts +90 -16
- package/src/workspace/config-imports.ts +96 -41
- package/src/workspace/config-schema.ts +126 -25
- package/src/workspace/dependencies-lock.ts +7 -14
- package/src/workspace/filter.ts +10 -1
- package/src/workspace/index.ts +3 -1
- package/src/workspace/json-data.ts +34 -8
- package/src/workspace/lockfile.ts +86 -37
- package/src/workspace/migration.ts +46 -25
- package/src/workspace/module-state.ts +62 -0
- package/src/workspace/nested-dirs.ts +8 -0
- package/src/workspace/project-loader.ts +276 -32
- package/src/workspace/repo-id.ts +29 -6
- package/src/workspace/workspace.ts +190 -30
package/src/report/events.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { exitSignal } from '../exec/index.js'
|
|
10
10
|
import type { InputChanges, TaskNode, TaskOutcome } from '../graph/index.js'
|
|
11
|
-
import { isGroupTask } from '../graph/index.js'
|
|
11
|
+
import { isGroupTask, isKeyFailed, markOutputsFailed, outputsFailedOf } from '../graph/index.js'
|
|
12
12
|
import { heldSecrets, maskedCommand, type SecretMask, secretMask } from '../util/index.js'
|
|
13
13
|
import type { Logger } from './logger.js'
|
|
14
14
|
import type { RunContext } from './summary.js'
|
|
@@ -41,7 +41,8 @@ export interface RunStartInfo {
|
|
|
41
41
|
* refs — zero-copy, because the default terminal subscriber runs on the
|
|
42
42
|
* same thread and reads those objects directly. Crossing a thread or RPC
|
|
43
43
|
* needs the serializable WireEvent form (see `projectNode` / `toWireEvent`
|
|
44
|
-
* below); that mapping is the boundary's job, not the producer's.
|
|
44
|
+
* below); that mapping is the boundary's job, not the producer's. A
|
|
45
|
+
* plugin gets read-only views of them (orchestrator/read-only.ts).
|
|
45
46
|
*/
|
|
46
47
|
export type RunEvent =
|
|
47
48
|
| { kind: 'run:start'; info: RunStartInfo }
|
|
@@ -283,6 +284,8 @@ export interface OutcomeView {
|
|
|
283
284
|
isGroup?: boolean
|
|
284
285
|
/** Executed without `cache: true`: never consulted the cache, not a miss. */
|
|
285
286
|
noCache?: boolean
|
|
287
|
+
/** A failed hit whose restore threw (see `TaskOutcome`): neither a miss nor a hit. */
|
|
288
|
+
restoreFailed?: true
|
|
286
289
|
/** Cache hits only: the exec time the entry was stored with — the work
|
|
287
290
|
* this hit skipped, as opposed to `durationMs`, the restore it cost. */
|
|
288
291
|
storedDurationMs?: number
|
|
@@ -344,12 +347,18 @@ export function outcomeWord(o: Pick<OutcomeView, 'status' | 'restored'>): string
|
|
|
344
347
|
}
|
|
345
348
|
}
|
|
346
349
|
|
|
347
|
-
/**
|
|
350
|
+
/**
|
|
351
|
+
* `outcomeWord` with the exit code a failure carries: `failed (exit 9)`.
|
|
352
|
+
* A hit whose restore failed ran nothing, so it has no exit to name, nor
|
|
353
|
+
* does a task whose key could not be derived (`isKeyFailed`) or whose
|
|
354
|
+
* output clean or save vx refused (`outputsFailedOf`): the step reads.
|
|
355
|
+
*/
|
|
348
356
|
export function outcomeLabel(
|
|
349
357
|
o: Pick<
|
|
350
358
|
OutcomeView,
|
|
351
359
|
| 'status'
|
|
352
360
|
| 'restored'
|
|
361
|
+
| 'restoreFailed'
|
|
353
362
|
| 'exitCode'
|
|
354
363
|
| 'timedOut'
|
|
355
364
|
| 'sandboxViolations'
|
|
@@ -359,6 +368,11 @@ export function outcomeLabel(
|
|
|
359
368
|
| 'otherPlatform'
|
|
360
369
|
>,
|
|
361
370
|
): string {
|
|
371
|
+
if (o.status === 'failed' && o.restoreFailed === true) return 'failed (cache restore failed)'
|
|
372
|
+
if (o.status === 'failed' && isKeyFailed(o)) return 'failed (cache key not derived)'
|
|
373
|
+
const step = o.status === 'failed' ? outputsFailedOf(o) : undefined
|
|
374
|
+
if (step !== undefined)
|
|
375
|
+
return step === 'save' ? 'failed (cache save failed)' : 'failed (output clean failed)'
|
|
362
376
|
if (o.status === 'failed')
|
|
363
377
|
return failedLabel(o.exitCode, o.timedOut, o.sandboxViolations, o.notReady, o.inputMoved)
|
|
364
378
|
if (o.status === 'skipped') return skippedLabel(o)
|
|
@@ -449,6 +463,9 @@ export function projectOutcome(outcome: TaskOutcome): OutcomeView {
|
|
|
449
463
|
}
|
|
450
464
|
if (isGroupTask(outcome.node)) view.isGroup = true
|
|
451
465
|
else if (ranNoCache(outcome)) view.noCache = true
|
|
466
|
+
if (outcome.restoreFailed === true) view.restoreFailed = true
|
|
467
|
+
const step = outputsFailedOf(outcome)
|
|
468
|
+
if (step !== undefined) markOutputsFailed(view, step)
|
|
452
469
|
if (outcome.storedDurationMs !== undefined) view.storedDurationMs = outcome.storedDurationMs
|
|
453
470
|
if (outcome.storedCpuMs !== undefined) view.storedCpuMs = outcome.storedCpuMs
|
|
454
471
|
if (outcome.storedPeakRssBytes !== undefined) view.storedPeakRssBytes = outcome.storedPeakRssBytes
|
package/src/report/forecast.ts
CHANGED
|
@@ -33,6 +33,11 @@ export interface BlockedTask {
|
|
|
33
33
|
by: string
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* One pass over what is unfinished. Called again with the same `finished`
|
|
38
|
+
* set, that set has only grown: the pass reads only what the last one left
|
|
39
|
+
* unfinished. Another set starts over.
|
|
40
|
+
*/
|
|
36
41
|
export type Forecast = (
|
|
37
42
|
finished: ReadonlySet<string>,
|
|
38
43
|
running: ReadonlyMap<string, number>,
|
|
@@ -78,68 +83,129 @@ export function createForecast(
|
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
for (const n of nodes.values()) place(n)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
// Each node by its place in `order`, its facts in arrays. A pass built
|
|
87
|
+
// Maps of the whole graph and read every finished task again, and each
|
|
88
|
+
// task event forces a frame, each frame a pass: a 20,000-task run spent
|
|
89
|
+
// ~1.6 s of its main thread on them on a terminal (2026-11-07).
|
|
90
|
+
const size = order.length
|
|
91
|
+
const position = new Map(order.map((n, i) => [n.id, i]))
|
|
92
|
+
const deps = order.map((n) => {
|
|
93
|
+
const at: number[] = []
|
|
94
|
+
// A dependency outside the graph never has an end: leaving it out changes nothing.
|
|
95
|
+
for (const d of n.deps) {
|
|
96
|
+
const i = position.get(d)
|
|
97
|
+
if (i !== undefined) at.push(i)
|
|
98
|
+
}
|
|
99
|
+
return at
|
|
100
|
+
})
|
|
101
|
+
const ids = order.map((n) => n.id)
|
|
102
|
+
const group = Uint8Array.from(order, (n) => (isGroupTask(n) ? 1 : 0))
|
|
103
|
+
// NaN: no p50.
|
|
104
|
+
const cost = new Float64Array(size)
|
|
105
|
+
const ranks = new Float64Array(size)
|
|
106
|
+
for (let i = 0; i < size; i++) {
|
|
107
|
+
const n = order[i]!
|
|
108
|
+
cost[i] = (group[i] ? undefined : p50s.get(n.id)) ?? NaN
|
|
109
|
+
ranks[i] = rank.get(n.id) ?? 0
|
|
85
110
|
}
|
|
86
111
|
const workers = Math.max(1, concurrency)
|
|
87
112
|
// The scheduler's own baseline ranking (`rank`), most dependents first,
|
|
88
113
|
// ties in graph order.
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return d !== 0 ? d > 0 : position.get(a)! < position.get(b)!
|
|
114
|
+
const before = (a: number, b: number): boolean => {
|
|
115
|
+
const d = ranks[a]! - ranks[b]!
|
|
116
|
+
return d !== 0 ? d > 0 : a < b
|
|
93
117
|
}
|
|
118
|
+
// Reused by every pass. `end` NaN: no end yet (finished, or not reached);
|
|
119
|
+
// `since` NaN: not running; `head` -1: no running task its chain waits on.
|
|
120
|
+
const end = new Float64Array(size)
|
|
121
|
+
const since = new Float64Array(size)
|
|
122
|
+
const head = new Int32Array(size)
|
|
123
|
+
// What the last pass left unfinished, in order: a pass reads only these,
|
|
124
|
+
// not every finished task again. Rebuilt when `finished` is another set
|
|
125
|
+
// or shrank; the logger's only grows.
|
|
126
|
+
const live = new Int32Array(size)
|
|
127
|
+
let liveCount = -1
|
|
128
|
+
let lastFinished: ReadonlySet<string> | undefined
|
|
129
|
+
let lastSize = 0
|
|
94
130
|
|
|
95
131
|
return (finished, running, nowMs) => {
|
|
132
|
+
if (finished !== lastFinished || finished.size < lastSize || liveCount < 0) {
|
|
133
|
+
for (let i = 0; i < size; i++) live[i] = i
|
|
134
|
+
liveCount = size
|
|
135
|
+
}
|
|
136
|
+
let kept = 0
|
|
137
|
+
for (let r = 0; r < liveCount; r++) {
|
|
138
|
+
const i = live[r]!
|
|
139
|
+
if (!finished.has(ids[i]!)) live[kept++] = i
|
|
140
|
+
}
|
|
141
|
+
liveCount = kept
|
|
142
|
+
lastFinished = finished
|
|
143
|
+
lastSize = finished.size
|
|
144
|
+
end.fill(NaN)
|
|
145
|
+
since.fill(NaN)
|
|
146
|
+
head.fill(-1)
|
|
147
|
+
for (const [id, at] of running) {
|
|
148
|
+
const i = position.get(id)
|
|
149
|
+
if (i !== undefined) since[i] = at
|
|
150
|
+
}
|
|
96
151
|
let waiting = 0
|
|
97
|
-
const end = new Map<string, number>()
|
|
98
|
-
// The running task each node's longest chain waits on.
|
|
99
|
-
const head = new Map<string, string>()
|
|
100
152
|
let chain = 0
|
|
101
|
-
let chainEnd
|
|
153
|
+
let chainEnd = -1
|
|
102
154
|
let work = 0
|
|
103
155
|
let known = false
|
|
104
156
|
// The best `workers` blocked tasks, kept sorted as the pass meets them.
|
|
105
157
|
const held: BlockedTask[] = []
|
|
158
|
+
const heldAt: number[] = []
|
|
106
159
|
const blockedBy = new Map<string, number>()
|
|
107
|
-
for (
|
|
108
|
-
|
|
160
|
+
for (let r = 0; r < liveCount; r++) {
|
|
161
|
+
const i = live[r]!
|
|
162
|
+
const ds = deps[i]!
|
|
109
163
|
let start = 0
|
|
110
|
-
let via
|
|
111
|
-
for (const d of
|
|
112
|
-
const e = end
|
|
113
|
-
if (e
|
|
164
|
+
let via = -1
|
|
165
|
+
for (const d of ds) {
|
|
166
|
+
const e = end[d]!
|
|
167
|
+
if (e === e && (via < 0 || e > start)) {
|
|
114
168
|
start = e
|
|
115
169
|
via = d
|
|
116
170
|
}
|
|
117
171
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
172
|
+
const s = since[i]!
|
|
173
|
+
const runs = s === s
|
|
174
|
+
if (runs) head[i] = i
|
|
175
|
+
else if (via >= 0 && head[via]! >= 0) head[i] = head[via]!
|
|
176
|
+
if (!runs && !group[i]) {
|
|
177
|
+
let by = head[i]!
|
|
178
|
+
if (by < 0)
|
|
179
|
+
for (const d of ds)
|
|
180
|
+
if (end[d] === end[d]) {
|
|
181
|
+
by = d
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
if (by >= 0) {
|
|
185
|
+
waiting++
|
|
186
|
+
let at = held.length
|
|
187
|
+
while (at > 0 && before(i, heldAt[at - 1]!)) at--
|
|
188
|
+
if (at < workers) {
|
|
189
|
+
held.splice(at, 0, { id: ids[i]!, by: ids[by]! })
|
|
190
|
+
heldAt.splice(at, 0, i)
|
|
191
|
+
if (held.length > workers) {
|
|
192
|
+
held.pop()
|
|
193
|
+
heldAt.pop()
|
|
194
|
+
}
|
|
195
|
+
}
|
|
130
196
|
}
|
|
131
197
|
}
|
|
132
|
-
const p = cost
|
|
198
|
+
const p = cost[i]!
|
|
133
199
|
let own = 0
|
|
134
|
-
if (p
|
|
200
|
+
if (p === p) {
|
|
135
201
|
known = true
|
|
136
|
-
own =
|
|
202
|
+
own = runs ? Math.max(0, p - (nowMs - s)) : p
|
|
137
203
|
}
|
|
138
204
|
const e = start + own
|
|
139
|
-
end
|
|
140
|
-
if (e > chain || chainEnd
|
|
205
|
+
end[i] = e
|
|
206
|
+
if (e > chain || chainEnd < 0) {
|
|
141
207
|
chain = e
|
|
142
|
-
chainEnd =
|
|
208
|
+
chainEnd = i
|
|
143
209
|
}
|
|
144
210
|
work += own
|
|
145
211
|
}
|
|
@@ -148,7 +214,7 @@ export function createForecast(
|
|
|
148
214
|
if (d !== undefined && d > 0) blockedBy.set(id, d)
|
|
149
215
|
}
|
|
150
216
|
if (!known) return { waiting, blocked: held, blockedBy }
|
|
151
|
-
const critical = chainEnd
|
|
217
|
+
const critical = chainEnd < 0 || head[chainEnd]! < 0 ? undefined : ids[head[chainEnd]!]!
|
|
152
218
|
return {
|
|
153
219
|
etaMs: Math.max(chain, work / workers),
|
|
154
220
|
...(critical !== undefined ? { critical } : {}),
|