@caupulican/pi-adaptative 0.80.6 → 0.80.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/CHANGELOG.md +14 -0
- package/README.md +15 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/model-resolver.d.ts +7 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +85 -16
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/reload-blockers.d.ts +2 -0
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +8 -2
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +7 -3
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +3 -0
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +14 -8
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +154 -41
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/adaptive-extension-shared-state-audit.md +43 -0
- package/docs/providers.md +27 -2
- package/docs/settings.md +2 -2
- package/docs/skills.md +3 -3
- 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 +12 -12
- package/package.json +4 -4
|
@@ -18,7 +18,7 @@ import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
|
18
18
|
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
|
|
19
19
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
20
20
|
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
21
|
-
import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
|
|
21
|
+
import { cliProviderAliases, defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope, } from "../../core/model-resolver.js";
|
|
22
22
|
import { DefaultPackageManager } from "../../core/package-manager.js";
|
|
23
23
|
import { BUILT_IN_PROVIDER_DISPLAY_NAMES } from "../../core/provider-display-names.js";
|
|
24
24
|
import { getPendingReloadBlockers } from "../../core/reload-blockers.js";
|
|
@@ -158,6 +158,14 @@ function definedStringSet(values) {
|
|
|
158
158
|
}
|
|
159
159
|
return set;
|
|
160
160
|
}
|
|
161
|
+
function sanitizeAutoLearnPathPart(input, fallback) {
|
|
162
|
+
const cleaned = (input || fallback)
|
|
163
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "-")
|
|
164
|
+
.replace(/-+/g, "-")
|
|
165
|
+
.replace(/^-+|-+$/g, "")
|
|
166
|
+
.slice(0, 80);
|
|
167
|
+
return cleaned || fallback;
|
|
168
|
+
}
|
|
161
169
|
function isOldAutoLearnArtifact(filePath, now, retentionMs) {
|
|
162
170
|
const stats = fs.lstatSync(filePath);
|
|
163
171
|
return stats.isFile() && now - stats.mtimeMs > retentionMs;
|
|
@@ -171,6 +179,22 @@ function removeOldAutoLearnArtifact(filePath, result, counter) {
|
|
|
171
179
|
result.errors++;
|
|
172
180
|
}
|
|
173
181
|
}
|
|
182
|
+
function isPathInside(target, root) {
|
|
183
|
+
const resolvedTarget = path.resolve(target);
|
|
184
|
+
const resolvedRoot = path.resolve(root);
|
|
185
|
+
return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(`${resolvedRoot}${path.sep}`);
|
|
186
|
+
}
|
|
187
|
+
function removeAutoLearnArtifactPath(filePath, root) {
|
|
188
|
+
if (!isPathInside(filePath, root))
|
|
189
|
+
return false;
|
|
190
|
+
try {
|
|
191
|
+
fs.rmSync(filePath, { recursive: true, force: true });
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
174
198
|
function readAutoLearnSessionIdFromFile(filePath) {
|
|
175
199
|
let fd;
|
|
176
200
|
try {
|
|
@@ -232,24 +256,20 @@ function pruneAutoLearnSessionFiles(dir, activeSessionIds, now, retentionMs, res
|
|
|
232
256
|
removeOldAutoLearnArtifact(filePath, result, "sessionFiles");
|
|
233
257
|
}
|
|
234
258
|
}
|
|
235
|
-
|
|
236
|
-
const result = { promptFiles: 0, logFiles: 0, sessionFiles: 0, errors: 0 };
|
|
237
|
-
const dataDir = path.resolve(options.dataDir);
|
|
238
|
-
const now = options.now ?? Date.now();
|
|
239
|
-
const retentionMs = options.retentionMs ?? AUTO_LEARN_HISTORY_RETENTION_MS;
|
|
240
|
-
const activeRunIds = definedStringSet(options.activeRunIds);
|
|
241
|
-
const activeSessionIds = definedStringSet(options.activeSessionIds);
|
|
242
|
-
if (retentionMs <= 0 || !fs.existsSync(dataDir))
|
|
243
|
-
return result;
|
|
259
|
+
function pruneAutoLearnRunArtifacts(dir, activeRunIds, now, retentionMs, result) {
|
|
244
260
|
let entries;
|
|
245
261
|
try {
|
|
246
|
-
entries = fs.readdirSync(
|
|
262
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
247
263
|
}
|
|
248
264
|
catch {
|
|
249
|
-
|
|
250
|
-
return result;
|
|
265
|
+
return;
|
|
251
266
|
}
|
|
252
267
|
for (const entry of entries) {
|
|
268
|
+
const filePath = path.join(dir, entry.name);
|
|
269
|
+
if (entry.isDirectory()) {
|
|
270
|
+
pruneAutoLearnRunArtifacts(filePath, activeRunIds, now, retentionMs, result);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
253
273
|
if (!entry.isFile())
|
|
254
274
|
continue;
|
|
255
275
|
const promptRunId = entry.name.endsWith(".prompt.md") ? entry.name.slice(0, -".prompt.md".length) : undefined;
|
|
@@ -257,7 +277,6 @@ export function pruneAutoLearnConversationHistory(options) {
|
|
|
257
277
|
const runId = promptRunId ?? logRunId;
|
|
258
278
|
if (!runId || activeRunIds.has(runId))
|
|
259
279
|
continue;
|
|
260
|
-
const filePath = path.join(dataDir, entry.name);
|
|
261
280
|
let shouldPrune = false;
|
|
262
281
|
try {
|
|
263
282
|
shouldPrune = isOldAutoLearnArtifact(filePath, now, retentionMs);
|
|
@@ -270,7 +289,18 @@ export function pruneAutoLearnConversationHistory(options) {
|
|
|
270
289
|
continue;
|
|
271
290
|
removeOldAutoLearnArtifact(filePath, result, promptRunId ? "promptFiles" : "logFiles");
|
|
272
291
|
}
|
|
273
|
-
|
|
292
|
+
}
|
|
293
|
+
export function pruneAutoLearnConversationHistory(options) {
|
|
294
|
+
const result = { promptFiles: 0, logFiles: 0, sessionFiles: 0, errors: 0 };
|
|
295
|
+
const dataDir = path.resolve(options.dataDir);
|
|
296
|
+
const now = options.now ?? Date.now();
|
|
297
|
+
const retentionMs = options.retentionMs ?? AUTO_LEARN_HISTORY_RETENTION_MS;
|
|
298
|
+
const activeRunIds = definedStringSet(options.activeRunIds);
|
|
299
|
+
const activeSessionIds = definedStringSet(options.activeSessionIds);
|
|
300
|
+
if (retentionMs <= 0 || !fs.existsSync(dataDir))
|
|
301
|
+
return result;
|
|
302
|
+
pruneAutoLearnRunArtifacts(dataDir, activeRunIds, now, retentionMs, result);
|
|
303
|
+
pruneAutoLearnSessionFiles(dataDir, activeSessionIds, now, retentionMs, result);
|
|
274
304
|
return result;
|
|
275
305
|
}
|
|
276
306
|
export function buildAutoLearnSpawnArgs(spawnTarget, options) {
|
|
@@ -2389,13 +2419,13 @@ export class InteractiveMode {
|
|
|
2389
2419
|
this.editor.setText("");
|
|
2390
2420
|
return;
|
|
2391
2421
|
}
|
|
2392
|
-
if (text === "/login") {
|
|
2393
|
-
this.showOAuthSelector("login");
|
|
2422
|
+
if (text === "/login" || text.startsWith("/login ")) {
|
|
2423
|
+
await this.showOAuthSelector("login", text.slice("/login".length).trim() || undefined);
|
|
2394
2424
|
this.editor.setText("");
|
|
2395
2425
|
return;
|
|
2396
2426
|
}
|
|
2397
|
-
if (text === "/logout") {
|
|
2398
|
-
this.showOAuthSelector("logout");
|
|
2427
|
+
if (text === "/logout" || text.startsWith("/logout ")) {
|
|
2428
|
+
await this.showOAuthSelector("logout", text.slice("/logout".length).trim() || undefined);
|
|
2399
2429
|
this.editor.setText("");
|
|
2400
2430
|
return;
|
|
2401
2431
|
}
|
|
@@ -3759,13 +3789,21 @@ export class InteractiveMode {
|
|
|
3759
3789
|
getAutoLearnTenantKey() {
|
|
3760
3790
|
return `${this.sessionManager.getCwd()}::${this.session.sessionId}`;
|
|
3761
3791
|
}
|
|
3792
|
+
getAutoLearnTenantId() {
|
|
3793
|
+
const cwdHash = crypto.createHash("sha256").update(this.sessionManager.getCwd()).digest("hex").slice(0, 8);
|
|
3794
|
+
const sessionPart = sanitizeAutoLearnPathPart(this.session.sessionId, "session");
|
|
3795
|
+
return `${sessionPart}-${cwdHash}`;
|
|
3796
|
+
}
|
|
3797
|
+
getAutoLearnTenantDataDir() {
|
|
3798
|
+
return path.join(this.getAutoLearnDataDir(), "tenants", this.getAutoLearnTenantId());
|
|
3799
|
+
}
|
|
3762
3800
|
getAutoLearnMessageCount() {
|
|
3763
3801
|
return this.sessionManager.getBranch().filter((entry) => entry.type === "message").length;
|
|
3764
3802
|
}
|
|
3765
3803
|
buildAutoLearnDecisionFromState(state, settings, force = false) {
|
|
3766
3804
|
const now = Date.now();
|
|
3767
3805
|
const tenant = this.getAutoLearnTenantKey();
|
|
3768
|
-
const runningCount = Object.
|
|
3806
|
+
const runningCount = Object.values(state.runs ?? {}).filter((run) => run.tenant === tenant).length;
|
|
3769
3807
|
const lastLaunch = state.lastLaunchByTenant?.[tenant] ?? 0;
|
|
3770
3808
|
const cooldownMs = settings.cooldownMinutes * 60 * 1000;
|
|
3771
3809
|
const cooldownRemainingMs = Math.max(0, lastLaunch + cooldownMs - now);
|
|
@@ -3784,7 +3822,7 @@ export class InteractiveMode {
|
|
|
3784
3822
|
if (runningCount >= settings.maxConcurrentLearners) {
|
|
3785
3823
|
return {
|
|
3786
3824
|
shouldRun: false,
|
|
3787
|
-
reason: `max learners running (${runningCount}/${settings.maxConcurrentLearners})`,
|
|
3825
|
+
reason: `max tenant learners running (${runningCount}/${settings.maxConcurrentLearners})`,
|
|
3788
3826
|
messageCount,
|
|
3789
3827
|
contextPercent,
|
|
3790
3828
|
cooldownRemainingMs,
|
|
@@ -3926,7 +3964,7 @@ export class InteractiveMode {
|
|
|
3926
3964
|
const objective = options.kind === "reflection"
|
|
3927
3965
|
? "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"
|
|
3928
3966
|
: "run one bounded continuous-learning pass for this Pi tenant";
|
|
3929
|
-
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
|
|
3967
|
+
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. Memory confrontation is mandatory before accepting, merging, upgrading, or rejecting learning candidates.\n2. Run the available Auto Learn tooling, preferably learning_run_auto, with applyHighConfidence=${settings.applyHighConfidence}. Process candidate validation in vectorized chunks/batches; avoid scalar per-candidate memory queries except for final selected writes.\n3. Apply the learning validation tree to each candidate chunk: (a) Why is this good for the user? (b) Is it unique, or similar to existing memory/skills/agents so it should merge or upgrade existing knowledge? (c) Will this make Pi a better agent? Candidates that cannot answer all three are noise.\n4. Treat the latest-turn digest as current-session evidence only; do not auto-commit one-off cues unless deterministic tooling and memory confrontation corroborate them.\n5. In mode=full, apply safe memory/skill/user-extension/authorized-source improvements under the standing grant above; otherwise keep them proposal-gated.\n6. Never cross hard-stop boundaries from the authority policy.\n7. If the learning tools are unavailable, report BLOCKED with the missing tool names and do not improvise.\n8. Finish with PASS, BLOCKED, or FAIL and concise evidence, including chunk counts, merge/upgrade decisions, and cleanup/purge status.${reflectionBlock}`;
|
|
3930
3968
|
}
|
|
3931
3969
|
reserveAutoLearnRun(params) {
|
|
3932
3970
|
return this.withAutoLearnStateLock((current) => {
|
|
@@ -3993,6 +4031,20 @@ export class InteractiveMode {
|
|
|
3993
4031
|
return { result: undefined, next };
|
|
3994
4032
|
});
|
|
3995
4033
|
}
|
|
4034
|
+
cleanupCompletedAutoLearnRun(runId, artifactPaths) {
|
|
4035
|
+
const dataDir = this.getAutoLearnDataDir();
|
|
4036
|
+
const removed = artifactPaths.filter((filePath) => removeAutoLearnArtifactPath(filePath, dataDir)).length;
|
|
4037
|
+
this.withAutoLearnStateLock((current) => {
|
|
4038
|
+
const state = this.pruneAutoLearnState(current);
|
|
4039
|
+
const runs = { ...(state.runs ?? {}) };
|
|
4040
|
+
delete runs[runId];
|
|
4041
|
+
return { result: undefined, next: { ...state, runs } };
|
|
4042
|
+
});
|
|
4043
|
+
if (removed > 0) {
|
|
4044
|
+
this.autoLearnLastStatus = `cleaned ${removed} artifact(s)`;
|
|
4045
|
+
this.updateAutoLearnFooter();
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
3996
4048
|
markAutoLearnReservationRunning(reservation, pid, settings) {
|
|
3997
4049
|
this.withAutoLearnStateLock((current) => {
|
|
3998
4050
|
const now = Date.now();
|
|
@@ -4028,14 +4080,14 @@ export class InteractiveMode {
|
|
|
4028
4080
|
if (!spawnTarget) {
|
|
4029
4081
|
return "Auto Learn not started: could not resolve current pi CLI path.";
|
|
4030
4082
|
}
|
|
4031
|
-
const dir = this.
|
|
4083
|
+
const dir = this.getAutoLearnTenantDataDir();
|
|
4032
4084
|
fs.mkdirSync(dir, { recursive: true });
|
|
4033
4085
|
const runId = `${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
4034
4086
|
const logPath = path.join(dir, `${runId}.log`);
|
|
4035
4087
|
const promptPath = path.join(dir, `${runId}.prompt.md`);
|
|
4036
4088
|
const kind = options.promptKind ?? "auto";
|
|
4037
|
-
const sessionDir = path.join(dir, "sessions");
|
|
4038
|
-
const sessionId = `auto-learn-${kind}-${runId}`;
|
|
4089
|
+
const sessionDir = path.join(dir, "sessions", runId);
|
|
4090
|
+
const sessionId = `auto-learn-${kind}-${this.getAutoLearnTenantId()}-${runId}`;
|
|
4039
4091
|
fs.mkdirSync(sessionDir, { recursive: true });
|
|
4040
4092
|
const prompt = this.buildAutoLearnPrompt(reason, settings, {
|
|
4041
4093
|
kind,
|
|
@@ -4129,6 +4181,10 @@ export class InteractiveMode {
|
|
|
4129
4181
|
return `Auto Learn not started: failed to spawn background learner. Log: ${logPath}`;
|
|
4130
4182
|
}
|
|
4131
4183
|
const childPid = child.pid;
|
|
4184
|
+
child.once("exit", (code) => {
|
|
4185
|
+
if (code === 0)
|
|
4186
|
+
this.cleanupCompletedAutoLearnRun(reservation.runId, [promptPath, logPath, sessionDir]);
|
|
4187
|
+
});
|
|
4132
4188
|
child.unref();
|
|
4133
4189
|
this.markAutoLearnReservationRunning(reservation, childPid, settings);
|
|
4134
4190
|
this.autoLearnLastStatus = `running ${modelPattern}`;
|
|
@@ -4212,7 +4268,7 @@ export class InteractiveMode {
|
|
|
4212
4268
|
});
|
|
4213
4269
|
const now = Date.now();
|
|
4214
4270
|
const tenant = this.getAutoLearnTenantKey();
|
|
4215
|
-
const runningCount = Object.
|
|
4271
|
+
const runningCount = Object.values(state.runs ?? {}).filter((run) => run.tenant === tenant).length;
|
|
4216
4272
|
const lastReflection = state.lastReflectionByTenant?.[tenant] ?? 0;
|
|
4217
4273
|
const cooldownMs = settings.reflectionCooldownMinutes * 60 * 1000;
|
|
4218
4274
|
const cooldownRemainingMs = Math.max(0, lastReflection + cooldownMs - now);
|
|
@@ -4233,7 +4289,7 @@ export class InteractiveMode {
|
|
|
4233
4289
|
return {
|
|
4234
4290
|
...base,
|
|
4235
4291
|
shouldRun: false,
|
|
4236
|
-
reason: `max learners running (${runningCount}/${settings.maxConcurrentLearners})`,
|
|
4292
|
+
reason: `max tenant learners running (${runningCount}/${settings.maxConcurrentLearners})`,
|
|
4237
4293
|
};
|
|
4238
4294
|
}
|
|
4239
4295
|
if (cooldownRemainingMs > 0)
|
|
@@ -4305,7 +4361,9 @@ export class InteractiveMode {
|
|
|
4305
4361
|
const settings = this.getEffectiveAutoLearnSettings();
|
|
4306
4362
|
const decision = this.evaluateAutoLearn(false);
|
|
4307
4363
|
const state = this.getPrunedAutoLearnState();
|
|
4308
|
-
const
|
|
4364
|
+
const tenant = this.getAutoLearnTenantKey();
|
|
4365
|
+
const runs = Object.entries(state.runs ?? {}).filter(([, run]) => run.tenant === tenant);
|
|
4366
|
+
const otherTenantRuns = Object.values(state.runs ?? {}).filter((run) => run.tenant !== tenant).length;
|
|
4309
4367
|
const contextText = decision.contextPercent === null ? "unknown" : `${decision.contextPercent.toFixed(1)}%`;
|
|
4310
4368
|
const cooldownText = decision.cooldownRemainingMs > 0 ? `${Math.ceil(decision.cooldownRemainingMs / 60000)}m remaining` : "ready";
|
|
4311
4369
|
const runLines = runs.length
|
|
@@ -4333,13 +4391,15 @@ export class InteractiveMode {
|
|
|
4333
4391
|
const reflectionLast = state.lastReflectionByTenant?.[this.getAutoLearnTenantKey()] ?? 0;
|
|
4334
4392
|
const reflectionCooldownRemainingMs = Math.max(0, reflectionLast + settings.reflectionCooldownMinutes * 60 * 1000 - Date.now());
|
|
4335
4393
|
const reflectionCooldownText = reflectionCooldownRemainingMs > 0 ? `${Math.ceil(reflectionCooldownRemainingMs / 60000)}m remaining` : "ready";
|
|
4336
|
-
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})\nHistory retention: 7 days for internal Auto Learn prompts/logs/sessions\nRunning leases: ${runs.length}/${settings.maxConcurrentLearners}\nPi auto-reload blockers: ${reloadBlockers.pending ? reloadBlockers.reason : "none"}\n${reloadBlockerLines}\nRuns:\n${runLines}`;
|
|
4394
|
+
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})\nHistory retention: 7 days for internal Auto Learn prompts/logs/sessions\nRunning tenant leases: ${runs.length}/${settings.maxConcurrentLearners}\nOther tenant leases: ${otherTenantRuns}\nTenant artifact dir: ${this.getAutoLearnTenantDataDir()}\nPi auto-reload blockers: ${reloadBlockers.pending ? reloadBlockers.reason : "none"}\n${reloadBlockerLines}\nRuns:\n${runLines}`;
|
|
4337
4395
|
}
|
|
4338
4396
|
formatAutonomyStatus() {
|
|
4339
4397
|
const autonomy = this.settingsManager.getAutonomySettings();
|
|
4340
4398
|
const settings = this.getEffectiveAutoLearnSettings();
|
|
4341
4399
|
const autoLearnState = this.getPrunedAutoLearnState();
|
|
4342
|
-
const
|
|
4400
|
+
const tenant = this.getAutoLearnTenantKey();
|
|
4401
|
+
const running = Object.entries(autoLearnState.runs ?? {}).filter(([, run]) => run.tenant === tenant);
|
|
4402
|
+
const otherTenantRunning = Object.values(autoLearnState.runs ?? {}).filter((run) => run.tenant !== tenant).length;
|
|
4343
4403
|
const safety = autonomy.mode === "full"
|
|
4344
4404
|
? "standing grant for memory, skills, user/project extensions, autonomy/autoLearn tuning, and authorized selfModification.sourcePath edits; hard stops still require explicit foreground approval"
|
|
4345
4405
|
: "proposal-gated outside configured high-confidence memory policy";
|
|
@@ -4352,10 +4412,12 @@ export class InteractiveMode {
|
|
|
4352
4412
|
`Auto Learn: ${settings.enabled ? "enabled" : "disabled"}; model=${settings.model}; applyHighConfidence=${settings.applyHighConfidence}`,
|
|
4353
4413
|
`Long-session trigger: ${settings.longSessionMessages} messages or ${settings.longSessionContextPercent}% context; cooldown=${settings.cooldownMinutes}m`,
|
|
4354
4414
|
reflectionLine,
|
|
4355
|
-
`Running learners: ${running.length}/${settings.maxConcurrentLearners}`,
|
|
4415
|
+
`Running tenant learners: ${running.length}/${settings.maxConcurrentLearners}`,
|
|
4416
|
+
`Other tenant learners: ${otherTenantRunning}`,
|
|
4356
4417
|
"History retention: 7 days for internal Auto Learn prompts/logs/sessions",
|
|
4357
4418
|
`Standing authority: ${safety}`,
|
|
4358
4419
|
`Audit/log dir: ${this.getAutoLearnDataDir()}`,
|
|
4420
|
+
`Tenant artifact dir: ${this.getAutoLearnTenantDataDir()}`,
|
|
4359
4421
|
"Use /autonomy off|safe|balanced|full to switch presets. Advanced overrides remain in /settings → Auto Learn Advanced.",
|
|
4360
4422
|
].join("\n");
|
|
4361
4423
|
}
|
|
@@ -5021,6 +5083,35 @@ export class InteractiveMode {
|
|
|
5021
5083
|
}
|
|
5022
5084
|
return options.sort((a, b) => a.name.localeCompare(b.name));
|
|
5023
5085
|
}
|
|
5086
|
+
resolveAuthProviderOption(providerReference, providerOptions) {
|
|
5087
|
+
const normalized = providerReference.trim().toLowerCase();
|
|
5088
|
+
if (!normalized)
|
|
5089
|
+
return undefined;
|
|
5090
|
+
const exactMatch = providerOptions.find((provider) => {
|
|
5091
|
+
const id = provider.id.toLowerCase();
|
|
5092
|
+
const name = provider.name.toLowerCase();
|
|
5093
|
+
return id === normalized || name === normalized;
|
|
5094
|
+
});
|
|
5095
|
+
if (exactMatch)
|
|
5096
|
+
return exactMatch;
|
|
5097
|
+
const aliasTarget = cliProviderAliases[normalized] ?? normalized;
|
|
5098
|
+
return providerOptions.find((provider) => {
|
|
5099
|
+
const id = provider.id.toLowerCase();
|
|
5100
|
+
const name = provider.name.toLowerCase();
|
|
5101
|
+
return id === aliasTarget || name === aliasTarget;
|
|
5102
|
+
});
|
|
5103
|
+
}
|
|
5104
|
+
async startProviderLogin(providerOption) {
|
|
5105
|
+
if (providerOption.authType === "oauth") {
|
|
5106
|
+
await this.showLoginDialog(providerOption.id, providerOption.name);
|
|
5107
|
+
}
|
|
5108
|
+
else if (providerOption.id === BEDROCK_PROVIDER_ID) {
|
|
5109
|
+
this.showBedrockSetupDialog(providerOption.id, providerOption.name);
|
|
5110
|
+
}
|
|
5111
|
+
else {
|
|
5112
|
+
await this.showApiKeyLoginDialog(providerOption.id, providerOption.name);
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
5024
5115
|
showLoginAuthTypeSelector() {
|
|
5025
5116
|
const subscriptionLabel = "Use a subscription";
|
|
5026
5117
|
const apiKeyLabel = "Use an API key";
|
|
@@ -5049,15 +5140,7 @@ export class InteractiveMode {
|
|
|
5049
5140
|
if (!providerOption) {
|
|
5050
5141
|
return;
|
|
5051
5142
|
}
|
|
5052
|
-
|
|
5053
|
-
await this.showLoginDialog(providerOption.id, providerOption.name);
|
|
5054
|
-
}
|
|
5055
|
-
else if (providerOption.id === BEDROCK_PROVIDER_ID) {
|
|
5056
|
-
this.showBedrockSetupDialog(providerOption.id, providerOption.name);
|
|
5057
|
-
}
|
|
5058
|
-
else {
|
|
5059
|
-
await this.showApiKeyLoginDialog(providerOption.id, providerOption.name);
|
|
5060
|
-
}
|
|
5143
|
+
await this.startProviderLogin(providerOption);
|
|
5061
5144
|
}, () => {
|
|
5062
5145
|
done();
|
|
5063
5146
|
this.showLoginAuthTypeSelector();
|
|
@@ -5065,8 +5148,18 @@ export class InteractiveMode {
|
|
|
5065
5148
|
return { component: selector, focus: selector };
|
|
5066
5149
|
});
|
|
5067
5150
|
}
|
|
5068
|
-
async showOAuthSelector(mode) {
|
|
5151
|
+
async showOAuthSelector(mode, providerReference) {
|
|
5069
5152
|
if (mode === "login") {
|
|
5153
|
+
if (providerReference) {
|
|
5154
|
+
const providerOptions = this.getLoginProviderOptions();
|
|
5155
|
+
const providerOption = this.resolveAuthProviderOption(providerReference, providerOptions);
|
|
5156
|
+
if (!providerOption) {
|
|
5157
|
+
this.showError(`Unknown login provider "${providerReference}". Use /login to select from available providers.`);
|
|
5158
|
+
return;
|
|
5159
|
+
}
|
|
5160
|
+
await this.startProviderLogin(providerOption);
|
|
5161
|
+
return;
|
|
5162
|
+
}
|
|
5070
5163
|
this.showLoginAuthTypeSelector();
|
|
5071
5164
|
return;
|
|
5072
5165
|
}
|
|
@@ -5075,6 +5168,26 @@ export class InteractiveMode {
|
|
|
5075
5168
|
this.showStatus("No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.");
|
|
5076
5169
|
return;
|
|
5077
5170
|
}
|
|
5171
|
+
if (providerReference) {
|
|
5172
|
+
const providerOption = this.resolveAuthProviderOption(providerReference, providerOptions);
|
|
5173
|
+
if (!providerOption) {
|
|
5174
|
+
this.showError(`No stored credentials found for "${providerReference}". Use /logout to select a saved provider.`);
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
try {
|
|
5178
|
+
this.session.modelRegistry.authStorage.logout(providerOption.id);
|
|
5179
|
+
this.session.modelRegistry.refresh();
|
|
5180
|
+
await this.updateAvailableProviderCount();
|
|
5181
|
+
const message = providerOption.authType === "oauth"
|
|
5182
|
+
? `Logged out of ${providerOption.name}`
|
|
5183
|
+
: `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;
|
|
5184
|
+
this.showStatus(message);
|
|
5185
|
+
}
|
|
5186
|
+
catch (error) {
|
|
5187
|
+
this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
5188
|
+
}
|
|
5189
|
+
return;
|
|
5190
|
+
}
|
|
5078
5191
|
this.showSelector((done) => {
|
|
5079
5192
|
const selector = new OAuthSelectorComponent(mode, this.session.modelRegistry.authStorage, providerOptions, async (providerId) => {
|
|
5080
5193
|
done();
|