@dreb/coding-agent 2.53.1 → 2.54.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/README.md +2 -2
- package/dist/core/model-resolver.d.ts +15 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +24 -12
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +7 -7
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +109 -19
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +18 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/dashboard.md +2 -2
- package/docs/rpc.md +25 -1
- package/docs/settings.md +7 -3
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ import { canonicalizeTrustedRoots, matchContextTrust, validateTrustedContextFold
|
|
|
18
18
|
import { DailyCostTracker } from "../../core/daily-cost-tracker.js";
|
|
19
19
|
import { acquireDreamLock, buildDreamPrompt, cleanupDreamTmpDirs, parseDreamCommand, performDreamBackup, pruneOldBackups, resolveDreamContext, validateArchivePath, validateMemoryLinks, } from "../../core/dream.js";
|
|
20
20
|
import { getGitBranch } from "../../core/git-branch.js";
|
|
21
|
-
import { parseModelPattern } from "../../core/model-resolver.js";
|
|
21
|
+
import { parseModelPattern, resolveModelScopePatterns } from "../../core/model-resolver.js";
|
|
22
22
|
import { takeOverStdout, writeRawStdout } from "../../core/output-guard.js";
|
|
23
23
|
import { SessionManager } from "../../core/session-manager.js";
|
|
24
24
|
import { BUILTIN_SLASH_COMMANDS, parseBuiltinSlashCommand } from "../../core/slash-commands.js";
|
|
@@ -281,9 +281,12 @@ export function listAgentTypesForRpc(cwd) {
|
|
|
281
281
|
* These seed fresh runtimes, NOT the live session state (`get_state` reports that). Extracted
|
|
282
282
|
* (like {@link deleteSessionForRpc}) so it is unit-testable without a live RPC session.
|
|
283
283
|
*/
|
|
284
|
-
export function getSettingsForRpc(settingsManager) {
|
|
284
|
+
export function getSettingsForRpc(settingsManager, availableModels = []) {
|
|
285
285
|
const contextTrust = settingsManager.getGlobalContextTrustPolicy();
|
|
286
286
|
const configuredTrustedFolders = settingsManager.getConfiguredTrustedContextFolders();
|
|
287
|
+
const enabledModels = settingsManager.getEnabledModels();
|
|
288
|
+
const hasProjectEnabledModelsOverride = settingsManager.hasProjectEnabledModelsOverride();
|
|
289
|
+
const scopeResolution = resolveModelScopePatterns(enabledModels ?? [], availableModels);
|
|
287
290
|
return {
|
|
288
291
|
defaultProvider: settingsManager.getDefaultProvider(),
|
|
289
292
|
defaultModel: settingsManager.getDefaultModel(),
|
|
@@ -302,8 +305,26 @@ export function getSettingsForRpc(settingsManager) {
|
|
|
302
305
|
hideThinkingBlock: settingsManager.getHideThinkingBlock(),
|
|
303
306
|
agentModels: settingsManager.getAgentModels(),
|
|
304
307
|
subagentArbiter: settingsManager.getGlobalSubagentArbiterSettings(),
|
|
308
|
+
enabledModels,
|
|
309
|
+
resolvedScopedModels: scopeResolution.models.map(({ model, thinkingLevel }) => ({
|
|
310
|
+
provider: model.provider,
|
|
311
|
+
id: model.id,
|
|
312
|
+
name: model.name,
|
|
313
|
+
reasoning: model.reasoning,
|
|
314
|
+
...(thinkingLevel !== undefined ? { thinkingLevel } : {}),
|
|
315
|
+
})),
|
|
316
|
+
scopeWarnings: scopeResolution.warnings,
|
|
317
|
+
hasProjectEnabledModelsOverride,
|
|
318
|
+
enabledModelsSource: hasProjectEnabledModelsOverride
|
|
319
|
+
? "project"
|
|
320
|
+
: enabledModels === undefined
|
|
321
|
+
? "default"
|
|
322
|
+
: "global",
|
|
305
323
|
};
|
|
306
324
|
}
|
|
325
|
+
async function buildSettingsSnapshotForRpc(settingsManager, modelRegistry) {
|
|
326
|
+
return getSettingsForRpc(settingsManager, modelRegistry ? await modelRegistry.getAvailable() : []);
|
|
327
|
+
}
|
|
307
328
|
function formatSettingsErrors(errors) {
|
|
308
329
|
return errors.map((entry) => `${entry.scope}: ${entry.error.message}`).join("; ");
|
|
309
330
|
}
|
|
@@ -316,7 +337,7 @@ function formatSettingsErrors(errors) {
|
|
|
316
337
|
* content. Reload failures leave SettingsManager's previous values in memory, therefore
|
|
317
338
|
* they must be surfaced as an RPC error instead of producing a stale snapshot.
|
|
318
339
|
*/
|
|
319
|
-
export async function getFreshSettingsForRpc(settingsManager) {
|
|
340
|
+
export async function getFreshSettingsForRpc(settingsManager, modelRegistry) {
|
|
320
341
|
return settingsWriteLock(async () => {
|
|
321
342
|
try {
|
|
322
343
|
// A runtime setter may have enqueued a write outside the RPC settings lock.
|
|
@@ -349,7 +370,13 @@ export async function getFreshSettingsForRpc(settingsManager) {
|
|
|
349
370
|
error: detail ? `Failed to reload settings: ${detail}` : "Failed to reload settings from durable storage",
|
|
350
371
|
};
|
|
351
372
|
}
|
|
352
|
-
|
|
373
|
+
try {
|
|
374
|
+
const availableModels = modelRegistry ? await modelRegistry.getAvailable() : [];
|
|
375
|
+
return { ok: true, settings: getSettingsForRpc(settingsManager, availableModels) };
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
return { ok: false, error: `Failed to load available models: ${error.message}` };
|
|
379
|
+
}
|
|
353
380
|
});
|
|
354
381
|
}
|
|
355
382
|
const SETTINGS_UPDATE_KEYS = [
|
|
@@ -368,6 +395,7 @@ const SETTINGS_UPDATE_KEYS = [
|
|
|
368
395
|
"transport",
|
|
369
396
|
"hideThinkingBlock",
|
|
370
397
|
"agentModels",
|
|
398
|
+
"enabledModels",
|
|
371
399
|
"subagentArbiter",
|
|
372
400
|
];
|
|
373
401
|
const QUEUE_MODES = ["all", "one-at-a-time"];
|
|
@@ -450,7 +478,7 @@ function settingsWriteLock(fn) {
|
|
|
450
478
|
settingsWriteQueue = next.catch(() => { });
|
|
451
479
|
return next;
|
|
452
480
|
}
|
|
453
|
-
async function persistContextTrustMutationForRpc(settingsManager, folders, targetPath, mutation) {
|
|
481
|
+
async function persistContextTrustMutationForRpc(settingsManager, folders, targetPath, mutation, modelRegistry) {
|
|
454
482
|
settingsManager.drainErrors();
|
|
455
483
|
if (settingsManager.hasGlobalSettingsLoadError()) {
|
|
456
484
|
return {
|
|
@@ -482,11 +510,15 @@ async function persistContextTrustMutationForRpc(settingsManager, folders, targe
|
|
|
482
510
|
return evaluated;
|
|
483
511
|
return {
|
|
484
512
|
ok: true,
|
|
485
|
-
result: {
|
|
513
|
+
result: {
|
|
514
|
+
evaluation: evaluated.evaluation,
|
|
515
|
+
settings: await buildSettingsSnapshotForRpc(settingsManager, modelRegistry),
|
|
516
|
+
...mutation,
|
|
517
|
+
},
|
|
486
518
|
};
|
|
487
519
|
}
|
|
488
520
|
/** Add a canonical directory to the global trusted roots and flush the change. */
|
|
489
|
-
export async function trustContextFolderForRpc(settingsManager, path) {
|
|
521
|
+
export async function trustContextFolderForRpc(settingsManager, path, modelRegistry) {
|
|
490
522
|
return settingsWriteLock(async () => {
|
|
491
523
|
const evaluated = evaluateContextTrustForRpc(settingsManager, path);
|
|
492
524
|
if (!evaluated.ok)
|
|
@@ -505,11 +537,11 @@ export async function trustContextFolderForRpc(settingsManager, path) {
|
|
|
505
537
|
...(folders.includes(evaluated.evaluation.canonicalTarget)
|
|
506
538
|
? { addedRoot: evaluated.evaluation.canonicalTarget }
|
|
507
539
|
: {}),
|
|
508
|
-
});
|
|
540
|
+
}, modelRegistry);
|
|
509
541
|
});
|
|
510
542
|
}
|
|
511
543
|
/** Remove the root actually granting a directory access, rather than the supplied descendant. */
|
|
512
|
-
export async function untrustContextFolderForRpc(settingsManager, path) {
|
|
544
|
+
export async function untrustContextFolderForRpc(settingsManager, path, modelRegistry) {
|
|
513
545
|
return settingsWriteLock(async () => {
|
|
514
546
|
const evaluated = evaluateContextTrustForRpc(settingsManager, path);
|
|
515
547
|
if (!evaluated.ok)
|
|
@@ -523,16 +555,19 @@ export async function untrustContextFolderForRpc(settingsManager, path) {
|
|
|
523
555
|
if (evaluated.evaluation.state === "untrusted") {
|
|
524
556
|
return {
|
|
525
557
|
ok: true,
|
|
526
|
-
result: {
|
|
558
|
+
result: {
|
|
559
|
+
evaluation: evaluated.evaluation,
|
|
560
|
+
settings: await buildSettingsSnapshotForRpc(settingsManager, modelRegistry),
|
|
561
|
+
},
|
|
527
562
|
};
|
|
528
563
|
}
|
|
529
564
|
const removedRoot = evaluated.evaluation.grantingRoot;
|
|
530
565
|
const roots = canonicalizeTrustedRoots(settingsManager.getConfiguredTrustedContextFolders());
|
|
531
|
-
return persistContextTrustMutationForRpc(settingsManager, roots.filter((root) => root !== removedRoot), evaluated.evaluation.canonicalTarget, { removedRoot });
|
|
566
|
+
return persistContextTrustMutationForRpc(settingsManager, roots.filter((root) => root !== removedRoot), evaluated.evaluation.canonicalTarget, { removedRoot }, modelRegistry);
|
|
532
567
|
});
|
|
533
568
|
}
|
|
534
569
|
/** Remove a configured trusted-folder string exactly as stored and flush the change. */
|
|
535
|
-
export async function removeTrustedContextFolderForRpc(settingsManager, path) {
|
|
570
|
+
export async function removeTrustedContextFolderForRpc(settingsManager, path, modelRegistry) {
|
|
536
571
|
return settingsWriteLock(async () => {
|
|
537
572
|
if (typeof path !== "string" || path.length === 0) {
|
|
538
573
|
return { ok: false, error: "remove_trusted_context_folder requires a non-empty string path" };
|
|
@@ -563,7 +598,13 @@ export async function removeTrustedContextFolderForRpc(settingsManager, path) {
|
|
|
563
598
|
settingsManager.reload();
|
|
564
599
|
return { ok: false, error: `Failed to persist settings: ${detail}` };
|
|
565
600
|
}
|
|
566
|
-
return {
|
|
601
|
+
return {
|
|
602
|
+
ok: true,
|
|
603
|
+
result: {
|
|
604
|
+
settings: await buildSettingsSnapshotForRpc(settingsManager, modelRegistry),
|
|
605
|
+
removedFolder: path,
|
|
606
|
+
},
|
|
607
|
+
};
|
|
567
608
|
});
|
|
568
609
|
}
|
|
569
610
|
/**
|
|
@@ -646,6 +687,48 @@ export async function setSettingsForRpc(settingsManager, modelRegistry, update)
|
|
|
646
687
|
return validation;
|
|
647
688
|
}
|
|
648
689
|
}
|
|
690
|
+
let canonicalEnabledModels;
|
|
691
|
+
if (update.enabledModels !== undefined && update.enabledModels !== null) {
|
|
692
|
+
if (!Array.isArray(update.enabledModels)) {
|
|
693
|
+
return {
|
|
694
|
+
ok: false,
|
|
695
|
+
error: "Invalid enabledModels: must be a non-empty array of exact model references or null",
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
if (update.enabledModels.length === 0) {
|
|
699
|
+
return {
|
|
700
|
+
ok: false,
|
|
701
|
+
error: "Invalid enabledModels: at least one model must remain enabled; use null to enable all",
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
const availableModels = await modelRegistry.getAvailable();
|
|
705
|
+
const seen = new Set();
|
|
706
|
+
canonicalEnabledModels = [];
|
|
707
|
+
for (const reference of update.enabledModels) {
|
|
708
|
+
if (typeof reference !== "string" || reference.trim().length === 0 || reference !== reference.trim()) {
|
|
709
|
+
return { ok: false, error: "Invalid enabledModels: every entry must be a non-empty exact model reference" };
|
|
710
|
+
}
|
|
711
|
+
const normalizedReference = reference.toLowerCase();
|
|
712
|
+
const matches = availableModels.filter((model) => `${model.provider}/${model.id}`.toLowerCase() === normalizedReference);
|
|
713
|
+
if (matches.length !== 1) {
|
|
714
|
+
return {
|
|
715
|
+
ok: false,
|
|
716
|
+
error: `Invalid enabledModels entry ${JSON.stringify(reference)}: expected an available exact provider/model reference`,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const match = matches[0];
|
|
720
|
+
const canonical = `${match.provider}/${match.id}`;
|
|
721
|
+
if (seen.has(canonical)) {
|
|
722
|
+
return { ok: false, error: `Invalid enabledModels: duplicate model ${JSON.stringify(canonical)}` };
|
|
723
|
+
}
|
|
724
|
+
seen.add(canonical);
|
|
725
|
+
canonicalEnabledModels.push(canonical);
|
|
726
|
+
}
|
|
727
|
+
if (canonicalEnabledModels.length === availableModels.length &&
|
|
728
|
+
availableModels.every((model) => seen.has(`${model.provider}/${model.id}`))) {
|
|
729
|
+
canonicalEnabledModels = undefined;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
649
732
|
let subagentArbiter = update.subagentArbiter === null ? undefined : update.subagentArbiter;
|
|
650
733
|
if (subagentArbiter !== undefined) {
|
|
651
734
|
if (!isPlainObject(subagentArbiter)) {
|
|
@@ -789,6 +872,12 @@ export async function setSettingsForRpc(settingsManager, modelRegistry, update)
|
|
|
789
872
|
settingsManager.setHideThinkingBlock(update.hideThinkingBlock);
|
|
790
873
|
}
|
|
791
874
|
const warnings = [];
|
|
875
|
+
if (update.enabledModels !== undefined) {
|
|
876
|
+
if (settingsManager.hasProjectEnabledModelsOverride()) {
|
|
877
|
+
warnings.push("A project-level enabledModels value (.dreb/settings.json) takes precedence — this change to global settings will have no effect. Edit the project settings file to change it.");
|
|
878
|
+
}
|
|
879
|
+
settingsManager.setEnabledModels(canonicalEnabledModels);
|
|
880
|
+
}
|
|
792
881
|
if (update.subagentArbiter !== undefined) {
|
|
793
882
|
settingsManager.setGlobalSubagentArbiterSettings(subagentArbiter);
|
|
794
883
|
}
|
|
@@ -832,9 +921,10 @@ export async function setSettingsForRpc(settingsManager, modelRegistry, update)
|
|
|
832
921
|
return { ok: false, error: `Failed to persist settings: ${detail}` };
|
|
833
922
|
}
|
|
834
923
|
}
|
|
924
|
+
const snapshot = getSettingsForRpc(settingsManager, await modelRegistry.getAvailable());
|
|
835
925
|
return warnings.length > 0
|
|
836
|
-
? { ok: true, settings:
|
|
837
|
-
: { ok: true, settings:
|
|
926
|
+
? { ok: true, settings: snapshot, warnings }
|
|
927
|
+
: { ok: true, settings: snapshot };
|
|
838
928
|
}
|
|
839
929
|
catch (error) {
|
|
840
930
|
if (updatesContextTrustPolicy) {
|
|
@@ -1693,7 +1783,7 @@ export async function runRpcMode(session, modelFallbackMessage) {
|
|
|
1693
1783
|
// Settings (persistent defaults)
|
|
1694
1784
|
// =================================================================
|
|
1695
1785
|
case "get_settings": {
|
|
1696
|
-
const result = await getFreshSettingsForRpc(session.settingsManager);
|
|
1786
|
+
const result = await getFreshSettingsForRpc(session.settingsManager, session.modelRegistry);
|
|
1697
1787
|
return result.ok ? success(id, "get_settings", result.settings) : error(id, "get_settings", result.error);
|
|
1698
1788
|
}
|
|
1699
1789
|
case "set_settings": {
|
|
@@ -1712,19 +1802,19 @@ export async function runRpcMode(session, modelFallbackMessage) {
|
|
|
1712
1802
|
: error(id, "evaluate_context_trust", result.error);
|
|
1713
1803
|
}
|
|
1714
1804
|
case "trust_context_folder": {
|
|
1715
|
-
const result = await trustContextFolderForRpc(session.settingsManager, command.path);
|
|
1805
|
+
const result = await trustContextFolderForRpc(session.settingsManager, command.path, session.modelRegistry);
|
|
1716
1806
|
return result.ok
|
|
1717
1807
|
? success(id, "trust_context_folder", result.result)
|
|
1718
1808
|
: error(id, "trust_context_folder", result.error);
|
|
1719
1809
|
}
|
|
1720
1810
|
case "untrust_context_folder": {
|
|
1721
|
-
const result = await untrustContextFolderForRpc(session.settingsManager, command.path);
|
|
1811
|
+
const result = await untrustContextFolderForRpc(session.settingsManager, command.path, session.modelRegistry);
|
|
1722
1812
|
return result.ok
|
|
1723
1813
|
? success(id, "untrust_context_folder", result.result)
|
|
1724
1814
|
: error(id, "untrust_context_folder", result.error);
|
|
1725
1815
|
}
|
|
1726
1816
|
case "remove_trusted_context_folder": {
|
|
1727
|
-
const result = await removeTrustedContextFolderForRpc(session.settingsManager, command.path);
|
|
1817
|
+
const result = await removeTrustedContextFolderForRpc(session.settingsManager, command.path, session.modelRegistry);
|
|
1728
1818
|
return result.ok
|
|
1729
1819
|
? success(id, "remove_trusted_context_folder", result.result)
|
|
1730
1820
|
: error(id, "remove_trusted_context_folder", result.error);
|