@dreb/coding-agent 2.52.0 → 2.53.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.
@@ -16,12 +16,15 @@ import { isValidThinkingLevel, VALID_THINKING_LEVELS } from "../../cli/args.js";
16
16
  import { VERSION } from "../../config.js";
17
17
  import { canonicalizeTrustedRoots, matchContextTrust, validateTrustedContextFolder, validateTrustedContextFolders, } from "../../core/context-trust.js";
18
18
  import { DailyCostTracker } from "../../core/daily-cost-tracker.js";
19
+ import { acquireDreamLock, buildDreamPrompt, cleanupDreamTmpDirs, parseDreamCommand, performDreamBackup, pruneOldBackups, resolveDreamContext, validateArchivePath, validateMemoryLinks, } from "../../core/dream.js";
19
20
  import { getGitBranch } from "../../core/git-branch.js";
20
21
  import { parseModelPattern } from "../../core/model-resolver.js";
21
22
  import { takeOverStdout, writeRawStdout } from "../../core/output-guard.js";
22
23
  import { SessionManager } from "../../core/session-manager.js";
24
+ import { BUILTIN_SLASH_COMMANDS, parseBuiltinSlashCommand } from "../../core/slash-commands.js";
23
25
  import { TabTitleGenerator } from "../../core/tab-title.js";
24
26
  import { validateThinkingLevelForModel } from "../../core/thinking.js";
27
+ import { resolveToCwd } from "../../core/tools/path-utils.js";
25
28
  import { discoverAgentTypes, getBackgroundAgents, rehydrateBackgroundAgentsFromDisk, } from "../../core/tools/subagent.js";
26
29
  import { theme } from "../interactive/theme/theme.js";
27
30
  import { attachJsonlLineReader, serializeJsonLine } from "./jsonl.js";
@@ -71,6 +74,111 @@ export function getResourcesForRpc(session) {
71
74
  session.resourceLoader.getAppendSystemPrompt().length > 0,
72
75
  };
73
76
  }
77
+ export function getCommandsForRpc(session) {
78
+ const commands = new Map();
79
+ for (const command of session.extensionRunner?.getRegisteredCommands() ?? []) {
80
+ commands.set(command.invocationName, {
81
+ name: command.invocationName,
82
+ description: command.description,
83
+ source: "extension",
84
+ sourceInfo: command.sourceInfo,
85
+ });
86
+ }
87
+ for (const template of session.promptTemplates) {
88
+ if (!commands.has(template.name)) {
89
+ commands.set(template.name, {
90
+ name: template.name,
91
+ description: template.description,
92
+ source: "prompt",
93
+ sourceInfo: template.sourceInfo,
94
+ });
95
+ }
96
+ }
97
+ for (const skill of session.getFilteredSkills()) {
98
+ const name = `skill:${skill.name}`;
99
+ if (!commands.has(name)) {
100
+ commands.set(name, { name, description: skill.description, source: "skill", sourceInfo: skill.sourceInfo });
101
+ }
102
+ }
103
+ // Built-ins deliberately win collisions, matching interactive autocomplete and
104
+ // ensuring a colliding resource can never make built-in text reach the model.
105
+ for (const command of BUILTIN_SLASH_COMMANDS) {
106
+ commands.set(command.name, {
107
+ name: command.name,
108
+ description: command.description,
109
+ source: "builtin",
110
+ dashboard: command.dashboard !== false,
111
+ });
112
+ }
113
+ return [...commands.values()];
114
+ }
115
+ export function getBuiltinPromptRejection(message) {
116
+ const parsed = parseBuiltinSlashCommand(message);
117
+ return parsed
118
+ ? `Built-in slash command /${parsed.command.name} must be handled by the RPC client; it was not sent to the model.`
119
+ : undefined;
120
+ }
121
+ async function runDreamForRpc(session, args = "") {
122
+ const command = parseDreamCommand(`/dream${args ? ` ${args}` : ""}`);
123
+ if (command.type === "showBackup") {
124
+ return { message: `Dream backup path: ${session.settingsManager.getDreamArchivePath()}` };
125
+ }
126
+ if (command.type === "setBackup") {
127
+ const absolutePath = resolveToCwd(command.path, session.sessionManager.getCwd());
128
+ const context = await resolveDreamContext(session.settingsManager);
129
+ validateArchivePath(absolutePath, [
130
+ context.globalMemoryDir,
131
+ ...context.projectMemoryDirs,
132
+ ...context.claudeMemoryDirs,
133
+ ]);
134
+ if (session.settingsManager.hasGlobalSettingsLoadError()) {
135
+ throw new Error("Cannot write dream backup path: the global settings file failed to load");
136
+ }
137
+ session.settingsManager.drainErrors();
138
+ session.settingsManager.setDreamArchivePath(absolutePath);
139
+ try {
140
+ await session.settingsManager.flush();
141
+ }
142
+ catch (error) {
143
+ session.settingsManager.reload();
144
+ throw error;
145
+ }
146
+ const writeErrors = session.settingsManager.drainErrors();
147
+ if (writeErrors.length > 0) {
148
+ session.settingsManager.reload();
149
+ throw new Error(`Failed to persist dream backup path: ${writeErrors.map((entry) => `${entry.scope}: ${entry.error.message}`).join("; ")}`);
150
+ }
151
+ return { message: `Dream backup path set to: ${absolutePath}` };
152
+ }
153
+ let releaseLock;
154
+ let context;
155
+ try {
156
+ releaseLock = await acquireDreamLock();
157
+ context = await resolveDreamContext(session.settingsManager);
158
+ validateArchivePath(context.archivePath, [
159
+ context.globalMemoryDir,
160
+ ...context.projectMemoryDirs,
161
+ ...context.claudeMemoryDirs,
162
+ ]);
163
+ const backup = await performDreamBackup(context);
164
+ if (!backup.verified) {
165
+ throw new Error(`Backup verification failed; check ${backup.backupPath}`);
166
+ }
167
+ await session.prompt(buildDreamPrompt(context, backup), { source: "rpc" });
168
+ const links = validateMemoryLinks([context.globalMemoryDir, ...context.projectMemoryDirs]);
169
+ await pruneOldBackups(context.archivePath);
170
+ return {
171
+ message: links.valid
172
+ ? `Dream completed. Backup: ${backup.backupPath}`
173
+ : `Dream completed with ${links.brokenLinks.length} broken memory link(s). Backup: ${backup.backupPath}`,
174
+ };
175
+ }
176
+ finally {
177
+ releaseLock?.();
178
+ if (context)
179
+ cleanupDreamTmpDirs([context.globalMemoryDir, ...context.projectMemoryDirs]);
180
+ }
181
+ }
74
182
  export function getPendingMessagesForRpc(session) {
75
183
  return {
76
184
  steering: [...session.getSteeringMessages()],
@@ -1273,6 +1381,9 @@ export async function runRpcMode(session, modelFallbackMessage) {
1273
1381
  // Prompting
1274
1382
  // =================================================================
1275
1383
  case "prompt": {
1384
+ const rejection = getBuiltinPromptRejection(command.message);
1385
+ if (rejection)
1386
+ return error(id, "prompt", rejection);
1276
1387
  // Don't await - events will stream
1277
1388
  // Extension commands are executed immediately, file prompt templates are expanded
1278
1389
  // If streaming and streamingBehavior specified, queues via steer/followUp
@@ -1286,10 +1397,16 @@ export async function runRpcMode(session, modelFallbackMessage) {
1286
1397
  return success(id, "prompt");
1287
1398
  }
1288
1399
  case "steer": {
1400
+ const rejection = getBuiltinPromptRejection(command.message);
1401
+ if (rejection)
1402
+ return error(id, "steer", rejection);
1289
1403
  await session.steer(command.message, command.images);
1290
1404
  return success(id, "steer");
1291
1405
  }
1292
1406
  case "follow_up": {
1407
+ const rejection = getBuiltinPromptRejection(command.message);
1408
+ if (rejection)
1409
+ return error(id, "follow_up", rejection);
1293
1410
  await session.followUp(command.message, command.images);
1294
1411
  return success(id, "follow_up");
1295
1412
  }
@@ -1304,6 +1421,13 @@ export async function runRpcMode(session, modelFallbackMessage) {
1304
1421
  const cancelled = !(await session.newSession(options));
1305
1422
  return success(id, "new_session", { cancelled });
1306
1423
  }
1424
+ case "reload": {
1425
+ await session.reload();
1426
+ return success(id, "reload");
1427
+ }
1428
+ case "dream": {
1429
+ return success(id, "dream", await runDreamForRpc(session, command.args));
1430
+ }
1307
1431
  // =================================================================
1308
1432
  // State
1309
1433
  // =================================================================
@@ -1479,6 +1603,10 @@ export async function runRpcMode(session, modelFallbackMessage) {
1479
1603
  const path = await session.exportToHtml(command.outputPath);
1480
1604
  return success(id, "export_html", { path });
1481
1605
  }
1606
+ case "import_jsonl": {
1607
+ const cancelled = !(await session.importFromJsonl(command.inputPath));
1608
+ return success(id, "import_jsonl", { cancelled });
1609
+ }
1482
1610
  case "switch_session": {
1483
1611
  const cancelled = !(await session.switchSession(command.sessionPath));
1484
1612
  return success(id, "switch_session", { cancelled });
@@ -1534,7 +1662,7 @@ export async function runRpcMode(session, modelFallbackMessage) {
1534
1662
  return success(id, "get_messages", { messages: session.messages });
1535
1663
  }
1536
1664
  // =================================================================
1537
- // Commands (available for invocation via prompt)
1665
+ // Command discovery (resource commands plus client-handled built-ins)
1538
1666
  // =================================================================
1539
1667
  // =================================================================
1540
1668
  // Session Listing
@@ -1608,32 +1736,7 @@ export async function runRpcMode(session, modelFallbackMessage) {
1608
1736
  return success(id, "get_version", { version: VERSION });
1609
1737
  }
1610
1738
  case "get_commands": {
1611
- const commands = [];
1612
- for (const command of session.extensionRunner?.getRegisteredCommands() ?? []) {
1613
- commands.push({
1614
- name: command.invocationName,
1615
- description: command.description,
1616
- source: "extension",
1617
- sourceInfo: command.sourceInfo,
1618
- });
1619
- }
1620
- for (const template of session.promptTemplates) {
1621
- commands.push({
1622
- name: template.name,
1623
- description: template.description,
1624
- source: "prompt",
1625
- sourceInfo: template.sourceInfo,
1626
- });
1627
- }
1628
- for (const skill of session.getFilteredSkills()) {
1629
- commands.push({
1630
- name: `skill:${skill.name}`,
1631
- description: skill.description,
1632
- source: "skill",
1633
- sourceInfo: skill.sourceInfo,
1634
- });
1635
- }
1636
- return success(id, "get_commands", { commands });
1739
+ return success(id, "get_commands", { commands: getCommandsForRpc(session) });
1637
1740
  }
1638
1741
  default: {
1639
1742
  const unknownCommand = command;