inspect: restore model selection when adding inspectors · Entire

inspect: restore model selection when adding inspectors

6c7a25f→main·

dipree·4w ago·1 file·+38 added/-18 removed

The guided inspector slot picker still prompted only for the agent when adding a slot, and editing a slot only allowed changing/removing the agent. Restore the model step in that interactive flow: adding a slot now asks for agent and model, changing an agent asks for the new agent's model, and existing slots can change just the model. Current models remain preselected when editing.

Sessions

1055f5ace809View transcript

[?
Checkout the hand off doc that I just added.Pi·Opus 4.8·2 steps](/content/gh/entireio/cli/session/019eca64-8c2c-7b00-90c6-3aa49738c497#timeline-1055f5ace809/index.html)

Changes

1

366 unmodified lines

367
368
369
370
371
372
370
371
372
5 unmodified lines

378
379
380
381
382
383
384
385
386
387
388
389
1 unmodified line

391
392
393
391
392
393
394
395
396
397
398
399
400
399
401
402
403
404
405
406
407
408
409
410
411
402
403
404
412
413
414
415
416
417
418
419
420
421
411
422
423
413
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
24 unmodified lines

464
465
466
444
445
446
467
448
468
469
470
471
472
473
474
475
476
11 unmodified lines

488
489
490
470
471
491
492
493
494

366 unmodified lines

if convErr != nil || idx < 0 || idx >= len(slots) {
            continue
        }
        // Inspectors are identified by their agent and run on the agent's
        // default model, so editing only swaps the agent (when more than one is
        // available) or removes the slot — there is no per-inspector model.
        action, err := promptSlotAction(ctx, slots[idx], len(candidates) > 1)
        if err != nil {
            return nil, err
5 unmodified lines

return nil, err
        }
        slots[idx] = slot
        case "model":
            slot, err := promptChangeModel(ctx, slots[idx])
            if err != nil {
                return nil, err
            }
        slots[idx] = slot
        case "remove":
            slots = append(slots[:idx], slots[idx+1:]...)
        }
1 unmodified line

}

// promptCrewSlot prompts for one inspector slot: just the agent. Inspectors run
// on the agent's default model, so no model is asked here. seed pre-selects the
// current agent when editing (zero value when adding).
// promptCrewSlot prompts for one inspector slot: agent plus model. seed
// pre-selects the current agent/model when editing (zero value when adding).
func promptCrewSlot(ctx context.Context, launchable []string, seed crewSlot) (crewSlot, error) {
    agentName, err := promptCrewAgent(ctx, launchable, seed.agent, false)
    if err != nil {
        return crewSlot{}, err
    }
    return crewSlot{agent: agentName}, nil
    seedModel := ""
    if agentName == seed.agent {
        seedModel = seed.model
    }
    model, err := promptCrewModel(ctx, agentName, seedModel)
    if err != nil {
        return crewSlot{}, err
    }
    return crewSlot{agent: agentName, model: model}, nil
}

// promptChangeAgent swaps the agent on an existing inspector slot. Keeping the
// same agent leaves the slot untouched (preserving any model set via scripted
// config); choosing a different agent resets to that agent's default model.
// promptChangeAgent swaps the agent on an existing inspector slot and then asks
// for that agent's model. Keeping the same agent preserves the current model as
// the preselected value.
func promptChangeAgent(ctx context.Context, candidates []string, seed crewSlot) (crewSlot, error) {
    agentName, err := promptCrewAgent(ctx, candidates, seed.agent, true)
    if err != nil {
        return crewSlot{}, err
    }
    seedModel := ""
    if agentName == seed.agent {
        return seed, nil
        seedModel = seed.model
    }
    return crewSlot{agent: agentName}, nil
    model, err := promptCrewModel(ctx, agentName, seedModel)
    if err != nil {
        return crewSlot{}, err
    }
    return crewSlot{agent: agentName, model: model}, nil
}

func promptChangeModel(ctx context.Context, seed crewSlot) (crewSlot, error) {
    model, err := promptCrewModel(ctx, seed.agent, seed.model)
    if err != nil {
        return crewSlot{}, err
    }
    return crewSlot{agent: seed.agent, model: model}, nil
}

// buildCrewProfile turns an ordered slot list into a profile. Each slot becomes
24 unmodified lines

// promptSlotAction asks what to do with an existing inspector slot row.
// Inspectors run on the agent's default model, so the only edits are swapping
// the agent (offered when allowAgentChange is set — more than one candidate
// exists) or removing the slot.
func promptSlotAction(ctx context.Context, slot crewSlot, allowAgentChange bool) (string, error) {
    options := make([]huh.Option[string], 0, 3)
    options := make([]huh.Option[string], 0, 4)
    if allowAgentChange {
        options = append(options, huh.NewOption("Change agent", "agent"))
    }
    options = append(options,
        huh.NewOption("Change model", "model"),
        huh.NewOption("Remove", "remove"),
        huh.NewOption("Cancel", "cancel"),
    )
11 unmodified lines

}

func slotLabel(s crewSlot) string {
    // Inspectors normally run on the agent's default model; only surface a model
    // when one was set explicitly (e.g. via scripted --set-slot).
    // Surface the model when one was set explicitly.
    if model := strings.TrimSpace(s.model); model != "" {
        return labelForSimpleAgent(s.agent) + " · " + model
    }