ur-agent 1.45.6 → 1.47.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/QUALITY.md +8 -3
  3. package/README.md +67 -13
  4. package/RELEASE.md +9 -6
  5. package/dist/cli.js +73656 -36080
  6. package/docs/A2A.md +180 -0
  7. package/docs/ACP.md +69 -17
  8. package/docs/AGENT_FEATURES.md +64 -7
  9. package/docs/AGENT_TRENDS.md +91 -26
  10. package/docs/AG_UI.md +132 -0
  11. package/docs/CONFIGURATION.md +123 -3
  12. package/docs/DEVELOPMENT.md +1 -0
  13. package/docs/IDE.md +20 -17
  14. package/docs/TROUBLESHOOTING.md +5 -3
  15. package/docs/USAGE.md +34 -4
  16. package/docs/VALIDATION.md +31 -1
  17. package/docs/providers.md +15 -1
  18. package/documentation/app.js +18 -6
  19. package/documentation/index.html +27 -8
  20. package/examples/agent_features.md +18 -0
  21. package/examples/agent_trends.md +5 -0
  22. package/examples/mcp.md +4 -0
  23. package/examples/memory.md +5 -2
  24. package/extensions/jetbrains-ur/README.md +12 -5
  25. package/extensions/jetbrains-ur/build.gradle.kts +22 -6
  26. package/extensions/jetbrains-ur/gradle.properties +1 -0
  27. package/extensions/jetbrains-ur/settings.gradle.kts +4 -0
  28. package/extensions/jetbrains-ur/src/main/kotlin/dev/urnexus/SendSelectionAction.kt +39 -5
  29. package/extensions/jetbrains-ur/src/main/kotlin/dev/urnexus/UrAcpClient.kt +52 -20
  30. package/extensions/jetbrains-ur/src/main/kotlin/dev/urnexus/UrToolWindowFactory.kt +7 -4
  31. package/extensions/jetbrains-ur/src/main/resources/META-INF/plugin.xml +1 -1
  32. package/extensions/vscode-ur-inline-diffs/README.md +6 -2
  33. package/extensions/vscode-ur-inline-diffs/out/extension.js +328 -157
  34. package/extensions/vscode-ur-inline-diffs/package.json +33 -5
  35. package/package.json +70 -56
  36. package/plugins/core/engineering-discipline/skills/reproducible-release/SKILL.md +2 -1
  37. package/plugins/core/github/skills/github-workflow/SKILL.md +2 -1
  38. package/plugins/core/gitlab/skills/gitlab-workflow/SKILL.md +2 -1
  39. package/plugins/core/huggingface/skills/huggingface-workflow/SKILL.md +2 -1
  40. package/plugins/core/miro/skills/miro-workflow/SKILL.md +2 -1
  41. package/plugins/core/obsidian/skills/second-brain/SKILL.md +2 -1
  42. package/plugins/core/powerpoint/skills/deck-craft/SKILL.md +2 -1
  43. package/plugins/core/skill-forge/skills/skill-authoring/SKILL.md +2 -1
  44. package/plugins/core/word/skills/document-craft/SKILL.md +2 -1
@@ -37,33 +37,15 @@ module.exports = __toCommonJS(extension_exports);
37
37
  var vscode17 = __toESM(require("vscode"));
38
38
 
39
39
  // src/actions/actions.ts
40
- var vscode = __toESM(require("vscode"));
41
- async function openBackgroundLog(item) {
42
- const logFile = item?.task.logFile;
43
- if (!logFile) {
44
- vscode.window.showWarningMessage("No log file for this background task.");
45
- return;
46
- }
47
- try {
48
- const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(logFile));
49
- await vscode.window.showTextDocument(doc, { preview: true });
50
- } catch (error) {
51
- vscode.window.showErrorMessage(
52
- `Could not open background task log: ${error instanceof Error ? error.message : String(error)}`
53
- );
54
- }
55
- }
56
-
57
- // src/actions/actionsTreeProvider.ts
58
- var vscode4 = __toESM(require("vscode"));
40
+ var vscode2 = __toESM(require("vscode"));
59
41
 
60
42
  // src/diffs/store.ts
61
43
  var fs = __toESM(require("node:fs"));
62
44
  var path = __toESM(require("node:path"));
63
- var vscode2 = __toESM(require("vscode"));
45
+ var vscode = __toESM(require("vscode"));
64
46
  function workspaceRoot() {
65
- const activeUri = vscode2.window.activeTextEditor?.document.uri;
66
- return (activeUri ? vscode2.workspace.getWorkspaceFolder(activeUri) : void 0)?.uri.fsPath ?? vscode2.workspace.workspaceFolders?.[0]?.uri.fsPath;
47
+ const activeUri = vscode.window.activeTextEditor?.document.uri;
48
+ return (activeUri ? vscode.workspace.getWorkspaceFolder(activeUri) : void 0)?.uri.fsPath ?? vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
67
49
  }
68
50
  function diffsRoot(root) {
69
51
  return path.join(root, ".ur", "ide", "diffs");
@@ -132,120 +114,6 @@ function writeBundleMetadata(root, bundle) {
132
114
  writeJson(metadataPath(root, bundle), bundle);
133
115
  }
134
116
 
135
- // src/diffs/treeProvider.ts
136
- var vscode3 = __toESM(require("vscode"));
137
-
138
- // src/util/format.ts
139
- function escapeHtml(text) {
140
- return String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
141
- }
142
- function formatCount(count, singular, plural = `${singular}s`) {
143
- return `${count} ${count === 1 ? singular : plural}`;
144
- }
145
- function formatRelativeTime(value) {
146
- if (!value) return "unknown time";
147
- const date = new Date(value);
148
- if (Number.isNaN(date.getTime())) return String(value);
149
- const deltaMs = Date.now() - date.getTime();
150
- const minute = 60 * 1e3;
151
- const hour = 60 * minute;
152
- const day = 24 * hour;
153
- if (deltaMs < minute) return "just now";
154
- if (deltaMs < hour) return `${Math.max(1, Math.floor(deltaMs / minute))}m ago`;
155
- if (deltaMs < day) return `${Math.floor(deltaMs / hour)}h ago`;
156
- if (deltaMs < 7 * day) return `${Math.floor(deltaMs / day)}d ago`;
157
- return date.toLocaleDateString();
158
- }
159
- function errorMessage(error) {
160
- return error instanceof Error ? error.message : String(error);
161
- }
162
- function processErrorMessage(error) {
163
- if (typeof error === "object" && error !== null && "stderr" in error) {
164
- const stderr = error.stderr;
165
- if (typeof stderr === "string" && stderr.trim()) return stderr.trim();
166
- }
167
- return errorMessage(error);
168
- }
169
-
170
- // src/diffs/treeProvider.ts
171
- function statusIcon(status) {
172
- switch (status) {
173
- case "approved":
174
- return new vscode3.ThemeIcon("check", new vscode3.ThemeColor("testing.iconPassed"));
175
- case "rejected":
176
- return new vscode3.ThemeIcon("circle-slash", new vscode3.ThemeColor("testing.iconFailed"));
177
- case "commented":
178
- return new vscode3.ThemeIcon("comment-discussion", new vscode3.ThemeColor("charts.yellow"));
179
- default:
180
- return new vscode3.ThemeIcon("diff", new vscode3.ThemeColor("charts.blue"));
181
- }
182
- }
183
- var DiffTreeItem = class extends vscode3.TreeItem {
184
- bundle;
185
- constructor(bundle) {
186
- const title = bundle.title || bundle.id;
187
- super(title, vscode3.TreeItemCollapsibleState.None);
188
- this.bundle = bundle;
189
- this.contextValue = "diff";
190
- const fileCount = bundle.files?.length ?? 0;
191
- const changedAt = bundle.updatedAt ?? bundle.createdAt;
192
- this.description = `${bundle.status ?? "captured"} \xB7 ${formatCount(fileCount, "file")} \xB7 ${formatRelativeTime(changedAt)}`;
193
- this.iconPath = statusIcon(bundle.status);
194
- this.tooltip = new vscode3.MarkdownString(
195
- [
196
- `**${escapeHtml(title)}**`,
197
- "",
198
- `- ID: \`${escapeHtml(bundle.id)}\``,
199
- `- Status: ${escapeHtml(bundle.status ?? "captured")}`,
200
- `- Files: ${fileCount}`,
201
- `- Patch: \`${escapeHtml(bundle.patchFile)}\``
202
- ].join("\n")
203
- );
204
- this.command = {
205
- command: "urInlineDiffs.open",
206
- title: "Open Inline Diff",
207
- arguments: [this]
208
- };
209
- }
210
- };
211
- var InfoItem = class extends vscode3.TreeItem {
212
- constructor(label, description, icon = "info") {
213
- super(label, vscode3.TreeItemCollapsibleState.None);
214
- this.contextValue = "urInfo";
215
- this.description = description;
216
- this.iconPath = new vscode3.ThemeIcon(icon);
217
- this.tooltip = `${label}${description ? ` \u2014 ${description}` : ""}`;
218
- }
219
- };
220
- var DiffTreeProvider = class {
221
- _onDidChangeTreeData = new vscode3.EventEmitter();
222
- onDidChangeTreeData = this._onDidChangeTreeData.event;
223
- refresh() {
224
- this._onDidChangeTreeData.fire();
225
- }
226
- getTreeItem(item) {
227
- return item;
228
- }
229
- getChildren() {
230
- const root = workspaceRoot();
231
- if (!root) {
232
- return [
233
- new InfoItem(
234
- "Open a workspace folder",
235
- "UR inline diffs are scoped to the active project",
236
- "folder-opened"
237
- )
238
- ];
239
- }
240
- const manifest = loadManifest(root);
241
- const diffs = manifest.diffs.slice().sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)));
242
- if (diffs.length === 0) {
243
- return [];
244
- }
245
- return diffs.map((bundle) => new DiffTreeItem(bundle));
246
- }
247
- };
248
-
249
117
  // src/bridge/urCli.ts
250
118
  var import_node_child_process2 = require("node:child_process");
251
119
  var import_node_util = require("node:util");
@@ -291,13 +159,25 @@ function isBunAvailable(commandName) {
291
159
 
292
160
  // src/bridge/urCli.ts
293
161
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process2.execFile);
162
+ function boundedExecutionOptions(options) {
163
+ const timeout = Number.isSafeInteger(options.timeoutMs) && (options.timeoutMs ?? 0) > 0 ? Math.min(options.timeoutMs, 10 * 6e4) : 12e4;
164
+ const maxBuffer = Number.isSafeInteger(options.maxBufferBytes) && (options.maxBufferBytes ?? 0) > 0 ? Math.min(options.maxBufferBytes, 16 * 1024 * 1024) : 4 * 1024 * 1024;
165
+ return {
166
+ cwd: options.cwd,
167
+ shell: false,
168
+ timeout,
169
+ maxBuffer,
170
+ windowsHide: true
171
+ };
172
+ }
294
173
  async function runUrCli(args, options) {
295
174
  const executable = resolveUrCommand({ cwd: options.cwd, config: readUrCommandConfig() });
296
175
  try {
297
- const { stdout, stderr } = await execFileAsync(executable.command, [...executable.args, ...args], {
298
- cwd: options.cwd,
299
- shell: false
300
- });
176
+ const { stdout, stderr } = await execFileAsync(
177
+ executable.command,
178
+ [...executable.args, ...args],
179
+ boundedExecutionOptions(options)
180
+ );
301
181
  return { stdout, stderr };
302
182
  } catch (error) {
303
183
  throw new Error(formatUrCliError(args, error, executable, options.cwd));
@@ -306,10 +186,11 @@ async function runUrCli(args, options) {
306
186
  async function runUrCliCapture(args, options) {
307
187
  const executable = resolveUrCommand({ cwd: options.cwd, config: readUrCommandConfig() });
308
188
  try {
309
- const { stdout, stderr } = await execFileAsync(executable.command, [...executable.args, ...args], {
310
- cwd: options.cwd,
311
- shell: false
312
- });
189
+ const { stdout, stderr } = await execFileAsync(
190
+ executable.command,
191
+ [...executable.args, ...args],
192
+ boundedExecutionOptions(options)
193
+ );
313
194
  return { stdout, stderr, exitCode: 0 };
314
195
  } catch (error) {
315
196
  if (isCapturedNonZeroExit(error)) {
@@ -356,19 +237,20 @@ var VALID_STATUSES = ["queued", "running", "completed", "failed", "canceled"];
356
237
  function isRecord(value) {
357
238
  return typeof value === "object" && value !== null;
358
239
  }
359
- function parseBackgroundListJson(raw) {
240
+ function tryParseBackgroundListJson(raw) {
360
241
  let data;
361
242
  try {
362
243
  data = JSON.parse(raw);
363
244
  } catch {
364
- return [];
245
+ return null;
365
246
  }
366
- if (!isRecord(data) || !Array.isArray(data.tasks)) return [];
247
+ if (!isRecord(data) || !Array.isArray(data.tasks)) return null;
367
248
  const summaries = [];
368
249
  for (const entry of data.tasks) {
369
250
  if (!isRecord(entry)) continue;
370
251
  if (typeof entry.id !== "string" || typeof entry.task !== "string") continue;
371
- const status = VALID_STATUSES.includes(entry.status) ? entry.status : "queued";
252
+ if (!VALID_STATUSES.includes(entry.status)) continue;
253
+ const status = entry.status;
372
254
  summaries.push({
373
255
  id: entry.id,
374
256
  task: entry.task,
@@ -378,14 +260,287 @@ function parseBackgroundListJson(raw) {
378
260
  }
379
261
  return summaries;
380
262
  }
263
+ function buildBackgroundRunArgs(task, options) {
264
+ const normalized = task.trim();
265
+ if (!normalized || normalized.length > 64e3 || normalized.includes("\0")) {
266
+ throw new Error("Background task must contain 1 to 64,000 characters and no NUL bytes.");
267
+ }
268
+ return [
269
+ "bg",
270
+ "run",
271
+ normalized,
272
+ ...options.worktree ? ["--worktree"] : [],
273
+ ...options.offline ? ["--offline"] : [],
274
+ "--json"
275
+ ];
276
+ }
277
+ function buildBackgroundCancelArgs(id) {
278
+ if (!id || id.length > 256 || id.includes("\0")) {
279
+ throw new Error("Background task id is invalid.");
280
+ }
281
+ return ["bg", "kill", id];
282
+ }
381
283
  async function loadBackgroundTasks(cwd) {
284
+ const { stdout, stderr, exitCode } = await runUrCliCapture(
285
+ ["bg", "list", "--json"],
286
+ { cwd }
287
+ );
288
+ if (exitCode !== 0) {
289
+ throw new Error(stderr.trim() || `ur bg list exited with status ${exitCode}`);
290
+ }
291
+ const tasks = tryParseBackgroundListJson(stdout);
292
+ if (!tasks) throw new Error("ur bg list returned invalid JSON");
293
+ return tasks;
294
+ }
295
+
296
+ // src/actions/actions.ts
297
+ var LAUNCH_CHOICES = [
298
+ {
299
+ label: "$(git-branch) Isolated worktree",
300
+ description: "Recommended",
301
+ detail: "Keep the agent away from uncommitted changes in the active checkout.",
302
+ worktree: true,
303
+ offline: false
304
+ },
305
+ {
306
+ label: "$(device-desktop) Offline isolated worktree",
307
+ description: "Local models only",
308
+ detail: "Use an isolated worktree and disable cloud API dispatch.",
309
+ worktree: true,
310
+ offline: true
311
+ },
312
+ {
313
+ label: "$(folder-active) Current workspace",
314
+ description: "Edits the active checkout",
315
+ detail: "Run directly in this workspace without worktree isolation.",
316
+ worktree: false,
317
+ offline: false
318
+ }
319
+ ];
320
+ async function startBackgroundTask(view) {
321
+ const root = workspaceRoot();
322
+ if (!root) {
323
+ await vscode2.window.showWarningMessage("Open a workspace folder before starting a UR task.");
324
+ return;
325
+ }
326
+ const task = await vscode2.window.showInputBox({
327
+ title: "Start UR Background Task",
328
+ prompt: "Describe the outcome the background agent should deliver.",
329
+ placeHolder: "For example: add regression tests for the authentication flow",
330
+ ignoreFocusOut: true,
331
+ validateInput: (value) => {
332
+ const length = value.trim().length;
333
+ if (length === 0) return "Enter a task.";
334
+ if (length > 64e3) return "Task descriptions are limited to 64,000 characters.";
335
+ if (value.includes("\0")) return "Task descriptions cannot contain NUL bytes.";
336
+ return void 0;
337
+ }
338
+ });
339
+ if (task === void 0) return;
340
+ const choice = await vscode2.window.showQuickPick(LAUNCH_CHOICES, {
341
+ title: "Choose background task isolation",
342
+ placeHolder: "Isolated worktree (recommended)",
343
+ ignoreFocusOut: true
344
+ });
345
+ if (!choice) return;
346
+ const confirmation = await vscode2.window.showWarningMessage(
347
+ `Start this UR background task${choice.worktree ? " in an isolated worktree" : " in the current workspace"}?`,
348
+ {
349
+ modal: true,
350
+ detail: "The task may edit files and use the configured model provider. It will not push or open a pull request."
351
+ },
352
+ "Start Task"
353
+ );
354
+ if (confirmation !== "Start Task") return;
382
355
  try {
383
- const { stdout } = await runUrCliCapture(["bg", "list", "--json"], { cwd });
384
- return parseBackgroundListJson(stdout);
385
- } catch {
386
- return [];
356
+ const result = await vscode2.window.withProgress(
357
+ {
358
+ location: vscode2.ProgressLocation.Notification,
359
+ title: "Starting UR background task\u2026",
360
+ cancellable: false
361
+ },
362
+ () => runUrCli(buildBackgroundRunArgs(task, choice), { cwd: root })
363
+ );
364
+ let id;
365
+ try {
366
+ const parsed = JSON.parse(result.stdout);
367
+ if (typeof parsed.task?.id === "string") id = parsed.task.id;
368
+ } catch {
369
+ }
370
+ await vscode2.window.showInformationMessage(
371
+ id ? `Started UR background task ${id}.` : "Started UR background task."
372
+ );
373
+ view.refresh();
374
+ } catch (error) {
375
+ await vscode2.window.showErrorMessage(
376
+ `Could not start UR background task: ${error instanceof Error ? error.message : String(error)}`
377
+ );
378
+ }
379
+ }
380
+ async function cancelBackgroundTask(item, view) {
381
+ if (!item || item.task.status !== "queued" && item.task.status !== "running") {
382
+ await vscode2.window.showWarningMessage("Select a queued or running UR background task to cancel.");
383
+ return;
384
+ }
385
+ const confirmation = await vscode2.window.showWarningMessage(
386
+ `Cancel background task ${item.task.id}?`,
387
+ { modal: true, detail: item.task.task },
388
+ "Cancel Task"
389
+ );
390
+ if (confirmation !== "Cancel Task") return;
391
+ const root = workspaceRoot();
392
+ if (!root) return;
393
+ try {
394
+ await runUrCli(buildBackgroundCancelArgs(item.task.id), { cwd: root });
395
+ await vscode2.window.showInformationMessage(`Canceled UR background task ${item.task.id}.`);
396
+ view.refresh();
397
+ } catch (error) {
398
+ await vscode2.window.showErrorMessage(
399
+ `Could not cancel UR background task: ${error instanceof Error ? error.message : String(error)}`
400
+ );
401
+ }
402
+ }
403
+ async function openBackgroundLog(item) {
404
+ if (!item) {
405
+ await vscode2.window.showWarningMessage("Select a UR background task first.");
406
+ return;
407
+ }
408
+ const root = workspaceRoot();
409
+ if (!root) return;
410
+ try {
411
+ const { stdout } = await runUrCli(
412
+ ["bg", "logs", item.task.id, "--tail", "2000"],
413
+ { cwd: root }
414
+ );
415
+ const doc = await vscode2.workspace.openTextDocument({
416
+ language: "log",
417
+ content: stdout || `No log output is available for ${item.task.id}.
418
+ `
419
+ });
420
+ await vscode2.window.showTextDocument(doc, { preview: true });
421
+ } catch (error) {
422
+ await vscode2.window.showErrorMessage(
423
+ `Could not open background task log: ${error instanceof Error ? error.message : String(error)}`
424
+ );
425
+ }
426
+ }
427
+
428
+ // src/actions/actionsTreeProvider.ts
429
+ var vscode4 = __toESM(require("vscode"));
430
+
431
+ // src/diffs/treeProvider.ts
432
+ var vscode3 = __toESM(require("vscode"));
433
+
434
+ // src/util/format.ts
435
+ function escapeHtml(text) {
436
+ return String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
437
+ }
438
+ function formatCount(count, singular, plural = `${singular}s`) {
439
+ return `${count} ${count === 1 ? singular : plural}`;
440
+ }
441
+ function formatRelativeTime(value) {
442
+ if (!value) return "unknown time";
443
+ const date = new Date(value);
444
+ if (Number.isNaN(date.getTime())) return String(value);
445
+ const deltaMs = Date.now() - date.getTime();
446
+ const minute = 60 * 1e3;
447
+ const hour = 60 * minute;
448
+ const day = 24 * hour;
449
+ if (deltaMs < minute) return "just now";
450
+ if (deltaMs < hour) return `${Math.max(1, Math.floor(deltaMs / minute))}m ago`;
451
+ if (deltaMs < day) return `${Math.floor(deltaMs / hour)}h ago`;
452
+ if (deltaMs < 7 * day) return `${Math.floor(deltaMs / day)}d ago`;
453
+ return date.toLocaleDateString();
454
+ }
455
+ function errorMessage(error) {
456
+ return error instanceof Error ? error.message : String(error);
457
+ }
458
+ function processErrorMessage(error) {
459
+ if (typeof error === "object" && error !== null && "stderr" in error) {
460
+ const stderr = error.stderr;
461
+ if (typeof stderr === "string" && stderr.trim()) return stderr.trim();
462
+ }
463
+ return errorMessage(error);
464
+ }
465
+
466
+ // src/diffs/treeProvider.ts
467
+ function statusIcon(status) {
468
+ switch (status) {
469
+ case "approved":
470
+ return new vscode3.ThemeIcon("check", new vscode3.ThemeColor("testing.iconPassed"));
471
+ case "rejected":
472
+ return new vscode3.ThemeIcon("circle-slash", new vscode3.ThemeColor("testing.iconFailed"));
473
+ case "commented":
474
+ return new vscode3.ThemeIcon("comment-discussion", new vscode3.ThemeColor("charts.yellow"));
475
+ default:
476
+ return new vscode3.ThemeIcon("diff", new vscode3.ThemeColor("charts.blue"));
387
477
  }
388
478
  }
479
+ var DiffTreeItem = class extends vscode3.TreeItem {
480
+ bundle;
481
+ constructor(bundle) {
482
+ const title = bundle.title || bundle.id;
483
+ super(title, vscode3.TreeItemCollapsibleState.None);
484
+ this.bundle = bundle;
485
+ this.contextValue = "diff";
486
+ const fileCount = bundle.files?.length ?? 0;
487
+ const changedAt = bundle.updatedAt ?? bundle.createdAt;
488
+ this.description = `${bundle.status ?? "captured"} \xB7 ${formatCount(fileCount, "file")} \xB7 ${formatRelativeTime(changedAt)}`;
489
+ this.iconPath = statusIcon(bundle.status);
490
+ this.tooltip = new vscode3.MarkdownString(
491
+ [
492
+ `**${escapeHtml(title)}**`,
493
+ "",
494
+ `- ID: \`${escapeHtml(bundle.id)}\``,
495
+ `- Status: ${escapeHtml(bundle.status ?? "captured")}`,
496
+ `- Files: ${fileCount}`,
497
+ `- Patch: \`${escapeHtml(bundle.patchFile)}\``
498
+ ].join("\n")
499
+ );
500
+ this.command = {
501
+ command: "urInlineDiffs.open",
502
+ title: "Open Inline Diff",
503
+ arguments: [this]
504
+ };
505
+ }
506
+ };
507
+ var InfoItem = class extends vscode3.TreeItem {
508
+ constructor(label, description, icon = "info") {
509
+ super(label, vscode3.TreeItemCollapsibleState.None);
510
+ this.contextValue = "urInfo";
511
+ this.description = description;
512
+ this.iconPath = new vscode3.ThemeIcon(icon);
513
+ this.tooltip = `${label}${description ? ` \u2014 ${description}` : ""}`;
514
+ }
515
+ };
516
+ var DiffTreeProvider = class {
517
+ _onDidChangeTreeData = new vscode3.EventEmitter();
518
+ onDidChangeTreeData = this._onDidChangeTreeData.event;
519
+ refresh() {
520
+ this._onDidChangeTreeData.fire();
521
+ }
522
+ getTreeItem(item) {
523
+ return item;
524
+ }
525
+ getChildren() {
526
+ const root = workspaceRoot();
527
+ if (!root) {
528
+ return [
529
+ new InfoItem(
530
+ "Open a workspace folder",
531
+ "UR inline diffs are scoped to the active project",
532
+ "folder-opened"
533
+ )
534
+ ];
535
+ }
536
+ const manifest = loadManifest(root);
537
+ const diffs = manifest.diffs.slice().sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)));
538
+ if (diffs.length === 0) {
539
+ return [];
540
+ }
541
+ return diffs.map((bundle) => new DiffTreeItem(bundle));
542
+ }
543
+ };
389
544
 
390
545
  // src/actions/actionsTreeProvider.ts
391
546
  function backgroundStatusIcon(status) {
@@ -407,7 +562,7 @@ var BackgroundTaskItem = class extends vscode4.TreeItem {
407
562
  constructor(task) {
408
563
  super(task.task, vscode4.TreeItemCollapsibleState.None);
409
564
  this.task = task;
410
- this.contextValue = "backgroundTask";
565
+ this.contextValue = task.status === "queued" || task.status === "running" ? "backgroundTaskActive" : "backgroundTask";
411
566
  this.description = task.status;
412
567
  this.iconPath = backgroundStatusIcon(task.status);
413
568
  this.tooltip = `${task.id} \u2014 ${task.status}${task.logFile ? `
@@ -440,6 +595,7 @@ var ActionsTreeProvider = class {
440
595
  onDidChangeTreeData = this._onDidChangeTreeData.event;
441
596
  diffs = [];
442
597
  backgroundTasks = [];
598
+ backgroundError;
443
599
  loaded = false;
444
600
  refresh() {
445
601
  this.loaded = false;
@@ -455,17 +611,29 @@ var ActionsTreeProvider = class {
455
611
  }
456
612
  if (!this.loaded) {
457
613
  this.diffs = loadManifest(root).diffs;
458
- this.backgroundTasks = await loadBackgroundTasks(root);
614
+ try {
615
+ this.backgroundTasks = await loadBackgroundTasks(root);
616
+ this.backgroundError = void 0;
617
+ } catch (error) {
618
+ this.backgroundTasks = [];
619
+ this.backgroundError = error instanceof Error ? error.message : String(error);
620
+ }
459
621
  this.loaded = true;
460
622
  }
461
623
  if (!element) {
462
- if (this.diffs.length === 0 && this.backgroundTasks.length === 0) {
624
+ if (this.diffs.length === 0 && this.backgroundTasks.length === 0 && !this.backgroundError) {
463
625
  return [];
464
626
  }
465
- return [
627
+ const sections = [
466
628
  new SectionItem("diffs", "Diff Bundles", this.diffs.length),
467
629
  new SectionItem("background", "Background Tasks", this.backgroundTasks.length)
468
630
  ];
631
+ if (this.backgroundError) {
632
+ sections.push(
633
+ new InfoItem2("Could not load background tasks", this.backgroundError, "warning")
634
+ );
635
+ }
636
+ return sections;
469
637
  }
470
638
  if (element instanceof SectionItem && element.kind === "diffs") {
471
639
  if (this.diffs.length === 0) {
@@ -2398,6 +2566,7 @@ var ACTION_REGISTRY = [
2398
2566
  { id: "openArtifacts", label: "Open Artifacts", commandId: "urInlineDiffs.openArtifacts", description: "Reveal the .ur workspace directory" },
2399
2567
  { id: "runSpec", label: "Run Spec", commandId: "urInlineDiffs.runSpec", description: "Ask UR to list and run specs (ur spec)" },
2400
2568
  { id: "runWorkflow", label: "Run Workflow", commandId: "urInlineDiffs.runWorkflow", description: "Ask UR to list and run workflows (ur workflow)" },
2569
+ { id: "startBackgroundTask", label: "Start Background Task", commandId: "urActions.runBackground", description: "Launch an isolated or workspace-scoped UR background agent" },
2401
2570
  { id: "refreshActions", label: "Refresh IDE Actions", commandId: "urActions.refresh", description: "Refresh the UR actions panel" }
2402
2571
  ];
2403
2572
 
@@ -2552,7 +2721,7 @@ function renderStatusHtml(status) {
2552
2721
  ${boundary}
2553
2722
  ${row("Sandbox mode", knownText2(status.sandboxMode))}
2554
2723
  ${row("Verifier mode", knownText2(status.verifierMode))}
2555
- ${row("ACP server", acp)}
2724
+ ${row("UR HTTP server", acp)}
2556
2725
  ${row("Plugins loaded", String(status.pluginCount))}
2557
2726
  <section>
2558
2727
  <h2>Warnings</h2>
@@ -2704,6 +2873,8 @@ function activate(context) {
2704
2873
  vscode17.commands.registerCommand("urInlineDiffs.runSpec", () => runSpecAction(chat)),
2705
2874
  vscode17.commands.registerCommand("urInlineDiffs.runWorkflow", () => runWorkflowAction(chat)),
2706
2875
  vscode17.commands.registerCommand("urActions.refresh", () => actionsTreeProvider.refresh()),
2876
+ vscode17.commands.registerCommand("urActions.runBackground", () => startBackgroundTask(actionsTreeProvider)),
2877
+ vscode17.commands.registerCommand("urActions.cancelBackground", (item) => cancelBackgroundTask(item, actionsTreeProvider)),
2707
2878
  vscode17.commands.registerCommand("urActions.openBackgroundLog", (item) => openBackgroundLog(item))
2708
2879
  );
2709
2880
  }
@@ -2,7 +2,7 @@
2
2
  "name": "ur-inline-diffs",
3
3
  "displayName": "UR Inline Diffs",
4
4
  "description": "Review, apply, and reject UR inline diff bundles from .ur/ide/diffs inside VS Code.",
5
- "version": "1.45.6",
5
+ "version": "1.47.0",
6
6
  "publisher": "ur-nexus",
7
7
  "engines": {
8
8
  "vscode": "^1.92.0"
@@ -40,6 +40,8 @@
40
40
  "onCommand:urInlineDiffs.runSpec",
41
41
  "onCommand:urInlineDiffs.runWorkflow",
42
42
  "onCommand:urActions.refresh",
43
+ "onCommand:urActions.runBackground",
44
+ "onCommand:urActions.cancelBackground",
43
45
  "onCommand:urActions.openBackgroundLog"
44
46
  ],
45
47
  "main": "./out/extension.js",
@@ -219,9 +221,20 @@
219
221
  "title": "UR: Refresh IDE Actions",
220
222
  "icon": "$(refresh)"
221
223
  },
224
+ {
225
+ "command": "urActions.runBackground",
226
+ "title": "UR: Start Background Task",
227
+ "icon": "$(play)"
228
+ },
229
+ {
230
+ "command": "urActions.cancelBackground",
231
+ "title": "UR: Cancel Background Task",
232
+ "icon": "$(stop-circle)"
233
+ },
222
234
  {
223
235
  "command": "urActions.openBackgroundLog",
224
- "title": "UR: Open Background Task Log"
236
+ "title": "UR: Open Background Task Log",
237
+ "icon": "$(output)"
225
238
  }
226
239
  ],
227
240
  "viewsWelcome": [
@@ -235,7 +248,7 @@
235
248
  },
236
249
  {
237
250
  "view": "urActions",
238
- "contents": "Background tasks and diff actions will appear here.\n[Refresh](command:urActions.refresh) [Search Actions](command:urInlineDiffs.searchActions)"
251
+ "contents": "Background tasks and diff actions will appear here.\n[Start Task](command:urActions.runBackground) [Refresh](command:urActions.refresh) [Search Actions](command:urInlineDiffs.searchActions)"
239
252
  }
240
253
  ],
241
254
  "menus": {
@@ -280,15 +293,20 @@
280
293
  "when": "view == urInlineDiffs",
281
294
  "group": "navigation"
282
295
  },
296
+ {
297
+ "command": "urActions.runBackground",
298
+ "when": "view == urActions",
299
+ "group": "navigation@1"
300
+ },
283
301
  {
284
302
  "command": "urActions.refresh",
285
303
  "when": "view == urActions",
286
- "group": "navigation"
304
+ "group": "navigation@2"
287
305
  },
288
306
  {
289
307
  "command": "urInlineDiffs.agentStatus",
290
308
  "when": "view == urActions",
291
- "group": "navigation"
309
+ "group": "navigation@3"
292
310
  }
293
311
  ],
294
312
  "view/item/context": [
@@ -311,6 +329,16 @@
311
329
  "command": "urInlineDiffs.comment",
312
330
  "when": "(view == urInlineDiffs || view == urActions) && viewItem == diff",
313
331
  "group": "2_review"
332
+ },
333
+ {
334
+ "command": "urActions.openBackgroundLog",
335
+ "when": "view == urActions && (viewItem == backgroundTask || viewItem == backgroundTaskActive)",
336
+ "group": "inline"
337
+ },
338
+ {
339
+ "command": "urActions.cancelBackground",
340
+ "when": "view == urActions && viewItem == backgroundTaskActive",
341
+ "group": "1_actions"
314
342
  }
315
343
  ],
316
344
  "editor/context": [