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/index.js CHANGED
@@ -4514,21 +4514,38 @@ import * as path8 from "path";
4514
4514
  import * as yaml2 from "yaml";
4515
4515
  import { fileURLToPath as fileURLToPath2 } from "url";
4516
4516
  function getDefaultSkillsPath() {
4517
- const cwdPath = path8.resolve(process.cwd(), "src/skills");
4517
+ const userSkillsPath = path8.join(getViberRoot(), "skills");
4518
4518
  try {
4519
- fsSync.accessSync(cwdPath);
4520
- console.log(`[SkillRegistry] Using skills path (cwd): ${cwdPath}`);
4521
- return cwdPath;
4519
+ fsSync.accessSync(userSkillsPath);
4520
+ console.log(`[SkillRegistry] Using skills path (user): ${userSkillsPath}`);
4521
+ return userSkillsPath;
4522
4522
  } catch {
4523
- const relativePath = path8.resolve(__dirname2, ".");
4523
+ const bundledPath = path8.resolve(__dirname2, ".");
4524
4524
  try {
4525
- fsSync.accessSync(relativePath);
4526
- console.log(`[SkillRegistry] Using skills path (relative): ${relativePath}`);
4527
- return relativePath;
4525
+ fsSync.accessSync(bundledPath);
4526
+ const hasSkillDirs = fsSync.readdirSync(bundledPath).some((f) => {
4527
+ const skillMd = path8.join(bundledPath, f, "SKILL.md");
4528
+ try {
4529
+ fsSync.accessSync(skillMd);
4530
+ return true;
4531
+ } catch {
4532
+ return false;
4533
+ }
4534
+ });
4535
+ if (hasSkillDirs) {
4536
+ console.log(`[SkillRegistry] Using skills path (bundled): ${bundledPath}`);
4537
+ return bundledPath;
4538
+ }
4528
4539
  } catch {
4529
- const viberPath = path8.join(getViberRoot(), "skills");
4530
- console.log(`[SkillRegistry] Using skills path (viber root): ${viberPath}`);
4531
- return viberPath;
4540
+ }
4541
+ const devPath = path8.resolve(process.cwd(), "src/skills");
4542
+ try {
4543
+ fsSync.accessSync(devPath);
4544
+ console.log(`[SkillRegistry] Using skills path (dev): ${devPath}`);
4545
+ return devPath;
4546
+ } catch {
4547
+ console.log(`[SkillRegistry] Using skills path (default): ${userSkillsPath}`);
4548
+ return userSkillsPath;
4532
4549
  }
4533
4550
  }
4534
4551
  }
@@ -7347,103 +7364,33 @@ async function runTask(goal, options, messages) {
7347
7364
  }
7348
7365
 
7349
7366
  // src/daemon/terminal.ts
7350
- import { spawn, execSync } from "child_process";
7367
+ import { spawn, spawnSync, execSync } from "child_process";
7351
7368
  import { EventEmitter } from "events";
7352
- function listSessions() {
7353
- try {
7354
- const out = execSync(
7355
- "tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
7356
- { encoding: "utf8", stdio: "pipe" }
7357
- ).trim();
7358
- if (!out) return [];
7359
- return out.split("\n").map((line) => {
7360
- const [name, windows, attached] = line.split("|");
7361
- return {
7362
- name,
7363
- windows: parseInt(windows, 10) || 0,
7364
- attached: attached === "1"
7365
- };
7366
- });
7367
- } catch {
7368
- return [];
7369
- }
7370
- }
7371
- function listPanes() {
7372
- try {
7373
- const out = execSync(
7374
- "tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{pane_index}|#{pane_current_command}' 2>/dev/null",
7375
- { encoding: "utf8", stdio: "pipe" }
7376
- ).trim();
7377
- if (!out) return [];
7378
- return out.split("\n").map((line) => {
7379
- const [session, window2, windowName, pane, command] = line.split("|");
7380
- return {
7381
- session,
7382
- window: window2,
7383
- windowName,
7384
- pane,
7385
- command,
7386
- target: `${session}:${window2}.${pane}`
7387
- };
7388
- });
7389
- } catch {
7390
- return [];
7391
- }
7369
+ import { randomUUID } from "crypto";
7370
+ var SAFE_NAME_RE = /[^a-zA-Z0-9_.:-]/g;
7371
+ function sanitizeName(input) {
7372
+ return input.replace(SAFE_NAME_RE, "-");
7392
7373
  }
7393
- function sendKeys(target, keys, pressEnter = false) {
7394
- try {
7395
- const args = ["send-keys", "-t", target, keys];
7396
- if (pressEnter) args.push("Enter");
7397
- execSync(`tmux ${args.map((a) => `'${a}'`).join(" ")}`, {
7398
- encoding: "utf8",
7399
- stdio: "pipe"
7400
- });
7401
- return true;
7402
- } catch {
7403
- return false;
7404
- }
7405
- }
7406
- function capturePane(target, lines = 500) {
7407
- const cmds = [
7408
- `tmux capture-pane -t '${target}' -pae -S -${lines}`,
7409
- `tmux capture-pane -t '${target}' -pe -S -${lines}`
7410
- ];
7411
- for (const cmd of cmds) {
7412
- try {
7413
- return execSync(cmd, { encoding: "utf8", stdio: "pipe" });
7414
- } catch {
7415
- }
7416
- }
7417
- return "";
7418
- }
7419
- function resizePane(target, cols, rows) {
7420
- try {
7421
- execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
7422
- encoding: "utf8",
7423
- stdio: "pipe"
7424
- });
7425
- return true;
7426
- } catch {
7427
- return false;
7374
+ function resolveAppTarget(target, appHint) {
7375
+ if (target.includes("::")) {
7376
+ const [appId, ...rest] = target.split("::");
7377
+ return { appId, rawTarget: rest.join("::") };
7428
7378
  }
7379
+ return { appId: appHint || "tmux", rawTarget: target };
7429
7380
  }
7430
- var TerminalStream = class extends EventEmitter {
7431
- target;
7432
- catProcess = null;
7433
- pipePath;
7434
- isAttached = false;
7381
+ var TmuxTerminalStream = class extends EventEmitter {
7435
7382
  constructor(target) {
7436
7383
  super();
7437
7384
  this.target = target;
7438
7385
  this.pipePath = `/tmp/viber-term-${target.replace(/[^a-zA-Z0-9]/g, "-")}-${Date.now()}`;
7439
7386
  }
7440
- /**
7441
- * Start streaming the pane output
7442
- */
7387
+ catProcess = null;
7388
+ pipePath;
7389
+ isAttached = false;
7443
7390
  async attach() {
7444
7391
  if (this.isAttached) return true;
7445
7392
  try {
7446
- const history = capturePane(this.target, 200);
7393
+ const history = captureTmuxPane(this.target, 200);
7447
7394
  if (history) {
7448
7395
  this.emit("data", history);
7449
7396
  }
@@ -7473,9 +7420,6 @@ var TerminalStream = class extends EventEmitter {
7473
7420
  return false;
7474
7421
  }
7475
7422
  }
7476
- /**
7477
- * Stop streaming
7478
- */
7479
7423
  detach() {
7480
7424
  if (!this.isAttached) return;
7481
7425
  try {
@@ -7484,12 +7428,6 @@ var TerminalStream = class extends EventEmitter {
7484
7428
  }
7485
7429
  this.cleanup();
7486
7430
  }
7487
- /**
7488
- * Send input to the pane
7489
- */
7490
- sendInput(keys) {
7491
- return sendKeys(this.target, keys, false);
7492
- }
7493
7431
  cleanup() {
7494
7432
  this.isAttached = false;
7495
7433
  if (this.catProcess) {
@@ -7506,20 +7444,61 @@ var TerminalStream = class extends EventEmitter {
7506
7444
  return this.isAttached;
7507
7445
  }
7508
7446
  };
7509
- var TerminalManager = class {
7447
+ var TmuxTerminalApp = class {
7448
+ id = "tmux";
7449
+ label = "tmux";
7510
7450
  streams = /* @__PURE__ */ new Map();
7511
- /**
7512
- * List all available terminals
7513
- */
7514
- list() {
7515
- return {
7516
- sessions: listSessions(),
7517
- panes: listPanes()
7518
- };
7451
+ isAvailable() {
7452
+ try {
7453
+ execSync("tmux -V", { stdio: "pipe" });
7454
+ return true;
7455
+ } catch {
7456
+ return false;
7457
+ }
7458
+ }
7459
+ listSessions() {
7460
+ try {
7461
+ const out = execSync(
7462
+ "tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
7463
+ { encoding: "utf8", stdio: "pipe" }
7464
+ ).trim();
7465
+ if (!out) return [];
7466
+ return out.split("\n").map((line) => {
7467
+ const [name, windows, attached] = line.split("|");
7468
+ return {
7469
+ appId: this.id,
7470
+ name,
7471
+ windows: parseInt(windows, 10) || 0,
7472
+ attached: attached === "1"
7473
+ };
7474
+ });
7475
+ } catch {
7476
+ return [];
7477
+ }
7478
+ }
7479
+ listPanes() {
7480
+ try {
7481
+ const out = execSync(
7482
+ "tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{pane_index}|#{pane_current_command}' 2>/dev/null",
7483
+ { encoding: "utf8", stdio: "pipe" }
7484
+ ).trim();
7485
+ if (!out) return [];
7486
+ return out.split("\n").map((line) => {
7487
+ const [session, window2, windowName, pane, command] = line.split("|");
7488
+ return {
7489
+ appId: this.id,
7490
+ session,
7491
+ window: window2,
7492
+ windowName,
7493
+ pane,
7494
+ command,
7495
+ target: `${session}:${window2}.${pane}`
7496
+ };
7497
+ });
7498
+ } catch {
7499
+ return [];
7500
+ }
7519
7501
  }
7520
- /**
7521
- * Attach to a pane and return the stream
7522
- */
7523
7502
  async attach(target, onData, onClose) {
7524
7503
  let stream = this.streams.get(target);
7525
7504
  if (stream && stream.attached) {
@@ -7527,7 +7506,7 @@ var TerminalManager = class {
7527
7506
  stream.on("close", onClose);
7528
7507
  return true;
7529
7508
  }
7530
- stream = new TerminalStream(target);
7509
+ stream = new TmuxTerminalStream(target);
7531
7510
  stream.on("data", onData);
7532
7511
  stream.on("close", () => {
7533
7512
  this.streams.delete(target);
@@ -7539,31 +7518,62 @@ var TerminalManager = class {
7539
7518
  }
7540
7519
  return ok;
7541
7520
  }
7542
- /**
7543
- * Detach from a pane
7544
- */
7545
7521
  detach(target) {
7546
7522
  const stream = this.streams.get(target);
7547
- if (stream) {
7548
- stream.detach();
7549
- this.streams.delete(target);
7550
- }
7523
+ if (!stream) return;
7524
+ stream.detach();
7525
+ this.streams.delete(target);
7551
7526
  }
7552
- /**
7553
- * Send input to a pane
7554
- */
7555
7527
  sendInput(target, keys) {
7556
- return sendKeys(target, keys, false);
7528
+ return sendTmuxKeys(target, keys);
7557
7529
  }
7558
- /**
7559
- * Resize pane to match web terminal
7560
- */
7561
7530
  resize(target, cols, rows) {
7562
- return resizePane(target, cols, rows);
7531
+ try {
7532
+ execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
7533
+ encoding: "utf8",
7534
+ stdio: "pipe"
7535
+ });
7536
+ return true;
7537
+ } catch {
7538
+ return false;
7539
+ }
7540
+ }
7541
+ createSession(sessionName, windowName = "main", cwd) {
7542
+ const safeSession = sanitizeName(sessionName || "coding");
7543
+ const safeWindow = sanitizeName(windowName || "main");
7544
+ try {
7545
+ execSync(`tmux has-session -t '${safeSession}' 2>/dev/null`, { stdio: "pipe" });
7546
+ return { ok: true, appId: this.id, sessionName: safeSession, created: false };
7547
+ } catch {
7548
+ }
7549
+ const args = ["new-session", "-d", "-s", safeSession, "-n", safeWindow];
7550
+ if (cwd) {
7551
+ args.push("-c", cwd);
7552
+ }
7553
+ const result = spawnSync("tmux", args, {
7554
+ encoding: "utf8",
7555
+ stdio: "pipe"
7556
+ });
7557
+ if (result.error) {
7558
+ return {
7559
+ ok: false,
7560
+ appId: this.id,
7561
+ sessionName: safeSession,
7562
+ created: false,
7563
+ error: `Failed to start tmux: ${result.error.message}`
7564
+ };
7565
+ }
7566
+ if (result.status !== 0) {
7567
+ return {
7568
+ ok: false,
7569
+ appId: this.id,
7570
+ sessionName: safeSession,
7571
+ created: false,
7572
+ error: (result.stderr || result.stdout || "Failed to create tmux session").trim()
7573
+ };
7574
+ }
7575
+ return { ok: true, appId: this.id, sessionName: safeSession, created: true };
7563
7576
  }
7564
- /**
7565
- * Detach all streams
7566
- */
7567
7577
  detachAll() {
7568
7578
  for (const stream of this.streams.values()) {
7569
7579
  stream.detach();
@@ -7571,6 +7581,203 @@ var TerminalManager = class {
7571
7581
  this.streams.clear();
7572
7582
  }
7573
7583
  };
7584
+ var ShellTerminalApp = class {
7585
+ id = "shell";
7586
+ label = "shell";
7587
+ sessions = /* @__PURE__ */ new Map();
7588
+ isAvailable() {
7589
+ return true;
7590
+ }
7591
+ listSessions() {
7592
+ return Array.from(this.sessions.values()).map((state) => ({
7593
+ appId: this.id,
7594
+ name: state.sessionName,
7595
+ windows: 1,
7596
+ attached: state.listeners.size > 0
7597
+ }));
7598
+ }
7599
+ listPanes() {
7600
+ return Array.from(this.sessions.entries()).map(([id, state]) => ({
7601
+ appId: this.id,
7602
+ session: state.sessionName,
7603
+ window: "0",
7604
+ windowName: state.windowName,
7605
+ pane: state.pane,
7606
+ command: process.env.SHELL || "sh",
7607
+ target: id
7608
+ }));
7609
+ }
7610
+ async attach(target, onData, onClose) {
7611
+ const state = this.sessions.get(target);
7612
+ if (!state) return false;
7613
+ state.listeners.add(onData);
7614
+ state.closeListeners.add(onClose);
7615
+ if (state.history.length > 0) {
7616
+ onData(state.history.join(""));
7617
+ }
7618
+ return true;
7619
+ }
7620
+ detach(target) {
7621
+ const state = this.sessions.get(target);
7622
+ if (!state) return;
7623
+ state.listeners.clear();
7624
+ state.closeListeners.clear();
7625
+ }
7626
+ sendInput(target, keys) {
7627
+ const state = this.sessions.get(target);
7628
+ if (!state || !state.proc.stdin?.writable) return false;
7629
+ state.proc.stdin.write(keys);
7630
+ return true;
7631
+ }
7632
+ resize(_target, _cols, _rows) {
7633
+ return true;
7634
+ }
7635
+ createSession(sessionName, windowName = "main", cwd) {
7636
+ const safeSession = sanitizeName(sessionName || `shell-${Date.now()}`);
7637
+ const shell = process.env.SHELL || "sh";
7638
+ const target = `${safeSession}:${randomUUID().slice(0, 8)}`;
7639
+ if (this.sessions.has(target)) {
7640
+ return { ok: true, appId: this.id, sessionName: safeSession, created: false };
7641
+ }
7642
+ const proc = spawn(shell, [], {
7643
+ cwd,
7644
+ stdio: ["pipe", "pipe", "pipe"],
7645
+ env: process.env
7646
+ });
7647
+ if (!proc.pid) {
7648
+ return {
7649
+ ok: false,
7650
+ appId: this.id,
7651
+ sessionName: safeSession,
7652
+ created: false,
7653
+ error: "Failed to start shell process"
7654
+ };
7655
+ }
7656
+ const state = {
7657
+ proc,
7658
+ sessionName: safeSession,
7659
+ windowName,
7660
+ pane: "0",
7661
+ history: [],
7662
+ listeners: /* @__PURE__ */ new Set(),
7663
+ closeListeners: /* @__PURE__ */ new Set()
7664
+ };
7665
+ const pushChunk = (chunk) => {
7666
+ const text = chunk.toString();
7667
+ state.history.push(text);
7668
+ if (state.history.length > 200) {
7669
+ state.history.shift();
7670
+ }
7671
+ for (const listener of state.listeners) {
7672
+ listener(text);
7673
+ }
7674
+ };
7675
+ proc.stdout?.on("data", pushChunk);
7676
+ proc.stderr?.on("data", pushChunk);
7677
+ proc.on("close", () => {
7678
+ for (const onClose of state.closeListeners) {
7679
+ onClose();
7680
+ }
7681
+ this.sessions.delete(target);
7682
+ });
7683
+ this.sessions.set(target, state);
7684
+ return { ok: true, appId: this.id, sessionName: safeSession, created: true };
7685
+ }
7686
+ detachAll() {
7687
+ for (const [id, state] of this.sessions.entries()) {
7688
+ state.proc.kill();
7689
+ this.sessions.delete(id);
7690
+ }
7691
+ }
7692
+ };
7693
+ function sendTmuxKeys(target, keys, pressEnter = false) {
7694
+ try {
7695
+ const args = ["send-keys", "-t", target, keys];
7696
+ if (pressEnter) args.push("Enter");
7697
+ execSync(`tmux ${args.map((a) => `'${a}'`).join(" ")}`, {
7698
+ encoding: "utf8",
7699
+ stdio: "pipe"
7700
+ });
7701
+ return true;
7702
+ } catch {
7703
+ return false;
7704
+ }
7705
+ }
7706
+ function captureTmuxPane(target, lines = 500) {
7707
+ const cmds = [
7708
+ `tmux capture-pane -t '${target}' -pae -S -${lines}`,
7709
+ `tmux capture-pane -t '${target}' -pe -S -${lines}`
7710
+ ];
7711
+ for (const cmd of cmds) {
7712
+ try {
7713
+ return execSync(cmd, { encoding: "utf8", stdio: "pipe" });
7714
+ } catch {
7715
+ }
7716
+ }
7717
+ return "";
7718
+ }
7719
+ var TerminalManager = class {
7720
+ apps = /* @__PURE__ */ new Map();
7721
+ constructor(adapters) {
7722
+ const defaultAdapters = adapters ?? [new TmuxTerminalApp(), new ShellTerminalApp()];
7723
+ for (const adapter of defaultAdapters) {
7724
+ this.apps.set(adapter.id, adapter);
7725
+ }
7726
+ }
7727
+ list() {
7728
+ const metadata = Array.from(this.apps.values()).map((app) => ({
7729
+ id: app.id,
7730
+ label: app.label,
7731
+ available: app.isAvailable()
7732
+ }));
7733
+ const sessions = [];
7734
+ const panes = [];
7735
+ for (const app of this.apps.values()) {
7736
+ if (!app.isAvailable()) continue;
7737
+ sessions.push(...app.listSessions());
7738
+ panes.push(...app.listPanes());
7739
+ }
7740
+ return { apps: metadata, sessions, panes };
7741
+ }
7742
+ async attach(target, onData, onClose, appHint) {
7743
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7744
+ const app = this.apps.get(appId);
7745
+ if (!app || !app.isAvailable()) return false;
7746
+ return app.attach(rawTarget, onData, onClose);
7747
+ }
7748
+ detach(target, appHint) {
7749
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7750
+ this.apps.get(appId)?.detach(rawTarget);
7751
+ }
7752
+ sendInput(target, keys, appHint) {
7753
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7754
+ const app = this.apps.get(appId);
7755
+ return !!app && app.isAvailable() ? app.sendInput(rawTarget, keys) : false;
7756
+ }
7757
+ resize(target, cols, rows, appHint) {
7758
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7759
+ const app = this.apps.get(appId);
7760
+ return !!app && app.isAvailable() ? app.resize(rawTarget, cols, rows) : false;
7761
+ }
7762
+ createSession(sessionName, windowName = "main", cwd, appId = "tmux") {
7763
+ const app = this.apps.get(appId);
7764
+ if (!app || !app.isAvailable()) {
7765
+ return {
7766
+ ok: false,
7767
+ appId,
7768
+ sessionName,
7769
+ created: false,
7770
+ error: `Terminal app '${appId}' is not available`
7771
+ };
7772
+ }
7773
+ return app.createSession(sessionName, windowName, cwd);
7774
+ }
7775
+ detachAll() {
7776
+ for (const app of this.apps.values()) {
7777
+ app.detachAll();
7778
+ }
7779
+ }
7780
+ };
7574
7781
 
7575
7782
  // src/daemon/controller.ts
7576
7783
  var ViberController = class extends EventEmitter2 {
@@ -7721,16 +7928,16 @@ var ViberController = class extends EventEmitter2 {
7721
7928
  this.handleTerminalList();
7722
7929
  break;
7723
7930
  case "terminal:attach":
7724
- await this.handleTerminalAttach(message.target);
7931
+ await this.handleTerminalAttach(message.target, message.appId);
7725
7932
  break;
7726
7933
  case "terminal:detach":
7727
- this.handleTerminalDetach(message.target);
7934
+ this.handleTerminalDetach(message.target, message.appId);
7728
7935
  break;
7729
7936
  case "terminal:input":
7730
- this.handleTerminalInput(message.target, message.keys);
7937
+ this.handleTerminalInput(message.target, message.keys, message.appId);
7731
7938
  break;
7732
7939
  case "terminal:resize":
7733
- this.handleTerminalResize(message.target, message.cols, message.rows);
7940
+ this.handleTerminalResize(message.target, message.cols, message.rows, message.appId);
7734
7941
  break;
7735
7942
  }
7736
7943
  } catch (error) {
@@ -7760,7 +7967,87 @@ var ViberController = class extends EventEmitter2 {
7760
7967
  },
7761
7968
  messages
7762
7969
  );
7763
- const finalText = await streamResult.text;
7970
+ let finalText = "";
7971
+ let pendingDelta = "";
7972
+ let lastProgressAt = 0;
7973
+ const flushProgress = (force = false) => {
7974
+ if (!pendingDelta) return;
7975
+ const now = Date.now();
7976
+ if (!force && now - lastProgressAt < 250) return;
7977
+ this.send({
7978
+ type: "task:progress",
7979
+ taskId,
7980
+ event: {
7981
+ kind: "text-delta",
7982
+ delta: pendingDelta,
7983
+ totalLength: finalText.length,
7984
+ at: new Date(now).toISOString()
7985
+ }
7986
+ });
7987
+ pendingDelta = "";
7988
+ lastProgressAt = now;
7989
+ };
7990
+ this.send({
7991
+ type: "task:progress",
7992
+ taskId,
7993
+ event: {
7994
+ kind: "status",
7995
+ phase: "executing",
7996
+ message: "Agent execution started",
7997
+ at: (/* @__PURE__ */ new Date()).toISOString()
7998
+ }
7999
+ });
8000
+ for await (const part of streamResult.fullStream) {
8001
+ switch (part.type) {
8002
+ case "text-delta":
8003
+ if (part.text) {
8004
+ finalText += part.text;
8005
+ pendingDelta += part.text;
8006
+ flushProgress(false);
8007
+ }
8008
+ break;
8009
+ case "tool-call":
8010
+ this.send({
8011
+ type: "task:progress",
8012
+ taskId,
8013
+ event: {
8014
+ kind: "tool-call",
8015
+ toolName: part.toolName,
8016
+ toolCallId: part.toolCallId,
8017
+ at: (/* @__PURE__ */ new Date()).toISOString()
8018
+ }
8019
+ });
8020
+ break;
8021
+ case "tool-result":
8022
+ this.send({
8023
+ type: "task:progress",
8024
+ taskId,
8025
+ event: {
8026
+ kind: "tool-result",
8027
+ toolCallId: part.toolCallId,
8028
+ at: (/* @__PURE__ */ new Date()).toISOString()
8029
+ }
8030
+ });
8031
+ break;
8032
+ case "error":
8033
+ throw new Error(part.error?.message || "Task stream error");
8034
+ case "finish":
8035
+ flushProgress(true);
8036
+ this.send({
8037
+ type: "task:progress",
8038
+ taskId,
8039
+ event: {
8040
+ kind: "status",
8041
+ phase: "verifying",
8042
+ message: "Agent execution finished, preparing final output",
8043
+ at: (/* @__PURE__ */ new Date()).toISOString()
8044
+ }
8045
+ });
8046
+ break;
8047
+ default:
8048
+ break;
8049
+ }
8050
+ }
7764
8051
  this.send({
7765
8052
  type: "task:completed",
7766
8053
  taskId,
@@ -7797,33 +8084,34 @@ var ViberController = class extends EventEmitter2 {
7797
8084
  }
7798
8085
  // ==================== Terminal Streaming ====================
7799
8086
  handleTerminalList() {
7800
- const { sessions, panes } = this.terminalManager.list();
7801
- this.send({ type: "terminal:list", sessions, panes });
8087
+ const { apps, sessions, panes } = this.terminalManager.list();
8088
+ this.send({ type: "terminal:list", apps, sessions, panes });
7802
8089
  }
7803
- async handleTerminalAttach(target) {
8090
+ async handleTerminalAttach(target, appId) {
7804
8091
  console.log(`[Viber] Attaching to terminal: ${target}`);
7805
8092
  const ok = await this.terminalManager.attach(
7806
8093
  target,
7807
8094
  (data) => {
7808
- this.send({ type: "terminal:output", target, data });
8095
+ this.send({ type: "terminal:output", target, appId, data });
7809
8096
  },
7810
8097
  () => {
7811
- this.send({ type: "terminal:detached", target });
7812
- }
8098
+ this.send({ type: "terminal:detached", target, appId });
8099
+ },
8100
+ appId
7813
8101
  );
7814
- this.send({ type: "terminal:attached", target, ok });
8102
+ this.send({ type: "terminal:attached", target, appId, ok });
7815
8103
  }
7816
- handleTerminalDetach(target) {
8104
+ handleTerminalDetach(target, appId) {
7817
8105
  console.log(`[Viber] Detaching from terminal: ${target}`);
7818
- this.terminalManager.detach(target);
7819
- this.send({ type: "terminal:detached", target });
8106
+ this.terminalManager.detach(target, appId);
8107
+ this.send({ type: "terminal:detached", target, appId });
7820
8108
  }
7821
- handleTerminalInput(target, keys) {
7822
- this.terminalManager.sendInput(target, keys);
8109
+ handleTerminalInput(target, keys, appId) {
8110
+ this.terminalManager.sendInput(target, keys, appId);
7823
8111
  }
7824
- handleTerminalResize(target, cols, rows) {
7825
- const ok = this.terminalManager.resize(target, cols, rows);
7826
- this.send({ type: "terminal:resized", target, ok });
8112
+ handleTerminalResize(target, cols, rows, appId) {
8113
+ const ok = this.terminalManager.resize(target, cols, rows, appId);
8114
+ this.send({ type: "terminal:resized", target, appId, ok });
7827
8115
  }
7828
8116
  // ==================== Communication ====================
7829
8117
  send(message) {