@caupulican/pi-adaptative 0.78.4 → 0.80.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.
- package/CHANGELOG.md +17 -0
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +34 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/resource-loader.d.ts +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +26 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +14 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +32 -2
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +16 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +441 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +36 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +731 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/settings.md +33 -5
- package/docs/usage.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +15 -18
- package/package.json +4 -4
|
@@ -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,62 @@ 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
|
+
enabled: false,
|
|
93
|
+
model: "active",
|
|
94
|
+
longSessionMessages: 32,
|
|
95
|
+
longSessionContextPercent: 70,
|
|
96
|
+
cooldownMinutes: 120,
|
|
97
|
+
leaseMinutes: 90,
|
|
98
|
+
maxConcurrentLearners: 2,
|
|
99
|
+
applyHighConfidence: false,
|
|
100
|
+
reflectionReview: true,
|
|
101
|
+
reflectionMinToolCalls: 5,
|
|
102
|
+
reflectionCooldownMinutes: 60,
|
|
103
|
+
};
|
|
104
|
+
const AUTONOMY_AUTO_LEARN_PRESETS = {
|
|
105
|
+
off: { ...AUTO_LEARN_DEFAULTS, enabled: false, reflectionReview: false },
|
|
106
|
+
safe: {
|
|
107
|
+
...AUTO_LEARN_DEFAULTS,
|
|
108
|
+
enabled: true,
|
|
109
|
+
longSessionMessages: 48,
|
|
110
|
+
longSessionContextPercent: 80,
|
|
111
|
+
cooldownMinutes: 180,
|
|
112
|
+
leaseMinutes: 60,
|
|
113
|
+
maxConcurrentLearners: 1,
|
|
114
|
+
applyHighConfidence: false,
|
|
115
|
+
reflectionReview: true,
|
|
116
|
+
reflectionMinToolCalls: 8,
|
|
117
|
+
reflectionCooldownMinutes: 120,
|
|
118
|
+
},
|
|
119
|
+
balanced: {
|
|
120
|
+
...AUTO_LEARN_DEFAULTS,
|
|
121
|
+
enabled: true,
|
|
122
|
+
longSessionMessages: 32,
|
|
123
|
+
longSessionContextPercent: 70,
|
|
124
|
+
cooldownMinutes: 120,
|
|
125
|
+
leaseMinutes: 90,
|
|
126
|
+
maxConcurrentLearners: 2,
|
|
127
|
+
applyHighConfidence: false,
|
|
128
|
+
reflectionReview: true,
|
|
129
|
+
reflectionMinToolCalls: 5,
|
|
130
|
+
reflectionCooldownMinutes: 60,
|
|
131
|
+
},
|
|
132
|
+
full: {
|
|
133
|
+
...AUTO_LEARN_DEFAULTS,
|
|
134
|
+
enabled: true,
|
|
135
|
+
longSessionMessages: 8,
|
|
136
|
+
longSessionContextPercent: 50,
|
|
137
|
+
cooldownMinutes: 15,
|
|
138
|
+
leaseMinutes: 90,
|
|
139
|
+
maxConcurrentLearners: 3,
|
|
140
|
+
applyHighConfidence: true,
|
|
141
|
+
reflectionReview: true,
|
|
142
|
+
reflectionMinToolCalls: 1,
|
|
143
|
+
reflectionCooldownMinutes: 0,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const AUTONOMY_MODES = ["off", "safe", "balanced", "full"];
|
|
91
147
|
function isAnthropicSubscriptionAuthKey(apiKey) {
|
|
92
148
|
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|
|
93
149
|
}
|
|
@@ -188,6 +244,8 @@ export class InteractiveMode {
|
|
|
188
244
|
// Auto-compaction state
|
|
189
245
|
autoCompactionLoader = undefined;
|
|
190
246
|
autoCompactionEscapeHandler;
|
|
247
|
+
// Auto Learn background runner state
|
|
248
|
+
autoLearnLastStatus = "idle";
|
|
191
249
|
// Auto-retry state
|
|
192
250
|
retryLoader = undefined;
|
|
193
251
|
retryCountdown = undefined;
|
|
@@ -504,8 +562,9 @@ export class InteractiveMode {
|
|
|
504
562
|
this.footerDataProvider.onBranchChange(() => {
|
|
505
563
|
this.ui.requestRender();
|
|
506
564
|
});
|
|
507
|
-
// Initialize available provider count for footer display
|
|
565
|
+
// Initialize available provider count and Auto Learn status for footer display
|
|
508
566
|
await this.updateAvailableProviderCount();
|
|
567
|
+
this.updateAutoLearnFooter();
|
|
509
568
|
}
|
|
510
569
|
/**
|
|
511
570
|
* Update terminal title with session name and cwd.
|
|
@@ -2050,6 +2109,16 @@ export class InteractiveMode {
|
|
|
2050
2109
|
this.editor.setText("");
|
|
2051
2110
|
return;
|
|
2052
2111
|
}
|
|
2112
|
+
if (text === "/auto-learn" || text.startsWith("/auto-learn ")) {
|
|
2113
|
+
this.handleAutoLearnCommand(text);
|
|
2114
|
+
this.editor.setText("");
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
if (text === "/autonomy" || text.startsWith("/autonomy ")) {
|
|
2118
|
+
this.handleAutonomyCommand(text);
|
|
2119
|
+
this.editor.setText("");
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2053
2122
|
if (text === "/scoped-models") {
|
|
2054
2123
|
this.editor.setText("");
|
|
2055
2124
|
await this.showModelsSelector();
|
|
@@ -2371,6 +2440,9 @@ export class InteractiveMode {
|
|
|
2371
2440
|
break;
|
|
2372
2441
|
}
|
|
2373
2442
|
case "agent_end":
|
|
2443
|
+
if (!this.maybeStartAutoLearn()) {
|
|
2444
|
+
this.maybeStartAutonomyReview(event.messages);
|
|
2445
|
+
}
|
|
2374
2446
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2375
2447
|
this.ui.terminal.setProgress(false);
|
|
2376
2448
|
}
|
|
@@ -3260,8 +3332,634 @@ export class InteractiveMode {
|
|
|
3260
3332
|
this.ui.setFocus(focus);
|
|
3261
3333
|
this.ui.requestRender();
|
|
3262
3334
|
}
|
|
3335
|
+
getAutoLearnModelAuthPriority(model) {
|
|
3336
|
+
if (this.session.model && model.provider === this.session.model.provider && model.id === this.session.model.id) {
|
|
3337
|
+
return 0;
|
|
3338
|
+
}
|
|
3339
|
+
const credential = this.session.modelRegistry.authStorage.get(model.provider);
|
|
3340
|
+
if (credential?.type === "oauth")
|
|
3341
|
+
return 1;
|
|
3342
|
+
if (credential?.type === "api_key")
|
|
3343
|
+
return 2;
|
|
3344
|
+
const authStatus = this.session.modelRegistry.getProviderAuthStatus(model.provider);
|
|
3345
|
+
switch (authStatus.source) {
|
|
3346
|
+
case "runtime":
|
|
3347
|
+
return 3;
|
|
3348
|
+
case "environment":
|
|
3349
|
+
return 4;
|
|
3350
|
+
case "models_json_key":
|
|
3351
|
+
case "models_json_command":
|
|
3352
|
+
case "fallback":
|
|
3353
|
+
return 5;
|
|
3354
|
+
default:
|
|
3355
|
+
return 6;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
getAutoLearnModelAuthLabel(model) {
|
|
3359
|
+
const credential = this.session.modelRegistry.authStorage.get(model.provider);
|
|
3360
|
+
if (credential?.type === "oauth")
|
|
3361
|
+
return "subscription";
|
|
3362
|
+
if (credential?.type === "api_key")
|
|
3363
|
+
return "API key";
|
|
3364
|
+
const authStatus = this.session.modelRegistry.getProviderAuthStatus(model.provider);
|
|
3365
|
+
switch (authStatus.source) {
|
|
3366
|
+
case "runtime":
|
|
3367
|
+
return authStatus.label ? `runtime ${authStatus.label}` : "runtime API key";
|
|
3368
|
+
case "environment":
|
|
3369
|
+
return authStatus.label ? `env ${authStatus.label}` : "environment API key";
|
|
3370
|
+
case "models_json_key":
|
|
3371
|
+
return "models.json API key";
|
|
3372
|
+
case "models_json_command":
|
|
3373
|
+
return "models.json command";
|
|
3374
|
+
case "fallback":
|
|
3375
|
+
return authStatus.label ?? "custom provider config";
|
|
3376
|
+
default:
|
|
3377
|
+
return "configured";
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
getAutoLearnModelOptions() {
|
|
3381
|
+
this.session.modelRegistry.refresh();
|
|
3382
|
+
const availableModels = this.session.modelRegistry.getAvailable();
|
|
3383
|
+
const sortedModels = [...availableModels].sort((a, b) => {
|
|
3384
|
+
const priorityDelta = this.getAutoLearnModelAuthPriority(a) - this.getAutoLearnModelAuthPriority(b);
|
|
3385
|
+
if (priorityDelta !== 0)
|
|
3386
|
+
return priorityDelta;
|
|
3387
|
+
const providerDelta = this.session.modelRegistry
|
|
3388
|
+
.getProviderDisplayName(a.provider)
|
|
3389
|
+
.localeCompare(this.session.modelRegistry.getProviderDisplayName(b.provider));
|
|
3390
|
+
if (providerDelta !== 0)
|
|
3391
|
+
return providerDelta;
|
|
3392
|
+
return a.id.localeCompare(b.id);
|
|
3393
|
+
});
|
|
3394
|
+
return sortedModels.map((model) => {
|
|
3395
|
+
const providerName = this.session.modelRegistry.getProviderDisplayName(model.provider);
|
|
3396
|
+
const authLabel = this.getAutoLearnModelAuthLabel(model);
|
|
3397
|
+
const modelPattern = `${model.provider}/${model.id}`;
|
|
3398
|
+
const currentLabel = this.session.model && model.provider === this.session.model.provider && model.id === this.session.model.id
|
|
3399
|
+
? " · current"
|
|
3400
|
+
: "";
|
|
3401
|
+
const displayName = model.name && model.name !== model.id ? ` · ${model.name}` : "";
|
|
3402
|
+
return {
|
|
3403
|
+
value: modelPattern,
|
|
3404
|
+
label: modelPattern,
|
|
3405
|
+
description: `${providerName} · ${authLabel}${currentLabel}${displayName}`,
|
|
3406
|
+
};
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3409
|
+
getAutoLearnDataDir() {
|
|
3410
|
+
return path.join(getAgentDir(), "auto-learn");
|
|
3411
|
+
}
|
|
3412
|
+
getAutoLearnStatePath() {
|
|
3413
|
+
return path.join(this.getAutoLearnDataDir(), "state.json");
|
|
3414
|
+
}
|
|
3415
|
+
readAutoLearnState() {
|
|
3416
|
+
try {
|
|
3417
|
+
const statePath = this.getAutoLearnStatePath();
|
|
3418
|
+
if (!fs.existsSync(statePath))
|
|
3419
|
+
return {};
|
|
3420
|
+
return JSON.parse(fs.readFileSync(statePath, "utf-8"));
|
|
3421
|
+
}
|
|
3422
|
+
catch {
|
|
3423
|
+
return {};
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
writeAutoLearnState(state) {
|
|
3427
|
+
const dir = this.getAutoLearnDataDir();
|
|
3428
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
3429
|
+
fs.writeFileSync(this.getAutoLearnStatePath(), `${JSON.stringify(state, null, 2)}\n`, "utf-8");
|
|
3430
|
+
}
|
|
3431
|
+
isAutoLearnPidAlive(pid) {
|
|
3432
|
+
if (typeof pid !== "number" || pid <= 0)
|
|
3433
|
+
return false;
|
|
3434
|
+
try {
|
|
3435
|
+
process.kill(pid, 0);
|
|
3436
|
+
return true;
|
|
3437
|
+
}
|
|
3438
|
+
catch (error) {
|
|
3439
|
+
const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
|
|
3440
|
+
return code === "EPERM";
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
pruneAutoLearnState(state, now = Date.now()) {
|
|
3444
|
+
const runs = { ...(state.runs ?? {}) };
|
|
3445
|
+
for (const [id, run] of Object.entries(runs)) {
|
|
3446
|
+
if (run.expiresAt <= now || !this.isAutoLearnPidAlive(run.pid)) {
|
|
3447
|
+
delete runs[id];
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
return { ...state, runs };
|
|
3451
|
+
}
|
|
3452
|
+
getAutoLearnPresetForAutonomyMode(mode, current = {}) {
|
|
3453
|
+
const preset = AUTONOMY_AUTO_LEARN_PRESETS[mode] ?? AUTONOMY_AUTO_LEARN_PRESETS.off;
|
|
3454
|
+
return { ...preset, model: current.model?.trim() || preset.model };
|
|
3455
|
+
}
|
|
3456
|
+
getEffectiveAutoLearnSettings() {
|
|
3457
|
+
const settings = this.settingsManager.getAutoLearnSettings();
|
|
3458
|
+
const preset = this.getAutoLearnPresetForAutonomyMode(this.settingsManager.getAutonomySettings().mode, settings);
|
|
3459
|
+
return {
|
|
3460
|
+
enabled: settings.enabled ?? preset.enabled,
|
|
3461
|
+
model: settings.model?.trim() || preset.model,
|
|
3462
|
+
longSessionMessages: settings.longSessionMessages ?? preset.longSessionMessages,
|
|
3463
|
+
longSessionContextPercent: settings.longSessionContextPercent ?? preset.longSessionContextPercent,
|
|
3464
|
+
cooldownMinutes: settings.cooldownMinutes ?? preset.cooldownMinutes,
|
|
3465
|
+
leaseMinutes: settings.leaseMinutes ?? preset.leaseMinutes,
|
|
3466
|
+
maxConcurrentLearners: settings.maxConcurrentLearners ?? preset.maxConcurrentLearners,
|
|
3467
|
+
applyHighConfidence: settings.applyHighConfidence ?? preset.applyHighConfidence,
|
|
3468
|
+
reflectionReview: settings.reflectionReview ?? preset.reflectionReview,
|
|
3469
|
+
reflectionMinToolCalls: settings.reflectionMinToolCalls ?? preset.reflectionMinToolCalls,
|
|
3470
|
+
reflectionCooldownMinutes: settings.reflectionCooldownMinutes ?? preset.reflectionCooldownMinutes,
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
getAutoLearnTenantKey() {
|
|
3474
|
+
return `${this.sessionManager.getCwd()}::${this.session.sessionId}`;
|
|
3475
|
+
}
|
|
3476
|
+
getAutoLearnMessageCount() {
|
|
3477
|
+
return this.sessionManager.getBranch().filter((entry) => entry.type === "message").length;
|
|
3478
|
+
}
|
|
3479
|
+
resolveAutoLearnModelPattern(settings) {
|
|
3480
|
+
if (settings.model === "active") {
|
|
3481
|
+
return this.session.model ? `${this.session.model.provider}/${this.session.model.id}` : undefined;
|
|
3482
|
+
}
|
|
3483
|
+
return settings.model;
|
|
3484
|
+
}
|
|
3485
|
+
getAutoLearnSpawnTarget() {
|
|
3486
|
+
const overridePath = process.env.PI_AUTO_LEARN_CLI_PATH?.trim();
|
|
3487
|
+
if (overridePath) {
|
|
3488
|
+
return { command: overridePath, argsPrefix: [] };
|
|
3489
|
+
}
|
|
3490
|
+
const execBase = path.basename(process.execPath).toLowerCase();
|
|
3491
|
+
const isScriptRuntime = execBase === "node" || execBase === "node.exe" || execBase === "bun" || execBase === "bun.exe";
|
|
3492
|
+
if (!isScriptRuntime) {
|
|
3493
|
+
return { command: process.execPath, argsPrefix: [] };
|
|
3494
|
+
}
|
|
3495
|
+
const cliPath = process.argv[1];
|
|
3496
|
+
if (!cliPath || cliPath.startsWith("-")) {
|
|
3497
|
+
return undefined;
|
|
3498
|
+
}
|
|
3499
|
+
return { command: process.execPath, argsPrefix: [cliPath] };
|
|
3500
|
+
}
|
|
3501
|
+
validateAutoLearnModelValue(value) {
|
|
3502
|
+
const modelValue = value?.trim();
|
|
3503
|
+
if (!modelValue || modelValue === "active")
|
|
3504
|
+
return undefined;
|
|
3505
|
+
const available = this.session.modelRegistry.getAvailable();
|
|
3506
|
+
if (modelValue.includes("/")) {
|
|
3507
|
+
const [provider, modelId] = modelValue.split("/", 2);
|
|
3508
|
+
if (available.some((model) => model.provider === provider && model.id === modelId))
|
|
3509
|
+
return undefined;
|
|
3510
|
+
return `Auto Learn model "${modelValue}" is not in configured subscription/API models; saved as manual/unverified.`;
|
|
3511
|
+
}
|
|
3512
|
+
if (available.some((model) => model.id === modelValue))
|
|
3513
|
+
return undefined;
|
|
3514
|
+
return `Auto Learn model "${modelValue}" is not in configured subscription/API models; saved as manual/unverified.`;
|
|
3515
|
+
}
|
|
3516
|
+
validateSelfModificationSource(settings) {
|
|
3517
|
+
if (!settings.enabled)
|
|
3518
|
+
return undefined;
|
|
3519
|
+
const rawPath = settings.sourcePath?.trim();
|
|
3520
|
+
if (!rawPath)
|
|
3521
|
+
return "Self modification is enabled, but no pi-adaptative source path is set.";
|
|
3522
|
+
const sourcePath = resolvePath(rawPath, this.sessionManager.getCwd(), { trim: true });
|
|
3523
|
+
if (!fs.existsSync(sourcePath))
|
|
3524
|
+
return `Self modification source path does not exist: ${sourcePath}`;
|
|
3525
|
+
if (!fs.existsSync(path.join(sourcePath, "package.json"))) {
|
|
3526
|
+
return `Self modification source path has no package.json: ${sourcePath}`;
|
|
3527
|
+
}
|
|
3528
|
+
if (!fs.existsSync(path.join(sourcePath, "packages", "coding-agent"))) {
|
|
3529
|
+
return `Self modification source path does not look like pi-adaptative (missing packages/coding-agent): ${sourcePath}`;
|
|
3530
|
+
}
|
|
3531
|
+
return undefined;
|
|
3532
|
+
}
|
|
3533
|
+
evaluateAutoLearn(force = false) {
|
|
3534
|
+
const settings = this.getEffectiveAutoLearnSettings();
|
|
3535
|
+
const state = this.pruneAutoLearnState(this.readAutoLearnState());
|
|
3536
|
+
this.writeAutoLearnState(state);
|
|
3537
|
+
const now = Date.now();
|
|
3538
|
+
const tenant = this.getAutoLearnTenantKey();
|
|
3539
|
+
const runningCount = Object.keys(state.runs ?? {}).length;
|
|
3540
|
+
const lastLaunch = state.lastLaunchByTenant?.[tenant] ?? 0;
|
|
3541
|
+
const cooldownMs = settings.cooldownMinutes * 60 * 1000;
|
|
3542
|
+
const cooldownRemainingMs = Math.max(0, lastLaunch + cooldownMs - now);
|
|
3543
|
+
const messageCount = this.getAutoLearnMessageCount();
|
|
3544
|
+
const contextPercent = this.session.getContextUsage()?.percent ?? null;
|
|
3545
|
+
if (!settings.enabled && !force) {
|
|
3546
|
+
return {
|
|
3547
|
+
shouldRun: false,
|
|
3548
|
+
reason: "disabled",
|
|
3549
|
+
messageCount,
|
|
3550
|
+
contextPercent,
|
|
3551
|
+
cooldownRemainingMs,
|
|
3552
|
+
runningCount,
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
if (runningCount >= settings.maxConcurrentLearners) {
|
|
3556
|
+
return {
|
|
3557
|
+
shouldRun: false,
|
|
3558
|
+
reason: `max learners running (${runningCount}/${settings.maxConcurrentLearners})`,
|
|
3559
|
+
messageCount,
|
|
3560
|
+
contextPercent,
|
|
3561
|
+
cooldownRemainingMs,
|
|
3562
|
+
runningCount,
|
|
3563
|
+
};
|
|
3564
|
+
}
|
|
3565
|
+
if (!force && cooldownRemainingMs > 0) {
|
|
3566
|
+
return {
|
|
3567
|
+
shouldRun: false,
|
|
3568
|
+
reason: "cooldown",
|
|
3569
|
+
messageCount,
|
|
3570
|
+
contextPercent,
|
|
3571
|
+
cooldownRemainingMs,
|
|
3572
|
+
runningCount,
|
|
3573
|
+
};
|
|
3574
|
+
}
|
|
3575
|
+
if (force) {
|
|
3576
|
+
return { shouldRun: true, reason: "manual", messageCount, contextPercent, cooldownRemainingMs, runningCount };
|
|
3577
|
+
}
|
|
3578
|
+
if (messageCount >= settings.longSessionMessages) {
|
|
3579
|
+
return {
|
|
3580
|
+
shouldRun: true,
|
|
3581
|
+
reason: `message trigger (${messageCount}/${settings.longSessionMessages})`,
|
|
3582
|
+
messageCount,
|
|
3583
|
+
contextPercent,
|
|
3584
|
+
cooldownRemainingMs,
|
|
3585
|
+
runningCount,
|
|
3586
|
+
};
|
|
3587
|
+
}
|
|
3588
|
+
if (contextPercent !== null && contextPercent >= settings.longSessionContextPercent) {
|
|
3589
|
+
return {
|
|
3590
|
+
shouldRun: true,
|
|
3591
|
+
reason: `context trigger (${contextPercent.toFixed(1)}%/${settings.longSessionContextPercent}%)`,
|
|
3592
|
+
messageCount,
|
|
3593
|
+
contextPercent,
|
|
3594
|
+
cooldownRemainingMs,
|
|
3595
|
+
runningCount,
|
|
3596
|
+
};
|
|
3597
|
+
}
|
|
3598
|
+
return {
|
|
3599
|
+
shouldRun: false,
|
|
3600
|
+
reason: "thresholds not met",
|
|
3601
|
+
messageCount,
|
|
3602
|
+
contextPercent,
|
|
3603
|
+
cooldownRemainingMs,
|
|
3604
|
+
runningCount,
|
|
3605
|
+
};
|
|
3606
|
+
}
|
|
3607
|
+
buildAutonomyAuthorityPrompt() {
|
|
3608
|
+
const autonomy = this.settingsManager.getAutonomySettings();
|
|
3609
|
+
const selfModification = this.settingsManager.getSelfModificationSettings();
|
|
3610
|
+
if (autonomy.mode !== "full") {
|
|
3611
|
+
return [
|
|
3612
|
+
"Authority mode: proposal-gated.",
|
|
3613
|
+
"- You may autonomously query memory and run bounded learning tools.",
|
|
3614
|
+
"- Durable memory writes require the configured high-confidence policy; skills, extensions, source, settings, publishing, tagging, and releases remain proposals unless the foreground user explicitly asks.",
|
|
3615
|
+
].join("\n");
|
|
3616
|
+
}
|
|
3617
|
+
const sourceAuthority = selfModification.enabled && selfModification.sourcePath?.trim()
|
|
3618
|
+
? `- Pi source: standing authority to edit the authorized selfModification.sourcePath (${selfModification.sourcePath.trim()}) for self-evolution improvements; inspect git status first, preserve unrelated user changes, run focused validation, and leave a clear rollback/test summary.`
|
|
3619
|
+
: "- Pi source: no standing source-edit authority until selfModification.enabled and selfModification.sourcePath are set.";
|
|
3620
|
+
return [
|
|
3621
|
+
"Authority mode: FULL AUTONOMOUS standing grant.",
|
|
3622
|
+
"- Memory: may apply high-confidence durable Automata memory/corrections after duplicate and corroboration checks.",
|
|
3623
|
+
"- Skills: may create or patch user/project-owned procedural skills and support files; prefer updating existing umbrella skills; do not delete skills without making a recoverable archive/backup.",
|
|
3624
|
+
"- User/project extensions and tools: may create or patch small scoped extensions/tools under Pi user/project roots when they address repeated workflow/tooling gaps; validate and keep changes auditable.",
|
|
3625
|
+
"- Settings: may auto-tune autonomy/autoLearn settings to reduce bottlenecks; do not modify credentials, provider auth, package sources, or unrelated user preferences.",
|
|
3626
|
+
sourceAuthority,
|
|
3627
|
+
"- Hard stops without explicit foreground approval: publish, npm release, git push, tag creation, credential changes, destructive user-data deletion, network-exposed services, or authority expansion beyond this policy.",
|
|
3628
|
+
"- Audit: final output must list changed paths/settings, commands/tests run, evidence, residual risks, and rollback guidance. If no safe validation is possible, leave the change as a proposal instead of applying it.",
|
|
3629
|
+
].join("\n");
|
|
3630
|
+
}
|
|
3631
|
+
buildAutoLearnPrompt(reason, settings, options = {}) {
|
|
3632
|
+
const authorityBlock = this.buildAutonomyAuthorityPrompt();
|
|
3633
|
+
const reflectionBlock = options.kind === "reflection" && options.turnDigest
|
|
3634
|
+
? `\n\nLatest completed turn digest (bounded; use only as current-session evidence, not as longitudinal proof):\n<turn_digest>\n${options.turnDigest}\n</turn_digest>`
|
|
3635
|
+
: "";
|
|
3636
|
+
const objective = options.kind === "reflection"
|
|
3637
|
+
? "review the latest completed turn for durable memory, skill, validation, and tooling-improvement cues, then run one bounded continuous-learning pass if the learning tools are available"
|
|
3638
|
+
: "run one bounded continuous-learning pass for this Pi tenant";
|
|
3639
|
+
return `You are Pi Auto Learn running as a background learner.\n\nObjective: ${objective}.\nTrigger: ${reason}.\n\n${authorityBlock}\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. Treat the latest-turn digest as current-session evidence only; do not auto-commit one-off cues unless deterministic tooling corroborates them.\n4. In mode=full, apply safe memory/skill/user-extension/authorized-source improvements under the standing grant above; otherwise keep them proposal-gated.\n5. Never cross hard-stop boundaries from the authority policy.\n6. If the learning tools are unavailable, report BLOCKED with the missing tool names and do not improvise.\n7. Finish with PASS, BLOCKED, or FAIL and concise evidence.${reflectionBlock}`;
|
|
3640
|
+
}
|
|
3641
|
+
launchAutoLearn(reason, force = false, options = {}) {
|
|
3642
|
+
const settings = this.getEffectiveAutoLearnSettings();
|
|
3643
|
+
const decision = this.evaluateAutoLearn(force);
|
|
3644
|
+
if (!decision.shouldRun) {
|
|
3645
|
+
return `Auto Learn not started: ${decision.reason}`;
|
|
3646
|
+
}
|
|
3647
|
+
const modelPattern = this.resolveAutoLearnModelPattern(settings);
|
|
3648
|
+
if (!modelPattern) {
|
|
3649
|
+
return "Auto Learn not started: no active model is available for model=active.";
|
|
3650
|
+
}
|
|
3651
|
+
const spawnTarget = this.getAutoLearnSpawnTarget();
|
|
3652
|
+
if (!spawnTarget) {
|
|
3653
|
+
return "Auto Learn not started: could not resolve current pi CLI path.";
|
|
3654
|
+
}
|
|
3655
|
+
const dir = this.getAutoLearnDataDir();
|
|
3656
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
3657
|
+
const runId = `${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
3658
|
+
const logPath = path.join(dir, `${runId}.log`);
|
|
3659
|
+
const promptPath = path.join(dir, `${runId}.prompt.md`);
|
|
3660
|
+
const outFd = fs.openSync(logPath, "a");
|
|
3661
|
+
const kind = options.promptKind ?? "auto";
|
|
3662
|
+
const prompt = this.buildAutoLearnPrompt(reason, settings, {
|
|
3663
|
+
kind,
|
|
3664
|
+
turnDigest: options.turnDigest,
|
|
3665
|
+
});
|
|
3666
|
+
fs.writeFileSync(promptPath, prompt, "utf-8");
|
|
3667
|
+
const args = [
|
|
3668
|
+
...spawnTarget.argsPrefix,
|
|
3669
|
+
"--print",
|
|
3670
|
+
"--name",
|
|
3671
|
+
`Auto Learn ${runId}`,
|
|
3672
|
+
"--model",
|
|
3673
|
+
modelPattern,
|
|
3674
|
+
prompt,
|
|
3675
|
+
];
|
|
3676
|
+
const child = spawn(spawnTarget.command, args, {
|
|
3677
|
+
cwd: this.sessionManager.getCwd(),
|
|
3678
|
+
detached: true,
|
|
3679
|
+
stdio: ["ignore", outFd, outFd],
|
|
3680
|
+
env: { ...process.env, PI_AUTO_LEARN_CHILD: "1" },
|
|
3681
|
+
});
|
|
3682
|
+
child.unref();
|
|
3683
|
+
fs.closeSync(outFd);
|
|
3684
|
+
const now = Date.now();
|
|
3685
|
+
const state = this.pruneAutoLearnState(this.readAutoLearnState(), now);
|
|
3686
|
+
if (options.cooldownKind === "reflection") {
|
|
3687
|
+
state.lastReflectionByTenant = {
|
|
3688
|
+
...(state.lastReflectionByTenant ?? {}),
|
|
3689
|
+
[this.getAutoLearnTenantKey()]: now,
|
|
3690
|
+
};
|
|
3691
|
+
}
|
|
3692
|
+
else {
|
|
3693
|
+
state.lastLaunchByTenant = { ...(state.lastLaunchByTenant ?? {}), [this.getAutoLearnTenantKey()]: now };
|
|
3694
|
+
}
|
|
3695
|
+
state.runs = {
|
|
3696
|
+
...(state.runs ?? {}),
|
|
3697
|
+
[runId]: {
|
|
3698
|
+
tenant: this.getAutoLearnTenantKey(),
|
|
3699
|
+
pid: child.pid,
|
|
3700
|
+
model: modelPattern,
|
|
3701
|
+
reason,
|
|
3702
|
+
startedAt: now,
|
|
3703
|
+
expiresAt: now + settings.leaseMinutes * 60 * 1000,
|
|
3704
|
+
cwd: this.sessionManager.getCwd(),
|
|
3705
|
+
logPath,
|
|
3706
|
+
promptPath,
|
|
3707
|
+
kind,
|
|
3708
|
+
autonomyMode: this.settingsManager.getAutonomySettings().mode,
|
|
3709
|
+
authority: this.settingsManager.getAutonomySettings().mode === "full"
|
|
3710
|
+
? "standing-full-autonomous"
|
|
3711
|
+
: "proposal-gated",
|
|
3712
|
+
},
|
|
3713
|
+
};
|
|
3714
|
+
this.writeAutoLearnState(state);
|
|
3715
|
+
this.autoLearnLastStatus = `running ${modelPattern}`;
|
|
3716
|
+
this.updateAutoLearnFooter();
|
|
3717
|
+
return `Auto Learn started (${reason}) with ${modelPattern}. Log: ${logPath}`;
|
|
3718
|
+
}
|
|
3719
|
+
sanitizeAutoLearnDigestText(text) {
|
|
3720
|
+
return text
|
|
3721
|
+
.replace(/-----BEGIN [A-Z ]*(?:PRIVATE|OPENSSH|RSA|DSA|EC) KEY-----[\s\S]*?-----END [A-Z ]*(?:PRIVATE|OPENSSH|RSA|DSA|EC) KEY-----/g, "[redacted-private-key]")
|
|
3722
|
+
.replace(/\b(?:sk|pk)-(?:proj-)?[A-Za-z0-9_-]{12,}/g, "[redacted-api-key]")
|
|
3723
|
+
.replace(/\bsk-ant-[A-Za-z0-9_-]{12,}/g, "[redacted-api-key]")
|
|
3724
|
+
.replace(/\b(?:ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_]{20,}/g, "[redacted-github-token]")
|
|
3725
|
+
.replace(/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, "[redacted-aws-access-key]")
|
|
3726
|
+
.replace(/(?:Bearer\s+)[A-Za-z0-9._-]{16,}/gi, "Bearer [redacted]")
|
|
3727
|
+
.replace(/([?&](?:key|token|api_key|access_token|secret|password)=)[^&\s]+/gi, "$1[redacted]")
|
|
3728
|
+
.replace(/((?:access|refresh|token|apiKey|api_key|password|secret|authorization|auth)\s*[:=]\s*)[^\s,'"}]{8,}/gi, "$1[redacted]");
|
|
3729
|
+
}
|
|
3730
|
+
capAutoLearnDigestText(text, maxChars) {
|
|
3731
|
+
const compact = this.sanitizeAutoLearnDigestText(text).replace(/\s+/g, " ").trim();
|
|
3732
|
+
if (compact.length <= maxChars)
|
|
3733
|
+
return compact;
|
|
3734
|
+
return `${compact.slice(0, Math.max(0, maxChars - 20)).trimEnd()} …[truncated]`;
|
|
3735
|
+
}
|
|
3736
|
+
getAgentMessagePlainText(message) {
|
|
3737
|
+
const raw = message;
|
|
3738
|
+
const content = raw.content;
|
|
3739
|
+
if (typeof content === "string")
|
|
3740
|
+
return content;
|
|
3741
|
+
if (!Array.isArray(content))
|
|
3742
|
+
return "";
|
|
3743
|
+
const parts = [];
|
|
3744
|
+
for (const block of content) {
|
|
3745
|
+
if (!block || typeof block !== "object")
|
|
3746
|
+
continue;
|
|
3747
|
+
const item = block;
|
|
3748
|
+
if (item.type === "text" && typeof item.text === "string")
|
|
3749
|
+
parts.push(item.text);
|
|
3750
|
+
if (item.type === "toolCall" && typeof item.name === "string")
|
|
3751
|
+
parts.push(`[tool call: ${item.name}]`);
|
|
3752
|
+
}
|
|
3753
|
+
return parts.join("\n");
|
|
3754
|
+
}
|
|
3755
|
+
countAgentToolCalls(messages) {
|
|
3756
|
+
let toolCalls = 0;
|
|
3757
|
+
let toolResults = 0;
|
|
3758
|
+
for (const message of messages) {
|
|
3759
|
+
const raw = message;
|
|
3760
|
+
const role = String(raw.role ?? "");
|
|
3761
|
+
if (role === "toolResult" || role === "bashExecution")
|
|
3762
|
+
toolResults++;
|
|
3763
|
+
const content = raw.content;
|
|
3764
|
+
if (!Array.isArray(content))
|
|
3765
|
+
continue;
|
|
3766
|
+
for (const block of content) {
|
|
3767
|
+
if (block && typeof block === "object" && block.type === "toolCall") {
|
|
3768
|
+
toolCalls++;
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
return Math.max(toolCalls, toolResults);
|
|
3773
|
+
}
|
|
3774
|
+
buildAutonomyReviewDigest(messages) {
|
|
3775
|
+
const lines = [];
|
|
3776
|
+
for (const message of messages.slice(-18)) {
|
|
3777
|
+
const raw = message;
|
|
3778
|
+
const role = String(raw.role ?? "message");
|
|
3779
|
+
const label = role === "toolResult" && typeof raw.toolName === "string" ? `toolResult:${raw.toolName}` : role;
|
|
3780
|
+
const text = this.capAutoLearnDigestText(this.getAgentMessagePlainText(message), 700);
|
|
3781
|
+
if (text)
|
|
3782
|
+
lines.push(`${label}: ${text}`);
|
|
3783
|
+
}
|
|
3784
|
+
const digest = lines.join("\n---\n");
|
|
3785
|
+
return this.capAutoLearnDigestText(digest || "[No textual turn digest available.]", 6000);
|
|
3786
|
+
}
|
|
3787
|
+
evaluateAutonomyReview(messages) {
|
|
3788
|
+
const settings = this.getEffectiveAutoLearnSettings();
|
|
3789
|
+
const autonomy = this.settingsManager.getAutonomySettings();
|
|
3790
|
+
const state = this.pruneAutoLearnState(this.readAutoLearnState());
|
|
3791
|
+
this.writeAutoLearnState(state);
|
|
3792
|
+
const now = Date.now();
|
|
3793
|
+
const tenant = this.getAutoLearnTenantKey();
|
|
3794
|
+
const runningCount = Object.keys(state.runs ?? {}).length;
|
|
3795
|
+
const lastReflection = state.lastReflectionByTenant?.[tenant] ?? 0;
|
|
3796
|
+
const cooldownMs = settings.reflectionCooldownMinutes * 60 * 1000;
|
|
3797
|
+
const cooldownRemainingMs = Math.max(0, lastReflection + cooldownMs - now);
|
|
3798
|
+
const messageCount = this.getAutoLearnMessageCount();
|
|
3799
|
+
const contextPercent = this.session.getContextUsage()?.percent ?? null;
|
|
3800
|
+
const toolCalls = this.countAgentToolCalls(messages);
|
|
3801
|
+
const userText = messages
|
|
3802
|
+
.filter((message) => String(message.role ?? "") === "user")
|
|
3803
|
+
.map((message) => this.getAgentMessagePlainText(message))
|
|
3804
|
+
.join("\n");
|
|
3805
|
+
const correctionSignal = /\b(next time|for future|from now on|remember this|don't|do not|avoid|instead|you should|should have|you forgot|you missed|not what i asked|wrong again)\b/i.test(userText);
|
|
3806
|
+
const base = { messageCount, contextPercent, cooldownRemainingMs, runningCount, toolCalls };
|
|
3807
|
+
if (!settings.enabled)
|
|
3808
|
+
return { ...base, shouldRun: false, reason: "disabled" };
|
|
3809
|
+
if (!settings.reflectionReview)
|
|
3810
|
+
return { ...base, shouldRun: false, reason: "reflection disabled" };
|
|
3811
|
+
if (runningCount >= settings.maxConcurrentLearners) {
|
|
3812
|
+
return {
|
|
3813
|
+
...base,
|
|
3814
|
+
shouldRun: false,
|
|
3815
|
+
reason: `max learners running (${runningCount}/${settings.maxConcurrentLearners})`,
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
if (cooldownRemainingMs > 0)
|
|
3819
|
+
return { ...base, shouldRun: false, reason: "reflection cooldown" };
|
|
3820
|
+
if (correctionSignal) {
|
|
3821
|
+
return {
|
|
3822
|
+
...base,
|
|
3823
|
+
shouldRun: true,
|
|
3824
|
+
reason: "reflection correction signal",
|
|
3825
|
+
digest: this.buildAutonomyReviewDigest(messages),
|
|
3826
|
+
};
|
|
3827
|
+
}
|
|
3828
|
+
if (autonomy.mode === "full") {
|
|
3829
|
+
return {
|
|
3830
|
+
...base,
|
|
3831
|
+
shouldRun: true,
|
|
3832
|
+
reason: "full autonomy post-turn review",
|
|
3833
|
+
digest: this.buildAutonomyReviewDigest(messages),
|
|
3834
|
+
};
|
|
3835
|
+
}
|
|
3836
|
+
if (toolCalls >= settings.reflectionMinToolCalls) {
|
|
3837
|
+
return {
|
|
3838
|
+
...base,
|
|
3839
|
+
shouldRun: true,
|
|
3840
|
+
reason: `reflection tool trigger (${toolCalls}/${settings.reflectionMinToolCalls})`,
|
|
3841
|
+
digest: this.buildAutonomyReviewDigest(messages),
|
|
3842
|
+
};
|
|
3843
|
+
}
|
|
3844
|
+
return { ...base, shouldRun: false, reason: "reflection thresholds not met" };
|
|
3845
|
+
}
|
|
3846
|
+
maybeStartAutoLearn() {
|
|
3847
|
+
if (process.env.PI_AUTO_LEARN_CHILD === "1")
|
|
3848
|
+
return false;
|
|
3849
|
+
const decision = this.evaluateAutoLearn(false);
|
|
3850
|
+
if (!decision.shouldRun) {
|
|
3851
|
+
this.autoLearnLastStatus = decision.reason;
|
|
3852
|
+
this.updateAutoLearnFooter();
|
|
3853
|
+
return false;
|
|
3854
|
+
}
|
|
3855
|
+
const message = this.launchAutoLearn(decision.reason, false);
|
|
3856
|
+
this.showStatus(message);
|
|
3857
|
+
return message.startsWith("Auto Learn started");
|
|
3858
|
+
}
|
|
3859
|
+
maybeStartAutonomyReview(messages) {
|
|
3860
|
+
if (process.env.PI_AUTO_LEARN_CHILD === "1")
|
|
3861
|
+
return false;
|
|
3862
|
+
const decision = this.evaluateAutonomyReview(messages);
|
|
3863
|
+
if (!decision.shouldRun)
|
|
3864
|
+
return false;
|
|
3865
|
+
const message = this.launchAutoLearn(decision.reason, true, {
|
|
3866
|
+
cooldownKind: "reflection",
|
|
3867
|
+
promptKind: "reflection",
|
|
3868
|
+
turnDigest: decision.digest,
|
|
3869
|
+
});
|
|
3870
|
+
this.showStatus(message);
|
|
3871
|
+
return message.startsWith("Auto Learn started");
|
|
3872
|
+
}
|
|
3873
|
+
updateAutoLearnFooter() {
|
|
3874
|
+
const settings = this.getEffectiveAutoLearnSettings();
|
|
3875
|
+
if (!settings.enabled) {
|
|
3876
|
+
this.footerDataProvider.setExtensionStatus("auto-learn", undefined);
|
|
3877
|
+
return;
|
|
3878
|
+
}
|
|
3879
|
+
this.footerDataProvider.setExtensionStatus("auto-learn", theme.fg("accent", `learn: ${this.autoLearnLastStatus}`));
|
|
3880
|
+
this.footer.invalidate();
|
|
3881
|
+
this.ui.requestRender();
|
|
3882
|
+
}
|
|
3883
|
+
formatAutoLearnStatus() {
|
|
3884
|
+
const settings = this.getEffectiveAutoLearnSettings();
|
|
3885
|
+
const decision = this.evaluateAutoLearn(false);
|
|
3886
|
+
const state = this.pruneAutoLearnState(this.readAutoLearnState());
|
|
3887
|
+
const runs = Object.entries(state.runs ?? {});
|
|
3888
|
+
const contextText = decision.contextPercent === null ? "unknown" : `${decision.contextPercent.toFixed(1)}%`;
|
|
3889
|
+
const cooldownText = decision.cooldownRemainingMs > 0 ? `${Math.ceil(decision.cooldownRemainingMs / 60000)}m remaining` : "ready";
|
|
3890
|
+
const runLines = runs.length
|
|
3891
|
+
? runs
|
|
3892
|
+
.map(([id, run]) => `- ${id}: ${run.model}, kind=${run.kind ?? "auto"}, authority=${run.authority ?? "unknown"}, pid=${run.pid ?? "?"}, log=${run.logPath}`)
|
|
3893
|
+
.join("\n")
|
|
3894
|
+
: "- none";
|
|
3895
|
+
const reflectionLast = state.lastReflectionByTenant?.[this.getAutoLearnTenantKey()] ?? 0;
|
|
3896
|
+
const reflectionCooldownRemainingMs = Math.max(0, reflectionLast + settings.reflectionCooldownMinutes * 60 * 1000 - Date.now());
|
|
3897
|
+
const reflectionCooldownText = reflectionCooldownRemainingMs > 0 ? `${Math.ceil(reflectionCooldownRemainingMs / 60000)}m remaining` : "ready";
|
|
3898
|
+
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}\nReflection review: ${settings.reflectionReview ? "enabled" : "disabled"} (tool trigger ${settings.reflectionMinToolCalls}, cooldown ${reflectionCooldownText})\nRunning leases: ${runs.length}/${settings.maxConcurrentLearners}\nRuns:\n${runLines}`;
|
|
3899
|
+
}
|
|
3900
|
+
formatAutonomyStatus() {
|
|
3901
|
+
const autonomy = this.settingsManager.getAutonomySettings();
|
|
3902
|
+
const settings = this.getEffectiveAutoLearnSettings();
|
|
3903
|
+
const autoLearnState = this.pruneAutoLearnState(this.readAutoLearnState());
|
|
3904
|
+
const running = Object.entries(autoLearnState.runs ?? {});
|
|
3905
|
+
const safety = autonomy.mode === "full"
|
|
3906
|
+
? "standing grant for memory, skills, user/project extensions, autonomy/autoLearn tuning, and authorized selfModification.sourcePath edits; hard stops still require explicit foreground approval"
|
|
3907
|
+
: "proposal-gated outside configured high-confidence memory policy";
|
|
3908
|
+
const reflectionLine = autonomy.mode === "full"
|
|
3909
|
+
? `Reflection review: ${settings.reflectionReview ? "enabled" : "disabled"}; post-turn when concurrency allows; cooldown=${settings.reflectionCooldownMinutes}m`
|
|
3910
|
+
: `Reflection review: ${settings.reflectionReview ? "enabled" : "disabled"}; tool trigger=${settings.reflectionMinToolCalls}; cooldown=${settings.reflectionCooldownMinutes}m`;
|
|
3911
|
+
return [
|
|
3912
|
+
"Autonomy status",
|
|
3913
|
+
`Mode: ${autonomy.mode}${autonomy.mode === "full" ? " (standing autonomy)" : ""}`,
|
|
3914
|
+
`Auto Learn: ${settings.enabled ? "enabled" : "disabled"}; model=${settings.model}; applyHighConfidence=${settings.applyHighConfidence}`,
|
|
3915
|
+
`Long-session trigger: ${settings.longSessionMessages} messages or ${settings.longSessionContextPercent}% context; cooldown=${settings.cooldownMinutes}m`,
|
|
3916
|
+
reflectionLine,
|
|
3917
|
+
`Running learners: ${running.length}/${settings.maxConcurrentLearners}`,
|
|
3918
|
+
`Standing authority: ${safety}`,
|
|
3919
|
+
`Audit/log dir: ${this.getAutoLearnDataDir()}`,
|
|
3920
|
+
"Use /autonomy off|safe|balanced|full to switch presets. Advanced overrides remain in /settings → Auto Learn Advanced.",
|
|
3921
|
+
].join("\n");
|
|
3922
|
+
}
|
|
3923
|
+
applyAutonomyMode(mode, scope = "global") {
|
|
3924
|
+
const currentAutoLearn = this.settingsManager.getAutoLearnSettings();
|
|
3925
|
+
const preset = this.getAutoLearnPresetForAutonomyMode(mode, currentAutoLearn);
|
|
3926
|
+
this.settingsManager.setAutonomySettings({ mode }, scope);
|
|
3927
|
+
this.settingsManager.setAutoLearnSettings(preset, scope);
|
|
3928
|
+
this.updateAutoLearnFooter();
|
|
3929
|
+
}
|
|
3930
|
+
handleAutonomyCommand(text) {
|
|
3931
|
+
const action = text.slice("/autonomy".length).trim() || "status";
|
|
3932
|
+
if (AUTONOMY_MODES.includes(action)) {
|
|
3933
|
+
const mode = action;
|
|
3934
|
+
this.applyAutonomyMode(mode);
|
|
3935
|
+
this.showStatus(`Autonomy mode set to ${mode}${mode === "full" ? " (standing autonomy)" : ""}.`);
|
|
3936
|
+
return;
|
|
3937
|
+
}
|
|
3938
|
+
if (action === "status") {
|
|
3939
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3940
|
+
this.chatContainer.addChild(new Text(this.formatAutonomyStatus(), 1, 0));
|
|
3941
|
+
this.ui.requestRender();
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
this.showStatus("Usage: /autonomy [status|off|safe|balanced|full]");
|
|
3945
|
+
}
|
|
3946
|
+
handleAutoLearnCommand(text) {
|
|
3947
|
+
const action = text.slice("/auto-learn".length).trim() || "status";
|
|
3948
|
+
if (action === "run" || action === "now" || action === "run-now") {
|
|
3949
|
+
this.showStatus(this.launchAutoLearn("manual", true));
|
|
3950
|
+
return;
|
|
3951
|
+
}
|
|
3952
|
+
if (action === "status") {
|
|
3953
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3954
|
+
this.chatContainer.addChild(new Text(this.formatAutoLearnStatus(), 1, 0));
|
|
3955
|
+
this.ui.requestRender();
|
|
3956
|
+
return;
|
|
3957
|
+
}
|
|
3958
|
+
this.showStatus("Usage: /auto-learn [status|run]");
|
|
3959
|
+
}
|
|
3263
3960
|
showSettingsSelector() {
|
|
3264
3961
|
this.showSelector((done) => {
|
|
3962
|
+
const projectSettings = this.settingsManager.getProjectSettings();
|
|
3265
3963
|
const selector = new SettingsSelectorComponent({
|
|
3266
3964
|
autoCompact: this.session.autoCompactionEnabled,
|
|
3267
3965
|
showImages: this.settingsManager.getShowImages(),
|
|
@@ -3289,6 +3987,16 @@ export class InteractiveMode {
|
|
|
3289
3987
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
3290
3988
|
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
|
3291
3989
|
warnings: this.settingsManager.getWarnings(),
|
|
3990
|
+
selfModification: this.settingsManager.getSelfModificationSettings(),
|
|
3991
|
+
selfModificationScope: projectSettings.selfModification ? "project" : "global",
|
|
3992
|
+
autonomy: this.settingsManager.getAutonomySettings(),
|
|
3993
|
+
autonomyScope: projectSettings.autonomy ? "project" : "global",
|
|
3994
|
+
autoLearn: this.settingsManager.getAutoLearnSettings(),
|
|
3995
|
+
autoLearnScope: projectSettings.autoLearn ? "project" : "global",
|
|
3996
|
+
autoLearnModelOptions: this.getAutoLearnModelOptions(),
|
|
3997
|
+
currentModelPattern: this.session.model
|
|
3998
|
+
? `${this.session.model.provider}/${this.session.model.id}`
|
|
3999
|
+
: undefined,
|
|
3292
4000
|
}, {
|
|
3293
4001
|
onAutoCompactChange: (enabled) => {
|
|
3294
4002
|
this.session.setAutoCompactionEnabled(enabled);
|
|
@@ -3409,6 +4117,27 @@ export class InteractiveMode {
|
|
|
3409
4117
|
onWarningsChange: (warnings) => {
|
|
3410
4118
|
this.settingsManager.setWarnings(warnings);
|
|
3411
4119
|
},
|
|
4120
|
+
onSelfModificationChange: (settings, scope) => {
|
|
4121
|
+
this.settingsManager.setSelfModificationSettings(settings, scope);
|
|
4122
|
+
const validationMessage = this.validateSelfModificationSource(settings);
|
|
4123
|
+
if (validationMessage) {
|
|
4124
|
+
this.showWarning(validationMessage);
|
|
4125
|
+
}
|
|
4126
|
+
this.showStatus(`Self modification settings saved to ${scope}. Start a new session or /reload for system-prompt guardrails to fully refresh.`);
|
|
4127
|
+
},
|
|
4128
|
+
onAutonomyChange: (settings, scope) => {
|
|
4129
|
+
this.applyAutonomyMode(settings.mode ?? "off", scope);
|
|
4130
|
+
this.showStatus(`Autonomy mode ${settings.mode ?? "off"} saved to ${scope}. Use /autonomy status.`);
|
|
4131
|
+
},
|
|
4132
|
+
onAutoLearnChange: (settings, scope) => {
|
|
4133
|
+
this.settingsManager.setAutoLearnSettings(settings, scope);
|
|
4134
|
+
const validationMessage = this.validateAutoLearnModelValue(settings.model);
|
|
4135
|
+
if (validationMessage) {
|
|
4136
|
+
this.showWarning(validationMessage);
|
|
4137
|
+
}
|
|
4138
|
+
this.updateAutoLearnFooter();
|
|
4139
|
+
this.showStatus(`Auto Learn settings saved to ${scope}. Use /auto-learn status or /auto-learn run.`);
|
|
4140
|
+
},
|
|
3412
4141
|
onCancel: () => {
|
|
3413
4142
|
done();
|
|
3414
4143
|
this.ui.requestRender();
|