openviber 0.4.3 → 0.5.1

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