tickmarkr 2.6.6 → 2.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude-code.js +8 -2
- package/dist/adapters/codex-commit-check.d.ts +3 -2
- package/dist/adapters/codex-commit-check.js +37 -10
- package/dist/adapters/codex.js +10 -4
- package/dist/cli/commands/beat.d.ts +7 -2
- package/dist/cli/commands/beat.js +144 -77
- package/dist/cli/commands/fleet.js +66 -21
- package/dist/cli/commands/init.js +18 -7
- package/dist/cli/commands/plan.js +16 -18
- package/dist/cli/commands/verify.js +4 -2
- package/dist/cli/help.d.ts +1 -0
- package/dist/cli/help.js +14 -6
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/drivers/herdr.js +2 -2
- package/dist/drivers/orca.d.ts +12 -4
- package/dist/drivers/orca.js +200 -39
- 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/evidence.js +3 -3
- package/dist/gates/review.d.ts +1 -1
- package/dist/gates/review.js +77 -13
- package/dist/gates/run-gates.d.ts +21 -6
- package/dist/gates/run-gates.js +439 -224
- package/dist/gates/test-manifest.d.ts +42 -0
- package/dist/gates/test-manifest.js +203 -118
- package/dist/run/beat-lifecycle.d.ts +239 -0
- package/dist/run/beat-lifecycle.js +1782 -0
- package/dist/run/daemon.d.ts +4 -0
- package/dist/run/daemon.js +129 -40
- package/dist/run/git-trust.d.ts +102 -0
- package/dist/run/git-trust.js +334 -0
- package/dist/run/git.d.ts +24 -3
- package/dist/run/git.js +169 -30
- 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 +4 -4
- 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 +53 -0
- package/dist/run/verification-job.js +159 -0
- package/dist/tui/ink/fleet-app.d.ts +16 -2
- package/dist/tui/ink/fleet-app.js +265 -141
- package/package.json +1 -1
- package/skills/tickmarkr-auto/SKILL.md +80 -27
- package/skills/tickmarkr-loop/SKILL.md +87 -27
- package/skills/tickmarkr-overseer/SKILL.md +125 -47
- package/skills/tickmarkr-overseer/scripts/watch-journal.sh +9 -7
|
@@ -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, holderOf, 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, layerStamp, 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…),
|
|
@@ -137,21 +137,51 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
137
137
|
// the STAGED policy (fleet loads the candidate bytes; see stagedRouting) and the recorded
|
|
138
138
|
// identity — never literal membership of the staged sets, which missed identity, bare-model and
|
|
139
139
|
// family entries and could not tell an allowlist exclusion from a deny.
|
|
140
|
-
|
|
140
|
+
// B-KEYS (D-1338): the staged CONTENT every row and the staged policy derive from — the deny sets,
|
|
141
|
+
// the allow complement, classifications, efforts and seeds, read raw off ui (the provenance a
|
|
142
|
+
// review folds is a pure function of them). Computed per call, never per render: a handler
|
|
143
|
+
// mutates ui and reads rows in the same tick, before bump.
|
|
144
|
+
const stagedKey = () => JSON.stringify([
|
|
145
|
+
DENY_SCOPES.map((scope) => [...ui[scope.key]].sort()),
|
|
146
|
+
[...ui.allowOut].sort(),
|
|
147
|
+
ui.classifications,
|
|
148
|
+
ui.efforts,
|
|
149
|
+
ui.seededAllowOut,
|
|
150
|
+
]);
|
|
151
|
+
// ponytail: both memos live in refs keyed on stagedKey — a render-local memo reset on every keypress,
|
|
152
|
+
// so each cursor move reloaded the policy and re-derived ~800 rows' reach (B-KEYS). A cursor move,
|
|
153
|
+
// a filter, showAll or an overlay never recompute a row; any staged edit changes the key and
|
|
154
|
+
// recomputes every group. Ceiling: one JSON.stringify of the staged sets per lookup (µs); a dirty
|
|
155
|
+
// counter bumped at every mutation site if a fleet ever outgrows it.
|
|
156
|
+
const routingMemo = useRef(null);
|
|
141
157
|
const stagedPolicy = () => {
|
|
158
|
+
const key = memoKey();
|
|
159
|
+
const memo = routingMemo.current;
|
|
160
|
+
if (memo?.key === key)
|
|
161
|
+
return memo.policy;
|
|
142
162
|
const deny = stagedDeny();
|
|
143
163
|
const stage = stagedMetadata();
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
? stagedRouting(deny, stage)
|
|
150
|
-
: { ok: true, routing: { deny: denyBlockFrom(denyLists()) } },
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
return routingMemo.policy;
|
|
164
|
+
const policy = stagedRouting
|
|
165
|
+
? stagedRouting(deny, stage)
|
|
166
|
+
: { ok: true, routing: { deny: denyBlockFrom(denyLists()) } };
|
|
167
|
+
routingMemo.current = { key, policy };
|
|
168
|
+
return policy;
|
|
154
169
|
};
|
|
170
|
+
const rowsMemo = useRef({ key: "", byAdapter: new Map() });
|
|
171
|
+
// B-KEYS r2 (D-1446): staged content alone does not identify a row — the rows also derive from the loaded
|
|
172
|
+
// overlay layers (stagedRouting and holderOf read both files) and the props they read. Once per render a
|
|
173
|
+
// generation moves when the layer bytes or one of those props changes, and both memos key on it beside the
|
|
174
|
+
// staged content: a layer written mid-session shows on the next frame, as on the base, while a cursor move
|
|
175
|
+
// over unchanged layers still derives nothing. Cost: one read of each overlay per frame (the B2 previewCfg key).
|
|
176
|
+
const layers = useRef({ stamp: "", inputs: [], gen: 0 });
|
|
177
|
+
{
|
|
178
|
+
const stamp = layerStamp?.() ?? "";
|
|
179
|
+
const inputs = [modelGroups, stagedRouting, holderOf];
|
|
180
|
+
if (stamp !== layers.current.stamp || inputs.some((input, at) => input !== layers.current.inputs[at])) {
|
|
181
|
+
layers.current = { stamp, inputs, gen: layers.current.gen + 1 };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const memoKey = () => `${layers.current.gen}\u0000${stagedKey()}`;
|
|
155
185
|
// OBS-1099 add.1: the browser's deny discovery ranges over the schema-enumerated scopes (the
|
|
156
186
|
// route collector names its four paths by hand, so a scope the schema gains would never render
|
|
157
187
|
// or lift here). A flat scope reaches every seat; a nested one only the seat its block names
|
|
@@ -183,20 +213,31 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
183
213
|
}),
|
|
184
214
|
};
|
|
185
215
|
};
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
216
|
+
// T5: the real channels a displayed row stands for — every collapsed variant and catalog-fold
|
|
217
|
+
// constituent fleet derived from the cached doctor list, each with its own identity; a row without
|
|
218
|
+
// them stands for its folded ids, else its reference real id (classifyModel) or its own. Display labels
|
|
219
|
+
// are never channels.
|
|
220
|
+
const membersOf = (adapter, row) => {
|
|
221
|
+
const listed = row.members?.length ? row.members
|
|
222
|
+
: (row.foldedModels?.length ? row.foldedModels : [row.classifyModel ?? row.model]).map((model) => ({ model, identity: row.evidence?.identity }));
|
|
223
|
+
// one channel per real id: a repeated id is one member, so a grouped act takes ONE reason from it
|
|
224
|
+
return listed.filter((member, at) => listed.findIndex((other) => other.model === member.model) === at)
|
|
225
|
+
.map((member) => ({ adapter, model: member.model, ...(member.identity !== undefined ? { identity: member.identity } : {}) }));
|
|
226
|
+
};
|
|
227
|
+
// OBS-1099 review: a displayed row may fold several discovered gateway ids; each real member is a
|
|
228
|
+
// channel the policy matches on its own, so every one but a channel itself counts as a sibling —
|
|
229
|
+
// an entry covering another member is shared, never channel-owned (grouping never privatizes it)
|
|
230
|
+
const displayedChannels = () => modelGroups.flatMap((group) => group.rows.flatMap((other) => membersOf(group.adapter, other)));
|
|
192
231
|
const siblingsOf = (self) => displayedChannels()
|
|
193
232
|
.filter((other) => other.adapter !== self.adapter || other.model !== self.model);
|
|
194
233
|
// Judge c3 (R107): exactly ONE selected entry per act — the channel's adapter:model key when it
|
|
195
|
-
// is a candidate, else the first candidate in sorted order; only an entry no sibling matches
|
|
196
|
-
|
|
234
|
+
// is a candidate, else the first candidate in sorted order; only an entry no sibling matches.
|
|
235
|
+
// T5: matched as production matches the list — a deny entry with family matching, an allow-complement
|
|
236
|
+
// entry exactly as routing.allow admits (`fake:C-low` never reaches a bare `fake:C` there)
|
|
237
|
+
const selectOwn = (entries, self, family = true) => {
|
|
197
238
|
const others = siblingsOf(self);
|
|
198
|
-
const candidates = [...entries].filter((entry) => entryMatchesChannel(entry, self,
|
|
199
|
-
&& !others.some((other) => entryMatchesChannel(entry, other,
|
|
239
|
+
const candidates = [...entries].filter((entry) => entryMatchesChannel(entry, self, family)
|
|
240
|
+
&& !others.some((other) => entryMatchesChannel(entry, other, family)));
|
|
200
241
|
const id = `${self.adapter}:${self.model}`;
|
|
201
242
|
return candidates.includes(id) ? id : candidates.sort()[0];
|
|
202
243
|
};
|
|
@@ -286,11 +327,48 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
286
327
|
const shared = isAdapterWide(scope.entry, self) || covers.length > 1;
|
|
287
328
|
return { entry: scope.entry, scope: scope.configPath, covers, shared, edits: reachEdits(owned) };
|
|
288
329
|
};
|
|
330
|
+
// T5: a grouped row's reach over EVERY real member — the whole-group state only when every member
|
|
331
|
+
// holds it; otherwise the most-excluded member's reach, marked partial, each member-specific reason
|
|
332
|
+
// prefixed with its real id. A one-member row is exactly its channel's reach.
|
|
333
|
+
const rowReach = (members) => {
|
|
334
|
+
const each = members.map((member) => ({ model: member.model, ...reachFor(member.adapter, member.model, member.identity) }));
|
|
335
|
+
if (each.length === 1) {
|
|
336
|
+
const { model: _model, ...only } = each[0];
|
|
337
|
+
return { ...only, partial: false };
|
|
338
|
+
}
|
|
339
|
+
const folded = foldReach(each);
|
|
340
|
+
const uniform = each.every((channel) => channel.reach === each[0].reach);
|
|
341
|
+
return {
|
|
342
|
+
reach: uniform ? each[0].reach : folded.reach,
|
|
343
|
+
reasons: folded.reasons,
|
|
344
|
+
partial: folded.partial,
|
|
345
|
+
uncoveredAlias: each.some((channel) => channel.uncoveredAlias),
|
|
346
|
+
held: [...new Set(each.flatMap((channel) => channel.held))],
|
|
347
|
+
};
|
|
348
|
+
};
|
|
349
|
+
// T5: the covering entry a grouped row names — a shared one first, so l owns it
|
|
350
|
+
const rowCovering = (members) => {
|
|
351
|
+
const found = members.map(coveringFor).filter((covering) => covering !== undefined);
|
|
352
|
+
return found.find((covering) => covering.shared) ?? found[0];
|
|
353
|
+
};
|
|
354
|
+
// B-KEYS: one derivation per group per staged state — the memo above hands every later pass
|
|
355
|
+
// (visibleCount per rail row, modelRows, the hidden and folded counts, a handler's lookup) the
|
|
356
|
+
// same rows; the returned array is read, never mutated, by every caller.
|
|
289
357
|
const groupRows = (group) => {
|
|
358
|
+
const key = memoKey();
|
|
359
|
+
if (rowsMemo.current.key !== key)
|
|
360
|
+
rowsMemo.current = { key, byAdapter: new Map() };
|
|
361
|
+
const memo = rowsMemo.current.byAdapter;
|
|
362
|
+
const rows = memo.get(group.adapter) ?? deriveGroupRows(group);
|
|
363
|
+
memo.set(group.adapter, rows);
|
|
364
|
+
return rows;
|
|
365
|
+
};
|
|
366
|
+
const deriveGroupRows = (group) => {
|
|
290
367
|
const rows = group.rows.map((row) => {
|
|
368
|
+
const members = membersOf(group.adapter, row);
|
|
291
369
|
const staged = ui.classifications.find((classification) => classification.adapter === group.adapter
|
|
292
|
-
&& (classification.model === row.model ||
|
|
293
|
-
const reach =
|
|
370
|
+
&& (classification.model === row.model || members.some((member) => member.model === classification.model)));
|
|
371
|
+
const reach = rowReach(members);
|
|
294
372
|
return {
|
|
295
373
|
adapter: group.adapter,
|
|
296
374
|
model: row.model,
|
|
@@ -302,16 +380,17 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
302
380
|
classifyModel: row.classifyModel,
|
|
303
381
|
variants: row.variants,
|
|
304
382
|
foldedModels: row.foldedModels,
|
|
383
|
+
...(row.members ? { members: row.members } : {}),
|
|
305
384
|
score: row.score,
|
|
306
385
|
denied: reach.reach !== "in",
|
|
307
386
|
...reach,
|
|
308
387
|
// OBS-1099 item 2 (review round 2): every row is covered the same way — an alias through
|
|
309
388
|
// its identity, a plain row by a bare-model or adapter entry — so a shared bare-model
|
|
310
389
|
// entry across adapters has the one owning control too
|
|
311
|
-
covering:
|
|
390
|
+
covering: rowCovering(members),
|
|
312
391
|
};
|
|
313
392
|
});
|
|
314
|
-
const known = new Set(rows.flatMap((row) =>
|
|
393
|
+
const known = new Set(group.rows.flatMap((row) => [row.model, ...membersOf(group.adapter, row).map((member) => member.model)]));
|
|
315
394
|
for (const staged of ui.classifications) {
|
|
316
395
|
if (staged.adapter === group.adapter && !known.has(staged.model)) {
|
|
317
396
|
const reach = reachFor(group.adapter, staged.model);
|
|
@@ -448,27 +527,29 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
448
527
|
// its classification — the ownAllow spelling Space clears — so it can be admitted in this session.
|
|
449
528
|
// Re-staging a classification replaces its seed; a row the allow form admits seeds nothing. A row a
|
|
450
529
|
// flat deny ALSO excludes still seeds, so successive owning lifts (deny, then allow) admit it.
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
// full grouped-row membership (collapse + catalog fold) is 2.6.7 (T10 owed review F1, D-1016)
|
|
455
|
-
if (model !== displayModel)
|
|
456
|
-
return;
|
|
457
|
-
const identity = groupOf(adapter)?.rows.find((row) => row.model === displayModel)?.evidence?.identity;
|
|
458
|
-
const self = { adapter, model: displayModel, ...(identity !== undefined ? { identity } : {}) };
|
|
530
|
+
// T5: one seed per REAL member of a grouped row (collapsed variants, catalog-fold constituents), keyed and
|
|
531
|
+
// spelled by its real id with its own identity — the display label never names a channel.
|
|
532
|
+
const seedAllow = (self, displayModel) => {
|
|
459
533
|
const policy = stagedPolicy();
|
|
460
534
|
if (!policy.ok || !exclusionsOf(self, policy.routing, "judge").some((scope) => scope.by === "allow"))
|
|
461
535
|
return;
|
|
462
|
-
const entry = selectOwn(ui.allowOut, self) ?? `${adapter}:${
|
|
536
|
+
const entry = selectOwn(ui.allowOut, self, false) ?? `${self.adapter}:${self.model}`;
|
|
463
537
|
ui.allowOut = new Set([...ui.allowOut, entry]);
|
|
464
538
|
ui.seededAllowOut = [
|
|
465
|
-
...ui.seededAllowOut.filter((seed) => seed.adapter !== adapter || seed.model !== model),
|
|
466
|
-
{ adapter, model, displayModel, ...(identity !== undefined ? { identity } : {}), entry },
|
|
539
|
+
...ui.seededAllowOut.filter((seed) => seed.adapter !== self.adapter || seed.model !== self.model),
|
|
540
|
+
{ adapter: self.adapter, model: self.model, displayModel, ...(self.identity !== undefined ? { identity: self.identity } : {}), entry },
|
|
467
541
|
];
|
|
468
542
|
};
|
|
543
|
+
// T5: the real members a classification of this browser row stages — a row the browser never listed
|
|
544
|
+
// (n adds one) is its own one member
|
|
545
|
+
const classifiedMembers = (adapter, displayModel) => {
|
|
546
|
+
const row = groupOf(adapter)?.rows.find((candidate) => candidate.model === displayModel);
|
|
547
|
+
return row ? membersOf(adapter, row) : [{ adapter, model: displayModel }];
|
|
548
|
+
};
|
|
469
549
|
const stageSuggested = (adapter, rows, firstTouch) => {
|
|
470
|
-
for (const row of rows)
|
|
471
|
-
seedAllow(adapter, row.model, row.displayModel);
|
|
550
|
+
for (const row of rows) {
|
|
551
|
+
seedAllow({ adapter, model: row.model, ...(row.identity !== undefined ? { identity: row.identity } : {}) }, row.displayModel);
|
|
552
|
+
}
|
|
472
553
|
ui.classifications = [
|
|
473
554
|
...ui.classifications,
|
|
474
555
|
...rows.map((row) => ({
|
|
@@ -521,18 +602,23 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
521
602
|
}
|
|
522
603
|
setOverlay({ ...base, stage: "channel", vendor: group.vendor });
|
|
523
604
|
};
|
|
605
|
+
// T5: the picked tier and provenance land on EVERY real member of the row, each with its own seed —
|
|
606
|
+
// classifyModel stays the reference id the suggestion and the overlay title name
|
|
524
607
|
const applyClassification = (overlay) => {
|
|
525
608
|
const answered = overlay.vendor ? ui.channelByAdapter[overlay.adapter] : undefined;
|
|
526
|
-
|
|
609
|
+
const displayModel = overlay.displayModel ?? overlay.model;
|
|
610
|
+
const members = classifiedMembers(overlay.adapter, displayModel);
|
|
611
|
+
for (const member of members)
|
|
612
|
+
seedAllow(member, displayModel);
|
|
527
613
|
ui.classifications = [
|
|
528
614
|
...ui.classifications,
|
|
529
|
-
{
|
|
615
|
+
...members.map((member) => ({
|
|
530
616
|
adapter: overlay.adapter,
|
|
531
|
-
model:
|
|
617
|
+
model: member.model,
|
|
532
618
|
tier: TIERS[overlay.tierAt],
|
|
533
619
|
note: overlay.note.trim(),
|
|
534
620
|
...(overlay.vendor && answered ? { vendor: overlay.vendor, channel: answered } : {}),
|
|
535
|
-
},
|
|
621
|
+
})),
|
|
536
622
|
];
|
|
537
623
|
setOverlay(null);
|
|
538
624
|
};
|
|
@@ -560,8 +646,9 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
560
646
|
// channel-specific one carries its channel, so a partial exclusion is never intersected away.
|
|
561
647
|
const adapterReach = (id) => {
|
|
562
648
|
const group = modelGroups.find((candidate) => candidate.adapter === id);
|
|
563
|
-
|
|
564
|
-
|
|
649
|
+
// T5: every real member of every row — a collapsed or folded row's display label is no channel
|
|
650
|
+
const channels = (group?.rows ?? []).flatMap((row) => membersOf(id, row))
|
|
651
|
+
.map((channel) => ({ model: channel.model, ...reachFor(id, channel.model, channel.identity) }));
|
|
565
652
|
if (channels.length === 0) {
|
|
566
653
|
// ponytail: a rail entry with no probed channels has only its own literal entries to show
|
|
567
654
|
const reasons = [
|
|
@@ -574,6 +661,11 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
574
661
|
: NESTED_ADAPTER_SCOPES.some((scope) => ui[scope.key].has(id)) ? "out-workers" : "in";
|
|
575
662
|
return { reach, reasons, partial: false };
|
|
576
663
|
}
|
|
664
|
+
return foldReach(channels);
|
|
665
|
+
};
|
|
666
|
+
// the rail's (and a grouped row's) fold over its real channels — most-excluded reach, partial when they
|
|
667
|
+
// disagree, shared reasons once and channel-specific ones carrying their channel
|
|
668
|
+
function foldReach(channels) {
|
|
577
669
|
const unknown = channels.find((channel) => channel.reach === "unknown");
|
|
578
670
|
if (unknown)
|
|
579
671
|
return { reach: "unknown", reasons: unknown.reasons, partial: false };
|
|
@@ -588,7 +680,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
588
680
|
.filter((reason) => !shared.includes(reason))
|
|
589
681
|
.map((reason) => `${channel.model}: ${reason}`));
|
|
590
682
|
return { reach, reasons: [...shared, ...own], partial };
|
|
591
|
-
}
|
|
683
|
+
}
|
|
592
684
|
const describeReach = (state) => state.partial ? `${state.reach} (partial: not every channel)` : state.reach;
|
|
593
685
|
// OBS-1099 items 1 and 4: one picker choice changes exactly ONE selected entry in ONE scope —
|
|
594
686
|
// added, cleared, or re-scoped between the workers list and the flat list — then the row's
|
|
@@ -678,90 +770,106 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
678
770
|
if (!row)
|
|
679
771
|
return;
|
|
680
772
|
const id = `${row.adapter}:${row.model}`;
|
|
681
|
-
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
//
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
const
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
return;
|
|
773
|
+
// T5: a grouped row's act is the same one-reason act on EVERY real member, each decided from the reach
|
|
774
|
+
// it held BEFORE this act — a sibling's fresh entry that also matches a member's recorded identity never
|
|
775
|
+
// turns that member's own addition into "already" (review D-1081) — while every removal still takes only
|
|
776
|
+
// an entry the member owns alone over the staged sets; a member a shared entry keeps out is named and left
|
|
777
|
+
const members = membersOf(row.adapter, row);
|
|
778
|
+
const before = members.map((self) => (members.length === 1
|
|
779
|
+
? { reach: row.reach, reasons: row.reasons, covering: row.covering }
|
|
780
|
+
: { ...reachFor(self.adapter, self.model, self.identity), covering: coveringFor(self) }));
|
|
781
|
+
const outcomes = members.map((self, at) => {
|
|
782
|
+
const memberId = `${self.adapter}:${self.model}`;
|
|
783
|
+
const { reasons, covering } = before[at];
|
|
784
|
+
// LEG2-T3 round 2 finding 1 / D-233 / D-235 (review round 10): ONE ordered selector —
|
|
785
|
+
// ownedDenies — feeds the detail line AND every Space edit, so the entry named is the entry
|
|
786
|
+
// edited: a clear takes its head (own key first, then collector order); a demotion takes its
|
|
787
|
+
// first flat entry, a promotion its first workers entry; a bare adapter id or a sibling's
|
|
788
|
+
// entry is never in it. OBS-1046: an allow exclusion is the LAST reason taken, never together
|
|
789
|
+
// with an authored deny.
|
|
790
|
+
const edits = reachEdits(ownedDenies(self));
|
|
791
|
+
// review D-1081 add.: the seed this session generated for this member is its own even when a peer
|
|
792
|
+
// member's recorded identity also reaches it (C-high recorded as C-low) — never authored, and lifting
|
|
793
|
+
// it admits only this act's members; a seed any channel outside the row matches stays shared
|
|
794
|
+
const ownSeed = () => ui.seededAllowOut.find((seed) => seed.adapter === self.adapter && seed.model === self.model
|
|
795
|
+
&& ui.allowOut.has(seed.entry)
|
|
796
|
+
&& !siblingsOf(self).some((other) => !members.some((peer) => peer.adapter === other.adapter && peer.model === other.model)
|
|
797
|
+
&& entryMatchesChannel(seed.entry, other, false)))?.entry;
|
|
798
|
+
const ownAllow = () => {
|
|
799
|
+
const fromAllow = selectOwn(ui.allowOut, self, false) ?? ownSeed();
|
|
800
|
+
return fromAllow === undefined ? undefined : { entry: fromAllow, scope: "routing.allow" };
|
|
801
|
+
};
|
|
802
|
+
const ownFlat = () => edits.demote ?? ownAllow();
|
|
803
|
+
const ownWorkers = () => edits.promote;
|
|
804
|
+
const takeFlat = (found) => {
|
|
805
|
+
const scope = scopeOf(found.scope);
|
|
806
|
+
if (scope)
|
|
807
|
+
ui[scope.key] = without(ui[scope.key], found.entry);
|
|
808
|
+
else
|
|
809
|
+
ui.allowOut = without(ui.allowOut, found.entry);
|
|
810
|
+
};
|
|
811
|
+
const refuse = (reachLabel) => {
|
|
812
|
+
// OBS-1099 item 2: a covered row names the covering entry, every identity it covers, and
|
|
813
|
+
// the ONE control that lifts it — the T1 refusal wording stays in front of it
|
|
814
|
+
if (covering?.shared) {
|
|
815
|
+
// the notice clips at the browser width; the detail line and the l overlay list every identity
|
|
816
|
+
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` };
|
|
817
|
+
}
|
|
818
|
+
const shared = ui.denyAdapters.has(row.adapter) || ui.allowOut.has(row.adapter) || ui.denyWorkersAdapters.has(row.adapter)
|
|
819
|
+
? `every ${row.adapter} channel is out together — set the adapter's reach on the rail`
|
|
820
|
+
: "a shared entry covers other channels too";
|
|
821
|
+
return { refused: `${memberId} stays ${reachLabel} — ${reasons.join("; ")} — Space edits only this channel's own entries; ${shared}` };
|
|
822
|
+
};
|
|
823
|
+
const reach = before[at].reach;
|
|
824
|
+
const outAll = reach === "out-all" || reach === "out-allow";
|
|
825
|
+
if (choice === "out-workers" && reach === "in") {
|
|
826
|
+
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, memberId);
|
|
827
|
+
return { edit: `added ${memberId} to routing.deny.workers.models` };
|
|
709
828
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
};
|
|
715
|
-
const reach = row.reach;
|
|
716
|
-
const outAll = reach === "out-all" || reach === "out-allow";
|
|
717
|
-
let edit = "";
|
|
718
|
-
if (choice === "out-workers" && reach === "in") {
|
|
719
|
-
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, id);
|
|
720
|
-
edit = `added ${id} to routing.deny.workers.models`;
|
|
721
|
-
}
|
|
722
|
-
else if (choice === "out-workers" && outAll) {
|
|
723
|
-
const found = ownFlat();
|
|
724
|
-
if (found === undefined)
|
|
725
|
-
return refuse("out");
|
|
726
|
-
takeFlat(found);
|
|
727
|
-
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, found.entry);
|
|
728
|
-
edit = `moved ${found.entry} to routing.deny.workers.models`;
|
|
729
|
-
}
|
|
730
|
-
else if (choice === "out-all" && reach === "in") {
|
|
731
|
-
ui.denyModels = withEntry(ui.denyModels, id);
|
|
732
|
-
edit = `added ${id} to routing.deny.models`;
|
|
733
|
-
}
|
|
734
|
-
else if (choice === "out-all" && reach === "out-workers") {
|
|
735
|
-
// OBS-1099 review: a promotion, like a clear, takes only an entry no sibling row matches —
|
|
736
|
-
// a shared workers entry (adapter-wide or a bare id another row serves) is refused by name,
|
|
737
|
-
// never moved for its siblings and never doubled by a fresh flat deny
|
|
738
|
-
const found = ownWorkers();
|
|
739
|
-
if (found === undefined)
|
|
740
|
-
return refuse("out · workers");
|
|
741
|
-
takeFlat(found);
|
|
742
|
-
ui.denyModels = withEntry(ui.denyModels, found.entry);
|
|
743
|
-
edit = `moved ${found.entry} to routing.deny.models`;
|
|
744
|
-
}
|
|
745
|
-
else if (choice === "in" && reach !== "in") {
|
|
746
|
-
// D-205: in clears ONE row-owned reason from either the flat scopes or the workers scope —
|
|
747
|
-
// a shared flat reason (routing.deny.adapters covering every channel) never blocks clearing
|
|
748
|
-
// this row's own workers entry; the recomputed reach then names the shared reason that remains
|
|
749
|
-
const found = edits.clear ?? (outAll ? ownAllow() : undefined);
|
|
750
|
-
if (found !== undefined) {
|
|
829
|
+
else if (choice === "out-workers" && outAll) {
|
|
830
|
+
const found = ownFlat();
|
|
831
|
+
if (found === undefined)
|
|
832
|
+
return refuse("out");
|
|
751
833
|
takeFlat(found);
|
|
752
|
-
|
|
834
|
+
ui.denyWorkersModels = withEntry(ui.denyWorkersModels, found.entry);
|
|
835
|
+
return { edit: `moved ${found.entry} to routing.deny.workers.models` };
|
|
753
836
|
}
|
|
754
|
-
else {
|
|
755
|
-
|
|
837
|
+
else if (choice === "out-all" && reach === "in") {
|
|
838
|
+
ui.denyModels = withEntry(ui.denyModels, memberId);
|
|
839
|
+
return { edit: `added ${memberId} to routing.deny.models` };
|
|
756
840
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
841
|
+
else if (choice === "out-all" && reach === "out-workers") {
|
|
842
|
+
// OBS-1099 review: a promotion, like a clear, takes only an entry no sibling row matches —
|
|
843
|
+
// a shared workers entry (adapter-wide or a bare id another row serves) is refused by name,
|
|
844
|
+
// never moved for its siblings and never doubled by a fresh flat deny
|
|
845
|
+
const found = ownWorkers();
|
|
846
|
+
if (found === undefined)
|
|
847
|
+
return refuse("out · workers");
|
|
848
|
+
takeFlat(found);
|
|
849
|
+
ui.denyModels = withEntry(ui.denyModels, found.entry);
|
|
850
|
+
return { edit: `moved ${found.entry} to routing.deny.models` };
|
|
851
|
+
}
|
|
852
|
+
else if (choice === "in" && reach !== "in") {
|
|
853
|
+
// D-205: in clears ONE row-owned reason from either the flat scopes or the workers scope —
|
|
854
|
+
// a shared flat reason (routing.deny.adapters covering every channel) never blocks clearing
|
|
855
|
+
// this row's own workers entry; the recomputed reach then names the shared reason that remains
|
|
856
|
+
const found = edits.clear ?? (outAll ? ownAllow() : undefined);
|
|
857
|
+
if (found === undefined)
|
|
858
|
+
return refuse(outAll ? "out" : "out · workers");
|
|
859
|
+
takeFlat(found);
|
|
860
|
+
return { edit: `cleared ${found.entry} from ${found.scope}` };
|
|
861
|
+
}
|
|
862
|
+
return { already: true };
|
|
863
|
+
});
|
|
864
|
+
const made = outcomes.flatMap((outcome) => ("edit" in outcome ? [outcome.edit] : []));
|
|
865
|
+
const refused = outcomes.flatMap((outcome) => ("refused" in outcome ? [outcome.refused] : []));
|
|
866
|
+
if (!made.length) {
|
|
867
|
+
ui.notice = refused[0] ?? `${id} is already ${label}`;
|
|
760
868
|
return;
|
|
761
869
|
}
|
|
762
|
-
const after =
|
|
763
|
-
const still = reached(after.reach, choice) ? "" : ` — still out: ${after.reasons.join("; ")}`;
|
|
764
|
-
ui.lastEdit = { id, text: `space: ${
|
|
870
|
+
const after = rowReach(members);
|
|
871
|
+
const still = reached(after.reach, choice) && !after.partial ? "" : ` — still out: ${after.reasons.join("; ")}`;
|
|
872
|
+
ui.lastEdit = { id, text: `space: ${made.join("; ")}${still}` };
|
|
765
873
|
};
|
|
766
874
|
// OBS-1065: Enter on a greyed picker row lifts exactly its row-owned covering entry, then the
|
|
767
875
|
// same picker reopens over the recomputed policy — a shared or adapter-wide entry is refused by
|
|
@@ -1588,7 +1696,13 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1588
1696
|
const group = modelGroups[ui.adapterAt];
|
|
1589
1697
|
const suggested = rows
|
|
1590
1698
|
.filter((candidate) => candidate.adapter === group.adapter && !candidate.tier && candidate.suggestion !== undefined)
|
|
1591
|
-
|
|
1699
|
+
// T5: every real member of each suggested row stages, never only its reference id
|
|
1700
|
+
.flatMap((candidate) => membersOf(candidate.adapter, candidate).map((member) => ({
|
|
1701
|
+
model: member.model,
|
|
1702
|
+
displayModel: candidate.model,
|
|
1703
|
+
...(member.identity !== undefined ? { identity: member.identity } : {}),
|
|
1704
|
+
suggestion: candidate.suggestion,
|
|
1705
|
+
})));
|
|
1592
1706
|
if (!suggested.length) {
|
|
1593
1707
|
ui.notice = "no catalog tier suggestions among the visible unclassified models — evidence comes from the cached catalogs (AA index / API pricing)";
|
|
1594
1708
|
bump();
|
|
@@ -1722,6 +1836,9 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1722
1836
|
return "unauthed — re-probe with tickmarkr doctor (Space does not toggle reach)";
|
|
1723
1837
|
// T10: the reasons, then where each scope is held — the overlay a reviewed w writes
|
|
1724
1838
|
const reasons = `${row.reasons.join("; ")}${row.held.length ? ` — held: ${row.held.join("; ")}` : ""}`;
|
|
1839
|
+
// T5: members disagree — never a whole-group state; each member-specific reason names its real id
|
|
1840
|
+
if (row.partial)
|
|
1841
|
+
return `reach: partial — ${row.reach} for some members only — ${reasons} — Space applies one choice to every member`;
|
|
1725
1842
|
if (row.reach === "out-all")
|
|
1726
1843
|
return `reach: out · all seats — ${reasons} — Space picks in or out · workers`;
|
|
1727
1844
|
if (row.reach === "out-allow")
|
|
@@ -1732,7 +1849,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1732
1849
|
return `reach: unknown — ${reasons}`;
|
|
1733
1850
|
return "reach: in — Space picks out · workers or out · all seats";
|
|
1734
1851
|
};
|
|
1735
|
-
const reachCell = (row) => row.reach === "in" || row.reach === "unknown" ? row.reach : `out ${row.reach === "out-workers" ? "workers" : row.reach.slice(4)}`;
|
|
1852
|
+
const reachCell = (row) => row.partial ? "partial" : row.reach === "in" || row.reach === "unknown" ? row.reach : `out ${row.reach === "out-workers" ? "workers" : row.reach.slice(4)}`;
|
|
1736
1853
|
const identityDetail = (row) => {
|
|
1737
1854
|
if (row.evidence?.identity === undefined)
|
|
1738
1855
|
return "";
|
|
@@ -1764,11 +1881,16 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1764
1881
|
`${row.adapter}:${row.model}`,
|
|
1765
1882
|
row.tier ?? "unclassified",
|
|
1766
1883
|
ui.efforts[row.adapter]?.[row.model] ? `effort ${ui.efforts[row.adapter][row.model]}` : "",
|
|
1884
|
+
// T5: a grouped row names each member doctor recorded unauthed — up front, before the line clips
|
|
1885
|
+
...(row.evidence?.unauthed === undefined ? (row.members ?? []).filter((member) => member.unauthed !== undefined)
|
|
1886
|
+
.map((member) => `${row.adapter}:${member.model} UNAUTHED — ${member.unauthed} — re-probe with tickmarkr doctor`) : []),
|
|
1767
1887
|
ui.lastEdit?.id === `${row.adapter}:${row.model}` ? ui.lastEdit.text : "",
|
|
1768
1888
|
identityDetail(row),
|
|
1769
1889
|
row.tier ? reachDetail(row) : "",
|
|
1770
1890
|
row.variants?.length ? `variants ${row.variants.join(", ")}` : "",
|
|
1771
|
-
row
|
|
1891
|
+
// T5: a grouped row classifies (and edits) every real member; classifyModel is its reference id
|
|
1892
|
+
row.members?.length ? `classify writes ${row.members.map((member) => member.model).join(", ")}`
|
|
1893
|
+
: row.classifyModel ? `classify writes ${row.classifyModel}` : "",
|
|
1772
1894
|
row.foldedModels?.length ? `${row.foldedModels.length} gateway ids folded: ${row.foldedModels.join(", ")}` : "",
|
|
1773
1895
|
row.evidence?.unauthed !== undefined ? `UNAUTHED — ${row.evidence.unauthed}` : "",
|
|
1774
1896
|
row.evidence?.contextWindow !== undefined ? `${fmtCtx(row.evidence.contextWindow)} ctx` : "",
|
|
@@ -1792,15 +1914,17 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1792
1914
|
const folded = row.foldedModels?.length ?? 1;
|
|
1793
1915
|
const name = clipPathTail(`${row.adapter}/${row.model}${folded > 1 ? ` ×${folded}` : ""}`, nameW);
|
|
1794
1916
|
const prefixLen = Math.min(name.length, row.adapter.length + 1);
|
|
1795
|
-
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), row.
|
|
1796
|
-
? _jsx(Glyph, { kind: "
|
|
1797
|
-
: row.
|
|
1798
|
-
? _jsx(Glyph, { kind: "
|
|
1799
|
-
: row.reach === "
|
|
1800
|
-
? _jsx(Glyph, { kind: "
|
|
1801
|
-
: row.
|
|
1802
|
-
? _jsx(Glyph, { kind: "
|
|
1803
|
-
:
|
|
1917
|
+
return (_jsxs(Text, { wrap: "truncate", children: [_jsx(Pointer, { on: selected }), row.partial
|
|
1918
|
+
? _jsx(Glyph, { kind: "warn" })
|
|
1919
|
+
: row.reach === "out-all" || row.reach === "out-allow"
|
|
1920
|
+
? _jsx(Glyph, { kind: "off" })
|
|
1921
|
+
: row.evidence?.unauthed !== undefined || row.reach === "unknown"
|
|
1922
|
+
? _jsx(Glyph, { kind: "fail" })
|
|
1923
|
+
: row.reach === "out-workers"
|
|
1924
|
+
? _jsx(Glyph, { kind: "warn" })
|
|
1925
|
+
: row.tier
|
|
1926
|
+
? _jsx(Glyph, { kind: "on" })
|
|
1927
|
+
: _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}`));
|
|
1804
1928
|
};
|
|
1805
1929
|
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));
|
|
1806
1930
|
const renderShapeRow = (row, selected, rowW = bodyW - 2) => {
|
|
@@ -1846,8 +1970,8 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1846
1970
|
if (overlay.kind === "classify") {
|
|
1847
1971
|
const subject = overlay.bulk
|
|
1848
1972
|
? `${overlay.adapter} · ${overlay.bulk.rows.length} suggested models`
|
|
1849
|
-
: overlay.displayModel
|
|
1850
|
-
? `${overlay.adapter}:${overlay.displayModel} · writes ${overlay.model}`
|
|
1973
|
+
: overlay.displayModel || classifiedMembers(overlay.adapter, overlay.model).length > 1
|
|
1974
|
+
? `${overlay.adapter}:${overlay.displayModel ?? overlay.model} · writes ${classifiedMembers(overlay.adapter, overlay.displayModel ?? overlay.model).map((member) => member.model).join(", ")}`
|
|
1851
1975
|
: `${overlay.adapter}:${overlay.model}`;
|
|
1852
1976
|
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
|
|
1853
1977
|
? `catalog suggests ${overlay.suggestion.tier} — keep it and the provenance note arrives pre-typed`
|
|
@@ -1911,7 +2035,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1911
2035
|
const current = overlay.target.model === undefined
|
|
1912
2036
|
? adapterReach(overlay.target.adapter)
|
|
1913
2037
|
: modelRows().find((row) => row.adapter === overlay.target.adapter && row.model === overlay.target.model) ?? { reach: "unknown", reasons: [] };
|
|
1914
|
-
const partial = "partial" in current && current.partial;
|
|
2038
|
+
const partial = "partial" in current && current.partial === true;
|
|
1915
2039
|
// D-205: a partial rail marks no choice as selected — its channels disagree
|
|
1916
2040
|
const on = partial || current.reach === "unknown" ? null
|
|
1917
2041
|
: current.reach === "out-all" || current.reach === "out-allow" ? "out-all" : current.reach;
|
|
@@ -2098,7 +2222,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
2098
2222
|
? _jsx(Text, { color: INK.warn, children: ` ! ${clip(ui.notice, width - 4)}` })
|
|
2099
2223
|
: details.map((line) => _jsx(Text, { dimColor: true, children: ` ${clip(line, width - 2)}` }, line)), _jsx(KeyBar, { binds: keyBinds() })] }));
|
|
2100
2224
|
}
|
|
2101
|
-
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 }) {
|
|
2225
|
+
export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut = [], initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, layerStamp, reloadGuard, stagedRouting, holderOf, entry = "probe", initialJudge = "", judgeSeats = [], initialInput = [], input, output, debug = false, ...initialDenyProps }) {
|
|
2102
2226
|
const agents = adapters.flatMap((adapter) => {
|
|
2103
2227
|
const state = health[adapter.id];
|
|
2104
2228
|
if (!state?.installed)
|
|
@@ -2122,7 +2246,7 @@ export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowO
|
|
|
2122
2246
|
// rebuilt from output.columns/rows so a pane resize re-renders at the new size. Same root
|
|
2123
2247
|
// element type on every rerender ⇒ React keeps the component instance, so staged edits,
|
|
2124
2248
|
// cursor, and overlays all survive the resize.
|
|
2125
|
-
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,
|
|
2249
|
+
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, layerStamp: layerStamp, reloadGuard: reloadGuard, stagedRouting: stagedRouting, holderOf: holderOf, entry: entry, initialJudge: initialJudge, judgeSeats: judgeSeats,
|
|
2126
2250
|
// v1.90.9: cap every list to the terminal minus chrome so the cursor and header can
|
|
2127
2251
|
// never scroll off-screen (omp's 218-model list did exactly that pre-cap).
|
|
2128
2252
|
// OBS-523: chrome is ≤12 rows worst case (header, borders, padding, scope+search,
|