openviber 0.4.3 → 0.5.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/dist/cli/index.js CHANGED
@@ -35,10 +35,145 @@ var __decorateClass = (decorators, target, key, kind) => {
35
35
  };
36
36
 
37
37
  // src/core/message.ts
38
- var ConversationHistory;
38
+ var MessageQueue, ConversationHistory;
39
39
  var init_message = __esm({
40
40
  "src/core/message.ts"() {
41
41
  "use strict";
42
+ MessageQueue = class {
43
+ queue = [];
44
+ current;
45
+ processing = false;
46
+ listeners = /* @__PURE__ */ new Set();
47
+ nextId = 1;
48
+ /**
49
+ * Add message to queue
50
+ */
51
+ add(content, metadata) {
52
+ const message = {
53
+ id: `msg-${this.nextId++}`,
54
+ content,
55
+ status: "queued",
56
+ timestamp: Date.now(),
57
+ metadata
58
+ };
59
+ this.queue.push(message);
60
+ this.notify();
61
+ return message.id;
62
+ }
63
+ /**
64
+ * Get next message from queue
65
+ */
66
+ next() {
67
+ const message = this.queue.shift();
68
+ if (message) {
69
+ message.status = "processing";
70
+ this.current = message;
71
+ this.processing = true;
72
+ this.notify();
73
+ }
74
+ return message;
75
+ }
76
+ /**
77
+ * Mark current message as complete
78
+ */
79
+ complete(messageId) {
80
+ if (this.current?.id === messageId) {
81
+ this.current.status = "completed";
82
+ this.current = void 0;
83
+ this.processing = false;
84
+ this.notify();
85
+ }
86
+ }
87
+ /**
88
+ * Mark current message as error
89
+ */
90
+ error(messageId, error) {
91
+ if (this.current?.id === messageId) {
92
+ this.current.status = "error";
93
+ this.current.error = error;
94
+ this.current = void 0;
95
+ this.processing = false;
96
+ this.notify();
97
+ }
98
+ }
99
+ /**
100
+ * Remove message from queue
101
+ */
102
+ remove(messageId) {
103
+ const index = this.queue.findIndex((m) => m.id === messageId);
104
+ if (index > -1) {
105
+ this.queue.splice(index, 1);
106
+ this.notify();
107
+ return true;
108
+ }
109
+ return false;
110
+ }
111
+ /**
112
+ * Reorder queue
113
+ */
114
+ reorder(messageId, newIndex) {
115
+ const currentIndex = this.queue.findIndex((m) => m.id === messageId);
116
+ if (currentIndex > -1 && newIndex >= 0 && newIndex < this.queue.length) {
117
+ const [message] = this.queue.splice(currentIndex, 1);
118
+ this.queue.splice(newIndex, 0, message);
119
+ this.notify();
120
+ }
121
+ }
122
+ /**
123
+ * Edit queued message
124
+ */
125
+ edit(messageId, content) {
126
+ const message = this.queue.find((m) => m.id === messageId);
127
+ if (message && message.status === "queued") {
128
+ message.content = content;
129
+ message.edited = true;
130
+ this.notify();
131
+ }
132
+ }
133
+ /**
134
+ * Clear all queued messages
135
+ */
136
+ clear() {
137
+ this.queue = [];
138
+ this.notify();
139
+ }
140
+ /**
141
+ * Get queue state
142
+ */
143
+ getState() {
144
+ return {
145
+ current: this.current,
146
+ queue: [...this.queue],
147
+ processing: this.processing
148
+ };
149
+ }
150
+ /**
151
+ * Subscribe to queue changes
152
+ */
153
+ subscribe(listener) {
154
+ this.listeners.add(listener);
155
+ return () => this.listeners.delete(listener);
156
+ }
157
+ /**
158
+ * Check if queue is empty
159
+ */
160
+ isEmpty() {
161
+ return this.queue.length === 0;
162
+ }
163
+ /**
164
+ * Check if processing
165
+ */
166
+ isProcessing() {
167
+ return this.processing;
168
+ }
169
+ /**
170
+ * Notify listeners
171
+ */
172
+ notify() {
173
+ const state = this.getState();
174
+ this.listeners.forEach((listener) => listener(state));
175
+ }
176
+ };
42
177
  ConversationHistory = class {
43
178
  messages = [];
44
179
  add(message) {
@@ -4875,50 +5010,20 @@ var init_runtime = __esm({
4875
5010
  });
4876
5011
 
4877
5012
  // src/daemon/terminal.ts
4878
- import { spawn, execSync } from "child_process";
5013
+ import { spawn, spawnSync, execSync } from "child_process";
4879
5014
  import { EventEmitter } from "events";
4880
- function listSessions() {
4881
- try {
4882
- const out = execSync(
4883
- "tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
4884
- { encoding: "utf8", stdio: "pipe" }
4885
- ).trim();
4886
- if (!out) return [];
4887
- return out.split("\n").map((line) => {
4888
- const [name, windows, attached] = line.split("|");
4889
- return {
4890
- name,
4891
- windows: parseInt(windows, 10) || 0,
4892
- attached: attached === "1"
4893
- };
4894
- });
4895
- } catch {
4896
- return [];
4897
- }
5015
+ import { randomUUID } from "crypto";
5016
+ function sanitizeName(input) {
5017
+ return input.replace(SAFE_NAME_RE, "-");
4898
5018
  }
4899
- function listPanes() {
4900
- try {
4901
- const out = execSync(
4902
- "tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{pane_index}|#{pane_current_command}' 2>/dev/null",
4903
- { encoding: "utf8", stdio: "pipe" }
4904
- ).trim();
4905
- if (!out) return [];
4906
- return out.split("\n").map((line) => {
4907
- const [session, window2, windowName, pane, command] = line.split("|");
4908
- return {
4909
- session,
4910
- window: window2,
4911
- windowName,
4912
- pane,
4913
- command,
4914
- target: `${session}:${window2}.${pane}`
4915
- };
4916
- });
4917
- } catch {
4918
- return [];
5019
+ function resolveAppTarget(target, appHint) {
5020
+ if (target.includes("::")) {
5021
+ const [appId, ...rest] = target.split("::");
5022
+ return { appId, rawTarget: rest.join("::") };
4919
5023
  }
5024
+ return { appId: appHint || "tmux", rawTarget: target };
4920
5025
  }
4921
- function sendKeys(target, keys, pressEnter = false) {
5026
+ function sendTmuxKeys(target, keys, pressEnter = false) {
4922
5027
  try {
4923
5028
  const args = ["send-keys", "-t", target, keys];
4924
5029
  if (pressEnter) args.push("Enter");
@@ -4931,7 +5036,7 @@ function sendKeys(target, keys, pressEnter = false) {
4931
5036
  return false;
4932
5037
  }
4933
5038
  }
4934
- function capturePane(target, lines = 500) {
5039
+ function captureTmuxPane(target, lines = 500) {
4935
5040
  const cmds = [
4936
5041
  `tmux capture-pane -t '${target}' -pae -S -${lines}`,
4937
5042
  `tmux capture-pane -t '${target}' -pe -S -${lines}`
@@ -4944,38 +5049,24 @@ function capturePane(target, lines = 500) {
4944
5049
  }
4945
5050
  return "";
4946
5051
  }
4947
- function resizePane(target, cols, rows) {
4948
- try {
4949
- execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
4950
- encoding: "utf8",
4951
- stdio: "pipe"
4952
- });
4953
- return true;
4954
- } catch {
4955
- return false;
4956
- }
4957
- }
4958
- var TerminalStream, TerminalManager;
5052
+ var SAFE_NAME_RE, TmuxTerminalStream, TmuxTerminalApp, ShellTerminalApp, TerminalManager;
4959
5053
  var init_terminal = __esm({
4960
5054
  "src/daemon/terminal.ts"() {
4961
5055
  "use strict";
4962
- TerminalStream = class extends EventEmitter {
4963
- target;
4964
- catProcess = null;
4965
- pipePath;
4966
- isAttached = false;
5056
+ SAFE_NAME_RE = /[^a-zA-Z0-9_.:-]/g;
5057
+ TmuxTerminalStream = class extends EventEmitter {
4967
5058
  constructor(target) {
4968
5059
  super();
4969
5060
  this.target = target;
4970
5061
  this.pipePath = `/tmp/viber-term-${target.replace(/[^a-zA-Z0-9]/g, "-")}-${Date.now()}`;
4971
5062
  }
4972
- /**
4973
- * Start streaming the pane output
4974
- */
5063
+ catProcess = null;
5064
+ pipePath;
5065
+ isAttached = false;
4975
5066
  async attach() {
4976
5067
  if (this.isAttached) return true;
4977
5068
  try {
4978
- const history = capturePane(this.target, 200);
5069
+ const history = captureTmuxPane(this.target, 200);
4979
5070
  if (history) {
4980
5071
  this.emit("data", history);
4981
5072
  }
@@ -5005,9 +5096,6 @@ var init_terminal = __esm({
5005
5096
  return false;
5006
5097
  }
5007
5098
  }
5008
- /**
5009
- * Stop streaming
5010
- */
5011
5099
  detach() {
5012
5100
  if (!this.isAttached) return;
5013
5101
  try {
@@ -5016,12 +5104,6 @@ var init_terminal = __esm({
5016
5104
  }
5017
5105
  this.cleanup();
5018
5106
  }
5019
- /**
5020
- * Send input to the pane
5021
- */
5022
- sendInput(keys) {
5023
- return sendKeys(this.target, keys, false);
5024
- }
5025
5107
  cleanup() {
5026
5108
  this.isAttached = false;
5027
5109
  if (this.catProcess) {
@@ -5038,20 +5120,61 @@ var init_terminal = __esm({
5038
5120
  return this.isAttached;
5039
5121
  }
5040
5122
  };
5041
- TerminalManager = class {
5123
+ TmuxTerminalApp = class {
5124
+ id = "tmux";
5125
+ label = "tmux";
5042
5126
  streams = /* @__PURE__ */ new Map();
5043
- /**
5044
- * List all available terminals
5045
- */
5046
- list() {
5047
- return {
5048
- sessions: listSessions(),
5049
- panes: listPanes()
5050
- };
5127
+ isAvailable() {
5128
+ try {
5129
+ execSync("tmux -V", { stdio: "pipe" });
5130
+ return true;
5131
+ } catch {
5132
+ return false;
5133
+ }
5134
+ }
5135
+ listSessions() {
5136
+ try {
5137
+ const out = execSync(
5138
+ "tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
5139
+ { encoding: "utf8", stdio: "pipe" }
5140
+ ).trim();
5141
+ if (!out) return [];
5142
+ return out.split("\n").map((line) => {
5143
+ const [name, windows, attached] = line.split("|");
5144
+ return {
5145
+ appId: this.id,
5146
+ name,
5147
+ windows: parseInt(windows, 10) || 0,
5148
+ attached: attached === "1"
5149
+ };
5150
+ });
5151
+ } catch {
5152
+ return [];
5153
+ }
5154
+ }
5155
+ listPanes() {
5156
+ try {
5157
+ const out = execSync(
5158
+ "tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{pane_index}|#{pane_current_command}' 2>/dev/null",
5159
+ { encoding: "utf8", stdio: "pipe" }
5160
+ ).trim();
5161
+ if (!out) return [];
5162
+ return out.split("\n").map((line) => {
5163
+ const [session, window2, windowName, pane, command] = line.split("|");
5164
+ return {
5165
+ appId: this.id,
5166
+ session,
5167
+ window: window2,
5168
+ windowName,
5169
+ pane,
5170
+ command,
5171
+ target: `${session}:${window2}.${pane}`
5172
+ };
5173
+ });
5174
+ } catch {
5175
+ return [];
5176
+ }
5051
5177
  }
5052
- /**
5053
- * Attach to a pane and return the stream
5054
- */
5055
5178
  async attach(target, onData, onClose) {
5056
5179
  let stream = this.streams.get(target);
5057
5180
  if (stream && stream.attached) {
@@ -5059,7 +5182,7 @@ var init_terminal = __esm({
5059
5182
  stream.on("close", onClose);
5060
5183
  return true;
5061
5184
  }
5062
- stream = new TerminalStream(target);
5185
+ stream = new TmuxTerminalStream(target);
5063
5186
  stream.on("data", onData);
5064
5187
  stream.on("close", () => {
5065
5188
  this.streams.delete(target);
@@ -5071,31 +5194,62 @@ var init_terminal = __esm({
5071
5194
  }
5072
5195
  return ok;
5073
5196
  }
5074
- /**
5075
- * Detach from a pane
5076
- */
5077
5197
  detach(target) {
5078
5198
  const stream = this.streams.get(target);
5079
- if (stream) {
5080
- stream.detach();
5081
- this.streams.delete(target);
5082
- }
5199
+ if (!stream) return;
5200
+ stream.detach();
5201
+ this.streams.delete(target);
5083
5202
  }
5084
- /**
5085
- * Send input to a pane
5086
- */
5087
5203
  sendInput(target, keys) {
5088
- return sendKeys(target, keys, false);
5204
+ return sendTmuxKeys(target, keys);
5089
5205
  }
5090
- /**
5091
- * Resize pane to match web terminal
5092
- */
5093
5206
  resize(target, cols, rows) {
5094
- return resizePane(target, cols, rows);
5207
+ try {
5208
+ execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
5209
+ encoding: "utf8",
5210
+ stdio: "pipe"
5211
+ });
5212
+ return true;
5213
+ } catch {
5214
+ return false;
5215
+ }
5216
+ }
5217
+ createSession(sessionName, windowName = "main", cwd) {
5218
+ const safeSession = sanitizeName(sessionName || "coding");
5219
+ const safeWindow = sanitizeName(windowName || "main");
5220
+ try {
5221
+ execSync(`tmux has-session -t '${safeSession}' 2>/dev/null`, { stdio: "pipe" });
5222
+ return { ok: true, appId: this.id, sessionName: safeSession, created: false };
5223
+ } catch {
5224
+ }
5225
+ const args = ["new-session", "-d", "-s", safeSession, "-n", safeWindow];
5226
+ if (cwd) {
5227
+ args.push("-c", cwd);
5228
+ }
5229
+ const result = spawnSync("tmux", args, {
5230
+ encoding: "utf8",
5231
+ stdio: "pipe"
5232
+ });
5233
+ if (result.error) {
5234
+ return {
5235
+ ok: false,
5236
+ appId: this.id,
5237
+ sessionName: safeSession,
5238
+ created: false,
5239
+ error: `Failed to start tmux: ${result.error.message}`
5240
+ };
5241
+ }
5242
+ if (result.status !== 0) {
5243
+ return {
5244
+ ok: false,
5245
+ appId: this.id,
5246
+ sessionName: safeSession,
5247
+ created: false,
5248
+ error: (result.stderr || result.stdout || "Failed to create tmux session").trim()
5249
+ };
5250
+ }
5251
+ return { ok: true, appId: this.id, sessionName: safeSession, created: true };
5095
5252
  }
5096
- /**
5097
- * Detach all streams
5098
- */
5099
5253
  detachAll() {
5100
5254
  for (const stream of this.streams.values()) {
5101
5255
  stream.detach();
@@ -5103,6 +5257,177 @@ var init_terminal = __esm({
5103
5257
  this.streams.clear();
5104
5258
  }
5105
5259
  };
5260
+ ShellTerminalApp = class {
5261
+ id = "shell";
5262
+ label = "shell";
5263
+ sessions = /* @__PURE__ */ new Map();
5264
+ isAvailable() {
5265
+ return true;
5266
+ }
5267
+ listSessions() {
5268
+ return Array.from(this.sessions.values()).map((state) => ({
5269
+ appId: this.id,
5270
+ name: state.sessionName,
5271
+ windows: 1,
5272
+ attached: state.listeners.size > 0
5273
+ }));
5274
+ }
5275
+ listPanes() {
5276
+ return Array.from(this.sessions.entries()).map(([id, state]) => ({
5277
+ appId: this.id,
5278
+ session: state.sessionName,
5279
+ window: "0",
5280
+ windowName: state.windowName,
5281
+ pane: state.pane,
5282
+ command: process.env.SHELL || "sh",
5283
+ target: id
5284
+ }));
5285
+ }
5286
+ async attach(target, onData, onClose) {
5287
+ const state = this.sessions.get(target);
5288
+ if (!state) return false;
5289
+ state.listeners.add(onData);
5290
+ state.closeListeners.add(onClose);
5291
+ if (state.history.length > 0) {
5292
+ onData(state.history.join(""));
5293
+ }
5294
+ return true;
5295
+ }
5296
+ detach(target) {
5297
+ const state = this.sessions.get(target);
5298
+ if (!state) return;
5299
+ state.listeners.clear();
5300
+ state.closeListeners.clear();
5301
+ }
5302
+ sendInput(target, keys) {
5303
+ const state = this.sessions.get(target);
5304
+ if (!state || !state.proc.stdin?.writable) return false;
5305
+ state.proc.stdin.write(keys);
5306
+ return true;
5307
+ }
5308
+ resize(_target, _cols, _rows) {
5309
+ return true;
5310
+ }
5311
+ createSession(sessionName, windowName = "main", cwd) {
5312
+ const safeSession = sanitizeName(sessionName || `shell-${Date.now()}`);
5313
+ const shell = process.env.SHELL || "sh";
5314
+ const target = `${safeSession}:${randomUUID().slice(0, 8)}`;
5315
+ if (this.sessions.has(target)) {
5316
+ return { ok: true, appId: this.id, sessionName: safeSession, created: false };
5317
+ }
5318
+ const proc = spawn(shell, [], {
5319
+ cwd,
5320
+ stdio: ["pipe", "pipe", "pipe"],
5321
+ env: process.env
5322
+ });
5323
+ if (!proc.pid) {
5324
+ return {
5325
+ ok: false,
5326
+ appId: this.id,
5327
+ sessionName: safeSession,
5328
+ created: false,
5329
+ error: "Failed to start shell process"
5330
+ };
5331
+ }
5332
+ const state = {
5333
+ proc,
5334
+ sessionName: safeSession,
5335
+ windowName,
5336
+ pane: "0",
5337
+ history: [],
5338
+ listeners: /* @__PURE__ */ new Set(),
5339
+ closeListeners: /* @__PURE__ */ new Set()
5340
+ };
5341
+ const pushChunk = (chunk) => {
5342
+ const text = chunk.toString();
5343
+ state.history.push(text);
5344
+ if (state.history.length > 200) {
5345
+ state.history.shift();
5346
+ }
5347
+ for (const listener of state.listeners) {
5348
+ listener(text);
5349
+ }
5350
+ };
5351
+ proc.stdout?.on("data", pushChunk);
5352
+ proc.stderr?.on("data", pushChunk);
5353
+ proc.on("close", () => {
5354
+ for (const onClose of state.closeListeners) {
5355
+ onClose();
5356
+ }
5357
+ this.sessions.delete(target);
5358
+ });
5359
+ this.sessions.set(target, state);
5360
+ return { ok: true, appId: this.id, sessionName: safeSession, created: true };
5361
+ }
5362
+ detachAll() {
5363
+ for (const [id, state] of this.sessions.entries()) {
5364
+ state.proc.kill();
5365
+ this.sessions.delete(id);
5366
+ }
5367
+ }
5368
+ };
5369
+ TerminalManager = class {
5370
+ apps = /* @__PURE__ */ new Map();
5371
+ constructor(adapters) {
5372
+ const defaultAdapters = adapters ?? [new TmuxTerminalApp(), new ShellTerminalApp()];
5373
+ for (const adapter of defaultAdapters) {
5374
+ this.apps.set(adapter.id, adapter);
5375
+ }
5376
+ }
5377
+ list() {
5378
+ const metadata = Array.from(this.apps.values()).map((app) => ({
5379
+ id: app.id,
5380
+ label: app.label,
5381
+ available: app.isAvailable()
5382
+ }));
5383
+ const sessions = [];
5384
+ const panes = [];
5385
+ for (const app of this.apps.values()) {
5386
+ if (!app.isAvailable()) continue;
5387
+ sessions.push(...app.listSessions());
5388
+ panes.push(...app.listPanes());
5389
+ }
5390
+ return { apps: metadata, sessions, panes };
5391
+ }
5392
+ async attach(target, onData, onClose, appHint) {
5393
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
5394
+ const app = this.apps.get(appId);
5395
+ if (!app || !app.isAvailable()) return false;
5396
+ return app.attach(rawTarget, onData, onClose);
5397
+ }
5398
+ detach(target, appHint) {
5399
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
5400
+ this.apps.get(appId)?.detach(rawTarget);
5401
+ }
5402
+ sendInput(target, keys, appHint) {
5403
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
5404
+ const app = this.apps.get(appId);
5405
+ return !!app && app.isAvailable() ? app.sendInput(rawTarget, keys) : false;
5406
+ }
5407
+ resize(target, cols, rows, appHint) {
5408
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
5409
+ const app = this.apps.get(appId);
5410
+ return !!app && app.isAvailable() ? app.resize(rawTarget, cols, rows) : false;
5411
+ }
5412
+ createSession(sessionName, windowName = "main", cwd, appId = "tmux") {
5413
+ const app = this.apps.get(appId);
5414
+ if (!app || !app.isAvailable()) {
5415
+ return {
5416
+ ok: false,
5417
+ appId,
5418
+ sessionName,
5419
+ created: false,
5420
+ error: `Terminal app '${appId}' is not available`
5421
+ };
5422
+ }
5423
+ return app.createSession(sessionName, windowName, cwd);
5424
+ }
5425
+ detachAll() {
5426
+ for (const app of this.apps.values()) {
5427
+ app.detachAll();
5428
+ }
5429
+ }
5430
+ };
5106
5431
  }
5107
5432
  });
5108
5433
 
@@ -5267,16 +5592,16 @@ var init_controller = __esm({
5267
5592
  this.handleTerminalList();
5268
5593
  break;
5269
5594
  case "terminal:attach":
5270
- await this.handleTerminalAttach(message.target);
5595
+ await this.handleTerminalAttach(message.target, message.appId);
5271
5596
  break;
5272
5597
  case "terminal:detach":
5273
- this.handleTerminalDetach(message.target);
5598
+ this.handleTerminalDetach(message.target, message.appId);
5274
5599
  break;
5275
5600
  case "terminal:input":
5276
- this.handleTerminalInput(message.target, message.keys);
5601
+ this.handleTerminalInput(message.target, message.keys, message.appId);
5277
5602
  break;
5278
5603
  case "terminal:resize":
5279
- this.handleTerminalResize(message.target, message.cols, message.rows);
5604
+ this.handleTerminalResize(message.target, message.cols, message.rows, message.appId);
5280
5605
  break;
5281
5606
  }
5282
5607
  } catch (error) {
@@ -5306,7 +5631,87 @@ var init_controller = __esm({
5306
5631
  },
5307
5632
  messages
5308
5633
  );
5309
- const finalText = await streamResult.text;
5634
+ let finalText = "";
5635
+ let pendingDelta = "";
5636
+ let lastProgressAt = 0;
5637
+ const flushProgress = (force = false) => {
5638
+ if (!pendingDelta) return;
5639
+ const now = Date.now();
5640
+ if (!force && now - lastProgressAt < 250) return;
5641
+ this.send({
5642
+ type: "task:progress",
5643
+ taskId,
5644
+ event: {
5645
+ kind: "text-delta",
5646
+ delta: pendingDelta,
5647
+ totalLength: finalText.length,
5648
+ at: new Date(now).toISOString()
5649
+ }
5650
+ });
5651
+ pendingDelta = "";
5652
+ lastProgressAt = now;
5653
+ };
5654
+ this.send({
5655
+ type: "task:progress",
5656
+ taskId,
5657
+ event: {
5658
+ kind: "status",
5659
+ phase: "executing",
5660
+ message: "Agent execution started",
5661
+ at: (/* @__PURE__ */ new Date()).toISOString()
5662
+ }
5663
+ });
5664
+ for await (const part of streamResult.fullStream) {
5665
+ switch (part.type) {
5666
+ case "text-delta":
5667
+ if (part.text) {
5668
+ finalText += part.text;
5669
+ pendingDelta += part.text;
5670
+ flushProgress(false);
5671
+ }
5672
+ break;
5673
+ case "tool-call":
5674
+ this.send({
5675
+ type: "task:progress",
5676
+ taskId,
5677
+ event: {
5678
+ kind: "tool-call",
5679
+ toolName: part.toolName,
5680
+ toolCallId: part.toolCallId,
5681
+ at: (/* @__PURE__ */ new Date()).toISOString()
5682
+ }
5683
+ });
5684
+ break;
5685
+ case "tool-result":
5686
+ this.send({
5687
+ type: "task:progress",
5688
+ taskId,
5689
+ event: {
5690
+ kind: "tool-result",
5691
+ toolCallId: part.toolCallId,
5692
+ at: (/* @__PURE__ */ new Date()).toISOString()
5693
+ }
5694
+ });
5695
+ break;
5696
+ case "error":
5697
+ throw new Error(part.error?.message || "Task stream error");
5698
+ case "finish":
5699
+ flushProgress(true);
5700
+ this.send({
5701
+ type: "task:progress",
5702
+ taskId,
5703
+ event: {
5704
+ kind: "status",
5705
+ phase: "verifying",
5706
+ message: "Agent execution finished, preparing final output",
5707
+ at: (/* @__PURE__ */ new Date()).toISOString()
5708
+ }
5709
+ });
5710
+ break;
5711
+ default:
5712
+ break;
5713
+ }
5714
+ }
5310
5715
  this.send({
5311
5716
  type: "task:completed",
5312
5717
  taskId,
@@ -5343,33 +5748,34 @@ var init_controller = __esm({
5343
5748
  }
5344
5749
  // ==================== Terminal Streaming ====================
5345
5750
  handleTerminalList() {
5346
- const { sessions, panes } = this.terminalManager.list();
5347
- this.send({ type: "terminal:list", sessions, panes });
5751
+ const { apps, sessions, panes } = this.terminalManager.list();
5752
+ this.send({ type: "terminal:list", apps, sessions, panes });
5348
5753
  }
5349
- async handleTerminalAttach(target) {
5754
+ async handleTerminalAttach(target, appId) {
5350
5755
  console.log(`[Viber] Attaching to terminal: ${target}`);
5351
5756
  const ok = await this.terminalManager.attach(
5352
5757
  target,
5353
5758
  (data) => {
5354
- this.send({ type: "terminal:output", target, data });
5759
+ this.send({ type: "terminal:output", target, appId, data });
5355
5760
  },
5356
5761
  () => {
5357
- this.send({ type: "terminal:detached", target });
5358
- }
5762
+ this.send({ type: "terminal:detached", target, appId });
5763
+ },
5764
+ appId
5359
5765
  );
5360
- this.send({ type: "terminal:attached", target, ok });
5766
+ this.send({ type: "terminal:attached", target, appId, ok });
5361
5767
  }
5362
- handleTerminalDetach(target) {
5768
+ handleTerminalDetach(target, appId) {
5363
5769
  console.log(`[Viber] Detaching from terminal: ${target}`);
5364
- this.terminalManager.detach(target);
5365
- this.send({ type: "terminal:detached", target });
5770
+ this.terminalManager.detach(target, appId);
5771
+ this.send({ type: "terminal:detached", target, appId });
5366
5772
  }
5367
- handleTerminalInput(target, keys) {
5368
- this.terminalManager.sendInput(target, keys);
5773
+ handleTerminalInput(target, keys, appId) {
5774
+ this.terminalManager.sendInput(target, keys, appId);
5369
5775
  }
5370
- handleTerminalResize(target, cols, rows) {
5371
- const ok = this.terminalManager.resize(target, cols, rows);
5372
- this.send({ type: "terminal:resized", target, ok });
5776
+ handleTerminalResize(target, cols, rows, appId) {
5777
+ const ok = this.terminalManager.resize(target, cols, rows, appId);
5778
+ this.send({ type: "terminal:resized", target, appId, ok });
5373
5779
  }
5374
5780
  // ==================== Communication ====================
5375
5781
  send(message) {
@@ -5521,7 +5927,7 @@ var init_antigravity = __esm({
5521
5927
 
5522
5928
  // src/skills/cursor-agent/index.ts
5523
5929
  import { z as z9 } from "zod";
5524
- import { execSync as execSync2, spawnSync } from "child_process";
5930
+ import { execSync as execSync2, spawnSync as spawnSync2 } from "child_process";
5525
5931
  import * as path11 from "path";
5526
5932
  function runInTmux(goal, cwd, waitSeconds) {
5527
5933
  execSync2(
@@ -5532,17 +5938,17 @@ function runInTmux(goal, cwd, waitSeconds) {
5532
5938
  }
5533
5939
  );
5534
5940
  const cdCmd = cwd.includes(" ") ? `cd "${cwd.replace(/"/g, '\\"')}"` : `cd ${cwd}`;
5535
- spawnSync("tmux", ["send-keys", "-t", TMUX_SESSION, cdCmd, "Enter"], {
5941
+ spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, cdCmd, "Enter"], {
5536
5942
  encoding: "utf8",
5537
5943
  stdio: "pipe"
5538
5944
  });
5539
5945
  const sendArg = `agent -p ${JSON.stringify(goal)}`;
5540
- spawnSync("tmux", ["send-keys", "-t", TMUX_SESSION, sendArg, "Enter"], {
5946
+ spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, sendArg, "Enter"], {
5541
5947
  encoding: "utf8",
5542
5948
  stdio: "pipe"
5543
5949
  });
5544
5950
  execSync2("sleep 3", { encoding: "utf8", stdio: "pipe" });
5545
- spawnSync("tmux", ["send-keys", "-t", TMUX_SESSION, "a"], {
5951
+ spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, "a"], {
5546
5952
  encoding: "utf8",
5547
5953
  stdio: "pipe"
5548
5954
  });
@@ -5594,7 +6000,7 @@ var init_cursor_agent = __esm({
5594
6000
 
5595
6001
  // src/skills/tmux/index.ts
5596
6002
  import { z as z10 } from "zod";
5597
- import { execSync as execSync3, spawnSync as spawnSync2 } from "child_process";
6003
+ import { execSync as execSync3, spawnSync as spawnSync3 } from "child_process";
5598
6004
  import * as path12 from "path";
5599
6005
  function safeTarget(t) {
5600
6006
  return t.replace(SAFE_RE, "-");
@@ -5606,11 +6012,11 @@ function runInTmux2(sessionName, command, cwd, waitSeconds) {
5606
6012
  { encoding: "utf8", stdio: "pipe" }
5607
6013
  );
5608
6014
  const cdCmd = cwd.includes(" ") ? `cd "${cwd.replace(/"/g, '\\"')}"` : `cd ${cwd}`;
5609
- spawnSync2("tmux", ["send-keys", "-t", safeSession, cdCmd, "Enter"], {
6015
+ spawnSync3("tmux", ["send-keys", "-t", safeSession, cdCmd, "Enter"], {
5610
6016
  encoding: "utf8",
5611
6017
  stdio: "pipe"
5612
6018
  });
5613
- spawnSync2("tmux", ["send-keys", "-t", safeSession, command, "Enter"], {
6019
+ spawnSync3("tmux", ["send-keys", "-t", safeSession, command, "Enter"], {
5614
6020
  encoding: "utf8",
5615
6021
  stdio: "pipe"
5616
6022
  });
@@ -5698,9 +6104,9 @@ function getTools3() {
5698
6104
  if (args.cwd) {
5699
6105
  newWinArgs.push("-c", path12.resolve(args.cwd));
5700
6106
  }
5701
- spawnSync2("tmux", newWinArgs, { encoding: "utf8", stdio: "pipe" });
6107
+ spawnSync3("tmux", newWinArgs, { encoding: "utf8", stdio: "pipe" });
5702
6108
  if (args.command) {
5703
- spawnSync2("tmux", ["send-keys", "-t", session, args.command, "Enter"], {
6109
+ spawnSync3("tmux", ["send-keys", "-t", session, args.command, "Enter"], {
5704
6110
  encoding: "utf8",
5705
6111
  stdio: "pipe"
5706
6112
  });
@@ -5736,9 +6142,9 @@ function getTools3() {
5736
6142
  if (args.cwd) {
5737
6143
  splitArgs.push("-c", path12.resolve(args.cwd));
5738
6144
  }
5739
- spawnSync2("tmux", splitArgs, { encoding: "utf8", stdio: "pipe" });
6145
+ spawnSync3("tmux", splitArgs, { encoding: "utf8", stdio: "pipe" });
5740
6146
  if (args.command) {
5741
- spawnSync2("tmux", ["send-keys", "-t", t, args.command, "Enter"], {
6147
+ spawnSync3("tmux", ["send-keys", "-t", t, args.command, "Enter"], {
5742
6148
  encoding: "utf8",
5743
6149
  stdio: "pipe"
5744
6150
  });
@@ -5767,7 +6173,7 @@ function getTools3() {
5767
6173
  execute: async (args) => {
5768
6174
  const t = safeTarget(args.target);
5769
6175
  try {
5770
- spawnSync2("tmux", ["send-keys", "-t", t, args.keys, ...args.pressEnter !== false ? ["Enter"] : []], {
6176
+ spawnSync3("tmux", ["send-keys", "-t", t, args.keys, ...args.pressEnter !== false ? ["Enter"] : []], {
5771
6177
  encoding: "utf8",
5772
6178
  stdio: "pipe"
5773
6179
  });
@@ -5951,49 +6357,74 @@ var init_local_server = __esm({
5951
6357
  this.handleTerminalList(ws);
5952
6358
  break;
5953
6359
  case "terminal:attach":
5954
- this.handleTerminalAttach(ws, msg.target);
6360
+ this.handleTerminalAttach(ws, msg.target, msg.appId);
5955
6361
  break;
5956
6362
  case "terminal:detach":
5957
- this.handleTerminalDetach(ws, msg.target);
6363
+ this.handleTerminalDetach(ws, msg.target, msg.appId);
5958
6364
  break;
5959
6365
  case "terminal:input":
5960
- this.handleTerminalInput(msg.target, msg.keys);
6366
+ this.handleTerminalInput(msg.target, msg.keys, msg.appId);
5961
6367
  break;
5962
6368
  case "terminal:resize":
5963
- this.handleTerminalResize(ws, msg.target, msg.cols, msg.rows);
6369
+ this.handleTerminalResize(ws, msg.target, msg.cols, msg.rows, msg.appId);
6370
+ break;
6371
+ case "terminal:create-session":
6372
+ this.handleTerminalCreateSession(
6373
+ ws,
6374
+ msg.sessionName,
6375
+ msg.windowName,
6376
+ msg.cwd,
6377
+ msg.appId
6378
+ );
5964
6379
  break;
5965
6380
  default:
5966
6381
  console.log(`[Viber] Unknown message type: ${msg.type}`);
5967
6382
  }
5968
6383
  }
5969
6384
  handleTerminalList(ws) {
5970
- const { sessions, panes } = this.terminalManager.list();
5971
- this.send(ws, { type: "terminal:list", sessions, panes });
6385
+ const { apps, sessions, panes } = this.terminalManager.list();
6386
+ this.send(ws, { type: "terminal:list", apps, sessions, panes });
5972
6387
  }
5973
- async handleTerminalAttach(ws, target) {
6388
+ async handleTerminalAttach(ws, target, appId) {
5974
6389
  console.log(`[Viber] Attaching to terminal: ${target}`);
5975
6390
  const ok = await this.terminalManager.attach(
5976
6391
  target,
5977
6392
  (data) => {
5978
- this.send(ws, { type: "terminal:output", target, data });
6393
+ this.send(ws, { type: "terminal:output", target, appId, data });
5979
6394
  },
5980
6395
  () => {
5981
- this.send(ws, { type: "terminal:detached", target });
5982
- }
6396
+ this.send(ws, { type: "terminal:detached", target, appId });
6397
+ },
6398
+ appId
5983
6399
  );
5984
- this.send(ws, { type: "terminal:attached", target, ok });
6400
+ this.send(ws, { type: "terminal:attached", target, appId, ok });
5985
6401
  }
5986
- handleTerminalDetach(ws, target) {
6402
+ handleTerminalDetach(ws, target, appId) {
5987
6403
  console.log(`[Viber] Detaching from terminal: ${target}`);
5988
- this.terminalManager.detach(target);
5989
- this.send(ws, { type: "terminal:detached", target });
5990
- }
5991
- handleTerminalInput(target, keys) {
5992
- this.terminalManager.sendInput(target, keys);
5993
- }
5994
- handleTerminalResize(ws, target, cols, rows) {
5995
- const ok = this.terminalManager.resize(target, cols, rows);
5996
- this.send(ws, { type: "terminal:resized", target, ok });
6404
+ this.terminalManager.detach(target, appId);
6405
+ this.send(ws, { type: "terminal:detached", target, appId });
6406
+ }
6407
+ handleTerminalInput(target, keys, appId) {
6408
+ this.terminalManager.sendInput(target, keys, appId);
6409
+ }
6410
+ handleTerminalResize(ws, target, cols, rows, appId) {
6411
+ const ok = this.terminalManager.resize(target, cols, rows, appId);
6412
+ this.send(ws, { type: "terminal:resized", target, appId, ok });
6413
+ }
6414
+ handleTerminalCreateSession(ws, sessionName, windowName, cwd, appId = "tmux") {
6415
+ const result = this.terminalManager.createSession(
6416
+ sessionName || "coding",
6417
+ windowName || "main",
6418
+ cwd,
6419
+ appId
6420
+ );
6421
+ this.send(ws, {
6422
+ type: "terminal:session-created",
6423
+ ...result
6424
+ });
6425
+ if (result.ok) {
6426
+ this.handleTerminalList(ws);
6427
+ }
5997
6428
  }
5998
6429
  send(ws, msg) {
5999
6430
  if (ws.readyState === WebSocket3.OPEN) {
@@ -6173,7 +6604,9 @@ var init_hub = __esm({
6173
6604
  viberId: viber.id,
6174
6605
  goal,
6175
6606
  status: "pending",
6176
- createdAt: /* @__PURE__ */ new Date()
6607
+ createdAt: /* @__PURE__ */ new Date(),
6608
+ events: [],
6609
+ partialText: ""
6177
6610
  };
6178
6611
  this.tasks.set(taskId, task);
6179
6612
  viber.ws.send(
@@ -6199,7 +6632,9 @@ var init_hub = __esm({
6199
6632
  goal: t.goal,
6200
6633
  status: t.status,
6201
6634
  createdAt: t.createdAt.toISOString(),
6202
- completedAt: t.completedAt?.toISOString()
6635
+ completedAt: t.completedAt?.toISOString(),
6636
+ eventCount: t.events.length,
6637
+ partialText: t.partialText
6203
6638
  }));
6204
6639
  res.writeHead(200, { "Content-Type": "application/json" });
6205
6640
  res.end(JSON.stringify({ tasks }));
@@ -6221,7 +6656,10 @@ var init_hub = __esm({
6221
6656
  result: task.result,
6222
6657
  error: task.error,
6223
6658
  createdAt: task.createdAt.toISOString(),
6224
- completedAt: task.completedAt?.toISOString()
6659
+ completedAt: task.completedAt?.toISOString(),
6660
+ events: task.events,
6661
+ eventCount: task.events.length,
6662
+ partialText: task.partialText
6225
6663
  })
6226
6664
  );
6227
6665
  }
@@ -6275,6 +6713,7 @@ var init_hub = __esm({
6275
6713
  this.handleTaskStarted(msg.taskId);
6276
6714
  break;
6277
6715
  case "task:progress":
6716
+ this.handleTaskProgress(msg.taskId, msg.event);
6278
6717
  break;
6279
6718
  case "task:completed":
6280
6719
  this.handleTaskCompleted(msg.taskId, msg.result);
@@ -6320,9 +6759,27 @@ var init_hub = __esm({
6320
6759
  task.status = "completed";
6321
6760
  task.result = result;
6322
6761
  task.completedAt = /* @__PURE__ */ new Date();
6762
+ if (typeof result?.text === "string") {
6763
+ task.partialText = result.text;
6764
+ }
6323
6765
  console.log(`[Hub] Task completed: ${taskId}`);
6324
6766
  }
6325
6767
  }
6768
+ handleTaskProgress(taskId, event) {
6769
+ const task = this.tasks.get(taskId);
6770
+ if (!task) return;
6771
+ const at = (/* @__PURE__ */ new Date()).toISOString();
6772
+ task.events.push({ at, event });
6773
+ if (task.events.length > 500) {
6774
+ task.events.shift();
6775
+ }
6776
+ if (event?.kind === "text-delta" && typeof event?.delta === "string") {
6777
+ task.partialText = (task.partialText || "") + event.delta;
6778
+ if (task.partialText.length > 2e4) {
6779
+ task.partialText = task.partialText.slice(-2e4);
6780
+ }
6781
+ }
6782
+ }
6326
6783
  handleTaskError(taskId, error) {
6327
6784
  const task = this.tasks.get(taskId);
6328
6785
  if (task) {
@@ -6344,30 +6801,2027 @@ var init_hub = __esm({
6344
6801
  }
6345
6802
  });
6346
6803
 
6347
- // src/cli/index.ts
6348
- import "dotenv/config";
6349
- import { program } from "commander";
6350
- import * as os2 from "os";
6351
- import * as fs7 from "fs/promises";
6352
- import * as path13 from "path";
6353
- var VERSION = "1.0.0";
6354
- program.name("openviber").description("OpenViber - Workspace-first assistant runtime (vibers on your machines)").version(VERSION);
6355
- program.command("start").description(
6356
- "Start viber with all apps (local mode, or connect to server with --server)"
6357
- ).option("-s, --server <url>", "Command center URL (enables connected mode)").option("-t, --token <token>", "Authentication token (or set VIBER_TOKEN)").option("-n, --name <name>", "Viber name", `${os2.hostname()}-viber`).option("--desktop", "Enable desktop control (UI-TARS)").option("--disable-app <apps...>", "Disable specific apps (comma-separated)").option("--no-apps", "Disable all apps").option("--reconnect-interval <ms>", "Reconnect interval in ms", "5000").option("--heartbeat-interval <ms>", "Heartbeat interval in ms", "30000").action(async (options) => {
6358
- const { JobScheduler: JobScheduler2 } = await Promise.resolve().then(() => (init_scheduler(), scheduler_exports));
6359
- const { ViberController: ViberController2 } = await Promise.resolve().then(() => (init_controller(), controller_exports));
6360
- const { EventEmitter: EventEmitter3 } = await import("events");
6361
- await Promise.resolve().then(() => (init_skills(), skills_exports));
6362
- const viberId = await getViberId();
6363
- const token = options.token || process.env.VIBER_TOKEN;
6364
- const connectToServer = options.server && token;
6365
- if (options.server && !token) {
6366
- console.error(
6367
- "Error: Authentication token required when using --server."
6368
- );
6369
- console.error(
6370
- "Use --token <token> or set VIBER_TOKEN environment variable."
6804
+ // src/core/task.ts
6805
+ var Task;
6806
+ var init_task = __esm({
6807
+ "src/core/task.ts"() {
6808
+ "use strict";
6809
+ Task = class _Task {
6810
+ id;
6811
+ title;
6812
+ description;
6813
+ status;
6814
+ assignedTo;
6815
+ priority;
6816
+ estimatedTime;
6817
+ actualTime;
6818
+ dependencies;
6819
+ steps;
6820
+ tags;
6821
+ metadata;
6822
+ createdAt;
6823
+ updatedAt;
6824
+ startedAt;
6825
+ completedAt;
6826
+ error;
6827
+ constructor({
6828
+ id,
6829
+ title,
6830
+ description,
6831
+ status = "pending" /* PENDING */,
6832
+ assignedTo,
6833
+ priority = "medium",
6834
+ estimatedTime,
6835
+ dependencies = [],
6836
+ steps = [],
6837
+ tags = [],
6838
+ metadata = {}
6839
+ }) {
6840
+ this.id = id;
6841
+ this.title = title;
6842
+ this.description = description;
6843
+ this.status = status;
6844
+ this.assignedTo = assignedTo;
6845
+ this.priority = priority;
6846
+ this.estimatedTime = estimatedTime;
6847
+ this.dependencies = dependencies;
6848
+ this.steps = steps;
6849
+ this.tags = tags;
6850
+ this.metadata = metadata;
6851
+ this.createdAt = /* @__PURE__ */ new Date();
6852
+ this.updatedAt = /* @__PURE__ */ new Date();
6853
+ }
6854
+ start() {
6855
+ if (this.status !== "pending" /* PENDING */) {
6856
+ throw new Error(`Cannot start task in ${this.status} status`);
6857
+ }
6858
+ this.status = "running" /* RUNNING */;
6859
+ this.startedAt = /* @__PURE__ */ new Date();
6860
+ this.updatedAt = /* @__PURE__ */ new Date();
6861
+ }
6862
+ complete() {
6863
+ if (this.status !== "running" /* RUNNING */) {
6864
+ throw new Error(`Cannot complete task in ${this.status} status`);
6865
+ }
6866
+ this.status = "completed" /* COMPLETED */;
6867
+ this.completedAt = /* @__PURE__ */ new Date();
6868
+ this.updatedAt = /* @__PURE__ */ new Date();
6869
+ if (this.startedAt) {
6870
+ this.actualTime = this.calculateDuration(
6871
+ this.startedAt,
6872
+ this.completedAt
6873
+ );
6874
+ }
6875
+ }
6876
+ fail(error) {
6877
+ this.status = "failed" /* FAILED */;
6878
+ this.error = error;
6879
+ this.completedAt = /* @__PURE__ */ new Date();
6880
+ this.updatedAt = /* @__PURE__ */ new Date();
6881
+ }
6882
+ block(reason) {
6883
+ this.status = "blocked" /* BLOCKED */;
6884
+ this.error = reason;
6885
+ this.updatedAt = /* @__PURE__ */ new Date();
6886
+ }
6887
+ cancel() {
6888
+ this.status = "cancelled" /* CANCELLED */;
6889
+ this.completedAt = /* @__PURE__ */ new Date();
6890
+ this.updatedAt = /* @__PURE__ */ new Date();
6891
+ }
6892
+ isActionable(context) {
6893
+ return this.status === "pending" /* PENDING */ && !this.hasBlockingDependencies(context);
6894
+ }
6895
+ hasBlockingDependencies(context) {
6896
+ if (!context || !this.dependencies || this.dependencies.length === 0) {
6897
+ return false;
6898
+ }
6899
+ return this.dependencies.some((dep) => {
6900
+ if (dep.type === "optional") return false;
6901
+ const status = context.getTaskStatus(dep.taskId);
6902
+ return status !== "completed" /* COMPLETED */;
6903
+ });
6904
+ }
6905
+ calculateDuration(start, end) {
6906
+ const ms = end.getTime() - start.getTime();
6907
+ const seconds = Math.floor(ms / 1e3);
6908
+ const minutes = Math.floor(seconds / 60);
6909
+ const hours = Math.floor(minutes / 60);
6910
+ if (hours > 0) {
6911
+ return `${hours}h ${minutes % 60}m`;
6912
+ } else if (minutes > 0) {
6913
+ return `${minutes}m ${seconds % 60}s`;
6914
+ } else {
6915
+ return `${seconds}s`;
6916
+ }
6917
+ }
6918
+ toJSON() {
6919
+ return {
6920
+ id: this.id,
6921
+ title: this.title,
6922
+ description: this.description,
6923
+ status: this.status,
6924
+ assignedTo: this.assignedTo,
6925
+ priority: this.priority,
6926
+ estimatedTime: this.estimatedTime,
6927
+ actualTime: this.actualTime,
6928
+ dependencies: this.dependencies,
6929
+ steps: this.steps,
6930
+ tags: this.tags,
6931
+ metadata: this.metadata,
6932
+ createdAt: this.createdAt.toISOString(),
6933
+ updatedAt: this.updatedAt.toISOString(),
6934
+ startedAt: this.startedAt?.toISOString(),
6935
+ completedAt: this.completedAt?.toISOString(),
6936
+ error: this.error
6937
+ };
6938
+ }
6939
+ static fromJSON(data) {
6940
+ const task = new _Task({
6941
+ id: data.id,
6942
+ title: data.title,
6943
+ description: data.description,
6944
+ status: data.status,
6945
+ assignedTo: data.assignedTo,
6946
+ priority: data.priority,
6947
+ estimatedTime: data.estimatedTime,
6948
+ dependencies: data.dependencies,
6949
+ steps: data.steps,
6950
+ tags: data.tags,
6951
+ metadata: data.metadata
6952
+ });
6953
+ task.createdAt = new Date(data.createdAt);
6954
+ task.updatedAt = new Date(data.updatedAt);
6955
+ task.actualTime = data.actualTime;
6956
+ task.error = data.error;
6957
+ if (data.startedAt) {
6958
+ task.startedAt = new Date(data.startedAt);
6959
+ }
6960
+ if (data.completedAt) {
6961
+ task.completedAt = new Date(data.completedAt);
6962
+ }
6963
+ return task;
6964
+ }
6965
+ };
6966
+ }
6967
+ });
6968
+
6969
+ // src/core/plan.ts
6970
+ var Plan;
6971
+ var init_plan = __esm({
6972
+ "src/core/plan.ts"() {
6973
+ "use strict";
6974
+ init_task();
6975
+ Plan = class _Plan {
6976
+ tasks;
6977
+ goal;
6978
+ createdAt;
6979
+ updatedAt;
6980
+ constructor({ tasks = [], goal }) {
6981
+ this.tasks = tasks;
6982
+ this.goal = goal;
6983
+ this.createdAt = /* @__PURE__ */ new Date();
6984
+ this.updatedAt = /* @__PURE__ */ new Date();
6985
+ }
6986
+ addTask(task) {
6987
+ this.tasks.push(task);
6988
+ this.updatedAt = /* @__PURE__ */ new Date();
6989
+ }
6990
+ removeTask(taskId) {
6991
+ const index = this.tasks.findIndex((t) => t.id === taskId);
6992
+ if (index >= 0) {
6993
+ this.tasks.splice(index, 1);
6994
+ this.updatedAt = /* @__PURE__ */ new Date();
6995
+ return true;
6996
+ }
6997
+ return false;
6998
+ }
6999
+ getTaskById(taskId) {
7000
+ return this.tasks.find((t) => t.id === taskId);
7001
+ }
7002
+ updateTaskStatus(taskId, status) {
7003
+ const task = this.getTaskById(taskId);
7004
+ if (task) {
7005
+ task.status = status;
7006
+ task.updatedAt = /* @__PURE__ */ new Date();
7007
+ this.updatedAt = /* @__PURE__ */ new Date();
7008
+ return true;
7009
+ }
7010
+ return false;
7011
+ }
7012
+ getNextActionableTask() {
7013
+ return this.tasks.find(
7014
+ (task) => task.isActionable({
7015
+ getTaskStatus: (id) => this.getTaskById(id)?.status
7016
+ })
7017
+ );
7018
+ }
7019
+ getAllActionableTasks(maxTasks) {
7020
+ const actionableTasks = this.tasks.filter(
7021
+ (task) => task.isActionable({
7022
+ getTaskStatus: (id) => this.getTaskById(id)?.status
7023
+ })
7024
+ );
7025
+ return maxTasks ? actionableTasks.slice(0, maxTasks) : actionableTasks;
7026
+ }
7027
+ getTasksByStatus(status) {
7028
+ return this.tasks.filter((task) => task.status === status);
7029
+ }
7030
+ getTasksByAssignee(assignee) {
7031
+ return this.tasks.filter((task) => task.assignedTo === assignee);
7032
+ }
7033
+ isComplete() {
7034
+ return this.tasks.every(
7035
+ (task) => task.status === "completed" /* COMPLETED */ || task.status === "cancelled" /* CANCELLED */
7036
+ );
7037
+ }
7038
+ hasFailedTasks() {
7039
+ return this.tasks.some((task) => task.status === "failed" /* FAILED */);
7040
+ }
7041
+ hasBlockedTasks() {
7042
+ return this.tasks.some((task) => task.status === "blocked" /* BLOCKED */);
7043
+ }
7044
+ getProgressSummary() {
7045
+ const summary = {
7046
+ totalTasks: this.tasks.length,
7047
+ completedTasks: 0,
7048
+ runningTasks: 0,
7049
+ pendingTasks: 0,
7050
+ failedTasks: 0,
7051
+ blockedTasks: 0,
7052
+ progressPercentage: 0
7053
+ };
7054
+ for (const task of this.tasks) {
7055
+ switch (task.status) {
7056
+ case "completed" /* COMPLETED */:
7057
+ summary.completedTasks++;
7058
+ break;
7059
+ case "running" /* RUNNING */:
7060
+ summary.runningTasks++;
7061
+ break;
7062
+ case "pending" /* PENDING */:
7063
+ summary.pendingTasks++;
7064
+ break;
7065
+ case "failed" /* FAILED */:
7066
+ summary.failedTasks++;
7067
+ break;
7068
+ case "blocked" /* BLOCKED */:
7069
+ summary.blockedTasks++;
7070
+ break;
7071
+ }
7072
+ }
7073
+ if (summary.totalTasks > 0) {
7074
+ summary.progressPercentage = summary.completedTasks / summary.totalTasks * 100;
7075
+ }
7076
+ return summary;
7077
+ }
7078
+ reorderTasks(fromIndex, toIndex) {
7079
+ if (fromIndex < 0 || fromIndex >= this.tasks.length || toIndex < 0 || toIndex >= this.tasks.length) {
7080
+ throw new Error("Invalid task indices");
7081
+ }
7082
+ const [task] = this.tasks.splice(fromIndex, 1);
7083
+ this.tasks.splice(toIndex, 0, task);
7084
+ this.updatedAt = /* @__PURE__ */ new Date();
7085
+ }
7086
+ toJSON() {
7087
+ return {
7088
+ tasks: this.tasks.map((task) => task.toJSON()),
7089
+ goal: this.goal,
7090
+ createdAt: this.createdAt.toISOString(),
7091
+ updatedAt: this.updatedAt.toISOString()
7092
+ };
7093
+ }
7094
+ static fromJSON(data) {
7095
+ const plan = new _Plan({
7096
+ goal: data.goal,
7097
+ tasks: data.tasks.map((taskData) => Task.fromJSON(taskData))
7098
+ });
7099
+ plan.createdAt = new Date(data.createdAt);
7100
+ plan.updatedAt = new Date(data.updatedAt);
7101
+ return plan;
7102
+ }
7103
+ };
7104
+ }
7105
+ });
7106
+
7107
+ // src/core/collaboration.ts
7108
+ var AgentCollaborationManager, ParallelExecutionEngine, CollaborativePlanner;
7109
+ var init_collaboration = __esm({
7110
+ "src/core/collaboration.ts"() {
7111
+ "use strict";
7112
+ AgentCollaborationManager = class {
7113
+ space;
7114
+ messageQueue;
7115
+ // Agent ID -> messages
7116
+ sharedContext;
7117
+ listeners;
7118
+ constructor(space) {
7119
+ this.space = space;
7120
+ this.messageQueue = /* @__PURE__ */ new Map();
7121
+ this.sharedContext = {
7122
+ spaceId: space.spaceId,
7123
+ data: {},
7124
+ updatedAt: /* @__PURE__ */ new Date(),
7125
+ updatedBy: "system"
7126
+ };
7127
+ this.listeners = /* @__PURE__ */ new Map();
7128
+ }
7129
+ /**
7130
+ * Send a message from one agent to another
7131
+ */
7132
+ sendMessage(from, to, content, metadata) {
7133
+ const message = {
7134
+ from,
7135
+ to,
7136
+ content,
7137
+ metadata,
7138
+ timestamp: /* @__PURE__ */ new Date()
7139
+ };
7140
+ if (to === "broadcast") {
7141
+ for (const agentId of this.space.agents.keys()) {
7142
+ if (agentId !== from) {
7143
+ this.queueMessage(agentId, message);
7144
+ }
7145
+ }
7146
+ } else {
7147
+ this.queueMessage(to, message);
7148
+ }
7149
+ this.notifyListeners(to, message);
7150
+ if (to !== "broadcast") {
7151
+ this.notifyListeners("broadcast", message);
7152
+ }
7153
+ }
7154
+ /**
7155
+ * Queue a message for an agent
7156
+ */
7157
+ queueMessage(agentId, message) {
7158
+ if (!this.messageQueue.has(agentId)) {
7159
+ this.messageQueue.set(agentId, []);
7160
+ }
7161
+ this.messageQueue.get(agentId).push(message);
7162
+ }
7163
+ /**
7164
+ * Get pending messages for an agent
7165
+ */
7166
+ getMessages(agentId) {
7167
+ const messages = this.messageQueue.get(agentId) || [];
7168
+ this.messageQueue.set(agentId, []);
7169
+ return messages;
7170
+ }
7171
+ /**
7172
+ * Subscribe to messages for an agent
7173
+ */
7174
+ subscribe(agentId, callback) {
7175
+ if (!this.listeners.has(agentId)) {
7176
+ this.listeners.set(agentId, /* @__PURE__ */ new Set());
7177
+ }
7178
+ this.listeners.get(agentId).add(callback);
7179
+ return () => {
7180
+ const callbacks = this.listeners.get(agentId);
7181
+ if (callbacks) {
7182
+ callbacks.delete(callback);
7183
+ if (callbacks.size === 0) {
7184
+ this.listeners.delete(agentId);
7185
+ }
7186
+ }
7187
+ };
7188
+ }
7189
+ /**
7190
+ * Notify listeners of a new message
7191
+ */
7192
+ notifyListeners(agentId, message) {
7193
+ const callbacks = this.listeners.get(agentId);
7194
+ if (callbacks) {
7195
+ callbacks.forEach((cb) => {
7196
+ try {
7197
+ cb(message);
7198
+ } catch (error) {
7199
+ console.error(`[AgentCollaborationManager] Listener error:`, error);
7200
+ }
7201
+ });
7202
+ }
7203
+ }
7204
+ /**
7205
+ * Update shared context
7206
+ */
7207
+ updateContext(agentId, updates) {
7208
+ this.sharedContext.data = {
7209
+ ...this.sharedContext.data,
7210
+ ...updates
7211
+ };
7212
+ this.sharedContext.updatedAt = /* @__PURE__ */ new Date();
7213
+ this.sharedContext.updatedBy = agentId;
7214
+ }
7215
+ /**
7216
+ * Get shared context
7217
+ */
7218
+ getContext() {
7219
+ return { ...this.sharedContext };
7220
+ }
7221
+ /**
7222
+ * Get a specific value from shared context
7223
+ */
7224
+ getContextValue(key) {
7225
+ return this.sharedContext.data[key];
7226
+ }
7227
+ };
7228
+ ParallelExecutionEngine = class {
7229
+ space;
7230
+ maxConcurrency;
7231
+ activeTasks;
7232
+ constructor(space, maxConcurrency = 3) {
7233
+ this.space = space;
7234
+ this.maxConcurrency = maxConcurrency;
7235
+ this.activeTasks = /* @__PURE__ */ new Map();
7236
+ }
7237
+ /**
7238
+ * Execute multiple tasks in parallel
7239
+ */
7240
+ async executeParallel(tasks) {
7241
+ const sortedTasks = [...tasks].sort(
7242
+ (a, b) => (b.priority || 0) - (a.priority || 0)
7243
+ );
7244
+ const results = [];
7245
+ const executing = [];
7246
+ for (const task of sortedTasks) {
7247
+ if (executing.length >= this.maxConcurrency) {
7248
+ const completed = await Promise.race(executing);
7249
+ const index = executing.findIndex(
7250
+ (p) => p === Promise.resolve(completed)
7251
+ );
7252
+ if (index >= 0) {
7253
+ executing.splice(index, 1);
7254
+ }
7255
+ results.push(completed);
7256
+ }
7257
+ const promise = this.executeTask(task);
7258
+ executing.push(promise);
7259
+ this.activeTasks.set(task.id, promise);
7260
+ }
7261
+ const remaining = await Promise.allSettled(executing);
7262
+ for (const result of remaining) {
7263
+ if (result.status === "fulfilled") {
7264
+ results.push(result.value);
7265
+ } else {
7266
+ console.error("[ParallelExecutionEngine] Task failed:", result.reason);
7267
+ }
7268
+ }
7269
+ this.activeTasks.clear();
7270
+ return results;
7271
+ }
7272
+ /**
7273
+ * Execute a single task
7274
+ */
7275
+ async executeTask(task) {
7276
+ const startTime = Date.now();
7277
+ try {
7278
+ const agent = this.space.getAgent(task.agentId);
7279
+ if (!agent) {
7280
+ throw new Error(`Agent ${task.agentId} not found`);
7281
+ }
7282
+ const result = await agent.generateText({
7283
+ messages: task.messages,
7284
+ system: task.system,
7285
+ spaceId: this.space.spaceId,
7286
+ metadata: {
7287
+ ...task.metadata,
7288
+ parallelTaskId: task.id
7289
+ }
7290
+ });
7291
+ const duration = Date.now() - startTime;
7292
+ return {
7293
+ taskId: task.id,
7294
+ agentId: task.agentId,
7295
+ result: {
7296
+ text: result.text || "",
7297
+ toolCalls: result.toolCalls,
7298
+ reasoning: result.reasoning,
7299
+ metadata: result.metadata
7300
+ },
7301
+ duration
7302
+ };
7303
+ } catch (error) {
7304
+ const duration = Date.now() - startTime;
7305
+ return {
7306
+ taskId: task.id,
7307
+ agentId: task.agentId,
7308
+ result: {
7309
+ text: "",
7310
+ metadata: { error: String(error) }
7311
+ },
7312
+ error: error instanceof Error ? error : new Error(String(error)),
7313
+ duration
7314
+ };
7315
+ }
7316
+ }
7317
+ /**
7318
+ * Cancel a running task
7319
+ */
7320
+ cancelTask(taskId) {
7321
+ const task = this.activeTasks.get(taskId);
7322
+ if (task) {
7323
+ this.activeTasks.delete(taskId);
7324
+ }
7325
+ }
7326
+ /**
7327
+ * Get active task count
7328
+ */
7329
+ getActiveTaskCount() {
7330
+ return this.activeTasks.size;
7331
+ }
7332
+ };
7333
+ CollaborativePlanner = class {
7334
+ space;
7335
+ collaborationManager;
7336
+ constructor(space, collaborationManager) {
7337
+ this.space = space;
7338
+ this.collaborationManager = collaborationManager;
7339
+ }
7340
+ /**
7341
+ * Create a collaborative plan with multiple agents
7342
+ */
7343
+ async createCollaborativePlan(goal, agentIds) {
7344
+ const context = this.collaborationManager.getContext();
7345
+ const planningTasks = agentIds.map((agentId, index) => ({
7346
+ id: `plan-${agentId}-${Date.now()}`,
7347
+ agentId,
7348
+ messages: [
7349
+ {
7350
+ role: "user",
7351
+ content: `We need to create a collaborative plan for: ${goal}
7352
+
7353
+ Shared context: ${JSON.stringify(context.data, null, 2)}
7354
+
7355
+ You are one of ${agentIds.length} agents working together. Propose your part of the plan and identify dependencies on other agents.`
7356
+ }
7357
+ ],
7358
+ priority: agentIds.length - index,
7359
+ // First agent gets highest priority
7360
+ metadata: {
7361
+ planningSession: true,
7362
+ goal,
7363
+ otherAgents: agentIds.filter((id) => id !== agentId)
7364
+ }
7365
+ }));
7366
+ const executionEngine = new ParallelExecutionEngine(this.space);
7367
+ const results = await executionEngine.executeParallel(planningTasks);
7368
+ const agentPlans = /* @__PURE__ */ new Map();
7369
+ for (const result of results) {
7370
+ if (!result.error) {
7371
+ agentPlans.set(result.agentId, result.result);
7372
+ }
7373
+ }
7374
+ const plan = this.mergePlans(agentPlans, goal);
7375
+ const agentAssignments = this.assignTasks(plan, agentIds);
7376
+ return { plan, agentAssignments };
7377
+ }
7378
+ /**
7379
+ * Merge multiple agent plans into one
7380
+ */
7381
+ mergePlans(agentPlans, goal) {
7382
+ const tasks = [];
7383
+ let taskId = 1;
7384
+ for (const [agentId, plan] of agentPlans.entries()) {
7385
+ if (plan.text) {
7386
+ const lines = plan.text.split("\n").filter(
7387
+ (line) => line.trim().startsWith("-") || line.trim().match(/^\d+\./)
7388
+ );
7389
+ for (const line of lines) {
7390
+ tasks.push({
7391
+ id: `task-${taskId++}`,
7392
+ description: line.replace(/^[-•\d.]+\s*/, "").trim(),
7393
+ assignedAgent: agentId,
7394
+ status: "pending"
7395
+ });
7396
+ }
7397
+ }
7398
+ }
7399
+ return {
7400
+ goal,
7401
+ tasks,
7402
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
7403
+ };
7404
+ }
7405
+ /**
7406
+ * Assign tasks to agents
7407
+ */
7408
+ assignTasks(plan, agentIds) {
7409
+ const assignments = /* @__PURE__ */ new Map();
7410
+ for (const agentId of agentIds) {
7411
+ assignments.set(agentId, []);
7412
+ }
7413
+ for (const task of plan.tasks) {
7414
+ const agentId = task.assignedAgent || agentIds[0];
7415
+ const tasks = assignments.get(agentId) || [];
7416
+ tasks.push(task.id);
7417
+ assignments.set(agentId, tasks);
7418
+ }
7419
+ return assignments;
7420
+ }
7421
+ };
7422
+ }
7423
+ });
7424
+
7425
+ // src/core/space.ts
7426
+ var space_exports2 = {};
7427
+ __export(space_exports2, {
7428
+ Space: () => Space,
7429
+ startSpace: () => startSpace
7430
+ });
7431
+ async function startSpace({
7432
+ goal,
7433
+ spaceId,
7434
+ userId,
7435
+ spaceRoot,
7436
+ name,
7437
+ model
7438
+ }) {
7439
+ const id = spaceId || `proj_${Date.now().toString(36)}`;
7440
+ const spaceConfig = {
7441
+ name: name || goal.slice(0, 50),
7442
+ autoSave: true,
7443
+ checkpointInterval: 300
7444
+ };
7445
+ console.log(`[Space] Creating space - agents will be loaded on demand`);
7446
+ const storage = await SpaceStorageFactory.create(id);
7447
+ const agents = /* @__PURE__ */ new Map();
7448
+ console.log(`[Space] Space initialized (agents loaded on demand)`);
7449
+ const messageQueue = new MessageQueue();
7450
+ const history = new ConversationHistory();
7451
+ const space = new Space({
7452
+ spaceId: id,
7453
+ userId,
7454
+ config: spaceConfig,
7455
+ history,
7456
+ messageQueue,
7457
+ agents,
7458
+ storage,
7459
+ goal,
7460
+ name: name || spaceConfig.name
7461
+ });
7462
+ const viberAgentConfig = {
7463
+ name: "Viber",
7464
+ description: "I manage this space and coordinate all work.",
7465
+ provider: "openrouter",
7466
+ model: model || "deepseek/deepseek-chat",
7467
+ temperature: 0.7,
7468
+ promptFile: "",
7469
+ // ViberAgent doesn't use prompt files
7470
+ skills: ["tmux", "cursor-agent"]
7471
+ };
7472
+ const viberAgent = new ViberAgent(viberAgentConfig, space, {
7473
+ model,
7474
+ spaceId: id
7475
+ });
7476
+ space.viberAgent = viberAgent;
7477
+ await space.persistState();
7478
+ return space;
7479
+ }
7480
+ var Space;
7481
+ var init_space2 = __esm({
7482
+ "src/core/space.ts"() {
7483
+ "use strict";
7484
+ init_task();
7485
+ init_viber_agent();
7486
+ init_space();
7487
+ init_message();
7488
+ init_collaboration();
7489
+ Space = class {
7490
+ spaceId;
7491
+ userId;
7492
+ // User ID of space owner
7493
+ config;
7494
+ history;
7495
+ // Legacy: primary task history
7496
+ tasks;
7497
+ // NEW: Multiple tasks
7498
+ messageQueue;
7499
+ agents;
7500
+ storage;
7501
+ goal;
7502
+ name;
7503
+ viberAgent;
7504
+ createdAt;
7505
+ updatedAt;
7506
+ plan;
7507
+ artifacts;
7508
+ collaborationManager;
7509
+ parallelEngine;
7510
+ collaborativePlanner;
7511
+ constructor({
7512
+ spaceId,
7513
+ userId,
7514
+ config: config2,
7515
+ history,
7516
+ messageQueue,
7517
+ agents,
7518
+ storage,
7519
+ goal,
7520
+ name,
7521
+ viberAgent
7522
+ }) {
7523
+ this.spaceId = spaceId;
7524
+ this.userId = userId;
7525
+ this.config = config2;
7526
+ this.history = history;
7527
+ this.tasks = /* @__PURE__ */ new Map();
7528
+ this.messageQueue = messageQueue;
7529
+ this.agents = agents;
7530
+ this.storage = storage;
7531
+ this.goal = goal;
7532
+ this.name = name || `Space ${spaceId}`;
7533
+ this.viberAgent = viberAgent;
7534
+ this.createdAt = /* @__PURE__ */ new Date();
7535
+ this.updatedAt = /* @__PURE__ */ new Date();
7536
+ this.collaborationManager = new AgentCollaborationManager(this);
7537
+ this.parallelEngine = new ParallelExecutionEngine(this);
7538
+ this.collaborativePlanner = new CollaborativePlanner(
7539
+ this,
7540
+ this.collaborationManager
7541
+ );
7542
+ }
7543
+ /**
7544
+ * Get or create a task within this space
7545
+ */
7546
+ getOrCreateTask(taskId, title) {
7547
+ if (!this.tasks.has(taskId)) {
7548
+ const task = {
7549
+ id: taskId,
7550
+ spaceId: this.spaceId,
7551
+ title: title || `Task ${taskId}`,
7552
+ history: new ConversationHistory(),
7553
+ artifactIds: [],
7554
+ status: "active",
7555
+ createdAt: /* @__PURE__ */ new Date(),
7556
+ updatedAt: /* @__PURE__ */ new Date()
7557
+ };
7558
+ this.tasks.set(taskId, task);
7559
+ console.log(`[Space] Created task ${taskId} in space ${this.spaceId}`);
7560
+ }
7561
+ return this.tasks.get(taskId);
7562
+ }
7563
+ /**
7564
+ * Get task by ID
7565
+ */
7566
+ getTask(taskId) {
7567
+ return this.tasks.get(taskId);
7568
+ }
7569
+ /**
7570
+ * Get all tasks in this space
7571
+ */
7572
+ getAllTasks() {
7573
+ return Array.from(this.tasks.values());
7574
+ }
7575
+ /**
7576
+ * Update space task status (for conversation tasks, not Plan tasks)
7577
+ */
7578
+ updateSpaceTaskStatus(taskId, status) {
7579
+ const task = this.tasks.get(taskId);
7580
+ if (task) {
7581
+ task.status = status;
7582
+ task.updatedAt = /* @__PURE__ */ new Date();
7583
+ return true;
7584
+ }
7585
+ return false;
7586
+ }
7587
+ getAgent(name) {
7588
+ return this.agents.get(name);
7589
+ }
7590
+ registerAgent(name, agent) {
7591
+ this.agents.set(name, agent);
7592
+ console.log(`[Space] Registered agent: ${name} - ${agent.description}`);
7593
+ }
7594
+ complete() {
7595
+ console.log(`Space ${this.spaceId} completed`);
7596
+ }
7597
+ getContext() {
7598
+ const context = {
7599
+ spaceId: this.spaceId,
7600
+ goal: this.goal,
7601
+ storagePath: this.storage.getSpacePath(),
7602
+ agents: Array.from(this.agents.keys()),
7603
+ historyLength: this.history.messages.length,
7604
+ createdAt: this.createdAt.toISOString()
7605
+ };
7606
+ if (this.plan) {
7607
+ context.plan = {
7608
+ goal: this.goal,
7609
+ totalTasks: this.plan.tasks.length,
7610
+ progress: this.plan.getProgressSummary()
7611
+ };
7612
+ }
7613
+ return context;
7614
+ }
7615
+ async createPlan(plan) {
7616
+ this.plan = plan;
7617
+ await this.persistState();
7618
+ console.log(
7619
+ `Created plan for space ${this.spaceId} with ${plan.tasks.length} tasks`
7620
+ );
7621
+ }
7622
+ async updatePlan(plan) {
7623
+ this.plan = plan;
7624
+ this.updatedAt = /* @__PURE__ */ new Date();
7625
+ await this.persistState();
7626
+ console.log(`Updated plan for space ${this.spaceId}`);
7627
+ }
7628
+ async setName(name) {
7629
+ this.name = name;
7630
+ this.updatedAt = /* @__PURE__ */ new Date();
7631
+ await this.persistState();
7632
+ console.log(`Updated space ${this.spaceId} name to: ${name}`);
7633
+ }
7634
+ async getNextTask() {
7635
+ if (!this.plan) {
7636
+ return void 0;
7637
+ }
7638
+ return this.plan.getNextActionableTask();
7639
+ }
7640
+ async getParallelTasks(maxTasks = 3) {
7641
+ if (!this.plan) {
7642
+ return [];
7643
+ }
7644
+ return this.plan.getAllActionableTasks(maxTasks);
7645
+ }
7646
+ async updateTaskStatus(taskId, status) {
7647
+ if (!this.plan) {
7648
+ return false;
7649
+ }
7650
+ const success = this.plan.updateTaskStatus(taskId, status);
7651
+ if (success) {
7652
+ this.updatedAt = /* @__PURE__ */ new Date();
7653
+ await this.persistState();
7654
+ console.log(`Updated task ${taskId} status to ${status}`);
7655
+ }
7656
+ return success;
7657
+ }
7658
+ async assignTask(taskId, agentName) {
7659
+ if (!this.plan) {
7660
+ return false;
7661
+ }
7662
+ const task = this.plan.getTaskById(taskId);
7663
+ if (!task) {
7664
+ return false;
7665
+ }
7666
+ if (!this.agents.has(agentName)) {
7667
+ console.error(`Agent '${agentName}' not found in space team`);
7668
+ return false;
7669
+ }
7670
+ task.assignedTo = agentName;
7671
+ this.updatedAt = /* @__PURE__ */ new Date();
7672
+ await this.persistState();
7673
+ console.log(`Assigned task ${taskId} to agent ${agentName}`);
7674
+ return true;
7675
+ }
7676
+ isPlanComplete() {
7677
+ if (!this.plan) {
7678
+ return false;
7679
+ }
7680
+ return this.plan.isComplete();
7681
+ }
7682
+ hasFailedTasks() {
7683
+ if (!this.plan) {
7684
+ return false;
7685
+ }
7686
+ return this.plan.hasFailedTasks();
7687
+ }
7688
+ async persistState() {
7689
+ console.log("[Space] State persistence handled by database adapter");
7690
+ }
7691
+ async loadState() {
7692
+ try {
7693
+ console.log("[Space] State loading handled by database adapter");
7694
+ return false;
7695
+ } catch (error) {
7696
+ console.error("Failed to load space state:", error);
7697
+ }
7698
+ return false;
7699
+ }
7700
+ async loadPlan() {
7701
+ if (await this.loadState()) {
7702
+ return this.plan;
7703
+ }
7704
+ return void 0;
7705
+ }
7706
+ getState() {
7707
+ const state = {
7708
+ spaceId: this.spaceId,
7709
+ name: this.name,
7710
+ goal: this.goal,
7711
+ createdAt: this.createdAt.toISOString(),
7712
+ updatedAt: this.updatedAt.toISOString(),
7713
+ teamSize: this.agents.size
7714
+ };
7715
+ if (this.plan) {
7716
+ const taskStats = {
7717
+ total: this.plan.tasks.length,
7718
+ completed: this.plan.tasks.filter(
7719
+ (t) => t.status === "completed" /* COMPLETED */
7720
+ ).length,
7721
+ running: this.plan.tasks.filter((t) => t.status === "running" /* RUNNING */).length,
7722
+ pending: this.plan.tasks.filter((t) => t.status === "pending" /* PENDING */).length,
7723
+ failed: this.plan.tasks.filter((t) => t.status === "failed" /* FAILED */).length
7724
+ };
7725
+ state.tasks = taskStats;
7726
+ state.progressPercentage = taskStats.total > 0 ? taskStats.completed / taskStats.total * 100 : 0;
7727
+ }
7728
+ return state;
7729
+ }
7730
+ };
7731
+ }
7732
+ });
7733
+
7734
+ // src/core/viber-agent.ts
7735
+ import { generateText as generateText2, Output } from "ai";
7736
+ import { z as z11 } from "zod";
7737
+ var ViberAgent;
7738
+ var init_viber_agent = __esm({
7739
+ "src/core/viber-agent.ts"() {
7740
+ "use strict";
7741
+ init_agent();
7742
+ init_factory();
7743
+ init_plan();
7744
+ init_task();
7745
+ ViberAgent = class extends Agent {
7746
+ space;
7747
+ spaceId;
7748
+ abortController;
7749
+ singleAgentId;
7750
+ // If set, bypass planning
7751
+ constructor(config2, space, options) {
7752
+ const xConfig = {
7753
+ ...config2,
7754
+ name: "X",
7755
+ description: `I am X, the conversational representative for this space. I manage all aspects of the space and coordinate with other agents to achieve our goals.`
7756
+ };
7757
+ super(xConfig);
7758
+ this.space = space;
7759
+ this.spaceId = space.spaceId;
7760
+ this.singleAgentId = options?.singleAgentId;
7761
+ }
7762
+ /**
7763
+ * Getter for space (needed for external access)
7764
+ */
7765
+ getSpace() {
7766
+ return this.space;
7767
+ }
7768
+ /**
7769
+ * Override getSystemPrompt to include plan and artifacts context
7770
+ */
7771
+ getSystemPrompt(context) {
7772
+ const basePrompt = super.getSystemPrompt(context);
7773
+ return basePrompt + this.getPlanContext() + this.getArtifactsContext();
7774
+ }
7775
+ /**
7776
+ * Generate text - uses new AI SDK-style signature
7777
+ */
7778
+ async generateText(options) {
7779
+ return super.generateText({
7780
+ ...options,
7781
+ spaceId: this.space.spaceId,
7782
+ metadata: {
7783
+ spaceName: this.space.name,
7784
+ spaceGoal: this.space.goal,
7785
+ ...options.metadata
7786
+ }
7787
+ });
7788
+ }
7789
+ /**
7790
+ * ViberAgent streamText - Orchestration Layer
7791
+ * Responsibilities: History management, agent delegation, persistence
7792
+ */
7793
+ async streamText(options) {
7794
+ const {
7795
+ messages,
7796
+ system: systemMessage,
7797
+ spaceId,
7798
+ metadata = {},
7799
+ ...restOptions
7800
+ } = options;
7801
+ console.log("[ViberAgent] Orchestration layer: starting streamText");
7802
+ const mode = metadata?.mode;
7803
+ if (!mode || mode !== "agent") {
7804
+ throw new Error("ViberAgent only supports 'agent' mode");
7805
+ }
7806
+ await this.updateSpaceHistory(messages, metadata);
7807
+ const streamResult = await this.handleAgentMode(
7808
+ messages,
7809
+ systemMessage,
7810
+ spaceId,
7811
+ metadata,
7812
+ restOptions
7813
+ );
7814
+ if (spaceId) {
7815
+ this.handleMessagePersistence(streamResult, messages, spaceId, metadata);
7816
+ }
7817
+ return streamResult;
7818
+ }
7819
+ /**
7820
+ * Agent Mode Handler - Direct delegation with performance optimization
7821
+ * Supports both single agent and parallel execution
7822
+ */
7823
+ async handleAgentMode(messages, systemMessage, spaceId, metadata, restOptions) {
7824
+ const parallelAgents = metadata.parallelAgents;
7825
+ if (parallelAgents && parallelAgents.length > 1) {
7826
+ return this.handleParallelExecution(
7827
+ parallelAgents,
7828
+ messages,
7829
+ systemMessage,
7830
+ spaceId,
7831
+ metadata,
7832
+ restOptions
7833
+ );
7834
+ }
7835
+ const targetAgent = metadata.requestedAgent;
7836
+ if (!targetAgent) {
7837
+ throw new Error("Agent mode requires requestedAgent in metadata");
7838
+ }
7839
+ console.log(
7840
+ `[ViberAgent] Agent mode: direct delegation to '${targetAgent}'`
7841
+ );
7842
+ let agent = this.space.getAgent(targetAgent);
7843
+ if (!agent) {
7844
+ console.log(`[ViberAgent] Loading agent '${targetAgent}' on demand`);
7845
+ const dataAdapter = getServerDataAdapter();
7846
+ const agentConfig = await dataAdapter.getAgent(targetAgent);
7847
+ if (!agentConfig) {
7848
+ throw new Error(`Agent '${targetAgent}' not found`);
7849
+ }
7850
+ agent = new Agent(agentConfig);
7851
+ this.space.registerAgent(targetAgent, agent);
7852
+ }
7853
+ const taskId = metadata?.taskId || metadata?.conversationId || "default";
7854
+ const task = this.space.getOrCreateTask(taskId);
7855
+ const fullHistory = task.history.getMessages();
7856
+ const messagesForAgent = fullHistory.length > 0 ? this.optimizeContextForAgent(fullHistory) : this.optimizeContextForAgent(messages);
7857
+ console.log(
7858
+ `[ViberAgent] Agent mode: using ${messagesForAgent.length} messages from history`
7859
+ );
7860
+ return await agent.streamText({
7861
+ messages: messagesForAgent,
7862
+ system: systemMessage,
7863
+ spaceId,
7864
+ metadata: {
7865
+ ...metadata,
7866
+ delegationType: "direct",
7867
+ userId: this.space.userId
7868
+ // Pass space owner ID for tracking
7869
+ },
7870
+ ...restOptions
7871
+ });
7872
+ }
7873
+ /**
7874
+ * Handle parallel execution of multiple agents
7875
+ */
7876
+ async handleParallelExecution(agentIds, messages, systemMessage, spaceId, metadata, restOptions) {
7877
+ console.log(`[ViberAgent] Parallel execution: ${agentIds.length} agents`);
7878
+ const dataAdapter = getServerDataAdapter();
7879
+ for (const agentId of agentIds) {
7880
+ if (!this.space.getAgent(agentId)) {
7881
+ const agentConfig = await dataAdapter.getAgent(agentId);
7882
+ if (!agentConfig) {
7883
+ throw new Error(`Agent '${agentId}' not found`);
7884
+ }
7885
+ const agent = new Agent(agentConfig);
7886
+ this.space.registerAgent(agentId, agent);
7887
+ }
7888
+ }
7889
+ if (!this.space.parallelEngine) {
7890
+ throw new Error("Parallel execution engine not initialized");
7891
+ }
7892
+ const tasks = agentIds.map((agentId, index) => ({
7893
+ id: `parallel-${agentId}-${Date.now()}-${index}`,
7894
+ agentId,
7895
+ messages: this.optimizeContextForAgent(messages),
7896
+ system: systemMessage,
7897
+ metadata: {
7898
+ ...metadata,
7899
+ delegationType: "parallel",
7900
+ userId: this.space.userId,
7901
+ parallelIndex: index
7902
+ },
7903
+ priority: agentIds.length - index
7904
+ // First agent gets highest priority
7905
+ }));
7906
+ const results = await this.space.parallelEngine.executeParallel(tasks);
7907
+ return {
7908
+ text: results.map((r) => `[${r.agentId}]: ${r.result.text}`).join("\n\n"),
7909
+ toolCalls: results.flatMap((r) => r.result.toolCalls || []),
7910
+ metadata: {
7911
+ ...metadata,
7912
+ parallelResults: results,
7913
+ agentCount: agentIds.length
7914
+ }
7915
+ };
7916
+ }
7917
+ /**
7918
+ * Update space history with new messages
7919
+ * Now supports per-task history
7920
+ */
7921
+ async updateSpaceHistory(messages, metadata) {
7922
+ const taskId = metadata?.taskId || metadata?.conversationId || "default";
7923
+ const task = this.space.getOrCreateTask(taskId);
7924
+ const existingMessages = task.history.getMessages();
7925
+ const newMessages = messages.length > existingMessages.length ? messages.slice(existingMessages.length) : messages;
7926
+ if (newMessages.length > 0) {
7927
+ for (const msg of newMessages) {
7928
+ const formattedMsg = {
7929
+ ...msg,
7930
+ content: typeof msg.content === "string" ? msg.content : Array.isArray(msg.content) ? msg.content : [{ type: "text", text: msg.content }]
7931
+ };
7932
+ task.history.add(formattedMsg);
7933
+ }
7934
+ console.log(
7935
+ `[ViberAgent] Updated task ${taskId} history with ${newMessages.length} new messages`
7936
+ );
7937
+ }
7938
+ }
7939
+ /**
7940
+ * Optimize message context for single-agent performance
7941
+ */
7942
+ optimizeContextForAgent(messages) {
7943
+ return messages.slice(-4);
7944
+ }
7945
+ /**
7946
+ * Extract user prompt from messages for orchestration
7947
+ */
7948
+ extractPromptFromMessages(messages) {
7949
+ const lastMessage = messages[messages.length - 1];
7950
+ if (lastMessage?.role === "user") {
7951
+ const content = lastMessage.content;
7952
+ if (typeof content === "string") {
7953
+ return content;
7954
+ } else if (Array.isArray(content)) {
7955
+ return content.filter((part) => part.type === "text" && part.text).map((part) => part.text).join(" ");
7956
+ }
7957
+ }
7958
+ return "";
7959
+ }
7960
+ /**
7961
+ * Handle message persistence after streaming completes
7962
+ */
7963
+ handleMessagePersistence(streamResult, messages, spaceId, metadata) {
7964
+ (async () => {
7965
+ try {
7966
+ let finalText = "";
7967
+ const toolInvocations = [];
7968
+ for await (const part of streamResult.fullStream) {
7969
+ switch (part.type) {
7970
+ case "text-delta":
7971
+ if (part.text) {
7972
+ finalText += part.text;
7973
+ }
7974
+ break;
7975
+ case "tool-call":
7976
+ toolInvocations.push({
7977
+ toolCallId: part.toolCallId,
7978
+ toolName: part.toolName,
7979
+ args: part.args
7980
+ });
7981
+ break;
7982
+ case "tool-result":
7983
+ const toolCall = toolInvocations.find(
7984
+ (t) => t.toolCallId === part.toolCallId
7985
+ );
7986
+ if (toolCall) {
7987
+ toolCall.result = part.result;
7988
+ }
7989
+ break;
7990
+ case "finish":
7991
+ break;
7992
+ case "error":
7993
+ console.error("[ViberAgent] Stream error:", part.error);
7994
+ return;
7995
+ }
7996
+ }
7997
+ const assistantMessage = {
7998
+ role: "assistant",
7999
+ content: [{ type: "text", text: finalText }],
8000
+ id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
8001
+ metadata: {
8002
+ agentName: this.singleAgentId || "team",
8003
+ spaceId,
8004
+ timestamp: Date.now(),
8005
+ ...metadata
8006
+ },
8007
+ ...toolInvocations.length > 0 && { toolInvocations }
8008
+ };
8009
+ const taskId = metadata?.taskId || metadata?.conversationId || "default";
8010
+ const task = this.space.getOrCreateTask(taskId);
8011
+ task.history.add(assistantMessage);
8012
+ } catch (error) {
8013
+ console.error("[ViberAgent] Failed to persist messages:", error);
8014
+ }
8015
+ })();
8016
+ }
8017
+ /**
8018
+ * Save space to storage
8019
+ */
8020
+ async saveSpace() {
8021
+ try {
8022
+ console.log("[ViberAgent] Space persistence handled by database adapter");
8023
+ } catch (error) {
8024
+ console.error("[ViberAgent] Error saving space:", error);
8025
+ }
8026
+ }
8027
+ /**
8028
+ * Stop current operation
8029
+ */
8030
+ stop() {
8031
+ this.space.messageQueue.clear();
8032
+ if (this.abortController) {
8033
+ this.abortController.abort();
8034
+ }
8035
+ }
8036
+ /**
8037
+ * Add message to queue (soft interrupt)
8038
+ */
8039
+ addMessage(message, metadata) {
8040
+ return this.space.messageQueue.add(message, metadata);
8041
+ }
8042
+ /**
8043
+ * Enrich context with space information
8044
+ */
8045
+ enrichContext(context) {
8046
+ return {
8047
+ spaceId: this.space.spaceId,
8048
+ conversationHistory: this.space.history,
8049
+ metadata: {
8050
+ spaceName: this.space.name,
8051
+ spaceGoal: this.space.goal,
8052
+ ...context?.metadata
8053
+ },
8054
+ ...context
8055
+ };
8056
+ }
8057
+ /**
8058
+ * Create or update the space plan
8059
+ */
8060
+ async createPlan(goal) {
8061
+ const planGoal = goal || this.space.goal;
8062
+ const planSchema = z11.object({
8063
+ tasks: z11.array(
8064
+ z11.object({
8065
+ id: z11.string(),
8066
+ title: z11.string(),
8067
+ description: z11.string(),
8068
+ assignedTo: z11.string().optional(),
8069
+ priority: z11.enum(["low", "medium", "high"]).default("medium"),
8070
+ estimatedTime: z11.string().optional(),
8071
+ dependencies: z11.array(
8072
+ z11.object({
8073
+ taskId: z11.string(),
8074
+ type: z11.enum(["required", "optional"])
8075
+ })
8076
+ ).default([]),
8077
+ tags: z11.array(z11.string()).default([])
8078
+ })
8079
+ )
8080
+ });
8081
+ const result = await generateText2({
8082
+ model: this.getModel(),
8083
+ system: this.getSystemPrompt() + "\n\nCreate a detailed plan to achieve the goal.",
8084
+ prompt: `Goal: ${planGoal}
8085
+
8086
+ Available agents: ${Array.from(
8087
+ this.space.agents.keys()
8088
+ ).join(", ")}`,
8089
+ output: Output.object({ schema: planSchema })
8090
+ });
8091
+ const planData = result.output;
8092
+ const tasks = planData.tasks.map(
8093
+ (taskData) => new Task({
8094
+ ...taskData,
8095
+ status: "pending" /* PENDING */
8096
+ })
8097
+ );
8098
+ const plan = new Plan({
8099
+ goal: planGoal,
8100
+ tasks
8101
+ });
8102
+ await this.space.createPlan(plan);
8103
+ return plan;
8104
+ }
8105
+ /**
8106
+ * Adapt the plan based on new information or user feedback
8107
+ */
8108
+ async adaptPlan(feedback) {
8109
+ if (!this.space.plan) {
8110
+ return this.createPlan(feedback);
8111
+ }
8112
+ const currentPlan = this.space.plan;
8113
+ const progress = currentPlan.getProgressSummary();
8114
+ const adaptSchema = z11.object({
8115
+ preserveTasks: z11.array(z11.string()).describe("IDs of tasks to keep unchanged"),
8116
+ modifyTasks: z11.array(
8117
+ z11.object({
8118
+ id: z11.string(),
8119
+ changes: z11.object({
8120
+ title: z11.string().optional(),
8121
+ description: z11.string().optional(),
8122
+ priority: z11.enum(["low", "medium", "high"]).optional(),
8123
+ assignedTo: z11.string().optional()
8124
+ })
8125
+ })
8126
+ ).describe("Tasks to modify"),
8127
+ removeTasks: z11.array(z11.string()).describe("IDs of tasks to remove"),
8128
+ addTasks: z11.array(
8129
+ z11.object({
8130
+ id: z11.string(),
8131
+ title: z11.string(),
8132
+ description: z11.string(),
8133
+ assignedTo: z11.string().optional(),
8134
+ priority: z11.enum(["low", "medium", "high"]).default("medium"),
8135
+ dependencies: z11.array(
8136
+ z11.object({
8137
+ taskId: z11.string(),
8138
+ type: z11.enum(["required", "optional"])
8139
+ })
8140
+ ).default([]),
8141
+ tags: z11.array(z11.string()).default([])
8142
+ })
8143
+ ).describe("New tasks to add"),
8144
+ reasoning: z11.string().describe("Explanation of the plan changes")
8145
+ });
8146
+ const prompt = `
8147
+ Current Plan Progress:
8148
+ - Total tasks: ${progress.totalTasks}
8149
+ - Completed: ${progress.completedTasks}
8150
+ - In Progress: ${progress.runningTasks}
8151
+ - Pending: ${progress.pendingTasks}
8152
+
8153
+ Current Tasks:
8154
+ ${currentPlan.tasks.map((t) => `- [${t.id}] ${t.title} (${t.status})`).join("\n")}
8155
+
8156
+ User Feedback: ${feedback}
8157
+
8158
+ Analyze the current plan and adapt it based on the user's feedback.
8159
+ Keep completed tasks unless explicitly asked to redo them.
8160
+ Preserve tasks that are still relevant.
8161
+ Modify, remove, or add tasks as needed to better achieve the goal.
8162
+ `;
8163
+ const result = await generateText2({
8164
+ model: this.getModel(),
8165
+ system: this.getSystemPrompt() + "\n\nAdapt the existing plan based on user feedback.",
8166
+ prompt,
8167
+ output: Output.object({ schema: adaptSchema })
8168
+ });
8169
+ const adaptData = result.output;
8170
+ const adaptedTasks = [];
8171
+ for (const taskId of adaptData.preserveTasks) {
8172
+ const task = currentPlan.tasks.find((t) => t.id === taskId);
8173
+ if (task) {
8174
+ adaptedTasks.push(task);
8175
+ }
8176
+ }
8177
+ for (const modification of adaptData.modifyTasks) {
8178
+ const task = currentPlan.tasks.find((t) => t.id === modification.id);
8179
+ if (task) {
8180
+ if (modification.changes.title) task.title = modification.changes.title;
8181
+ if (modification.changes.description)
8182
+ task.description = modification.changes.description;
8183
+ if (modification.changes.priority)
8184
+ task.priority = modification.changes.priority;
8185
+ if (modification.changes.assignedTo)
8186
+ task.assignedTo = modification.changes.assignedTo;
8187
+ adaptedTasks.push(task);
8188
+ }
8189
+ }
8190
+ for (const newTaskData of adaptData.addTasks) {
8191
+ const newTask = new Task({
8192
+ ...newTaskData,
8193
+ status: "pending" /* PENDING */
8194
+ });
8195
+ adaptedTasks.push(newTask);
8196
+ }
8197
+ const adaptedPlan = new Plan({
8198
+ goal: currentPlan.goal,
8199
+ tasks: adaptedTasks
8200
+ });
8201
+ await this.space.createPlan(adaptedPlan);
8202
+ console.log("[Plan Adaptation]", adaptData.reasoning);
8203
+ return adaptedPlan;
8204
+ }
8205
+ /**
8206
+ * Get plan context for system prompt
8207
+ */
8208
+ getPlanContext() {
8209
+ if (!this.space.plan) {
8210
+ return "\n\nNo active plan for this space yet.";
8211
+ }
8212
+ const summary = this.space.plan.getProgressSummary();
8213
+ return `
8214
+
8215
+ Current Plan Status:
8216
+ - Total tasks: ${summary.totalTasks}
8217
+ - Completed: ${summary.completedTasks}
8218
+ - Running: ${summary.runningTasks}
8219
+ - Pending: ${summary.pendingTasks}
8220
+ - Failed: ${summary.failedTasks}
8221
+ - Progress: ${summary.progressPercentage.toFixed(1)}%
8222
+ `;
8223
+ }
8224
+ /**
8225
+ * Get artifacts context for system prompt
8226
+ */
8227
+ getArtifactsContext() {
8228
+ if (!this.space.artifacts || this.space.artifacts.length === 0) {
8229
+ return "";
8230
+ }
8231
+ const artifactsList = this.space.artifacts.map((a) => `- ${a.title || a.path} (${a.artifactType || "document"})`).join("\n");
8232
+ return `
8233
+
8234
+ Available Artifacts:
8235
+ ${artifactsList}
8236
+
8237
+ These artifacts are pre-loaded in the space and can be referenced in your responses.
8238
+ `;
8239
+ }
8240
+ /**
8241
+ * Get ViberAgent summary
8242
+ */
8243
+ getSummary() {
8244
+ const base = super.getSummary();
8245
+ return {
8246
+ ...base,
8247
+ spaceId: this.space.spaceId,
8248
+ spaceName: this.space.name,
8249
+ spaceGoal: this.space.goal,
8250
+ planStatus: this.space.plan?.getProgressSummary()
8251
+ };
8252
+ }
8253
+ /**
8254
+ * Static factory to start a new space
8255
+ */
8256
+ static async start(goal, options = {}) {
8257
+ const { spaceId, model, singleAgentId } = options;
8258
+ const id = spaceId || `proj_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
8259
+ const { startSpace: startSpace2 } = await Promise.resolve().then(() => (init_space2(), space_exports2));
8260
+ const space = await startSpace2({
8261
+ spaceId: id,
8262
+ goal,
8263
+ name: goal.slice(0, 50),
8264
+ model
8265
+ });
8266
+ if (!space.viberAgent) {
8267
+ throw new Error("Failed to initialize ViberAgent");
8268
+ }
8269
+ if (singleAgentId) {
8270
+ space.viberAgent.singleAgentId = singleAgentId;
8271
+ }
8272
+ return space.viberAgent;
8273
+ }
8274
+ /**
8275
+ * Static factory to resume an existing space
8276
+ */
8277
+ static async resume(spaceId, options = {}) {
8278
+ const { model } = options;
8279
+ const { SpaceStorageFactory: SpaceStorageFactory2 } = await Promise.resolve().then(() => (init_space(), space_exports));
8280
+ const { startSpace: startSpace2 } = await Promise.resolve().then(() => (init_space2(), space_exports2));
8281
+ const exists = await SpaceStorageFactory2.exists(spaceId);
8282
+ if (!exists) {
8283
+ throw new Error(`Space ${spaceId} not found`);
8284
+ }
8285
+ const storage = await SpaceStorageFactory2.create(spaceId);
8286
+ const spaceData = await storage.readJSON("space.json");
8287
+ if (!spaceData) {
8288
+ throw new Error(`Failed to load space ${spaceId}`);
8289
+ }
8290
+ const space = await startSpace2({
8291
+ spaceId,
8292
+ goal: spaceData.goal,
8293
+ name: spaceData.name,
8294
+ model: model || spaceData.model
8295
+ });
8296
+ if (!space.viberAgent) {
8297
+ throw new Error("Failed to initialize ViberAgent");
8298
+ }
8299
+ const agentId = options.singleAgentId || spaceData.singleAgentId;
8300
+ if (agentId) {
8301
+ space.viberAgent.singleAgentId = agentId;
8302
+ }
8303
+ const messages = await storage.readJSON("messages.json");
8304
+ if (messages && Array.isArray(messages)) {
8305
+ space.history.clear();
8306
+ for (const msg of messages) {
8307
+ const normalizedMsg = { ...msg };
8308
+ if (typeof msg.content === "string") {
8309
+ normalizedMsg.content = [{ type: "text", text: msg.content }];
8310
+ }
8311
+ space.history.add(normalizedMsg);
8312
+ }
8313
+ }
8314
+ return space.viberAgent;
8315
+ }
8316
+ };
8317
+ }
8318
+ });
8319
+
8320
+ // src/channels/manager.ts
8321
+ var manager_exports = {};
8322
+ __export(manager_exports, {
8323
+ ChannelManager: () => ChannelManager,
8324
+ channelManager: () => channelManager
8325
+ });
8326
+ import { EventEmitter as EventEmitter3 } from "events";
8327
+ var ChannelManager, channelManager;
8328
+ var init_manager = __esm({
8329
+ "src/channels/manager.ts"() {
8330
+ "use strict";
8331
+ init_viber_agent();
8332
+ ChannelManager = class extends EventEmitter3 {
8333
+ channels = /* @__PURE__ */ new Map();
8334
+ conversations = /* @__PURE__ */ new Map();
8335
+ /**
8336
+ * Register a channel
8337
+ */
8338
+ register(channel) {
8339
+ if (this.channels.has(channel.id)) {
8340
+ console.warn(`[ChannelManager] Channel ${channel.id} already registered`);
8341
+ return;
8342
+ }
8343
+ this.channels.set(channel.id, channel);
8344
+ console.log(`[ChannelManager] Registered channel: ${channel.id}`);
8345
+ }
8346
+ /**
8347
+ * Unregister a channel
8348
+ */
8349
+ unregister(id) {
8350
+ this.channels.delete(id);
8351
+ console.log(`[ChannelManager] Unregistered channel: ${id}`);
8352
+ }
8353
+ /**
8354
+ * Get a channel by ID
8355
+ */
8356
+ getChannel(id) {
8357
+ return this.channels.get(id);
8358
+ }
8359
+ /**
8360
+ * Start all registered channels
8361
+ */
8362
+ async startAll() {
8363
+ for (const [id, channel] of this.channels) {
8364
+ try {
8365
+ await channel.start();
8366
+ console.log(`[ChannelManager] Started channel: ${id}`);
8367
+ } catch (error) {
8368
+ console.error(`[ChannelManager] Failed to start channel ${id}:`, error);
8369
+ }
8370
+ }
8371
+ }
8372
+ /**
8373
+ * Stop all registered channels
8374
+ */
8375
+ async stopAll() {
8376
+ for (const [id, channel] of this.channels) {
8377
+ try {
8378
+ await channel.stop();
8379
+ console.log(`[ChannelManager] Stopped channel: ${id}`);
8380
+ } catch (error) {
8381
+ console.error(`[ChannelManager] Failed to stop channel ${id}:`, error);
8382
+ }
8383
+ }
8384
+ }
8385
+ /**
8386
+ * Route an inbound message to ViberAgent
8387
+ */
8388
+ async routeMessage(message) {
8389
+ const { source, conversationId, content, userId } = message;
8390
+ console.log(
8391
+ `[ChannelManager] Routing message from ${source}: ${content.substring(0, 50)}...`
8392
+ );
8393
+ let conversation = this.conversations.get(conversationId);
8394
+ if (!conversation) {
8395
+ const agent = await ViberAgent.start(content, {
8396
+ // Use conversation ID as space ID for isolation
8397
+ spaceId: conversationId
8398
+ });
8399
+ conversation = {
8400
+ channelId: source,
8401
+ agent,
8402
+ startedAt: /* @__PURE__ */ new Date()
8403
+ };
8404
+ this.conversations.set(conversationId, conversation);
8405
+ }
8406
+ try {
8407
+ const result = await conversation.agent.streamText({
8408
+ messages: [{ role: "user", content }],
8409
+ metadata: { userId, source }
8410
+ });
8411
+ const channel = this.channels.get(source);
8412
+ if (!channel) {
8413
+ console.error(`[ChannelManager] Channel not found: ${source}`);
8414
+ return;
8415
+ }
8416
+ for await (const chunk of result.fullStream) {
8417
+ await channel.stream(conversationId, this.mapToStreamEvent(chunk));
8418
+ }
8419
+ await channel.stream(conversationId, {
8420
+ type: "done",
8421
+ agentId: conversation.agent.spaceId
8422
+ });
8423
+ } catch (error) {
8424
+ console.error(`[ChannelManager] Error processing message:`, error);
8425
+ const channel = this.channels.get(source);
8426
+ if (channel) {
8427
+ await channel.stream(conversationId, {
8428
+ type: "error",
8429
+ error: error.message,
8430
+ agentId: conversation?.agent?.spaceId || "unknown"
8431
+ });
8432
+ }
8433
+ }
8434
+ }
8435
+ /**
8436
+ * Handle interrupt signal
8437
+ */
8438
+ async handleInterrupt(signal) {
8439
+ const conversation = this.conversations.get(signal.conversationId);
8440
+ if (conversation) {
8441
+ conversation.agent.stop();
8442
+ this.conversations.delete(signal.conversationId);
8443
+ console.log(
8444
+ `[ChannelManager] Interrupted conversation: ${signal.conversationId}`
8445
+ );
8446
+ }
8447
+ }
8448
+ /**
8449
+ * Map AI SDK stream chunk to AgentStreamEvent
8450
+ */
8451
+ mapToStreamEvent(chunk) {
8452
+ if (chunk.type === "text-delta") {
8453
+ return {
8454
+ type: "text-delta",
8455
+ content: chunk.textDelta,
8456
+ agentId: "viber"
8457
+ };
8458
+ }
8459
+ if (chunk.type === "tool-call") {
8460
+ return {
8461
+ type: "tool-call",
8462
+ tool: chunk.toolName,
8463
+ args: chunk.args,
8464
+ agentId: "viber"
8465
+ };
8466
+ }
8467
+ if (chunk.type === "tool-result") {
8468
+ return {
8469
+ type: "tool-result",
8470
+ tool: chunk.toolName,
8471
+ result: chunk.result,
8472
+ agentId: "viber"
8473
+ };
8474
+ }
8475
+ return {
8476
+ type: "state-change",
8477
+ state: chunk.type,
8478
+ agentId: "viber"
8479
+ };
8480
+ }
8481
+ };
8482
+ channelManager = new ChannelManager();
8483
+ }
8484
+ });
8485
+
8486
+ // src/channels/dingtalk.ts
8487
+ var dingtalk_exports = {};
8488
+ __export(dingtalk_exports, {
8489
+ DingTalkChannel: () => DingTalkChannel
8490
+ });
8491
+ import crypto from "crypto";
8492
+ var DingTalkChannel;
8493
+ var init_dingtalk = __esm({
8494
+ "src/channels/dingtalk.ts"() {
8495
+ "use strict";
8496
+ DingTalkChannel = class {
8497
+ id = "dingtalk";
8498
+ type = "webhook";
8499
+ config;
8500
+ sessionWebhooks = /* @__PURE__ */ new Map();
8501
+ responseBuffers = /* @__PURE__ */ new Map();
8502
+ constructor(config2) {
8503
+ this.config = config2;
8504
+ }
8505
+ async start() {
8506
+ console.log("[DingTalk] Channel started");
8507
+ }
8508
+ async stop() {
8509
+ this.sessionWebhooks.clear();
8510
+ this.responseBuffers.clear();
8511
+ console.log("[DingTalk] Channel stopped");
8512
+ }
8513
+ /**
8514
+ * Verify webhook signature
8515
+ */
8516
+ verifySignature(timestamp, sign) {
8517
+ const stringToSign = timestamp + "\n" + this.config.appSecret;
8518
+ const hmac = crypto.createHmac("sha256", this.config.appSecret);
8519
+ hmac.update(stringToSign);
8520
+ const expectedSign = hmac.digest("base64");
8521
+ return sign === expectedSign;
8522
+ }
8523
+ /**
8524
+ * Parse webhook payload to InboundMessage
8525
+ */
8526
+ parseWebhook(payload) {
8527
+ this.sessionWebhooks.set(payload.conversationId, payload.sessionWebhook);
8528
+ return {
8529
+ id: payload.msgId,
8530
+ source: this.id,
8531
+ userId: payload.senderId,
8532
+ conversationId: payload.conversationId,
8533
+ content: payload.text?.content || "",
8534
+ metadata: {
8535
+ senderNick: payload.senderNick,
8536
+ conversationType: payload.conversationType,
8537
+ conversationTitle: payload.conversationTitle,
8538
+ robotCode: payload.robotCode
8539
+ }
8540
+ };
8541
+ }
8542
+ async handleMessage(message) {
8543
+ this.responseBuffers.set(message.conversationId, "");
8544
+ }
8545
+ async stream(conversationId, event) {
8546
+ const sessionWebhook = this.sessionWebhooks.get(conversationId);
8547
+ if (!sessionWebhook) {
8548
+ console.error(
8549
+ `[DingTalk] No session webhook for conversation: ${conversationId}`
8550
+ );
8551
+ return;
8552
+ }
8553
+ if (event.type === "text-delta") {
8554
+ const current = this.responseBuffers.get(conversationId) || "";
8555
+ this.responseBuffers.set(conversationId, current + event.content);
8556
+ } else if (event.type === "done") {
8557
+ const text = this.responseBuffers.get(conversationId) || "";
8558
+ await this.sendMessage(sessionWebhook, {
8559
+ msgtype: "markdown",
8560
+ markdown: {
8561
+ title: "Reply",
8562
+ text
8563
+ }
8564
+ });
8565
+ this.responseBuffers.delete(conversationId);
8566
+ } else if (event.type === "error") {
8567
+ await this.sendMessage(sessionWebhook, {
8568
+ msgtype: "text",
8569
+ text: { content: `Error: ${event.error}` }
8570
+ });
8571
+ this.responseBuffers.delete(conversationId);
8572
+ }
8573
+ }
8574
+ /**
8575
+ * Send message via DingTalk session webhook
8576
+ */
8577
+ async sendMessage(webhookUrl, message) {
8578
+ try {
8579
+ const response = await fetch(webhookUrl, {
8580
+ method: "POST",
8581
+ headers: { "Content-Type": "application/json" },
8582
+ body: JSON.stringify(message)
8583
+ });
8584
+ if (!response.ok) {
8585
+ const error = await response.text();
8586
+ console.error("[DingTalk] Send message failed:", error);
8587
+ }
8588
+ } catch (error) {
8589
+ console.error("[DingTalk] Send message error:", error);
8590
+ }
8591
+ }
8592
+ };
8593
+ }
8594
+ });
8595
+
8596
+ // src/channels/wecom.ts
8597
+ var wecom_exports = {};
8598
+ __export(wecom_exports, {
8599
+ WeComChannel: () => WeComChannel
8600
+ });
8601
+ import crypto2 from "crypto";
8602
+ var WeComCrypto, WeComChannel;
8603
+ var init_wecom = __esm({
8604
+ "src/channels/wecom.ts"() {
8605
+ "use strict";
8606
+ WeComCrypto = class {
8607
+ token;
8608
+ aesKey;
8609
+ corpId;
8610
+ constructor(token, encodingAESKey, corpId) {
8611
+ this.token = token;
8612
+ this.corpId = corpId;
8613
+ this.aesKey = Buffer.from(encodingAESKey + "=", "base64");
8614
+ }
8615
+ /**
8616
+ * Verify callback URL signature
8617
+ */
8618
+ verifySignature(signature, timestamp, nonce, echostr) {
8619
+ const sortedParams = [this.token, timestamp, nonce, echostr || ""].filter(Boolean).sort().join("");
8620
+ const hash = crypto2.createHash("sha1").update(sortedParams).digest("hex");
8621
+ return hash === signature;
8622
+ }
8623
+ /**
8624
+ * Decrypt message
8625
+ */
8626
+ decrypt(encrypted) {
8627
+ const decipher = crypto2.createDecipheriv(
8628
+ "aes-256-cbc",
8629
+ this.aesKey,
8630
+ this.aesKey.subarray(0, 16)
8631
+ );
8632
+ decipher.setAutoPadding(false);
8633
+ let decrypted = Buffer.concat([
8634
+ decipher.update(Buffer.from(encrypted, "base64")),
8635
+ decipher.final()
8636
+ ]);
8637
+ const padLen = decrypted[decrypted.length - 1];
8638
+ decrypted = decrypted.subarray(0, decrypted.length - padLen);
8639
+ const msgLen = decrypted.readUInt32BE(16);
8640
+ const content = decrypted.subarray(20, 20 + msgLen).toString("utf-8");
8641
+ return content;
8642
+ }
8643
+ /**
8644
+ * Encrypt message for reply
8645
+ */
8646
+ encrypt(message) {
8647
+ const random = crypto2.randomBytes(16);
8648
+ const msgBuffer = Buffer.from(message, "utf-8");
8649
+ const msgLen = Buffer.alloc(4);
8650
+ msgLen.writeUInt32BE(msgBuffer.length);
8651
+ const corpIdBuffer = Buffer.from(this.corpId, "utf-8");
8652
+ const plaintext = Buffer.concat([
8653
+ random,
8654
+ msgLen,
8655
+ msgBuffer,
8656
+ corpIdBuffer
8657
+ ]);
8658
+ const blockSize = 32;
8659
+ const padLen = blockSize - plaintext.length % blockSize;
8660
+ const padding = Buffer.alloc(padLen, padLen);
8661
+ const padded = Buffer.concat([plaintext, padding]);
8662
+ const cipher = crypto2.createCipheriv(
8663
+ "aes-256-cbc",
8664
+ this.aesKey,
8665
+ this.aesKey.subarray(0, 16)
8666
+ );
8667
+ cipher.setAutoPadding(false);
8668
+ const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]);
8669
+ return encrypted.toString("base64");
8670
+ }
8671
+ };
8672
+ WeComChannel = class {
8673
+ id = "wecom";
8674
+ type = "webhook";
8675
+ config;
8676
+ crypto;
8677
+ accessToken = null;
8678
+ tokenExpiry = 0;
8679
+ responseBuffers = /* @__PURE__ */ new Map();
8680
+ constructor(config2) {
8681
+ this.config = config2;
8682
+ this.crypto = new WeComCrypto(config2.token, config2.aesKey, config2.corpId);
8683
+ }
8684
+ async start() {
8685
+ await this.refreshAccessToken();
8686
+ console.log("[WeCom] Channel started");
8687
+ }
8688
+ async stop() {
8689
+ this.accessToken = null;
8690
+ this.responseBuffers.clear();
8691
+ console.log("[WeCom] Channel stopped");
8692
+ }
8693
+ /**
8694
+ * Verify URL callback (for WeCom verification)
8695
+ */
8696
+ verifyUrl(signature, timestamp, nonce, echostr) {
8697
+ if (this.crypto.verifySignature(signature, timestamp, nonce, echostr)) {
8698
+ return this.crypto.decrypt(echostr);
8699
+ }
8700
+ return null;
8701
+ }
8702
+ /**
8703
+ * Parse XML webhook payload
8704
+ */
8705
+ parseWebhook(xmlContent, encrypted) {
8706
+ const decrypted = this.crypto.decrypt(encrypted);
8707
+ const getTag = (xml, tag) => {
8708
+ const match = xml.match(new RegExp(`<${tag}><\\!\\[CDATA\\[(.+?)\\]\\]></${tag}>`));
8709
+ return match ? match[1] : "";
8710
+ };
8711
+ const fromUser = getTag(decrypted, "FromUserName");
8712
+ const content = getTag(decrypted, "Content");
8713
+ const msgId = getTag(decrypted, "MsgId");
8714
+ if (!content) return null;
8715
+ return {
8716
+ id: msgId || crypto2.randomUUID(),
8717
+ source: this.id,
8718
+ userId: fromUser,
8719
+ conversationId: fromUser,
8720
+ // Use user ID as conversation ID
8721
+ content,
8722
+ metadata: {
8723
+ corpId: this.config.corpId,
8724
+ agentId: this.config.agentId
8725
+ }
8726
+ };
8727
+ }
8728
+ async handleMessage(message) {
8729
+ this.responseBuffers.set(message.conversationId, "");
8730
+ }
8731
+ async stream(conversationId, event) {
8732
+ if (event.type === "text-delta") {
8733
+ const current = this.responseBuffers.get(conversationId) || "";
8734
+ this.responseBuffers.set(conversationId, current + event.content);
8735
+ } else if (event.type === "done") {
8736
+ const text = this.responseBuffers.get(conversationId) || "";
8737
+ await this.sendMessage(conversationId, text);
8738
+ this.responseBuffers.delete(conversationId);
8739
+ } else if (event.type === "error") {
8740
+ await this.sendMessage(conversationId, `Error: ${event.error}`);
8741
+ this.responseBuffers.delete(conversationId);
8742
+ }
8743
+ }
8744
+ /**
8745
+ * Send message via WeCom API
8746
+ */
8747
+ async sendMessage(userId, content) {
8748
+ const token = await this.getAccessToken();
8749
+ const url = `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${token}`;
8750
+ try {
8751
+ const response = await fetch(url, {
8752
+ method: "POST",
8753
+ headers: { "Content-Type": "application/json" },
8754
+ body: JSON.stringify({
8755
+ touser: userId,
8756
+ msgtype: "markdown",
8757
+ agentid: this.config.agentId,
8758
+ markdown: { content }
8759
+ })
8760
+ });
8761
+ const result = await response.json();
8762
+ if (result.errcode !== 0) {
8763
+ console.error("[WeCom] Send message failed:", result);
8764
+ }
8765
+ } catch (error) {
8766
+ console.error("[WeCom] Send message error:", error);
8767
+ }
8768
+ }
8769
+ /**
8770
+ * Get access token (with caching)
8771
+ */
8772
+ async getAccessToken() {
8773
+ if (this.accessToken && Date.now() < this.tokenExpiry) {
8774
+ return this.accessToken;
8775
+ }
8776
+ await this.refreshAccessToken();
8777
+ return this.accessToken;
8778
+ }
8779
+ /**
8780
+ * Refresh access token from WeCom API
8781
+ */
8782
+ async refreshAccessToken() {
8783
+ const url = `https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${this.config.corpId}&corpsecret=${this.config.secret}`;
8784
+ try {
8785
+ const response = await fetch(url);
8786
+ const result = await response.json();
8787
+ if (result.errcode === 0) {
8788
+ this.accessToken = result.access_token;
8789
+ this.tokenExpiry = Date.now() + (result.expires_in - 300) * 1e3;
8790
+ } else {
8791
+ console.error("[WeCom] Get token failed:", result);
8792
+ }
8793
+ } catch (error) {
8794
+ console.error("[WeCom] Get token error:", error);
8795
+ }
8796
+ }
8797
+ };
8798
+ }
8799
+ });
8800
+
8801
+ // src/cli/index.ts
8802
+ import "dotenv/config";
8803
+ import { program } from "commander";
8804
+ import * as os2 from "os";
8805
+ import * as fs7 from "fs/promises";
8806
+ import * as path13 from "path";
8807
+ var VERSION = "1.0.0";
8808
+ program.name("openviber").description("OpenViber - Workspace-first assistant runtime (vibers on your machines)").version(VERSION);
8809
+ program.command("start").description(
8810
+ "Start viber with all apps (local mode, or connect to server with --server)"
8811
+ ).option("-s, --server <url>", "Command center URL (enables connected mode)").option("-t, --token <token>", "Authentication token (or set VIBER_TOKEN)").option("-n, --name <name>", "Viber name", `${os2.hostname()}-viber`).option("--desktop", "Enable desktop control (UI-TARS)").option("--disable-app <apps...>", "Disable specific apps (comma-separated)").option("--no-apps", "Disable all apps").option("--reconnect-interval <ms>", "Reconnect interval in ms", "5000").option("--heartbeat-interval <ms>", "Heartbeat interval in ms", "30000").action(async (options) => {
8812
+ const { JobScheduler: JobScheduler2 } = await Promise.resolve().then(() => (init_scheduler(), scheduler_exports));
8813
+ const { ViberController: ViberController2 } = await Promise.resolve().then(() => (init_controller(), controller_exports));
8814
+ const { EventEmitter: EventEmitter4 } = await import("events");
8815
+ await Promise.resolve().then(() => (init_skills(), skills_exports));
8816
+ const viberId = await getViberId();
8817
+ const token = options.token || process.env.VIBER_TOKEN;
8818
+ const connectToServer = options.server && token;
8819
+ if (options.server && !token) {
8820
+ console.error(
8821
+ "Error: Authentication token required when using --server."
8822
+ );
8823
+ console.error(
8824
+ "Use --token <token> or set VIBER_TOKEN environment variable."
6371
8825
  );
6372
8826
  console.error("\nTo get a token, run: viber login");
6373
8827
  process.exit(1);
@@ -6533,6 +8987,166 @@ Viber Status
6533
8987
  \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
6534
8988
  `);
6535
8989
  });
8990
+ program.command("onboard").description("Initialize OpenViber configuration (first-time setup)").action(async () => {
8991
+ const configDir = path13.join(os2.homedir(), ".openviber");
8992
+ const agentsDir = path13.join(configDir, "agents");
8993
+ const jobsDir = path13.join(configDir, "jobs");
8994
+ const spaceDir = path13.join(configDir, "space");
8995
+ console.log(`
8996
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
8997
+ \u2551 OPENVIBER SETUP \u2551
8998
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
8999
+ `);
9000
+ console.log("Creating directories...");
9001
+ await fs7.mkdir(configDir, { recursive: true });
9002
+ await fs7.mkdir(agentsDir, { recursive: true });
9003
+ await fs7.mkdir(jobsDir, { recursive: true });
9004
+ await fs7.mkdir(spaceDir, { recursive: true });
9005
+ console.log(` \u2713 ${configDir}`);
9006
+ console.log(` \u2713 ${agentsDir}`);
9007
+ console.log(` \u2713 ${jobsDir}`);
9008
+ console.log(` \u2713 ${spaceDir}`);
9009
+ const defaultAgentPath = path13.join(agentsDir, "default.yaml");
9010
+ try {
9011
+ await fs7.access(defaultAgentPath);
9012
+ console.log(`
9013
+ \u23ED agents/default.yaml already exists, skipping`);
9014
+ } catch {
9015
+ const defaultAgent = `# Default Viber Agent Configuration
9016
+ name: default
9017
+ description: General-purpose assistant
9018
+
9019
+ # LLM Provider (openrouter recommended for multi-model access)
9020
+ provider: openrouter
9021
+ model: anthropic/claude-sonnet-4-20250514
9022
+
9023
+ # System prompt
9024
+ systemPrompt: |
9025
+ You are a helpful AI assistant running on the user's local machine.
9026
+ You have access to files, terminal, and browser tools.
9027
+ Be concise and helpful.
9028
+
9029
+ # Tools available to this agent
9030
+ tools:
9031
+ - file
9032
+ - terminal
9033
+ - browser
9034
+
9035
+ # Working mode: "always-ask" | "agent-decides" | "always-execute"
9036
+ workingMode: agent-decides
9037
+ `;
9038
+ await fs7.writeFile(defaultAgentPath, defaultAgent);
9039
+ console.log(`
9040
+ \u2713 Created agents/default.yaml`);
9041
+ }
9042
+ const taskPath = path13.join(spaceDir, "task.md");
9043
+ try {
9044
+ await fs7.access(taskPath);
9045
+ } catch {
9046
+ await fs7.writeFile(taskPath, "# Current Task\n\nNo active task.\n");
9047
+ console.log(` \u2713 Created space/task.md`);
9048
+ }
9049
+ const memoryPath = path13.join(spaceDir, "MEMORY.md");
9050
+ try {
9051
+ await fs7.access(memoryPath);
9052
+ } catch {
9053
+ await fs7.writeFile(memoryPath, "# Memory\n\nLong-term notes and context.\n");
9054
+ console.log(` \u2713 Created space/MEMORY.md`);
9055
+ }
9056
+ const viberId = await getViberId();
9057
+ console.log(`
9058
+ \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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
9059
+ Setup complete!
9060
+
9061
+ Your viber ID: ${viberId}
9062
+ Config directory: ${configDir}
9063
+
9064
+ Next steps:
9065
+ 1. Set your API key:
9066
+ export OPENROUTER_API_KEY="sk-or-v1-xxx"
9067
+
9068
+ 2. Start your viber:
9069
+ openviber start
9070
+
9071
+ 3. Or run a quick task:
9072
+ openviber run "Hello, what can you do?"
9073
+
9074
+ Get an API key at: https://openrouter.ai/keys
9075
+ \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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
9076
+ `);
9077
+ });
9078
+ program.command("gateway").description("Start the enterprise channel gateway (DingTalk, WeCom, etc.)").option("-p, --port <port>", "Gateway port", "6009").action(async (options) => {
9079
+ const { channelManager: channelManager2 } = await Promise.resolve().then(() => (init_manager(), manager_exports));
9080
+ const { DingTalkChannel: DingTalkChannel2 } = await Promise.resolve().then(() => (init_dingtalk(), dingtalk_exports));
9081
+ const { WeComChannel: WeComChannel2 } = await Promise.resolve().then(() => (init_wecom(), wecom_exports));
9082
+ console.log(`
9083
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
9084
+ \u2551 GATEWAY STARTING \u2551
9085
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
9086
+ `);
9087
+ const channels = [];
9088
+ if (process.env.DINGTALK_APP_KEY && process.env.DINGTALK_APP_SECRET) {
9089
+ channelManager2.register(
9090
+ new DingTalkChannel2({
9091
+ enabled: true,
9092
+ appKey: process.env.DINGTALK_APP_KEY,
9093
+ appSecret: process.env.DINGTALK_APP_SECRET,
9094
+ robotCode: process.env.DINGTALK_ROBOT_CODE
9095
+ })
9096
+ );
9097
+ channels.push("DingTalk");
9098
+ }
9099
+ if (process.env.WECOM_CORP_ID && process.env.WECOM_AGENT_SECRET && process.env.WECOM_TOKEN && process.env.WECOM_ENCODING_AES_KEY) {
9100
+ channelManager2.register(
9101
+ new WeComChannel2({
9102
+ enabled: true,
9103
+ corpId: process.env.WECOM_CORP_ID,
9104
+ agentId: process.env.WECOM_AGENT_ID || "0",
9105
+ secret: process.env.WECOM_AGENT_SECRET,
9106
+ token: process.env.WECOM_TOKEN,
9107
+ aesKey: process.env.WECOM_ENCODING_AES_KEY
9108
+ })
9109
+ );
9110
+ channels.push("WeCom");
9111
+ }
9112
+ if (channels.length === 0) {
9113
+ console.log(`
9114
+ No channels configured. Set environment variables to enable channels:
9115
+
9116
+ DingTalk:
9117
+ DINGTALK_APP_KEY, DINGTALK_APP_SECRET, DINGTALK_ROBOT_CODE
9118
+
9119
+ WeCom:
9120
+ WECOM_CORP_ID, WECOM_AGENT_ID, WECOM_AGENT_SECRET
9121
+ WECOM_TOKEN, WECOM_ENCODING_AES_KEY (optional)
9122
+
9123
+ Run 'openviber gateway' again after setting environment variables.
9124
+ `);
9125
+ process.exit(1);
9126
+ }
9127
+ await channelManager2.startAll();
9128
+ process.on("SIGINT", async () => {
9129
+ console.log("\n[Gateway] Shutting down...");
9130
+ await channelManager2.stopAll();
9131
+ process.exit(0);
9132
+ });
9133
+ process.on("SIGTERM", async () => {
9134
+ console.log("\n[Gateway] Shutting down...");
9135
+ await channelManager2.stopAll();
9136
+ process.exit(0);
9137
+ });
9138
+ console.log(`
9139
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
9140
+ \u2551 GATEWAY RUNNING \u2551
9141
+ \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
9142
+ \u2551 Channels: ${channels.join(", ").padEnd(41)}\u2551
9143
+ \u2551 Status: \u25CF Ready \u2551
9144
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
9145
+
9146
+ Listening for messages from enterprise channels...
9147
+ Press Ctrl+C to stop.
9148
+ `);
9149
+ });
6536
9150
  async function getViberId() {
6537
9151
  const configDir = path13.join(os2.homedir(), ".openviber");
6538
9152
  const idFile = path13.join(configDir, "viber-id");