opencode-orchestrator 1.2.68 → 1.2.69

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 CHANGED
@@ -9,7 +9,7 @@
9
9
  [![MIT License](https://img.shields.io/badge/license-MIT-red.svg)](LICENSE)
10
10
  [![npm](https://img.shields.io/npm/v/opencode-orchestrator.svg)](https://www.npmjs.com/package/opencode-orchestrator)
11
11
  <!-- VERSION:START -->
12
- **Version:** `1.2.68`
12
+ **Version:** `1.2.69`
13
13
  <!-- VERSION:END -->
14
14
  </div>
15
15
 
@@ -21,6 +21,9 @@ export declare class TaskToastManager {
21
21
  private client;
22
22
  private concurrency;
23
23
  private todoSync;
24
+ private getSafeTaskDescription;
25
+ private getSafeAgent;
26
+ private showToast;
24
27
  /**
25
28
  * Initialize the manager with OpenCode client
26
29
  */
@@ -0,0 +1,3 @@
1
+ export declare function sanitizeToastTitle(value: string): string;
2
+ export declare function sanitizeToastInline(value: string, maxLength?: number): string;
3
+ export declare function sanitizeToastMessage(value: string, maxLength?: number, maxLines?: number): string;
package/dist/index.js CHANGED
@@ -939,6 +939,77 @@ var init_types4 = __esm({
939
939
  }
940
940
  });
941
941
 
942
+ // src/core/notification/toast-sanitizer.ts
943
+ function stripTerminalSequences(value) {
944
+ return value.replace(/\r\n?/g, "\n").replace(ANSI_PATTERN, "").replace(C0_CONTROL_PATTERN, "");
945
+ }
946
+ function truncate(value, maxLength) {
947
+ if (value.length <= maxLength) {
948
+ return value;
949
+ }
950
+ return `${value.slice(0, Math.max(0, maxLength - 1)).trimEnd()}\u2026`;
951
+ }
952
+ function sanitizeInternal(value, options) {
953
+ const stripped = stripTerminalSequences(value);
954
+ if (options.singleLine) {
955
+ const singleLine = stripped.replace(/\s+/g, " ").trim();
956
+ return singleLine ? truncate(singleLine, options.maxLength) : "";
957
+ }
958
+ const rawLines = stripped.split("\n");
959
+ const lines = [];
960
+ for (const rawLine of rawLines) {
961
+ const normalizedLine = rawLine.replace(/\t/g, " ").replace(/[^\S\n]+/g, " ").trim();
962
+ if (normalizedLine.length === 0) {
963
+ if (!options.singleLine && lines.length > 0 && lines[lines.length - 1] !== "") {
964
+ lines.push("");
965
+ }
966
+ continue;
967
+ }
968
+ lines.push(normalizedLine);
969
+ if (lines.length >= options.maxLines) {
970
+ break;
971
+ }
972
+ }
973
+ const collapsed = lines.join("\n").trim();
974
+ if (!collapsed) {
975
+ return "";
976
+ }
977
+ const truncated = truncate(collapsed, options.maxLength);
978
+ if (rawLines.length > options.maxLines && !truncated.endsWith("\u2026")) {
979
+ return truncate(`${truncated}
980
+ \u2026`, options.maxLength);
981
+ }
982
+ return truncated;
983
+ }
984
+ function sanitizeToastTitle(value) {
985
+ return sanitizeInternal(value, {
986
+ maxLength: 80,
987
+ maxLines: 1,
988
+ singleLine: true
989
+ });
990
+ }
991
+ function sanitizeToastInline(value, maxLength = 120) {
992
+ return sanitizeInternal(value, {
993
+ maxLength,
994
+ maxLines: 1,
995
+ singleLine: true
996
+ });
997
+ }
998
+ function sanitizeToastMessage(value, maxLength = 600, maxLines = 10) {
999
+ return sanitizeInternal(value, {
1000
+ maxLength,
1001
+ maxLines
1002
+ });
1003
+ }
1004
+ var ANSI_PATTERN, C0_CONTROL_PATTERN;
1005
+ var init_toast_sanitizer = __esm({
1006
+ "src/core/notification/toast-sanitizer.ts"() {
1007
+ "use strict";
1008
+ ANSI_PATTERN = /\u001B(?:\][^\u0007\u001B]*(?:\u0007|\u001B\\)|[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
1009
+ C0_CONTROL_PATTERN = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
1010
+ }
1011
+ });
1012
+
942
1013
  // src/core/notification/toast-core.ts
943
1014
  function initToastClient(client) {
944
1015
  tuiClient = client;
@@ -950,10 +1021,12 @@ function initToastClient(client) {
950
1021
  return cleanup;
951
1022
  }
952
1023
  function show(options) {
1024
+ const title = sanitizeToastTitle(options.title);
1025
+ const message = sanitizeToastMessage(options.message);
953
1026
  const toast = {
954
1027
  id: `toast_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
955
- title: options.title,
956
- message: options.message,
1028
+ title,
1029
+ message,
957
1030
  variant: options.variant || "info",
958
1031
  timestamp: /* @__PURE__ */ new Date(),
959
1032
  duration: options.duration ?? 5e3,
@@ -1009,6 +1082,7 @@ var init_toast_core = __esm({
1009
1082
  "src/core/notification/toast-core.ts"() {
1010
1083
  "use strict";
1011
1084
  init_shared();
1085
+ init_toast_sanitizer();
1012
1086
  tuiClient = null;
1013
1087
  toasts = [];
1014
1088
  handlers = [];
@@ -6488,11 +6562,30 @@ init_shared();
6488
6562
 
6489
6563
  // src/core/notification/task-toast-manager.ts
6490
6564
  init_shared();
6565
+ init_toast_sanitizer();
6491
6566
  var TaskToastManager = class {
6492
6567
  tasks = /* @__PURE__ */ new Map();
6493
6568
  client = null;
6494
6569
  concurrency = null;
6495
6570
  todoSync = null;
6571
+ getSafeTaskDescription(task) {
6572
+ return sanitizeToastInline(task.description, 100) || "Untitled task";
6573
+ }
6574
+ getSafeAgent(agent) {
6575
+ return sanitizeToastInline(agent, 32) || "unknown";
6576
+ }
6577
+ showToast(title, message, variant, duration5) {
6578
+ if (!this.client || !this.client.tui) return;
6579
+ this.client.tui.showToast({
6580
+ body: {
6581
+ title: sanitizeToastTitle(title) || "Notification",
6582
+ message: sanitizeToastMessage(message),
6583
+ variant,
6584
+ duration: duration5
6585
+ }
6586
+ }).catch(() => {
6587
+ });
6588
+ }
6496
6589
  /**
6497
6590
  * Initialize the manager with OpenCode client
6498
6591
  */
@@ -6607,7 +6700,7 @@ var TaskToastManager = class {
6607
6700
  const duration5 = this.formatDuration(task.startedAt);
6608
6701
  const bgTag = task.isBackground ? TUI_TAGS.BACKGROUND : TUI_TAGS.FOREGROUND;
6609
6702
  const isNew = newTask && task.id === newTask.id ? TUI_ICONS.NEW : "";
6610
- lines.push(`${bgTag} ${task.description} (${task.agent}) - ${duration5}${isNew}`);
6703
+ lines.push(`${bgTag} ${this.getSafeTaskDescription(task)} (${this.getSafeAgent(task.agent)}) - ${duration5}${isNew}`);
6611
6704
  }
6612
6705
  }
6613
6706
  if (queued.length > 0) {
@@ -6615,7 +6708,7 @@ var TaskToastManager = class {
6615
6708
  lines.push(`${TUI_ICONS.QUEUED} Queued (${queued.length}):`);
6616
6709
  for (const task of queued) {
6617
6710
  const bgTag = task.isBackground ? TUI_TAGS.WAITING : TUI_TAGS.PENDING;
6618
- lines.push(`${bgTag} ${task.description} (${task.agent})`);
6711
+ lines.push(`${bgTag} ${this.getSafeTaskDescription(task)} (${this.getSafeAgent(task.agent)})`);
6619
6712
  }
6620
6713
  }
6621
6714
  return lines.join("\n");
@@ -6624,40 +6717,37 @@ var TaskToastManager = class {
6624
6717
  * Show consolidated toast with all running/queued tasks
6625
6718
  */
6626
6719
  showTaskListToast(newTask) {
6627
- if (!this.client || !this.client.tui) return;
6628
6720
  const message = this.buildTaskListMessage(newTask);
6629
6721
  const running = this.getRunningTasks();
6630
6722
  const queued = this.getQueuedTasks();
6631
6723
  const title = newTask.isBackground ? `Background Task Started` : `Task Started`;
6632
- this.client.tui.showToast({
6633
- body: {
6634
- title,
6635
- message: message || `${newTask.description} (${newTask.agent})`,
6636
- variant: STATUS_LABEL.INFO,
6637
- duration: running.length + queued.length > 2 ? 5e3 : 3e3
6638
- }
6639
- }).catch(() => {
6640
- });
6724
+ this.showToast(
6725
+ title,
6726
+ message || `${this.getSafeTaskDescription(newTask)} (${this.getSafeAgent(newTask.agent)})`,
6727
+ STATUS_LABEL.INFO,
6728
+ running.length + queued.length > 2 ? 5e3 : 3e3
6729
+ );
6641
6730
  }
6642
6731
  /**
6643
6732
  * Show task completion toast
6644
6733
  */
6645
6734
  showCompletionToast(info) {
6646
- if (!this.client || !this.client.tui) return;
6647
6735
  this.removeTask(info.id);
6648
6736
  const remaining = this.getRunningTasks();
6649
6737
  const queued = this.getQueuedTasks();
6650
6738
  let message;
6651
6739
  let title;
6652
6740
  let variant;
6741
+ const safeDescription = sanitizeToastInline(info.description, 100) || "Untitled task";
6742
+ const safeError = info.error ? sanitizeToastMessage(info.error, 240, 4) : "";
6653
6743
  if (info.status === STATUS_LABEL.ERROR || info.status === STATUS_LABEL.CANCELLED || info.status === STATUS_LABEL.FAILED) {
6654
6744
  title = info.status === STATUS_LABEL.ERROR ? "Task Failed" : "Task Cancelled";
6655
- message = `[FAIL] "${info.description}" ${info.status}
6656
- ${info.error || ""}`;
6745
+ message = `[FAIL] "${safeDescription}" ${info.status}
6746
+ ${safeError}`;
6657
6747
  variant = STATUS_LABEL.ERROR;
6658
6748
  } else {
6659
6749
  title = "Task Completed";
6660
- message = `[DONE] "${info.description}" finished in ${info.duration}`;
6750
+ message = `[DONE] "${safeDescription}" finished in ${info.duration}`;
6661
6751
  variant = STATUS_LABEL.SUCCESS;
6662
6752
  }
6663
6753
  if (remaining.length > 0 || queued.length > 0) {
@@ -6665,41 +6755,28 @@ ${info.error || ""}`;
6665
6755
 
6666
6756
  Still running: ${remaining.length} | Queued: ${queued.length}`;
6667
6757
  }
6668
- this.client.tui.showToast({
6669
- body: {
6670
- title,
6671
- message,
6672
- variant,
6673
- duration: 5e3
6674
- }
6675
- }).catch(() => {
6676
- });
6758
+ this.showToast(title, message, variant, 5e3);
6677
6759
  }
6678
6760
  /**
6679
6761
  * Show all-tasks-complete summary toast
6680
6762
  */
6681
6763
  showAllCompleteToast(parentSessionID, completedTasks) {
6682
- if (!this.client || !this.client.tui) return;
6683
6764
  const successCount = completedTasks.filter((t) => t.status === STATUS_LABEL.COMPLETED).length;
6684
6765
  const failCount = completedTasks.filter((t) => t.status === STATUS_LABEL.ERROR || t.status === STATUS_LABEL.CANCELLED || t.status === STATUS_LABEL.FAILED).length;
6685
- const taskList = completedTasks.map((t) => `- [${t.status === STATUS_LABEL.COMPLETED ? "OK" : "FAIL"}] ${t.description} (${t.duration})`).join("\n");
6686
- this.client.tui.showToast({
6687
- body: {
6688
- title: "All Tasks Completed",
6689
- message: `${successCount} succeeded, ${failCount} failed
6766
+ const taskList = completedTasks.map((t) => `- [${t.status === STATUS_LABEL.COMPLETED ? "OK" : "FAIL"}] ${sanitizeToastInline(t.description, 80) || "Untitled task"} (${t.duration})`).join("\n");
6767
+ this.showToast(
6768
+ "All Tasks Completed",
6769
+ `${successCount} succeeded, ${failCount} failed
6690
6770
 
6691
6771
  ${taskList}`,
6692
- variant: failCount > 0 ? STATUS_LABEL.WARNING : STATUS_LABEL.SUCCESS,
6693
- duration: 7e3
6694
- }
6695
- }).catch(() => {
6696
- });
6772
+ failCount > 0 ? STATUS_LABEL.WARNING : STATUS_LABEL.SUCCESS,
6773
+ 7e3
6774
+ );
6697
6775
  }
6698
6776
  /**
6699
6777
  * Show Mission Complete toast (Grand Finale)
6700
6778
  */
6701
6779
  showMissionCompleteToast(title = "Mission Complete", message = "All tasks completed successfully.") {
6702
- if (!this.client || !this.client.tui) return;
6703
6780
  const decoratedMessage = `
6704
6781
  ${TUI_ICONS.MISSION_COMPLETE} ${TUI_MESSAGES.MISSION_COMPLETE_TITLE} ${TUI_ICONS.MISSION_COMPLETE}
6705
6782
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
@@ -6707,36 +6784,23 @@ ${message}
6707
6784
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
6708
6785
  ${TUI_MESSAGES.MISSION_COMPLETE_SUBTITLE}
6709
6786
  `.trim();
6710
- this.client.tui.showToast({
6711
- body: {
6712
- title: `${TUI_ICONS.SHIELD} ${title}`,
6713
- message: decoratedMessage,
6714
- variant: STATUS_LABEL.SUCCESS,
6715
- duration: 1e4
6716
- // Longer duration for the finale
6717
- }
6718
- }).catch(() => {
6719
- });
6787
+ this.showToast(`${TUI_ICONS.SHIELD} ${title}`, decoratedMessage, STATUS_LABEL.SUCCESS, 1e4);
6720
6788
  }
6721
6789
  /**
6722
6790
  * Show progress toast (for long-running tasks)
6723
6791
  */
6724
6792
  showProgressToast(taskId, progress) {
6725
- if (!this.client || !this.client.tui) return;
6726
6793
  const task = this.tasks.get(taskId);
6727
6794
  if (!task) return;
6728
6795
  const percentage = Math.round(progress.current / progress.total * 100);
6729
6796
  const progressBar = `[${"#".repeat(Math.floor(percentage / 10))}${"-".repeat(10 - Math.floor(percentage / 10))}]`;
6730
- this.client.tui.showToast({
6731
- body: {
6732
- title: `Task Progress: ${task.description}`,
6733
- message: `${progressBar} ${percentage}%
6797
+ this.showToast(
6798
+ `Task Progress: ${this.getSafeTaskDescription(task)}`,
6799
+ `${progressBar} ${percentage}%
6734
6800
  ${progress.message || ""}`,
6735
- variant: STATUS_LABEL.INFO,
6736
- duration: 2e3
6737
- }
6738
- }).catch(() => {
6739
- });
6801
+ STATUS_LABEL.INFO,
6802
+ 2e3
6803
+ );
6740
6804
  }
6741
6805
  /**
6742
6806
  * Clear all tracked tasks
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "opencode-orchestrator",
3
3
  "displayName": "OpenCode Orchestrator",
4
4
  "description": "Distributed Cognitive Architecture for OpenCode. Turns simple prompts into specialized multi-agent workflows (Planner, Coder, Reviewer).",
5
- "version": "1.2.68",
5
+ "version": "1.2.69",
6
6
  "author": "agnusdei1207",
7
7
  "license": "MIT",
8
8
  "repository": {