tickmarkr 2.5.4 → 2.5.5
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/dist/adapters/registry.js +6 -1
- package/dist/adapters/types.d.ts +3 -0
- package/dist/cli/commands/approve.d.ts +1 -0
- package/dist/cli/commands/approve.js +54 -6
- package/dist/cli/commands/doctor.js +44 -29
- package/dist/cli/commands/fleet.js +195 -33
- package/dist/cli/commands/init.js +196 -6
- package/dist/cli/commands/resume.js +4 -2
- package/dist/cli/commands/run.js +11 -2
- package/dist/cli/commands/verify.js +1 -1
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.js +3 -1
- package/dist/config/config.d.ts +14 -8
- package/dist/config/config.js +29 -26
- package/dist/config/fleet-overlay.d.ts +3 -9
- package/dist/config/fleet-overlay.js +68 -11
- package/dist/config/fleet-why.d.ts +7 -0
- package/dist/config/fleet-why.js +5 -0
- package/dist/drivers/index.d.ts +15 -1
- package/dist/drivers/index.js +38 -10
- package/dist/drivers/orca.d.ts +99 -10
- package/dist/drivers/orca.js +586 -97
- package/dist/gates/baseline.d.ts +3 -0
- package/dist/gates/baseline.js +2 -1
- package/dist/gates/cache.d.ts +100 -0
- package/dist/gates/cache.js +389 -0
- package/dist/gates/run-gates.d.ts +3 -0
- package/dist/gates/run-gates.js +117 -10
- package/dist/gates/test-manifest.d.ts +99 -0
- package/dist/gates/test-manifest.js +389 -0
- package/dist/gates/test-reporter.d.ts +4 -0
- package/dist/gates/test-reporter.js +49 -0
- package/dist/route/preference.d.ts +22 -1
- package/dist/route/preference.js +123 -25
- package/dist/route/router.js +31 -6
- package/dist/run/daemon.d.ts +13 -0
- package/dist/run/daemon.js +160 -70
- package/dist/run/git.d.ts +10 -1
- package/dist/run/git.js +43 -9
- package/dist/run/journal.d.ts +14 -2
- package/dist/run/journal.js +78 -10
- package/dist/run/lease.d.ts +14 -0
- package/dist/run/lease.js +87 -0
- package/dist/run/merge.d.ts +2 -0
- package/dist/run/merge.js +91 -3
- package/dist/run/operator-state.d.ts +11 -0
- package/dist/run/operator-state.js +17 -3
- package/dist/tui/cockpit/board.d.ts +96 -0
- package/dist/tui/cockpit/board.js +346 -0
- package/dist/tui/cockpit/decision-actions.js +2 -0
- package/dist/tui/cockpit/layout.d.ts +5 -1
- package/dist/tui/cockpit/layout.js +8 -3
- package/dist/tui/cockpit/live-runtime.js +71 -21
- package/dist/tui/cockpit/run-view.d.ts +7 -5
- package/dist/tui/cockpit/run-view.js +12 -11
- package/dist/tui/ink/fleet-app.d.ts +41 -27
- package/dist/tui/ink/fleet-app.js +204 -31
- package/package.json +1 -1
- package/skills/tickmarkr-overseer/SKILL.md +173 -113
|
@@ -4,6 +4,8 @@ import { useRef, useState } from "react";
|
|
|
4
4
|
import { MODEL_ID_RE } from "../../adapters/types.js";
|
|
5
5
|
import { retiredModelReason } from "../../adapters/model-lints.js";
|
|
6
6
|
import { fleetFirstTouchProvenance } from "../../config/fleet-overlay.js";
|
|
7
|
+
import { exclusionReason } from "../../config/fleet-why.js";
|
|
8
|
+
import { entryMatchesChannel, exclusionCollector } from "../../route/preference.js";
|
|
7
9
|
import { TIERS } from "../../graph/schema.js";
|
|
8
10
|
import { windowRows } from "./components.js";
|
|
9
11
|
import { clip, clipPathTail, ElisionMark, enterAltScreen, fmtCtx, fmtMs, fmtUsdPair, Glyph, INK, inkInput, inkOutput, KeyBar, OverlayPanel, padCell, padCellStart, Pointer, SearchRow, } from "./frame.js";
|
|
@@ -23,7 +25,7 @@ function splitSeat(seat) {
|
|
|
23
25
|
const at = seat.indexOf(":");
|
|
24
26
|
return { adapter: seat.slice(0, at), model: seat.slice(at + 1) };
|
|
25
27
|
}
|
|
26
|
-
export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, entry = "probe", initialJudge = "", judgeSeats = [], viewRows = Number.POSITIVE_INFINITY, frameColumns = 100, frameRows, }) {
|
|
28
|
+
export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels, initialDenyWorkersAdapters = [], initialDenyWorkersModels = [], modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, stagedRouting, entry = "probe", initialJudge = "", judgeSeats = [], viewRows = Number.POSITIVE_INFINITY, frameColumns = 100, frameRows, }) {
|
|
27
29
|
const { exit } = useApp();
|
|
28
30
|
const [, setRevision] = useState(0);
|
|
29
31
|
const uiRef = useRef(null);
|
|
@@ -40,8 +42,11 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
40
42
|
showAll: false,
|
|
41
43
|
presetsSeen: false,
|
|
42
44
|
notice: "",
|
|
45
|
+
lastEdit: null,
|
|
43
46
|
deny: new Set(initialDenyAdapters),
|
|
44
47
|
denyModels: new Set(initialDenyModels),
|
|
48
|
+
denyWorkersAdapters: new Set(initialDenyWorkersAdapters),
|
|
49
|
+
denyWorkersModels: new Set(initialDenyWorkersModels),
|
|
45
50
|
classifications: [],
|
|
46
51
|
selectedMode: initialMode,
|
|
47
52
|
map: structuredClone(initialMap),
|
|
@@ -59,6 +64,8 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
59
64
|
// chip, and the empty-`w` notice all read it. Counts staged EDITS, not diff hunks.
|
|
60
65
|
const initialDenySet = new Set(initialDenyAdapters);
|
|
61
66
|
const initialDenyModelSet = new Set(initialDenyModels);
|
|
67
|
+
const initialDenyWorkersAdapterSet = new Set(initialDenyWorkersAdapters);
|
|
68
|
+
const initialDenyWorkersModelSet = new Set(initialDenyWorkersModels);
|
|
62
69
|
const stagedCount = () => {
|
|
63
70
|
let n = ui.classifications.length
|
|
64
71
|
+ (ui.selectedMode !== initialMode ? 1 : 0)
|
|
@@ -75,6 +82,18 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
75
82
|
for (const model of initialDenyModelSet)
|
|
76
83
|
if (!ui.denyModels.has(model))
|
|
77
84
|
n += 1;
|
|
85
|
+
for (const adapter of ui.denyWorkersAdapters)
|
|
86
|
+
if (!initialDenyWorkersAdapterSet.has(adapter))
|
|
87
|
+
n += 1;
|
|
88
|
+
for (const adapter of initialDenyWorkersAdapterSet)
|
|
89
|
+
if (!ui.denyWorkersAdapters.has(adapter))
|
|
90
|
+
n += 1;
|
|
91
|
+
for (const model of ui.denyWorkersModels)
|
|
92
|
+
if (!initialDenyWorkersModelSet.has(model))
|
|
93
|
+
n += 1;
|
|
94
|
+
for (const model of initialDenyWorkersModelSet)
|
|
95
|
+
if (!ui.denyWorkersModels.has(model))
|
|
96
|
+
n += 1;
|
|
78
97
|
for (const shape of new Set([...Object.keys(initialMap), ...Object.keys(ui.map)])) {
|
|
79
98
|
// {} ≡ absent: pin-then-auto leaves an empty entry that writes nothing — not staged work
|
|
80
99
|
if (JSON.stringify(initialMap[shape] ?? {}) !== JSON.stringify(ui.map[shape] ?? {}))
|
|
@@ -87,17 +106,55 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
87
106
|
return n;
|
|
88
107
|
};
|
|
89
108
|
// ── derived data ───────────────────────────────────────────────────────────
|
|
90
|
-
|
|
109
|
+
// OBS-994/FL-1 repair: a flatly (or workers-) denied adapter's channels stay VISIBLE here —
|
|
110
|
+
// every channel needs a reach cell, and Space below can only cycle a scope it can see.
|
|
111
|
+
const enabledGroups = () => modelGroups;
|
|
91
112
|
const scopedGroups = () => {
|
|
92
113
|
if (ui.adapterAt === -1)
|
|
93
114
|
return enabledGroups();
|
|
94
115
|
const group = modelGroups[ui.adapterAt];
|
|
95
|
-
return group
|
|
116
|
+
return group ? [group] : [];
|
|
117
|
+
};
|
|
118
|
+
// LEG2-T3 finding 1: a channel's reach and every reason come from the exclusion collector over
|
|
119
|
+
// the STAGED policy (fleet loads the candidate bytes; see stagedRouting) and the recorded
|
|
120
|
+
// identity — never literal membership of the staged sets, which missed identity, bare-model and
|
|
121
|
+
// family entries and could not tell an allowlist exclusion from a deny.
|
|
122
|
+
let routingMemo = null;
|
|
123
|
+
const stagedPolicy = () => {
|
|
124
|
+
const deny = stagedDeny();
|
|
125
|
+
const key = JSON.stringify(deny);
|
|
126
|
+
if (routingMemo?.key !== key) {
|
|
127
|
+
routingMemo = {
|
|
128
|
+
key,
|
|
129
|
+
policy: stagedRouting
|
|
130
|
+
? stagedRouting(deny)
|
|
131
|
+
: { ok: true, routing: { deny: { adapters: deny.adapters, models: deny.models, workers: { adapters: deny.workersAdapters, models: deny.workersModels } } } },
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return routingMemo.policy;
|
|
135
|
+
};
|
|
136
|
+
const reachFor = (adapter, model, identity) => {
|
|
137
|
+
const policy = stagedPolicy();
|
|
138
|
+
if (!policy.ok)
|
|
139
|
+
return { reach: "unknown", reasons: [`preview unavailable (${policy.error})`], uncoveredAlias: false };
|
|
140
|
+
const channel = { adapter, model, ...(identity !== undefined ? { identity } : {}) };
|
|
141
|
+
const worker = exclusionCollector(channel, policy.routing, "worker");
|
|
142
|
+
const allSeats = exclusionCollector(channel, policy.routing, "judge");
|
|
143
|
+
const reach = allSeats.some((scope) => scope.by === "deny") ? "out-all"
|
|
144
|
+
: allSeats.length ? "out-allow"
|
|
145
|
+
: worker.length ? "out-workers"
|
|
146
|
+
: "in";
|
|
147
|
+
return {
|
|
148
|
+
reach,
|
|
149
|
+
reasons: worker.map(exclusionReason),
|
|
150
|
+
uncoveredAlias: identity !== undefined && !worker.some((scope) => scope.by === "deny"),
|
|
151
|
+
};
|
|
96
152
|
};
|
|
97
153
|
const groupRows = (group) => {
|
|
98
154
|
const rows = group.rows.map((row) => {
|
|
99
155
|
const staged = ui.classifications.find((classification) => classification.adapter === group.adapter
|
|
100
156
|
&& (classification.model === row.model || classification.model === row.classifyModel));
|
|
157
|
+
const reach = reachFor(group.adapter, row.model, row.evidence?.identity);
|
|
101
158
|
return {
|
|
102
159
|
adapter: group.adapter,
|
|
103
160
|
model: row.model,
|
|
@@ -110,18 +167,21 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
110
167
|
variants: row.variants,
|
|
111
168
|
foldedModels: row.foldedModels,
|
|
112
169
|
score: row.score,
|
|
113
|
-
denied:
|
|
170
|
+
denied: reach.reach !== "in",
|
|
171
|
+
...reach,
|
|
114
172
|
};
|
|
115
173
|
});
|
|
116
174
|
const known = new Set(rows.flatMap((row) => row.classifyModel ? [row.model, row.classifyModel] : [row.model]));
|
|
117
175
|
for (const staged of ui.classifications) {
|
|
118
176
|
if (staged.adapter === group.adapter && !known.has(staged.model)) {
|
|
177
|
+
const reach = reachFor(group.adapter, staged.model);
|
|
119
178
|
rows.push({
|
|
120
179
|
adapter: group.adapter,
|
|
121
180
|
model: staged.model,
|
|
122
181
|
tier: staged.tier,
|
|
123
182
|
channel: group.channel,
|
|
124
|
-
denied:
|
|
183
|
+
denied: reach.reach !== "in",
|
|
184
|
+
...reach,
|
|
125
185
|
});
|
|
126
186
|
}
|
|
127
187
|
}
|
|
@@ -151,8 +211,14 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
151
211
|
return groups.flatMap(groupRows)
|
|
152
212
|
.filter((row) => ui.showAll || row.tier !== undefined || retiredModelReason(row.model) === null).length;
|
|
153
213
|
};
|
|
154
|
-
// three preview callbacks share it in lockstep — the staged deny truth every preview ranks under
|
|
155
|
-
|
|
214
|
+
// three preview callbacks share it in lockstep — the staged deny truth every preview ranks under.
|
|
215
|
+
// LEG2-T3 finding 2: all four scopes travel distinct; the preview loads them as written.
|
|
216
|
+
const stagedDeny = () => ({
|
|
217
|
+
adapters: [...ui.deny].sort(),
|
|
218
|
+
models: [...ui.denyModels].sort(),
|
|
219
|
+
workersAdapters: [...ui.denyWorkersAdapters].sort(),
|
|
220
|
+
workersModels: [...ui.denyWorkersModels].sort(),
|
|
221
|
+
});
|
|
156
222
|
const shapeList = () => shapeRows(ui.selectedMode, ui.map, stagedDeny());
|
|
157
223
|
const steeringList = () => [
|
|
158
224
|
...STEERING_KEYS.map((key) => ({
|
|
@@ -179,6 +245,8 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
179
245
|
const editorState = () => ({
|
|
180
246
|
denyAdapters: [...ui.deny].sort(),
|
|
181
247
|
denyModels: [...ui.denyModels].sort(),
|
|
248
|
+
denyWorkersAdapters: [...ui.denyWorkersAdapters].sort(),
|
|
249
|
+
denyWorkersModels: [...ui.denyWorkersModels].sort(),
|
|
182
250
|
classifications: ui.classifications.map((classification) => classification.vendor && classification.channel
|
|
183
251
|
? {
|
|
184
252
|
...classification,
|
|
@@ -605,12 +673,13 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
605
673
|
}
|
|
606
674
|
if (overlay.kind === "candidates") {
|
|
607
675
|
const rows = overlay.rows.filter((candidate) => matches(candidate.label, ui.filter));
|
|
676
|
+
const ledger = overlay.ledger.filter((line) => matches(line, ui.filter));
|
|
608
677
|
if (key.escape) {
|
|
609
678
|
setOverlay(null);
|
|
610
679
|
return;
|
|
611
680
|
}
|
|
612
681
|
if (key.downArrow) {
|
|
613
|
-
overlay.at = Math.min(overlay.at + 1, Math.max(rows.length - 1, 0));
|
|
682
|
+
overlay.at = Math.min(overlay.at + 1, Math.max(rows.length + ledger.length - 1, 0));
|
|
614
683
|
bump();
|
|
615
684
|
return;
|
|
616
685
|
}
|
|
@@ -644,7 +713,8 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
644
713
|
}
|
|
645
714
|
return;
|
|
646
715
|
}
|
|
647
|
-
editFilter((f) => overlay.rows.filter((candidate) => matches(candidate.label, f)).length
|
|
716
|
+
editFilter((f) => overlay.rows.filter((candidate) => matches(candidate.label, f)).length
|
|
717
|
+
+ overlay.ledger.filter((line) => matches(line, f)).length, (max) => {
|
|
648
718
|
overlay.at = Math.min(overlay.at, max);
|
|
649
719
|
});
|
|
650
720
|
return;
|
|
@@ -931,13 +1001,71 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
931
1001
|
beginClassification(row.adapter, row.model);
|
|
932
1002
|
return;
|
|
933
1003
|
}
|
|
1004
|
+
// OBS-994/FL-1: membership can't fix an auth failure either — the row's reach stays
|
|
1005
|
+
// whatever it is and the operator is pointed at doctor instead of a toggle that lies.
|
|
1006
|
+
if (row.evidence?.unauthed !== undefined) {
|
|
1007
|
+
ui.notice = `${row.adapter}:${row.model} is unauthed — re-probe with tickmarkr doctor before its fleet reach can change`;
|
|
1008
|
+
bump();
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
934
1011
|
const id = `${row.adapter}:${row.model}`;
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1012
|
+
// OBS-994/FL-1, LEG2-T3 finding 3: Space cycles in → out(workers) → out(all) → in by editing
|
|
1013
|
+
// only THIS channel's own entries in the per-model scopes — its adapter:model key, or an
|
|
1014
|
+
// identity/bare-model entry no other browser row matches. An adapter-wide or shared entry
|
|
1015
|
+
// is never promoted or cleared from a channel row: that would move sibling channels and
|
|
1016
|
+
// wipe reasons that are not this row's to clear. The row's detail line names the entry each
|
|
1017
|
+
// press edited; a refused press names the shared entry in the notice instead.
|
|
1018
|
+
const self = { adapter: row.adapter, model: row.model, identity: row.evidence?.identity };
|
|
1019
|
+
const others = modelGroups.flatMap((group) => group.rows
|
|
1020
|
+
.filter((other) => group.adapter !== row.adapter || other.model !== row.model)
|
|
1021
|
+
.map((other) => ({ adapter: group.adapter, model: other.model, identity: other.evidence?.identity })));
|
|
1022
|
+
// Judge c3 (R107): exactly ONE selected entry per press — the channel's adapter:model key when
|
|
1023
|
+
// it is a candidate, else the first candidate in sorted order. The cycle is three states over
|
|
1024
|
+
// two scopes, so the out-workers → out-all press re-scopes that one entry from
|
|
1025
|
+
// routing.deny.workers.models to routing.deny.models; every other press adds or clears one
|
|
1026
|
+
// entry in one scope. The channel's other entries keep their reasons for later presses.
|
|
1027
|
+
// LEG2-T3 round 2 finding 1: promotion widens the selected ban as spelled — a bare model id
|
|
1028
|
+
// then excludes every seat serving that model — while a CLEAR only takes an entry no other
|
|
1029
|
+
// row matches, so no sibling is ever admitted by a channel toggle.
|
|
1030
|
+
const select = (entries, ownOnly) => {
|
|
1031
|
+
const candidates = [...entries].filter((entry) => entryMatchesChannel(entry, self, true)
|
|
1032
|
+
&& (!ownOnly || !others.some((other) => entryMatchesChannel(entry, other, true))));
|
|
1033
|
+
return candidates.includes(id) ? id : candidates.sort()[0];
|
|
1034
|
+
};
|
|
1035
|
+
if (row.reach === "unknown") {
|
|
1036
|
+
ui.notice = `${id} reach is unknown — Space does not toggle it until the staged overlay loads: ${row.reasons.join("; ")}`;
|
|
1037
|
+
bump();
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (row.reach === "in") {
|
|
1041
|
+
ui.denyWorkersModels = new Set([...ui.denyWorkersModels, id]);
|
|
1042
|
+
ui.lastEdit = { id, text: `space: added ${id} to routing.deny.workers.models` };
|
|
1043
|
+
}
|
|
1044
|
+
else if (row.reach === "out-workers") {
|
|
1045
|
+
const entry = select(ui.denyWorkersModels, false) ?? id;
|
|
1046
|
+
ui.denyWorkersModels = new Set([...ui.denyWorkersModels].filter((staged) => staged !== entry));
|
|
1047
|
+
ui.denyModels = new Set([...ui.denyModels, entry]);
|
|
1048
|
+
ui.lastEdit = { id, text: `space: moved ${entry} to routing.deny.models` };
|
|
1049
|
+
}
|
|
1050
|
+
else {
|
|
1051
|
+
// round 2 finding 2: either flat list may carry this channel's own reason
|
|
1052
|
+
const fromModels = select(ui.denyModels, true);
|
|
1053
|
+
const fromAdapters = fromModels === undefined ? select(ui.deny, true) : undefined;
|
|
1054
|
+
const entry = fromModels ?? fromAdapters;
|
|
1055
|
+
if (entry === undefined) {
|
|
1056
|
+
const shared = ui.deny.has(row.adapter)
|
|
1057
|
+
? `every ${row.adapter} channel is out together — take the adapter back in on the rail`
|
|
1058
|
+
: "a shared entry covers other channels too";
|
|
1059
|
+
ui.notice = `${id} stays out — ${row.reasons.join("; ")} — Space edits only this channel's own entries; ${shared}`;
|
|
1060
|
+
bump();
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
if (fromModels !== undefined)
|
|
1064
|
+
ui.denyModels = new Set([...ui.denyModels].filter((staged) => staged !== entry));
|
|
1065
|
+
else
|
|
1066
|
+
ui.deny = new Set([...ui.deny].filter((staged) => staged !== entry));
|
|
1067
|
+
ui.lastEdit = { id, text: `space: cleared ${entry} from routing.deny.${fromModels !== undefined ? "models" : "adapters"}` };
|
|
1068
|
+
}
|
|
941
1069
|
bump();
|
|
942
1070
|
return;
|
|
943
1071
|
}
|
|
@@ -1051,6 +1179,7 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
1051
1179
|
shape,
|
|
1052
1180
|
rows: picked.rows,
|
|
1053
1181
|
excludedNote: picked.excludedNote,
|
|
1182
|
+
ledger: picked.ledger ?? [],
|
|
1054
1183
|
// OBS-525: an existing pool round-trips — reopening the picker starts from the staged
|
|
1055
1184
|
// channels instead of an empty selection that would silently replace the pool with a pin.
|
|
1056
1185
|
chain: ui.map[shape]?.pool?.channels.slice() ?? [],
|
|
@@ -1116,10 +1245,37 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
1116
1245
|
return "sub";
|
|
1117
1246
|
return usd;
|
|
1118
1247
|
};
|
|
1248
|
+
// OBS-994/FL-1: the reach line — every collector scope that excludes this row, each with its
|
|
1249
|
+
// config path and matching entry (an unauthed row instead points at the doctor re-probe).
|
|
1250
|
+
const reachDetail = (row) => {
|
|
1251
|
+
if (row.evidence?.unauthed !== undefined)
|
|
1252
|
+
return "unauthed — re-probe with tickmarkr doctor (Space does not toggle reach)";
|
|
1253
|
+
const reasons = row.reasons.join("; ");
|
|
1254
|
+
if (row.reach === "out-all")
|
|
1255
|
+
return `reach: out · all seats — ${reasons}`;
|
|
1256
|
+
if (row.reach === "out-allow")
|
|
1257
|
+
return `reach: out · all seats · allow — ${reasons}`;
|
|
1258
|
+
if (row.reach === "out-workers")
|
|
1259
|
+
return `reach: out · workers only (judge/review/consult unaffected) — ${reasons}`;
|
|
1260
|
+
if (row.reach === "unknown")
|
|
1261
|
+
return `reach: unknown — ${reasons}`;
|
|
1262
|
+
return "reach: in — Space cycles to out · workers";
|
|
1263
|
+
};
|
|
1264
|
+
const reachCell = (row) => row.reach === "in" || row.reach === "unknown" ? row.reach : `out ${row.reach === "out-workers" ? "workers" : row.reach.slice(4)}`;
|
|
1265
|
+
const identityDetail = (row) => {
|
|
1266
|
+
if (row.evidence?.identity === undefined)
|
|
1267
|
+
return "";
|
|
1268
|
+
return row.uncoveredAlias
|
|
1269
|
+
? `resolved identity ${row.evidence.identity} — no deny entry covers it`
|
|
1270
|
+
: `resolved identity ${row.evidence.identity}`;
|
|
1271
|
+
};
|
|
1119
1272
|
const modelDetail = (row) => {
|
|
1120
1273
|
const parts = [
|
|
1121
1274
|
`${row.adapter}:${row.model}`,
|
|
1122
1275
|
row.tier ?? "unclassified",
|
|
1276
|
+
ui.lastEdit?.id === `${row.adapter}:${row.model}` ? ui.lastEdit.text : "",
|
|
1277
|
+
identityDetail(row),
|
|
1278
|
+
row.tier ? reachDetail(row) : "",
|
|
1123
1279
|
row.variants?.length ? `variants ${row.variants.join(", ")}` : "",
|
|
1124
1280
|
row.classifyModel ? `classify writes ${row.classifyModel}` : "",
|
|
1125
1281
|
row.foldedModels?.length ? `${row.foldedModels.length} gateway ids folded: ${row.foldedModels.join(", ")}` : "",
|
|
@@ -1138,20 +1294,22 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
1138
1294
|
const tier = tierCell(row);
|
|
1139
1295
|
const showProbe = bodyW >= 76;
|
|
1140
1296
|
const showPrice = bodyW >= 60;
|
|
1141
|
-
// every cell accounted for: pointer 2 + glyph 1 + gap 1 + tier 11 + ctx 6 (+ price 12 + probe 7)
|
|
1142
|
-
const nameW = bodyW - 4 - 11 - 6 - (showPrice ? 12 : 0) - (showProbe ? 7 : 0);
|
|
1297
|
+
// every cell accounted for: pointer 2 + glyph 1 + gap 1 + tier 11 + reach 12 + ctx 6 (+ price 12 + probe 7)
|
|
1298
|
+
const nameW = bodyW - 4 - 11 - 12 - 6 - (showPrice ? 12 : 0) - (showProbe ? 7 : 0);
|
|
1143
1299
|
// OBS-531: deep router ids (omp/prime-agent) clip tail-preserving — the LAST segment is the
|
|
1144
1300
|
// distinguishing half; end-clipping rendered ten identical "prime-agent/anthropic/claude-…" rows.
|
|
1145
1301
|
const folded = row.foldedModels?.length ?? 1;
|
|
1146
1302
|
const name = clipPathTail(`${row.adapter}/${row.model}${folded > 1 ? ` ×${folded}` : ""}`, nameW);
|
|
1147
1303
|
const prefixLen = Math.min(name.length, row.adapter.length + 1);
|
|
1148
|
-
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), row.
|
|
1304
|
+
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), row.reach === "out-all" || row.reach === "out-allow"
|
|
1149
1305
|
? _jsx(Glyph, { kind: "off" })
|
|
1150
|
-
: row.evidence?.unauthed !== undefined
|
|
1306
|
+
: row.evidence?.unauthed !== undefined || row.reach === "unknown"
|
|
1151
1307
|
? _jsx(Glyph, { kind: "fail" })
|
|
1152
|
-
: row.
|
|
1153
|
-
? _jsx(Glyph, { kind: "
|
|
1154
|
-
:
|
|
1308
|
+
: row.reach === "out-workers"
|
|
1309
|
+
? _jsx(Glyph, { kind: "warn" })
|
|
1310
|
+
: row.tier
|
|
1311
|
+
? _jsx(Glyph, { kind: "on" })
|
|
1312
|
+
: _jsx(Glyph, { kind: "unknown" }), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: row.denied, children: [_jsx(Text, { dimColor: true, children: name.slice(0, prefixLen) }), _jsx(Text, { bold: selected, children: name.slice(prefixLen).padEnd(Math.max(nameW - prefixLen, 0)) })] }), _jsx(Text, { color: tier.color, dimColor: tier.dim, children: padCellStart(tier.text, 11) }), _jsx(Text, { dimColor: row.reach === "in", children: padCellStart(row.tier ? reachCell(row) : "", 12) }), _jsx(Text, { dimColor: true, children: padCellStart(fmtCtx(row.evidence?.contextWindow), 6) }), showPrice && _jsx(Text, { dimColor: priceCell(row) === "sub", children: padCellStart(priceCell(row), 12) }), showProbe && _jsx(Text, { dimColor: true, children: padCellStart(fmtMs(row.evidence?.probeMs), 7) })] }, `${row.adapter}:${row.model}`));
|
|
1155
1313
|
};
|
|
1156
1314
|
const renderPlainRow = (id, label, selected, rowW = bodyW - 2) => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), _jsx(Text, { bold: selected, children: clip(label, rowW) })] }, id));
|
|
1157
1315
|
const renderShapeRow = (row, selected, rowW = bodyW - 2) => {
|
|
@@ -1213,12 +1371,24 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
1213
1371
|
}
|
|
1214
1372
|
if (overlay.kind === "candidates") {
|
|
1215
1373
|
const rows = overlay.rows.filter((candidate) => matches(candidate.label, ui.filter));
|
|
1216
|
-
|
|
1374
|
+
// the shape-wide captions, bounded to half the list; per-channel ledger rows are list items below
|
|
1375
|
+
const notes = overlay.excludedNote?.split("\n") ?? [];
|
|
1376
|
+
const noteRoom = Math.max(2, Math.floor(capacity / 2));
|
|
1377
|
+
const noteLines = notes.length > noteRoom
|
|
1378
|
+
? [...notes.slice(0, noteRoom - 1), `… +${notes.length - noteRoom + 1} more notes`]
|
|
1379
|
+
: notes;
|
|
1380
|
+
const items = [
|
|
1381
|
+
...rows.map((candidate) => ({ candidate, line: candidate.label })),
|
|
1382
|
+
...overlay.ledger.filter((line) => matches(line, ui.filter)).map((line) => ({ candidate: undefined, line })),
|
|
1383
|
+
];
|
|
1384
|
+
const { visible, start, above, below } = windowRows(items, overlay.at, Math.max(1, capacity - noteLines.length + 1));
|
|
1217
1385
|
const chained = overlay.chain.length > 0;
|
|
1218
|
-
return (_jsxs(OverlayPanel, { title: chained ? `pool · ${overlay.shape}` : `pin · ${overlay.shape}`, width: bodyW, children: [_jsx(Text, { dimColor: true, wrap: "truncate", children: clip("Enter pins one channel · Space selects a pool in order — Enter then asks its mode (pool replaces pin)", bodyW - 4) }), _jsx(SearchRow, { filter: ui.filter, active: true }),
|
|
1219
|
-
&& _jsx(Text, { dimColor: true, wrap: "truncate", children: clip(overlay.excludedNote, bodyW - 4) }), above > 0 && _jsx(ElisionMark, { count: above, side: "above" }), visible.map((candidate, index) => {
|
|
1220
|
-
const at = overlay.chain.indexOf(candidate.id);
|
|
1386
|
+
return (_jsxs(OverlayPanel, { title: chained ? `pool · ${overlay.shape}` : `pin · ${overlay.shape}`, width: bodyW, children: [_jsx(Text, { dimColor: true, wrap: "truncate", children: clip("Enter pins one channel · Space selects a pool in order — Enter then asks its mode (pool replaces pin)", bodyW - 4) }), _jsx(SearchRow, { filter: ui.filter, active: true }), noteLines.map((line, index) => _jsx(Text, { dimColor: true, wrap: "truncate", children: clip(line, bodyW - 4) }, `excluded-${index}`)), above > 0 && _jsx(ElisionMark, { count: above, side: "above" }), visible.map(({ candidate, line }, index) => {
|
|
1221
1387
|
const selected = start + index === overlay.at;
|
|
1388
|
+
if (candidate === undefined) {
|
|
1389
|
+
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), _jsx(Text, { dimColor: true, children: "✗ " }), _jsx(Text, { bold: selected, dimColor: !selected, children: clip(line, bodyW - 10) })] }, `ledger-${start + index}`));
|
|
1390
|
+
}
|
|
1391
|
+
const at = overlay.chain.indexOf(candidate.id);
|
|
1222
1392
|
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), at === -1 ? _jsx(Text, { dimColor: true, children: "· " }) : _jsx(Text, { color: INK.brand, children: `${at + 1} ` }), _jsx(Text, { bold: selected, children: clip(candidate.label, bodyW - 10) })] }, candidate.id));
|
|
1223
1393
|
}), below > 0 && _jsx(ElisionMark, { count: below, side: "below" })] }));
|
|
1224
1394
|
}
|
|
@@ -1263,8 +1433,9 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
1263
1433
|
const scopeLabel = ui.adapterAt === -1
|
|
1264
1434
|
? "All models"
|
|
1265
1435
|
: modelGroups[ui.adapterAt]?.adapter ?? "";
|
|
1266
|
-
|
|
1267
|
-
|
|
1436
|
+
// OBS-994/FL-1 repair: an adapter-wide deny (flat or workers) no longer hides this
|
|
1437
|
+
// scope's rows — every channel still needs its own reach cell, toggleable right here.
|
|
1438
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { bold: true, children: scopeLabel }), _jsx(Text, { dimColor: true, children: ` ${rows.length}` })] }), _jsx(SearchRow, { filter: ui.filter, active: ui.searching, hint: "/ to search" }), modelVisibilityLine() && _jsx(Text, { dimColor: true, children: ` ${modelVisibilityLine()}` }), above > 0 && _jsx(ElisionMark, { count: above, side: "above" }), visible.map((row, index) => renderModelRow(row, ui.focus === "list" && start + index === ui.listAt)), below > 0 && _jsx(ElisionMark, { count: below, side: "below", hint: "/ to search" }), rows.length === 0 && _jsx(Text, { dimColor: true, children: " no models match" })] }));
|
|
1268
1439
|
}
|
|
1269
1440
|
if (ui.view === "shapes") {
|
|
1270
1441
|
const rows = shapeList();
|
|
@@ -1281,7 +1452,9 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
1281
1452
|
if (overlay) {
|
|
1282
1453
|
if (overlay.kind === "candidates") {
|
|
1283
1454
|
const rows = overlay.rows.filter((candidate) => matches(candidate.label, ui.filter));
|
|
1284
|
-
|
|
1455
|
+
const ledger = overlay.ledger.filter((line) => matches(line, ui.filter));
|
|
1456
|
+
const line = rows[overlay.at]?.label ?? ledger[overlay.at - rows.length];
|
|
1457
|
+
return line ? [line] : [];
|
|
1285
1458
|
}
|
|
1286
1459
|
if (overlay.kind === "prefer") {
|
|
1287
1460
|
return overlay.chain.length ? [`chain: ${overlay.chain.join(" → ")}`] : [];
|
|
@@ -1405,7 +1578,7 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
|
|
|
1405
1578
|
? _jsx(Text, { color: INK.warn, children: ` ! ${clip(ui.notice, width - 4)}` })
|
|
1406
1579
|
: details.map((line) => _jsx(Text, { dimColor: true, children: ` ${clip(line, width - 2)}` }, line)), _jsx(KeyBar, { binds: keyBinds() })] }));
|
|
1407
1580
|
}
|
|
1408
|
-
export async function runFleetInkEditor({ ageMs, adapters, health, initialDenyAdapters, initialDenyModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, entry = "probe", initialJudge = "", judgeSeats = [], initialInput = [], input, output, debug = false, }) {
|
|
1581
|
+
export async function runFleetInkEditor({ ageMs, adapters, health, initialDenyAdapters, initialDenyModels, initialDenyWorkersAdapters = [], initialDenyWorkersModels = [], modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, stagedRouting, entry = "probe", initialJudge = "", judgeSeats = [], initialInput = [], input, output, debug = false, }) {
|
|
1409
1582
|
const agents = adapters.flatMap((adapter) => {
|
|
1410
1583
|
const state = health[adapter.id];
|
|
1411
1584
|
if (!state?.installed)
|
|
@@ -1429,7 +1602,7 @@ export async function runFleetInkEditor({ ageMs, adapters, health, initialDenyAd
|
|
|
1429
1602
|
// rebuilt from output.columns/rows so a pane resize re-renders at the new size. Same root
|
|
1430
1603
|
// element type on every rerender ⇒ React keeps the component instance, so staged edits,
|
|
1431
1604
|
// cursor, and overlays all survive the resize.
|
|
1432
|
-
const editorElement = () => (_jsx(FleetApp, { ageMs: ageMs, agents: agents, initialDenyAdapters: initialDenyAdapters, initialDenyModels: initialDenyModels, modelGroups: declaredModelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewOverlay: reviewOverlay, reloadGuard: reloadGuard, entry: entry, initialJudge: initialJudge, judgeSeats: judgeSeats,
|
|
1605
|
+
const editorElement = () => (_jsx(FleetApp, { ageMs: ageMs, agents: agents, initialDenyAdapters: initialDenyAdapters, initialDenyModels: initialDenyModels, initialDenyWorkersAdapters: initialDenyWorkersAdapters, initialDenyWorkersModels: initialDenyWorkersModels, modelGroups: declaredModelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewOverlay: reviewOverlay, reloadGuard: reloadGuard, stagedRouting: stagedRouting, entry: entry, initialJudge: initialJudge, judgeSeats: judgeSeats,
|
|
1433
1606
|
// v1.90.9: cap every list to the terminal minus chrome so the cursor and header can
|
|
1434
1607
|
// never scroll off-screen (omp's 218-model list did exactly that pre-cap).
|
|
1435
1608
|
// OBS-523: chrome is ≤12 rows worst case (header, borders, padding, scope+search,
|