tickmarkr 2.6.5 → 2.6.7
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 +7 -3
- package/dist/adapters/claude-code.js +8 -2
- package/dist/adapters/codex-commit-check.d.ts +16 -4
- package/dist/adapters/codex-commit-check.js +150 -26
- package/dist/adapters/codex.d.ts +1 -0
- package/dist/adapters/codex.js +20 -8
- package/dist/adapters/types.d.ts +4 -1
- package/dist/cli/commands/beat.d.ts +6 -1
- package/dist/cli/commands/beat.js +123 -76
- package/dist/cli/commands/doctor.js +3 -1
- package/dist/cli/commands/fleet.js +368 -85
- package/dist/cli/commands/init.js +23 -9
- package/dist/cli/commands/plan.js +12 -1
- package/dist/cli/commands/verify.js +37 -9
- package/dist/cli/help.d.ts +1 -0
- package/dist/cli/help.js +10 -5
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/config/fleet-overlay.d.ts +14 -0
- package/dist/config/fleet-overlay.js +154 -25
- package/dist/drivers/orca.d.ts +17 -3
- package/dist/drivers/orca.js +64 -20
- package/dist/gates/baseline.d.ts +9 -0
- package/dist/gates/baseline.js +14 -3
- package/dist/gates/cache.js +1 -1
- package/dist/gates/review.js +15 -0
- package/dist/gates/run-gates.d.ts +20 -7
- package/dist/gates/run-gates.js +345 -210
- package/dist/gates/test-manifest.d.ts +11 -0
- package/dist/gates/test-manifest.js +151 -111
- package/dist/gates/timeout-shaped.d.ts +3 -0
- package/dist/gates/timeout-shaped.js +10 -0
- package/dist/run/beat-lifecycle.d.ts +154 -0
- package/dist/run/beat-lifecycle.js +825 -0
- package/dist/run/daemon.js +202 -126
- package/dist/run/git-trust.d.ts +27 -0
- package/dist/run/git-trust.js +178 -0
- package/dist/run/git.js +34 -14
- package/dist/run/journal.js +15 -3
- package/dist/run/lease.d.ts +23 -0
- package/dist/run/lease.js +232 -38
- package/dist/run/merge.js +2 -2
- package/dist/run/stall.js +20 -1
- package/dist/run/supervision.d.ts +8 -0
- package/dist/run/supervision.js +24 -4
- package/dist/run/verification-job.d.ts +51 -0
- package/dist/run/verification-job.js +137 -0
- package/dist/tui/ink/fleet-app.d.ts +35 -5
- package/dist/tui/ink/fleet-app.js +244 -124
- package/package.json +1 -1
- package/skills/tickmarkr-auto/SKILL.md +19 -10
- package/skills/tickmarkr-loop/SKILL.md +19 -10
- package/skills/tickmarkr-overseer/SKILL.md +25 -16
- package/skills/tickmarkr-overseer/scripts/classify-vitest-log.sh +58 -16
- package/skills/tickmarkr-overseer/scripts/grade-ci.sh +19 -6
- package/skills/tickmarkr-overseer/scripts/watch-parks.sh +121 -64
|
@@ -49,7 +49,7 @@ const REACH_CHOICES = [
|
|
|
49
49
|
{ id: "out-workers", label: "out · workers", gloss: "workers skip it; judge/review/consult unaffected" },
|
|
50
50
|
{ id: "out-all", label: "out · all seats", gloss: "no seat routes here" },
|
|
51
51
|
];
|
|
52
|
-
export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts = {}, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, reloadGuard, stagedRouting, entry = "probe", initialJudge = "", judgeSeats = [], viewRows = Number.POSITIVE_INFINITY, frameColumns = 100, frameRows, ...initialDenyProps }) {
|
|
52
|
+
export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts = {}, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, reloadGuard, stagedRouting, holderOf, entry = "probe", initialJudge = "", judgeSeats = [], viewRows = Number.POSITIVE_INFINITY, frameColumns = 100, frameRows, ...initialDenyProps }) {
|
|
53
53
|
const { exit } = useApp();
|
|
54
54
|
const [, setRevision] = useState(0);
|
|
55
55
|
// the props are the fleet command's contract, one per schema-enumerated scope (initialDeny…),
|
|
@@ -71,6 +71,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
71
71
|
notice: "",
|
|
72
72
|
lastEdit: null,
|
|
73
73
|
allowOut: new Set(initialAllowOut),
|
|
74
|
+
seededAllowOut: [],
|
|
74
75
|
...Object.fromEntries(DENY_SCOPES.map((scope) => [scope.key, new Set(initialDeny[scope.key])])),
|
|
75
76
|
classifications: [],
|
|
76
77
|
efforts: structuredClone(initialEfforts),
|
|
@@ -139,12 +140,13 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
139
140
|
let routingMemo = null;
|
|
140
141
|
const stagedPolicy = () => {
|
|
141
142
|
const deny = stagedDeny();
|
|
142
|
-
const
|
|
143
|
+
const stage = stagedMetadata();
|
|
144
|
+
const key = JSON.stringify([deny, stage]);
|
|
143
145
|
if (routingMemo?.key !== key) {
|
|
144
146
|
routingMemo = {
|
|
145
147
|
key,
|
|
146
148
|
policy: stagedRouting
|
|
147
|
-
? stagedRouting(deny)
|
|
149
|
+
? stagedRouting(deny, stage)
|
|
148
150
|
: { ok: true, routing: { deny: denyBlockFrom(denyLists()) } },
|
|
149
151
|
};
|
|
150
152
|
}
|
|
@@ -163,7 +165,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
163
165
|
const reachFor = (adapter, model, identity) => {
|
|
164
166
|
const policy = stagedPolicy();
|
|
165
167
|
if (!policy.ok)
|
|
166
|
-
return { reach: "unknown", reasons: [`preview unavailable (${policy.error})`], uncoveredAlias: false };
|
|
168
|
+
return { reach: "unknown", reasons: [`preview unavailable (${policy.error})`], uncoveredAlias: false, held: [] };
|
|
167
169
|
const channel = { adapter, model, ...(identity !== undefined ? { identity } : {}) };
|
|
168
170
|
const worker = exclusionsOf(channel, policy.routing, "worker");
|
|
169
171
|
const allSeats = exclusionsOf(channel, policy.routing, "judge");
|
|
@@ -175,22 +177,37 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
175
177
|
reach,
|
|
176
178
|
reasons: worker.map(exclusionReason),
|
|
177
179
|
uncoveredAlias: identity !== undefined && !worker.some((scope) => scope.by === "deny"),
|
|
180
|
+
held: [...new Set(worker.map((scope) => scope.configPath))].flatMap((dotted) => {
|
|
181
|
+
const holder = holderOf?.(dotted);
|
|
182
|
+
return holder === undefined ? [] : [`${dotted} @ ${holder}`];
|
|
183
|
+
}),
|
|
178
184
|
};
|
|
179
185
|
};
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
// T5: the real channels a displayed row stands for — every collapsed variant and catalog-fold
|
|
187
|
+
// constituent fleet derived from the cached doctor list, each with its own identity; a row without
|
|
188
|
+
// them stands for its folded ids, else its reference real id (classifyModel) or its own. Display labels
|
|
189
|
+
// are never channels.
|
|
190
|
+
const membersOf = (adapter, row) => {
|
|
191
|
+
const listed = row.members?.length ? row.members
|
|
192
|
+
: (row.foldedModels?.length ? row.foldedModels : [row.classifyModel ?? row.model]).map((model) => ({ model, identity: row.evidence?.identity }));
|
|
193
|
+
// one channel per real id: a repeated id is one member, so a grouped act takes ONE reason from it
|
|
194
|
+
return listed.filter((member, at) => listed.findIndex((other) => other.model === member.model) === at)
|
|
195
|
+
.map((member) => ({ adapter, model: member.model, ...(member.identity !== undefined ? { identity: member.identity } : {}) }));
|
|
196
|
+
};
|
|
197
|
+
// OBS-1099 review: a displayed row may fold several discovered gateway ids; each real member is a
|
|
198
|
+
// channel the policy matches on its own, so every one but a channel itself counts as a sibling —
|
|
199
|
+
// an entry covering another member is shared, never channel-owned (grouping never privatizes it)
|
|
200
|
+
const displayedChannels = () => modelGroups.flatMap((group) => group.rows.flatMap((other) => membersOf(group.adapter, other)));
|
|
186
201
|
const siblingsOf = (self) => displayedChannels()
|
|
187
202
|
.filter((other) => other.adapter !== self.adapter || other.model !== self.model);
|
|
188
203
|
// Judge c3 (R107): exactly ONE selected entry per act — the channel's adapter:model key when it
|
|
189
|
-
// is a candidate, else the first candidate in sorted order; only an entry no sibling matches
|
|
190
|
-
|
|
204
|
+
// is a candidate, else the first candidate in sorted order; only an entry no sibling matches.
|
|
205
|
+
// T5: matched as production matches the list — a deny entry with family matching, an allow-complement
|
|
206
|
+
// entry exactly as routing.allow admits (`fake:C-low` never reaches a bare `fake:C` there)
|
|
207
|
+
const selectOwn = (entries, self, family = true) => {
|
|
191
208
|
const others = siblingsOf(self);
|
|
192
|
-
const candidates = [...entries].filter((entry) => entryMatchesChannel(entry, self,
|
|
193
|
-
&& !others.some((other) => entryMatchesChannel(entry, other,
|
|
209
|
+
const candidates = [...entries].filter((entry) => entryMatchesChannel(entry, self, family)
|
|
210
|
+
&& !others.some((other) => entryMatchesChannel(entry, other, family)));
|
|
194
211
|
const id = `${self.adapter}:${self.model}`;
|
|
195
212
|
return candidates.includes(id) ? id : candidates.sort()[0];
|
|
196
213
|
};
|
|
@@ -280,11 +297,36 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
280
297
|
const shared = isAdapterWide(scope.entry, self) || covers.length > 1;
|
|
281
298
|
return { entry: scope.entry, scope: scope.configPath, covers, shared, edits: reachEdits(owned) };
|
|
282
299
|
};
|
|
300
|
+
// T5: a grouped row's reach over EVERY real member — the whole-group state only when every member
|
|
301
|
+
// holds it; otherwise the most-excluded member's reach, marked partial, each member-specific reason
|
|
302
|
+
// prefixed with its real id. A one-member row is exactly its channel's reach.
|
|
303
|
+
const rowReach = (members) => {
|
|
304
|
+
const each = members.map((member) => ({ model: member.model, ...reachFor(member.adapter, member.model, member.identity) }));
|
|
305
|
+
if (each.length === 1) {
|
|
306
|
+
const { model: _model, ...only } = each[0];
|
|
307
|
+
return { ...only, partial: false };
|
|
308
|
+
}
|
|
309
|
+
const folded = foldReach(each);
|
|
310
|
+
const uniform = each.every((channel) => channel.reach === each[0].reach);
|
|
311
|
+
return {
|
|
312
|
+
reach: uniform ? each[0].reach : folded.reach,
|
|
313
|
+
reasons: folded.reasons,
|
|
314
|
+
partial: folded.partial,
|
|
315
|
+
uncoveredAlias: each.some((channel) => channel.uncoveredAlias),
|
|
316
|
+
held: [...new Set(each.flatMap((channel) => channel.held))],
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
// T5: the covering entry a grouped row names — a shared one first, so l owns it
|
|
320
|
+
const rowCovering = (members) => {
|
|
321
|
+
const found = members.map(coveringFor).filter((covering) => covering !== undefined);
|
|
322
|
+
return found.find((covering) => covering.shared) ?? found[0];
|
|
323
|
+
};
|
|
283
324
|
const groupRows = (group) => {
|
|
284
325
|
const rows = group.rows.map((row) => {
|
|
326
|
+
const members = membersOf(group.adapter, row);
|
|
285
327
|
const staged = ui.classifications.find((classification) => classification.adapter === group.adapter
|
|
286
|
-
&& (classification.model === row.model ||
|
|
287
|
-
const reach =
|
|
328
|
+
&& (classification.model === row.model || members.some((member) => member.model === classification.model)));
|
|
329
|
+
const reach = rowReach(members);
|
|
288
330
|
return {
|
|
289
331
|
adapter: group.adapter,
|
|
290
332
|
model: row.model,
|
|
@@ -296,16 +338,17 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
296
338
|
classifyModel: row.classifyModel,
|
|
297
339
|
variants: row.variants,
|
|
298
340
|
foldedModels: row.foldedModels,
|
|
341
|
+
...(row.members ? { members: row.members } : {}),
|
|
299
342
|
score: row.score,
|
|
300
343
|
denied: reach.reach !== "in",
|
|
301
344
|
...reach,
|
|
302
345
|
// OBS-1099 item 2 (review round 2): every row is covered the same way — an alias through
|
|
303
346
|
// its identity, a plain row by a bare-model or adapter entry — so a shared bare-model
|
|
304
347
|
// entry across adapters has the one owning control too
|
|
305
|
-
covering:
|
|
348
|
+
covering: rowCovering(members),
|
|
306
349
|
};
|
|
307
350
|
});
|
|
308
|
-
const known = new Set(rows.flatMap((row) =>
|
|
351
|
+
const known = new Set(group.rows.flatMap((row) => [row.model, ...membersOf(group.adapter, row).map((member) => member.model)]));
|
|
309
352
|
for (const staged of ui.classifications) {
|
|
310
353
|
if (staged.adapter === group.adapter && !known.has(staged.model)) {
|
|
311
354
|
const reach = reachFor(group.adapter, staged.model);
|
|
@@ -377,12 +420,13 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
377
420
|
// ── state transitions ──────────────────────────────────────────────────────
|
|
378
421
|
// T8: the previews fold the CURRENT staged metadata — never only what the last review saw
|
|
379
422
|
const stagedMetadata = () => {
|
|
380
|
-
const { classifications, efforts } = editorState();
|
|
381
|
-
return { classifications, efforts };
|
|
423
|
+
const { classifications, efforts, seededAllowOut } = editorState();
|
|
424
|
+
return { classifications, efforts, seededAllowOut };
|
|
382
425
|
};
|
|
383
426
|
const editorState = () => ({
|
|
384
427
|
...denyLists(),
|
|
385
428
|
allowOut: [...ui.allowOut].sort(),
|
|
429
|
+
seededAllowOut: structuredClone(ui.seededAllowOut),
|
|
386
430
|
classifications: ui.classifications.map((classification) => classification.vendor && classification.channel
|
|
387
431
|
? {
|
|
388
432
|
...classification,
|
|
@@ -430,9 +474,40 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
430
474
|
finish({ kind: "no-changes" });
|
|
431
475
|
return;
|
|
432
476
|
}
|
|
477
|
+
if (nextReview.kind === "refused") {
|
|
478
|
+
ui.notice = `w refused — ${nextReview.reason}`;
|
|
479
|
+
bump();
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
433
482
|
setOverlay({ kind: "review", review: nextReview, scroll: 0 });
|
|
434
483
|
};
|
|
484
|
+
// T10 (D-925/D-939): a model the current allow form leaves out gets its OWN allow entry staged beside
|
|
485
|
+
// its classification — the ownAllow spelling Space clears — so it can be admitted in this session.
|
|
486
|
+
// Re-staging a classification replaces its seed; a row the allow form admits seeds nothing. A row a
|
|
487
|
+
// flat deny ALSO excludes still seeds, so successive owning lifts (deny, then allow) admit it.
|
|
488
|
+
// T5: one seed per REAL member of a grouped row (collapsed variants, catalog-fold constituents), keyed and
|
|
489
|
+
// spelled by its real id with its own identity — the display label never names a channel.
|
|
490
|
+
const seedAllow = (self, displayModel) => {
|
|
491
|
+
const policy = stagedPolicy();
|
|
492
|
+
if (!policy.ok || !exclusionsOf(self, policy.routing, "judge").some((scope) => scope.by === "allow"))
|
|
493
|
+
return;
|
|
494
|
+
const entry = selectOwn(ui.allowOut, self, false) ?? `${self.adapter}:${self.model}`;
|
|
495
|
+
ui.allowOut = new Set([...ui.allowOut, entry]);
|
|
496
|
+
ui.seededAllowOut = [
|
|
497
|
+
...ui.seededAllowOut.filter((seed) => seed.adapter !== self.adapter || seed.model !== self.model),
|
|
498
|
+
{ adapter: self.adapter, model: self.model, displayModel, ...(self.identity !== undefined ? { identity: self.identity } : {}), entry },
|
|
499
|
+
];
|
|
500
|
+
};
|
|
501
|
+
// T5: the real members a classification of this browser row stages — a row the browser never listed
|
|
502
|
+
// (n adds one) is its own one member
|
|
503
|
+
const classifiedMembers = (adapter, displayModel) => {
|
|
504
|
+
const row = groupOf(adapter)?.rows.find((candidate) => candidate.model === displayModel);
|
|
505
|
+
return row ? membersOf(adapter, row) : [{ adapter, model: displayModel }];
|
|
506
|
+
};
|
|
435
507
|
const stageSuggested = (adapter, rows, firstTouch) => {
|
|
508
|
+
for (const row of rows) {
|
|
509
|
+
seedAllow({ adapter, model: row.model, ...(row.identity !== undefined ? { identity: row.identity } : {}) }, row.displayModel);
|
|
510
|
+
}
|
|
436
511
|
ui.classifications = [
|
|
437
512
|
...ui.classifications,
|
|
438
513
|
...rows.map((row) => ({
|
|
@@ -485,17 +560,23 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
485
560
|
}
|
|
486
561
|
setOverlay({ ...base, stage: "channel", vendor: group.vendor });
|
|
487
562
|
};
|
|
563
|
+
// T5: the picked tier and provenance land on EVERY real member of the row, each with its own seed —
|
|
564
|
+
// classifyModel stays the reference id the suggestion and the overlay title name
|
|
488
565
|
const applyClassification = (overlay) => {
|
|
489
566
|
const answered = overlay.vendor ? ui.channelByAdapter[overlay.adapter] : undefined;
|
|
567
|
+
const displayModel = overlay.displayModel ?? overlay.model;
|
|
568
|
+
const members = classifiedMembers(overlay.adapter, displayModel);
|
|
569
|
+
for (const member of members)
|
|
570
|
+
seedAllow(member, displayModel);
|
|
490
571
|
ui.classifications = [
|
|
491
572
|
...ui.classifications,
|
|
492
|
-
{
|
|
573
|
+
...members.map((member) => ({
|
|
493
574
|
adapter: overlay.adapter,
|
|
494
|
-
model:
|
|
575
|
+
model: member.model,
|
|
495
576
|
tier: TIERS[overlay.tierAt],
|
|
496
577
|
note: overlay.note.trim(),
|
|
497
578
|
...(overlay.vendor && answered ? { vendor: overlay.vendor, channel: answered } : {}),
|
|
498
|
-
},
|
|
579
|
+
})),
|
|
499
580
|
];
|
|
500
581
|
setOverlay(null);
|
|
501
582
|
};
|
|
@@ -523,8 +604,9 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
523
604
|
// channel-specific one carries its channel, so a partial exclusion is never intersected away.
|
|
524
605
|
const adapterReach = (id) => {
|
|
525
606
|
const group = modelGroups.find((candidate) => candidate.adapter === id);
|
|
526
|
-
|
|
527
|
-
|
|
607
|
+
// T5: every real member of every row — a collapsed or folded row's display label is no channel
|
|
608
|
+
const channels = (group?.rows ?? []).flatMap((row) => membersOf(id, row))
|
|
609
|
+
.map((channel) => ({ model: channel.model, ...reachFor(id, channel.model, channel.identity) }));
|
|
528
610
|
if (channels.length === 0) {
|
|
529
611
|
// ponytail: a rail entry with no probed channels has only its own literal entries to show
|
|
530
612
|
const reasons = [
|
|
@@ -537,6 +619,11 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
537
619
|
: NESTED_ADAPTER_SCOPES.some((scope) => ui[scope.key].has(id)) ? "out-workers" : "in";
|
|
538
620
|
return { reach, reasons, partial: false };
|
|
539
621
|
}
|
|
622
|
+
return foldReach(channels);
|
|
623
|
+
};
|
|
624
|
+
// the rail's (and a grouped row's) fold over its real channels — most-excluded reach, partial when they
|
|
625
|
+
// disagree, shared reasons once and channel-specific ones carrying their channel
|
|
626
|
+
function foldReach(channels) {
|
|
540
627
|
const unknown = channels.find((channel) => channel.reach === "unknown");
|
|
541
628
|
if (unknown)
|
|
542
629
|
return { reach: "unknown", reasons: unknown.reasons, partial: false };
|
|
@@ -551,7 +638,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
551
638
|
.filter((reason) => !shared.includes(reason))
|
|
552
639
|
.map((reason) => `${channel.model}: ${reason}`));
|
|
553
640
|
return { reach, reasons: [...shared, ...own], partial };
|
|
554
|
-
}
|
|
641
|
+
}
|
|
555
642
|
const describeReach = (state) => state.partial ? `${state.reach} (partial: not every channel)` : state.reach;
|
|
556
643
|
// OBS-1099 items 1 and 4: one picker choice changes exactly ONE selected entry in ONE scope —
|
|
557
644
|
// added, cleared, or re-scoped between the workers list and the flat list — then the row's
|
|
@@ -641,90 +728,106 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
641
728
|
if (!row)
|
|
642
729
|
return;
|
|
643
730
|
const id = `${row.adapter}:${row.model}`;
|
|
644
|
-
|
|
645
|
-
//
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
return;
|
|
731
|
+
// T5: a grouped row's act is the same one-reason act on EVERY real member, each decided from the reach
|
|
732
|
+
// it held BEFORE this act — a sibling's fresh entry that also matches a member's recorded identity never
|
|
733
|
+
// turns that member's own addition into "already" (review D-1081) — while every removal still takes only
|
|
734
|
+
// an entry the member owns alone over the staged sets; a member a shared entry keeps out is named and left
|
|
735
|
+
const members = membersOf(row.adapter, row);
|
|
736
|
+
const before = members.map((self) => (members.length === 1
|
|
737
|
+
? { reach: row.reach, reasons: row.reasons, covering: row.covering }
|
|
738
|
+
: { ...reachFor(self.adapter, self.model, self.identity), covering: coveringFor(self) }));
|
|
739
|
+
const outcomes = members.map((self, at) => {
|
|
740
|
+
const memberId = `${self.adapter}:${self.model}`;
|
|
741
|
+
const { reasons, covering } = before[at];
|
|
742
|
+
// LEG2-T3 round 2 finding 1 / D-233 / D-235 (review round 10): ONE ordered selector —
|
|
743
|
+
// ownedDenies — feeds the detail line AND every Space edit, so the entry named is the entry
|
|
744
|
+
// edited: a clear takes its head (own key first, then collector order); a demotion takes its
|
|
745
|
+
// first flat entry, a promotion its first workers entry; a bare adapter id or a sibling's
|
|
746
|
+
// entry is never in it. OBS-1046: an allow exclusion is the LAST reason taken, never together
|
|
747
|
+
// with an authored deny.
|
|
748
|
+
const edits = reachEdits(ownedDenies(self));
|
|
749
|
+
// review D-1081 add.: the seed this session generated for this member is its own even when a peer
|
|
750
|
+
// member's recorded identity also reaches it (C-high recorded as C-low) — never authored, and lifting
|
|
751
|
+
// it admits only this act's members; a seed any channel outside the row matches stays shared
|
|
752
|
+
const ownSeed = () => ui.seededAllowOut.find((seed) => seed.adapter === self.adapter && seed.model === self.model
|
|
753
|
+
&& ui.allowOut.has(seed.entry)
|
|
754
|
+
&& !siblingsOf(self).some((other) => !members.some((peer) => peer.adapter === other.adapter && peer.model === other.model)
|
|
755
|
+
&& entryMatchesChannel(seed.entry, other, false)))?.entry;
|
|
756
|
+
const ownAllow = () => {
|
|
757
|
+
const fromAllow = selectOwn(ui.allowOut, self, false) ?? ownSeed();
|
|
758
|
+
return fromAllow === undefined ? undefined : { entry: fromAllow, scope: "routing.allow" };
|
|
759
|
+
};
|
|
760
|
+
const ownFlat = () => edits.demote ?? ownAllow();
|
|
761
|
+
const ownWorkers = () => edits.promote;
|
|
762
|
+
const takeFlat = (found) => {
|
|
763
|
+
const scope = scopeOf(found.scope);
|
|
764
|
+
if (scope)
|
|
765
|
+
ui[scope.key] = without(ui[scope.key], found.entry);
|
|
766
|
+
else
|
|
767
|
+
ui.allowOut = without(ui.allowOut, found.entry);
|
|
768
|
+
};
|
|
769
|
+
const refuse = (reachLabel) => {
|
|
770
|
+
// OBS-1099 item 2: a covered row names the covering entry, every identity it covers, and
|
|
771
|
+
// the ONE control that lifts it — the T1 refusal wording stays in front of it
|
|
772
|
+
if (covering?.shared) {
|
|
773
|
+
// the notice clips at the browser width; the detail line and the l overlay list every identity
|
|
774
|
+
return { refused: `${memberId} stays ${reachLabel} — ${covering.scope} (${covering.entry}) covers ${covering.covers.length} channels — Space edits only this channel's own entries; l lists and lifts it` };
|
|
775
|
+
}
|
|
776
|
+
const shared = ui.denyAdapters.has(row.adapter) || ui.allowOut.has(row.adapter) || ui.denyWorkersAdapters.has(row.adapter)
|
|
777
|
+
? `every ${row.adapter} channel is out together — set the adapter's reach on the rail`
|
|
778
|
+
: "a shared entry covers other channels too";
|
|
779
|
+
return { refused: `${memberId} stays ${reachLabel} — ${reasons.join("; ")} — Space edits only this channel's own entries; ${shared}` };
|
|
780
|
+
};
|
|
781
|
+
const reach = before[at].reach;
|
|
782
|
+
const outAll = reach === "out-all" || reach === "out-allow";
|
|
783
|
+
if (choice === "out-workers" && reach === "in") {
|
|
784
|
+
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, memberId);
|
|
785
|
+
return { edit: `added ${memberId} to routing.deny.workers.models` };
|
|
672
786
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
};
|
|
678
|
-
const reach = row.reach;
|
|
679
|
-
const outAll = reach === "out-all" || reach === "out-allow";
|
|
680
|
-
let edit = "";
|
|
681
|
-
if (choice === "out-workers" && reach === "in") {
|
|
682
|
-
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, id);
|
|
683
|
-
edit = `added ${id} to routing.deny.workers.models`;
|
|
684
|
-
}
|
|
685
|
-
else if (choice === "out-workers" && outAll) {
|
|
686
|
-
const found = ownFlat();
|
|
687
|
-
if (found === undefined)
|
|
688
|
-
return refuse("out");
|
|
689
|
-
takeFlat(found);
|
|
690
|
-
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, found.entry);
|
|
691
|
-
edit = `moved ${found.entry} to routing.deny.workers.models`;
|
|
692
|
-
}
|
|
693
|
-
else if (choice === "out-all" && reach === "in") {
|
|
694
|
-
ui.denyModels = withEntry(ui.denyModels, id);
|
|
695
|
-
edit = `added ${id} to routing.deny.models`;
|
|
696
|
-
}
|
|
697
|
-
else if (choice === "out-all" && reach === "out-workers") {
|
|
698
|
-
// OBS-1099 review: a promotion, like a clear, takes only an entry no sibling row matches —
|
|
699
|
-
// a shared workers entry (adapter-wide or a bare id another row serves) is refused by name,
|
|
700
|
-
// never moved for its siblings and never doubled by a fresh flat deny
|
|
701
|
-
const found = ownWorkers();
|
|
702
|
-
if (found === undefined)
|
|
703
|
-
return refuse("out · workers");
|
|
704
|
-
takeFlat(found);
|
|
705
|
-
ui.denyModels = withEntry(ui.denyModels, found.entry);
|
|
706
|
-
edit = `moved ${found.entry} to routing.deny.models`;
|
|
707
|
-
}
|
|
708
|
-
else if (choice === "in" && reach !== "in") {
|
|
709
|
-
// D-205: in clears ONE row-owned reason from either the flat scopes or the workers scope —
|
|
710
|
-
// a shared flat reason (routing.deny.adapters covering every channel) never blocks clearing
|
|
711
|
-
// this row's own workers entry; the recomputed reach then names the shared reason that remains
|
|
712
|
-
const found = edits.clear ?? (outAll ? ownAllow() : undefined);
|
|
713
|
-
if (found !== undefined) {
|
|
787
|
+
else if (choice === "out-workers" && outAll) {
|
|
788
|
+
const found = ownFlat();
|
|
789
|
+
if (found === undefined)
|
|
790
|
+
return refuse("out");
|
|
714
791
|
takeFlat(found);
|
|
715
|
-
|
|
792
|
+
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, found.entry);
|
|
793
|
+
return { edit: `moved ${found.entry} to routing.deny.workers.models` };
|
|
716
794
|
}
|
|
717
|
-
else {
|
|
718
|
-
|
|
795
|
+
else if (choice === "out-all" && reach === "in") {
|
|
796
|
+
ui.denyModels = withEntry(ui.denyModels, memberId);
|
|
797
|
+
return { edit: `added ${memberId} to routing.deny.models` };
|
|
719
798
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
799
|
+
else if (choice === "out-all" && reach === "out-workers") {
|
|
800
|
+
// OBS-1099 review: a promotion, like a clear, takes only an entry no sibling row matches —
|
|
801
|
+
// a shared workers entry (adapter-wide or a bare id another row serves) is refused by name,
|
|
802
|
+
// never moved for its siblings and never doubled by a fresh flat deny
|
|
803
|
+
const found = ownWorkers();
|
|
804
|
+
if (found === undefined)
|
|
805
|
+
return refuse("out · workers");
|
|
806
|
+
takeFlat(found);
|
|
807
|
+
ui.denyModels = withEntry(ui.denyModels, found.entry);
|
|
808
|
+
return { edit: `moved ${found.entry} to routing.deny.models` };
|
|
809
|
+
}
|
|
810
|
+
else if (choice === "in" && reach !== "in") {
|
|
811
|
+
// D-205: in clears ONE row-owned reason from either the flat scopes or the workers scope —
|
|
812
|
+
// a shared flat reason (routing.deny.adapters covering every channel) never blocks clearing
|
|
813
|
+
// this row's own workers entry; the recomputed reach then names the shared reason that remains
|
|
814
|
+
const found = edits.clear ?? (outAll ? ownAllow() : undefined);
|
|
815
|
+
if (found === undefined)
|
|
816
|
+
return refuse(outAll ? "out" : "out · workers");
|
|
817
|
+
takeFlat(found);
|
|
818
|
+
return { edit: `cleared ${found.entry} from ${found.scope}` };
|
|
819
|
+
}
|
|
820
|
+
return { already: true };
|
|
821
|
+
});
|
|
822
|
+
const made = outcomes.flatMap((outcome) => ("edit" in outcome ? [outcome.edit] : []));
|
|
823
|
+
const refused = outcomes.flatMap((outcome) => ("refused" in outcome ? [outcome.refused] : []));
|
|
824
|
+
if (!made.length) {
|
|
825
|
+
ui.notice = refused[0] ?? `${id} is already ${label}`;
|
|
723
826
|
return;
|
|
724
827
|
}
|
|
725
|
-
const after =
|
|
726
|
-
const still = reached(after.reach, choice) ? "" : ` — still out: ${after.reasons.join("; ")}`;
|
|
727
|
-
ui.lastEdit = { id, text: `space: ${
|
|
828
|
+
const after = rowReach(members);
|
|
829
|
+
const still = reached(after.reach, choice) && !after.partial ? "" : ` — still out: ${after.reasons.join("; ")}`;
|
|
830
|
+
ui.lastEdit = { id, text: `space: ${made.join("; ")}${still}` };
|
|
728
831
|
};
|
|
729
832
|
// OBS-1065: Enter on a greyed picker row lifts exactly its row-owned covering entry, then the
|
|
730
833
|
// same picker reopens over the recomputed policy — a shared or adapter-wide entry is refused by
|
|
@@ -1551,7 +1654,13 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1551
1654
|
const group = modelGroups[ui.adapterAt];
|
|
1552
1655
|
const suggested = rows
|
|
1553
1656
|
.filter((candidate) => candidate.adapter === group.adapter && !candidate.tier && candidate.suggestion !== undefined)
|
|
1554
|
-
|
|
1657
|
+
// T5: every real member of each suggested row stages, never only its reference id
|
|
1658
|
+
.flatMap((candidate) => membersOf(candidate.adapter, candidate).map((member) => ({
|
|
1659
|
+
model: member.model,
|
|
1660
|
+
displayModel: candidate.model,
|
|
1661
|
+
...(member.identity !== undefined ? { identity: member.identity } : {}),
|
|
1662
|
+
suggestion: candidate.suggestion,
|
|
1663
|
+
})));
|
|
1555
1664
|
if (!suggested.length) {
|
|
1556
1665
|
ui.notice = "no catalog tier suggestions among the visible unclassified models — evidence comes from the cached catalogs (AA index / API pricing)";
|
|
1557
1666
|
bump();
|
|
@@ -1683,7 +1792,11 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1683
1792
|
const reachDetail = (row) => {
|
|
1684
1793
|
if (row.evidence?.unauthed !== undefined)
|
|
1685
1794
|
return "unauthed — re-probe with tickmarkr doctor (Space does not toggle reach)";
|
|
1686
|
-
|
|
1795
|
+
// T10: the reasons, then where each scope is held — the overlay a reviewed w writes
|
|
1796
|
+
const reasons = `${row.reasons.join("; ")}${row.held.length ? ` — held: ${row.held.join("; ")}` : ""}`;
|
|
1797
|
+
// T5: members disagree — never a whole-group state; each member-specific reason names its real id
|
|
1798
|
+
if (row.partial)
|
|
1799
|
+
return `reach: partial — ${row.reach} for some members only — ${reasons} — Space applies one choice to every member`;
|
|
1687
1800
|
if (row.reach === "out-all")
|
|
1688
1801
|
return `reach: out · all seats — ${reasons} — Space picks in or out · workers`;
|
|
1689
1802
|
if (row.reach === "out-allow")
|
|
@@ -1694,7 +1807,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1694
1807
|
return `reach: unknown — ${reasons}`;
|
|
1695
1808
|
return "reach: in — Space picks out · workers or out · all seats";
|
|
1696
1809
|
};
|
|
1697
|
-
const reachCell = (row) => row.reach === "in" || row.reach === "unknown" ? row.reach : `out ${row.reach === "out-workers" ? "workers" : row.reach.slice(4)}`;
|
|
1810
|
+
const reachCell = (row) => row.partial ? "partial" : row.reach === "in" || row.reach === "unknown" ? row.reach : `out ${row.reach === "out-workers" ? "workers" : row.reach.slice(4)}`;
|
|
1698
1811
|
const identityDetail = (row) => {
|
|
1699
1812
|
if (row.evidence?.identity === undefined)
|
|
1700
1813
|
return "";
|
|
@@ -1726,11 +1839,16 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1726
1839
|
`${row.adapter}:${row.model}`,
|
|
1727
1840
|
row.tier ?? "unclassified",
|
|
1728
1841
|
ui.efforts[row.adapter]?.[row.model] ? `effort ${ui.efforts[row.adapter][row.model]}` : "",
|
|
1842
|
+
// T5: a grouped row names each member doctor recorded unauthed — up front, before the line clips
|
|
1843
|
+
...(row.evidence?.unauthed === undefined ? (row.members ?? []).filter((member) => member.unauthed !== undefined)
|
|
1844
|
+
.map((member) => `${row.adapter}:${member.model} UNAUTHED — ${member.unauthed} — re-probe with tickmarkr doctor`) : []),
|
|
1729
1845
|
ui.lastEdit?.id === `${row.adapter}:${row.model}` ? ui.lastEdit.text : "",
|
|
1730
1846
|
identityDetail(row),
|
|
1731
1847
|
row.tier ? reachDetail(row) : "",
|
|
1732
1848
|
row.variants?.length ? `variants ${row.variants.join(", ")}` : "",
|
|
1733
|
-
row
|
|
1849
|
+
// T5: a grouped row classifies (and edits) every real member; classifyModel is its reference id
|
|
1850
|
+
row.members?.length ? `classify writes ${row.members.map((member) => member.model).join(", ")}`
|
|
1851
|
+
: row.classifyModel ? `classify writes ${row.classifyModel}` : "",
|
|
1734
1852
|
row.foldedModels?.length ? `${row.foldedModels.length} gateway ids folded: ${row.foldedModels.join(", ")}` : "",
|
|
1735
1853
|
row.evidence?.unauthed !== undefined ? `UNAUTHED — ${row.evidence.unauthed}` : "",
|
|
1736
1854
|
row.evidence?.contextWindow !== undefined ? `${fmtCtx(row.evidence.contextWindow)} ctx` : "",
|
|
@@ -1754,15 +1872,17 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1754
1872
|
const folded = row.foldedModels?.length ?? 1;
|
|
1755
1873
|
const name = clipPathTail(`${row.adapter}/${row.model}${folded > 1 ? ` ×${folded}` : ""}`, nameW);
|
|
1756
1874
|
const prefixLen = Math.min(name.length, row.adapter.length + 1);
|
|
1757
|
-
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), row.
|
|
1758
|
-
? _jsx(Glyph, { kind: "
|
|
1759
|
-
: row.
|
|
1760
|
-
? _jsx(Glyph, { kind: "
|
|
1761
|
-
: row.reach === "
|
|
1762
|
-
? _jsx(Glyph, { kind: "
|
|
1763
|
-
: row.
|
|
1764
|
-
? _jsx(Glyph, { kind: "
|
|
1765
|
-
:
|
|
1875
|
+
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), row.partial
|
|
1876
|
+
? _jsx(Glyph, { kind: "warn" })
|
|
1877
|
+
: row.reach === "out-all" || row.reach === "out-allow"
|
|
1878
|
+
? _jsx(Glyph, { kind: "off" })
|
|
1879
|
+
: row.evidence?.unauthed !== undefined || row.reach === "unknown"
|
|
1880
|
+
? _jsx(Glyph, { kind: "fail" })
|
|
1881
|
+
: row.reach === "out-workers"
|
|
1882
|
+
? _jsx(Glyph, { kind: "warn" })
|
|
1883
|
+
: row.tier
|
|
1884
|
+
? _jsx(Glyph, { kind: "on" })
|
|
1885
|
+
: _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}`));
|
|
1766
1886
|
};
|
|
1767
1887
|
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));
|
|
1768
1888
|
const renderShapeRow = (row, selected, rowW = bodyW - 2) => {
|
|
@@ -1808,8 +1928,8 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1808
1928
|
if (overlay.kind === "classify") {
|
|
1809
1929
|
const subject = overlay.bulk
|
|
1810
1930
|
? `${overlay.adapter} · ${overlay.bulk.rows.length} suggested models`
|
|
1811
|
-
: overlay.displayModel
|
|
1812
|
-
? `${overlay.adapter}:${overlay.displayModel} · writes ${overlay.model}`
|
|
1931
|
+
: overlay.displayModel || classifiedMembers(overlay.adapter, overlay.model).length > 1
|
|
1932
|
+
? `${overlay.adapter}:${overlay.displayModel ?? overlay.model} · writes ${classifiedMembers(overlay.adapter, overlay.displayModel ?? overlay.model).map((member) => member.model).join(", ")}`
|
|
1813
1933
|
: `${overlay.adapter}:${overlay.model}`;
|
|
1814
1934
|
return (_jsxs(OverlayPanel, { title: `classify · ${subject}`, width: bodyW, children: [overlay.stage === "channel" && (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: ["first touch for ", overlay.adapter, " \u2014 how is this CLI billed?"] }), _jsx(Text, { children: " " }), CHANNELS.map((channel, index) => (_jsxs(Text, { children: [_jsx(Pointer, { on: overlay.channelAt === index }), _jsx(Text, { bold: overlay.channelAt === index, children: padCell(channel, 5) }), _jsx(Text, { dimColor: true, children: channel === "sub" ? "flat-rate subscription quota" : "metered API billing" })] }, channel)))] })), overlay.stage === "tier" && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: overlay.suggestion
|
|
1815
1935
|
? `catalog suggests ${overlay.suggestion.tier} — keep it and the provenance note arrives pre-typed`
|
|
@@ -1873,7 +1993,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1873
1993
|
const current = overlay.target.model === undefined
|
|
1874
1994
|
? adapterReach(overlay.target.adapter)
|
|
1875
1995
|
: modelRows().find((row) => row.adapter === overlay.target.adapter && row.model === overlay.target.model) ?? { reach: "unknown", reasons: [] };
|
|
1876
|
-
const partial = "partial" in current && current.partial;
|
|
1996
|
+
const partial = "partial" in current && current.partial === true;
|
|
1877
1997
|
// D-205: a partial rail marks no choice as selected — its channels disagree
|
|
1878
1998
|
const on = partial || current.reach === "unknown" ? null
|
|
1879
1999
|
: current.reach === "out-all" || current.reach === "out-allow" ? "out-all" : current.reach;
|
|
@@ -1942,8 +2062,8 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1942
2062
|
lines.push(coveringDetail(row));
|
|
1943
2063
|
if (!row.tier) {
|
|
1944
2064
|
lines.push(row.suggestion
|
|
1945
|
-
? `catalog suggests ${row.suggestion.tier} — Space/Enter classifies with the note pre-typed · s stages every visible suggestion`
|
|
1946
|
-
: "unclassified — Space/Enter classifies; unclassified models are never routed");
|
|
2065
|
+
? `catalog suggests ${row.suggestion.tier} — Space/Enter classifies with the note pre-typed (t too) · s stages every visible suggestion`
|
|
2066
|
+
: "unclassified — Space/Enter classifies (t too), then Space sets its reach in this session; unclassified models are never routed");
|
|
1947
2067
|
}
|
|
1948
2068
|
}
|
|
1949
2069
|
return lines;
|
|
@@ -2060,7 +2180,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
2060
2180
|
? _jsx(Text, { color: INK.warn, children: ` ! ${clip(ui.notice, width - 4)}` })
|
|
2061
2181
|
: details.map((line) => _jsx(Text, { dimColor: true, children: ` ${clip(line, width - 2)}` }, line)), _jsx(KeyBar, { binds: keyBinds() })] }));
|
|
2062
2182
|
}
|
|
2063
|
-
export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut = [], initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, reloadGuard, stagedRouting, entry = "probe", initialJudge = "", judgeSeats = [], initialInput = [], input, output, debug = false, ...initialDenyProps }) {
|
|
2183
|
+
export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut = [], initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, reloadGuard, stagedRouting, holderOf, entry = "probe", initialJudge = "", judgeSeats = [], initialInput = [], input, output, debug = false, ...initialDenyProps }) {
|
|
2064
2184
|
const agents = adapters.flatMap((adapter) => {
|
|
2065
2185
|
const state = health[adapter.id];
|
|
2066
2186
|
if (!state?.installed)
|
|
@@ -2084,7 +2204,7 @@ export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowO
|
|
|
2084
2204
|
// rebuilt from output.columns/rows so a pane resize re-renders at the new size. Same root
|
|
2085
2205
|
// element type on every rerender ⇒ React keeps the component instance, so staged edits,
|
|
2086
2206
|
// cursor, and overlays all survive the resize.
|
|
2087
|
-
const editorElement = () => (_jsx(FleetApp, { ageMs: ageMs, agents: agents, ...initialDenyProps, initialAllowOut: initialAllowOut, initialEfforts: initialEfforts, modelGroups: declaredModelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewAdvisories: reviewAdvisories, reviewOverlay: reviewOverlay, reloadGuard: reloadGuard, stagedRouting: stagedRouting, entry: entry, initialJudge: initialJudge, judgeSeats: judgeSeats,
|
|
2207
|
+
const editorElement = () => (_jsx(FleetApp, { ageMs: ageMs, agents: agents, ...initialDenyProps, initialAllowOut: initialAllowOut, initialEfforts: initialEfforts, modelGroups: declaredModelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewAdvisories: reviewAdvisories, reviewOverlay: reviewOverlay, reloadGuard: reloadGuard, stagedRouting: stagedRouting, holderOf: holderOf, entry: entry, initialJudge: initialJudge, judgeSeats: judgeSeats,
|
|
2088
2208
|
// v1.90.9: cap every list to the terminal minus chrome so the cursor and header can
|
|
2089
2209
|
// never scroll off-screen (omp's 218-model list did exactly that pre-cap).
|
|
2090
2210
|
// OBS-523: chrome is ≤12 rows worst case (header, borders, padding, scope+search,
|