@caupulican/pi-adaptative 0.78.4 → 0.79.0

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.
@@ -28,7 +28,7 @@ import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/cha
28
28
  import { copyToClipboard } from "../../utils/clipboard.js";
29
29
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
30
30
  import { parseGitUrl } from "../../utils/git.js";
31
- import { getCwdRelativePath } from "../../utils/paths.js";
31
+ import { getCwdRelativePath, resolvePath } from "../../utils/paths.js";
32
32
  import { getPiUserAgent } from "../../utils/pi-user-agent.js";
33
33
  import { killTrackedDetachedChildren } from "../../utils/shell.js";
34
34
  import { ensureTool } from "../../utils/tools-manager.js";
@@ -88,6 +88,15 @@ function isDeadTerminalError(error) {
88
88
  return code !== undefined && DEAD_TERMINAL_ERROR_CODES.has(code);
89
89
  }
90
90
  const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.";
91
+ const AUTO_LEARN_DEFAULTS = {
92
+ model: "active",
93
+ longSessionMessages: 32,
94
+ longSessionContextPercent: 70,
95
+ cooldownMinutes: 120,
96
+ leaseMinutes: 90,
97
+ maxConcurrentLearners: 2,
98
+ applyHighConfidence: false,
99
+ };
91
100
  function isAnthropicSubscriptionAuthKey(apiKey) {
92
101
  return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
93
102
  }
@@ -188,6 +197,8 @@ export class InteractiveMode {
188
197
  // Auto-compaction state
189
198
  autoCompactionLoader = undefined;
190
199
  autoCompactionEscapeHandler;
200
+ // Auto Learn background runner state
201
+ autoLearnLastStatus = "idle";
191
202
  // Auto-retry state
192
203
  retryLoader = undefined;
193
204
  retryCountdown = undefined;
@@ -504,8 +515,9 @@ export class InteractiveMode {
504
515
  this.footerDataProvider.onBranchChange(() => {
505
516
  this.ui.requestRender();
506
517
  });
507
- // Initialize available provider count for footer display
518
+ // Initialize available provider count and Auto Learn status for footer display
508
519
  await this.updateAvailableProviderCount();
520
+ this.updateAutoLearnFooter();
509
521
  }
510
522
  /**
511
523
  * Update terminal title with session name and cwd.
@@ -2050,6 +2062,11 @@ export class InteractiveMode {
2050
2062
  this.editor.setText("");
2051
2063
  return;
2052
2064
  }
2065
+ if (text === "/auto-learn" || text.startsWith("/auto-learn ")) {
2066
+ this.handleAutoLearnCommand(text);
2067
+ this.editor.setText("");
2068
+ return;
2069
+ }
2053
2070
  if (text === "/scoped-models") {
2054
2071
  this.editor.setText("");
2055
2072
  await this.showModelsSelector();
@@ -2371,6 +2388,7 @@ export class InteractiveMode {
2371
2388
  break;
2372
2389
  }
2373
2390
  case "agent_end":
2391
+ this.maybeStartAutoLearn();
2374
2392
  if (this.settingsManager.getShowTerminalProgress()) {
2375
2393
  this.ui.terminal.setProgress(false);
2376
2394
  }
@@ -3260,8 +3278,382 @@ export class InteractiveMode {
3260
3278
  this.ui.setFocus(focus);
3261
3279
  this.ui.requestRender();
3262
3280
  }
3281
+ getAutoLearnModelAuthPriority(model) {
3282
+ if (this.session.model && model.provider === this.session.model.provider && model.id === this.session.model.id) {
3283
+ return 0;
3284
+ }
3285
+ const credential = this.session.modelRegistry.authStorage.get(model.provider);
3286
+ if (credential?.type === "oauth")
3287
+ return 1;
3288
+ if (credential?.type === "api_key")
3289
+ return 2;
3290
+ const authStatus = this.session.modelRegistry.getProviderAuthStatus(model.provider);
3291
+ switch (authStatus.source) {
3292
+ case "runtime":
3293
+ return 3;
3294
+ case "environment":
3295
+ return 4;
3296
+ case "models_json_key":
3297
+ case "models_json_command":
3298
+ case "fallback":
3299
+ return 5;
3300
+ default:
3301
+ return 6;
3302
+ }
3303
+ }
3304
+ getAutoLearnModelAuthLabel(model) {
3305
+ const credential = this.session.modelRegistry.authStorage.get(model.provider);
3306
+ if (credential?.type === "oauth")
3307
+ return "subscription";
3308
+ if (credential?.type === "api_key")
3309
+ return "API key";
3310
+ const authStatus = this.session.modelRegistry.getProviderAuthStatus(model.provider);
3311
+ switch (authStatus.source) {
3312
+ case "runtime":
3313
+ return authStatus.label ? `runtime ${authStatus.label}` : "runtime API key";
3314
+ case "environment":
3315
+ return authStatus.label ? `env ${authStatus.label}` : "environment API key";
3316
+ case "models_json_key":
3317
+ return "models.json API key";
3318
+ case "models_json_command":
3319
+ return "models.json command";
3320
+ case "fallback":
3321
+ return authStatus.label ?? "custom provider config";
3322
+ default:
3323
+ return "configured";
3324
+ }
3325
+ }
3326
+ getAutoLearnModelOptions() {
3327
+ this.session.modelRegistry.refresh();
3328
+ const availableModels = this.session.modelRegistry.getAvailable();
3329
+ const sortedModels = [...availableModels].sort((a, b) => {
3330
+ const priorityDelta = this.getAutoLearnModelAuthPriority(a) - this.getAutoLearnModelAuthPriority(b);
3331
+ if (priorityDelta !== 0)
3332
+ return priorityDelta;
3333
+ const providerDelta = this.session.modelRegistry
3334
+ .getProviderDisplayName(a.provider)
3335
+ .localeCompare(this.session.modelRegistry.getProviderDisplayName(b.provider));
3336
+ if (providerDelta !== 0)
3337
+ return providerDelta;
3338
+ return a.id.localeCompare(b.id);
3339
+ });
3340
+ return sortedModels.map((model) => {
3341
+ const providerName = this.session.modelRegistry.getProviderDisplayName(model.provider);
3342
+ const authLabel = this.getAutoLearnModelAuthLabel(model);
3343
+ const modelPattern = `${model.provider}/${model.id}`;
3344
+ const currentLabel = this.session.model && model.provider === this.session.model.provider && model.id === this.session.model.id
3345
+ ? " · current"
3346
+ : "";
3347
+ const displayName = model.name && model.name !== model.id ? ` · ${model.name}` : "";
3348
+ return {
3349
+ value: modelPattern,
3350
+ label: modelPattern,
3351
+ description: `${providerName} · ${authLabel}${currentLabel}${displayName}`,
3352
+ };
3353
+ });
3354
+ }
3355
+ getAutoLearnDataDir() {
3356
+ return path.join(getAgentDir(), "auto-learn");
3357
+ }
3358
+ getAutoLearnStatePath() {
3359
+ return path.join(this.getAutoLearnDataDir(), "state.json");
3360
+ }
3361
+ readAutoLearnState() {
3362
+ try {
3363
+ const statePath = this.getAutoLearnStatePath();
3364
+ if (!fs.existsSync(statePath))
3365
+ return {};
3366
+ return JSON.parse(fs.readFileSync(statePath, "utf-8"));
3367
+ }
3368
+ catch {
3369
+ return {};
3370
+ }
3371
+ }
3372
+ writeAutoLearnState(state) {
3373
+ const dir = this.getAutoLearnDataDir();
3374
+ fs.mkdirSync(dir, { recursive: true });
3375
+ fs.writeFileSync(this.getAutoLearnStatePath(), `${JSON.stringify(state, null, 2)}\n`, "utf-8");
3376
+ }
3377
+ isAutoLearnPidAlive(pid) {
3378
+ if (typeof pid !== "number" || pid <= 0)
3379
+ return false;
3380
+ try {
3381
+ process.kill(pid, 0);
3382
+ return true;
3383
+ }
3384
+ catch (error) {
3385
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
3386
+ return code === "EPERM";
3387
+ }
3388
+ }
3389
+ pruneAutoLearnState(state, now = Date.now()) {
3390
+ const runs = { ...(state.runs ?? {}) };
3391
+ for (const [id, run] of Object.entries(runs)) {
3392
+ if (run.expiresAt <= now || !this.isAutoLearnPidAlive(run.pid)) {
3393
+ delete runs[id];
3394
+ }
3395
+ }
3396
+ return { ...state, runs };
3397
+ }
3398
+ getEffectiveAutoLearnSettings() {
3399
+ const settings = this.settingsManager.getAutoLearnSettings();
3400
+ return {
3401
+ enabled: settings.enabled ?? false,
3402
+ model: settings.model?.trim() || AUTO_LEARN_DEFAULTS.model,
3403
+ longSessionMessages: settings.longSessionMessages ?? AUTO_LEARN_DEFAULTS.longSessionMessages,
3404
+ longSessionContextPercent: settings.longSessionContextPercent ?? AUTO_LEARN_DEFAULTS.longSessionContextPercent,
3405
+ cooldownMinutes: settings.cooldownMinutes ?? AUTO_LEARN_DEFAULTS.cooldownMinutes,
3406
+ leaseMinutes: settings.leaseMinutes ?? AUTO_LEARN_DEFAULTS.leaseMinutes,
3407
+ maxConcurrentLearners: settings.maxConcurrentLearners ?? AUTO_LEARN_DEFAULTS.maxConcurrentLearners,
3408
+ applyHighConfidence: settings.applyHighConfidence ?? AUTO_LEARN_DEFAULTS.applyHighConfidence,
3409
+ };
3410
+ }
3411
+ getAutoLearnTenantKey() {
3412
+ return `${this.sessionManager.getCwd()}::${this.session.sessionId}`;
3413
+ }
3414
+ getAutoLearnMessageCount() {
3415
+ return this.sessionManager.getBranch().filter((entry) => entry.type === "message").length;
3416
+ }
3417
+ resolveAutoLearnModelPattern(settings) {
3418
+ if (settings.model === "active") {
3419
+ return this.session.model ? `${this.session.model.provider}/${this.session.model.id}` : undefined;
3420
+ }
3421
+ return settings.model;
3422
+ }
3423
+ getAutoLearnSpawnTarget() {
3424
+ const overridePath = process.env.PI_AUTO_LEARN_CLI_PATH?.trim();
3425
+ if (overridePath) {
3426
+ return { command: overridePath, argsPrefix: [] };
3427
+ }
3428
+ const execBase = path.basename(process.execPath).toLowerCase();
3429
+ const isScriptRuntime = execBase === "node" || execBase === "node.exe" || execBase === "bun" || execBase === "bun.exe";
3430
+ if (!isScriptRuntime) {
3431
+ return { command: process.execPath, argsPrefix: [] };
3432
+ }
3433
+ const cliPath = process.argv[1];
3434
+ if (!cliPath || cliPath.startsWith("-")) {
3435
+ return undefined;
3436
+ }
3437
+ return { command: process.execPath, argsPrefix: [cliPath] };
3438
+ }
3439
+ validateAutoLearnModelValue(value) {
3440
+ const modelValue = value?.trim();
3441
+ if (!modelValue || modelValue === "active")
3442
+ return undefined;
3443
+ const available = this.session.modelRegistry.getAvailable();
3444
+ if (modelValue.includes("/")) {
3445
+ const [provider, modelId] = modelValue.split("/", 2);
3446
+ if (available.some((model) => model.provider === provider && model.id === modelId))
3447
+ return undefined;
3448
+ return `Auto Learn model "${modelValue}" is not in configured subscription/API models; saved as manual/unverified.`;
3449
+ }
3450
+ if (available.some((model) => model.id === modelValue))
3451
+ return undefined;
3452
+ return `Auto Learn model "${modelValue}" is not in configured subscription/API models; saved as manual/unverified.`;
3453
+ }
3454
+ validateSelfModificationSource(settings) {
3455
+ if (!settings.enabled)
3456
+ return undefined;
3457
+ const rawPath = settings.sourcePath?.trim();
3458
+ if (!rawPath)
3459
+ return "Self modification is enabled, but no pi-adaptative source path is set.";
3460
+ const sourcePath = resolvePath(rawPath, this.sessionManager.getCwd(), { trim: true });
3461
+ if (!fs.existsSync(sourcePath))
3462
+ return `Self modification source path does not exist: ${sourcePath}`;
3463
+ if (!fs.existsSync(path.join(sourcePath, "package.json"))) {
3464
+ return `Self modification source path has no package.json: ${sourcePath}`;
3465
+ }
3466
+ if (!fs.existsSync(path.join(sourcePath, "packages", "coding-agent"))) {
3467
+ return `Self modification source path does not look like pi-adaptative (missing packages/coding-agent): ${sourcePath}`;
3468
+ }
3469
+ return undefined;
3470
+ }
3471
+ evaluateAutoLearn(force = false) {
3472
+ const settings = this.getEffectiveAutoLearnSettings();
3473
+ const state = this.pruneAutoLearnState(this.readAutoLearnState());
3474
+ this.writeAutoLearnState(state);
3475
+ const now = Date.now();
3476
+ const tenant = this.getAutoLearnTenantKey();
3477
+ const runningCount = Object.keys(state.runs ?? {}).length;
3478
+ const lastLaunch = state.lastLaunchByTenant?.[tenant] ?? 0;
3479
+ const cooldownMs = settings.cooldownMinutes * 60 * 1000;
3480
+ const cooldownRemainingMs = Math.max(0, lastLaunch + cooldownMs - now);
3481
+ const messageCount = this.getAutoLearnMessageCount();
3482
+ const contextPercent = this.session.getContextUsage()?.percent ?? null;
3483
+ if (!settings.enabled && !force) {
3484
+ return {
3485
+ shouldRun: false,
3486
+ reason: "disabled",
3487
+ messageCount,
3488
+ contextPercent,
3489
+ cooldownRemainingMs,
3490
+ runningCount,
3491
+ };
3492
+ }
3493
+ if (runningCount >= settings.maxConcurrentLearners) {
3494
+ return {
3495
+ shouldRun: false,
3496
+ reason: `max learners running (${runningCount}/${settings.maxConcurrentLearners})`,
3497
+ messageCount,
3498
+ contextPercent,
3499
+ cooldownRemainingMs,
3500
+ runningCount,
3501
+ };
3502
+ }
3503
+ if (!force && cooldownRemainingMs > 0) {
3504
+ return {
3505
+ shouldRun: false,
3506
+ reason: "cooldown",
3507
+ messageCount,
3508
+ contextPercent,
3509
+ cooldownRemainingMs,
3510
+ runningCount,
3511
+ };
3512
+ }
3513
+ if (force) {
3514
+ return { shouldRun: true, reason: "manual", messageCount, contextPercent, cooldownRemainingMs, runningCount };
3515
+ }
3516
+ if (messageCount >= settings.longSessionMessages) {
3517
+ return {
3518
+ shouldRun: true,
3519
+ reason: `message trigger (${messageCount}/${settings.longSessionMessages})`,
3520
+ messageCount,
3521
+ contextPercent,
3522
+ cooldownRemainingMs,
3523
+ runningCount,
3524
+ };
3525
+ }
3526
+ if (contextPercent !== null && contextPercent >= settings.longSessionContextPercent) {
3527
+ return {
3528
+ shouldRun: true,
3529
+ reason: `context trigger (${contextPercent.toFixed(1)}%/${settings.longSessionContextPercent}%)`,
3530
+ messageCount,
3531
+ contextPercent,
3532
+ cooldownRemainingMs,
3533
+ runningCount,
3534
+ };
3535
+ }
3536
+ return {
3537
+ shouldRun: false,
3538
+ reason: "thresholds not met",
3539
+ messageCount,
3540
+ contextPercent,
3541
+ cooldownRemainingMs,
3542
+ runningCount,
3543
+ };
3544
+ }
3545
+ buildAutoLearnPrompt(reason, settings) {
3546
+ return `You are Pi Auto Learn running as a background learner.\n\nObjective: run one bounded continuous-learning pass for this Pi tenant.\nTrigger: ${reason}.\n\nRequired workflow:\n1. Query existing durable memory/rules first when tools allow it.\n2. Run the available Auto Learn tooling, preferably learning_run_auto, with applyHighConfidence=${settings.applyHighConfidence}.\n3. Keep tooling/core/source changes proposal/approval-gated; do not publish, tag, release, or modify Pi source from this background run.\n4. If the learning tools are unavailable, report BLOCKED with the missing tool names and do not improvise.\n5. Finish with PASS, BLOCKED, or FAIL and concise evidence.`;
3547
+ }
3548
+ launchAutoLearn(reason, force = false) {
3549
+ const settings = this.getEffectiveAutoLearnSettings();
3550
+ const decision = this.evaluateAutoLearn(force);
3551
+ if (!decision.shouldRun) {
3552
+ return `Auto Learn not started: ${decision.reason}`;
3553
+ }
3554
+ const modelPattern = this.resolveAutoLearnModelPattern(settings);
3555
+ if (!modelPattern) {
3556
+ return "Auto Learn not started: no active model is available for model=active.";
3557
+ }
3558
+ const spawnTarget = this.getAutoLearnSpawnTarget();
3559
+ if (!spawnTarget) {
3560
+ return "Auto Learn not started: could not resolve current pi CLI path.";
3561
+ }
3562
+ const dir = this.getAutoLearnDataDir();
3563
+ fs.mkdirSync(dir, { recursive: true });
3564
+ const runId = `${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
3565
+ const logPath = path.join(dir, `${runId}.log`);
3566
+ const outFd = fs.openSync(logPath, "a");
3567
+ const prompt = this.buildAutoLearnPrompt(reason, settings);
3568
+ const args = [
3569
+ ...spawnTarget.argsPrefix,
3570
+ "--print",
3571
+ "--name",
3572
+ `Auto Learn ${runId}`,
3573
+ "--model",
3574
+ modelPattern,
3575
+ prompt,
3576
+ ];
3577
+ const child = spawn(spawnTarget.command, args, {
3578
+ cwd: this.sessionManager.getCwd(),
3579
+ detached: true,
3580
+ stdio: ["ignore", outFd, outFd],
3581
+ env: { ...process.env, PI_AUTO_LEARN_CHILD: "1" },
3582
+ });
3583
+ child.unref();
3584
+ fs.closeSync(outFd);
3585
+ const now = Date.now();
3586
+ const state = this.pruneAutoLearnState(this.readAutoLearnState(), now);
3587
+ state.lastLaunchByTenant = { ...(state.lastLaunchByTenant ?? {}), [this.getAutoLearnTenantKey()]: now };
3588
+ state.runs = {
3589
+ ...(state.runs ?? {}),
3590
+ [runId]: {
3591
+ tenant: this.getAutoLearnTenantKey(),
3592
+ pid: child.pid,
3593
+ model: modelPattern,
3594
+ reason,
3595
+ startedAt: now,
3596
+ expiresAt: now + settings.leaseMinutes * 60 * 1000,
3597
+ cwd: this.sessionManager.getCwd(),
3598
+ logPath,
3599
+ },
3600
+ };
3601
+ this.writeAutoLearnState(state);
3602
+ this.autoLearnLastStatus = `running ${modelPattern}`;
3603
+ this.updateAutoLearnFooter();
3604
+ return `Auto Learn started (${reason}) with ${modelPattern}. Log: ${logPath}`;
3605
+ }
3606
+ maybeStartAutoLearn() {
3607
+ if (process.env.PI_AUTO_LEARN_CHILD === "1")
3608
+ return;
3609
+ const decision = this.evaluateAutoLearn(false);
3610
+ if (!decision.shouldRun) {
3611
+ this.autoLearnLastStatus = decision.reason;
3612
+ this.updateAutoLearnFooter();
3613
+ return;
3614
+ }
3615
+ const message = this.launchAutoLearn(decision.reason, false);
3616
+ this.showStatus(message);
3617
+ }
3618
+ updateAutoLearnFooter() {
3619
+ const settings = this.getEffectiveAutoLearnSettings();
3620
+ if (!settings.enabled) {
3621
+ this.footerDataProvider.setExtensionStatus("auto-learn", undefined);
3622
+ return;
3623
+ }
3624
+ this.footerDataProvider.setExtensionStatus("auto-learn", theme.fg("accent", `learn: ${this.autoLearnLastStatus}`));
3625
+ this.footer.invalidate();
3626
+ this.ui.requestRender();
3627
+ }
3628
+ formatAutoLearnStatus() {
3629
+ const settings = this.getEffectiveAutoLearnSettings();
3630
+ const decision = this.evaluateAutoLearn(false);
3631
+ const state = this.pruneAutoLearnState(this.readAutoLearnState());
3632
+ const runs = Object.entries(state.runs ?? {});
3633
+ const contextText = decision.contextPercent === null ? "unknown" : `${decision.contextPercent.toFixed(1)}%`;
3634
+ const cooldownText = decision.cooldownRemainingMs > 0 ? `${Math.ceil(decision.cooldownRemainingMs / 60000)}m remaining` : "ready";
3635
+ const runLines = runs.length
3636
+ ? runs.map(([id, run]) => `- ${id}: ${run.model}, pid=${run.pid ?? "?"}, log=${run.logPath}`).join("\n")
3637
+ : "- none";
3638
+ return `Auto Learn status\nEnabled: ${settings.enabled}\nModel: ${settings.model}\nNext decision: ${decision.shouldRun ? "ready" : decision.reason}\nMessages: ${decision.messageCount}/${settings.longSessionMessages}\nContext: ${contextText}/${settings.longSessionContextPercent}%\nCooldown: ${cooldownText}\nRunning leases: ${runs.length}/${settings.maxConcurrentLearners}\nRuns:\n${runLines}`;
3639
+ }
3640
+ handleAutoLearnCommand(text) {
3641
+ const action = text.slice("/auto-learn".length).trim() || "status";
3642
+ if (action === "run" || action === "now" || action === "run-now") {
3643
+ this.showStatus(this.launchAutoLearn("manual", true));
3644
+ return;
3645
+ }
3646
+ if (action === "status") {
3647
+ this.chatContainer.addChild(new Spacer(1));
3648
+ this.chatContainer.addChild(new Text(this.formatAutoLearnStatus(), 1, 0));
3649
+ this.ui.requestRender();
3650
+ return;
3651
+ }
3652
+ this.showStatus("Usage: /auto-learn [status|run]");
3653
+ }
3263
3654
  showSettingsSelector() {
3264
3655
  this.showSelector((done) => {
3656
+ const projectSettings = this.settingsManager.getProjectSettings();
3265
3657
  const selector = new SettingsSelectorComponent({
3266
3658
  autoCompact: this.session.autoCompactionEnabled,
3267
3659
  showImages: this.settingsManager.getShowImages(),
@@ -3289,6 +3681,14 @@ export class InteractiveMode {
3289
3681
  clearOnShrink: this.settingsManager.getClearOnShrink(),
3290
3682
  showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
3291
3683
  warnings: this.settingsManager.getWarnings(),
3684
+ selfModification: this.settingsManager.getSelfModificationSettings(),
3685
+ selfModificationScope: projectSettings.selfModification ? "project" : "global",
3686
+ autoLearn: this.settingsManager.getAutoLearnSettings(),
3687
+ autoLearnScope: projectSettings.autoLearn ? "project" : "global",
3688
+ autoLearnModelOptions: this.getAutoLearnModelOptions(),
3689
+ currentModelPattern: this.session.model
3690
+ ? `${this.session.model.provider}/${this.session.model.id}`
3691
+ : undefined,
3292
3692
  }, {
3293
3693
  onAutoCompactChange: (enabled) => {
3294
3694
  this.session.setAutoCompactionEnabled(enabled);
@@ -3409,6 +3809,23 @@ export class InteractiveMode {
3409
3809
  onWarningsChange: (warnings) => {
3410
3810
  this.settingsManager.setWarnings(warnings);
3411
3811
  },
3812
+ onSelfModificationChange: (settings, scope) => {
3813
+ this.settingsManager.setSelfModificationSettings(settings, scope);
3814
+ const validationMessage = this.validateSelfModificationSource(settings);
3815
+ if (validationMessage) {
3816
+ this.showWarning(validationMessage);
3817
+ }
3818
+ this.showStatus(`Self modification settings saved to ${scope}. Start a new session or /reload for system-prompt guardrails to fully refresh.`);
3819
+ },
3820
+ onAutoLearnChange: (settings, scope) => {
3821
+ this.settingsManager.setAutoLearnSettings(settings, scope);
3822
+ const validationMessage = this.validateAutoLearnModelValue(settings.model);
3823
+ if (validationMessage) {
3824
+ this.showWarning(validationMessage);
3825
+ }
3826
+ this.updateAutoLearnFooter();
3827
+ this.showStatus(`Auto Learn settings saved to ${scope}. Use /auto-learn status or /auto-learn run.`);
3828
+ },
3412
3829
  onCancel: () => {
3413
3830
  done();
3414
3831
  this.ui.requestRender();